summaryrefslogtreecommitdiffstats
path: root/kdecore/network/kclientsocketbase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/network/kclientsocketbase.cpp')
-rw-r--r--kdecore/network/kclientsocketbase.cpp477
1 files changed, 477 insertions, 0 deletions
diff --git a/kdecore/network/kclientsocketbase.cpp b/kdecore/network/kclientsocketbase.cpp
new file mode 100644
index 000000000..b777dc8de
--- /dev/null
+++ b/kdecore/network/kclientsocketbase.cpp
@@ -0,0 +1,477 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003,2005 Thiago Macieira <thiago.macieira@kdemail.net>
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <qsocketnotifier.h>
+#include <qtimer.h>
+#include <qmutex.h>
+
+#include "ksocketaddress.h"
+#include "kresolver.h"
+#include "ksocketbase.h"
+#include "ksocketdevice.h"
+#include "kclientsocketbase.h"
+
+using namespace KNetwork;
+
+class KNetwork::KClientSocketBasePrivate
+{
+public:
+ int state;
+
+ KResolver localResolver, peerResolver;
+ KResolverResults localResults, peerResults;
+
+ bool enableRead : 1, enableWrite : 1;
+};
+
+KClientSocketBase::KClientSocketBase(QObject *parent, const char *name)
+ : QObject(parent, name), d(new KClientSocketBasePrivate)
+{
+ d->state = Idle;
+ d->enableRead = true;
+ d->enableWrite = false;
+}
+
+KClientSocketBase::~KClientSocketBase()
+{
+ close();
+ delete d;
+}
+
+KClientSocketBase::SocketState KClientSocketBase::state() const
+{
+ return static_cast<SocketState>(d->state);
+}
+
+void KClientSocketBase::setState(SocketState state)
+{
+ d->state = state;
+ stateChanging(state);
+}
+
+bool KClientSocketBase::setSocketOptions(int opts)
+{
+ QMutexLocker locker(mutex());
+ KSocketBase::setSocketOptions(opts); // call parent
+
+ // don't create the device unnecessarily
+ if (hasDevice())
+ {
+ bool result = socketDevice()->setSocketOptions(opts); // and set the implementation
+ copyError();
+ return result;
+ }
+
+ return true;
+}
+
+KResolver& KClientSocketBase::peerResolver() const
+{
+ return d->peerResolver;
+}
+
+const KResolverResults& KClientSocketBase::peerResults() const
+{
+ return d->peerResults;
+}
+
+KResolver& KClientSocketBase::localResolver() const
+{
+ return d->localResolver;
+}
+
+const KResolverResults& KClientSocketBase::localResults() const
+{
+ return d->localResults;
+}
+
+void KClientSocketBase::setResolutionEnabled(bool enable)
+{
+ if (enable)
+ {
+ d->localResolver.setFlags(d->localResolver.flags() & ~KResolver::NoResolve);
+ d->peerResolver.setFlags(d->peerResolver.flags() & ~KResolver::NoResolve);
+ }
+ else
+ {
+ d->localResolver.setFlags(d->localResolver.flags() | KResolver::NoResolve);
+ d->peerResolver.setFlags(d->peerResolver.flags() | KResolver::NoResolve);
+ }
+}
+
+void KClientSocketBase::setFamily(int families)
+{
+ d->localResolver.setFamily(families);
+ d->peerResolver.setFamily(families);
+}
+
+bool KClientSocketBase::lookup()
+{
+ if (state() == HostLookup && !blocking())
+ return true; // already doing lookup
+
+ if (state() > HostLookup)
+ return true; // results are already available
+
+ if (state() < HostLookup)
+ {
+ if (d->localResolver.serviceName().isNull() &&
+ !d->localResolver.nodeName().isNull())
+ d->localResolver.setServiceName(QString::fromLatin1(""));
+
+ // don't restart the lookups if they had succeeded and
+ // the input values weren't changed
+ QObject::connect(&d->peerResolver, SIGNAL(finished(KResolverResults)),
+ this, SLOT(lookupFinishedSlot()));
+ QObject::connect(&d->localResolver, SIGNAL(finished(KResolverResults)),
+ this, SLOT(lookupFinishedSlot()));
+
+ if (d->localResolver.status() <= 0)
+ d->localResolver.start();
+ if (d->peerResolver.status() <= 0)
+ d->peerResolver.start();
+
+ setState(HostLookup);
+ emit stateChanged(HostLookup);
+
+ if (!d->localResolver.isRunning() && !d->peerResolver.isRunning())
+ {
+ // if nothing is running, then the lookup results are still valid
+ // pretend we had done lookup
+ if (blocking())
+ lookupFinishedSlot();
+ else
+ QTimer::singleShot(0, this, SLOT(lookupFinishedSlot()));
+ }
+ else
+ {
+ d->localResults = d->peerResults = KResolverResults();
+ }
+ }
+
+ if (blocking())
+ {
+ // we're in blocking mode operation
+ // wait for the results
+
+ localResolver().wait();
+ peerResolver().wait();
+
+ // lookupFinishedSlot has been called
+ }
+
+ return true;
+}
+
+bool KClientSocketBase::bind(const KResolverEntry& address)
+{
+ if (state() == HostLookup || state() > Connecting)
+ return false;
+
+ if (socketDevice()->bind(address))
+ {
+ resetError();
+
+ // don't set the state or emit signals if we are in a higher state
+ if (state() < Bound)
+ {
+ setState(Bound);
+ emit stateChanged(Bound);
+ emit bound(address);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool KClientSocketBase::connect(const KResolverEntry& address)
+{
+ if (state() == Connected)
+ return true; // to be compliant with the other classes
+ if (state() == HostLookup || state() > Connecting)
+ return false;
+
+ bool ok = socketDevice()->connect(address);
+ copyError();
+
+ if (ok)
+ {
+ SocketState newstate;
+ if (error() == InProgress)
+ newstate = Connecting;
+ else
+ newstate = Connected;
+
+ if (state() < newstate)
+ {
+ setState(newstate);
+ emit stateChanged(newstate);
+ if (error() == NoError)
+ {
+ setFlags(IO_Sequential | IO_Raw | IO_ReadWrite | IO_Open | IO_Async);
+ emit connected(address);
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool KClientSocketBase::disconnect()
+{
+ if (state() != Connected)
+ return false;
+
+ bool ok = socketDevice()->disconnect();
+ copyError();
+
+ if (ok)
+ {
+ setState(Unconnected);
+ emit stateChanged(Unconnected);
+ return true;
+ }
+ return false;
+}
+
+void KClientSocketBase::close()
+{
+ if (state() == Idle)
+ return; // nothing to do
+
+ if (state() == HostLookup)
+ {
+ d->peerResolver.cancel(false);
+ d->localResolver.cancel(false);
+ }
+
+ d->localResults = d->peerResults = KResolverResults();
+
+ socketDevice()->close();
+ setState(Idle);
+ emit stateChanged(Idle);
+ emit closed();
+}
+
+// This function is unlike all the others because it is const
+Q_LONG KClientSocketBase::bytesAvailable() const
+{
+ return socketDevice()->bytesAvailable();
+}
+
+// All the functions below look really alike
+// Should I use a macro to define them?
+
+Q_LONG KClientSocketBase::waitForMore(int msecs, bool *timeout)
+{
+ resetError();
+ Q_LONG retval = socketDevice()->waitForMore(msecs, timeout);
+ if (retval == -1)
+ {
+ copyError();
+ emit gotError(error());
+ }
+ return retval;
+}
+
+Q_LONG KClientSocketBase::readBlock(char *data, Q_ULONG maxlen)
+{
+ resetError();
+ Q_LONG retval = socketDevice()->readBlock(data, maxlen);
+ if (retval == -1)
+ {
+ copyError();
+ emit gotError(error());
+ }
+ return retval;
+}
+
+Q_LONG KClientSocketBase::readBlock(char *data, Q_ULONG maxlen, KSocketAddress& from)
+{
+ resetError();
+ Q_LONG retval = socketDevice()->readBlock(data, maxlen, from);
+ if (retval == -1)
+ {
+ copyError();
+ emit gotError(error());
+ }
+ return retval;
+}
+
+Q_LONG KClientSocketBase::peekBlock(char *data, Q_ULONG maxlen)
+{
+ resetError();
+ Q_LONG retval = socketDevice()->peekBlock(data, maxlen);
+ if (retval == -1)
+ {
+ copyError();
+ emit gotError(error());
+ }
+ return retval;
+}
+
+Q_LONG KClientSocketBase::peekBlock(char *data, Q_ULONG maxlen, KSocketAddress& from)
+{
+ resetError();
+ Q_LONG retval = socketDevice()->peekBlock(data, maxlen, from);
+ if (retval == -1)
+ {
+ copyError();
+ emit gotError(error());
+ }
+ return retval;
+}
+
+Q_LONG KClientSocketBase::writeBlock(const char *data, Q_ULONG len)
+{
+ resetError();
+ Q_LONG retval = socketDevice()->writeBlock(data, len);
+ if (retval == -1)
+ {
+ copyError();
+ emit gotError(error());
+ }
+ return retval;
+}
+
+Q_LONG KClientSocketBase::writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to)
+{
+ resetError();
+ Q_LONG retval = socketDevice()->writeBlock(data, len, to);
+ if (retval == -1)
+ {
+ copyError();
+ emit gotError(error());
+ }
+ return retval;
+}
+
+KSocketAddress KClientSocketBase::localAddress() const
+{
+ return socketDevice()->localAddress();
+}
+
+KSocketAddress KClientSocketBase::peerAddress() const
+{
+ return socketDevice()->peerAddress();
+}
+
+bool KClientSocketBase::emitsReadyRead() const
+{
+ return d->enableRead;
+}
+
+void KClientSocketBase::enableRead(bool enable)
+{
+ QMutexLocker locker(mutex());
+
+ d->enableRead = enable;
+ QSocketNotifier *n = socketDevice()->readNotifier();
+ if (n)
+ n->setEnabled(enable);
+}
+
+bool KClientSocketBase::emitsReadyWrite() const
+{
+ return d->enableWrite;
+}
+
+void KClientSocketBase::enableWrite(bool enable)
+{
+ QMutexLocker locker(mutex());
+
+ d->enableWrite = enable;
+ QSocketNotifier *n = socketDevice()->writeNotifier();
+ if (n)
+ n->setEnabled(enable);
+}
+
+void KClientSocketBase::slotReadActivity()
+{
+ if (d->enableRead)
+ emit readyRead();
+}
+
+void KClientSocketBase::slotWriteActivity()
+{
+ if (d->enableWrite)
+ emit readyWrite();
+}
+
+void KClientSocketBase::lookupFinishedSlot()
+{
+ if (d->peerResolver.isRunning() || d->localResolver.isRunning() || state() != HostLookup)
+ return;
+
+ QObject::disconnect(&d->peerResolver, 0L, this, SLOT(lookupFinishedSlot()));
+ QObject::disconnect(&d->localResolver, 0L, this, SLOT(lookupFinishedSlot()));
+ if (d->peerResolver.status() < 0 || d->localResolver.status() < 0)
+ {
+ setState(Idle); // backtrack
+ setError(IO_LookupError, LookupFailure);
+ emit stateChanged(Idle);
+ emit gotError(LookupFailure);
+ return;
+ }
+
+ d->localResults = d->localResolver.results();
+ d->peerResults = d->peerResolver.results();
+ setState(HostFound);
+ emit stateChanged(HostFound);
+ emit hostFound();
+}
+
+void KClientSocketBase::stateChanging(SocketState newState)
+{
+ if (newState == Connected && socketDevice())
+ {
+ QSocketNotifier *n = socketDevice()->readNotifier();
+ if (n)
+ {
+ n->setEnabled(d->enableRead);
+ QObject::connect(n, SIGNAL(activated(int)), this, SLOT(slotReadActivity()));
+ }
+ else
+ return;
+
+ n = socketDevice()->writeNotifier();
+ if (n)
+ {
+ n->setEnabled(d->enableWrite);
+ QObject::connect(n, SIGNAL(activated(int)), this, SLOT(slotWriteActivity()));
+ }
+ else
+ return;
+ }
+}
+
+void KClientSocketBase::copyError()
+{
+ setError(socketDevice()->status(), socketDevice()->error());
+}
+
+#include "kclientsocketbase.moc"