/* gwclientstream.cpp - Kopete Groupwise Protocol Copyright (c) 2004 SUSE Linux AG http://www.suse.com Based on Iris, Copyright (C) 2003 Justin Karneges encode_method from Gaim src/protocols/novell/nmconn.c Copyright (c) 2004 Novell, Inc. All Rights Reserved Kopete (c) 2002-2004 by the Kopete developers ************************************************************************* * * * 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 of the License, or (at your option) any later version. * * * ************************************************************************* */ //#include //#include // #include // #include // #include"bytestream.h" // #include"base64.h" // #include"hash.h" // #include"simplesasl.h" // #include"securestream.h" // #include"protocol.h" #include // for qdebug #include #include #include #include #include "bytestream.h" #include "connector.h" #include "coreprotocol.h" #include "request.h" #include "securestream.h" #include "tlshandler.h" //#include "iostream.h" #include "gwclientstream.h" //#define LIBGW_DEBUG 1 void cs_dump( const TQByteArray &bytes ); enum { Idle, Connecting, WaitVersion, WaitTLS, NeedParams, Active, Closing }; enum { Client, Server }; class ClientStream::Private { public: Private() { conn = 0; bs = 0; ss = 0; tlsHandler = 0; tls = 0; // sasl = 0; in.setAutoDelete(true); allowPlain = false; mutualAuth = false; haveLocalAddr = false; /* minimumSSF = 0; maximumSSF = 0;*/ doBinding = true; in_rrsig = false; reset(); } void reset() { state = Idle; notify = 0; newTransfers = false; // sasl_ssf = 0; tls_warned = false; using_tls = false; } NovellDN id; TQString server; bool oldOnly; bool allowPlain, mutualAuth; bool haveLocalAddr; TQHostAddress localAddr; TQ_UINT16 localPort; // int minimumSSF, maximumSSF; // TQString sasl_mech; bool doBinding; bool in_rrsig; Connector *conn; ByteStream *bs; TLSHandler *tlsHandler; QCA::TLS *tls; // QCA::SASL *sasl; SecureStream *ss; CoreProtocol client; //CoreProtocol srv; TQString defRealm; int mode; int state; int notify; bool newTransfers; // int sasl_ssf; bool tls_warned, using_tls; bool doAuth; // TQStringList sasl_mechlist; int errCond; TQString errText; TQPtrQueue in; TQTimer noopTimer; // probably not needed int noop_time; }; ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, TQObject *tqparent) :Stream(tqparent) { d = new Private; d->mode = Client; d->conn = conn; connect( d->conn, TQT_SIGNAL(connected()), TQT_SLOT(cr_connected()) ); connect( d->conn, TQT_SIGNAL(error()), TQT_SLOT(cr_error()) ); connect( &d->client, TQT_SIGNAL( outgoingData( const TQByteArray& ) ), TQT_SLOT ( cp_outgoingData( const TQByteArray & ) ) ); connect( &d->client, TQT_SIGNAL( incomingData() ), TQT_SLOT ( cp_incomingData() ) ); d->noop_time = 0; connect(&d->noopTimer, TQT_SIGNAL(timeout()), TQT_SLOT(doNoop())); d->tlsHandler = tlsHandler; // all the extra stuff happening in the larger ctor happens at connect time :) } ClientStream::~ClientStream() { reset(); delete d; } void ClientStream::reset(bool all) { d->reset(); d->noopTimer.stop(); // delete securestream delete d->ss; d->ss = 0; // reset sasl // delete d->sasl; // d->sasl = 0; // client if(d->mode == Client) { // reset tls if(d->tlsHandler) d->tlsHandler->reset(); // reset connector if(d->bs) { d->bs->close(); d->bs = 0; } d->conn->done(); // reset state machine d->client.reset(); } if(all) d->in.clear(); } // Jid ClientStream::jid() const // { // return d->jid; // } void ClientStream::connectToServer(const NovellDN &id, bool auth) { reset(true); d->state = Connecting; d->id = id; d->doAuth = auth; d->server = d->id.server; d->conn->connectToServer( d->server ); } void ClientStream::continueAfterWarning() { if(d->state == WaitVersion) { // if we don't have TLS yet, then we're never going to get it if(!d->tls_warned && !d->using_tls) { d->tls_warned = true; d->state = WaitTLS; emit warning(WarnNoTLS); return; } d->state = Connecting; processNext(); } else if(d->state == WaitTLS) { d->state = Connecting; processNext(); } } void ClientStream::accept() { /* d->srv.host = d->server; processNext();*/ } bool ClientStream::isActive() const { return (d->state != Idle); } bool ClientStream::isAuthenticated() const { return (d->state == Active); } // void ClientStream::setPassword(const TQString &s) // { // if(d->client.old) { // d->client.setPassword(s); // } // else { // if(d->sasl) // d->sasl->setPassword(s); // } // } // void ClientStream::setRealm(const TQString &s) // { // if(d->sasl) // d->sasl->setRealm(s); // } void ClientStream::continueAfterParams() { /* if(d->state == NeedParams) { d->state = Connecting; if(d->client.old) { processNext(); } else { if(d->sasl) d->sasl->continueAfterParams(); } }*/ } void ClientStream::setNoopTime(int mills) { d->noop_time = mills; if(d->state != Active) return; if(d->noop_time == 0) { d->noopTimer.stop(); return; } d->noopTimer.start(d->noop_time); } void ClientStream::setLocalAddr(const TQHostAddress &addr, TQ_UINT16 port) { d->haveLocalAddr = true; d->localAddr = addr; d->localPort = port; } int ClientStream::errorCondition() const { return d->errCond; } TQString ClientStream::errorText() const { return d->errText; } // TQDomElement ClientStream::errorAppSpec() const // { // return d->errAppSpec;cr_error // } // bool ClientStream::old() const // { // return d->client.old; // } void ClientStream::close() { if(d->state == Active) { d->state = Closing; // d->client.shutdown(); processNext(); } else if(d->state != Idle && d->state != Closing) { reset(); } } void ClientStream::setAllowPlain(bool b) { d->allowPlain = b; } void ClientStream::setRequireMutualAuth(bool b) { d->mutualAuth = b; } // void ClientStream::setSSFRange(int low, int high) // { // d->minimumSSF = low; // d->maximumSSF = high; // } // void ClientStream::setOldOnly(bool b) // { // d->oldOnly = b; // } bool ClientStream::transfersAvailable() const { return ( !d->in.isEmpty() ); } Transfer * ClientStream::read() { if(d->in.isEmpty()) return 0; //first from queue... else return d->in.dequeue(); } void ClientStream::write( Request *request ) { // pass to CoreProtocol for transformation into wire format d->client.outgoingTransfer( request ); } void cs_dump( const TQByteArray &bytes ) { //#define GW_CLIENTSTREAM_DEBUG 1 #ifdef GW_CLIENTSTREAM_DEBUG CoreProtocol::debug( TQString( "tqcontains: %1 bytes " ).tqarg( bytes.count() ) ); uint count = 0; while ( count < bytes.count() ) { int dword = 0; for ( int i = 0; i < 8; ++i ) { if ( count + i < bytes.count() ) printf( "%02x ", bytes[ count + i ] ); else printf( " " ); if ( i == 3 ) printf( " " ); } printf(" | "); dword = 0; for ( int i = 0; i < 8; ++i ) { if ( count + i < bytes.count() ) { int j = bytes [ count + i ]; if ( j >= 0x20 && j <= 0x7e ) printf( "%2c ", j ); else printf( "%2c ", '.' ); } else printf( " " ); if ( i == 3 ) printf( " " ); } printf( "\n" ); count += 8; } printf( "\n" ); #else Q_UNUSED( bytes ); #endif } void ClientStream::cp_outgoingData( const TQByteArray& outgoingBytes ) { // take formatted bytes from CoreProtocol and put them on the wire #ifdef LIBGW_DEBUG CoreProtocol::debug( "ClientStream::cp_outgoingData:" ); cs_dump( outgoingBytes ); #endif d->ss->write( outgoingBytes ); } void ClientStream::cp_incomingData() { CoreProtocol::debug( "ClientStream::cp_incomingData:" ); Transfer * incoming = d->client.incomingTransfer(); if ( incoming ) { CoreProtocol::debug( " - got a new transfer" ); d->in.enqueue( incoming ); d->newTransfers = true; emit doReadyRead(); } else CoreProtocol::debug( TQString( " - client signalled incomingData but none was available, state is: %1" ).tqarg( d->client.state() ) ); } void ClientStream::cr_connected() { d->bs = d->conn->stream(); connect(d->bs, TQT_SIGNAL(connectionClosed()), TQT_SLOT(bs_connectionClosed())); connect(d->bs, TQT_SIGNAL(delayedCloseFinished()), TQT_SLOT(bs_delayedCloseFinished())); TQByteArray spare = d->bs->read(); d->ss = new SecureStream(d->bs); connect(d->ss, TQT_SIGNAL(readyRead()), TQT_SLOT(ss_readyRead())); connect(d->ss, TQT_SIGNAL(bytesWritten(int)), TQT_SLOT(ss_bytesWritten(int))); connect(d->ss, TQT_SIGNAL(tlsHandshaken()), TQT_SLOT(ss_tlsHandshaken())); connect(d->ss, TQT_SIGNAL(tlsClosed()), TQT_SLOT(ss_tlsClosed())); connect(d->ss, TQT_SIGNAL(error(int)), TQT_SLOT(ss_error(int))); //d->client.startDialbackOut("andbit.net", "im.pyxa.org"); //d->client.startServerOut(d->server); // d->client.startClientOut(d->jid, d->oldOnly, d->conn->useSSL(), d->doAuth); // d->client.setAllowTLS(d->tlsHandler ? true: false); // d->client.setAllowBind(d->doBinding); // d->client.setAllowPlain(d->allowPlain); /*d->client.jid = d->jid; d->client.server = d->server; d->client.allowPlain = d->allowPlain; d->client.oldOnly = d->oldOnly; d->client.sasl_mech = d->sasl_mech; d->client.doTLS = d->tlsHandler ? true: false; d->client.doBinding = d->doBinding;*/ TQGuardedPtr self = this; emit connected(); if(!self) return; // immediate SSL? if(d->conn->useSSL()) { CoreProtocol::debug( "CLIENTSTREAM: cr_connected(), starting TLS" ); d->using_tls = true; d->ss->startTLSClient(d->tlsHandler, d->server, spare); } else { /* d->client.addIncomingData(spare); processNext();*/ } } void ClientStream::cr_error() { reset(); emit error(ErrConnection); } void ClientStream::bs_connectionClosed() { reset(); emit connectionClosed(); } void ClientStream::bs_delayedCloseFinished() { // we don't care about this (we track all important data ourself) } void ClientStream::bs_error(int) { // TODO } void ClientStream::ss_readyRead() { TQByteArray a; a = d->ss->read(); #ifdef LIBGW_DEBUG TQCString cs(a.data(), a.size()+1); CoreProtocol::debug( TQString( "ClientStream: ss_readyRead() recv: %1 bytes" ).tqarg( a.size() ) ); cs_dump( a ); #endif d->client.addIncomingData(a); /* if(d->notify & CoreProtocol::NRecv) { */ //processNext(); } void ClientStream::ss_bytesWritten(int bytes) { #ifdef LIBGW_DEBUG CoreProtocol::debug( TQString( "ClientStream::ss_bytesWritten: %1 bytes written" ).tqarg( bytes ) ); #else Q_UNUSED( bytes ); #endif } void ClientStream::ss_tlsHandshaken() { TQGuardedPtr self = this; emit securityLayerActivated(LayerTLS); if(!self) return; processNext(); } void ClientStream::ss_tlsClosed() { CoreProtocol::debug( "ClientStream::ss_tlsClosed()" ); reset(); emit connectionClosed(); } void ClientStream::ss_error(int x) { CoreProtocol::debug( TQString( "ClientStream::ss_error() x=%1 ").tqarg( x ) ); if(x == SecureStream::ErrTLS) { reset(); d->errCond = TLSFail; emit error(ErrTLS); } else { reset(); emit error(ErrSecurityLayer); } } void ClientStream::srvProcessNext() { } void ClientStream::doReadyRead() { //TQGuardedPtr self = this; emit readyRead(); //if(!self) // return; //d->in_rrsig = false; } void ClientStream::processNext() { if( !d->in.isEmpty() ) { //d->in_rrsig = true; TQTimer::singleShot(0, this, TQT_SLOT(doReadyRead())); } } bool ClientStream::handleNeed() { return false; } void ClientStream::doNoop() { } void ClientStream::handleError() { } #include "gwclientstream.moc"