summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/groupwise/libgroupwise/securestream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/groupwise/libgroupwise/securestream.cpp')
-rw-r--r--kopete/protocols/groupwise/libgroupwise/securestream.cpp542
1 files changed, 542 insertions, 0 deletions
diff --git a/kopete/protocols/groupwise/libgroupwise/securestream.cpp b/kopete/protocols/groupwise/libgroupwise/securestream.cpp
new file mode 100644
index 00000000..583be03e
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/securestream.cpp
@@ -0,0 +1,542 @@
+/*
+ securestream.h - Kopete Groupwise Protocol
+ Combines a ByteStream with TLS and SASL
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ 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. *
+ * *
+ *************************************************************************
+*/
+
+/*
+ Note: SecureStream depends on the underlying security layers to signal
+ plain-to-encrypted results immediately (as opposed to waiting for the
+ event loop) so that the user cannot add/remove security layers during
+ this conversion moment. QCA::TLS and QCA::SASL behave as expected,
+ but future layers might not.
+*/
+
+#include<qguardedptr.h>
+#include<qvaluelist.h>
+#include<qtimer.h>
+
+#include"securestream.h"
+
+//----------------------------------------------------------------------------
+// LayerTracker
+//----------------------------------------------------------------------------
+LayerTracker::LayerTracker()
+{
+ p = 0;
+}
+
+void LayerTracker::reset()
+{USE_TLSHANDLER
+ p = 0;
+ list.clear();
+}
+
+void LayerTracker::addPlain(int plain)
+{
+ p += plain;
+}
+
+void LayerTracker::specifyEncoded(int encoded, int plain)
+{
+ // can't specify more bytes than we have
+ if(plain > p)
+ plain = p;
+ p -= plain;
+ Item i;
+ i.plain = plain;
+ i.encoded = encoded;
+ list += i;
+}
+
+int LayerTracker::finished(int encoded)
+{
+ int plain = 0;
+ for(QValueList<Item>::Iterator it = list.begin(); it != list.end();) {
+ Item &i = *it;
+
+ // not enough?
+ if(encoded < i.encoded) {
+ i.encoded -= encoded;
+ break;
+ }
+
+ encoded -= i.encoded;
+ plain += i.plain;
+ it = list.remove(it);
+ }
+ return plain;
+}
+
+//----------------------------------------------------------------------------
+// SecureStream
+//----------------------------------------------------------------------------
+
+SecureLayer::SecureLayer(QCA::TLS *t)
+{
+ type = TLS;
+ p.tls = t;
+ init();
+ connect(p.tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
+ connect(p.tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
+ connect(p.tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int)));
+ connect(p.tls, SIGNAL(closed()), SLOT(tls_closed()));
+ connect(p.tls, SIGNAL(error(int)), SLOT(tls_error(int)));
+}
+
+SecureLayer::SecureLayer(QCA::SASL *s)
+{
+ type = SASL;
+ p.sasl = s;
+ init();
+ connect(p.sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead()));
+ connect(p.sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int)));
+ connect(p.sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+}
+
+#ifdef USE_TLSHANDLER
+SecureLayer::SecureLayer(TLSHandler *t)
+{
+ type = TLSH;
+ p.tlsHandler = t;
+ init();
+ connect(p.tlsHandler, SIGNAL(success()), SLOT(tlsHandler_success()));
+ connect(p.tlsHandler, SIGNAL(fail()), SLOT(tlsHandler_fail()));
+ connect(p.tlsHandler, SIGNAL(closed()), SLOT(tlsHandler_closed()));
+ connect(p.tlsHandler, SIGNAL(readyRead(const QByteArray &)), SLOT(tlsHandler_readyRead(const QByteArray &)));
+ connect(p.tlsHandler, SIGNAL(readyReadOutgoing(const QByteArray &, int)), SLOT(tlsHandler_readyReadOutgoing(const QByteArray &, int)));
+}
+#endif
+
+void SecureLayer::init()
+{
+ tls_done = false;
+ prebytes = 0;
+}
+
+void SecureLayer::write(const QByteArray &a)
+{
+ layer.addPlain(a.size());
+ switch(type) {
+ case TLS: { p.tls->write(a); break; }
+ case SASL: { p.sasl->write(a); break; }
+#ifdef USE_TLSHANDLER
+ case TLSH: { p.tlsHandler->write(a); break; }
+#endif
+ }
+}
+
+void SecureLayer::writeIncoming(const QByteArray &a)
+{
+ switch(type) {
+ case TLS: { p.tls->writeIncoming(a); break; }
+ case SASL: { p.sasl->writeIncoming(a); break; }
+#ifdef USE_TLSHANDLER
+ case TLSH: { p.tlsHandler->writeIncoming(a); break; }
+#endif
+ }
+}
+
+int SecureLayer::finished(int plain)
+{
+ int written = 0;
+
+ // deal with prebytes (bytes sent prior to this security layer)
+ if(prebytes > 0) {
+ if(prebytes >= plain) {
+ written += plain;
+ prebytes -= plain;
+ plain = 0;
+ }
+ else {
+ written += prebytes;
+ plain -= prebytes;
+ prebytes = 0;
+ }
+ }
+
+ // put remainder into the layer tracker
+ if(type == SASL || tls_done)
+ written += layer.finished(plain);
+
+ return written;
+}
+
+void SecureLayer::tls_handshaken()
+{
+ tls_done = true;
+ tlsHandshaken();
+}
+
+void SecureLayer::tls_readyRead()
+{
+ QByteArray a = p.tls->read();
+ readyRead(a);
+}
+
+void SecureLayer::tls_readyReadOutgoing(int plainBytes)
+{
+ QByteArray a = p.tls->readOutgoing();
+ if(tls_done)
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+}
+
+void SecureLayer::tls_closed()
+{
+ QByteArray a = p.tls->readUnprocessed();
+ tlsClosed(a);
+}
+
+void SecureLayer::tls_error(int x)
+{
+ error(x);
+}
+
+void SecureLayer::sasl_readyRead()
+{
+ QByteArray a = p.sasl->read();
+ readyRead(a);
+}
+
+void SecureLayer::sasl_readyReadOutgoing(int plainBytes)
+{
+ QByteArray a = p.sasl->readOutgoing();
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+}
+
+void SecureLayer::sasl_error(int x)
+{
+ error(x);
+}
+
+#ifdef USE_TLSHANDLER
+void SecureLayer::tlsHandler_success()
+{
+ tls_done = true;
+ tlsHandshaken();
+}
+
+void SecureLayer::tlsHandler_fail()
+{
+ error(0);
+}
+
+void SecureLayer::tlsHandler_closed()
+{
+ tlsClosed(QByteArray());
+}
+
+void SecureLayer::tlsHandler_readyRead(const QByteArray &a)
+{
+ readyRead(a);
+}
+
+void SecureLayer::tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes)
+{
+ if(tls_done)
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+}
+#endif
+
+class SecureStream::Private
+{
+public:
+ ByteStream *bs;
+ QPtrList<SecureLayer> layers;
+ int pending;
+ int errorCode;
+ bool active;
+ bool topInProgress;
+
+ bool haveTLS() const
+ {
+ QPtrListIterator<SecureLayer> it(layers);
+ for(SecureLayer *s; (s = it.current()); ++it) {
+ if(s->type == SecureLayer::TLS
+#ifdef USE_TLSHANDLER
+ || s->type == SecureLayer::TLSH
+#endif
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool haveSASL() const
+ {
+ QPtrListIterator<SecureLayer> it(layers);
+ for(SecureLayer *s; (s = it.current()); ++it) {
+ if(s->type == SecureLayer::SASL)
+ return true;
+ }
+ return false;
+ }
+};
+
+SecureStream::SecureStream(ByteStream *s)
+:ByteStream(0)
+{
+ d = new Private;
+
+ d->bs = s;
+ connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+ connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+
+ d->layers.setAutoDelete(true);
+ d->pending = 0;
+ d->active = true;
+ d->topInProgress = false;
+}
+
+SecureStream::~SecureStream()
+{
+ delete d;
+}
+
+void SecureStream::linkLayer(QObject *s)
+{
+ connect(s, SIGNAL(tlsHandshaken()), SLOT(layer_tlsHandshaken()));
+ connect(s, SIGNAL(tlsClosed(const QByteArray &)), SLOT(layer_tlsClosed(const QByteArray &)));
+ connect(s, SIGNAL(readyRead(const QByteArray &)), SLOT(layer_readyRead(const QByteArray &)));
+ connect(s, SIGNAL(needWrite(const QByteArray &)), SLOT(layer_needWrite(const QByteArray &)));
+ connect(s, SIGNAL(error(int)), SLOT(layer_error(int)));
+}
+
+int SecureStream::calcPrebytes() const
+{
+ int x = 0;
+ QPtrListIterator<SecureLayer> it(d->layers);
+ for(SecureLayer *s; (s = it.current()); ++it)
+ x += s->prebytes;
+ return (d->pending - x);
+}
+
+void SecureStream::startTLSClient(QCA::TLS *t, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ insertData(spare);
+}
+
+void SecureStream::startTLSServer(QCA::TLS *t, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ insertData(spare);
+}
+
+void SecureStream::setLayerSASL(QCA::SASL *sasl, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveSASL())
+ return;
+
+ SecureLayer *s = new SecureLayer(sasl);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+
+ insertData(spare);
+}
+
+#ifdef USE_TLSHANDLER
+void SecureStream::startTLSClient(TLSHandler *t, const QString &server, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ // unlike QCA::TLS, TLSHandler has no return value
+ s->p.tlsHandler->startClient(server);
+
+ insertData(spare);
+}
+#endif
+
+void SecureStream::closeTLS()
+{
+ SecureLayer *s = d->layers.getLast();
+ if(s) {
+ if(s->type == SecureLayer::TLS)
+ s->p.tls->close();
+ }
+}
+
+int SecureStream::errorCode() const
+{
+ return d->errorCode;
+}
+
+bool SecureStream::isOpen() const
+{
+ return d->active;
+}
+
+void SecureStream::write(const QByteArray &a)
+{
+ if(!isOpen())
+ return;
+
+ d->pending += a.size();
+
+ // send to the last layer
+ SecureLayer *s = d->layers.getLast();
+ if(s)
+ s->write(a);
+ else
+ writeRawData(a);
+}
+
+int SecureStream::bytesToWrite() const
+{
+ return d->pending;
+}
+
+void SecureStream::bs_readyRead()
+{
+ QByteArray a = d->bs->read();
+
+ // send to the first layer
+ SecureLayer *s = d->layers.getFirst();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+}
+
+void SecureStream::bs_bytesWritten(int bytes)
+{
+ QPtrListIterator<SecureLayer> it(d->layers);
+ for(SecureLayer *s; (s = it.current()); ++it)
+ bytes = s->finished(bytes);
+
+ if(bytes > 0) {
+ d->pending -= bytes;
+ bytesWritten(bytes);
+ }
+}
+
+void SecureStream::layer_tlsHandshaken()
+{
+ d->topInProgress = false;
+ tlsHandshaken();
+}
+
+void SecureStream::layer_tlsClosed(const QByteArray &)
+{
+ d->active = false;
+ d->layers.clear();
+ tlsClosed();
+}
+
+void SecureStream::layer_readyRead(const QByteArray &a)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ QPtrListIterator<SecureLayer> it(d->layers);
+ while(it.current() != s)
+ ++it;
+
+ // pass upwards
+ ++it;
+ s = it.current();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+}
+
+void SecureStream::layer_needWrite(const QByteArray &a)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ QPtrListIterator<SecureLayer> it(d->layers);
+ while(it.current() != s)
+ ++it;
+
+ // pass downwards
+ --it;
+ s = it.current();
+ if(s)
+ s->write(a);
+ else
+ writeRawData(a);
+}
+
+void SecureStream::layer_error(int x)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ int type = s->type;
+ d->errorCode = x;
+ d->active = false;
+ d->layers.clear();
+ if(type == SecureLayer::TLS)
+ error(ErrTLS);
+ else if(type == SecureLayer::SASL)
+ error(ErrSASL);
+#ifdef USE_TLSHANDLER
+ else if(type == SecureLayer::TLSH)
+ error(ErrTLS);
+#endif
+}
+
+void SecureStream::insertData(const QByteArray &a)
+{
+ if(!a.isEmpty()) {
+ SecureLayer *s = d->layers.getLast();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+ }
+}
+
+void SecureStream::writeRawData(const QByteArray &a)
+{
+ d->bs->write(a);
+}
+
+void SecureStream::incomingData(const QByteArray &a)
+{
+ appendRead(a);
+ //qDebug( "SecureStream::incomingData() got %i bytes ", a.size() );
+
+ if(bytesAvailable())
+ readyRead();
+}
+
+#include "securestream.moc"