aboutsummaryrefslogtreecommitdiff
path: root/src/base
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2012-02-08 12:51:15 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2012-02-08 12:51:15 +0000
commitdeb9d6cb53057ca8b90d10d6a3bdc5dcfd1b3ee4 (patch)
treed956bad28e28aadc3c83dbf88b3eddb5e1d9a9f4 /src/base
parente8a1ad0cc608f12874ba4bafbd8282fa537ec9fb (diff)
downloadqxmpp-deb9d6cb53057ca8b90d10d6a3bdc5dcfd1b3ee4.tar.gz
move files common to client/server into "base"
Diffstat (limited to 'src/base')
-rw-r--r--src/base/QXmppArchiveIq.cpp572
-rw-r--r--src/base/QXmppArchiveIq.h249
-rw-r--r--src/base/QXmppBindIq.cpp90
-rw-r--r--src/base/QXmppBindIq.h60
-rw-r--r--src/base/QXmppBookmarkSet.cpp232
-rw-r--r--src/base/QXmppBookmarkSet.h101
-rw-r--r--src/base/QXmppByteStreamIq.cpp183
-rw-r--r--src/base/QXmppByteStreamIq.h97
-rw-r--r--src/base/QXmppCodec.cpp1218
-rw-r--r--src/base/QXmppCodec.h192
-rw-r--r--src/base/QXmppConstants.cpp75
-rw-r--r--src/base/QXmppConstants.h74
-rw-r--r--src/base/QXmppDataForm.cpp448
-rw-r--r--src/base/QXmppDataForm.h140
-rw-r--r--src/base/QXmppDiscoveryIq.cpp300
-rw-r--r--src/base/QXmppDiscoveryIq.h117
-rw-r--r--src/base/QXmppElement.cpp241
-rw-r--r--src/base/QXmppElement.h77
-rw-r--r--src/base/QXmppEntityTimeIq.cpp90
-rw-r--r--src/base/QXmppEntityTimeIq.h56
-rw-r--r--src/base/QXmppGlobal.cpp34
-rw-r--r--src/base/QXmppGlobal.h43
-rw-r--r--src/base/QXmppIbbIq.cpp169
-rw-r--r--src/base/QXmppIbbIq.h105
-rw-r--r--src/base/QXmppIq.cpp147
-rw-r--r--src/base/QXmppIq.h73
-rw-r--r--src/base/QXmppJingleIq.cpp848
-rw-r--r--src/base/QXmppJingleIq.h330
-rw-r--r--src/base/QXmppLogger.cpp245
-rw-r--r--src/base/QXmppLogger.h169
-rw-r--r--src/base/QXmppMessage.cpp417
-rw-r--r--src/base/QXmppMessage.h123
-rw-r--r--src/base/QXmppMucIq.cpp300
-rw-r--r--src/base/QXmppMucIq.h152
-rw-r--r--src/base/QXmppNonSASLAuth.cpp106
-rw-r--r--src/base/QXmppNonSASLAuth.h61
-rw-r--r--src/base/QXmppPacket.cpp34
-rw-r--r--src/base/QXmppPacket.h48
-rw-r--r--src/base/QXmppPingIq.cpp47
-rw-r--r--src/base/QXmppPingIq.h40
-rw-r--r--src/base/QXmppPresence.cpp510
-rw-r--r--src/base/QXmppPresence.h176
-rw-r--r--src/base/QXmppPubSubIq.cpp253
-rw-r--r--src/base/QXmppPubSubIq.h108
-rw-r--r--src/base/QXmppRosterIq.cpp253
-rw-r--r--src/base/QXmppRosterIq.h107
-rw-r--r--src/base/QXmppRpcIq.cpp443
-rw-r--r--src/base/QXmppRpcIq.h130
-rw-r--r--src/base/QXmppRtpChannel.cpp1042
-rw-r--r--src/base/QXmppRtpChannel.h289
-rw-r--r--src/base/QXmppSaslAuth.cpp214
-rw-r--r--src/base/QXmppSaslAuth.h72
-rw-r--r--src/base/QXmppSessionIq.cpp44
-rw-r--r--src/base/QXmppSessionIq.h49
-rw-r--r--src/base/QXmppSocks.cpp345
-rw-r--r--src/base/QXmppSocks.h81
-rw-r--r--src/base/QXmppStanza.cpp436
-rw-r--r--src/base/QXmppStanza.h162
-rw-r--r--src/base/QXmppStream.cpp259
-rw-r--r--src/base/QXmppStream.h92
-rw-r--r--src/base/QXmppStreamFeatures.cpp222
-rw-r--r--src/base/QXmppStreamFeatures.h76
-rw-r--r--src/base/QXmppStreamInitiationIq.cpp106
-rw-r--r--src/base/QXmppStreamInitiationIq.h69
-rw-r--r--src/base/QXmppStun.cpp2581
-rw-r--r--src/base/QXmppStun.h440
-rw-r--r--src/base/QXmppUtils.cpp339
-rw-r--r--src/base/QXmppUtils.h66
-rw-r--r--src/base/QXmppVCardIq.cpp310
-rw-r--r--src/base/QXmppVCardIq.h105
-rw-r--r--src/base/QXmppVersionIq.cpp111
-rw-r--r--src/base/QXmppVersionIq.h62
-rw-r--r--src/base/qdnslookup.cpp989
-rw-r--r--src/base/qdnslookup.h238
-rw-r--r--src/base/qdnslookup_p.h216
-rw-r--r--src/base/qdnslookup_stub.cpp54
-rw-r--r--src/base/qdnslookup_symbian.cpp98
-rw-r--r--src/base/qdnslookup_unix.cpp323
-rw-r--r--src/base/qdnslookup_win.cpp183
79 files changed, 19756 insertions, 0 deletions
diff --git a/src/base/QXmppArchiveIq.cpp b/src/base/QXmppArchiveIq.cpp
new file mode 100644
index 00000000..e8bef8fc
--- /dev/null
+++ b/src/base/QXmppArchiveIq.cpp
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppArchiveIq.h"
+#include "QXmppUtils.h"
+
+static const char *ns_archive = "urn:xmpp:archive";
+static const char *ns_rsm = "http://jabber.org/protocol/rsm";
+
+QXmppArchiveMessage::QXmppArchiveMessage()
+ : m_received(false)
+{
+}
+
+/// Returns the archived message's body.
+
+QString QXmppArchiveMessage::body() const
+{
+ return m_body;
+}
+
+/// Sets the archived message's body.
+///
+/// \param body
+void QXmppArchiveMessage::setBody(const QString &body)
+{
+ m_body = body;
+}
+
+/// Returns the archived message's date.
+
+QDateTime QXmppArchiveMessage::date() const
+{
+ return m_date;
+}
+
+//// Sets the archived message's date.
+///
+/// \param date
+
+void QXmppArchiveMessage::setDate(const QDateTime &date)
+{
+ m_date = date;
+}
+
+/// Returns true if the archived message was received, false if it was sent.
+
+bool QXmppArchiveMessage::isReceived() const
+{
+ return m_received;
+}
+
+/// Set to true if the archived message was received, false if it was sent.
+///
+/// \param isReceived
+
+void QXmppArchiveMessage::setReceived(bool isReceived)
+{
+ m_received = isReceived;
+}
+
+QXmppArchiveChat::QXmppArchiveChat()
+ : m_version(0)
+{
+}
+
+void QXmppArchiveChat::parse(const QDomElement &element)
+{
+ m_with = element.attribute("with");
+ m_start = datetimeFromString(element.attribute("start"));
+ m_subject = element.attribute("subject");
+ m_thread = element.attribute("thread");
+ m_version = element.attribute("version").toInt();
+
+ QDomElement child = element.firstChildElement();
+ while (!child.isNull())
+ {
+ if ((child.tagName() == "from") || (child.tagName() == "to"))
+ {
+ QXmppArchiveMessage message;
+ message.setBody(child.firstChildElement("body").text());
+ message.setDate(m_start.addSecs(child.attribute("secs").toInt()));
+ message.setReceived(child.tagName() == "from");
+ m_messages << message;
+ }
+ child = child.nextSiblingElement();
+ }
+}
+
+void QXmppArchiveChat::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("chat");
+ writer->writeAttribute("xmlns", ns_archive);
+ helperToXmlAddAttribute(writer, "with", m_with);
+ if (m_start.isValid())
+ helperToXmlAddAttribute(writer, "start", datetimeToString(m_start));
+ helperToXmlAddAttribute(writer, "subject", m_subject);
+ helperToXmlAddAttribute(writer, "thread", m_thread);
+ if (m_version)
+ helperToXmlAddAttribute(writer, "version", QString::number(m_version));
+ foreach (const QXmppArchiveMessage &message, m_messages)
+ {
+ writer->writeStartElement(message.isReceived() ? "from" : "to");
+ helperToXmlAddAttribute(writer, "secs", QString::number(m_start.secsTo(message.date())));
+ writer->writeTextElement("body", message.body());
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+}
+
+/// Returns the conversation's messages.
+
+QList<QXmppArchiveMessage> QXmppArchiveChat::messages() const
+{
+ return m_messages;
+}
+
+/// Sets the conversation's messages.
+
+void QXmppArchiveChat::setMessages(const QList<QXmppArchiveMessage> &messages)
+{
+ m_messages = messages;
+}
+
+/// Returns the start of this conversation.
+
+QDateTime QXmppArchiveChat::start() const
+{
+ return m_start;
+}
+
+/// Sets the start of this conversation.
+
+void QXmppArchiveChat::setStart(const QDateTime &start)
+{
+ m_start = start;
+}
+
+/// Returns the conversation's subject.
+
+QString QXmppArchiveChat::subject() const
+{
+ return m_subject;
+}
+
+/// Sets the conversation's subject.
+
+void QXmppArchiveChat::setSubject(const QString &subject)
+{
+ m_subject = subject;
+}
+
+/// Returns the conversation's thread.
+
+QString QXmppArchiveChat::thread() const
+{
+ return m_thread;
+}
+
+/// Sets the conversation's thread.
+
+void QXmppArchiveChat::setThread(const QString &thread)
+{
+ m_thread = thread;
+}
+
+/// Returns the conversation's version.
+
+int QXmppArchiveChat::version() const
+{
+ return m_version;
+}
+
+/// Sets the conversation's version.
+
+void QXmppArchiveChat::setVersion(int version)
+{
+ m_version = version;
+}
+
+/// Returns the JID of the remote party.
+
+QString QXmppArchiveChat::with() const
+{
+ return m_with;
+}
+
+/// Sets the JID of the remote party.
+
+void QXmppArchiveChat::setWith(const QString &with)
+{
+ m_with = with;
+}
+
+/// Returns the chat conversation carried by this IQ.
+
+QXmppArchiveChat QXmppArchiveChatIq::chat() const
+{
+ return m_chat;
+}
+
+/// Sets the chat conversation carried by this IQ.
+
+void QXmppArchiveChatIq::setChat(const QXmppArchiveChat &chat)
+{
+ m_chat = chat;
+}
+
+bool QXmppArchiveChatIq::isArchiveChatIq(const QDomElement &element)
+{
+ QDomElement chatElement = element.firstChildElement("chat");
+ return !chatElement.attribute("with").isEmpty();
+ //return (chatElement.namespaceURI() == ns_archive);
+}
+
+void QXmppArchiveChatIq::parseElementFromChild(const QDomElement &element)
+{
+ m_chat.parse(element.firstChildElement("chat"));
+}
+
+void QXmppArchiveChatIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ m_chat.toXml(writer);
+}
+
+/// Constructs a QXmppArchiveListIq.
+
+QXmppArchiveListIq::QXmppArchiveListIq()
+ : QXmppIq(QXmppIq::Get), m_max(0)
+{
+}
+
+/// Returns the list of chat conversations.
+
+QList<QXmppArchiveChat> QXmppArchiveListIq::chats() const
+{
+ return m_chats;
+}
+
+/// Sets the list of chat conversations.
+
+void QXmppArchiveListIq::setChats(const QList<QXmppArchiveChat> &chats)
+{
+ m_chats = chats;
+}
+
+/// Returns the maximum number of results.
+///
+
+int QXmppArchiveListIq::max() const
+{
+ return m_max;
+}
+
+/// Sets the maximum number of results.
+///
+/// \param max
+
+void QXmppArchiveListIq::setMax(int max)
+{
+ m_max = max;
+}
+
+/// Returns the JID which archived conversations must match.
+///
+
+QString QXmppArchiveListIq::with() const
+{
+ return m_with;
+}
+
+/// Sets the JID which archived conversations must match.
+///
+/// \param with
+
+void QXmppArchiveListIq::setWith(const QString &with)
+{
+ m_with = with;
+}
+
+/// Returns the start date/time for the archived conversations.
+///
+
+QDateTime QXmppArchiveListIq::start() const
+{
+ return m_start;
+}
+
+/// Sets the start date/time for the archived conversations.
+///
+/// \param start
+
+void QXmppArchiveListIq::setStart(const QDateTime &start)
+{
+ m_start = start;
+}
+
+/// Returns the end date/time for the archived conversations.
+///
+
+QDateTime QXmppArchiveListIq::end() const
+{
+ return m_end;
+}
+
+/// Sets the end date/time for the archived conversations.
+///
+/// \param end
+
+void QXmppArchiveListIq::setEnd(const QDateTime &end)
+{
+ m_end = end;
+}
+
+bool QXmppArchiveListIq::isArchiveListIq(const QDomElement &element)
+{
+ QDomElement listElement = element.firstChildElement("list");
+ return (listElement.namespaceURI() == ns_archive);
+}
+
+void QXmppArchiveListIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement listElement = element.firstChildElement("list");
+ m_with = listElement.attribute("with");
+ m_start = datetimeFromString(listElement.attribute("start"));
+ m_end = datetimeFromString(listElement.attribute("end"));
+
+ QDomElement setElement = listElement.firstChildElement("set");
+ if (setElement.namespaceURI() == ns_rsm)
+ m_max = setElement.firstChildElement("max").text().toInt();
+
+ QDomElement child = listElement.firstChildElement();
+ while (!child.isNull())
+ {
+ if (child.tagName() == "chat")
+ {
+ QXmppArchiveChat chat;
+ chat.parse(child);
+ m_chats << chat;
+ }
+ child = child.nextSiblingElement();
+ }
+}
+
+void QXmppArchiveListIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("list");
+ writer->writeAttribute("xmlns", ns_archive);
+ if (!m_with.isEmpty())
+ helperToXmlAddAttribute(writer, "with", m_with);
+ if (m_start.isValid())
+ helperToXmlAddAttribute(writer, "start", datetimeToString(m_start));
+ if (m_end.isValid())
+ helperToXmlAddAttribute(writer, "end", datetimeToString(m_end));
+ if (m_max > 0)
+ {
+ writer->writeStartElement("set");
+ writer->writeAttribute("xmlns", ns_rsm);
+ helperToXmlAddTextElement(writer, "max", QString::number(m_max));
+ writer->writeEndElement();
+ }
+ foreach (const QXmppArchiveChat &chat, m_chats)
+ chat.toXml(writer);
+ writer->writeEndElement();
+}
+
+bool QXmppArchivePrefIq::isArchivePrefIq(const QDomElement &element)
+{
+ QDomElement prefElement = element.firstChildElement("pref");
+ return (prefElement.namespaceURI() == ns_archive);
+}
+
+void QXmppArchivePrefIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("pref");
+ Q_UNUSED(queryElement);
+}
+
+void QXmppArchivePrefIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("pref");
+ writer->writeAttribute("xmlns", ns_archive);
+ writer->writeEndElement();
+}
+
+/// Returns the JID which archived conversations must match.
+///
+
+QString QXmppArchiveRemoveIq::with() const
+{
+ return m_with;
+}
+
+/// Sets the JID which archived conversations must match.
+///
+/// \param with
+
+void QXmppArchiveRemoveIq::setWith(const QString &with)
+{
+ m_with = with;
+}
+
+/// Returns the start date/time for the archived conversations.
+///
+
+QDateTime QXmppArchiveRemoveIq::start() const
+{
+ return m_start;
+}
+
+/// Sets the start date/time for the archived conversations.
+///
+/// \param start
+
+void QXmppArchiveRemoveIq::setStart(const QDateTime &start)
+{
+ m_start = start;
+}
+
+/// Returns the end date/time for the archived conversations.
+///
+
+QDateTime QXmppArchiveRemoveIq::end() const
+{
+ return m_end;
+}
+
+/// Sets the end date/time for the archived conversations.
+///
+/// \param end
+
+void QXmppArchiveRemoveIq::setEnd(const QDateTime &end)
+{
+ m_end = end;
+}
+
+bool QXmppArchiveRemoveIq::isArchiveRemoveIq(const QDomElement &element)
+{
+ QDomElement retrieveElement = element.firstChildElement("remove");
+ return (retrieveElement.namespaceURI() == ns_archive);
+}
+
+void QXmppArchiveRemoveIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement listElement = element.firstChildElement("remove");
+ m_with = listElement.attribute("with");
+ m_start = datetimeFromString(listElement.attribute("start"));
+ m_end = datetimeFromString(listElement.attribute("end"));
+}
+
+void QXmppArchiveRemoveIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("remove");
+ writer->writeAttribute("xmlns", ns_archive);
+ if (!m_with.isEmpty())
+ helperToXmlAddAttribute(writer, "with", m_with);
+ if (m_start.isValid())
+ helperToXmlAddAttribute(writer, "start", datetimeToString(m_start));
+ if (m_end.isValid())
+ helperToXmlAddAttribute(writer, "end", datetimeToString(m_end));
+ writer->writeEndElement();
+}
+
+QXmppArchiveRetrieveIq::QXmppArchiveRetrieveIq()
+ : QXmppIq(QXmppIq::Get), m_max(0)
+{
+}
+
+/// Returns the maximum number of results.
+///
+
+int QXmppArchiveRetrieveIq::max() const
+{
+ return m_max;
+}
+
+/// Sets the maximum number of results.
+///
+/// \param max
+
+void QXmppArchiveRetrieveIq::setMax(int max)
+{
+ m_max = max;
+}
+
+/// Returns the start date/time for the archived conversations.
+///
+
+QDateTime QXmppArchiveRetrieveIq::start() const
+{
+ return m_start;
+}
+
+/// Sets the start date/time for the archived conversations.
+///
+/// \param start
+
+void QXmppArchiveRetrieveIq::setStart(const QDateTime &start)
+{
+ m_start = start;
+}
+
+/// Returns the JID which archived conversations must match.
+///
+
+QString QXmppArchiveRetrieveIq::with() const
+{
+ return m_with;
+}
+
+/// Sets the JID which archived conversations must match.
+///
+/// \param with
+
+void QXmppArchiveRetrieveIq::setWith(const QString &with)
+{
+ m_with = with;
+}
+
+bool QXmppArchiveRetrieveIq::isArchiveRetrieveIq(const QDomElement &element)
+{
+ QDomElement retrieveElement = element.firstChildElement("retrieve");
+ return (retrieveElement.namespaceURI() == ns_archive);
+}
+
+void QXmppArchiveRetrieveIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement retrieveElement = element.firstChildElement("retrieve");
+ m_with = retrieveElement.attribute("with");
+ m_start = datetimeFromString(retrieveElement.attribute("start"));
+ QDomElement setElement = retrieveElement.firstChildElement("set");
+ if (setElement.namespaceURI() == ns_rsm)
+ m_max = setElement.firstChildElement("max").text().toInt();
+}
+
+void QXmppArchiveRetrieveIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("retrieve");
+ writer->writeAttribute("xmlns", ns_archive);
+ helperToXmlAddAttribute(writer, "with", m_with);
+ helperToXmlAddAttribute(writer, "start", datetimeToString(m_start));
+ if (m_max > 0)
+ {
+ writer->writeStartElement("set");
+ writer->writeAttribute("xmlns", ns_rsm);
+ helperToXmlAddTextElement(writer, "max", QString::number(m_max));
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+}
diff --git a/src/base/QXmppArchiveIq.h b/src/base/QXmppArchiveIq.h
new file mode 100644
index 00000000..c0784891
--- /dev/null
+++ b/src/base/QXmppArchiveIq.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPARCHIVEIQ_H
+#define QXMPPARCHIVEIQ_H
+
+#include "QXmppIq.h"
+
+#include <QDateTime>
+
+class QXmlStreamWriter;
+class QDomElement;
+
+/// \brief The QXmppArchiveMessage represents an archived message
+/// as defined by XEP-0136: Message Archiving.
+
+class QXmppArchiveMessage
+{
+public:
+ QXmppArchiveMessage();
+
+ QString body() const;
+ void setBody(const QString &body);
+
+ QDateTime date() const;
+ void setDate(const QDateTime &date);
+
+ bool isReceived() const;
+ void setReceived(bool isReceived);
+
+private:
+ QString m_body;
+ QDateTime m_date;
+ bool m_received;
+};
+
+/// \brief The QXmppArchiveChat represents an archived conversation
+/// as defined by XEP-0136: Message Archiving.
+
+class QXmppArchiveChat
+{
+public:
+ QXmppArchiveChat();
+
+ QList<QXmppArchiveMessage> messages() const;
+ void setMessages(const QList<QXmppArchiveMessage> &messages);
+
+ QDateTime start() const;
+ void setStart(const QDateTime &start);
+
+ QString subject() const;
+ void setSubject(const QString &subject);
+
+ QString thread() const;
+ void setThread(const QString &thread);
+
+ int version() const;
+ void setVersion(int version);
+
+ QString with() const;
+ void setWith(const QString &with);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QList<QXmppArchiveMessage> m_messages;
+ QDateTime m_start;
+ QString m_subject;
+ QString m_thread;
+ int m_version;
+ QString m_with;
+};
+
+/// \brief Represents an archive chat as defined by XEP-0136: Message Archiving.
+///
+/// It is used to get chat as a QXmppArchiveChat.
+///
+/// \ingroup Stanzas
+
+class QXmppArchiveChatIq : public QXmppIq
+{
+public:
+ QXmppArchiveChat chat() const;
+ void setChat(const QXmppArchiveChat &chat);
+
+ /// \cond
+ static bool isArchiveChatIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QXmppArchiveChat m_chat;
+};
+
+/// \brief Represents an archive list as defined by XEP-0136: Message Archiving.
+///
+/// \ingroup Stanzas
+
+class QXmppArchiveListIq : public QXmppIq
+{
+public:
+ QXmppArchiveListIq();
+
+ QList<QXmppArchiveChat> chats() const;
+ void setChats(const QList<QXmppArchiveChat> &chats);
+
+ int max() const;
+ void setMax(int max);
+
+ QString with() const;
+ void setWith( const QString &with );
+
+ QDateTime start() const;
+ void setStart(const QDateTime &start );
+
+ QDateTime end() const;
+ void setEnd(const QDateTime &end );
+
+ /// \cond
+ static bool isArchiveListIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ int m_max;
+ QString m_with;
+ QDateTime m_start;
+ QDateTime m_end;
+ QList<QXmppArchiveChat> m_chats;
+};
+
+/// \brief Represents an archive remove IQ as defined by XEP-0136: Message Archiving.
+///
+/// \ingroup Stanzas
+
+class QXmppArchiveRemoveIq : public QXmppIq
+{
+public:
+ QString with() const;
+ void setWith( const QString &with );
+
+ QDateTime start() const;
+ void setStart(const QDateTime &start );
+
+ QDateTime end() const;
+ void setEnd(const QDateTime &end );
+
+ /// \cond
+ static bool isArchiveRemoveIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString m_with;
+ QDateTime m_start;
+ QDateTime m_end;
+};
+
+/// \brief Represents an archive retrieve IQ as defined by XEP-0136: Message Archiving.
+///
+/// \ingroup Stanzas
+
+class QXmppArchiveRetrieveIq : public QXmppIq
+{
+public:
+ QXmppArchiveRetrieveIq();
+
+ int max() const;
+ void setMax(int max);
+
+ QDateTime start() const;
+ void setStart(const QDateTime &start);
+
+ QString with() const;
+ void setWith(const QString &with);
+
+ /// \cond
+ static bool isArchiveRetrieveIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ int m_max;
+ QString m_with;
+ QDateTime m_start;
+};
+
+/// \brief Represents an archive preference IQ as defined by XEP-0136: Message Archiving.
+///
+/// \ingroup Stanzas
+
+class QXmppArchivePrefIq : public QXmppIq
+{
+public:
+ /// \cond
+ static bool isArchivePrefIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+};
+
+#endif // QXMPPARCHIVEIQ_H
diff --git a/src/base/QXmppBindIq.cpp b/src/base/QXmppBindIq.cpp
new file mode 100644
index 00000000..f818e002
--- /dev/null
+++ b/src/base/QXmppBindIq.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+#include <QTextStream>
+#include <QXmlStreamWriter>
+
+#include "QXmppBindIq.h"
+#include "QXmppUtils.h"
+#include "QXmppConstants.h"
+
+/// Returns the bound JID.
+///
+
+QString QXmppBindIq::jid() const
+{
+ return m_jid;
+}
+
+/// Sets the bound JID.
+///
+/// \param jid
+
+void QXmppBindIq::setJid(const QString& jid)
+{
+ m_jid = jid;
+}
+
+/// Returns the requested resource.
+///
+
+QString QXmppBindIq::resource() const
+{
+ return m_resource;
+}
+
+/// Sets the requested resource.
+///
+/// \param resource
+
+void QXmppBindIq::setResource(const QString& resource)
+{
+ m_resource = resource;
+}
+
+bool QXmppBindIq::isBindIq(const QDomElement &element)
+{
+ QDomElement bindElement = element.firstChildElement("bind");
+ return (bindElement.namespaceURI() == ns_bind);
+}
+
+void QXmppBindIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement bindElement = element.firstChildElement("bind");
+ m_jid = bindElement.firstChildElement("jid").text();
+ m_resource = bindElement.firstChildElement("resource").text();
+}
+
+void QXmppBindIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("bind");
+ writer->writeAttribute("xmlns", ns_bind);
+ if (!m_jid.isEmpty())
+ helperToXmlAddTextElement(writer, "jid", m_jid);
+ if (!m_resource.isEmpty())
+ helperToXmlAddTextElement(writer, "resource", m_resource);
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppBindIq.h b/src/base/QXmppBindIq.h
new file mode 100644
index 00000000..0131510c
--- /dev/null
+++ b/src/base/QXmppBindIq.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPBINDIQ_H
+#define QXMPPBINDIQ_H
+
+#include "QXmppIq.h"
+
+/// \brief The QXmppBindIq class represents an IQ used for resource
+/// binding as defined by RFC 5921.
+///
+/// \ingroup Stanzas
+
+class QXmppBindIq : public QXmppIq
+{
+public:
+ QString jid() const;
+ void setJid(const QString&);
+
+ QString resource() const;
+ void setResource(const QString&);
+
+ /// \cond
+ static bool isBindIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString m_jid;
+ QString m_resource;
+};
+
+#endif // QXMPPBIND_H
diff --git a/src/base/QXmppBookmarkSet.cpp b/src/base/QXmppBookmarkSet.cpp
new file mode 100644
index 00000000..b418d498
--- /dev/null
+++ b/src/base/QXmppBookmarkSet.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppBookmarkSet.h"
+#include "QXmppUtils.h"
+
+static const char *ns_bookmarks = "storage:bookmarks";
+
+/// Constructs a new conference room bookmark.
+///
+
+QXmppBookmarkConference::QXmppBookmarkConference()
+ : m_autoJoin(false)
+{
+}
+
+/// Returns whether the client should automatically join the conference room
+/// on login.
+///
+
+bool QXmppBookmarkConference::autoJoin() const
+{
+ return m_autoJoin;
+}
+
+/// Sets whether the client should automatically join the conference room
+/// on login.
+///
+/// \param autoJoin
+
+void QXmppBookmarkConference::setAutoJoin(bool autoJoin)
+{
+ m_autoJoin = autoJoin;
+}
+
+/// Returns the JID of the conference room.
+///
+
+QString QXmppBookmarkConference::jid() const
+{
+ return m_jid;
+}
+
+/// Sets the JID of the conference room.
+///
+/// \param jid
+
+void QXmppBookmarkConference::setJid(const QString &jid)
+{
+ m_jid = jid;
+}
+
+/// Returns the friendly name for the bookmark.
+///
+
+QString QXmppBookmarkConference::name() const
+{
+ return m_name;
+}
+
+/// Sets the friendly name for the bookmark.
+///
+/// \param name
+
+void QXmppBookmarkConference::setName(const QString &name)
+{
+ m_name = name;
+}
+
+/// Returns the preferred nickname for the conference room.
+///
+
+QString QXmppBookmarkConference::nickName() const
+{
+ return m_nickName;
+}
+
+/// Sets the preferred nickname for the conference room.
+///
+/// \param nickName
+
+void QXmppBookmarkConference::setNickName(const QString &nickName)
+{
+ m_nickName = nickName;
+}
+
+/// Returns the friendly name for the bookmark.
+///
+
+QString QXmppBookmarkUrl::name() const
+{
+ return m_name;
+}
+
+/// Sets the friendly name for the bookmark.
+///
+/// \param name
+
+void QXmppBookmarkUrl::setName(const QString &name)
+{
+ m_name = name;
+}
+
+/// Returns the URL for the web page.
+///
+
+QUrl QXmppBookmarkUrl::url() const
+{
+ return m_url;
+}
+
+/// Sets the URL for the web page.
+///
+/// \param url
+
+void QXmppBookmarkUrl::setUrl(const QUrl &url)
+{
+ m_url = url;
+}
+
+/// Returns the conference rooms bookmarks in this bookmark set.
+///
+
+QList<QXmppBookmarkConference> QXmppBookmarkSet::conferences() const
+{
+ return m_conferences;
+}
+
+/// Sets the conference rooms bookmarks in this bookmark set.
+///
+/// \param conferences
+
+void QXmppBookmarkSet::setConferences(const QList<QXmppBookmarkConference> &conferences)
+{
+ m_conferences = conferences;
+}
+
+/// Returns the web page bookmarks in this bookmark set.
+///
+
+QList<QXmppBookmarkUrl> QXmppBookmarkSet::urls() const
+{
+ return m_urls;
+}
+
+/// Sets the web page bookmarks in this bookmark set.
+///
+/// \param urls
+
+void QXmppBookmarkSet::setUrls(const QList<QXmppBookmarkUrl> &urls)
+{
+ m_urls = urls;
+}
+
+bool QXmppBookmarkSet::isBookmarkSet(const QDomElement &element)
+{
+ return element.tagName() == "storage" &&
+ element.namespaceURI() == ns_bookmarks;
+}
+
+void QXmppBookmarkSet::parse(const QDomElement &element)
+{
+ QDomElement childElement = element.firstChildElement();
+ while (!childElement.isNull())
+ {
+ if (childElement.tagName() == "conference")
+ {
+ QXmppBookmarkConference conference;
+ conference.setAutoJoin(childElement.attribute("autojoin") == "true");
+ conference.setJid(childElement.attribute("jid"));
+ conference.setName(childElement.attribute("name"));
+ conference.setNickName(childElement.firstChildElement("nick").text());
+ m_conferences << conference;
+ }
+ else if (childElement.tagName() == "url")
+ {
+ QXmppBookmarkUrl url;
+ url.setName(childElement.attribute("name"));
+ url.setUrl(childElement.attribute("url"));
+ m_urls << url;
+ }
+ childElement = childElement.nextSiblingElement();
+ }
+}
+
+void QXmppBookmarkSet::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("storage");
+ writer->writeAttribute("xmlns", ns_bookmarks);
+ foreach (const QXmppBookmarkConference &conference, m_conferences)
+ {
+ writer->writeStartElement("conference");
+ if (conference.autoJoin())
+ helperToXmlAddAttribute(writer, "autojoin", "true");
+ helperToXmlAddAttribute(writer, "jid", conference.jid());
+ helperToXmlAddAttribute(writer, "name", conference.name());
+ if (!conference.nickName().isEmpty())
+ helperToXmlAddTextElement(writer, "nick", conference.nickName());
+ writer->writeEndElement();
+ }
+ foreach (const QXmppBookmarkUrl &url, m_urls)
+ {
+ writer->writeStartElement("url");
+ helperToXmlAddAttribute(writer, "name", url.name());
+ helperToXmlAddAttribute(writer, "url", url.url().toString());
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppBookmarkSet.h b/src/base/QXmppBookmarkSet.h
new file mode 100644
index 00000000..aa90f2cf
--- /dev/null
+++ b/src/base/QXmppBookmarkSet.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPBOOKMARKSET_H
+#define QXMPPBOOKMARKSET_H
+
+#include <QList>
+#include <QString>
+#include <QUrl>
+#include <QXmlStreamWriter>
+
+class QDomElement;
+
+/// \brief The QXmppBookmarkConference class represents a bookmark for a conference room,
+/// as defined by XEP-0048: Bookmarks.
+///
+class QXmppBookmarkConference
+{
+public:
+ QXmppBookmarkConference();
+
+ bool autoJoin() const;
+ void setAutoJoin(bool autoJoin);
+
+ QString jid() const;
+ void setJid(const QString &jid);
+
+ QString name() const;
+ void setName(const QString &name);
+
+ QString nickName() const;
+ void setNickName(const QString &nickName);
+
+private:
+ bool m_autoJoin;
+ QString m_jid;
+ QString m_name;
+ QString m_nickName;
+};
+
+/// \brief The QXmppBookmarkUrl class represents a bookmark for a web page,
+/// as defined by XEP-0048: Bookmarks.
+///
+class QXmppBookmarkUrl
+{
+public:
+ QString name() const;
+ void setName(const QString &name);
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+private:
+ QString m_name;
+ QUrl m_url;
+};
+
+/// \brief The QXmppbookmarkSets class represents a set of bookmarks, as defined
+/// by XEP-0048: Bookmarks.
+///
+class QXmppBookmarkSet
+{
+public:
+ QList<QXmppBookmarkConference> conferences() const;
+ void setConferences(const QList<QXmppBookmarkConference> &conferences);
+
+ QList<QXmppBookmarkUrl> urls() const;
+ void setUrls(const QList<QXmppBookmarkUrl> &urls);
+
+ /// \cond
+ static bool isBookmarkSet(const QDomElement &element);
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endconf
+
+private:
+ QList<QXmppBookmarkConference> m_conferences;
+ QList<QXmppBookmarkUrl> m_urls;
+};
+
+#endif
diff --git a/src/base/QXmppByteStreamIq.cpp b/src/base/QXmppByteStreamIq.cpp
new file mode 100644
index 00000000..e1628e72
--- /dev/null
+++ b/src/base/QXmppByteStreamIq.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppByteStreamIq.h"
+#include "QXmppConstants.h"
+#include "QXmppUtils.h"
+
+QHostAddress QXmppByteStreamIq::StreamHost::host() const
+{
+ return m_host;
+}
+
+void QXmppByteStreamIq::StreamHost::setHost(const QHostAddress &host)
+{
+ m_host = host;
+}
+
+QString QXmppByteStreamIq::StreamHost::jid() const
+{
+ return m_jid;
+}
+
+void QXmppByteStreamIq::StreamHost::setJid(const QString &jid)
+{
+ m_jid = jid;
+}
+
+quint16 QXmppByteStreamIq::StreamHost::port() const
+{
+ return m_port;
+}
+
+void QXmppByteStreamIq::StreamHost::setPort(quint16 port)
+{
+ m_port = port;
+}
+
+QString QXmppByteStreamIq::StreamHost::zeroconf() const
+{
+ return m_zeroconf;
+}
+
+void QXmppByteStreamIq::StreamHost::setZeroconf(const QString &zeroconf)
+{
+ m_zeroconf = zeroconf;
+}
+
+QXmppByteStreamIq::Mode QXmppByteStreamIq::mode() const
+{
+ return m_mode;
+}
+
+void QXmppByteStreamIq::setMode(QXmppByteStreamIq::Mode mode)
+{
+ m_mode = mode;
+}
+
+QString QXmppByteStreamIq::sid() const
+{
+ return m_sid;
+}
+
+void QXmppByteStreamIq::setSid(const QString &sid)
+{
+ m_sid = sid;
+}
+
+QString QXmppByteStreamIq::activate() const
+{
+ return m_activate;
+}
+
+void QXmppByteStreamIq::setActivate(const QString &activate)
+{
+ m_activate = activate;
+}
+
+QList<QXmppByteStreamIq::StreamHost> QXmppByteStreamIq::streamHosts() const
+{
+ return m_streamHosts;
+}
+
+void QXmppByteStreamIq::setStreamHosts(const QList<QXmppByteStreamIq::StreamHost> &streamHosts)
+{
+ m_streamHosts = streamHosts;
+}
+
+QString QXmppByteStreamIq::streamHostUsed() const
+{
+ return m_streamHostUsed;
+}
+
+void QXmppByteStreamIq::setStreamHostUsed(const QString &jid)
+{
+ m_streamHostUsed = jid;
+}
+
+bool QXmppByteStreamIq::isByteStreamIq(const QDomElement &element)
+{
+ return element.firstChildElement("query").namespaceURI() == ns_bytestreams;
+}
+
+void QXmppByteStreamIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ m_sid = queryElement.attribute("sid");
+ const QString modeStr = queryElement.attribute("mode");
+ if (modeStr == "tcp")
+ m_mode = Tcp;
+ else if (modeStr == "udp")
+ m_mode = Udp;
+ else
+ m_mode = None;
+
+ QDomElement hostElement = queryElement.firstChildElement("streamhost");
+ while (!hostElement.isNull())
+ {
+ StreamHost streamHost;
+ streamHost.setHost(QHostAddress(hostElement.attribute("host")));
+ streamHost.setJid(hostElement.attribute("jid"));
+ streamHost.setPort(hostElement.attribute("port").toInt());
+ streamHost.setZeroconf(hostElement.attribute("zeroconf"));
+ m_streamHosts.append(streamHost);
+
+ hostElement = hostElement.nextSiblingElement("streamhost");
+ }
+ m_activate = queryElement.firstChildElement("activate").text();
+ m_streamHostUsed = queryElement.firstChildElement("streamhost-used").attribute("jid");
+}
+
+void QXmppByteStreamIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute("xmlns", ns_bytestreams);
+ helperToXmlAddAttribute(writer, "sid", m_sid);
+ QString modeStr;
+ if (m_mode == Tcp)
+ modeStr = "tcp";
+ else if (m_mode == Udp)
+ modeStr = "udp";
+ helperToXmlAddAttribute(writer, "mode", modeStr);
+ foreach (const StreamHost& streamHost, m_streamHosts)
+ {
+ writer->writeStartElement("streamhost");
+ helperToXmlAddAttribute(writer, "host", streamHost.host().toString());
+ helperToXmlAddAttribute(writer, "jid", streamHost.jid());
+ helperToXmlAddAttribute(writer, "port", QString::number(streamHost.port()));
+ helperToXmlAddAttribute(writer, "zeroconf", streamHost.zeroconf());
+ writer->writeEndElement();
+ }
+ if (!m_activate.isEmpty())
+ helperToXmlAddTextElement(writer, "activate", m_activate);
+ if (!m_streamHostUsed.isEmpty())
+ {
+ writer->writeStartElement("streamhost-used");
+ helperToXmlAddAttribute(writer, "jid", m_streamHostUsed);
+ writer->writeEndElement();
+ }
+
+ writer->writeEndElement();
+}
diff --git a/src/base/QXmppByteStreamIq.h b/src/base/QXmppByteStreamIq.h
new file mode 100644
index 00000000..604c12e5
--- /dev/null
+++ b/src/base/QXmppByteStreamIq.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPBYTESTREAMIQ_H
+#define QXMPPBYTESTREAMIQ_H
+
+#include "QXmppIq.h"
+
+#include <QHostAddress>
+
+class QDomElement;
+class QXmlStreamWriter;
+
+class QXmppByteStreamIq : public QXmppIq
+{
+public:
+ enum Mode {
+ None = 0,
+ Tcp,
+ Udp,
+ };
+
+ class StreamHost
+ {
+ public:
+ QString jid() const;
+ void setJid(const QString &jid);
+
+ QHostAddress host() const;
+ void setHost(const QHostAddress &host);
+
+ quint16 port() const;
+ void setPort(quint16 port);
+
+ QString zeroconf() const;
+ void setZeroconf(const QString &zeroconf);
+
+ private:
+ QHostAddress m_host;
+ QString m_jid;
+ quint16 m_port;
+ QString m_zeroconf;
+ };
+
+ QXmppByteStreamIq::Mode mode() const;
+ void setMode(QXmppByteStreamIq::Mode mode);
+
+ QString sid() const;
+ void setSid(const QString &sid);
+
+ QString activate() const;
+ void setActivate(const QString &activate);
+
+ QList<QXmppByteStreamIq::StreamHost> streamHosts() const;
+ void setStreamHosts(const QList<QXmppByteStreamIq::StreamHost> &streamHosts);
+
+ QString streamHostUsed() const;
+ void setStreamHostUsed(const QString &jid);
+
+ static bool isByteStreamIq(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ Mode m_mode;
+ QString m_sid;
+
+ QString m_activate;
+ QList<StreamHost> m_streamHosts;
+ QString m_streamHostUsed;
+};
+
+#endif
diff --git a/src/base/QXmppCodec.cpp b/src/base/QXmppCodec.cpp
new file mode 100644
index 00000000..a614ce01
--- /dev/null
+++ b/src/base/QXmppCodec.cpp
@@ -0,0 +1,1218 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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.
+ *
+ */
+
+/*
+ * G.711 based on reference implementation by Sun Microsystems, Inc.
+ */
+
+#include <QDataStream>
+#include <QDebug>
+#include <QSize>
+
+#include "QXmppCodec.h"
+#include "QXmppRtpChannel.h"
+
+#include <cstring>
+
+#ifdef QXMPP_USE_SPEEX
+#include <speex/speex.h>
+#endif
+
+#ifdef QXMPP_USE_THEORA
+#include <theora/theoradec.h>
+#include <theora/theoraenc.h>
+#endif
+
+#ifdef QXMPP_USE_VPX
+#define VPX_CODEC_DISABLE_COMPAT 1
+#include <vpx/vpx_decoder.h>
+#include <vpx/vpx_encoder.h>
+#include <vpx/vp8cx.h>
+#include <vpx/vp8dx.h>
+#endif
+
+#define BIAS (0x84) /* Bias for linear code. */
+#define CLIP 8159
+
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+enum FragmentType {
+ NoFragment = 0,
+ StartFragment,
+ MiddleFragment,
+ EndFragment,
+};
+
+static qint16 seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static qint16 seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+static qint16 search(qint16 val, qint16 *table, qint16 size)
+{
+ qint16 i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * Accepts a 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+quint8 linear2alaw(qint16 pcm_val)
+{
+ qint16 mask;
+ qint16 seg;
+ quint8 aval;
+
+ pcm_val = pcm_val >> 3;
+
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (quint8) (0x7F ^ mask);
+ else {
+ aval = (quint8) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+qint16 alaw2linear(quint8 a_val)
+{
+ qint16 t;
+ qint16 seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((qint16)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+quint8 linear2ulaw(qint16 pcm_val)
+{
+ qint16 mask;
+ qint16 seg;
+ quint8 uval;
+
+ /* Get the sign and the magnitude of the value. */
+ pcm_val = pcm_val >> 2;
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if (pcm_val > CLIP) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (quint8) (0x7F ^ mask);
+ else {
+ uval = (quint8) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+qint16 ulaw2linear(quint8 u_val)
+{
+ qint16 t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+QXmppG711aCodec::QXmppG711aCodec(int clockrate)
+{
+ m_frequency = clockrate;
+}
+
+qint64 QXmppG711aCodec::encode(QDataStream &input, QDataStream &output)
+{
+ qint64 samples = 0;
+ qint16 pcm;
+ while (!input.atEnd())
+ {
+ input >> pcm;
+ output << linear2alaw(pcm);
+ ++samples;
+ }
+ return samples;
+}
+
+qint64 QXmppG711aCodec::decode(QDataStream &input, QDataStream &output)
+{
+ qint64 samples = 0;
+ quint8 g711;
+ while (!input.atEnd())
+ {
+ input >> g711;
+ output << alaw2linear(g711);
+ ++samples;
+ }
+ return samples;
+}
+
+QXmppG711uCodec::QXmppG711uCodec(int clockrate)
+{
+ m_frequency = clockrate;
+}
+
+qint64 QXmppG711uCodec::encode(QDataStream &input, QDataStream &output)
+{
+ qint64 samples = 0;
+ qint16 pcm;
+ while (!input.atEnd())
+ {
+ input >> pcm;
+ output << linear2ulaw(pcm);
+ ++samples;
+ }
+ return samples;
+}
+
+qint64 QXmppG711uCodec::decode(QDataStream &input, QDataStream &output)
+{
+ qint64 samples = 0;
+ quint8 g711;
+ while (!input.atEnd())
+ {
+ input >> g711;
+ output << ulaw2linear(g711);
+ ++samples;
+ }
+ return samples;
+}
+
+#ifdef QXMPP_USE_SPEEX
+QXmppSpeexCodec::QXmppSpeexCodec(int clockrate)
+{
+ const SpeexMode *mode = &speex_nb_mode;
+ if (clockrate == 32000)
+ mode = &speex_uwb_mode;
+ else if (clockrate == 16000)
+ mode = &speex_wb_mode;
+ else if (clockrate == 8000)
+ mode = &speex_nb_mode;
+ else
+ qWarning() << "QXmppSpeexCodec got invalid clockrate" << clockrate;
+
+ // encoder
+ encoder_bits = new SpeexBits;
+ speex_bits_init(encoder_bits);
+ encoder_state = speex_encoder_init(mode);
+
+ // decoder
+ decoder_bits = new SpeexBits;
+ speex_bits_init(decoder_bits);
+ decoder_state = speex_decoder_init(mode);
+
+ // get frame size in samples
+ speex_encoder_ctl(encoder_state, SPEEX_GET_FRAME_SIZE, &frame_samples);
+}
+
+QXmppSpeexCodec::~QXmppSpeexCodec()
+{
+ delete encoder_bits;
+ delete decoder_bits;
+}
+
+qint64 QXmppSpeexCodec::encode(QDataStream &input, QDataStream &output)
+{
+ QByteArray pcm_buffer(frame_samples * 2, 0);
+ const int length = input.readRawData(pcm_buffer.data(), pcm_buffer.size());
+ if (length != pcm_buffer.size())
+ {
+ qWarning() << "Read only read" << length << "bytes";
+ return 0;
+ }
+ speex_bits_reset(encoder_bits);
+ speex_encode_int(encoder_state, (short*)pcm_buffer.data(), encoder_bits);
+ QByteArray speex_buffer(speex_bits_nbytes(encoder_bits), 0);
+ speex_bits_write(encoder_bits, speex_buffer.data(), speex_buffer.size());
+ output.writeRawData(speex_buffer.data(), speex_buffer.size());
+ return frame_samples;
+}
+
+qint64 QXmppSpeexCodec::decode(QDataStream &input, QDataStream &output)
+{
+ const int length = input.device()->bytesAvailable();
+ QByteArray speex_buffer(length, 0);
+ input.readRawData(speex_buffer.data(), speex_buffer.size());
+ speex_bits_read_from(decoder_bits, speex_buffer.data(), speex_buffer.size());
+ QByteArray pcm_buffer(frame_samples * 2, 0);
+ speex_decode_int(decoder_state, decoder_bits, (short*)pcm_buffer.data());
+ output.writeRawData(pcm_buffer.data(), pcm_buffer.size());
+ return frame_samples;
+}
+
+#endif
+
+#ifdef QXMPP_USE_THEORA
+
+class QXmppTheoraDecoderPrivate
+{
+public:
+ bool decodeFrame(const QByteArray &buffer, QXmppVideoFrame *frame);
+
+ th_comment comment;
+ th_info info;
+ th_setup_info *setup_info;
+ th_dec_ctx *ctx;
+
+ QByteArray packetBuffer;
+};
+
+bool QXmppTheoraDecoderPrivate::decodeFrame(const QByteArray &buffer, QXmppVideoFrame *frame)
+{
+ if (!ctx)
+ return false;
+
+ ogg_packet packet;
+ packet.packet = (unsigned char*) buffer.data();
+ packet.bytes = buffer.size();
+ packet.b_o_s = 1;
+ packet.e_o_s = 0;
+ packet.granulepos = -1;
+ packet.packetno = 0;
+ if (th_decode_packetin(ctx, &packet, 0) != 0) {
+ qWarning("Theora packet could not be decoded");
+ return false;
+ }
+
+ th_ycbcr_buffer ycbcr_buffer;
+ if (th_decode_ycbcr_out(ctx, ycbcr_buffer) != 0) {
+ qWarning("Theora packet has no Y'CbCr");
+ return false;
+ }
+
+ if (info.pixel_fmt == TH_PF_420) {
+ if (!frame->isValid()) {
+ const int bytes = ycbcr_buffer[0].stride * ycbcr_buffer[0].height
+ + ycbcr_buffer[1].stride * ycbcr_buffer[1].height
+ + ycbcr_buffer[2].stride * ycbcr_buffer[2].height;
+
+ *frame = QXmppVideoFrame(bytes,
+ QSize(ycbcr_buffer[0].width, ycbcr_buffer[0].height),
+ ycbcr_buffer[0].stride,
+ QXmppVideoFrame::Format_YUV420P);
+ }
+ uchar *output = frame->bits();
+ for (int i = 0; i < 3; ++i) {
+ const int length = ycbcr_buffer[i].stride * ycbcr_buffer[i].height;
+ memcpy(output, ycbcr_buffer[i].data, length);
+ output += length;
+ }
+ return true;
+ } else if (info.pixel_fmt == TH_PF_422) {
+ if (!frame->isValid()) {
+ const int bytes = ycbcr_buffer[0].width * ycbcr_buffer[0].height * 2;
+
+ *frame = QXmppVideoFrame(bytes,
+ QSize(ycbcr_buffer[0].width, ycbcr_buffer[0].height),
+ ycbcr_buffer[0].width * 2,
+ QXmppVideoFrame::Format_YUYV);
+ }
+
+ // YUV 4:2:2 packing
+ const int width = ycbcr_buffer[0].width;
+ const int height = ycbcr_buffer[0].height;
+ const int y_stride = ycbcr_buffer[0].stride;
+ const int c_stride = ycbcr_buffer[1].stride;
+ const uchar *y_row = ycbcr_buffer[0].data;
+ const uchar *cb_row = ycbcr_buffer[1].data;
+ const uchar *cr_row = ycbcr_buffer[2].data;
+ uchar *output = frame->bits();
+ for (int y = 0; y < height; ++y) {
+ const uchar *y_ptr = y_row;
+ const uchar *cb_ptr = cb_row;
+ const uchar *cr_ptr = cr_row;
+ for (int x = 0; x < width; x += 2) {
+ *(output++) = *(y_ptr++);
+ *(output++) = *(cb_ptr++);
+ *(output++) = *(y_ptr++);
+ *(output++) = *(cr_ptr++);
+ }
+ y_row += y_stride;
+ cb_row += c_stride;
+ cr_row += c_stride;
+ }
+ return true;
+ } else {
+ qWarning("Theora decoder received an unsupported frame format");
+ return false;
+ }
+}
+
+QXmppTheoraDecoder::QXmppTheoraDecoder()
+{
+ d = new QXmppTheoraDecoderPrivate;
+ th_comment_init(&d->comment);
+ th_info_init(&d->info);
+ d->setup_info = 0;
+ d->ctx = 0;
+}
+
+QXmppTheoraDecoder::~QXmppTheoraDecoder()
+{
+ th_comment_clear(&d->comment);
+ th_info_clear(&d->info);
+ if (d->setup_info)
+ th_setup_free(d->setup_info);
+ if (d->ctx)
+ th_decode_free(d->ctx);
+ delete d;
+}
+
+QXmppVideoFormat QXmppTheoraDecoder::format() const
+{
+ QXmppVideoFormat format;
+ format.setFrameSize(QSize(d->info.frame_width, d->info.frame_height));
+ if (d->info.pixel_fmt == TH_PF_420)
+ format.setPixelFormat(QXmppVideoFrame::Format_YUV420P);
+ else if (d->info.pixel_fmt == TH_PF_422)
+ format.setPixelFormat(QXmppVideoFrame::Format_YUYV);
+ else
+ format.setPixelFormat(QXmppVideoFrame::Format_Invalid);
+ if (d->info.fps_denominator > 0)
+ format.setFrameRate(qreal(d->info.fps_numerator) / qreal(d->info.fps_denominator));
+ return format;
+}
+
+QList<QXmppVideoFrame> QXmppTheoraDecoder::handlePacket(const QXmppRtpPacket &packet)
+{
+ QList<QXmppVideoFrame> frames;
+
+ // theora deframing: draft-ietf-avt-rtp-theora-00
+ QDataStream stream(packet.payload);
+ quint32 theora_header;
+ stream >> theora_header;
+
+ quint32 theora_ident = (theora_header >> 8) & 0xffffff;
+ Q_UNUSED(theora_ident);
+ quint8 theora_frag = (theora_header & 0xc0) >> 6;
+ quint8 theora_type = (theora_header & 0x30) >> 4;
+ quint8 theora_packets = (theora_header & 0x0f);
+
+ //qDebug("ident: 0x%08x, F: %d, TDT: %d, packets: %d", theora_ident, theora_frag, theora_type, theora_packets);
+
+ // We only handle raw theora data
+ if (theora_type != 0)
+ return frames;
+
+ QXmppVideoFrame frame;
+ quint16 packetLength;
+
+ if (theora_frag == NoFragment) {
+ // unfragmented packet(s)
+ for (int i = 0; i < theora_packets; ++i) {
+ stream >> packetLength;
+ if (packetLength > stream.device()->bytesAvailable()) {
+ qWarning("Theora unfragmented packet has an invalid length");
+ return frames;
+ }
+
+ d->packetBuffer.resize(packetLength);
+ stream.readRawData(d->packetBuffer.data(), packetLength);
+ if (d->decodeFrame(d->packetBuffer, &frame))
+ frames << frame;
+ d->packetBuffer.resize(0);
+ }
+ } else {
+ // fragments
+ stream >> packetLength;
+ if (packetLength > stream.device()->bytesAvailable()) {
+ qWarning("Theora packet has an invalid length");
+ return frames;
+ }
+
+ int pos;
+ if (theora_frag == StartFragment) {
+ // start fragment
+ pos = 0;
+ d->packetBuffer.resize(packetLength);
+ } else {
+ // continuation or end fragment
+ pos = d->packetBuffer.size();
+ d->packetBuffer.resize(pos + packetLength);
+ }
+ stream.readRawData(d->packetBuffer.data() + pos, packetLength);
+
+ if (theora_frag == EndFragment) {
+ // end fragment
+ if (d->decodeFrame(d->packetBuffer, &frame))
+ frames << frame;
+ d->packetBuffer.resize(0);
+ }
+ }
+ return frames;
+}
+
+bool QXmppTheoraDecoder::setParameters(const QMap<QString, QString> &parameters)
+{
+ QByteArray config = QByteArray::fromBase64(parameters.value("configuration").toAscii());
+ QDataStream stream(config);
+ const QIODevice *device = stream.device();
+
+ if (device->bytesAvailable() < 4) {
+ qWarning("Theora configuration is too small");
+ return false;
+ }
+
+ // Process packed headers
+ int done = 0;
+ quint32 header_count;
+ stream >> header_count;
+ for (quint32 i = 0; i < header_count; ++i) {
+ if (device->bytesAvailable() < 6) {
+ qWarning("Theora configuration is too small");
+ return false;
+ }
+ QByteArray ident(3, 0);
+ quint16 length;
+ quint8 h_count;
+
+ stream.readRawData(ident.data(), ident.size());
+ stream >> length;
+ stream >> h_count;
+#ifdef QXMPP_DEBUG_THEORA
+ qDebug("Theora packed header %u ident=%s bytes=%u count=%u", i, ident.toHex().data(), length, h_count);
+#endif
+
+ // get header sizes
+ QList<qint64> h_sizes;
+ for (int h = 0; h < h_count; ++h) {
+ quint16 h_size = 0;
+ quint8 b;
+ do {
+ if (device->bytesAvailable() < 1) {
+ qWarning("Theora configuration is too small");
+ return false;
+ }
+ stream >> b;
+ h_size = (h_size << 7) | (b & 0x7f);
+ } while (b & 0x80);
+ h_sizes << h_size;
+#ifdef QXMPP_DEBUG_THEORA
+ qDebug("Theora header %d size %u", h_sizes.size() - 1, h_sizes.last());
+#endif
+ length -= h_size;
+ }
+ h_sizes << length;
+#ifdef QXMPP_DEBUG_THEORA
+ qDebug("Theora header %d size %u", h_sizes.size() - 1, h_sizes.last());
+#endif
+
+ // decode headers
+ ogg_packet packet;
+ packet.b_o_s = 1;
+ packet.e_o_s = 0;
+ packet.granulepos = -1;
+ packet.packetno = 0;
+
+ foreach (int h_size, h_sizes) {
+ if (device->bytesAvailable() < h_size) {
+ qWarning("Theora configuration is too small");
+ return false;
+ }
+
+ packet.packet = (unsigned char*) (config.data() + device->pos());
+ packet.bytes = h_size;
+ int ret = th_decode_headerin(&d->info, &d->comment, &d->setup_info, &packet);
+ if (ret < 0) {
+ qWarning("Theora header could not be decoded");
+ return false;
+ }
+ done += ret;
+ stream.skipRawData(h_size);
+ }
+ }
+
+ // check for completion
+ if (done < 3) {
+ qWarning("Theora configuration did not contain enough headers");
+ return false;
+ }
+
+#ifdef QXMPP_DEBUG_THEORA
+ qDebug("Theora frame_width %i, frame_height %i, colorspace %i, pixel_fmt: %i, target_bitrate: %i, quality: %i, keyframe_granule_shift: %i",
+ d->info.frame_width,
+ d->info.frame_height,
+ d->info.colorspace,
+ d->info.pixel_fmt,
+ d->info.target_bitrate,
+ d->info.quality,
+ d->info.keyframe_granule_shift);
+#endif
+ if (d->info.pixel_fmt != TH_PF_420 && d->info.pixel_fmt != TH_PF_422) {
+ qWarning("Theora frames have an unsupported pixel format %d", d->info.pixel_fmt);
+ return false;
+ }
+ if (d->ctx)
+ th_decode_free(d->ctx);
+ d->ctx = th_decode_alloc(&d->info, d->setup_info);
+ if (!d->ctx) {
+ qWarning("Theora decoder could not be allocated");
+ return false;
+ }
+ return true;
+}
+
+class QXmppTheoraEncoderPrivate
+{
+public:
+ void writeFragment(QDataStream &stream, FragmentType frag_type, quint8 theora_packets, const char *data, quint16 length);
+
+ th_comment comment;
+ th_info info;
+ th_setup_info *setup_info;
+ th_enc_ctx *ctx;
+ th_ycbcr_buffer ycbcr_buffer;
+
+ QByteArray buffer;
+ QByteArray configuration;
+ QByteArray ident;
+};
+
+void QXmppTheoraEncoderPrivate::writeFragment(QDataStream &stream, FragmentType frag_type, quint8 theora_packets, const char *data, quint16 length)
+{
+ // theora framing: draft-ietf-avt-rtp-theora-00
+ const quint8 theora_type = 0; // raw data
+ stream.writeRawData(ident.constData(), ident.size());
+ stream << quint8(((frag_type << 6) & 0xc0) |
+ ((theora_type << 4) & 0x30) |
+ (theora_packets & 0x0f));
+ stream << quint16(length);
+ stream.writeRawData(data, length);
+}
+
+QXmppTheoraEncoder::QXmppTheoraEncoder()
+{
+ d = new QXmppTheoraEncoderPrivate;
+ d->ident = QByteArray("\xc3\x45\xae");
+ th_comment_init(&d->comment);
+ th_info_init(&d->info);
+ d->setup_info = 0;
+ d->ctx = 0;
+}
+
+QXmppTheoraEncoder::~QXmppTheoraEncoder()
+{
+ th_comment_clear(&d->comment);
+ th_info_clear(&d->info);
+ if (d->setup_info)
+ th_setup_free(d->setup_info);
+ if (d->ctx)
+ th_encode_free(d->ctx);
+ delete d;
+}
+
+bool QXmppTheoraEncoder::setFormat(const QXmppVideoFormat &format)
+{
+ const QXmppVideoFrame::PixelFormat pixelFormat = format.pixelFormat();
+ if ((pixelFormat != QXmppVideoFrame::Format_YUV420P) &&
+ (pixelFormat != QXmppVideoFrame::Format_YUYV)) {
+ qWarning("Theora encoder does not support the given format");
+ return false;
+ }
+
+ d->info.frame_width = format.frameSize().width();
+ d->info.frame_height = format.frameSize().height();
+ d->info.pic_height = format.frameSize().height();
+ d->info.pic_width = format.frameSize().width();
+ d->info.pic_x = 0;
+ d->info.pic_y = 0;
+ d->info.colorspace = TH_CS_UNSPECIFIED;
+ d->info.target_bitrate = 0;
+ d->info.quality = 48;
+ d->info.keyframe_granule_shift = 6;
+
+ // FIXME: how do we handle floating point frame rates?
+ d->info.fps_numerator = format.frameRate();
+ d->info.fps_denominator = 1;
+
+ if (pixelFormat == QXmppVideoFrame::Format_YUV420P) {
+ d->info.pixel_fmt = TH_PF_420;
+ d->ycbcr_buffer[0].width = d->info.frame_width;
+ d->ycbcr_buffer[0].height = d->info.frame_height;
+ d->ycbcr_buffer[1].width = d->ycbcr_buffer[0].width / 2;
+ d->ycbcr_buffer[1].height = d->ycbcr_buffer[0].height / 2;
+ d->ycbcr_buffer[2].width = d->ycbcr_buffer[1].width;
+ d->ycbcr_buffer[2].height = d->ycbcr_buffer[1].height;
+ } else if (pixelFormat == QXmppVideoFrame::Format_YUYV) {
+ d->info.pixel_fmt = TH_PF_422;
+ d->buffer.resize(d->info.frame_width * d->info.frame_height * 2);
+ d->ycbcr_buffer[0].width = d->info.frame_width;
+ d->ycbcr_buffer[0].height = d->info.frame_height;
+ d->ycbcr_buffer[0].stride = d->info.frame_width;
+ d->ycbcr_buffer[0].data = (uchar*) d->buffer.data();
+ d->ycbcr_buffer[1].width = d->ycbcr_buffer[0].width / 2;
+ d->ycbcr_buffer[1].height = d->ycbcr_buffer[0].height;
+ d->ycbcr_buffer[1].stride = d->ycbcr_buffer[0].stride / 2;
+ d->ycbcr_buffer[1].data = d->ycbcr_buffer[0].data + d->ycbcr_buffer[0].stride * d->ycbcr_buffer[0].height;
+ d->ycbcr_buffer[2].width = d->ycbcr_buffer[1].width;
+ d->ycbcr_buffer[2].height = d->ycbcr_buffer[1].height;
+ d->ycbcr_buffer[2].stride = d->ycbcr_buffer[1].stride;
+ d->ycbcr_buffer[2].data = d->ycbcr_buffer[1].data + d->ycbcr_buffer[1].stride * d->ycbcr_buffer[1].height;
+ }
+
+ // create encoder
+ if (d->ctx) {
+ th_encode_free(d->ctx);
+ d->ctx = 0;
+ }
+ d->ctx = th_encode_alloc(&d->info);
+ if (!d->ctx) {
+ qWarning("Theora encoder could not be allocated");
+ return false;
+ }
+
+ // fetch headers
+ QList<QByteArray> headers;
+ ogg_packet packet;
+ while (th_encode_flushheader(d->ctx, &d->comment, &packet) > 0)
+ headers << QByteArray((const char*)packet.packet, packet.bytes);
+
+ // store configuration
+ d->configuration.clear();
+ QDataStream stream(&d->configuration, QIODevice::WriteOnly);
+ stream << quint32(1);
+
+ quint16 length = 0;
+ foreach (const QByteArray &header, headers)
+ length += header.size();
+
+ quint8 h_count = headers.size() - 1;
+ stream.writeRawData(d->ident.constData(), d->ident.size());
+ stream << length;
+ stream << h_count;
+#ifdef QXMPP_DEBUG_THEORA
+ qDebug("Theora packed header %u ident=%s bytes=%u count=%u", 0, d->ident.toHex().data(), length, h_count);
+#endif
+
+ // write header sizes
+ for (int h = 0; h < h_count; ++h) {
+ quint16 h_size = headers[h].size();
+ do {
+ quint8 b = (h_size & 0x7f);
+ h_size >>= 7;
+ if (h_size)
+ b |= 0x80;
+ stream << b;
+ } while (h_size);
+ }
+
+ // write headers
+ for (int h = 0; h < headers.size(); ++h) {
+#ifdef QXMPP_DEBUG_THEORA
+ qDebug("Header %d size %d", h, headers[h].size());
+#endif
+ stream.writeRawData(headers[h].data(), headers[h].size());
+ }
+
+ return true;
+}
+
+QList<QByteArray> QXmppTheoraEncoder::handleFrame(const QXmppVideoFrame &frame)
+{
+ QList<QByteArray> packets;
+ const int PACKET_MAX = 1388;
+
+ if (!d->ctx)
+ return packets;
+
+ if (d->info.pixel_fmt == TH_PF_420) {
+ d->ycbcr_buffer[0].stride = frame.bytesPerLine();
+ d->ycbcr_buffer[0].data = (unsigned char*) frame.bits();
+ d->ycbcr_buffer[1].stride = d->ycbcr_buffer[0].stride / 2;
+ d->ycbcr_buffer[1].data = d->ycbcr_buffer[0].data + d->ycbcr_buffer[0].stride * d->ycbcr_buffer[0].height;
+ d->ycbcr_buffer[2].stride = d->ycbcr_buffer[1].stride;
+ d->ycbcr_buffer[2].data = d->ycbcr_buffer[1].data + d->ycbcr_buffer[1].stride * d->ycbcr_buffer[1].height;
+ } else if (d->info.pixel_fmt == TH_PF_422) {
+ // YUV 4:2:2 unpacking
+ const int width = frame.width();
+ const int height = frame.height();
+ const int stride = frame.bytesPerLine();
+ const uchar *row = frame.bits();
+ uchar *y_out = d->ycbcr_buffer[0].data;
+ uchar *cb_out = d->ycbcr_buffer[1].data;
+ uchar *cr_out = d->ycbcr_buffer[2].data;
+ for (int y = 0; y < height; ++y) {
+ const uchar *ptr = row;
+ for (int x = 0; x < width; x += 2) {
+ *(y_out++) = *(ptr++);
+ *(cb_out++) = *(ptr++);
+ *(y_out++) = *(ptr++);
+ *(cr_out++) = *(ptr++);
+ }
+ row += stride;
+ }
+ } else {
+ qWarning("Theora encoder received an unsupported frame format");
+ return packets;
+ }
+
+ if (th_encode_ycbcr_in(d->ctx, d->ycbcr_buffer) != 0) {
+ qWarning("Theora encoder could not handle frame");
+ return packets;
+ }
+
+ QByteArray payload;
+ ogg_packet packet;
+ while (th_encode_packetout(d->ctx, 0, &packet) > 0) {
+#ifdef QXMPP_DEBUG_THEORA
+ qDebug("Theora encoded packet %d bytes", packet.bytes);
+#endif
+ QDataStream stream(&payload, QIODevice::WriteOnly);
+ const char *data = (const char*) packet.packet;
+ int size = packet.bytes;
+ if (size <= PACKET_MAX) {
+ // no fragmentation
+ stream.device()->reset();
+ payload.resize(0);
+ d->writeFragment(stream, NoFragment, 1, data, size);
+ packets << payload;
+ } else {
+ // fragmentation
+ FragmentType frag_type = StartFragment;
+ while (size) {
+ const int length = qMin(PACKET_MAX, size);
+ stream.device()->reset();
+ payload.resize(0);
+ d->writeFragment(stream, frag_type, 0, data, length);
+ data += length;
+ size -= length;
+ frag_type = (size > PACKET_MAX) ? MiddleFragment : EndFragment;
+ packets << payload;
+ }
+ }
+ }
+
+ return packets;
+}
+
+QMap<QString, QString> QXmppTheoraEncoder::parameters() const
+{
+ QMap<QString, QString> params;
+ if (d->ctx) {
+ params.insert("delivery-method", "inline");
+ params.insert("configuration", d->configuration.toBase64());
+ }
+ return params;
+}
+
+#endif
+
+#ifdef QXMPP_USE_VPX
+
+class QXmppVpxDecoderPrivate
+{
+public:
+ bool decodeFrame(const QByteArray &buffer, QXmppVideoFrame *frame);
+
+ vpx_codec_ctx_t codec;
+ QByteArray packetBuffer;
+};
+
+bool QXmppVpxDecoderPrivate::decodeFrame(const QByteArray &buffer, QXmppVideoFrame *frame)
+{
+ if (vpx_codec_decode(&codec, (const uint8_t*)buffer.constData(), buffer.size(), NULL, 0) != VPX_CODEC_OK) {
+ qWarning("Vpx packet could not be decoded: %s", vpx_codec_error_detail(&codec));
+ return false;
+ }
+
+ vpx_codec_iter_t iter = NULL;
+ vpx_image_t *img;
+ while ((img = vpx_codec_get_frame(&codec, &iter))) {
+ if (img->fmt == VPX_IMG_FMT_I420) {
+ if (!frame->isValid()) {
+ const int bytes = img->d_w * img->d_h * 3 / 2;
+
+ *frame = QXmppVideoFrame(bytes,
+ QSize(img->d_w, img->d_h),
+ img->d_w,
+ QXmppVideoFrame::Format_YUV420P);
+ }
+ uchar *output = frame->bits();
+
+ for (int i = 0; i < 3; ++i) {
+ uchar *input = img->planes[i];
+ const int div = (i == 0) ? 1 : 2;
+ for (unsigned int y = 0; y < img->d_h / div; ++y) {
+ memcpy(output, input, img->d_w / div);
+ input += img->stride[i];
+ output += img->d_w / div;
+ }
+ }
+ } else {
+ qWarning("Vpx decoder received an unsupported frame format: %d", img->fmt);
+ }
+ }
+
+ return true;
+}
+
+QXmppVpxDecoder::QXmppVpxDecoder()
+{
+ d = new QXmppVpxDecoderPrivate;
+ if (vpx_codec_dec_init(&d->codec, vpx_codec_vp8_dx(), NULL, 0) != VPX_CODEC_OK) {
+ qWarning("Vpx decoder could not be initialised");
+ }
+}
+
+QXmppVpxDecoder::~QXmppVpxDecoder()
+{
+ vpx_codec_destroy(&d->codec);
+ delete d;
+}
+
+QXmppVideoFormat QXmppVpxDecoder::format() const
+{
+ QXmppVideoFormat format;
+ format.setFrameRate(15.0);
+ format.setFrameSize(QSize(320, 240));
+ format.setPixelFormat(QXmppVideoFrame::Format_YUV420P);
+ return format;
+}
+
+QList<QXmppVideoFrame> QXmppVpxDecoder::handlePacket(const QXmppRtpPacket &packet)
+{
+ QList<QXmppVideoFrame> frames;
+
+ // vp8 deframing: http://tools.ietf.org/html/draft-westin-payload-vp8-00
+ QDataStream stream(packet.payload);
+ quint8 vpx_header;
+ stream >> vpx_header;
+
+ const bool have_id = (vpx_header & 0x10) != 0;
+ const quint8 frag_type = (vpx_header & 0x6) >> 1;
+ if (have_id) {
+ qWarning("Vpx decoder does not support pictureId yet");
+ return frames;
+ }
+
+ const int packetLength = packet.payload.size() - 1;
+#ifdef QXMPP_DEBUG_VPX
+ qDebug("Vpx fragment FI: %d, size %d", frag_type, packetLength);
+#endif
+
+ QXmppVideoFrame frame;
+
+ if (frag_type == NoFragment) {
+ // unfragmented packet
+ if (d->decodeFrame(packet.payload.mid(1), &frame))
+ frames << frame;
+ d->packetBuffer.resize(0);
+ } else {
+ // fragments
+ if (frag_type == StartFragment) {
+ // start fragment
+ d->packetBuffer = packet.payload.mid(1);
+ } else {
+ // continuation or end fragment
+ const int packetPos = d->packetBuffer.size();
+ d->packetBuffer.resize(packetPos + packetLength);
+ stream.readRawData(d->packetBuffer.data() + packetPos, packetLength);
+ }
+
+ if (frag_type == EndFragment) {
+ // end fragment
+ if (d->decodeFrame(d->packetBuffer, &frame))
+ frames << frame;
+ d->packetBuffer.resize(0);
+ }
+
+ }
+
+ return frames;
+}
+
+bool QXmppVpxDecoder::setParameters(const QMap<QString, QString> &parameters)
+{
+ return true;
+}
+
+class QXmppVpxEncoderPrivate
+{
+public:
+ void writeFragment(QDataStream &stream, FragmentType frag_type, const char *data, quint16 length);
+
+ vpx_codec_ctx_t codec;
+ vpx_codec_enc_cfg_t cfg;
+ vpx_image_t *imageBuffer;
+ int frameCount;
+};
+
+void QXmppVpxEncoderPrivate::writeFragment(QDataStream &stream, FragmentType frag_type, const char *data, quint16 length)
+{
+ // vp8 framing: http://tools.ietf.org/html/draft-westin-payload-vp8-00
+#ifdef QXMPP_DEBUG_VPX
+ qDebug("Vpx encoder writing packet frag: %i, size: %u", frag_type, length);
+#endif
+ stream << quint8(((frag_type << 1) & 0x6) |
+ (frag_type == NoFragment || frag_type == StartFragment));
+ stream.writeRawData(data, length);
+}
+
+QXmppVpxEncoder::QXmppVpxEncoder()
+{
+ d = new QXmppVpxEncoderPrivate;
+ d->frameCount = 0;
+ d->imageBuffer = 0;
+ vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &d->cfg, 0);
+}
+
+QXmppVpxEncoder::~QXmppVpxEncoder()
+{
+ vpx_codec_destroy(&d->codec);
+ if (d->imageBuffer)
+ vpx_img_free(d->imageBuffer);
+ delete d;
+}
+
+bool QXmppVpxEncoder::setFormat(const QXmppVideoFormat &format)
+{
+ const QXmppVideoFrame::PixelFormat pixelFormat = format.pixelFormat();
+ if (pixelFormat != QXmppVideoFrame::Format_YUYV) {
+ qWarning("Vpx encoder does not support the given format");
+ return false;
+ }
+
+ d->cfg.rc_target_bitrate = format.frameSize().width() * format.frameSize().height() * d->cfg.rc_target_bitrate / d->cfg.g_w / d->cfg.g_h;
+ d->cfg.g_w = format.frameSize().width();
+ d->cfg.g_h = format.frameSize().height();
+ if (vpx_codec_enc_init(&d->codec, vpx_codec_vp8_cx(), &d->cfg, 0) != VPX_CODEC_OK) {
+ qWarning("Vpx encoder could not be initialised");
+ return false;
+ }
+
+ d->imageBuffer = vpx_img_alloc(NULL, VPX_IMG_FMT_I420,
+ format.frameSize().width(), format.frameSize().height(), 1);
+ return true;
+}
+
+QList<QByteArray> QXmppVpxEncoder::handleFrame(const QXmppVideoFrame &frame)
+{
+ const int PACKET_MAX = 1388;
+ QList<QByteArray> packets;
+
+ // try to encode frame
+ if (frame.pixelFormat() == QXmppVideoFrame::Format_YUYV) {
+ // YUYV -> YUV420P
+ const int width = frame.width();
+ const int height = frame.height();
+ const int stride = frame.bytesPerLine();
+ const uchar *row = frame.bits();
+ uchar *y_row = d->imageBuffer->planes[VPX_PLANE_Y];
+ uchar *cb_row = d->imageBuffer->planes[VPX_PLANE_U];
+ uchar *cr_row = d->imageBuffer->planes[VPX_PLANE_V];
+ for (int y = 0; y < height; y += 2) {
+ // odd row
+ const uchar *ptr = row;
+ uchar *y_out = y_row;
+ uchar *cb_out = cb_row;
+ uchar *cr_out = cr_row;
+ for (int x = 0; x < width; x += 2) {
+ *(y_out++) = *(ptr++);
+ *(cb_out++) = *(ptr++);
+ *(y_out++) = *(ptr++);
+ *(cr_out++) = *(ptr++);
+ }
+ row += stride;
+ y_row += d->imageBuffer->stride[VPX_PLANE_Y];
+ cb_row += d->imageBuffer->stride[VPX_PLANE_U];
+ cr_row += d->imageBuffer->stride[VPX_PLANE_V];
+
+ // even row
+ ptr = row;
+ y_out = y_row;
+ for (int x = 0; x < width; x += 2) {
+ *(y_out++) = *(ptr++);
+ ptr++;
+ *(y_out++) = *(ptr++);
+ ptr++;
+ }
+ row += stride;
+ y_row += d->imageBuffer->stride[VPX_PLANE_Y];
+ }
+ } else {
+ qWarning("Vpx encoder does not support the given format");
+ return packets;
+ }
+
+ if (vpx_codec_encode(&d->codec, d->imageBuffer, d->frameCount, 1, 0, VPX_DL_REALTIME) != VPX_CODEC_OK) {
+ qWarning("Vpx encoder could not handle frame: %s", vpx_codec_error_detail(&d->codec));
+ return packets;
+ }
+
+ // extract data
+ QByteArray payload;
+ vpx_codec_iter_t iter = NULL;
+ const vpx_codec_cx_pkt_t *pkt;
+ while ((pkt = vpx_codec_get_cx_data(&d->codec, &iter))) {
+ if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
+#ifdef QXMPP_DEBUG_VPX
+ qDebug("Vpx encoded packet %lu bytes", pkt->data.frame.sz);
+#endif
+ QDataStream stream(&payload, QIODevice::WriteOnly);
+ const char *data = (const char*) pkt->data.frame.buf;
+ int size = pkt->data.frame.sz;
+ if (size <= PACKET_MAX) {
+ // no fragmentation
+ stream.device()->reset();
+ payload.resize(0);
+ d->writeFragment(stream, NoFragment, data, size);
+ packets << payload;
+ } else {
+ // fragmentation
+ FragmentType frag_type = StartFragment;
+ while (size) {
+ const int length = qMin(PACKET_MAX, size);
+ stream.device()->reset();
+ payload.resize(0);
+ d->writeFragment(stream, frag_type, data, length);
+ data += length;
+ size -= length;
+ frag_type = (size > PACKET_MAX) ? MiddleFragment : EndFragment;
+ packets << payload;
+ }
+ }
+ }
+ }
+ d->frameCount++;
+
+ return packets;
+}
+
+QMap<QString, QString> QXmppVpxEncoder::parameters() const
+{
+ return QMap<QString, QString>();
+}
+
+#endif
diff --git a/src/base/QXmppCodec.h b/src/base/QXmppCodec.h
new file mode 100644
index 00000000..61dd45a2
--- /dev/null
+++ b/src/base/QXmppCodec.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 QXMPPCODEC_H
+#define QXMPPCODEC_H
+
+#include <QtGlobal>
+
+class QXmppRtpPacket;
+class QXmppVideoFormat;
+class QXmppVideoFrame;
+
+/// \brief The QXmppCodec class is the base class for audio codecs capable of
+/// encoding and decoding audio samples.
+///
+/// Samples must be 16-bit little endian.
+
+class QXmppCodec
+{
+public:
+ /// Reads samples from the input stream, encodes them and writes the
+ /// encoded data to the output stream.
+ virtual qint64 encode(QDataStream &input, QDataStream &output) = 0;
+
+ /// Reads encoded data from the input stream, decodes it and writes the
+ /// decoded samples to the output stream.
+ virtual qint64 decode(QDataStream &input, QDataStream &output) = 0;
+};
+
+/// \internal
+///
+/// The QXmppG711aCodec class represent a G.711 a-law PCM codec.
+
+class QXmppG711aCodec : public QXmppCodec
+{
+public:
+ QXmppG711aCodec(int clockrate);
+
+ qint64 encode(QDataStream &input, QDataStream &output);
+ qint64 decode(QDataStream &input, QDataStream &output);
+
+private:
+ int m_frequency;
+};
+
+/// \internal
+///
+/// The QXmppG711uCodec class represent a G.711 u-law PCM codec.
+
+class QXmppG711uCodec : public QXmppCodec
+{
+public:
+ QXmppG711uCodec(int clockrate);
+
+ qint64 encode(QDataStream &input, QDataStream &output);
+ qint64 decode(QDataStream &input, QDataStream &output);
+
+private:
+ int m_frequency;
+};
+
+#ifdef QXMPP_USE_SPEEX
+typedef struct SpeexBits SpeexBits;
+
+/// \internal
+///
+/// The QXmppSpeexCodec class represent a SPEEX codec.
+
+class QXmppSpeexCodec : public QXmppCodec
+{
+public:
+ QXmppSpeexCodec(int clockrate);
+ ~QXmppSpeexCodec();
+
+ qint64 encode(QDataStream &input, QDataStream &output);
+ qint64 decode(QDataStream &input, QDataStream &output);
+
+private:
+ SpeexBits *encoder_bits;
+ void *encoder_state;
+ SpeexBits *decoder_bits;
+ void *decoder_state;
+ int frame_samples;
+};
+#endif
+
+/// \brief The QXmppVideoDecoder class is the base class for video decoders.
+///
+
+class QXmppVideoDecoder
+{
+public:
+ virtual QXmppVideoFormat format() const = 0;
+ virtual QList<QXmppVideoFrame> handlePacket(const QXmppRtpPacket &packet) = 0;
+ virtual bool setParameters(const QMap<QString, QString> &parameters) = 0;
+};
+
+class QXmppVideoEncoder
+{
+public:
+ virtual bool setFormat(const QXmppVideoFormat &format) = 0;
+ virtual QList<QByteArray> handleFrame(const QXmppVideoFrame &frame) = 0;
+ virtual QMap<QString, QString> parameters() const = 0;
+};
+
+#ifdef QXMPP_USE_THEORA
+class QXmppTheoraDecoderPrivate;
+class QXmppTheoraEncoderPrivate;
+
+class QXmppTheoraDecoder : public QXmppVideoDecoder
+{
+public:
+ QXmppTheoraDecoder();
+ ~QXmppTheoraDecoder();
+
+ QXmppVideoFormat format() const;
+ QList<QXmppVideoFrame> handlePacket(const QXmppRtpPacket &packet);
+ bool setParameters(const QMap<QString, QString> &parameters);
+
+private:
+ QXmppTheoraDecoderPrivate *d;
+};
+
+class QXmppTheoraEncoder : public QXmppVideoEncoder
+{
+public:
+ QXmppTheoraEncoder();
+ ~QXmppTheoraEncoder();
+
+ bool setFormat(const QXmppVideoFormat &format);
+ QList<QByteArray> handleFrame(const QXmppVideoFrame &frame);
+ QMap<QString, QString> parameters() const;
+
+private:
+ QXmppTheoraEncoderPrivate *d;
+};
+#endif
+
+#ifdef QXMPP_USE_VPX
+class QXmppVpxDecoderPrivate;
+class QXmppVpxEncoderPrivate;
+
+class QXmppVpxDecoder : public QXmppVideoDecoder
+{
+public:
+ QXmppVpxDecoder();
+ ~QXmppVpxDecoder();
+
+ QXmppVideoFormat format() const;
+ QList<QXmppVideoFrame> handlePacket(const QXmppRtpPacket &packet);
+ bool setParameters(const QMap<QString, QString> &parameters);
+
+private:
+ QXmppVpxDecoderPrivate *d;
+};
+
+class QXmppVpxEncoder : public QXmppVideoEncoder
+{
+public:
+ QXmppVpxEncoder();
+ ~QXmppVpxEncoder();
+
+ bool setFormat(const QXmppVideoFormat &format);
+ QList<QByteArray> handleFrame(const QXmppVideoFrame &frame);
+ QMap<QString, QString> parameters() const;
+
+private:
+ QXmppVpxEncoderPrivate *d;
+};
+#endif
+
+#endif
diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp
new file mode 100644
index 00000000..c77859b6
--- /dev/null
+++ b/src/base/QXmppConstants.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#include "QXmppConstants.h"
+
+const char* ns_stream = "http://etherx.jabber.org/streams";
+const char* ns_client = "jabber:client";
+const char* ns_server = "jabber:server";
+const char* ns_server_dialback = "jabber:server:dialback";
+const char* ns_roster = "jabber:iq:roster";
+const char* ns_tls = "urn:ietf:params:xml:ns:xmpp-tls";
+const char* ns_sasl = "urn:ietf:params:xml:ns:xmpp-sasl";
+const char* ns_bind = "urn:ietf:params:xml:ns:xmpp-bind";
+const char* ns_session = "urn:ietf:params:xml:ns:xmpp-session";
+const char* ns_stanza = "urn:ietf:params:xml:ns:xmpp-stanzas";
+const char* ns_vcard = "vcard-temp";
+const char* ns_vcard_update = "vcard-temp:x:update";
+const char* ns_auth = "jabber:iq:auth";
+const char* ns_authFeature = "http://jabber.org/features/iq-auth";
+const char* ns_capabilities = "http://jabber.org/protocol/caps";
+const char* ns_compress = "http://jabber.org/protocol/compress";
+const char* ns_compressFeature = "http://jabber.org/features/compress";
+const char* ns_disco_info = "http://jabber.org/protocol/disco#info";
+const char* ns_disco_items = "http://jabber.org/protocol/disco#items";
+const char* ns_ibb = "http://jabber.org/protocol/ibb";
+const char* ns_rpc = "jabber:iq:rpc";
+const char *ns_ping = "urn:xmpp:ping";
+const char *ns_conference = "jabber:x:conference";
+const char *ns_message_receipts = "urn:xmpp:receipts";
+const char *ns_delayed_delivery = "urn:xmpp:delay";
+const char *ns_legacy_delayed_delivery = "jabber:x:delay";
+const char *ns_muc = "http://jabber.org/protocol/muc";
+const char *ns_muc_admin = "http://jabber.org/protocol/muc#admin";
+const char *ns_muc_owner = "http://jabber.org/protocol/muc#owner";
+const char *ns_muc_user = "http://jabber.org/protocol/muc#user";
+const char *ns_chat_states = "http://jabber.org/protocol/chatstates";
+const char *ns_stream_initiation = "http://jabber.org/protocol/si";
+const char *ns_stream_initiation_file_transfer = "http://jabber.org/protocol/si/profile/file-transfer";
+const char *ns_feature_negotiation = "http://jabber.org/protocol/feature-neg";
+const char *ns_bytestreams = "http://jabber.org/protocol/bytestreams";
+// XEP-0092: Software Version
+const char *ns_version = "jabber:iq:version";
+const char *ns_data = "jabber:x:data";
+// XEP-0166: Jingle
+const char *ns_jingle = "urn:xmpp:jingle:1";
+const char *ns_jingle_raw_udp = "urn:xmpp:jingle:transports:raw-udp:1";
+const char* ns_jingle_ice_udp = "urn:xmpp:jingle:transports:ice-udp:1";
+const char *ns_jingle_rtp = "urn:xmpp:jingle:apps:rtp:1";
+const char *ns_jingle_rtp_audio = "urn:xmpp:jingle:apps:rtp:audio";
+const char *ns_jingle_rtp_video = "urn:xmpp:jingle:apps:rtp:video";
+// XEP-0202: Entity Time
+const char *ns_entity_time = "urn:xmpp:time";
+// XEP-0224: Attention
+const char *ns_attention = "urn:xmpp:attention:0";
diff --git a/src/base/QXmppConstants.h b/src/base/QXmppConstants.h
new file mode 100644
index 00000000..909bddd9
--- /dev/null
+++ b/src/base/QXmppConstants.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPCONSTANTS_H
+#define QXMPPCONSTANTS_H
+
+extern const char* ns_stream;
+extern const char* ns_client;
+extern const char* ns_server;
+extern const char* ns_server_dialback;
+extern const char* ns_roster;
+extern const char* ns_tls;
+extern const char* ns_sasl;
+extern const char* ns_bind;
+extern const char* ns_session;
+extern const char* ns_stanza;
+extern const char* ns_vcard;
+extern const char* ns_vcard_update;
+extern const char* ns_auth;
+extern const char* ns_authFeature;
+extern const char* ns_capabilities;
+extern const char* ns_compress;
+extern const char* ns_compressFeature;
+extern const char* ns_disco_info;
+extern const char* ns_disco_items;
+extern const char* ns_ibb;
+extern const char* ns_rpc;
+extern const char* ns_ping;
+extern const char *ns_conference;
+extern const char *ns_message_receipts;
+extern const char *ns_delayed_delivery;
+extern const char *ns_legacy_delayed_delivery;
+extern const char *ns_muc;
+extern const char *ns_muc_admin;
+extern const char *ns_muc_owner;
+extern const char *ns_muc_user;
+extern const char *ns_chat_states;
+extern const char *ns_stream_initiation;
+extern const char *ns_stream_initiation_file_transfer;
+extern const char *ns_feature_negotiation;
+extern const char *ns_bytestreams;
+extern const char *ns_version;
+extern const char *ns_data;
+extern const char *ns_jingle;
+extern const char* ns_jingle_ice_udp;
+extern const char* ns_jingle_raw_udp;
+extern const char *ns_jingle_rtp;
+extern const char *ns_jingle_rtp_audio;
+extern const char *ns_jingle_rtp_video;
+extern const char *ns_entity_time;
+extern const char *ns_attention;
+
+#endif // QXMPPCONSTANTS_H
diff --git a/src/base/QXmppDataForm.cpp b/src/base/QXmppDataForm.cpp
new file mode 100644
index 00000000..aab3857d
--- /dev/null
+++ b/src/base/QXmppDataForm.cpp
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 <QDebug>
+#include <QDomElement>
+#include <QStringList>
+
+#include "QXmppConstants.h"
+#include "QXmppDataForm.h"
+#include "QXmppUtils.h"
+
+struct field_type {
+ QXmppDataForm::Field::Type type;
+ const char *str;
+};
+
+static field_type field_types[] = {
+ {QXmppDataForm::Field::BooleanField, "boolean"},
+ {QXmppDataForm::Field::FixedField, "fixed"},
+ {QXmppDataForm::Field::HiddenField, "hidden"},
+ {QXmppDataForm::Field::JidMultiField, "jid-multi"},
+ {QXmppDataForm::Field::JidSingleField, "jid-single"},
+ {QXmppDataForm::Field::ListMultiField, "list-multi"},
+ {QXmppDataForm::Field::ListSingleField, "list-single"},
+ {QXmppDataForm::Field::TextMultiField, "text-multi"},
+ {QXmppDataForm::Field::TextPrivateField, "text-private"},
+ {QXmppDataForm::Field::TextSingleField, "text-single"},
+ {static_cast<QXmppDataForm::Field::Type>(-1), NULL},
+};
+
+/// Constructs a QXmppDataForm::Field of the specified \a type.
+///
+/// \param type
+
+QXmppDataForm::Field::Field(QXmppDataForm::Field::Type type)
+ : m_required(false),
+ m_type(type)
+{
+}
+
+/// Returns the field's description.
+
+QString QXmppDataForm::Field::description() const
+{
+ return m_description;
+}
+
+/// Sets the field's description.
+///
+/// \param description
+
+void QXmppDataForm::Field::setDescription(const QString &description)
+{
+ m_description = description;
+}
+
+/// Returns the field's key.
+
+QString QXmppDataForm::Field::key() const
+{
+ return m_key;
+}
+
+/// Sets the field's key.
+///
+/// \param key
+
+void QXmppDataForm::Field::setKey(const QString &key)
+{
+ m_key = key;
+}
+
+/// Returns the field's label.
+
+QString QXmppDataForm::Field::label() const
+{
+ return m_label;
+}
+
+/// Sets the field's label.
+///
+/// \param label
+
+void QXmppDataForm::Field::setLabel(const QString &label)
+{
+ m_label = label;
+}
+
+/// Returns the field's options.
+
+QList<QPair<QString, QString> > QXmppDataForm::Field::options() const
+{
+ return m_options;
+}
+
+/// Sets the field's options.
+///
+/// \param options
+
+void QXmppDataForm::Field::setOptions(const QList<QPair<QString, QString> > &options)
+{
+ m_options = options;
+}
+
+/// Returns true if the field is required, false otherwise.
+
+bool QXmppDataForm::Field::isRequired() const
+{
+ return m_required;
+}
+
+/// Set to true if the field is required, false otherwise.
+///
+/// \param required
+
+void QXmppDataForm::Field::setRequired(bool required)
+{
+ m_required = required;
+}
+
+/// Returns the field's type.
+
+QXmppDataForm::Field::Type QXmppDataForm::Field::type() const
+{
+ return m_type;
+}
+
+/// Sets the field's type.
+///
+/// \param type
+
+void QXmppDataForm::Field::setType(QXmppDataForm::Field::Type type)
+{
+ m_type = type;
+}
+
+/// Returns the field's value.
+
+QVariant QXmppDataForm::Field::value() const
+{
+ return m_value;
+}
+
+/// Sets the field's value.
+///
+/// \param value
+
+void QXmppDataForm::Field::setValue(const QVariant &value)
+{
+ m_value = value;
+}
+
+/// Constructs a QXmppDataForm of the specified \a type.
+///
+/// \param type
+
+QXmppDataForm::QXmppDataForm(QXmppDataForm::Type type)
+ : m_type(type)
+{
+}
+
+/// Returns the form's fields.
+
+QList<QXmppDataForm::Field> QXmppDataForm::fields() const
+{
+ return m_fields;
+}
+
+/// Returns the form's fields by reference.
+
+QList<QXmppDataForm::Field> &QXmppDataForm::fields()
+{
+ return m_fields;
+}
+
+/// Sets the form's fields.
+///
+/// \param fields
+
+void QXmppDataForm::setFields(const QList<QXmppDataForm::Field> &fields)
+{
+ m_fields = fields;
+}
+
+/// Returns the form's instructions.
+
+QString QXmppDataForm::instructions() const
+{
+ return m_instructions;
+}
+
+/// Sets the form's instructions.
+///
+/// \param instructions
+
+void QXmppDataForm::setInstructions(const QString &instructions)
+{
+ m_instructions = instructions;
+}
+
+/// Returns the form's title.
+
+QString QXmppDataForm::title() const
+{
+ return m_title;
+}
+
+/// Sets the form's title.
+///
+/// \param title
+
+void QXmppDataForm::setTitle(const QString &title)
+{
+ m_title = title;
+}
+
+/// Returns the form's type.
+
+QXmppDataForm::Type QXmppDataForm::type() const
+{
+ return m_type;
+}
+
+/// Sets the form's type.
+///
+/// \param type
+
+void QXmppDataForm::setType(QXmppDataForm::Type type)
+{
+ m_type = type;
+}
+
+/// Returns true if the form has an unknown type.
+
+bool QXmppDataForm::isNull() const
+{
+ return m_type == QXmppDataForm::None;
+}
+
+void QXmppDataForm::parse(const QDomElement &element)
+{
+ if (element.isNull())
+ return;
+
+ /* form type */
+ const QString typeStr = element.attribute("type");
+ if (typeStr == "form")
+ m_type = QXmppDataForm::Form;
+ else if (typeStr == "submit")
+ m_type = QXmppDataForm::Submit;
+ else if (typeStr == "cancel")
+ m_type = QXmppDataForm::Cancel;
+ else if (typeStr == "result")
+ m_type = QXmppDataForm::Result;
+ else
+ {
+ qWarning() << "Unknown form type" << typeStr;
+ return;
+ }
+
+ /* form properties */
+ m_title = element.firstChildElement("title").text();
+ m_instructions = element.firstChildElement("instructions").text();
+
+ QDomElement fieldElement = element.firstChildElement("field");
+ while (!fieldElement.isNull())
+ {
+ QXmppDataForm::Field field;
+
+ /* field type */
+ QXmppDataForm::Field::Type type = QXmppDataForm::Field::TextSingleField;
+ const QString typeStr = fieldElement.attribute("type");
+ struct field_type *ptr;
+ for (ptr = field_types; ptr->str; ptr++)
+ {
+ if (typeStr == ptr->str)
+ {
+ type = ptr->type;
+ break;
+ }
+ }
+ field.setType(type);
+
+ /* field attributes */
+ field.setLabel(fieldElement.attribute("label"));
+ field.setKey(fieldElement.attribute("var"));
+
+ /* field value(s) */
+ if (type == QXmppDataForm::Field::BooleanField)
+ {
+ const QString valueStr = fieldElement.firstChildElement("value").text();
+ field.setValue(valueStr == "1" || valueStr == "true");
+ }
+ else if (type == QXmppDataForm::Field::ListMultiField ||
+ type == QXmppDataForm::Field::JidMultiField ||
+ type == QXmppDataForm::Field::TextMultiField)
+ {
+ QStringList values;
+ QDomElement valueElement = fieldElement.firstChildElement("value");
+ while (!valueElement.isNull())
+ {
+ values.append(valueElement.text());
+ valueElement = valueElement.nextSiblingElement("value");
+ }
+ field.setValue(values);
+ }
+ else
+ {
+ field.setValue(fieldElement.firstChildElement("value").text());
+ }
+
+ /* field options */
+ if (type == QXmppDataForm::Field::ListMultiField ||
+ type == QXmppDataForm::Field::ListSingleField)
+ {
+ QList<QPair<QString, QString> > options;
+ QDomElement optionElement = fieldElement.firstChildElement("option");
+ while (!optionElement.isNull())
+ {
+ options.append(QPair<QString, QString>(optionElement.attribute("label"),
+ optionElement.firstChildElement("value").text()));
+ optionElement = optionElement.nextSiblingElement("option");
+ }
+ field.setOptions(options);
+ }
+
+ /* other properties */
+ field.setDescription(fieldElement.firstChildElement("description").text());
+ field.setRequired(!fieldElement.firstChildElement("required").isNull());
+
+ m_fields.append(field);
+
+ fieldElement = fieldElement.nextSiblingElement("field");
+ }
+}
+
+void QXmppDataForm::toXml(QXmlStreamWriter *writer) const
+{
+ if (isNull())
+ return;
+
+ writer->writeStartElement("x");
+ writer->writeAttribute("xmlns", ns_data);
+
+ /* form type */
+ QString typeStr;
+ if (m_type == QXmppDataForm::Form)
+ typeStr = "form";
+ else if (m_type == QXmppDataForm::Submit)
+ typeStr = "submit";
+ else if (m_type == QXmppDataForm::Cancel)
+ typeStr = "cancel";
+ else if (m_type == QXmppDataForm::Result)
+ typeStr = "result";
+ helperToXmlAddAttribute(writer, "type", typeStr);
+
+ /* form properties */
+ if (!m_title.isEmpty())
+ helperToXmlAddTextElement(writer, "title", m_title);
+ if (!m_instructions.isEmpty())
+ helperToXmlAddTextElement(writer, "instructions", m_instructions);
+
+
+ foreach (const QXmppDataForm::Field &field, m_fields)
+ {
+ writer->writeStartElement("field");
+
+ /* field type */
+ const QXmppDataForm::Field::Type type = field.type();
+ QString typeStr;
+ struct field_type *ptr;
+ for (ptr = field_types; ptr->str; ptr++)
+ {
+ if (type == ptr->type)
+ {
+ typeStr = ptr->str;
+ break;
+ }
+ }
+ helperToXmlAddAttribute(writer, "type", typeStr);
+
+ /* field attributes */
+ helperToXmlAddAttribute(writer, "label", field.label());
+ helperToXmlAddAttribute(writer, "var", field.key());
+
+ /* field value(s) */
+ if (type == QXmppDataForm::Field::BooleanField)
+ {
+ helperToXmlAddTextElement(writer, "value", field.value().toBool() ? "1" : "0");
+ }
+ else if (type == QXmppDataForm::Field::ListMultiField ||
+ type == QXmppDataForm::Field::JidMultiField ||
+ type == QXmppDataForm::Field::TextMultiField)
+ {
+ foreach (const QString &value, field.value().toStringList())
+ helperToXmlAddTextElement(writer, "value", value);
+ }
+ else
+ {
+ helperToXmlAddTextElement(writer, "value", field.value().toString());
+ }
+
+ /* field options */
+ if (type == QXmppDataForm::Field::ListMultiField ||
+ type == QXmppDataForm::Field::ListSingleField)
+ {
+ QPair<QString, QString> option;
+ foreach (option, field.options())
+ {
+ writer->writeStartElement("option");
+ helperToXmlAddAttribute(writer, "label", option.first);
+ helperToXmlAddTextElement(writer, "value", option.second);
+ writer->writeEndElement();
+ }
+ }
+
+ /* other properties */
+ if (!field.description().isEmpty())
+ helperToXmlAddTextElement(writer, "description", field.description());
+ if (field.isRequired())
+ helperToXmlAddTextElement(writer, "required", "");
+
+ writer->writeEndElement();
+ }
+
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppDataForm.h b/src/base/QXmppDataForm.h
new file mode 100644
index 00000000..4f38a932
--- /dev/null
+++ b/src/base/QXmppDataForm.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPDATAFORM_H
+#define QXMPPDATAFORM_H
+
+#include <QPair>
+#include <QString>
+#include <QVariant>
+#include <QXmlStreamWriter>
+
+class QDomElement;
+
+/// \brief The QXmppDataForm class represents a data form as defined by
+/// XEP-0004: Data Forms.
+///
+
+class QXmppDataForm
+{
+public:
+ /// \brief The QXmppDataForm::Field class represents a data form field
+ /// as defined by XEP-0004: Data Forms.
+ ///
+
+ class Field
+ {
+ public:
+ /// This enum is used to describe a field's type.
+ enum Type
+ {
+ BooleanField,
+ FixedField,
+ HiddenField,
+ JidMultiField,
+ JidSingleField,
+ ListMultiField,
+ ListSingleField,
+ TextMultiField,
+ TextPrivateField,
+ TextSingleField,
+ };
+
+ Field(QXmppDataForm::Field::Type type = QXmppDataForm::Field::TextSingleField);
+
+ QString description() const;
+ void setDescription(const QString &description);
+
+ QString key() const;
+ void setKey(const QString &key);
+
+ QString label() const;
+ void setLabel(const QString &label);
+
+ QList<QPair<QString, QString> > options() const;
+ void setOptions(const QList<QPair<QString, QString> > &options);
+
+ bool isRequired() const;
+ void setRequired(bool required);
+
+ QXmppDataForm::Field::Type type() const;
+ void setType(QXmppDataForm::Field::Type type);
+
+ QVariant value() const;
+ void setValue(const QVariant &value);
+
+ private:
+ QString m_description;
+ QString m_key;
+ QString m_label;
+ QList<QPair<QString, QString> > m_options;
+ bool m_required;
+ QXmppDataForm::Field::Type m_type;
+ QVariant m_value;
+ };
+
+ /// This enum is used to describe a form's type.
+ enum Type
+ {
+ None, ///< Unknown form type
+ Form, ///< The form-processing entity is asking the form-submitting
+ ///< entity to complete a form.
+ Submit, ///< The form-submitting entity is submitting data to the
+ ///< form-processing entity.
+ Cancel, ///< The form-submitting entity has cancelled submission
+ ///< of data to the form-processing entity.
+ Result, ///< The form-processing entity is returning data
+ ///< (e.g., search results) to the form-submitting entity,
+ ///< or the data is a generic data set.
+ };
+
+ QXmppDataForm(QXmppDataForm::Type type = QXmppDataForm::None);
+
+ QString instructions() const;
+ void setInstructions(const QString &instructions);
+
+ QList<Field> fields() const;
+ QList<Field> &fields();
+ void setFields(const QList<QXmppDataForm::Field> &fields);
+
+ QString title() const;
+ void setTitle(const QString &title);
+
+ QXmppDataForm::Type type() const;
+ void setType(QXmppDataForm::Type type);
+
+ bool isNull() const;
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString m_instructions;
+ QList<Field> m_fields;
+ QString m_title;
+ QXmppDataForm::Type m_type;
+};
+
+#endif
diff --git a/src/base/QXmppDiscoveryIq.cpp b/src/base/QXmppDiscoveryIq.cpp
new file mode 100644
index 00000000..6e0d33d5
--- /dev/null
+++ b/src/base/QXmppDiscoveryIq.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 <QCryptographicHash>
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppDiscoveryIq.h"
+#include "QXmppUtils.h"
+
+static bool identityLessThan(const QXmppDiscoveryIq::Identity &i1, const QXmppDiscoveryIq::Identity &i2)
+{
+ if (i1.category() < i2.category())
+ return true;
+ else if (i1.category() > i2.category())
+ return false;
+
+ if (i1.type() < i2.type())
+ return true;
+ else if (i1.type() > i2.type())
+ return false;
+
+ if (i1.language() < i2.language())
+ return true;
+ else if (i1.language() > i2.language())
+ return false;
+
+ if (i1.name() < i2.name())
+ return true;
+ else if (i1.name() > i2.name())
+ return false;
+
+ return false;
+}
+
+QString QXmppDiscoveryIq::Identity::category() const
+{
+ return m_category;
+}
+
+void QXmppDiscoveryIq::Identity::setCategory(const QString &category)
+{
+ m_category = category;
+}
+
+QString QXmppDiscoveryIq::Identity::language() const
+{
+ return m_language;
+}
+
+void QXmppDiscoveryIq::Identity::setLanguage(const QString &language)
+{
+ m_language = language;
+}
+
+QString QXmppDiscoveryIq::Identity::name() const
+{
+ return m_name;
+}
+
+void QXmppDiscoveryIq::Identity::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QString QXmppDiscoveryIq::Identity::type() const
+{
+ return m_type;
+}
+
+void QXmppDiscoveryIq::Identity::setType(const QString &type)
+{
+ m_type = type;
+}
+
+QString QXmppDiscoveryIq::Item::jid() const
+{
+ return m_jid;
+}
+
+void QXmppDiscoveryIq::Item::setJid(const QString &jid)
+{
+ m_jid = jid;
+}
+
+QString QXmppDiscoveryIq::Item::name() const
+{
+ return m_name;
+}
+
+void QXmppDiscoveryIq::Item::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QString QXmppDiscoveryIq::Item::node() const
+{
+ return m_node;
+}
+
+void QXmppDiscoveryIq::Item::setNode(const QString &node)
+{
+ m_node = node;
+}
+
+QStringList QXmppDiscoveryIq::features() const
+{
+ return m_features;
+}
+
+void QXmppDiscoveryIq::setFeatures(const QStringList &features)
+{
+ m_features = features;
+}
+
+QList<QXmppDiscoveryIq::Identity> QXmppDiscoveryIq::identities() const
+{
+ return m_identities;
+}
+
+void QXmppDiscoveryIq::setIdentities(const QList<QXmppDiscoveryIq::Identity> &identities)
+{
+ m_identities = identities;
+}
+
+QList<QXmppDiscoveryIq::Item> QXmppDiscoveryIq::items() const
+{
+ return m_items;
+}
+
+void QXmppDiscoveryIq::setItems(const QList<QXmppDiscoveryIq::Item> &items)
+{
+ m_items = items;
+}
+
+/// Returns the QXmppDataForm for this IQ, as defined by
+/// XEP-0128: Service Discovery Extensions.
+///
+
+QXmppDataForm QXmppDiscoveryIq::form() const
+{
+ return m_form;
+}
+
+/// Sets the QXmppDataForm for this IQ, as define by
+/// XEP-0128: Service Discovery Extensions.
+///
+/// \param form
+///
+
+void QXmppDiscoveryIq::setForm(const QXmppDataForm &form)
+{
+ m_form = form;
+}
+
+QString QXmppDiscoveryIq::queryNode() const
+{
+ return m_queryNode;
+}
+
+void QXmppDiscoveryIq::setQueryNode(const QString &node)
+{
+ m_queryNode = node;
+}
+
+enum QXmppDiscoveryIq::QueryType QXmppDiscoveryIq::queryType() const
+{
+ return m_queryType;
+}
+
+void QXmppDiscoveryIq::setQueryType(enum QXmppDiscoveryIq::QueryType type)
+{
+ m_queryType = type;
+}
+
+/// Calculate the verification string for XEP-0115 : Entity Capabilities
+
+QByteArray QXmppDiscoveryIq::verificationString() const
+{
+ QString S;
+ QList<QXmppDiscoveryIq::Identity> sortedIdentities = m_identities;
+ qSort(sortedIdentities.begin(), sortedIdentities.end(), identityLessThan);
+ QStringList sortedFeatures = m_features;
+ qSort(sortedFeatures);
+ foreach (const QXmppDiscoveryIq::Identity &identity, sortedIdentities)
+ S += QString("%1/%2/%3/%4<").arg(identity.category(), identity.type(), identity.language(), identity.name());
+ foreach (const QString &feature, sortedFeatures)
+ S += feature + QLatin1String("<");
+ QCryptographicHash hasher(QCryptographicHash::Sha1);
+ hasher.addData(S.toUtf8());
+ return hasher.result();
+}
+
+bool QXmppDiscoveryIq::isDiscoveryIq(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ return (queryElement.namespaceURI() == ns_disco_info ||
+ queryElement.namespaceURI() == ns_disco_items);
+}
+
+void QXmppDiscoveryIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ m_queryNode = queryElement.attribute("node");
+ if (queryElement.namespaceURI() == ns_disco_items)
+ m_queryType = ItemsQuery;
+ else
+ m_queryType = InfoQuery;
+
+ QDomElement itemElement = queryElement.firstChildElement();
+ while (!itemElement.isNull())
+ {
+ if (itemElement.tagName() == "feature")
+ {
+ m_features.append(itemElement.attribute("var"));
+ }
+ else if (itemElement.tagName() == "identity")
+ {
+ QXmppDiscoveryIq::Identity identity;
+ identity.setLanguage(itemElement.attribute("xml:lang"));
+ identity.setCategory(itemElement.attribute("category"));
+ identity.setName(itemElement.attribute("name"));
+ identity.setType(itemElement.attribute("type"));
+ m_identities.append(identity);
+ }
+ else if (itemElement.tagName() == "item")
+ {
+ QXmppDiscoveryIq::Item item;
+ item.setJid(itemElement.attribute("jid"));
+ item.setName(itemElement.attribute("name"));
+ item.setNode(itemElement.attribute("node"));
+ m_items.append(item);
+ }
+ else if (itemElement.tagName() == "x" &&
+ itemElement.namespaceURI() == ns_data)
+ {
+ m_form.parse(itemElement);
+ }
+ itemElement = itemElement.nextSiblingElement();
+ }
+}
+
+void QXmppDiscoveryIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute("xmlns",
+ m_queryType == InfoQuery ? ns_disco_info : ns_disco_items);
+ helperToXmlAddAttribute(writer, "node", m_queryNode);
+
+ foreach (const QString &feature, m_features)
+ {
+ writer->writeStartElement("feature");
+ helperToXmlAddAttribute(writer, "var", feature);
+ writer->writeEndElement();
+ }
+
+ foreach (const QXmppDiscoveryIq::Identity& identity, m_identities)
+ {
+ writer->writeStartElement("identity");
+ helperToXmlAddAttribute(writer, "xml:lang", identity.language());
+ helperToXmlAddAttribute(writer, "category", identity.category());
+ helperToXmlAddAttribute(writer, "name", identity.name());
+ helperToXmlAddAttribute(writer, "type", identity.type());
+ writer->writeEndElement();
+ }
+
+ foreach (const QXmppDiscoveryIq::Item& item, m_items)
+ {
+ writer->writeStartElement("item");
+ helperToXmlAddAttribute(writer, "jid", item.jid());
+ helperToXmlAddAttribute(writer, "name", item.name());
+ helperToXmlAddAttribute(writer, "node", item.node());
+ writer->writeEndElement();
+ }
+
+ m_form.toXml(writer);
+
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppDiscoveryIq.h b/src/base/QXmppDiscoveryIq.h
new file mode 100644
index 00000000..40aa1a3b
--- /dev/null
+++ b/src/base/QXmppDiscoveryIq.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPDISCOVERY_H
+#define QXMPPDISCOVERY_H
+
+#include "QXmppDataForm.h"
+#include "QXmppIq.h"
+
+class QDomElement;
+
+class QXmppDiscoveryIq : public QXmppIq
+{
+public:
+ class Identity
+ {
+ public:
+ QString category() const;
+ void setCategory(const QString &category);
+
+ QString language() const;
+ void setLanguage(const QString &language);
+
+ QString name() const;
+ void setName(const QString &name);
+
+ QString type() const;
+ void setType(const QString &type);
+
+ private:
+ QString m_category;
+ QString m_language;
+ QString m_name;
+ QString m_type;
+ };
+
+ class Item
+ {
+ public:
+ QString jid() const;
+ void setJid(const QString &jid);
+
+ QString name() const;
+ void setName(const QString &name);
+
+ QString node() const;
+ void setNode(const QString &node);
+
+ private:
+ QString m_jid;
+ QString m_name;
+ QString m_node;
+ };
+
+ enum QueryType {
+ InfoQuery,
+ ItemsQuery,
+ };
+
+ QStringList features() const;
+ void setFeatures(const QStringList &features);
+
+ QList<QXmppDiscoveryIq::Identity> identities() const;
+ void setIdentities(const QList<QXmppDiscoveryIq::Identity> &identities);
+
+ QList<QXmppDiscoveryIq::Item> items() const;
+ void setItems(const QList<QXmppDiscoveryIq::Item> &items);
+
+ QXmppDataForm form() const;
+ void setForm(const QXmppDataForm &form);
+
+ QString queryNode() const;
+ void setQueryNode(const QString &node);
+
+ enum QueryType queryType() const;
+ void setQueryType(enum QueryType type);
+
+ QByteArray verificationString() const;
+
+ static bool isDiscoveryIq(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QStringList m_features;
+ QList<QXmppDiscoveryIq::Identity> m_identities;
+ QList<QXmppDiscoveryIq::Item> m_items;
+ QXmppDataForm m_form;
+ QString m_queryNode;
+ enum QueryType m_queryType;
+};
+
+#endif
diff --git a/src/base/QXmppElement.cpp b/src/base/QXmppElement.cpp
new file mode 100644
index 00000000..904ab473
--- /dev/null
+++ b/src/base/QXmppElement.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include "QXmppElement.h"
+#include "QXmppUtils.h"
+
+#include <QDomElement>
+
+class QXmppElementPrivate
+{
+public:
+ QXmppElementPrivate();
+ QXmppElementPrivate(const QDomElement &element);
+ ~QXmppElementPrivate();
+
+ QAtomicInt counter;
+
+ QXmppElementPrivate *parent;
+ QMap<QString, QString> attributes;
+ QList<QXmppElementPrivate*> children;
+ QString name;
+ QString value;
+};
+
+QXmppElementPrivate::QXmppElementPrivate()
+ : counter(1), parent(NULL)
+{
+}
+
+QXmppElementPrivate::QXmppElementPrivate(const QDomElement &element)
+ : counter(1), parent(NULL)
+{
+ if (element.isNull())
+ return;
+
+ name = element.tagName();
+ QString xmlns = element.namespaceURI();
+ QString parentns = element.parentNode().namespaceURI();
+ if (!xmlns.isEmpty() && xmlns != parentns)
+ attributes.insert("xmlns", xmlns);
+ QDomNamedNodeMap attrs = element.attributes();
+ for (int i = 0; i < attrs.size(); i++)
+ {
+ QDomAttr attr = attrs.item(i).toAttr();
+ attributes.insert(attr.name(), attr.value());
+ }
+
+ QDomNode childNode = element.firstChild();
+ while (!childNode.isNull())
+ {
+ if (childNode.isElement())
+ {
+ QXmppElementPrivate *child = new QXmppElementPrivate(childNode.toElement());
+ child->parent = this;
+ children.append(child);
+ } else if (childNode.isText()) {
+ value += childNode.toText().data();
+ }
+ childNode = childNode.nextSibling();
+ }
+}
+
+QXmppElementPrivate::~QXmppElementPrivate()
+{
+ foreach (QXmppElementPrivate *child, children)
+ if (!child->counter.deref())
+ delete child;
+}
+
+QXmppElement::QXmppElement()
+{
+ d = new QXmppElementPrivate();
+}
+
+QXmppElement::QXmppElement(const QXmppElement &other)
+{
+ other.d->counter.ref();
+ d = other.d;
+}
+
+QXmppElement::QXmppElement(QXmppElementPrivate *other)
+{
+ other->counter.ref();
+ d = other;
+}
+
+QXmppElement::QXmppElement(const QDomElement &element)
+{
+ d = new QXmppElementPrivate(element);
+}
+
+QXmppElement::~QXmppElement()
+{
+ if (!d->counter.deref())
+ delete d;
+}
+
+QXmppElement &QXmppElement::operator=(const QXmppElement &other)
+{
+ other.d->counter.ref();
+ if (!d->counter.deref())
+ delete d;
+ d = other.d;
+ return *this;
+}
+
+QStringList QXmppElement::attributeNames() const
+{
+ return d->attributes.keys();
+}
+
+QString QXmppElement::attribute(const QString &name) const
+{
+ return d->attributes.value(name);
+}
+
+void QXmppElement::setAttribute(const QString &name, const QString &value)
+{
+ d->attributes.insert(name, value);
+}
+
+void QXmppElement::appendChild(const QXmppElement &child)
+{
+ if (child.d->parent == d)
+ return;
+
+ if (child.d->parent)
+ child.d->parent->children.removeAll(child.d);
+ else
+ child.d->counter.ref();
+ child.d->parent = d;
+ d->children.append(child.d);
+}
+
+QXmppElement QXmppElement::firstChildElement(const QString &name) const
+{
+ foreach (QXmppElementPrivate *child_d, d->children)
+ if (name.isEmpty() || child_d->name == name)
+ return QXmppElement(child_d);
+ return QXmppElement();
+}
+
+QXmppElement QXmppElement::nextSiblingElement(const QString &name) const
+{
+ if (!d->parent)
+ return QXmppElement();
+ const QList<QXmppElementPrivate*> &siblings_d = d->parent->children;
+ for (int i = siblings_d.indexOf(d) + 1; i < siblings_d.size(); i++)
+ if (name.isEmpty() || siblings_d[i]->name == name)
+ return QXmppElement(siblings_d[i]);
+ return QXmppElement();
+}
+
+bool QXmppElement::isNull() const
+{
+ return d->name.isEmpty();
+}
+
+void QXmppElement::removeChild(const QXmppElement &child)
+{
+ if (child.d->parent != d)
+ return;
+
+ d->children.removeAll(child.d);
+ child.d->counter.deref();
+ child.d->parent = NULL;
+}
+
+QString QXmppElement::tagName() const
+{
+ return d->name;
+}
+
+void QXmppElement::setTagName(const QString &tagName)
+{
+ d->name = tagName;
+}
+
+QString QXmppElement::value() const
+{
+ return d->value;
+}
+
+void QXmppElement::setValue(const QString &value)
+{
+ d->value = value;
+}
+
+void QXmppElement::toXml(QXmlStreamWriter *writer) const
+{
+ if (isNull())
+ return;
+
+ writer->writeStartElement(d->name);
+ if (d->attributes.contains("xmlns"))
+ writer->writeAttribute("xmlns", d->attributes.value("xmlns"));
+ foreach (const QString &attr, d->attributes.keys())
+ if (attr != "xmlns")
+ helperToXmlAddAttribute(writer, attr, d->attributes.value(attr));
+ if (!d->value.isEmpty())
+ writer->writeCharacters(d->value);
+ foreach (const QXmppElement &child, d->children)
+ child.toXml(writer);
+ writer->writeEndElement();
+}
+
+QXmppElementList::QXmppElementList()
+{
+}
+
+QXmppElementList::QXmppElementList(const QXmppElement &element)
+{
+ append(element);
+}
+
+
+QXmppElementList::QXmppElementList(const QList<QXmppElement> &other)
+ : QList<QXmppElement>(other)
+{
+}
+
diff --git a/src/base/QXmppElement.h b/src/base/QXmppElement.h
new file mode 100644
index 00000000..7fa75494
--- /dev/null
+++ b/src/base/QXmppElement.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPELEMENT_H
+#define QXMPPELEMENT_H
+
+#include <QMap>
+#include <QStringList>
+#include <QXmlStreamWriter>
+
+class QDomElement;
+class QXmppElement;
+class QXmppElementPrivate;
+
+class QXmppElementList : public QList<QXmppElement>
+{
+public:
+ QXmppElementList();
+ QXmppElementList(const QXmppElement &element);
+ QXmppElementList(const QList<QXmppElement> &other);
+};
+
+class QXmppElement
+{
+public:
+ QXmppElement();
+ QXmppElement(const QXmppElement &other);
+ QXmppElement(const QDomElement &element);
+ ~QXmppElement();
+
+ QStringList attributeNames() const;
+
+ QString attribute(const QString &name) const;
+ void setAttribute(const QString &name, const QString &value);
+
+ void appendChild(const QXmppElement &child);
+ QXmppElement firstChildElement(const QString &name = QString()) const;
+ QXmppElement nextSiblingElement(const QString &name = QString()) const;
+ void removeChild(const QXmppElement &child);
+
+ QString tagName() const;
+ void setTagName(const QString &type);
+
+ QString value() const;
+ void setValue(const QString &text);
+
+ bool isNull() const;
+ void toXml(QXmlStreamWriter *writer) const;
+
+ QXmppElement &operator=(const QXmppElement &other);
+
+private:
+ QXmppElement(QXmppElementPrivate *other);
+ QXmppElementPrivate *d;
+};
+
+#endif
diff --git a/src/base/QXmppEntityTimeIq.cpp b/src/base/QXmppEntityTimeIq.cpp
new file mode 100644
index 00000000..5e41e39f
--- /dev/null
+++ b/src/base/QXmppEntityTimeIq.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#include "QXmppEntityTimeIq.h"
+
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppUtils.h"
+
+/// Returns the timezone offset in seconds.
+///
+
+int QXmppEntityTimeIq::tzo() const
+{
+ return m_tzo;
+}
+
+/// Sets the timezone offset in seconds.
+///
+/// \param tzo
+
+void QXmppEntityTimeIq::setTzo(int tzo)
+{
+ m_tzo = tzo;
+}
+
+/// Returns the date/time in Coordinated Universal Time (UTC).
+///
+
+QDateTime QXmppEntityTimeIq::utc() const
+{
+ return m_utc;
+}
+
+/// Sets the date/time in Coordinated Universal Time (UTC).
+///
+/// \param utc
+
+void QXmppEntityTimeIq::setUtc(const QDateTime &utc)
+{
+ m_utc = utc;
+}
+
+bool QXmppEntityTimeIq::isEntityTimeIq(const QDomElement &element)
+{
+ QDomElement timeElement = element.firstChildElement("time");
+ return timeElement.namespaceURI() == ns_entity_time;
+}
+
+void QXmppEntityTimeIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement timeElement = element.firstChildElement("time");
+ m_tzo = timezoneOffsetFromString(timeElement.firstChildElement("tzo").text());
+ m_utc = datetimeFromString(timeElement.firstChildElement("utc").text());
+}
+
+void QXmppEntityTimeIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("time");
+ writer->writeAttribute("xmlns", ns_entity_time);
+
+ if(m_utc.isValid())
+ {
+ helperToXmlAddTextElement(writer, "tzo", timezoneOffsetToString(m_tzo));
+ helperToXmlAddTextElement(writer, "utc", datetimeToString(m_utc));
+ }
+ writer->writeEndElement();
+}
diff --git a/src/base/QXmppEntityTimeIq.h b/src/base/QXmppEntityTimeIq.h
new file mode 100644
index 00000000..fa3d5f12
--- /dev/null
+++ b/src/base/QXmppEntityTimeIq.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPENTITYTIMEIQ_H
+#define QXMPPENTITYTIMEIQ_H
+
+#include <QDateTime>
+
+#include "QXmppIq.h"
+
+/// \ingroup Stanzas
+
+class QXmppEntityTimeIq : public QXmppIq
+{
+public:
+ int tzo() const;
+ void setTzo(int tzo);
+
+ QDateTime utc() const;
+ void setUtc(const QDateTime &utc);
+
+ static bool isEntityTimeIq(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ int m_tzo;
+ QDateTime m_utc;
+};
+
+#endif //QXMPPENTITYTIMEIQ_H
diff --git a/src/base/QXmppGlobal.cpp b/src/base/QXmppGlobal.cpp
new file mode 100644
index 00000000..2735788a
--- /dev/null
+++ b/src/base/QXmppGlobal.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/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 "QXmppGlobal.h"
+
+QString QXmppVersion()
+{
+ return QString("%1.%2.%3").arg(
+ QString::number((QXMPP_VERSION >> 16) & 0xff),
+ QString::number((QXMPP_VERSION >> 8) & 0xff),
+ QString::number(QXMPP_VERSION & 0xff));
+}
+
diff --git a/src/base/QXmppGlobal.h b/src/base/QXmppGlobal.h
new file mode 100644
index 00000000..36122d1a
--- /dev/null
+++ b/src/base/QXmppGlobal.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPGLOBAL_H
+#define QXMPPGLOBAL_H
+
+#include <QString>
+
+/// This macro expands a numeric value of the form 0xMMNNPP (MM =
+/// major, NN = minor, PP = patch) that specifies QXmpp's version
+/// number. For example, if you compile your application against
+/// QXmpp 1.2.3, the QXMPP_VERSION macro will expand to 0x010203.
+///
+/// You can use QXMPP_VERSION to use the latest QXmpp features where
+/// available.
+///
+
+#define QXMPP_VERSION 0x00035b
+
+QString QXmppVersion();
+
+#endif //QXMPPGLOBAL_H
diff --git a/src/base/QXmppIbbIq.cpp b/src/base/QXmppIbbIq.cpp
new file mode 100644
index 00000000..85aba54a
--- /dev/null
+++ b/src/base/QXmppIbbIq.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+#include <QXmlStreamWriter>
+
+#include "QXmppConstants.h"
+#include "QXmppIbbIq.h"
+
+QXmppIbbOpenIq::QXmppIbbOpenIq() : QXmppIq(QXmppIq::Set), m_block_size(1024)
+{
+
+}
+
+long QXmppIbbOpenIq::blockSize() const
+{
+ return m_block_size;
+}
+
+void QXmppIbbOpenIq::setBlockSize( long block_size )
+{
+ m_block_size = block_size;
+}
+
+QString QXmppIbbOpenIq::sid() const
+{
+ return m_sid;
+}
+
+void QXmppIbbOpenIq::setSid( const QString &sid )
+{
+ m_sid = sid;
+}
+
+bool QXmppIbbOpenIq::isIbbOpenIq(const QDomElement &element)
+{
+ QDomElement openElement = element.firstChildElement("open");
+ return openElement.namespaceURI() == ns_ibb;
+}
+
+void QXmppIbbOpenIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement openElement = element.firstChildElement("open");
+ m_sid = openElement.attribute( "sid" );
+ m_block_size = openElement.attribute( "block-size" ).toLong();
+}
+
+void QXmppIbbOpenIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("open");
+ writer->writeAttribute( "xmlns",ns_ibb);
+ writer->writeAttribute( "sid",m_sid);
+ writer->writeAttribute( "block-size",QString::number(m_block_size) );
+ writer->writeEndElement();
+}
+
+QXmppIbbCloseIq::QXmppIbbCloseIq() : QXmppIq(QXmppIq::Set)
+{
+
+}
+
+QString QXmppIbbCloseIq::sid() const
+{
+ return m_sid;
+}
+
+void QXmppIbbCloseIq::setSid( const QString &sid )
+{
+ m_sid = sid;
+}
+
+bool QXmppIbbCloseIq::isIbbCloseIq(const QDomElement &element)
+{
+ QDomElement openElement = element.firstChildElement("close");
+ return openElement.namespaceURI() == ns_ibb;
+}
+
+void QXmppIbbCloseIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement openElement = element.firstChildElement("close");
+ m_sid = openElement.attribute( "sid" );
+}
+
+void QXmppIbbCloseIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("close");
+ writer->writeAttribute( "xmlns",ns_ibb);
+ writer->writeAttribute( "sid",m_sid);
+ writer->writeEndElement();
+}
+
+QXmppIbbDataIq::QXmppIbbDataIq() : QXmppIq( QXmppIq::Set ), m_seq(0)
+{
+}
+
+quint16 QXmppIbbDataIq::sequence() const
+{
+ return m_seq;
+}
+
+void QXmppIbbDataIq::setSequence( quint16 seq )
+{
+ m_seq = seq;
+}
+
+QString QXmppIbbDataIq::sid() const
+{
+ return m_sid;
+}
+
+void QXmppIbbDataIq::setSid( const QString &sid )
+{
+ m_sid = sid;
+}
+
+QByteArray QXmppIbbDataIq::payload() const
+{
+ return m_payload;
+}
+
+void QXmppIbbDataIq::setPayload( const QByteArray &data )
+{
+ m_payload = data;
+}
+
+bool QXmppIbbDataIq::isIbbDataIq(const QDomElement &element)
+{
+ QDomElement dataElement = element.firstChildElement("data");
+ return dataElement.namespaceURI() == ns_ibb;
+}
+
+void QXmppIbbDataIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement dataElement = element.firstChildElement("data");
+ m_sid = dataElement.attribute( "sid" );
+ m_seq = dataElement.attribute( "seq" ).toLong();
+ m_payload = QByteArray::fromBase64( dataElement.text().toLatin1() );
+}
+
+void QXmppIbbDataIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("data");
+ writer->writeAttribute( "xmlns",ns_ibb);
+ writer->writeAttribute( "sid",m_sid);
+ writer->writeAttribute( "seq",QString::number(m_seq) );
+ writer->writeCharacters( m_payload.toBase64() );
+ writer->writeEndElement();
+}
diff --git a/src/base/QXmppIbbIq.h b/src/base/QXmppIbbIq.h
new file mode 100644
index 00000000..592b12f4
--- /dev/null
+++ b/src/base/QXmppIbbIq.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPIBBIQ_H
+#define QXMPPIBBIQ_H
+
+#include "QXmppIq.h"
+
+class QDomElement;
+class QXmlStreamWriter;
+
+class QXmppIbbOpenIq: public QXmppIq
+{
+public:
+ QXmppIbbOpenIq();
+
+ long blockSize() const;
+ void setBlockSize( long block_size );
+
+ QString sid() const;
+ void setSid( const QString &sid );
+
+ static bool isIbbOpenIq(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ long m_block_size;
+ QString m_sid;
+};
+
+class QXmppIbbCloseIq: public QXmppIq
+{
+public:
+ QXmppIbbCloseIq();
+
+ QString sid() const;
+ void setSid( const QString &sid );
+
+ static bool isIbbCloseIq(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString m_sid;
+};
+
+class QXmppIbbDataIq : public QXmppIq
+{
+public:
+ QXmppIbbDataIq();
+
+ quint16 sequence() const;
+ void setSequence( quint16 seq );
+
+ QString sid() const;
+ void setSid( const QString &sid );
+
+ QByteArray payload() const;
+ void setPayload( const QByteArray &data );
+
+ static bool isIbbDataIq(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ quint16 m_seq;
+ QString m_sid;
+ QByteArray m_payload;
+};
+
+#endif // QXMPPIBBIQS_H
diff --git a/src/base/QXmppIq.cpp b/src/base/QXmppIq.cpp
new file mode 100644
index 00000000..d0ee0a7a
--- /dev/null
+++ b/src/base/QXmppIq.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#include "QXmppUtils.h"
+#include "QXmppIq.h"
+
+#include <QDomElement>
+#include <QXmlStreamWriter>
+
+/// Constructs a QXmppIq with the specified \a type.
+///
+/// \param type
+
+QXmppIq::QXmppIq(QXmppIq::Type type)
+ : QXmppStanza(), m_type(type)
+{
+ generateAndSetNextId();
+}
+
+/// Returns the IQ's type.
+///
+
+QXmppIq::Type QXmppIq::type() const
+{
+ return m_type;
+}
+
+/// Sets the IQ's type.
+///
+/// \param type
+
+void QXmppIq::setType(QXmppIq::Type type)
+{
+ m_type = type;
+}
+
+void QXmppIq::parse(const QDomElement &element)
+{
+ QXmppStanza::parse(element);
+ setTypeFromStr(element.attribute("type"));
+ parseElementFromChild(element);
+}
+
+void QXmppIq::parseElementFromChild(const QDomElement &element)
+{
+ QXmppElementList extensions;
+ QDomElement itemElement = element.firstChildElement();
+ while (!itemElement.isNull())
+ {
+ extensions.append(QXmppElement(itemElement));
+ itemElement = itemElement.nextSiblingElement();
+ }
+ setExtensions(extensions);
+}
+
+void QXmppIq::toXml( QXmlStreamWriter *xmlWriter ) const
+{
+ xmlWriter->writeStartElement("iq");
+
+ helperToXmlAddAttribute(xmlWriter, "id", id());
+ helperToXmlAddAttribute(xmlWriter, "to", to());
+ helperToXmlAddAttribute(xmlWriter, "from", from());
+ if(getTypeStr().isEmpty())
+ helperToXmlAddAttribute(xmlWriter, "type", "get");
+ else
+ helperToXmlAddAttribute(xmlWriter, "type", getTypeStr());
+ toXmlElementFromChild(xmlWriter);
+ error().toXml(xmlWriter);
+ xmlWriter->writeEndElement();
+}
+
+void QXmppIq::toXmlElementFromChild( QXmlStreamWriter *writer ) const
+{
+ foreach (const QXmppElement &extension, extensions())
+ extension.toXml(writer);
+}
+
+QString QXmppIq::getTypeStr() const
+{
+ switch(m_type)
+ {
+ case QXmppIq::Error:
+ return "error";
+ case QXmppIq::Get:
+ return "get";
+ case QXmppIq::Set:
+ return "set";
+ case QXmppIq::Result:
+ return "result";
+ default:
+ qWarning("QXmppIq::getTypeStr() invalid type %d", (int)m_type);
+ return "";
+ }
+}
+
+void QXmppIq::setTypeFromStr(const QString& str)
+{
+ if(str == "error")
+ {
+ setType(QXmppIq::Error);
+ return;
+ }
+ else if(str == "get")
+ {
+ setType(QXmppIq::Get);
+ return;
+ }
+ else if(str == "set")
+ {
+ setType(QXmppIq::Set);
+ return;
+ }
+ else if(str == "result")
+ {
+ setType(QXmppIq::Result);
+ return;
+ }
+ else
+ {
+ setType(static_cast<QXmppIq::Type>(-1));
+ qWarning("QXmppIq::setTypeFromStr() invalid input string type: %s",
+ qPrintable(str));
+ return;
+ }
+}
+
diff --git a/src/base/QXmppIq.h b/src/base/QXmppIq.h
new file mode 100644
index 00000000..bbde3d06
--- /dev/null
+++ b/src/base/QXmppIq.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPIQ_H
+#define QXMPPIQ_H
+
+#include "QXmppStanza.h"
+
+// forward declarations of QXmlStream* classes will not work on Mac, we need to
+// include the whole header.
+// See http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html
+// for an explanation.
+#include <QXmlStreamWriter>
+
+/// \brief The QXmppIq class is the base class for all IQs.
+///
+/// \ingroup Stanzas
+
+class QXmppIq : public QXmppStanza
+{
+public:
+ /// This enum describes the type of IQ.
+ enum Type
+ {
+ Error = 0, ///< Error response.
+ Get, ///< Get request.
+ Set, ///< Set request.
+ Result ///< Result.
+ };
+
+ QXmppIq(QXmppIq::Type type = QXmppIq::Get);
+
+ QXmppIq::Type type() const;
+ void setType(QXmppIq::Type);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+
+protected:
+ virtual void parseElementFromChild(const QDomElement &element);
+ virtual void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString getTypeStr() const;
+ void setTypeFromStr(const QString& str);
+
+ Type m_type;
+};
+
+#endif // QXMPPIQ_H
diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp
new file mode 100644
index 00000000..5eb11322
--- /dev/null
+++ b/src/base/QXmppJingleIq.cpp
@@ -0,0 +1,848 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppJingleIq.h"
+#include "QXmppUtils.h"
+
+static const char* ns_jingle_rtp_info = "urn:xmpp:jingle:apps:rtp:info:1";
+
+static const char* jingle_actions[] = {
+ "content-accept",
+ "content-add",
+ "content-modify",
+ "content-reject",
+ "content-remove",
+ "description-info",
+ "security-info",
+ "session-accept",
+ "session-info",
+ "session-initiate",
+ "session-terminate",
+ "transport-accept",
+ "transport-info",
+ "transport-reject",
+ "transport-replace",
+};
+
+static const char* jingle_reasons[] = {
+ "",
+ "alternative-session",
+ "busy",
+ "cancel",
+ "connectivity-error",
+ "decline",
+ "expired",
+ "failed-application",
+ "failed-transport",
+ "general-error",
+ "gone",
+ "incompatible-parameters",
+ "media-error",
+ "security-error",
+ "success",
+ "timeout",
+ "unsupported-applications",
+ "unsupported-transports",
+};
+
+QXmppJingleIq::Content::Content()
+{
+}
+
+QString QXmppJingleIq::Content::creator() const
+{
+ return m_creator;
+}
+
+void QXmppJingleIq::Content::setCreator(const QString &creator)
+{
+ m_creator = creator;
+}
+
+QString QXmppJingleIq::Content::name() const
+{
+ return m_name;
+}
+
+void QXmppJingleIq::Content::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QString QXmppJingleIq::Content::senders() const
+{
+ return m_senders;
+}
+
+void QXmppJingleIq::Content::setSenders(const QString &senders)
+{
+ m_senders = senders;
+}
+
+QString QXmppJingleIq::Content::descriptionMedia() const
+{
+ return m_descriptionMedia;
+}
+
+void QXmppJingleIq::Content::setDescriptionMedia(const QString &media)
+{
+ m_descriptionMedia = media;
+}
+
+void QXmppJingleIq::Content::addPayloadType(const QXmppJinglePayloadType &payload)
+{
+ m_descriptionType = ns_jingle_rtp;
+ m_payloadTypes << payload;
+}
+
+QList<QXmppJinglePayloadType> QXmppJingleIq::Content::payloadTypes() const
+{
+ return m_payloadTypes;
+}
+
+void QXmppJingleIq::Content::setPayloadTypes(const QList<QXmppJinglePayloadType> &payloadTypes)
+{
+ m_descriptionType = payloadTypes.isEmpty() ? QString() : ns_jingle_rtp;
+ m_payloadTypes = payloadTypes;
+}
+
+void QXmppJingleIq::Content::addTransportCandidate(const QXmppJingleCandidate &candidate)
+{
+ m_transportType = ns_jingle_ice_udp;
+ m_transportCandidates << candidate;
+}
+
+QList<QXmppJingleCandidate> QXmppJingleIq::Content::transportCandidates() const
+{
+ return m_transportCandidates;
+}
+
+QString QXmppJingleIq::Content::transportUser() const
+{
+ return m_transportUser;
+}
+
+void QXmppJingleIq::Content::setTransportUser(const QString &user)
+{
+ m_transportUser = user;
+}
+
+QString QXmppJingleIq::Content::transportPassword() const
+{
+ return m_transportPassword;
+}
+
+void QXmppJingleIq::Content::setTransportPassword(const QString &password)
+{
+ m_transportPassword = password;
+}
+
+void QXmppJingleIq::Content::parse(const QDomElement &element)
+{
+ m_creator = element.attribute("creator");
+ m_disposition = element.attribute("disposition");
+ m_name = element.attribute("name");
+ m_senders = element.attribute("senders");
+
+ // description
+ QDomElement descriptionElement = element.firstChildElement("description");
+ m_descriptionType = descriptionElement.namespaceURI();
+ m_descriptionMedia = descriptionElement.attribute("media");
+ QDomElement child = descriptionElement.firstChildElement("payload-type");
+ while (!child.isNull())
+ {
+ QXmppJinglePayloadType payload;
+ payload.parse(child);
+ m_payloadTypes << payload;
+ child = child.nextSiblingElement("payload-type");
+ }
+
+ // transport
+ QDomElement transportElement = element.firstChildElement("transport");
+ m_transportType = transportElement.namespaceURI();
+ m_transportUser = transportElement.attribute("ufrag");
+ m_transportPassword = transportElement.attribute("pwd");
+ child = transportElement.firstChildElement("candidate");
+ while (!child.isNull())
+ {
+ QXmppJingleCandidate candidate;
+ candidate.parse(child);
+ m_transportCandidates << candidate;
+ child = child.nextSiblingElement("candidate");
+ }
+}
+
+void QXmppJingleIq::Content::toXml(QXmlStreamWriter *writer) const
+{
+ if (m_creator.isEmpty() || m_name.isEmpty())
+ return;
+
+ writer->writeStartElement("content");
+ helperToXmlAddAttribute(writer, "creator", m_creator);
+ helperToXmlAddAttribute(writer, "disposition", m_disposition);
+ helperToXmlAddAttribute(writer, "name", m_name);
+ helperToXmlAddAttribute(writer, "senders", m_senders);
+
+ // description
+ if (!m_descriptionType.isEmpty() || !m_payloadTypes.isEmpty())
+ {
+ writer->writeStartElement("description");
+ writer->writeAttribute("xmlns", m_descriptionType);
+ helperToXmlAddAttribute(writer, "media", m_descriptionMedia);
+ foreach (const QXmppJinglePayloadType &payload, m_payloadTypes)
+ payload.toXml(writer);
+ writer->writeEndElement();
+ }
+
+ // transport
+ if (!m_transportType.isEmpty() || !m_transportCandidates.isEmpty())
+ {
+ writer->writeStartElement("transport");
+ writer->writeAttribute("xmlns", m_transportType);
+ helperToXmlAddAttribute(writer, "ufrag", m_transportUser);
+ helperToXmlAddAttribute(writer, "pwd", m_transportPassword);
+ foreach (const QXmppJingleCandidate &candidate, m_transportCandidates)
+ candidate.toXml(writer);
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+}
+
+QXmppJingleIq::Reason::Reason()
+ : m_type(None)
+{
+}
+
+QString QXmppJingleIq::Reason::text() const
+{
+ return m_text;
+}
+
+void QXmppJingleIq::Reason::setText(const QString &text)
+{
+ m_text = text;
+}
+
+QXmppJingleIq::Reason::Type QXmppJingleIq::Reason::type() const
+{
+ return m_type;
+}
+
+void QXmppJingleIq::Reason::setType(QXmppJingleIq::Reason::Type type)
+{
+ m_type = type;
+}
+
+void QXmppJingleIq::Reason::parse(const QDomElement &element)
+{
+ m_text = element.firstChildElement("text").text();
+ for (int i = AlternativeSession; i <= UnsupportedTransports; i++)
+ {
+ if (!element.firstChildElement(jingle_reasons[i]).isNull())
+ {
+ m_type = static_cast<Type>(i);
+ break;
+ }
+ }
+}
+
+void QXmppJingleIq::Reason::toXml(QXmlStreamWriter *writer) const
+{
+ if (m_type < AlternativeSession || m_type > UnsupportedTransports)
+ return;
+
+ writer->writeStartElement("reason");
+ if (!m_text.isEmpty())
+ helperToXmlAddTextElement(writer, "text", m_text);
+ writer->writeEmptyElement(jingle_reasons[m_type]);
+ writer->writeEndElement();
+}
+
+/// Constructs a QXmppJingleIq.
+
+QXmppJingleIq::QXmppJingleIq()
+ : m_ringing(false)
+{
+}
+
+/// Returns the Jingle IQ's action.
+
+QXmppJingleIq::Action QXmppJingleIq::action() const
+{
+ return m_action;
+}
+
+/// Sets the Jingle IQ's action.
+///
+/// \param action
+
+void QXmppJingleIq::setAction(QXmppJingleIq::Action action)
+{
+ m_action = action;
+}
+
+/// Returns the session initiator.
+
+QString QXmppJingleIq::initiator() const
+{
+ return m_initiator;
+}
+
+/// Sets the session initiator.
+///
+/// \param initiator
+
+void QXmppJingleIq::setInitiator(const QString &initiator)
+{
+ m_initiator = initiator;
+}
+
+/// Returns the session responder.
+
+QString QXmppJingleIq::responder() const
+{
+ return m_responder;
+}
+
+/// Sets the session responder.
+///
+/// \param responder
+
+void QXmppJingleIq::setResponder(const QString &responder)
+{
+ m_responder = responder;
+}
+
+/// Returns the session ID.
+
+QString QXmppJingleIq::sid() const
+{
+ return m_sid;
+}
+
+/// Sets the session ID.
+///
+/// \param sid
+
+void QXmppJingleIq::setSid(const QString &sid)
+{
+ m_sid = sid;
+}
+
+bool QXmppJingleIq::isJingleIq(const QDomElement &element)
+{
+ QDomElement jingleElement = element.firstChildElement("jingle");
+ return (jingleElement.namespaceURI() == ns_jingle);
+}
+
+/// Returns true if the call is ringing.
+
+bool QXmppJingleIq::ringing() const
+{
+ return m_ringing;
+}
+
+/// Set to true if the call is ringing.
+///
+/// \param ringing
+
+void QXmppJingleIq::setRinging(bool ringing)
+{
+ m_ringing = ringing;
+}
+
+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<Action>(i);
+ break;
+ }
+ }
+ m_initiator = jingleElement.attribute("initiator");
+ m_responder = jingleElement.attribute("responder");
+ m_sid = jingleElement.attribute("sid");
+
+ // content
+ QDomElement contentElement = jingleElement.firstChildElement("content");
+ m_content.parse(contentElement);
+ QDomElement reasonElement = jingleElement.firstChildElement("reason");
+ m_reason.parse(reasonElement);
+
+ // ringing
+ QDomElement ringingElement = jingleElement.firstChildElement("ringing");
+ m_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);
+
+ // ringing
+ if (m_ringing)
+ {
+ writer->writeStartElement("ringing");
+ writer->writeAttribute("xmlns", ns_jingle_rtp_info);
+ writer->writeEndElement();
+ }
+
+ writer->writeEndElement();
+}
+
+QXmppJingleCandidate::QXmppJingleCandidate()
+ : m_component(0),
+ m_foundation(0),
+ m_generation(0),
+ m_network(0),
+ m_port(0),
+ m_priority(0),
+ m_type(HostType)
+{
+}
+
+/// Returns the candidate's component ID.
+
+int QXmppJingleCandidate::component() const
+{
+ return m_component;
+}
+
+/// Sets the candidates's component ID.
+///
+/// \param component
+
+void QXmppJingleCandidate::setComponent(int component)
+{
+ m_component = component;
+}
+
+/// Returns the candidate's foundation.
+
+int QXmppJingleCandidate::foundation() const
+{
+ return m_foundation;
+}
+
+/// Sets the candidate's foundation.
+///
+/// \param foundation
+
+void QXmppJingleCandidate::setFoundation(int foundation)
+{
+ m_foundation = foundation;
+}
+
+/// Returns the candidate's host address.
+///
+
+QHostAddress QXmppJingleCandidate::host() const
+{
+ return m_host;
+}
+
+/// Sets the candidate's host address.
+///
+/// \param host
+
+void QXmppJingleCandidate::setHost(const QHostAddress &host)
+{
+ m_host = host;
+}
+
+/// Returns the candidate's unique identifier.
+///
+
+QString QXmppJingleCandidate::id() const
+{
+ return m_id;
+}
+
+/// Sets the candidate's unique identifier.
+///
+/// \param id
+
+void QXmppJingleCandidate::setId(const QString &id)
+{
+ m_id = id;
+}
+
+/// Returns the network index (starting at 0) the candidate is on.
+///
+
+int QXmppJingleCandidate::network() const
+{
+ return m_network;
+}
+
+/// Sets the network index (starting at 0) the candidate is on.
+///
+/// \param network
+
+void QXmppJingleCandidate::setNetwork(int network)
+{
+ m_network = network;
+}
+
+/// Returns the candidate's port number.
+///
+
+quint16 QXmppJingleCandidate::port() const
+{
+ return m_port;
+}
+
+/// Sets the candidate's port number.
+///
+/// \param port
+
+void QXmppJingleCandidate::setPort(quint16 port)
+{
+ m_port = port;
+}
+
+/// Returns the candidate's priority.
+///
+
+int QXmppJingleCandidate::priority() const
+{
+ return m_priority;
+}
+
+/// Sets the candidate's priority.
+///
+/// \param priority
+
+void QXmppJingleCandidate::setPriority(int priority)
+{
+ m_priority = priority;
+}
+
+/// Returns the candidate's protocol (e.g. "udp").
+///
+
+QString QXmppJingleCandidate::protocol() const
+{
+ return m_protocol;
+}
+
+/// Sets the candidate's protocol (e.g. "udp").
+///
+/// \param protocol
+
+void QXmppJingleCandidate::setProtocol(const QString &protocol)
+{
+ m_protocol = protocol;
+}
+
+/// Returns the candidate type (e.g. "host").
+///
+
+QXmppJingleCandidate::Type QXmppJingleCandidate::type() const
+{
+ return m_type;
+}
+
+/// Sets the candidate type (e.g. "host").
+///
+/// \param type
+
+void QXmppJingleCandidate::setType(QXmppJingleCandidate::Type type)
+{
+ m_type = type;
+}
+
+/// Returns true if the host address or port are empty.
+///
+
+bool QXmppJingleCandidate::isNull() const
+{
+ return m_host.isNull() || !m_port;
+}
+
+void QXmppJingleCandidate::parse(const QDomElement &element)
+{
+ m_component = element.attribute("component").toInt();
+ m_foundation = element.attribute("foundation").toInt();
+ 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"));
+}
+
+void QXmppJingleCandidate::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("candidate");
+ helperToXmlAddAttribute(writer, "component", QString::number(m_component));
+ helperToXmlAddAttribute(writer, "foundation", QString::number(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));
+ writer->writeEndElement();
+}
+
+QXmppJingleCandidate::Type QXmppJingleCandidate::typeFromString(const QString &typeStr, bool *ok)
+{
+ QXmppJingleCandidate::Type type;
+ if (typeStr == "host")
+ type = HostType;
+ else if (typeStr == "prflx")
+ type = PeerReflexiveType;
+ else if (typeStr == "srflx")
+ type = ServerReflexiveType;
+ else if (typeStr == "relay")
+ type = RelayedType;
+ else {
+ qWarning() << "Unknown candidate type" << typeStr;
+ if (ok)
+ *ok = false;
+ return HostType;
+ }
+ if (ok)
+ *ok = true;
+ return type;
+}
+
+QString QXmppJingleCandidate::typeToString(QXmppJingleCandidate::Type type)
+{
+ QString typeStr;
+ switch (type)
+ {
+ case HostType:
+ typeStr = "host";
+ break;
+ case PeerReflexiveType:
+ typeStr = "prflx";
+ break;
+ case ServerReflexiveType:
+ typeStr = "srflx";
+ break;
+ case RelayedType:
+ typeStr = "relay";
+ break;
+ }
+ return typeStr;
+}
+
+QXmppJinglePayloadType::QXmppJinglePayloadType()
+ : m_channels(1),
+ m_clockrate(0),
+ m_id(0),
+ m_maxptime(0),
+ m_ptime(0)
+{
+}
+
+/// Returns the number of channels (e.g. 1 for mono, 2 for stereo).
+///
+
+unsigned char QXmppJinglePayloadType::channels() const
+{
+ return m_channels;
+}
+
+/// Sets the number of channels (e.g. 1 for mono, 2 for stereo).
+///
+/// \param channels
+
+void QXmppJinglePayloadType::setChannels(unsigned char channels)
+{
+ m_channels = channels;
+}
+
+/// Returns the clockrate in Hz, i.e. the number of samples per second.
+///
+
+unsigned int QXmppJinglePayloadType::clockrate() const
+{
+ return m_clockrate;
+}
+
+/// Sets the clockrate in Hz, i.e. the number of samples per second.
+///
+/// \param clockrate
+
+void QXmppJinglePayloadType::setClockrate(unsigned int clockrate)
+{
+ m_clockrate = clockrate;
+}
+
+/// Returns the payload type identifier.
+///
+
+unsigned char QXmppJinglePayloadType::id() const
+{
+ return m_id;
+}
+
+/// Sets the payload type identifier.
+///
+
+void QXmppJinglePayloadType::setId(unsigned char id)
+{
+ Q_ASSERT(id <= 127);
+ m_id = id;
+}
+
+/// Returns the maximum packet time in milliseconds.
+///
+
+unsigned int QXmppJinglePayloadType::maxptime() const
+{
+ return m_maxptime;
+}
+
+/// Sets the maximum packet type in milliseconds.
+///
+/// \param maxptime
+
+void QXmppJinglePayloadType::setMaxptime(unsigned int maxptime)
+{
+ m_maxptime = maxptime;
+}
+
+/// Returns the payload type name.
+///
+
+QString QXmppJinglePayloadType::name() const
+{
+ return m_name;
+}
+
+/// Sets the payload type name.
+///
+/// \param name
+
+void QXmppJinglePayloadType::setName(const QString &name)
+{
+ m_name = name;
+}
+
+/// Returns the payload parameters.
+
+QMap<QString,QString> QXmppJinglePayloadType::parameters() const
+{
+ return m_parameters;
+}
+
+/// Sets the payload parameters.
+
+void QXmppJinglePayloadType::setParameters(const QMap<QString, QString> &parameters)
+{
+ m_parameters = parameters;
+}
+
+/// Returns the packet time in milliseconds (20 by default).
+///
+
+unsigned int QXmppJinglePayloadType::ptime() const
+{
+ return m_ptime ? m_ptime : 20;
+}
+
+/// Sets the packet time in milliseconds (20 by default).
+///
+/// \param ptime
+
+void QXmppJinglePayloadType::setPtime(unsigned int ptime)
+{
+ m_ptime = ptime;
+}
+
+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();
+
+ QDomElement child = element.firstChildElement("parameter");
+ while (!child.isNull()) {
+ m_parameters.insert(child.attribute("name"), child.attribute("value"));
+ child = child.nextSiblingElement("parameter");
+ }
+}
+
+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()) {
+ writer->writeStartElement("parameter");
+ writer->writeAttribute("name", key);
+ writer->writeAttribute("value", m_parameters.value(key));
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+}
+
+/// Returns true if this QXmppJinglePayloadType and \a other refer to the same payload type.
+///
+/// \param other
+
+bool QXmppJinglePayloadType::operator==(const QXmppJinglePayloadType &other) const
+{
+ // FIXME : what to do with m_ptime and m_maxptime?
+ if (m_id <= 95)
+ return other.m_id == m_id && other.m_clockrate == m_clockrate;
+ else
+ return other.m_channels == m_channels &&
+ other.m_clockrate == m_clockrate &&
+ other.m_name.toLower() == m_name.toLower();
+}
diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h
new file mode 100644
index 00000000..ef5b66ec
--- /dev/null
+++ b/src/base/QXmppJingleIq.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPJINGLEIQ_H
+#define QXMPPJINGLEIQ_H
+
+#include <QHostAddress>
+
+#include "QXmppIq.h"
+
+/// \brief The QXmppJinglePayloadType class represents a payload type
+/// as specified by XEP-0167: Jingle RTP Sessions and RFC 5245.
+///
+
+class QXmppJinglePayloadType
+{
+public:
+ QXmppJinglePayloadType();
+
+ unsigned char channels() const;
+ void setChannels(unsigned char channels);
+
+ unsigned int clockrate() const;
+ void setClockrate(unsigned int clockrate);
+
+ unsigned char id() const;
+ void setId(unsigned char id);
+
+ unsigned int maxptime() const;
+ void setMaxptime(unsigned int maxptime);
+
+ QString name() const;
+ void setName(const QString &name);
+
+ QMap<QString, QString> parameters() const;
+ void setParameters(const QMap<QString, QString> &parameters);
+
+ unsigned int ptime() const;
+ void setPtime(unsigned int ptime);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ 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<QString, QString> m_parameters;
+ unsigned int m_ptime;
+};
+
+/// \brief The QXmppJingleCandidate class represents a transport candidate
+/// as specified by XEP-0176: Jingle ICE-UDP Transport Method.
+///
+
+class QXmppJingleCandidate
+{
+public:
+ /// This enum is used to describe a candidate's type.
+ enum Type
+ {
+ HostType, ///< Host candidate, a local address/port.
+ PeerReflexiveType, ///< Peer-reflexive candidate,
+ ///< the address/port as seen from the peer.
+ ServerReflexiveType, ///< Server-reflexive candidate,
+ ///< the address/port as seen by the STUN server
+ RelayedType, ///< Relayed candidate, a candidate from
+ ///< a TURN relay.
+ };
+
+ QXmppJingleCandidate();
+
+ int component() const;
+ void setComponent(int component);
+
+ int foundation() const;
+ void setFoundation(int foundation);
+
+ QHostAddress host() const;
+ void setHost(const QHostAddress &host);
+
+ QString id() const;
+ void setId(const QString &id);
+
+ int network() const;
+ void setNetwork(int network);
+
+ quint16 port() const;
+ void setPort(quint16 port);
+
+ int priority() const;
+ void setPriority(int priority);
+
+ QString protocol() const;
+ void setProtocol(const QString &protocol);
+
+ QXmppJingleCandidate::Type type() const;
+ void setType(QXmppJingleCandidate::Type);
+
+ bool isNull() const;
+
+ static QXmppJingleCandidate::Type typeFromString(const QString &typeStr, bool *ok = 0);
+ static QString typeToString(QXmppJingleCandidate::Type type);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ int m_component;
+ int 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;
+};
+
+/// \brief The QXmppJingleIq class represents an IQ used for initiating media
+/// sessions as specified by XEP-0166: Jingle.
+///
+/// \ingroup Stanzas
+
+class QXmppJingleIq : public QXmppIq
+{
+public:
+ /// This enum is used to describe a Jingle action.
+ enum Action {
+ ContentAccept,
+ ContentAdd,
+ ContentModify,
+ ContentReject,
+ ContentRemove,
+ DescriptionInfo,
+ SecurityInfo,
+ SessionAccept,
+ SessionInfo,
+ SessionInitiate,
+ SessionTerminate,
+ TransportAccept,
+ TransportInfo,
+ TransportReject,
+ TransportReplace,
+ };
+
+ /// \internal
+ ///
+ /// The QXmppJingleIq::Content class represents the "content" element of a
+ /// QXmppJingleIq.
+
+ class Content
+ {
+ public:
+ Content();
+
+ QString creator() const;
+ void setCreator(const QString &creator);
+
+ QString name() const;
+ void setName(const QString &name);
+
+ QString senders() const;
+ void setSenders(const QString &senders);
+
+ // XEP-0167: Jingle RTP Sessions
+ QString descriptionMedia() const;
+ void setDescriptionMedia(const QString &media);
+
+ void addPayloadType(const QXmppJinglePayloadType &payload);
+ QList<QXmppJinglePayloadType> payloadTypes() const;
+ void setPayloadTypes(const QList<QXmppJinglePayloadType> &payloadTypes);
+
+ void addTransportCandidate(const QXmppJingleCandidate &candidate);
+ QList<QXmppJingleCandidate> transportCandidates() const;
+
+ QString transportUser() const;
+ void setTransportUser(const QString &user);
+
+ QString transportPassword() const;
+ void setTransportPassword(const QString &password);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ private:
+ QString m_creator;
+ QString m_disposition;
+ QString m_name;
+ QString m_senders;
+
+ QString m_descriptionMedia;
+ QString m_descriptionType;
+ QString m_transportType;
+ QString m_transportUser;
+ QString m_transportPassword;
+ QList<QXmppJinglePayloadType> m_payloadTypes;
+ QList<QXmppJingleCandidate> m_transportCandidates;
+ };
+
+ /// \internal
+ ///
+ /// The QXmppJingleIq::Reason class represents the "reason" element of a
+ /// QXmppJingleIq.
+
+ class Reason
+ {
+ public:
+ enum Type {
+ None,
+ AlternativeSession,
+ Busy,
+ Cancel,
+ ConnectivityError,
+ Decline,
+ Expired,
+ FailedApplication,
+ FailedTransport,
+ GeneralError,
+ Gone,
+ IncompatibleParameters,
+ MediaError,
+ SecurityError,
+ Success,
+ Timeout,
+ UnsupportedApplications,
+ UnsupportedTransports,
+ };
+
+ Reason();
+
+ QString text() const;
+ void setText(const QString &text);
+
+ Type type() const;
+ void setType(Type type);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ private:
+ QString m_text;
+ Type m_type;
+ };
+
+ QXmppJingleIq();
+
+ Action action() const;
+ void setAction(Action action);
+
+ QString initiator() const;
+ void setInitiator(const QString &initiator);
+
+ 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);
+
+ /// \cond
+ static bool isJingleIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ Action m_action;
+ QString m_initiator;
+ QString m_responder;
+ QString m_sid;
+
+ Content m_content;
+ Reason m_reason;
+ bool m_ringing;
+};
+
+#endif
diff --git a/src/base/QXmppLogger.cpp b/src/base/QXmppLogger.cpp
new file mode 100644
index 00000000..107f72d9
--- /dev/null
+++ b/src/base/QXmppLogger.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 <iostream>
+
+#include <QChildEvent>
+#include <QDateTime>
+#include <QFile>
+#include <QMetaType>
+#include <QTextStream>
+
+#include "QXmppLogger.h"
+
+QXmppLogger* QXmppLogger::m_logger = 0;
+
+static const char *typeName(QXmppLogger::MessageType type)
+{
+ switch (type)
+ {
+ case QXmppLogger::DebugMessage:
+ return "DEBUG";
+ case QXmppLogger::InformationMessage:
+ return "INFO";
+ case QXmppLogger::WarningMessage:
+ return "WARNING";
+ case QXmppLogger::ReceivedMessage:
+ return "RECEIVED";
+ case QXmppLogger::SentMessage:
+ return "SENT";
+ default:
+ return "";
+ }
+}
+
+static QString formatted(QXmppLogger::MessageType type, const QString& text)
+{
+ return QDateTime::currentDateTime().toString() + " " +
+ QString::fromLatin1(typeName(type)) + " " +
+ text;
+}
+
+/// Constructs a new QXmppLoggable.
+///
+/// \param parent
+
+QXmppLoggable::QXmppLoggable(QObject *parent)
+ : QObject(parent)
+{
+ QXmppLoggable *logParent = qobject_cast<QXmppLoggable*>(parent);
+ if (logParent) {
+ connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
+ logParent, SIGNAL(logMessage(QXmppLogger::MessageType,QString)));
+ }
+}
+
+void QXmppLoggable::childEvent(QChildEvent *event)
+{
+ QXmppLoggable *child = qobject_cast<QXmppLoggable*>(event->child());
+ if (!child)
+ return;
+
+ if (event->added()) {
+ connect(child, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
+ this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)));
+ } else if (event->removed()) {
+ disconnect(child, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
+ this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)));
+ }
+}
+
+class QXmppLoggerPrivate
+{
+public:
+ QXmppLoggerPrivate(QXmppLogger *qq);
+
+ QXmppLogger::LoggingType loggingType;
+ QFile *logFile;
+ QString logFilePath;
+ QXmppLogger::MessageTypes messageTypes;
+
+private:
+ QXmppLogger *q;
+};
+
+QXmppLoggerPrivate::QXmppLoggerPrivate(QXmppLogger *qq)
+ : loggingType(QXmppLogger::NoLogging),
+ logFile(0),
+ logFilePath("QXmppClientLog.log"),
+ messageTypes(QXmppLogger::AnyMessage),
+ q(qq)
+{
+}
+
+/// Constructs a new QXmppLogger.
+///
+/// \param parent
+
+QXmppLogger::QXmppLogger(QObject *parent)
+ : QObject(parent)
+{
+ d = new QXmppLoggerPrivate(this);
+
+ // make it possible to pass QXmppLogger::MessageType between threads
+ qRegisterMetaType< QXmppLogger::MessageType >("QXmppLogger::MessageType");
+}
+
+QXmppLogger::~QXmppLogger()
+{
+ delete d;
+}
+
+/// Returns the default logger.
+///
+
+QXmppLogger* QXmppLogger::getLogger()
+{
+ if(!m_logger)
+ m_logger = new QXmppLogger();
+
+ return m_logger;
+}
+
+/// Returns the handler for logging messages.
+///
+
+QXmppLogger::LoggingType QXmppLogger::loggingType()
+{
+ return d->loggingType;
+}
+
+/// Sets the handler for logging messages.
+///
+/// \param type
+
+void QXmppLogger::setLoggingType(QXmppLogger::LoggingType type)
+{
+ if (d->loggingType != type) {
+ d->loggingType = type;
+ reopen();
+ }
+}
+
+/// Returns the types of messages to log.
+///
+
+QXmppLogger::MessageTypes QXmppLogger::messageTypes()
+{
+ return d->messageTypes;
+}
+
+/// Sets the types of messages to log.
+///
+/// \param types
+
+void QXmppLogger::setMessageTypes(QXmppLogger::MessageTypes types)
+{
+ d->messageTypes = types;
+}
+
+/// Add a logging message.
+///
+/// \param type
+/// \param text
+
+void QXmppLogger::log(QXmppLogger::MessageType type, const QString& text)
+{
+ // filter messages
+ if (!d->messageTypes.testFlag(type))
+ return;
+
+ switch(d->loggingType)
+ {
+ case QXmppLogger::FileLogging:
+ if (!d->logFile) {
+ d->logFile = new QFile(d->logFilePath);
+ d->logFile->open(QIODevice::WriteOnly | QIODevice::Append);
+ }
+ QTextStream(d->logFile) << formatted(type, text) << "\n";
+ break;
+ case QXmppLogger::StdoutLogging:
+ std::cout << qPrintable(formatted(type, text)) << std::endl;
+ break;
+ case QXmppLogger::SignalLogging:
+ emit message(type, text);
+ break;
+ default:
+ break;
+ }
+}
+
+/// Returns the path to which logging messages should be written.
+///
+/// \sa loggingType()
+
+QString QXmppLogger::logFilePath()
+{
+ return d->logFilePath;
+}
+
+/// Sets the path to which logging messages should be written.
+///
+/// \param path
+///
+/// \sa setLoggingType()
+
+void QXmppLogger::setLogFilePath(const QString &path)
+{
+ if (d->logFilePath != path) {
+ d->logFilePath = path;
+ reopen();
+ }
+}
+
+/// If logging to a file, causes the file to be re-opened.
+///
+
+void QXmppLogger::reopen()
+{
+ if (d->logFile) {
+ delete d->logFile;
+ d->logFile = 0;
+ }
+}
+
diff --git a/src/base/QXmppLogger.h b/src/base/QXmppLogger.h
new file mode 100644
index 00000000..93de33f7
--- /dev/null
+++ b/src/base/QXmppLogger.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPLOGGER_H
+#define QXMPPLOGGER_H
+
+#include <QObject>
+
+#ifdef QXMPP_LOGGABLE_TRACE
+#define qxmpp_loggable_trace(x) QString("%1(0x%2) %3").arg(metaObject()->className(), QString::number(reinterpret_cast<qint64>(this), 16), x)
+#else
+#define qxmpp_loggable_trace(x) (x)
+#endif
+
+class QXmppLoggerPrivate;
+
+/// \brief The QXmppLogger class represents a sink for logging messages.
+///
+/// \ingroup Core
+
+class QXmppLogger : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(LoggingType)
+ Q_FLAGS(MessageType MessageTypes)
+ Q_PROPERTY(QString logFilePath READ logFilePath WRITE setLogFilePath)
+ Q_PROPERTY(LoggingType loggingType READ loggingType WRITE setLoggingType)
+ Q_PROPERTY(MessageTypes messageTypes READ messageTypes WRITE setMessageTypes)
+
+public:
+ /// This enum describes how log message are handled.
+ enum LoggingType
+ {
+ NoLogging = 0, ///< Log messages are discarded
+ FileLogging = 1, ///< Log messages are written to a file
+ StdoutLogging = 2, ///< Log messages are written to the standard output
+ SignalLogging = 4, ///< Log messages are emitted as a signal
+ };
+
+ /// This enum describes a type of log message.
+ enum MessageType
+ {
+ NoMessage = 0, ///< No message type
+ DebugMessage = 1, ///< Debugging message
+ InformationMessage = 2, ///< Informational message
+ WarningMessage = 4, ///< Warning message
+ ReceivedMessage = 8, ///< Message received from server
+ SentMessage = 16, ///< Message sent to server
+ AnyMessage = 31, ///< Any message type
+ };
+ Q_DECLARE_FLAGS(MessageTypes, MessageType)
+
+ QXmppLogger(QObject *parent = 0);
+ ~QXmppLogger();
+
+ static QXmppLogger* getLogger();
+
+ QXmppLogger::LoggingType loggingType();
+ void setLoggingType(QXmppLogger::LoggingType type);
+
+ QString logFilePath();
+ void setLogFilePath(const QString &path);
+
+ QXmppLogger::MessageTypes messageTypes();
+ void setMessageTypes(QXmppLogger::MessageTypes types);
+
+public slots:
+ void log(QXmppLogger::MessageType type, const QString& text);
+ void reopen();
+
+signals:
+ /// This signal is emitted whenever a log message is received.
+ void message(QXmppLogger::MessageType type, const QString &text);
+
+private:
+ static QXmppLogger* m_logger;
+ QXmppLoggerPrivate *d;
+};
+
+/// \brief The QXmppLoggable class represents a source of logging messages.
+///
+/// \ingroup Core
+
+class QXmppLoggable : public QObject
+{
+ Q_OBJECT
+
+public:
+ QXmppLoggable(QObject *parent = 0);
+
+protected:
+ /// \cond
+ virtual void childEvent(QChildEvent *event);
+ /// \endcond
+
+ /// Logs a debugging message.
+ ///
+ /// \param message
+
+ void debug(const QString &message)
+ {
+ emit logMessage(QXmppLogger::DebugMessage, qxmpp_loggable_trace(message));
+ }
+
+ /// Logs an informational message.
+ ///
+ /// \param message
+
+ void info(const QString &message)
+ {
+ emit logMessage(QXmppLogger::InformationMessage, qxmpp_loggable_trace(message));
+ }
+
+ /// Logs a warning message.
+ ///
+ /// \param message
+
+ void warning(const QString &message)
+ {
+ emit logMessage(QXmppLogger::WarningMessage, qxmpp_loggable_trace(message));
+ }
+
+ /// Logs a received packet.
+ ///
+ /// \param message
+
+ void logReceived(const QString &message)
+ {
+ emit logMessage(QXmppLogger::ReceivedMessage, qxmpp_loggable_trace(message));
+ }
+
+ /// Logs a sent packet.
+ ///
+ /// \param message
+
+ void logSent(const QString &message)
+ {
+ emit logMessage(QXmppLogger::SentMessage, qxmpp_loggable_trace(message));
+ }
+
+signals:
+ /// This signal is emitted to send logging messages.
+ void logMessage(QXmppLogger::MessageType type, const QString &msg);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QXmppLogger::MessageTypes)
+#endif // QXMPPLOGGER_H
diff --git a/src/base/QXmppMessage.cpp b/src/base/QXmppMessage.cpp
new file mode 100644
index 00000000..109c0d84
--- /dev/null
+++ b/src/base/QXmppMessage.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+#include <QXmlStreamWriter>
+
+#include "QXmppConstants.h"
+#include "QXmppMessage.h"
+#include "QXmppUtils.h"
+
+static const char* chat_states[] = {
+ "",
+ "active",
+ "inactive",
+ "gone",
+ "composing",
+ "paused",
+};
+
+/// Constructs a QXmppMessage.
+///
+/// \param from
+/// \param to
+/// \param body
+/// \param thread
+
+QXmppMessage::QXmppMessage(const QString& from, const QString& to, const
+ QString& body, const QString& thread)
+ : QXmppStanza(from, to),
+ m_type(Chat),
+ m_stampType(QXmppMessage::DelayedDelivery),
+ m_state(None),
+ m_attentionRequested(false),
+ m_body(body),
+ m_thread(thread),
+ m_receiptRequested(false)
+{
+}
+
+QXmppMessage::~QXmppMessage()
+{
+
+}
+
+/// Returns the message's body.
+///
+
+QString QXmppMessage::body() const
+{
+ return m_body;
+}
+
+/// Sets the message's body.
+///
+/// \param body
+
+void QXmppMessage::setBody(const QString& body)
+{
+ m_body = body;
+}
+
+/// Returns true if the user's attention is requested, as defined
+/// by XEP-0224: Attention.
+
+bool QXmppMessage::isAttentionRequested() const
+{
+ return m_attentionRequested;
+}
+
+/// Sets whether the user's attention is requested, as defined
+/// by XEP-0224: Attention.
+///
+/// \a param requested
+
+void QXmppMessage::setAttentionRequested(bool requested)
+{
+ m_attentionRequested = requested;
+}
+
+/// Returns true if a delivery receipt is requested, as defined
+/// by XEP-0184: Message Delivery Receipts.
+
+bool QXmppMessage::isReceiptRequested() const
+{
+ return m_receiptRequested;
+}
+
+/// Sets whether a delivery receipt is requested, as defined
+/// by XEP-0184: Message Delivery Receipts.
+///
+/// \a param requested
+
+void QXmppMessage::setReceiptRequested(bool requested)
+{
+ m_receiptRequested = requested;
+ if (requested && id().isEmpty())
+ generateAndSetNextId();
+}
+
+/// If this message is a delivery receipt, returns the ID of the
+/// original message.
+
+QString QXmppMessage::receiptId() const
+{
+ return m_receiptId;
+}
+
+/// Make this message a delivery receipt for the message with
+/// the given \a id.
+
+void QXmppMessage::setReceiptId(const QString &id)
+{
+ m_receiptId = id;
+}
+
+/// Returns the message's type.
+///
+
+QXmppMessage::Type QXmppMessage::type() const
+{
+ return m_type;
+}
+
+QString QXmppMessage::getTypeStr() const
+{
+ switch(m_type)
+ {
+ case QXmppMessage::Error:
+ return "error";
+ case QXmppMessage::Normal:
+ return "normal";
+ case QXmppMessage::Chat:
+ return "chat";
+ case QXmppMessage::GroupChat:
+ return "groupchat";
+ case QXmppMessage::Headline:
+ return "headline";
+ default:
+ qWarning("QXmppMessage::getTypeStr() invalid type %d", (int)m_type);
+ return "";
+ }
+}
+
+/// Sets the message's type.
+///
+/// \param type
+
+void QXmppMessage::setType(QXmppMessage::Type type)
+{
+ m_type = type;
+}
+
+void QXmppMessage::setTypeFromStr(const QString& str)
+{
+ if(str == "error")
+ {
+ setType(QXmppMessage::Error);
+ return;
+ }
+ else if(str == "") // if no type is specified
+ {
+ setType(QXmppMessage::Normal);
+ return;
+ }
+ else if(str == "normal")
+ {
+ setType(QXmppMessage::Normal);
+ return;
+ }
+ else if(str == "chat")
+ {
+ setType(QXmppMessage::Chat);
+ return;
+ }
+ else if(str == "groupchat")
+ {
+ setType(QXmppMessage::GroupChat);
+ return;
+ }
+ else if(str == "headline")
+ {
+ setType(QXmppMessage::Headline);
+ return;
+ }
+ else
+ {
+ setType(static_cast<QXmppMessage::Type>(-1));
+ qWarning("QXmppMessage::setTypeFromStr() invalid input string type: %s",
+ qPrintable(str));
+ return;
+ }
+}
+
+/// Returns the message's timestamp (if any).
+
+QDateTime QXmppMessage::stamp() const
+{
+ return m_stamp;
+}
+
+/// Sets the message's timestamp.
+///
+/// \param stamp
+
+void QXmppMessage::setStamp(const QDateTime &stamp)
+{
+ m_stamp = stamp;
+}
+
+/// Returns the message's chat state.
+///
+
+QXmppMessage::State QXmppMessage::state() const
+{
+ return m_state;
+}
+
+/// Sets the message's chat state.
+///
+/// \param state
+
+void QXmppMessage::setState(QXmppMessage::State state)
+{
+ m_state = state;
+}
+
+void QXmppMessage::parse(const QDomElement &element)
+{
+ QXmppStanza::parse(element);
+
+ setTypeFromStr(element.attribute("type"));
+ m_body = element.firstChildElement("body").text();
+ m_subject = element.firstChildElement("subject").text();
+ m_thread = element.firstChildElement("thread").text();
+
+ // chat states
+ for (int i = Active; i <= Paused; i++)
+ {
+ QDomElement stateElement = element.firstChildElement(chat_states[i]);
+ if (!stateElement.isNull() &&
+ stateElement.namespaceURI() == ns_chat_states)
+ {
+ m_state = static_cast<QXmppMessage::State>(i);
+ break;
+ }
+ }
+
+ // XEP-0184: Message Delivery Receipts
+ QDomElement receivedElement = element.firstChildElement("received");
+ if (!receivedElement.isNull() && receivedElement.namespaceURI() == ns_message_receipts) {
+ m_receiptId = receivedElement.attribute("id");
+
+ // compatibility with old-style XEP
+ if (m_receiptId.isEmpty())
+ m_receiptId = id();
+ } else {
+ m_receiptId = QString();
+ }
+ m_receiptRequested = element.firstChildElement("request").namespaceURI() == ns_message_receipts;
+
+ // XEP-0203: Delayed Delivery
+ QDomElement delayElement = element.firstChildElement("delay");
+ if (!delayElement.isNull() && delayElement.namespaceURI() == ns_delayed_delivery)
+ {
+ const QString str = delayElement.attribute("stamp");
+ m_stamp = datetimeFromString(str);
+ m_stampType = QXmppMessage::DelayedDelivery;
+ }
+
+ // XEP-0224: Attention
+ m_attentionRequested = element.firstChildElement("attention").namespaceURI() == ns_attention;
+
+ QXmppElementList extensions;
+ QDomElement xElement = element.firstChildElement("x");
+ while (!xElement.isNull())
+ {
+ if (xElement.namespaceURI() == ns_legacy_delayed_delivery)
+ {
+ // XEP-0091: Legacy Delayed Delivery
+ const QString str = xElement.attribute("stamp");
+ m_stamp = QDateTime::fromString(str, "yyyyMMddThh:mm:ss");
+ m_stamp.setTimeSpec(Qt::UTC);
+ m_stampType = QXmppMessage::LegacyDelayedDelivery;
+ } else {
+ // other extensions
+ extensions << QXmppElement(xElement);
+ }
+ xElement = xElement.nextSiblingElement("x");
+ }
+ setExtensions(extensions);
+}
+
+void QXmppMessage::toXml(QXmlStreamWriter *xmlWriter) const
+{
+
+ xmlWriter->writeStartElement("message");
+ helperToXmlAddAttribute(xmlWriter, "xml:lang", lang());
+ helperToXmlAddAttribute(xmlWriter, "id", id());
+ helperToXmlAddAttribute(xmlWriter, "to", to());
+ helperToXmlAddAttribute(xmlWriter, "from", from());
+ helperToXmlAddAttribute(xmlWriter, "type", getTypeStr());
+ if (!m_subject.isEmpty())
+ helperToXmlAddTextElement(xmlWriter, "subject", m_subject);
+ if (!m_body.isEmpty())
+ helperToXmlAddTextElement(xmlWriter, "body", m_body);
+ if (!m_thread.isEmpty())
+ helperToXmlAddTextElement(xmlWriter, "thread", m_thread);
+ error().toXml(xmlWriter);
+
+ // chat states
+ if (m_state > None && m_state <= Paused)
+ {
+ xmlWriter->writeStartElement(chat_states[m_state]);
+ xmlWriter->writeAttribute("xmlns", ns_chat_states);
+ xmlWriter->writeEndElement();
+ }
+
+ // time stamp
+ if (m_stamp.isValid())
+ {
+ QDateTime utcStamp = m_stamp.toUTC();
+ if (m_stampType == QXmppMessage::DelayedDelivery)
+ {
+ // XEP-0203: Delayed Delivery
+ xmlWriter->writeStartElement("delay");
+ xmlWriter->writeAttribute("xmlns", ns_delayed_delivery);
+ helperToXmlAddAttribute(xmlWriter, "stamp", datetimeToString(utcStamp));
+ xmlWriter->writeEndElement();
+ } else {
+ // XEP-0091: Legacy Delayed Delivery
+ xmlWriter->writeStartElement("x");
+ xmlWriter->writeAttribute("xmlns", ns_legacy_delayed_delivery);
+ helperToXmlAddAttribute(xmlWriter, "stamp", utcStamp.toString("yyyyMMddThh:mm:ss"));
+ xmlWriter->writeEndElement();
+ }
+ }
+
+ // XEP-0184: Message Delivery Receipts
+ if (!m_receiptId.isEmpty()) {
+ xmlWriter->writeStartElement("received");
+ xmlWriter->writeAttribute("xmlns", ns_message_receipts);
+ xmlWriter->writeAttribute("id", m_receiptId);
+ xmlWriter->writeEndElement();
+ }
+ if (m_receiptRequested) {
+ xmlWriter->writeStartElement("request");
+ xmlWriter->writeAttribute("xmlns", ns_message_receipts);
+ xmlWriter->writeEndElement();
+ }
+
+ // XEP-0224: Attention
+ if (m_attentionRequested) {
+ xmlWriter->writeStartElement("attention");
+ xmlWriter->writeAttribute("xmlns", ns_attention);
+ xmlWriter->writeEndElement();
+ }
+
+ // other extensions
+ foreach (const QXmppElement &extension, extensions())
+ extension.toXml(xmlWriter);
+ xmlWriter->writeEndElement();
+}
+
+/// Returns the message's subject.
+///
+
+QString QXmppMessage::subject() const
+{
+ return m_subject;
+}
+
+/// Sets the message's subject.
+///
+/// \param subject
+
+void QXmppMessage::setSubject(const QString& subject)
+{
+ m_subject = subject;
+}
+
+/// Returns the message's thread.
+
+QString QXmppMessage::thread() const
+{
+ return m_thread;
+}
+
+/// Sets the message's thread.
+///
+/// \param thread
+
+void QXmppMessage::setThread(const QString& thread)
+{
+ m_thread = thread;
+}
+
diff --git a/src/base/QXmppMessage.h b/src/base/QXmppMessage.h
new file mode 100644
index 00000000..a981f1db
--- /dev/null
+++ b/src/base/QXmppMessage.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPMESSAGE_H
+#define QXMPPMESSAGE_H
+
+#include <QDateTime>
+#include "QXmppStanza.h"
+
+/// \brief The QXmppMessage class represents an XMPP message.
+///
+/// \ingroup Stanzas
+///
+
+class QXmppMessage : public QXmppStanza
+{
+public:
+ /// This enum described a message type.
+ enum Type
+ {
+ Error = 0,
+ Normal,
+ Chat,
+ GroupChat,
+ Headline
+ };
+
+ /// This enum describes a chat state as defined by
+ /// XEP-0085 : Chat State Notifications.
+ enum State
+ {
+ None = 0, ///< The message does not contain any chat state information.
+ Active, ///< User is actively participating in the chat session.
+ Inactive, ///< User has not been actively participating in the chat session.
+ Gone, ///< User has effectively ended their participation in the chat session.
+ Composing, ///< User is composing a message.
+ Paused, ///< User had been composing but now has stopped.
+ };
+
+ QXmppMessage(const QString& from = "", const QString& to = "",
+ const QString& body = "", const QString& thread = "");
+ ~QXmppMessage();
+
+ QString body() const;
+ void setBody(const QString&);
+
+ bool isAttentionRequested() const;
+ void setAttentionRequested(bool requested);
+
+ bool isReceiptRequested() const;
+ void setReceiptRequested(bool requested);
+
+ QString receiptId() const;
+ void setReceiptId(const QString &id);
+
+ QDateTime stamp() const;
+ void setStamp(const QDateTime &stamp);
+
+ QXmppMessage::State state() const;
+ void setState(QXmppMessage::State);
+
+ QString subject() const;
+ void setSubject(const QString&);
+
+ QString thread() const;
+ void setThread(const QString&);
+
+ QXmppMessage::Type type() const;
+ void setType(QXmppMessage::Type);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ /// This enum describe a type of message timestamp.
+ enum StampType
+ {
+ LegacyDelayedDelivery, ///< XEP-0091: Legacy Delayed Delivery
+ DelayedDelivery, ///< XEP-0203: Delayed Delivery
+ };
+
+ QString getTypeStr() const;
+ void setTypeFromStr(const QString&);
+
+ Type m_type;
+ QDateTime m_stamp;
+ StampType m_stampType;
+ State m_state;
+
+ bool m_attentionRequested;
+ QString m_body;
+ QString m_subject;
+ QString m_thread;
+
+ // Request message receipt as per XEP-0184.
+ QString m_receiptId;
+ bool m_receiptRequested;
+};
+
+#endif // QXMPPMESSAGE_H
diff --git a/src/base/QXmppMucIq.cpp b/src/base/QXmppMucIq.cpp
new file mode 100644
index 00000000..97328a2f
--- /dev/null
+++ b/src/base/QXmppMucIq.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppMucIq.h"
+#include "QXmppUtils.h"
+
+QXmppMucItem::QXmppMucItem()
+ : m_affiliation(QXmppMucItem::UnspecifiedAffiliation),
+ m_role(QXmppMucItem::UnspecifiedRole)
+{
+}
+
+bool QXmppMucItem::isNull() const
+{
+ return m_actor.isEmpty() &&
+ m_affiliation == UnspecifiedAffiliation &&
+ m_jid.isEmpty() &&
+ m_nick.isEmpty() &&
+ m_reason.isEmpty() &&
+ m_role == UnspecifiedRole;
+}
+
+QString QXmppMucItem::actor() const
+{
+ return m_actor;
+}
+
+void QXmppMucItem::setActor(const QString &actor)
+{
+ m_actor = actor;
+}
+
+/// Returns the user's affiliation, i.e. long-lived permissions.
+
+QXmppMucItem::Affiliation QXmppMucItem::affiliation() const
+{
+ return m_affiliation;
+}
+
+QXmppMucItem::Affiliation QXmppMucItem::affiliationFromString(const QString &affiliationStr)
+{
+ if (affiliationStr == "owner")
+ return QXmppMucItem::OwnerAffiliation;
+ else if (affiliationStr == "admin")
+ return QXmppMucItem::AdminAffiliation;
+ else if (affiliationStr == "member")
+ return QXmppMucItem::MemberAffiliation;
+ else if (affiliationStr == "outcast")
+ return QXmppMucItem::OutcastAffiliation;
+ else if (affiliationStr == "none")
+ return QXmppMucItem::NoAffiliation;
+ else
+ return QXmppMucItem::UnspecifiedAffiliation;
+}
+
+QString QXmppMucItem::affiliationToString(Affiliation affiliation)
+{
+ switch (affiliation) {
+ case QXmppMucItem::OwnerAffiliation:
+ return "owner";
+ case QXmppMucItem::AdminAffiliation:
+ return "admin";
+ case QXmppMucItem::MemberAffiliation:
+ return "member";
+ case QXmppMucItem::OutcastAffiliation:
+ return "outcast";
+ case QXmppMucItem::NoAffiliation:
+ return "none";
+ default:
+ return QString();
+ }
+}
+
+/// Sets the user's affiliation, i.e. long-lived permissions.
+///
+/// \param affiliation
+
+void QXmppMucItem::setAffiliation(Affiliation affiliation)
+{
+ m_affiliation = affiliation;
+}
+
+/// Returns the user's real JID.
+
+QString QXmppMucItem::jid() const
+{
+ return m_jid;
+}
+
+/// Sets the user's real JID.
+///
+/// \param jid
+
+void QXmppMucItem::setJid(const QString &jid)
+{
+ m_jid = jid;
+}
+
+/// Returns the user's nickname.
+
+QString QXmppMucItem::nick() const
+{
+ return m_nick;
+}
+
+/// Sets the user's nickname.
+///
+/// \param nick
+
+void QXmppMucItem::setNick(const QString &nick)
+{
+ m_nick = nick;
+}
+
+QString QXmppMucItem::reason() const
+{
+ return m_reason;
+}
+
+void QXmppMucItem::setReason(const QString &reason)
+{
+ m_reason = reason;
+}
+
+/// Returns the user's role, i.e. short-lived permissions.
+
+QXmppMucItem::Role QXmppMucItem::role() const
+{
+ return m_role;
+}
+
+QXmppMucItem::Role QXmppMucItem::roleFromString(const QString &roleStr)
+{
+ if (roleStr == "moderator")
+ return QXmppMucItem::ModeratorRole;
+ else if (roleStr == "participant")
+ return QXmppMucItem::ParticipantRole;
+ else if (roleStr == "visitor")
+ return QXmppMucItem::VisitorRole;
+ else if (roleStr == "none")
+ return QXmppMucItem::NoRole;
+ else
+ return QXmppMucItem::UnspecifiedRole;
+}
+
+QString QXmppMucItem::roleToString(Role role)
+{
+ switch (role) {
+ case QXmppMucItem::ModeratorRole:
+ return "moderator";
+ case QXmppMucItem::ParticipantRole:
+ return "participant";
+ case QXmppMucItem::VisitorRole:
+ return "visitor";
+ case QXmppMucItem::NoRole:
+ return "none";
+ default:
+ return QString();
+ }
+}
+
+/// Sets the user's role, i.e. short-lived permissions.
+///
+/// \param role
+
+void QXmppMucItem::setRole(Role role)
+{
+ m_role = role;
+}
+
+void QXmppMucItem::parse(const QDomElement &element)
+{
+ m_affiliation = QXmppMucItem::affiliationFromString(element.attribute("affiliation").toLower());
+ m_jid = element.attribute("jid");
+ m_nick = element.attribute("nick");
+ m_role = QXmppMucItem::roleFromString(element.attribute("role").toLower());
+ m_actor = element.firstChildElement("actor").attribute("jid");
+ m_reason = element.firstChildElement("reason").text();
+}
+
+void QXmppMucItem::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("item");
+ helperToXmlAddAttribute(writer, "affiliation", affiliationToString(m_affiliation));
+ helperToXmlAddAttribute(writer, "jid", m_jid);
+ helperToXmlAddAttribute(writer, "nick", m_nick);
+ helperToXmlAddAttribute(writer, "role", roleToString(m_role));
+ if (!m_actor.isEmpty()) {
+ writer->writeStartElement("actor");
+ helperToXmlAddAttribute(writer, "jid", m_actor);
+ writer->writeEndElement();
+ }
+ if (!m_reason.isEmpty())
+ helperToXmlAddTextElement(writer, "reason", m_reason);
+ writer->writeEndElement();
+}
+
+/// Returns the IQ's items.
+
+QList<QXmppMucItem> QXmppMucAdminIq::items() const
+{
+ return m_items;
+}
+
+/// Sets the IQ's items.
+///
+/// \param items
+
+void QXmppMucAdminIq::setItems(const QList<QXmppMucItem> &items)
+{
+ m_items = items;
+}
+
+bool QXmppMucAdminIq::isMucAdminIq(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ return (queryElement.namespaceURI() == ns_muc_admin);
+}
+
+void QXmppMucAdminIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ QDomElement child = queryElement.firstChildElement("item");
+ while (!child.isNull())
+ {
+ QXmppMucItem item;
+ item.parse(child);
+ m_items << item;
+ child = child.nextSiblingElement("item");
+ }
+}
+
+void QXmppMucAdminIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute("xmlns", ns_muc_admin);
+ foreach (const QXmppMucItem &item, m_items)
+ item.toXml(writer);
+ writer->writeEndElement();
+}
+
+/// Returns the IQ's data form.
+
+QXmppDataForm QXmppMucOwnerIq::form() const
+{
+ return m_form;
+}
+
+/// Sets the IQ's data form.
+///
+/// \param form
+
+void QXmppMucOwnerIq::setForm(const QXmppDataForm &form)
+{
+ m_form = form;
+}
+
+bool QXmppMucOwnerIq::isMucOwnerIq(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ return (queryElement.namespaceURI() == ns_muc_owner);
+}
+
+void QXmppMucOwnerIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ m_form.parse(queryElement.firstChildElement("x"));
+}
+
+void QXmppMucOwnerIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute("xmlns", ns_muc_owner);
+ m_form.toXml(writer);
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppMucIq.h b/src/base/QXmppMucIq.h
new file mode 100644
index 00000000..5e73fc9b
--- /dev/null
+++ b/src/base/QXmppMucIq.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPMUCIQ_H
+#define QXMPPMUCIQ_H
+
+#include "QXmppDataForm.h"
+#include "QXmppIq.h"
+
+/// \brief The QXmppMucItem class represents a chat room "item".
+///
+/// It is used to convey information such as permissions.
+///
+/// \ingroup Stanzas
+
+class QXmppMucItem
+{
+public:
+ /// This enum is used to represent long-lived permissions in a room (affiliations).
+ enum Affiliation {
+ UnspecifiedAffiliation,
+ OutcastAffiliation,
+ NoAffiliation,
+ MemberAffiliation,
+ AdminAffiliation,
+ OwnerAffiliation,
+ };
+
+ /// This enum is used to represent short-lived permissions in a room (roles).
+ enum Role {
+ UnspecifiedRole,
+ NoRole,
+ VisitorRole,
+ ParticipantRole,
+ ModeratorRole,
+ };
+
+ QXmppMucItem();
+ bool isNull() const;
+
+ QString actor() const;
+ void setActor(const QString &actor);
+
+ Affiliation affiliation() const;
+ void setAffiliation(Affiliation affiliation);
+
+ QString jid() const;
+ void setJid(const QString &jid);
+
+ QString nick() const;
+ void setNick(const QString &nick);
+
+ QString reason() const;
+ void setReason(const QString &reason);
+
+ Role role() const;
+ void setRole(Role role);
+
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+
+ /// \cond
+ static Affiliation affiliationFromString(const QString &affiliationStr);
+ static QString affiliationToString(Affiliation affiliation);
+ static Role roleFromString(const QString &roleStr);
+ static QString roleToString(Role role);
+ /// \endcond
+private:
+ QString m_actor;
+ Affiliation m_affiliation;
+ QString m_jid;
+ QString m_nick;
+ QString m_reason;
+ Role m_role;
+};
+
+/// \brief The QXmppMucAdminIq class represents a chat room administration IQ
+/// as defined by XEP-0045: Multi-User Chat.
+///
+/// It is used to get or modify room memberships.
+///
+/// \ingroup Stanzas
+
+class QXmppMucAdminIq : public QXmppIq
+{
+public:
+ QList<QXmppMucItem> items() const;
+ void setItems(const QList<QXmppMucItem> &items);
+
+ /// \cond
+ static bool isMucAdminIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QList<QXmppMucItem> m_items;
+};
+
+/// \brief The QXmppMucOwnerIq class represents a chat room configuration IQ as
+/// defined by XEP-0045: Multi-User Chat.
+///
+/// It is used to get or modify room configuration options.
+///
+/// \sa QXmppDataForm
+///
+
+class QXmppMucOwnerIq : public QXmppIq
+{
+public:
+ QXmppDataForm form() const;
+ void setForm(const QXmppDataForm &form);
+
+ /// \cond
+ static bool isMucOwnerIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QXmppDataForm m_form;
+};
+
+#endif
diff --git a/src/base/QXmppNonSASLAuth.cpp b/src/base/QXmppNonSASLAuth.cpp
new file mode 100644
index 00000000..0dea6184
--- /dev/null
+++ b/src/base/QXmppNonSASLAuth.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/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 <QCryptographicHash>
+#include <QDomElement>
+#include <QXmlStreamWriter>
+
+#include "QXmppConstants.h"
+#include "QXmppNonSASLAuth.h"
+#include "QXmppUtils.h"
+
+QXmppNonSASLAuthIq::QXmppNonSASLAuthIq()
+ : QXmppIq(QXmppIq::Set)
+{
+}
+
+bool QXmppNonSASLAuthIq::isNonSASLAuthIq(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ return queryElement.namespaceURI() == ns_auth;
+}
+
+void QXmppNonSASLAuthIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ m_username = queryElement.firstChildElement("username").text();
+ m_password = queryElement.firstChildElement("password").text();
+ m_digest = QByteArray::fromHex(queryElement.firstChildElement("digest").text().toAscii());
+ m_resource = queryElement.firstChildElement("resource").text();
+}
+
+void QXmppNonSASLAuthIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute("xmlns", ns_auth);
+ if (!m_username.isEmpty())
+ writer->writeTextElement("username", m_username);
+ if (!m_digest.isEmpty())
+ writer->writeTextElement("digest", m_digest.toHex());
+ if (!m_password.isEmpty())
+ writer->writeTextElement("password", m_password);
+ if (!m_resource.isEmpty())
+ writer->writeTextElement("resource", m_resource);
+ writer->writeEndElement();
+}
+
+QString QXmppNonSASLAuthIq::username() const
+{
+ return m_username;
+}
+
+void QXmppNonSASLAuthIq::setUsername( const QString &username )
+{
+ m_username = username;
+}
+
+QByteArray QXmppNonSASLAuthIq::digest() const
+{
+ return m_digest;
+}
+
+void QXmppNonSASLAuthIq::setDigest(const QString &streamId, const QString &password)
+{
+ m_digest = QCryptographicHash::hash(streamId.toUtf8() + password.toUtf8(), QCryptographicHash::Sha1);
+}
+
+QString QXmppNonSASLAuthIq::password() const
+{
+ return m_password;
+}
+
+void QXmppNonSASLAuthIq::setPassword( const QString &password )
+{
+ m_password = password;
+}
+
+QString QXmppNonSASLAuthIq::resource() const
+{
+ return m_resource;
+}
+
+void QXmppNonSASLAuthIq::setResource(const QString &resource)
+{
+ m_resource = resource;
+}
+
diff --git a/src/base/QXmppNonSASLAuth.h b/src/base/QXmppNonSASLAuth.h
new file mode 100644
index 00000000..a17436e7
--- /dev/null
+++ b/src/base/QXmppNonSASLAuth.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXmppNonSASLAuth_H
+#define QXmppNonSASLAuth_H
+
+#include "QXmppIq.h"
+
+class QXmppNonSASLAuthIq : public QXmppIq
+{
+public:
+ QXmppNonSASLAuthIq();
+
+ QString username() const;
+ void setUsername(const QString &username);
+
+ QByteArray digest() const;
+ void setDigest(const QString &streamId, const QString &password);
+
+ QString password() const;
+ void setPassword(const QString &password);
+
+ QString resource() const;
+ void setResource(const QString &resource);
+
+ static bool isNonSASLAuthIq(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString m_username;
+ QByteArray m_digest;
+ QString m_password;
+ QString m_resource;
+};
+
+#endif // QXmppNonSASLAuth_H
diff --git a/src/base/QXmppPacket.cpp b/src/base/QXmppPacket.cpp
new file mode 100644
index 00000000..1d284325
--- /dev/null
+++ b/src/base/QXmppPacket.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/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 "QXmppPacket.h"
+
+QXmppPacket::QXmppPacket()
+{
+}
+
+QXmppPacket::~QXmppPacket()
+{
+}
+
diff --git a/src/base/QXmppPacket.h b/src/base/QXmppPacket.h
new file mode 100644
index 00000000..427217c7
--- /dev/null
+++ b/src/base/QXmppPacket.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/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 QXMPPPACKET_H
+#define QXMPPPACKET_H
+
+#include <QByteArray>
+
+// forward declarations of QXmlStream* classes will not work on Mac, we need to
+// include the whole header.
+// See http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html
+// for an explanation.
+#include <QXmlStreamWriter>
+
+class QDomElement;
+
+class QXmppPacket
+{
+public:
+ QXmppPacket();
+ virtual ~QXmppPacket();
+
+ virtual void parse(const QDomElement &element) = 0;
+ virtual void toXml( QXmlStreamWriter *writer ) const = 0;
+};
+
+#endif // QXMPPPACKET_H
diff --git a/src/base/QXmppPingIq.cpp b/src/base/QXmppPingIq.cpp
new file mode 100644
index 00000000..7058c6d0
--- /dev/null
+++ b/src/base/QXmppPingIq.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include "QXmppConstants.h"
+#include "QXmppPingIq.h"
+#include "QXmppUtils.h"
+
+#include <QDomElement>
+
+QXmppPingIq::QXmppPingIq() : QXmppIq(QXmppIq::Get)
+{
+}
+
+bool QXmppPingIq::isPingIq(const QDomElement &element)
+{
+ QDomElement pingElement = element.firstChildElement("ping");
+ return (element.attribute("type") == "get" &&
+ pingElement.namespaceURI() == ns_ping);
+}
+
+void QXmppPingIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("ping");
+ writer->writeAttribute("xmlns", ns_ping);
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppPingIq.h b/src/base/QXmppPingIq.h
new file mode 100644
index 00000000..527eeb68
--- /dev/null
+++ b/src/base/QXmppPingIq.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPPINGIQ_H
+#define QXMPPPINGIQ_H
+
+#include "QXmppIq.h"
+
+class QXmlStreamWriter;
+class QDomElement;
+
+class QXmppPingIq : public QXmppIq
+{
+public:
+ QXmppPingIq();
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ static bool isPingIq(const QDomElement &element);
+};
+
+#endif
diff --git a/src/base/QXmppPresence.cpp b/src/base/QXmppPresence.cpp
new file mode 100644
index 00000000..b967c8e5
--- /dev/null
+++ b/src/base/QXmppPresence.cpp
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#include "QXmppPresence.h"
+#include "QXmppUtils.h"
+#include <QtDebug>
+#include <QDomElement>
+#include <QXmlStreamWriter>
+#include "QXmppConstants.h"
+
+/// Constructs a QXmppPresence.
+///
+/// \param type
+/// \param status
+
+QXmppPresence::QXmppPresence(QXmppPresence::Type type,
+ const QXmppPresence::Status& status)
+ : QXmppStanza(),
+ m_type(type),
+ m_status(status),
+ m_vCardUpdateType(VCardUpdateNone)
+{
+
+}
+
+/// Destroys a QXmppPresence.
+
+QXmppPresence::~QXmppPresence()
+{
+
+}
+
+/// Returns the presence type.
+///
+/// You can use this method to determine the action which needs to be
+/// taken in response to receiving the presence. For instance, if the type is
+/// QXmppPresence::Available or QXmppPresence::Unavailable, you could update
+/// the icon representing a contact's availability.
+
+QXmppPresence::Type QXmppPresence::type() const
+{
+ return m_type;
+}
+
+/// Sets the presence type.
+///
+/// \param type
+
+void QXmppPresence::setType(QXmppPresence::Type type)
+{
+ m_type = type;
+}
+
+/// Returns the presence status.
+
+const QXmppPresence::Status& QXmppPresence::status() const
+{
+ return m_status;
+}
+
+/// Returns a reference to the presence status, allowing you to change it.
+
+QXmppPresence::Status& QXmppPresence::status()
+{
+ return m_status;
+}
+
+/// Sets the presence status.
+///
+/// \param status
+
+void QXmppPresence::setStatus(const QXmppPresence::Status& status)
+{
+ m_status = status;
+}
+
+void QXmppPresence::parse(const QDomElement &element)
+{
+ QXmppStanza::parse(element);
+
+ setTypeFromStr(element.attribute("type"));
+ m_status.parse(element);
+
+ QXmppElementList extensions;
+ QDomElement xElement = element.firstChildElement();
+ m_vCardUpdateType = VCardUpdateNone;
+ while(!xElement.isNull())
+ {
+ // XEP-0045: Multi-User Chat
+ if(xElement.namespaceURI() == ns_muc_user)
+ {
+ QDomElement itemElement = xElement.firstChildElement("item");
+ m_mucItem.parse(itemElement);
+ QDomElement statusElement = xElement.firstChildElement("status");
+ m_mucStatusCodes.clear();
+ while (!statusElement.isNull()) {
+ m_mucStatusCodes << statusElement.attribute("code").toInt();
+ statusElement = statusElement.nextSiblingElement("status");
+ }
+ }
+ // XEP-0153: vCard-Based Avatars
+ else if(xElement.namespaceURI() == ns_vcard_update)
+ {
+ QDomElement photoElement = xElement.firstChildElement("photo");
+ if(!photoElement.isNull())
+ {
+ m_photoHash = QByteArray::fromHex(photoElement.text().toAscii());
+ if(m_photoHash.isEmpty())
+ m_vCardUpdateType = VCardUpdateNoPhoto;
+ else
+ m_vCardUpdateType = VCardUpdateValidPhoto;
+ }
+ else
+ {
+ m_photoHash = QByteArray();
+ m_vCardUpdateType = VCardUpdateNotReady;
+ }
+ }
+ // XEP-0115: Entity Capabilities
+ else if(xElement.tagName() == "c" && xElement.namespaceURI() == ns_capabilities)
+ {
+ m_capabilityNode = xElement.attribute("node");
+ m_capabilityVer = QByteArray::fromBase64(xElement.attribute("ver").toAscii());
+ m_capabilityHash = xElement.attribute("hash");
+ m_capabilityExt = xElement.attribute("ext").split(" ", QString::SkipEmptyParts);
+ }
+ else if (xElement.tagName() == "error")
+ {
+ }
+ else if (xElement.tagName() == "show")
+ {
+ }
+ else if (xElement.tagName() == "status")
+ {
+ }
+ else if (xElement.tagName() == "priority")
+ {
+ }
+ else
+ {
+ // other extensions
+ extensions << QXmppElement(xElement);
+ }
+ xElement = xElement.nextSiblingElement();
+ }
+ setExtensions(extensions);
+}
+
+void QXmppPresence::toXml(QXmlStreamWriter *xmlWriter) const
+{
+ xmlWriter->writeStartElement("presence");
+ helperToXmlAddAttribute(xmlWriter,"xml:lang", lang());
+ helperToXmlAddAttribute(xmlWriter,"id", id());
+ helperToXmlAddAttribute(xmlWriter,"to", to());
+ helperToXmlAddAttribute(xmlWriter,"from", from());
+ helperToXmlAddAttribute(xmlWriter,"type", getTypeStr());
+ m_status.toXml(xmlWriter);
+
+ error().toXml(xmlWriter);
+
+ // XEP-0045: Multi-User Chat
+ if(!m_mucItem.isNull() || !m_mucStatusCodes.isEmpty())
+ {
+ xmlWriter->writeStartElement("x");
+ xmlWriter->writeAttribute("xmlns", ns_muc_user);
+ if (!m_mucItem.isNull())
+ m_mucItem.toXml(xmlWriter);
+ foreach (int code, m_mucStatusCodes) {
+ xmlWriter->writeStartElement("status");
+ xmlWriter->writeAttribute("code", QString::number(code));
+ xmlWriter->writeEndElement();
+ }
+ xmlWriter->writeEndElement();
+ }
+
+ // XEP-0153: vCard-Based Avatars
+ if(m_vCardUpdateType != VCardUpdateNone)
+ {
+ xmlWriter->writeStartElement("x");
+ xmlWriter->writeAttribute("xmlns", ns_vcard_update);
+ switch(m_vCardUpdateType)
+ {
+ case VCardUpdateNoPhoto:
+ helperToXmlAddTextElement(xmlWriter, "photo", "");
+ break;
+ case VCardUpdateValidPhoto:
+ helperToXmlAddTextElement(xmlWriter, "photo", m_photoHash.toHex());
+ break;
+ case VCardUpdateNotReady:
+ break;
+ default:
+ break;
+ }
+ xmlWriter->writeEndElement();
+ }
+
+ if(!m_capabilityNode.isEmpty() && !m_capabilityVer.isEmpty()
+ && !m_capabilityHash.isEmpty())
+ {
+ xmlWriter->writeStartElement("c");
+ xmlWriter->writeAttribute("xmlns", ns_capabilities);
+ helperToXmlAddAttribute(xmlWriter, "hash", m_capabilityHash);
+ helperToXmlAddAttribute(xmlWriter, "node", m_capabilityNode);
+ helperToXmlAddAttribute(xmlWriter, "ver", m_capabilityVer.toBase64());
+ xmlWriter->writeEndElement();
+ }
+
+ foreach (const QXmppElement &extension, extensions())
+ extension.toXml(xmlWriter);
+
+ xmlWriter->writeEndElement();
+}
+
+QString QXmppPresence::getTypeStr() const
+{
+ switch(m_type) {
+ case QXmppPresence::Error:
+ return "error";
+ case QXmppPresence::Available:
+ return "";
+ case QXmppPresence::Unavailable:
+ return "unavailable";
+ case QXmppPresence::Subscribe:
+ return "subscribe";
+ case QXmppPresence::Subscribed:
+ return "subscribed";
+ case QXmppPresence::Unsubscribe:
+ return "unsubscribe";
+ case QXmppPresence::Unsubscribed:
+ return "unsubscribed";
+ case QXmppPresence::Probe:
+ return "probe";
+ default:
+ qWarning("QXmppPresence::getTypeStr() invalid type %d", (int)m_type);
+ return "";
+ }
+}
+
+void QXmppPresence::setTypeFromStr(const QString& str)
+{
+ if(str == "error")
+ m_type = QXmppPresence::Error;
+ else if(str == "")
+ m_type = QXmppPresence::Available;
+ else if(str == "unavailable")
+ m_type = QXmppPresence::Unavailable;
+ else if(str == "subscribe")
+ m_type = QXmppPresence::Subscribe;
+ else if(str == "subscribed")
+ m_type = QXmppPresence::Subscribed;
+ else if(str == "unsubscribe")
+ m_type = QXmppPresence::Unsubscribe;
+ else if(str == "unsubscribed")
+ m_type = QXmppPresence::Unsubscribed;
+ else if(str == "probe")
+ m_type = QXmppPresence::Probe;
+ else {
+ qWarning("QXmppPresence::setTypeFromStr() invalid input string type: %s",
+ qPrintable(str));
+ m_type = QXmppPresence::Error;
+ }
+}
+
+/// Constructs a presence status.
+
+QXmppPresence::Status::Status(QXmppPresence::Status::Type type,
+ const QString statusText, int priority) :
+ m_type(type),
+ m_statusText(statusText), m_priority(priority)
+{
+}
+
+/// Returns the status type, for instance busy or away.
+
+QXmppPresence::Status::Type QXmppPresence::Status::type() const
+{
+ return m_type;
+}
+
+/// Sets the status type.
+
+void QXmppPresence::Status::setType(QXmppPresence::Status::Type type)
+{
+ m_type = type;
+}
+
+void QXmppPresence::Status::setTypeFromStr(const QString& str)
+{
+ // FIXME: there is no keyword for Offline
+ if(str == "")
+ m_type = QXmppPresence::Status::Online;
+ else if(str == "away")
+ m_type = QXmppPresence::Status::Away;
+ else if(str == "chat")
+ m_type = QXmppPresence::Status::Chat;
+ else if(str == "dnd")
+ m_type = QXmppPresence::Status::DND;
+ else if(str == "xa")
+ m_type = QXmppPresence::Status::XA;
+ else {
+ qWarning("QXmppPresence::Status::setTypeFromStr() invalid input string type %s",
+ qPrintable(str));
+ m_type = QXmppPresence::Status::Online;
+ }
+}
+
+QString QXmppPresence::Status::getTypeStr() const
+{
+ switch(m_type) {
+ case QXmppPresence::Status::Online:
+ return "";
+ case QXmppPresence::Status::Offline:
+ // FIXME: there is no keyword for Offline
+ return "";
+ case QXmppPresence::Status::Away:
+ return "away";
+ case QXmppPresence::Status::XA:
+ return "xa";
+ case QXmppPresence::Status::DND:
+ return "dnd";
+ case QXmppPresence::Status::Chat:
+ return "chat";
+ default:
+ qWarning("QXmppPresence::Status::getTypeStr() invalid type %d",
+ (int)m_type);
+ return "";
+ }
+}
+
+/// Returns the status text, a textual description of the user's status.
+
+QString QXmppPresence::Status::statusText() const
+{
+ return m_statusText;
+}
+
+/// Sets the status text, a textual description of the user's status.
+///
+/// \param str The status text, for example "Gone fishing".
+
+void QXmppPresence::Status::setStatusText(const QString& str)
+{
+ m_statusText = str;
+}
+
+/// Returns the priority level of the resource.
+
+int QXmppPresence::Status::priority() const
+{
+ return m_priority;
+}
+
+/// Sets the priority level of the resource.
+///
+/// \param priority
+
+void QXmppPresence::Status::setPriority(int priority)
+{
+ m_priority = priority;
+}
+
+void QXmppPresence::Status::parse(const QDomElement &element)
+{
+ setTypeFromStr(element.firstChildElement("show").text());
+ m_statusText = element.firstChildElement("status").text();
+ m_priority = element.firstChildElement("priority").text().toInt();
+}
+
+void QXmppPresence::Status::toXml(QXmlStreamWriter *xmlWriter) const
+{
+ const QString show = getTypeStr();
+ if (!show.isEmpty())
+ helperToXmlAddTextElement(xmlWriter, "show", getTypeStr());
+ if (!m_statusText.isEmpty())
+ helperToXmlAddTextElement(xmlWriter, "status", m_statusText);
+ if (m_priority != 0)
+ helperToXmlAddTextElement(xmlWriter, "priority", QString::number(m_priority));
+}
+
+/// Returns the photo-hash of the VCardUpdate.
+///
+/// \return QByteArray
+
+QByteArray QXmppPresence::photoHash() const
+{
+ return m_photoHash;
+}
+
+/// Sets the photo-hash of the VCardUpdate.
+///
+/// \param photoHash as QByteArray
+
+void QXmppPresence::setPhotoHash(const QByteArray& photoHash)
+{
+ m_photoHash = photoHash;
+}
+
+/// Returns the type of VCardUpdate
+///
+/// \return VCardUpdateType
+
+QXmppPresence::VCardUpdateType QXmppPresence::vCardUpdateType() const
+{
+ return m_vCardUpdateType;
+}
+
+/// Sets the type of VCardUpdate
+///
+/// \param type VCardUpdateType
+
+void QXmppPresence::setVCardUpdateType(VCardUpdateType type)
+{
+ m_vCardUpdateType = type;
+}
+
+/// XEP-0115: Entity Capabilities
+QString QXmppPresence::capabilityHash() const
+{
+ return m_capabilityHash;
+}
+
+/// XEP-0115: Entity Capabilities
+void QXmppPresence::setCapabilityHash(const QString& hash)
+{
+ m_capabilityHash = hash;
+}
+
+/// XEP-0115: Entity Capabilities
+QString QXmppPresence::capabilityNode() const
+{
+ return m_capabilityNode;
+}
+
+/// XEP-0115: Entity Capabilities
+void QXmppPresence::setCapabilityNode(const QString& node)
+{
+ m_capabilityNode = node;
+}
+
+/// XEP-0115: Entity Capabilities
+QByteArray QXmppPresence::capabilityVer() const
+{
+ return m_capabilityVer;
+}
+
+/// XEP-0115: Entity Capabilities
+void QXmppPresence::setCapabilityVer(const QByteArray& ver)
+{
+ m_capabilityVer = ver;
+}
+
+/// Legacy XEP-0115: Entity Capabilities
+QStringList QXmppPresence::capabilityExt() const
+{
+ return m_capabilityExt;
+}
+
+/// Returns the MUC item.
+
+QXmppMucItem QXmppPresence::mucItem() const
+{
+ return m_mucItem;
+}
+
+/// Sets the MUC item.
+///
+/// \param item
+
+void QXmppPresence::setMucItem(const QXmppMucItem &item)
+{
+ m_mucItem = item;
+}
+
+/// Returns the MUC status codes.
+
+QList<int> QXmppPresence::mucStatusCodes() const
+{
+ return m_mucStatusCodes;
+}
+
+/// Sets the MUC status codes.
+///
+/// \param codes
+
+void QXmppPresence::setMucStatusCodes(const QList<int> &codes)
+{
+ m_mucStatusCodes = codes;
+}
+
diff --git a/src/base/QXmppPresence.h b/src/base/QXmppPresence.h
new file mode 100644
index 00000000..8b85e01b
--- /dev/null
+++ b/src/base/QXmppPresence.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPPRESENCE_H
+#define QXMPPPRESENCE_H
+
+#include "QXmppStanza.h"
+#include "QXmppMucIq.h"
+
+/// \brief The QXmppPresence class represents an XMPP presence stanza.
+///
+/// \ingroup Stanzas
+class QXmppPresence : public QXmppStanza
+{
+public:
+ /// This enum is used to describe a presence type.
+ enum Type
+ {
+ Error = 0, ///< An error has occurred regarding processing or delivery of a previously-sent presence stanza.
+ Available, ///< Signals that the sender is online and available for communication.
+ Unavailable, ///< Signals that the sender is no longer available for communication.
+ Subscribe, ///< The sender wishes to subscribe to the recipient's presence.
+ Subscribed, ///< The sender has allowed the recipient to receive their presence.
+ Unsubscribe, ///< The sender is unsubscribing from another entity's presence.
+ Unsubscribed, ///< The subscription request has been denied or a previously-granted subscription has been cancelled.
+ Probe ///< A request for an entity's current presence; SHOULD be generated only by a server on behalf of a user.
+ };
+
+ // XEP-0153: vCard-Based Avatars
+ enum VCardUpdateType
+ {
+ VCardUpdateNone = 0, ///< Protocol is not supported
+ VCardUpdateNoPhoto, ///< User is not using any image
+ VCardUpdateValidPhoto, ///< User is advertising an image
+ VCardUpdateNotReady ///< User is not ready to advertise an image
+
+/// \note This enables recipients to distinguish between the absence of an image
+/// (empty photo element) and mere support for the protocol (empty update child).
+ };
+
+ /// \brief The QXmppPresence::Status class represents the status of an XMPP entity.
+ ///
+ /// It stores information such as the "away", "busy" status of a user, or
+ /// a human-readable description.
+
+ class Status
+ {
+ public:
+ /// This enum is used to describe an availability status.
+ enum Type
+ {
+ Offline = 0,
+ Online, ///< The entity or resource is online.
+ Away, ///< The entity or resource is temporarily away.
+ XA, ///< The entity or resource is away for an extended period.
+ DND, ///< The entity or resource is busy ("Do Not Disturb").
+ Chat, ///< The entity or resource is actively interested in chatting.
+ };
+
+ Status(QXmppPresence::Status::Type type = QXmppPresence::Status::Online,
+ const QString statusText = "", int priority = 0);
+
+ QXmppPresence::Status::Type type() const;
+ void setType(QXmppPresence::Status::Type);
+
+ QString statusText() const;
+ void setStatusText(const QString&);
+
+ int priority() const;
+ void setPriority(int);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ private:
+ QString getTypeStr() const;
+ void setTypeFromStr(const QString&);
+
+ QXmppPresence::Status::Type m_type;
+ QString m_statusText;
+ int m_priority;
+ };
+
+ QXmppPresence(QXmppPresence::Type type = QXmppPresence::Available,
+ const QXmppPresence::Status& status = QXmppPresence::Status());
+ ~QXmppPresence();
+
+ QXmppPresence::Type type() const;
+ void setType(QXmppPresence::Type);
+
+ QXmppPresence::Status& status();
+ const QXmppPresence::Status& status() const;
+ void setStatus(const QXmppPresence::Status&);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ // XEP-0045: Multi-User Chat
+ QXmppMucItem mucItem() const;
+ void setMucItem(const QXmppMucItem &item);
+
+ QList<int> mucStatusCodes() const;
+ void setMucStatusCodes(const QList<int> &codes);
+
+ /// XEP-0153: vCard-Based Avatars
+ QByteArray photoHash() const;
+ void setPhotoHash(const QByteArray&);
+
+ VCardUpdateType vCardUpdateType() const;
+ void setVCardUpdateType(VCardUpdateType type);
+
+ // XEP-0115: Entity Capabilities
+ QString capabilityHash() const;
+ void setCapabilityHash(const QString&);
+
+ QString capabilityNode() const;
+ void setCapabilityNode(const QString&);
+
+ QByteArray capabilityVer() const;
+ void setCapabilityVer(const QByteArray&);
+
+ QStringList capabilityExt() const;
+
+private:
+ QString getTypeStr() const;
+ void setTypeFromStr(const QString&);
+
+ Type m_type;
+ QXmppPresence::Status m_status;
+
+
+ /// XEP-0153: vCard-Based Avatars
+
+ /// m_photoHash: the SHA1 hash of the avatar image data itself (not the base64-encoded version)
+ /// in accordance with RFC 3174
+ QByteArray m_photoHash;
+ VCardUpdateType m_vCardUpdateType;
+
+ // XEP-0115: Entity Capabilities
+ QString m_capabilityHash;
+ QString m_capabilityNode;
+ QByteArray m_capabilityVer;
+ // Legacy XEP-0115: Entity Capabilities
+ QStringList m_capabilityExt;
+
+ // XEP-0045: Multi-User Chat
+ QXmppMucItem m_mucItem;
+ QList<int> m_mucStatusCodes;
+};
+
+#endif // QXMPPPRESENCE_H
diff --git a/src/base/QXmppPubSubIq.cpp b/src/base/QXmppPubSubIq.cpp
new file mode 100644
index 00000000..1ad1c39d
--- /dev/null
+++ b/src/base/QXmppPubSubIq.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppPubSubIq.h"
+#include "QXmppUtils.h"
+
+static const char *ns_pubsub = "http://jabber.org/protocol/pubsub";
+
+static const char *pubsub_queries[] = {
+ "affiliations",
+ "default",
+ "items",
+ "publish",
+ "retract",
+ "subscribe",
+ "subscription",
+ "subscriptions",
+ "unsubscribe",
+};
+
+/// Returns the ID of the PubSub item.
+///
+
+QString QXmppPubSubItem::id() const
+{
+ return m_id;
+}
+
+/// Sets the ID of the PubSub item.
+///
+/// \param id
+
+void QXmppPubSubItem::setId(const QString &id)
+{
+ m_id = id;
+}
+
+/// Returns the contents of the PubSub item.
+///
+
+QXmppElement QXmppPubSubItem::contents() const
+{
+ return m_contents;
+}
+
+/// Sets the contents of the PubSub item.
+///
+/// \param contents
+
+void QXmppPubSubItem::setContents(const QXmppElement &contents)
+{
+ m_contents = contents;
+}
+
+void QXmppPubSubItem::parse(const QDomElement &element)
+{
+ m_id = element.attribute("id");
+ m_contents = QXmppElement(element.firstChildElement());
+}
+
+void QXmppPubSubItem::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("item");
+ helperToXmlAddAttribute(writer, "id", m_id);
+ m_contents.toXml(writer);
+ writer->writeEndElement();
+}
+
+/// Returns the PubSub queryType for this IQ.
+///
+
+QXmppPubSubIq::QueryType QXmppPubSubIq::queryType() const
+{
+ return m_queryType;
+}
+
+/// Sets the PubSub queryType for this IQ.
+///
+/// \param queryType
+
+void QXmppPubSubIq::setQueryType(QXmppPubSubIq::QueryType queryType)
+{
+ m_queryType = queryType;
+}
+
+/// Returns the JID being queried.
+///
+
+QString QXmppPubSubIq::queryJid() const
+{
+ return m_queryJid;
+}
+
+/// Sets the JID being queried.
+///
+/// \param queryJid
+
+void QXmppPubSubIq::setQueryJid(const QString &queryJid)
+{
+ m_queryJid = queryJid;
+}
+
+/// Returns the node being queried.
+///
+
+QString QXmppPubSubIq::queryNode() const
+{
+ return m_queryNode;
+}
+
+/// Sets the node being queried.
+///
+/// \param queryNode
+
+void QXmppPubSubIq::setQueryNode(const QString &queryNode)
+{
+ m_queryNode = queryNode;
+}
+
+/// Returns the subscription ID.
+///
+
+QString QXmppPubSubIq::subscriptionId() const
+{
+ return m_subscriptionId;
+}
+
+/// Sets the subscription ID.
+///
+/// \param subscriptionId
+
+void QXmppPubSubIq::setSubscriptionId(const QString &subscriptionId)
+{
+ m_subscriptionId = subscriptionId;
+}
+
+/// Returns the IQ's items.
+///
+
+QList<QXmppPubSubItem> QXmppPubSubIq::items() const
+{
+ return m_items;
+}
+
+/// Sets the IQ's items.
+///
+/// \param items
+
+void QXmppPubSubIq::setItems(const QList<QXmppPubSubItem> &items)
+{
+ m_items = items;
+}
+
+bool QXmppPubSubIq::isPubSubIq(const QDomElement &element)
+{
+ const QDomElement pubSubElement = element.firstChildElement("pubsub");
+ return pubSubElement.namespaceURI() == ns_pubsub;
+}
+
+void QXmppPubSubIq::parseElementFromChild(const QDomElement &element)
+{
+ const QDomElement pubSubElement = element.firstChildElement("pubsub");
+
+ const QDomElement queryElement = pubSubElement.firstChildElement();
+
+ // determine query type
+ const QString tagName = queryElement.tagName();
+ for (int i = ItemsQuery; i <= SubscriptionsQuery; i++)
+ {
+ if (tagName == pubsub_queries[i])
+ {
+ m_queryType = static_cast<QueryType>(i);
+ break;
+ }
+ }
+ m_queryJid = queryElement.attribute("jid");
+ m_queryNode = queryElement.attribute("node");
+
+ // parse contents
+ QDomElement childElement;
+ switch (m_queryType)
+ {
+ case QXmppPubSubIq::ItemsQuery:
+ case QXmppPubSubIq::PublishQuery:
+ childElement = queryElement.firstChildElement("item");
+ while (!childElement.isNull())
+ {
+ QXmppPubSubItem item;
+ item.parse(childElement);
+ m_items << item;
+ childElement = childElement.nextSiblingElement("item");
+ }
+ break;
+ case QXmppPubSubIq::SubscriptionQuery:
+ m_subscriptionId = queryElement.attribute("subid");
+ m_subscriptionType = queryElement.attribute("subscription");
+ break;
+ default:
+ break;
+ }
+}
+
+void QXmppPubSubIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("pubsub");
+ writer->writeAttribute("xmlns", ns_pubsub);
+
+ // write query type
+ writer->writeStartElement(pubsub_queries[m_queryType]);
+ helperToXmlAddAttribute(writer, "jid", m_queryJid);
+ helperToXmlAddAttribute(writer, "node", m_queryNode);
+
+ // write contents
+ switch (m_queryType)
+ {
+ case QXmppPubSubIq::ItemsQuery:
+ case QXmppPubSubIq::PublishQuery:
+ foreach (const QXmppPubSubItem &item, m_items)
+ item.toXml(writer);
+ break;
+ case QXmppPubSubIq::SubscriptionQuery:
+ helperToXmlAddAttribute(writer, "subid", m_subscriptionId);
+ helperToXmlAddAttribute(writer, "subscription", m_subscriptionType);
+ break;
+ default:
+ break;
+ }
+ writer->writeEndElement();
+ writer->writeEndElement();
+}
diff --git a/src/base/QXmppPubSubIq.h b/src/base/QXmppPubSubIq.h
new file mode 100644
index 00000000..01dc3a92
--- /dev/null
+++ b/src/base/QXmppPubSubIq.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPPUBSUBIQ_H
+#define QXMPPPUBSUBIQ_H
+
+#include "QXmppIq.h"
+
+/// \brief The QXmppPubSubItem class represents a publish-subscribe item
+/// as defined by XEP-0060: Publish-Subscribe.
+///
+
+class QXmppPubSubItem
+{
+public:
+ QString id() const;
+ void setId(const QString &id);
+
+ QXmppElement contents() const;
+ void setContents(const QXmppElement &contents);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString m_id;
+ QXmppElement m_contents;
+};
+
+/// \brief The QXmppPubSubIq class represents an IQ used for the
+/// publish-subscribe mechanisms defined by XEP-0060: Publish-Subscribe.
+///
+/// \ingroup Stanzas
+
+class QXmppPubSubIq : public QXmppIq
+{
+public:
+ /// This enum is used to describe a publish-subscribe query type.
+ enum QueryType
+ {
+ AffiliationsQuery,
+ DefaultQuery,
+ ItemsQuery,
+ PublishQuery,
+ RetractQuery,
+ SubscribeQuery,
+ SubscriptionQuery,
+ SubscriptionsQuery,
+ UnsubscribeQuery,
+ };
+
+ QXmppPubSubIq::QueryType queryType() const;
+ void setQueryType(QXmppPubSubIq::QueryType queryType);
+
+ QString queryJid() const;
+ void setQueryJid(const QString &jid);
+
+ QString queryNode() const;
+ void setQueryNode(const QString &node);
+
+ QList<QXmppPubSubItem> items() const;
+ void setItems(const QList<QXmppPubSubItem> &items);
+
+ QString subscriptionId() const;
+ void setSubscriptionId(const QString &id);
+
+ /// \cond
+ static bool isPubSubIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement&);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QXmppPubSubIq::QueryType m_queryType;
+ QString m_queryJid;
+ QString m_queryNode;
+ QList<QXmppPubSubItem> m_items;
+ QString m_subscriptionId;
+ QString m_subscriptionType;
+};
+
+#endif
diff --git a/src/base/QXmppRosterIq.cpp b/src/base/QXmppRosterIq.cpp
new file mode 100644
index 00000000..0b2e1716
--- /dev/null
+++ b/src/base/QXmppRosterIq.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+#include <QXmlStreamWriter>
+
+#include "QXmppRosterIq.h"
+#include "QXmppConstants.h"
+#include "QXmppUtils.h"
+
+/// Adds an item to the roster IQ.
+///
+/// \param item
+
+void QXmppRosterIq::addItem(const Item& item)
+{
+ m_items.append(item);
+}
+
+/// Returns the roster IQ's items.
+
+QList<QXmppRosterIq::Item> QXmppRosterIq::items() const
+{
+ return m_items;
+}
+
+bool QXmppRosterIq::isRosterIq(const QDomElement &element)
+{
+ return (element.firstChildElement("query").namespaceURI() == ns_roster);
+}
+
+void QXmppRosterIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement itemElement = element.
+ firstChildElement("query").
+ firstChildElement("item");
+ while(!itemElement.isNull())
+ {
+ QXmppRosterIq::Item item;
+ item.parse(itemElement);
+ m_items.append(item);
+ itemElement = itemElement.nextSiblingElement();
+ }
+}
+
+void QXmppRosterIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute( "xmlns", ns_roster);
+
+ for(int i = 0; i < m_items.count(); ++i)
+ m_items.at(i).toXml(writer);
+ writer->writeEndElement();
+}
+
+/// Returns the bareJid of the roster entry.
+///
+/// \return bareJid as a QString
+///
+
+QString QXmppRosterIq::Item::bareJid() const
+{
+ return m_bareJid;
+}
+
+/// Sets the bareJid of the roster entry.
+///
+/// \param bareJid as a QString
+///
+
+void QXmppRosterIq::Item::setBareJid(const QString &bareJid)
+{
+ m_bareJid = bareJid;
+}
+
+/// Returns the groups of the roster entry.
+///
+/// \return QSet<QString> list of all the groups
+///
+
+QSet<QString> QXmppRosterIq::Item::groups() const
+{
+ return m_groups;
+}
+
+/// Sets the groups of the roster entry.
+///
+/// \param groups list of all the groups as a QSet<QString>
+///
+
+void QXmppRosterIq::Item::setGroups(const QSet<QString>& groups)
+{
+ m_groups = groups;
+}
+
+/// Returns the name of the roster entry.
+///
+/// \return name as a QString
+///
+
+QString QXmppRosterIq::Item::name() const
+{
+ return m_name;
+}
+
+/// Sets the name of the roster entry.
+///
+/// \param name as a QString
+///
+
+void QXmppRosterIq::Item::setName(const QString &name)
+{
+ m_name = name;
+}
+
+/// Returns the subscription status of the roster entry. It is the "ask"
+/// attribute in the Roster IQ stanza. Its value can be "subscribe" or "unsubscribe"
+/// or empty.
+///
+/// \return subscription status as a QString
+///
+///
+
+QString QXmppRosterIq::Item::subscriptionStatus() const
+{
+ return m_subscriptionStatus;
+}
+
+/// Sets the subscription status of the roster entry. It is the "ask"
+/// attribute in the Roster IQ stanza. Its value can be "subscribe" or "unsubscribe"
+/// or empty.
+///
+/// \param status as a QString
+///
+
+void QXmppRosterIq::Item::setSubscriptionStatus(const QString &status)
+{
+ m_subscriptionStatus = status;
+}
+
+/// Returns the subscription type of the roster entry.
+///
+
+QXmppRosterIq::Item::SubscriptionType
+ QXmppRosterIq::Item::subscriptionType() const
+{
+ return m_type;
+}
+
+/// Sets the subscription type of the roster entry.
+///
+/// \param type
+///
+
+void QXmppRosterIq::Item::setSubscriptionType(SubscriptionType type)
+{
+ m_type = type;
+}
+
+QString QXmppRosterIq::Item::getSubscriptionTypeStr() const
+{
+ switch(m_type)
+ {
+ case NotSet:
+ return "";
+ case None:
+ return "none";
+ case Both:
+ return "both";
+ case From:
+ return "from";
+ case To:
+ return "to";
+ case Remove:
+ return "remove";
+ default:
+ {
+ qWarning("QXmppRosterIq::Item::getTypeStr(): invalid type");
+ return "";
+ }
+ }
+}
+
+void QXmppRosterIq::Item::setSubscriptionTypeFromStr(const QString& type)
+{
+ if(type == "")
+ setSubscriptionType(NotSet);
+ else if(type == "none")
+ setSubscriptionType(None);
+ else if(type == "both")
+ setSubscriptionType(Both);
+ else if(type == "from")
+ setSubscriptionType(From);
+ else if(type == "to")
+ setSubscriptionType(To);
+ else if(type == "remove")
+ setSubscriptionType(Remove);
+ else
+ qWarning("QXmppRosterIq::Item::setTypeFromStr(): invalid type");
+}
+
+void QXmppRosterIq::Item::parse(const QDomElement &element)
+{
+ m_name = element.attribute("name");
+ m_bareJid = element.attribute("jid");
+ setSubscriptionTypeFromStr(element.attribute("subscription"));
+ setSubscriptionStatus(element.attribute("ask"));
+
+ QDomElement groupElement = element.firstChildElement("group");
+ while(!groupElement.isNull())
+ {
+ m_groups << groupElement.text();
+ groupElement = groupElement.nextSiblingElement("group");
+ }
+}
+
+void QXmppRosterIq::Item::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("item");
+ helperToXmlAddAttribute(writer,"jid", m_bareJid);
+ helperToXmlAddAttribute(writer,"name", m_name);
+ helperToXmlAddAttribute(writer,"subscription", getSubscriptionTypeStr());
+ helperToXmlAddAttribute(writer, "ask", subscriptionStatus());
+
+ QSet<QString>::const_iterator i = m_groups.constBegin();
+ while(i != m_groups.constEnd())
+ {
+ helperToXmlAddTextElement(writer,"group", *i);
+ ++i;
+ }
+ writer->writeEndElement();
+}
diff --git a/src/base/QXmppRosterIq.h b/src/base/QXmppRosterIq.h
new file mode 100644
index 00000000..9680a176
--- /dev/null
+++ b/src/base/QXmppRosterIq.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPROSTERIQ_H
+#define QXMPPROSTERIQ_H
+
+#include "QXmppIq.h"
+#include <QList>
+#include <QSet>
+
+/// \brief The QXmppRosterIq class represents a roster IQ.
+///
+/// \ingroup Stanzas
+
+class QXmppRosterIq : public QXmppIq
+{
+public:
+
+ /// \brief The QXmppRosterIq::Item class represents a roster entry.
+ class Item
+ {
+ public:
+ /// An enumeration for type of subscription with the bareJid in the roster.
+ enum SubscriptionType
+ {
+ None = 0, ///< the user does not have a subscription to the
+ ///< contact's presence information, and the contact does
+ ///< not have a subscription to the user's presence information
+ From = 1, ///< the contact has a subscription to the user's presence information,
+ ///< but the user does not have a subscription to the contact's presence information
+ To = 2, ///< the user has a subscription to the contact's presence information,
+ ///< but the contact does not have a subscription to the user's presence information
+ Both = 3, ///< both the user and the contact have subscriptions to each
+ ///< other's presence information
+ Remove = 4, ///< to delete a roster item
+ NotSet = 8 ///< the subscription state was not specified
+ };
+
+ QString bareJid() const;
+ QSet<QString> groups() const;
+ QString name() const;
+ QString subscriptionStatus() const;
+ SubscriptionType subscriptionType() const;
+
+ void setBareJid(const QString&);
+ void setGroups(const QSet<QString>&);
+ void setName(const QString&);
+ void setSubscriptionStatus(const QString&);
+ void setSubscriptionType(SubscriptionType);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ private:
+ QString getSubscriptionTypeStr() const;
+ void setSubscriptionTypeFromStr(const QString&);
+
+ QString m_bareJid;
+ SubscriptionType m_type;
+ QString m_name;
+ // can be subscribe/unsubscribe (attribute "ask")
+ QString m_subscriptionStatus;
+ QSet<QString> m_groups;
+ };
+
+ void addItem(const Item&);
+ QList<Item> items() const;
+
+ /// \cond
+ static bool isRosterIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QList<Item> m_items;
+};
+
+#endif // QXMPPROSTERIQ_H
diff --git a/src/base/QXmppRpcIq.cpp b/src/base/QXmppRpcIq.cpp
new file mode 100644
index 00000000..a2647077
--- /dev/null
+++ b/src/base/QXmppRpcIq.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Ian Reinhart Geiser
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+#include <QMap>
+#include <QVariant>
+#include <QDateTime>
+#include <QStringList>
+
+#include "QXmppConstants.h"
+#include "QXmppRpcIq.h"
+#include "QXmppUtils.h"
+
+void XMLRPC::marshall(QXmlStreamWriter *writer, const QVariant &value)
+{
+ writer->writeStartElement("value");
+ switch( value.type() )
+ {
+ case QVariant::Int:
+ case QVariant::UInt:
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ writer->writeTextElement("i4", value.toString());
+ break;
+ case QVariant::Double:
+ writer->writeTextElement("double", value.toString());
+ break;
+ case QVariant::Bool:
+ writer->writeTextElement("boolean", value.toBool() ? "1" : "0");
+ break;
+ case QVariant::Date:
+ writer->writeTextElement("dateTime.iso8601", value.toDate().toString( Qt::ISODate ) );
+ break;
+ case QVariant::DateTime:
+ writer->writeTextElement("dateTime.iso8601", value.toDateTime().toString( Qt::ISODate ) );
+ break;
+ case QVariant::Time:
+ writer->writeTextElement("dateTime.iso8601", value.toTime().toString( Qt::ISODate ) );
+ break;
+ case QVariant::StringList:
+ case QVariant::List:
+ {
+ writer->writeStartElement("array");
+ writer->writeStartElement("data");
+ foreach(const QVariant &item, value.toList())
+ marshall(writer, item);
+ writer->writeEndElement();
+ writer->writeEndElement();
+ break;
+ }
+ case QVariant::Map:
+ {
+ writer->writeStartElement("struct");
+ QMap<QString, QVariant> map = value.toMap();
+ QMap<QString, QVariant>::ConstIterator index = map.begin();
+ while( index != map.end() )
+ {
+ writer->writeStartElement("member");
+ writer->writeTextElement("name", index.key());
+ marshall( writer, *index );
+ writer->writeEndElement();
+ ++index;
+ }
+ writer->writeEndElement();
+ break;
+ }
+ case QVariant::ByteArray:
+ {
+ writer->writeTextElement("base64", value.toByteArray().toBase64() );
+ break;
+ }
+ default:
+ {
+ if (value.isNull())
+ writer->writeEmptyElement("nil");
+ else if( value.canConvert(QVariant::String) )
+ {
+ writer->writeTextElement("string", value.toString() );
+ }
+ break;
+ }
+ }
+ writer->writeEndElement();
+}
+
+QVariant XMLRPC::demarshall(const QDomElement &elem, QStringList &errors)
+{
+ if ( elem.tagName().toLower() != "value" )
+ {
+ errors << "Bad param value";
+ return QVariant();
+ }
+
+ if ( !elem.firstChild().isElement() )
+ {
+ return QVariant( elem.text() );
+ }
+
+ const QDomElement typeData = elem.firstChild().toElement();
+ const QString typeName = typeData.tagName().toLower();
+
+ if (typeName == "nil")
+ {
+ return QVariant();
+ }
+ if ( typeName == "string" )
+ {
+ return QVariant( typeData.text() );
+ }
+ else if (typeName == "int" || typeName == "i4" )
+ {
+ bool ok = false;
+ QVariant val( typeData.text().toInt( &ok ) );
+ if (ok)
+ return val;
+ errors << "I was looking for an integer but data was courupt";
+ return QVariant();
+ }
+ else if( typeName == "double" )
+ {
+ bool ok = false;
+ QVariant val( typeData.text().toDouble( &ok ) );
+ if (ok)
+ return val;
+ errors << "I was looking for an double but data was corrupt";
+ }
+ else if( typeName == "boolean" )
+ return QVariant( typeData.text() == "1" || typeData.text().toLower() == "true" );
+ else if( typeName == "datetime" || typeName == "datetime.iso8601" )
+ return QVariant( QDateTime::fromString( typeData.text(), Qt::ISODate ) );
+ else if( typeName == "array" )
+ {
+ QVariantList arr;
+ QDomElement valueNode = typeData.firstChildElement("data").firstChildElement();
+ while (!valueNode.isNull() && errors.isEmpty())
+ {
+ arr.append(demarshall(valueNode, errors));
+ valueNode = valueNode.nextSiblingElement();
+ }
+ return QVariant( arr );
+ }
+ else if( typeName == "struct" )
+ {
+ QMap<QString,QVariant> stct;
+ QDomNode valueNode = typeData.firstChild();
+ while(!valueNode.isNull() && errors.isEmpty())
+ {
+ const QDomElement memberNode = valueNode.toElement().elementsByTagName("name").item(0).toElement();
+ const QDomElement dataNode = valueNode.toElement().elementsByTagName("value").item(0).toElement();
+ stct[ memberNode.text() ] = demarshall(dataNode, errors);
+ valueNode = valueNode.nextSibling();
+ }
+ return QVariant(stct);
+ }
+ else if( typeName == "base64" )
+ {
+ QVariant returnVariant;
+ QByteArray dest;
+ QByteArray src = typeData.text().toLatin1();
+ return QVariant(QByteArray::fromBase64(src));
+ }
+
+ errors << QString( "Cannot handle type %1").arg(typeName);
+ return QVariant();
+}
+
+QXmppRpcErrorIq::QXmppRpcErrorIq() : QXmppIq( QXmppIq::Error )
+{
+
+}
+
+QXmppRpcInvokeIq QXmppRpcErrorIq::query() const
+{
+ return m_query;
+}
+
+void QXmppRpcErrorIq::setQuery(const QXmppRpcInvokeIq &query)
+{
+ m_query = query;
+}
+
+bool QXmppRpcErrorIq::isRpcErrorIq(const QDomElement &element)
+{
+ QString type = element.attribute("type");
+ QDomElement errorElement = element.firstChildElement("error");
+ QDomElement queryElement = element.firstChildElement("query");
+ return (type == "error") &&
+ !errorElement.isNull() &&
+ queryElement.namespaceURI() == ns_rpc;
+}
+
+void QXmppRpcErrorIq::parseElementFromChild(const QDomElement &element)
+{
+ m_query.parseElementFromChild(element);
+}
+
+void QXmppRpcErrorIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ m_query.toXmlElementFromChild(writer);
+}
+
+QXmppRpcResponseIq::QXmppRpcResponseIq()
+ : QXmppIq(QXmppIq::Result),
+ m_faultCode(0)
+{
+}
+
+/// Returns the fault code.
+///
+
+int QXmppRpcResponseIq::faultCode() const
+{
+ return m_faultCode;
+}
+
+/// Sets the fault code.
+///
+/// \param faultCode
+
+void QXmppRpcResponseIq::setFaultCode(int faultCode)
+{
+ m_faultCode = faultCode;
+}
+
+/// Returns the fault string.
+///
+
+QString QXmppRpcResponseIq::faultString() const
+{
+ return m_faultString;
+}
+
+/// Sets the fault string.
+///
+/// \param faultString
+
+void QXmppRpcResponseIq::setFaultString(const QString& faultString)
+{
+ m_faultString = faultString;
+}
+
+/// Returns the response values.
+///
+
+QVariantList QXmppRpcResponseIq::values() const
+{
+ return m_values;
+}
+
+/// Sets the response values.
+///
+/// \param values
+
+void QXmppRpcResponseIq::setValues(const QVariantList &values)
+{
+ m_values = values;
+}
+
+bool QXmppRpcResponseIq::isRpcResponseIq(const QDomElement &element)
+{
+ QString type = element.attribute("type");
+ QDomElement dataElement = element.firstChildElement("query");
+ return dataElement.namespaceURI() == ns_rpc &&
+ type == "result";
+}
+
+void QXmppRpcResponseIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ QDomElement methodElement = queryElement.firstChildElement("methodResponse");
+
+ const QDomElement contents = methodElement.firstChildElement();
+ if( contents.tagName().toLower() == "params")
+ {
+ QDomNode param = contents.firstChildElement("param");
+ while (!param.isNull())
+ {
+ QStringList errors;
+ const QVariant value = XMLRPC::demarshall(param.firstChildElement("value"), errors);
+ if (!errors.isEmpty())
+ break;
+ m_values << value;
+ param = param.nextSiblingElement("param");
+ }
+ }
+ else if( contents.tagName().toLower() == "fault")
+ {
+ QStringList errors;
+ const QDomElement errElement = contents.firstChildElement("value");
+ const QVariant error = XMLRPC::demarshall(errElement, errors);
+ if (!errors.isEmpty())
+ return;
+ m_faultCode = error.toMap()["faultCode"].toInt();
+ m_faultString = error.toMap()["faultString"].toString();
+ }
+}
+
+void QXmppRpcResponseIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute("xmlns", ns_rpc);
+
+ writer->writeStartElement("methodResponse");
+ if (m_faultCode)
+ {
+ writer->writeStartElement("fault");
+ QMap<QString,QVariant> fault;
+ fault["faultCode"] = m_faultCode;
+ fault["faultString"] = m_faultString;
+ XMLRPC::marshall(writer, fault);
+ writer->writeEndElement();
+ }
+ else if (!m_values.isEmpty())
+ {
+ writer->writeStartElement("params");
+ foreach (const QVariant &arg, m_values)
+ {
+ writer->writeStartElement("param");
+ XMLRPC::marshall(writer, arg);
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+
+ writer->writeEndElement();
+}
+
+QXmppRpcInvokeIq::QXmppRpcInvokeIq()
+ : QXmppIq(QXmppIq::Set)
+{
+}
+
+/// Returns the method arguments.
+///
+
+QVariantList QXmppRpcInvokeIq::arguments() const
+{
+ return m_arguments;
+}
+
+/// Sets the method arguments.
+///
+/// \param arguments
+
+void QXmppRpcInvokeIq::setArguments(const QVariantList &arguments)
+{
+ m_arguments = arguments;
+}
+
+/// Returns the method name.
+///
+
+QString QXmppRpcInvokeIq::method() const
+{
+ return m_method;
+}
+
+/// Sets the method name.
+///
+/// \param method
+
+void QXmppRpcInvokeIq::setMethod(const QString &method)
+{
+ m_method = method;
+}
+
+bool QXmppRpcInvokeIq::isRpcInvokeIq(const QDomElement &element)
+{
+ QString type = element.attribute("type");
+ QDomElement dataElement = element.firstChildElement("query");
+ return dataElement.namespaceURI() == ns_rpc &&
+ type == "set";
+}
+
+void QXmppRpcInvokeIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ QDomElement methodElement = queryElement.firstChildElement("methodCall");
+
+ m_method = methodElement.firstChildElement("methodName").text();
+
+ const QDomElement methodParams = methodElement.firstChildElement("params");
+ m_arguments.clear();
+ if( !methodParams.isNull() )
+ {
+ QDomNode param = methodParams.firstChildElement("param");
+ while (!param.isNull())
+ {
+ QStringList errors;
+ QVariant arg = XMLRPC::demarshall(param.firstChildElement("value"), errors);
+ if (!errors.isEmpty())
+ break;
+ m_arguments << arg;
+ param = param.nextSiblingElement("param");
+ }
+ }
+}
+
+void QXmppRpcInvokeIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute("xmlns", ns_rpc);
+
+ writer->writeStartElement("methodCall");
+ writer->writeTextElement("methodName", m_method);
+ if (!m_arguments.isEmpty())
+ {
+ writer->writeStartElement("params");
+ foreach(const QVariant &arg, m_arguments)
+ {
+ writer->writeStartElement("param");
+ XMLRPC::marshall(writer, arg);
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppRpcIq.h b/src/base/QXmppRpcIq.h
new file mode 100644
index 00000000..f557686a
--- /dev/null
+++ b/src/base/QXmppRpcIq.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Ian Reinhart Geiser
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPRPCIQ_H
+#define QXMPPRPCIQ_H
+
+#include "QXmppIq.h"
+#include <QVariant>
+
+class QXmlStreamWriter;
+class QDomElement;
+
+namespace XMLRPC
+{
+ void marshall( QXmlStreamWriter *writer, const QVariant &value);
+ QVariant demarshall(const QDomElement &elem, QStringList &errors);
+}
+
+/// \brief The QXmppRpcResponseIq class represents an IQ used to carry
+/// an RPC response as specified by XEP-0009: Jabber-RPC.
+///
+/// \ingroup Stanzas
+
+class QXmppRpcResponseIq : public QXmppIq
+{
+public:
+ QXmppRpcResponseIq();
+
+ int faultCode() const;
+ void setFaultCode(int faultCode);
+
+ QString faultString() const;
+ void setFaultString(const QString &faultString);
+
+ QVariantList values() const;
+ void setValues(const QVariantList &values);
+
+ /// \cond
+ static bool isRpcResponseIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ int m_faultCode;
+ QString m_faultString;
+ QVariantList m_values;
+};
+
+/// \brief The QXmppRpcInvokeIq class represents an IQ used to carry
+/// an RPC invocation as specified by XEP-0009: Jabber-RPC.
+///
+/// \ingroup Stanzas
+
+class QXmppRpcInvokeIq : public QXmppIq
+{
+public:
+ QXmppRpcInvokeIq();
+
+ QString method() const;
+ void setMethod( const QString &method );
+
+ QVariantList arguments() const;
+ void setArguments(const QVariantList &arguments);
+
+ /// \cond
+ static bool isRpcInvokeIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QVariantList m_arguments;
+ QString m_method;
+
+ friend class QXmppRpcErrorIq;
+};
+
+class QXmppRpcErrorIq : public QXmppIq
+{
+public:
+ QXmppRpcErrorIq();
+
+ QXmppRpcInvokeIq query() const;
+ void setQuery(const QXmppRpcInvokeIq &query);
+
+ /// \cond
+ static bool isRpcErrorIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QXmppRpcInvokeIq m_query;
+};
+
+#endif // QXMPPRPCIQ_H
diff --git a/src/base/QXmppRtpChannel.cpp b/src/base/QXmppRtpChannel.cpp
new file mode 100644
index 00000000..df26bc30
--- /dev/null
+++ b/src/base/QXmppRtpChannel.cpp
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 <cmath>
+
+#include <QDataStream>
+#include <QMetaType>
+#include <QTimer>
+
+#include "QXmppCodec.h"
+#include "QXmppJingleIq.h"
+#include "QXmppRtpChannel.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846264338327950288
+#endif
+
+//#define QXMPP_DEBUG_RTP
+//#define QXMPP_DEBUG_RTP_BUFFER
+#define SAMPLE_BYTES 2
+
+const quint8 RTP_VERSION = 0x02;
+
+/// Parses an RTP packet.
+///
+/// \param ba
+
+bool QXmppRtpPacket::decode(const QByteArray &ba)
+{
+ if (ba.isEmpty())
+ return false;
+
+ // fixed header
+ quint8 tmp;
+ QDataStream stream(ba);
+ stream >> tmp;
+ version = (tmp >> 6);
+ const quint8 cc = (tmp >> 1) & 0xf;
+ const int hlen = 12 + 4 * cc;
+ if (version != RTP_VERSION || ba.size() < hlen)
+ return false;
+ stream >> tmp;
+ marker = (tmp >> 7);
+ type = tmp & 0x7f;
+ stream >> sequence;
+ stream >> stamp;
+ stream >> ssrc;
+
+ // contributing source IDs
+ csrc.clear();
+ quint32 src;
+ for (int i = 0; i < cc; ++i) {
+ stream >> src;
+ csrc << src;
+ }
+
+ // retrieve payload
+ payload = ba.right(ba.size() - hlen);
+ return true;
+}
+
+/// Encodes an RTP packet.
+
+QByteArray QXmppRtpPacket::encode() const
+{
+ Q_ASSERT(csrc.size() < 16);
+
+ // fixed header
+ QByteArray ba;
+ ba.resize(payload.size() + 12 + 4 * csrc.size());
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ stream << quint8(((version & 0x3) << 6) |
+ ((csrc.size() & 0xf) << 1));
+ stream << quint8((type & 0x7f) | (marker << 7));
+ stream << sequence;
+ stream << stamp;
+ stream << ssrc;
+
+ // contributing source ids
+ foreach (const quint32 &src, csrc)
+ stream << src;
+
+ stream.writeRawData(payload.constData(), payload.size());
+ return ba;
+}
+
+/// Returns a string representation of the RTP header.
+
+QString QXmppRtpPacket::toString() const
+{
+ return QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
+ QString::number(sequence),
+ QString::number(stamp),
+ QString::number(marker),
+ QString::number(type),
+ QString::number(payload.size()));
+}
+
+/// Creates a new RTP channel.
+
+QXmppRtpChannel::QXmppRtpChannel()
+ : m_outgoingPayloadNumbered(false)
+{
+}
+
+/// Returns the local payload types.
+///
+
+QList<QXmppJinglePayloadType> QXmppRtpChannel::localPayloadTypes()
+{
+ m_outgoingPayloadNumbered = true;
+ return m_outgoingPayloadTypes;
+}
+
+/// Sets the remote payload types.
+///
+/// \param remotePayloadTypes
+
+void QXmppRtpChannel::setRemotePayloadTypes(const QList<QXmppJinglePayloadType> &remotePayloadTypes)
+{
+ QList<QXmppJinglePayloadType> commonOutgoingTypes;
+ QList<QXmppJinglePayloadType> commonIncomingTypes;
+
+ foreach (const QXmppJinglePayloadType &incomingType, remotePayloadTypes) {
+ // check we support this payload type
+ int outgoingIndex = m_outgoingPayloadTypes.indexOf(incomingType);
+ if (outgoingIndex < 0)
+ continue;
+ QXmppJinglePayloadType outgoingType = m_outgoingPayloadTypes[outgoingIndex];
+
+ // be kind and try to adopt the other agent's numbering
+ if (!m_outgoingPayloadNumbered && outgoingType.id() > 95) {
+ outgoingType.setId(incomingType.id());
+ }
+ commonIncomingTypes << incomingType;
+ commonOutgoingTypes << outgoingType;
+ }
+ if (commonOutgoingTypes.isEmpty()) {
+ qWarning("QXmppRtpChannel could not negociate a common codec");
+ return;
+ }
+ m_incomingPayloadTypes = commonIncomingTypes;
+ m_outgoingPayloadTypes = commonOutgoingTypes;
+ m_outgoingPayloadNumbered = true;
+
+ // call hook
+ payloadTypesChanged();
+}
+
+void QXmppRtpChannel::payloadTypesChanged()
+{
+}
+
+enum CodecId {
+ G711u = 0,
+ GSM = 3,
+ G723 = 4,
+ G711a = 8,
+ G722 = 9,
+ L16Stereo = 10,
+ L16Mono = 11,
+ G728 = 15,
+ G729 = 18,
+};
+
+struct ToneInfo
+{
+ QXmppRtpAudioChannel::Tone tone;
+ quint32 incomingStart;
+ quint32 outgoingStart;
+ bool finished;
+};
+
+static QPair<int, int> toneFreqs(QXmppRtpAudioChannel::Tone tone)
+{
+ switch (tone) {
+ case QXmppRtpAudioChannel::Tone_1: return qMakePair(697, 1209);
+ case QXmppRtpAudioChannel::Tone_2: return qMakePair(697, 1336);
+ case QXmppRtpAudioChannel::Tone_3: return qMakePair(697, 1477);
+ case QXmppRtpAudioChannel::Tone_A: return qMakePair(697, 1633);
+ case QXmppRtpAudioChannel::Tone_4: return qMakePair(770, 1209);
+ case QXmppRtpAudioChannel::Tone_5: return qMakePair(770, 1336);
+ case QXmppRtpAudioChannel::Tone_6: return qMakePair(770, 1477);
+ case QXmppRtpAudioChannel::Tone_B: return qMakePair(770, 1633);
+ case QXmppRtpAudioChannel::Tone_7: return qMakePair(852, 1209);
+ case QXmppRtpAudioChannel::Tone_8: return qMakePair(852, 1336);
+ case QXmppRtpAudioChannel::Tone_9: return qMakePair(852, 1477);
+ case QXmppRtpAudioChannel::Tone_C: return qMakePair(852, 1633);
+ case QXmppRtpAudioChannel::Tone_Star: return qMakePair(941, 1209);
+ case QXmppRtpAudioChannel::Tone_0: return qMakePair(941, 1336);
+ case QXmppRtpAudioChannel::Tone_Pound: return qMakePair(941, 1477);
+ case QXmppRtpAudioChannel::Tone_D: return qMakePair(941, 1633);
+ }
+ return qMakePair(0, 0);
+}
+
+QByteArray renderTone(QXmppRtpAudioChannel::Tone tone, int clockrate, quint32 clockTick, qint64 samples)
+{
+ QPair<int,int> tf = toneFreqs(tone);
+ const float clockMult = 2.0 * M_PI / float(clockrate);
+ QByteArray chunk;
+ chunk.reserve(samples * SAMPLE_BYTES);
+ QDataStream output(&chunk, QIODevice::WriteOnly);
+ output.setByteOrder(QDataStream::LittleEndian);
+ for (quint32 i = 0; i < samples; ++i) {
+ quint16 val = 16383.0 * (sin(clockMult * clockTick * tf.first) + sin(clockMult * clockTick * tf.second));
+ output << val;
+ clockTick++;
+ }
+ return chunk;
+}
+
+class QXmppRtpAudioChannelPrivate
+{
+public:
+ QXmppRtpAudioChannelPrivate(QXmppRtpAudioChannel *qq);
+ QXmppCodec *codecForPayloadType(const QXmppJinglePayloadType &payloadType);
+
+ // signals
+ bool signalsEmitted;
+ qint64 writtenSinceLastEmit;
+
+ // RTP
+ QHostAddress remoteHost;
+ quint16 remotePort;
+
+ QByteArray incomingBuffer;
+ bool incomingBuffering;
+ QMap<int, QXmppCodec*> incomingCodecs;
+ int incomingMinimum;
+ int incomingMaximum;
+ // position of the head of the incoming buffer, in bytes
+ qint64 incomingPos;
+ quint16 incomingSequence;
+
+ QByteArray outgoingBuffer;
+ quint16 outgoingChunk;
+ QXmppCodec *outgoingCodec;
+ bool outgoingMarker;
+ bool outgoingPayloadNumbered;
+ quint16 outgoingSequence;
+ quint32 outgoingStamp;
+ QTimer *outgoingTimer;
+ QList<ToneInfo> outgoingTones;
+ QXmppJinglePayloadType outgoingTonesType;
+
+ quint32 outgoingSsrc;
+ QXmppJinglePayloadType payloadType;
+
+private:
+ QXmppRtpAudioChannel *q;
+};
+
+QXmppRtpAudioChannelPrivate::QXmppRtpAudioChannelPrivate(QXmppRtpAudioChannel *qq)
+ : signalsEmitted(false),
+ writtenSinceLastEmit(0),
+ incomingBuffering(true),
+ incomingMinimum(0),
+ incomingMaximum(0),
+ incomingPos(0),
+ incomingSequence(0),
+ outgoingCodec(0),
+ outgoingMarker(true),
+ outgoingPayloadNumbered(false),
+ outgoingSequence(1),
+ outgoingStamp(0),
+ outgoingSsrc(0),
+ q(qq)
+{
+ qRegisterMetaType<QXmppRtpAudioChannel::Tone>("QXmppRtpAudioChannel::Tone");
+ outgoingSsrc = qrand();
+}
+
+/// Returns the audio codec for the given payload type.
+///
+
+QXmppCodec *QXmppRtpAudioChannelPrivate::codecForPayloadType(const QXmppJinglePayloadType &payloadType)
+{
+ if (payloadType.id() == G711u)
+ return new QXmppG711uCodec(payloadType.clockrate());
+ else if (payloadType.id() == G711a)
+ return new QXmppG711aCodec(payloadType.clockrate());
+#ifdef QXMPP_USE_SPEEX
+ else if (payloadType.name().toLower() == "speex")
+ return new QXmppSpeexCodec(payloadType.clockrate());
+#endif
+ return 0;
+}
+
+/// Creates a new RTP audio channel.
+///
+/// \param parent
+
+QXmppRtpAudioChannel::QXmppRtpAudioChannel(QObject *parent)
+ : QIODevice(parent)
+{
+ d = new QXmppRtpAudioChannelPrivate(this);
+ QXmppLoggable *logParent = qobject_cast<QXmppLoggable*>(parent);
+ if (logParent) {
+ connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
+ logParent, SIGNAL(logMessage(QXmppLogger::MessageType,QString)));
+ }
+ d->outgoingTimer = new QTimer(this);
+ connect(d->outgoingTimer, SIGNAL(timeout()), this, SLOT(writeDatagram()));
+
+ // set supported codecs
+ QXmppJinglePayloadType payload;
+
+#ifdef QXMPP_USE_SPEEX
+ payload.setId(96);
+ payload.setChannels(1);
+ payload.setName("speex");
+ payload.setClockrate(8000);
+ m_outgoingPayloadTypes << payload;
+#endif
+
+ payload.setId(G711u);
+ payload.setChannels(1);
+ payload.setName("PCMU");
+ payload.setClockrate(8000);
+ m_outgoingPayloadTypes << payload;
+
+ payload.setId(G711a);
+ payload.setChannels(1);
+ payload.setName("PCMA");
+ payload.setClockrate(8000);
+ m_outgoingPayloadTypes << payload;
+
+ QMap<QString, QString> parameters;
+ parameters.insert("events", "0-15");
+ payload.setId(101);
+ payload.setChannels(1);
+ payload.setName("telephone-event");
+ payload.setClockrate(8000);
+ payload.setParameters(parameters);
+ m_outgoingPayloadTypes << payload;
+}
+
+/// Destroys an RTP audio channel.
+///
+
+QXmppRtpAudioChannel::~QXmppRtpAudioChannel()
+{
+ foreach (QXmppCodec *codec, d->incomingCodecs)
+ delete codec;
+ if (d->outgoingCodec)
+ delete d->outgoingCodec;
+ delete d;
+}
+
+/// Returns the number of bytes that are available for reading.
+///
+
+qint64 QXmppRtpAudioChannel::bytesAvailable() const
+{
+ return d->incomingBuffer.size();
+}
+
+/// Closes the RTP channel.
+///
+
+void QXmppRtpAudioChannel::close()
+{
+ d->outgoingTimer->stop();
+ QIODevice::close();
+}
+
+/// Processes an incoming RTP packet.
+///
+/// \param ba
+
+void QXmppRtpAudioChannel::datagramReceived(const QByteArray &ba)
+{
+ QXmppRtpPacket packet;
+ if (!packet.decode(ba))
+ return;
+
+#ifdef QXMPP_DEBUG_RTP
+ logReceived(packet.toString());
+#endif
+
+ // check sequence number
+#if 0
+ if (d->incomingSequence && packet.sequence != d->incomingSequence + 1)
+ warning(QString("RTP packet seq %1 is out of order, previous was %2")
+ .arg(QString::number(packet.sequence))
+ .arg(QString::number(d->incomingSequence)));
+#endif
+ d->incomingSequence = packet.sequence;
+
+ // get or create codec
+ QXmppCodec *codec = 0;
+ if (!d->incomingCodecs.contains(packet.type)) {
+ foreach (const QXmppJinglePayloadType &payload, m_incomingPayloadTypes) {
+ if (packet.type == payload.id()) {
+ codec = d->codecForPayloadType(payload);
+ break;
+ }
+ }
+ if (codec)
+ d->incomingCodecs.insert(packet.type, codec);
+ else
+ warning(QString("Could not find codec for RTP type %1").arg(QString::number(packet.type)));
+ } else {
+ codec = d->incomingCodecs.value(packet.type);
+ }
+ if (!codec)
+ return;
+
+ // determine packet's position in the buffer (in bytes)
+ qint64 packetOffset = 0;
+ if (!d->incomingBuffer.isEmpty()) {
+ packetOffset = packet.stamp * SAMPLE_BYTES - d->incomingPos;
+ if (packetOffset < 0) {
+#ifdef QXMPP_DEBUG_RTP_BUFFER
+ warning(QString("RTP packet stamp %1 is too old, buffer start is %2")
+ .arg(QString::number(packet.stamp))
+ .arg(QString::number(d->incomingPos)));
+#endif
+ return;
+ }
+ } else {
+ d->incomingPos = packet.stamp * SAMPLE_BYTES + (d->incomingPos % SAMPLE_BYTES);
+ }
+
+ // allocate space for new packet
+ // FIXME: this is wrong, we want the decoded data size!
+ qint64 packetLength = packet.payload.size();
+ if (packetOffset + packetLength > d->incomingBuffer.size())
+ d->incomingBuffer += QByteArray(packetOffset + packetLength - d->incomingBuffer.size(), 0);
+ QDataStream input(packet.payload);
+ QDataStream output(&d->incomingBuffer, QIODevice::WriteOnly);
+ output.device()->seek(packetOffset);
+ output.setByteOrder(QDataStream::LittleEndian);
+ codec->decode(input, output);
+
+ // check whether we are running late
+ if (d->incomingBuffer.size() > d->incomingMaximum)
+ {
+ qint64 droppedSize = d->incomingBuffer.size() - d->incomingMinimum;
+ const int remainder = droppedSize % SAMPLE_BYTES;
+ if (remainder)
+ droppedSize -= remainder;
+#ifdef QXMPP_DEBUG_RTP_BUFFER
+ warning(QString("Incoming RTP buffer is too full, dropping %1 bytes")
+ .arg(QString::number(droppedSize)));
+#endif
+ d->incomingBuffer.remove(0, droppedSize);
+ d->incomingPos += droppedSize;
+ }
+ // check whether we have filled the initial buffer
+ if (d->incomingBuffer.size() >= d->incomingMinimum)
+ d->incomingBuffering = false;
+ if (!d->incomingBuffering)
+ emit readyRead();
+}
+
+void QXmppRtpAudioChannel::emitSignals()
+{
+ emit bytesWritten(d->writtenSinceLastEmit);
+ d->writtenSinceLastEmit = 0;
+ d->signalsEmitted = false;
+}
+
+/// Returns true, as the RTP channel is a sequential device.
+///
+
+bool QXmppRtpAudioChannel::isSequential() const
+{
+ return true;
+}
+
+QIODevice::OpenMode QXmppRtpAudioChannel::openMode() const
+{
+ return QIODevice::openMode();
+}
+
+qint64 QXmppRtpAudioChannel::readData(char * data, qint64 maxSize)
+{
+ // if we are filling the buffer, return empty samples
+ if (d->incomingBuffering)
+ {
+ // FIXME: if we are asked for a non-integer number of samples,
+ // we will return junk on next read as we don't increment d->incomingPos
+ memset(data, 0, maxSize);
+ return maxSize;
+ }
+
+ qint64 readSize = qMin(maxSize, qint64(d->incomingBuffer.size()));
+ memcpy(data, d->incomingBuffer.constData(), readSize);
+ d->incomingBuffer.remove(0, readSize);
+ if (readSize < maxSize)
+ {
+#ifdef QXMPP_DEBUG_RTP
+ debug(QString("QXmppRtpAudioChannel::readData missing %1 bytes").arg(QString::number(maxSize - readSize)));
+#endif
+ memset(data + readSize, 0, maxSize - readSize);
+ }
+
+ // add local DTMF echo
+ if (!d->outgoingTones.isEmpty()) {
+ const int headOffset = d->incomingPos % SAMPLE_BYTES;
+ const int samples = (headOffset + maxSize + SAMPLE_BYTES - 1) / SAMPLE_BYTES;
+ const QByteArray chunk = renderTone(
+ d->outgoingTones[0].tone,
+ d->payloadType.clockrate(),
+ d->incomingPos / SAMPLE_BYTES - d->outgoingTones[0].incomingStart,
+ samples);
+ memcpy(data, chunk.constData() + headOffset, maxSize);
+ }
+
+ d->incomingPos += maxSize;
+ return maxSize;
+}
+
+/// Returns the RTP channel's payload type.
+///
+/// You can use this to determine the QAudioFormat to use with your
+/// QAudioInput/QAudioOutput.
+
+QXmppJinglePayloadType QXmppRtpAudioChannel::payloadType() const
+{
+ return d->payloadType;
+}
+
+void QXmppRtpAudioChannel::payloadTypesChanged()
+{
+ // delete incoming codecs
+ foreach (QXmppCodec *codec, d->incomingCodecs)
+ delete codec;
+ d->incomingCodecs.clear();
+
+ // delete outgoing codec
+ if (d->outgoingCodec) {
+ delete d->outgoingCodec;
+ d->outgoingCodec = 0;
+ }
+
+ // create outgoing codec
+ foreach (const QXmppJinglePayloadType &outgoingType, m_outgoingPayloadTypes) {
+ // check for telephony events
+ if (outgoingType.name() == "telephone-event") {
+ d->outgoingTonesType = outgoingType;
+ }
+ else if (!d->outgoingCodec) {
+ QXmppCodec *codec = d->codecForPayloadType(outgoingType);
+ if (codec) {
+ d->payloadType = outgoingType;
+ d->outgoingCodec = codec;
+ }
+ }
+ }
+
+ // size in bytes of an decoded packet
+ d->outgoingChunk = SAMPLE_BYTES * d->payloadType.ptime() * d->payloadType.clockrate() / 1000;
+ d->outgoingTimer->setInterval(d->payloadType.ptime());
+
+ d->incomingMinimum = d->outgoingChunk * 5;
+ d->incomingMaximum = d->outgoingChunk * 15;
+
+ open(QIODevice::ReadWrite | QIODevice::Unbuffered);
+}
+
+/// Returns the position in the received audio data.
+
+qint64 QXmppRtpAudioChannel::pos() const
+{
+ return d->incomingPos;
+}
+
+/// Seeks in the received audio data.
+///
+/// Seeking backwards will result in empty samples being added at the start
+/// of the buffer.
+///
+/// \param pos
+
+bool QXmppRtpAudioChannel::seek(qint64 pos)
+{
+ qint64 delta = pos - d->incomingPos;
+ if (delta < 0)
+ d->incomingBuffer.prepend(QByteArray(-delta, 0));
+ else
+ d->incomingBuffer.remove(0, delta);
+ d->incomingPos = pos;
+ return true;
+}
+
+/// Starts sending the specified DTMF tone.
+///
+/// \param tone
+
+void QXmppRtpAudioChannel::startTone(QXmppRtpAudioChannel::Tone tone)
+{
+ ToneInfo info;
+ info.tone = tone;
+ info.incomingStart = d->incomingPos / SAMPLE_BYTES;
+ info.outgoingStart = d->outgoingStamp;
+ info.finished = false;
+ d->outgoingTones << info;
+}
+
+/// Stops sending the specified DTMF tone.
+///
+/// \param tone
+
+void QXmppRtpAudioChannel::stopTone(QXmppRtpAudioChannel::Tone tone)
+{
+ for (int i = 0; i < d->outgoingTones.size(); ++i) {
+ if (d->outgoingTones[i].tone == tone) {
+ d->outgoingTones[i].finished = true;
+ break;
+ }
+ }
+}
+
+qint64 QXmppRtpAudioChannel::writeData(const char * data, qint64 maxSize)
+{
+ if (!d->outgoingCodec) {
+ warning("QXmppRtpAudioChannel::writeData before codec was set");
+ return -1;
+ }
+
+ d->outgoingBuffer += QByteArray::fromRawData(data, maxSize);
+
+ // start sending audio chunks
+ if (!d->outgoingTimer->isActive())
+ d->outgoingTimer->start();
+
+ return maxSize;
+}
+
+void QXmppRtpAudioChannel::writeDatagram()
+{
+ // read audio chunk
+ QByteArray chunk;
+ if (d->outgoingBuffer.size() < d->outgoingChunk) {
+#ifdef QXMPP_DEBUG_RTP_BUFFER
+ warning("Outgoing RTP buffer is starved");
+#endif
+ chunk = QByteArray(d->outgoingChunk, 0);
+ } else {
+ chunk = d->outgoingBuffer.left(d->outgoingChunk);
+ d->outgoingBuffer.remove(0, d->outgoingChunk);
+ }
+
+ bool sendAudio = true;
+ if (!d->outgoingTones.isEmpty()) {
+ const quint32 packetTicks = (d->payloadType.clockrate() * d->payloadType.ptime()) / 1000;
+ const ToneInfo info = d->outgoingTones[0];
+
+ if (d->outgoingTonesType.id()) {
+ // send RFC 2833 DTMF
+ QXmppRtpPacket packet;
+ packet.version = RTP_VERSION;
+ packet.marker = (info.outgoingStart == d->outgoingStamp);
+ packet.type = d->outgoingTonesType.id();
+ packet.sequence = d->outgoingSequence;
+ packet.stamp = info.outgoingStart;
+ packet.ssrc = d->outgoingSsrc;
+
+ QDataStream output(&packet.payload, QIODevice::WriteOnly);
+ output << quint8(info.tone);
+ output << quint8(info.finished ? 0x80 : 0x00);
+ output << quint16(d->outgoingStamp + packetTicks - info.outgoingStart);
+#ifdef QXMPP_DEBUG_RTP
+ logSent(packet.toString());
+#endif
+ emit sendDatagram(packet.encode());
+ d->outgoingSequence++;
+ d->outgoingStamp += packetTicks;
+
+ sendAudio = false;
+ } else {
+ // generate in-band DTMF
+ chunk = renderTone(info.tone, d->payloadType.clockrate(), d->outgoingStamp - info.outgoingStart, packetTicks);
+ }
+
+ // if the tone is finished, remove it
+ if (info.finished)
+ d->outgoingTones.removeFirst();
+ }
+
+ if (sendAudio) {
+ // send audio data
+ QXmppRtpPacket packet;
+ packet.version = RTP_VERSION;
+ if (d->outgoingMarker)
+ {
+ packet.marker = true;
+ d->outgoingMarker = false;
+ } else {
+ packet.marker = false;
+ }
+ packet.type = d->payloadType.id();
+ packet.sequence = d->outgoingSequence;
+ packet.stamp = d->outgoingStamp;
+ packet.ssrc = d->outgoingSsrc;
+
+ // encode audio chunk
+ QDataStream input(chunk);
+ input.setByteOrder(QDataStream::LittleEndian);
+ QDataStream output(&packet.payload, QIODevice::WriteOnly);
+ const qint64 packetTicks = d->outgoingCodec->encode(input, output);
+
+#ifdef QXMPP_DEBUG_RTP
+ logSent(packet.toString());
+#endif
+ emit sendDatagram(packet.encode());
+ d->outgoingSequence++;
+ d->outgoingStamp += packetTicks;
+ }
+
+ // queue signals
+ d->writtenSinceLastEmit += chunk.size();
+ if (!d->signalsEmitted && !signalsBlocked()) {
+ d->signalsEmitted = true;
+ QMetaObject::invokeMethod(this, "emitSignals", Qt::QueuedConnection);
+ }
+}
+
+/** Constructs a null video frame.
+ */
+QXmppVideoFrame::QXmppVideoFrame()
+ : m_bytesPerLine(0),
+ m_height(0),
+ m_mappedBytes(0),
+ m_pixelFormat(Format_Invalid),
+ m_width(0)
+{
+}
+
+/** Constructs a video frame of the given pixel format and size in pixels.
+ *
+ * @param bytes
+ * @param size
+ * @param bytesPerLine
+ * @param format
+ */
+QXmppVideoFrame::QXmppVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format)
+ : m_bytesPerLine(bytesPerLine),
+ m_height(size.height()),
+ m_mappedBytes(bytes),
+ m_pixelFormat(format),
+ m_width(size.width())
+{
+ m_data.resize(bytes);
+}
+
+uchar *QXmppVideoFrame::bits()
+{
+ return (uchar*)m_data.data();
+}
+
+const uchar *QXmppVideoFrame::bits() const
+{
+ return (const uchar*)m_data.constData();
+}
+
+/** Returns the number of bytes in a scan line.
+ */
+int QXmppVideoFrame::bytesPerLine() const
+{
+ return m_bytesPerLine;
+}
+
+/** Returns the height of a video frame.
+ */
+int QXmppVideoFrame::height() const
+{
+ return m_height;
+}
+
+/** Returns true if the frame is valid.
+ */
+bool QXmppVideoFrame::isValid() const
+{
+ return m_pixelFormat != Format_Invalid &&
+ m_height > 0 && m_width > 0 &&
+ m_mappedBytes > 0;
+}
+
+/** Returns the number of bytes occupied by the mapped frame data.
+ */
+int QXmppVideoFrame::mappedBytes() const
+{
+ return m_mappedBytes;
+}
+
+/** Returns the color format of a video frame.
+ */
+QXmppVideoFrame::PixelFormat QXmppVideoFrame::pixelFormat() const
+{
+ return m_pixelFormat;
+}
+
+/** Returns the size of a video frame.
+ */
+QSize QXmppVideoFrame::size() const
+{
+ return QSize(m_width, m_height);
+}
+
+/** Returns the width of a video frame.
+ */
+int QXmppVideoFrame::width() const
+{
+ return m_width;
+}
+
+class QXmppRtpVideoChannelPrivate
+{
+public:
+ QXmppRtpVideoChannelPrivate();
+ QMap<int, QXmppVideoDecoder*> decoders;
+ QXmppVideoEncoder *encoder;
+ QList<QXmppVideoFrame> frames;
+
+ // local
+ QXmppVideoFormat outgoingFormat;
+ quint8 outgoingId;
+ quint16 outgoingSequence;
+ quint32 outgoingStamp;
+ quint32 outgoingSsrc;
+};
+
+QXmppRtpVideoChannelPrivate::QXmppRtpVideoChannelPrivate()
+ : encoder(0),
+ outgoingId(0),
+ outgoingSequence(1),
+ outgoingStamp(0),
+ outgoingSsrc(0)
+{
+ outgoingSsrc = qrand();
+}
+
+QXmppRtpVideoChannel::QXmppRtpVideoChannel(QObject *parent)
+ : QXmppLoggable(parent)
+{
+ d = new QXmppRtpVideoChannelPrivate;
+ d->outgoingFormat.setFrameRate(15.0);
+ d->outgoingFormat.setFrameSize(QSize(320, 240));
+ d->outgoingFormat.setPixelFormat(QXmppVideoFrame::Format_YUYV);
+
+ // set supported codecs
+ QXmppVideoEncoder *encoder;
+ QXmppJinglePayloadType payload;
+ Q_UNUSED(encoder);
+ Q_UNUSED(payload);
+
+#ifdef QXMPP_USE_VPX
+ encoder = new QXmppVpxEncoder;
+ encoder->setFormat(d->outgoingFormat);
+ payload.setId(96);
+ payload.setName("vp8");
+ payload.setClockrate(90000);
+ payload.setParameters(encoder->parameters());
+ m_outgoingPayloadTypes << payload;
+ delete encoder;
+#endif
+
+#ifdef QXMPP_USE_THEORA
+ encoder = new QXmppTheoraEncoder;
+ encoder->setFormat(d->outgoingFormat);
+ payload.setId(97);
+ payload.setName("theora");
+ payload.setClockrate(90000);
+ payload.setParameters(encoder->parameters());
+ m_outgoingPayloadTypes << payload;
+ delete encoder;
+#endif
+}
+
+QXmppRtpVideoChannel::~QXmppRtpVideoChannel()
+{
+ foreach (QXmppVideoDecoder *decoder, d->decoders)
+ delete decoder;
+ if (d->encoder)
+ delete d->encoder;
+ delete d;
+}
+
+/// Closes the RTP channel.
+///
+
+void QXmppRtpVideoChannel::close()
+{
+}
+
+/// Processes an incoming RTP video packet.
+///
+/// \param ba
+
+void QXmppRtpVideoChannel::datagramReceived(const QByteArray &ba)
+{
+ QXmppRtpPacket packet;
+ if (!packet.decode(ba))
+ return;
+
+#ifdef QXMPP_DEBUG_RTP
+ logReceived(packet.toString());
+#endif
+
+ // get codec
+ QXmppVideoDecoder *decoder = d->decoders.value(packet.type);
+ if (!decoder)
+ return;
+ d->frames << decoder->handlePacket(packet);
+}
+
+QXmppVideoFormat QXmppRtpVideoChannel::decoderFormat() const
+{
+ if (d->decoders.isEmpty())
+ return QXmppVideoFormat();
+ const int key = d->decoders.keys().first();
+ return d->decoders.value(key)->format();
+}
+
+QXmppVideoFormat QXmppRtpVideoChannel::encoderFormat() const
+{
+ return d->outgoingFormat;
+}
+
+void QXmppRtpVideoChannel::setEncoderFormat(const QXmppVideoFormat &format)
+{
+ if (d->encoder && !d->encoder->setFormat(format))
+ return;
+ d->outgoingFormat = format;
+}
+
+QIODevice::OpenMode QXmppRtpVideoChannel::openMode() const
+{
+ QIODevice::OpenMode mode = QIODevice::NotOpen;
+ if (!d->decoders.isEmpty())
+ mode |= QIODevice::ReadOnly;
+ if (d->encoder)
+ mode |= QIODevice::WriteOnly;
+ return mode;
+}
+
+void QXmppRtpVideoChannel::payloadTypesChanged()
+{
+ // refresh decoders
+ foreach (QXmppVideoDecoder *decoder, d->decoders)
+ delete decoder;
+ d->decoders.clear();
+ foreach (const QXmppJinglePayloadType &payload, m_incomingPayloadTypes) {
+ QXmppVideoDecoder *decoder = 0;
+ if (false)
+ {}
+#ifdef QXMPP_USE_THEORA
+ else if (payload.name().toLower() == "theora")
+ decoder = new QXmppTheoraDecoder;
+#endif
+#ifdef QXMPP_USE_VPX
+ else if (payload.name().toLower() == "vp8")
+ decoder = new QXmppVpxDecoder;
+#endif
+ if (decoder) {
+ decoder->setParameters(payload.parameters());
+ d->decoders.insert(payload.id(), decoder);
+ }
+ }
+
+ // refresh encoder
+ if (d->encoder) {
+ delete d->encoder;
+ d->encoder = 0;
+ }
+ foreach (const QXmppJinglePayloadType &payload, m_outgoingPayloadTypes) {
+ QXmppVideoEncoder *encoder = 0;
+ if (false)
+ {}
+#ifdef QXMPP_USE_THEORA
+ else if (payload.name().toLower() == "theora")
+ encoder = new QXmppTheoraEncoder;
+#endif
+#ifdef QXMPP_USE_VPX
+ else if (payload.name().toLower() == "vp8") {
+ encoder = new QXmppVpxEncoder;
+ }
+#endif
+ if (encoder) {
+ encoder->setFormat(d->outgoingFormat);
+ d->encoder = encoder;
+ d->outgoingId = payload.id();
+ break;
+ }
+ }
+}
+
+QList<QXmppVideoFrame> QXmppRtpVideoChannel::readFrames()
+{
+ const QList<QXmppVideoFrame> frames = d->frames;
+ d->frames.clear();
+ return frames;
+}
+
+void QXmppRtpVideoChannel::writeFrame(const QXmppVideoFrame &frame)
+{
+ if (!d->encoder) {
+ warning("QXmppRtpVideoChannel::writeFrame before codec was set");
+ return;
+ }
+
+ QXmppRtpPacket packet;
+ packet.version = RTP_VERSION;
+ packet.marker = false;
+ packet.type = d->outgoingId;
+ packet.ssrc = d->outgoingSsrc;
+ foreach (const QByteArray &payload, d->encoder->handleFrame(frame)) {
+ packet.sequence = d->outgoingSequence++;
+ packet.stamp = d->outgoingStamp;
+ packet.payload = payload;
+#ifdef QXMPP_DEBUG_RTP
+ logSent(packet.toString());
+#endif
+ emit sendDatagram(packet.encode());
+ }
+ d->outgoingStamp += 1;
+}
+
diff --git a/src/base/QXmppRtpChannel.h b/src/base/QXmppRtpChannel.h
new file mode 100644
index 00000000..0af6596c
--- /dev/null
+++ b/src/base/QXmppRtpChannel.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 QXMPPRTPCHANNEL_H
+#define QXMPPRTPCHANNEL_H
+
+#include <QIODevice>
+#include <QSize>
+
+#include "QXmppJingleIq.h"
+#include "QXmppLogger.h"
+
+class QXmppCodec;
+class QXmppJinglePayloadType;
+class QXmppRtpAudioChannelPrivate;
+class QXmppRtpVideoChannelPrivate;
+
+/// \brief The QXmppRtpPacket class represents an RTP packet.
+///
+
+class QXmppRtpPacket
+{
+public:
+ bool decode(const QByteArray &ba);
+ QByteArray encode() const;
+ QString toString() const;
+
+ quint8 version;
+ bool marker;
+ quint8 type;
+ quint32 ssrc;
+ QList<quint32> csrc;
+ quint16 sequence;
+ quint32 stamp;
+ QByteArray payload;
+};
+
+class QXmppRtpChannel
+{
+public:
+ QXmppRtpChannel();
+
+ virtual void close() = 0;
+ virtual QIODevice::OpenMode openMode() const = 0;
+ QList<QXmppJinglePayloadType> localPayloadTypes();
+ void setRemotePayloadTypes(const QList<QXmppJinglePayloadType> &remotePayloadTypes);
+
+protected:
+ virtual void payloadTypesChanged();
+
+ QList<QXmppJinglePayloadType> m_incomingPayloadTypes;
+ QList<QXmppJinglePayloadType> m_outgoingPayloadTypes;
+ bool m_outgoingPayloadNumbered;
+};
+
+/// \brief The QXmppRtpAudioChannel class represents an RTP audio channel to a remote party.
+///
+/// It acts as a QIODevice so that you can read / write audio samples, for
+/// instance using a QAudioOutput and a QAudioInput.
+///
+/// \note THIS API IS NOT FINALIZED YET
+
+class QXmppRtpAudioChannel : public QIODevice, public QXmppRtpChannel
+{
+ Q_OBJECT
+ Q_ENUMS(Tone)
+
+public:
+ /// This enum is used to describe a DTMF tone.
+ enum Tone {
+ Tone_0 = 0, ///< Tone for the 0 key.
+ Tone_1, ///< Tone for the 1 key.
+ Tone_2, ///< Tone for the 2 key.
+ Tone_3, ///< Tone for the 3 key.
+ Tone_4, ///< Tone for the 4 key.
+ Tone_5, ///< Tone for the 5 key.
+ Tone_6, ///< Tone for the 6 key.
+ Tone_7, ///< Tone for the 7 key.
+ Tone_8, ///< Tone for the 8 key.
+ Tone_9, ///< Tone for the 9 key.
+ Tone_Star, ///< Tone for the * key.
+ Tone_Pound, ///< Tone for the # key.
+ Tone_A, ///< Tone for the A key.
+ Tone_B, ///< Tone for the B key.
+ Tone_C, ///< Tone for the C key.
+ Tone_D ///< Tone for the D key.
+ };
+
+ QXmppRtpAudioChannel(QObject *parent = 0);
+ ~QXmppRtpAudioChannel();
+
+ QXmppJinglePayloadType payloadType() const;
+
+ /// \cond
+ qint64 bytesAvailable() const;
+ void close();
+ bool isSequential() const;
+ QIODevice::OpenMode openMode() const;
+ qint64 pos() const;
+ bool seek(qint64 pos);
+ /// \endcond
+
+signals:
+ /// \brief This signal is emitted when a datagram needs to be sent.
+ void sendDatagram(const QByteArray &ba);
+
+ /// \brief This signal is emitted to send logging messages.
+ void logMessage(QXmppLogger::MessageType type, const QString &msg);
+
+public slots:
+ void datagramReceived(const QByteArray &ba);
+ void startTone(QXmppRtpAudioChannel::Tone tone);
+ void stopTone(QXmppRtpAudioChannel::Tone tone);
+
+protected:
+ /// \cond
+ void debug(const QString &message)
+ {
+ emit logMessage(QXmppLogger::DebugMessage, qxmpp_loggable_trace(message));
+ }
+
+ void warning(const QString &message)
+ {
+ emit logMessage(QXmppLogger::WarningMessage, qxmpp_loggable_trace(message));
+ }
+
+ void logReceived(const QString &message)
+ {
+ emit logMessage(QXmppLogger::ReceivedMessage, qxmpp_loggable_trace(message));
+ }
+
+ void logSent(const QString &message)
+ {
+ emit logMessage(QXmppLogger::SentMessage, qxmpp_loggable_trace(message));
+ }
+
+ void payloadTypesChanged();
+ qint64 readData(char * data, qint64 maxSize);
+ qint64 writeData(const char * data, qint64 maxSize);
+ /// \endcond
+
+private slots:
+ void emitSignals();
+ void writeDatagram();
+
+private:
+ friend class QXmppRtpAudioChannelPrivate;
+ QXmppRtpAudioChannelPrivate * d;
+};
+
+/// \brief The QXmppVideoFrame class provides a representation of a frame of video data.
+///
+/// \note THIS API IS NOT FINALIZED YET
+
+class QXmppVideoFrame
+{
+public:
+ enum PixelFormat {
+ Format_Invalid = 0,
+ Format_RGB32 = 3,
+ Format_RGB24 = 4,
+ Format_YUV420P = 18,
+ Format_UYVY = 20,
+ Format_YUYV = 21,
+ };
+
+ QXmppVideoFrame();
+ QXmppVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format);
+ uchar *bits();
+ const uchar *bits() const;
+ int bytesPerLine() const;
+ int height() const;
+ bool isValid() const;
+ int mappedBytes() const;
+ PixelFormat pixelFormat() const;
+ QSize size() const;
+ int width() const;
+
+private:
+ int m_bytesPerLine;
+ QByteArray m_data;
+ int m_height;
+ int m_mappedBytes;
+ PixelFormat m_pixelFormat;
+ int m_width;
+};
+
+class QXmppVideoFormat
+{
+public:
+ int frameHeight() const {
+ return m_frameSize.height();
+ }
+
+ int frameWidth() const {
+ return m_frameSize.width();
+ }
+
+ qreal frameRate() const {
+ return m_frameRate;
+ }
+
+ void setFrameRate(qreal frameRate) {
+ m_frameRate = frameRate;
+ }
+
+ QSize frameSize() const {
+ return m_frameSize;
+ }
+
+ void setFrameSize(const QSize &frameSize) {
+ m_frameSize = frameSize;
+ }
+
+ QXmppVideoFrame::PixelFormat pixelFormat() const {
+ return m_pixelFormat;
+ }
+
+ void setPixelFormat(QXmppVideoFrame::PixelFormat pixelFormat) {
+ m_pixelFormat = pixelFormat;
+ }
+
+private:
+ qreal m_frameRate;
+ QSize m_frameSize;
+ QXmppVideoFrame::PixelFormat m_pixelFormat;
+};
+
+
+/// \brief The QXmppRtpVideoChannel class represents an RTP video channel to a remote party.
+///
+/// \note THIS API IS NOT FINALIZED YET
+
+class QXmppRtpVideoChannel : public QXmppLoggable, public QXmppRtpChannel
+{
+ Q_OBJECT
+
+public:
+ QXmppRtpVideoChannel(QObject *parent = 0);
+ ~QXmppRtpVideoChannel();
+
+ // incoming stream
+ QXmppVideoFormat decoderFormat() const;
+ QList<QXmppVideoFrame> readFrames();
+
+ // outgoing stream
+ QXmppVideoFormat encoderFormat() const;
+ void setEncoderFormat(const QXmppVideoFormat &format);
+ void writeFrame(const QXmppVideoFrame &frame);
+
+ QIODevice::OpenMode openMode() const;
+ void close();
+
+signals:
+ /// \brief This signal is emitted when a datagram needs to be sent.
+ void sendDatagram(const QByteArray &ba);
+
+public slots:
+ void datagramReceived(const QByteArray &ba);
+
+protected:
+ void payloadTypesChanged();
+
+private:
+ friend class QXmppRtpVideoChannelPrivate;
+ QXmppRtpVideoChannelPrivate * d;
+};
+
+#endif
diff --git a/src/base/QXmppSaslAuth.cpp b/src/base/QXmppSaslAuth.cpp
new file mode 100644
index 00000000..a528cd6d
--- /dev/null
+++ b/src/base/QXmppSaslAuth.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 <cstdlib>
+
+#include <QCryptographicHash>
+
+#include "QXmppSaslAuth.h"
+#include "QXmppUtils.h"
+
+QByteArray QXmppSaslDigestMd5::authzid() const
+{
+ return m_authzid;
+}
+
+void QXmppSaslDigestMd5::setAuthzid(const QByteArray &authzid)
+{
+ m_authzid = authzid;
+}
+
+QByteArray QXmppSaslDigestMd5::cnonce() const
+{
+ return m_cnonce;
+}
+
+void QXmppSaslDigestMd5::setCnonce(const QByteArray &cnonce)
+{
+ m_cnonce = cnonce;
+}
+
+QByteArray QXmppSaslDigestMd5::digestUri() const
+{
+ return m_digestUri;
+}
+
+void QXmppSaslDigestMd5::setDigestUri(const QByteArray &digestUri)
+{
+ m_digestUri = digestUri;
+}
+
+QByteArray QXmppSaslDigestMd5::nc() const
+{
+ return m_nc;
+}
+
+void QXmppSaslDigestMd5::setNc(const QByteArray &nc)
+{
+ m_nc = nc;
+}
+
+QByteArray QXmppSaslDigestMd5::nonce() const
+{
+ return m_nonce;
+}
+
+void QXmppSaslDigestMd5::setNonce(const QByteArray &nonce)
+{
+ m_nonce = nonce;
+}
+
+QByteArray QXmppSaslDigestMd5::qop() const
+{
+ return m_qop;
+}
+
+void QXmppSaslDigestMd5::setQop(const QByteArray &qop)
+{
+ m_qop = qop;
+}
+
+void QXmppSaslDigestMd5::setSecret(const QByteArray &secret)
+{
+ m_secret = secret;
+}
+
+QByteArray QXmppSaslDigestMd5::generateNonce()
+{
+ QByteArray nonce = generateRandomBytes(32);
+
+ // The random data can the '=' char is not valid as it is a delimiter,
+ // so to be safe, base64 the nonce
+ return nonce.toBase64();
+}
+
+/// Calculate digest response for use with XMPP/SASL.
+///
+/// \param A2
+///
+
+QByteArray QXmppSaslDigestMd5::calculateDigest(const QByteArray &A2) const
+{
+ QByteArray ha1 = m_secret + ':' + m_nonce + ':' + m_cnonce;
+
+ if (!m_authzid.isEmpty())
+ ha1 += ':' + m_authzid;
+
+ return calculateDigest(ha1, A2);
+}
+
+/// Calculate generic digest response.
+///
+/// \param A1
+/// \param A2
+///
+
+QByteArray QXmppSaslDigestMd5::calculateDigest(const QByteArray &A1, const QByteArray &A2) const
+{
+ QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex();
+ QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex();
+ QByteArray KD;
+ if (m_qop == "auth" || m_qop == "auth-int")
+ KD = HA1 + ':' + m_nonce + ':' + m_nc + ':' + m_cnonce + ':' + m_qop + ':' + HA2;
+ else
+ KD = HA1 + ':' + m_nonce + ':' + HA2;
+ return QCryptographicHash::hash(KD, QCryptographicHash::Md5).toHex();
+}
+
+QMap<QByteArray, QByteArray> QXmppSaslDigestMd5::parseMessage(const QByteArray &ba)
+{
+ QMap<QByteArray, QByteArray> map;
+ int startIndex = 0;
+ int pos = 0;
+ while ((pos = ba.indexOf("=", startIndex)) >= 0)
+ {
+ // key get name and skip equals
+ const QByteArray key = ba.mid(startIndex, pos - startIndex).trimmed();
+ pos++;
+
+ // check whether string is quoted
+ if (ba.at(pos) == '"')
+ {
+ // skip opening quote
+ pos++;
+ int endPos = ba.indexOf('"', pos);
+ // skip quoted quotes
+ while (endPos >= 0 && ba.at(endPos - 1) == '\\')
+ endPos = ba.indexOf('"', endPos + 1);
+ if (endPos < 0)
+ {
+ qWarning("Unfinished quoted string");
+ return map;
+ }
+ // unquote
+ QByteArray value = ba.mid(pos, endPos - pos);
+ value.replace("\\\"", "\"");
+ value.replace("\\\\", "\\");
+ map[key] = value;
+ // skip closing quote and comma
+ startIndex = endPos + 2;
+ } else {
+ // non-quoted string
+ int endPos = ba.indexOf(',', pos);
+ if (endPos < 0)
+ endPos = ba.size();
+ map[key] = ba.mid(pos, endPos - pos);
+ // skip comma
+ startIndex = endPos + 1;
+ }
+ }
+ return map;
+}
+
+QByteArray QXmppSaslDigestMd5::serializeMessage(const QMap<QByteArray, QByteArray> &map)
+{
+ QByteArray ba;
+ foreach (const QByteArray &key, map.keys())
+ {
+ if (!ba.isEmpty())
+ ba.append(',');
+ ba.append(key + "=");
+ QByteArray value = map[key];
+ const char *separators = "()<>@,;:\\\"/[]?={} \t";
+ bool quote = false;
+ for (const char *c = separators; *c; c++)
+ {
+ if (value.contains(*c))
+ {
+ quote = true;
+ break;
+ }
+ }
+ if (quote)
+ {
+ value.replace("\\", "\\\\");
+ value.replace("\"", "\\\"");
+ ba.append("\"" + value + "\"");
+ }
+ else
+ ba.append(value);
+ }
+ return ba;
+}
+
diff --git a/src/base/QXmppSaslAuth.h b/src/base/QXmppSaslAuth.h
new file mode 100644
index 00000000..409636cb
--- /dev/null
+++ b/src/base/QXmppSaslAuth.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 QXMPPSASLAUTH_H
+#define QXMPPSASLAUTH_H
+
+#include <QByteArray>
+#include <QMap>
+
+class QXmppSaslDigestMd5
+{
+public:
+ QByteArray authzid() const;
+ void setAuthzid(const QByteArray &cnonce);
+
+ QByteArray cnonce() const;
+ void setCnonce(const QByteArray &cnonce);
+
+ QByteArray digestUri() const;
+ void setDigestUri(const QByteArray &digestUri);
+
+ QByteArray nc() const;
+ void setNc(const QByteArray &nc);
+
+ QByteArray nonce() const;
+ void setNonce(const QByteArray &nonce);
+
+ QByteArray qop() const;
+ void setQop(const QByteArray &qop);
+
+ void setSecret(const QByteArray &secret);
+
+ QByteArray calculateDigest(const QByteArray &A2) const;
+ QByteArray calculateDigest(const QByteArray &A1, const QByteArray &A2) const;
+ static QByteArray generateNonce();
+
+ // message parsing and serialization
+ static QMap<QByteArray, QByteArray> parseMessage(const QByteArray &ba);
+ static QByteArray serializeMessage(const QMap<QByteArray, QByteArray> &map);
+
+private:
+ QByteArray m_authzid;
+ QByteArray m_cnonce;
+ QByteArray m_digestUri;
+ QByteArray m_nc;
+ QByteArray m_nonce;
+ QByteArray m_qop;
+ QByteArray m_secret;
+};
+
+#endif
diff --git a/src/base/QXmppSessionIq.cpp b/src/base/QXmppSessionIq.cpp
new file mode 100644
index 00000000..6672cc0e
--- /dev/null
+++ b/src/base/QXmppSessionIq.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+#include <QXmlStreamWriter>
+
+#include "QXmppSessionIq.h"
+#include "QXmppConstants.h"
+#include "QXmppUtils.h"
+
+bool QXmppSessionIq::isSessionIq(const QDomElement &element)
+{
+ QDomElement sessionElement = element.firstChildElement("session");
+ return (sessionElement.namespaceURI() == ns_session);
+}
+
+void QXmppSessionIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("session");;
+ writer->writeAttribute("xmlns", ns_session);
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppSessionIq.h b/src/base/QXmppSessionIq.h
new file mode 100644
index 00000000..2cbcd58c
--- /dev/null
+++ b/src/base/QXmppSessionIq.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPSESSIONIQ_H
+#define QXMPPSESSIONIQ_H
+
+#include "QXmppIq.h"
+
+/// \brief The QXmppSessionIq class represents an IQ used for session
+/// establishment as defined by RFC 5921.
+///
+/// \ingroup Stanzas
+
+class QXmppSessionIq : public QXmppIq
+{
+public:
+ /// \cond
+ static bool isSessionIq(const QDomElement &element);
+ /// \endcond
+
+private:
+ /// \cond
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+};
+
+#endif // QXMPPSESSION_H
diff --git a/src/base/QXmppSocks.cpp b/src/base/QXmppSocks.cpp
new file mode 100644
index 00000000..1c6f162c
--- /dev/null
+++ b/src/base/QXmppSocks.cpp
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 <QDataStream>
+#include <QEventLoop>
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QTimer>
+
+#include "QXmppSocks.h"
+
+const static char SocksVersion = 5;
+
+enum AuthenticationMethod {
+ NoAuthentication = 0,
+ GSSAPI = 1,
+ UsernamePassword = 2,
+};
+
+enum Command {
+ ConnectCommand = 1,
+ BindCommand = 2,
+ AssociateCommand = 3,
+};
+
+enum AddressType {
+ IPv4Address = 1,
+ DomainName = 3,
+ IPv6Address = 4,
+};
+
+enum ReplyType {
+ Succeeded = 0,
+ SocksFailure = 1,
+ ConnectionNotAllowed = 2,
+ NetworkUnreachable = 3,
+ HostUnreachable = 4,
+ ConnectionRefused = 5,
+ TtlExpired = 6,
+ CommandNotSupported = 7,
+ AddressTypeNotSupported = 8,
+};
+
+enum State {
+ ConnectState = 0,
+ CommandState = 1,
+ ReadyState = 2,
+};
+
+static QByteArray encodeHostAndPort(quint8 type, const QByteArray &host, quint16 port)
+{
+ QByteArray buffer;
+ QDataStream stream(&buffer, QIODevice::WriteOnly);
+ // set host name
+ quint8 hostLength = host.size();
+ stream << type;
+ stream << hostLength;
+ stream.writeRawData(host.constData(), hostLength);
+ // set port
+ stream << port;
+ return buffer;
+}
+
+static bool parseHostAndPort(const QByteArray buffer, quint8 &type, QByteArray &host, quint16 &port)
+{
+ if (buffer.size() < 4)
+ return false;
+
+ QDataStream stream(buffer);
+ // get host name
+ quint8 hostLength;
+ stream >> type;
+ stream >> hostLength;
+ if (buffer.size() < hostLength + 4)
+ {
+ qWarning("Invalid host length");
+ return false;
+ }
+ host.resize(hostLength);
+ stream.readRawData(host.data(), hostLength);
+ // get port
+ stream >> port;
+ return true;
+}
+
+QXmppSocksClient::QXmppSocksClient(const QHostAddress &proxyAddress, quint16 proxyPort, QObject *parent)
+ : QTcpSocket(parent),
+ m_proxyAddress(proxyAddress),
+ m_proxyPort(proxyPort),
+ m_step(ConnectState)
+{
+ connect(this, SIGNAL(connected()), this, SLOT(slotConnected()));
+ connect(this, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
+}
+
+void QXmppSocksClient::connectToHost(const QString &hostName, quint16 hostPort)
+{
+ m_hostName = hostName;
+ m_hostPort = hostPort;
+ QTcpSocket::connectToHost(m_proxyAddress, m_proxyPort);
+}
+
+void QXmppSocksClient::slotConnected()
+{
+ m_step = ConnectState;
+
+ // disconnect from signal
+ disconnect(this, SIGNAL(connected()), this, SLOT(slotConnected()));
+
+ // send connect to server
+ QByteArray buffer;
+ buffer.resize(3);
+ buffer[0] = SocksVersion;
+ buffer[1] = 0x01; // number of methods
+ buffer[2] = NoAuthentication;
+ write(buffer);
+}
+
+void QXmppSocksClient::slotReadyRead()
+{
+ if (m_step == ConnectState)
+ {
+ m_step++;
+
+ // receive connect to server response
+ QByteArray buffer = readAll();
+ if (buffer.size() != 2 || buffer.at(0) != SocksVersion || buffer.at(1) != NoAuthentication)
+ {
+ qWarning("QXmppSocksClient received an invalid response during handshake");
+ close();
+ return;
+ }
+
+ // send CONNECT command
+ buffer.resize(3);
+ buffer[0] = SocksVersion;
+ buffer[1] = ConnectCommand;
+ buffer[2] = 0x00; // reserved
+ buffer.append(encodeHostAndPort(
+ DomainName,
+ m_hostName.toAscii(),
+ m_hostPort));
+ write(buffer);
+
+ } else if (m_step == CommandState) {
+ m_step++;
+
+ // disconnect from signal
+ disconnect(this, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
+
+ // receive CONNECT response
+ QByteArray buffer = readAll();
+ if (buffer.size() < 6 ||
+ buffer.at(0) != SocksVersion ||
+ buffer.at(1) != Succeeded ||
+ buffer.at(2) != 0)
+ {
+ qWarning("QXmppSocksClient received an invalid response to CONNECT command");
+ close();
+ return;
+ }
+
+ // parse host
+ quint8 hostType;
+ QByteArray hostName;
+ quint16 hostPort;
+ if (!parseHostAndPort(buffer.mid(3), hostType, hostName, hostPort))
+ {
+ qWarning("QXmppSocksClient could not parse type/host/port");
+ close();
+ return;
+ }
+ // FIXME : what do we do with the resulting name / port?
+
+ // notify of connection
+ emit ready();
+ }
+}
+
+bool QXmppSocksClient::waitForReady(int msecs)
+{
+ QEventLoop loop;
+ connect(this, SIGNAL(disconnected()), &loop, SLOT(quit()));
+ connect(this, SIGNAL(ready()), &loop, SLOT(quit()));
+ QTimer::singleShot(msecs, &loop, SLOT(quit()));
+ loop.exec();
+
+ if (m_step == ReadyState && isValid())
+ return true;
+ else
+ return false;
+}
+
+QXmppSocksServer::QXmppSocksServer(QObject *parent)
+ : QObject(parent)
+{
+ m_server = new QTcpServer(this);
+ connect(m_server, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
+}
+
+void QXmppSocksServer::close()
+{
+ m_server->close();
+}
+
+bool QXmppSocksServer::listen(const QHostAddress &address, quint16 port)
+{
+ return m_server->listen(address, port);
+}
+
+bool QXmppSocksServer::isListening() const
+{
+ return m_server->isListening();
+}
+
+QHostAddress QXmppSocksServer::serverAddress() const
+{
+ return m_server->serverAddress();
+}
+
+quint16 QXmppSocksServer::serverPort() const
+{
+ return m_server->serverPort();
+}
+
+void QXmppSocksServer::slotNewConnection()
+{
+ QTcpSocket *socket = m_server->nextPendingConnection();
+ if (!socket)
+ return;
+
+ // register socket
+ m_states.insert(socket, ConnectState);
+ connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
+}
+
+void QXmppSocksServer::slotReadyRead()
+{
+ QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
+ if (!socket || !m_states.contains(socket))
+ return;
+
+ if (m_states.value(socket) == ConnectState)
+ {
+ m_states.insert(socket, CommandState);
+
+ // receive connect to server request
+ QByteArray buffer = socket->readAll();
+ if (buffer.size() < 3 ||
+ buffer.at(0) != SocksVersion ||
+ buffer.at(1) + 2 != buffer.size())
+ {
+ qWarning("QXmppSocksServer received invalid handshake");
+ socket->close();
+ return;
+ }
+
+ // check authentication method
+ bool foundMethod = false;
+ for (int i = 2; i < buffer.size(); i++)
+ {
+ if (buffer.at(i) == NoAuthentication)
+ {
+ foundMethod = true;
+ break;
+ }
+ }
+ if (!foundMethod)
+ {
+ qWarning("QXmppSocksServer received bad authentication method");
+ socket->close();
+ return;
+ }
+
+ // send connect to server response
+ buffer.resize(2);
+ buffer[0] = SocksVersion;
+ buffer[1] = NoAuthentication;
+ socket->write(buffer);
+
+ } else if (m_states.value(socket) == CommandState) {
+ m_states.insert(socket, ReadyState);
+
+ // disconnect from signals
+ disconnect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
+
+ // receive command
+ QByteArray buffer = socket->readAll();
+ if (buffer.size() < 4 ||
+ buffer.at(0) != SocksVersion ||
+ buffer.at(1) != ConnectCommand ||
+ buffer.at(2) != 0x00)
+ {
+ qWarning("QXmppSocksServer received an invalid command");
+ socket->close();
+ return;
+ }
+
+ // parse host
+ quint8 hostType;
+ QByteArray hostName;
+ quint16 hostPort;
+ if (!parseHostAndPort(buffer.mid(3), hostType, hostName, hostPort))
+ {
+ qWarning("QXmppSocksServer could not parse type/host/port");
+ socket->close();
+ return;
+ }
+
+ // notify of connection
+ emit newConnection(socket, hostName, hostPort);
+
+ // send response
+ buffer.resize(3);
+ buffer[0] = SocksVersion;
+ buffer[1] = Succeeded;
+ buffer[2] = 0x00;
+ buffer.append(encodeHostAndPort(
+ DomainName,
+ hostName,
+ hostPort));
+ socket->write(buffer);
+ }
+}
+
diff --git a/src/base/QXmppSocks.h b/src/base/QXmppSocks.h
new file mode 100644
index 00000000..111055fe
--- /dev/null
+++ b/src/base/QXmppSocks.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPSOCKS_H
+#define QXMPPSOCKS_H
+
+#include <QHostAddress>
+#include <QTcpSocket>
+
+class QTcpServer;
+
+class QXmppSocksClient : public QTcpSocket
+{
+ Q_OBJECT
+
+public:
+ QXmppSocksClient(const QHostAddress &proxyAddress, quint16 proxyPort, QObject *parent=0);
+ void connectToHost(const QString &hostName, quint16 hostPort);
+ bool waitForReady(int msecs = 30000);
+
+signals:
+ void ready();
+
+private slots:
+ void slotConnected();
+ void slotReadyRead();
+
+private:
+ QHostAddress m_proxyAddress;
+ quint16 m_proxyPort;
+ QString m_hostName;
+ quint16 m_hostPort;
+ int m_step;
+};
+
+class QXmppSocksServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ QXmppSocksServer(QObject *parent=0);
+ void close();
+ bool isListening() const;
+ bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
+
+ QHostAddress serverAddress() const;
+ quint16 serverPort() const;
+
+signals:
+ void newConnection(QTcpSocket *socket, QString hostName, quint16 port);
+
+private slots:
+ void slotNewConnection();
+ void slotReadyRead();
+
+private:
+ QTcpServer *m_server;
+ QMap<QTcpSocket*, int> m_states;
+};
+
+#endif
diff --git a/src/base/QXmppStanza.cpp b/src/base/QXmppStanza.cpp
new file mode 100644
index 00000000..e8937b6f
--- /dev/null
+++ b/src/base/QXmppStanza.cpp
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#include "QXmppStanza.h"
+#include "QXmppUtils.h"
+#include "QXmppConstants.h"
+
+#include <QDomElement>
+#include <QXmlStreamWriter>
+
+uint QXmppStanza::s_uniqeIdNo = 0;
+
+QXmppStanza::Error::Error():
+ m_code(0),
+ m_type(static_cast<QXmppStanza::Error::Type>(-1)),
+ m_condition(static_cast<QXmppStanza::Error::Condition>(-1))
+{
+}
+
+QXmppStanza::Error::Error(Type type, Condition cond, const QString& text):
+ m_code(0),
+ m_type(type),
+ m_condition(cond),
+ m_text(text)
+{
+}
+
+QXmppStanza::Error::Error(const QString& type, const QString& cond,
+ const QString& text):
+ m_code(0),
+ m_text(text)
+{
+ setTypeFromStr(type);
+ setConditionFromStr(cond);
+}
+
+QString QXmppStanza::Error::text() const
+{
+ return m_text;
+}
+
+void QXmppStanza::Error::setText(const QString& text)
+{
+ m_text = text;
+}
+
+int QXmppStanza::Error::code() const
+{
+ return m_code;
+}
+
+void QXmppStanza::Error::setCode(int code)
+{
+ m_code = code;
+}
+
+QXmppStanza::Error::Condition QXmppStanza::Error::condition() const
+{
+ return m_condition;
+}
+
+void QXmppStanza::Error::setCondition(QXmppStanza::Error::Condition cond)
+{
+ m_condition = cond;
+}
+
+QXmppStanza::Error::Type QXmppStanza::Error::type() const
+{
+ return m_type;
+}
+
+void QXmppStanza::Error::setType(QXmppStanza::Error::Type type)
+{
+ m_type = type;
+}
+
+QString QXmppStanza::Error::getTypeStr() const
+{
+ switch(m_type)
+ {
+ case Cancel:
+ return "cancel";
+ case Continue:
+ return "continue";
+ case Modify:
+ return "modify";
+ case Auth:
+ return "auth";
+ case Wait:
+ return "wait";
+ default:
+ return "";
+ }
+}
+
+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 "";
+ }
+}
+
+void QXmppStanza::Error::setTypeFromStr(const QString& type)
+{
+ if(type == "cancel")
+ setType(Cancel);
+ else if(type == "continue")
+ setType(Continue);
+ else if(type == "modify")
+ setType(Modify);
+ else if(type == "auth")
+ setType(Auth);
+ else if(type == "wait")
+ setType(Wait);
+ else
+ setType(static_cast<QXmppStanza::Error::Type>(-1));
+}
+
+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));
+}
+
+bool QXmppStanza::Error::isValid() const
+{
+ return !(getTypeStr().isEmpty() && getConditionStr().isEmpty());
+}
+
+void QXmppStanza::Error::parse(const QDomElement &errorElement)
+{
+ setCode(errorElement.attribute("code").toInt());
+ setTypeFromStr(errorElement.attribute("type"));
+
+ QString text;
+ QString cond;
+ QDomElement element = errorElement.firstChildElement();
+ while(!element.isNull())
+ {
+ if(element.tagName() == "text")
+ text = element.text();
+ else if(element.namespaceURI() == ns_stanza)
+ {
+ cond = element.tagName();
+ }
+ element = element.nextSiblingElement();
+ }
+
+ setConditionFromStr(cond);
+ setText(text);
+}
+
+void QXmppStanza::Error::toXml( QXmlStreamWriter *writer ) const
+{
+ QString cond = getConditionStr();
+ QString type = getTypeStr();
+
+ if(cond.isEmpty() && type.isEmpty())
+ return;
+
+ writer->writeStartElement("error");
+ helperToXmlAddAttribute(writer, "type", type);
+
+ if (m_code > 0)
+ helperToXmlAddAttribute(writer, "code", QString::number(m_code));
+
+ if(!cond.isEmpty())
+ {
+ writer->writeStartElement(cond);
+ writer->writeAttribute("xmlns", ns_stanza);
+ writer->writeEndElement();
+ }
+ if(!m_text.isEmpty())
+ {
+ writer->writeStartElement("text");
+ writer->writeAttribute("xml:lang", "en");
+ writer->writeAttribute("xmlns", ns_stanza);
+ writer->writeCharacters(m_text);
+ writer->writeEndElement();
+ }
+
+ writer->writeEndElement();
+}
+
+/// Constructs a QXmppStanza with the specified sender and recipient.
+///
+/// \param from
+/// \param to
+
+QXmppStanza::QXmppStanza(const QString& from, const QString& to)
+ : QXmppPacket(),
+ m_to(to),
+ m_from(from)
+{
+}
+
+/// Destroys a QXmppStanza.
+
+QXmppStanza::~QXmppStanza()
+{
+}
+
+/// Returns the stanza's recipient JID.
+///
+
+QString QXmppStanza::to() const
+{
+ return m_to;
+}
+
+/// Sets the stanza's recipient JID.
+///
+/// \param to
+
+void QXmppStanza::setTo(const QString& to)
+{
+ m_to = to;
+}
+
+/// Returns the stanza's sender JID.
+
+QString QXmppStanza::from() const
+{
+ return m_from;
+}
+
+/// Sets the stanza's sender JID.
+///
+/// \param from
+
+void QXmppStanza::setFrom(const QString& from)
+{
+ m_from = from;
+}
+
+/// Returns the stanza's identifier.
+
+QString QXmppStanza::id() const
+{
+ return m_id;
+}
+
+/// Sets the stanza's identifier.
+///
+/// \param id
+
+void QXmppStanza::setId(const QString& id)
+{
+ m_id = id;
+}
+
+/// Returns the stanza's language.
+
+QString QXmppStanza::lang() const
+{
+ return m_lang;
+}
+
+/// Sets the stanza's language.
+///
+/// \param lang
+
+void QXmppStanza::setLang(const QString& lang)
+{
+ m_lang = lang;
+}
+
+/// Returns the stanza's error.
+
+QXmppStanza::Error QXmppStanza::error() const
+{
+ return m_error;
+}
+
+/// Sets the stanza's error.
+///
+/// \param error
+
+void QXmppStanza::setError(const QXmppStanza::Error& error)
+{
+ m_error = error;
+}
+
+/// Returns the stanza's "extensions".
+///
+/// Extensions are XML elements which are not handled internally by QXmpp.
+
+QXmppElementList QXmppStanza::extensions() const
+{
+ return m_extensions;
+}
+
+/// Sets the stanza's "extensions".
+///
+/// \param extensions
+
+void QXmppStanza::setExtensions(const QXmppElementList &extensions)
+{
+ m_extensions = extensions;
+}
+
+void QXmppStanza::generateAndSetNextId()
+{
+ // get back
+ ++s_uniqeIdNo;
+ m_id = "qxmpp" + QString::number(s_uniqeIdNo);
+}
+
+bool QXmppStanza::isErrorStanza() const
+{
+ return m_error.isValid();
+}
+
+void QXmppStanza::parse(const QDomElement &element)
+{
+ m_from = element.attribute("from");
+ m_to = element.attribute("to");
+ m_id = element.attribute("id");
+ m_lang = element.attribute("lang");
+
+ QDomElement errorElement = element.firstChildElement("error");
+ if(!errorElement.isNull())
+ m_error.parse(errorElement);
+}
+
diff --git a/src/base/QXmppStanza.h b/src/base/QXmppStanza.h
new file mode 100644
index 00000000..dc1c1346
--- /dev/null
+++ b/src/base/QXmppStanza.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPSTANZA_H
+#define QXMPPSTANZA_H
+
+#include "QXmppElement.h"
+#include "QXmppPacket.h"
+#include <QString>
+
+// forward declarations of QXmlStream* classes will not work on Mac, we need to
+// include the whole header.
+// See http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html
+// for an explanation.
+#include <QXmlStreamWriter>
+
+/// \defgroup Stanzas
+
+/// \brief The QXmppStanza class is the base class for all XMPP stanzas.
+///
+/// \ingroup Stanzas
+
+class QXmppStanza : public QXmppPacket
+{
+public:
+ class Error
+ {
+ public:
+ enum Type
+ {
+ Cancel,
+ Continue,
+ Modify,
+ Auth,
+ Wait
+ };
+
+ enum Condition
+ {
+ BadRequest,
+ Conflict,
+ FeatureNotImplemented,
+ Forbidden,
+ Gone,
+ InternalServerError,
+ ItemNotFound,
+ JidMalformed,
+ NotAcceptable,
+ NotAllowed,
+ NotAuthorized,
+ PaymentRequired,
+ RecipientUnavailable,
+ Redirect,
+ RegistrationRequired,
+ RemoteServerNotFound,
+ RemoteServerTimeout,
+ ResourceConstraint,
+ ServiceUnavailable,
+ SubscriptionRequired,
+ UndefinedCondition,
+ UnexpectedRequest
+ };
+
+ Error();
+ Error(Type type, Condition cond, const QString& text="");
+ Error(const QString& type, const QString& cond, const QString& text="");
+
+ int code() const;
+ void setCode(int code);
+
+ QString text() const;
+ void setText(const QString& text);
+
+ Condition condition() const;
+ void setCondition(Condition cond);
+
+ void setType(Type type);
+ Type type() const;
+
+ // FIXME : remove this once is gone
+ bool isValid() const;
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ private:
+ QString getConditionStr() const;
+ void setConditionFromStr(const QString& cond);
+
+ QString getTypeStr() const;
+ void setTypeFromStr(const QString& type);
+
+ int m_code;
+ Type m_type;
+ Condition m_condition;
+ QString m_text;
+ };
+
+ QXmppStanza(const QString& from = QString(), const QString& to = QString());
+ ~QXmppStanza();
+
+ QString to() const;
+ void setTo(const QString&);
+
+ QString from() const;
+ void setFrom(const QString&);
+
+ QString id() const;
+ void setId(const QString&);
+
+ QString lang() const;
+ void setLang(const QString&);
+
+ QXmppStanza::Error error() const;
+ void setError(const QXmppStanza::Error& error);
+
+ QXmppElementList extensions() const;
+ void setExtensions(const QXmppElementList &elements);
+
+ /// \cond
+ // FIXME : why is this needed?
+ bool isErrorStanza() const;
+
+protected:
+ void generateAndSetNextId();
+ void parse(const QDomElement &element);
+ /// \endcond
+
+private:
+ static uint s_uniqeIdNo;
+ QString m_to;
+ QString m_from;
+ QString m_id;
+ QString m_lang;
+ QXmppStanza::Error m_error;
+ QXmppElementList m_extensions;
+};
+
+#endif // QXMPPSTANZA_H
diff --git a/src/base/QXmppStream.cpp b/src/base/QXmppStream.cpp
new file mode 100644
index 00000000..d9f7ff88
--- /dev/null
+++ b/src/base/QXmppStream.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#include "QXmppConstants.h"
+#include "QXmppLogger.h"
+#include "QXmppPacket.h"
+#include "QXmppStream.h"
+#include "QXmppUtils.h"
+
+#include <QBuffer>
+#include <QDomDocument>
+#include <QHostAddress>
+#include <QRegExp>
+#include <QSslSocket>
+#include <QStringList>
+#include <QTime>
+#include <QXmlStreamWriter>
+
+static bool randomSeeded = false;
+static const QByteArray streamRootElementEnd = "</stream:stream>";
+
+class QXmppStreamPrivate
+{
+public:
+ QXmppStreamPrivate();
+
+ QByteArray dataBuffer;
+ QSslSocket* socket;
+
+ // stream state
+ QByteArray streamStart;
+};
+
+QXmppStreamPrivate::QXmppStreamPrivate()
+ : socket(0)
+{
+}
+
+/// Constructs a base XMPP stream.
+///
+/// \param parent
+
+QXmppStream::QXmppStream(QObject *parent)
+ : QXmppLoggable(parent),
+ d(new QXmppStreamPrivate)
+{
+ // Make sure the random number generator is seeded
+ if (!randomSeeded)
+ {
+ qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
+ randomSeeded = true;
+ }
+}
+
+/// Destroys a base XMPP stream.
+
+QXmppStream::~QXmppStream()
+{
+ delete d;
+}
+
+/// Disconnects from the remote host.
+///
+
+void QXmppStream::disconnectFromHost()
+{
+ sendData(streamRootElementEnd);
+ if (d->socket)
+ {
+ d->socket->flush();
+ d->socket->disconnectFromHost();
+ }
+}
+
+/// Handles a stream start event, which occurs when the underlying transport
+/// becomes ready (socket connected, encryption started).
+///
+/// If you redefine handleStart(), make sure to call the base class's method.
+
+void QXmppStream::handleStart()
+{
+ d->dataBuffer.clear();
+ d->streamStart.clear();
+}
+
+/// Returns true if the stream is connected.
+///
+
+bool QXmppStream::isConnected() const
+{
+ return d->socket &&
+ d->socket->state() == QAbstractSocket::ConnectedState;
+}
+
+/// Sends raw data to the peer.
+///
+/// \param data
+
+bool QXmppStream::sendData(const QByteArray &data)
+{
+ logSent(QString::fromUtf8(data));
+ if (!d->socket || d->socket->state() != QAbstractSocket::ConnectedState)
+ return false;
+ return d->socket->write(data) == data.size();
+}
+
+/// Sends an XMPP packet to the peer.
+///
+/// \param packet
+
+bool QXmppStream::sendPacket(const QXmppPacket &packet)
+{
+ // prepare packet
+ QByteArray data;
+ QXmlStreamWriter xmlStream(&data);
+ packet.toXml(&xmlStream);
+
+ // send packet
+ return sendData(data);
+}
+
+/// Returns the QSslSocket used for this stream.
+///
+
+QSslSocket *QXmppStream::socket() const
+{
+ return d->socket;
+}
+
+/// Sets the QSslSocket used for this stream.
+///
+
+void QXmppStream::setSocket(QSslSocket *socket)
+{
+ bool check;
+ Q_UNUSED(check);
+
+ d->socket = socket;
+ if (!d->socket)
+ return;
+
+ // socket events
+ check = connect(socket, SIGNAL(connected()),
+ this, SLOT(_q_socketConnected()));
+ Q_ASSERT(check);
+
+ check = connect(socket, SIGNAL(disconnected()),
+ this, SLOT(_q_socketDisconnected()));
+ Q_ASSERT(check);
+
+ check = connect(socket, SIGNAL(encrypted()),
+ this, SLOT(_q_socketEncrypted()));
+ Q_ASSERT(check);
+
+ check = connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(_q_socketError(QAbstractSocket::SocketError)));
+
+ check = connect(socket, SIGNAL(readyRead()),
+ this, SLOT(_q_socketReadyRead()));
+ Q_ASSERT(check);
+
+ // relay signals
+ check = connect(socket, SIGNAL(disconnected()),
+ this, SIGNAL(disconnected()));
+ Q_ASSERT(check);
+}
+
+void QXmppStream::_q_socketConnected()
+{
+ info(QString("Socket connected to %1 %2").arg(
+ d->socket->peerAddress().toString(),
+ QString::number(d->socket->peerPort())));
+ handleStart();
+}
+
+void QXmppStream::_q_socketDisconnected()
+{
+ info("Socket disconnected");
+}
+
+void QXmppStream::_q_socketEncrypted()
+{
+ debug("Socket encrypted");
+ handleStart();
+}
+
+void QXmppStream::_q_socketError(QAbstractSocket::SocketError socketError)
+{
+ Q_UNUSED(socketError);
+ warning(QString("Socket error: " + socket()->errorString()));
+}
+
+void QXmppStream::_q_socketReadyRead()
+{
+ d->dataBuffer.append(d->socket->readAll());
+
+ // FIXME : maybe these QRegExps could be static?
+ QRegExp startStreamRegex("^(<\\?xml.*\\?>)?\\s*<stream:stream.*>");
+ startStreamRegex.setMinimal(true);
+ QRegExp endStreamRegex("</stream:stream>$");
+ endStreamRegex.setMinimal(true);
+
+ // check whether we need to add stream start / end elements
+ QByteArray completeXml = d->dataBuffer;
+ const QString strData = QString::fromUtf8(d->dataBuffer);
+ bool streamStart = false;
+ if (d->streamStart.isEmpty() && strData.contains(startStreamRegex))
+ streamStart = true;
+ else
+ completeXml.prepend(d->streamStart);
+ if (!strData.contains(endStreamRegex))
+ completeXml.append(streamRootElementEnd);
+
+ // check whether we have a valid XML document
+ QDomDocument doc;
+ if (!doc.setContent(completeXml, true))
+ return;
+
+ // remove data from buffer
+ logReceived(strData);
+ d->dataBuffer.clear();
+ if (streamStart)
+ d->streamStart = startStreamRegex.cap(0).toUtf8();
+
+ // process stream start
+ if (streamStart)
+ handleStream(doc.documentElement());
+
+ // process stanzas
+ QDomElement nodeRecv = doc.documentElement().firstChildElement();
+ while (!nodeRecv.isNull()) {
+ handleStanza(nodeRecv);
+ nodeRecv = nodeRecv.nextSiblingElement();
+ }
+}
+
+
diff --git a/src/base/QXmppStream.h b/src/base/QXmppStream.h
new file mode 100644
index 00000000..bcbb7616
--- /dev/null
+++ b/src/base/QXmppStream.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPSTREAM_H
+#define QXMPPSTREAM_H
+
+#include <QAbstractSocket>
+#include <QObject>
+#include "QXmppLogger.h"
+
+class QDomElement;
+class QSslSocket;
+class QXmppPacket;
+class QXmppStreamPrivate;
+
+/// \brief The QXmppStream class is the base class for all XMPP streams.
+///
+
+class QXmppStream : public QXmppLoggable
+{
+ Q_OBJECT
+
+public:
+ QXmppStream(QObject *parent);
+ ~QXmppStream();
+
+ virtual bool isConnected() const;
+ bool sendPacket(const QXmppPacket&);
+
+signals:
+ /// This signal is emitted when the stream is connected.
+ void connected();
+
+ /// This signal is emitted when the stream is disconnected.
+ void disconnected();
+
+protected:
+ // Access to underlying socket
+ QSslSocket *socket() const;
+ void setSocket(QSslSocket *socket);
+
+ // Overridable methods
+ virtual void handleStart();
+
+ /// Handles an incoming XMPP stanza.
+ ///
+ /// \param element
+ virtual void handleStanza(const QDomElement &element) = 0;
+
+ /// Handles an incoming XMPP stream start.
+ ///
+ /// \param element
+ virtual void handleStream(const QDomElement &element) = 0;
+
+public slots:
+ virtual void disconnectFromHost();
+ virtual bool sendData(const QByteArray&);
+
+private slots:
+ void _q_socketConnected();
+ void _q_socketDisconnected();
+ void _q_socketEncrypted();
+ void _q_socketError(QAbstractSocket::SocketError error);
+ void _q_socketReadyRead();
+
+private:
+ QXmppStreamPrivate * const d;
+};
+
+#endif // QXMPPSTREAM_H
diff --git a/src/base/QXmppStreamFeatures.cpp b/src/base/QXmppStreamFeatures.cpp
new file mode 100644
index 00000000..241f676f
--- /dev/null
+++ b/src/base/QXmppStreamFeatures.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppStreamFeatures.h"
+
+QXmppStreamFeatures::QXmppStreamFeatures()
+ : m_bindMode(Disabled),
+ m_sessionMode(Disabled),
+ m_nonSaslAuthMode(Disabled),
+ m_tlsMode(Disabled)
+{
+}
+
+QXmppStreamFeatures::Mode QXmppStreamFeatures::bindMode() const
+{
+ return m_bindMode;
+}
+
+void QXmppStreamFeatures::setBindMode(QXmppStreamFeatures::Mode mode)
+{
+ m_bindMode = mode;
+}
+
+QXmppStreamFeatures::Mode QXmppStreamFeatures::sessionMode() const
+{
+ return m_sessionMode;
+}
+
+void QXmppStreamFeatures::setSessionMode(Mode mode)
+{
+ m_sessionMode = mode;
+}
+
+QXmppStreamFeatures::Mode QXmppStreamFeatures::nonSaslAuthMode() const
+{
+ return m_nonSaslAuthMode;
+}
+
+void QXmppStreamFeatures::setNonSaslAuthMode(QXmppStreamFeatures::Mode mode)
+{
+ m_nonSaslAuthMode = mode;
+}
+
+QList<QXmppConfiguration::SASLAuthMechanism> QXmppStreamFeatures::authMechanisms() const
+{
+ return m_authMechanisms;
+}
+
+void QXmppStreamFeatures::setAuthMechanisms(QList<QXmppConfiguration::SASLAuthMechanism> &mechanisms)
+{
+ m_authMechanisms = mechanisms;
+}
+
+QList<QXmppConfiguration::CompressionMethod> QXmppStreamFeatures::compressionMethods() const
+{
+ return m_compressionMethods;
+}
+
+void QXmppStreamFeatures::setCompressionMethods(QList<QXmppConfiguration::CompressionMethod> &methods)
+{
+ m_compressionMethods = methods;
+}
+
+QXmppStreamFeatures::Mode QXmppStreamFeatures::tlsMode() const
+{
+ return m_tlsMode;
+}
+
+void QXmppStreamFeatures::setTlsMode(QXmppStreamFeatures::Mode mode)
+{
+ m_tlsMode = mode;
+}
+
+bool QXmppStreamFeatures::isStreamFeatures(const QDomElement &element)
+{
+ return element.namespaceURI() == ns_stream &&
+ element.tagName() == "features";
+}
+
+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;
+ }
+}
+
+void QXmppStreamFeatures::parse(const QDomElement &element)
+{
+ m_bindMode = readFeature(element, "bind", ns_bind);
+ m_sessionMode = readFeature(element, "session", ns_session);
+ m_nonSaslAuthMode = readFeature(element, "auth", ns_authFeature);
+ m_tlsMode = readFeature(element, "starttls", ns_tls);
+
+ // parse advertised compression methods
+ QDomElement compression = element.firstChildElement("compression");
+ if (compression.namespaceURI() == ns_compressFeature)
+ {
+ QDomElement subElement = compression.firstChildElement("method");
+ while(!subElement.isNull())
+ {
+ if (subElement.text() == QLatin1String("zlib"))
+ m_compressionMethods << QXmppConfiguration::ZlibCompression;
+ subElement = subElement.nextSiblingElement("method");
+ }
+ }
+
+ // parse advertised SASL Authentication mechanisms
+ QDomElement mechs = element.firstChildElement("mechanisms");
+ if (mechs.namespaceURI() == ns_sasl)
+ {
+ QDomElement subElement = mechs.firstChildElement("mechanism");
+ while(!subElement.isNull())
+ {
+ if (subElement.text() == QLatin1String("PLAIN"))
+ m_authMechanisms << QXmppConfiguration::SASLPlain;
+ else if (subElement.text() == QLatin1String("DIGEST-MD5"))
+ m_authMechanisms << QXmppConfiguration::SASLDigestMD5;
+ else if (subElement.text() == QLatin1String("ANONYMOUS"))
+ m_authMechanisms << QXmppConfiguration::SASLAnonymous;
+ else if (subElement.text() == QLatin1String("X-FACEBOOK-PLATFORM"))
+ m_authMechanisms << QXmppConfiguration::SASLXFacebookPlatform;
+ subElement = subElement.nextSiblingElement("mechanism");
+ }
+ }
+}
+
+static void writeFeature(QXmlStreamWriter *writer, const char *tagName, const char *tagNs, QXmppStreamFeatures::Mode mode)
+{
+ if (mode != QXmppStreamFeatures::Disabled)
+ {
+ writer->writeStartElement(tagName);
+ writer->writeAttribute("xmlns", tagNs);
+ if (mode == QXmppStreamFeatures::Required)
+ writer->writeEmptyElement("required");
+ writer->writeEndElement();
+ }
+}
+
+void QXmppStreamFeatures::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("stream:features");
+ writeFeature(writer, "bind", ns_bind, m_bindMode);
+ writeFeature(writer, "session", ns_session, m_sessionMode);
+ writeFeature(writer, "auth", ns_authFeature, m_nonSaslAuthMode);
+ writeFeature(writer, "starttls", ns_tls, m_tlsMode);
+
+ if (!m_compressionMethods.isEmpty())
+ {
+ writer->writeStartElement("compression");
+ writer->writeAttribute("xmlns", ns_compressFeature);
+ for (int i = 0; i < m_compressionMethods.size(); i++)
+ {
+ writer->writeStartElement("method");
+ switch (m_compressionMethods[i])
+ {
+ case QXmppConfiguration::ZlibCompression:
+ writer->writeCharacters("zlib");
+ break;
+ }
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+ }
+ if (!m_authMechanisms.isEmpty())
+ {
+ writer->writeStartElement("mechanisms");
+ writer->writeAttribute("xmlns", ns_sasl);
+ for (int i = 0; i < m_authMechanisms.size(); i++)
+ {
+ writer->writeStartElement("mechanism");
+ switch (m_authMechanisms[i])
+ {
+ case QXmppConfiguration::SASLPlain:
+ writer->writeCharacters("PLAIN");
+ break;
+ case QXmppConfiguration::SASLDigestMD5:
+ writer->writeCharacters("DIGEST-MD5");
+ break;
+ case QXmppConfiguration::SASLAnonymous:
+ writer->writeCharacters("ANONYMOUS");
+ break;
+ case QXmppConfiguration::SASLXFacebookPlatform:
+ writer->writeCharacters("X-FACEBOOK-PLATFORM");
+ break;
+ }
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+ }
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppStreamFeatures.h b/src/base/QXmppStreamFeatures.h
new file mode 100644
index 00000000..bfc42c64
--- /dev/null
+++ b/src/base/QXmppStreamFeatures.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPSTREAMFEATURES_H
+#define QXMPPSTREAMFEATURES_H
+
+#include "QXmppConfiguration.h"
+#include "QXmppStanza.h"
+
+class QXmppStreamFeatures : public QXmppStanza
+{
+public:
+ QXmppStreamFeatures();
+
+ enum Mode
+ {
+ Disabled = 0,
+ Enabled,
+ Required
+ };
+
+ Mode bindMode() const;
+ void setBindMode(Mode mode);
+
+ Mode sessionMode() const;
+ void setSessionMode(Mode mode);
+
+ Mode nonSaslAuthMode() const;
+ void setNonSaslAuthMode(Mode mode);
+
+ QList<QXmppConfiguration::SASLAuthMechanism> authMechanisms() const;
+ void setAuthMechanisms(QList<QXmppConfiguration::SASLAuthMechanism> &mecanisms);
+
+ QList<QXmppConfiguration::CompressionMethod> compressionMethods() const;
+ void setCompressionMethods(QList<QXmppConfiguration::CompressionMethod> &methods);
+
+ Mode tlsMode() const;
+ void setTlsMode(Mode mode);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ static bool isStreamFeatures(const QDomElement &element);
+
+private:
+ Mode m_bindMode;
+ Mode m_sessionMode;
+ Mode m_nonSaslAuthMode;
+ Mode m_tlsMode;
+ QList<QXmppConfiguration::SASLAuthMechanism> m_authMechanisms;
+ QList<QXmppConfiguration::CompressionMethod> m_compressionMethods;
+};
+
+#endif
diff --git a/src/base/QXmppStreamInitiationIq.cpp b/src/base/QXmppStreamInitiationIq.cpp
new file mode 100644
index 00000000..32dd0c78
--- /dev/null
+++ b/src/base/QXmppStreamInitiationIq.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppStreamInitiationIq.h"
+#include "QXmppUtils.h"
+
+QString QXmppStreamInitiationIq::mimeType() const
+{
+ return m_mimeType;
+}
+
+void QXmppStreamInitiationIq::setMimeType(const QString &mimeType)
+{
+ m_mimeType = mimeType;
+}
+
+QXmppStreamInitiationIq::Profile QXmppStreamInitiationIq::profile() const
+{
+ return m_profile;
+}
+
+void QXmppStreamInitiationIq::setProfile(QXmppStreamInitiationIq::Profile profile)
+{
+ m_profile = profile;
+}
+
+QXmppElementList QXmppStreamInitiationIq::siItems() const
+{
+ return m_siItems;
+}
+
+QString QXmppStreamInitiationIq::siId() const
+{
+ return m_siId;
+}
+
+void QXmppStreamInitiationIq::setSiId(const QString &id)
+{
+ m_siId = id;
+}
+
+void QXmppStreamInitiationIq::setSiItems(const QXmppElementList &items)
+{
+ m_siItems = items;
+}
+
+bool QXmppStreamInitiationIq::isStreamInitiationIq(const QDomElement &element)
+{
+ QDomElement siElement = element.firstChildElement("si");
+ return (siElement.namespaceURI() == ns_stream_initiation);
+}
+
+void QXmppStreamInitiationIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement siElement = element.firstChildElement("si");
+ m_siId = siElement.attribute("id");
+ m_mimeType = siElement.attribute("mime-type");
+ if (siElement.attribute("profile") == ns_stream_initiation_file_transfer)
+ m_profile = FileTransfer;
+ else
+ m_profile = None;
+
+ QDomElement itemElement = siElement.firstChildElement();
+ while (!itemElement.isNull())
+ {
+ m_siItems.append(QXmppElement(itemElement));
+ itemElement = itemElement.nextSiblingElement();
+ }
+}
+
+void QXmppStreamInitiationIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("si");
+ writer->writeAttribute("xmlns", ns_stream_initiation);
+ helperToXmlAddAttribute(writer, "id", m_siId);
+ helperToXmlAddAttribute(writer, "mime-type", m_mimeType);
+ if (m_profile == FileTransfer)
+ helperToXmlAddAttribute(writer, "profile", ns_stream_initiation_file_transfer);
+ foreach (const QXmppElement &item, m_siItems)
+ item.toXml(writer);
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppStreamInitiationIq.h b/src/base/QXmppStreamInitiationIq.h
new file mode 100644
index 00000000..27739b46
--- /dev/null
+++ b/src/base/QXmppStreamInitiationIq.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPSTREAMINITIATIONIQ_H
+#define QXMPPSTREAMINITIATIONIQ_H
+
+#include <QDateTime>
+
+#include "QXmppIq.h"
+
+class QDomElement;
+class QXmlStreamWriter;
+
+class QXmppStreamInitiationIq : public QXmppIq
+{
+public:
+ enum Profile {
+ None = 0,
+ FileTransfer,
+ };
+
+ QString mimeType() const;
+ void setMimeType(const QString &mimeType);
+
+ QXmppStreamInitiationIq::Profile profile() const;
+ void setProfile(QXmppStreamInitiationIq::Profile profile);
+
+ QString siId() const;
+ void setSiId(const QString &id);
+
+ QXmppElementList siItems() const;
+ void setSiItems(const QXmppElementList &items);
+
+ static bool isStreamInitiationIq(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString m_mimeType;
+ Profile m_profile;
+ QString m_siId;
+ QXmppElementList m_siItems;
+};
+
+#endif
diff --git a/src/base/QXmppStun.cpp b/src/base/QXmppStun.cpp
new file mode 100644
index 00000000..16cef928
--- /dev/null
+++ b/src/base/QXmppStun.cpp
@@ -0,0 +1,2581 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#define QXMPP_DEBUG_STUN
+
+#include <QCryptographicHash>
+#include <QHostInfo>
+#include <QNetworkInterface>
+#include <QUdpSocket>
+#include <QTimer>
+
+#include "QXmppStun.h"
+#include "QXmppUtils.h"
+
+#define ID_SIZE 12
+#define STUN_RTO_INTERVAL 500
+#define STUN_RTO_MAX 7
+
+static const quint32 STUN_MAGIC = 0x2112A442;
+static const quint16 STUN_HEADER = 20;
+static const quint8 STUN_IPV4 = 0x01;
+static const quint8 STUN_IPV6 = 0x02;
+
+enum AttributeType {
+ MappedAddress = 0x0001, // RFC5389
+ ChangeRequest = 0x0003, // RFC5389
+ SourceAddress = 0x0004, // RFC5389
+ ChangedAddress = 0x0005, // RFC5389
+ Username = 0x0006, // RFC5389
+ MessageIntegrity = 0x0008, // RFC5389
+ ErrorCode = 0x0009, // RFC5389
+ ChannelNumber = 0x000c, // RFC5766 : TURN
+ Lifetime = 0x000d, // RFC5766 : TURN
+ XorPeerAddress = 0x0012, // RFC5766 : TURN
+ DataAttr = 0x0013, // RFC5766 : TURN
+ Realm = 0x0014, // RFC5389
+ Nonce = 0x0015, // RFC5389
+ XorRelayedAddress= 0x0016, // RFC5766 : TURN
+ EvenPort = 0x0018, // RFC5766 : TURN
+ RequestedTransport=0x0019, // RFC5766 : TURN
+ XorMappedAddress = 0x0020, // RFC5389
+ ReservationToken = 0x0022, // RFC5766 : TURN
+ Priority = 0x0024, // RFC5245
+ UseCandidate = 0x0025, // RFC5245
+ Software = 0x8022, // RFC5389
+ Fingerprint = 0x8028, // RFC5389
+ IceControlled = 0x8029, // RFC5245
+ IceControlling = 0x802a, // RFC5245
+ OtherAddress = 0x802c, // RFC5780
+};
+
+// FIXME : we need to set local preference to discriminate between
+// multiple IP addresses
+static quint32 candidatePriority(const QXmppJingleCandidate &candidate, int localPref = 65535)
+{
+ int typePref;
+ switch (candidate.type())
+ {
+ case QXmppJingleCandidate::HostType:
+ typePref = 126;
+ break;
+ case QXmppJingleCandidate::PeerReflexiveType:
+ typePref = 110;
+ break;
+ case QXmppJingleCandidate::ServerReflexiveType:
+ typePref = 100;
+ break;
+ default:
+ typePref = 0;
+ }
+
+ return (1 << 24) * typePref + \
+ (1 << 8) * localPref + \
+ (256 - candidate.component());
+}
+
+static bool isIPv6LinkLocalAddress(const QHostAddress &addr)
+{
+ if (addr.protocol() != QAbstractSocket::IPv6Protocol)
+ return false;
+ Q_IPV6ADDR ipv6addr = addr.toIPv6Address();
+ return (((ipv6addr[0] << 8) + ipv6addr[1]) & 0xffc0) == 0xfe80;
+}
+
+static bool decodeAddress(QDataStream &stream, quint16 a_length, QHostAddress &address, quint16 &port, const QByteArray &xorId = QByteArray())
+{
+ if (a_length < 4)
+ return false;
+ quint8 reserved, protocol;
+ quint16 rawPort;
+ stream >> reserved;
+ stream >> protocol;
+ stream >> rawPort;
+ if (xorId.isEmpty())
+ port = rawPort;
+ else
+ port = rawPort ^ (STUN_MAGIC >> 16);
+ if (protocol == STUN_IPV4)
+ {
+ if (a_length != 8)
+ return false;
+ quint32 addr;
+ stream >> addr;
+ if (xorId.isEmpty())
+ address = QHostAddress(addr);
+ else
+ address = QHostAddress(addr ^ STUN_MAGIC);
+ } else if (protocol == STUN_IPV6) {
+ if (a_length != 20)
+ return false;
+ Q_IPV6ADDR addr;
+ stream.readRawData((char*)&addr, sizeof(addr));
+ if (!xorId.isEmpty())
+ {
+ QByteArray xpad;
+ QDataStream(&xpad, QIODevice::WriteOnly) << STUN_MAGIC;
+ xpad += xorId;
+ for (int i = 0; i < 16; i++)
+ addr[i] ^= xpad[i];
+ }
+ address = QHostAddress(addr);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static void encodeAddress(QDataStream &stream, quint16 type, const QHostAddress &address, quint16 port, const QByteArray &xorId = QByteArray())
+{
+ const quint8 reserved = 0;
+ if (address.protocol() == QAbstractSocket::IPv4Protocol)
+ {
+ stream << type;
+ stream << quint16(8);
+ stream << reserved;
+ stream << quint8(STUN_IPV4);
+ quint32 addr = address.toIPv4Address();
+ if (!xorId.isEmpty())
+ {
+ port ^= (STUN_MAGIC >> 16);
+ addr ^= STUN_MAGIC;
+ }
+ stream << port;
+ stream << addr;
+ } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+ stream << type;
+ stream << quint16(20);
+ stream << reserved;
+ stream << quint8(STUN_IPV6);
+ Q_IPV6ADDR addr = address.toIPv6Address();
+ if (!xorId.isEmpty())
+ {
+ port ^= (STUN_MAGIC >> 16);
+ QByteArray xpad;
+ QDataStream(&xpad, QIODevice::WriteOnly) << STUN_MAGIC;
+ xpad += xorId;
+ for (int i = 0; i < 16; i++)
+ addr[i] ^= xpad[i];
+ }
+ stream << port;
+ stream.writeRawData((char*)&addr, sizeof(addr));
+ } else {
+ qWarning("Cannot write STUN attribute for unknown IP version");
+ }
+}
+
+static void addAddress(QDataStream &stream, quint16 type, const QHostAddress &host, quint16 port, const QByteArray &xorId = QByteArray())
+{
+ if (port && !host.isNull() &&
+ (host.protocol() == QAbstractSocket::IPv4Protocol ||
+ host.protocol() == QAbstractSocket::IPv6Protocol))
+ {
+ encodeAddress(stream, type, host, port, xorId);
+ }
+}
+
+static void encodeString(QDataStream &stream, quint16 type, const QString &string)
+{
+ const QByteArray utf8string = string.toUtf8();
+ stream << type;
+ stream << quint16(utf8string.size());
+ stream.writeRawData(utf8string.data(), utf8string.size());
+ if (utf8string.size() % 4)
+ {
+ const QByteArray padding(4 - (utf8string.size() % 4), 0);
+ stream.writeRawData(padding.data(), padding.size());
+ }
+}
+
+static void setBodyLength(QByteArray &buffer, qint16 length)
+{
+ QDataStream stream(&buffer, QIODevice::WriteOnly);
+ stream.device()->seek(2);
+ stream << length;
+}
+
+/// Constructs a new QXmppStunMessage.
+
+QXmppStunMessage::QXmppStunMessage()
+ : errorCode(0),
+ changedPort(0),
+ mappedPort(0),
+ otherPort(0),
+ sourcePort(0),
+ xorMappedPort(0),
+ xorPeerPort(0),
+ xorRelayedPort(0),
+ useCandidate(false),
+ m_cookie(STUN_MAGIC),
+ m_type(0),
+ m_changeRequest(0),
+ m_channelNumber(0),
+ m_lifetime(0),
+ m_priority(0)
+{
+ m_id = QByteArray(ID_SIZE, 0);
+}
+
+quint32 QXmppStunMessage::cookie() const
+{
+ return m_cookie;
+}
+
+void QXmppStunMessage::setCookie(quint32 cookie)
+{
+ m_cookie = cookie;
+}
+
+QByteArray QXmppStunMessage::id() const
+{
+ return m_id;
+}
+
+void QXmppStunMessage::setId(const QByteArray &id)
+{
+ Q_ASSERT(id.size() == ID_SIZE);
+ m_id = id;
+}
+
+quint16 QXmppStunMessage::messageClass() const
+{
+ return m_type & 0x0110;
+}
+
+quint16 QXmppStunMessage::messageMethod() const
+{
+ return m_type & 0x3eef;
+}
+
+quint16 QXmppStunMessage::type() const
+{
+ return m_type;
+}
+
+void QXmppStunMessage::setType(quint16 type)
+{
+ m_type = type;
+}
+
+/// Returns the CHANGE-REQUEST attribute, indicating whether to change
+/// the IP and / or port from which the response is sent.
+
+quint32 QXmppStunMessage::changeRequest() const
+{
+ return m_changeRequest;
+}
+
+/// Sets the CHANGE-REQUEST attribute, indicating whether to change
+/// the IP and / or port from which the response is sent.
+///
+/// \param changeRequest
+
+void QXmppStunMessage::setChangeRequest(quint32 changeRequest)
+{
+ m_changeRequest = changeRequest;
+ m_attributes << ChangeRequest;
+}
+
+/// Returns the CHANNEL-NUMBER attribute.
+
+quint16 QXmppStunMessage::channelNumber() const
+{
+ return m_channelNumber;
+}
+
+/// Sets the CHANNEL-NUMBER attribute.
+///
+/// \param channelNumber
+
+void QXmppStunMessage::setChannelNumber(quint16 channelNumber)
+{
+ m_channelNumber = channelNumber;
+ m_attributes << ChannelNumber;
+}
+
+/// Returns the DATA attribute.
+
+QByteArray QXmppStunMessage::data() const
+{
+ return m_data;
+}
+
+/// Sets the DATA attribute.
+
+void QXmppStunMessage::setData(const QByteArray &data)
+{
+ m_data = data;
+ m_attributes << DataAttr;
+}
+
+/// Returns the LIFETIME attribute, indicating the duration in seconds for
+/// which the server will maintain an allocation.
+
+quint32 QXmppStunMessage::lifetime() const
+{
+ return m_lifetime;
+}
+
+/// Sets the LIFETIME attribute, indicating the duration in seconds for
+/// which the server will maintain an allocation.
+///
+/// \param lifetime
+
+void QXmppStunMessage::setLifetime(quint32 lifetime)
+{
+ m_lifetime = lifetime;
+ m_attributes << Lifetime;
+}
+
+/// Returns the NONCE attribute.
+
+QByteArray QXmppStunMessage::nonce() const
+{
+ return m_nonce;
+}
+
+/// Sets the NONCE attribute.
+///
+/// \param nonce
+
+void QXmppStunMessage::setNonce(const QByteArray &nonce)
+{
+ m_nonce = nonce;
+ m_attributes << Nonce;
+}
+
+/// Returns the PRIORITY attribute, the priority that would be assigned to
+/// a peer reflexive candidate discovered during the ICE check.
+
+quint32 QXmppStunMessage::priority() const
+{
+ return m_priority;
+}
+
+/// Sets the PRIORITY attribute, the priority that would be assigned to
+/// a peer reflexive candidate discovered during the ICE check.
+///
+/// \param priority
+
+void QXmppStunMessage::setPriority(quint32 priority)
+{
+ m_priority = priority;
+ m_attributes << Priority;
+}
+
+/// Returns the REALM attribute.
+
+QString QXmppStunMessage::realm() const
+{
+ return m_realm;
+}
+
+/// Sets the REALM attribute.
+///
+/// \param realm
+
+void QXmppStunMessage::setRealm(const QString &realm)
+{
+ m_realm = realm;
+ m_attributes << Realm;
+}
+
+/// Returns the REQUESTED-TRANSPORT attribute.
+
+quint8 QXmppStunMessage::requestedTransport() const
+{
+ return m_requestedTransport;
+}
+
+/// Sets the REQUESTED-TRANSPORT attribute.
+///
+/// \param requestedTransport
+
+void QXmppStunMessage::setRequestedTransport(quint8 requestedTransport)
+{
+ m_requestedTransport = requestedTransport;
+ m_attributes << RequestedTransport;
+}
+
+/// Returns the RESERVATION-TOKEN attribute.
+
+QByteArray QXmppStunMessage::reservationToken() const
+{
+ return m_reservationToken;
+}
+
+/// Sets the RESERVATION-TOKEN attribute.
+///
+/// \param reservationToken
+
+void QXmppStunMessage::setReservationToken(const QByteArray &reservationToken)
+{
+ m_reservationToken = reservationToken;
+ m_reservationToken.resize(8);
+ m_attributes << ReservationToken;
+}
+
+/// Returns the SOFTWARE attribute, containing a textual description of the
+/// software being used.
+
+QString QXmppStunMessage::software() const
+{
+ return m_software;
+}
+
+/// Sets the SOFTWARE attribute, containing a textual description of the
+/// software being used.
+///
+/// \param software
+
+void QXmppStunMessage::setSoftware(const QString &software)
+{
+ m_software = software;
+ m_attributes << Software;
+}
+
+/// Returns the USERNAME attribute, containing the username to use for
+/// authentication.
+
+QString QXmppStunMessage::username() const
+{
+ return m_username;
+}
+
+/// Sets the USERNAME attribute, containing the username to use for
+/// authentication.
+///
+/// \param username
+
+void QXmppStunMessage::setUsername(const QString &username)
+{
+ m_username = username;
+ m_attributes << Username;
+}
+
+/// Decodes a QXmppStunMessage and checks its integrity using the given key.
+///
+/// \param buffer
+/// \param key
+/// \param errors
+
+bool QXmppStunMessage::decode(const QByteArray &buffer, const QByteArray &key, QStringList *errors)
+{
+ QStringList silent;
+ if (!errors)
+ errors = &silent;
+
+ if (buffer.size() < STUN_HEADER)
+ {
+ *errors << QLatin1String("Received a truncated STUN packet");
+ return false;
+ }
+
+ // parse STUN header
+ QDataStream stream(buffer);
+ quint16 length;
+ stream >> m_type;
+ stream >> length;
+ stream >> m_cookie;
+ stream.readRawData(m_id.data(), m_id.size());
+
+ if (length != buffer.size() - STUN_HEADER)
+ {
+ *errors << QLatin1String("Received an invalid STUN packet");
+ return false;
+ }
+
+ // parse STUN attributes
+ int done = 0;
+ bool after_integrity = false;
+ while (done < length)
+ {
+ quint16 a_type, a_length;
+ stream >> a_type;
+ stream >> a_length;
+ const int pad_length = 4 * ((a_length + 3) / 4) - a_length;
+
+ // only FINGERPRINT is allowed after MESSAGE-INTEGRITY
+ if (after_integrity && a_type != Fingerprint)
+ {
+ *errors << QString("Skipping attribute %1 after MESSAGE-INTEGRITY").arg(QString::number(a_type));
+ stream.skipRawData(a_length + pad_length);
+ done += 4 + a_length + pad_length;
+ continue;
+ }
+
+ if (a_type == Priority)
+ {
+ // PRIORITY
+ if (a_length != sizeof(m_priority))
+ return false;
+ stream >> m_priority;
+ m_attributes << Priority;
+
+ } else if (a_type == ErrorCode) {
+
+ // ERROR-CODE
+ if (a_length < 4)
+ return false;
+ quint16 reserved;
+ quint8 errorCodeHigh, errorCodeLow;
+ stream >> reserved;
+ stream >> errorCodeHigh;
+ stream >> errorCodeLow;
+ errorCode = errorCodeHigh * 100 + errorCodeLow;
+ QByteArray phrase(a_length - 4, 0);
+ stream.readRawData(phrase.data(), phrase.size());
+ errorPhrase = QString::fromUtf8(phrase);
+
+ } else if (a_type == UseCandidate) {
+
+ // USE-CANDIDATE
+ if (a_length != 0)
+ return false;
+ useCandidate = true;
+
+ } else if (a_type == ChannelNumber) {
+
+ // CHANNEL-NUMBER
+ if (a_length != 4)
+ return false;
+ stream >> m_channelNumber;
+ stream.skipRawData(2);
+ m_attributes << ChannelNumber;
+
+ } else if (a_type == DataAttr) {
+
+ // DATA
+ m_data.resize(a_length);
+ stream.readRawData(m_data.data(), m_data.size());
+ m_attributes << DataAttr;
+
+ } else if (a_type == Lifetime) {
+
+ // LIFETIME
+ if (a_length != sizeof(m_lifetime))
+ return false;
+ stream >> m_lifetime;
+ m_attributes << Lifetime;
+
+ } else if (a_type == Nonce) {
+
+ // NONCE
+ m_nonce.resize(a_length);
+ stream.readRawData(m_nonce.data(), m_nonce.size());
+ m_attributes << Nonce;
+
+ } else if (a_type == Realm) {
+
+ // REALM
+ QByteArray utf8Realm(a_length, 0);
+ stream.readRawData(utf8Realm.data(), utf8Realm.size());
+ m_realm = QString::fromUtf8(utf8Realm);
+ m_attributes << Realm;
+
+ } else if (a_type == RequestedTransport) {
+
+ // REQUESTED-TRANSPORT
+ if (a_length != 4)
+ return false;
+ stream >> m_requestedTransport;
+ stream.skipRawData(3);
+ m_attributes << RequestedTransport;
+
+ } else if (a_type == ReservationToken) {
+
+ // RESERVATION-TOKEN
+ if (a_length != 8)
+ return false;
+ m_reservationToken.resize(a_length);
+ stream.readRawData(m_reservationToken.data(), m_reservationToken.size());
+ m_attributes << ReservationToken;
+
+ } else if (a_type == Software) {
+
+ // SOFTWARE
+ QByteArray utf8Software(a_length, 0);
+ stream.readRawData(utf8Software.data(), utf8Software.size());
+ m_software = QString::fromUtf8(utf8Software);
+ m_attributes << Software;
+
+ } else if (a_type == Username) {
+
+ // USERNAME
+ QByteArray utf8Username(a_length, 0);
+ stream.readRawData(utf8Username.data(), utf8Username.size());
+ m_username = QString::fromUtf8(utf8Username);
+ m_attributes << Username;
+
+ } else if (a_type == MappedAddress) {
+
+ // MAPPED-ADDRESS
+ if (!decodeAddress(stream, a_length, mappedHost, mappedPort))
+ {
+ *errors << QLatin1String("Bad MAPPED-ADDRESS");
+ return false;
+ }
+
+ } else if (a_type == ChangeRequest) {
+
+ // CHANGE-REQUEST
+ if (a_length != sizeof(m_changeRequest))
+ return false;
+ stream >> m_changeRequest;
+ m_attributes << ChangeRequest;
+
+ } else if (a_type == SourceAddress) {
+
+ // SOURCE-ADDRESS
+ if (!decodeAddress(stream, a_length, sourceHost, sourcePort))
+ {
+ *errors << QLatin1String("Bad SOURCE-ADDRESS");
+ return false;
+ }
+
+ } else if (a_type == ChangedAddress) {
+
+ // CHANGED-ADDRESS
+ if (!decodeAddress(stream, a_length, changedHost, changedPort))
+ {
+ *errors << QLatin1String("Bad CHANGED-ADDRESS");
+ return false;
+ }
+
+ } else if (a_type == OtherAddress) {
+
+ // OTHER-ADDRESS
+ if (!decodeAddress(stream, a_length, otherHost, otherPort))
+ {
+ *errors << QLatin1String("Bad OTHER-ADDRESS");
+ return false;
+ }
+
+ } else if (a_type == XorMappedAddress) {
+
+ // XOR-MAPPED-ADDRESS
+ if (!decodeAddress(stream, a_length, xorMappedHost, xorMappedPort, m_id))
+ {
+ *errors << QLatin1String("Bad XOR-MAPPED-ADDRESS");
+ return false;
+ }
+
+ } else if (a_type == XorPeerAddress) {
+
+ // XOR-PEER-ADDRESS
+ if (!decodeAddress(stream, a_length, xorPeerHost, xorPeerPort, m_id))
+ {
+ *errors << QLatin1String("Bad XOR-PEER-ADDRESS");
+ return false;
+ }
+
+ } else if (a_type == XorRelayedAddress) {
+
+ // XOR-RELAYED-ADDRESS
+ if (!decodeAddress(stream, a_length, xorRelayedHost, xorRelayedPort, m_id))
+ {
+ *errors << QLatin1String("Bad XOR-RELAYED-ADDRESS");
+ return false;
+ }
+
+ } else if (a_type == MessageIntegrity) {
+
+ // MESSAGE-INTEGRITY
+ if (a_length != 20)
+ return false;
+ QByteArray integrity(20, 0);
+ stream.readRawData(integrity.data(), integrity.size());
+
+ // check HMAC-SHA1
+ if (!key.isEmpty())
+ {
+ QByteArray copy = buffer.left(STUN_HEADER + done);
+ setBodyLength(copy, done + 24);
+ if (integrity != generateHmacSha1(key, copy))
+ {
+ *errors << QLatin1String("Bad message integrity");
+ return false;
+ }
+ }
+
+ // from here onwards, only FINGERPRINT is allowed
+ after_integrity = true;
+
+ } else if (a_type == Fingerprint) {
+
+ // FINGERPRINT
+ if (a_length != 4)
+ return false;
+ quint32 fingerprint;
+ stream >> fingerprint;
+
+ // check CRC32
+ QByteArray copy = buffer.left(STUN_HEADER + done);
+ setBodyLength(copy, done + 8);
+ const quint32 expected = generateCrc32(copy) ^ 0x5354554eL;
+ if (fingerprint != expected)
+ {
+ *errors << QLatin1String("Bad fingerprint");
+ return false;
+ }
+
+ // stop parsing, no more attributes are allowed
+ return true;
+
+ } else if (a_type == IceControlling) {
+
+ /// ICE-CONTROLLING
+ if (a_length != 8)
+ return false;
+ iceControlling.resize(a_length);
+ stream.readRawData(iceControlling.data(), iceControlling.size());
+
+ } else if (a_type == IceControlled) {
+
+ /// ICE-CONTROLLED
+ if (a_length != 8)
+ return false;
+ iceControlled.resize(a_length);
+ stream.readRawData(iceControlled.data(), iceControlled.size());
+
+ } else {
+
+ // Unknown attribute
+ stream.skipRawData(a_length);
+ *errors << QString("Skipping unknown attribute %1").arg(QString::number(a_type));
+
+ }
+ stream.skipRawData(pad_length);
+ done += 4 + a_length + pad_length;
+ }
+ return true;
+}
+
+/// Encodes the current QXmppStunMessage, optionally calculating the
+/// message integrity attribute using the given key.
+///
+/// \param key
+/// \param addFingerprint
+
+QByteArray QXmppStunMessage::encode(const QByteArray &key, bool addFingerprint) const
+{
+ QByteArray buffer;
+ QDataStream stream(&buffer, QIODevice::WriteOnly);
+
+ // encode STUN header
+ quint16 length = 0;
+ stream << m_type;
+ stream << length;
+ stream << m_cookie;
+ stream.writeRawData(m_id.data(), m_id.size());
+
+ // MAPPED-ADDRESS
+ addAddress(stream, MappedAddress, mappedHost, mappedPort);
+
+ // CHANGE-REQUEST
+ if (m_attributes.contains(ChangeRequest)) {
+ stream << quint16(ChangeRequest);
+ stream << quint16(sizeof(m_changeRequest));
+ stream << m_changeRequest;
+ }
+
+ // SOURCE-ADDRESS
+ addAddress(stream, SourceAddress, sourceHost, sourcePort);
+
+ // CHANGED-ADDRESS
+ addAddress(stream, ChangedAddress, changedHost, changedPort);
+
+ // OTHER-ADDRESS
+ addAddress(stream, OtherAddress, otherHost, otherPort);
+
+ // XOR-MAPPED-ADDRESS
+ addAddress(stream, XorMappedAddress, xorMappedHost, xorMappedPort, m_id);
+
+ // XOR-PEER-ADDRESS
+ addAddress(stream, XorPeerAddress, xorPeerHost, xorPeerPort, m_id);
+
+ // XOR-RELAYED-ADDRESS
+ addAddress(stream, XorRelayedAddress, xorRelayedHost, xorRelayedPort, m_id);
+
+ // ERROR-CODE
+ if (errorCode)
+ {
+ const quint16 reserved = 0;
+ const quint8 errorCodeHigh = errorCode / 100;
+ const quint8 errorCodeLow = errorCode % 100;
+ const QByteArray phrase = errorPhrase.toUtf8();
+ stream << quint16(ErrorCode);
+ stream << quint16(phrase.size() + 4);
+ stream << reserved;
+ stream << errorCodeHigh;
+ stream << errorCodeLow;
+ stream.writeRawData(phrase.data(), phrase.size());
+ if (phrase.size() % 4)
+ {
+ const QByteArray padding(4 - (phrase.size() % 4), 0);
+ stream.writeRawData(padding.data(), padding.size());
+ }
+ }
+
+ // PRIORITY
+ if (m_attributes.contains(Priority))
+ {
+ stream << quint16(Priority);
+ stream << quint16(sizeof(m_priority));
+ stream << m_priority;
+ }
+
+ // USE-CANDIDATE
+ if (useCandidate)
+ {
+ stream << quint16(UseCandidate);
+ stream << quint16(0);
+ }
+
+ // CHANNEL-NUMBER
+ if (m_attributes.contains(ChannelNumber)) {
+ stream << quint16(ChannelNumber);
+ stream << quint16(4);
+ stream << m_channelNumber;
+ stream << quint16(0);
+ }
+
+ // DATA
+ if (m_attributes.contains(DataAttr)) {
+ stream << quint16(DataAttr);
+ stream << quint16(m_data.size());
+ stream.writeRawData(m_data.data(), m_data.size());
+ if (m_data.size() % 4) {
+ const QByteArray padding(4 - (m_data.size() % 4), 0);
+ stream.writeRawData(padding.data(), padding.size());
+ }
+ }
+
+ // LIFETIME
+ if (m_attributes.contains(Lifetime)) {
+ stream << quint16(Lifetime);
+ stream << quint16(sizeof(m_lifetime));
+ stream << m_lifetime;
+ }
+
+ // NONCE
+ if (m_attributes.contains(Nonce)) {
+ stream << quint16(Nonce);
+ stream << quint16(m_nonce.size());
+ stream.writeRawData(m_nonce.data(), m_nonce.size());
+ }
+
+ // REALM
+ if (m_attributes.contains(Realm))
+ encodeString(stream, Realm, m_realm);
+
+ // REQUESTED-TRANSPORT
+ if (m_attributes.contains(RequestedTransport)) {
+ const QByteArray reserved(3, 0);
+ stream << quint16(RequestedTransport);
+ stream << quint16(4);
+ stream << m_requestedTransport;
+ stream.writeRawData(reserved.data(), reserved.size());
+ }
+
+ // RESERVATION-TOKEN
+ if (m_attributes.contains(ReservationToken)) {
+ stream << quint16(ReservationToken);
+ stream << quint16(m_reservationToken.size());
+ stream.writeRawData(m_reservationToken.data(), m_reservationToken.size());
+ }
+
+ // SOFTWARE
+ if (m_attributes.contains(Software))
+ encodeString(stream, Software, m_software);
+
+ // USERNAME
+ if (m_attributes.contains(Username))
+ encodeString(stream, Username, m_username);
+
+ // ICE-CONTROLLING or ICE-CONTROLLED
+ if (!iceControlling.isEmpty())
+ {
+ stream << quint16(IceControlling);
+ stream << quint16(iceControlling.size());
+ stream.writeRawData(iceControlling.data(), iceControlling.size());
+ } else if (!iceControlled.isEmpty()) {
+ stream << quint16(IceControlled);
+ stream << quint16(iceControlled.size());
+ stream.writeRawData(iceControlled.data(), iceControlled.size());
+ }
+
+ // set body length
+ setBodyLength(buffer, buffer.size() - STUN_HEADER);
+
+ // MESSAGE-INTEGRITY
+ if (!key.isEmpty())
+ {
+ setBodyLength(buffer, buffer.size() - STUN_HEADER + 24);
+ QByteArray integrity = generateHmacSha1(key, buffer);
+ stream << quint16(MessageIntegrity);
+ stream << quint16(integrity.size());
+ stream.writeRawData(integrity.data(), integrity.size());
+ }
+
+ // FINGERPRINT
+ if (addFingerprint)
+ {
+ setBodyLength(buffer, buffer.size() - STUN_HEADER + 8);
+ quint32 fingerprint = generateCrc32(buffer) ^ 0x5354554eL;
+ stream << quint16(Fingerprint);
+ stream << quint16(sizeof(fingerprint));
+ stream << fingerprint;
+ }
+
+ return buffer;
+}
+
+/// If the given packet looks like a STUN message, returns the message
+/// type, otherwise returns 0.
+///
+/// \param buffer
+/// \param cookie
+/// \param id
+
+quint16 QXmppStunMessage::peekType(const QByteArray &buffer, quint32 &cookie, QByteArray &id)
+{
+ if (buffer.size() < STUN_HEADER)
+ return 0;
+
+ // parse STUN header
+ QDataStream stream(buffer);
+ quint16 type;
+ quint16 length;
+ stream >> type;
+ stream >> length;
+ stream >> cookie;
+
+ if (length != buffer.size() - STUN_HEADER)
+ return 0;
+
+ id.resize(ID_SIZE);
+ stream.readRawData(id.data(), id.size());
+ return type;
+}
+
+QString QXmppStunMessage::toString() const
+{
+ QStringList dumpLines;
+ QString typeName;
+ switch (messageMethod())
+ {
+ case Binding: typeName = "Binding"; break;
+ case SharedSecret: typeName = "Shared Secret"; break;
+ case Allocate: typeName = "Allocate"; break;
+ case Refresh: typeName = "Refresh"; break;
+ case Send: typeName = "Send"; break;
+ case Data: typeName = "Data"; break;
+ case CreatePermission: typeName = "CreatePermission"; break;
+ case ChannelBind: typeName = "ChannelBind"; break;
+ default: typeName = "Unknown"; break;
+ }
+ switch (messageClass())
+ {
+ case Request: typeName += " Request"; break;
+ case Indication: typeName += " Indication"; break;
+ case Response: typeName += " Response"; break;
+ case Error: typeName += " Error"; break;
+ default: break;
+ }
+ dumpLines << QString(" type %1 (%2)")
+ .arg(typeName)
+ .arg(QString::number(m_type));
+ dumpLines << QString(" id %1").arg(QString::fromAscii(m_id.toHex()));
+
+ // attributes
+ if (m_attributes.contains(ChannelNumber))
+ dumpLines << QString(" * CHANNEL-NUMBER %1").arg(QString::number(m_channelNumber));
+ if (errorCode)
+ dumpLines << QString(" * ERROR-CODE %1 %2")
+ .arg(QString::number(errorCode), errorPhrase);
+ if (m_attributes.contains(Lifetime))
+ dumpLines << QString(" * LIFETIME %1").arg(QString::number(m_lifetime));
+ if (m_attributes.contains(Nonce))
+ dumpLines << QString(" * NONCE %1").arg(QString::fromLatin1(m_nonce));
+ if (m_attributes.contains(Realm))
+ dumpLines << QString(" * REALM %1").arg(m_realm);
+ if (m_attributes.contains(RequestedTransport))
+ dumpLines << QString(" * REQUESTED-TRANSPORT 0x%1").arg(QString::number(m_requestedTransport, 16));
+ if (m_attributes.contains(ReservationToken))
+ dumpLines << QString(" * RESERVATION-TOKEN %1").arg(QString::fromAscii(m_reservationToken.toHex()));
+ if (m_attributes.contains(Software))
+ dumpLines << QString(" * SOFTWARE %1").arg(m_software);
+ if (m_attributes.contains(Username))
+ dumpLines << QString(" * USERNAME %1").arg(m_username);
+ if (mappedPort)
+ dumpLines << QString(" * MAPPED-ADDRESS %1 %2")
+ .arg(mappedHost.toString(), QString::number(mappedPort));
+ if (m_attributes.contains(ChangeRequest))
+ dumpLines << QString(" * CHANGE-REQUEST %1")
+ .arg(QString::number(m_changeRequest));
+ if (sourcePort)
+ dumpLines << QString(" * SOURCE-ADDRESS %1 %2")
+ .arg(sourceHost.toString(), QString::number(sourcePort));
+ if (changedPort)
+ dumpLines << QString(" * CHANGED-ADDRESS %1 %2")
+ .arg(changedHost.toString(), QString::number(changedPort));
+ if (otherPort)
+ dumpLines << QString(" * OTHER-ADDRESS %1 %2")
+ .arg(otherHost.toString(), QString::number(otherPort));
+ if (xorMappedPort)
+ dumpLines << QString(" * XOR-MAPPED-ADDRESS %1 %2")
+ .arg(xorMappedHost.toString(), QString::number(xorMappedPort));
+ if (xorPeerPort)
+ dumpLines << QString(" * XOR-PEER-ADDRESS %1 %2")
+ .arg(xorPeerHost.toString(), QString::number(xorPeerPort));
+ if (xorRelayedPort)
+ dumpLines << QString(" * XOR-RELAYED-ADDRESS %1 %2")
+ .arg(xorRelayedHost.toString(), QString::number(xorRelayedPort));
+ if (m_attributes.contains(Priority))
+ dumpLines << QString(" * PRIORITY %1").arg(QString::number(m_priority));
+ if (!iceControlling.isEmpty())
+ dumpLines << QString(" * ICE-CONTROLLING %1")
+ .arg(QString::fromAscii(iceControlling.toHex()));
+ if (!iceControlled.isEmpty())
+ dumpLines << QString(" * ICE-CONTROLLED %1")
+ .arg(QString::fromAscii(iceControlled.toHex()));
+
+ return dumpLines.join("\n");
+}
+
+/// Constructs a new QXmppStunTransaction.
+///
+/// \param request
+/// \param receiver
+
+QXmppStunTransaction::QXmppStunTransaction(const QXmppStunMessage &request, QObject *receiver)
+ : QXmppLoggable(receiver),
+ m_request(request),
+ m_tries(0)
+{
+ bool check;
+ Q_UNUSED(check);
+
+ check = connect(this, SIGNAL(writeStun(QXmppStunMessage)),
+ receiver, SLOT(writeStun(QXmppStunMessage)));
+ Q_ASSERT(check);
+
+ check = connect(this, SIGNAL(finished()),
+ receiver, SLOT(transactionFinished()));
+ Q_ASSERT(check);
+
+ // RTO timer
+ m_retryTimer = new QTimer(this);
+ m_retryTimer->setSingleShot(true);
+ check = connect(m_retryTimer, SIGNAL(timeout()),
+ this, SLOT(retry()));
+
+ // send packet immediately
+ m_tries++;
+ emit writeStun(m_request);
+ m_retryTimer->start(STUN_RTO_INTERVAL);
+}
+
+void QXmppStunTransaction::readStun(const QXmppStunMessage &response)
+{
+ if (response.messageClass() == QXmppStunMessage::Error ||
+ response.messageClass() == QXmppStunMessage::Response) {
+ m_response = response;
+ emit finished();
+ }
+}
+
+/// Returns the STUN request.
+
+QXmppStunMessage QXmppStunTransaction::request() const
+{
+ return m_request;
+}
+
+/// Returns the STUN response.
+
+QXmppStunMessage QXmppStunTransaction::response() const
+{
+ return m_response;
+}
+
+void QXmppStunTransaction::retry()
+{
+ if (m_tries >= STUN_RTO_MAX) {
+ m_response.setType(QXmppStunMessage::Error);
+ m_response.errorPhrase = QLatin1String("Request timed out");
+ emit finished();
+ return;
+ }
+
+ // resend request
+ m_tries++;
+ emit writeStun(m_request);
+ m_retryTimer->start(2 * m_retryTimer->interval());
+}
+
+/// Constructs a new QXmppTurnAllocation.
+///
+/// \param parent
+
+QXmppTurnAllocation::QXmppTurnAllocation(QObject *parent)
+ : QXmppLoggable(parent),
+ m_relayedPort(0),
+ m_turnPort(0),
+ m_channelNumber(0x4000),
+ m_lifetime(600),
+ m_state(UnconnectedState)
+{
+ bool check;
+ Q_UNUSED(check);
+
+ socket = new QUdpSocket(this);
+ check = connect(socket, SIGNAL(readyRead()),
+ this, SLOT(readyRead()));
+ Q_ASSERT(check);
+
+ m_timer = new QTimer(this);
+ m_timer->setSingleShot(true);
+ check = connect(m_timer, SIGNAL(timeout()),
+ this, SLOT(refresh()));
+ Q_ASSERT(check);
+
+ // channels are valid 600s, we refresh every 500s
+ m_channelTimer = new QTimer(this);
+ m_channelTimer->setInterval(500 * 1000);
+ check = connect(m_channelTimer, SIGNAL(timeout()),
+ this, SLOT(refreshChannels()));
+ Q_ASSERT(check);
+}
+
+/// Destroys the TURN allocation.
+
+QXmppTurnAllocation::~QXmppTurnAllocation()
+{
+ if (m_state == ConnectedState)
+ disconnectFromHost();
+}
+
+/// Allocates the TURN allocation.
+
+void QXmppTurnAllocation::connectToHost()
+{
+ if (m_state != UnconnectedState)
+ return;
+
+ // start listening for UDP
+ if (socket->state() == QAbstractSocket::UnconnectedState) {
+ if (!socket->bind()) {
+ warning("Could not start listening for TURN");
+ return;
+ }
+ }
+
+ // send allocate request
+ QXmppStunMessage request;
+ request.setType(QXmppStunMessage::Allocate | QXmppStunMessage::Request);
+ request.setId(generateRandomBytes(12));
+ request.setLifetime(m_lifetime);
+ request.setRequestedTransport(0x11);
+ m_transactions << new QXmppStunTransaction(request, this);
+
+ // update state
+ setState(ConnectingState);
+}
+
+/// Releases the TURN allocation.
+
+void QXmppTurnAllocation::disconnectFromHost()
+{
+ m_channelTimer->stop();
+ m_timer->stop();
+
+ // clear channels and any outstanding transactions
+ m_channels.clear();
+ foreach (QXmppStunTransaction *transaction, m_transactions)
+ delete transaction;
+ m_transactions.clear();
+
+ // end allocation
+ if (m_state == ConnectedState) {
+ QXmppStunMessage request;
+ request.setType(QXmppStunMessage::Refresh | QXmppStunMessage::Request);
+ request.setId(generateRandomBytes(12));
+ request.setNonce(m_nonce);
+ request.setRealm(m_realm);
+ request.setUsername(m_username);
+ request.setLifetime(0);
+ m_transactions << new QXmppStunTransaction(request, this);
+
+ setState(ClosingState);
+ } else {
+ setState(UnconnectedState);
+ }
+}
+
+void QXmppTurnAllocation::readyRead()
+{
+ QByteArray buffer;
+ QHostAddress remoteHost;
+ quint16 remotePort;
+ while (socket->hasPendingDatagrams()) {
+ const qint64 size = socket->pendingDatagramSize();
+ buffer.resize(size);
+ socket->readDatagram(buffer.data(), buffer.size(), &remoteHost, &remotePort);
+ handleDatagram(buffer, remoteHost, remotePort);
+ }
+}
+
+void QXmppTurnAllocation::handleDatagram(const QByteArray &buffer, const QHostAddress &remoteHost, quint16 remotePort)
+{
+ // demultiplex channel data
+ if (buffer.size() >= 4 && (buffer[0] & 0xc0) == 0x40) {
+ QDataStream stream(buffer);
+ quint16 channel, length;
+ stream >> channel;
+ stream >> length;
+ if (m_state == ConnectedState && m_channels.contains(channel) && length <= buffer.size() - 4) {
+ emit datagramReceived(buffer.mid(4, length), m_channels[channel].first,
+ m_channels[channel].second);
+ }
+ return;
+ }
+
+ // parse STUN message
+ QXmppStunMessage message;
+ QStringList errors;
+ if (!message.decode(buffer, QByteArray(), &errors)) {
+ foreach (const QString &error, errors)
+ warning(error);
+ return;
+ }
+
+#ifdef QXMPP_DEBUG_STUN
+ logReceived(QString("TURN packet from %1 port %2\n%3").arg(
+ remoteHost.toString(),
+ QString::number(remotePort),
+ message.toString()));
+#endif
+
+ // find transaction
+ foreach (QXmppStunTransaction *transaction, m_transactions) {
+ if (transaction->request().id() == message.id() &&
+ transaction->request().messageMethod() == message.messageMethod()) {
+ transaction->readStun(message);
+ return;
+ }
+ }
+}
+
+/// Refresh allocation.
+
+void QXmppTurnAllocation::refresh()
+{
+ QXmppStunMessage request;
+ request.setType(QXmppStunMessage::Refresh | QXmppStunMessage::Request);
+ request.setId(generateRandomBytes(12));
+ request.setNonce(m_nonce);
+ request.setRealm(m_realm);
+ request.setUsername(m_username);
+ m_transactions << new QXmppStunTransaction(request, this);
+}
+
+/// Refresh channel bindings.
+
+void QXmppTurnAllocation::refreshChannels()
+{
+ foreach (quint16 channel, m_channels.keys()) {
+ QXmppStunMessage request;
+ request.setType(QXmppStunMessage::ChannelBind | QXmppStunMessage::Request);
+ request.setId(generateRandomBytes(12));
+ request.setNonce(m_nonce);
+ request.setRealm(m_realm);
+ request.setUsername(m_username);
+ request.setChannelNumber(channel);
+ request.xorPeerHost = m_channels[channel].first;
+ request.xorPeerPort = m_channels[channel].second;
+ m_transactions << new QXmppStunTransaction(request, this);
+ }
+}
+
+/// Returns the relayed host address, i.e. the address on the server
+/// used to communicate with peers.
+
+QHostAddress QXmppTurnAllocation::relayedHost() const
+{
+ return m_relayedHost;
+}
+
+/// Returns the relayed port, i.e. the port on the server used to communicate
+/// with peers.
+
+quint16 QXmppTurnAllocation::relayedPort() const
+{
+ return m_relayedPort;
+}
+
+/// Sets the password used to authenticate with the TURN server.
+///
+/// \param password
+
+void QXmppTurnAllocation::setPassword(const QString &password)
+{
+ m_password = password;
+}
+
+/// Sets the TURN server to use.
+///
+/// \param host The address of the TURN server.
+/// \param port The port of the TURN server.
+
+void QXmppTurnAllocation::setServer(const QHostAddress &host, quint16 port)
+{
+ m_turnHost = host;
+ m_turnPort = port;
+}
+
+/// Sets the \a user used for authentication with the TURN server.
+///
+/// \param user
+
+void QXmppTurnAllocation::setUser(const QString &user)
+{
+ m_username = user;
+}
+
+/// Returns the current state of the allocation.
+///
+
+QXmppTurnAllocation::AllocationState QXmppTurnAllocation::state() const
+{
+ return m_state;
+}
+
+void QXmppTurnAllocation::setState(AllocationState state)
+{
+ if (state == m_state)
+ return;
+ m_state = state;
+ if (m_state == ConnectedState) {
+ emit connected();
+ } else if (m_state == UnconnectedState) {
+ m_timer->stop();
+ emit disconnected();
+ }
+}
+
+void QXmppTurnAllocation::transactionFinished()
+{
+ QXmppStunTransaction *transaction = qobject_cast<QXmppStunTransaction*>(sender());
+ if (!transaction || !m_transactions.removeAll(transaction))
+ return;
+ transaction->deleteLater();
+
+ // handle authentication
+ const QXmppStunMessage reply = transaction->response();
+ if (reply.messageClass() == QXmppStunMessage::Error &&
+ reply.errorCode == 401 &&
+ (reply.nonce() != m_nonce && reply.realm() != m_realm))
+ {
+ // update long-term credentials
+ m_nonce = reply.nonce();
+ m_realm = reply.realm();
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ hash.addData((m_username + ":" + m_realm + ":" + m_password).toUtf8());
+ m_key = hash.result();
+
+ // retry request
+ QXmppStunMessage request(transaction->request());
+ request.setId(generateRandomBytes(12));
+ request.setNonce(m_nonce);
+ request.setRealm(m_realm);
+ request.setUsername(m_username);
+ m_transactions << new QXmppStunTransaction(request, this);
+ return;
+ }
+
+ const quint16 method = transaction->request().messageMethod();
+ if (method == QXmppStunMessage::Allocate) {
+
+ if (reply.messageClass() == QXmppStunMessage::Error) {
+ warning(QString("Allocation failed: %1 %2").arg(
+ QString::number(reply.errorCode), reply.errorPhrase));
+ setState(UnconnectedState);
+ return;
+ }
+ if (reply.xorRelayedHost.isNull() ||
+ reply.xorRelayedHost.protocol() != QAbstractSocket::IPv4Protocol ||
+ !reply.xorRelayedPort) {
+ warning("Allocation did not yield a valid relayed address");
+ setState(UnconnectedState);
+ return;
+ }
+
+ // store relayed address
+ m_relayedHost = reply.xorRelayedHost;
+ m_relayedPort = reply.xorRelayedPort;
+
+ // schedule refresh
+ m_lifetime = reply.lifetime();
+ m_timer->start((m_lifetime - 60) * 1000);
+
+ setState(ConnectedState);
+
+ } else if (method == QXmppStunMessage::ChannelBind) {
+
+ if (reply.messageClass() == QXmppStunMessage::Error) {
+ warning(QString("ChannelBind failed: %1 %2").arg(
+ QString::number(reply.errorCode), reply.errorPhrase));
+
+ // remove channel
+ m_channels.remove(transaction->request().channelNumber());
+ if (m_channels.isEmpty())
+ m_channelTimer->stop();
+ return;
+ }
+
+ } else if (method == QXmppStunMessage::Refresh) {
+
+ if (reply.messageClass() == QXmppStunMessage::Error) {
+ warning(QString("Refresh failed: %1 %2").arg(
+ QString::number(reply.errorCode), reply.errorPhrase));
+ setState(UnconnectedState);
+ return;
+ }
+
+ if (m_state == ClosingState) {
+ setState(UnconnectedState);
+ return;
+ }
+
+ // schedule refresh
+ m_lifetime = reply.lifetime();
+ m_timer->start((m_lifetime - 60) * 1000);
+
+ }
+}
+
+qint64 QXmppTurnAllocation::writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port)
+{
+ if (m_state != ConnectedState)
+ return -1;
+
+ const Address addr = qMakePair(host, port);
+ quint16 channel = m_channels.key(addr);
+
+ if (!channel) {
+ channel = m_channelNumber++;
+ m_channels.insert(channel, addr);
+
+ // bind channel
+ QXmppStunMessage request;
+ request.setType(QXmppStunMessage::ChannelBind | QXmppStunMessage::Request);
+ request.setId(generateRandomBytes(12));
+ request.setNonce(m_nonce);
+ request.setRealm(m_realm);
+ request.setUsername(m_username);
+ request.setChannelNumber(channel);
+ request.xorPeerHost = host;
+ request.xorPeerPort = port;
+ m_transactions << new QXmppStunTransaction(request, this);
+
+ // schedule refresh
+ if (!m_channelTimer->isActive())
+ m_channelTimer->start();
+ }
+
+ // send data
+ QByteArray channelData;
+ channelData.reserve(4 + data.size());
+ QDataStream stream(&channelData, QIODevice::WriteOnly);
+ stream << channel;
+ stream << quint16(data.size());
+ stream.writeRawData(data.data(), data.size());
+ if (socket->writeDatagram(channelData, m_turnHost, m_turnPort) == channelData.size())
+ return data.size();
+ else
+ return -1;
+}
+
+void QXmppTurnAllocation::writeStun(const QXmppStunMessage &message)
+{
+ socket->writeDatagram(message.encode(m_key), m_turnHost, m_turnPort);
+#ifdef QXMPP_DEBUG_STUN
+ logSent(QString("TURN packet to %1 port %2\n%3").arg(
+ m_turnHost.toString(),
+ QString::number(m_turnPort),
+ message.toString()));
+#endif
+}
+
+QXmppIceComponent::Pair::Pair(int component, bool controlling)
+ : checked(QIODevice::NotOpen),
+ socket(0),
+ m_component(component),
+ m_controlling(controlling)
+{
+ transaction = generateRandomBytes(ID_SIZE);
+}
+
+quint64 QXmppIceComponent::Pair::priority() const
+{
+ QXmppJingleCandidate local;
+ local.setComponent(m_component);
+ local.setType(socket ? QXmppJingleCandidate::HostType : QXmppJingleCandidate::RelayedType);
+ local.setPriority(candidatePriority(local));
+
+ // see RFC 5245 - 5.7.2. Computing Pair Priority and Ordering Pairs
+ const quint32 G = m_controlling ? local.priority() : remote.priority();
+ const quint32 D = m_controlling ? remote.priority() : local.priority();
+ return (quint64(1) << 32) * qMin(G, D) + 2 * qMax(G, D) + (G > D ? 1 : 0);
+}
+
+QString QXmppIceComponent::Pair::toString() const
+{
+ QString str = QString("%1 port %2").arg(remote.host().toString(), QString::number(remote.port()));
+ if (socket)
+ str += QString(" (local %1 port %2)").arg(socket->localAddress().toString(), QString::number(socket->localPort()));
+ else
+ str += QString(" (relayed)");
+ if (!reflexive.host().isNull() && reflexive.port())
+ str += QString(" (reflexive %1 port %2)").arg(reflexive.host().toString(), QString::number(reflexive.port()));
+ return str;
+}
+
+/// Constructs a new QXmppIceComponent.
+///
+/// \param controlling
+/// \param parent
+
+QXmppIceComponent::QXmppIceComponent(QObject *parent)
+ : QXmppLoggable(parent),
+ m_component(0),
+ m_activePair(0),
+ m_fallbackPair(0),
+ m_iceControlling(false),
+ m_peerReflexivePriority(0),
+ m_stunPort(0),
+ m_stunTries(0),
+ m_turnConfigured(false)
+{
+ bool check;
+ Q_UNUSED(check);
+
+ m_localUser = generateStanzaHash(4);
+ m_localPassword = generateStanzaHash(22);
+
+ m_timer = new QTimer(this);
+ m_timer->setInterval(500);
+ check = connect(m_timer, SIGNAL(timeout()),
+ this, SLOT(checkCandidates()));
+ Q_ASSERT(check);
+
+ m_stunTimer = new QTimer(this);
+ m_stunTimer->setInterval(500);
+ check = connect(m_stunTimer, SIGNAL(timeout()),
+ this, SLOT(checkStun()));
+ Q_ASSERT(check);
+
+ m_turnAllocation = new QXmppTurnAllocation(this);
+ check = connect(m_turnAllocation, SIGNAL(connected()),
+ this, SLOT(turnConnected()));
+ Q_ASSERT(check);
+ check = connect(m_turnAllocation, SIGNAL(datagramReceived(QByteArray,QHostAddress,quint16)),
+ this, SLOT(handleDatagram(QByteArray,QHostAddress,quint16)));
+ Q_ASSERT(check);
+}
+
+/// Destroys the QXmppIceComponent.
+
+QXmppIceComponent::~QXmppIceComponent()
+{
+ foreach (Pair *pair, m_pairs)
+ delete pair;
+}
+
+/// Returns the component id for the current socket, e.g. 1 for RTP
+/// and 2 for RTCP.
+
+int QXmppIceComponent::component() const
+{
+ return m_component;
+}
+
+/// Sets the component id for the current socket, e.g. 1 for RTP
+/// and 2 for RTCP.
+///
+/// \param component
+
+void QXmppIceComponent::setComponent(int component)
+{
+ m_component = component;
+
+ // calculate peer-reflexive candidate priority
+ // see RFC 5245 - 7.1.2.1. PRIORITY and USE-CANDIDATE
+ QXmppJingleCandidate reflexive;
+ reflexive.setComponent(m_component);
+ reflexive.setType(QXmppJingleCandidate::PeerReflexiveType);
+ m_peerReflexivePriority = candidatePriority(reflexive);
+
+ setObjectName(QString("STUN(%1)").arg(QString::number(m_component)));
+}
+
+void QXmppIceComponent::checkCandidates()
+{
+ debug("Checking remote candidates");
+ foreach (Pair *pair, m_pairs)
+ {
+ if (m_remoteUser.isEmpty())
+ continue;
+
+ // send a binding request
+ QXmppStunMessage message;
+ message.setId(pair->transaction);
+ message.setType(QXmppStunMessage::Binding | QXmppStunMessage::Request);
+ message.setPriority(m_peerReflexivePriority);
+ message.setUsername(QString("%1:%2").arg(m_remoteUser, m_localUser));
+ if (m_iceControlling)
+ {
+ message.iceControlling = QByteArray(8, 0);
+ message.useCandidate = true;
+ } else {
+ message.iceControlled = QByteArray(8, 0);
+ }
+ writeStun(message, pair);
+ }
+
+}
+
+void QXmppIceComponent::checkStun()
+{
+ if (m_stunHost.isNull() || !m_stunPort || m_stunTries > 10) {
+ m_stunTimer->stop();
+ return;
+ }
+
+ // Send a request to STUN server to determine server-reflexive candidate
+ foreach (QUdpSocket *socket, m_sockets)
+ {
+ QXmppStunMessage msg;
+ msg.setType(QXmppStunMessage::Binding | QXmppStunMessage::Request);
+ msg.setId(m_stunId);
+#ifdef QXMPP_DEBUG_STUN
+ logSent(QString("STUN packet to %1 port %2\n%3").arg(m_stunHost.toString(),
+ QString::number(m_stunPort), msg.toString()));
+#endif
+ socket->writeDatagram(msg.encode(), m_stunHost, m_stunPort);
+ }
+ m_stunTries++;
+}
+
+/// Stops ICE connectivity checks and closes the underlying sockets.
+
+void QXmppIceComponent::close()
+{
+ foreach (QUdpSocket *socket, m_sockets)
+ socket->close();
+ m_turnAllocation->disconnectFromHost();
+ m_timer->stop();
+ m_stunTimer->stop();
+ m_activePair = 0;
+}
+
+/// Starts ICE connectivity checks.
+
+void QXmppIceComponent::connectToHost()
+{
+ if (m_activePair)
+ return;
+
+ checkCandidates();
+ m_timer->start();
+}
+
+/// Returns true if ICE negotiation completed, false otherwise.
+
+bool QXmppIceComponent::isConnected() const
+{
+ return m_activePair != 0;
+}
+
+void QXmppIceComponent::setIceControlling(bool controlling)
+{
+ m_iceControlling = controlling;
+}
+
+/// Returns the list of local candidates.
+
+QList<QXmppJingleCandidate> QXmppIceComponent::localCandidates() const
+{
+ return m_localCandidates;
+}
+
+/// Sets the local user fragment.
+///
+/// \param user
+
+void QXmppIceComponent::setLocalUser(const QString &user)
+{
+ m_localUser = user;
+}
+
+/// Sets the local password.
+///
+/// \param password
+
+void QXmppIceComponent::setLocalPassword(const QString &password)
+{
+ m_localPassword = password;
+}
+
+/// Adds a remote STUN candidate.
+
+bool QXmppIceComponent::addRemoteCandidate(const QXmppJingleCandidate &candidate)
+{
+ if (candidate.component() != m_component ||
+ (candidate.type() != QXmppJingleCandidate::HostType &&
+ candidate.type() != QXmppJingleCandidate::RelayedType &&
+ candidate.type() != QXmppJingleCandidate::ServerReflexiveType) ||
+ candidate.protocol() != "udp" ||
+ (candidate.host().protocol() != QAbstractSocket::IPv4Protocol &&
+ candidate.host().protocol() != QAbstractSocket::IPv6Protocol))
+ return false;
+
+ foreach (Pair *pair, m_pairs)
+ if (pair->remote.host() == candidate.host() &&
+ pair->remote.port() == candidate.port())
+ return false;
+
+ foreach (QUdpSocket *socket, m_sockets)
+ {
+ // do not pair IPv4 with IPv6 or global with link-local addresses
+ if (socket->localAddress().protocol() != candidate.host().protocol() ||
+ isIPv6LinkLocalAddress(socket->localAddress()) != isIPv6LinkLocalAddress(candidate.host()))
+ continue;
+
+ Pair *pair = new Pair(m_component, m_iceControlling);
+ pair->remote = candidate;
+ if (isIPv6LinkLocalAddress(pair->remote.host()))
+ {
+ QHostAddress remoteHost = pair->remote.host();
+ remoteHost.setScopeId(socket->localAddress().scopeId());
+ pair->remote.setHost(remoteHost);
+ }
+ pair->socket = socket;
+ m_pairs << pair;
+
+ if (!m_fallbackPair)
+ m_fallbackPair = pair;
+ }
+
+ // only use relaying for IPv4 candidates
+ if (m_turnConfigured && candidate.host().protocol() == QAbstractSocket::IPv4Protocol) {
+ Pair *pair = new Pair(m_component, m_iceControlling);
+ pair->remote = candidate;
+ pair->socket = 0;
+ m_pairs << pair;
+ }
+ return true;
+}
+
+/// Adds a discovered peer-reflexive STUN candidate.
+
+QXmppIceComponent::Pair *QXmppIceComponent::addRemoteCandidate(QUdpSocket *socket, const QHostAddress &host, quint16 port, quint32 priority)
+{
+ foreach (Pair *pair, m_pairs)
+ if (pair->remote.host() == host &&
+ pair->remote.port() == port &&
+ pair->socket == socket)
+ return pair;
+
+ QXmppJingleCandidate candidate;
+ candidate.setComponent(m_component);
+ candidate.setHost(host);
+ candidate.setId(generateStanzaHash(10));
+ candidate.setPort(port);
+ candidate.setPriority(priority);
+ candidate.setProtocol("udp");
+ candidate.setType(QXmppJingleCandidate::PeerReflexiveType);
+
+ Pair *pair = new Pair(m_component, m_iceControlling);
+ pair->remote = candidate;
+ pair->socket = socket;
+ m_pairs << pair;
+
+ debug(QString("Added candidate %1").arg(pair->toString()));
+ return pair;
+}
+
+/// Sets the remote user fragment.
+///
+/// \param user
+
+void QXmppIceComponent::setRemoteUser(const QString &user)
+{
+ m_remoteUser = user;
+}
+
+/// Sets the remote password.
+///
+/// \param password
+
+void QXmppIceComponent::setRemotePassword(const QString &password)
+{
+ m_remotePassword = password;
+}
+
+/// Sets the list of sockets to use for this component.
+///
+/// \param sockets
+
+void QXmppIceComponent::setSockets(QList<QUdpSocket*> sockets)
+{
+ // clear previous candidates and sockets
+ m_localCandidates.clear();
+ foreach (QUdpSocket *socket, m_sockets)
+ delete socket;
+ m_sockets.clear();
+
+ // store candidates
+ int foundation = 0;
+ foreach (QUdpSocket *socket, sockets)
+ {
+ socket->setParent(this);
+ connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
+
+ QXmppJingleCandidate candidate;
+ candidate.setComponent(m_component);
+ candidate.setFoundation(foundation++);
+ // remove scope ID from IPv6 non-link local addresses
+ QHostAddress addr(socket->localAddress());
+ if (addr.protocol() == QAbstractSocket::IPv6Protocol &&
+ !isIPv6LinkLocalAddress(addr)) {
+ addr.setScopeId(QString());
+ }
+ candidate.setHost(addr);
+ candidate.setId(generateStanzaHash(10));
+ candidate.setPort(socket->localPort());
+ candidate.setProtocol("udp");
+ candidate.setType(QXmppJingleCandidate::HostType);
+ candidate.setPriority(candidatePriority(candidate));
+
+ m_sockets << socket;
+ m_localCandidates << candidate;
+ }
+
+ // start STUN checks
+ if (!m_stunHost.isNull() && m_stunPort) {
+ m_stunTries = 0;
+ checkStun();
+ m_stunTimer->start();
+ }
+
+ // connect to TURN server
+ if (m_turnConfigured)
+ m_turnAllocation->connectToHost();
+}
+
+/// Sets the STUN server to use to determine server-reflexive addresses
+/// and ports.
+///
+/// \param host The address of the STUN server.
+/// \param port The port of the STUN server.
+
+void QXmppIceComponent::setStunServer(const QHostAddress &host, quint16 port)
+{
+ m_stunHost = host;
+ m_stunPort = port;
+ m_stunId = generateRandomBytes(ID_SIZE);
+}
+
+/// Sets the TURN server to use to relay packets in double-NAT configurations.
+///
+/// \param host The address of the TURN server.
+/// \param port The port of the TURN server.
+
+void QXmppIceComponent::setTurnServer(const QHostAddress &host, quint16 port)
+{
+ m_turnAllocation->setServer(host, port);
+ m_turnConfigured = !host.isNull() && port;
+}
+
+/// Sets the \a user used for authentication with the TURN server.
+///
+/// \param user
+
+void QXmppIceComponent::setTurnUser(const QString &user)
+{
+ m_turnAllocation->setUser(user);
+}
+
+/// Sets the \a password used for authentication with the TURN server.
+///
+/// \param password
+
+void QXmppIceComponent::setTurnPassword(const QString &password)
+{
+ m_turnAllocation->setPassword(password);
+}
+
+void QXmppIceComponent::readyRead()
+{
+ QUdpSocket *socket = qobject_cast<QUdpSocket*>(sender());
+ if (!socket)
+ return;
+
+ QByteArray buffer;
+ QHostAddress remoteHost;
+ quint16 remotePort;
+ while (socket->hasPendingDatagrams()) {
+ const qint64 size = socket->pendingDatagramSize();
+ buffer.resize(size);
+ socket->readDatagram(buffer.data(), buffer.size(), &remoteHost, &remotePort);
+ handleDatagram(buffer, remoteHost, remotePort, socket);
+ }
+}
+
+void QXmppIceComponent::handleDatagram(const QByteArray &buffer, const QHostAddress &remoteHost, quint16 remotePort, QUdpSocket *socket)
+{
+ // if this is not a STUN message, emit it
+ quint32 messageCookie;
+ QByteArray messageId;
+ quint16 messageType = QXmppStunMessage::peekType(buffer, messageCookie, messageId);
+ if (!messageType || messageCookie != STUN_MAGIC)
+ {
+ // use this as an opportunity to flag a potential pair
+ foreach (Pair *pair, m_pairs) {
+ if (pair->remote.host() == remoteHost &&
+ pair->remote.port() == remotePort) {
+ m_fallbackPair = pair;
+ break;
+ }
+ }
+ emit datagramReceived(buffer);
+ return;
+ }
+
+ // determine password to use
+ QString messagePassword;
+ if (messageId != m_stunId)
+ {
+ messagePassword = (messageType & 0xFF00) ? m_remotePassword : m_localPassword;
+ if (messagePassword.isEmpty())
+ return;
+ }
+
+ // parse STUN message
+ QXmppStunMessage message;
+ QStringList errors;
+ if (!message.decode(buffer, messagePassword.toUtf8(), &errors))
+ {
+ foreach (const QString &error, errors)
+ warning(error);
+ return;
+ }
+#ifdef QXMPP_DEBUG_STUN
+ logReceived(QString("STUN packet from %1 port %2\n%3").arg(
+ remoteHost.toString(),
+ QString::number(remotePort),
+ message.toString()));
+#endif
+
+ // check how to handle message
+ if (message.id() == m_stunId)
+ {
+ m_stunTimer->stop();
+
+ // determine server-reflexive address
+ QHostAddress reflexiveHost;
+ quint16 reflexivePort = 0;
+ if (!message.xorMappedHost.isNull() && message.xorMappedPort != 0)
+ {
+ reflexiveHost = message.xorMappedHost;
+ reflexivePort = message.xorMappedPort;
+ }
+ else if (!message.mappedHost.isNull() && message.mappedPort != 0)
+ {
+ reflexiveHost = message.mappedHost;
+ reflexivePort = message.mappedPort;
+ } else {
+ warning("STUN server did not provide a reflexive address");
+ return;
+ }
+
+ // check whether this candidates is already known
+ foreach (const QXmppJingleCandidate &candidate, m_localCandidates)
+ {
+ if (candidate.host() == reflexiveHost &&
+ candidate.port() == reflexivePort &&
+ candidate.type() == QXmppJingleCandidate::ServerReflexiveType)
+ return;
+ }
+
+ // add the new local candidate
+ debug(QString("Adding server-reflexive candidate %1 port %2").arg(reflexiveHost.toString(), QString::number(reflexivePort)));
+ QXmppJingleCandidate candidate;
+ candidate.setComponent(m_component);
+ candidate.setHost(reflexiveHost);
+ candidate.setId(generateStanzaHash(10));
+ candidate.setPort(reflexivePort);
+ candidate.setProtocol("udp");
+ candidate.setType(QXmppJingleCandidate::ServerReflexiveType);
+ candidate.setPriority(candidatePriority(candidate));
+ m_localCandidates << candidate;
+
+ emit localCandidatesChanged();
+ return;
+ }
+
+ // process message from peer
+ Pair *pair = 0;
+ if (message.type() == (QXmppStunMessage::Binding | QXmppStunMessage::Request))
+ {
+ // add remote candidate
+ pair = addRemoteCandidate(socket, remoteHost, remotePort, message.priority());
+
+ // send a binding response
+ QXmppStunMessage response;
+ response.setId(message.id());
+ response.setType(QXmppStunMessage::Binding | QXmppStunMessage::Response);
+ response.setUsername(message.username());
+ response.xorMappedHost = pair->remote.host();
+ response.xorMappedPort = pair->remote.port();
+ writeStun(response, pair);
+
+ // update state
+ if (m_iceControlling || message.useCandidate)
+ {
+ debug(QString("ICE reverse check complete %1").arg(pair->toString()));
+ pair->checked |= QIODevice::ReadOnly;
+ }
+
+ if (!m_iceControlling && !m_activePair && !m_remoteUser.isEmpty())
+ {
+ // send a triggered connectivity test
+ QXmppStunMessage message;
+ message.setId(pair->transaction);
+ message.setType(QXmppStunMessage::Binding | QXmppStunMessage::Request);
+ message.setPriority(m_peerReflexivePriority);
+ message.setUsername(QString("%1:%2").arg(m_remoteUser, m_localUser));
+ message.iceControlled = QByteArray(8, 0);
+ writeStun(message, pair);
+ }
+
+ } else if (message.type() == (QXmppStunMessage::Binding | QXmppStunMessage::Response)) {
+
+ // find the pair for this transaction
+ foreach (Pair *ptr, m_pairs)
+ {
+ if (ptr->transaction == message.id())
+ {
+ pair = ptr;
+ break;
+ }
+ }
+ if (!pair)
+ {
+ debug(QString("Unknown transaction %1").arg(QString::fromAscii(message.id().toHex())));
+ return;
+ }
+ // store peer-reflexive address
+ pair->reflexive.setHost(message.xorMappedHost);
+ pair->reflexive.setPort(message.xorMappedPort);
+
+#if 0
+ // send a binding indication
+ QXmppStunMessage indication;
+ indication.setId(generateRandomBytes(ID_SIZE));
+ indication.setType(BindingIndication);
+ m_socket->writeStun(indication, pair);
+#endif
+
+ // outgoing media can flow
+ debug(QString("ICE forward check complete %1").arg(pair->toString()));
+ pair->checked |= QIODevice::WriteOnly;
+ }
+
+ // signal completion
+ if (pair && pair->checked == QIODevice::ReadWrite)
+ {
+ m_timer->stop();
+ if (!m_activePair || pair->priority() > m_activePair->priority()) {
+ info(QString("ICE pair selected %1 (priority: %2)").arg(
+ pair->toString(), QString::number(pair->priority())));
+ const bool wasConnected = (m_activePair != 0);
+ m_activePair = pair;
+ if (!wasConnected)
+ emit connected();
+ }
+ }
+}
+
+void QXmppIceComponent::turnConnected()
+{
+ // add the new local candidate
+ debug(QString("Adding relayed candidate %1 port %2").arg(
+ m_turnAllocation->relayedHost().toString(),
+ QString::number(m_turnAllocation->relayedPort())));
+ QXmppJingleCandidate candidate;
+ candidate.setComponent(m_component);
+ candidate.setHost(m_turnAllocation->relayedHost());
+ candidate.setId(generateStanzaHash(10));
+ candidate.setPort(m_turnAllocation->relayedPort());
+ candidate.setProtocol("udp");
+ candidate.setType(QXmppJingleCandidate::RelayedType);
+ candidate.setPriority(candidatePriority(candidate));
+ m_localCandidates << candidate;
+
+ emit localCandidatesChanged();
+}
+
+static QList<QUdpSocket*> reservePort(const QList<QHostAddress> &addresses, quint16 port, QObject *parent)
+{
+ QList<QUdpSocket*> sockets;
+ foreach (const QHostAddress &address, addresses) {
+ QUdpSocket *socket = new QUdpSocket(parent);
+ sockets << socket;
+ if (!socket->bind(address, port)) {
+ for (int i = 0; i < sockets.size(); ++i)
+ delete sockets[i];
+ sockets.clear();
+ break;
+ }
+ }
+ return sockets;
+}
+
+/// Returns the list of local network addresses.
+
+QList<QHostAddress> QXmppIceComponent::discoverAddresses()
+{
+ QList<QHostAddress> addresses;
+ foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces())
+ {
+ if (!(interface.flags() & QNetworkInterface::IsRunning) ||
+ interface.flags() & QNetworkInterface::IsLoopBack)
+ continue;
+
+ foreach (const QNetworkAddressEntry &entry, interface.addressEntries())
+ {
+ QHostAddress ip = entry.ip();
+ if ((ip.protocol() != QAbstractSocket::IPv4Protocol &&
+ ip.protocol() != QAbstractSocket::IPv6Protocol) ||
+ entry.netmask().isNull())
+ continue;
+
+#ifdef Q_OS_MAC
+ // FIXME: on Mac OS X, sending IPv6 UDP packets fails
+ if (ip.protocol() == QAbstractSocket::IPv6Protocol)
+ continue;
+#endif
+
+ // FIXME: for now skip IPv6 link-local addresses, seems to upset
+ // clients such as empathy
+ if (isIPv6LinkLocalAddress(ip)) {
+ ip.setScopeId(interface.name());
+ continue;
+ }
+ addresses << ip;
+ }
+ }
+ return addresses;
+}
+
+/// Tries to bind \a count UDP sockets on each of the given \a addresses.
+///
+/// The port numbers are chosen so that they are consecutive, starting at
+/// an even port. This makes them suitable for RTP/RTCP sockets pairs.
+///
+/// \param addresses The network address on which to bind the sockets.
+/// \param count The number of ports to reserve.
+/// \param parent The parent object for the sockets.
+
+QList<QUdpSocket*> QXmppIceComponent::reservePorts(const QList<QHostAddress> &addresses, int count, QObject *parent)
+{
+ QList<QUdpSocket*> sockets;
+ if (addresses.isEmpty() || !count)
+ return sockets;
+
+ const int expectedSize = addresses.size() * count;
+ quint16 port = 49152;
+ while (sockets.size() != expectedSize) {
+ // reserve first port (even number)
+ if (port % 2)
+ port++;
+ QList<QUdpSocket*> socketChunk;
+ while (socketChunk.isEmpty() && port <= 65536 - count) {
+ socketChunk = reservePort(addresses, port, parent);
+ if (socketChunk.isEmpty())
+ port += 2;
+ }
+ if (socketChunk.isEmpty())
+ return sockets;
+
+ // reserve other ports
+ sockets << socketChunk;
+ for (int i = 1; i < count; ++i) {
+ socketChunk = reservePort(addresses, ++port, parent);
+ if (socketChunk.isEmpty())
+ break;
+ sockets << socketChunk;
+ }
+
+ // cleanup if we failed
+ if (sockets.size() != expectedSize) {
+ for (int i = 0; i < sockets.size(); ++i)
+ delete sockets[i];
+ sockets.clear();
+ }
+ }
+ return sockets;
+}
+
+/// Sends a data packet to the remote party.
+///
+/// \param datagram
+
+qint64 QXmppIceComponent::sendDatagram(const QByteArray &datagram)
+{
+ Pair *pair = m_activePair ? m_activePair : m_fallbackPair;
+ if (!pair)
+ return -1;
+ if (pair->socket)
+ return pair->socket->writeDatagram(datagram, pair->remote.host(), pair->remote.port());
+ else if (m_turnAllocation->state() == QXmppTurnAllocation::ConnectedState)
+ return m_turnAllocation->writeDatagram(datagram, pair->remote.host(), pair->remote.port());
+ else
+ return -1;
+}
+
+/// Sends a STUN packet to the remote party.
+
+qint64 QXmppIceComponent::writeStun(const QXmppStunMessage &message, QXmppIceComponent::Pair *pair)
+{
+ qint64 ret;
+ const QString messagePassword = (message.type() & 0xFF00) ? m_localPassword : m_remotePassword;
+ if (pair->socket)
+ ret = pair->socket->writeDatagram(
+ message.encode(messagePassword.toUtf8()),
+ pair->remote.host(),
+ pair->remote.port());
+ else if (m_turnAllocation->state() == QXmppTurnAllocation::ConnectedState)
+ ret = m_turnAllocation->writeDatagram(
+ message.encode(messagePassword.toUtf8()),
+ pair->remote.host(),
+ pair->remote.port());
+ else
+ return -1;
+#ifdef QXMPP_DEBUG_STUN
+ logSent(QString("Sent to %1\n%2").arg(pair->toString(), message.toString()));
+#endif
+ return ret;
+}
+
+/// Constructs a new ICE connection.
+///
+/// \param controlling
+/// \param parent
+
+QXmppIceConnection::QXmppIceConnection(QObject *parent)
+ : QXmppLoggable(parent),
+ m_iceControlling(false),
+ m_stunPort(0)
+{
+ bool check;
+
+ m_localUser = generateStanzaHash(4);
+ m_localPassword = generateStanzaHash(22);
+
+ // timer to limit connection time to 30 seconds
+ m_connectTimer = new QTimer(this);
+ m_connectTimer->setInterval(30000);
+ m_connectTimer->setSingleShot(true);
+ check = connect(m_connectTimer, SIGNAL(timeout()),
+ this, SLOT(slotTimeout()));
+ Q_ASSERT(check);
+ Q_UNUSED(check);
+}
+
+/// Returns the given component of this ICE connection.
+///
+/// \param component
+
+QXmppIceComponent *QXmppIceConnection::component(int component)
+{
+ return m_components.value(component);
+}
+
+/// Adds a component to this ICE connection, for instance 1 for RTP
+/// or 2 for RTCP.
+///
+/// \param component
+
+void QXmppIceConnection::addComponent(int component)
+{
+ bool check;
+ Q_UNUSED(check);
+
+ if (m_components.contains(component))
+ {
+ warning(QString("Already have component %1").arg(QString::number(component)));
+ return;
+ }
+
+ QXmppIceComponent *socket = new QXmppIceComponent(this);
+ socket->setComponent(component);
+ socket->setIceControlling(m_iceControlling);
+ socket->setLocalUser(m_localUser);
+ socket->setLocalPassword(m_localPassword);
+ socket->setStunServer(m_stunHost, m_stunPort);
+ socket->setTurnServer(m_turnHost, m_turnPort);
+ socket->setTurnUser(m_turnUser);
+ socket->setTurnPassword(m_turnPassword);
+
+ check = connect(socket, SIGNAL(localCandidatesChanged()),
+ this, SIGNAL(localCandidatesChanged()));
+ Q_ASSERT(check);
+
+ check = connect(socket, SIGNAL(connected()),
+ this, SLOT(slotConnected()));
+ Q_ASSERT(check);
+
+ m_components[component] = socket;
+}
+
+/// Adds a candidate for one of the remote components.
+///
+/// \param candidate
+
+void QXmppIceConnection::addRemoteCandidate(const QXmppJingleCandidate &candidate)
+{
+ QXmppIceComponent *socket = m_components.value(candidate.component());
+ if (!socket)
+ {
+ warning(QString("Not adding candidate for unknown component %1").arg(
+ QString::number(candidate.component())));
+ return;
+ }
+ socket->addRemoteCandidate(candidate);
+}
+
+/// Binds the local sockets to the specified addresses.
+///
+/// \param addresses The addresses on which to listen.
+
+bool QXmppIceConnection::bind(const QList<QHostAddress> &addresses)
+{
+ // reserve ports
+ QList<QUdpSocket*> sockets = QXmppIceComponent::reservePorts(addresses, m_components.size());
+ if (sockets.isEmpty() && !addresses.isEmpty())
+ return false;
+
+ // assign sockets
+ QList<int> keys = m_components.keys();
+ qSort(keys);
+ int s = 0;
+ foreach (int k, keys) {
+ m_components[k]->setSockets(sockets.mid(s, addresses.size()));
+ s += addresses.size();
+ }
+
+ return true;
+}
+
+/// Closes the ICE connection.
+
+void QXmppIceConnection::close()
+{
+ m_connectTimer->stop();
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->close();
+}
+
+/// Starts ICE connectivity checks.
+
+void QXmppIceConnection::connectToHost()
+{
+ if (isConnected() || m_connectTimer->isActive())
+ return;
+
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->connectToHost();
+ m_connectTimer->start();
+}
+
+
+/// Returns true if ICE negotiation completed, false otherwise.
+
+bool QXmppIceConnection::isConnected() const
+{
+ foreach (QXmppIceComponent *socket, m_components.values())
+ if (!socket->isConnected())
+ return false;
+ return true;
+}
+
+void QXmppIceConnection::setIceControlling(bool controlling)
+{
+ m_iceControlling = controlling;
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setIceControlling(controlling);
+}
+
+/// Returns the list of local HOST CANDIDATES candidates by iterating
+/// over the available network interfaces.
+
+QList<QXmppJingleCandidate> QXmppIceConnection::localCandidates() const
+{
+ QList<QXmppJingleCandidate> candidates;
+ foreach (QXmppIceComponent *socket, m_components.values())
+ candidates += socket->localCandidates();
+ return candidates;
+}
+
+/// Returns the local user fragment.
+
+QString QXmppIceConnection::localUser() const
+{
+ return m_localUser;
+}
+
+/// Sets the local user fragment.
+///
+/// You do not usually need to call this as one is automatically generated.
+///
+/// \param user
+
+void QXmppIceConnection::setLocalUser(const QString &user)
+{
+ m_localUser = user;
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setLocalUser(user);
+}
+
+/// Returns the local password.
+
+QString QXmppIceConnection::localPassword() const
+{
+ return m_localPassword;
+}
+
+/// Sets the local password.
+///
+/// You do not usually need to call this as one is automatically generated.
+///
+/// \param password
+
+void QXmppIceConnection::setLocalPassword(const QString &password)
+{
+ m_localPassword = password;
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setLocalPassword(password);
+}
+
+/// Sets the remote user fragment.
+///
+/// \param user
+
+void QXmppIceConnection::setRemoteUser(const QString &user)
+{
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setRemoteUser(user);
+}
+
+/// Sets the remote password.
+///
+/// \param password
+
+void QXmppIceConnection::setRemotePassword(const QString &password)
+{
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setRemotePassword(password);
+}
+
+/// Sets the STUN server to use to determine server-reflexive addresses
+/// and ports.
+///
+/// \param host The address of the STUN server.
+/// \param port The port of the STUN server.
+
+void QXmppIceConnection::setStunServer(const QHostAddress &host, quint16 port)
+{
+ m_stunHost = host;
+ m_stunPort = port;
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setStunServer(host, port);
+}
+
+/// Sets the TURN server to use to relay packets in double-NAT configurations.
+///
+/// \param host The address of the TURN server.
+/// \param port The port of the TURN server.
+
+void QXmppIceConnection::setTurnServer(const QHostAddress &host, quint16 port)
+{
+ m_turnHost = host;
+ m_turnPort = port;
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setTurnServer(host, port);
+}
+
+/// Sets the \a user used for authentication with the TURN server.
+///
+/// \param user
+
+void QXmppIceConnection::setTurnUser(const QString &user)
+{
+ m_turnUser = user;
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setTurnUser(user);
+}
+
+/// Sets the \a password used for authentication with the TURN server.
+///
+/// \param password
+
+void QXmppIceConnection::setTurnPassword(const QString &password)
+{
+ m_turnPassword = password;
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->setTurnPassword(password);
+}
+
+void QXmppIceConnection::slotConnected()
+{
+ foreach (QXmppIceComponent *socket, m_components.values())
+ if (!socket->isConnected())
+ return;
+ info(QString("ICE negotiation completed"));
+ m_connectTimer->stop();
+ emit connected();
+}
+
+void QXmppIceConnection::slotTimeout()
+{
+ warning(QString("ICE negotiation timed out"));
+ foreach (QXmppIceComponent *socket, m_components.values())
+ socket->close();
+ emit disconnected();
+}
+
diff --git a/src/base/QXmppStun.h b/src/base/QXmppStun.h
new file mode 100644
index 00000000..1792d3cb
--- /dev/null
+++ b/src/base/QXmppStun.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPSTUN_H
+#define QXMPPSTUN_H
+
+#include <QObject>
+
+#include "QXmppLogger.h"
+#include "QXmppJingleIq.h"
+
+class QDataStream;
+class QUdpSocket;
+class QTimer;
+
+/// \internal
+///
+/// The QXmppStunMessage class represents a STUN message.
+///
+
+class QXmppStunMessage
+{
+public:
+ enum MethodType {
+ Binding = 0x1,
+ SharedSecret = 0x2,
+ Allocate = 0x3,
+ Refresh = 0x4,
+ Send = 0x6,
+ Data = 0x7,
+ CreatePermission = 0x8,
+ ChannelBind = 0x9,
+ };
+
+ enum ClassType {
+ Request = 0x000,
+ Indication = 0x010,
+ Response = 0x100,
+ Error = 0x110,
+ };
+
+ QXmppStunMessage();
+
+ quint32 cookie() const;
+ void setCookie(quint32 cookie);
+
+ QByteArray id() const;
+ void setId(const QByteArray &id);
+
+ quint16 messageClass() const;
+ quint16 messageMethod() const;
+
+ quint16 type() const;
+ void setType(quint16 type);
+
+ // attributes
+
+ quint32 changeRequest() const;
+ void setChangeRequest(quint32 changeRequest);
+
+ quint16 channelNumber() const;
+ void setChannelNumber(quint16 channelNumber);
+
+ QByteArray data() const;
+ void setData(const QByteArray &data);
+
+ quint32 lifetime() const;
+ void setLifetime(quint32 changeRequest);
+
+ QByteArray nonce() const;
+ void setNonce(const QByteArray &nonce);
+
+ quint32 priority() const;
+ void setPriority(quint32 priority);
+
+ QString realm() const;
+ void setRealm(const QString &realm);
+
+ QByteArray reservationToken() const;
+ void setReservationToken(const QByteArray &reservationToken);
+
+ quint8 requestedTransport() const;
+ void setRequestedTransport(quint8 requestedTransport);
+
+ QString software() const;
+ void setSoftware(const QString &software);
+
+ QString username() const;
+ void setUsername(const QString &username);
+
+ QByteArray encode(const QByteArray &key = QByteArray(), bool addFingerprint = true) const;
+ bool decode(const QByteArray &buffer, const QByteArray &key = QByteArray(), QStringList *errors = 0);
+ QString toString() const;
+ static quint16 peekType(const QByteArray &buffer, quint32 &cookie, QByteArray &id);
+
+ // attributes
+ int errorCode;
+ QString errorPhrase;
+ QByteArray iceControlling;
+ QByteArray iceControlled;
+ QHostAddress changedHost;
+ quint16 changedPort;
+ QHostAddress mappedHost;
+ quint16 mappedPort;
+ QHostAddress otherHost;
+ quint16 otherPort;
+ QHostAddress sourceHost;
+ quint16 sourcePort;
+ QHostAddress xorMappedHost;
+ quint16 xorMappedPort;
+ QHostAddress xorPeerHost;
+ quint16 xorPeerPort;
+ QHostAddress xorRelayedHost;
+ quint16 xorRelayedPort;
+ bool useCandidate;
+
+private:
+ quint32 m_cookie;
+ QByteArray m_id;
+ quint16 m_type;
+
+ // attributes
+ QSet<quint16> m_attributes;
+ quint32 m_changeRequest;
+ quint16 m_channelNumber;
+ QByteArray m_data;
+ quint32 m_lifetime;
+ QByteArray m_nonce;
+ quint32 m_priority;
+ QString m_realm;
+ quint8 m_requestedTransport;
+ QByteArray m_reservationToken;
+ QString m_software;
+ QString m_username;
+};
+
+/// \internal
+///
+/// The QXmppStunTransaction class represents a STUN transaction.
+///
+
+class QXmppStunTransaction : public QXmppLoggable
+{
+ Q_OBJECT
+
+public:
+ QXmppStunTransaction(const QXmppStunMessage &request, QObject *parent);
+ QXmppStunMessage request() const;
+ QXmppStunMessage response() const;
+
+signals:
+ void finished();
+ void writeStun(const QXmppStunMessage &request);
+
+public slots:
+ void readStun(const QXmppStunMessage &response);
+
+private slots:
+ void retry();
+
+private:
+ QXmppStunMessage m_request;
+ QXmppStunMessage m_response;
+ QTimer *m_retryTimer;
+ int m_tries;
+};
+
+/// \internal
+///
+/// The QXmppTurnAllocation class represents a TURN allocation as defined
+/// by RFC 5766 Traversal Using Relays around NAT (TURN).
+///
+
+class QXmppTurnAllocation : public QXmppLoggable
+{
+ Q_OBJECT
+
+public:
+ enum AllocationState
+ {
+ UnconnectedState,
+ ConnectingState,
+ ConnectedState,
+ ClosingState,
+ };
+
+ QXmppTurnAllocation(QObject *parent = 0);
+ ~QXmppTurnAllocation();
+
+ QHostAddress relayedHost() const;
+ quint16 relayedPort() const;
+ AllocationState state() const;
+
+ void setServer(const QHostAddress &host, quint16 port = 3478);
+ void setUser(const QString &user);
+ void setPassword(const QString &password);
+
+ qint64 writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port);
+
+signals:
+ /// \brief This signal is emitted once TURN allocation succeeds.
+ void connected();
+
+ /// \brief This signal is emitted when a data packet is received.
+ void datagramReceived(const QByteArray &data, const QHostAddress &host, quint16 port);
+
+ /// \brief This signal is emitted when TURN allocation fails.
+ void disconnected();
+
+public slots:
+ void connectToHost();
+ void disconnectFromHost();
+
+private slots:
+ void readyRead();
+ void refresh();
+ void refreshChannels();
+ void transactionFinished();
+ void writeStun(const QXmppStunMessage &message);
+
+private:
+ void handleDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port);
+ void setState(AllocationState state);
+
+ QUdpSocket *socket;
+ QTimer *m_timer;
+ QTimer *m_channelTimer;
+ QString m_password;
+ QString m_username;
+ QHostAddress m_relayedHost;
+ quint16 m_relayedPort;
+ QHostAddress m_turnHost;
+ quint16 m_turnPort;
+
+ // channels
+ typedef QPair<QHostAddress, quint16> Address;
+ quint16 m_channelNumber;
+ QMap<quint16, Address> m_channels;
+
+ // state
+ quint32 m_lifetime;
+ QByteArray m_key;
+ QString m_realm;
+ QByteArray m_nonce;
+ AllocationState m_state;
+ QList<QXmppStunTransaction*> m_transactions;
+};
+
+/// \brief The QXmppIceComponent class represents a piece of a media stream
+/// requiring a single transport address, as defined by RFC 5245
+/// (Interactive Connectivity Establishment).
+
+class QXmppIceComponent : public QXmppLoggable
+{
+ Q_OBJECT
+
+public:
+ QXmppIceComponent(QObject *parent=0);
+ ~QXmppIceComponent();
+ void setIceControlling(bool controlling);
+ void setStunServer(const QHostAddress &host, quint16 port);
+ void setTurnServer(const QHostAddress &host, quint16 port);
+ void setTurnUser(const QString &user);
+ void setTurnPassword(const QString &password);
+
+ QList<QXmppJingleCandidate> localCandidates() const;
+ void setLocalUser(const QString &user);
+ void setLocalPassword(const QString &password);
+
+ int component() const;
+ void setComponent(int component);
+
+ bool addRemoteCandidate(const QXmppJingleCandidate &candidate);
+ void setRemoteUser(const QString &user);
+ void setRemotePassword(const QString &password);
+
+ bool isConnected() const;
+ void setSockets(QList<QUdpSocket*> sockets);
+
+ static QList<QHostAddress> discoverAddresses();
+ static QList<QUdpSocket*> reservePorts(const QList<QHostAddress> &addresses, int count, QObject *parent = 0);
+
+public slots:
+ void close();
+ void connectToHost();
+ qint64 sendDatagram(const QByteArray &datagram);
+
+private slots:
+ void checkCandidates();
+ void checkStun();
+ void handleDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port, QUdpSocket *socket = 0);
+ void readyRead();
+ void turnConnected();
+
+signals:
+ /// \brief This signal is emitted once ICE negotiation succeeds.
+ void connected();
+
+ /// \brief This signal is emitted when a data packet is received.
+ void datagramReceived(const QByteArray &datagram);
+
+ /// \brief This signal is emitted when the list of local candidates changes.
+ void localCandidatesChanged();
+
+private:
+ class Pair {
+ public:
+ Pair(int component, bool controlling);
+ quint64 priority() const;
+ QString toString() const;
+
+ QIODevice::OpenMode checked;
+ QXmppJingleCandidate remote;
+ QXmppJingleCandidate reflexive;
+ QByteArray transaction;
+ QUdpSocket *socket;
+
+ private:
+ int m_component;
+ bool m_controlling;
+ };
+
+ Pair *addRemoteCandidate(QUdpSocket *socket, const QHostAddress &host, quint16 port, quint32 priority);
+ qint64 writeStun(const QXmppStunMessage &message, QXmppIceComponent::Pair *pair);
+
+ int m_component;
+
+ QList<QXmppJingleCandidate> m_localCandidates;
+ QString m_localUser;
+ QString m_localPassword;
+
+ Pair *m_activePair;
+ Pair *m_fallbackPair;
+ bool m_iceControlling;
+ QList<Pair*> m_pairs;
+ quint32 m_peerReflexivePriority;
+ QString m_remoteUser;
+ QString m_remotePassword;
+
+ QList<QUdpSocket*> m_sockets;
+ QTimer *m_timer;
+
+ // STUN server
+ QByteArray m_stunId;
+ QHostAddress m_stunHost;
+ quint16 m_stunPort;
+ QTimer *m_stunTimer;
+ int m_stunTries;
+
+ // TURN server
+ QXmppTurnAllocation *m_turnAllocation;
+ bool m_turnConfigured;
+};
+
+/// \brief The QXmppIceConnection class represents a set of UDP sockets
+/// capable of performing Interactive Connectivity Establishment (RFC 5245).
+///
+
+class QXmppIceConnection : public QXmppLoggable
+{
+ Q_OBJECT
+
+public:
+ QXmppIceConnection(QObject *parent = 0);
+
+ QXmppIceComponent *component(int component);
+ void addComponent(int component);
+ void setIceControlling(bool controlling);
+
+ QList<QXmppJingleCandidate> localCandidates() const;
+ QString localUser() const;
+ void setLocalUser(const QString &user);
+ QString localPassword() const;
+ void setLocalPassword(const QString &password);
+
+ void addRemoteCandidate(const QXmppJingleCandidate &candidate);
+ void setRemoteUser(const QString &user);
+ void setRemotePassword(const QString &password);
+
+ void setStunServer(const QHostAddress &host, quint16 port = 3478);
+ void setTurnServer(const QHostAddress &host, quint16 port = 3478);
+ void setTurnUser(const QString &user);
+ void setTurnPassword(const QString &password);
+
+ bool bind(const QList<QHostAddress> &addresses);
+ bool isConnected() const;
+
+signals:
+ /// \brief This signal is emitted once ICE negotiation succeeds.
+ void connected();
+
+ /// \brief This signal is emitted when ICE negotiation fails.
+ void disconnected();
+
+ /// \brief This signal is emitted when the list of local candidates changes.
+ void localCandidatesChanged();
+
+public slots:
+ void close();
+ void connectToHost();
+
+private slots:
+ void slotConnected();
+ void slotTimeout();
+
+private:
+ QTimer *m_connectTimer;
+ bool m_iceControlling;
+ QMap<int, QXmppIceComponent*> m_components;
+ QString m_localUser;
+ QString m_localPassword;
+ QHostAddress m_stunHost;
+ quint16 m_stunPort;
+ QHostAddress m_turnHost;
+ quint16 m_turnPort;
+ QString m_turnUser;
+ QString m_turnPassword;
+};
+
+#endif
diff --git a/src/base/QXmppUtils.cpp b/src/base/QXmppUtils.cpp
new file mode 100644
index 00000000..1698582f
--- /dev/null
+++ b/src/base/QXmppUtils.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/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 <QBuffer>
+#include <QByteArray>
+#include <QCryptographicHash>
+#include <QDateTime>
+#include <QDebug>
+#include <QDomElement>
+#include <QRegExp>
+#include <QString>
+#include <QStringList>
+#include <QXmlStreamWriter>
+
+#include "QXmppUtils.h"
+#include "QXmppLogger.h"
+
+// adapted from public domain source by Ross Williams and Eric Durbin
+// FIXME : is this valid for big-endian machines?
+static quint32 crctable[256] =
+{
+ 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
+ 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
+ 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
+ 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
+ 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
+ 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
+ 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
+ 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
+ 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
+ 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
+ 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
+ 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
+ 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
+ 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
+ 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
+ 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
+ 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
+ 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
+ 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
+ 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
+ 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
+ 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
+ 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
+ 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
+ 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
+ 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
+ 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
+ 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
+ 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
+ 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
+ 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
+ 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
+ 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
+ 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
+ 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
+ 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
+ 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
+ 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
+ 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
+ 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
+ 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
+ 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
+ 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
+ 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
+ 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
+ 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
+ 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
+ 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
+ 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
+ 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
+ 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
+ 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
+ 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
+ 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
+ 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
+ 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
+ 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
+ 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
+ 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
+ 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
+ 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
+ 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
+ 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
+ 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
+};
+
+QDateTime datetimeFromString(const QString &str)
+{
+ QRegExp tzRe("(Z|([+-])([0-9]{2}):([0-9]{2}))");
+ int tzPos = tzRe.indexIn(str, 19);
+ if (str.size() < 20 || tzPos < 0)
+ return QDateTime();
+
+ // process date and time
+ QDateTime dt = QDateTime::fromString(str.left(19), "yyyy-MM-ddThh:mm:ss");
+ dt.setTimeSpec(Qt::UTC);
+
+ // process milliseconds
+ if (tzPos > 20 && str.at(19) == '.')
+ {
+ QString millis = (str.mid(20, tzPos - 20) + "000").left(3);
+ dt = dt.addMSecs(millis.toInt());
+ }
+
+ // process time zone
+ if (tzRe.cap(1) != "Z")
+ {
+ int offset = tzRe.cap(3).toInt() * 3600 + tzRe.cap(4).toInt() * 60;
+ if (tzRe.cap(2) == "+")
+ dt = dt.addSecs(-offset);
+ else
+ dt = dt.addSecs(offset);
+ }
+ return dt;
+}
+
+QString datetimeToString(const QDateTime &dt)
+{
+ QDateTime utc = dt.toUTC();
+ if (utc.time().msec())
+ return utc.toString("yyyy-MM-ddThh:mm:ss.zzzZ");
+ else
+ return utc.toString("yyyy-MM-ddThh:mm:ssZ");
+}
+
+/// Parses a timezone offset (in seconds) from a string.
+///
+/// \param str
+///
+
+int timezoneOffsetFromString(const QString &str)
+{
+ QRegExp tzRe("(Z|([+-])([0-9]{2}):([0-9]{2}))");
+ if (!tzRe.exactMatch(str))
+ return 0;
+
+ // No offset from UTC
+ if (tzRe.cap(1) == "Z")
+ return 0;
+
+ // Calculate offset
+ const int offset = tzRe.cap(3).toInt() * 3600 +
+ tzRe.cap(4).toInt() * 60;
+ if (tzRe.cap(2) == "-")
+ return -offset;
+ else
+ return offset;
+}
+
+/// Serializes a timezone offset (in seconds) to a string.
+///
+/// \param secs
+
+QString timezoneOffsetToString(int secs)
+{
+ if (!secs)
+ return QString::fromLatin1("Z");
+
+ const QTime tzoTime = QTime(0, 0, 0).addSecs(qAbs(secs));
+ return (secs < 0 ? "-" : "+") + tzoTime.toString("hh:mm");
+}
+
+QString jidToDomain(const QString &jid)
+{
+ return jidToBareJid(jid).split("@").last();
+}
+
+QString jidToResource(const QString& jid)
+{
+ const int pos = jid.indexOf(QChar('/'));
+ if (pos < 0)
+ return QString();
+ return jid.mid(pos+1);
+}
+
+QString jidToUser(const QString &jid)
+{
+ const int pos = jid.indexOf(QChar('@'));
+ if (pos < 0)
+ return QString();
+ return jid.left(pos);
+}
+
+QString jidToBareJid(const QString& jid)
+{
+ const int pos = jid.indexOf(QChar('/'));
+ if (pos < 0)
+ return jid;
+ return jid.left(pos);
+}
+
+quint32 generateCrc32(const QByteArray &in)
+{
+ quint32 result = 0xffffffff;
+ for(int n = 0; n < in.size(); ++n)
+ result = (result >> 8) ^ (crctable[(result & 0xff) ^ (quint8)in[n]]);
+ return result ^= 0xffffffff;
+}
+
+static QByteArray generateHmac(QCryptographicHash::Algorithm algorithm, const QByteArray &key, const QByteArray &text)
+{
+ QCryptographicHash hasher(algorithm);
+
+ const int B = 64;
+ QByteArray kpad = key + QByteArray(B - key.size(), 0);
+
+ QByteArray ba;
+ for (int i = 0; i < B; ++i)
+ ba += kpad[i] ^ 0x5c;
+
+ QByteArray tmp;
+ for (int i = 0; i < B; ++i)
+ tmp += kpad[i] ^ 0x36;
+ hasher.addData(tmp);
+ hasher.addData(text);
+ ba += hasher.result();
+
+ hasher.reset();
+ hasher.addData(ba);
+ return hasher.result();
+}
+
+QByteArray generateHmacMd5(const QByteArray &key, const QByteArray &text)
+{
+ return generateHmac(QCryptographicHash::Md5, key, text);
+}
+
+QByteArray generateHmacSha1(const QByteArray &key, const QByteArray &text)
+{
+ return generateHmac(QCryptographicHash::Sha1, key, text);
+}
+
+/// Generates a random integer x between 0 and N-1.
+///
+/// \param N
+
+int generateRandomInteger(int N)
+{
+ Q_ASSERT(N > 0 && N <= RAND_MAX);
+ int val;
+ while (N <= (val = qrand() / (RAND_MAX/N))) {};
+ return val;
+}
+
+/// Returns a random byte array of the specified size.
+///
+/// \param length
+
+QByteArray generateRandomBytes(int length)
+{
+ QByteArray bytes(length, 'm');
+ for (int i = 0; i < length; ++i)
+ bytes[i] = (char)generateRandomInteger(256);
+ return bytes;
+}
+
+/// Returns a random alphanumerical string of the specified size.
+///
+/// \param length
+
+QString generateStanzaHash(int length)
+{
+ const QString somechars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ const int N = somechars.size();
+ QString hashResult;
+ for ( int idx = 0; idx < length; ++idx )
+ hashResult += somechars[generateRandomInteger(N)];
+ return hashResult;
+}
+
+void helperToXmlAddAttribute(QXmlStreamWriter* stream, const QString& name,
+ const QString& value)
+{
+ if(!value.isEmpty())
+ stream->writeAttribute(name,value);
+}
+
+void helperToXmlAddDomElement(QXmlStreamWriter* stream, const QDomElement& element, const QStringList &omitNamespaces)
+{
+ stream->writeStartElement(element.tagName());
+
+ /* attributes */
+ QString xmlns = element.namespaceURI();
+ if (!xmlns.isEmpty() && !omitNamespaces.contains(xmlns))
+ stream->writeAttribute("xmlns", xmlns);
+ QDomNamedNodeMap attrs = element.attributes();
+ for (int i = 0; i < attrs.size(); i++)
+ {
+ QDomAttr attr = attrs.item(i).toAttr();
+ stream->writeAttribute(attr.name(), attr.value());
+ }
+
+ /* children */
+ QDomNode childNode = element.firstChild();
+ while (!childNode.isNull())
+ {
+ if (childNode.isElement())
+ {
+ helperToXmlAddDomElement(stream, childNode.toElement(), QStringList() << xmlns);
+ } else if (childNode.isText()) {
+ stream->writeCharacters(childNode.toText().data());
+ }
+ childNode = childNode.nextSibling();
+ }
+ stream->writeEndElement();
+}
+
+void helperToXmlAddTextElement(QXmlStreamWriter* stream, const QString& name,
+ const QString& value)
+{
+ if(!value.isEmpty())
+ stream->writeTextElement( name, value);
+ else
+ stream->writeEmptyElement(name);
+}
+
diff --git a/src/base/QXmppUtils.h b/src/base/QXmppUtils.h
new file mode 100644
index 00000000..80d84240
--- /dev/null
+++ b/src/base/QXmppUtils.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Authors:
+ * Manjeet Dahiya
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPUTILS_H
+#define QXMPPUTILS_H
+
+// forward declarations of QXmlStream* classes will not work on Mac, we need to
+// include the whole header.
+// See http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html
+// for an explanation.
+#include <QXmlStreamWriter>
+
+class QByteArray;
+class QDateTime;
+class QDomElement;
+class QString;
+class QStringList;
+
+// XEP-0082: XMPP Date and Time Profiles
+QDateTime datetimeFromString(const QString &str);
+QString datetimeToString(const QDateTime &dt);
+int timezoneOffsetFromString(const QString &str);
+QString timezoneOffsetToString(int secs);
+
+QString jidToDomain(const QString& jid);
+QString jidToResource(const QString& jid);
+QString jidToUser(const QString& jid);
+QString jidToBareJid(const QString& jid);
+
+quint32 generateCrc32(const QByteArray &input);
+QByteArray generateHmacMd5(const QByteArray &key, const QByteArray &text);
+QByteArray generateHmacSha1(const QByteArray &key, const QByteArray &text);
+int generateRandomInteger(int N);
+QByteArray generateRandomBytes(int length);
+QString generateStanzaHash(int length=32);
+
+void helperToXmlAddAttribute(QXmlStreamWriter* stream, const QString& name,
+ const QString& value);
+void helperToXmlAddDomElement(QXmlStreamWriter* stream,
+ const QDomElement& element, const QStringList &omitNamespaces);
+void helperToXmlAddTextElement(QXmlStreamWriter* stream, const QString& name,
+ const QString& value);
+
+#endif // QXMPPUTILS_H
diff --git a/src/base/QXmppVCardIq.cpp b/src/base/QXmppVCardIq.cpp
new file mode 100644
index 00000000..aaaf4554
--- /dev/null
+++ b/src/base/QXmppVCardIq.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/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 <QBuffer>
+#include <QXmlStreamWriter>
+
+#include "QXmppVCardIq.h"
+#include "QXmppUtils.h"
+#include "QXmppConstants.h"
+
+QString getImageType(const QByteArray &contents)
+{
+ if (contents.startsWith("\x89PNG\x0d\x0a\x1a\x0a"))
+ return "image/png";
+ else if (contents.startsWith("\x8aMNG"))
+ return "video/x-mng";
+ else if (contents.startsWith("GIF8"))
+ return "image/gif";
+ else if (contents.startsWith("BM"))
+ return "image/bmp";
+ else if (contents.contains("/* XPM */"))
+ return "image/x-xpm";
+ else if (contents.contains("<?xml") && contents.contains("<svg"))
+ return "image/svg+xml";
+ else if (contents.startsWith("\xFF\xD8\xFF\xE0"))
+ return "image/jpeg";
+ return "image/unknown";
+}
+
+/// Constructs a QXmppVCardIq for the specified recipient.
+///
+/// \param jid
+
+QXmppVCardIq::QXmppVCardIq(const QString& jid) : QXmppIq(QXmppIq::Get)
+{
+ // for self jid should be empty
+ setTo(jid);
+}
+
+/// Returns the date of birth of the individual associated with the vCard.
+///
+
+QDate QXmppVCardIq::birthday() const
+{
+ return m_birthday;
+}
+
+/// Sets the date of birth of the individual associated with the vCard.
+///
+/// \param birthday
+
+void QXmppVCardIq::setBirthday(const QDate &birthday)
+{
+ m_birthday = birthday;
+}
+
+/// Returns the email address.
+///
+
+QString QXmppVCardIq::email() const
+{
+ return m_email;
+}
+
+/// Sets the email address.
+///
+/// \param email
+
+void QXmppVCardIq::setEmail(const QString &email)
+{
+ m_email = email;
+}
+
+/// Returns the first name.
+///
+
+QString QXmppVCardIq::firstName() const
+{
+ return m_firstName;
+}
+
+/// Sets the first name.
+///
+/// \param firstName
+
+void QXmppVCardIq::setFirstName(const QString &firstName)
+{
+ m_firstName = firstName;
+}
+
+/// Returns the full name.
+///
+
+QString QXmppVCardIq::fullName() const
+{
+ return m_fullName;
+}
+
+/// Sets the full name.
+///
+/// \param fullName
+
+void QXmppVCardIq::setFullName(const QString &fullName)
+{
+ m_fullName = fullName;
+}
+
+/// Returns the last name.
+///
+
+QString QXmppVCardIq::lastName() const
+{
+ return m_lastName;
+}
+
+/// Sets the last name.
+///
+/// \param lastName
+
+void QXmppVCardIq::setLastName(const QString &lastName)
+{
+ m_lastName = lastName;
+}
+
+/// Returns the middle name.
+///
+
+QString QXmppVCardIq::middleName() const
+{
+ return m_middleName;
+}
+
+/// Sets the middle name.
+///
+/// \param middleName
+
+void QXmppVCardIq::setMiddleName(const QString &middleName)
+{
+ m_middleName = middleName;
+}
+
+/// Returns the nickname.
+///
+
+QString QXmppVCardIq::nickName() const
+{
+ return m_nickName;
+}
+
+/// Sets the nickname.
+///
+/// \param nickName
+
+void QXmppVCardIq::setNickName(const QString &nickName)
+{
+ m_nickName = nickName;
+}
+
+/// Returns the URL associated with the vCard. It can represent the user's
+/// homepage or a location at which you can find real-time information about
+/// the vCard.
+
+QString QXmppVCardIq::url() const
+{
+ return m_url;
+}
+
+/// Sets the URL associated with the vCard. It can represent the user's
+/// homepage or a location at which you can find real-time information about
+/// the vCard.
+///
+/// \param url
+
+void QXmppVCardIq::setUrl(const QString& url)
+{
+ m_url = url;
+}
+
+/// Returns the photo's binary contents.
+///
+/// If you want to use the photo as a QImage you can use:
+///
+/// \code
+/// QBuffer buffer;
+/// buffer.setData(myCard.photo());
+/// buffer.open(QIODevice::ReadOnly);
+/// QImageReader imageReader(&buffer);
+/// QImage myImage = imageReader.read();
+/// \endcode
+
+QByteArray QXmppVCardIq::photo() const
+{
+ return m_photo;
+}
+
+/// Sets the photo's binary contents.
+
+void QXmppVCardIq::setPhoto(const QByteArray& photo)
+{
+ m_photo = photo;
+}
+
+/// Returns the photo's MIME type.
+
+QString QXmppVCardIq::photoType() const
+{
+ return m_photoType;
+}
+
+/// Sets the photo's MIME type.
+
+void QXmppVCardIq::setPhotoType(const QString& photoType)
+{
+ m_photoType = photoType;
+}
+
+bool QXmppVCardIq::isVCard(const QDomElement &nodeRecv)
+{
+ return nodeRecv.firstChildElement("vCard").namespaceURI() == ns_vcard;
+}
+
+void QXmppVCardIq::parseElementFromChild(const QDomElement& nodeRecv)
+{
+ // vCard
+ QDomElement cardElement = nodeRecv.firstChildElement("vCard");
+ m_birthday = QDate::fromString(cardElement.firstChildElement("BDAY").text(), "yyyy-MM-dd");
+ QDomElement emailElement = cardElement.firstChildElement("EMAIL");
+ m_email = emailElement.firstChildElement("USERID").text();
+ m_fullName = cardElement.firstChildElement("FN").text();
+ m_nickName = cardElement.firstChildElement("NICKNAME").text();
+ QDomElement nameElement = cardElement.firstChildElement("N");
+ m_firstName = nameElement.firstChildElement("GIVEN").text();
+ m_lastName = nameElement.firstChildElement("FAMILY").text();
+ m_middleName = nameElement.firstChildElement("MIDDLE").text();
+ m_url = cardElement.firstChildElement("URL").text();
+ QDomElement photoElement = cardElement.firstChildElement("PHOTO");
+ QByteArray base64data = photoElement.
+ firstChildElement("BINVAL").text().toAscii();
+ m_photo = QByteArray::fromBase64(base64data);
+ m_photoType = photoElement.firstChildElement("TYPE").text();
+}
+
+void QXmppVCardIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("vCard");
+ writer->writeAttribute("xmlns", ns_vcard);
+ if (m_birthday.isValid())
+ helperToXmlAddTextElement(writer, "BDAY", m_birthday.toString("yyyy-MM-dd"));
+ if (!m_email.isEmpty())
+ {
+ writer->writeStartElement("EMAIL");
+ writer->writeEmptyElement("INTERNET");
+ helperToXmlAddTextElement(writer, "USERID", m_email);
+ writer->writeEndElement();
+ }
+ if (!m_fullName.isEmpty())
+ helperToXmlAddTextElement(writer, "FN", m_fullName);
+ if(!m_nickName.isEmpty())
+ helperToXmlAddTextElement(writer, "NICKNAME", m_nickName);
+ if (!m_firstName.isEmpty() ||
+ !m_lastName.isEmpty() ||
+ !m_middleName.isEmpty())
+ {
+ writer->writeStartElement("N");
+ if (!m_firstName.isEmpty())
+ helperToXmlAddTextElement(writer, "GIVEN", m_firstName);
+ if (!m_lastName.isEmpty())
+ helperToXmlAddTextElement(writer, "FAMILY", m_lastName);
+ if (!m_middleName.isEmpty())
+ helperToXmlAddTextElement(writer, "MIDDLE", m_middleName);
+ writer->writeEndElement();
+ }
+ if (!m_url.isEmpty())
+ helperToXmlAddTextElement(writer, "URL", m_url);
+
+ if(!photo().isEmpty())
+ {
+ writer->writeStartElement("PHOTO");
+ QString photoType = m_photoType;
+ if (photoType.isEmpty())
+ photoType = getImageType(m_photo);
+ helperToXmlAddTextElement(writer, "TYPE", photoType);
+ helperToXmlAddTextElement(writer, "BINVAL", m_photo.toBase64());
+ writer->writeEndElement();
+ }
+
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppVCardIq.h b/src/base/QXmppVCardIq.h
new file mode 100644
index 00000000..348c00fc
--- /dev/null
+++ b/src/base/QXmppVCardIq.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Manjeet Dahiya
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+
+#ifndef QXMPPVCARDIQ_H
+#define QXMPPVCARDIQ_H
+
+#include "QXmppIq.h"
+#include <QDate>
+#include <QMap>
+#include <QDomElement>
+
+class QImage;
+
+/// \brief Represents the XMPP vCard.
+///
+/// The functions names are self explanatory.
+/// Look at QXmppVCardManager and XEP-0054: vcard-temp for more details.
+///
+/// There are many field of XMPP vCard which are not present in
+/// this class. File a issue for the same. We will add the requested
+/// field to this class.
+///
+
+class QXmppVCardIq : public QXmppIq
+{
+public:
+ QXmppVCardIq(const QString& bareJid = "");
+
+ QDate birthday() const;
+ void setBirthday(const QDate &birthday);
+
+ QString email() const;
+ void setEmail(const QString&);
+
+ QString firstName() const;
+ void setFirstName(const QString&);
+
+ QString fullName() const;
+ void setFullName(const QString&);
+
+ QString lastName() const;
+ void setLastName(const QString&);
+
+ QString middleName() const;
+ void setMiddleName(const QString&);
+
+ QString nickName() const;
+ void setNickName(const QString&);
+
+ QByteArray photo() const;
+ void setPhoto(const QByteArray&);
+
+ QString photoType() const;
+ void setPhotoType(const QString &type);
+
+ QString url() const;
+ void setUrl(const QString&);
+
+ /// \cond
+ static bool isVCard(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement&);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QDate m_birthday;
+ QString m_email;
+ QString m_firstName;
+ QString m_fullName;
+ QString m_lastName;
+ QString m_middleName;
+ QString m_nickName;
+ QString m_url;
+
+ // not as 64 base
+ QByteArray m_photo;
+ QString m_photoType;
+};
+
+#endif // QXMPPVCARDIQ_H
diff --git a/src/base/QXmppVersionIq.cpp b/src/base/QXmppVersionIq.cpp
new file mode 100644
index 00000000..358596b6
--- /dev/null
+++ b/src/base/QXmppVersionIq.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppUtils.h"
+#include "QXmppVersionIq.h"
+
+/// Returns the name of the software.
+///
+
+QString QXmppVersionIq::name() const
+{
+ return m_name;
+}
+
+/// Sets the name of the software.
+///
+/// \param name
+
+void QXmppVersionIq::setName(const QString &name)
+{
+ m_name = name;
+}
+
+/// Returns the operating system.
+///
+
+QString QXmppVersionIq::os() const
+{
+ return m_os;
+}
+
+/// Sets the operating system.
+///
+/// \param os
+
+void QXmppVersionIq::setOs(const QString &os)
+{
+ m_os = os;
+}
+
+/// Returns the software version.
+///
+
+QString QXmppVersionIq::version() const
+{
+ return m_version;
+}
+
+/// Sets the software version.
+///
+/// \param version
+
+void QXmppVersionIq::setVersion(const QString &version)
+{
+ m_version = version;
+}
+
+bool QXmppVersionIq::isVersionIq(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ return queryElement.namespaceURI() == ns_version;
+}
+
+void QXmppVersionIq::parseElementFromChild(const QDomElement &element)
+{
+ QDomElement queryElement = element.firstChildElement("query");
+ m_name = queryElement.firstChildElement("name").text();
+ m_os = queryElement.firstChildElement("os").text();
+ m_version = queryElement.firstChildElement("version").text();
+}
+
+void QXmppVersionIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("query");
+ writer->writeAttribute("xmlns", ns_version);
+
+ if (!m_name.isEmpty())
+ helperToXmlAddTextElement(writer, "name", m_name);
+
+ if (!m_os.isEmpty())
+ helperToXmlAddTextElement(writer, "os", m_os);
+
+ if (!m_version.isEmpty())
+ helperToXmlAddTextElement(writer, "version", m_version);
+
+ writer->writeEndElement();
+}
+
diff --git a/src/base/QXmppVersionIq.h b/src/base/QXmppVersionIq.h
new file mode 100644
index 00000000..57a800e0
--- /dev/null
+++ b/src/base/QXmppVersionIq.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPVERSIONIQ_H
+#define QXMPPVERSIONIQ_H
+
+#include "QXmppIq.h"
+
+/// \brief The QXmppVersionIq class represents an IQ for conveying a software
+/// version as defined by XEP-0092: Software Version.
+///
+/// \ingroup Stanzas
+
+class QXmppVersionIq : public QXmppIq
+{
+public:
+ QString name() const;
+ void setName(const QString &name);
+
+ QString os() const;
+ void setOs(const QString &os);
+
+ QString version() const;
+ void setVersion(const QString &version);
+
+ /// \cond
+ static bool isVersionIq(const QDomElement &element);
+ /// \endcond
+
+protected:
+ /// \cond
+ void parseElementFromChild(const QDomElement &element);
+ void toXmlElementFromChild(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+private:
+ QString m_name;
+ QString m_os;
+ QString m_version;
+};
+
+#endif
diff --git a/src/base/qdnslookup.cpp b/src/base/qdnslookup.cpp
new file mode 100644
index 00000000..7b1e7c8f
--- /dev/null
+++ b/src/base/qdnslookup.cpp
@@ -0,0 +1,989 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup.h"
+#include "qdnslookup_p.h"
+
+#include <QCoreApplication>
+#include <QDateTime>
+#include <QMetaType>
+#include <QThreadStorage>
+#include <QUrl>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
+Q_GLOBAL_STATIC(QThreadStorage<bool *>, theDnsLookupSeedStorage);
+
+static bool qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord &r1, const QDnsMailExchangeRecord &r2)
+{
+ // Lower numbers are more preferred than higher ones.
+ return r1.preference() < r2.preference();
+}
+
+/*!
+ Sorts a list of QDnsMailExchangeRecord objects according to RFC 5321.
+*/
+
+static void qt_qdnsmailexchangerecord_sort(QList<QDnsMailExchangeRecord> &records)
+{
+ // If we have no more than one result, we are done.
+ if (records.size() <= 1)
+ return;
+
+ // Order the records by preference.
+ qSort(records.begin(), records.end(), qt_qdnsmailexchangerecord_less_than);
+
+ int i = 0;
+ while (i < records.size()) {
+
+ // Determine the slice of records with the current preference.
+ QList<QDnsMailExchangeRecord> slice;
+ const quint16 slicePreference = records[i].preference();
+ for (int j = i; j < records.size(); ++j) {
+ if (records[j].preference() != slicePreference)
+ break;
+ slice << records[j];
+ }
+
+ // Randomize the slice of records.
+ while (!slice.isEmpty()) {
+ const unsigned int pos = qrand() % slice.size();
+ records[i++] = slice.takeAt(pos);
+ }
+ }
+}
+
+static bool qt_qdnsservicerecord_less_than(const QDnsServiceRecord &r1, const QDnsServiceRecord &r2)
+{
+ // Order by priority, or if the priorities are equal,
+ // put zero weight records first.
+ return r1.priority() < r2.priority()
+ || (r1.priority() == r2.priority()
+ && r1.weight() == 0 && r2.weight() > 0);
+}
+
+/*!
+ Sorts a list of QDnsServiceRecord objects according to RFC 2782.
+*/
+
+static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
+{
+ // If we have no more than one result, we are done.
+ if (records.size() <= 1)
+ return;
+
+ // Order the records by priority, and for records with an equal
+ // priority, put records with a zero weight first.
+ qSort(records.begin(), records.end(), qt_qdnsservicerecord_less_than);
+
+ int i = 0;
+ while (i < records.size()) {
+
+ // Determine the slice of records with the current priority.
+ QList<QDnsServiceRecord> slice;
+ const quint16 slicePriority = records[i].priority();
+ unsigned int sliceWeight = 0;
+ for (int j = i; j < records.size(); ++j) {
+ if (records[j].priority() != slicePriority)
+ break;
+ sliceWeight += records[j].weight();
+ slice << records[j];
+ }
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("qt_qdnsservicerecord_sort() : priority %i (size: %i, total weight: %i)",
+ slicePriority, slice.size(), sliceWeight);
+#endif
+
+ // Order the slice of records.
+ while (!slice.isEmpty()) {
+ const unsigned int weightThreshold = qrand() % (sliceWeight + 1);
+ unsigned int summedWeight = 0;
+ for (int j = 0; j < slice.size(); ++j) {
+ summedWeight += slice[j].weight();
+ if (summedWeight >= weightThreshold) {
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("qt_qdnsservicerecord_sort() : adding %s %i (weight: %i)",
+ qPrintable(slice[j].target()), slice[j].port(),
+ slice[j].weight());
+#endif
+ // Adjust the slice weight and take the current record.
+ sliceWeight -= slice[j].weight();
+ records[i++] = slice.takeAt(j);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*!
+ \class QDnsLookup
+ \brief The QDnsLookup class represents a DNS lookup.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ QDnsLookup uses the mechanisms provided by the operating system to perform
+ DNS lookups. To perform a lookup you need to specify a \l name and \l type
+ then invoke the \l{QDnsLookup::lookup()}{lookup()} slot. The
+ \l{QDnsLookup::finished()}{finished()} signal will be emitted upon
+ completion.
+
+ For example, you can determine which servers an XMPP chat client should
+ connect to for a given domain with:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qdnslookup.cpp 0
+
+ Once the request finishes you can handle the results with:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qdnslookup.cpp 1
+
+ \note If you simply want to find the IP address(es) associated with a host
+ name, or the host name associated with an IP address you should use
+ QHostInfo instead.
+*/
+
+/*!
+ \enum QDnsLookup::Error
+
+ Indicates all possible error conditions found during the
+ processing of the DNS lookup.
+
+ \value NoError no error condition.
+
+ \value ResolverError there was an error initializing the system's
+ DNS resolver.
+
+ \value OperationCancelledError the lookup was aborted using the abort()
+ method.
+
+ \value InvalidRequestError the requested DNS lookup was invalid.
+
+ \value InvalidReplyError the reply returned by the server was invalid.
+
+ \value ServerFailureError the server encountered an internal failure
+ while processing the request (SERVFAIL).
+
+ \value ServerRefusedError the server refused to process the request for
+ security or policy reasons (REFUSED).
+
+ \value NotFoundError the requested domain name does not exist
+ (NXDOMAIN).
+*/
+
+/*!
+ \enum QDnsLookup::Type
+
+ Indicates the type of DNS lookup that was performed.
+
+ \value A IPv4 address records.
+
+ \value AAAA IPv6 address records.
+
+ \value ANY any records.
+
+ \value CNAME canonical name records.
+
+ \value MX mail exchange records.
+
+ \value NS name server records.
+
+ \value PTR pointer records.
+
+ \value SRV service records.
+
+ \value TXT text records.
+*/
+
+/*!
+ \fn void QDnsLookup::finished()
+
+ This signal is emitted when the reply has finished processing.
+*/
+
+/*!
+ \fn void QDnsLookup::nameChanged(const QString &name)
+
+ This signal is emitted when the lookup \l name changes.
+ \a name is the new lookup name.
+*/
+
+/*!
+ \fn void QDnsLookup::typeChanged(Type type)
+
+ This signal is emitted when the lookup \l type changes.
+ \a type is the new lookup type.
+*/
+
+/*!
+ Constructs a QDnsLookup object and sets \a parent as the parent object.
+
+ The \l type property will default to QDnsLookup::A.
+*/
+
+QDnsLookup::QDnsLookup(QObject *parent)
+ : QObject(parent)
+ , d_ptr(new QDnsLookupPrivate(this))
+{
+ qRegisterMetaType<QDnsLookupReply>();
+}
+/*!
+ Constructs a QDnsLookup object for the given \a type and \a name and sets
+ \a parent as the parent object.
+*/
+
+QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
+ : QObject(parent)
+ , d_ptr(new QDnsLookupPrivate(this))
+{
+ Q_D(QDnsLookup);
+ qRegisterMetaType<QDnsLookupReply>();
+ d->name = name;
+ d->type = type;
+}
+
+/*!
+ Destroys the QDnsLookup object.
+
+ It is safe to delete a QDnsLookup object even if it is not finished, you
+ will simply never receive its results.
+*/
+
+QDnsLookup::~QDnsLookup()
+{
+}
+
+/*!
+ \property QDnsLookup::error
+ \brief the type of error that occurred if the DNS lookup failed, or NoError.
+*/
+
+QDnsLookup::Error QDnsLookup::error() const
+{
+ return d_func()->reply.error;
+}
+
+/*!
+ \property QDnsLookup::errorString
+ \brief a human-readable description of the error if the DNS lookup failed.
+*/
+
+QString QDnsLookup::errorString() const
+{
+ return d_func()->reply.errorString;
+}
+
+/*!
+ \property QDnsLookup::finished
+ \brief whether the reply has finished or was aborted.
+*/
+
+bool QDnsLookup::isFinished() const
+{
+ return d_func()->isFinished;
+}
+
+/*!
+ \property QDnsLookup::name
+ \brief the name to lookup.
+
+ \note The name will be encoded using IDNA, which means it's unsuitable for
+ querying SRV records compatible with the DNS-SD specification.
+*/
+
+QString QDnsLookup::name() const
+{
+ return d_func()->name;
+}
+
+void QDnsLookup::setName(const QString &name)
+{
+ Q_D(QDnsLookup);
+ if (name != d->name) {
+ d->name = name;
+ emit nameChanged(name);
+ }
+}
+
+/*!
+ \property QDnsLookup::type
+ \brief the type of DNS lookup.
+*/
+
+QDnsLookup::Type QDnsLookup::type() const
+{
+ return d_func()->type;
+}
+
+void QDnsLookup::setType(Type type)
+{
+ Q_D(QDnsLookup);
+ if (type != d->type) {
+ d->type = type;
+ emit typeChanged(type);
+ }
+}
+
+/*!
+ Returns the list of canonical name records associated with this lookup.
+*/
+
+QList<QDnsDomainNameRecord> QDnsLookup::canonicalNameRecords() const
+{
+ return d_func()->reply.canonicalNameRecords;
+}
+
+/*!
+ Returns the list of host address records associated with this lookup.
+*/
+
+QList<QDnsHostAddressRecord> QDnsLookup::hostAddressRecords() const
+{
+ return d_func()->reply.hostAddressRecords;
+}
+
+/*!
+ Returns the list of mail exchange records associated with this lookup.
+
+ The records are sorted according to
+ \l{http://www.rfc-editor.org/rfc/rfc5321.txt}{RFC 5321}, so if you use them
+ to connect to servers, you should try them in the order they are listed.
+*/
+
+QList<QDnsMailExchangeRecord> QDnsLookup::mailExchangeRecords() const
+{
+ return d_func()->reply.mailExchangeRecords;
+}
+
+/*!
+ Returns the list of name server records associated with this lookup.
+*/
+
+QList<QDnsDomainNameRecord> QDnsLookup::nameServerRecords() const
+{
+ return d_func()->reply.nameServerRecords;
+}
+
+/*!
+ Returns the list of pointer records associated with this lookup.
+*/
+
+QList<QDnsDomainNameRecord> QDnsLookup::pointerRecords() const
+{
+ return d_func()->reply.pointerRecords;
+}
+
+/*!
+ Returns the list of service records associated with this lookup.
+
+ The records are sorted according to
+ \l{http://www.rfc-editor.org/rfc/rfc2782.txt}{RFC 2782}, so if you use them
+ to connect to servers, you should try them in the order they are listed.
+*/
+
+QList<QDnsServiceRecord> QDnsLookup::serviceRecords() const
+{
+ return d_func()->reply.serviceRecords;
+}
+
+/*!
+ Returns the list of text records associated with this lookup.
+*/
+
+QList<QDnsTextRecord> QDnsLookup::textRecords() const
+{
+ return d_func()->reply.textRecords;
+}
+
+/*!
+ Aborts the DNS lookup operation.
+
+ If the lookup is already finished, does nothing.
+*/
+
+void QDnsLookup::abort()
+{
+ Q_D(QDnsLookup);
+ if (d->runnable) {
+ d->runnable = 0;
+ d->reply = QDnsLookupReply();
+ d->reply.error = QDnsLookup::OperationCancelledError;
+ d->reply.errorString = tr("Operation cancelled");
+ d->isFinished = true;
+ emit finished();
+ }
+}
+
+/*!
+ Performs the DNS lookup.
+
+ The \l{QDnsLookup::finished()}{finished()} signal is emitted upon completion.
+*/
+
+void QDnsLookup::lookup()
+{
+ Q_D(QDnsLookup);
+ d->isFinished = false;
+ d->reply = QDnsLookupReply();
+ d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name));
+ connect(d->runnable, SIGNAL(finished(QDnsLookupReply)),
+ this, SLOT(_q_lookupFinished(QDnsLookupReply)),
+ Qt::BlockingQueuedConnection);
+ theDnsLookupThreadPool()->start(d->runnable);
+}
+
+/*!
+ \class QDnsDomainNameRecord
+ \brief The QDnsDomainNameRecord class stores information about a domain
+ name record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing a name server lookup, zero or more records will be returned.
+ Each record is represented by a QDnsDomainNameRecord instance.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty domain name record object.
+*/
+
+QDnsDomainNameRecord::QDnsDomainNameRecord()
+ : d(new QDnsDomainNameRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsDomainNameRecord::QDnsDomainNameRecord(const QDnsDomainNameRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a domain name record.
+*/
+
+QDnsDomainNameRecord::~QDnsDomainNameRecord()
+{
+}
+
+/*!
+ Returns the name for this record.
+*/
+
+QString QDnsDomainNameRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsDomainNameRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the value for this domain name record.
+*/
+
+QString QDnsDomainNameRecord::value() const
+{
+ return d->value;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsDomainNameRecord &QDnsDomainNameRecord::operator=(const QDnsDomainNameRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \class QDnsHostAddressRecord
+ \brief The QDnsHostAddressRecord class stores information about a host
+ address record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing an address lookup, zero or more records will be
+ returned. Each record is represented by a QDnsHostAddressRecord instance.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty host address record object.
+*/
+
+QDnsHostAddressRecord::QDnsHostAddressRecord()
+ : d(new QDnsHostAddressRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsHostAddressRecord::QDnsHostAddressRecord(const QDnsHostAddressRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a host address record.
+*/
+
+QDnsHostAddressRecord::~QDnsHostAddressRecord()
+{
+}
+
+/*!
+ Returns the name for this record.
+*/
+
+QString QDnsHostAddressRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsHostAddressRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the value for this host address record.
+*/
+
+QHostAddress QDnsHostAddressRecord::value() const
+{
+ return d->value;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsHostAddressRecord &QDnsHostAddressRecord::operator=(const QDnsHostAddressRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \class QDnsMailExchangeRecord
+ \brief The QDnsMailExchangeRecord class stores information about a DNS MX record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing a lookup on a service, zero or more records will be
+ returned. Each record is represented by a QDnsMailExchangeRecord instance.
+
+ The meaning of the fields is defined in
+ \l{http://www.rfc-editor.org/rfc/rfc1035.txt}{RFC 1035}.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty mail exchange record object.
+*/
+
+QDnsMailExchangeRecord::QDnsMailExchangeRecord()
+ : d(new QDnsMailExchangeRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsMailExchangeRecord::QDnsMailExchangeRecord(const QDnsMailExchangeRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a mail exchange record.
+*/
+
+QDnsMailExchangeRecord::~QDnsMailExchangeRecord()
+{
+}
+
+/*!
+ Returns the domain name of the mail exchange for this record.
+*/
+
+QString QDnsMailExchangeRecord::exchange() const
+{
+ return d->exchange;
+}
+
+/*!
+ Returns the name for this record.
+*/
+
+QString QDnsMailExchangeRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the preference for this record.
+*/
+
+quint16 QDnsMailExchangeRecord::preference() const
+{
+ return d->preference;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsMailExchangeRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsMailExchangeRecord &QDnsMailExchangeRecord::operator=(const QDnsMailExchangeRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \class QDnsServiceRecord
+ \brief The QDnsServiceRecord class stores information about a DNS SRV record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing a lookup on a service, zero or more records will be
+ returned. Each record is represented by a QDnsServiceRecord instance.
+
+ The meaning of the fields is defined in
+ \l{http://www.rfc-editor.org/rfc/rfc2782.txt}{RFC 2782}.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty service record object.
+*/
+
+QDnsServiceRecord::QDnsServiceRecord()
+ : d(new QDnsServiceRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsServiceRecord::QDnsServiceRecord(const QDnsServiceRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a service record.
+*/
+
+QDnsServiceRecord::~QDnsServiceRecord()
+{
+}
+
+/*!
+ Returns the name for this record.
+*/
+
+QString QDnsServiceRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the port on the target host for this service record.
+*/
+
+quint16 QDnsServiceRecord::port() const
+{
+ return d->port;
+}
+
+/*!
+ Returns the priority for this service record.
+
+ A client must attempt to contact the target host with the lowest-numbered
+ priority.
+*/
+
+quint16 QDnsServiceRecord::priority() const
+{
+ return d->priority;
+}
+
+/*!
+ Returns the domain name of the target host for this service record.
+*/
+
+QString QDnsServiceRecord::target() const
+{
+ return d->target;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsServiceRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the weight for this service record.
+
+ The weight field specifies a relative weight for entries with the same
+ priority. Entries with higher weights should be selected with a higher
+ probability.
+*/
+
+quint16 QDnsServiceRecord::weight() const
+{
+ return d->weight;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsServiceRecord &QDnsServiceRecord::operator=(const QDnsServiceRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \class QDnsTextRecord
+ \brief The QDnsTextRecord class stores information about a DNS TXT record.
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ When performing a text lookup, zero or more records will be
+ returned. Each record is represented by a QDnsTextRecord instance.
+
+ The meaning of the fields is defined in
+ \l{http://www.rfc-editor.org/rfc/rfc1035.txt}{RFC 1035}.
+
+ \sa QDnsLookup
+*/
+
+/*!
+ Constructs an empty text record object.
+*/
+
+QDnsTextRecord::QDnsTextRecord()
+ : d(new QDnsTextRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QDnsTextRecord::QDnsTextRecord(const QDnsTextRecord &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys a text record.
+*/
+
+QDnsTextRecord::~QDnsTextRecord()
+{
+}
+
+/*!
+ Returns the name for this text record.
+*/
+
+QString QDnsTextRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+
+quint32 QDnsTextRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the values for this text record.
+*/
+
+QList<QByteArray> QDnsTextRecord::values() const
+{
+ return d->values;
+}
+
+/*!
+ Assigns the data of the \a other object to this record object,
+ and returns a reference to it.
+*/
+
+QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other)
+{
+ d = other.d;
+ return *this;
+}
+
+void QDnsLookupPrivate::_q_lookupFinished(const QDnsLookupReply &_reply)
+{
+ Q_Q(QDnsLookup);
+ if (runnable == q->sender()) {
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("DNS reply for %s: %i (%s)", qPrintable(name), _reply.error, qPrintable(_reply.errorString));
+#endif
+ reply = _reply;
+ runnable = 0;
+ isFinished = true;
+ emit q->finished();
+ }
+}
+
+void QDnsLookupRunnable::run()
+{
+ QDnsLookupReply reply;
+
+ // Validate input.
+ if (requestName.isEmpty()) {
+ reply.error = QDnsLookup::InvalidRequestError;
+ reply.errorString = tr("Invalid domain name");
+ emit finished(reply);
+ return;
+ }
+
+ // Perform request.
+ query(requestType, requestName, &reply);
+
+ // Sort results.
+ if (!theDnsLookupSeedStorage()->hasLocalData()) {
+ qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(this));
+ theDnsLookupSeedStorage()->setLocalData(new bool(true));
+ }
+ qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
+ qt_qdnsservicerecord_sort(reply.serviceRecords);
+
+ emit finished(reply);
+}
+
+QDnsLookupThreadPool::QDnsLookupThreadPool()
+ : signalsConnected(false)
+{
+ // Run up to 5 lookups in parallel.
+ setMaxThreadCount(5);
+}
+
+void QDnsLookupThreadPool::start(QRunnable *runnable)
+{
+ // Ensure threads complete at application destruction.
+ if (!signalsConnected) {
+ QMutexLocker signalsLocker(&signalsMutex);
+ if (!signalsConnected) {
+ QCoreApplication *app = QCoreApplication::instance();
+ if (!app) {
+ qWarning("QDnsLookup requires a QCoreApplication");
+ delete runnable;
+ return;
+ }
+
+ moveToThread(app->thread());
+ connect(app, SIGNAL(destroyed()),
+ SLOT(_q_applicationDestroyed()), Qt::DirectConnection);
+ signalsConnected = true;
+ }
+ }
+
+ QThreadPool::start(runnable);
+}
+
+void QDnsLookupThreadPool::_q_applicationDestroyed()
+{
+ waitForDone();
+ signalsConnected = false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/base/qdnslookup.h b/src/base/qdnslookup.h
new file mode 100644
index 00000000..6f913896
--- /dev/null
+++ b/src/base/qdnslookup.h
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDNSLOOKUP_H
+#define QDNSLOOKUP_H
+
+#include <QList>
+#include <QObject>
+#include <QSharedData>
+#include <QSharedPointer>
+#include <QString>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QHostAddress;
+class QDnsLookupPrivate;
+class QDnsDomainNameRecordPrivate;
+class QDnsHostAddressRecordPrivate;
+class QDnsMailExchangeRecordPrivate;
+class QDnsServiceRecordPrivate;
+class QDnsTextRecordPrivate;
+
+class QDnsDomainNameRecord
+{
+public:
+ QDnsDomainNameRecord();
+ QDnsDomainNameRecord(const QDnsDomainNameRecord &other);
+ ~QDnsDomainNameRecord();
+
+ QString name() const;
+ quint32 timeToLive() const;
+ QString value() const;
+
+ QDnsDomainNameRecord &operator=(const QDnsDomainNameRecord &other);
+
+private:
+ QSharedDataPointer<QDnsDomainNameRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class QDnsHostAddressRecord
+{
+public:
+ QDnsHostAddressRecord();
+ QDnsHostAddressRecord(const QDnsHostAddressRecord &other);
+ ~QDnsHostAddressRecord();
+
+ QString name() const;
+ quint32 timeToLive() const;
+ QHostAddress value() const;
+
+ QDnsHostAddressRecord &operator=(const QDnsHostAddressRecord &other);
+
+private:
+ QSharedDataPointer<QDnsHostAddressRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class QDnsMailExchangeRecord
+{
+public:
+ QDnsMailExchangeRecord();
+ QDnsMailExchangeRecord(const QDnsMailExchangeRecord &other);
+ ~QDnsMailExchangeRecord();
+
+ QString exchange() const;
+ QString name() const;
+ quint16 preference() const;
+ quint32 timeToLive() const;
+
+ QDnsMailExchangeRecord &operator=(const QDnsMailExchangeRecord &other);
+
+private:
+ QSharedDataPointer<QDnsMailExchangeRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class QDnsServiceRecord
+{
+public:
+ QDnsServiceRecord();
+ QDnsServiceRecord(const QDnsServiceRecord &other);
+ ~QDnsServiceRecord();
+
+ QString name() const;
+ quint16 port() const;
+ quint16 priority() const;
+ QString target() const;
+ quint32 timeToLive() const;
+ quint16 weight() const;
+
+ QDnsServiceRecord &operator=(const QDnsServiceRecord &other);
+
+private:
+ QSharedDataPointer<QDnsServiceRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class QDnsTextRecord
+{
+public:
+ QDnsTextRecord();
+ QDnsTextRecord(const QDnsTextRecord &other);
+ ~QDnsTextRecord();
+
+ QString name() const;
+ quint32 timeToLive() const;
+ QList<QByteArray> values() const;
+
+ QDnsTextRecord &operator=(const QDnsTextRecord &other);
+
+private:
+ QSharedDataPointer<QDnsTextRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+class QDnsLookup : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(Error Type)
+ Q_PROPERTY(Error error READ error NOTIFY finished)
+ Q_PROPERTY(QString errorString READ errorString NOTIFY finished)
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+ Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged)
+
+public:
+ enum Error
+ {
+ NoError = 0,
+ ResolverError,
+ OperationCancelledError,
+ InvalidRequestError,
+ InvalidReplyError,
+ ServerFailureError,
+ ServerRefusedError,
+ NotFoundError
+ };
+
+ enum Type
+ {
+ A = 1,
+ AAAA = 28,
+ ANY = 255,
+ CNAME = 5,
+ MX = 15,
+ NS = 2,
+ PTR = 12,
+ SRV = 33,
+ TXT = 16
+ };
+
+ QDnsLookup(QObject *parent = 0);
+ QDnsLookup(Type type, const QString &name, QObject *parent = 0);
+ ~QDnsLookup();
+
+ Error error() const;
+ QString errorString() const;
+ bool isFinished() const;
+
+ QString name() const;
+ void setName(const QString &name);
+
+ Type type() const;
+ void setType(QDnsLookup::Type);
+
+ QList<QDnsDomainNameRecord> canonicalNameRecords() const;
+ QList<QDnsHostAddressRecord> hostAddressRecords() const;
+ QList<QDnsMailExchangeRecord> mailExchangeRecords() const;
+ QList<QDnsDomainNameRecord> nameServerRecords() const;
+ QList<QDnsDomainNameRecord> pointerRecords() const;
+ QList<QDnsServiceRecord> serviceRecords() const;
+ QList<QDnsTextRecord> textRecords() const;
+
+
+public Q_SLOTS:
+ void abort();
+ void lookup();
+
+Q_SIGNALS:
+ void finished();
+ void nameChanged(const QString &name);
+ void typeChanged(Type type);
+
+private:
+ QDnsLookupPrivate *d_ptr;
+ Q_DECLARE_PRIVATE(QDnsLookup)
+ Q_PRIVATE_SLOT(d_func(), void _q_lookupFinished(const QDnsLookupReply &reply))
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#include "qdnslookup_p.h"
+
+#endif // QDNSLOOKUP_H
diff --git a/src/base/qdnslookup_p.h b/src/base/qdnslookup_p.h
new file mode 100644
index 00000000..4797f345
--- /dev/null
+++ b/src/base/qdnslookup_p.h
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDNSLOOKUP_P_H
+#define QDNSLOOKUP_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QDnsLookup class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QHostAddress>
+#include <QMetaType>
+#include <QMutex>
+#include <QRunnable>
+#include <QSharedPointer>
+#include <QThreadPool>
+
+#include "qdnslookup.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define QDNSLOOKUP_DEBUG
+
+class QDnsLookupRunnable;
+
+class QDnsLookupReply
+{
+public:
+ QDnsLookupReply()
+ : error(QDnsLookup::NoError)
+ { }
+
+ QDnsLookup::Error error;
+ QString errorString;
+
+ QList<QDnsDomainNameRecord> canonicalNameRecords;
+ QList<QDnsHostAddressRecord> hostAddressRecords;
+ QList<QDnsMailExchangeRecord> mailExchangeRecords;
+ QList<QDnsDomainNameRecord> nameServerRecords;
+ QList<QDnsDomainNameRecord> pointerRecords;
+ QList<QDnsServiceRecord> serviceRecords;
+ QList<QDnsTextRecord> textRecords;
+};
+
+class QDnsLookupPrivate
+{
+public:
+ QDnsLookupPrivate(QDnsLookup *qq)
+ : isFinished(false)
+ , type(QDnsLookup::A)
+ , runnable(0)
+ , q_ptr(qq)
+ { }
+
+ void _q_lookupFinished(const QDnsLookupReply &reply);
+
+ bool isFinished;
+ QString name;
+ QDnsLookup::Type type;
+ QDnsLookupReply reply;
+ QDnsLookupRunnable *runnable;
+ QDnsLookup *q_ptr;
+
+ Q_DECLARE_PUBLIC(QDnsLookup)
+};
+
+class QDnsLookupRunnable : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name)
+ : requestType(type)
+ , requestName(name)
+ { }
+ void run();
+
+signals:
+ void finished(const QDnsLookupReply &reply);
+
+private:
+ static void query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply);
+ QDnsLookup::Type requestType;
+ QByteArray requestName;
+};
+
+class QDnsLookupThreadPool : public QThreadPool
+{
+ Q_OBJECT
+
+public:
+ QDnsLookupThreadPool();
+ void start(QRunnable *runnable);
+
+private slots:
+ void _q_applicationDestroyed();
+
+private:
+ QMutex signalsMutex;
+ bool signalsConnected;
+};
+
+class QDnsRecordPrivate : public QSharedData
+{
+public:
+ QDnsRecordPrivate()
+ : timeToLive(0)
+ { }
+
+ QString name;
+ quint32 timeToLive;
+};
+
+class QDnsDomainNameRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsDomainNameRecordPrivate()
+ { }
+
+ QString value;
+};
+
+class QDnsHostAddressRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsHostAddressRecordPrivate()
+ { }
+
+ QHostAddress value;
+};
+
+class QDnsMailExchangeRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsMailExchangeRecordPrivate()
+ : preference(0)
+ { }
+
+ QString exchange;
+ quint16 preference;
+};
+
+class QDnsServiceRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsServiceRecordPrivate()
+ : port(0),
+ priority(0),
+ weight(0)
+ { }
+
+ QString target;
+ quint16 port;
+ quint16 priority;
+ quint16 weight;
+};
+
+class QDnsTextRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsTextRecordPrivate()
+ { }
+
+ QList<QByteArray> values;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QDnsLookupReply)
+
+#endif // QDNSLOOKUP_P_H
diff --git a/src/base/qdnslookup_stub.cpp b/src/base/qdnslookup_stub.cpp
new file mode 100644
index 00000000..df9ed349
--- /dev/null
+++ b/src/base/qdnslookup_stub.cpp
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+{
+ Q_UNUSED(requestType);
+ Q_UNUSED(requestName);
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = QLatin1String("QDnsLookup is not implemented for this platform");
+}
+
+QT_END_NAMESPACE
diff --git a/src/base/qdnslookup_symbian.cpp b/src/base/qdnslookup_symbian.cpp
new file mode 100644
index 00000000..bed5278e
--- /dev/null
+++ b/src/base/qdnslookup_symbian.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup_p.h"
+
+#include <QUrl>
+#include <QMutex>
+#include <QLibrary>
+
+#include <dns_qry.h>
+
+QT_BEGIN_NAMESPACE
+
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+{
+ RHostResolver dnsResolver;
+ RSocketServ dnsSocket;
+
+ // Initialise resolver.
+ TInt err = dnsSocket.Connect();
+ err = dnsResolver.Open(dnsSocket, KAfInet, KProtocolInetUdp);
+ if (err != KErrNone) {
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = QLatin1String("RHostResolver::Open failed");
+ return;
+ }
+
+ // Perform DNS query.
+ TDnsQueryBuf dnsQuery;
+ TDnsRespSRVBuf dnsResponse;
+ dnsQuery().SetClass(KDnsRRClassIN);
+ TPtrC8 queryPtr(reinterpret_cast<const TUint8*>(requestName.constData()), requestName.size());
+ dnsQuery().SetData(queryPtr);
+ dnsQuery().SetType(requestType);
+ err = dnsResolver.Query(dnsQuery, dnsResponse);
+ if (err != KErrNone) {
+ reply->error = QDnsLookup::NotFoundError;
+ reply->errorString = QLatin1String("RHostResolver::Query failed");
+ return;
+ }
+
+ // Extract results.
+ while (err == KErrNone) {
+ const QByteArray aceName((const char*)dnsResponse().Target().Ptr(),
+ dnsResponse().Target().Length());
+
+ QDnsServiceRecord record;
+ record.d->name = QUrl::fromAce(requestName);
+ record.d->target = QUrl::fromAce(aceName);
+ record.d->port = dnsResponse().Port();
+ record.d->priority = dnsResponse().Priority();
+ record.d->timeToLive = dnsResponse().RRTtl();
+ record.d->weight = dnsResponse().Weight();
+ reply->serviceRecords.append(record);
+
+ err = dnsResolver.QueryGetNext(dnsResponse);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/base/qdnslookup_unix.cpp b/src/base/qdnslookup_unix.cpp
new file mode 100644
index 00000000..f4d1c64c
--- /dev/null
+++ b/src/base/qdnslookup_unix.cpp
@@ -0,0 +1,323 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup_p.h"
+
+#include <QLibrary>
+#include <QMutex>
+#include <QScopedPointer>
+#include <QUrl>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/nameser_compat.h>
+#include <resolv.h>
+
+QT_BEGIN_NAMESPACE
+
+static QMutex local_res_mutex;
+typedef int (*dn_expand_proto)(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
+static dn_expand_proto local_dn_expand = 0;
+typedef void (*res_nclose_proto)(res_state);
+static res_nclose_proto local_res_nclose = 0;
+typedef int (*res_ninit_proto)(res_state);
+static res_ninit_proto local_res_ninit = 0;
+typedef int (*res_nquery_proto)(res_state, const char *, int, int, unsigned char *, int);
+static res_nquery_proto local_res_nquery = 0;
+
+// Custom deleter to close resolver state.
+
+struct QDnsLookupStateDeleter
+{
+ static inline void cleanup(struct __res_state *pointer)
+ {
+ local_res_nclose(pointer);
+ }
+};
+
+static void resolveLibrary()
+{
+ QLibrary lib(QLatin1String("resolv"));
+ if (!lib.load())
+ return;
+
+ local_dn_expand = dn_expand_proto(lib.resolve("__dn_expand"));
+ if (!local_dn_expand)
+ local_dn_expand = dn_expand_proto(lib.resolve("dn_expand"));
+
+ local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose"));
+ if (!local_res_nclose)
+ local_res_nclose = res_nclose_proto(lib.resolve("res_9_nclose"));
+ if (!local_res_nclose)
+ local_res_nclose = res_nclose_proto(lib.resolve("res_nclose"));
+
+ local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
+ if (!local_res_ninit)
+ local_res_ninit = res_ninit_proto(lib.resolve("res_9_ninit"));
+ if (!local_res_ninit)
+ local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
+
+ local_res_nquery = res_nquery_proto(lib.resolve("__res_nquery"));
+ if (!local_res_nquery)
+ local_res_nquery = res_nquery_proto(lib.resolve("res_9_nquery"));
+ if (!local_res_nquery)
+ local_res_nquery = res_nquery_proto(lib.resolve("res_nquery"));
+}
+
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+{
+ // Load dn_expand, res_ninit and res_nquery on demand.
+ static volatile bool triedResolve = false;
+ if (!triedResolve) {
+ QMutexLocker locker(&local_res_mutex);
+ if (!triedResolve) {
+ resolveLibrary();
+ triedResolve = true;
+ }
+ }
+
+ // If dn_expand, res_ninit or res_nquery is missing, fail.
+ if (!local_dn_expand || !local_res_nclose || !local_res_ninit || !local_res_nquery) {
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("Resolver functions not found");
+ return;
+ }
+
+ // Initialize state.
+ struct __res_state state;
+ memset(&state, 0, sizeof(state));
+ if (local_res_ninit(&state) < 0) {
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("Resolver initialization failed");
+ return;
+ }
+#ifdef QDNSLOOKUP_DEBUG
+ state.options |= RES_DEBUG;
+#endif
+ QScopedPointer<struct __res_state, QDnsLookupStateDeleter> state_ptr(&state);
+
+ // Perform DNS query.
+ unsigned char response[PACKETSZ];
+ memset(response, 0, sizeof(response));
+ const int responseLength = local_res_nquery(&state, requestName, C_IN, requestType, response, sizeof(response));
+
+ // Check the response header.
+ HEADER *header = (HEADER*)response;
+ const int answerCount = ntohs(header->ancount);
+ switch (header->rcode) {
+ case NOERROR:
+ break;
+ case FORMERR:
+ reply->error = QDnsLookup::InvalidRequestError;
+ reply->errorString = tr("Server could not process query");
+ return;
+ case SERVFAIL:
+ reply->error = QDnsLookup::ServerFailureError;
+ reply->errorString = tr("Server failure");
+ return;
+ case NXDOMAIN:
+ reply->error = QDnsLookup::NotFoundError;
+ reply->errorString = tr("Non existent domain");
+ return;
+ case REFUSED:
+ reply->error = QDnsLookup::ServerRefusedError;
+ reply->errorString = tr("Server refused to answer");
+ return;
+ default:
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid reply received");
+ return;
+ }
+
+ // Check the reply is valid.
+ if (responseLength < int(sizeof(HEADER))) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid reply received");
+ return;
+ }
+
+ // Skip the query host, type (2 bytes) and class (2 bytes).
+ char host[PACKETSZ], answer[PACKETSZ];
+ unsigned char *p = response + sizeof(HEADER);
+ int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Could not expand domain name");
+ return;
+ }
+ p += status + 4;
+
+ // Extract results.
+ int answerIndex = 0;
+ while ((p < response + responseLength) && (answerIndex < answerCount)) {
+ status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Could not expand domain name");
+ return;
+ }
+ const QString name = QUrl::fromAce(host);
+
+ p += status;
+ const quint16 type = (p[0] << 8) | p[1];
+ p += 2; // RR type
+ p += 2; // RR class
+ const quint32 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ const quint16 size = (p[0] << 8) | p[1];
+ p += 2;
+
+ if (type == QDnsLookup::A) {
+ if (size != 4) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid IPv4 address record");
+ return;
+ }
+ const quint32 addr = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ QDnsHostAddressRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QHostAddress(addr);
+ reply->hostAddressRecords.append(record);
+ } else if (type == QDnsLookup::AAAA) {
+ if (size != 16) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid IPv6 address record");
+ return;
+ }
+ QDnsHostAddressRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QHostAddress(p);
+ reply->hostAddressRecords.append(record);
+ } else if (type == QDnsLookup::CNAME) {
+ status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid canonical name record");
+ return;
+ }
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QUrl::fromAce(answer);
+ reply->canonicalNameRecords.append(record);
+ } else if (type == QDnsLookup::NS) {
+ status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid name server record");
+ return;
+ }
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QUrl::fromAce(answer);
+ reply->nameServerRecords.append(record);
+ } else if (type == QDnsLookup::PTR) {
+ status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid pointer record");
+ return;
+ }
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->value = QUrl::fromAce(answer);
+ reply->pointerRecords.append(record);
+ } else if (type == QDnsLookup::MX) {
+ const quint16 preference = (p[0] << 8) | p[1];
+ status = local_dn_expand(response, response + responseLength, p + 2, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid mail exchange record");
+ return;
+ }
+ QDnsMailExchangeRecord record;
+ record.d->exchange = QUrl::fromAce(answer);
+ record.d->name = name;
+ record.d->preference = preference;
+ record.d->timeToLive = ttl;
+ reply->mailExchangeRecords.append(record);
+ } else if (type == QDnsLookup::SRV) {
+ const quint16 priority = (p[0] << 8) | p[1];
+ const quint16 weight = (p[2] << 8) | p[3];
+ const quint16 port = (p[4] << 8) | p[5];
+ status = local_dn_expand(response, response + responseLength, p + 6, answer, sizeof(answer));
+ if (status < 0) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid service record");
+ return;
+ }
+ QDnsServiceRecord record;
+ record.d->name = name;
+ record.d->target = QUrl::fromAce(answer);
+ record.d->port = port;
+ record.d->priority = priority;
+ record.d->timeToLive = ttl;
+ record.d->weight = weight;
+ reply->serviceRecords.append(record);
+ } else if (type == QDnsLookup::TXT) {
+ unsigned char *txt = p;
+ QDnsTextRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ while (txt < p + size) {
+ const unsigned char length = *txt;
+ txt++;
+ if (txt + length > p + size) {
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid text record");
+ return;
+ }
+ record.d->values << QByteArray((char*)txt, length);
+ txt += length;
+ }
+ reply->textRecords.append(record);
+ }
+ p += size;
+ answerIndex++;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/base/qdnslookup_win.cpp b/src/base/qdnslookup_win.cpp
new file mode 100644
index 00000000..87d6955a
--- /dev/null
+++ b/src/base/qdnslookup_win.cpp
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup_p.h"
+
+#include <QUrl>
+#include <QMutex>
+#include <QLibrary>
+
+#include <windows.h>
+#include <windns.h>
+#include <winsock2.h>
+
+QT_BEGIN_NAMESPACE
+
+static QMutex local_dns_mutex;
+typedef DNS_STATUS (*dns_query_utf8_proto)(PCSTR,WORD,DWORD,PIP4_ARRAY,PDNS_RECORD*,PVOID*);
+static dns_query_utf8_proto local_dns_query_utf8 = 0;
+typedef void (*dns_record_list_free_proto)(PDNS_RECORD,DNS_FREE_TYPE);
+static dns_record_list_free_proto local_dns_record_list_free = 0;
+
+static void resolveLibrary()
+{
+ QLibrary lib(QLatin1String("dnsapi"));
+ if (!lib.load())
+ return;
+
+ local_dns_query_utf8 = (dns_query_utf8_proto) lib.resolve(QLatin1String("dnsapi"), "DnsQuery_UTF8");
+ local_dns_record_list_free = (dns_record_list_free_proto) lib.resolve(QLatin1String("dnsapi"), "DnsRecordListFree");
+}
+
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+{
+ // Load DnsQuery_UTF8 and DnsRecordListFree on demand.
+ static volatile bool triedResolve = false;
+ if (!triedResolve) {
+ QMutexLocker locker(&local_dns_mutex);
+ if (!triedResolve) {
+ resolveLibrary();
+ triedResolve = true;
+ }
+ }
+
+ // If DnsQuery_UTF8 or DnsRecordListFree is missing, fail.
+ if (!local_dns_query_utf8 || !local_dns_record_list_free) {
+ reply->error = QDnsLookup::ResolverError,
+ reply->errorString = tr("Resolver functions not found");
+ return;
+ }
+
+ // Perform DNS query.
+ PDNS_RECORD dns_records;
+ const DNS_STATUS status = local_dns_query_utf8(requestName, requestType, DNS_QUERY_STANDARD, NULL, &dns_records, NULL);
+ switch (status) {
+ case ERROR_SUCCESS:
+ break;
+ case DNS_ERROR_RCODE_FORMAT_ERROR:
+ reply->error = QDnsLookup::InvalidRequestError;
+ reply->errorString = tr("Server could not process query");
+ return;
+ case DNS_ERROR_RCODE_SERVER_FAILURE:
+ reply->error = QDnsLookup::ServerFailureError;
+ reply->errorString = tr("Server failure");
+ return;
+ case DNS_ERROR_RCODE_NAME_ERROR:
+ reply->error = QDnsLookup::NotFoundError;
+ reply->errorString = tr("Non existent domain");
+ return;
+ case DNS_ERROR_RCODE_REFUSED:
+ reply->error = QDnsLookup::ServerRefusedError;
+ reply->errorString = tr("Server refused to answer");
+ return;
+ default:
+ reply->error = QDnsLookup::InvalidReplyError;
+ reply->errorString = tr("Invalid reply received");
+ return;
+ }
+
+ // Extract results.
+ for (PDNS_RECORD ptr = dns_records; ptr != NULL; ptr = ptr->pNext) {
+ const QString name = QUrl::fromAce((char*)ptr->pName);
+ if (ptr->wType == QDnsLookup::A) {
+ QDnsHostAddressRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+ record.d->value = QHostAddress(ntohl(ptr->Data.A.IpAddress));
+ reply->hostAddressRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::AAAA) {
+ Q_IPV6ADDR addr;
+ memcpy(&addr, &ptr->Data.AAAA.Ip6Address, sizeof(Q_IPV6ADDR));
+
+ QDnsHostAddressRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+ record.d->value = QHostAddress(addr);
+ reply->hostAddressRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::CNAME) {
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+ record.d->value = QUrl::fromAce((char*)ptr->Data.Cname.pNameHost);
+ reply->canonicalNameRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::MX) {
+ QDnsMailExchangeRecord record;
+ record.d->name = name;
+ record.d->exchange = QUrl::fromAce((char*)ptr->Data.Mx.pNameExchange);
+ record.d->preference = ptr->Data.Mx.wPreference;
+ record.d->timeToLive = ptr->dwTtl;
+ reply->mailExchangeRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::NS) {
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+ record.d->value = QUrl::fromAce((char*)ptr->Data.Ns.pNameHost);
+ reply->nameServerRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::PTR) {
+ QDnsDomainNameRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+ record.d->value = QUrl::fromAce((char*)ptr->Data.Ptr.pNameHost);
+ reply->pointerRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::SRV) {
+ QDnsServiceRecord record;
+ record.d->name = name;
+ record.d->target = QUrl::fromAce((char*)ptr->Data.Srv.pNameTarget);
+ record.d->port = ptr->Data.Srv.wPort;
+ record.d->priority = ptr->Data.Srv.wPriority;
+ record.d->timeToLive = ptr->dwTtl;
+ record.d->weight = ptr->Data.Srv.wWeight;
+ reply->serviceRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::TXT) {
+ QDnsTextRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+ for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) {
+ record.d->values << QByteArray((char*)ptr->Data.Txt.pStringArray[i]);
+ }
+ reply->textRecords.append(record);
+ }
+ }
+
+ local_dns_record_list_free(dns_records, DnsFreeRecordList);
+}
+
+QT_END_NAMESPACE