1 /*
2  * s5b.cpp - direct connection protocol via tcp
3  * Copyright (C) 2003  Justin Karneges
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  */
20 
21 #include "s5b.h"
22 
23 #include <QTimer>
24 #include <QPointer>
25 #include <QByteArray>
26 #include <stdlib.h>
27 #include <qca.h>
28 
29 #include "xmpp_xmlcommon.h"
30 #include "im.h"
31 #include "socks.h"
32 #include "safedelete.h"
33 
34 #ifdef Q_OS_WIN
35 # include <windows.h>
36 #else
37 # include <netinet/in.h>
38 #endif
39 
40 #define MAXSTREAMHOSTS 5
41 
42 static const char *S5B_NS = "http://jabber.org/protocol/bytestreams";
43 
44 namespace XMPP {
45 
makeKey(const QString & sid,const Jid & requester,const Jid & target)46 static QString makeKey(const QString &sid, const Jid &requester, const Jid &target)
47 {
48 #ifdef S5B_DEBUG
49 	qDebug("makeKey: sid=%s requester=%s target=%s %s", qPrintable(sid),
50 		   qPrintable(requester.full()), qPrintable(target.full()),
51 		   qPrintable(QCA::Hash("sha1").hashToString(QString(sid + requester.full() + target.full()).toUtf8())));
52 #endif
53 	QString str = sid + requester.full() + target.full();
54 	return QCA::Hash("sha1").hashToString(str.toUtf8());
55 }
56 
haveHost(const StreamHostList & list,const Jid & j)57 static bool haveHost(const StreamHostList &list, const Jid &j)
58 {
59 	for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) {
60 		if((*it).jid().compare(j))
61 			return true;
62 	}
63 	return false;
64 }
65 
66 class S5BManager::Item : public QObject
67 {
68 	Q_OBJECT
69 public:
70 	enum { Idle, Requester, Target, Active };
71 	enum { ErrRefused, ErrConnect, ErrWrongHost, ErrProxy };
72 	enum { Unknown, Fast, NotFast };
73 	S5BManager *m;
74 	int state;
75 	QString sid, key, out_key, out_id, in_id;
76 	Jid self, peer;
77 	StreamHostList in_hosts;
78 	JT_S5B *task, *proxy_task;
79 	SocksClient *client, *client_out;
80 	SocksUDP *client_udp, *client_out_udp;
81 	S5BConnector *conn, *proxy_conn;
82 	bool wantFast;
83 	StreamHost proxy;
84 	int targetMode; // requester sets this once it figures it out
85 	bool fast; // target sets this
86 	bool activated;
87 	bool lateProxy;
88 	bool connSuccess;
89 	bool localFailed, remoteFailed;
90 	bool allowIncoming;
91 	bool udp;
92 	int statusCode;
93 	Jid activatedStream;
94 
95 	Item(S5BManager *manager);
96 	~Item();
97 
98 	void resetConnection();
99 	void startRequester(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool udp);
100 	void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer,
101 					 const QString &_dstaddr, const StreamHostList &hosts,
102 					 const QString &iq_id, bool fast, bool udp);
103 	void handleFast(const StreamHostList &hosts, const QString &iq_id);
104 
105 	void doOutgoing();
106 	void doIncoming();
107 	void setIncomingClient(SocksClient *sc);
108 	void incomingActivate(const Jid &streamHost);
109 
110 signals:
111 	void accepted();
112 	void tryingHosts(const StreamHostList &list);
113 	void proxyConnect();
114 	void waitingForActivation();
115 	void connected();
116 	void error(int);
117 
118 private slots:
119 	void jt_finished();
120 	void conn_result(bool b);
121 	void proxy_result(bool b);
122 	void proxy_finished();
123 	void sc_readyRead();
124 	void sc_bytesWritten(qint64);
125 	void sc_error(int);
126 
127 private:
128 	void doConnectError();
129 	void tryActivation();
130 	void checkForActivation();
131 	void checkFailure();
132 	void finished();
133 };
134 
135 //----------------------------------------------------------------------------
136 // S5BDatagram
137 //----------------------------------------------------------------------------
S5BDatagram()138 S5BDatagram::S5BDatagram()
139 {
140 	_source = 0;
141 	_dest = 0;
142 }
143 
S5BDatagram(int source,int dest,const QByteArray & data)144 S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data)
145 {
146 	_source = source;
147 	_dest = dest;
148 	_buf = data;
149 }
150 
sourcePort() const151 int S5BDatagram::sourcePort() const
152 {
153 	return _source;
154 }
155 
destPort() const156 int S5BDatagram::destPort() const
157 {
158 	return _dest;
159 }
160 
data() const161 QByteArray S5BDatagram::data() const
162 {
163 	return _buf;
164 }
165 
166 //----------------------------------------------------------------------------
167 // S5BConnection
168 //----------------------------------------------------------------------------
169 class S5BConnection::Private
170 {
171 public:
172 	S5BManager *m;
173 	SocksClient *sc;
174 	SocksUDP *su;
175 	int state;
176 	Jid peer;
177 	QString sid;
178 	bool remote;
179 	bool switched;
180 	bool notifyRead, notifyClose;
181 	int id;
182 	S5BRequest req;
183 	Jid proxy;
184 	Mode mode;
185 	QList<S5BDatagram*> dglist;
186 };
187 
188 static int id_conn = 0;
189 static int num_conn = 0;
190 
S5BConnection(S5BManager * m,QObject * parent)191 S5BConnection::S5BConnection(S5BManager *m, QObject *parent)
192 	: BSConnection(parent)
193 {
194 	d = new Private;
195 	d->m = m;
196 	d->sc = 0;
197 	d->su = 0;
198 
199 	++num_conn;
200 	d->id = id_conn++;
201 #ifdef S5B_DEBUG
202 	qDebug("S5BConnection[%d]: constructing, count=%d, %p\n", d->id, num_conn, this);
203 #endif
204 
205 	resetConnection();
206 }
207 
~S5BConnection()208 S5BConnection::~S5BConnection()
209 {
210 	resetConnection(true);
211 
212 	--num_conn;
213 #ifdef S5B_DEBUG
214 	qDebug("S5BConnection[%d]: destructing, count=%d\n", d->id, num_conn);
215 #endif
216 
217 	delete d;
218 }
219 
resetConnection(bool clear)220 void S5BConnection::resetConnection(bool clear)
221 {
222 	d->m->con_unlink(this);
223 	if(clear && d->sc) {
224 		delete d->sc;
225 		d->sc = 0;
226 	}
227 	delete d->su;
228 	d->su = 0;
229 	if(clear) {
230 		while (!d->dglist.isEmpty()) {
231 			delete d->dglist.takeFirst();
232 		}
233 	}
234 	d->state = Idle;
235 	setOpenMode(QIODevice::NotOpen);
236 	d->peer = Jid();
237 	d->sid = QString();
238 	d->remote = false;
239 	d->switched = false;
240 	d->notifyRead = false;
241 	d->notifyClose = false;
242 }
243 
proxy() const244 Jid S5BConnection::proxy() const
245 {
246 	return d->proxy;
247 }
248 
setProxy(const Jid & proxy)249 void S5BConnection::setProxy(const Jid &proxy)
250 {
251 	d->proxy = proxy;
252 }
253 
connectToJid(const Jid & peer,const QString & sid,Mode m)254 void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m)
255 {
256 	resetConnection(true);
257 	if(!d->m->isAcceptableSID(peer, sid))
258 		return;
259 
260 	d->peer = peer;
261 	d->sid = sid;
262 	d->state = Requesting;
263 	d->mode = m;
264 #ifdef S5B_DEBUG
265 	qDebug("S5BConnection[%d]: connecting %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
266 #endif
267 	d->m->con_connect(this);
268 }
269 
accept()270 void S5BConnection::accept()
271 {
272 	if(d->state != WaitingForAccept)
273 		return;
274 
275 	d->state = Connecting;
276 #ifdef S5B_DEBUG
277 	qDebug("S5BConnection[%d]: accepting %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
278 #endif
279 	d->m->con_accept(this);
280 }
281 
close()282 void S5BConnection::close()
283 {
284 	if(d->state == Idle)
285 		return;
286 
287 	if(d->state == WaitingForAccept)
288 		d->m->con_reject(this);
289 	else if(d->state == Active)
290 		d->sc->close();
291 #ifdef S5B_DEBUG
292 	qDebug("S5BConnection[%d]: closing %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
293 #endif
294 	resetConnection();
295 }
296 
peer() const297 Jid S5BConnection::peer() const
298 {
299 	return d->peer;
300 }
301 
sid() const302 QString S5BConnection::sid() const
303 {
304 	return d->sid;
305 }
306 
manager() const307 BytestreamManager* S5BConnection::manager() const
308 {
309 	return d->m;
310 }
311 
isRemote() const312 bool S5BConnection::isRemote() const
313 {
314 	return d->remote;
315 }
316 
mode() const317 S5BConnection::Mode S5BConnection::mode() const
318 {
319 	return d->mode;
320 }
321 
state() const322 int S5BConnection::state() const
323 {
324 	return d->state;
325 }
326 
writeData(const char * data,qint64 maxSize)327 qint64 S5BConnection::writeData(const char *data, qint64 maxSize)
328 {
329 	if(d->state == Active && d->mode == Stream)
330 		return d->sc->write(data, maxSize);
331 	return 0;
332 }
333 
readData(char * data,qint64 maxSize)334 qint64 S5BConnection::readData(char *data, qint64 maxSize)
335 {
336 	if(d->sc)
337 		return d->sc->read(data, maxSize);
338 	else
339 		return 0;
340 }
341 
bytesAvailable() const342 qint64 S5BConnection::bytesAvailable() const
343 {
344 	if(d->sc)
345 		return d->sc->bytesAvailable();
346 	else
347 		return 0;
348 }
349 
bytesToWrite() const350 qint64 S5BConnection::bytesToWrite() const
351 {
352 	if(d->state == Active)
353 		return d->sc->bytesToWrite();
354 	else
355 		return 0;
356 }
357 
writeDatagram(const S5BDatagram & i)358 void S5BConnection::writeDatagram(const S5BDatagram &i)
359 {
360 	QByteArray buf;
361 	buf.resize(i.data().size() + 4);
362 	ushort ssp = htons(i.sourcePort());
363 	ushort sdp = htons(i.destPort());
364 	QByteArray data = i.data();
365 	memcpy(buf.data(), &ssp, 2);
366 	memcpy(buf.data() + 2, &sdp, 2);
367 	memcpy(buf.data() + 4, data.data(), data.size());
368 	sendUDP(buf);
369 }
370 
readDatagram()371 S5BDatagram S5BConnection::readDatagram()
372 {
373 	if(d->dglist.isEmpty())
374 		return S5BDatagram();
375 	S5BDatagram *i = d->dglist.takeFirst();
376 	S5BDatagram val = *i;
377 	delete i;
378 	return val;
379 }
380 
datagramsAvailable() const381 int S5BConnection::datagramsAvailable() const
382 {
383 	return d->dglist.count();
384 }
385 
man_waitForAccept(const S5BRequest & r)386 void S5BConnection::man_waitForAccept(const S5BRequest &r)
387 {
388 	d->state = WaitingForAccept;
389 	d->remote = true;
390 	d->req = r;
391 	d->peer = r.from;
392 	d->sid = r.sid;
393 	d->mode = r.udp ? Datagram : Stream;
394 }
395 
man_clientReady(SocksClient * sc,SocksUDP * sc_udp)396 void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp)
397 {
398 	d->sc = sc;
399 	connect(d->sc, SIGNAL(connectionClosed()), SLOT(sc_connectionClosed()));
400 	connect(d->sc, SIGNAL(delayedCloseFinished()), SLOT(sc_delayedCloseFinished()));
401 	connect(d->sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
402 	connect(d->sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64)));
403 	connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int)));
404 
405 	if(sc_udp) {
406 		d->su = sc_udp;
407 		connect(d->su, SIGNAL(packetReady(QByteArray)), SLOT(su_packetReady(QByteArray)));
408 	}
409 
410 	d->state = Active;
411 	setOpenMode(QIODevice::ReadWrite);
412 #ifdef S5B_DEBUG
413 	qDebug("S5BConnection[%d]: %s [%s] <<< success >>>\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
414 #endif
415 
416 	// bytes already in the stream?
417 	if(d->sc->bytesAvailable()) {
418 #ifdef S5B_DEBUG
419 		qDebug("Stream has %d bytes in it.\n", (int)d->sc->bytesAvailable());
420 #endif
421 		d->notifyRead = true;
422 	}
423 	// closed before it got here?
424 	if(!d->sc->isOpen()) {
425 #ifdef S5B_DEBUG
426 		qDebug("Stream was closed before S5B request finished?\n");
427 #endif
428 		d->notifyClose = true;
429 	}
430 	if(d->notifyRead || d->notifyClose)
431 		QTimer::singleShot(0, this, SLOT(doPending()));
432 	emit connected();
433 }
434 
doPending()435 void S5BConnection::doPending()
436 {
437 	if(d->notifyRead) {
438 		if(d->notifyClose)
439 			QTimer::singleShot(0, this, SLOT(doPending()));
440 		sc_readyRead();
441 	}
442 	else if(d->notifyClose)
443 		sc_connectionClosed();
444 }
445 
man_udpReady(const QByteArray & buf)446 void S5BConnection::man_udpReady(const QByteArray &buf)
447 {
448 	handleUDP(buf);
449 }
450 
man_failed(int x)451 void S5BConnection::man_failed(int x)
452 {
453 	resetConnection(true);
454 	if(x == S5BManager::Item::ErrRefused)
455 		setError(ErrRefused);
456 	if(x == S5BManager::Item::ErrConnect)
457 		setError(ErrConnect);
458 	if(x == S5BManager::Item::ErrWrongHost)
459 		setError(ErrConnect);
460 	if(x == S5BManager::Item::ErrProxy)
461 		setError(ErrProxy);
462 }
463 
sc_connectionClosed()464 void S5BConnection::sc_connectionClosed()
465 {
466 	// if we have a pending read notification, postpone close
467 	if(d->notifyRead) {
468 #ifdef S5B_DEBUG
469 		qDebug("closed while pending read\n");
470 #endif
471 		d->notifyClose = true;
472 		return;
473 	}
474 	d->notifyClose = false;
475 	resetConnection();
476 	connectionClosed();
477 }
478 
sc_delayedCloseFinished()479 void S5BConnection::sc_delayedCloseFinished()
480 {
481 	// echo
482 	emit delayedCloseFinished();
483 }
484 
sc_readyRead()485 void S5BConnection::sc_readyRead()
486 {
487 	if(d->mode == Datagram) {
488 		// throw the data away
489 		d->sc->readAll();
490 		return;
491 	}
492 
493 	d->notifyRead = false;
494 	// echo
495 	emit readyRead();
496 }
497 
sc_bytesWritten(qint64 x)498 void S5BConnection::sc_bytesWritten(qint64 x)
499 {
500 	// echo
501 	bytesWritten(x);
502 }
503 
sc_error(int)504 void S5BConnection::sc_error(int)
505 {
506 	resetConnection();
507 	setError(ErrSocket);
508 }
509 
su_packetReady(const QByteArray & buf)510 void S5BConnection::su_packetReady(const QByteArray &buf)
511 {
512 	handleUDP(buf);
513 }
514 
handleUDP(const QByteArray & buf)515 void S5BConnection::handleUDP(const QByteArray &buf)
516 {
517 	// must be at least 4 bytes, to accomodate virtual ports
518 	if(buf.size() < 4)
519 		return; // drop
520 
521 	ushort ssp, sdp;
522 	memcpy(&ssp, buf.data(), 2);
523 	memcpy(&sdp, buf.data() + 2, 2);
524 	int source = ntohs(ssp);
525 	int dest = ntohs(sdp);
526 	QByteArray data;
527 	data.resize(buf.size() - 4);
528 	memcpy(data.data(), buf.data() + 4, data.size());
529 	d->dglist.append(new S5BDatagram(source, dest, data));
530 
531 	datagramReady();
532 }
533 
sendUDP(const QByteArray & buf)534 void S5BConnection::sendUDP(const QByteArray &buf)
535 {
536 	if(d->su)
537 		d->su->write(buf);
538 	else
539 		d->m->con_sendUDP(this, buf);
540 }
541 
542 //----------------------------------------------------------------------------
543 // S5BManager
544 //----------------------------------------------------------------------------
545 class S5BManager::Entry
546 {
547 public:
Entry()548 	Entry()
549 	{
550 		i = 0;
551 		query = 0;
552 		udp_init = false;
553 	}
554 
~Entry()555 	~Entry()
556 	{
557 		delete query;
558 	}
559 
560 	S5BConnection *c;
561 	Item *i;
562 	QString sid;
563 	JT_S5B *query;
564 	StreamHost proxyInfo;
565 	QPointer<S5BServer> relatedServer;
566 
567 	bool udp_init;
568 	QHostAddress udp_addr;
569 	int udp_port;
570 };
571 
572 class S5BManager::Private
573 {
574 public:
575 	Client *client;
576 	S5BServer *serv;
577 	QList<Entry*> activeList;
578 	S5BConnectionList incomingConns;
579 	JT_PushS5B *ps;
580 };
581 
S5BManager(Client * parent)582 S5BManager::S5BManager(Client *parent)
583 	: BytestreamManager(parent)
584 {
585 	// S5B needs SHA1
586 	//if(!QCA::isSupported(QCA::CAP_SHA1))
587 	//	QCA::insertProvider(createProviderHash());
588 
589 	d = new Private;
590 	d->client = parent;
591 	d->serv = 0;
592 
593 	d->ps = new JT_PushS5B(d->client->rootTask());
594 	connect(d->ps, SIGNAL(incoming(S5BRequest)), SLOT(ps_incoming(S5BRequest)));
595 	connect(d->ps, SIGNAL(incomingUDPSuccess(Jid,QString)), SLOT(ps_incomingUDPSuccess(Jid,QString)));
596 	connect(d->ps, SIGNAL(incomingActivate(Jid,QString,Jid)), SLOT(ps_incomingActivate(Jid,QString,Jid)));
597 }
598 
~S5BManager()599 S5BManager::~S5BManager()
600 {
601 	setServer(0);
602 	while (!d->incomingConns.isEmpty()) {
603 		delete d->incomingConns.takeFirst();
604 	}
605 	delete d->ps;
606 	delete d;
607 }
608 
ns()609 const char* S5BManager::ns()
610 {
611 	return S5B_NS;
612 }
613 
client() const614 Client *S5BManager::client() const
615 {
616 	return d->client;
617 }
618 
server() const619 S5BServer *S5BManager::server() const
620 {
621 	return d->serv;
622 }
623 
setServer(S5BServer * serv)624 void S5BManager::setServer(S5BServer *serv)
625 {
626 	if(d->serv) {
627 		d->serv->unlink(this);
628 		d->serv = 0;
629 	}
630 
631 	if(serv) {
632 		d->serv = serv;
633 		d->serv->link(this);
634 	}
635 }
636 
createConnection()637 BSConnection *S5BManager::createConnection()
638 {
639 	return new S5BConnection(this);
640 }
641 
takeIncoming()642 S5BConnection *S5BManager::takeIncoming()
643 {
644 	if(d->incomingConns.isEmpty())
645 		return 0;
646 
647 	S5BConnection *c = d->incomingConns.takeFirst();
648 
649 	// move to activeList
650 	Entry *e = new Entry;
651 	e->c = c;
652 	e->sid = c->d->sid;
653 	d->activeList.append(e);
654 
655 	return c;
656 }
657 
ps_incoming(const S5BRequest & req)658 void S5BManager::ps_incoming(const S5BRequest &req)
659 {
660 #ifdef S5B_DEBUG
661 	qDebug("S5BManager: incoming from %s\n", qPrintable(req.from.full()));
662 #endif
663 
664 	bool ok = false;
665 	// ensure we don't already have an incoming connection from this peer+sid
666 	S5BConnection *c = findIncoming(req.from, req.sid);
667 	if(!c) {
668 		// do we have an active entry with this sid already?
669 		Entry *e = findEntryBySID(req.from, req.sid);
670 		if(e) {
671 			if(e->i) {
672 				// loopback
673 				if(req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) {
674 #ifdef S5B_DEBUG
675 					qDebug("ALLOWED: loopback\n");
676 #endif
677 					ok = true;
678 				}
679 				// allowed by 'fast mode'
680 				else if(e->i->state == Item::Requester && e->i->targetMode == Item::Unknown) {
681 #ifdef S5B_DEBUG
682 					qDebug("ALLOWED: fast-mode\n");
683 #endif
684 					e->i->handleFast(req.hosts, req.id);
685 					return;
686 				}
687 			}
688 		}
689 		else {
690 #ifdef S5B_DEBUG
691 			qDebug("ALLOWED: we don't have it\n");
692 #endif
693 			ok = true;
694 		}
695 	}
696 	if(!ok) {
697 		d->ps->respondError(req.from, req.id, Stanza::Error::NotAcceptable, "SID in use");
698 		return;
699 	}
700 
701 	// create an incoming connection
702 	c = new S5BConnection(this);
703 	c->man_waitForAccept(req);
704 	d->incomingConns.append(c);
705 	emit incomingReady();
706 }
707 
ps_incomingUDPSuccess(const Jid & from,const QString & key)708 void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key)
709 {
710 	Entry *e = findEntryByHash(key);
711 	if(e && e->i) {
712 		if(e->i->conn)
713 			e->i->conn->man_udpSuccess(from);
714 		else if(e->i->proxy_conn)
715 			e->i->proxy_conn->man_udpSuccess(from);
716 	}
717 }
718 
ps_incomingActivate(const Jid & from,const QString & sid,const Jid & streamHost)719 void S5BManager::ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost)
720 {
721 	Entry *e = findEntryBySID(from, sid);
722 	if(e && e->i)
723 		e->i->incomingActivate(streamHost);
724 }
725 
doSuccess(const Jid & peer,const QString & id,const Jid & streamHost)726 void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &streamHost)
727 {
728 	d->ps->respondSuccess(peer, id, streamHost);
729 }
730 
doError(const Jid & peer,const QString & id,Stanza::Error::ErrorCond cond,const QString & str)731 void S5BManager::doError(const Jid &peer, const QString &id,
732 						 Stanza::Error::ErrorCond cond, const QString &str)
733 {
734 	d->ps->respondError(peer, id, cond, str);
735 }
736 
doActivate(const Jid & peer,const QString & sid,const Jid & streamHost)737 void S5BManager::doActivate(const Jid &peer, const QString &sid, const Jid &streamHost)
738 {
739 	d->ps->sendActivate(peer, sid, streamHost);
740 }
741 
isAcceptableSID(const Jid & peer,const QString & sid) const742 bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const
743 {
744 	QString key = makeKey(sid, d->client->jid(), peer);
745 	QString key_out = makeKey(sid, peer, d->client->jid()); //not valid in muc via proxy
746 
747 	// if we have a server, then check through it
748 	if(d->serv) {
749 		if(findServerEntryByHash(key) || findServerEntryByHash(key_out))
750 			return false;
751 	}
752 	else {
753 		if(findEntryByHash(key) || findEntryByHash(key_out))
754 			return false;
755 	}
756 	return true;
757 }
758 
sidPrefix() const759 const char* S5BManager::sidPrefix() const
760 {
761 	return "s5b_";
762 }
763 
findIncoming(const Jid & from,const QString & sid) const764 S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const
765 {
766 	foreach(S5BConnection *c, d->incomingConns) {
767 		if(c->d->peer.compare(from) && c->d->sid == sid)
768 			return c;
769 	}
770 	return 0;
771 }
772 
findEntry(S5BConnection * c) const773 S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const
774 {
775 	foreach(Entry *e, d->activeList) {
776 		if(e->c == c)
777 			return e;
778 	}
779 	return 0;
780 }
781 
findEntry(Item * i) const782 S5BManager::Entry *S5BManager::findEntry(Item *i) const
783 {
784 	foreach(Entry *e, d->activeList) {
785 		if(e->i == i)
786 			return e;
787 	}
788 	return 0;
789 }
790 
findEntryByHash(const QString & key) const791 S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const
792 {
793 	foreach(Entry *e, d->activeList) {
794 		if(e->i && e->i->key == key)
795 			return e;
796 	}
797 	return 0;
798 }
799 
findEntryBySID(const Jid & peer,const QString & sid) const800 S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const
801 {
802 	foreach(Entry *e, d->activeList) {
803 		if(e->i && e->i->peer.compare(peer) && e->sid == sid)
804 			return e;
805 	}
806 	return 0;
807 }
808 
findServerEntryByHash(const QString & key) const809 S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const
810 {
811 	const QList<S5BManager*> &manList = d->serv->managerList();
812 	foreach(S5BManager *m, manList) {
813 		Entry *e = m->findEntryByHash(key);
814 		if(e)
815 			return e;
816 	}
817 	return 0;
818 }
819 
srv_ownsHash(const QString & key) const820 bool S5BManager::srv_ownsHash(const QString &key) const
821 {
822 	if(findEntryByHash(key))
823 		return true;
824 	return false;
825 }
826 
srv_incomingReady(SocksClient * sc,const QString & key)827 void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key)
828 {
829 	Entry *e = findEntryByHash(key);
830 	if(!e->i->allowIncoming) {
831 		sc->requestDeny();
832 		sc->deleteLater();
833 		return;
834 	}
835 	if(e->c->d->mode == S5BConnection::Datagram)
836 		sc->grantUDPAssociate("", 0);
837 	else
838 		sc->grantConnect();
839 	e->relatedServer = (S5BServer *)sender();
840 	e->i->setIncomingClient(sc);
841 }
842 
srv_incomingUDP(bool init,const QHostAddress & addr,int port,const QString & key,const QByteArray & data)843 void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data)
844 {
845 	Entry *e = findEntryByHash(key);
846 	if(e->c->d->mode != S5BConnection::Datagram)
847 		return; // this key isn't in udp mode?  drop!
848 
849 	if(init) {
850 		if(e->udp_init)
851 			return; // only init once
852 
853 		// lock on to this sender
854 		e->udp_addr = addr;
855 		e->udp_port = port;
856 		e->udp_init = true;
857 
858 		// reply that initialization was successful
859 		d->ps->sendUDPSuccess(e->c->d->peer, key);
860 		return;
861 	}
862 
863 	// not initialized yet?  something went wrong
864 	if(!e->udp_init)
865 		return;
866 
867 	// must come from same source as when initialized
868 	if(addr.toString() != e->udp_addr.toString() || port != e->udp_port)
869 		return;
870 
871 	e->c->man_udpReady(data);
872 }
873 
srv_unlink()874 void S5BManager::srv_unlink()
875 {
876 	d->serv = 0;
877 }
878 
con_connect(S5BConnection * c)879 void S5BManager::con_connect(S5BConnection *c)
880 {
881 	if(findEntry(c))
882 		return;
883 	Entry *e = new Entry;
884 	e->c = c;
885 	e->sid = c->d->sid;
886 	d->activeList.append(e);
887 
888 	if(c->d->proxy.isValid()) {
889 		queryProxy(e);
890 		return;
891 	}
892 	entryContinue(e);
893 }
894 
con_accept(S5BConnection * c)895 void S5BManager::con_accept(S5BConnection *c)
896 {
897 	Entry *e = findEntry(c);
898 	if(!e)
899 		return;
900 
901 	if(e->c->d->req.fast) {
902 		if(targetShouldOfferProxy(e)) {
903 			queryProxy(e);
904 			return;
905 		}
906 	}
907 	entryContinue(e);
908 }
909 
con_reject(S5BConnection * c)910 void S5BManager::con_reject(S5BConnection *c)
911 {
912 	d->ps->respondError(c->d->peer, c->d->req.id, Stanza::Error::NotAcceptable,
913 						"Not acceptable");
914 }
915 
con_unlink(S5BConnection * c)916 void S5BManager::con_unlink(S5BConnection *c)
917 {
918 	Entry *e = findEntry(c);
919 	if(!e)
920 		return;
921 
922 	// active incoming request?  cancel it
923 	if(e->i && e->i->conn)
924 		d->ps->respondError(e->i->peer, e->i->out_id,
925 							Stanza::Error::NotAcceptable, "Not acceptable");
926 	delete e->i;
927 	d->activeList.removeAll(e);
928 	delete e;
929 }
930 
con_sendUDP(S5BConnection * c,const QByteArray & buf)931 void S5BManager::con_sendUDP(S5BConnection *c, const QByteArray &buf)
932 {
933 	Entry *e = findEntry(c);
934 	if(!e)
935 		return;
936 	if(!e->udp_init)
937 		return;
938 
939 	if(e->relatedServer)
940 		e->relatedServer->writeUDP(e->udp_addr, e->udp_port, buf);
941 }
942 
item_accepted()943 void S5BManager::item_accepted()
944 {
945 	Item *i = (Item *)sender();
946 	Entry *e = findEntry(i);
947 
948 	emit e->c->accepted(); // signal
949 }
950 
item_tryingHosts(const StreamHostList & list)951 void S5BManager::item_tryingHosts(const StreamHostList &list)
952 {
953 	Item *i = (Item *)sender();
954 	Entry *e = findEntry(i);
955 
956 	e->c->tryingHosts(list); // signal
957 }
958 
item_proxyConnect()959 void S5BManager::item_proxyConnect()
960 {
961 	Item *i = (Item *)sender();
962 	Entry *e = findEntry(i);
963 
964 	e->c->proxyConnect(); // signal
965 }
966 
item_waitingForActivation()967 void S5BManager::item_waitingForActivation()
968 {
969 	Item *i = (Item *)sender();
970 	Entry *e = findEntry(i);
971 
972 	e->c->waitingForActivation(); // signal
973 }
974 
item_connected()975 void S5BManager::item_connected()
976 {
977 	Item *i = (Item *)sender();
978 	Entry *e = findEntry(i);
979 
980 	// grab the client
981 	SocksClient *client = i->client;
982 	i->client = 0;
983 	SocksUDP *client_udp = i->client_udp;
984 	i->client_udp = 0;
985 
986 	// give it to the connection
987 	e->c->man_clientReady(client, client_udp);
988 }
989 
item_error(int x)990 void S5BManager::item_error(int x)
991 {
992 	Item *i = (Item *)sender();
993 	Entry *e = findEntry(i);
994 
995 	e->c->man_failed(x);
996 }
997 
entryContinue(Entry * e)998 void S5BManager::entryContinue(Entry *e)
999 {
1000 	e->i = new Item(this);
1001 	e->i->proxy = e->proxyInfo;
1002 
1003 	connect(e->i, SIGNAL(accepted()), SLOT(item_accepted()));
1004 	connect(e->i, SIGNAL(tryingHosts(StreamHostList)), SLOT(item_tryingHosts(StreamHostList)));
1005 	connect(e->i, SIGNAL(proxyConnect()), SLOT(item_proxyConnect()));
1006 	connect(e->i, SIGNAL(waitingForActivation()), SLOT(item_waitingForActivation()));
1007 	connect(e->i, SIGNAL(connected()), SLOT(item_connected()));
1008 	connect(e->i, SIGNAL(error(int)), SLOT(item_error(int)));
1009 
1010 	if(e->c->isRemote()) {
1011 		const S5BRequest &req = e->c->d->req;
1012 		e->i->startTarget(e->sid, d->client->jid(), e->c->d->peer, req.dstaddr, req.hosts, req.id, req.fast, req.udp);
1013 	}
1014 	else {
1015 		e->i->startRequester(e->sid, d->client->jid(), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram ? true: false);
1016 		e->c->requesting(); // signal
1017 	}
1018 }
1019 
queryProxy(Entry * e)1020 void S5BManager::queryProxy(Entry *e)
1021 {
1022 	QPointer<QObject> self = this;
1023 	e->c->proxyQuery(); // signal
1024 	if(!self)
1025 		return;
1026 
1027 #ifdef S5B_DEBUG
1028 	qDebug("querying proxy: [%s]\n", qPrintable(e->c->d->proxy.full()));
1029 #endif
1030 	e->query = new JT_S5B(d->client->rootTask());
1031 	connect(e->query, SIGNAL(finished()), SLOT(query_finished()));
1032 	e->query->requestProxyInfo(e->c->d->proxy);
1033 	e->query->go(true);
1034 }
1035 
query_finished()1036 void S5BManager::query_finished()
1037 {
1038 	JT_S5B *query = (JT_S5B *)sender();
1039 	Entry* e = 0;
1040 	foreach(Entry* i, d->activeList) {
1041 		if(i->query == query) {
1042 			e = i;
1043 			break;
1044 		}
1045 	}
1046 	if(!e)
1047 		return;
1048 	e->query = 0;
1049 
1050 #ifdef S5B_DEBUG
1051 	qDebug("query finished: ");
1052 #endif
1053 	if(query->success()) {
1054 		e->proxyInfo = query->proxyInfo();
1055 #ifdef S5B_DEBUG
1056 		qDebug("host/ip=[%s] port=[%d]\n", qPrintable(e->proxyInfo.host()), e->proxyInfo.port());
1057 #endif
1058 	}
1059 	else {
1060 #ifdef S5B_DEBUG
1061 		qDebug("fail\n");
1062 #endif
1063 	}
1064 
1065 	QPointer<QObject> self = this;
1066 	e->c->proxyResult(query->success()); // signal
1067 	if(!self)
1068 		return;
1069 
1070 	entryContinue(e);
1071 }
1072 
targetShouldOfferProxy(Entry * e)1073 bool S5BManager::targetShouldOfferProxy(Entry *e)
1074 {
1075 	if(!e->c->d->proxy.isValid())
1076 		return false;
1077 
1078 	// if target, don't offer any proxy if the requester already did
1079 	const StreamHostList &hosts = e->c->d->req.hosts;
1080 	for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1081 		if((*it).isProxy())
1082 			return false;
1083 	}
1084 
1085 	// ensure we don't offer the same proxy as the requester
1086 	if(haveHost(hosts, e->c->d->proxy))
1087 		return false;
1088 
1089 	return true;
1090 }
1091 
1092 //----------------------------------------------------------------------------
1093 // S5BManager::Item
1094 //----------------------------------------------------------------------------
Item(S5BManager * manager)1095 S5BManager::Item::Item(S5BManager *manager) : QObject(0)
1096 {
1097 	m = manager;
1098 	task = 0;
1099 	proxy_task = 0;
1100 	conn = 0;
1101 	proxy_conn = 0;
1102 	client_udp = 0;
1103 	client = 0;
1104 	client_out_udp = 0;
1105 	client_out = 0;
1106 	resetConnection();
1107 }
1108 
~Item()1109 S5BManager::Item::~Item()
1110 {
1111 	resetConnection();
1112 }
1113 
resetConnection()1114 void S5BManager::Item::resetConnection()
1115 {
1116 	delete task;
1117 	task = 0;
1118 
1119 	delete proxy_task;
1120 	proxy_task = 0;
1121 
1122 	delete conn;
1123 	conn = 0;
1124 
1125 	delete proxy_conn;
1126 	proxy_conn = 0;
1127 
1128 	delete client_udp;
1129 	client_udp = 0;
1130 
1131 	delete client;
1132 	client = 0;
1133 
1134 	delete client_out_udp;
1135 	client_out_udp = 0;
1136 
1137 	delete client_out;
1138 	client_out = 0;
1139 
1140 	state = Idle;
1141 	wantFast = false;
1142 	targetMode = Unknown;
1143 	fast = false;
1144 	activated = false;
1145 	lateProxy = false;
1146 	connSuccess = false;
1147 	localFailed = false;
1148 	remoteFailed = false;
1149 	allowIncoming = false;
1150 	udp = false;
1151 }
1152 
startRequester(const QString & _sid,const Jid & _self,const Jid & _peer,bool fast,bool _udp)1153 void S5BManager::Item::startRequester(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool _udp)
1154 {
1155 	sid = _sid;
1156 	self = _self;
1157 	peer = _peer;
1158 	key = makeKey(sid, self, peer);
1159 	out_key = makeKey(sid, peer, self);
1160 	wantFast = fast;
1161 	udp = _udp;
1162 
1163 #ifdef S5B_DEBUG
1164 	qDebug("S5BManager::Item initiating request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), qPrintable(key));
1165 #endif
1166 	state = Requester;
1167 	doOutgoing();
1168 }
1169 
startTarget(const QString & _sid,const Jid & _self,const Jid & _peer,const QString & _dstaddr,const StreamHostList & hosts,const QString & iq_id,bool _fast,bool _udp)1170 void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self,
1171 								   const Jid &_peer, const QString &_dstaddr,
1172 								   const StreamHostList &hosts, const QString &iq_id,
1173 								   bool _fast, bool _udp)
1174 {
1175 	sid = _sid;
1176 	peer = _peer;
1177 	self = _self;
1178 	in_hosts = hosts;
1179 	in_id = iq_id;
1180 	fast = _fast;
1181 	key = makeKey(sid, self, peer);
1182 	out_key = _dstaddr.isEmpty() ? makeKey(sid, peer, self) : _dstaddr;
1183 	udp = _udp;
1184 
1185 #ifdef S5B_DEBUG
1186 	qDebug("S5BManager::Item incoming request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), qPrintable(key));
1187 #endif
1188 	state = Target;
1189 	if(fast)
1190 		doOutgoing();
1191 	doIncoming();
1192 }
1193 
handleFast(const StreamHostList & hosts,const QString & iq_id)1194 void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq_id)
1195 {
1196 	targetMode = Fast;
1197 
1198 	QPointer<QObject> self = this;
1199 	emit accepted();
1200 	if(!self)
1201 		return;
1202 
1203 	// if we already have a stream, then bounce this request
1204 	if(client) {
1205 		m->doError(peer, iq_id, Stanza::Error::NotAcceptable, "Not acceptable");
1206 	}
1207 	else {
1208 		in_hosts = hosts;
1209 		in_id = iq_id;
1210 		doIncoming();
1211 	}
1212 }
1213 
doOutgoing()1214 void S5BManager::Item::doOutgoing()
1215 {
1216 	StreamHostList hosts;
1217 	S5BServer *serv = m->server();
1218 	if(serv && serv->isActive() && !haveHost(in_hosts, self)) {
1219 		QStringList hostList = serv->hostList();
1220 		foreach (const QString & it, hostList) {
1221 			StreamHost h;
1222 			h.setJid(self);
1223 			h.setHost(it);
1224 			h.setPort(serv->port());
1225 			hosts += h;
1226 		}
1227 	}
1228 
1229 	// if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict)
1230 	if(proxy.jid().isValid())
1231 		hosts += proxy;
1232 
1233 	// if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode
1234 	if(state == Target && hosts.isEmpty()) {
1235 		fast = false;
1236 		return;
1237 	}
1238 
1239 	allowIncoming = true;
1240 
1241 	task = new JT_S5B(m->client()->rootTask());
1242 	connect(task, SIGNAL(finished()), SLOT(jt_finished()));
1243 	task->request(peer, sid, key, hosts, state == Requester ? wantFast : false, udp);
1244 	out_id = task->id();
1245 	task->go(true);
1246 }
1247 
doIncoming()1248 void S5BManager::Item::doIncoming()
1249 {
1250 	if(in_hosts.isEmpty()) {
1251 		doConnectError();
1252 		return;
1253 	}
1254 
1255 	StreamHostList list;
1256 	if(lateProxy) {
1257 		// take just the proxy streamhosts
1258 		foreach (const StreamHost& it, in_hosts) {
1259 			if (it.isProxy())
1260 				list += it;
1261 		}
1262 		lateProxy = false;
1263 	}
1264 	else {
1265 		// only try doing the late proxy trick if using fast mode AND we did not offer a proxy
1266 		if((state == Requester || (state == Target && fast)) && !proxy.jid().isValid()) {
1267 			// take just the non-proxy streamhosts
1268 			bool hasProxies = false;
1269 			foreach (const StreamHost& it, in_hosts) {
1270 				if (it.isProxy())
1271 					hasProxies = true;
1272 				else
1273 					list += it;
1274 			}
1275 			if(hasProxies) {
1276 				lateProxy = true;
1277 
1278 				// no regular streamhosts?  wait for remote error
1279 				if(list.isEmpty())
1280 					return;
1281 			}
1282 		}
1283 		else
1284 			list = in_hosts;
1285 	}
1286 
1287 	conn = new S5BConnector;
1288 	connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool)));
1289 
1290 	QPointer<QObject> self = this;
1291 	tryingHosts(list);
1292 	if(!self)
1293 		return;
1294 
1295 	conn->start(this->self, list, out_key, udp, lateProxy ? 10 : 30);
1296 }
1297 
setIncomingClient(SocksClient * sc)1298 void S5BManager::Item::setIncomingClient(SocksClient *sc)
1299 {
1300 #ifdef S5B_DEBUG
1301 	qDebug("S5BManager::Item: %s [%s] successful incoming connection\n", qPrintable(peer.full()), qPrintable(sid));
1302 #endif
1303 
1304 	connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1305 	connect(sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64)));
1306 	connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1307 
1308 	client = sc;
1309 	allowIncoming = false;
1310 }
1311 
incomingActivate(const Jid & streamHost)1312 void S5BManager::Item::incomingActivate(const Jid &streamHost)
1313 {
1314 	if(!activated) {
1315 		activatedStream = streamHost;
1316 		checkForActivation();
1317 	}
1318 }
1319 
jt_finished()1320 void S5BManager::Item::jt_finished()
1321 {
1322 	JT_S5B *j = task;
1323 	task = 0;
1324 
1325 #ifdef S5B_DEBUG
1326 	qDebug("jt_finished: state=%s, %s\n", state == Requester ? "requester" : "target", j->success() ? "ok" : "fail");
1327 #endif
1328 
1329 	if(state == Requester) {
1330 		if(targetMode == Unknown) {
1331 			targetMode = NotFast;
1332 			QPointer<QObject> self = this;
1333 			emit accepted();
1334 			if(!self)
1335 				return;
1336 		}
1337 	}
1338 
1339 	// if we've already reported successfully connecting to them, then this response doesn't matter
1340 	if(state == Requester && connSuccess) {
1341 		tryActivation();
1342 		return;
1343 	}
1344 
1345 	if(j->success()) {
1346 		// stop connecting out
1347 		if(conn || lateProxy) {
1348 			delete conn;
1349 			conn = 0;
1350 			doConnectError();
1351 		}
1352 
1353 		Jid streamHost = j->streamHostUsed();
1354 
1355 		// they connected to us?
1356 		if(streamHost.compare(self)) {
1357 			if(client) {
1358 				if(state == Requester) {
1359 					activatedStream = streamHost;
1360 					tryActivation();
1361 				}
1362 				else
1363 					checkForActivation();
1364 			}
1365 			else {
1366 #ifdef S5B_DEBUG
1367 				qDebug("S5BManager::Item %s claims to have connected to us, but we don't see this\n", qPrintable(peer.full()));
1368 #endif
1369 				resetConnection();
1370 				error(ErrWrongHost);
1371 			}
1372 		}
1373 		else if(streamHost.compare(proxy.jid())) {
1374 			// toss out any direct incoming, since it won't be used
1375 			delete client;
1376 			client = 0;
1377 			allowIncoming = false;
1378 
1379 #ifdef S5B_DEBUG
1380 			qDebug("attempting to connect to proxy\n");
1381 #endif
1382 			// connect to the proxy
1383 			proxy_conn = new S5BConnector;
1384 			connect(proxy_conn, SIGNAL(result(bool)), SLOT(proxy_result(bool)));
1385 			StreamHostList list;
1386 			list += proxy;
1387 
1388 			QPointer<QObject> self = this;
1389 			proxyConnect();
1390 			if(!self)
1391 				return;
1392 
1393 			proxy_conn->start(this->self, list, key, udp, 30);
1394 		}
1395 		else {
1396 #ifdef S5B_DEBUG
1397 			qDebug("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", qPrintable(peer.full()));
1398 #endif
1399 			resetConnection();
1400 			error(ErrWrongHost);
1401 		}
1402 	}
1403 	else {
1404 #ifdef S5B_DEBUG
1405 		qDebug("S5BManager::Item %s [%s] error\n", qPrintable(peer.full()), qPrintable(sid));
1406 #endif
1407 		remoteFailed = true;
1408 		statusCode = j->statusCode();
1409 
1410 		if(lateProxy) {
1411 			if(!conn)
1412 				doIncoming();
1413 		}
1414 		else {
1415 			// if connSuccess is true at this point, then we're a Target
1416 			if(connSuccess)
1417 				checkForActivation();
1418 			else
1419 				checkFailure();
1420 		}
1421 	}
1422 }
1423 
conn_result(bool b)1424 void S5BManager::Item::conn_result(bool b)
1425 {
1426 	if(b) {
1427 		SocksClient *sc = conn->takeClient();
1428 		SocksUDP *sc_udp = conn->takeUDP();
1429 		StreamHost h = conn->streamHostUsed();
1430 		delete conn;
1431 		conn = 0;
1432 		connSuccess = true;
1433 
1434 #ifdef S5B_DEBUG
1435 		qDebug("S5BManager::Item: %s [%s] successful outgoing connection\n",
1436 			   qPrintable(peer.full()), qPrintable(sid));
1437 #endif
1438 
1439 		connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1440 		connect(sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64)));
1441 		connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1442 
1443 		m->doSuccess(peer, in_id, h.jid());
1444 
1445 		// if the first batch works, don't try proxy
1446 		lateProxy = false;
1447 
1448 		// if requester, run with this one
1449 		if(state == Requester) {
1450 			// if we had an incoming one, toss it
1451 			delete client_udp;
1452 			client_udp = sc_udp;
1453 			delete client;
1454 			client = sc;
1455 			allowIncoming = false;
1456 			activatedStream = peer;
1457 			tryActivation();
1458 		}
1459 		else {
1460 			client_out_udp = sc_udp;
1461 			client_out = sc;
1462 			checkForActivation();
1463 		}
1464 	}
1465 	else {
1466 		delete conn;
1467 		conn = 0;
1468 
1469 		// if we delayed the proxies for later, try now
1470 		if(lateProxy) {
1471 			if(remoteFailed)
1472 				doIncoming();
1473 		}
1474 		else
1475 			doConnectError();
1476 	}
1477 }
1478 
proxy_result(bool b)1479 void S5BManager::Item::proxy_result(bool b)
1480 {
1481 #ifdef S5B_DEBUG
1482 	qDebug("proxy_result: %s\n", b ? "ok" : "fail");
1483 #endif
1484 	if(b) {
1485 		SocksClient *sc = proxy_conn->takeClient();
1486 		SocksUDP *sc_udp = proxy_conn->takeUDP();
1487 		delete proxy_conn;
1488 		proxy_conn = 0;
1489 
1490 		connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1491 		connect(sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64)));
1492 		connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1493 
1494 		client = sc;
1495 		client_udp = sc_udp;
1496 
1497 		// activate
1498 #ifdef S5B_DEBUG
1499 		qDebug("activating proxy stream\n");
1500 #endif
1501 		proxy_task = new JT_S5B(m->client()->rootTask());
1502 		connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished()));
1503 		proxy_task->requestActivation(proxy.jid(), sid, peer);
1504 		proxy_task->go(true);
1505 	}
1506 	else {
1507 		delete proxy_conn;
1508 		proxy_conn = 0;
1509 		resetConnection();
1510 		error(ErrProxy);
1511 	}
1512 }
1513 
proxy_finished()1514 void S5BManager::Item::proxy_finished()
1515 {
1516 	JT_S5B *j = proxy_task;
1517 	proxy_task = 0;
1518 
1519 	if(j->success()) {
1520 #ifdef S5B_DEBUG
1521 		qDebug("proxy stream activated\n");
1522 #endif
1523 		if(state == Requester) {
1524 			activatedStream = proxy.jid();
1525 			tryActivation();
1526 		}
1527 		else
1528 			checkForActivation();
1529 	}
1530 	else {
1531 		resetConnection();
1532 		error(ErrProxy);
1533 	}
1534 }
1535 
sc_readyRead()1536 void S5BManager::Item::sc_readyRead()
1537 {
1538 #ifdef S5B_DEBUG
1539 	qDebug("sc_readyRead\n");
1540 #endif
1541 	// only targets check for activation, and only should do it if there is no pending outgoing iq-set
1542 	if(state == Target && !task && !proxy_task)
1543 		checkForActivation();
1544 }
1545 
sc_bytesWritten(qint64)1546 void S5BManager::Item::sc_bytesWritten(qint64)
1547 {
1548 #ifdef S5B_DEBUG
1549 	qDebug("sc_bytesWritten\n");
1550 #endif
1551 	// this should only happen to the requester, and should always be 1 byte (the '\r' sent earlier)
1552 	finished();
1553 }
1554 
sc_error(int)1555 void S5BManager::Item::sc_error(int)
1556 {
1557 #ifdef S5B_DEBUG
1558 	qDebug("sc_error\n");
1559 #endif
1560 	resetConnection();
1561 	error(ErrConnect);
1562 }
1563 
doConnectError()1564 void S5BManager::Item::doConnectError()
1565 {
1566 	localFailed = true;
1567 	m->doError(peer, in_id, Stanza::Error::RemoteServerNotFound,
1568 			   "Could not connect to given hosts");
1569 	checkFailure();
1570 }
1571 
tryActivation()1572 void S5BManager::Item::tryActivation()
1573 {
1574 #ifdef S5B_DEBUG
1575 	qDebug("tryActivation\n");
1576 #endif
1577 	if(activated) {
1578 #ifdef S5B_DEBUG
1579 		qDebug("already activated !?\n");
1580 #endif
1581 		return;
1582 	}
1583 
1584 	if(targetMode == NotFast) {
1585 #ifdef S5B_DEBUG
1586 		qDebug("tryActivation: NotFast\n");
1587 #endif
1588 		// nothing to activate, we're done
1589 		finished();
1590 	}
1591 	else if(targetMode == Fast) {
1592 		// with fast mode, we don't wait for the iq reply, so delete the task (if any)
1593 		delete task;
1594 		task = 0;
1595 
1596 		activated = true;
1597 
1598 		// if udp, activate using special stanza
1599 		if(udp) {
1600 			m->doActivate(peer, sid, activatedStream);
1601 		}
1602 		else {
1603 #ifdef S5B_DEBUG
1604 			qDebug("sending extra CR\n");
1605 #endif
1606 			// must send [CR] to activate target streamhost
1607 			client->write("\r", 1);
1608 		}
1609 	}
1610 }
1611 
checkForActivation()1612 void S5BManager::Item::checkForActivation()
1613 {
1614 	QList<SocksClient*> clientList;
1615 	if(client)
1616 		clientList.append(client);
1617 	if(client_out)
1618 		clientList.append(client_out);
1619 	foreach(SocksClient *sc, clientList) {
1620 #ifdef S5B_DEBUG
1621 		qDebug("checking for activation\n");
1622 #endif
1623 		if(fast) {
1624 			bool ok = false;
1625 			if(udp) {
1626 				if((sc == client_out && activatedStream.compare(self)) || (sc == client && !activatedStream.compare(self))) {
1627 					clientList.removeAll(sc);
1628 					ok = true;
1629 				}
1630 			}
1631 			else {
1632 #ifdef S5B_DEBUG
1633 				qDebug("need CR\n");
1634 #endif
1635 				if(sc->bytesAvailable() >= 1) {
1636 					clientList.removeAll(sc);
1637 					char c;
1638 					if(!sc->getChar(&c) || c != '\r') {
1639 						delete sc; // FIXME breaks S5BManager::Item destructor?
1640 						return;
1641 					}
1642 					ok = true;
1643 				}
1644 			}
1645 
1646 			if(ok) {
1647 				SocksUDP *sc_udp = 0;
1648 				if(sc == client) {
1649 					delete client_out_udp;
1650 					client_out_udp = 0;
1651 					sc_udp = client_udp;
1652 				}
1653 				else if(sc == client_out) {
1654 					delete client_udp;
1655 					client_udp = 0;
1656 					sc_udp = client_out_udp;
1657 				}
1658 
1659 				sc->disconnect(this);
1660 				while (!clientList.isEmpty()) {
1661 					delete clientList.takeFirst();
1662 				}
1663 				client = sc;
1664 				client_out = 0;
1665 				client_udp = sc_udp;
1666 				activated = true;
1667 #ifdef S5B_DEBUG
1668 				qDebug("activation success\n");
1669 #endif
1670 				break;
1671 			}
1672 		}
1673 		else {
1674 #ifdef S5B_DEBUG
1675 			qDebug("not fast mode, no need to wait for anything\n");
1676 #endif
1677 			clientList.removeAll(sc);
1678 			sc->disconnect(this);
1679 			while (!clientList.isEmpty()) {
1680 				delete clientList.takeFirst();
1681 			}
1682 			client = sc;
1683 			client_out = 0;
1684 			activated = true;
1685 			break;
1686 		}
1687 	}
1688 
1689 	if(activated) {
1690 		finished();
1691 	}
1692 	else {
1693 		// only emit waitingForActivation if there is nothing left to do
1694 		if((connSuccess || localFailed) && !proxy_task && !proxy_conn)
1695 			waitingForActivation();
1696 	}
1697 }
1698 
checkFailure()1699 void S5BManager::Item::checkFailure()
1700 {
1701 	bool failed = false;
1702 	if(state == Requester) {
1703 		if(remoteFailed) {
1704 			if((localFailed && targetMode == Fast) || targetMode == NotFast)
1705 				failed = true;
1706 		}
1707 	}
1708 	else {
1709 		if(localFailed) {
1710 			if((remoteFailed && fast) || !fast)
1711 				failed = true;
1712 		}
1713 	}
1714 
1715 	if(failed) {
1716 		if(state == Requester) {
1717 			resetConnection();
1718 			if(statusCode == 404)
1719 				error(ErrConnect);
1720 			else
1721 				error(ErrRefused);
1722 		}
1723 		else {
1724 			resetConnection();
1725 			error(ErrConnect);
1726 		}
1727 	}
1728 }
1729 
finished()1730 void S5BManager::Item::finished()
1731 {
1732 	client->disconnect(this);
1733 	state = Active;
1734 #ifdef S5B_DEBUG
1735 	qDebug("S5BManager::Item %s [%s] linked successfully\n", qPrintable(peer.full()), qPrintable(sid));
1736 #endif
1737 	emit connected();
1738 }
1739 
1740 //----------------------------------------------------------------------------
1741 // S5BConnector
1742 //----------------------------------------------------------------------------
1743 class S5BConnector::Item : public QObject
1744 {
1745 	Q_OBJECT
1746 public:
1747 	SocksClient *client;
1748 	SocksUDP *client_udp;
1749 	StreamHost host;
1750 	QString key;
1751 	bool udp;
1752 	int udp_tries;
1753 	QTimer t;
1754 	Jid jid;
1755 
Item(const Jid & self,const StreamHost & _host,const QString & _key,bool _udp)1756 	Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) : QObject(0)
1757 	{
1758 		jid = self;
1759 		host = _host;
1760 		key = _key;
1761 		udp = _udp;
1762 		client = new SocksClient;
1763 		client_udp = 0;
1764 		connect(client, SIGNAL(connected()), SLOT(sc_connected()));
1765 		connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1766 		connect(&t, SIGNAL(timeout()), SLOT(trySendUDP()));
1767 	}
1768 
~Item()1769 	~Item()
1770 	{
1771 		cleanup();
1772 	}
1773 
start()1774 	void start()
1775 	{
1776 		client->connectToHost(host.host(), host.port(), key, 0, udp);
1777 	}
1778 
udpSuccess()1779 	void udpSuccess()
1780 	{
1781 		t.stop();
1782 		client_udp->change(key, 0); // flip over to the data port
1783 		success();
1784 	}
1785 
1786 signals:
1787 	void result(bool);
1788 
1789 private slots:
sc_connected()1790 	void sc_connected()
1791 	{
1792 		// if udp, need to send init packet before we are good
1793 		if(udp) {
1794 			// port 1 is init
1795 			client_udp = client->createUDP(key, 1, client->peerAddress(), client->peerPort());
1796 			udp_tries = 0;
1797 			t.start(5000);
1798 			trySendUDP();
1799 			return;
1800 		}
1801 
1802 		success();
1803 	}
1804 
sc_error(int)1805 	void sc_error(int)
1806 	{
1807 #ifdef S5B_DEBUG
1808 		qDebug("S5BConnector[%s]: error\n", qPrintable(host.host()));
1809 #endif
1810 		cleanup();
1811 		result(false);
1812 	}
1813 
trySendUDP()1814 	void trySendUDP()
1815 	{
1816 		if(udp_tries == 5) {
1817 			t.stop();
1818 			cleanup();
1819 			result(false);
1820 			return;
1821 		}
1822 
1823 		// send initialization with our JID
1824 		QByteArray a(jid.full().toUtf8());
1825 		client_udp->write(a);
1826 		++udp_tries;
1827 	}
1828 
1829 private:
cleanup()1830 	void cleanup()
1831 	{
1832 		delete client_udp;
1833 		client_udp = 0;
1834 		delete client;
1835 		client = 0;
1836 	}
1837 
success()1838 	void success()
1839 	{
1840 #ifdef S5B_DEBUG
1841 		qDebug("S5BConnector[%s]: success\n", qPrintable(host.host()));
1842 #endif
1843 		client->disconnect(this);
1844 		result(true);
1845 	}
1846 };
1847 
1848 class S5BConnector::Private
1849 {
1850 public:
1851 	SocksClient *active;
1852 	SocksUDP *active_udp;
1853 	QList<Item*> itemList;
1854 	QString key;
1855 	StreamHost activeHost;
1856 	QTimer t;
1857 };
1858 
S5BConnector(QObject * parent)1859 S5BConnector::S5BConnector(QObject *parent)
1860 :QObject(parent)
1861 {
1862 	d = new Private;
1863 	d->active = 0;
1864 	d->active_udp = 0;
1865 	connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
1866 }
1867 
~S5BConnector()1868 S5BConnector::~S5BConnector()
1869 {
1870 	resetConnection();
1871 	delete d;
1872 }
1873 
resetConnection()1874 void S5BConnector::resetConnection()
1875 {
1876 	d->t.stop();
1877 	delete d->active_udp;
1878 	d->active_udp = 0;
1879 	delete d->active;
1880 	d->active = 0;
1881 	while (!d->itemList.empty()) {
1882 		delete d->itemList.takeFirst();
1883 	}
1884 }
1885 
start(const Jid & self,const StreamHostList & hosts,const QString & key,bool udp,int timeout)1886 void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout)
1887 {
1888 	resetConnection();
1889 
1890 #ifdef S5B_DEBUG
1891 	qDebug("S5BConnector: starting [%p]!\n", this);
1892 #endif
1893 	for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1894 		Item *i = new Item(self, *it, key, udp);
1895 		connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
1896 		d->itemList.append(i);
1897 		i->start();
1898 	}
1899 	d->t.start(timeout * 1000);
1900 }
1901 
takeClient()1902 SocksClient *S5BConnector::takeClient()
1903 {
1904 	SocksClient *c = d->active;
1905 	d->active = 0;
1906 	return c;
1907 }
1908 
takeUDP()1909 SocksUDP *S5BConnector::takeUDP()
1910 {
1911 	SocksUDP *c = d->active_udp;
1912 	d->active_udp = 0;
1913 	return c;
1914 }
1915 
streamHostUsed() const1916 StreamHost S5BConnector::streamHostUsed() const
1917 {
1918 	return d->activeHost;
1919 }
1920 
item_result(bool b)1921 void S5BConnector::item_result(bool b)
1922 {
1923 	Item *i = (Item *)sender();
1924 	if(b) {
1925 		d->active = i->client;
1926 		i->client = 0;
1927 		d->active_udp = i->client_udp;
1928 		i->client_udp = 0;
1929 		d->activeHost = i->host;
1930 		while (!d->itemList.isEmpty()) {
1931 			delete d->itemList.takeFirst();
1932 		}
1933 		d->t.stop();
1934 #ifdef S5B_DEBUG
1935 		qDebug("S5BConnector: complete! [%p]\n", this);
1936 #endif
1937 		emit result(true);
1938 	}
1939 	else {
1940 		d->itemList.removeAll(i);
1941 		delete i;
1942 		if(d->itemList.isEmpty()) {
1943 			d->t.stop();
1944 #ifdef S5B_DEBUG
1945 			qDebug("S5BConnector: failed! [%p]\n", this);
1946 #endif
1947 			emit result(false);
1948 		}
1949 	}
1950 }
1951 
t_timeout()1952 void S5BConnector::t_timeout()
1953 {
1954 	resetConnection();
1955 #ifdef S5B_DEBUG
1956 	qDebug("S5BConnector: failed! (timeout)\n");
1957 #endif
1958 	result(false);
1959 }
1960 
man_udpSuccess(const Jid & streamHost)1961 void S5BConnector::man_udpSuccess(const Jid &streamHost)
1962 {
1963 	// was anyone sending to this streamhost?
1964 	foreach(Item *i, d->itemList) {
1965 		if(i->host.jid().compare(streamHost) && i->client_udp) {
1966 			i->udpSuccess();
1967 			return;
1968 		}
1969 	}
1970 }
1971 
1972 //----------------------------------------------------------------------------
1973 // S5BServer
1974 //----------------------------------------------------------------------------
1975 class S5BServer::Item : public QObject
1976 {
1977 	Q_OBJECT
1978 public:
1979 	SocksClient *client;
1980 	QString host;
1981 	QTimer expire;
1982 
Item(SocksClient * c)1983 	Item(SocksClient *c) : QObject(0)
1984 	{
1985 		client = c;
1986 		connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
1987 		connect(client, SIGNAL(incomingConnectRequest(QString,int)), SLOT(sc_incomingConnectRequest(QString,int)));
1988 		connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1989 
1990 		connect(&expire, SIGNAL(timeout()), SLOT(doError()));
1991 		resetExpiration();
1992 	}
1993 
~Item()1994 	~Item()
1995 	{
1996 		delete client;
1997 	}
1998 
resetExpiration()1999 	void resetExpiration()
2000 	{
2001 		expire.start(30000);
2002 	}
2003 
2004 signals:
2005 	void result(bool);
2006 
2007 private slots:
doError()2008 	void doError()
2009 	{
2010 		expire.stop();
2011 		delete client;
2012 		client = 0;
2013 		result(false);
2014 	}
2015 
sc_incomingMethods(int m)2016 	void sc_incomingMethods(int m)
2017 	{
2018 		if(m & SocksClient::AuthNone)
2019 			client->chooseMethod(SocksClient::AuthNone);
2020 		else
2021 			doError();
2022 	}
2023 
sc_incomingConnectRequest(const QString & _host,int port)2024 	void sc_incomingConnectRequest(const QString &_host, int port)
2025 	{
2026 		if(port == 0) {
2027 			host = _host;
2028 			client->disconnect(this);
2029 			emit result(true);
2030 		}
2031 		else
2032 			doError();
2033 	}
2034 
sc_error(int)2035 	void sc_error(int)
2036 	{
2037 		doError();
2038 	}
2039 };
2040 
2041 class S5BServer::Private
2042 {
2043 public:
2044 	SocksServer serv;
2045 	QStringList hostList;
2046 	QList<S5BManager*> manList;
2047 	QList<Item*> itemList;
2048 };
2049 
S5BServer(QObject * parent)2050 S5BServer::S5BServer(QObject *parent)
2051 :QObject(parent)
2052 {
2053 	d = new Private;
2054 	connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
2055 	connect(&d->serv, SIGNAL(incomingUDP(QString,int,QHostAddress,int,QByteArray)), SLOT(ss_incomingUDP(QString,int,QHostAddress,int,QByteArray)));
2056 }
2057 
~S5BServer()2058 S5BServer::~S5BServer()
2059 {
2060 	unlinkAll();
2061 	delete d;
2062 }
2063 
isActive() const2064 bool S5BServer::isActive() const
2065 {
2066 	return d->serv.isActive();
2067 }
2068 
start(int port)2069 bool S5BServer::start(int port)
2070 {
2071 	d->serv.stop();
2072 	//return d->serv.listen(port, true);
2073 	return d->serv.listen(port);
2074 }
2075 
stop()2076 void S5BServer::stop()
2077 {
2078 	d->serv.stop();
2079 }
2080 
setHostList(const QStringList & list)2081 void S5BServer::setHostList(const QStringList &list)
2082 {
2083 	d->hostList = list;
2084 }
2085 
hostList() const2086 QStringList S5BServer::hostList() const
2087 {
2088 	return d->hostList;
2089 }
2090 
port() const2091 int S5BServer::port() const
2092 {
2093 	return d->serv.port();
2094 }
2095 
ss_incomingReady()2096 void S5BServer::ss_incomingReady()
2097 {
2098 	Item *i = new Item(d->serv.takeIncoming());
2099 #ifdef S5B_DEBUG
2100 	qDebug("S5BServer: incoming connection from %s:%d\n", qPrintable(i->client->peerAddress().toString()), i->client->peerPort());
2101 #endif
2102 	connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
2103 	d->itemList.append(i);
2104 }
2105 
ss_incomingUDP(const QString & host,int port,const QHostAddress & addr,int sourcePort,const QByteArray & data)2106 void S5BServer::ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data)
2107 {
2108 	if(port != 0 && port != 1)
2109 		return;
2110 
2111 	foreach(S5BManager* m, d->manList) {
2112 		if(m->srv_ownsHash(host)) {
2113 			m->srv_incomingUDP(port == 1 ? true : false, addr, sourcePort, host, data);
2114 			return;
2115 		}
2116 	}
2117 }
2118 
item_result(bool b)2119 void S5BServer::item_result(bool b)
2120 {
2121 	Item *i = (Item *)sender();
2122 #ifdef S5B_DEBUG
2123 	qDebug("S5BServer item result: %d\n", b);
2124 #endif
2125 	if(!b) {
2126 		d->itemList.removeAll(i);
2127 		delete i;
2128 		return;
2129 	}
2130 
2131 	SocksClient *c = i->client;
2132 	i->client = 0;
2133 	QString key = i->host;
2134 	d->itemList.removeAll(i);
2135 	delete i;
2136 
2137 	// find the appropriate manager for this incoming connection
2138 	foreach(S5BManager *m, d->manList) {
2139 		if(m->srv_ownsHash(key)) {
2140 			m->srv_incomingReady(c, key);
2141 			return;
2142 		}
2143 	}
2144 
2145 #ifdef S5B_DEBUG
2146 	qDebug("S5BServer item result: unknown hash [%s]\n", qPrintable(key));
2147 #endif
2148 
2149 	// throw it away
2150 	delete c;
2151 }
2152 
link(S5BManager * m)2153 void S5BServer::link(S5BManager *m)
2154 {
2155 	d->manList.append(m);
2156 }
2157 
unlink(S5BManager * m)2158 void S5BServer::unlink(S5BManager *m)
2159 {
2160 	d->manList.removeAll(m);
2161 }
2162 
unlinkAll()2163 void S5BServer::unlinkAll()
2164 {
2165 	foreach(S5BManager *m, d->manList) {
2166 		m->srv_unlink();
2167 	}
2168 	d->manList.clear();
2169 }
2170 
managerList() const2171 const QList<S5BManager*> & S5BServer::managerList() const
2172 {
2173 	return d->manList;
2174 }
2175 
writeUDP(const QHostAddress & addr,int port,const QByteArray & data)2176 void S5BServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
2177 {
2178 	d->serv.writeUDP(addr, port, data);
2179 }
2180 
2181 //----------------------------------------------------------------------------
2182 // JT_S5B
2183 //----------------------------------------------------------------------------
2184 class JT_S5B::Private
2185 {
2186 public:
2187 	QDomElement iq;
2188 	Jid to;
2189 	Jid streamHost;
2190 	StreamHost proxyInfo;
2191 	int mode;
2192 	QTimer t;
2193 };
2194 
JT_S5B(Task * parent)2195 JT_S5B::JT_S5B(Task *parent)
2196 :Task(parent)
2197 {
2198 	d = new Private;
2199 	d->mode = -1;
2200 	connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
2201 }
2202 
~JT_S5B()2203 JT_S5B::~JT_S5B()
2204 {
2205 	delete d;
2206 }
2207 
request(const Jid & to,const QString & sid,const QString & dstaddr,const StreamHostList & hosts,bool fast,bool udp)2208 void JT_S5B::request(const Jid &to, const QString &sid, const QString &dstaddr,
2209 					 const StreamHostList &hosts, bool fast, bool udp)
2210 {
2211 	d->mode = 0;
2212 
2213 	QDomElement iq;
2214 	d->to = to;
2215 	iq = createIQ(doc(), "set", to.full(), id());
2216 	QDomElement query = doc()->createElement("query");
2217 	query.setAttribute("xmlns", S5B_NS);
2218 	query.setAttribute("sid", sid);
2219 	if (!client()->groupChatNick(to.domain(), to.node()).isEmpty()) {
2220 		query.setAttribute("dstaddr", dstaddr); // special case for muc as in xep-0065rc3
2221 	}
2222 	query.setAttribute("mode", udp ? "udp" : "tcp" );
2223 	iq.appendChild(query);
2224 	for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
2225 		QDomElement shost = doc()->createElement("streamhost");
2226 		shost.setAttribute("jid", (*it).jid().full());
2227 		shost.setAttribute("host", (*it).host());
2228 		shost.setAttribute("port", QString::number((*it).port()));
2229 		if((*it).isProxy()) {
2230 			QDomElement p = doc()->createElement("proxy");
2231 			p.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2232 			shost.appendChild(p);
2233 		}
2234 		query.appendChild(shost);
2235 	}
2236 	if(fast) {
2237 		QDomElement e = doc()->createElement("fast");
2238 		e.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2239 		query.appendChild(e);
2240 	}
2241 	d->iq = iq;
2242 }
2243 
requestProxyInfo(const Jid & to)2244 void JT_S5B::requestProxyInfo(const Jid &to)
2245 {
2246 	d->mode = 1;
2247 
2248 	QDomElement iq;
2249 	d->to = to;
2250 	iq = createIQ(doc(), "get", to.full(), id());
2251 	QDomElement query = doc()->createElement("query");
2252 	query.setAttribute("xmlns", S5B_NS);
2253 	iq.appendChild(query);
2254 	d->iq = iq;
2255 }
2256 
requestActivation(const Jid & to,const QString & sid,const Jid & target)2257 void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &target)
2258 {
2259 	d->mode = 2;
2260 
2261 	QDomElement iq;
2262 	d->to = to;
2263 	iq = createIQ(doc(), "set", to.full(), id());
2264 	QDomElement query = doc()->createElement("query");
2265 	query.setAttribute("xmlns", S5B_NS);
2266 	query.setAttribute("sid", sid);
2267 	iq.appendChild(query);
2268 	QDomElement act = doc()->createElement("activate");
2269 	act.appendChild(doc()->createTextNode(target.full()));
2270 	query.appendChild(act);
2271 	d->iq = iq;
2272 }
2273 
onGo()2274 void JT_S5B::onGo()
2275 {
2276 	if(d->mode == 1) {
2277 		d->t.setSingleShot(true);
2278 		d->t.start(15000);
2279 	}
2280 	send(d->iq);
2281 }
2282 
onDisconnect()2283 void JT_S5B::onDisconnect()
2284 {
2285 	d->t.stop();
2286 }
2287 
take(const QDomElement & x)2288 bool JT_S5B::take(const QDomElement &x)
2289 {
2290 	if(d->mode == -1)
2291 		return false;
2292 
2293 	if(!iqVerify(x, d->to, id()))
2294 		return false;
2295 
2296 	d->t.stop();
2297 
2298 	if(x.attribute("type") == "result") {
2299 		QDomElement q = queryTag(x);
2300 		if(d->mode == 0) {
2301 			d->streamHost = "";
2302 			if(!q.isNull()) {
2303 				QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement();
2304 				if(!shost.isNull())
2305 					d->streamHost = shost.attribute("jid");
2306 			}
2307 
2308 			setSuccess();
2309 		}
2310 		else if(d->mode == 1) {
2311 			if(!q.isNull()) {
2312 				QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement();
2313 				if(!shost.isNull()) {
2314 					Jid j = shost.attribute("jid");
2315 					if(j.isValid()) {
2316 						QString host = shost.attribute("host");
2317 						if(!host.isEmpty()) {
2318 							int port = shost.attribute("port").toInt();
2319 							StreamHost h;
2320 							h.setJid(j);
2321 							h.setHost(host);
2322 							h.setPort(port);
2323 							h.setIsProxy(true);
2324 							d->proxyInfo = h;
2325 						}
2326 					}
2327 				}
2328 			}
2329 
2330 			setSuccess();
2331 		}
2332 		else {
2333 			setSuccess();
2334 		}
2335 	}
2336 	else {
2337 		setError(x);
2338 	}
2339 
2340 	return true;
2341 }
2342 
t_timeout()2343 void JT_S5B::t_timeout()
2344 {
2345 	d->mode = -1;
2346 	setError(500, "Timed out");
2347 }
2348 
streamHostUsed() const2349 Jid JT_S5B::streamHostUsed() const
2350 {
2351 	return d->streamHost;
2352 }
2353 
proxyInfo() const2354 StreamHost JT_S5B::proxyInfo() const
2355 {
2356 	return d->proxyInfo;
2357 }
2358 
2359 //----------------------------------------------------------------------------
2360 // JT_PushS5B
2361 //----------------------------------------------------------------------------
JT_PushS5B(Task * parent)2362 JT_PushS5B::JT_PushS5B(Task *parent)
2363 :Task(parent)
2364 {
2365 }
2366 
~JT_PushS5B()2367 JT_PushS5B::~JT_PushS5B()
2368 {
2369 }
2370 
priority() const2371 int JT_PushS5B::priority() const
2372 {
2373 	return 1;
2374 }
2375 
take(const QDomElement & e)2376 bool JT_PushS5B::take(const QDomElement &e)
2377 {
2378 	// look for udpsuccess
2379 	if(e.tagName() == "message") {
2380 		QDomElement x = e.elementsByTagName("udpsuccess").item(0).toElement();
2381 		if(!x.isNull() && x.attribute("xmlns") == S5B_NS) {
2382 			incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr"));
2383 			return true;
2384 		}
2385 		x = e.elementsByTagName("activate").item(0).toElement();
2386 		if(!x.isNull() && x.attribute("xmlns") == "http://affinix.com/jabber/stream") {
2387 			incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid")));
2388 			return true;
2389 		}
2390 		return false;
2391 	}
2392 
2393 	// must be an iq-set tag
2394 	if(e.tagName() != "iq")
2395 		return false;
2396 	if(e.attribute("type") != "set")
2397 		return false;
2398 	if(queryNS(e) != S5B_NS)
2399 		return false;
2400 
2401 	Jid from(e.attribute("from"));
2402 	QDomElement q = queryTag(e);
2403 	QString sid = q.attribute("sid");
2404 
2405 	StreamHostList hosts;
2406 	QDomNodeList nl = q.elementsByTagName("streamhost");
2407 	for(int n = 0; n < nl.count(); ++n) {
2408 		QDomElement shost = nl.item(n).toElement();
2409 		if(hosts.count() < MAXSTREAMHOSTS) {
2410 			Jid j = shost.attribute("jid");
2411 			if(!j.isValid())
2412 				continue;
2413 			QString host = shost.attribute("host");
2414 			if(host.isEmpty())
2415 				continue;
2416 			int port = shost.attribute("port").toInt();
2417 			QDomElement p = shost.elementsByTagName("proxy").item(0).toElement();
2418 			bool isProxy = false;
2419 			if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream")
2420 				isProxy = true;
2421 
2422 			StreamHost h;
2423 			h.setJid(j);
2424 			h.setHost(host);
2425 			h.setPort(port);
2426 			h.setIsProxy(isProxy);
2427 			hosts += h;
2428 		}
2429 	}
2430 
2431 	bool fast = false;
2432 	QDomElement t;
2433 	t = q.elementsByTagName("fast").item(0).toElement();
2434 	if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream")
2435 		fast = true;
2436 
2437 	S5BRequest r;
2438 	r.from = from;
2439 	r.id = e.attribute("id");
2440 	r.sid = sid;
2441 	r.dstaddr = q.attribute("dstaddr"); // special case for muc as in xep-0065rc3
2442 	r.hosts = hosts;
2443 	r.fast = fast;
2444 	r.udp = q.attribute("mode") == "udp" ? true: false;
2445 
2446 	emit incoming(r);
2447 	return true;
2448 }
2449 
respondSuccess(const Jid & to,const QString & id,const Jid & streamHost)2450 void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost)
2451 {
2452 	QDomElement iq = createIQ(doc(), "result", to.full(), id);
2453 	QDomElement query = doc()->createElement("query");
2454 	query.setAttribute("xmlns", S5B_NS);
2455 	iq.appendChild(query);
2456 	QDomElement shost = doc()->createElement("streamhost-used");
2457 	shost.setAttribute("jid", streamHost.full());
2458 	query.appendChild(shost);
2459 	send(iq);
2460 }
2461 
respondError(const Jid & to,const QString & id,Stanza::Error::ErrorCond cond,const QString & str)2462 void JT_PushS5B::respondError(const Jid &to, const QString &id,
2463 							  Stanza::Error::ErrorCond cond, const QString &str)
2464 {
2465 	QDomElement iq = createIQ(doc(), "error", to.full(), id);
2466 	Stanza::Error error(Stanza::Error::Cancel, cond, str);
2467 	iq.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS()));
2468 	send(iq);
2469 }
2470 
sendUDPSuccess(const Jid & to,const QString & dstaddr)2471 void JT_PushS5B::sendUDPSuccess(const Jid &to, const QString &dstaddr)
2472 {
2473 	QDomElement m = doc()->createElement("message");
2474 	m.setAttribute("to", to.full());
2475 	QDomElement u = doc()->createElement("udpsuccess");
2476 	u.setAttribute("xmlns", S5B_NS);
2477 	u.setAttribute("dstaddr", dstaddr);
2478 	m.appendChild(u);
2479 	send(m);
2480 }
2481 
sendActivate(const Jid & to,const QString & sid,const Jid & streamHost)2482 void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &streamHost)
2483 {
2484 	QDomElement m = doc()->createElement("message");
2485 	m.setAttribute("to", to.full());
2486 	QDomElement act = doc()->createElement("activate");
2487 	act.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2488 	act.setAttribute("sid", sid);
2489 	act.setAttribute("jid", streamHost.full());
2490 	m.appendChild(act);
2491 	send(m);
2492 }
2493 
2494 //----------------------------------------------------------------------------
2495 // StreamHost
2496 //----------------------------------------------------------------------------
StreamHost()2497 StreamHost::StreamHost()
2498 {
2499 	v_port = -1;
2500 	proxy = false;
2501 }
2502 
jid() const2503 const Jid & StreamHost::jid() const
2504 {
2505 	return j;
2506 }
2507 
host() const2508 const QString & StreamHost::host() const
2509 {
2510 	return v_host;
2511 }
2512 
port() const2513 int StreamHost::port() const
2514 {
2515 	return v_port;
2516 }
2517 
isProxy() const2518 bool StreamHost::isProxy() const
2519 {
2520 	return proxy;
2521 }
2522 
setJid(const Jid & _j)2523 void StreamHost::setJid(const Jid &_j)
2524 {
2525 	j = _j;
2526 }
2527 
setHost(const QString & host)2528 void StreamHost::setHost(const QString &host)
2529 {
2530 	v_host = host;
2531 }
2532 
setPort(int port)2533 void StreamHost::setPort(int port)
2534 {
2535 	v_port = port;
2536 }
2537 
setIsProxy(bool b)2538 void StreamHost::setIsProxy(bool b)
2539 {
2540 	proxy = b;
2541 }
2542 
2543 }
2544 
2545 #include "s5b.moc"
2546