diff options
| author | Manjeet Dahiya <manjeetdahiya@gmail.com> | 2009-10-26 17:01:09 +0000 |
|---|---|---|
| committer | Manjeet Dahiya <manjeetdahiya@gmail.com> | 2009-10-26 17:01:09 +0000 |
| commit | 239b2b15c3cfd9e4d7cc085ed31645d6ae6942ee (patch) | |
| tree | 928a71086a6d328c97ffc468ba4ceb41fc7b6043 /source/QXmppStream.cpp | |
| parent | f988517a9ba9d9a7753270f1cb0c4c2212c7ac22 (diff) | |
| download | qxmpp-239b2b15c3cfd9e4d7cc085ed31645d6ae6942ee.tar.gz | |
Fix for
Issue 23: QXmppBind compilation error on macosx starting from r23
Issue 26: examples fail to link on macosx and linux
Issue 24: all text files should have the svn property eol-style set
Contributed by: Marco Molteni
Diffstat (limited to 'source/QXmppStream.cpp')
| -rw-r--r-- | source/QXmppStream.cpp | 1862 |
1 files changed, 931 insertions, 931 deletions
diff --git a/source/QXmppStream.cpp b/source/QXmppStream.cpp index 70e9a4e8..44d8c5f2 100644 --- a/source/QXmppStream.cpp +++ b/source/QXmppStream.cpp @@ -1,931 +1,931 @@ -/*
- * Copyright (C) 2008-2009 Manjeet Dahiya
- *
- * 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 "QXmppStream.h"
-#include "QXmppPacket.h"
-#include "QXmppUtils.h"
-#include "QXmppClient.h"
-#include "QXmppRoster.h"
-#include "QXmppPresence.h"
-#include "QXmppIq.h"
-#include "QXmppBind.h"
-#include "QXmppSession.h"
-#include "QXmppRosterIq.h"
-#include "QXmppMessage.h"
-#include "QXmppConstants.h"
-#include "QXmppVCard.h"
-#include "QXmppNonSASLAuth.h"
-#include "QXmppInformationRequestResult.h"
-#include "QXmppLogger.h"
-
-#include <QDomDocument>
-#include <QStringList>
-#include <QRegExp>
-#include <QHostAddress>
-#include <QXmlStreamWriter>
-
-static const QByteArray streamRootElementStart = "<?xml version=\"1.0\"?><stream:stream xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" xmlns=\"jabber:client\" xml:lang=\"en\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">\n";
-static const QByteArray streamRootElementEnd = "</stream:stream>";
-
-QXmppStream::QXmppStream(QXmppClient* client)
- : QObject(client), m_client(client), m_roster(this),
- m_sessionAvaliable(false), m_vCardManager(m_client)
-{
- bool check = QObject::connect(&m_socket, SIGNAL(hostFound()),
- this, SLOT(socketHostFound()));
- Q_ASSERT(check);
- check = QObject::connect(&m_socket, SIGNAL(connected()),
- this, SLOT(socketConnected()));
- Q_ASSERT(check);
- check = QObject::connect(&m_socket, SIGNAL(disconnected()),
- this, SLOT(socketDisconnected()));
- Q_ASSERT(check);
- check = QObject::connect(&m_socket, SIGNAL(readyRead()),
- this, SLOT(socketReadReady()));
- Q_ASSERT(check);
- check = QObject::connect(&m_socket, SIGNAL(encrypted()),
- this, SLOT(socketEncrypted()));
- Q_ASSERT(check);
- check = QObject::connect(&m_socket,
- SIGNAL(sslErrors(const QList<QSslError>&)), this,
- SLOT(socketSslErrors(const QList<QSslError>&)));
- Q_ASSERT(check);
- check = QObject::connect(&m_socket,
- SIGNAL(error(QAbstractSocket::SocketError)), this,
- SLOT(socketError(QAbstractSocket::SocketError)));
- Q_ASSERT(check);
-
- check = QObject::connect(this,
- SIGNAL(presenceReceived(const QXmppPresence&)),
- &m_roster,
- SLOT(presenceReceived(const QXmppPresence&)));
- Q_ASSERT(check);
-
- check = QObject::connect(this, SIGNAL(rosterIqReceived(const QXmppRosterIq&)),
- &m_roster, SLOT(rosterIqReceived(const QXmppRosterIq&)));
- Q_ASSERT(check);
-
- check = QObject::connect(this, SIGNAL(vCardIqReceived(const QXmppVCard&)),
- &m_vCardManager, SLOT(vCardIqReceived(const QXmppVCard&)));
- Q_ASSERT(check);
-}
-
-QXmppStream::~QXmppStream()
-{
-
-}
-
-QXmppConfiguration& QXmppStream::getConfiguration()
-{
- return m_client->getConfiguration();
-}
-
-void QXmppStream::connect()
-{
- log(QString("Connecting to: %1:%2").arg(getConfiguration().
- getHost()).arg(getConfiguration().getPort()));
-
- m_socket.setProxy(getConfiguration().getNetworkProxy());
- m_socket.connectToHost(getConfiguration().
- getHost(), getConfiguration().getPort());
-}
-
-void QXmppStream::socketSslErrors(const QList<QSslError> & error)
-{
- log(QString("SSL errors"));
- m_socket.ignoreSslErrors();
- for(int i = 0; i< error.count(); ++i)
- log(error.at(i).errorString());
-}
-
-void QXmppStream::socketHostFound()
-{
- log(QString("Host found"));
- emit hostFound();
-}
-
-void QXmppStream::socketConnected()
-{
- flushDataBuffer();
- log(QString("Connected"));
- emit connected();
- sendStartStream();
-}
-
-void QXmppStream::socketDisconnected()
-{
- flushDataBuffer();
- log(QString("Disconnected"));
- emit disconnected();
-}
-
-void QXmppStream::socketEncrypted()
-{
- log(QString("Encrypted"));
- sendStartStream();
-}
-
-void QXmppStream::socketError(QAbstractSocket::SocketError ee)
-{
- m_socketError = ee;
- emit error(QXmppClient::SocketError);
- log(QString("Socket error: " + m_socket.errorString()));
-}
-
-void QXmppStream::socketReadReady()
-{
- QByteArray data = m_socket.readAll();
- log("SERVER [COULD BE PARTIAL DATA]:" + data.left(20));
- parser(data);
-}
-
-void QXmppStream::sendNonSASLAuthQuery( const QString &to )
-{
- QXmppNonSASLAuthTypesRequestIq authQuery;
- authQuery.setTo(to);
- authQuery.setUsername(getConfiguration().getUser());
-
- sendPacket(authQuery);
-}
-
-void QXmppStream::parser(const QByteArray& data)
-{
- QDomDocument doc;
- QByteArray completeXml;
-
- m_dataBuffer = m_dataBuffer + data;
-
- if(hasStartStreamElement(m_dataBuffer))
- {
- completeXml = m_dataBuffer + streamRootElementEnd;
- }
- else if(hasEndStreamElement(data))
- {
- completeXml = streamRootElementStart + m_dataBuffer;
- }
- else
- {
- completeXml = streamRootElementStart + m_dataBuffer + streamRootElementEnd;
- }
-
- if(doc.setContent(completeXml, true))
- {
- log("SERVER:" + m_dataBuffer);
- flushDataBuffer();
-
- QDomElement nodeRecv = doc.documentElement().firstChildElement();
-
- if(nodeRecv.isNull())
- {
- QDomElement streamElement = doc.documentElement();
- if(m_streamId.isEmpty())
- m_streamId = streamElement.attribute("id");
- if(m_XMPPVersion.isEmpty())
- {
- m_XMPPVersion = streamElement.attribute("version");
- if(m_XMPPVersion.isEmpty())
- {
- // no version specified, signals XMPP Version < 1.0.
- // switch to old auth mechanism
- sendNonSASLAuthQuery(doc.documentElement().attribute("from"));
- }
- }
- }
- else
- {
- //TODO: Make a login error here.
- }
-
- while(!nodeRecv.isNull())
- {
- QString ns = nodeRecv.namespaceURI();
- if(ns == ns_stream && nodeRecv.tagName() == "features")
- {
- bool nonSaslAvailable = nodeRecv.firstChildElement("auth").
- namespaceURI() == ns_authFeature;
- bool saslAvailable = nodeRecv.firstChildElement("mechanisms").
- namespaceURI() == ns_sasl;
- bool useSasl = getConfiguration().getUseSASLAuthentication();
-
- if(nodeRecv.firstChildElement("starttls").namespaceURI()
- == ns_tls && !m_socket.isEncrypted())
- {
- if(nodeRecv.firstChildElement("starttls").
- firstChildElement().tagName() == "required")
- {
- // TLS is must from the server side
- sendStartTls();
- return;
- }
- else
- {
- // TLS is optional from the server side
- switch(getConfiguration().getStreamSecurityMode())
- {
- case QXmppConfiguration::TLSEnabled:
- case QXmppConfiguration::TLSRequired:
- sendStartTls();
- return;
- case QXmppConfiguration::TLSDisabled:
- break;
- }
- }
- }
- else if(!m_socket.isEncrypted()) // TLS not supported by server
- {
- if(getConfiguration().getStreamSecurityMode() ==
- QXmppConfiguration::TLSRequired)
- {
- // disconnect as the for client TLS is compulsory but
- // not available on the server
- //
- log(QString("Disconnecting as TLS not available at the server"));
- disconnect();
- return;
- }
- }
-
- if((saslAvailable && nonSaslAvailable && !useSasl) ||
- (!saslAvailable && nonSaslAvailable))
- {
- sendNonSASLAuthQuery(doc.documentElement().attribute("from"));
- }
- else if(saslAvailable)
- {
- // SASL Authentication
- QDomElement element = nodeRecv.firstChildElement("mechanisms");
- log(QString("Mechanisms:"));
- QDomElement subElement = element.firstChildElement();
- QStringList mechanisms;
- while(!subElement.isNull())
- {
- if(subElement.tagName() == "mechanism")
- {
- log(subElement.text());
- mechanisms << subElement.text();
- }
- subElement = subElement.nextSiblingElement();
- }
-
- switch(getConfiguration().getSASLAuthMechanism())
- {
- case QXmppConfiguration::SASLPlain:
- if(mechanisms.contains("PLAIN"))
- {
- sendAuthPlain();
- break;
- }
- case QXmppConfiguration::SASLDigestMD5:
- if(mechanisms.contains("DIGEST-MD5"))
- {
- sendAuthDigestMD5();
- break;
- }
- default:
- log(QString("Desired SASL Auth mechanism not available trying the available ones"));
- if(mechanisms.contains("DIGEST-MD5"))
- sendAuthDigestMD5();
- else if(mechanisms.contains("PLAIN"))
- sendAuthPlain();
- else
- {
- log(QString("SASL Auth mechanism not available"));
- disconnect();
- return;
- }
- break;
- }
- }
-
- if(nodeRecv.firstChildElement("bind").
- namespaceURI() == ns_bind)
- {
- sendBindIQ();
- }
-
- if(nodeRecv.firstChildElement("session").
- namespaceURI() == ns_session)
- {
- m_sessionAvaliable = true;
- }
- }
- else if(ns == ns_tls)
- {
- if(nodeRecv.tagName() == "proceed")
- {
- log(QString("Starting encryption"));
- m_socket.startClientEncryption();
- return;
- }
- }
- else if(ns == ns_sasl)
- {
- if(nodeRecv.tagName() == "success")
- {
- log(QString("Authenticated"));
- sendStartStream();
- }
- else if(nodeRecv.tagName() == "challenge")
- {
- sendAuthDigestMD5Response(nodeRecv.text());
- }
- }
- else if(ns == ns_client)
- {
-
- if(nodeRecv.tagName() == "iq")
- {
- QDomElement element = nodeRecv.firstChildElement();
- QString id = nodeRecv.attribute("id");
- QString to = nodeRecv.attribute("to");
- QString from = nodeRecv.attribute("from");
- QString type = nodeRecv.attribute("type");
- if(type.isEmpty())
- qWarning("QXmppStream: iq type can't be empty");
- QXmppIq iqPacket; // to emit
-
-
- QDomElement elemen = nodeRecv.firstChildElement("error");
- QXmppStanza::Error error = parseStanzaError(elemen);
-
-
- if(id == m_sessionId)
- {
- // get back add configuration whether to send
- // roster and intial presence in beginning
- // process SessionIq
-
- // xmpp connection made
- emit xmppConnected();
-
- sendRosterRequest();
- sendInitialPresence();
-
- QXmppBind session(type);
- session.setId(id);
- session.setTo(to);
- session.setFrom(from);
- iqPacket = session;
- }
- else if(id == m_bindId)
- {
- QXmppBind bind(type);
- QString jid = nodeRecv.firstChildElement("bind").
- firstChildElement("jid").text();
- bind.setResource(jidToResource(jid));
- bind.setJid(jidToBareJid(jid));
- bind.setId(id);
- bind.setTo(to);
- bind.setFrom(from);
- processBindIq(bind);
- iqPacket = bind;
- }
- else if(nodeRecv.firstChildElement("query").
- namespaceURI() == ns_roster)
- {
- QDomElement itemElement = nodeRecv.
- firstChildElement("query").
- firstChildElement("item");
- QXmppRosterIq rosterIq(nodeRecv.attribute("type"));
- rosterIq.setId(id);
- rosterIq.setTo(to);
- rosterIq.setFrom(from);
- while(!itemElement.isNull())
- {
- QXmppRosterIq::Item item;
- item.setName(itemElement.attribute("name"));
- item.setBareJid(itemElement.attribute("jid"));
- item.setSubscriptionTypeFromStr(
- itemElement.attribute("subscription"));
- item.setSubscriptionStatus(
- itemElement.attribute("ask"));
- item.addGroup(
- itemElement.firstChildElement("group").firstChildElement().text());
- rosterIq.addItem(item);
- itemElement = itemElement.nextSiblingElement();
- }
- processRosterIq(rosterIq);
- iqPacket = rosterIq;
- }
- // extensions
- // vCard - XEP-0054
- // http://xmpp.org/extensions/xep-0054.html
- else if(nodeRecv.firstChildElement("vCard").
- namespaceURI() == ns_vcard)
- {
- QXmppVCard vcardIq;
- vcardIq.parse(nodeRecv);
- emit vCardIqReceived(vcardIq);
- iqPacket = vcardIq;
- }
- // XEP-0030 info query
- else if(nodeRecv.firstChildElement("query").
- namespaceURI() == ns_disco_info &&
- type == "get")
- {
- QXmppInformationRequestResult qxmppFeatures;
- qxmppFeatures.setId(id);
- qxmppFeatures.setTo(from);
- qxmppFeatures.setFrom(to);
- sendPacket(qxmppFeatures);
- }
- else if(id == m_nonSASLAuthId && type == "result")
- {
- // successful Non-SASL Authentication
- log(QString("Authenticated (Non-SASL)"));
-
- emit xmppConnected();
-
- sendRosterRequest();
- sendInitialPresence();
- }
- else if(nodeRecv.firstChildElement("query").
- namespaceURI() == ns_auth)
- {
- if(type == "result")
- {
- bool digest = !nodeRecv.firstChildElement("query").
- firstChildElement("digest").isNull();
- bool plain = !nodeRecv.firstChildElement("query").
- firstChildElement("password").isNull();
- bool plainText = false;
-
- if(plain && digest)
- {
- if(getConfiguration().getNonSASLAuthMechanism() ==
- QXmppConfiguration::NonSASLDigest)
- plainText = false;
- else
- plainText = true;
- }
- else if(plain)
- plainText = true;
- else if(digest)
- plainText = false;
- else
- {
- //TODO Login error
- return;
- }
- sendNonSASLAuth(plainText);
- }
- }
- else // didn't understant the iq...reply with error
- {
- if(type != "result") // but not incase of result iqs
- {
- QXmppIq iq(QXmppIq::Error);
- iq.setId(id);
- iq.setTo(from);
- iq.setFrom(to);
- QXmppStanza::Error error(QXmppStanza::Error::Cancel,
- QXmppStanza::Error::FeatureNotImplemented);
- iq.setError(error);
- sendPacket(iq);
- }
- }
-
- iqPacket.setError(error);
- processIq(iqPacket);
- }
- else if(nodeRecv.tagName() == "presence")
- {
- QXmppPresence presence;
- presence.setTypeFromStr(nodeRecv.attribute("type"));
- presence.setFrom(nodeRecv.attribute("from"));
- presence.setTo(nodeRecv.attribute("to"));
-
- QString statusText = nodeRecv.
- firstChildElement("status").text();
- QString show = nodeRecv.
- firstChildElement("show").text();
- int priority = nodeRecv.
- firstChildElement("priority").text().toInt();
- QXmppPresence::Status status;
- status.setTypeFromStr(show);
- status.setStatusText(statusText);
- status.setPriority(priority);
- presence.setStatus(status);
-
- QDomElement errorElement = nodeRecv.
- firstChildElement("error");
- if(!errorElement.isNull())
- {
- QXmppStanza::Error error =
- parseStanzaError(errorElement);
- presence.setError(error);
- }
-
- processPresence(presence);
- }
- else if(nodeRecv.tagName() == "message")
- {
- QString from = nodeRecv.attribute("from");
- QString to = nodeRecv.attribute("to");
- QString type = nodeRecv.attribute("type");
- QString body = unescapeString(
- nodeRecv.firstChildElement("body").text());
- QString sub = unescapeString(
- nodeRecv.firstChildElement("subject").text());
- QString thread = nodeRecv.firstChildElement("thread").text();
- QXmppMessage message(from, to, body, thread);
- message.setSubject(sub);
- message.setTypeFromStr(type);
-
- QDomElement errorElement = nodeRecv.
- firstChildElement("error");
- if(!errorElement.isNull())
- {
- QXmppStanza::Error error = parseStanzaError(errorElement);
- message.setError(error);
- }
- processMessage(message);
- }
- }
- nodeRecv = nodeRecv.nextSiblingElement();
- }
- }
- else
- {
- //wait for complete packet
- }
-}
-
-
-void QXmppStream::sendStartStream()
-{
- QByteArray data = "<?xml version='1.0'?><stream:stream to='";
- data.append(getConfiguration().getDomain());
- data.append("' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>");
- sendToServer(data);
-}
-
-void QXmppStream::sendToServer(const QByteArray& packet)
-{
- log("CLIENT: " + packet);
- m_socket.write( packet );
-}
-
-bool QXmppStream::hasStartStreamElement(const QByteArray& data)
-{
- QString str(data);
- QRegExp regex("(<\\?xml.*\\?>)?\\s*<stream:stream.*>");
- regex.setMinimal(true);
- if(str.contains(regex))
- return true;
- else
- return false;
-}
-
-bool QXmppStream::hasEndStreamElement(const QByteArray& data)
-{
- QString str(data);
- QRegExp regex("</stream:stream>");
- regex.setMinimal(true);
- if(str.contains(regex))
- return true;
- else
- return false;
-}
-
-void QXmppStream::sendStartTls()
-{
- sendToServer("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
-}
-
-void QXmppStream::sendNonSASLAuth(bool plainText)
-{
- QXmppNonSASLAuthIq authQuery;
- authQuery.setUsername(getConfiguration().getUser());
- authQuery.setPassword(getConfiguration().getPasswd());
- authQuery.setResource(getConfiguration().getResource());
- authQuery.setStreamId(m_streamId);
- authQuery.setUsePlainText(plainText);
- m_nonSASLAuthId = authQuery.getId();
- sendPacket(authQuery);
-}
-
-void QXmppStream::sendAuthPlain()
-{
- QByteArray data = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>";
- QString userPass('\0' + getConfiguration().getUser() +
- '\0' + getConfiguration().getPasswd());
- data += userPass.toUtf8().toBase64();
- data += "</auth>";
- sendToServer(data);
-}
-
-void QXmppStream::sendAuthDigestMD5()
-{
- QByteArray packet = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>";
- sendToServer(packet);
-}
-
-// challenge is BASE64 encoded string
-void QXmppStream::sendAuthDigestMD5Response(const QString& challenge)
-{
- QByteArray ba = QByteArray::fromBase64(challenge.toUtf8());
-
- //log(ba);
-
- ba.replace('"', QString(""));
- QList<QByteArray> list = ba.split(',');
-
- QMap<QByteArray, QByteArray> map;
-
- QList<QByteArray> list2;
- for(int i = 0; i < list.count(); ++i)
- {
- list2 = list.at(i).split('=');
- if(list2.count() == 2)
- map[list2.at(0).trimmed()] = list2.at(1).trimmed();
- else
- log(QString("Invalid challenge send"));
- }
-
- QByteArray user = getConfiguration().getUser().toUtf8();
- QByteArray passwd = getConfiguration().getPasswd().toUtf8();
- QByteArray domain = getConfiguration().getDomain().toUtf8();
- QByteArray realm;
- if(map.contains("realm"))
- realm = map["realm"];
-
- QByteArray response;
-
- // First challenge
- if(map.contains("nonce"))
- {
- QByteArray cnonce(32, 'm');
- for(int n = 0; n < cnonce.size(); ++n)
- cnonce[n] = (char)(256.0*qrand()/(RAND_MAX+1.0));
-
- QByteArray nc = "00000001";
- QByteArray digest_uri = "xmpp/" + domain;
-
- QByteArray a1 = user + ':' + realm + ':' + passwd;
- QByteArray ha1 = QCryptographicHash::hash(a1, QCryptographicHash::Md5);
- ha1 += ':' + map["nonce"] + ':' + cnonce;
-
- if(map.contains("authzid"))
- ha1 += ':' + map["authzid"];
-
- QByteArray A1(ha1);
- QByteArray A2 = "AUTHENTICATE:" + digest_uri;
- QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex();
- QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex();
- QByteArray KD = HA1 + ':' + map["nonce"] + ':' + nc + ':' + cnonce + ':'
- + "auth" + ':' + HA2;
- QByteArray Z = QCryptographicHash::hash(KD, QCryptographicHash::Md5).toHex();
-
- response += "username=\"" + user + "\",";
-
- if(!realm.isEmpty())
- response += "realm=\"" + realm + "\",";
-
- response += "nonce=\"" + map["nonce"] + "\",";
- response += "cnonce=\"" + cnonce + "\",";
- response += "nc=" + nc + ",";
- response += "qop=auth,";
- response += "digest-uri=\"" + digest_uri + "\",";
- response += "response=" + Z + ",";
- if(map.contains("authzid"))
- response += "authzid=\"" + map["authzid"] + "\",";
- response += "charset=utf-8";
-
- log(response);
- QByteArray packet = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
- + response.toBase64() + "</response>";
- sendToServer(packet);
- }
- else if(map.contains("rspauth"))
- {
- sendToServer("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
- }
- else
- {
- disconnect();
- log(QString("sendAuthDigestMD5Response: Invalid input"));
- }
-}
-
-void QXmppStream::sendBindIQ()
-{
- QXmppBind bind(QXmppIq::Set);
- bind.setResource(getConfiguration().getResource());
- m_bindId = bind.getId();
- sendPacket(bind);
-}
-
-void QXmppStream::sendSessionIQ()
-{
- QXmppSession session(QXmppIq::Set);
- session.setTo(getConfiguration().getDomain());
- m_sessionId = session.getId();
- sendPacket(session);
-}
-
-void QXmppStream::sendInitialPresence()
-{
- if(m_client)
- sendPacket(m_client->getClientPresence());
-}
-
-void QXmppStream::acceptSubscriptionRequest(const QString& from, bool accept)
-{
- QXmppPresence presence;
- presence.setTo(from);
- if(accept)
- presence.setType(QXmppPresence::Subscribed);
- else
- presence.setType(QXmppPresence::Unsubscribed);
-
- sendPacket(presence);
-}
-
-void QXmppStream::sendSubscriptionRequest(const QString& to)
-{
- if(to.isEmpty())
- return;
-
- QXmppPresence presence;
- presence.setTo(to);
- presence.setType(QXmppPresence::Subscribe);
- sendPacket(presence);
-}
-
-void QXmppStream::sendRosterRequest()
-{
- QXmppRosterIq roster(QXmppIq::Get);
- roster.setFrom(getConfiguration().getJid());
- m_rosterReqId = roster.getId();
- sendPacket(roster);
-}
-
-void QXmppStream::disconnect()
-{
- sendEndStream();
- m_socket.disconnectFromHost();
-}
-
-QXmppRoster& QXmppStream::getRoster()
-{
- return m_roster;
-}
-
-void QXmppStream::sendPacket(const QXmppPacket& packet)
-{
- if(QXmppLogger::getLogger()->getLoggingType() != QXmppLogger::NONE)
- {
- QByteArray logPacket;
- QXmlStreamWriter xmlStreamLog(&logPacket);
- packet.toXml(&xmlStreamLog);
- log("CLIENT: "+ logPacket);
- }
-
- QXmlStreamWriter xmlStream(&m_socket);
- packet.toXml(&xmlStream);
-}
-
-void QXmppStream::processPresence(const QXmppPresence& presence)
-{
- switch(presence.getType())
- {
- case QXmppPresence::Error:
- break;
- case QXmppPresence::Available:
- break;
- case QXmppPresence::Unavailable:
- break;
- case QXmppPresence::Subscribe:
- if(!presence.getFrom().isEmpty())
- {
- if(getConfiguration().getAutoAcceptSubscriptions())
- acceptSubscriptionRequest(presence.getFrom());
- emit subscriptionRequestReceived(presence.getFrom());
- }
- break;
- case QXmppPresence::Unsubscribe:
- break;
- case QXmppPresence::Unsubscribed:
- break;
- case QXmppPresence::Probe:
- break;
- default:
- break;
- }
- emit presenceReceived(presence);
-}
-
-void QXmppStream::processMessage(const QXmppMessage& message)
-{
- emit messageReceived(message);
-}
-
-void QXmppStream::processIq(const QXmppIq& iq)
-{
- emit iqReceived(iq);
-}
-
-void QXmppStream::sendEndStream()
-{
- sendToServer(streamRootElementEnd);
-}
-
-void QXmppStream::processBindIq(const QXmppBind& bind)
-{
- switch(bind.getType())
- {
- case QXmppIq::Result:
- if(!bind.getResource().isEmpty())
- getConfiguration().setResource(bind.getResource());
- if(m_sessionAvaliable)
- sendSessionIQ();
- break;
- default:
- break;
- }
-}
-
-void QXmppStream::processRosterIq(const QXmppRosterIq& rosterIq)
-{
- emit rosterIqReceived(rosterIq);
- switch(rosterIq.getType())
- {
- case QXmppIq::Set:
- // when contact subscribes user...user sends 'subscribed' presence
- // then after recieving following iq user requests contact for subscription
-
- // check thet "from" is newly added in the roster...and remove this ask thing...and do this for all items
- if(rosterIq.getItems().at(0).getSubscriptionType() ==
- QXmppRosterIq::Item::From && rosterIq.getItems().at(0).
- getSubscriptionStatus().isEmpty())
- sendSubscriptionRequest(rosterIq.getItems().at(0).getBareJid());
- break;
- default:
- break;
- }
-}
-
-QXmppStanza::Error QXmppStream::parseStanzaError(QDomElement & errorElement)
-{
- QXmppStanza::Error error;
-
- if(errorElement.isNull())
- return error;
-
- QString type = 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();
- }
-
- error.setConditionFromStr(cond);
- error.setTypeFromStr(type);
- error.setText(text);
- return error;
-}
-
-QAbstractSocket::SocketError QXmppStream::getSocketError()
-{
- return m_socketError;
-}
-
-QXmppVCardManager& QXmppStream::getVCardManager()
-{
- return m_vCardManager;
-}
-
-void QXmppStream::flushDataBuffer()
-{
- m_dataBuffer.clear();
-}
+/* + * Copyright (C) 2008-2009 Manjeet Dahiya + * + * 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 "QXmppStream.h" +#include "QXmppPacket.h" +#include "QXmppUtils.h" +#include "QXmppClient.h" +#include "QXmppRoster.h" +#include "QXmppPresence.h" +#include "QXmppIq.h" +#include "QXmppBind.h" +#include "QXmppSession.h" +#include "QXmppRosterIq.h" +#include "QXmppMessage.h" +#include "QXmppConstants.h" +#include "QXmppVCard.h" +#include "QXmppNonSASLAuth.h" +#include "QXmppInformationRequestResult.h" +#include "QXmppLogger.h" + +#include <QDomDocument> +#include <QStringList> +#include <QRegExp> +#include <QHostAddress> +#include <QXmlStreamWriter> + +static const QByteArray streamRootElementStart = "<?xml version=\"1.0\"?><stream:stream xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" xmlns=\"jabber:client\" xml:lang=\"en\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">\n"; +static const QByteArray streamRootElementEnd = "</stream:stream>"; + +QXmppStream::QXmppStream(QXmppClient* client) + : QObject(client), m_client(client), m_roster(this), + m_sessionAvaliable(false), m_vCardManager(m_client) +{ + bool check = QObject::connect(&m_socket, SIGNAL(hostFound()), + this, SLOT(socketHostFound())); + Q_ASSERT(check); + check = QObject::connect(&m_socket, SIGNAL(connected()), + this, SLOT(socketConnected())); + Q_ASSERT(check); + check = QObject::connect(&m_socket, SIGNAL(disconnected()), + this, SLOT(socketDisconnected())); + Q_ASSERT(check); + check = QObject::connect(&m_socket, SIGNAL(readyRead()), + this, SLOT(socketReadReady())); + Q_ASSERT(check); + check = QObject::connect(&m_socket, SIGNAL(encrypted()), + this, SLOT(socketEncrypted())); + Q_ASSERT(check); + check = QObject::connect(&m_socket, + SIGNAL(sslErrors(const QList<QSslError>&)), this, + SLOT(socketSslErrors(const QList<QSslError>&))); + Q_ASSERT(check); + check = QObject::connect(&m_socket, + SIGNAL(error(QAbstractSocket::SocketError)), this, + SLOT(socketError(QAbstractSocket::SocketError))); + Q_ASSERT(check); + + check = QObject::connect(this, + SIGNAL(presenceReceived(const QXmppPresence&)), + &m_roster, + SLOT(presenceReceived(const QXmppPresence&))); + Q_ASSERT(check); + + check = QObject::connect(this, SIGNAL(rosterIqReceived(const QXmppRosterIq&)), + &m_roster, SLOT(rosterIqReceived(const QXmppRosterIq&))); + Q_ASSERT(check); + + check = QObject::connect(this, SIGNAL(vCardIqReceived(const QXmppVCard&)), + &m_vCardManager, SLOT(vCardIqReceived(const QXmppVCard&))); + Q_ASSERT(check); +} + +QXmppStream::~QXmppStream() +{ + +} + +QXmppConfiguration& QXmppStream::getConfiguration() +{ + return m_client->getConfiguration(); +} + +void QXmppStream::connect() +{ + log(QString("Connecting to: %1:%2").arg(getConfiguration(). + getHost()).arg(getConfiguration().getPort())); + + m_socket.setProxy(getConfiguration().getNetworkProxy()); + m_socket.connectToHost(getConfiguration(). + getHost(), getConfiguration().getPort()); +} + +void QXmppStream::socketSslErrors(const QList<QSslError> & error) +{ + log(QString("SSL errors")); + m_socket.ignoreSslErrors(); + for(int i = 0; i< error.count(); ++i) + log(error.at(i).errorString()); +} + +void QXmppStream::socketHostFound() +{ + log(QString("Host found")); + emit hostFound(); +} + +void QXmppStream::socketConnected() +{ + flushDataBuffer(); + log(QString("Connected")); + emit connected(); + sendStartStream(); +} + +void QXmppStream::socketDisconnected() +{ + flushDataBuffer(); + log(QString("Disconnected")); + emit disconnected(); +} + +void QXmppStream::socketEncrypted() +{ + log(QString("Encrypted")); + sendStartStream(); +} + +void QXmppStream::socketError(QAbstractSocket::SocketError ee) +{ + m_socketError = ee; + emit error(QXmppClient::SocketError); + log(QString("Socket error: " + m_socket.errorString())); +} + +void QXmppStream::socketReadReady() +{ + QByteArray data = m_socket.readAll(); + log("SERVER [COULD BE PARTIAL DATA]:" + data.left(20)); + parser(data); +} + +void QXmppStream::sendNonSASLAuthQuery( const QString &to ) +{ + QXmppNonSASLAuthTypesRequestIq authQuery; + authQuery.setTo(to); + authQuery.setUsername(getConfiguration().getUser()); + + sendPacket(authQuery); +} + +void QXmppStream::parser(const QByteArray& data) +{ + QDomDocument doc; + QByteArray completeXml; + + m_dataBuffer = m_dataBuffer + data; + + if(hasStartStreamElement(m_dataBuffer)) + { + completeXml = m_dataBuffer + streamRootElementEnd; + } + else if(hasEndStreamElement(data)) + { + completeXml = streamRootElementStart + m_dataBuffer; + } + else + { + completeXml = streamRootElementStart + m_dataBuffer + streamRootElementEnd; + } + + if(doc.setContent(completeXml, true)) + { + log("SERVER:" + m_dataBuffer); + flushDataBuffer(); + + QDomElement nodeRecv = doc.documentElement().firstChildElement(); + + if(nodeRecv.isNull()) + { + QDomElement streamElement = doc.documentElement(); + if(m_streamId.isEmpty()) + m_streamId = streamElement.attribute("id"); + if(m_XMPPVersion.isEmpty()) + { + m_XMPPVersion = streamElement.attribute("version"); + if(m_XMPPVersion.isEmpty()) + { + // no version specified, signals XMPP Version < 1.0. + // switch to old auth mechanism + sendNonSASLAuthQuery(doc.documentElement().attribute("from")); + } + } + } + else + { + //TODO: Make a login error here. + } + + while(!nodeRecv.isNull()) + { + QString ns = nodeRecv.namespaceURI(); + if(ns == ns_stream && nodeRecv.tagName() == "features") + { + bool nonSaslAvailable = nodeRecv.firstChildElement("auth"). + namespaceURI() == ns_authFeature; + bool saslAvailable = nodeRecv.firstChildElement("mechanisms"). + namespaceURI() == ns_sasl; + bool useSasl = getConfiguration().getUseSASLAuthentication(); + + if(nodeRecv.firstChildElement("starttls").namespaceURI() + == ns_tls && !m_socket.isEncrypted()) + { + if(nodeRecv.firstChildElement("starttls"). + firstChildElement().tagName() == "required") + { + // TLS is must from the server side + sendStartTls(); + return; + } + else + { + // TLS is optional from the server side + switch(getConfiguration().getStreamSecurityMode()) + { + case QXmppConfiguration::TLSEnabled: + case QXmppConfiguration::TLSRequired: + sendStartTls(); + return; + case QXmppConfiguration::TLSDisabled: + break; + } + } + } + else if(!m_socket.isEncrypted()) // TLS not supported by server + { + if(getConfiguration().getStreamSecurityMode() == + QXmppConfiguration::TLSRequired) + { + // disconnect as the for client TLS is compulsory but + // not available on the server + // + log(QString("Disconnecting as TLS not available at the server")); + disconnect(); + return; + } + } + + if((saslAvailable && nonSaslAvailable && !useSasl) || + (!saslAvailable && nonSaslAvailable)) + { + sendNonSASLAuthQuery(doc.documentElement().attribute("from")); + } + else if(saslAvailable) + { + // SASL Authentication + QDomElement element = nodeRecv.firstChildElement("mechanisms"); + log(QString("Mechanisms:")); + QDomElement subElement = element.firstChildElement(); + QStringList mechanisms; + while(!subElement.isNull()) + { + if(subElement.tagName() == "mechanism") + { + log(subElement.text()); + mechanisms << subElement.text(); + } + subElement = subElement.nextSiblingElement(); + } + + switch(getConfiguration().getSASLAuthMechanism()) + { + case QXmppConfiguration::SASLPlain: + if(mechanisms.contains("PLAIN")) + { + sendAuthPlain(); + break; + } + case QXmppConfiguration::SASLDigestMD5: + if(mechanisms.contains("DIGEST-MD5")) + { + sendAuthDigestMD5(); + break; + } + default: + log(QString("Desired SASL Auth mechanism not available trying the available ones")); + if(mechanisms.contains("DIGEST-MD5")) + sendAuthDigestMD5(); + else if(mechanisms.contains("PLAIN")) + sendAuthPlain(); + else + { + log(QString("SASL Auth mechanism not available")); + disconnect(); + return; + } + break; + } + } + + if(nodeRecv.firstChildElement("bind"). + namespaceURI() == ns_bind) + { + sendBindIQ(); + } + + if(nodeRecv.firstChildElement("session"). + namespaceURI() == ns_session) + { + m_sessionAvaliable = true; + } + } + else if(ns == ns_tls) + { + if(nodeRecv.tagName() == "proceed") + { + log(QString("Starting encryption")); + m_socket.startClientEncryption(); + return; + } + } + else if(ns == ns_sasl) + { + if(nodeRecv.tagName() == "success") + { + log(QString("Authenticated")); + sendStartStream(); + } + else if(nodeRecv.tagName() == "challenge") + { + sendAuthDigestMD5Response(nodeRecv.text()); + } + } + else if(ns == ns_client) + { + + if(nodeRecv.tagName() == "iq") + { + QDomElement element = nodeRecv.firstChildElement(); + QString id = nodeRecv.attribute("id"); + QString to = nodeRecv.attribute("to"); + QString from = nodeRecv.attribute("from"); + QString type = nodeRecv.attribute("type"); + if(type.isEmpty()) + qWarning("QXmppStream: iq type can't be empty"); + QXmppIq iqPacket; // to emit + + + QDomElement elemen = nodeRecv.firstChildElement("error"); + QXmppStanza::Error error = parseStanzaError(elemen); + + + if(id == m_sessionId) + { + // get back add configuration whether to send + // roster and intial presence in beginning + // process SessionIq + + // xmpp connection made + emit xmppConnected(); + + sendRosterRequest(); + sendInitialPresence(); + + QXmppBind session(type); + session.setId(id); + session.setTo(to); + session.setFrom(from); + iqPacket = session; + } + else if(id == m_bindId) + { + QXmppBind bind(type); + QString jid = nodeRecv.firstChildElement("bind"). + firstChildElement("jid").text(); + bind.setResource(jidToResource(jid)); + bind.setJid(jidToBareJid(jid)); + bind.setId(id); + bind.setTo(to); + bind.setFrom(from); + processBindIq(bind); + iqPacket = bind; + } + else if(nodeRecv.firstChildElement("query"). + namespaceURI() == ns_roster) + { + QDomElement itemElement = nodeRecv. + firstChildElement("query"). + firstChildElement("item"); + QXmppRosterIq rosterIq(nodeRecv.attribute("type")); + rosterIq.setId(id); + rosterIq.setTo(to); + rosterIq.setFrom(from); + while(!itemElement.isNull()) + { + QXmppRosterIq::Item item; + item.setName(itemElement.attribute("name")); + item.setBareJid(itemElement.attribute("jid")); + item.setSubscriptionTypeFromStr( + itemElement.attribute("subscription")); + item.setSubscriptionStatus( + itemElement.attribute("ask")); + item.addGroup( + itemElement.firstChildElement("group").firstChildElement().text()); + rosterIq.addItem(item); + itemElement = itemElement.nextSiblingElement(); + } + processRosterIq(rosterIq); + iqPacket = rosterIq; + } + // extensions + // vCard - XEP-0054 + // http://xmpp.org/extensions/xep-0054.html + else if(nodeRecv.firstChildElement("vCard"). + namespaceURI() == ns_vcard) + { + QXmppVCard vcardIq; + vcardIq.parse(nodeRecv); + emit vCardIqReceived(vcardIq); + iqPacket = vcardIq; + } + // XEP-0030 info query + else if(nodeRecv.firstChildElement("query"). + namespaceURI() == ns_disco_info && + type == "get") + { + QXmppInformationRequestResult qxmppFeatures; + qxmppFeatures.setId(id); + qxmppFeatures.setTo(from); + qxmppFeatures.setFrom(to); + sendPacket(qxmppFeatures); + } + else if(id == m_nonSASLAuthId && type == "result") + { + // successful Non-SASL Authentication + log(QString("Authenticated (Non-SASL)")); + + emit xmppConnected(); + + sendRosterRequest(); + sendInitialPresence(); + } + else if(nodeRecv.firstChildElement("query"). + namespaceURI() == ns_auth) + { + if(type == "result") + { + bool digest = !nodeRecv.firstChildElement("query"). + firstChildElement("digest").isNull(); + bool plain = !nodeRecv.firstChildElement("query"). + firstChildElement("password").isNull(); + bool plainText = false; + + if(plain && digest) + { + if(getConfiguration().getNonSASLAuthMechanism() == + QXmppConfiguration::NonSASLDigest) + plainText = false; + else + plainText = true; + } + else if(plain) + plainText = true; + else if(digest) + plainText = false; + else + { + //TODO Login error + return; + } + sendNonSASLAuth(plainText); + } + } + else // didn't understant the iq...reply with error + { + if(type != "result") // but not incase of result iqs + { + QXmppIq iq(QXmppIq::Error); + iq.setId(id); + iq.setTo(from); + iq.setFrom(to); + QXmppStanza::Error error(QXmppStanza::Error::Cancel, + QXmppStanza::Error::FeatureNotImplemented); + iq.setError(error); + sendPacket(iq); + } + } + + iqPacket.setError(error); + processIq(iqPacket); + } + else if(nodeRecv.tagName() == "presence") + { + QXmppPresence presence; + presence.setTypeFromStr(nodeRecv.attribute("type")); + presence.setFrom(nodeRecv.attribute("from")); + presence.setTo(nodeRecv.attribute("to")); + + QString statusText = nodeRecv. + firstChildElement("status").text(); + QString show = nodeRecv. + firstChildElement("show").text(); + int priority = nodeRecv. + firstChildElement("priority").text().toInt(); + QXmppPresence::Status status; + status.setTypeFromStr(show); + status.setStatusText(statusText); + status.setPriority(priority); + presence.setStatus(status); + + QDomElement errorElement = nodeRecv. + firstChildElement("error"); + if(!errorElement.isNull()) + { + QXmppStanza::Error error = + parseStanzaError(errorElement); + presence.setError(error); + } + + processPresence(presence); + } + else if(nodeRecv.tagName() == "message") + { + QString from = nodeRecv.attribute("from"); + QString to = nodeRecv.attribute("to"); + QString type = nodeRecv.attribute("type"); + QString body = unescapeString( + nodeRecv.firstChildElement("body").text()); + QString sub = unescapeString( + nodeRecv.firstChildElement("subject").text()); + QString thread = nodeRecv.firstChildElement("thread").text(); + QXmppMessage message(from, to, body, thread); + message.setSubject(sub); + message.setTypeFromStr(type); + + QDomElement errorElement = nodeRecv. + firstChildElement("error"); + if(!errorElement.isNull()) + { + QXmppStanza::Error error = parseStanzaError(errorElement); + message.setError(error); + } + processMessage(message); + } + } + nodeRecv = nodeRecv.nextSiblingElement(); + } + } + else + { + //wait for complete packet + } +} + + +void QXmppStream::sendStartStream() +{ + QByteArray data = "<?xml version='1.0'?><stream:stream to='"; + data.append(getConfiguration().getDomain()); + data.append("' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"); + sendToServer(data); +} + +void QXmppStream::sendToServer(const QByteArray& packet) +{ + log("CLIENT: " + packet); + m_socket.write( packet ); +} + +bool QXmppStream::hasStartStreamElement(const QByteArray& data) +{ + QString str(data); + QRegExp regex("(<\\?xml.*\\?>)?\\s*<stream:stream.*>"); + regex.setMinimal(true); + if(str.contains(regex)) + return true; + else + return false; +} + +bool QXmppStream::hasEndStreamElement(const QByteArray& data) +{ + QString str(data); + QRegExp regex("</stream:stream>"); + regex.setMinimal(true); + if(str.contains(regex)) + return true; + else + return false; +} + +void QXmppStream::sendStartTls() +{ + sendToServer("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"); +} + +void QXmppStream::sendNonSASLAuth(bool plainText) +{ + QXmppNonSASLAuthIq authQuery; + authQuery.setUsername(getConfiguration().getUser()); + authQuery.setPassword(getConfiguration().getPasswd()); + authQuery.setResource(getConfiguration().getResource()); + authQuery.setStreamId(m_streamId); + authQuery.setUsePlainText(plainText); + m_nonSASLAuthId = authQuery.getId(); + sendPacket(authQuery); +} + +void QXmppStream::sendAuthPlain() +{ + QByteArray data = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>"; + QString userPass('\0' + getConfiguration().getUser() + + '\0' + getConfiguration().getPasswd()); + data += userPass.toUtf8().toBase64(); + data += "</auth>"; + sendToServer(data); +} + +void QXmppStream::sendAuthDigestMD5() +{ + QByteArray packet = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>"; + sendToServer(packet); +} + +// challenge is BASE64 encoded string +void QXmppStream::sendAuthDigestMD5Response(const QString& challenge) +{ + QByteArray ba = QByteArray::fromBase64(challenge.toUtf8()); + + //log(ba); + + ba.replace('"', QString("")); + QList<QByteArray> list = ba.split(','); + + QMap<QByteArray, QByteArray> map; + + QList<QByteArray> list2; + for(int i = 0; i < list.count(); ++i) + { + list2 = list.at(i).split('='); + if(list2.count() == 2) + map[list2.at(0).trimmed()] = list2.at(1).trimmed(); + else + log(QString("Invalid challenge send")); + } + + QByteArray user = getConfiguration().getUser().toUtf8(); + QByteArray passwd = getConfiguration().getPasswd().toUtf8(); + QByteArray domain = getConfiguration().getDomain().toUtf8(); + QByteArray realm; + if(map.contains("realm")) + realm = map["realm"]; + + QByteArray response; + + // First challenge + if(map.contains("nonce")) + { + QByteArray cnonce(32, 'm'); + for(int n = 0; n < cnonce.size(); ++n) + cnonce[n] = (char)(256.0*qrand()/(RAND_MAX+1.0)); + + QByteArray nc = "00000001"; + QByteArray digest_uri = "xmpp/" + domain; + + QByteArray a1 = user + ':' + realm + ':' + passwd; + QByteArray ha1 = QCryptographicHash::hash(a1, QCryptographicHash::Md5); + ha1 += ':' + map["nonce"] + ':' + cnonce; + + if(map.contains("authzid")) + ha1 += ':' + map["authzid"]; + + QByteArray A1(ha1); + QByteArray A2 = "AUTHENTICATE:" + digest_uri; + QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex(); + QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex(); + QByteArray KD = HA1 + ':' + map["nonce"] + ':' + nc + ':' + cnonce + ':' + + "auth" + ':' + HA2; + QByteArray Z = QCryptographicHash::hash(KD, QCryptographicHash::Md5).toHex(); + + response += "username=\"" + user + "\","; + + if(!realm.isEmpty()) + response += "realm=\"" + realm + "\","; + + response += "nonce=\"" + map["nonce"] + "\","; + response += "cnonce=\"" + cnonce + "\","; + response += "nc=" + nc + ","; + response += "qop=auth,"; + response += "digest-uri=\"" + digest_uri + "\","; + response += "response=" + Z + ","; + if(map.contains("authzid")) + response += "authzid=\"" + map["authzid"] + "\","; + response += "charset=utf-8"; + + log(response); + QByteArray packet = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + + response.toBase64() + "</response>"; + sendToServer(packet); + } + else if(map.contains("rspauth")) + { + sendToServer("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); + } + else + { + disconnect(); + log(QString("sendAuthDigestMD5Response: Invalid input")); + } +} + +void QXmppStream::sendBindIQ() +{ + QXmppBind bind(QXmppIq::Set); + bind.setResource(getConfiguration().getResource()); + m_bindId = bind.getId(); + sendPacket(bind); +} + +void QXmppStream::sendSessionIQ() +{ + QXmppSession session(QXmppIq::Set); + session.setTo(getConfiguration().getDomain()); + m_sessionId = session.getId(); + sendPacket(session); +} + +void QXmppStream::sendInitialPresence() +{ + if(m_client) + sendPacket(m_client->getClientPresence()); +} + +void QXmppStream::acceptSubscriptionRequest(const QString& from, bool accept) +{ + QXmppPresence presence; + presence.setTo(from); + if(accept) + presence.setType(QXmppPresence::Subscribed); + else + presence.setType(QXmppPresence::Unsubscribed); + + sendPacket(presence); +} + +void QXmppStream::sendSubscriptionRequest(const QString& to) +{ + if(to.isEmpty()) + return; + + QXmppPresence presence; + presence.setTo(to); + presence.setType(QXmppPresence::Subscribe); + sendPacket(presence); +} + +void QXmppStream::sendRosterRequest() +{ + QXmppRosterIq roster(QXmppIq::Get); + roster.setFrom(getConfiguration().getJid()); + m_rosterReqId = roster.getId(); + sendPacket(roster); +} + +void QXmppStream::disconnect() +{ + sendEndStream(); + m_socket.disconnectFromHost(); +} + +QXmppRoster& QXmppStream::getRoster() +{ + return m_roster; +} + +void QXmppStream::sendPacket(const QXmppPacket& packet) +{ + if(QXmppLogger::getLogger()->getLoggingType() != QXmppLogger::NONE) + { + QByteArray logPacket; + QXmlStreamWriter xmlStreamLog(&logPacket); + packet.toXml(&xmlStreamLog); + log("CLIENT: "+ logPacket); + } + + QXmlStreamWriter xmlStream(&m_socket); + packet.toXml(&xmlStream); +} + +void QXmppStream::processPresence(const QXmppPresence& presence) +{ + switch(presence.getType()) + { + case QXmppPresence::Error: + break; + case QXmppPresence::Available: + break; + case QXmppPresence::Unavailable: + break; + case QXmppPresence::Subscribe: + if(!presence.getFrom().isEmpty()) + { + if(getConfiguration().getAutoAcceptSubscriptions()) + acceptSubscriptionRequest(presence.getFrom()); + emit subscriptionRequestReceived(presence.getFrom()); + } + break; + case QXmppPresence::Unsubscribe: + break; + case QXmppPresence::Unsubscribed: + break; + case QXmppPresence::Probe: + break; + default: + break; + } + emit presenceReceived(presence); +} + +void QXmppStream::processMessage(const QXmppMessage& message) +{ + emit messageReceived(message); +} + +void QXmppStream::processIq(const QXmppIq& iq) +{ + emit iqReceived(iq); +} + +void QXmppStream::sendEndStream() +{ + sendToServer(streamRootElementEnd); +} + +void QXmppStream::processBindIq(const QXmppBind& bind) +{ + switch(bind.getType()) + { + case QXmppIq::Result: + if(!bind.getResource().isEmpty()) + getConfiguration().setResource(bind.getResource()); + if(m_sessionAvaliable) + sendSessionIQ(); + break; + default: + break; + } +} + +void QXmppStream::processRosterIq(const QXmppRosterIq& rosterIq) +{ + emit rosterIqReceived(rosterIq); + switch(rosterIq.getType()) + { + case QXmppIq::Set: + // when contact subscribes user...user sends 'subscribed' presence + // then after recieving following iq user requests contact for subscription + + // check thet "from" is newly added in the roster...and remove this ask thing...and do this for all items + if(rosterIq.getItems().at(0).getSubscriptionType() == + QXmppRosterIq::Item::From && rosterIq.getItems().at(0). + getSubscriptionStatus().isEmpty()) + sendSubscriptionRequest(rosterIq.getItems().at(0).getBareJid()); + break; + default: + break; + } +} + +QXmppStanza::Error QXmppStream::parseStanzaError(QDomElement & errorElement) +{ + QXmppStanza::Error error; + + if(errorElement.isNull()) + return error; + + QString type = 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(); + } + + error.setConditionFromStr(cond); + error.setTypeFromStr(type); + error.setText(text); + return error; +} + +QAbstractSocket::SocketError QXmppStream::getSocketError() +{ + return m_socketError; +} + +QXmppVCardManager& QXmppStream::getVCardManager() +{ + return m_vCardManager; +} + +void QXmppStream::flushDataBuffer() +{ + m_dataBuffer.clear(); +} |
