/* * connector.cpp - establish a connection to an XMPP server * Copyright (C) 2003 Justin Karneges * * 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* TODO: - Test and analyze all possible branches XMPP::AdvancedConnector is "good for now." The only real issue is that most of what it provides is just to work around the old Jabber/XMPP 0.9 connection behavior. When XMPP 1.0 has taken over the world, we can greatly simplify this class. - Sep 3rd, 2003. */ #include "xmpp.h" #include #include #include "safedelete.h" #ifdef NO_NDNS #include #else #include "ndns.h" #endif #include "srvresolver.h" #include "bsocket.h" #include "httpconnect.h" #include "httppoll.h" #include "socks.h" #include "hash.h" //#define XMPP_DEBUG using namespace XMPP; //---------------------------------------------------------------------------- // Connector //---------------------------------------------------------------------------- Connector::Connector(TQObject *parent) :TQObject(parent) { setUseSSL(false); setPeerAddressNone(); } Connector::~Connector() { } bool Connector::useSSL() const { return ssl; } bool Connector::havePeerAddress() const { return haveaddr; } TQHostAddress Connector::peerAddress() const { return addr; } TQ_UINT16 Connector::peerPort() const { return port; } void Connector::setUseSSL(bool b) { ssl = b; } void Connector::setPeerAddressNone() { haveaddr = false; addr = TQHostAddress(); port = 0; } void Connector::setPeerAddress(const TQHostAddress &_addr, TQ_UINT16 _port) { haveaddr = true; addr = _addr; port = _port; } //---------------------------------------------------------------------------- // AdvancedConnector::Proxy //---------------------------------------------------------------------------- AdvancedConnector::Proxy::Proxy() { t = None; v_poll = 30; } AdvancedConnector::Proxy::~Proxy() { } int AdvancedConnector::Proxy::type() const { return t; } TQString AdvancedConnector::Proxy::host() const { return v_host; } TQ_UINT16 AdvancedConnector::Proxy::port() const { return v_port; } TQString AdvancedConnector::Proxy::url() const { return v_url; } TQString AdvancedConnector::Proxy::user() const { return v_user; } TQString AdvancedConnector::Proxy::pass() const { return v_pass; } int AdvancedConnector::Proxy::pollInterval() const { return v_poll; } void AdvancedConnector::Proxy::setHttpConnect(const TQString &host, TQ_UINT16 port) { t = HttpConnect; v_host = host; v_port = port; } void AdvancedConnector::Proxy::setHttpPoll(const TQString &host, TQ_UINT16 port, const TQString &url) { t = HttpPoll; v_host = host; v_port = port; v_url = url; } void AdvancedConnector::Proxy::setSocks(const TQString &host, TQ_UINT16 port) { t = Socks; v_host = host; v_port = port; } void AdvancedConnector::Proxy::setUserPass(const TQString &user, const TQString &pass) { v_user = user; v_pass = pass; } void AdvancedConnector::Proxy::setPollInterval(int secs) { v_poll = secs; } //---------------------------------------------------------------------------- // AdvancedConnector //---------------------------------------------------------------------------- enum { Idle, Connecting, Connected }; class AdvancedConnector::Private { public: int mode; ByteStream *bs; #ifdef NO_NDNS TQDns *qdns; #else NDns dns; #endif SrvResolver srv; TQString server; TQString opt_host; int opt_port; bool opt_probe, opt_ssl; Proxy proxy; TQString host; int port; TQValueList servers; int errorCode; bool multi, using_srv; bool will_be_ssl; int probe_mode; bool aaaa; SafeDelete sd; }; AdvancedConnector::AdvancedConnector(TQObject *parent) :Connector(parent) { d = new Private; d->bs = 0; #ifdef NO_NDNS d->qdns = 0; #else connect(&d->dns, TQT_SIGNAL(resultsReady()), TQT_SLOT(dns_done())); #endif connect(&d->srv, TQT_SIGNAL(resultsReady()), TQT_SLOT(srv_done())); d->opt_probe = false; d->opt_ssl = false; cleanup(); d->errorCode = 0; } AdvancedConnector::~AdvancedConnector() { cleanup(); delete d; } void AdvancedConnector::cleanup() { d->mode = Idle; // stop any dns #ifdef NO_NDNS if(d->qdns) { d->qdns->disconnect(this); d->qdns->deleteLater(); //d->sd.deleteLater(d->qdns); d->qdns = 0; } #else if(d->dns.isBusy()) d->dns.stop(); #endif if(d->srv.isBusy()) d->srv.stop(); // destroy the bytestream, if there is one delete d->bs; d->bs = 0; d->multi = false; d->using_srv = false; d->will_be_ssl = false; d->probe_mode = -1; setUseSSL(false); setPeerAddressNone(); } void AdvancedConnector::setProxy(const Proxy &proxy) { if(d->mode != Idle) return; d->proxy = proxy; } void AdvancedConnector::setOptHostPort(const TQString &host, TQ_UINT16 _port) { if(d->mode != Idle) return; d->opt_host = host; d->opt_port = _port; } void AdvancedConnector::setOptProbe(bool b) { if(d->mode != Idle) return; d->opt_probe = b; } void AdvancedConnector::setOptSSL(bool b) { if(d->mode != Idle) return; d->opt_ssl = b; } void AdvancedConnector::connectToServer(const TQString &server) { if(d->mode != Idle) return; if(server.isEmpty()) return; d->errorCode = 0; d->server = server; d->mode = Connecting; d->aaaa = true; if(d->proxy.type() == Proxy::HttpPoll) { // need SHA1 here if(!TQCA::isSupported(TQCA::CAP_SHA1)) TQCA::insertProvider(createProviderHash()); HttpPoll *s = new HttpPoll; d->bs = s; connect(s, TQT_SIGNAL(connected()), TQT_SLOT(bs_connected())); connect(s, TQT_SIGNAL(syncStarted()), TQT_SLOT(http_syncStarted())); connect(s, TQT_SIGNAL(syncFinished()), TQT_SLOT(http_syncFinished())); connect(s, TQT_SIGNAL(error(int)), TQT_SLOT(bs_error(int))); if(!d->proxy.user().isEmpty()) s->setAuth(d->proxy.user(), d->proxy.pass()); s->setPollInterval(d->proxy.pollInterval()); if(d->proxy.host().isEmpty()) s->connectToUrl(d->proxy.url()); else s->connectToHost(d->proxy.host(), d->proxy.port(), d->proxy.url()); } else { if(!d->opt_host.isEmpty()) { d->host = d->opt_host; d->port = d->opt_port; do_resolve(); } else { d->multi = true; TQGuardedPtr self = this; srvLookup(d->server); if(!self) return; d->srv.resolveSrvOnly(d->server, "xmpp-client", "tcp"); } } } void AdvancedConnector::changePollInterval(int secs) { if(d->bs && (d->bs->inherits("XMPP::HttpPoll") || d->bs->inherits("HttpPoll"))) { HttpPoll *s = static_cast(d->bs); s->setPollInterval(secs); } } ByteStream *AdvancedConnector::stream() const { if(d->mode == Connected) return d->bs; else return 0; } void AdvancedConnector::done() { cleanup(); } int AdvancedConnector::errorCode() const { return d->errorCode; } void AdvancedConnector::do_resolve() { #ifdef NO_NDNS printf("resolving (aaaa=%d)\n", d->aaaa); d->qdns = new TQDns; connect(d->qdns, TQT_SIGNAL(resultsReady()), TQT_SLOT(dns_done())); if(d->aaaa) d->qdns->setRecordType(TQDns::Aaaa); // IPv6 else d->qdns->setRecordType(TQDns::A); // IPv4 d->qdns->setLabel(d->host); #else d->dns.resolve(d->host); #endif } void AdvancedConnector::dns_done() { bool failed = false; TQHostAddress addr; #ifdef NO_NDNS //if(!d->qdns) // return; // apparently we sometimes get this signal even though the results aren' t ready //if(d->qdns->isWorking()) // return; //SafeDeleteLock s(&d->sd); // grab the address list and destroy the qdns object TQValueList list = d->qdns->addresses(); d->qdns->disconnect(this); d->qdns->deleteLater(); //d->sd.deleteLater(d->qdns); d->qdns = 0; if(list.isEmpty()) { if(d->aaaa) { d->aaaa = false; do_resolve(); return; } //do_resolve(); //return; failed = true; } else addr = list.first(); #else if(d->dns.result() == 0) failed = true; else addr = TQHostAddress(d->dns.result()); #endif if(failed) { #ifdef XMPP_DEBUG printf("dns1\n"); #endif // using proxy? then try the unresolved host through the proxy if(d->proxy.type() != Proxy::None) { #ifdef XMPP_DEBUG printf("dns1.1\n"); #endif do_connect(); } else if(d->using_srv) { #ifdef XMPP_DEBUG printf("dns1.2\n"); #endif if(d->servers.isEmpty()) { #ifdef XMPP_DEBUG printf("dns1.2.1\n"); #endif cleanup(); d->errorCode = ErrConnectionRefused; error(); } else { #ifdef XMPP_DEBUG printf("dns1.2.2\n"); #endif tryNextSrv(); return; } } else { #ifdef XMPP_DEBUG printf("dns1.3\n"); #endif cleanup(); d->errorCode = ErrHostNotFound; error(); } } else { #ifdef XMPP_DEBUG printf("dns2\n"); #endif d->host = addr.toString(); do_connect(); } } void AdvancedConnector::do_connect() { #ifdef XMPP_DEBUG printf("trying %s:%d\n", d->host.latin1(), d->port); #endif int t = d->proxy.type(); if(t == Proxy::None) { #ifdef XMPP_DEBUG printf("do_connect1\n"); #endif BSocket *s = new BSocket; d->bs = s; connect(s, TQT_SIGNAL(connected()), TQT_SLOT(bs_connected())); connect(s, TQT_SIGNAL(error(int)), TQT_SLOT(bs_error(int))); s->connectToHost(d->host, d->port); } else if(t == Proxy::HttpConnect) { #ifdef XMPP_DEBUG printf("do_connect2\n"); #endif HttpConnect *s = new HttpConnect; d->bs = s; connect(s, TQT_SIGNAL(connected()), TQT_SLOT(bs_connected())); connect(s, TQT_SIGNAL(error(int)), TQT_SLOT(bs_error(int))); if(!d->proxy.user().isEmpty()) s->setAuth(d->proxy.user(), d->proxy.pass()); s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port); } else if(t == Proxy::Socks) { #ifdef XMPP_DEBUG printf("do_connect3\n"); #endif SocksClient *s = new SocksClient; d->bs = s; connect(s, TQT_SIGNAL(connected()), TQT_SLOT(bs_connected())); connect(s, TQT_SIGNAL(error(int)), TQT_SLOT(bs_error(int))); if(!d->proxy.user().isEmpty()) s->setAuth(d->proxy.user(), d->proxy.pass()); s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port); } } void AdvancedConnector::tryNextSrv() { #ifdef XMPP_DEBUG printf("trying next srv\n"); #endif d->host = d->servers.first().name; d->port = d->servers.first().port; d->servers.remove(d->servers.begin()); do_resolve(); } void AdvancedConnector::srv_done() { TQGuardedPtr self = this; #ifdef XMPP_DEBUG printf("srv_done1\n"); #endif d->servers = d->srv.servers(); if(d->servers.isEmpty()) { srvResult(false); if(!self) return; #ifdef XMPP_DEBUG printf("srv_done1.1\n"); #endif // fall back to A record d->using_srv = false; d->host = d->server; if(d->opt_probe) { #ifdef XMPP_DEBUG printf("srv_done1.1.1\n"); #endif d->probe_mode = 0; d->port = 5223; d->will_be_ssl = true; } else { #ifdef XMPP_DEBUG printf("srv_done1.1.2\n"); #endif d->probe_mode = 1; d->port = 5222; } do_resolve(); return; } srvResult(true); if(!self) return; d->using_srv = true; tryNextSrv(); } void AdvancedConnector::bs_connected() { if(d->proxy.type() == Proxy::None) { TQHostAddress h = (static_cast(d->bs))->peerAddress(); int p = (static_cast(d->bs))->peerPort(); setPeerAddress(h, p); } // only allow ssl override if proxy==poll or host:port if((d->proxy.type() == Proxy::HttpPoll || !d->opt_host.isEmpty()) && d->opt_ssl) setUseSSL(true); else if(d->will_be_ssl) setUseSSL(true); d->mode = Connected; connected(); } void AdvancedConnector::bs_error(int x) { if(d->mode == Connected) { d->errorCode = ErrStream; error(); return; } bool proxyError = false; int err = ErrConnectionRefused; int t = d->proxy.type(); #ifdef XMPP_DEBUG printf("bse1\n"); #endif // figure out the error if(t == Proxy::None) { if(x == BSocket::ErrHostNotFound) err = ErrHostNotFound; else err = ErrConnectionRefused; } else if(t == Proxy::HttpConnect) { if(x == HttpConnect::ErrConnectionRefused) err = ErrConnectionRefused; else if(x == HttpConnect::ErrHostNotFound) err = ErrHostNotFound; else { proxyError = true; if(x == HttpConnect::ErrProxyAuth) err = ErrProxyAuth; else if(x == HttpConnect::ErrProxyNeg) err = ErrProxyNeg; else err = ErrProxyConnect; } } else if(t == Proxy::HttpPoll) { if(x == HttpPoll::ErrConnectionRefused) err = ErrConnectionRefused; else if(x == HttpPoll::ErrHostNotFound) err = ErrHostNotFound; else { proxyError = true; if(x == HttpPoll::ErrProxyAuth) err = ErrProxyAuth; else if(x == HttpPoll::ErrProxyNeg) err = ErrProxyNeg; else err = ErrProxyConnect; } } else if(t == Proxy::Socks) { if(x == SocksClient::ErrConnectionRefused) err = ErrConnectionRefused; else if(x == SocksClient::ErrHostNotFound) err = ErrHostNotFound; else { proxyError = true; if(x == SocksClient::ErrProxyAuth) err = ErrProxyAuth; else if(x == SocksClient::ErrProxyNeg) err = ErrProxyNeg; else err = ErrProxyConnect; } } // no-multi or proxy error means we quit if(!d->multi || proxyError) { cleanup(); d->errorCode = err; error(); return; } if(d->using_srv && !d->servers.isEmpty()) { #ifdef XMPP_DEBUG printf("bse1.1\n"); #endif tryNextSrv(); } else if(!d->using_srv && d->opt_probe && d->probe_mode == 0) { #ifdef XMPP_DEBUG printf("bse1.2\n"); #endif d->probe_mode = 1; d->port = 5222; d->will_be_ssl = false; do_connect(); } else { #ifdef XMPP_DEBUG printf("bse1.3\n"); #endif cleanup(); d->errorCode = ErrConnectionRefused; error(); } } void AdvancedConnector::http_syncStarted() { httpSyncStarted(); } void AdvancedConnector::http_syncFinished() { httpSyncFinished(); }