/* * Copyright (C) 2010 Bollor telecom * See AUTHORS file for a full list of contributors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */#include <QCryptographicHash>#include <QDebug>#include <QDomElement>#include "qxmpp/QXmppByteStreamIq.h"#include "qxmpp/QXmppConfiguration.h"#include "qxmpp/QXmppConstants.h"#include "qxmpp/QXmppDiscoveryIq.h"#include "qxmpp/QXmppSocks.h"#include "proxy.h"static QString streamHash(const QString &sid, const QString &initiatorJid, const QString &targetJid){ QCryptographicHash hash(QCryptographicHash::Sha1); QString str = sid + initiatorJid + targetJid; hash.addData(str.toAscii()); return hash.result().toHex();}Proxy::Proxy(const QHostAddress &host, quint16 port) : m_host(host), m_port(port), m_server(0){ m_server = new QXmppSocksServer(this); m_server->listen(m_host, m_port); connect(m_server, SIGNAL(newConnection(QTcpSocket*, const QString&, quint16)), this, SLOT(slotNewConnection(QTcpSocket*, const QString &, quint16)));}/** Open the connection to the chat server. * * @param jid * @param password */bool Proxy::open(const QString &jid, const QString &password, bool ignoreSslErrors){ QXmppConfiguration config; config.setResource("xmas-stocking"); /* get user and domain */ QStringList bits = jid.split("@"); config.setPasswd(password); config.setUser(bits[0]); config.setDomain(bits[1]); /* get the server */ config.setHost(config.getDomain()); /* set security parameters */ config.setAutoAcceptSubscriptions(false); config.setStreamSecurityMode(QXmppConfiguration::TLSRequired); config.setIgnoreSslErrors(ignoreSslErrors); /* connect to server */ connectToServer(config); return true;}bool Proxy::handleStreamElement(const QDomElement &element){ if (element.tagName() != "iq") return false; if (QXmppDiscoveryIq::isDiscoveryIq(element)) { QXmppDiscoveryIq discoIq; discoIq.parse(element); if (discoIq.type() == QXmppIq::Get && discoIq.queryType() == QXmppDiscoveryIq::InfoQuery) { qDebug() << "GOT" << element.tagName(); QXmppDiscoveryIq responseIq; responseIq.setTo(discoIq.from()); responseIq.setId(discoIq.id()); responseIq.setType(QXmppIq::Result); responseIq.setQueryType(discoIq.queryType()); QList<QXmppElement> items; QXmppElement identity; identity.setTagName("identity"); identity.setAttribute("category", "proxy"); identity.setAttribute("type", "bytestreams"); identity.setAttribute("name", "SOCKS5 Bytestreams"); items.append(identity); QStringList features = QStringList() << ns_disco_info << ns_disco_items << ns_bytestreams; foreach (const QString &feature, features) { QXmppElement item; item.setTagName("feature"); item.setAttribute("var", feature); items.append(item); } responseIq.setQueryItems(items); sendPacket(responseIq); return true; } } else if (QXmppByteStreamIq::isByteStreamIq(element)) { QXmppByteStreamIq bsIq; bsIq.parse(element); if (bsIq.type() == QXmppIq::Get) { QXmppByteStreamIq responseIq; responseIq.setType(QXmppIq::Result); responseIq.setTo(bsIq.from()); responseIq.setId(bsIq.id()); QList<QXmppByteStreamIq::StreamHost> streamHosts; QXmppByteStreamIq::StreamHost streamHost; streamHost.setJid(getConfiguration().getJid()); streamHost.setHost(m_host); streamHost.setPort(m_port); streamHosts.append(streamHost); responseIq.setStreamHosts(streamHosts); sendPacket(responseIq); return true; } else if (bsIq.type() == QXmppIq::Set) { QString hash = streamHash(bsIq.sid(), bsIq.from(), bsIq.activate()); if (m_pairs.contains(hash)) { qDebug() << "ACTIVATING" << hash; QTcpSocketPair pair = m_pairs.value(hash); connect(pair.first, SIGNAL(readyRead()), this, SLOT(slotReadyRead())); connect(pair.second, SIGNAL(readyRead()), this, SLOT(slotReadyRead())); QXmppIq responseIq; responseIq.setType(QXmppIq::Result); responseIq.setTo(bsIq.from()); responseIq.setId(bsIq.id()); sendPacket(responseIq); return true; } } } return false;}void Proxy::slotNewConnection(QTcpSocket *socket, const QString &hostName, quint16 port){ qDebug() << "new connection" << hostName; if (m_pairs.contains(hostName)) { m_pairs[hostName].second = socket; } else { m_pairs[hostName] = QTcpSocketPair(socket, 0); }}void Proxy::slotReadyRead(){ QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender()); if (!socket) return; foreach (const QTcpSocketPair &pair, m_pairs) { if (pair.first == socket) { pair.second->write(socket->readAll()); break; } else if (pair.second == socket) { pair.first->write(socket->readAll()); break; } }}