You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/groupwise/libgroupwise/gwclientstream.cpp

607 lines
12 KiB

/*
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 Pidgin src/protocols/novell/nmconn.c
Copyright (c) 2004 Novell, Inc. All Rights Reserved
Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
*************************************************************************
* *
* 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 <tqtextstream.h>
//#include <tqguardedptr.h>
// #include <tqca.h>
// #include <stdlib.h>
// #include "bytestream.h"
// #include "base64.h"
// #include "hash.h"
// #include "simplesasl.h"
// #include "securestream.h"
// #include "protocol.h"
#include <tqapplication.h> // for qdebug
#include <tqguardedptr.h>
#include <tqobject.h>
#include <tqptrqueue.h>
#include <tqtimer.h>
#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;
TQCA::TLS *tls;
// TQCA::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<Transfer> in;
TQTimer noopTimer; // probably not needed
int noop_time;
};
ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, TQObject *parent)
:Stream(parent)
{
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( "contains: %1 bytes " ).arg( 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" ).arg( 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<TQObject> 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" ).arg( 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" ).arg( bytes ) );
#else
Q_UNUSED( bytes );
#endif
}
void ClientStream::ss_tlsHandshaken()
{
TQGuardedPtr<TQObject> 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 ").arg( x ) );
if(x == SecureStream::ErrTLS) {
reset();
d->errCond = TLSFail;
emit error(ErrTLS);
}
else {
reset();
emit error(ErrSecurityLayer);
}
}
void ClientStream::srvProcessNext()
{
}
void ClientStream::doReadyRead()
{
//TQGuardedPtr<TQObject> 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"