summaryrefslogtreecommitdiffstats
path: root/kdecore/network
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/network')
-rw-r--r--kdecore/network/Makefile.am59
-rw-r--r--kdecore/network/ipv6blacklist3
-rw-r--r--kdecore/network/kbufferedsocket.cpp414
-rw-r--r--kdecore/network/kbufferedsocket.h253
-rw-r--r--kdecore/network/kclientsocketbase.cpp477
-rw-r--r--kdecore/network/kclientsocketbase.h517
-rw-r--r--kdecore/network/kdatagramsocket.cpp283
-rw-r--r--kdecore/network/kdatagramsocket.h278
-rw-r--r--kdecore/network/khttpproxysocketdevice.cpp281
-rw-r--r--kdecore/network/khttpproxysocketdevice.h122
-rw-r--r--kdecore/network/kiobuffer.h144
-rw-r--r--kdecore/network/kmulticastsocket.h113
-rw-r--r--kdecore/network/kmulticastsocketdevice.h151
-rw-r--r--kdecore/network/knetworkinterface.h46
-rw-r--r--kdecore/network/kresolver.cpp1164
-rw-r--r--kdecore/network/kresolver.h945
-rw-r--r--kdecore/network/kresolver_p.h353
-rw-r--r--kdecore/network/kresolvermanager.cpp822
-rw-r--r--kdecore/network/kresolverstandardworkers.cpp1028
-rw-r--r--kdecore/network/kresolverstandardworkers_p.h111
-rw-r--r--kdecore/network/kresolverworkerbase.cpp150
-rw-r--r--kdecore/network/kresolverworkerbase.h317
-rw-r--r--kdecore/network/kreverseresolver.cpp263
-rw-r--r--kdecore/network/kreverseresolver.h195
-rw-r--r--kdecore/network/kserversocket.cpp413
-rw-r--r--kdecore/network/kserversocket.h435
-rw-r--r--kdecore/network/ksocketaddress.cpp957
-rw-r--r--kdecore/network/ksocketaddress.h912
-rw-r--r--kdecore/network/ksocketbase.cpp327
-rw-r--r--kdecore/network/ksocketbase.h762
-rw-r--r--kdecore/network/ksocketbuffer.cpp329
-rw-r--r--kdecore/network/ksocketbuffer_p.h164
-rw-r--r--kdecore/network/ksocketdevice.cpp886
-rw-r--r--kdecore/network/ksocketdevice.h428
-rw-r--r--kdecore/network/ksockssocketdevice.cpp487
-rw-r--r--kdecore/network/ksockssocketdevice.h129
-rw-r--r--kdecore/network/ksrvresolverworker.cpp257
-rw-r--r--kdecore/network/ksrvresolverworker_p.h85
-rw-r--r--kdecore/network/kstreamsocket.cpp368
-rw-r--r--kdecore/network/kstreamsocket.h249
-rw-r--r--kdecore/network/syssocket.h93
41 files changed, 15770 insertions, 0 deletions
diff --git a/kdecore/network/Makefile.am b/kdecore/network/Makefile.am
new file mode 100644
index 000000000..71ed013d9
--- /dev/null
+++ b/kdecore/network/Makefile.am
@@ -0,0 +1,59 @@
+## Makefile.am for libqt-addon
+
+# this is the program that gets installed. it's name is used for all
+# of the other Makefile.am variables
+noinst_LTLIBRARIES = libkdecorenetwork.la
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes)
+
+# the library search path.
+# convenience lib - no LDFLAGS or LIBADD !
+# Note:
+# ksocketdevice.cpp must appear before any inclusion of ksocketdevice.h
+libkdecorenetwork_la_SOURCES = kresolver.cpp \
+ kresolvermanager.cpp \
+ kresolverworkerbase.cpp \
+ ksocketaddress.cpp \
+ kresolverstandardworkers.cpp \
+ kreverseresolver.cpp \
+ ksocketdevice.cpp \
+ ksocketbase.cpp \
+ kclientsocketbase.cpp \
+ kstreamsocket.cpp \
+ kserversocket.cpp \
+ kdatagramsocket.cpp \
+ khttpproxysocketdevice.cpp \
+ ksockssocketdevice.cpp \
+ kbufferedsocket.cpp \
+ ksocketbuffer.cpp \
+ ksrvresolverworker.cpp
+
+include_HEADERS = kresolver.h \
+ kreverseresolver.h \
+ ksocketaddress.h \
+ ksocketbase.h \
+ ksocketdevice.h \
+ kclientsocketbase.h \
+ kstreamsocket.h \
+ kserversocket.h \
+ kdatagramsocket.h \
+ kmulticastsocketdevice.h \
+ kmulticastsocket.h \
+ knetworkinterface.h \
+ khttpproxysocketdevice.h \
+ ksockssocketdevice.h \
+ kbufferedsocket.h \
+ kiobuffer.h
+noinst_HEADERS = kresolver_p.h \
+ kresolverworkerbase.h \
+ kresolverstandardworkers_p.h \
+ ksocketbuffer_p.h \
+ ksrvresolverworker_p.h \
+ syssocket.h
+
+configdir = $(kde_confdir)
+config_DATA = ipv6blacklist
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
diff --git a/kdecore/network/ipv6blacklist b/kdecore/network/ipv6blacklist
new file mode 100644
index 000000000..f4d4d1252
--- /dev/null
+++ b/kdecore/network/ipv6blacklist
@@ -0,0 +1,3 @@
+.doubleclick.net
+.linebourse.fr
+.banquepopulaire.fr
diff --git a/kdecore/network/kbufferedsocket.cpp b/kdecore/network/kbufferedsocket.cpp
new file mode 100644
index 000000000..7207a9c4f
--- /dev/null
+++ b/kdecore/network/kbufferedsocket.cpp
@@ -0,0 +1,414 @@
+/* -*- 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 <qmutex.h>
+#include <qtimer.h>
+
+#include "ksocketdevice.h"
+#include "ksocketaddress.h"
+#include "ksocketbuffer_p.h"
+#include "kbufferedsocket.h"
+
+using namespace KNetwork;
+using namespace KNetwork::Internal;
+
+class KNetwork::KBufferedSocketPrivate
+{
+public:
+ mutable KSocketBuffer *input, *output;
+
+ KBufferedSocketPrivate()
+ {
+ input = 0L;
+ output = 0L;
+ }
+};
+
+KBufferedSocket::KBufferedSocket(const QString& host, const QString& service,
+ QObject *parent, const char *name)
+ : KStreamSocket(host, service, parent, name),
+ d(new KBufferedSocketPrivate)
+{
+ setInputBuffering(true);
+ setOutputBuffering(true);
+}
+
+KBufferedSocket::~KBufferedSocket()
+{
+ closeNow();
+ delete d->input;
+ delete d->output;
+ delete d;
+}
+
+void KBufferedSocket::setSocketDevice(KSocketDevice* device)
+{
+ KStreamSocket::setSocketDevice(device);
+ device->setBlocking(false);
+}
+
+bool KBufferedSocket::setSocketOptions(int opts)
+{
+ if (opts == Blocking)
+ return false;
+
+ opts &= ~Blocking;
+ return KStreamSocket::setSocketOptions(opts);
+}
+
+void KBufferedSocket::close()
+{
+ if (!d->output || d->output->isEmpty())
+ closeNow();
+ else
+ {
+ setState(Closing);
+ QSocketNotifier *n = socketDevice()->readNotifier();
+ if (n)
+ n->setEnabled(false);
+ emit stateChanged(Closing);
+ }
+}
+
+Q_LONG KBufferedSocket::bytesAvailable() const
+{
+ if (!d->input)
+ return KStreamSocket::bytesAvailable();
+
+ return d->input->length();
+}
+
+Q_LONG KBufferedSocket::waitForMore(int msecs, bool *timeout)
+{
+ Q_LONG retval = KStreamSocket::waitForMore(msecs, timeout);
+ if (d->input)
+ {
+ resetError();
+ slotReadActivity();
+ return bytesAvailable();
+ }
+ return retval;
+}
+
+Q_LONG KBufferedSocket::readBlock(char *data, Q_ULONG maxlen)
+{
+ if (d->input)
+ {
+ if (d->input->isEmpty())
+ {
+ setError(IO_ReadError, WouldBlock);
+ emit gotError(WouldBlock);
+ return -1;
+ }
+ resetError();
+ return d->input->consumeBuffer(data, maxlen);
+ }
+ return KStreamSocket::readBlock(data, maxlen);
+}
+
+Q_LONG KBufferedSocket::readBlock(char *data, Q_ULONG maxlen, KSocketAddress& from)
+{
+ from = peerAddress();
+ return readBlock(data, maxlen);
+}
+
+Q_LONG KBufferedSocket::peekBlock(char *data, Q_ULONG maxlen)
+{
+ if (d->input)
+ {
+ if (d->input->isEmpty())
+ {
+ setError(IO_ReadError, WouldBlock);
+ emit gotError(WouldBlock);
+ return -1;
+ }
+ resetError();
+ return d->input->consumeBuffer(data, maxlen, false);
+ }
+ return KStreamSocket::peekBlock(data, maxlen);
+}
+
+Q_LONG KBufferedSocket::peekBlock(char *data, Q_ULONG maxlen, KSocketAddress& from)
+{
+ from = peerAddress();
+ return peekBlock(data, maxlen);
+}
+
+Q_LONG KBufferedSocket::writeBlock(const char *data, Q_ULONG len)
+{
+ if (state() != Connected)
+ {
+ // cannot write now!
+ setError(IO_WriteError, NotConnected);
+ return -1;
+ }
+
+ if (d->output)
+ {
+ if (d->output->isFull())
+ {
+ setError(IO_WriteError, WouldBlock);
+ emit gotError(WouldBlock);
+ return -1;
+ }
+ resetError();
+
+ // enable notifier to send data
+ QSocketNotifier *n = socketDevice()->writeNotifier();
+ if (n)
+ n->setEnabled(true);
+
+ return d->output->feedBuffer(data, len);
+ }
+
+ return KStreamSocket::writeBlock(data, len);
+}
+
+Q_LONG KBufferedSocket::writeBlock(const char *data, Q_ULONG maxlen,
+ const KSocketAddress&)
+{
+ // ignore the third parameter
+ return writeBlock(data, maxlen);
+}
+
+void KBufferedSocket::enableRead(bool enable)
+{
+ KStreamSocket::enableRead(enable);
+ if (!enable && d->input)
+ {
+ // reenable it
+ QSocketNotifier *n = socketDevice()->readNotifier();
+ if (n)
+ n->setEnabled(true);
+ }
+
+ if (enable && state() != Connected && d->input && !d->input->isEmpty())
+ // this means the buffer is still dirty
+ // allow the signal to be emitted
+ QTimer::singleShot(0, this, SLOT(slotReadActivity()));
+}
+
+void KBufferedSocket::enableWrite(bool enable)
+{
+ KStreamSocket::enableWrite(enable);
+ if (!enable && d->output && !d->output->isEmpty())
+ {
+ // reenable it
+ QSocketNotifier *n = socketDevice()->writeNotifier();
+ if (n)
+ n->setEnabled(true);
+ }
+}
+
+void KBufferedSocket::stateChanging(SocketState newState)
+{
+ if (newState == Connecting || newState == Connected)
+ {
+ // we're going to connect
+ // make sure the buffers are clean
+ if (d->input)
+ d->input->clear();
+ if (d->output)
+ d->output->clear();
+
+ // also, turn on notifiers
+ enableRead(emitsReadyRead());
+ enableWrite(emitsReadyWrite());
+ }
+ KStreamSocket::stateChanging(newState);
+}
+
+void KBufferedSocket::setInputBuffering(bool enable)
+{
+ QMutexLocker locker(mutex());
+ if (!enable)
+ {
+ delete d->input;
+ d->input = 0L;
+ }
+ else if (d->input == 0L)
+ {
+ d->input = new KSocketBuffer;
+ }
+}
+
+KIOBufferBase* KBufferedSocket::inputBuffer()
+{
+ return d->input;
+}
+
+void KBufferedSocket::setOutputBuffering(bool enable)
+{
+ QMutexLocker locker(mutex());
+ if (!enable)
+ {
+ delete d->output;
+ d->output = 0L;
+ }
+ else if (d->output == 0L)
+ {
+ d->output = new KSocketBuffer;
+ }
+}
+
+KIOBufferBase* KBufferedSocket::outputBuffer()
+{
+ return d->output;
+}
+
+Q_ULONG KBufferedSocket::bytesToWrite() const
+{
+ if (!d->output)
+ return 0;
+
+ return d->output->length();
+}
+
+void KBufferedSocket::closeNow()
+{
+ KStreamSocket::close();
+ if (d->output)
+ d->output->clear();
+}
+
+bool KBufferedSocket::canReadLine() const
+{
+ if (!d->input)
+ return false;
+
+ return d->input->canReadLine();
+}
+
+QCString KBufferedSocket::readLine()
+{
+ return d->input->readLine();
+}
+
+void KBufferedSocket::waitForConnect()
+{
+ if (state() != Connecting)
+ return; // nothing to be waited on
+
+ KStreamSocket::setSocketOptions(socketOptions() | Blocking);
+ connectionEvent();
+ KStreamSocket::setSocketOptions(socketOptions() & ~Blocking);
+}
+
+void KBufferedSocket::slotReadActivity()
+{
+ if (d->input && state() == Connected)
+ {
+ mutex()->lock();
+ Q_LONG len = d->input->receiveFrom(socketDevice());
+
+ if (len == -1)
+ {
+ if (socketDevice()->error() != WouldBlock)
+ {
+ // nope, another error!
+ copyError();
+ mutex()->unlock();
+ emit gotError(error());
+ closeNow(); // emits closed
+ return;
+ }
+ }
+ else if (len == 0)
+ {
+ // remotely closed
+ setError(IO_ReadError, RemotelyDisconnected);
+ mutex()->unlock();
+ emit gotError(error());
+ closeNow(); // emits closed
+ return;
+ }
+
+ // no error
+ mutex()->unlock();
+ }
+
+ if (state() == Connected)
+ KStreamSocket::slotReadActivity(); // this emits readyRead
+ else if (emitsReadyRead()) // state() != Connected
+ {
+ if (d->input && !d->input->isEmpty())
+ {
+ // buffer isn't empty
+ // keep emitting signals till it is
+ QTimer::singleShot(0, this, SLOT(slotReadActivity()));
+ emit readyRead();
+ }
+ }
+}
+
+void KBufferedSocket::slotWriteActivity()
+{
+ if (d->output && !d->output->isEmpty() &&
+ (state() == Connected || state() == Closing))
+ {
+ mutex()->lock();
+ Q_LONG len = d->output->sendTo(socketDevice());
+
+ if (len == -1)
+ {
+ if (socketDevice()->error() != WouldBlock)
+ {
+ // nope, another error!
+ copyError();
+ mutex()->unlock();
+ emit gotError(error());
+ closeNow();
+ return;
+ }
+ }
+ else if (len == 0)
+ {
+ // remotely closed
+ setError(IO_ReadError, RemotelyDisconnected);
+ mutex()->unlock();
+ emit gotError(error());
+ closeNow();
+ return;
+ }
+
+ if (d->output->isEmpty())
+ // deactivate the notifier until we have something to send
+ // writeNotifier can't return NULL here
+ socketDevice()->writeNotifier()->setEnabled(false);
+
+ mutex()->unlock();
+ emit bytesWritten(len);
+ }
+
+ if (state() != Closing)
+ KStreamSocket::slotWriteActivity();
+ else if (d->output && d->output->isEmpty() && state() == Closing)
+ {
+ KStreamSocket::close(); // finished sending data
+ }
+}
+
+#include "kbufferedsocket.moc"
diff --git a/kdecore/network/kbufferedsocket.h b/kdecore/network/kbufferedsocket.h
new file mode 100644
index 000000000..b0b99f8d1
--- /dev/null
+++ b/kdecore/network/kbufferedsocket.h
@@ -0,0 +1,253 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 Thiago Macieira <thiago@kde.org>
+ *
+ *
+ * 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.
+ */
+
+#ifndef KBUFFEREDSOCKET_H
+#define KBUFFEREDSOCKET_H
+
+#include <qobject.h>
+#include <qcstring.h>
+#include <qvaluelist.h>
+#include "kstreamsocket.h"
+#include <kdelibs_export.h>
+
+class KIOBufferBase;
+
+namespace KNetwork {
+
+class KBufferedSocketPrivate;
+/** @class KBufferedSocket kbufferedsocket.h kbufferedsocket.h
+ * @brief Buffered stream sockets.
+ *
+ * This class allows the user to create and operate buffered stream sockets
+ * such as those used in most Internet connections. This class is
+ * also the one that resembles the most to the old @ref QSocket
+ * implementation.
+ *
+ * Objects of this type operate only in non-blocking mode. A call to
+ * setBlocking(true) will result in an error.
+ *
+ * @note Buffered sockets only make sense if you're using them from
+ * the main (event-loop) thread. This is actually a restriction
+ * imposed by Qt's QSocketNotifier. If you want to use a socket
+ * in an auxiliary thread, please use KStreamSocket.
+ *
+ * @see KNetwork::KStreamSocket, KNetwork::KServerSocket
+ * @author Thiago Macieira <thiago@kde.org>
+ */
+class KDECORE_EXPORT KBufferedSocket: public KStreamSocket
+{
+ Q_OBJECT
+public:
+ /**
+ * Default constructor.
+ *
+ * @param node destination host
+ * @param service destination service to connect to
+ * @param parent the parent object for this object
+ * @param name the internal name for this object
+ */
+ KBufferedSocket(const QString& node = QString::null, const QString& service = QString::null,
+ QObject* parent = 0L, const char *name = 0L);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KBufferedSocket();
+
+ /**
+ * Be sure to catch new devices.
+ */
+ virtual void setSocketDevice(KSocketDevice* device);
+
+protected:
+ /**
+ * Buffered sockets can only operate in non-blocking mode.
+ */
+ virtual bool setSocketOptions(int opts);
+
+public:
+ /**
+ * Closes the socket for new data, but allow data that had been buffered
+ * for output with @ref writeBlock to be still be written.
+ *
+ * @sa closeNow
+ */
+ virtual void close();
+
+ /**
+ * Make use of the buffers.
+ */
+ virtual Q_LONG bytesAvailable() const;
+
+ /**
+ * Make use of buffers.
+ */
+ virtual Q_LONG waitForMore(int msecs, bool *timeout = 0L);
+
+ /**
+ * Reads data from the socket. Make use of buffers.
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen);
+
+ /**
+ * @overload
+ * Reads data from a socket.
+ *
+ * The @p from parameter is always set to @ref peerAddress()
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen, KSocketAddress& from);
+
+ /**
+ * Peeks data from the socket.
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen);
+
+ /**
+ * @overload
+ * Peeks data from the socket.
+ *
+ * The @p from parameter is always set to @ref peerAddress()
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen, KSocketAddress &from);
+
+ /**
+ * Writes data to the socket.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len);
+
+ /**
+ * @overload
+ * Writes data to the socket.
+ *
+ * The @p to parameter is discarded.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to);
+
+ /**
+ * Catch changes.
+ */
+ virtual void enableRead(bool enable);
+
+ /**
+ * Catch changes.
+ */
+ virtual void enableWrite(bool enable);
+
+ /**
+ * Sets the use of input buffering.
+ */
+ void setInputBuffering(bool enable);
+
+ /**
+ * Retrieves the input buffer object.
+ */
+ KIOBufferBase* inputBuffer();
+
+ /**
+ * Sets the use of output buffering.
+ */
+ void setOutputBuffering(bool enable);
+
+ /**
+ * Retrieves the output buffer object.
+ */
+ KIOBufferBase* outputBuffer();
+
+ /**
+ * Returns the length of the output buffer.
+ */
+ virtual Q_ULONG bytesToWrite() const;
+
+ /**
+ * Closes the socket and discards any output data that had been buffered
+ * with @ref writeBlock but that had not yet been written.
+ *
+ * @sa close
+ */
+ virtual void closeNow();
+
+ /**
+ * Returns true if a line can be read with @ref readLine
+ */
+ bool canReadLine() const;
+
+ /**
+ * Reads a line of data from the socket buffers.
+ */
+ QCString readLine();
+
+ // KDE4: make virtual, add timeout to match the Qt4 signature
+ // and move to another class up the hierarchy
+ /**
+ * Blocks until the connection is either established, or completely
+ * failed.
+ */
+ void waitForConnect();
+
+protected:
+ /**
+ * Catch connection to clear the buffers
+ */
+ virtual void stateChanging(SocketState newState);
+
+protected slots:
+ /**
+ * Slot called when there's read activity.
+ */
+ virtual void slotReadActivity();
+
+ /**
+ * Slot called when there's write activity.
+ */
+ virtual void slotWriteActivity();
+
+signals:
+ /**
+ * This signal is emitted whenever data is written.
+ */
+ void bytesWritten(int bytes);
+
+private:
+ KBufferedSocket(const KBufferedSocket&);
+ KBufferedSocket& operator=(const KBufferedSocket&);
+
+ KBufferedSocketPrivate *d;
+
+public:
+ // KDE4: remove this function
+ /**
+ * @deprecated
+ * Closes the socket.
+ *
+ * This function is provided to ease porting from KExtendedSocket,
+ * which required a call to reset() in order to be able to connect again
+ * using the same device. This is not necessary in KBufferedSocket any more.
+ */
+ inline void reset()
+ { closeNow(); }
+};
+
+} // namespace KNetwork
+
+#endif
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"
diff --git a/kdecore/network/kclientsocketbase.h b/kdecore/network/kclientsocketbase.h
new file mode 100644
index 000000000..ccd94994a
--- /dev/null
+++ b/kdecore/network/kclientsocketbase.h
@@ -0,0 +1,517 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KCLIENTSOCKETBASE_H
+#define KCLIENTSOCKETBASE_H
+
+#include <qobject.h>
+#include <qstring.h>
+
+#include "ksocketbase.h"
+#include "kresolver.h"
+#include <kdelibs_export.h>
+
+namespace KNetwork {
+
+class KClientSocketBasePrivate;
+/** @class KClientSocketBase kclientsocketbase.h kclientsocketbase.h
+ * @brief Abstract client socket class.
+ *
+ * This class provides the base functionality for client sockets,
+ * such as, and especially, name resolution and signals.
+ *
+ * @note This class is abstract. If you're looking for a normal,
+ * client socket class, see @ref KStreamSocket and KBufferedSocket
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KClientSocketBase : public QObject, public KActiveSocketBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Socket states.
+ *
+ * These are the possible states for a KClientSocketBase:
+ * - Idle: socket is not connected
+ * - HostLookup: socket is doing host lookup prior to connecting
+ * - HostFound: name lookup is complete
+ * - Bound: the socket is locally bound
+ * - Connecting: socket is attempting connection
+ * - Open: socket is open
+ * - Connected (=Open): socket is connected
+ * - Connection (=Open): yet another name for a connected socket
+ * - Closing: socket is shutting down
+ *
+ * Whenever the socket state changes, the @ref stateChanged(int) signal
+ * will be emitted.
+ */
+ enum SocketState
+ {
+ Idle,
+ HostLookup,
+ HostFound,
+ Bound,
+ Connecting,
+ Open,
+ Closing,
+
+ Unconnected = Bound,
+ Connected = Open,
+ Connection = Open
+ };
+
+public:
+ /**
+ * Default constructor.
+ *
+ * @param parent the parent QObject object
+ * @param name the name of this object
+ */
+ KClientSocketBase(QObject* parent, const char *name);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KClientSocketBase();
+
+ /**
+ * Returns the current state for this socket.
+ * @see SocketState
+ */
+ SocketState state() const;
+
+protected:
+ /**
+ * Sets the socket options. Reimplemented from KSocketBase.
+ */
+ virtual bool setSocketOptions(int opts);
+
+public:
+ /**
+ * Returns the internal KResolver object used for
+ * looking up the peer host name and service.
+ *
+ * This can be used to set extra options to the
+ * lookup process other than the default values, as well
+ * as obtaining the error codes in case of lookup failure.
+ */
+ KResolver& peerResolver() const;
+
+ /**
+ * Returns the internal list of resolved results for the peer address.
+ */
+ const KResolverResults& peerResults() const;
+
+ /**
+ * Returns the internal KResolver object used for
+ * looking up the local host name and service.
+ *
+ * This can be used to set extra options to the
+ * lookup process other than the default values, as well
+ * as obtaining the error codes in case of lookup failure.
+ */
+ KResolver& localResolver() const;
+
+ /**
+ * Returns the internal list of resolved results for the local address.
+ */
+ const KResolverResults& localResults() const;
+
+ /**
+ * Enables or disables name resolution. If this flag is set to true,
+ * @ref bind and @ref connect operations will trigger name lookup
+ * operations (i.e., converting a hostname into its binary form).
+ * If the flag is set to false, those operations will instead
+ * try to convert a string representation of an address without
+ * attempting name resolution.
+ *
+ * This is useful, for instance, when IP addresses are in
+ * their string representation (such as "1.2.3.4") or come
+ * from other sources like @ref KSocketAddress.
+ *
+ * @param enable whether to enable
+ */
+ void setResolutionEnabled(bool enable);
+
+ /**
+ * Sets the allowed families for the resolutions.
+ *
+ * @param families the families that we want/accept
+ * @see KResolver::SocketFamilies for possible values
+ */
+ void setFamily(int families);
+
+ /**
+ * Starts the lookup for peer and local hostnames as
+ * well as their services.
+ *
+ * If the blocking mode for this object is on, this function will
+ * wait for the lookup results to be available (by calling the
+ * @ref KResolver::wait method on the resolver objects).
+ *
+ * When the lookup is done, the signal @ref hostFound will be
+ * emitted (only once, even if we're doing a double lookup).
+ * If the lookup failed (for any of the two lookups) the
+ * @ref gotError signal will be emitted with the appropriate
+ * error condition (see @ref KSocketBase::SocketError).
+ *
+ * This function returns true on success and false on error. Note that
+ * this is not the lookup result!
+ */
+ virtual bool lookup();
+
+ /**
+ * Binds this socket to the given nodename and service,
+ * or use the default ones if none are given.
+ *
+ * Upon successful binding, the @ref bound signal will be
+ * emitted. If an error is found, the @ref gotError
+ * signal will be emitted.
+ *
+ * @note Due to the internals of the name lookup and binding
+ * mechanism, some (if not most) implementations of this function
+ * do not actually bind the socket until the connection
+ * is requested (see @ref connect). They only set the values
+ * for future reference.
+ *
+ * This function returns true on success.
+ *
+ * @param node the nodename
+ * @param service the service
+ */
+ virtual bool bind(const QString& node = QString::null,
+ const QString& service = QString::null) = 0;
+
+ /**
+ * Reimplemented from KSocketBase. Connect this socket to this
+ * specific address.
+ *
+ * Unlike @ref bind(const QString&, const QString&) above, this function
+ * really does bind the socket. No lookup is performed. The @ref bound
+ * signal will be emitted.
+ */
+ virtual bool bind(const KResolverEntry& address);
+
+ /**
+ * Attempts to connect to the these hostname and service,
+ * or use the default ones if none are given. If a connection attempt
+ * is already in progress, check on its state and set the error status
+ * (NoError or InProgress).
+ *
+ * If the blocking mode for this object is on, this function will only
+ * return when all the resolved peer addresses have been tried or when
+ * a connection is established.
+ *
+ * Upon successfully connecting, the @ref connected signal
+ * will be emitted. If an error is found, the @ref gotError
+ * signal will be emitted.
+ *
+ * @par Note for derived classes:
+ * Derived classes must implement this function. The implementation
+ * will set the parameters for the lookup (using the peer KResolver
+ * object) and call @ref lookup to start it.
+ *
+ * @par
+ * The implementation should use the @ref hostFound
+ * signal to be notified of the completion of the lookup process and
+ * then proceed to start the connection itself. Care should be taken
+ * regarding the value of @ref blocking flag.
+ *
+ * @param node the nodename
+ * @param service the service
+ */
+ virtual bool connect(const QString& node = QString::null,
+ const QString& service = QString::null) = 0;
+
+ /**
+ * @overload
+ * Reimplemented from KSocketBase.
+ */
+ virtual bool connect(const KResolverEntry& address);
+
+ /**
+ * @deprecated
+ * This is a convenience function provided to ease migrating from
+ * Qt 3.x's QSocket class.
+ */
+ inline void connectToHost(const QString& host, Q_UINT16 port)
+ { connect(host, QString::number(port)); }
+
+ /**
+ * Disconnects the socket.
+ * Note that not all socket types can disconnect.
+ */
+ virtual bool disconnect();
+
+ /**
+ * Opens the socket. Reimplemented from QIODevice.
+ *
+ * You should not call this function; instead, use @ref connect
+ */
+ virtual inline bool open(int)
+ { return connect(); }
+
+ /**
+ * Closes the socket. Reimplemented from QIODevice.
+ *
+ * The closing of the socket causes the emission of the
+ * signal @ref closed.
+ */
+ virtual void close();
+
+ /**
+ * This call is not supported on sockets. Reimplemented from QIODevice.
+ */
+ virtual void flush()
+ { }
+
+ /**
+ * Returns the number of bytes available on this socket.
+ * Reimplemented from KSocketBase.
+ */
+ virtual Q_LONG bytesAvailable() const;
+
+ /**
+ * Waits for more data. Reimplemented from KSocketBase.
+ */
+ virtual Q_LONG waitForMore(int msecs, bool *timeout = 0L);
+
+ /**
+ * Reads data from a socket. Reimplemented from KSocketBase.
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen);
+
+ /**
+ * @overload
+ * Reads data from a socket. Reimplemented from KSocketBase.
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen, KSocketAddress& from);
+
+ /**
+ * Peeks data from the socket. Reimplemented from KSocketBase.
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen);
+
+ /**
+ * @overload
+ * Peeks data from the socket. Reimplemented from KSocketBase.
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen, KSocketAddress &from);
+
+ /**
+ * Writes data to the socket. Reimplemented from KSocketBase.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len);
+
+ /**
+ * @overload
+ * Writes data to the socket. Reimplemented from KSocketBase.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to);
+
+ /**
+ * Returns the local socket address. Reimplemented from KSocketBase.
+ */
+ virtual KSocketAddress localAddress() const;
+
+ /**
+ * Returns the peer socket address. Reimplemented from KSocketBase.
+ */
+ virtual KSocketAddress peerAddress() const;
+
+ /**
+ * Returns true if the readyRead signal is set to be emitted.
+ */
+ bool emitsReadyRead() const;
+
+ /**
+ * Enables the emission of the readyRead signal.
+ * By default, this signal is enabled.
+ *
+ * @param enable whether to enable the signal
+ */
+ virtual void enableRead(bool enable);
+
+ /**
+ * Returns true if the readyWrite signal is set to be emitted.
+ */
+ bool emitsReadyWrite() const;
+
+ /**
+ * Enables the emission of the readyWrite signal.
+ * By default, this signal is disabled.
+ *
+ * @param enable whether to enable the signal
+ */
+ virtual void enableWrite(bool enable);
+
+protected slots:
+ // protected slots
+
+ /**
+ * This slot is connected to the read notifier's signal meaning
+ * the socket can read more data.
+ *
+ * The default implementation only emits the readyRead signal.
+ *
+ * Override if your class requires processing of incoming
+ * data.
+ */
+ virtual void slotReadActivity();
+
+ /**
+ * This slot is connected to the write notifier's signal
+ * meaning the socket can write more data.
+ *
+ * The default implementation only emits the readyWrite signal.
+ *
+ * Override if your class writes data from another source
+ * (like a buffer).
+ */
+ virtual void slotWriteActivity();
+
+private slots:
+ void lookupFinishedSlot();
+
+signals:
+ /**
+ * This signal is emitted whenever the socket state changes.
+ *
+ * Note: do not delete this object inside the slot called by this
+ * signal.
+ *
+ * @param newstate the new state of the socket object
+ */
+ void stateChanged(int newstate);
+
+ /**
+ * This signal is emitted when this object finds an error.
+ * The @p code parameter contains the error code that can
+ * also be found by calling @ref error.
+ */
+ void gotError(int code);
+
+ /**
+ * This signal is emitted when the lookup is successfully completed.
+ */
+ void hostFound();
+
+ /**
+ * This signal is emitted when the socket successfully binds
+ * to an address.
+ *
+ * @param local the local address we bound to
+ */
+ void bound(const KResolverEntry& local);
+
+ /**
+ * This signal is emitted when the socket is about to connect
+ * to an address (but before doing so).
+ *
+ * The @p skip parameter can be used to make the loop skip this address.
+ * Its value is initially false: change it to true if you want to
+ * skip the current address (as given by @p remote).
+ *
+ * This function is also useful if one wants to reset the timeout.
+ *
+ * @param remote the address we're about to connect to
+ * @param skip set to true if you want to skip this address
+ * @note if the connection is successful, the @ref connected signal will be
+ * emitted.
+ */
+ void aboutToConnect(const KResolverEntry& remote, bool& skip);
+
+ /**
+ * This socket is emitted when the socket successfully connects
+ * to a remote address.
+ *
+ * @param remote the remote address we did connect to
+ */
+ void connected(const KResolverEntry& remote);
+
+ /**
+ * This signal is emitted when the socket completes the
+ * closing/shut down process.
+ */
+ void closed();
+
+ /**
+ * This signal is emitted whenever the socket is ready for
+ * reading -- i.e., there is data to be read in the buffers.
+ * The subsequent read operation is guaranteed to be non-blocking.
+ *
+ * You can toggle the emission of this signal with the @ref enableRead
+ * function. This signal is by default enabled.
+ */
+ void readyRead();
+
+ /**
+ * This signal is emitted whenever the socket is ready for
+ * writing -- i.e., whenever there's space available in the buffers
+ * to receive more data. The subsequent write operation is
+ * guaranteed to be non-blocking.
+ *
+ * You can toggle the emission of this signal with the @ref enableWrite
+ * function. This signal is by default disabled. You will
+ * want to disable this signal after the first reception, since
+ * it'll probably fire at every event loop.
+ */
+ void readyWrite();
+
+protected:
+ /**
+ * Sets the socket state to @p state. This function does not
+ * emit the @ref stateChanged signal.
+ */
+ void setState(SocketState state);
+
+ /**
+ * This function is called by @ref setState whenever the state
+ * changes. You should override it if you need to specify any
+ * actions to be done when the state changes.
+ *
+ * The default implementation acts for these states only:
+ * - Connected: it sets up the socket notifiers to fire readyRead and
+ * readyWrite signals.
+ */
+ virtual void stateChanging(SocketState newState);
+
+ /**
+ * Convenience function to set this object's error code to match
+ * that of the socket device.
+ */
+ void copyError();
+
+private:
+ KClientSocketBase(const KClientSocketBase&);
+ KClientSocketBase& operator=(const KClientSocketBase&);
+
+ KClientSocketBasePrivate *d;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/kdatagramsocket.cpp b/kdecore/network/kdatagramsocket.cpp
new file mode 100644
index 000000000..a0d3bc05d
--- /dev/null
+++ b/kdecore/network/kdatagramsocket.cpp
@@ -0,0 +1,283 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003,2004 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 <sys/types.h>
+#include <sys/socket.h>
+
+#include "ksocketaddress.h"
+#include "kresolver.h"
+#include "ksocketdevice.h"
+#include "kdatagramsocket.h"
+
+using namespace KNetwork;
+
+/*
+ * TODO:
+ *
+ * don't use signals and slots to track state changes: use stateChanging
+ *
+ */
+
+KDatagramSocket::KDatagramSocket(QObject* parent, const char *name)
+ : KClientSocketBase(parent, name), d(0L)
+{
+ peerResolver().setFamily(KResolver::KnownFamily);
+ localResolver().setFamily(KResolver::KnownFamily);
+
+ peerResolver().setSocketType(SOCK_DGRAM);
+ localResolver().setSocketType(SOCK_DGRAM);
+
+ localResolver().setFlags(KResolver::Passive);
+
+ // QObject::connect(localResolver(), SIGNAL(finished(KResolverResults)),
+ // this, SLOT(lookupFinishedLocal()));
+ QObject::connect(&peerResolver(), SIGNAL(finished(KResolverResults)),
+ this, SLOT(lookupFinishedPeer()));
+ QObject::connect(this, SIGNAL(hostFound()), this, SLOT(lookupFinishedLocal()));
+}
+
+KDatagramSocket::~KDatagramSocket()
+{
+ // KClientSocketBase's destructor closes the socket
+
+ //delete d;
+}
+
+bool KDatagramSocket::bind(const QString& node, const QString& service)
+{
+ if (state() >= Bound)
+ return false;
+
+ if (localResolver().isRunning())
+ localResolver().cancel(false);
+
+ // no, we must do a host lookup
+ localResolver().setAddress(node, service);
+
+ if (!lookup())
+ return false;
+
+ // see if lookup has finished already
+ // this also catches blocking mode, since lookup has to finish
+ // its processing if we're in blocking mode
+ if (state() > HostLookup)
+ return doBind();
+
+ return true;
+}
+
+bool KDatagramSocket::connect(const QString& node, const QString& service)
+{
+ if (state() >= Connected)
+ return true; // already connected
+
+ if (peerResolver().nodeName() != node ||
+ peerResolver().serviceName() != service)
+ peerResolver().setAddress(node, service); // this resets the resolver's state
+
+ // KClientSocketBase::lookup only works if the state is Idle or HostLookup
+ // therefore, we store the old state, call the lookup routine and then set
+ // it back.
+ SocketState s = state();
+ setState(s == Connecting ? HostLookup : Idle);
+ bool ok = lookup();
+ if (!ok)
+ {
+ setState(s); // go back
+ return false;
+ }
+
+ // check if lookup is finished
+ // if we're in blocking mode, then the lookup has to be finished
+ if (state() == HostLookup)
+ {
+ // it hasn't finished
+ setState(Connecting);
+ emit stateChanged(Connecting);
+ return true;
+ }
+
+ // it has to be finished here
+ if (state() != Connected)
+ {
+ setState(Connecting);
+ emit stateChanged(Connecting);
+ lookupFinishedPeer();
+ }
+
+ return state() == Connected;
+}
+
+KDatagramPacket KDatagramSocket::receive()
+{
+ Q_LONG size = bytesAvailable();
+ if (size == 0)
+ {
+ // nothing available yet to read
+ // wait for data if we're not blocking
+ if (blocking())
+ socketDevice()->waitForMore(-1); // wait forever
+ else
+ {
+ // mimic error
+ setError(IO_ReadError, WouldBlock);
+ emit gotError(WouldBlock);
+ return KDatagramPacket();
+ }
+
+ // try again
+ size = bytesAvailable();
+ }
+
+ QByteArray data(size);
+ KSocketAddress address;
+
+ // now do the reading
+ size = readBlock(data.data(), size, address);
+ if (size < 0)
+ // error has been set
+ return KDatagramPacket();
+
+ data.resize(size); // just to be sure
+ return KDatagramPacket(data, address);
+}
+
+Q_LONG KDatagramSocket::send(const KDatagramPacket& packet)
+{
+ return writeBlock(packet.data(), packet.size(), packet.address());
+}
+
+Q_LONG KDatagramSocket::writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to)
+{
+ if (to.family() != AF_UNSPEC)
+ {
+ // make sure the socket is open at this point
+ if (!socketDevice()->isOpen())
+ // error handling will happen below
+ socketDevice()->create(to.family(), SOCK_DGRAM, 0);
+ }
+ return KClientSocketBase::writeBlock(data, len, to);
+}
+
+void KDatagramSocket::lookupFinishedLocal()
+{
+ // bind lookup has finished and succeeded
+ // state() == HostFound
+
+ if (!doBind())
+ return; // failed binding
+
+ if (peerResults().count() > 0)
+ {
+ setState(Connecting);
+ emit stateChanged(Connecting);
+
+ lookupFinishedPeer();
+ }
+}
+
+void KDatagramSocket::lookupFinishedPeer()
+{
+ // this function is called by lookupFinishedLocal above
+ // and is also connected to a signal
+ // so it might be called twice.
+
+ if (state() != Connecting)
+ return;
+
+ if (peerResults().count() == 0)
+ {
+ setState(Unconnected);
+ emit stateChanged(Unconnected);
+ return;
+ }
+
+ KResolverResults::ConstIterator it = peerResults().begin();
+ for ( ; it != peerResults().end(); ++it)
+ if (connect(*it))
+ {
+ // weee, we connected
+
+ setState(Connected); // this sets up signals
+ //setupSignals(); // setState sets up the signals
+
+ emit stateChanged(Connected);
+ emit connected(*it);
+ return;
+ }
+
+ // no connection
+ copyError();
+ setState(Unconnected);
+ emit stateChanged(Unconnected);
+ emit gotError(error());
+}
+
+bool KDatagramSocket::doBind()
+{
+ if (localResults().count() == 0)
+ return true;
+ if (state() >= Bound)
+ return true; // already bound
+
+ KResolverResults::ConstIterator it = localResults().begin();
+ for ( ; it != localResults().end(); ++it)
+ if (bind(*it))
+ {
+ // bound
+ setupSignals();
+ return true;
+ }
+
+ // not bound
+ // no need to set state since it can only be HostFound already
+ copyError();
+ emit gotError(error());
+ return false;
+}
+
+void KDatagramSocket::setupSignals()
+{
+ QSocketNotifier *n = socketDevice()->readNotifier();
+ if (n)
+ {
+ n->setEnabled(emitsReadyRead());
+ QObject::connect(n, SIGNAL(activated(int)), this, SLOT(slotReadActivity()));
+ }
+ else
+ return;
+
+ n = socketDevice()->writeNotifier();
+ if (n)
+ {
+ n->setEnabled(emitsReadyWrite());
+ QObject::connect(n, SIGNAL(activated(int)), this, SLOT(slotWriteActivity()));
+ }
+ else
+ return;
+}
+
+#include "kdatagramsocket.moc"
diff --git a/kdecore/network/kdatagramsocket.h b/kdecore/network/kdatagramsocket.h
new file mode 100644
index 000000000..31ccf9af7
--- /dev/null
+++ b/kdecore/network/kdatagramsocket.h
@@ -0,0 +1,278 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KDATAGRAMSOCKET_H
+#define KDATAGRAMSOCKET_H
+
+#include <qcstring.h>
+
+#include "ksocketaddress.h"
+#include "kclientsocketbase.h"
+
+namespace KNetwork {
+
+class KResolverEntry;
+
+/**
+ * @class KDatagramPacket kdatagramsocket.h kdatagramsocket.h
+ * @brief one datagram
+ *
+ * This object represents one datagram of data sent or received through
+ * a datagram socket (as @ref KDatagramSocket or derived classes). A datagram
+ * consists of data as well as a network address associated (whither to send
+ * the data or whence it came).
+ *
+ * This is a lightweight class. Data is stored in a @ref QByteArray, which means
+ * that it is explicitly shared.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KDatagramPacket
+{
+ QByteArray m_data;
+ KSocketAddress m_address;
+
+public:
+ /**
+ * Default constructor.
+ */
+ KDatagramPacket()
+ { }
+
+ /**
+ * Constructs the datagram with the specified content.
+ */
+ KDatagramPacket(const QByteArray& content)
+ : m_data(content)
+ { }
+
+ /**
+ * Constructs the datagram with the specified content.
+ *
+ * @see setData for information on data sharing.
+ */
+ KDatagramPacket(const char* content, uint length)
+ { setData(content, length); }
+
+ /**
+ * Constructs the datagram with the specified content and address.
+ */
+ KDatagramPacket(const QByteArray& content, const KSocketAddress& addr)
+ : m_data(content), m_address(addr)
+ { }
+
+ /**
+ * Constructs the datagram with the specified content and address.
+ */
+ KDatagramPacket(const char *content, uint length, const KSocketAddress& addr)
+ : m_address(addr)
+ { setData(content, length); }
+
+ /**
+ * Copy constructor. Note that data is explicitly shared.
+ */
+ KDatagramPacket(const KDatagramPacket& other)
+ { *this = other; }
+
+ /**
+ * Destructor. Non-virtual.
+ */
+ ~KDatagramPacket()
+ { }
+
+ /**
+ * Returns the data.
+ */
+ const QByteArray& data() const
+ { return m_data; }
+
+ /**
+ * Returns the data length.
+ */
+ uint length() const
+ { return m_data.size(); }
+
+ /**
+ * Returns the data length.
+ */
+ uint size() const
+ { return m_data.size(); }
+
+ /**
+ * Returns true if this object is empty.
+ */
+ bool isEmpty() const
+ { return m_data.isEmpty(); }
+
+ /**
+ * Returns true if this object is null.
+ */
+ bool isNull() const
+ { return m_data.isNull(); }
+
+ /**
+ * Returns the socket address
+ */
+ const KSocketAddress& address() const
+ { return m_address; }
+
+ /**
+ * Sets the address stored to the given value.
+ */
+ void setAddress(const KSocketAddress& addr)
+ { m_address = addr; }
+
+ /**
+ * Detaches our data from a shared pool.
+ * @see QByteArray::detach
+ */
+ void detach()
+ { m_data.detach(); }
+
+ /**
+ * Sets the data to the given value. Data is explicitly shared.
+ */
+ void setData(const QByteArray& data)
+ { m_data = data; }
+
+ /**
+ * Sets the data to the given buffer and size.
+ */
+ void setData(const char* data, uint length)
+ { m_data.duplicate(data, length); }
+};
+
+class KDatagramSocketPrivate;
+/**
+ * @class KDatagramSocket kdatagramsocket.h kdatagramsocket.h
+ * @brief A socket that operates on datagrams.
+ *
+ * Unlike @ref KStreamSocket, which operates on a connection-based stream
+ * socket (generally TCP), this class and its descendants operates on datagrams,
+ * which are normally connectionless.
+ *
+ * This class in specific provides easy access to the system's connectionless
+ * SOCK_DGRAM sockets.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KDatagramSocket: public KClientSocketBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Default constructor.
+ */
+ KDatagramSocket(QObject* parent = 0L, const char *name = 0L);
+
+ /**
+ * Destructor. This closes the socket.
+ */
+ virtual ~KDatagramSocket();
+
+ /**
+ * Performs host lookups.
+ */
+ // virtual bool lookup();
+
+ /**
+ * Binds this socket to the given address. If the socket is blocking,
+ * the socket will be bound when this function returns.
+ *
+ * Note that binding a socket is not necessary to be able to send datagrams.
+ * Some protocol families will use anonymous source addresses, while others
+ * will allocate an address automatically.
+ */
+ virtual bool bind(const QString& node = QString::null,
+ const QString& service = QString::null);
+
+ /**
+ * @overload
+ * Binds this socket to the given address.
+ */
+ virtual bool bind(const KResolverEntry& entry)
+ { return KClientSocketBase::bind(entry); }
+
+ /**
+ * "Connects" this socket to the given address. Note that connecting
+ * a datagram socket normally does not establish a permanent connection
+ * with the peer nor normally returns an error in case of failure.
+ *
+ * Connecting means only to designate the given address as the default
+ * destination address for datagrams sent without destination addresses
+ * ( writeBlock(const char *, Q_ULONG) ).
+ *
+ * @note Calling connect will not cause the socket to be bound. You have
+ * to call @ref bind explicitly.
+ */
+ virtual bool connect(const QString& node = QString::null,
+ const QString& service = QString::null);
+
+ /**
+ * @overload
+ * "Connects" this socket to the given address.
+ */
+ virtual bool connect(const KResolverEntry& entry)
+ { return KClientSocketBase::connect(entry); }
+
+ /**
+ * Writes data to the socket. Reimplemented from KClientSocketBase.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to);
+
+ /**
+ * Receives one datagram from the stream. The reading process is guaranteed
+ * to be atomical and not lose data from the packet.
+ *
+ * If nothing could be read, a null object will be returned.
+ */
+ virtual KDatagramPacket receive();
+
+ /**
+ * Sends one datagram into the stream. The destination address must be
+ * set if this socket has not been connected (see @ref connect).
+ *
+ * The data in this packet will be sent only in one single datagram. If the
+ * system cannot send it like that, this function will fail. So, please take
+ * into consideration the datagram size limits.
+ *
+ * @returns the number of bytes written or -1 in case of error.
+ */
+ virtual Q_LONG send(const KDatagramPacket& packet);
+
+private slots:
+ void lookupFinishedLocal();
+ void lookupFinishedPeer();
+
+private:
+ bool doBind();
+ void setupSignals();
+
+ KDatagramSocketPrivate *d;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/khttpproxysocketdevice.cpp b/kdecore/network/khttpproxysocketdevice.cpp
new file mode 100644
index 000000000..73e433a2e
--- /dev/null
+++ b/kdecore/network/khttpproxysocketdevice.cpp
@@ -0,0 +1,281 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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 <sys/types.h>
+#include <sys/socket.h>
+
+#include <qsocketnotifier.h>
+#include <qcstring.h>
+
+#include "kresolver.h"
+#include "ksocketaddress.h"
+#include "ksocketdevice.h"
+#include "khttpproxysocketdevice.h"
+
+using namespace KNetwork;
+
+KResolverEntry KHttpProxySocketDevice::defaultProxy;
+
+class KNetwork::KHttpProxySocketDevicePrivate
+{
+public:
+ KResolverEntry proxy;
+ QCString request;
+ QCString reply;
+ KSocketAddress peer;
+
+ KHttpProxySocketDevicePrivate()
+ : proxy(KHttpProxySocketDevice::defaultProxy)
+ { }
+};
+
+KHttpProxySocketDevice::KHttpProxySocketDevice(const KSocketBase* parent)
+ : KSocketDevice(parent), d(new KHttpProxySocketDevicePrivate)
+{
+}
+
+KHttpProxySocketDevice::KHttpProxySocketDevice(const KResolverEntry& proxy)
+ : d(new KHttpProxySocketDevicePrivate)
+{
+ d->proxy = proxy;
+}
+
+KHttpProxySocketDevice::~KHttpProxySocketDevice()
+{
+ // nothing special to be done during closing
+ // KSocketDevice::~KSocketDevice closes the socket
+
+ delete d;
+}
+
+int KHttpProxySocketDevice::capabilities() const
+{
+ return CanConnectString | CanNotBind | CanNotListen | CanNotUseDatagrams;
+}
+
+const KResolverEntry&
+KHttpProxySocketDevice::proxyServer() const
+{
+ return d->proxy;
+}
+
+void KHttpProxySocketDevice::setProxyServer(const KResolverEntry& proxy)
+{
+ d->proxy = proxy;
+}
+
+void KHttpProxySocketDevice::close()
+{
+ d->reply = d->request = QCString();
+ d->peer = KSocketAddress();
+ KSocketDevice::close();
+}
+
+KSocketAddress KHttpProxySocketDevice::peerAddress() const
+{
+ if (isOpen())
+ return d->peer;
+ return KSocketAddress();
+}
+
+KSocketAddress KHttpProxySocketDevice::externalAddress() const
+{
+ return KSocketAddress();
+}
+
+bool KHttpProxySocketDevice::connect(const KResolverEntry& address)
+{
+ if (d->proxy.family() == AF_UNSPEC)
+ // no proxy server set !
+ return KSocketDevice::connect(address);
+
+ if (isOpen())
+ {
+ // socket is already open
+ resetError();
+ return true;
+ }
+
+ if (m_sockfd == -1)
+ // socket isn't created yet
+ return connect(address.address().nodeName(),
+ address.address().serviceName());
+
+ d->peer = address.address();
+ return parseServerReply();
+}
+
+bool KHttpProxySocketDevice::connect(const QString& node, const QString& service)
+{
+ // same safety checks as above
+ if (m_sockfd == -1 && (d->proxy.family() == AF_UNSPEC ||
+ node.isEmpty() || service.isEmpty()))
+ {
+ // no proxy server set !
+ setError(IO_ConnectError, NotSupported);
+ return false;
+ }
+
+ if (isOpen())
+ {
+ // socket is already open
+ return true;
+ }
+
+ if (m_sockfd == -1)
+ {
+ // must create the socket
+ if (!KSocketDevice::connect(d->proxy))
+ return false; // also unable to contact proxy server
+ setState(0); // unset open flag
+
+ // prepare the request
+ QString request = QString::fromLatin1("CONNECT %1:%2 HTTP/1.1\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Host: \r\n"
+ "\r\n");
+ QString node2 = node;
+ if (node.contains(':'))
+ node2 = '[' + node + ']';
+
+ d->request = request.arg(node2).arg(service).latin1();
+ }
+
+ return parseServerReply();
+}
+
+bool KHttpProxySocketDevice::parseServerReply()
+{
+ // make sure we're connected
+ if (!KSocketDevice::connect(d->proxy))
+ if (error() == InProgress)
+ return true;
+ else if (error() != NoError)
+ return false;
+
+ if (!d->request.isEmpty())
+ {
+ // send request
+ Q_LONG written = writeBlock(d->request, d->request.length());
+ if (written < 0)
+ {
+ qDebug("KHttpProxySocketDevice: would block writing request!");
+ if (error() == WouldBlock)
+ setError(IO_ConnectError, InProgress);
+ return error() == WouldBlock; // error
+ }
+ qDebug("KHttpProxySocketDevice: request written");
+
+ d->request.remove(0, written);
+
+ if (!d->request.isEmpty())
+ {
+ setError(IO_ConnectError, InProgress);
+ return true; // still in progress
+ }
+ }
+
+ // request header is sent
+ // must parse reply, but must also be careful not to read too much
+ // from the buffer
+
+ int index;
+ if (!blocking())
+ {
+ Q_LONG avail = bytesAvailable();
+ qDebug("KHttpProxySocketDevice: %ld bytes available", avail);
+ setState(0);
+ if (avail == 0)
+ {
+ setError(IO_ConnectError, InProgress);
+ return true;
+ }
+ else if (avail < 0)
+ return false; // error!
+
+ QByteArray buf(avail);
+ if (peekBlock(buf.data(), avail) < 0)
+ return false; // error!
+
+ QCString fullHeaders = d->reply + buf.data();
+ // search for the end of the headers
+ index = fullHeaders.find("\r\n\r\n");
+ if (index == -1)
+ {
+ // no, headers not yet finished...
+ // consume data from socket
+ readBlock(buf.data(), avail);
+ d->reply += buf.data();
+ setError(IO_ConnectError, InProgress);
+ return true;
+ }
+
+ // headers are finished
+ index -= d->reply.length();
+ d->reply += fullHeaders.mid(d->reply.length(), index + 4);
+
+ // consume from socket
+ readBlock(buf.data(), index + 4);
+ }
+ else
+ {
+ int state = 0;
+ if (d->reply.right(3) == "\r\n\r")
+ state = 3;
+ else if (d->reply.right(2) == "\r\n")
+ state = 2;
+ else if (d->reply.right(1) == "\r")
+ state = 1;
+ while (state != 4)
+ {
+ char c = getch();
+ d->reply += c;
+
+ if ((state == 3 && c == '\n') ||
+ (state == 1 && c == '\n') ||
+ c == '\r')
+ ++state;
+ else
+ state = 0;
+ }
+ }
+
+ // now really parse the reply
+ qDebug("KHttpProxySocketDevice: get reply: %s\n",
+ d->reply.left(d->reply.find('\r')).data());
+ if (d->reply.left(7) != "HTTP/1." ||
+ (index = d->reply.find(' ')) == -1 ||
+ d->reply[index + 1] != '2')
+ {
+ setError(IO_ConnectError, NetFailure);
+ return false;
+ }
+
+ // we've got it
+ resetError();
+ setState(IO_Open);
+ return true;
+}
diff --git a/kdecore/network/khttpproxysocketdevice.h b/kdecore/network/khttpproxysocketdevice.h
new file mode 100644
index 000000000..ed4f1d60c
--- /dev/null
+++ b/kdecore/network/khttpproxysocketdevice.h
@@ -0,0 +1,122 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KHTTPPROXYSOCKETDEVICE_H
+#define KHTTPPROXYSOCKETDEVICE_H
+
+#include "ksocketdevice.h"
+
+namespace KNetwork {
+
+class KHttpProxySocketDevicePrivate;
+
+/**
+ * @class KHttpProxySocketDevice khttpproxysocketdevice.h khttproxysocketdevice.h
+ * @brief The low-level backend for HTTP proxying.
+ *
+ * This class derives from @ref KSocketDevice and implements the necessary
+ * calls to make a connection through an HTTP proxy.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KHttpProxySocketDevice: public KSocketDevice
+{
+public:
+ /**
+ * Constructor.
+ */
+ KHttpProxySocketDevice(const KSocketBase* = 0L);
+
+ /**
+ * Constructor with proxy server's address.
+ */
+ KHttpProxySocketDevice(const KResolverEntry& proxy);
+
+ /**
+ * Destructor
+ */
+ virtual ~KHttpProxySocketDevice();
+
+ /**
+ * Sets our capabilities.
+ */
+ virtual int capabilities() const;
+
+ /**
+ * Retrieves the proxy server address.
+ */
+ const KResolverEntry& proxyServer() const;
+
+ /**
+ * Sets the proxy server address.
+ */
+ void setProxyServer(const KResolverEntry& proxy);
+
+ /**
+ * Closes the socket.
+ */
+ virtual void close();
+
+ /**
+ * Overrides connection.
+ */
+ virtual bool connect(const KResolverEntry& address);
+
+ /**
+ * Name-based connection.
+ * We can tell the HTTP proxy server the full name.
+ */
+ virtual bool connect(const QString& name, const QString& service);
+
+ /**
+ * Return the peer address.
+ */
+ virtual KSocketAddress peerAddress() const;
+
+ /**
+ * Return the externally visible address. We can't tell what that address is,
+ * so this function always returns an empty object.
+ */
+ virtual KSocketAddress externalAddress() const;
+
+private:
+ /**
+ * Parses the server reply after sending the connect command.
+ * Returns true on success and false on failure.
+ */
+ bool parseServerReply();
+ KHttpProxySocketDevicePrivate *d;
+
+public:
+ /**
+ * This is the default proxy server to be used.
+ * Applications may want to set this value so that calling @ref setProxyServer
+ * is unnecessary.
+ */
+ static KResolverEntry defaultProxy;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/kiobuffer.h b/kdecore/network/kiobuffer.h
new file mode 100644
index 000000000..4da5ecb43
--- /dev/null
+++ b/kdecore/network/kiobuffer.h
@@ -0,0 +1,144 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KIOBUFFER_H
+#define KIOBUFFER_H
+
+#include <qcstring.h>
+
+#include <kdelibs_export.h>
+
+class QIODevice;
+
+/**
+ * @class KIOBufferBase kiobuffer.h kiobuffer.h
+ * @brief base for I/O buffer implementation
+ *
+ * This class declares the base methods to interface with an I/O buffer.
+ * Most applications will not need to access this class directly, since
+ * it is all handled by @ref KNetwork::KBufferedSocket and other buffering
+ * classes.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KIOBufferBase
+{
+public:
+ /**
+ * Default constructor. Does nothing.
+ */
+ KIOBufferBase()
+ { }
+
+ /**
+ * Copy constructor. Does nothing here.
+ */
+ KIOBufferBase(const KIOBufferBase& )
+ { }
+
+ /**
+ * Virtual destructor. Does nothing.
+ */
+ virtual ~KIOBufferBase()
+ { }
+
+ /**
+ * Assignment operator. Does nothing.
+ */
+ KIOBufferBase& operator=(const KIOBufferBase& )
+ { return *this; }
+
+ /**
+ * Returns true if a line can be read from the buffer.
+ */
+ virtual bool canReadLine() const = 0;
+
+ /**
+ * Reads a line from the buffer and discards it.
+ */
+ virtual QCString readLine() = 0;
+
+ /**
+ * Returns the number of bytes in the buffer. Note that this is not
+ * the size of the buffer.
+ *
+ * @sa size
+ */
+ virtual Q_LONG length() const = 0;
+
+ /**
+ * Returns true if the buffer is empty of data.
+ */
+ inline bool isEmpty() const
+ { return length() == 0; }
+
+ /**
+ * Retrieves the buffer size. The value of -1 indicates that
+ * the buffer has no defined upper limit.
+ *
+ * @sa length for the length of the data stored
+ */
+ virtual Q_LONG size() const = 0;
+
+ /**
+ * Returns true if the buffer is full (i.e., cannot receive more data)
+ */
+ inline bool isFull() const
+ { return size() != -1 && size() == length(); }
+
+ /**
+ * Sets the size of the buffer, if allowed.
+ *
+ * @param size the maximum size, use -1 for unlimited.
+ * @returns true on success, false if an error occurred.
+ * @note if the new size is less than length(), the buffer will be truncated
+ */
+ virtual bool setSize(Q_LONG size) = 0;
+
+ /**
+ * Adds data to the end of the buffer.
+ *
+ * @param data the data to be added
+ * @param len the data length, in bytes
+ * @returns the number of bytes added to the end of the buffer.
+ */
+ virtual Q_LONG feedBuffer(const char *data, Q_LONG len) = 0;
+
+ /**
+ * Consumes data from the beginning of the buffer.
+ *
+ * @param data where to copy the data to
+ * @param maxlen the maximum length to copy, in bytes
+ * @param discard if true, the bytes copied will be discarded
+ * @returns the number of bytes copied from the buffer
+ */
+ virtual Q_LONG consumeBuffer(char *data, Q_LONG maxlen, bool discard = true) = 0;
+
+ /**
+ * Clears the buffer.
+ */
+ virtual void clear() = 0;
+};
+
+#endif
diff --git a/kdecore/network/kmulticastsocket.h b/kdecore/network/kmulticastsocket.h
new file mode 100644
index 000000000..80c2f6990
--- /dev/null
+++ b/kdecore/network/kmulticastsocket.h
@@ -0,0 +1,113 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KMULTICASTSOCKET_H
+#define KMULTICASTSOCKET_H
+
+#include "kdatagramsocket.h"
+#include "kmulticastsocketdevice.h"
+
+namespace KNetwork {
+
+class KMulticastSocketPrivate;
+/**
+ * @class KMulticastSocket kmulticastsocket.h kmulticastsocket.h
+ * @brief A multicast-capable datagram socket class
+ *
+ * This class derives from @ref KDatagramSocket adding methods to it to
+ * allow better control over the multicast functionality. In special,
+ * the join and leave group functions are added.
+ *
+ * Other more low-level options on multicast sockets can be accessed
+ * directly with the @ref KMulticastSocketImpl class returned by
+ * @ref multicastSocketDevice.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KMulticastSocket: public KDatagramSocket
+{
+ // Q_add-it-here_OBJECT
+public:
+ /**
+ * Constructor.
+ */
+ KMulticastSocket(QObject* parent = 0L, const char *name = 0L);
+
+ /**
+ * Destructor.
+ */
+ ~KMulticastSocket();
+
+ /**
+ * Returns the multicast socket device in use by this object.
+ *
+ * @note The returned object can be null.
+ */
+ KMulticastSocketImpl* multicastSocketDevice();
+
+ /**
+ * @overload
+ */
+ const KMulticastSocketImpl* multicastSocketDevice() const;
+
+ /**
+ * Joins a multicast group. The group to be joined is identified by the
+ * @p group parameter.
+ *
+ * @param group the multicast group to join
+ * @returns true on success
+ */
+ virtual bool joinGroup(const KSocketAddress& group);
+
+ /**
+ * @overload
+ * Joins a multicast group. This function also specifies the network interface
+ * to be used.
+ */
+ virtual bool joinGroup(const KSocketAddress& group,
+ const KNetworkInterface& iface);
+
+ /**
+ * Leaves a multicast group. The group being left is given by its address in the
+ * @p group parameter.
+ *
+ * @param group the group to leave
+ * @returns true on successful leaving the group
+ */
+ virtual bool leaveGroup(const KSocketAddress& group);
+
+ /**
+ * @overload
+ * Leaves a multicast group.
+ */
+ virtual bool leaveGroup(const KSocketAddress& group,
+ const KNetworkInterface& iface);
+
+private:
+ KMulticastSocketPrivate *d;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/kmulticastsocketdevice.h b/kdecore/network/kmulticastsocketdevice.h
new file mode 100644
index 000000000..7710fe5c1
--- /dev/null
+++ b/kdecore/network/kmulticastsocketdevice.h
@@ -0,0 +1,151 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KMULTICASTSOCKETDEVICE_H
+#define KMULTICASTSOCKETDEVICE_H
+
+#include "ksocketdevice.h"
+#include "knetworkinterface.h"
+#include "ksocketaddress.h"
+
+namespace KNetwork {
+
+class KMulticastSocketImplPrivate;
+
+/**
+ * @class KMulticastSocketImpl kmulticastsocketdevice.h kmulticastsocketdevice.h
+ * @brief The low-level backend for multicasting sockets.
+ *
+ * This class is an interface providing methods for handling multicast
+ * operations.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KMulticastSocketImpl: public KSocketDevice
+{
+public:
+ /**
+ * Constructor.
+ */
+ KMulticastSocketImpl(const KSocketBase* = 0L);
+
+ /**
+ * Destructor
+ */
+ virtual ~KMulticastSocketImpl();
+
+ /**
+ * Sets our capabilities.
+ */
+ virtual int capabilities() const;
+
+ /**
+ * Overrides the socket creation.
+ */
+ virtual bool create(int family, int type, int protocol);
+
+ /**
+ * Overrides connection. Multicast sockets may not connect.
+ */
+ virtual bool connect(const KResolverEntry& address);
+
+ /**
+ * Retrieves the time-to-live/hop count value on multicast packets being sent.
+ */
+ virtual int timeToLive() const;
+
+ /**
+ * Sets the time-to-live/hop count for outgoing multicast packets.
+ *
+ * @param ttl the hop count, from 0 to 255
+ * @returns true if setting the value was successful.
+ */
+ virtual bool setTimeToLive(int ttl);
+
+ /**
+ * Retrieves the flag indicating if sent packets will be echoed back
+ * to sender.
+ */
+ virtual bool multicastLoop() const;
+
+ /**
+ * Sets the flag indicating the loopback of packets to the sender.
+ *
+ * @param enable if true, will echo back
+ * @returns true if setting the value was successful.
+ */
+ virtual bool setMulticastLoop(bool enable);
+
+ /**
+ * Retrieves the network interface this socket is associated to.
+ */
+ virtual KNetworkInterface networkInterface();
+
+ /**
+ * Sets the network interface on which this socket should work.
+ *
+ * @param iface the interface to associate with
+ * @return true if setting the value was successful.
+ */
+ virtual bool setNetworkInterface(const KNetworkInterface& iface);
+
+ /**
+ * Joins a multicast group. The group to be joined is identified by the
+ * @p group parameter.
+ *
+ * @param group the multicast group to join
+ * @returns true on success
+ */
+ virtual bool joinGroup(const KSocketAddress& group);
+
+ /**
+ * @overload
+ * Joins a multicast group. This function also specifies the network interface
+ * to be used.
+ */
+ virtual bool joinGroup(const KSocketAddress& group,
+ const KNetworkInterface& iface);
+
+ /**
+ * Leaves a multicast group. The group being left is given by its address in the
+ * @p group parameter.
+ *
+ * @param group the group to leave
+ * @returns true on successful leaving the group
+ */
+ virtual bool leaveGroup(const KSocketAddress& group);
+
+ /**
+ * @overload
+ * Leaves a multicast group.
+ */
+ virtual bool leaveGroup(const KSocketAddress& group,
+ const KNetworkInterface& iface);
+private:
+ KMulticastSocketImplPrivate *d;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/knetworkinterface.h b/kdecore/network/knetworkinterface.h
new file mode 100644
index 000000000..74fd52d55
--- /dev/null
+++ b/kdecore/network/knetworkinterface.h
@@ -0,0 +1,46 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+#ifndef KNETWORKINTERFACE_H
+#define KNETWORKINTERFACE_H
+
+#include <kdelibs_export.h>
+
+namespace KNetwork {
+
+/**
+ * A place-holder class for a future network interface class.
+ * This class is to be replaced with a more powerful version, inspired
+ * by:
+ * - KInetInterface (kdenetwork/krfb/srvloc)
+ * - NWInterface (kdenonbeta/knot/lib)
+ * - java.net.NetworkInterface
+ */
+class KNetworkInterface
+{
+};
+
+} // namespace KNetwork
+
+#endif
+
diff --git a/kdecore/network/kresolver.cpp b/kdecore/network/kresolver.cpp
new file mode 100644
index 000000000..915288123
--- /dev/null
+++ b/kdecore/network/kresolver.cpp
@@ -0,0 +1,1164 @@
+/* -*- 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"
+
+// System includes
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+// Qt includes
+#include <qapplication.h>
+#include <qstring.h>
+#include <qcstring.h>
+#include <qstrlist.h>
+#include <qstringlist.h>
+#include <qshared.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+#include <qmutex.h>
+#include <qguardedptr.h>
+
+// IDN
+#ifdef HAVE_IDNA_H
+# include <idna.h>
+#endif
+
+// KDE
+#include <klocale.h>
+
+// Us
+#include "kresolver.h"
+#include "kresolver_p.h"
+#include "ksocketaddress.h"
+
+#ifdef NEED_MUTEX
+#warning "mutex"
+QMutex getXXbyYYmutex;
+#endif
+
+using namespace KNetwork;
+using namespace KNetwork::Internal;
+
+/////////////////////////////////////////////
+// class KResolverEntry
+
+class KNetwork::KResolverEntryPrivate: public QShared
+{
+public:
+ KSocketAddress addr;
+ int socktype;
+ int protocol;
+ QString canonName;
+ QCString encodedName;
+
+ inline KResolverEntryPrivate() :
+ socktype(0), protocol(0)
+ { }
+};
+
+// default constructor
+KResolverEntry::KResolverEntry() :
+ d(0L)
+{
+}
+
+// constructor with stuff
+KResolverEntry::KResolverEntry(const KSocketAddress& addr, int socktype, int protocol,
+ const QString& canonName, const QCString& encodedName) :
+ d(new KResolverEntryPrivate)
+{
+ d->addr = addr;
+ d->socktype = socktype;
+ d->protocol = protocol;
+ d->canonName = canonName;
+ d->encodedName = encodedName;
+}
+
+// constructor with even more stuff
+KResolverEntry::KResolverEntry(const struct sockaddr* sa, Q_UINT16 salen, int socktype,
+ int protocol, const QString& canonName,
+ const QCString& encodedName) :
+ d(new KResolverEntryPrivate)
+{
+ d->addr = KSocketAddress(sa, salen);
+ d->socktype = socktype;
+ d->protocol = protocol;
+ d->canonName = canonName;
+ d->encodedName = encodedName;
+}
+
+// copy constructor
+KResolverEntry::KResolverEntry(const KResolverEntry& that) :
+ d(0L)
+{
+ *this = that;
+}
+
+// destructor
+KResolverEntry::~KResolverEntry()
+{
+ if (d == 0L)
+ return;
+
+ if (d->deref())
+ delete d;
+}
+
+// returns the socket address
+KSocketAddress KResolverEntry::address() const
+{
+ return d ? d->addr : KSocketAddress();
+}
+
+// returns the length
+Q_UINT16 KResolverEntry::length() const
+{
+ return d ? d->addr.length() : 0;
+}
+
+// returns the family
+int KResolverEntry::family() const
+{
+ return d ? d->addr.family() : AF_UNSPEC;
+}
+
+// returns the canonical name
+QString KResolverEntry::canonicalName() const
+{
+ return d ? d->canonName : QString::null;
+}
+
+// returns the encoded name
+QCString KResolverEntry::encodedName() const
+{
+ return d ? d->encodedName : QCString();
+}
+
+// returns the socket type
+int KResolverEntry::socketType() const
+{
+ return d ? d->socktype : 0;
+}
+
+// returns the protocol
+int KResolverEntry::protocol() const
+{
+ return d ? d->protocol : 0;
+}
+
+// assignment operator
+KResolverEntry& KResolverEntry::operator= (const KResolverEntry& that)
+{
+ // copy the data
+ if (that.d)
+ that.d->ref();
+
+ if (d && d->deref())
+ delete d;
+
+ d = that.d;
+ return *this;
+}
+
+/////////////////////////////////////////////
+// class KResolverResults
+
+class KNetwork::KResolverResultsPrivate
+{
+public:
+ QString node, service;
+ int errorcode, syserror;
+
+ KResolverResultsPrivate() :
+ errorcode(0), syserror(0)
+ { }
+};
+
+// default constructor
+KResolverResults::KResolverResults()
+ : d(new KResolverResultsPrivate)
+{
+}
+
+// copy constructor
+KResolverResults::KResolverResults(const KResolverResults& other)
+ : QValueList<KResolverEntry>(other), d(new KResolverResultsPrivate)
+{
+ *d = *other.d;
+}
+
+// destructor
+KResolverResults::~KResolverResults()
+{
+ delete d;
+}
+
+// assignment operator
+KResolverResults&
+KResolverResults::operator= (const KResolverResults& other)
+{
+ if (this == &other)
+ return *this;
+
+ // copy over the other data
+ *d = *other.d;
+
+ // now let QValueList do the rest of the work
+ QValueList<KResolverEntry>::operator =(other);
+
+ return *this;
+}
+
+// gets the error code
+int KResolverResults::error() const
+{
+ return d->errorcode;
+}
+
+// gets the system errno
+int KResolverResults::systemError() const
+{
+ return d->syserror;
+}
+
+// sets the error codes
+void KResolverResults::setError(int errorcode, int systemerror)
+{
+ d->errorcode = errorcode;
+ d->syserror = systemerror;
+}
+
+// gets the hostname
+QString KResolverResults::nodeName() const
+{
+ return d->node;
+}
+
+// gets the service name
+QString KResolverResults::serviceName() const
+{
+ return d->service;
+}
+
+// sets the address
+void KResolverResults::setAddress(const QString& node,
+ const QString& service)
+{
+ d->node = node;
+ d->service = service;
+}
+
+void KResolverResults::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+
+///////////////////////
+// class KResolver
+
+QStringList *KResolver::idnDomains = 0;
+
+
+// default constructor
+KResolver::KResolver(QObject *parent, const char *name)
+ : QObject(parent, name), d(new KResolverPrivate(this))
+{
+}
+
+// constructor with host and service
+KResolver::KResolver(const QString& nodename, const QString& servicename,
+ QObject *parent, const char *name)
+ : QObject(parent, name), d(new KResolverPrivate(this, nodename, servicename))
+{
+}
+
+// destructor
+KResolver::~KResolver()
+{
+ cancel(false);
+ delete d;
+}
+
+// get the status
+int KResolver::status() const
+{
+ return d->status;
+}
+
+// get the error code
+int KResolver::error() const
+{
+ return d->errorcode;
+}
+
+// get the errno
+int KResolver::systemError() const
+{
+ return d->syserror;
+}
+
+// are we running?
+bool KResolver::isRunning() const
+{
+ return d->status > 0 && d->status < Success;
+}
+
+// get the hostname
+QString KResolver::nodeName() const
+{
+ return d->input.node;
+}
+
+// get the service
+QString KResolver::serviceName() const
+{
+ return d->input.service;
+}
+
+// sets the hostname
+void KResolver::setNodeName(const QString& nodename)
+{
+ // don't touch those values if we're working!
+ if (!isRunning())
+ {
+ d->input.node = nodename;
+ d->status = Idle;
+ d->results.setAddress(nodename, d->input.service);
+ }
+}
+
+// sets the service
+void KResolver::setServiceName(const QString& service)
+{
+ // don't change if running
+ if (!isRunning())
+ {
+ d->input.service = service;
+ d->status = Idle;
+ d->results.setAddress(d->input.node, service);
+ }
+}
+
+// sets the address
+void KResolver::setAddress(const QString& nodename, const QString& service)
+{
+ setNodeName(nodename);
+ setServiceName(service);
+}
+
+// get the flags
+int KResolver::flags() const
+{
+ return d->input.flags;
+}
+
+// sets the flags
+int KResolver::setFlags(int flags)
+{
+ int oldflags = d->input.flags;
+ if (!isRunning())
+ {
+ d->input.flags = flags;
+ d->status = Idle;
+ }
+ return oldflags;
+}
+
+// sets the family mask
+void KResolver::setFamily(int families)
+{
+ if (!isRunning())
+ {
+ d->input.familyMask = families;
+ d->status = Idle;
+ }
+}
+
+// sets the socket type
+void KResolver::setSocketType(int type)
+{
+ if (!isRunning())
+ {
+ d->input.socktype = type;
+ d->status = Idle;
+ }
+}
+
+// sets the protocol
+void KResolver::setProtocol(int protonum, const char *name)
+{
+ if (isRunning())
+ return; // can't change now
+
+ // we copy the given protocol name. If it isn't an empty string
+ // and the protocol number was 0, we will look it up in /etc/protocols
+ // we also leave the error reporting to the actual lookup routines, in
+ // case the given protocol name doesn't exist
+
+ d->input.protocolName = name;
+ if (protonum == 0 && name != 0L && *name != '\0')
+ {
+ // must look up the protocol number
+ d->input.protocol = KResolver::protocolNumber(name);
+ }
+ else
+ d->input.protocol = protonum;
+ d->status = Idle;
+}
+
+bool KResolver::start()
+{
+ if (!isRunning())
+ {
+ d->results.empty();
+
+ // is there anything to be queued?
+ if (d->input.node.isEmpty() && d->input.service.isEmpty())
+ {
+ d->status = KResolver::Success;
+ emitFinished();
+ }
+ else
+ KResolverManager::manager()->enqueue(this, 0L);
+ }
+
+ return true;
+}
+
+bool KResolver::wait(int msec)
+{
+ if (!isRunning())
+ {
+ emitFinished();
+ return true;
+ }
+
+ QMutexLocker locker(&d->mutex);
+
+ if (!isRunning())
+ {
+ // it was running and no longer is?
+ // That means the manager has finished its processing and has posted
+ // an event for the signal to be emitted already. This means the signal
+ // will be emitted twice!
+
+ emitFinished();
+ return true;
+ }
+ else
+ {
+ QTime t;
+ t.start();
+
+ while (!msec || t.elapsed() < msec)
+ {
+ // wait on the manager to broadcast completion
+ d->waiting = true;
+ if (msec)
+ KResolverManager::manager()->notifyWaiters.wait(&d->mutex, msec - t.elapsed());
+ else
+ KResolverManager::manager()->notifyWaiters.wait(&d->mutex);
+
+ // the manager has processed
+ // see if this object is done
+ if (!isRunning())
+ {
+ // it's done
+ d->waiting = false;
+ emitFinished();
+ return true;
+ }
+ }
+
+ // if we've got here, we've timed out
+ d->waiting = false;
+ return false;
+ }
+}
+
+void KResolver::cancel(bool emitSignal)
+{
+ KResolverManager::manager()->dequeue(this);
+ if (emitSignal)
+ emitFinished();
+}
+
+KResolverResults
+KResolver::results() const
+{
+ if (!isRunning())
+ return d->results;
+
+ // return a dummy, empty result
+ KResolverResults r;
+ r.setAddress(d->input.node, d->input.service);
+ r.setError(d->errorcode, d->syserror);
+ return r;
+}
+
+bool KResolver::event(QEvent* e)
+{
+ if (static_cast<int>(e->type()) == KResolverManager::ResolutionCompleted)
+ {
+ emitFinished();
+ return true;
+ }
+
+ return false;
+}
+
+void KResolver::emitFinished()
+{
+ if (isRunning())
+ d->status = KResolver::Success;
+
+ QGuardedPtr<QObject> p = this; // guard against deletion
+
+ emit finished(d->results);
+
+ if (p && d->deleteWhenDone)
+ deleteLater(); // in QObject
+}
+
+QString KResolver::errorString(int errorcode, int syserror)
+{
+ // no i18n now...
+ static const char * const messages[] =
+ {
+ I18N_NOOP("no error"), // NoError
+ I18N_NOOP("requested family not supported for this host name"), // AddrFamily
+ I18N_NOOP("temporary failure in name resolution"), // TryAgain
+ I18N_NOOP("non-recoverable failure in name resolution"), // NonRecoverable
+ I18N_NOOP("invalid flags"), // BadFlags
+ I18N_NOOP("memory allocation failure"), // Memory
+ I18N_NOOP("name or service not known"), // NoName
+ I18N_NOOP("requested family not supported"), // UnsupportedFamily
+ I18N_NOOP("requested service not supported for this socket type"), // UnsupportedService
+ I18N_NOOP("requested socket type not supported"), // UnsupportedSocketType
+ I18N_NOOP("unknown error"), // UnknownError
+ I18N_NOOP2("1: the i18n'ed system error code, from errno",
+ "system error: %1") // SystemError
+ };
+
+ // handle the special value
+ if (errorcode == Canceled)
+ return i18n("request was canceled");
+
+ if (errorcode > 0 || errorcode < SystemError)
+ return QString::null;
+
+ QString msg = i18n(messages[-errorcode]);
+ if (errorcode == SystemError)
+ msg.arg(QString::fromLocal8Bit(strerror(syserror)));
+
+ return msg;
+}
+
+KResolverResults
+KResolver::resolve(const QString& host, const QString& service, int flags,
+ int families)
+{
+ KResolver qres(host, service, qApp, "synchronous KResolver");
+ qres.setFlags(flags);
+ qres.setFamily(families);
+ qres.start();
+ qres.wait();
+ return qres.results();
+}
+
+bool KResolver::resolveAsync(QObject* userObj, const char *userSlot,
+ const QString& host, const QString& service,
+ int flags, int families)
+{
+ KResolver* qres = new KResolver(host, service, qApp, "asynchronous KResolver");
+ QObject::connect(qres, SIGNAL(finished(KResolverResults)), userObj, userSlot);
+ qres->setFlags(flags);
+ qres->setFamily(families);
+ qres->d->deleteWhenDone = true; // this is the only difference from the example code
+ return qres->start();
+}
+
+QStrList KResolver::protocolName(int protonum)
+{
+ struct protoent *pe = 0L;
+#ifndef HAVE_GETPROTOBYNAME_R
+ QMutexLocker locker(&getXXbyYYmutex);
+
+ pe = getprotobynumber(protonum);
+
+#else
+ size_t buflen = 1024;
+ struct protoent protobuf;
+ char *buf;
+ do
+ {
+ buf = new char[buflen];
+# ifdef USE_SOLARIS // Solaris uses a 4 argument getprotobynumber_r which returns struct *protoent or NULL
+ if ((pe = getprotobynumber_r(protonum, &protobuf, buf, buflen)) && (errno == ERANGE))
+# else
+ if (getprotobynumber_r(protonum, &protobuf, buf, buflen, &pe) == ERANGE)
+# endif
+ {
+ pe = 0L;
+ buflen += 1024;
+ delete [] buf;
+ }
+ else
+ break;
+ }
+ while (pe == 0L);
+#endif
+
+ // Do common processing
+ QStrList lst(true); // use deep copies
+ if (pe != NULL)
+ {
+ lst.append(pe->p_name);
+ for (char **p = pe->p_aliases; *p; p++)
+ lst.append(*p);
+ }
+
+#ifdef HAVE_GETPROTOBYNAME_R
+ delete [] buf;
+#endif
+
+ return lst;
+}
+
+QStrList KResolver::protocolName(const char *protoname)
+{
+ struct protoent *pe = 0L;
+#ifndef HAVE_GETPROTOBYNAME_R
+ QMutexLocker locker(&getXXbyYYmutex);
+
+ pe = getprotobyname(protoname);
+
+#else
+ size_t buflen = 1024;
+ struct protoent protobuf;
+ char *buf;
+ do
+ {
+ buf = new char[buflen];
+# ifdef USE_SOLARIS // Solaris uses a 4 argument getprotobyname_r which returns struct *protoent or NULL
+ if ((pe = getprotobyname_r(protoname, &protobuf, buf, buflen)) && (errno == ERANGE))
+# else
+ if (getprotobyname_r(protoname, &protobuf, buf, buflen, &pe) == ERANGE)
+# endif
+ {
+ pe = 0L;
+ buflen += 1024;
+ delete [] buf;
+ }
+ else
+ break;
+ }
+ while (pe == 0L);
+#endif
+
+ // Do common processing
+ QStrList lst(true); // use deep copies
+ if (pe != NULL)
+ {
+ lst.append(pe->p_name);
+ for (char **p = pe->p_aliases; *p; p++)
+ lst.append(*p);
+ }
+
+#ifdef HAVE_GETPROTOBYNAME_R
+ delete [] buf;
+#endif
+
+ return lst;
+}
+
+int KResolver::protocolNumber(const char *protoname)
+{
+ struct protoent *pe = 0L;
+#ifndef HAVE_GETPROTOBYNAME_R
+ QMutexLocker locker(&getXXbyYYmutex);
+
+ pe = getprotobyname(protoname);
+
+#else
+ size_t buflen = 1024;
+ struct protoent protobuf;
+ char *buf;
+ do
+ {
+ buf = new char[buflen];
+# ifdef USE_SOLARIS // Solaris uses a 4 argument getprotobyname_r which returns struct *protoent or NULL
+ if ((pe = getprotobyname_r(protoname, &protobuf, buf, buflen)) && (errno == ERANGE))
+# else
+ if (getprotobyname_r(protoname, &protobuf, buf, buflen, &pe) == ERANGE)
+# endif
+ {
+ pe = 0L;
+ buflen += 1024;
+ delete [] buf;
+ }
+ else
+ break;
+ }
+ while (pe == 0L);
+#endif
+
+ // Do common processing
+ int protonum = -1;
+ if (pe != NULL)
+ protonum = pe->p_proto;
+
+#ifdef HAVE_GETPROTOBYNAME_R
+ delete [] buf;
+#endif
+
+ return protonum;
+}
+
+int KResolver::servicePort(const char *servname, const char *protoname)
+{
+ struct servent *se = 0L;
+#ifndef HAVE_GETSERVBYNAME_R
+ QMutexLocker locker(&getXXbyYYmutex);
+
+ se = getservbyname(servname, protoname);
+
+#else
+ size_t buflen = 1024;
+ struct servent servbuf;
+ char *buf;
+ do
+ {
+ buf = new char[buflen];
+# ifdef USE_SOLARIS // Solaris uses a 5 argument getservbyname_r which returns struct *servent or NULL
+ if ((se = getservbyname_r(servname, protoname, &servbuf, buf, buflen)) && (errno == ERANGE))
+# else
+ if (getservbyname_r(servname, protoname, &servbuf, buf, buflen, &se) == ERANGE)
+# endif
+ {
+ se = 0L;
+ buflen += 1024;
+ delete [] buf;
+ }
+ else
+ break;
+ }
+ while (se == 0L);
+#endif
+
+ // Do common processing
+ int servport = -1;
+ if (se != NULL)
+ servport = ntohs(se->s_port);
+
+#ifdef HAVE_GETSERVBYNAME_R
+ delete [] buf;
+#endif
+
+ return servport;
+}
+
+QStrList KResolver::serviceName(const char* servname, const char *protoname)
+{
+ struct servent *se = 0L;
+#ifndef HAVE_GETSERVBYNAME_R
+ QMutexLocker locker(&getXXbyYYmutex);
+
+ se = getservbyname(servname, protoname);
+
+#else
+ size_t buflen = 1024;
+ struct servent servbuf;
+ char *buf;
+ do
+ {
+ buf = new char[buflen];
+# ifdef USE_SOLARIS // Solaris uses a 5 argument getservbyname_r which returns struct *servent or NULL
+ if ((se = getservbyname_r(servname, protoname, &servbuf, buf, buflen)) && (errno == ERANGE))
+# else
+ if (getservbyname_r(servname, protoname, &servbuf, buf, buflen, &se) == ERANGE)
+# endif
+ {
+ se = 0L;
+ buflen += 1024;
+ delete [] buf;
+ }
+ else
+ break;
+ }
+ while (se == 0L);
+#endif
+
+ // Do common processing
+ QStrList lst(true); // use deep copies
+ if (se != NULL)
+ {
+ lst.append(se->s_name);
+ for (char **p = se->s_aliases; *p; p++)
+ lst.append(*p);
+ }
+
+#ifdef HAVE_GETSERVBYNAME_R
+ delete [] buf;
+#endif
+
+ return lst;
+}
+
+QStrList KResolver::serviceName(int port, const char *protoname)
+{
+ struct servent *se = 0L;
+#ifndef HAVE_GETSERVBYPORT_R
+ QMutexLocker locker(&getXXbyYYmutex);
+
+ se = getservbyport(port, protoname);
+
+#else
+ size_t buflen = 1024;
+ struct servent servbuf;
+ char *buf;
+ do
+ {
+ buf = new char[buflen];
+# ifdef USE_SOLARIS // Solaris uses a 5 argument getservbyport_r which returns struct *servent or NULL
+ if ((se = getservbyport_r(port, protoname, &servbuf, buf, buflen)) && (errno == ERANGE))
+# else
+ if (getservbyport_r(port, protoname, &servbuf, buf, buflen, &se) == ERANGE)
+# endif
+ {
+ se = 0L;
+ buflen += 1024;
+ delete [] buf;
+ }
+ else
+ break;
+ }
+ while (se == 0L);
+#endif
+
+ // Do common processing
+ QStrList lst(true); // use deep copies
+ if (se != NULL)
+ {
+ lst.append(se->s_name);
+ for (char **p = se->s_aliases; *p; p++)
+ lst.append(*p);
+ }
+
+#ifdef HAVE_GETSERVBYPORT_R
+ delete [] buf;
+#endif
+
+ return lst;
+}
+
+QString KResolver::localHostName()
+{
+ QCString name;
+ int len;
+
+#ifdef MAXHOSTNAMELEN
+ len = MAXHOSTNAMELEN;
+#else
+ len = 256;
+#endif
+
+ while (true)
+ {
+ name.resize(len);
+
+ if (gethostname(name.data(), len - 1) == 0)
+ {
+ // Call succeeded, but it's not guaranteed to be NUL-terminated
+ // Note that some systems return success even if they did truncation
+ name[len - 1] = '\0';
+ break;
+ }
+
+ // Call failed
+ if (errno == ENAMETOOLONG || errno == EINVAL)
+ len += 256;
+ else
+ {
+ // Oops! Unknown error!
+ name = QCString();
+ }
+ }
+
+ if (name.isEmpty())
+ return QString::fromLatin1("localhost");
+
+ if (name.find('.') == -1)
+ {
+ // not fully qualified
+ // must resolve
+ KResolverResults results = resolve(name, "0", CanonName);
+ if (results.isEmpty())
+ // cannot find a valid hostname!
+ return QString::fromLatin1("localhost");
+ else
+ return results.first().canonicalName();
+ }
+
+ return domainToUnicode(name);
+}
+
+
+// forward declaration
+static QStringList splitLabels(const QString& unicodeDomain);
+static QCString ToASCII(const QString& label);
+static QString ToUnicode(const QString& label);
+
+static QStringList *KResolver_initIdnDomains()
+{
+ const char *kde_use_idn = getenv("KDE_USE_IDN");
+ if (!kde_use_idn)
+ kde_use_idn = "ac:at:br:cat:ch:cl:cn:de:dk:fi:gr:hu:info:io:is:jp:kr:li:lt:museum:org:no:se:sh:th:tm:tw:vn";
+ return new QStringList(QStringList::split(':', QString::fromLatin1(kde_use_idn).lower()));
+}
+
+// implement the ToAscii function, as described by IDN documents
+QCString KResolver::domainToAscii(const QString& unicodeDomain)
+{
+ if (!idnDomains)
+ idnDomains = KResolver_initIdnDomains();
+
+ QCString retval;
+ // RFC 3490, section 4 describes the operation:
+ // 1) this is a query, so don't allow unassigned
+
+ // 2) split the domain into individual labels, without
+ // separators.
+ QStringList input = splitLabels(unicodeDomain);
+
+ // Do we allow IDN names for this TLD?
+ if (input.count() && !idnDomains->contains(input[input.count()-1].lower()))
+ return input.join(".").lower().latin1(); // No IDN allowed for this TLD
+
+ // 3) decide whether to enforce the STD3 rules for chars < 0x7F
+ // we don't enforce
+
+ // 4) for each label, apply ToASCII
+ QStringList::Iterator it = input.begin();
+ const QStringList::Iterator end = input.end();
+ for ( ; it != end; ++it)
+ {
+ QCString cs = ToASCII(*it);
+ if (cs.isNull())
+ return QCString(); // error!
+
+ // no, all is Ok.
+ if (!retval.isEmpty())
+ retval += '.';
+ retval += cs;
+ }
+
+ return retval;
+}
+
+QString KResolver::domainToUnicode(const QCString& asciiDomain)
+{
+ return domainToUnicode(QString::fromLatin1(asciiDomain));
+}
+
+// implement the ToUnicode function, as described by IDN documents
+QString KResolver::domainToUnicode(const QString& asciiDomain)
+{
+ if (asciiDomain.isEmpty())
+ return asciiDomain;
+ if (!idnDomains)
+ idnDomains = KResolver_initIdnDomains();
+
+ QString retval;
+
+ // draft-idn-idna-14.txt, section 4 describes the operation:
+ // 1) this is a query, so don't allow unassigned
+ // besides, input is ASCII
+
+ // 2) split the domain into individual labels, without
+ // separators.
+ QStringList input = splitLabels(asciiDomain);
+
+ // Do we allow IDN names for this TLD?
+ if (input.count() && !idnDomains->contains(input[input.count()-1].lower()))
+ return asciiDomain.lower(); // No TLDs allowed
+
+ // 3) decide whether to enforce the STD3 rules for chars < 0x7F
+ // we don't enforce
+
+ // 4) for each label, apply ToUnicode
+ QStringList::Iterator it;
+ const QStringList::Iterator end = input.end();
+ for (it = input.begin(); it != end; ++it)
+ {
+ QString label = ToUnicode(*it).lower();
+
+ // ToUnicode can't fail
+ if (!retval.isEmpty())
+ retval += '.';
+ retval += label;
+ }
+
+ return retval;
+}
+
+QString KResolver::normalizeDomain(const QString& domain)
+{
+ return domainToUnicode(domainToAscii(domain));
+}
+
+void KResolver::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+// here follows IDN functions
+// all IDN functions conform to the following documents:
+// RFC 3454 - Preparation of Internationalized Strings
+// RFC 3490 - Internationalizing Domain Names in Applications (IDNA)
+// RFC 3491 - Nameprep: A Stringprep Profile for
+// Internationalized Domain Names (IDN
+// RFC 3492 - Punycode: A Bootstring encoding of Unicode
+// for Internationalized Domain Names in Applications (IDNA)
+
+static QStringList splitLabels(const QString& unicodeDomain)
+{
+ // From RFC 3490 section 3.1:
+ // "Whenever dots are used as label separators, the following characters
+ // MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full
+ // stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full
+ // stop)."
+ static const unsigned int separators[] = { 0x002E, 0x3002, 0xFF0E, 0xFF61 };
+
+ QStringList lst;
+ int start = 0;
+ uint i;
+ for (i = 0; i < unicodeDomain.length(); i++)
+ {
+ unsigned int c = unicodeDomain[i].unicode();
+
+ if (c == separators[0] ||
+ c == separators[1] ||
+ c == separators[2] ||
+ c == separators[3])
+ {
+ // found a separator!
+ lst << unicodeDomain.mid(start, i - start);
+ start = i + 1;
+ }
+ }
+ if ((long)i >= start)
+ // there is still one left
+ lst << unicodeDomain.mid(start, i - start);
+
+ return lst;
+}
+
+static QCString ToASCII(const QString& label)
+{
+#ifdef HAVE_IDNA_H
+ // We have idna.h, so we can use the idna_to_ascii
+ // function :)
+
+ if (label.length() > 64)
+ return (char*)0L; // invalid label
+
+ if (label.length() == 0)
+ // this is allowed
+ return QCString(""); // empty, not null
+
+ QCString retval;
+ char buf[65];
+
+ Q_UINT32* ucs4 = new Q_UINT32[label.length() + 1];
+
+ uint i;
+ for (i = 0; i < label.length(); i++)
+ ucs4[i] = (unsigned long)label[i].unicode();
+ ucs4[i] = 0; // terminate with NUL, just to be on the safe side
+
+ if (idna_to_ascii_4i(ucs4, label.length(), buf, 0) == IDNA_SUCCESS)
+ // success!
+ retval = buf;
+
+ delete [] ucs4;
+ return retval;
+#else
+ return label.latin1();
+#endif
+}
+
+static QString ToUnicode(const QString& label)
+{
+#ifdef HAVE_IDNA_H
+ // We have idna.h, so we can use the idna_to_unicode
+ // function :)
+
+ Q_UINT32 *ucs4_input, *ucs4_output;
+ size_t outlen;
+
+ ucs4_input = new Q_UINT32[label.length() + 1];
+ for (uint i = 0; i < label.length(); i++)
+ ucs4_input[i] = (unsigned long)label[i].unicode();
+
+ // try the same length for output
+ ucs4_output = new Q_UINT32[outlen = label.length()];
+
+ idna_to_unicode_44i(ucs4_input, label.length(),
+ ucs4_output, &outlen,
+ 0);
+
+ if (outlen > label.length())
+ {
+ // it must have failed
+ delete [] ucs4_output;
+ ucs4_output = new Q_UINT32[outlen];
+
+ idna_to_unicode_44i(ucs4_input, label.length(),
+ ucs4_output, &outlen,
+ 0);
+ }
+
+ // now set the answer
+ QString result;
+ result.setLength(outlen);
+ for (uint i = 0; i < outlen; i++)
+ result[i] = (unsigned int)ucs4_output[i];
+
+ delete [] ucs4_input;
+ delete [] ucs4_output;
+
+ return result;
+#else
+ return label;
+#endif
+}
+
+#include "kresolver.moc"
diff --git a/kdecore/network/kresolver.h b/kdecore/network/kresolver.h
new file mode 100644
index 000000000..b4e4334be
--- /dev/null
+++ b/kdecore/network/kresolver.h
@@ -0,0 +1,945 @@
+/* -*- mode: C++; coding: utf-8; -*-
+ * 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.
+ */
+
+#ifndef KRESOLVER_H
+#define KRESOLVER_H
+
+//////////////////
+// Needed includes
+#include <qvaluelist.h>
+#include <qobject.h>
+#include "ksocketaddress.h"
+
+
+////////////////////////
+// Forward declarations
+struct sockaddr;
+class QString;
+class QCString;
+class QStrList;
+
+//////////////////
+// Our definitions
+
+namespace KNetwork {
+
+ namespace Internal { class KResolverManager; }
+
+class KResolverEntryPrivate;
+/** @class KResolverEntry kresolver.h kresolver.h
+ * @brief One resolution entry.
+ *
+ * This class is one element in the resolution results list.
+ * It contains the socket address for connecting, as well as
+ * a bit more of information: the socket type, address family
+ * and protocol numbers.
+ *
+ * This class contains all the information required for creating,
+ * binding and connecting a socket.
+ *
+ * KResolverEntry objects implicitly share data, so copying them
+ * is quite efficient.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KResolverEntry
+{
+public:
+ /**
+ * Default constructor
+ *
+ */
+ KResolverEntry();
+
+ /**
+ * Constructs a new KResolverEntry from a KSocketAddress
+ * and other data.
+ *
+ * The KSocketAddress @p addr parameter will be deep-copied.
+ *
+ * @param addr the address that was resolved
+ * @param socktype the socket type of the resolved address
+ * @param protocol the protocol of the resolved address
+ * @param canonName the canonical name of the resolved hostname
+ * @param encodedName the ASCII-compatible encoding of the hostname
+ */
+ KResolverEntry(const KSocketAddress& addr, int socktype, int protocol,
+ const QString& canonName = QString::null,
+ const QCString& encodedName = QCString());
+
+ /**
+ * Constructs a new KResolverEntry from raw forms of
+ * socket addresses and other data.
+ *
+ * This constructor instead creates an internal KSocketAddress object.
+ *
+ * @param sa the sockaddr structure containing the raw address
+ * @param salen the length of the sockaddr structure
+ * @param socktype the socket type of the resolved address
+ * @param protocol the protocol of the resolved address
+ * @param canonName the canonical name of the resolved hostname
+ * @param encodedName the ASCII-compatible encoding of the hostname
+ */
+ KResolverEntry(const struct sockaddr *sa, Q_UINT16 salen, int socktype,
+ int protocol, const QString& canonName = QString::null,
+ const QCString& encodedName = QCString());
+
+ /**
+ * Copy constructor.
+ *
+ * This constructor performs a shallow-copy of the other object.
+ */
+ KResolverEntry(const KResolverEntry &other);
+
+ /**
+ * Destructor.
+ *
+ * The destructor frees associated resources with this object. It does
+ * not destroy shared data.
+ */
+ ~KResolverEntry();
+
+ /**
+ * Retrieves the socket address associated with this entry.
+ */
+ KSocketAddress address() const;
+
+ /**
+ * Retrieves the length of the socket address structure.
+ */
+ Q_UINT16 length() const;
+
+ /**
+ * Retrieves the family associated with this socket address.
+ */
+ int family() const;
+
+ /**
+ * Retrieves the canonical name associated with this entry, if there is any.
+ * If the canonical name was not found, this function returns QString::null.
+ */
+ QString canonicalName() const;
+
+ /**
+ * Retrieves the encoded domain name associated with this entry, if there is
+ * any. If this domain has been resolved through DNS, this will be the
+ * the ACE-encoded hostname.
+ *
+ * Returns a null QCString if such information is not available.
+ *
+ * Please note that this information is NOT to be presented to the user,
+ * unless requested.
+ */
+ QCString encodedName() const;
+
+ /**
+ * Retrieves the socket type associated with this entry.
+ */
+ int socketType() const;
+
+ /**
+ * Retrieves the protocol associated with this entry.
+ */
+ int protocol() const;
+
+ /**
+ * Assignment operator
+ *
+ * This function copies the contents of the other object into this one.
+ * Data will be shared between the two of them.
+ */
+ KResolverEntry& operator=(const KResolverEntry& other);
+
+private:
+ KResolverEntryPrivate* d;
+};
+
+class KResolverResultsPrivate;
+/**
+ * @class KResolverResults kresolver.h kresolver.h
+ * @brief Name and service resolution results.
+ *
+ * This object contains the results of a name and service resolution, as
+ * those performed by @ref KResolver. It is also a descendant of QValueList, so
+ * you may use all its member functions here to access the elements.
+ *
+ * A KResolverResults object is associated with a resolution, so, in addition
+ * to the resolved elements, you can also retrieve information about the
+ * resolution process itself, like the nodename that was resolved or an error
+ * code.
+ *
+ * Note Resolver also uses KResolverResults objects to indicate failure, so
+ * you should test for failure.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KResolverResults: public QValueList<KResolverEntry>
+{
+public:
+ /**
+ * Default constructor.
+ *
+ * Constructs an empty list.
+ */
+ KResolverResults();
+
+ /**
+ * Copy constructor
+ *
+ * Creates a new object with the contents of the other one. Data will be
+ * shared by the two objects, like QValueList
+ */
+ KResolverResults(const KResolverResults& other);
+
+ /**
+ * Destructor
+ *
+ * Destroys the object and frees associated resources.
+ */
+ virtual ~KResolverResults();
+
+ /**
+ * Assignment operator
+ *
+ * Copies the contents of the other container into this one, discarding
+ * our current values.
+ */
+ KResolverResults& operator=(const KResolverResults& other);
+
+ /**
+ * Retrieves the error code associated with this resolution. The values
+ * here are the same as in @ref KResolver::ErrorCodes.
+ */
+ int error() const;
+
+ /**
+ * Retrieves the system error code, if any.
+ * @see KResolver::systemError for more information
+ */
+ int systemError() const;
+
+ /**
+ * Sets the error codes
+ *
+ * @param errorcode the error code in @ref KResolver::ErrorCodes
+ * @param systemerror the system error code associated, if any
+ */
+ void setError(int errorcode, int systemerror = 0);
+
+ /**
+ * The nodename to which the resolution was performed.
+ */
+ QString nodeName() const;
+
+ /**
+ * The service name to which the resolution was performed.
+ */
+ QString serviceName() const;
+
+ /**
+ * Sets the new nodename and service name
+ */
+ void setAddress(const QString& host, const QString& service);
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ KResolverResultsPrivate* d;
+};
+
+class KResolverPrivate;
+/**
+ * @class KResolver kresolver.h kresolver.h
+ * @brief Name and service resolution class.
+ *
+ * This class provides support for doing name-to-binary resolution
+ * for nodenames and service ports. You should use this class if you
+ * need specific resolution techniques when creating a socket or if you
+ * want to inspect the results before calling the socket functions.
+ *
+ * You can either create an object and set the options you want in it
+ * or you can simply call the static member functions, which will create
+ * standard Resolver objects and dispatch the resolution for you. Normally,
+ * the static functions will be used, except in cases where specific options
+ * must be set.
+ *
+ * A Resolver object defaults to the following:
+ * @li address family: any address family
+ * @li socket type: streaming socket
+ * @li protocol: implementation-defined. Generally, TCP
+ * @li host and service: unset
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KResolver: public QObject
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Address family selection types
+ *
+ * These values can be OR-ed together to form a composite family selection.
+ *
+ * @li UnknownFamily: a family that is unknown to the current implementation
+ * @li KnownFamily: a family that is known to the implementation (the exact
+ * opposite of UnknownFamily)
+ * @li AnyFamilies: any address family is acceptable
+ * @li InternetFamily: an address for connecting to the Internet
+ * @li InetFamily: alias for InternetFamily
+ * @li IPv6Family: an IPv6 address only
+ * @li IPv4Family: an IPv4 address only
+ * @li UnixFamily: an address for the local Unix namespace (i.e., Unix sockets)
+ * @li LocalFamily: alias for UnixFamily
+ */
+ enum SocketFamilies
+ {
+ UnknownFamily = 0x0001,
+
+ UnixFamily = 0x0002,
+ LocalFamily = UnixFamily,
+
+ IPv4Family = 0x0004,
+ IPv6Family = 0x0008,
+ InternetFamily = IPv4Family | IPv6Family,
+ InetFamily = InternetFamily,
+
+ KnownFamily = ~UnknownFamily,
+ AnyFamily = KnownFamily | UnknownFamily
+ };
+
+ /**
+ * Flags for the resolution.
+ *
+ * These flags are used for setting the resolution behaviour for this
+ * object:
+ * @li Passive: resolve to a passive socket (i.e., one that can be used for
+ * binding to a local interface)
+ * @li CanonName: request that the canonical name for the given nodename
+ * be found and recorded
+ * @li NoResolve: request that no external resolution be performed. The given
+ * nodename and servicename will be resolved locally only.
+ * @li NoSrv: don't try to use SRV-based name-resolution. (deprecated)
+ * @li UseSrv: use SRV-based name resolution.
+ * @li Multiport: the port/service argument is a list of port numbers and
+ * ranges. (future extension)
+ *
+ * @note SRV-based lookup and Multiport are not implemented yet.
+ */
+ enum Flags
+ {
+ Passive = 0x01,
+ CanonName = 0x02,
+ NoResolve = 0x04,
+ NoSrv = 0x08,
+ Multiport = 0x10,
+ UseSrv = 0x20
+ };
+
+ /**
+ * Error codes
+ *
+ * These are the possible error values that objects of this class
+ * may return. See \ref errorString() for getting a string representation
+ * for these errors.
+ *
+ * @li AddrFamily: Address family for the given nodename is not supported.
+ * @li TryAgain: Temporary failure in name resolution. You should try again.
+ * @li NonRecoverable: Non-recoverable failure in name resolution.
+ * @li BadFlags: Invalid flags were given.
+ * @li Memory: Memory allocation failure.
+ * @li NoName: The specified name or service doesn't exist.
+ * @li UnsupportedFamily: The requested socket family is not supported.
+ * @li UnsupportedService: The requested service is not supported for this
+ * socket type (i.e., a datagram service in a streaming socket).
+ * @li UnsupportedSocketType: The requested socket type is not supported.
+ * @li UnknownError: An unknown, unexpected error occurred.
+ * @li SystemError: A system error occurred. See @ref systemError.
+ * @li Canceled: This request was cancelled by the user.
+ */
+ enum ErrorCodes
+ {
+ // note: if you change this enum, take a look at KResolver::errorString
+ NoError = 0,
+ AddrFamily = -1,
+ TryAgain = -2,
+ NonRecoverable = -3,
+ BadFlags = -4,
+ Memory = -5,
+ NoName = -6,
+ UnsupportedFamily = -7,
+ UnsupportedService = -8,
+ UnsupportedSocketType = -9,
+ UnknownError = -10,
+ SystemError = -11,
+ Canceled = -100
+ };
+
+ /**
+ * Status codes.
+ *
+ * These are the possible status for a Resolver object. A value
+ * greater than zero indicates normal behaviour, while negative
+ * values either indicate failure or error.
+ *
+ * @li Idle: resolution has not yet been started.
+ * @li Queued: resolution is queued but not yet in progress.
+ * @li InProgress: resolution is in progress.
+ * @li PostProcessing: resolution is in progress.
+ * @li Success: resolution is done; you can retrieve the results.
+ * @li Canceled: request cancelled by the user.
+ * @li Failed: resolution is done, but failed.
+ *
+ * Note: the status Canceled and the error code Canceled are the same.
+ *
+ * Note 2: the status Queued and InProgress might not be distinguishable.
+ * Some implementations might not differentiate one from the other.
+ */
+ enum StatusCodes
+ {
+ Idle = 0,
+ Queued = 1,
+ InProgress = 5,
+ PostProcessing = 6,
+ Success = 10,
+ //Canceled = -100, // already defined above
+ Failed = -101
+ };
+
+ /**
+ * Default constructor.
+ *
+ * Creates an empty Resolver object. You should set the wanted
+ * names and flags using the member functions before starting
+ * the name resolution.
+ */
+ KResolver(QObject * = 0L, const char * = 0L);
+
+ /**
+ * Constructor with host and service names.
+ *
+ * Creates a Resolver object with the given host and
+ * service names. Flags are initialised to 0 and any address family
+ * will be accepted.
+ *
+ * @param nodename The host name we want resolved.
+ * @param servicename The service name associated, like "http".
+ */
+ KResolver(const QString& nodename, const QString& servicename = QString::null,
+ QObject * = 0L, const char * = 0L);
+
+ /**
+ * Destructor.
+ *
+ * When this object is deleted, it'll destroy all associated
+ * resources. If the resolution is still in progress, it will be
+ * cancelled and the signal will \b not be emitted.
+ */
+ virtual ~KResolver();
+
+ /**
+ * Retrieve the current status of this object.
+ *
+ * @see StatusCodes for the possible status codes.
+ */
+ int status() const;
+
+ /**
+ * Retrieve the error code in this object.
+ *
+ * This function will return NoError if we are not in
+ * an error condition. See @ref status and @ref StatusCodes to
+ * find out what the current status is.
+ *
+ * @see errorString for getting a textual representation of
+ * this error
+ */
+ int error() const;
+
+ /**
+ * Retrieve the associated system error code in this object.
+ *
+ * Many resolution operations may generate an extra error code
+ * as given by the C errno variable. That value is stored in the
+ * object and can be retrieved by this function.
+ */
+ int systemError() const;
+
+ /**
+ * Returns the textual representation of the error in this object.
+ */
+ inline QString errorString() const
+ { return errorString(error(), systemError()); }
+
+ /**
+ * Returns true if this object is currently running
+ */
+ bool isRunning() const;
+
+ /**
+ * The nodename to which the resolution was/is to be performed.
+ */
+ QString nodeName() const;
+
+ /**
+ * The service name to which the resolution was/is to be performed.
+ */
+ QString serviceName() const;
+
+ /**
+ * Sets the nodename for the resolution.
+ *
+ * Set the nodename to QString::null to unset it.
+ * @param nodename The nodename to be resolved.
+ */
+ void setNodeName(const QString& nodename);
+
+ /**
+ * Sets the service name to be resolved.
+ *
+ * Set it to QString::null to unset it.
+ * @param service The service to be resolved.
+ */
+ void setServiceName(const QString& service);
+
+ /**
+ * Sets both the host and the service names.
+ *
+ * Setting either value to QString::null will unset them.
+ * @param node The nodename
+ * @param service The service name
+ */
+ void setAddress(const QString& node, const QString& service);
+
+ /**
+ * Retrieves the flags set for the resolution.
+ *
+ * @see Flags for an explanation on what flags are possible
+ */
+ int flags() const;
+
+ /**
+ * Sets the flags.
+ *
+ * @param flags the new flags
+ * @return the old flags
+ * @see Flags for an explanation on the flags
+ */
+ int setFlags(int flags);
+
+ /**
+ * Sets the allowed socket families.
+ *
+ * @param families the families that we want/accept
+ * @see SocketFamilies for possible values
+ */
+ void setFamily(int families);
+
+ /**
+ * Sets the socket type we want.
+ *
+ * The values for the @p type parameter are the SOCK_*
+ * constants, defined in <sys/socket.h>. The most common
+ * values are:
+ * @li SOCK_STREAM streaming socket (= reliable, sequenced,
+ * connection-based)
+ * @li SOCK_DGRAM datagram socket (= unreliable, connectionless)
+ * @li SOCK_RAW raw socket, with direct access to the
+ * container protocol (such as IP)
+ *
+ * These three are the only values to which it is guaranteed that
+ * resolution will work. Some systems may define other constants (such as
+ * SOCK_RDM for reliable datagrams), but support is implementation-defined.
+ *
+ * @param type the wanted socket type (SOCK_* constants). Set
+ * 0 to use the default.
+ */
+ void setSocketType(int type);
+
+ /**
+ * Sets the protocol we want.
+ *
+ * Protocols are dependant on the selected address family, so you should know
+ * what you are doing if you use this function. Besides, protocols generally
+ * are either stream-based or datagram-based, so the value of the socket
+ * type is also important. The resolution will fail if these values don't match.
+ *
+ * When using an Internet socket, the values for the protocol are the
+ * IPPROTO_* constants, defined in <netinet/in.h>.
+ *
+ * You may choose to set the protocol either by its number or by its name, or
+ * by both. If you set:
+ * @li the number and the name: both values will be stored internally; you
+ * may set the name to an empty value, if wanted
+ * @li the number only (name = NULL): the name will be searched in the
+ * protocols database
+ * @li the name only (number = 0): the number will be searched in the
+ * database
+ * @li neither name nor number: reset to default behaviour
+ *
+ * @param protonum the protocol number we want
+ * @param name the protocol name
+ */
+ void setProtocol(int protonum, const char *name = 0L);
+
+ /**
+ * Starts the name resolution asynchronously.
+ *
+ * This function will queue this object for resolution
+ * and will return immediately. The status upon exit will either be
+ * Queued or InProgress or Failed.
+ *
+ * This function does nothing if the object is already queued. But if
+ * it had already succeeded or failed, this function will re-start it.
+ *
+ * Note: if both the nodename and the servicename are unset, this function
+ * will not queue, but will set a success state and emit the signal. Also
+ * note that in this case and maybe others, the signal @ref finished might
+ * be emitted before this function returns.
+ *
+ * @return true if this request was successfully queued for asynchronous
+ * resolution
+ */
+ bool start();
+
+ /**
+ * Waits for a request to finish resolving.
+ *
+ * This function will wait on a running request for its termination. The
+ * status upon exit will either be Success or Failed or Canceled.
+ *
+ * This function may be called from any thread, even one that is not the
+ * GUI thread or the one that started the resolution process. But note this
+ * function is not thread-safe nor reentrant: i.e., only one thread can be
+ * waiting on one given object.
+ *
+ * Also note that this function ensures that the @ref finished signal is
+ * emitted before it returns. That means that, as a side-effect, whenever
+ * wait() is called, the signal is emitted on the thread calling wait().
+ *
+ * @param msec the time to wait, in milliseconds or 0 to
+ * wait forever
+ * @return true if the resolution has finished processing, even when it
+ * failed or was canceled. False means the wait timed out and
+ * the resolution is still running.
+ */
+ bool wait(int msec = 0);
+
+ /**
+ * Cancels a running request
+ *
+ * This function will cancel a running request. If the request is not
+ * currently running or queued, this function does nothing.
+ *
+ * Note: if you tell the signal to be emitted, be aware that it might
+ * or might not be emitted before this function returns.
+ *
+ * @param emitSignal whether to emit the @ref finished signal or not
+ */
+ void cancel(bool emitSignal = true);
+
+ /**
+ * Retrieves the results of this resolution
+ *
+ * Use this function to retrieve the results of the resolution. If no
+ * data was resolved (yet) or if we failed, this function will return
+ * an empty object.
+ *
+ * @return the resolved data
+ * @see status for information on finding out if the resolution was successful
+ */
+ KResolverResults results() const;
+
+ /**
+ * Handles events. Reimplemented from QObject.
+ *
+ * This function handles the events generated by the manager indicating that
+ * this object has finished processing.
+ *
+ * Do not post events to this object.
+ */
+ virtual bool event(QEvent*);
+
+signals:
+ // signals
+
+ /**
+ * This signal is emitted whenever the resolution is finished, one
+ * way or another (success or failure). The @p results parameter
+ * will contain the resolved data.
+ *
+ * Note: if you are doing multiple resolutions, you can use the
+ * QObject::sender() function to distinguish one Resolver object from
+ * another.
+ *
+ * @param results the resolved data; might be empty if the resolution
+ * failed
+ * @see results for information on what the results are
+ *
+ * @note This signal is @b always delivered in the GUI event thread, even for
+ * resolutions that were started in secondary threads.
+ */
+ void finished(KResolverResults results);
+
+private:
+ void emitFinished();
+
+public:
+ // Static functions
+
+ /**
+ * Returns the string representation of this error code.
+ *
+ * @param errorcode the error code. See @ref ErrorCodes.
+ * @param syserror the system error code associated.
+ * @return the string representation. This is already
+ * i18n'ed.
+ */
+ static QString errorString(int errorcode, int syserror = 0);
+
+ /**
+ * Resolve the nodename and service name synchronously
+ *
+ * This static function is provided as convenience for simplifying
+ * name resolution. It resolves the given host and service names synchronously
+ * and returns the results it found. It is equivalent to the following code:
+ *
+ * \code
+ * KResolver qres(host, service);
+ * qres.setFlags(flags);
+ * qres.setFamily(families)
+ * qres.start();
+ * qres.wait();
+ * return qres.results();
+ * \endcode
+ *
+ * @param host the nodename to resolve
+ * @param service the service to resolve
+ * @param flags flags to be used
+ * @param families the families to be searched
+ * @return a KResolverResults object containing the results
+ * @see KResolverResults for information on how to obtain the error code
+ */
+ static KResolverResults resolve(const QString& host, const QString& service,
+ int flags = 0, int families = KResolver::InternetFamily);
+
+ /**
+ * Start an asynchronous name resolution
+ *
+ * This function is provided as a convenience to simplify the resolution
+ * process. It creates an internal KResolver object, connects the
+ * @ref finished signal to the given slot and starts the resolution
+ * asynchronously. It is more or less equivalent to the following code:
+ *
+ * \b Note: this function may trigger the signal before it returns, so
+ * your code must be prepared for this situation.
+ *
+ * \code
+ * KResolver* qres = new KResolver(host, service);
+ * QObject::connect(qres, SIGNAL(finished(KResolverResults)),
+ * userObj, userSlot);
+ * qres->setFlags(flags);
+ * qres->setFamily(families);
+ * return qres->start();
+ * \endcode
+ *
+ * You should use it like this in your code:
+ * \code
+ * KResolver::resolveAsync(myObj, SLOT(mySlot(KResolverResults)), host, service);
+ * \endcode
+ *
+ * @param userObj the object whose slot @p userSlot we will connect
+ * @param userSlot the slot to which we'll connect
+ * @param host the nodename to resolve
+ * @param service the service to resolve
+ * @param flags flags to be used
+ * @param families families to be searcheed
+ * @return true if the queueing was successful, false if not
+ * @see KResolverResults for information on how to obtain the error code
+ */
+ static bool resolveAsync(QObject* userObj, const char *userSlot,
+ const QString& host, const QString& service,
+ int flags = 0, int families = KResolver::InternetFamily);
+
+ /**
+ * Returns the domain name in an ASCII Compatible Encoding form, suitable
+ * for DNS lookups. This is the base for International Domain Name support
+ * over the Internet.
+ *
+ * Note this function may fail, in which case it'll return a null
+ * QCString. Reasons for failure include use of unknown code
+ * points (Unicode characters).
+ *
+ * Note that the encoding is illegible and, thus, should not be presented
+ * to the user, except if requested.
+ *
+ * @param unicodeDomain the domain name to be encoded
+ * @return the ACE-encoded suitable for DNS queries if successful, a null
+ * QCString if failure.
+ */
+ static QCString domainToAscii(const QString& unicodeDomain);
+
+ /**
+ * Does the inverse of @ref domainToAscii and return an Unicode domain
+ * name from the given ACE-encoded domain.
+ *
+ * This function may fail if the given domain cannot be successfully
+ * converted back to Unicode. Reasons for failure include a malformed
+ * domain name or good ones whose reencoding back to ACE don't match
+ * the form given here (e.g., ACE-encoding of an already
+ * ASCII-compatible domain).
+ *
+ * It is, however, guaranteed that domains returned
+ * by @ref domainToAscii will work.
+ *
+ * @param asciiDomain the ACE-encoded domain name to be decoded
+ * @return the Unicode representation of the given domain name
+ * if successful, the original string if not
+ * @note ACE = ASCII-Compatible Encoding, i.e., 7-bit
+ */
+ static QString domainToUnicode(const QCString& asciiDomain);
+
+ /**
+ * The same as above, but taking a QString argument.
+ *
+ * @param asciiDomain the ACE-encoded domain name to be decoded
+ * @return the Unicode representation of the given domain name
+ * if successful, QString::null if not.
+ */
+ static QString domainToUnicode(const QString& asciiDomain);
+
+ /**
+ * Normalise a domain name.
+ *
+ * In order to prevent simple mistakes in International Domain
+ * Names (IDN), it has been decided that certain code points
+ * (characters in Unicode) would be instead converted to others.
+ * This includes turning them all to lower case, as well certain
+ * other specific operations, as specified in the documents.
+ *
+ * For instance, the German 'ß' will be changed into 'ss', while
+ * the micro symbol 'µ' will be changed to the Greek mu 'μ'.
+ *
+ * Two equivalent domains have the same normalised form. And the
+ * normalised form of a normalised domain is itself (i.e., if
+ * d is normalised, the following is true: d == normalizeDomain(d) )
+ *
+ * This operation is equivalent to encoding and the decoding a Unicode
+ * hostname.
+ *
+ * @param domain a domain to be normalised
+ * @return the normalised domain, or QString::null if the domain is
+ * invalid.
+ */
+ static QString normalizeDomain(const QString& domain);
+
+ /**
+ * Resolves a protocol number to its names
+ *
+ * Note: the returned QStrList operates on deep-copies.
+ *
+ * @param protonum the protocol number to be looked for
+ * @return all the protocol names in a list. The first is the "proper"
+ * name.
+ */
+ static QStrList protocolName(int protonum);
+
+ /**
+ * Finds all aliases for a given protocol name
+ *
+ * @param protoname the protocol name to be looked for
+ * @return all the protocol names in a list. The first is the "proper"
+ * name.
+ */
+ static QStrList protocolName(const char *protoname);
+
+ /**
+ * Resolves a protocol name to its number
+ *
+ * @param protoname the protocol name to be looked for
+ * @return the protocol number or -1 if we couldn't locate it
+ */
+ static int protocolNumber(const char *protoname);
+
+ /**
+ * Resolves a service name to its port number
+ *
+ * @param servname the service name to be looked for
+ * @param protoname the protocol it is associated with
+ * @return the port number in host byte-order or -1 in case of error
+ */
+ static int servicePort(const char *servname, const char *protoname);
+
+ /**
+ * Finds all the aliases for a given service name
+ *
+ * Note: the returned QStrList operates on deep-copies.
+ *
+ * @param servname the service alias to be looked for
+ * @param protoname the protocol it is associated with
+ * @return all the service names in a list. The first is the "proper"
+ * name.
+ */
+ static QStrList serviceName(const char *servname, const char *protoname);
+
+ /**
+ * Resolves a port number to its names
+ *
+ * Note: the returned QStrList operates on deep copies.
+ *
+ * @param port the port number, in host byte-order
+ * @param protoname the protocol it is associated with
+ * @return all the service names in a list. The first is the "proper"
+ * name.
+ */
+ static QStrList serviceName(int port, const char *protoname);
+
+ /**
+ * Returns this machine's local hostname.
+ *
+ * @return this machine's local hostname
+ * @since 3.5
+ */
+ static QString localHostName();
+
+protected:
+
+ /**
+ * Sets the error codes
+ */
+ void setError(int errorcode, int systemerror = 0);
+
+ virtual void virtual_hook( int id, void* data );
+private:
+ KResolverPrivate* d;
+ friend class KResolverResults;
+ friend class ::KNetwork::Internal::KResolverManager;
+
+ static QStringList *idnDomains;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/kresolver_p.h b/kdecore/network/kresolver_p.h
new file mode 100644
index 000000000..2fac7eb45
--- /dev/null
+++ b/kdecore/network/kresolver_p.h
@@ -0,0 +1,353 @@
+/* -*- 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.
+ */
+
+#ifndef KRESOLVER_P_H
+#define KRESOLVER_P_H
+
+#include <config.h>
+#include <sys/types.h>
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qvaluelist.h>
+#include <qptrlist.h>
+#include <qptrqueue.h>
+#include <qthread.h>
+#include <qmutex.h>
+#include <qwaitcondition.h>
+#include <qsemaphore.h>
+#include <qevent.h>
+
+#include "kresolver.h"
+
+/* decide whether we need a mutex */
+#if !defined(HAVE_GETPROTOBYNAME_R) || !defined(HAVE_GETSERVBYNAME_R) || !defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETSERVBYPORT_R)
+# define NEED_MUTEX
+extern QMutex getXXbyYYmutex;
+#endif
+
+/* some systems have the functions, but don't declare them */
+#if defined(HAVE_GETSERVBYNAME_R) && !HAVE_DECL_GETSERVBYNAME_R
+extern "C" {
+ struct servent;
+ extern int getservbyname_r(const char* serv, const char* proto,
+ struct servent* servbuf,
+ char* buf, size_t buflen,
+ struct servent** result);
+ extern int getservbyport_r(int port, const char* proto,
+ struct servent* servbuf,
+ char* buf, size_t buflen,
+ struct servent** result);
+
+ struct protoent;
+ extern int getprotobyname_r(const char* proto, struct protoent* protobuf,
+ char *buf, size_t buflen,
+ struct protoent** result);
+ extern int getprotobynumber_r(int proto, struct protoent* protobuf,
+ char *buf, size_t buflen,
+ struct protoent** result);
+}
+#endif
+
+/* decide whether res_init is thread-safe or not */
+#if defined(__GLIBC__)
+# undef RES_INIT_THREADSAFE
+#endif
+
+namespace KNetwork
+{
+ // defined in network/qresolverworkerbase.h
+ class KResolverWorkerBase;
+ class KResolverWorkerFactoryBase;
+ class KResolverPrivate;
+
+ namespace Internal
+ {
+ class KResolverManager;
+ class KResolverThread;
+ struct RequestData;
+
+ struct InputData
+ {
+ QString node, service;
+ QCString protocolName;
+ int flags;
+ int familyMask;
+ int socktype;
+ int protocol;
+ };
+ }
+
+ class KResolverPrivate
+ {
+ public:
+ // parent class. Should never be changed!
+ KResolver* parent;
+ bool deleteWhenDone : 1;
+ bool waiting : 1;
+
+ // class status. Should not be changed by worker threads!
+ volatile int status;
+ volatile int errorcode, syserror;
+
+ // input data. Should not be changed by worker threads!
+ Internal::InputData input;
+
+ // mutex
+ QMutex mutex;
+
+ // output data
+ KResolverResults results;
+
+ KResolverPrivate(KResolver* _parent,
+ const QString& _node = QString::null,
+ const QString& _service = QString::null)
+ : parent(_parent), deleteWhenDone(false), waiting(false),
+ status(0), errorcode(0), syserror(0)
+ {
+ input.node = _node;
+ input.service = _service;
+ input.flags = 0;
+ input.familyMask = KResolver::AnyFamily;
+ input.socktype = 0;
+ input.protocol = 0;
+
+ results.setAddress(_node, _service);
+ }
+ };
+
+ namespace Internal
+ {
+ struct RequestData
+ {
+ // worker threads should not change values in the input data
+ KNetwork::KResolverPrivate *obj;
+ const KNetwork::Internal::InputData *input;
+ KNetwork::KResolverWorkerBase *worker; // worker class
+ RequestData *requestor; // class that requested us
+
+ volatile int nRequests; // how many requests that we made we still have left
+ };
+
+ /*
+ * @internal
+ * This class is the resolver manager
+ */
+ class KResolverManager
+ {
+ public:
+ enum EventTypes
+ { ResolutionCompleted = 1576 }; // arbitrary value;
+
+ /*
+ * This wait condition is used to notify wait states (KResolver::wait) that
+ * the resolver manager has finished processing one or more objects. All
+ * objects in wait state will be woken up and will check if they are done.
+ * If they aren't, they will go back to sleeping.
+ */
+ QWaitCondition notifyWaiters;
+
+ private:
+ /*
+ * This variable is used to count the number of threads that are running
+ */
+ volatile unsigned short runningThreads;
+
+ /*
+ * This variable is used to count the number of threads that are currently
+ * waiting for data.
+ */
+ unsigned short availableThreads;
+
+ /*
+ * This wait condition is used to notify worker threads that there is new
+ * data available that has to be processed. All worker threads wait on this
+ * waitcond for a limited amount of time.
+ */
+ QWaitCondition feedWorkers;
+
+ // this mutex protects the data in this object
+ QMutex mutex;
+
+ // hold a list of all the current threads we have
+ QPtrList<KResolverThread> workers;
+
+ // hold a list of all the new requests we have
+ QPtrList<RequestData> newRequests;
+
+ // hold a list of all the requests in progress we have
+ QPtrList<RequestData> currentRequests;
+
+ // hold a list of all the workers we have
+ QPtrList<KNetwork::KResolverWorkerFactoryBase> workerFactories;
+
+ // private constructor
+ KResolverManager();
+
+ public:
+ static KResolverManager* manager() KDE_NO_EXPORT; // creates and returns the global manager
+
+ // destructor
+ ~KResolverManager();
+
+ /*
+ * Register this thread in the pool
+ */
+ void registerThread(KResolverThread* id);
+
+ /*
+ * Unregister this thread from the pool
+ */
+ void unregisterThread(KResolverThread* id);
+
+ /*
+ * Requests new data to work on.
+ *
+ * This function should only be called from a worker thread. This function
+ * is thread-safe.
+ *
+ * If there is data to be worked on, this function will return it. If there is
+ * none, this function will return a null pointer.
+ */
+ RequestData* requestData(KResolverThread* id, int maxWaitTime);
+
+ /*
+ * Releases the resources and returns the resolved data.
+ *
+ * This function should only be called from a worker thread. It is
+ * thread-safe. It does not post the event to the manager.
+ */
+ void releaseData(KResolverThread *id, RequestData* data);
+
+ /*
+ * Registers a new worker class by way of its factory.
+ *
+ * This function is NOT thread-safe.
+ */
+ void registerNewWorker(KNetwork::KResolverWorkerFactoryBase *factory);
+
+ /*
+ * Enqueues new resolutions.
+ */
+ void enqueue(KNetwork::KResolver *obj, RequestData* requestor);
+
+ /*
+ * Dispatch a new request
+ */
+ void dispatch(RequestData* data);
+
+ /*
+ * Dequeues a resolution.
+ */
+ void dequeue(KNetwork::KResolver *obj);
+
+ /*
+ * Notifies the manager that the given resolution is about to
+ * be deleted. This function should only be called by the
+ * KResolver destructor.
+ */
+ void aboutToBeDeleted(KNetwork::KResolver *obj);
+
+ /*
+ * Notifies the manager that new events are ready.
+ */
+ void newEvent();
+
+ /*
+ * This function is called by the manager to receive a new event. It operates
+ * on the @ref eventSemaphore semaphore, which means it will block till there
+ * is at least one event to go.
+ */
+ void receiveEvent();
+
+ private:
+ /*
+ * finds a suitable worker for this request
+ */
+ KNetwork::KResolverWorkerBase *findWorker(KNetwork::KResolverPrivate *p);
+
+ /*
+ * finds data for this request
+ */
+ RequestData* findData(KResolverThread*);
+
+ /*
+ * Handle completed requests.
+ *
+ * This function is called by releaseData above
+ */
+ void handleFinished();
+
+ /*
+ * Handle one completed request.
+ *
+ * This function is called by handleFinished above.
+ */
+ bool handleFinishedItem(RequestData* item);
+
+ /*
+ * Notifies the parent class that this request is done.
+ *
+ * This function deletes the request
+ */
+ void doNotifying(RequestData *p);
+
+ /*
+ * Dequeues and notifies an object that is in Queued state
+ * Returns true if the object is no longer queued; false if it could not
+ * be dequeued (i.e., it's running)
+ */
+ bool dequeueNew(KNetwork::KResolver* obj);
+ };
+
+ /*
+ * @internal
+ * This class is a worker thread in the resolver system.
+ * This class must be thread-safe.
+ */
+ class KResolverThread: public QThread
+ {
+ private:
+ // private constructor. Only the manager can create worker threads
+ KResolverThread();
+ RequestData* data;
+
+ protected:
+ virtual void run(); // here the thread starts
+
+ friend class KNetwork::Internal::KResolverManager;
+ friend class KNetwork::KResolverWorkerBase;
+
+ public:
+ bool checkResolver(); // see KResolverWorkerBase::checkResolver
+ void acquireResolver(); // see KResolverWorkerBase::acquireResolver
+ void releaseResolver(); // see KResolverWorkerBase::releaseResolver
+ };
+
+ } // namespace Internal
+
+} // namespace KNetwork
+
+
+#endif
diff --git a/kdecore/network/kresolvermanager.cpp b/kdecore/network/kresolvermanager.cpp
new file mode 100644
index 000000000..204031915
--- /dev/null
+++ b/kdecore/network/kresolvermanager.cpp
@@ -0,0 +1,822 @@
+/* -*- 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 <sys/types.h>
+#include <netinet/in.h>
+#include <limits.h>
+#include <unistd.h> // only needed for pid_t
+
+#ifdef HAVE_RES_INIT
+# include <sys/stat.h>
+extern "C" {
+# include <arpa/nameser.h>
+}
+# include <time.h>
+# include <resolv.h>
+#endif
+
+#include <qapplication.h>
+#include <qstring.h>
+#include <qcstring.h>
+#include <qptrlist.h>
+#include <qtimer.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+#include <qsemaphore.h>
+
+#include <kde_file.h>
+#include <kdebug.h>
+#include "kresolver.h"
+#include "kresolver_p.h"
+#include "kresolverworkerbase.h"
+
+namespace KNetwork
+{
+ namespace Internal
+ {
+ void initSrvWorker();
+ void initStandardWorkers();
+ }
+}
+
+using namespace KNetwork;
+using namespace KNetwork::Internal;
+
+/*
+ * Explanation on how the resolver system works
+
+ When KResolver::start is called, it calls KResolverManager::enqueue to add
+ an entry to the queue. KResolverManager::enqueue will verify the availability
+ of a worker thread: if one is available, it will dispatch the request to it.
+ If no threads are available, it will then decide whether to launch a thread
+ or to queue for the future.
+
+ (This process is achieved by always queueing the new request, starting a
+ new thread if necessary and then notifying of the availability of data
+ to all worker threads).
+
+ * Worker thread
+ A new thread, when started, will enter its event loop
+ immediately. That is, it'll first try to acquire new data to
+ process, which means it will lock and unlock the manager mutex in
+ the process.
+
+ If it finds no new data, it'll wait on the feedWorkers condition
+ for a certain maximum time. If that time expires and there's still
+ no data, the thread will exit, in order to save system resources.
+
+ If it finds data, however, it'll set up and call the worker class
+ that has been selected by the manager. Once that worker is done,
+ the thread releases the data through KResolverManager::releaseData.
+
+ * Data requesting/releasing
+ A worker thread always calls upon functions on the resolver manager
+ in order to acquire and release data.
+
+ When data is being requested, the KResolverManager::requestData
+ function will look the currentRequests list and return the first
+ Queued request it finds, while marking it to be InProgress.
+
+ When the worker class has returned, the worker thread will release
+ that data through the KResolverManager::releaseData function. If the
+ worker class has requested no further data (nRequests == 0), the
+ request's status is marked to be Done. It'll then look at the
+ requestor for that data: if it was requested by another worker,
+ it'll decrement the requests count for that one and add the results
+ to a list. And, finally, if the requests count for the requestor
+ becomes 0, it'll repeat this process for the requestor as well
+ (change status to Done, check for a requestor).
+ */
+
+namespace
+{
+
+/*
+ * This class is used to control the access to the
+ * system's resolver API.
+ *
+ * It is necessary to periodically poll /etc/resolv.conf and reload
+ * it if any changes are noticed. This class does exactly that.
+ *
+ * However, there's also the problem of reloading the structure while
+ * some threads are in progress. Therefore, we keep a usage reference count.
+ */
+class ResInitUsage
+{
+public:
+
+#ifdef HAVE_RES_INIT
+ time_t mTime;
+ int useCount;
+
+# ifndef RES_INIT_THREADSAFE
+ QWaitCondition cond;
+ QMutex mutex;
+# endif
+
+ bool shouldResInit()
+ {
+ // check if /etc/resolv.conf has changed
+ KDE_struct_stat st;
+ if (KDE_stat("/etc/resolv.conf", &st) != 0)
+ return false;
+
+ if (mTime != st.st_mtime)
+ {
+ kdDebug(179) << "shouldResInit: /etc/resolv.conf updated" << endl;
+ return true;
+ }
+ return false;
+ }
+
+ void callResInit()
+ {
+ if (mTime != 0)
+ {
+ // don't call it the first time
+ // let it be initialised naturally
+ kdDebug(179) << "callResInit: calling res_init()" << endl;
+ res_init();
+ }
+
+ KDE_struct_stat st;
+ if (KDE_stat("/etc/resolv.conf", &st) == 0)
+ mTime = st.st_mtime;
+ }
+
+ ResInitUsage()
+ : mTime(0), useCount(0)
+ { }
+
+ /*
+ * Marks the end of usage to the resolver tools
+ */
+ void release()
+ {
+# ifndef RES_INIT_THREADSAFE
+ QMutexLocker locker(&mutex);
+ if (--useCount == 0)
+ {
+ if (shouldResInit())
+ callResInit();
+
+ // we've reached 0, wake up anyone that's waiting to call res_init
+ cond.wakeAll();
+ }
+# else
+ // do nothing
+# endif
+ }
+
+ /*
+ * Marks the beginning of usage of the resolver API
+ */
+ void acquire()
+ {
+# ifndef RES_INIT_THREADSAFE
+ mutex.lock();
+
+ if (shouldResInit())
+ {
+ if (useCount)
+ {
+ // other threads are already using the API, so wait till
+ // it's all clear
+ // the thread that emits this condition will also call res_init
+ //qDebug("ResInitUsage: waiting for libresolv to be clear");
+ cond.wait(&mutex);
+ }
+ else
+ // we're clear
+ callResInit();
+ }
+ useCount++;
+ mutex.unlock();
+
+# else
+ if (shouldResInit())
+ callResInit();
+
+# endif
+ }
+
+#else
+ ResInitUsage()
+ { }
+
+ bool shouldResInit()
+ { return false; }
+
+ void acquire()
+ { }
+
+ void release()
+ { }
+#endif
+
+} resInit;
+
+} // anonymous namespace
+
+/*
+ * parameters
+ */
+// a thread will try maxThreadRetries to get data, waiting at most
+// maxThreadWaitTime milliseconds between each attempt. After that, it'll
+// exit
+static const int maxThreadWaitTime = 2000; // 2 seconds
+static const int maxThreads = 5;
+
+static pid_t pid; // FIXME -- disable when everything is ok
+
+KResolverThread::KResolverThread()
+ : data(0L)
+{
+}
+
+// remember! This function runs in a separate thread!
+void KResolverThread::run()
+{
+ // initialisation
+ // enter the loop already
+
+ //qDebug("KResolverThread(thread %u/%p): started", pid, (void*)QThread::currentThread());
+ KResolverManager::manager()->registerThread(this);
+ while (true)
+ {
+ data = KResolverManager::manager()->requestData(this, ::maxThreadWaitTime);
+ //qDebug("KResolverThread(thread %u/%p) got data %p", KResolverManager::pid,
+ // (void*)QThread::currentThread(), (void*)data);
+ if (data)
+ {
+ // yes, we got data
+ // process it!
+
+ // 1) set up
+ ;
+
+ // 2) run it
+ data->worker->run();
+
+ // 3) release data
+ KResolverManager::manager()->releaseData(this, data);
+
+ // now go back to the loop
+ }
+ else
+ break;
+ }
+
+ KResolverManager::manager()->unregisterThread(this);
+ //qDebug("KResolverThread(thread %u/%p): exiting", pid, (void*)QThread::currentThread());
+}
+
+bool KResolverThread::checkResolver()
+{
+ return resInit.shouldResInit();
+}
+
+void KResolverThread::acquireResolver()
+{
+#if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD)
+ getXXbyYYmutex.lock();
+#endif
+
+ resInit.acquire();
+}
+
+void KResolverThread::releaseResolver()
+{
+#if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD)
+ getXXbyYYmutex.unlock();
+#endif
+
+ resInit.release();
+}
+
+static KResolverManager *globalManager;
+
+KResolverManager* KResolverManager::manager()
+{
+ if (globalManager == 0L)
+ new KResolverManager();
+ return globalManager;
+}
+
+KResolverManager::KResolverManager()
+ : runningThreads(0), availableThreads(0)
+{
+ globalManager = this;
+ workers.setAutoDelete(true);
+ currentRequests.setAutoDelete(true);
+ initSrvWorker();
+ initStandardWorkers();
+
+ pid = getpid();
+}
+
+KResolverManager::~KResolverManager()
+{
+ // this should never be called
+
+ // kill off running threads
+ for (workers.first(); workers.current(); workers.next())
+ workers.current()->terminate();
+}
+
+void KResolverManager::registerThread(KResolverThread* )
+{
+}
+
+void KResolverManager::unregisterThread(KResolverThread*)
+{
+ runningThreads--;
+}
+
+// this function is called by KResolverThread::run
+RequestData* KResolverManager::requestData(KResolverThread *th, int maxWaitTime)
+{
+ /////
+ // This function is called in a worker thread!!
+ /////
+
+ // lock the mutex, so that the manager thread or other threads won't
+ // interfere.
+ QMutexLocker locker(&mutex);
+ RequestData *data = findData(th);
+
+ if (data)
+ // it found something, that's good
+ return data;
+
+ // nope, nothing found; sleep for a while
+ availableThreads++;
+ feedWorkers.wait(&mutex, maxWaitTime);
+ availableThreads--;
+
+ data = findData(th);
+ return data;
+}
+
+RequestData* KResolverManager::findData(KResolverThread* th)
+{
+ /////
+ // This function is called by @ref requestData above and must
+ // always be called with a locked mutex
+ /////
+
+ // now find data to be processed
+ for (RequestData *curr = newRequests.first(); curr; curr = newRequests.next())
+ if (!curr->worker->m_finished)
+ {
+ // found one
+ if (curr->obj)
+ curr->obj->status = KResolver::InProgress;
+ curr->worker->th = th;
+
+ // move it to the currentRequests list
+ currentRequests.append(newRequests.take());
+
+ return curr;
+ }
+
+ // found nothing!
+ return 0L;
+}
+
+// this function is called by KResolverThread::run
+void KResolverManager::releaseData(KResolverThread *, RequestData* data)
+{
+ /////
+ // This function is called in a worker thread!!
+ /////
+
+ //qDebug("KResolverManager::releaseData(%u/%p): %p has been released", pid,
+// (void*)QThread::currentThread(), (void*)data);
+
+ if (data->obj)
+ {
+ data->obj->status = KResolver::PostProcessing;
+ }
+
+ data->worker->m_finished = true;
+ data->worker->th = 0L; // this releases the object
+
+ // handle finished requests
+ handleFinished();
+}
+
+// this function is called by KResolverManager::releaseData above
+void KResolverManager::handleFinished()
+{
+ bool redo = false;
+ QPtrQueue<RequestData> doneRequests;
+
+ mutex.lock();
+
+ // loop over all items on the currently running list
+ // we loop from the last to the first so that we catch requests with "requestors" before
+ // we catch the requestor itself.
+ RequestData *curr = currentRequests.last();
+ while (curr)
+ {
+ if (curr->worker->th == 0L)
+ {
+ if (handleFinishedItem(curr))
+ {
+ doneRequests.enqueue(currentRequests.take());
+ if (curr->requestor &&
+ curr->requestor->nRequests == 0 &&
+ curr->requestor->worker->m_finished)
+ // there's a requestor that is now finished
+ redo = true;
+ }
+ }
+
+ curr = currentRequests.prev();
+ }
+
+ //qDebug("KResolverManager::handleFinished(%u): %d requests to notify", pid, doneRequests.count());
+ while (RequestData *d = doneRequests.dequeue())
+ doNotifying(d);
+
+ mutex.unlock();
+
+ if (redo)
+ {
+ //qDebug("KResolverManager::handleFinished(%u): restarting processing to catch requestor",
+ // pid);
+ handleFinished();
+ }
+}
+
+// This function is called by KResolverManager::handleFinished above
+bool KResolverManager::handleFinishedItem(RequestData* curr)
+
+{
+ // for all items that aren't currently running, remove from the list
+ // this includes all finished or cancelled requests
+
+ if (curr->worker->m_finished && curr->nRequests == 0)
+ {
+ // this one has finished
+ if (curr->obj)
+ curr->obj->status = KResolver::PostProcessing; // post-processing is run in doNotifying()
+
+ if (curr->requestor)
+ --curr->requestor->nRequests;
+
+ //qDebug("KResolverManager::handleFinishedItem(%u): removing %p since it's done",
+ // pid, (void*)curr);
+ return true;
+ }
+ return false;
+}
+
+
+
+void KResolverManager::registerNewWorker(KResolverWorkerFactoryBase *factory)
+{
+ workerFactories.append(factory);
+}
+
+KResolverWorkerBase* KResolverManager::findWorker(KResolverPrivate* p)
+{
+ /////
+ // this function can be called on any user thread
+ /////
+
+ // this function is called with an unlocked mutex and it's expected to be
+ // thread-safe!
+ // but the factory list is expected not to be changed asynchronously
+
+ // This function is responsible for finding a suitable worker for the given
+ // input. That means we have to do a costly operation to create each worker
+ // class and call their preprocessing functions. The first one that
+ // says they can process (i.e., preprocess() returns true) will get the job.
+
+ KResolverWorkerBase *worker;
+ for (KResolverWorkerFactoryBase *factory = workerFactories.first(); factory;
+ factory = workerFactories.next())
+ {
+ worker = factory->create();
+
+ // set up the data the worker needs to preprocess
+ worker->input = &p->input;
+
+ if (worker->preprocess())
+ {
+ // good, this one says it can process
+ if (worker->m_finished)
+ p->status = KResolver::PostProcessing;
+ else
+ p->status = KResolver::Queued;
+ return worker;
+ }
+
+ // no, try again
+ delete worker;
+ }
+
+ // found no worker
+ return 0L;
+}
+
+void KResolverManager::doNotifying(RequestData *p)
+{
+ /////
+ // This function may be called on any thread
+ // any thread at all: user threads, GUI thread, manager thread or worker thread
+ /////
+
+ // Notification and finalisation
+ //
+ // Once a request has finished the normal processing, we call the
+ // post processing function.
+ //
+ // After that is done, we will consolidate all results in the object's
+ // KResolverResults and then post an event indicating that the signal
+ // be emitted
+ //
+ // In case we detect that the object is waiting for completion, we do not
+ // post the event, for KResolver::wait will take care of emitting the
+ // signal.
+ //
+ // Once we release the mutex on the object, we may no longer reference it
+ // for it might have been deleted.
+
+ // "User" objects are those that are not created by the manager. Note that
+ // objects created by worker threads are considered "user" objects. Objects
+ // created by the manager are those created for KResolver::resolveAsync.
+ // We should delete them.
+
+ if (p->obj)
+ {
+ // lock the object
+ p->obj->mutex.lock();
+ KResolver* parent = p->obj->parent; // is 0 for non-"user" objects
+ KResolverResults& r = p->obj->results;
+
+ if (p->obj->status == KResolver::Canceled)
+ {
+ p->obj->status = KResolver::Canceled;
+ p->obj->errorcode = KResolver::Canceled;
+ p->obj->syserror = 0;
+ r.setError(KResolver::Canceled, 0);
+ }
+ else if (p->worker)
+ {
+ // post processing
+ p->worker->postprocess(); // ignore the result
+
+ // copy the results from the worker thread to the final
+ // object
+ r = p->worker->results;
+
+ // reset address
+ r.setAddress(p->input->node, p->input->service);
+
+ //qDebug("KResolverManager::doNotifying(%u/%p): for %p whose status is %d and has %d results",
+ //pid, (void*)QThread::currentThread(), (void*)p, p->obj->status, r.count());
+
+ p->obj->errorcode = r.error();
+ p->obj->syserror = r.systemError();
+ p->obj->status = !r.isEmpty() ?
+ KResolver::Success : KResolver::Failed;
+ }
+ else
+ {
+ r.empty();
+ r.setError(p->obj->errorcode, p->obj->syserror);
+ }
+
+ // check whether there's someone waiting
+ if (!p->obj->waiting && parent)
+ // no, so we must post an event requesting that the signal be emitted
+ // sorry for the C-style cast, but neither static nor reintepret cast work
+ // here; I'd have to do two casts
+ QApplication::postEvent(parent, new QEvent((QEvent::Type)(ResolutionCompleted)));
+
+ // release the mutex
+ p->obj->mutex.unlock();
+ }
+ else
+ {
+ // there's no object!
+ if (p->worker)
+ p->worker->postprocess();
+ }
+
+ delete p->worker;
+
+ // ignore p->requestor and p->nRequests
+ // they have been dealt with by the main loop
+
+ delete p;
+
+ // notify any objects waiting in KResolver::wait
+ notifyWaiters.wakeAll();
+}
+
+// enqueue a new request
+// this function is called from KResolver::start and
+// from KResolverWorkerBase::enqueue
+void KResolverManager::enqueue(KResolver *obj, RequestData *requestor)
+{
+ RequestData *newrequest = new RequestData;
+ newrequest->nRequests = 0;
+ newrequest->obj = obj->d;
+ newrequest->input = &obj->d->input;
+ newrequest->requestor = requestor;
+
+ // when processing a new request, find the most
+ // suitable worker
+ if ((newrequest->worker = findWorker(obj->d)) == 0L)
+ {
+ // oops, problem
+ // cannot find a worker class for this guy
+ obj->d->status = KResolver::Failed;
+ obj->d->errorcode = KResolver::UnsupportedFamily;
+ obj->d->syserror = 0;
+
+ doNotifying(newrequest);
+ return;
+ }
+
+ // no, queue it
+ // p->status was set in findWorker!
+ if (requestor)
+ requestor->nRequests++;
+
+ if (!newrequest->worker->m_finished)
+ dispatch(newrequest);
+ else if (newrequest->nRequests > 0)
+ {
+ mutex.lock();
+ currentRequests.append(newrequest);
+ mutex.unlock();
+ }
+ else
+ // already done
+ doNotifying(newrequest);
+}
+
+// a new request has been created
+// dispatch it
+void KResolverManager::dispatch(RequestData *data)
+{
+ // As stated in the beginning of the file, this function
+ // is supposed to verify the availability of threads, start
+ // any if necessary
+
+ QMutexLocker locker(&mutex);
+
+ // add to the queue
+ newRequests.append(data);
+
+ // check if we need to start a new thread
+ //
+ // we depend on the variables availableThreads and runningThreads to
+ // know if we are supposed to start any threads:
+ // - if availableThreads > 0, then there is at least one thread waiting,
+ // blocked in KResolverManager::requestData. It can't unblock
+ // while we are holding the mutex locked, therefore we are sure that
+ // our event will be handled
+ // - if availableThreads == 0:
+ // - if runningThreads < maxThreads
+ // we will start a new thread, which will certainly block in
+ // KResolverManager::requestData because we are holding the mutex locked
+ // - if runningThreads == maxThreads
+ // This situation generally means that we have already maxThreads running
+ // and that all of them are processing. We will not start any new threads,
+ // but will instead wait for one to finish processing and request new data
+ //
+ // There's a possible race condition here, which goes unhandled: if one of
+ // threads has timed out waiting for new data and is in the process of
+ // exiting. In that case, availableThreads == 0 and runningThreads will not
+ // have decremented yet. This means that we will not start a new thread
+ // that we could have. However, since there are other threads working, our
+ // event should be handled soon.
+ // It won't be handled if and only if ALL threads are in the process of
+ // exiting. That situation is EXTREMELY unlikely and is not handled either.
+ //
+ if (availableThreads == 0 && runningThreads < maxThreads)
+ {
+ // yes, a new thread should be started
+
+ // find if there's a finished one
+ KResolverThread *th = workers.first();
+ while (th && th->running())
+ th = workers.next();
+
+ if (th == 0L)
+ // no, create one
+ th = new KResolverThread;
+ else
+ workers.take();
+
+ th->start();
+ workers.append(th);
+ runningThreads++;
+ }
+
+ feedWorkers.wakeAll();
+
+ // clean up idle threads
+ workers.first();
+ while (workers.current())
+ {
+ if (!workers.current()->running())
+ workers.remove();
+ else
+ workers.next();
+ }
+}
+
+// this function is called by KResolverManager::dequeue
+bool KResolverManager::dequeueNew(KResolver* obj)
+{
+ // This function must be called with a locked mutex
+ // Deadlock warning:
+ // always lock the global mutex first if both mutexes must be locked
+
+ KResolverPrivate *d = obj->d;
+
+ // check if it's in the new request list
+ RequestData *curr = newRequests.first();
+ while (curr)
+ if (curr->obj == d)
+ {
+ // yes, this object is still in the list
+ // but it has never been processed
+ d->status = KResolver::Canceled;
+ d->errorcode = KResolver::Canceled;
+ d->syserror = 0;
+ newRequests.take();
+
+ delete curr->worker;
+ delete curr;
+
+ return true;
+ }
+ else
+ curr = newRequests.next();
+
+ // check if it's running
+ curr = currentRequests.first();
+ while (curr)
+ if (curr->obj == d)
+ {
+ // it's running. We cannot simply take it out of the list.
+ // it will be handled when the thread that is working on it finishes
+ d->mutex.lock();
+
+ d->status = KResolver::Canceled;
+ d->errorcode = KResolver::Canceled;
+ d->syserror = 0;
+
+ // disengage from the running threads
+ curr->obj = 0L;
+ curr->input = 0L;
+ if (curr->worker)
+ curr->worker->input = 0L;
+
+ d->mutex.unlock();
+ }
+ else
+ curr = currentRequests.next();
+
+ return false;
+}
+
+// this function is called by KResolver::cancel
+// it's expected to be thread-safe
+void KResolverManager::dequeue(KResolver *obj)
+{
+ QMutexLocker locker(&mutex);
+ dequeueNew(obj);
+}
diff --git a/kdecore/network/kresolverstandardworkers.cpp b/kdecore/network/kresolverstandardworkers.cpp
new file mode 100644
index 000000000..93c706306
--- /dev/null
+++ b/kdecore/network/kresolverstandardworkers.cpp
@@ -0,0 +1,1028 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003,2004 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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+#include <qthread.h>
+#include <qmutex.h>
+#include <qstrlist.h>
+#include <qfile.h>
+
+#include "kdebug.h"
+#include "kglobal.h"
+#include "kstandarddirs.h"
+#include "kapplication.h"
+
+#include "kresolver.h"
+#include "ksocketaddress.h"
+#include "kresolverstandardworkers_p.h"
+
+struct hostent;
+struct addrinfo;
+
+using namespace KNetwork;
+using namespace KNetwork::Internal;
+
+static bool hasIPv6()
+{
+#ifndef AF_INET6
+ return false;
+#else
+ if (getenv("KDE_NO_IPV6") != 0L)
+ return false;
+
+ int fd = ::socket(AF_INET6, SOCK_STREAM, 0);
+ if (fd == -1)
+ return false;
+
+ ::close(fd);
+ return true;
+#endif
+}
+
+// blacklist management
+static QMutex blacklistMutex; // KDE4: change to a QReadWriteLock
+QStringList KBlacklistWorker::blacklist;
+
+void KBlacklistWorker::init()
+{
+ // HACK!
+ // FIXME KDE4: How do I detect there is an instance, without triggering
+ // its creation or an assertion fault?
+ if (!KGlobal::_instance)
+ return;
+
+ static bool beenhere = false;
+
+ if (beenhere)
+ return;
+
+ beenhere = true;
+ loadBlacklist();
+}
+
+void KBlacklistWorker::loadBlacklist()
+{
+ QMutexLocker locker(&blacklistMutex);
+ QStringList filelist = KGlobal::dirs()->findAllResources("config", "ipv6blacklist");
+
+ QStringList::ConstIterator it = filelist.constBegin(),
+ end = filelist.constEnd();
+ for ( ; it != end; ++it)
+ {
+ // for each file, each line is a domainname to be blacklisted
+ QFile f(*it);
+ if (!f.open(IO_ReadOnly))
+ continue;
+
+ QTextStream stream(&f);
+ stream.setEncoding(QTextStream::Latin1);
+ for (QString line = stream.readLine(); !line.isNull();
+ line = stream.readLine())
+ {
+ if (line.isEmpty())
+ continue;
+
+ // make sure there are no surrounding whitespaces
+ // and that it starts with .
+ line = line.stripWhiteSpace();
+ if (line[0] != '.')
+ line.prepend('.');
+
+ blacklist.append(line.lower());
+ }
+ }
+}
+
+// checks the blacklist to see if the domain is listed
+// it matches the domain ending part
+bool KBlacklistWorker::isBlacklisted(const QString& host)
+{
+ KBlacklistWorker::init();
+
+ // empty hostnames cannot be blacklisted
+ if (host.isEmpty())
+ return false;
+
+ // KDE4: QLatin1String
+ QString ascii = QString::fromLatin1(KResolver::domainToAscii(host));
+
+ QMutexLocker locker(&blacklistMutex);
+
+ // now find out if this hostname is present
+ QStringList::ConstIterator it = blacklist.constBegin(),
+ end = blacklist.constEnd();
+ for ( ; it != end; ++it)
+ if (ascii.endsWith(*it))
+ return true;
+
+ // no match:
+ return false;
+}
+
+bool KBlacklistWorker::preprocess()
+{
+ if (isBlacklisted(nodeName()))
+ {
+ results.setError(KResolver::NoName);
+ finished();
+ return true;
+ }
+ return false;
+}
+
+bool KBlacklistWorker::run()
+{
+ results.setError(KResolver::NoName);
+ finished();
+ return false; // resolution failure
+}
+
+namespace
+{
+ /*
+ * Note on the use of the system resolver functions:
+ *
+ * In all cases, we prefer to use the new getaddrinfo(3) call. That means
+ * it will always be used if it is found.
+ *
+ * If it's not found, we have the option to use gethostbyname2_r,
+ * gethostbyname_r, gethostbyname2 and gethostbyname. If gethostbyname2_r
+ * is defined, we will use it.
+ *
+ * If it's not defined, we have to choose between the non-reentrant
+ * gethostbyname2 and the reentrant but IPv4-only gethostbyname_r:
+ * we will choose gethostbyname2 if AF_INET6 is defined.
+ *
+ * Lastly, gethostbyname will be used if nothing else is present.
+ */
+
+#ifndef HAVE_GETADDRINFO
+
+# if defined(HAVE_GETHOSTBYNAME2_R)
+# define USE_GETHOSTBYNAME2_R
+# elif defined(HAVE_GETHOSTBYNAME_R) && (!defined(AF_INET6) || !defined(HAVE_GETHOSTBYNAME2))
+# define USE_GETHOSTBYNAME_R
+# elif defined(HAVE_GETHOSTBYNAME2)
+# define USE_GETHOSTBYNAME2)
+# else
+# define USE_GETHOSTBYNAME
+# endif
+
+ class GetHostByNameThread: public KResolverWorkerBase
+ {
+ public:
+ QCString m_hostname; // might be different!
+ Q_UINT16 m_port;
+ int m_scopeid;
+ int m_af;
+ KResolverResults& results;
+
+ GetHostByNameThread(const char * hostname, Q_UINT16 port,
+ int scopeid, int af, KResolverResults* res) :
+ m_hostname(hostname), m_port(port), m_scopeid(scopeid), m_af(af),
+ results(*res)
+ { }
+
+ ~GetHostByNameThread()
+ { }
+
+ virtual bool preprocess()
+ { return true; }
+
+ virtual bool run();
+
+ void processResults(hostent* he, int my_h_errno);
+ };
+
+ bool GetHostByNameThread::run()
+ {
+
+ hostent *resultptr;
+ hostent my_results;
+ unsigned buflen = 1024;
+ int res;
+ int my_h_errno;
+ char *buf = 0L;
+
+ // qDebug("ResolveThread::run(): started threaded gethostbyname for %s (af = %d)",
+ // m_hostname.data(), m_af);
+
+ ResolverLocker resLock( this );
+ do
+ {
+ res = 0;
+ my_h_errno = HOST_NOT_FOUND;
+
+ // check blacklist
+ if (m_af != AF_INET &&
+ KBlacklistWorker::isBlacklisted(QString::fromLatin1(m_hostname)))
+ break;
+
+# ifdef USE_GETHOSTBYNAME2_R
+ buf = new char[buflen];
+ res = gethostbyname2_r(m_hostname, m_af, &my_results, buf, buflen,
+ &resultptr, &my_h_errno);
+
+# elif defined(USE_GETHOSTBYNAME_R)
+ if (m_af == AF_INET)
+ {
+ buf = new char[buflen];
+ res = gethostbyname_r(m_hostname, &my_results, buf, buflen,
+ &resultptr, &my_h_errno);
+ }
+ else
+ resultptr = 0; // signal error
+
+# elif defined(USE_GETHOSTBYNAME2)
+ // must lock mutex
+ resultptr = gethostbyname2(m_hostname, m_af);
+ my_h_errno = h_errno;
+
+# else
+ if (m_af == AF_INET)
+ {
+ // must lock mutex
+ resultptr = gethostbyname(m_hostname);
+ my_h_errno = h_errno;
+ }
+ else
+ resultptr = 0;
+# endif
+
+ if (resultptr != 0L)
+ my_h_errno = 0;
+ // qDebug("GetHostByNameThread::run(): gethostbyname for %s (af = %d) returned: %d",
+ // m_hostname.data(), m_af, my_h_errno);
+
+ if (res == ERANGE)
+ {
+ // Enlarge the buffer
+ buflen += 1024;
+ delete [] buf;
+ buf = new char[buflen];
+ }
+
+ if ((res == ERANGE || my_h_errno != 0) && checkResolver())
+ {
+ // resolver needs updating, so we might as well do it now
+ resLock.openClose();
+ }
+ }
+ while (res == ERANGE);
+ processResults(resultptr, my_h_errno);
+
+ delete [] buf;
+
+ finished();
+ return results.error() == KResolver::NoError;
+ }
+
+ void GetHostByNameThread::processResults(hostent *he, int herrno)
+ {
+ if (herrno)
+ {
+ qDebug("KStandardWorker::processResults: got error %d", herrno);
+ switch (herrno)
+ {
+ case HOST_NOT_FOUND:
+ results.setError(KResolver::NoName);
+ return;
+
+ case TRY_AGAIN:
+ results.setError(KResolver::TryAgain);
+ return;
+
+ case NO_RECOVERY:
+ results.setError(KResolver::NonRecoverable);
+ return;
+
+ case NO_ADDRESS:
+ results.setError(KResolver::NoName);
+ return;
+
+ default:
+ results.setError(KResolver::UnknownError);
+ return;
+ }
+ }
+ else if (he == 0L)
+ {
+ results.setError(KResolver::NoName);
+ return; // this was an error
+ }
+
+ // clear any errors
+ setError(KResolver::NoError);
+ results.setError(KResolver::NoError);
+
+ // we process results in the reverse order
+ // that is, we prepend each result to the list of results
+ int proto = protocol();
+ int socktype = socketType();
+ if (socktype == 0)
+ socktype = SOCK_STREAM; // default
+
+ QString canon = KResolver::domainToUnicode(QString::fromLatin1(he->h_name));
+ KInetSocketAddress sa;
+ sa.setPort(m_port);
+ if (he->h_addrtype != AF_INET)
+ sa.setScopeId(m_scopeid); // this will also change the socket into IPv6
+
+ for (int i = 0; he->h_addr_list[i]; i++)
+ {
+ sa.setHost(KIpAddress(he->h_addr_list[i], he->h_addrtype == AF_INET ? 4 : 6));
+ results.prepend(KResolverEntry(sa, socktype, proto, canon, m_hostname));
+ // qDebug("KStandardWorker::processResults: adding %s", sa.toString().latin1());
+ }
+ // qDebug("KStandardWorker::processResults: added %d entries", i);
+ }
+
+#else // HAVE_GETADDRINFO
+
+ class GetAddrInfoThread: public KResolverWorkerBase
+ {
+ public:
+ QCString m_node;
+ QCString m_serv;
+ int m_af;
+ int m_flags;
+ KResolverResults& results;
+
+ GetAddrInfoThread(const char* node, const char* serv, int af, int flags,
+ KResolverResults* res) :
+ m_node(node), m_serv(serv), m_af(af), m_flags(flags), results(*res)
+ { }
+
+ ~GetAddrInfoThread()
+ { }
+
+ virtual bool preprocess()
+ { return true; }
+
+ virtual bool run();
+
+ void processResults(addrinfo* ai, int ret_code, KResolverResults& rr);
+ };
+
+ bool GetAddrInfoThread::run()
+ {
+ // check blacklist
+ if ((m_af != AF_INET && m_af != AF_UNSPEC) &&
+ KBlacklistWorker::isBlacklisted(QString::fromLatin1(m_node)))
+ {
+ results.setError(KResolver::NoName);
+ finished();
+ return false; // failed
+ }
+
+ do
+ {
+ ResolverLocker resLock( this );
+
+ // process hints
+ addrinfo hint;
+ memset(&hint, 0, sizeof(hint));
+ hint.ai_family = m_af;
+ hint.ai_socktype = socketType();
+ hint.ai_protocol = protocol();
+
+ if (hint.ai_socktype == 0)
+ hint.ai_socktype = SOCK_STREAM; // default
+
+ if (m_flags & KResolver::Passive)
+ hint.ai_flags |= AI_PASSIVE;
+ if (m_flags & KResolver::CanonName)
+ hint.ai_flags |= AI_CANONNAME;
+# ifdef AI_NUMERICHOST
+ if (m_flags & KResolver::NoResolve)
+ hint.ai_flags |= AI_NUMERICHOST;
+# endif
+# ifdef AI_ADDRCONFIG
+ hint.ai_flags |= AI_ADDRCONFIG;
+# endif
+
+ // now we do the blocking processing
+ if (m_node.isEmpty())
+ m_node = "*";
+
+ addrinfo *result;
+ int res = getaddrinfo(m_node, m_serv, &hint, &result);
+ // kdDebug(179) << k_funcinfo << "getaddrinfo(\""
+ // << m_node << "\", \"" << m_serv << "\", af="
+ // << m_af << ") returned " << res << endl;
+
+ if (res != 0)
+ {
+ if (checkResolver())
+ {
+ // resolver requires reinitialisation
+ resLock.openClose();
+ continue;
+ }
+
+ switch (res)
+ {
+ case EAI_BADFLAGS:
+ results.setError(KResolver::BadFlags);
+ break;
+
+#ifdef EAI_NODATA
+ // In some systems, EAI_NODATA was #define'd to EAI_NONAME which would break this case.
+#if EAI_NODATA != EAI_NONAME
+ case EAI_NODATA: // it was removed in RFC 3493
+#endif
+#endif
+ case EAI_NONAME:
+ results.setError(KResolver::NoName);
+ break;
+
+ case EAI_AGAIN:
+ results.setError(KResolver::TryAgain);
+ break;
+
+ case EAI_FAIL:
+ results.setError(KResolver::NonRecoverable);
+ break;
+
+ case EAI_FAMILY:
+ results.setError(KResolver::UnsupportedFamily);
+ break;
+
+ case EAI_SOCKTYPE:
+ results.setError(KResolver::UnsupportedSocketType);
+ break;
+
+ case EAI_SERVICE:
+ results.setError(KResolver::UnsupportedService);
+ break;
+
+ case EAI_MEMORY:
+ results.setError(KResolver::Memory);
+ break;
+
+ case EAI_SYSTEM:
+ results.setError(KResolver::SystemError, errno);
+ break;
+
+ default:
+ results.setError(KResolver::UnknownError, errno);
+ break;
+ }
+
+ finished();
+ return false; // failed
+ }
+
+ // if we are here, lookup succeeded
+ QString canon;
+ const char *previous_canon = 0L;
+
+ for (addrinfo* p = result; p; p = p->ai_next)
+ {
+ // cache the last canon name to avoid doing the ToUnicode processing unnecessarily
+ if ((previous_canon && !p->ai_canonname) ||
+ (!previous_canon && p->ai_canonname) ||
+ (p->ai_canonname != previous_canon &&
+ strcmp(p->ai_canonname, previous_canon) != 0))
+ {
+ canon = KResolver::domainToUnicode(QString::fromAscii(p->ai_canonname));
+ previous_canon = p->ai_canonname;
+ }
+
+ results.append(KResolverEntry(p->ai_addr, p->ai_addrlen, p->ai_socktype,
+ p->ai_protocol, canon, m_node));
+ }
+
+ freeaddrinfo(result);
+ results.setError(KResolver::NoError);
+ finished();
+ return results.error() == KResolver::NoError;
+ }
+ while (true);
+ }
+
+#endif // HAVE_GETADDRINFO
+} // namespace
+
+bool KStandardWorker::sanityCheck()
+{
+ // check that the requested values are sensible
+
+ if (!nodeName().isEmpty())
+ {
+ QString node = nodeName();
+ if (node.find('%') != -1)
+ node.truncate(node.find('%'));
+
+ if (node.isEmpty() || node == QString::fromLatin1("*") ||
+ node == QString::fromLatin1("localhost"))
+ m_encodedName.truncate(0);
+ else
+ {
+ m_encodedName = KResolver::domainToAscii(node);
+
+ if (m_encodedName.isNull())
+ {
+ qDebug("could not encode hostname '%s' (UTF-8)", node.utf8().data());
+ setError(KResolver::NoName);
+ return false; // invalid hostname!
+ }
+
+ // qDebug("Using encoded hostname '%s' for '%s' (UTF-8)", m_encodedName.data(),
+ // node.utf8().data());
+ }
+ }
+ else
+ m_encodedName.truncate(0); // just to be sure, but it should be clear already
+
+ if (protocol() == -1)
+ {
+ setError(KResolver::NonRecoverable);
+ return false; // user passed invalid protocol name
+ }
+
+ return true; // it's sane
+}
+
+bool KStandardWorker::resolveScopeId()
+{
+ // we must test the original name, not the encoded one
+ scopeid = 0;
+ int pos = nodeName().findRev('%');
+ if (pos == -1)
+ return true;
+
+ QString scopename = nodeName().mid(pos + 1);
+
+ bool ok;
+ scopeid = scopename.toInt(&ok);
+ if (!ok)
+ {
+ // it's not a number
+ // therefore, it's an interface name
+#ifdef HAVE_IF_NAMETOINDEX
+ scopeid = if_nametoindex(scopename.latin1());
+#else
+ scopeid = 0;
+#endif
+ }
+
+ return true;
+}
+
+bool KStandardWorker::resolveService()
+{
+ // find the service first
+ bool ok;
+ port = serviceName().toUInt(&ok);
+ if (!ok)
+ {
+ // service name does not contain a port number
+ // must be a name
+
+ if (serviceName().isEmpty() || serviceName().compare(QString::fromLatin1("*")) == 0)
+ port = 0;
+ else
+ {
+ // it's a name. We need the protocol name in order to lookup.
+ QCString protoname = protocolName();
+
+ if (protoname.isEmpty() && protocol())
+ {
+ protoname = KResolver::protocolName(protocol()).first();
+
+ // if it's still empty...
+ if (protoname.isEmpty())
+ {
+ // lookup failed!
+ setError(KResolver::NoName);
+ return false;
+ }
+ }
+ else
+ protoname = "tcp";
+
+ // it's not, so we can do a port lookup
+ int result = KResolver::servicePort(serviceName().latin1(), protoname);
+ if (result == -1)
+ {
+ // lookup failed!
+ setError(KResolver::NoName);
+ return false;
+ }
+
+ // it worked, we have a port number
+ port = (Q_UINT16)result;
+ }
+ }
+
+ // we found a port
+ return true;
+}
+
+KResolver::ErrorCodes KStandardWorker::addUnix()
+{
+ // before trying to add, see if the user wants Unix sockets
+ if ((familyMask() & KResolver::UnixFamily) == 0)
+ // no, Unix sockets are not wanted
+ return KResolver::UnsupportedFamily;
+
+ // now check if the requested data are good for a Unix socket
+ if (!m_encodedName.isEmpty())
+ return KResolver::AddrFamily; // non local hostname
+
+ if (protocol() || protocolName())
+ return KResolver::BadFlags; // cannot have Unix sockets with protocols
+
+ QString pathname = serviceName();
+ if (pathname.isEmpty())
+ return KResolver::NoName;; // no path?
+
+ if (pathname[0] != '/')
+ // non absolute pathname
+ // put it in /tmp
+ pathname.prepend("/tmp/");
+
+ // qDebug("QNoResolveWorker::addUnix(): adding Unix socket for %s", pathname.local8Bit().data());
+ KUnixSocketAddress sa(pathname);
+ int socktype = socketType();
+ if (socktype == 0)
+ socktype = SOCK_STREAM; // default
+
+ results.append(KResolverEntry(sa, socktype, 0));
+ setError(KResolver::NoError);
+
+ return KResolver::NoError;
+}
+
+bool KStandardWorker::resolveNumerically()
+{
+ // if the NoResolve flag is active, our result from this point forward
+ // will always be true, even if the resolution failed.
+ // that indicates that our result is authoritative.
+
+ bool wantV4 = familyMask() & KResolver::IPv4Family,
+ wantV6 = familyMask() & KResolver::IPv6Family;
+
+ if (!wantV6 && !wantV4)
+ // no Internet address is wanted!
+ return (flags() & KResolver::NoResolve);
+
+ // now try to find results
+ if (!resolveScopeId() || !resolveService())
+ return (flags() & KResolver::NoResolve);
+
+ // we have scope IDs and port numbers
+ // now try to resolve the hostname numerically
+ KInetSocketAddress sa;
+ setError(KResolver::NoError);
+ sa.setHost(KIpAddress(QString::fromLatin1(m_encodedName)));
+
+ // if it failed, the length was reset to 0
+ bool ok = sa.length() != 0;
+
+ sa.setPort(port);
+ if (sa.ipVersion() == 6)
+ sa.setScopeId(scopeid);
+ int proto = protocol();
+ int socktype = socketType();
+ if (socktype == 0)
+ socktype = SOCK_STREAM;
+
+ if (ok)
+ {
+ // the given hostname was successfully converted to an IP address
+ // check if the user wanted this kind of address
+
+ if ((sa.ipVersion() == 4 && wantV4) ||
+ (sa.ipVersion() == 6 && wantV6))
+ results.append(KResolverEntry(sa, socktype, proto));
+ else
+ {
+ // Note: the address *IS* a numeric IP
+ // but it's not of the kind the user asked for
+ //
+ // that means that it cannot be a Unix socket (because it's an IP)
+ // and that means that no resolution will tell us otherwise
+ //
+ // This is a failed resolution
+
+ setError(KResolver::AddrFamily);
+ return true;
+ }
+ }
+ else if (m_encodedName.isEmpty())
+ {
+ // user wanted localhost
+ if (flags() & KResolver::Passive)
+ {
+ if (wantV6)
+ {
+ sa.setHost(KIpAddress::anyhostV6);
+ results.append(KResolverEntry(sa, socktype, proto));
+ }
+
+ if (wantV4)
+ {
+ sa.setHost(KIpAddress::anyhostV4);
+ results.append(KResolverEntry(sa, socktype, proto));
+ }
+ }
+ else
+ {
+ if (wantV6)
+ {
+ sa.setHost(KIpAddress::localhostV6);
+ results.append(KResolverEntry(sa, socktype, proto));
+ }
+
+ if (wantV4)
+ {
+ sa.setHost(KIpAddress::localhostV4);
+ results.append(KResolverEntry(sa, socktype, proto));
+ }
+ }
+
+ ok = true;
+ }
+ else
+ {
+ // probably bad flags, since the address is not convertible without
+ // resolution
+
+ setError(KResolver::BadFlags);
+ ok = false;
+ }
+
+ return ok || (flags() & KResolver::NoResolve);
+}
+
+bool KStandardWorker::preprocess()
+{
+ // check sanity
+ if (!sanityCheck())
+ return false;
+
+ // this worker class can only handle known families
+ if (familyMask() & KResolver::UnknownFamily)
+ {
+ setError(KResolver::UnsupportedFamily);
+ return false; // we don't know about this
+ }
+
+ // check the socket types
+ if (socketType() != SOCK_STREAM && socketType() != SOCK_DGRAM && socketType() != 0)
+ {
+ setError(KResolver::UnsupportedSocketType);
+ return false;
+ }
+
+ // check if we can resolve all numerically
+ // resolveNumerically always returns true if the NoResolve flag is set
+ if (resolveNumerically() || m_encodedName.isEmpty())
+ {
+ // indeed, we have resolved numerically
+ setError(addUnix());
+ if (results.count())
+ setError(KResolver::NoError);
+ finished();
+ return true;
+ }
+
+ // check if the user wants something we know about
+#ifdef AF_INET6
+# define mask (KResolver::IPv6Family | KResolver::IPv4Family | KResolver::UnixFamily)
+#else
+# define mask (KResolver::IPv4Family | KResolver::UnixFamily)
+#endif
+
+ if ((familyMask() & mask) == 0)
+ // errr... nothing we know about
+ return false;
+
+#undef mask
+
+ return true; // it's ok
+}
+
+bool KStandardWorker::run()
+{
+#ifndef HAVE_GETADDRINFO
+ // check the scope id first
+ // since most of the resolutions won't have a scope id, this should be fast
+ // and we won't have wasted time on services if this fails
+ if (!resolveScopeId())
+ return false;
+
+ // resolve the service now, before entering the blocking operation
+ if (!resolveService())
+ return false;
+#endif
+
+ // good
+ // now we need the hostname
+ setError(KResolver::NoName);
+
+ // these are the family types that we know of
+ struct
+ {
+ KResolver::SocketFamilies mask;
+ int af;
+ } families[] = { { KResolver::IPv4Family, AF_INET }
+#ifdef AF_INET6
+ , { KResolver::IPv6Family, AF_INET6 }
+#endif
+ };
+ int familyCount = sizeof(families)/sizeof(families[0]);
+ bool skipIPv6 = !hasIPv6();
+ resultList.setAutoDelete(true);
+
+ for (int i = 0; i < familyCount; i++)
+ if (familyMask() & families[i].mask)
+ {
+#ifdef AF_INET6
+ if (skipIPv6 && families[i].af == AF_INET6)
+ continue;
+#endif
+
+ KResolverWorkerBase *worker;
+ KResolverResults *res = new KResolverResults;
+ resultList.append(res);
+#ifdef HAVE_GETADDRINFO
+ worker = new GetAddrInfoThread(m_encodedName,
+ serviceName().latin1(),
+ families[i].af, flags(), res);
+#else
+ worker = new GetHostByNameThread(m_encodedName, port, scopeid,
+ families[i].af, res);
+#endif
+
+ enqueue(worker);
+ }
+
+ // not finished
+ return true;
+}
+
+bool KStandardWorker::postprocess()
+{
+ if (results.count())
+ return true; // no need
+ // now copy over what we need from the underlying results
+
+ // start backwards because IPv6 was launched later (if at all)
+ if (resultList.isEmpty())
+ {
+ results.setError(KResolver::NoName);
+ return true;
+ }
+
+ KResolverResults *rr = resultList.last();
+ while (rr)
+ {
+ if (!rr->isEmpty())
+ {
+ results.setError(KResolver::NoError);
+ KResolverResults::Iterator it = rr->begin();
+ for ( ; it != rr->end(); ++it)
+ results.append(*it);
+ }
+ else if (results.isEmpty())
+ // this generated an error
+ // copy the error code over
+ setError(rr->error(), rr->systemError());
+
+ rr = resultList.prev();
+ }
+
+ resultList.clear();
+ return true;
+}
+
+#ifdef HAVE_GETADDRINFO
+KGetAddrinfoWorker::~KGetAddrinfoWorker()
+{
+}
+
+bool KGetAddrinfoWorker::preprocess()
+{
+ // getaddrinfo(3) can always handle any kind of request that makes sense
+ if (!sanityCheck())
+ return false;
+
+ if (flags() & KResolver::NoResolve)
+ // oops, numeric resolution?
+ return run();
+
+ return true;
+}
+
+bool KGetAddrinfoWorker::run()
+{
+ // make an AF_UNSPEC getaddrinfo(3) call
+ GetAddrInfoThread worker(m_encodedName, serviceName().latin1(),
+ AF_UNSPEC, flags(), &results);
+
+ if (!worker.run())
+ {
+ if (wantThis(AF_UNIX))
+ {
+ if (addUnix() == KResolver::NoError)
+ setError(KResolver::NoError);
+ }
+ else
+ setError(worker.results.error(), worker.results.systemError());
+
+ return false;
+ }
+
+ // The worker has finished working
+ // now copy over only what we may want
+ // keep track of any Unix-domain sockets
+
+ bool seen_unix = false;
+ KResolverResults::Iterator it = results.begin();
+ for ( ; it != results.end(); )
+ {
+ if ((*it).family() == AF_UNIX)
+ seen_unix = true;
+ if (!wantThis((*it).family()))
+ it = results.remove(it);
+ else
+ ++it;
+ }
+
+ if (!seen_unix)
+ addUnix();
+
+ finished();
+ return true;
+}
+
+bool KGetAddrinfoWorker::wantThis(int family)
+{
+ // tells us if the user wants a socket of this family
+
+#ifdef AF_INET6
+ if (family == AF_INET6 && familyMask() & KResolver::IPv6Family)
+ return true;
+#endif
+ if (family == AF_INET && familyMask() & KResolver::IPv4Family)
+ return true;
+ if (family == AF_UNIX && familyMask() & KResolver::UnixFamily)
+ return true;
+
+ // it's not a family we know about...
+ if (familyMask() & KResolver::UnknownFamily)
+ return true;
+
+ return false;
+}
+
+#endif
+
+void KNetwork::Internal::initStandardWorkers()
+{
+ //KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KBlacklistWorker>);
+ KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KStandardWorker>);
+
+#ifdef HAVE_GETADDRINFO
+ KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KGetAddrinfoWorker>);
+#endif
+}
diff --git a/kdecore/network/kresolverstandardworkers_p.h b/kdecore/network/kresolverstandardworkers_p.h
new file mode 100644
index 000000000..30c8f3c1e
--- /dev/null
+++ b/kdecore/network/kresolverstandardworkers_p.h
@@ -0,0 +1,111 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KRESOLVERSTANDARDWORKERS_P_H
+#define KRESOLVERSTANDARDWORKERS_P_H
+
+#include <sys/types.h>
+#include <netdb.h>
+
+#include <qptrlist.h>
+#include <qcstring.h>
+#include <qstringlist.h>
+
+#include "kresolver.h"
+#include "kresolverworkerbase.h"
+
+#include <config.h>
+
+namespace KNetwork { namespace Internal
+{
+ extern void initStandardWorkers() KDE_NO_EXPORT;
+
+ /**
+ * @internal
+ * The blacklist worker.
+ */
+ class KBlacklistWorker: public KNetwork::KResolverWorkerBase
+ {
+ public:
+ static QStringList blacklist;
+
+ static void loadBlacklist();
+ static void init();
+ static bool isBlacklisted(const QString&);
+
+ virtual bool preprocess();
+ virtual bool run();
+ virtual bool postprocess() { return true; }
+ };
+
+ /** @internal
+ * Standard worker.
+ */
+ class KStandardWorker: public KNetwork::KResolverWorkerBase
+ {
+ protected:
+ mutable QCString m_encodedName;
+ Q_UINT16 port;
+ int scopeid;
+ QPtrList<KNetwork::KResolverResults> resultList;
+
+ public:
+ bool sanityCheck();
+
+ virtual bool preprocess();
+ virtual bool run();
+ virtual bool postprocess();
+
+ bool resolveScopeId();
+ bool resolveService();
+ bool resolveNumerically();
+
+ KNetwork::KResolver::ErrorCodes addUnix();
+ };
+
+#if defined(HAVE_GETADDRINFO)
+ /** @internal
+ * Worker class based on getaddrinfo(3).
+ *
+ * This class does not do post-processing.
+ */
+ class KGetAddrinfoWorker: public KStandardWorker
+ {
+ public:
+ KGetAddrinfoWorker()
+ { }
+
+ virtual ~KGetAddrinfoWorker();
+ virtual bool preprocess();
+ virtual bool run();
+ virtual bool postprocess() { return true; }
+
+ bool wantThis(int family);
+ };
+#endif // HAVE_GETADDRINFO
+
+} } // namespace KNetwork::Internal
+
+
+#endif
diff --git a/kdecore/network/kresolverworkerbase.cpp b/kdecore/network/kresolverworkerbase.cpp
new file mode 100644
index 000000000..32a32da2a
--- /dev/null
+++ b/kdecore/network/kresolverworkerbase.cpp
@@ -0,0 +1,150 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003,2004 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 <assert.h>
+
+#include <qcstring.h>
+#include <qstring.h>
+
+#include "kresolver.h"
+#include "kresolver_p.h"
+#include "kresolverworkerbase.h"
+
+using namespace KNetwork;
+using namespace KNetwork::Internal;
+
+KResolverWorkerBase::KResolverWorkerBase()
+ : th(0L), input(0L), m_finished(0), m_reserved(0)
+{
+}
+
+KResolverWorkerBase::~KResolverWorkerBase()
+{
+}
+
+QString KResolverWorkerBase::nodeName() const
+{
+ if (input)
+ return input->node;
+ return QString::null;
+}
+
+QString KResolverWorkerBase::serviceName() const
+{
+ if (input)
+ return input->service;
+ return QString::null;
+}
+
+int KResolverWorkerBase::flags() const
+{
+ if (input)
+ return input->flags;
+ return 0;
+}
+
+int KResolverWorkerBase::familyMask() const
+{
+ if (input)
+ return input->familyMask;
+ return 0;
+}
+
+int KResolverWorkerBase::socketType() const
+{
+ if (input)
+ return input->socktype;
+ return 0;
+}
+
+int KResolverWorkerBase::protocol() const
+{
+ if (input)
+ return input->protocol;
+ return 0;
+}
+
+QCString KResolverWorkerBase::protocolName() const
+{
+ QCString res;
+ if (input)
+ res = input->protocolName;
+ return res;
+}
+
+void KResolverWorkerBase::finished()
+{
+ m_finished = true;
+}
+
+bool KResolverWorkerBase::postprocess()
+{
+ return true; // no post-processing is a always successful postprocessing
+}
+
+bool KResolverWorkerBase::enqueue(KResolver* res)
+{
+ KResolverManager::manager()->enqueue(res, th->data);
+ return true;
+}
+
+bool KResolverWorkerBase::enqueue(KResolverWorkerBase* worker)
+{
+ RequestData *myself = th->data;
+ RequestData *newrequest = new RequestData;
+ newrequest->obj = 0;
+ newrequest->input = input; // same input
+ newrequest->requestor = myself;
+ newrequest->nRequests = 0;
+ newrequest->worker = worker;
+ myself->nRequests++;
+ KResolverManager::manager()->dispatch(newrequest);
+ return true;
+}
+
+bool KResolverWorkerBase::checkResolver()
+{
+ assert(th != 0L);
+ return th->checkResolver();
+}
+
+void KResolverWorkerBase::acquireResolver()
+{
+ assert(th != 0L);
+ th->acquireResolver();
+}
+
+void KResolverWorkerBase::releaseResolver()
+{
+ assert(th != 0L);
+ th->releaseResolver();
+}
+
+void KResolverWorkerFactoryBase::registerNewWorker(KResolverWorkerFactoryBase* factory)
+{
+ KResolverManager::manager()->registerNewWorker(factory);
+}
+
diff --git a/kdecore/network/kresolverworkerbase.h b/kdecore/network/kresolverworkerbase.h
new file mode 100644
index 000000000..8776e9142
--- /dev/null
+++ b/kdecore/network/kresolverworkerbase.h
@@ -0,0 +1,317 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003,2004 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.
+ */
+
+#ifndef KRESOLVERWORKERBASE_H
+#define KRESOLVERWORKERBASE_H
+
+#include "kresolver.h"
+
+// forward declarations
+class QString;
+template <class T> class QValueList;
+
+namespace KNetwork {
+
+ namespace Internal
+ {
+ class KResolverThread;
+ struct InputData;
+ }
+
+/** @internal
+ * This class is the base functionality for a resolver worker. That is,
+ * the class that does the actual work.
+ *
+ * In the future, this class might be exposed to allow plug-ins. So, try and
+ * make it binary compatible.
+ *
+ * Note that hostnames are still encoded in Unicode at this point. It's up to
+ * the worker class to decide which encoding to use. In the case of DNS,
+ * an ASCII Compatible Encoding (ACE) must be used.
+ * See @ref KResolver::domainToAscii.
+ *
+ * Also specially note that the run method in this class is called in a
+ * thread that is not the program's main thread. So do not do anything there
+ * that you shouldn't!
+ *
+ * @class KResolverWorkerBase kresolverworkerbase.h kresolverworkerbase.h
+ */
+class KResolverWorkerBase
+{
+public:
+
+ /**
+ * Helper class for locking the resolver subsystem.
+ * Similar to QMutexLocker.
+ *
+ * @author Luís Pedro Coelho
+ * @since 3.4
+ */
+ class ResolverLocker
+ {
+ public:
+ /**
+ * Constructor. Acquires a lock.
+ */
+ ResolverLocker(KResolverWorkerBase* parent)
+ : parent( parent )
+ {
+ parent->acquireResolver();
+ }
+
+ /**
+ * Destructor. Releases the lock.
+ */
+ ~ResolverLocker()
+ {
+ parent->releaseResolver();
+ }
+
+ /**
+ * Releases the lock and then reacquires it.
+ * It may be necessary to call this if the resolving function
+ * decides to retry.
+ */
+ void openClose()
+ {
+ parent->releaseResolver();
+ parent->acquireResolver();
+ }
+
+ private:
+ /// @internal
+ KResolverWorkerBase* parent;
+ };
+private:
+ // this will be like our d pointer
+ KNetwork::Internal::KResolverThread *th;
+ const KNetwork::Internal::InputData *input;
+ friend class KNetwork::Internal::KResolverThread;
+ friend class KNetwork::Internal::KResolverManager;
+ friend class KResolverWorkerBase::ResolverLocker;
+
+ int m_finished : 1;
+ int m_reserved : 31; // reserved
+
+public:
+ /**
+ * Derived classes will put their resolved data in this list, or will
+ * leave it empty in case of error.
+ *
+ * Status and error codes should also be stored in this object (the
+ * @ref setError function does that).
+ */
+ KResolverResults results;
+
+public:
+ // default constructor
+ KResolverWorkerBase();
+
+ // virtual destructor
+ virtual ~KResolverWorkerBase();
+
+ /**
+ * This is the hostname to be looked for
+ */
+ QString nodeName() const;
+
+ /**
+ * And this is the service name
+ */
+ QString serviceName() const;
+
+ /**
+ * gets the flags
+ */
+ int flags() const;
+
+ /**
+ * gets the family mask
+ */
+ int familyMask() const;
+
+ /**
+ * gets the socket type
+ */
+ int socketType() const;
+
+ /**
+ * gets the protocol number
+ */
+ int protocol() const;
+
+ /**
+ * gets the protocol name, if applicable
+ */
+ QCString protocolName() const;
+
+ /**
+ * Call this function to indicate that processing
+ * has finished. This is useful in the preprocessing
+ * stage, to indicate that @ref run doesn't have to be
+ * called.
+ */
+ void finished();
+
+protected:
+ // like a QThread
+ /**
+ * This is the function that should be overriden in derived classes.
+ *
+ * Derived classes will do their blocking job in this function and return
+ * either success or failure to work (not the lookup). That is, whether the
+ * lookup result was a domain found or not, if we got our answer, we should
+ * indicate success. The error itself must be set with @ref setError.
+ *
+ * \b Important: this function gets called in a separate thread!
+ *
+ * @return true on success
+ */
+ virtual bool run() = 0;
+
+ /**
+ * This function gets called during pre processing for this class and you must
+ * override it.
+ *
+ * \b Important: this function gets called in the main event thread. And it MUST
+ * NOT block.
+ *
+ * This function can be used for an object to determine if it will be able
+ * to resolve the given data or not even before launching into a blocking
+ * operation. This function should return true if the object is capable of
+ * handling this kind of data; false otherwise. Note that the return value
+ * of 'true' means that the object's blocking answer will be considered authoritative.
+ *
+ * This function MUST NOT queue further requests. Leave that to @ref run.
+ *
+ * This function is pure virtual; you must override it.
+ *
+ * @return true on success
+ */
+ virtual bool preprocess() = 0;
+
+ /**
+ * This function gets called during post processing for this class.
+ *
+ * \b Important: this function gets called in the main event thread. And it MUST
+ * NOT block.
+ *
+ * @returns true on success
+ */
+ virtual bool postprocess();
+
+ /**
+ * Sets the error
+ */
+ inline void setError(int errorcode, int syserror = 0)
+ { results.setError(errorcode, syserror); }
+
+ /**
+ * Enqueue the given resolver for post-processing.
+ *
+ * Use this function to make the manager call for another resolution.
+ * This is suitable for workers that do post-processing.
+ *
+ * The manager will make sure that any requests enqueued by this function
+ * are done before calling the postprocessing function, which you should
+ * override.
+ *
+ * \b Important: do use KResolver's own enqueueing functions (i.e., @ref KResolver::start).
+ * Instead, use this function.
+ *
+ * @returns true on successful queueing or false if a problem ocurred
+ */
+ bool enqueue(KResolver* other);
+
+ /**
+ * @overload
+ */
+ bool enqueue(KResolverWorkerBase* worker);
+
+ /**
+ * Checks the resolver subsystem status.
+ * @returns true if the resolver subsystem changed, false otherwise.
+ * If this function returns true, it might be necessary to
+ * restart the resolution altogether.
+ * @since 3.4
+ */
+ bool checkResolver();
+
+ /**
+ * This function has to be called from the resolver workers that require
+ * use of the DNS resolver code (i.e., res_* functions, generally in
+ * libresolv). It indicates that the function is starting a resolution
+ * and that the resolver backend shouldn't change asynchronously.
+ *
+ * If any pending res_init's are required, they will be performed before
+ * this function returns.
+ *
+ * @since 3.4
+ */
+ void acquireResolver();
+
+ /**
+ * This function is the counterpart for @ref acquireResolver: the worker
+ * thread indicates that it's done with the resolver.
+ *
+ * @since 3.4
+ */
+ void releaseResolver();
+
+};
+
+/** @internal
+ * This class provides functionality for creating and registering worker classes.
+ *
+ * @class KResolverWorkerFactoryBase kresolverworkerbase.h kresolverworkerbase.h
+ */
+class KResolverWorkerFactoryBase
+{
+public:
+ virtual KResolverWorkerBase* create() const = 0;
+
+ /**
+ * Wrapper call to register workers
+ *
+ * It is NOT thread-safe!
+ */
+ static void registerNewWorker(KResolverWorkerFactoryBase* factory);
+};
+
+/** @internal
+ * This class provides functionality for creating and registering worker classes.
+ *
+ * @class KResolverWorkerFactory kresolverworkerbase.h kresolverworkerbase.h
+ */
+template<class Worker>
+class KResolverWorkerFactory: public KResolverWorkerFactoryBase
+{
+public:
+ virtual KResolverWorkerBase* create() const
+ { return new Worker; }
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/kreverseresolver.cpp b/kdecore/network/kreverseresolver.cpp
new file mode 100644
index 000000000..dbcc23d79
--- /dev/null
+++ b/kdecore/network/kreverseresolver.cpp
@@ -0,0 +1,263 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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"
+
+// System includes
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <signal.h>
+
+// Qt
+#include <qevent.h>
+#include <qmutex.h>
+#include <qapplication.h>
+
+// Us
+#include "kreverseresolver.h"
+#include "kresolver_p.h"
+#include "kresolverworkerbase.h"
+#include "ksocketaddress.h"
+
+#ifndef HAVE_GETNAMEINFO
+// FIXME KDE4:
+// move to syssocket or adapt
+# include "netsupp.h"
+#endif
+
+using namespace KNetwork;
+using namespace KNetwork::Internal;
+
+namespace
+{
+ class ReverseThread: public KResolverWorkerBase
+ {
+ public:
+ ReverseThread(const KSocketAddress& addr, int flags)
+ : m_addr(addr), m_flags(flags), m_parent(0L)
+ { }
+
+ virtual ~ReverseThread()
+ { }
+
+ virtual bool preprocess()
+ { return true; }
+ virtual bool run();
+ virtual bool postprocess();
+
+ // input:
+ KSocketAddress m_addr;
+ int m_flags;
+ KReverseResolver *m_parent;
+
+ // output:
+ QString node;
+ QString service;
+ bool success;
+ };
+
+ class KReverseResolverEvent: public QEvent
+ {
+ public:
+ static const int myType = QEvent::User + 63; // arbitrary value
+ QString node;
+ QString service;
+ bool success;
+
+ KReverseResolverEvent(const QString& _node, const QString& _service,
+ bool _success)
+ : QEvent((Type)myType), node(_node),
+ service(_service), success(_success)
+ { }
+ };
+}
+
+class KNetwork::KReverseResolverPrivate
+{
+public:
+ QString node;
+ QString service;
+ KSocketAddress addr;
+ int flags;
+
+ ReverseThread* worker;
+ bool success;
+
+ inline KReverseResolverPrivate(const KSocketAddress& _addr)
+ : addr(_addr), worker(0L), success(false)
+ { }
+};
+
+KReverseResolver::KReverseResolver(const KSocketAddress& addr, int flags,
+ QObject *parent, const char* name)
+ : QObject(parent, name), d(new KReverseResolverPrivate(addr))
+{
+ d->flags = flags;
+}
+
+KReverseResolver::~KReverseResolver()
+{
+ if (d->worker)
+ d->worker->m_parent = 0L;
+}
+
+bool KReverseResolver::isRunning() const
+{
+ return d->worker != 0L;
+}
+
+bool KReverseResolver::success() const
+{
+ return !isRunning() && d->success;
+}
+
+bool KReverseResolver::failure() const
+{
+ return !isRunning() && !d->success;
+}
+
+QString KReverseResolver::node() const
+{
+ return d->node;
+}
+
+QString KReverseResolver::service() const
+{
+ return d->service;
+}
+
+const KSocketAddress& KReverseResolver::address() const
+{
+ return d->addr;
+}
+
+bool KReverseResolver::start()
+{
+ if (d->worker != 0L)
+ return true; // already started
+
+ d->worker = new ReverseThread(d->addr, d->flags);
+ d->worker->m_parent = this;
+
+ RequestData *req = new RequestData;
+ req->obj = 0L;
+ req->input = 0L;
+ req->requestor = 0L;
+ req->worker = d->worker;
+ KResolverManager::manager()->dispatch(req);
+ return true;
+}
+
+bool KReverseResolver::event(QEvent *e)
+{
+ if (e->type() != KReverseResolverEvent::myType)
+ return QObject::event(e); // call parent
+
+ KReverseResolverEvent *re = static_cast<KReverseResolverEvent*>(e);
+ d->node = re->node;
+ d->service = re->service;
+ d->success = re->success;
+
+ // don't delete d->worker!
+ // KResolverManager::doNotifying takes care of that, if it hasn't already
+ d->worker = 0L;
+
+ // emit signal
+ emit finished(*this);
+
+ return true;
+}
+
+bool KReverseResolver::resolve(const KSocketAddress& addr, QString& node,
+ QString& serv, int flags)
+{
+ ReverseThread th(addr, flags);
+ if (th.run())
+ {
+ node = th.node;
+ serv = th.service;
+ return true;
+ }
+ return false;
+}
+
+bool KReverseResolver::resolve(const struct sockaddr* sa, Q_UINT16 salen,
+ QString& node, QString& serv, int flags)
+{
+ return resolve(KSocketAddress(sa, salen), node, serv, flags);
+}
+
+bool ReverseThread::run()
+{
+ int err;
+ char h[NI_MAXHOST], s[NI_MAXSERV];
+ int niflags = 0;
+
+ h[0] = s[0] = '\0';
+
+ if (m_flags & KReverseResolver::NumericHost)
+ niflags |= NI_NUMERICHOST;
+ if (m_flags & KReverseResolver::NumericService)
+ niflags |= NI_NUMERICSERV;
+ if (m_flags & KReverseResolver::NodeNameOnly)
+ niflags |= NI_NOFQDN;
+ if (m_flags & KReverseResolver::Datagram)
+ niflags |= NI_DGRAM;
+ if (m_flags & KReverseResolver::ResolutionRequired)
+ niflags |= NI_NAMEREQD;
+
+ {
+#ifdef NEED_MUTEX
+ QMutexLocker locker(&::getXXbyYYmutex);
+#endif
+ err = ::getnameinfo(m_addr, m_addr.length(),
+ h, sizeof(h) - 1, s, sizeof(s) - 1, niflags);
+ }
+
+ if (err == 0)
+ {
+ node = KResolver::domainToUnicode(QString::fromLatin1(h));
+ service = QString::fromLatin1(s);
+ success = true;
+ }
+ else
+ {
+ node = service = QString::null;
+ success = false;
+ }
+
+ return success;
+}
+
+bool ReverseThread::postprocess()
+{
+ // post an event
+ if (m_parent)
+ QApplication::postEvent(m_parent,
+ new KReverseResolverEvent(node, service, success));
+ return true;
+}
+
+#include "kreverseresolver.moc"
diff --git a/kdecore/network/kreverseresolver.h b/kdecore/network/kreverseresolver.h
new file mode 100644
index 000000000..325d97d82
--- /dev/null
+++ b/kdecore/network/kreverseresolver.h
@@ -0,0 +1,195 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KREVERSERESOLVER_H
+#define KREVERSERESOLVER_H
+
+//////////////////
+// Needed includes
+#include <qobject.h>
+#include <qstring.h>
+
+#include "ksocketaddress.h"
+
+namespace KNetwork {
+
+class KReverseResolverPrivate;
+/** @class KReverseResolver kreverseresolver.h kreverseresolver.h
+ * @brief Run a reverse-resolution on a socket address.
+ *
+ * This class is provided as a counterpart to KResolver in such a way
+ * as it produces a reverse resolution: it resolves a socket address
+ * from its binary representations into a textual representation.
+ *
+ * Most users will use the static functions @ref resolve, which work
+ * both synchronously (blocking) and asynchronously (non-blocking).
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KReverseResolver: public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Flags for the reverse resolution.
+ *
+ * These flags are used by the reverse resolution functions for
+ * setting resolution parameters. The possible values are:
+ * @li NumericHost: don't try to resolve the host address to a text form.
+ * Instead, convert the address to its numeric textual representation.
+ * @li NumericService: the same as NumericHost, but for the service name
+ * @li NodeNameOnly: returns the node name only (i.e., not the Fully
+ * Qualified Domain Name)
+ * @li Datagram: in case of ambiguity in the service name, prefer the
+ * name associated with the datagram protocol
+ * @li NumericScope: for those addresses which have the concept of scope,
+ * resolve using the numeric value instead of the proper scope name.
+ * @li ResolutionRequired: normally, when resolving, if the name resolution
+ * fails, the process normally converts the numeric address into its
+ * presentation forms. This flag causes the function to return
+ * with error instead.
+ */
+ enum Flags
+ {
+ NumericHost = 0x01,
+ NumericService = 0x02,
+ NodeNameOnly = 0x04,
+ Datagram = 0x08,
+ NumericScope = 0x10,
+ ResolutionRequired = 0x20
+ };
+
+ /**
+ * Constructs this object to resolve the given socket address.
+ *
+ * @param addr the address to resolve
+ * @param flags the flags to use, see @ref Flags
+ */
+ KReverseResolver(const KSocketAddress& addr, int flags = 0,
+ QObject * = 0L, const char * = 0L);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KReverseResolver();
+
+ /**
+ * This function returns 'true' if the processing is still running.
+ */
+ bool isRunning() const;
+
+ /**
+ * This function returns true if the processing has finished with
+ * success, false if it's still running or failed.
+ */
+ bool success() const;
+
+ /**
+ * This function returns true if the processing has finished with
+ * failure, false if it's still running or succeeded.
+ */
+ bool failure() const;
+
+ /**
+ * Returns the resolved node name, if the resolution has finished
+ * successfully, or QString::null otherwise.
+ */
+ QString node() const;
+
+ /**
+ * Returns the resolved service name, if the resolution has finished
+ * successfully, or QString::null otherwise.
+ */
+ QString service() const;
+
+ /**
+ * Returns the socket address which was subject to resolution.
+ */
+ const KSocketAddress& address() const;
+
+ /**
+ * Starts the resolution. This function returns 'true'
+ * if the resolution has started successfully.
+ */
+ bool start();
+
+ /**
+ * Overrides event handling
+ */
+ virtual bool event(QEvent* );
+
+signals:
+ /**
+ * This signal is emitted when the resolution has finished.
+ *
+ * @param obj this class, which contains the results
+ */
+ void finished(const KReverseResolver& obj);
+
+public:
+ /**
+ * Resolves a socket address to its textual representation
+ *
+ * FIXME!! How can we do this in a non-blocking manner!?
+ *
+ * This function is used to resolve a socket address from its
+ * binary representation to a textual form, even if numeric only.
+ *
+ * @param addr the socket address to be resolved
+ * @param node the QString where we will store the resolved node
+ * @param serv the QString where we will store the resolved service
+ * @param flags flags to be used for this resolution.
+ * @return true if the resolution succeeded, false if not
+ * @see ReverseFlags for the possible values for @p flags
+ */
+ static bool resolve(const KSocketAddress& addr, QString& node,
+ QString& serv, int flags = 0);
+
+ /**
+ * Resolves a socket address to its textual representation
+ *
+ * FIXME!! How can we do this in a non-blocking manner!?
+ *
+ * This function behaves just like the above one, except it takes
+ * a sockaddr structure and its size as parameters.
+ *
+ * @param sa the sockaddr structure containing the address to be resolved
+ * @param salen the length of the sockaddr structure
+ * @param node the QString where we will store the resolved node
+ * @param serv the QString where we will store the resolved service
+ * @param flags flags to be used for this resolution.
+ * @return true if the resolution succeeded, false if not
+ * @see ReverseFlags for the possible values for @p flags
+ */
+ static bool resolve(const struct sockaddr* sa, Q_UINT16 salen,
+ QString& node, QString& serv, int flags = 0);
+
+private:
+ KReverseResolverPrivate* d;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/kserversocket.cpp b/kdecore/network/kserversocket.cpp
new file mode 100644
index 000000000..9f32b4119
--- /dev/null
+++ b/kdecore/network/kserversocket.cpp
@@ -0,0 +1,413 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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 <qmutex.h>
+
+#include "ksocketaddress.h"
+#include "kresolver.h"
+#include "ksocketbase.h"
+#include "ksocketdevice.h"
+#include "kstreamsocket.h"
+#include "kbufferedsocket.h"
+#include "kserversocket.h"
+
+using namespace KNetwork;
+
+class KNetwork::KServerSocketPrivate
+{
+public:
+ KResolver resolver;
+ KResolverResults resolverResults;
+
+ enum { None, LookupDone, Bound, Listening } state;
+ int backlog;
+ int timeout;
+
+ bool bindWhenFound : 1, listenWhenBound : 1, useKBufferedSocket : 1;
+
+ KServerSocketPrivate()
+ : state(None), timeout(0), bindWhenFound(false), listenWhenBound(false),
+ useKBufferedSocket(true)
+ {
+ resolver.setFlags(KResolver::Passive);
+ resolver.setFamily(KResolver::KnownFamily);
+ }
+};
+
+KServerSocket::KServerSocket(QObject* parent, const char *name)
+ : QObject(parent, name), d(new KServerSocketPrivate)
+{
+ QObject::connect(&d->resolver, SIGNAL(finished(KResolverResults)),
+ this, SLOT(lookupFinishedSlot()));
+}
+
+KServerSocket::KServerSocket(const QString& service, QObject* parent, const char *name)
+ : QObject(parent, name), d(new KServerSocketPrivate)
+{
+ QObject::connect(&d->resolver, SIGNAL(finished(KResolverResults)),
+ this, SLOT(lookupFinishedSlot()));
+ d->resolver.setServiceName(service);
+}
+
+KServerSocket::KServerSocket(const QString& node, const QString& service,
+ QObject* parent, const char* name)
+ : QObject(parent, name), d(new KServerSocketPrivate)
+{
+ QObject::connect(&d->resolver, SIGNAL(finished(KResolverResults)),
+ this, SLOT(lookupFinishedSlot()));
+ setAddress(node, service);
+}
+
+KServerSocket::~KServerSocket()
+{
+ close();
+ delete d;
+}
+
+bool KServerSocket::setSocketOptions(int opts)
+{
+ QMutexLocker locker(mutex());
+ KSocketBase::setSocketOptions(opts); // call parent
+ bool result = socketDevice()->setSocketOptions(opts); // and set the implementation
+ copyError();
+ return result;
+}
+
+KResolver& KServerSocket::resolver() const
+{
+ return d->resolver;
+}
+
+const KResolverResults& KServerSocket::resolverResults() const
+{
+ return d->resolverResults;
+}
+
+void KServerSocket::setResolutionEnabled(bool enable)
+{
+ if (enable)
+ d->resolver.setFlags(d->resolver.flags() & ~KResolver::NoResolve);
+ else
+ d->resolver.setFlags(d->resolver.flags() | KResolver::NoResolve);
+}
+
+void KServerSocket::setFamily(int families)
+{
+ d->resolver.setFamily(families);
+}
+
+void KServerSocket::setAddress(const QString& service)
+{
+ d->resolver.setNodeName(QString::null);
+ d->resolver.setServiceName(service);
+ d->resolverResults.empty();
+ if (d->state <= KServerSocketPrivate::LookupDone)
+ d->state = KServerSocketPrivate::None;
+}
+
+void KServerSocket::setAddress(const QString& node, const QString& service)
+{
+ d->resolver.setNodeName(node);
+ d->resolver.setServiceName(service);
+ d->resolverResults.empty();
+ if (d->state <= KServerSocketPrivate::LookupDone)
+ d->state = KServerSocketPrivate::None;
+}
+
+void KServerSocket::setTimeout(int msec)
+{
+ d->timeout = msec;
+}
+
+bool KServerSocket::lookup()
+{
+ setError(NoError);
+ if (d->resolver.isRunning() && !blocking())
+ return true; // already doing lookup
+
+ if (d->state >= KServerSocketPrivate::LookupDone)
+ return true; // results are already available
+
+ // make sure we have at least one parameter for lookup
+ if (d->resolver.serviceName().isNull() &&
+ !d->resolver.nodeName().isNull())
+ d->resolver.setServiceName(QString::fromLatin1(""));
+
+ // don't restart the lookups if they had succeeded and
+ // the input values weren't changed
+
+ // reset results
+ d->resolverResults = KResolverResults();
+
+ if (d->resolver.status() <= 0)
+ // if it's already running, there's no harm in calling again
+ d->resolver.start(); // signal may emit
+
+ if (blocking())
+ {
+ // we're in blocking mode operation
+ // wait for the results
+
+ d->resolver.wait(); // signal may be emitted again
+ // lookupFinishedSlot has been called
+ }
+
+ return true;
+}
+
+bool KServerSocket::bind(const KResolverEntry& address)
+{
+ if (socketDevice()->bind(address))
+ {
+ setError(NoError);
+
+ d->state = KServerSocketPrivate::Bound;
+ emit bound(address);
+ return true;
+ }
+ copyError();
+ return false;
+}
+
+bool KServerSocket::bind(const QString& node, const QString& service)
+{
+ setAddress(node, service);
+ return bind();
+}
+
+bool KServerSocket::bind(const QString& service)
+{
+ setAddress(service);
+ return bind();
+}
+
+bool KServerSocket::bind()
+{
+ if (d->state >= KServerSocketPrivate::Bound)
+ return true;
+
+ if (d->state < KServerSocketPrivate::LookupDone)
+ {
+ if (!blocking())
+ {
+ d->bindWhenFound = true;
+ bool ok = lookup(); // will call doBind
+ if (d->state >= KServerSocketPrivate::Bound)
+ d->bindWhenFound = false;
+ return ok;
+ }
+
+ // not blocking
+ if (!lookup())
+ return false;
+ }
+
+ return doBind();
+}
+
+bool KServerSocket::listen(int backlog)
+{
+ // WARNING
+ // this function has to be reentrant
+ // due to the mechanisms used for binding, this function might
+ // end up calling itself
+
+ if (d->state == KServerSocketPrivate::Listening)
+ return true; // already listening
+
+ d->backlog = backlog;
+
+ if (d->state < KServerSocketPrivate::Bound)
+ {
+ // we must bind
+ // note that we can end up calling ourselves here
+ d->listenWhenBound = true;
+ if (!bind())
+ {
+ d->listenWhenBound = false;
+ return false;
+ }
+
+ if (d->state < KServerSocketPrivate::Bound)
+ // asynchronous lookup in progress...
+ // we can't be blocking here anyways
+ return true;
+
+ d->listenWhenBound = false;
+ }
+
+ if (d->state < KServerSocketPrivate::Listening)
+ return doListen();
+
+ return true;
+}
+
+void KServerSocket::close()
+{
+ socketDevice()->close();
+ if (d->resolver.isRunning())
+ d->resolver.cancel(false);
+ d->state = KServerSocketPrivate::None;
+ emit closed();
+}
+
+void KServerSocket::setAcceptBuffered(bool enable)
+{
+ d->useKBufferedSocket = enable;
+}
+
+KActiveSocketBase* KServerSocket::accept()
+{
+ if (d->state < KServerSocketPrivate::Listening)
+ {
+ if (!blocking())
+ {
+ listen();
+ setError(WouldBlock);
+ return NULL;
+ }
+ else if (!listen())
+ // error happened during listen
+ return false;
+ }
+
+ // check to see if we're doing a timeout
+ if (blocking() && d->timeout > 0)
+ {
+ bool timedout;
+ if (!socketDevice()->poll(d->timeout, &timedout))
+ {
+ copyError();
+ return NULL;
+ }
+
+ if (timedout)
+ return 0L;
+ }
+
+ // we're listening here
+ KSocketDevice* accepted = socketDevice()->accept();
+ if (!accepted)
+ {
+ // error happened during accept
+ copyError();
+ return NULL;
+ }
+
+ KStreamSocket* streamsocket;
+ if (d->useKBufferedSocket)
+ streamsocket = new KBufferedSocket();
+ else
+ streamsocket = new KStreamSocket();
+ streamsocket->setSocketDevice(accepted);
+
+ // FIXME!
+ // when KStreamSocket can find out the state of the socket passed through
+ // setSocketDevice, this will probably be unnecessary:
+ streamsocket->setState(KStreamSocket::Connected);
+ streamsocket->setFlags(IO_Sequential | IO_Raw | IO_ReadWrite | IO_Open | IO_Async);
+
+ return streamsocket;
+}
+
+KSocketAddress KServerSocket::localAddress() const
+{
+ return socketDevice()->localAddress();
+}
+
+KSocketAddress KServerSocket::externalAddress() const
+{
+ return socketDevice()->externalAddress();
+}
+
+void KServerSocket::lookupFinishedSlot()
+{
+ if (d->resolver.isRunning() || d->state > KServerSocketPrivate::LookupDone)
+ return;
+
+ if (d->resolver.status() < 0)
+ {
+ setError(LookupFailure);
+ emit gotError(LookupFailure);
+ d->bindWhenFound = d->listenWhenBound = false;
+ d->state = KServerSocketPrivate::None;
+ return;
+ }
+
+ // lookup succeeded
+ d->resolverResults = d->resolver.results();
+ d->state = KServerSocketPrivate::LookupDone;
+ emit hostFound();
+
+ if (d->bindWhenFound)
+ doBind();
+}
+
+void KServerSocket::copyError()
+{
+ setError(socketDevice()->error());
+}
+
+bool KServerSocket::doBind()
+{
+ d->bindWhenFound = false;
+ // loop through the results and bind to the first that works
+
+ KResolverResults::ConstIterator it = d->resolverResults.begin();
+ for ( ; it != d->resolverResults.end(); ++it)
+ if (bind(*it))
+ {
+ if (d->listenWhenBound)
+ return doListen();
+ return true;
+ }
+ else
+ socketDevice()->close(); // didn't work, try again
+
+ // failed to bind
+ emit gotError(error());
+ return false;
+}
+
+bool KServerSocket::doListen()
+{
+ if (!socketDevice()->listen(d->backlog))
+ {
+ copyError();
+ emit gotError(error());
+ return false; // failed to listen
+ }
+
+ // set up ready accept signal
+ QObject::connect(socketDevice()->readNotifier(), SIGNAL(activated(int)),
+ this, SIGNAL(readyAccept()));
+ d->state = KServerSocketPrivate::Listening;
+ return true;
+}
+
+
+#include "kserversocket.moc"
diff --git a/kdecore/network/kserversocket.h b/kdecore/network/kserversocket.h
new file mode 100644
index 000000000..27b3df1cc
--- /dev/null
+++ b/kdecore/network/kserversocket.h
@@ -0,0 +1,435 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 Thiago Macieira <thiago@kde.org>
+ *
+ *
+ * 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.
+ */
+
+#ifndef KSERVERSOCKET_H
+#define KSERVERSOCKET_H
+
+#include <qobject.h>
+#include "ksocketbase.h"
+
+namespace KNetwork {
+
+class KSocketDevice;
+class KStreamSocket;
+class KResolver;
+class KResolverResults;
+
+class KServerSocketPrivate;
+/**
+ * @class KServerSocket kserversocket.h kserversocket.h
+ * @brief A server socket for accepting connections.
+ *
+ * This class provides functionality for creating a socket to
+ * listen for incoming connections and subsequently accept them.
+ *
+ * To use this class, you must first set the parameters for the listening
+ * socket's address, then place it in listening mode.
+ *
+ * A typical example would look like:
+ * \code
+ * QString service = "http";
+ * KServerSocket *ss = new KServerSocket(service);
+ * connect(ss, SIGNAL(readyAccept()), this, SLOT(slotReadyAccept()));
+ * connect(ss, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
+ * ss->listen();
+ * \endcode
+ *
+ * In this case, this class will place the socket into listening mode on the
+ * service pointed to by @p service and will emit the @ref readyAccept signal
+ * when a connection is ready for accepting. The called slot is responsible for
+ * calling @ref accept.
+ *
+ * The location of the services file (where @p service is looked up)
+ * is defined by _PATH_SERVICES in /usr/include/netdb.h. This is
+ * usually set to /etc/services.
+ * See RFC 1700 for more information on services.
+ * You can specify @p service as a port number directly, rather than as a service
+ * name. This is discouraged as it prevents the end user from easily modifying
+ * the port number.
+ *
+ * For another example of usage, this below code attempts to make a connection on any port within a range:
+ * \code
+ * KServerSocket *ss = new KServerSocket();
+ * ss->setFamily(KResolver::InetFamily);
+ * bool found = false;
+ * for( unsigned int port = firstport; port <= lastport; ++port) {
+ * ss->setAddress( QString::number( port ) );
+ * bool success = ss->listen();
+ * if( found = ( success && ss->error() ==
+ * KSocketBase::NoError ) )
+ * break;
+ * ss->close();
+ * }
+ * if( !found ) {
+ * // Couldn't connect to any port.
+ * } else {
+ * connect(ss, SIGNAL(readyAccept()), this, SLOT(slotReadyAccept()));
+ * connect(ss, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
+ * ss->listen();
+ * }
+ * \endcode
+ *
+ * The called slot slotReadyAccept() is responsible for calling
+ * @ref accept.
+ *
+ * It is important to note that @ref accept can return either an
+ * object of type KNetwork::KStreamSocket or
+ * KNetwork::KBufferedSocket (default). If you want to accept a
+ * non-buffered socket, you must first call setAcceptBuffered.
+ *
+ * @warning If you use KServerSocket in an auxiliary (non-GUI) thread,
+ * you need to accept only KNetwork::KStreamSocket objects.
+ *
+ * @see KNetwork::KStreamSocket, KNetwork::KBufferedSocket
+ * @author Thiago Macieira <thiago@kde.org>
+ */
+class KDECORE_EXPORT KServerSocket: public QObject, public KPassiveSocketBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Default constructor.
+ *
+ * If the binding address isn't changed by setAddress, this socket will
+ * bind to all interfaces on this node and the port will be selected by the
+ * operating system.
+ *
+ * @param parent the parent QObject object
+ * @param name the name of this object
+ */
+ KServerSocket(QObject* parent = 0L, const char *name = 0L);
+
+ /**
+ * Construct this object specifying the service to listen on.
+ *
+ * If the binding address isn't changed by setAddress, this socket will
+ * bind to all interfaces and will listen on the port specified by
+ * @p service. This is either a service name (e.g. 'www') or a port
+ * number (e.g. '80').
+ *
+ * The location of the services file (where @p service is looked up)
+ * is defined by _PATH_SERVICES in /usr/include/netdb.h. This is
+ * usually set to /etc/services.
+ * See RFC 1700 for more information on services.
+ *
+ * @param service the service name to listen on
+ * @param parent the parent QObject object
+ * @param name the name of this object
+ */
+ KServerSocket(const QString& service, QObject* parent = 0L, const char *name = 0L);
+
+ /**
+ * Construct this object specifying the node and service names to listen on.
+ *
+ * If the binding address isn't changed by setAddress, this socket will
+ * bind to the interface specified by @p node and the port specified by
+ * @p service. This is either a service name (e.g. 'www') or a port
+ * number (e.g. '80').
+ *
+ * The location of the services file (where @p service is looked up)
+ * is defined by _PATH_SERVICES in /usr/include/netdb.h. This is
+ * usually set to /etc/services.
+ * See RFC 1700 for more information on services.
+ *
+ * @param node the node to bind to
+ * @param service the service port to listen on
+ * @param parent the parent QObject object
+ * @param name the name of this object
+ */
+ KServerSocket(const QString& node, const QString& service,
+ QObject* parent = 0L, const char *name = 0L);
+
+ /**
+ * Destructor. This will close the socket, if open.
+ *
+ * Note, however, that accepted sockets do not get closed when this
+ * object closes.
+ */
+ ~KServerSocket();
+
+protected:
+ /**
+ * Sets the socket options. Reimplemented from KSocketBase.
+ */
+ virtual bool setSocketOptions(int opts);
+
+public:
+ /**
+ * Returns the internal KResolver object used for
+ * looking up the host name and service.
+ *
+ * This can be used to set extra options to the
+ * lookup process other than the default values, as well
+ * as obtaining the error codes in case of lookup failure.
+ */
+ KResolver& resolver() const;
+
+ /**
+ * Returns the internal list of resolved results for the binding address.
+ */
+ const KResolverResults& resolverResults() const;
+
+ /**
+ * Enables or disables name resolution. If this flag is set to true,
+ * the @ref bind operation will trigger name lookup
+ * operations (i.e., converting a hostname into its binary form).
+ * If the flag is set to false, those operations will instead
+ * try to convert a string representation of an address without
+ * attempting name resolution.
+ *
+ * This is useful, for instance, when IP addresses are in
+ * their string representation (such as "1.2.3.4") or come
+ * from other sources like @ref KSocketAddress.
+ *
+ * @param enable whether to enable
+ */
+ void setResolutionEnabled(bool enable);
+
+ /**
+ * Sets the allowed families for the resolutions.
+ *
+ * @param families the families that we want/accept
+ * @see KResolver::SocketFamilies for possible values
+ */
+ void setFamily(int families);
+
+ /**
+ * Sets the address on which we will listen. The port to listen on is given by
+ * @p service, and we will bind to all interfaces. To let the operating system choose a
+ * port, set the service to "0". @p service can either be a service name
+ * (e.g. 'www') or a port number (e.g. '80').
+ *
+ * The location of the services file (where @p service is looked up)
+ * is defined by _PATH_SERVICES in /usr/include/netdb.h. This is
+ * usually set to /etc/services.
+ * See RFC 1700 for more information on services.
+ *
+ * @param service the service name to listen on
+ */
+ void setAddress(const QString& service);
+
+ /**
+ * @overload
+ * Sets the address on which we will listen. This will cause the socket to listen
+ * only on the interface given by @p node and on the port given by @p service.
+ * @p service can either be a service name (e.g. 'www') or a port number
+ * (e.g. '80').
+ *
+ * The location of the services file (where @p service is looked up)
+ * is defined by _PATH_SERVICES in /usr/include/netdb.h. This is
+ * usually set to /etc/services.
+ * See RFC 1700 for more information on services.
+ *
+ * @param node the node to bind to
+ * @param service the service port to listen on
+ */
+ void setAddress(const QString& node, const QString& service);
+
+ /**
+ * Sets the timeout for accepting. When you call @ref accept,
+ * it will wait at most @p msecs milliseconds or return with an error
+ * (returning a NULL object).
+ *
+ * @param msecs the time in milliseconds to wait, 0 to wait forever
+ */
+ void setTimeout(int msecs);
+
+ /**
+ * Starts the lookup for peer and local hostnames as
+ * well as their services.
+ *
+ * If the blocking mode for this object is on, this function will
+ * wait for the lookup results to be available (by calling the
+ * @ref KResolver::wait method on the resolver objects).
+ *
+ * When the lookup is done, the signal @ref hostFound will be
+ * emitted (only once, even if we're doing a double lookup).
+ * If the lookup failed (for any of the two lookups) the
+ * @ref gotError signal will be emitted with the appropriate
+ * error condition (see @ref KSocketBase::SocketError).
+ *
+ * This function returns true on success and false on error. Note that
+ * this is not the lookup result!
+ */
+ virtual bool lookup();
+
+ /**
+ * Binds this socket to the given nodename and service,
+ * or use the default ones if none are given.
+ *
+ * Upon successful binding, the @ref bound signal will be
+ * emitted. If an error is found, the @ref gotError
+ * signal will be emitted.
+ *
+ * This function returns true on success.
+ *
+ * @param node the nodename
+ * @param service the service
+ */
+ virtual bool bind(const QString& node, const QString& service);
+
+ /**
+ * Binds the socket to the given service name.
+ * @overload
+ *
+ * @param service the service
+ */
+ virtual bool bind(const QString& service);
+
+ /**
+ * Binds the socket to the addresses previously set with @ref setAddress.
+ * @overload
+ *
+ */
+ virtual bool bind();
+
+ /**
+ * Connect this socket to this specific address. Reimplemented from KSocketBase.
+ *
+ * Unlike @ref bind(const QString&, const QString&) above, this function
+ * really does bind the socket. No lookup is performed. The @ref bound signal
+ * will be emitted.
+ */
+ virtual bool bind(const KResolverEntry& address);
+
+ /**
+ * Puts this socket into listening mode. Reimplemented from @ref KPassiveSocketBase.
+ *
+ * Placing a socket into listening mode means it will be able to receive incoming
+ * connections through the @ref accept method.
+ *
+ * If you do not call this method but call @ref accept directly, the socket will
+ * be placed into listening mode automatically.
+ *
+ * @param backlog the number of connection the system is to
+ * queue without @ref accept being called
+ * @returns true if the socket is now in listening mode.
+ */
+ virtual bool listen(int backlog = 5); // 5 is arbitrary
+
+ /**
+ * Closes this socket.
+ */
+ virtual void close();
+
+ /**
+ * Toggles whether the accepted socket will be buffered or not.
+ * That is, the @ref accept function will always return a KStreamSocket
+ * object or descended from it. If buffering is enabled, the class
+ * to be returned will be KBufferedSocket.
+ *
+ * By default, this flag is set to true.
+ *
+ * @param enable whether to set the accepted socket to
+ * buffered mode
+ */
+ void setAcceptBuffered(bool enable);
+
+ /**
+ * Accepts one incoming connection and return the associated, open
+ * socket.
+ *
+ * If this function cannot accept a new connection, it will return NULL.
+ * The specific object class returned by this function may vary according
+ * to the implementation: derived classes may return specialised objects
+ * descended from KStreamSocket.
+ *
+ * @note This function should return a KStreamSocket object, but compiler
+ * deficiencies prevent such an adjustment. Therefore, we return
+ * the base class for active sockets, but it is guaranteed
+ * that the object will be a KStreamSocket or derived from it.
+ *
+ * @sa KBufferedSocket
+ * @sa setAcceptBuffered
+ */
+ virtual KActiveSocketBase* accept();
+
+ /**
+ * Returns this socket's local address.
+ */
+ virtual KSocketAddress localAddress() const;
+
+ /**
+ * Returns this socket's externally-visible address if know.
+ */
+ virtual KSocketAddress externalAddress() const;
+
+private slots:
+ void lookupFinishedSlot();
+
+signals:
+ /**
+ * This signal is emitted when this object finds an error.
+ * The @p code parameter contains the error code that can
+ * also be found by calling @ref error.
+ */
+ void gotError(int code);
+
+ /**
+ * This signal is emitted when the lookup is successfully completed.
+ */
+ void hostFound();
+
+ /**
+ * This signal is emitted when the socket successfully binds
+ * to an address.
+ *
+ * @param local the local address we bound to
+ */
+ void bound(const KResolverEntry& local);
+
+ /**
+ * This signal is emitted when the socket completes the
+ * closing/shut down process.
+ */
+ void closed();
+
+ /**
+ * This signal is emitted whenever the socket is ready for
+ * accepting -- i.e., there is at least one connection waiting to
+ * be accepted.
+ */
+ void readyAccept();
+
+protected:
+ /**
+ * Convenience function to set this object's error code to match
+ * that of the socket device.
+ */
+ void copyError();
+
+private:
+ bool doBind();
+ bool doListen();
+
+private:
+ KServerSocket(const KServerSocket&);
+ KServerSocket& operator=(const KServerSocket&);
+
+ KServerSocketPrivate *d;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/ksocketaddress.cpp b/kdecore/network/ksocketaddress.cpp
new file mode 100644
index 000000000..6c1316e94
--- /dev/null
+++ b/kdecore/network/ksocketaddress.cpp
@@ -0,0 +1,957 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <qfile.h>
+#include <qobject.h>
+
+#include "klocale.h"
+#include "ksocketaddress.h"
+
+#include "netsupp.h"
+
+using namespace KNetwork;
+
+#if 0
+class KIpAddress_localhostV4 : public KIpAddress
+{
+public:
+ KIpAddress_localhostV4()
+ {
+ *m_data = htonl(0x7f000001);
+ m_version = 4;
+ }
+};
+
+class KIpAddress_localhostV6 : public KIpAddress
+{
+public:
+ KIpAddress_localhostV6()
+ : KIpAddress(0L, 6)
+ {
+ m_data[3] = htonl(1);
+ }
+};
+#endif
+
+static const char localhostV4_data[] = { 127, 0, 0, 1 };
+static const char localhostV6_data[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,1 };
+
+const KIpAddress KIpAddress::localhostV4(&localhostV4_data, 4);
+const KIpAddress KIpAddress::localhostV6(&localhostV6_data, 6);
+const KIpAddress KIpAddress::anyhostV4(0L, 4);
+const KIpAddress KIpAddress::anyhostV6(0L, 6);
+
+// helper function to test if an IPv6 v4-mapped address is equal to its IPv4 counterpart
+static bool check_v4mapped(const Q_UINT32* v6addr, Q_UINT32 v4addr)
+{
+ // check that the v6 is a v4-mapped address
+ if (!(v6addr[0] == 0 && v6addr[1] == 0 && v6addr[2] == htonl(0x0000ffff)))
+ return false; // not a v4-mapped address
+
+ return v6addr[3] == v4addr;
+}
+
+// copy operator
+KIpAddress& KIpAddress::operator =(const KIpAddress& other)
+{
+ m_version = other.m_version;
+ if (m_version == 4 || m_version == 6)
+ memcpy(m_data, other.m_data, sizeof(m_data));
+ return *this;
+}
+
+// comparison
+bool KIpAddress::compare(const KIpAddress& other, bool checkMapped) const
+{
+ if (m_version == other.m_version)
+ switch (m_version)
+ {
+ case 0:
+ // both objects are empty
+ return true;
+
+ case 4:
+ // IPv4 address
+ return *m_data == *other.m_data;
+
+ case 6:
+ // IPv6 address
+ // they are 128-bit long, that is, 16 bytes
+ return memcmp(m_data, other.m_data, 16) == 0;
+ }
+
+ if (checkMapped)
+ {
+ // check the possibility of a v4-mapped address being compared to an IPv4 one
+ if (m_version == 6 && other.m_version == 4 && check_v4mapped(m_data, *other.m_data))
+ return true;
+
+ if (other.m_version == 6 && m_version == 4 && check_v4mapped(other.m_data, *m_data))
+ return true;
+ }
+
+ return false;
+}
+
+// sets the address to the given address
+bool KIpAddress::setAddress(const QString& address)
+{
+ m_version = 0;
+
+ // try to guess the address version
+ if (address.find(':') != -1)
+ {
+#ifdef AF_INET6
+ // guessing IPv6
+
+ Q_UINT32 buf[4];
+ if (inet_pton(AF_INET6, address.latin1(), buf))
+ {
+ memcpy(m_data, buf, sizeof(m_data));
+ m_version = 6;
+ return true;
+ }
+#endif
+
+ return false;
+ }
+ else
+ {
+ Q_UINT32 buf;
+ if (inet_pton(AF_INET, address.latin1(), &buf))
+ {
+ *m_data = buf;
+ m_version = 4;
+ return true;
+ }
+
+ return false;
+ }
+
+ return false; // can never happen!
+}
+
+bool KIpAddress::setAddress(const char* address)
+{
+ return setAddress(QString::fromLatin1(address));
+}
+
+// set from binary data
+bool KIpAddress::setAddress(const void* raw, int version)
+{
+ // this always succeeds
+ // except if version is invalid
+ if (version != 4 && version != 6)
+ return false;
+
+ m_version = version;
+ if (raw != 0L)
+ memcpy(m_data, raw, version == 4 ? 4 : 16);
+ else
+ memset(m_data, 0, 16);
+
+ return true;
+}
+
+// presentation form
+QString KIpAddress::toString() const
+{
+ char buf[sizeof "1111:2222:3333:4444:5555:6666:255.255.255.255" + 2];
+ buf[0] = '\0';
+ switch (m_version)
+ {
+ case 4:
+ inet_ntop(AF_INET, m_data, buf, sizeof(buf) - 1);
+ return QString::fromLatin1(buf);
+
+ case 6:
+#ifdef AF_INET6
+ inet_ntop(AF_INET6, m_data, buf, sizeof(buf) - 1);
+#endif
+ return QString::fromLatin1(buf);
+ }
+
+ return QString::null;
+}
+
+Q_UINT32 KIpAddress::hostIPv4Addr(bool convertMapped) const
+{
+ Q_UINT32 addr = IPv4Addr(convertMapped);
+ return ntohl(addr);
+}
+
+/*
+ * An IPv6 socket address
+ * This is taken from RFC 2553.
+ */
+struct our_sockaddr_in6
+{
+# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ Q_UINT8 sin6_len;
+ Q_UINT8 sin6_family;
+# else //!HAVE_STRUCT_SOCKADDR_SA_LEN
+ Q_UINT16 sin6_family;
+# endif
+ Q_UINT16 sin6_port; /* RFC says in_port_t */
+ Q_UINT32 sin6_flowinfo;
+ Q_UINT8 sin6_addr[16]; // 24 bytes up to here
+ Q_UINT32 sin6_scope_id; // 28 bytes total
+};
+
+// useful definitions
+#define MIN_SOCKADDR_LEN sizeof(Q_UINT16)
+#define SOCKADDR_IN_LEN sizeof(sockaddr_in)
+#define MIN_SOCKADDR_IN6_LEN ((unsigned long) &(((our_sockaddr_in6*)0)->sin6_scope_id))
+#define SOCKADDR_IN6_LEN sizeof(our_sockaddr_in6)
+#define MIN_SOCKADDR_UN_LEN (sizeof(Q_UINT16) + sizeof(char))
+
+
+class KNetwork::KSocketAddressData
+{
+public:
+ /*
+ * Note: maybe this should be virtual
+ * But since the data is shared via the d pointer, it doesn't really matter
+ * what one class sees, so will the other
+ */
+ class QMixSocketAddressRef : public KInetSocketAddress, public KUnixSocketAddress
+ {
+ public:
+ QMixSocketAddressRef(KSocketAddressData* d)
+ : KInetSocketAddress(d), KUnixSocketAddress(d)
+ {
+ }
+ };
+ QMixSocketAddressRef ref;
+
+ union
+ {
+ struct sockaddr *generic;
+ struct sockaddr_in *in;
+ struct our_sockaddr_in6 *in6;
+ struct sockaddr_un *un;
+ } addr;
+ Q_UINT16 curlen, reallen;
+
+ KSocketAddressData()
+ : ref(this)
+ {
+ addr.generic = 0L;
+ curlen = 0;
+ invalidate();
+ }
+
+ ~KSocketAddressData()
+ {
+ if (addr.generic != 0L)
+ free(addr.generic);
+ }
+
+ inline bool invalid() const
+ { return reallen == 0; }
+
+ inline void invalidate()
+ { reallen = 0; }
+
+ void dup(const sockaddr* sa, Q_UINT16 len, bool clear = true);
+
+ void makeipv4()
+ {
+ short oldport = 0;
+ if (!invalid())
+ switch (addr.generic->sa_family)
+ {
+ case AF_INET:
+ return; // nothing to do here
+#ifdef AF_INET6
+ case AF_INET6:
+ oldport = addr.in6->sin6_port;
+ break;
+#endif
+ }
+
+ // create new space
+ dup(0L, SOCKADDR_IN_LEN);
+
+ addr.in->sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ addr.in->sin_len = SOCKADDR_IN_LEN;
+#endif
+ addr.in->sin_port = oldport;
+ }
+
+ void makeipv6()
+ {
+ short oldport = 0;
+ if (!invalid())
+ switch (addr.generic->sa_family)
+ {
+ case AF_INET:
+ oldport = addr.in->sin_port;
+ break;
+
+#ifdef AF_INET6
+ case AF_INET6:
+ return; // nothing to do here
+#endif
+ }
+
+ // make room
+ dup(0L, SOCKADDR_IN6_LEN);
+#ifdef AF_INET6
+ addr.in6->sin6_family = AF_INET6;
+#endif
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ addr.in6->sin6_len = SOCKADDR_IN6_LEN;
+#endif
+ addr.in6->sin6_port = oldport;
+ // sin6_scope_id and sin6_flowid are zero
+ }
+
+};
+
+// create duplicates of
+void KSocketAddressData::dup(const sockaddr* sa, Q_UINT16 len, bool clear)
+{
+ if (len < MIN_SOCKADDR_LEN)
+ {
+ // certainly invalid
+ invalidate();
+ return;
+ }
+
+ if (sa && ((sa->sa_family == AF_INET && len < SOCKADDR_IN_LEN) ||
+#ifdef AF_INET6
+ (sa->sa_family == AF_INET6 && len < MIN_SOCKADDR_IN6_LEN) ||
+#endif
+ (sa->sa_family == AF_UNIX && len < MIN_SOCKADDR_UN_LEN)))
+ {
+ // also invalid
+ invalidate();
+ return;
+ }
+
+ // good
+ reallen = len;
+ if (len > curlen)
+ {
+ if (len < 32)
+ curlen = 32; // big enough for sockaddr_in and sockaddr_in6
+ else
+ curlen = len;
+ addr.generic = (sockaddr*)realloc(addr.generic, curlen);
+ }
+
+ if (sa != 0L)
+ {
+ memcpy(addr.generic, sa, len); // copy
+
+ // now, normalise the data
+ if (addr.generic->sa_family == AF_INET)
+ reallen = SOCKADDR_IN_LEN; // no need to be larger
+#ifdef AF_INET6
+ else if (addr.generic->sa_family == AF_INET6)
+ {
+ // set the extra field (sin6_scope_id)
+
+ // the buffer is never smaller than 32 bytes, so this is always
+ // allowed
+ if (reallen < SOCKADDR_IN6_LEN)
+ addr.in6->sin6_scope_id = 0;
+
+ reallen = SOCKADDR_IN6_LEN;
+ }
+#endif
+ else if (addr.generic->sa_family == AF_UNIX)
+ reallen = MIN_SOCKADDR_UN_LEN + strlen(addr.un->sun_path);
+ }
+ else if (clear)
+ {
+ memset(addr.generic, 0, len);
+ addr.generic->sa_family = AF_UNSPEC;
+ }
+}
+
+// default constructor
+KSocketAddress::KSocketAddress()
+ : d(new KSocketAddressData)
+{
+}
+
+// constructor from binary data
+KSocketAddress::KSocketAddress(const sockaddr *sa, Q_UINT16 len)
+ : d(new KSocketAddressData)
+{
+ setAddress(sa, len);
+}
+
+KSocketAddress::KSocketAddress(const KSocketAddress& other)
+ : d(new(KSocketAddressData))
+{
+ *this = other;
+}
+
+KSocketAddress::KSocketAddress(KSocketAddressData *d2)
+ : d(d2)
+{
+}
+
+KSocketAddress::~KSocketAddress()
+{
+ // prevent double-deletion, since we're already being deleted
+ if (d)
+ {
+ d->ref.KInetSocketAddress::d = 0L;
+ d->ref.KUnixSocketAddress::d = 0L;
+ delete d;
+ }
+}
+
+KSocketAddress& KSocketAddress::operator =(const KSocketAddress& other)
+{
+ if (other.d && !other.d->invalid())
+ d->dup(other.d->addr.generic, other.d->reallen);
+ else
+ d->invalidate();
+ return *this;
+}
+
+const sockaddr* KSocketAddress::address() const
+{
+ if (d->invalid())
+ return 0L;
+ return d->addr.generic;
+}
+
+sockaddr* KSocketAddress::address()
+{
+ if (d->invalid())
+ return 0L;
+ return d->addr.generic;
+}
+
+KSocketAddress& KSocketAddress::setAddress(const sockaddr* sa, Q_UINT16 len)
+{
+ if (sa != 0L && len >= MIN_SOCKADDR_LEN)
+ d->dup(sa, len);
+ else
+ d->invalidate();
+
+ return *this;
+}
+
+Q_UINT16 KSocketAddress::length() const
+{
+ if (d->invalid())
+ return 0;
+ return d->reallen;
+}
+
+KSocketAddress& KSocketAddress::setLength(Q_UINT16 len)
+{
+ d->dup((sockaddr*)0L, len, false);
+
+ return *this;
+}
+
+int KSocketAddress::family() const
+{
+ if (d->invalid())
+ return AF_UNSPEC;
+ return d->addr.generic->sa_family;
+}
+
+KSocketAddress& KSocketAddress::setFamily(int family)
+{
+ if (d->invalid())
+ d->dup((sockaddr*)0L, MIN_SOCKADDR_LEN);
+ d->addr.generic->sa_family = family;
+
+ return *this;
+}
+
+bool KSocketAddress::operator ==(const KSocketAddress& other) const
+{
+ // if this is invalid, it's only equal if the other one is invalid as well
+ if (d->invalid())
+ return other.d->invalid();
+
+ // check the family to make sure we don't do unnecessary comparison
+ if (d->addr.generic->sa_family != other.d->addr.generic->sa_family)
+ return false; // not the same family, not equal
+
+ // same family then
+ // check the ones we know already
+ switch (d->addr.generic->sa_family)
+ {
+ case AF_INET:
+ Q_ASSERT(d->reallen == SOCKADDR_IN_LEN);
+ Q_ASSERT(other.d->reallen == SOCKADDR_IN_LEN);
+ return memcmp(d->addr.in, other.d->addr.in, SOCKADDR_IN_LEN) == 0;
+
+#ifdef AF_INET6
+ case AF_INET6:
+ Q_ASSERT(d->reallen >= MIN_SOCKADDR_IN6_LEN);
+ Q_ASSERT(other.d->reallen >= MIN_SOCKADDR_IN6_LEN);
+
+# if !defined(HAVE_STRUCT_SOCKADDR_IN6) || defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)
+ // check for the case where sin6_scope_id isn't present
+ if (d->reallen != other.d->reallen)
+ {
+ if (memcmp(d->addr.in6, other.d->addr.in6, MIN_SOCKADDR_IN6_LEN) != 0)
+ return false; // not equal
+ if (d->reallen > other.d->reallen)
+ return d->addr.in6->sin6_scope_id == 0;
+ else
+ return other.d->addr.in6->sin6_scope_id == 0;
+ }
+# endif
+
+ return memcmp(d->addr.in6, other.d->addr.in6, d->reallen) == 0;
+#endif
+
+ case AF_UNIX:
+ Q_ASSERT(d->reallen >= MIN_SOCKADDR_UN_LEN);
+ Q_ASSERT(other.d->reallen >= MIN_SOCKADDR_UN_LEN);
+
+ // do a string comparison here
+ return strcmp(d->addr.un->sun_path, other.d->addr.un->sun_path) == 0;
+
+ default:
+ // something else we don't know about
+ // they are equal if and only if they are exactly equal
+ if (d->reallen == other.d->reallen)
+ return memcmp(d->addr.generic, other.d->addr.generic, d->reallen) == 0;
+ }
+
+ return false; // not equal in any other case
+}
+
+QString KSocketAddress::nodeName() const
+{
+ if (d->invalid())
+ return QString::null;
+
+ switch (d->addr.generic->sa_family)
+ {
+ case AF_INET:
+#ifdef AF_INET6
+ case AF_INET6:
+
+ QString scopeid("%");
+ if (d->addr.generic->sa_family == AF_INET6 && d->addr.in6->sin6_scope_id)
+ scopeid += QString::number(d->addr.in6->sin6_scope_id);
+ else
+ scopeid.truncate(0);
+ return d->ref.ipAddress().toString() + scopeid;
+#else
+ return d->ref.ipAddress().toString();
+#endif
+ }
+
+ // any other case, including AF_UNIX
+ return QString::null;
+}
+
+QString KSocketAddress::serviceName() const
+{
+ if (d->invalid())
+ return QString::null;
+
+ switch (d->addr.generic->sa_family)
+ {
+ case AF_INET:
+#ifdef AF_INET6
+ case AF_INET6:
+#endif
+ return QString::number(d->ref.port());
+
+ case AF_UNIX:
+ return d->ref.pathname();
+ }
+
+ return QString::null;
+}
+
+QString KSocketAddress::toString() const
+{
+ if (d->invalid())
+ return QString::null;
+
+ QString fmt;
+
+ if (d->addr.generic->sa_family == AF_INET)
+ fmt = "%1:%2";
+#ifdef AF_INET6
+ else if (d->addr.generic->sa_family == AF_INET6)
+ fmt = "[%1]:%2";
+#endif
+ else if (d->addr.generic->sa_family == AF_UNIX)
+ return QString::fromLatin1("unix:%1").arg(serviceName());
+ else
+ return i18n("1: the unknown socket address family number",
+ "Unknown family %1").arg(d->addr.generic->sa_family);
+
+ return fmt.arg(nodeName()).arg(serviceName());
+}
+
+KInetSocketAddress& KSocketAddress::asInet()
+{
+ return d->ref;
+}
+
+KInetSocketAddress KSocketAddress::asInet() const
+{
+ return d->ref;
+}
+
+KUnixSocketAddress& KSocketAddress::asUnix()
+{
+ return d->ref;
+}
+
+KUnixSocketAddress KSocketAddress::asUnix() const
+{
+ return d->ref;
+}
+
+int KSocketAddress::ianaFamily(int af)
+{
+ switch (af)
+ {
+ case AF_INET:
+ return 1;
+
+#ifdef AF_INET6
+ case AF_INET6:
+ return 2;
+#endif
+
+ default:
+ return 0;
+ }
+}
+
+int KSocketAddress::fromIanaFamily(int iana)
+{
+ switch (iana)
+ {
+ case 1:
+ return AF_INET;
+
+#ifdef AF_INET6
+ case 2:
+ return AF_INET6;
+#endif
+
+ default:
+ return AF_UNSPEC;
+ }
+}
+
+// default constructor
+KInetSocketAddress::KInetSocketAddress()
+{
+}
+
+// binary data constructor
+KInetSocketAddress::KInetSocketAddress(const sockaddr* sa, Q_UINT16 len)
+ : KSocketAddress(sa, len)
+{
+ if (!d->invalid())
+ update();
+}
+
+// create from IP and port
+KInetSocketAddress::KInetSocketAddress(const KIpAddress& host, Q_UINT16 port)
+{
+ setHost(host);
+ setPort(port);
+}
+
+// copy constructor
+KInetSocketAddress::KInetSocketAddress(const KInetSocketAddress& other)
+ : KSocketAddress(other)
+{
+}
+
+// special copy constructor
+KInetSocketAddress::KInetSocketAddress(const KSocketAddress& other)
+ : KSocketAddress(other)
+{
+ if (!d->invalid())
+ update();
+}
+
+// special constructor
+KInetSocketAddress::KInetSocketAddress(KSocketAddressData *d)
+ : KSocketAddress(d)
+{
+}
+
+// destructor
+KInetSocketAddress::~KInetSocketAddress()
+{
+ /* nothing to do */
+}
+
+// copy operator
+KInetSocketAddress& KInetSocketAddress::operator =(const KInetSocketAddress& other)
+{
+ KSocketAddress::operator =(other);
+ return *this;
+}
+
+// IP version
+int KInetSocketAddress::ipVersion() const
+{
+ if (d->invalid())
+ return 0;
+
+ switch (d->addr.generic->sa_family)
+ {
+ case AF_INET:
+ return 4;
+
+#ifdef AF_INET6
+ case AF_INET6:
+ return 6;
+#endif
+ }
+
+ return 0; // for all other cases
+}
+
+KIpAddress KInetSocketAddress::ipAddress() const
+{
+ if (d->invalid())
+ return KIpAddress(); // return an empty address as well
+
+ switch (d->addr.generic->sa_family)
+ {
+ case AF_INET:
+ return KIpAddress(&d->addr.in->sin_addr, 4);
+#ifdef AF_INET6
+ case AF_INET6:
+ return KIpAddress(&d->addr.in6->sin6_addr, 6);
+#endif
+ }
+
+ return KIpAddress(); // empty in all other cases
+}
+
+KInetSocketAddress& KInetSocketAddress::setHost(const KIpAddress& ip)
+{
+ switch (ip.version())
+ {
+ case 4:
+ makeIPv4();
+ memcpy(&d->addr.in->sin_addr, ip.addr(), sizeof(d->addr.in->sin_addr));
+ break;
+
+ case 6:
+ makeIPv6();
+ memcpy(&d->addr.in6->sin6_addr, ip.addr(), sizeof(d->addr.in6->sin6_addr));
+ break;
+
+ default:
+ // empty
+ d->invalidate();
+ }
+
+ return *this;
+}
+
+// returns the port
+Q_UINT16 KInetSocketAddress::port() const
+{
+ if (d->invalid())
+ return 0;
+
+ switch (d->addr.generic->sa_family)
+ {
+ case AF_INET:
+ return ntohs(d->addr.in->sin_port);
+
+#ifdef AF_INET6
+ case AF_INET6:
+ return ntohs(d->addr.in6->sin6_port);
+#endif
+ }
+
+ return 0;
+}
+
+KInetSocketAddress& KInetSocketAddress::setPort(Q_UINT16 port)
+{
+ if (d->invalid())
+ makeIPv4();
+
+ switch (d->addr.generic->sa_family)
+ {
+ case AF_INET:
+ d->addr.in->sin_port = htons(port);
+ break;
+
+#ifdef AF_INET6
+ case AF_INET6:
+ d->addr.in6->sin6_port = htons(port);
+ break;
+#endif
+
+ default:
+ d->invalidate(); // setting the port on something else
+ }
+
+ return *this;
+}
+
+KInetSocketAddress& KInetSocketAddress::makeIPv4()
+{
+ d->makeipv4();
+ return *this;
+}
+
+KInetSocketAddress& KInetSocketAddress::makeIPv6()
+{
+ d->makeipv6();
+ return *this;
+}
+
+Q_UINT32 KInetSocketAddress::flowinfo() const
+{
+#ifndef AF_INET6
+ return 0;
+#else
+
+ if (!d->invalid() && d->addr.in6->sin6_family == AF_INET6)
+ return d->addr.in6->sin6_flowinfo;
+ return 0;
+#endif
+}
+
+KInetSocketAddress& KInetSocketAddress::setFlowinfo(Q_UINT32 flowinfo)
+{
+ makeIPv6(); // must set here
+ d->addr.in6->sin6_flowinfo = flowinfo;
+ return *this;
+}
+
+int KInetSocketAddress::scopeId() const
+{
+#ifndef AF_INET6
+ return 0;
+#else
+
+ if (!d->invalid() && d->addr.in6->sin6_family == AF_INET6)
+ return d->addr.in6->sin6_scope_id;
+ return 0;
+#endif
+}
+
+KInetSocketAddress& KInetSocketAddress::setScopeId(int scopeid)
+{
+ makeIPv6(); // must set here
+ d->addr.in6->sin6_scope_id = scopeid;
+ return *this;
+}
+
+void KInetSocketAddress::update()
+{
+ if (d->addr.generic->sa_family == AF_INET)
+ return;
+#ifdef AF_INET6
+ else if (d->addr.generic->sa_family == AF_INET6)
+ return;
+#endif
+ else
+ d->invalidate();
+}
+
+KUnixSocketAddress::KUnixSocketAddress()
+{
+}
+
+KUnixSocketAddress::KUnixSocketAddress(const sockaddr* sa, Q_UINT16 len)
+ : KSocketAddress(sa, len)
+{
+ if (!d->invalid() && d->addr.un->sun_family != AF_UNIX)
+ d->invalidate();
+}
+
+KUnixSocketAddress::KUnixSocketAddress(const KUnixSocketAddress& other)
+ : KSocketAddress(other)
+{
+}
+
+KUnixSocketAddress::KUnixSocketAddress(const QString& pathname)
+{
+ setPathname(pathname);
+}
+
+KUnixSocketAddress::KUnixSocketAddress(KSocketAddressData* d)
+ : KSocketAddress(d)
+{
+}
+
+KUnixSocketAddress::~KUnixSocketAddress()
+{
+}
+
+KUnixSocketAddress& KUnixSocketAddress::operator =(const KUnixSocketAddress& other)
+{
+ KSocketAddress::operator =(other);
+ return *this;
+}
+
+QString KUnixSocketAddress::pathname() const
+{
+ if (!d->invalid() && d->addr.un->sun_family == AF_UNIX)
+ return QFile::decodeName(d->addr.un->sun_path);
+ return QString::null;
+}
+
+KUnixSocketAddress& KUnixSocketAddress::setPathname(const QString& path)
+{
+ d->dup(0L, MIN_SOCKADDR_UN_LEN + path.length());
+ d->addr.un->sun_family = AF_UNIX;
+ strcpy(d->addr.un->sun_path, QFile::encodeName(path));
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ d->addr.un->sun_len = d->reallen;
+#endif
+
+ return *this;
+}
diff --git a/kdecore/network/ksocketaddress.h b/kdecore/network/ksocketaddress.h
new file mode 100644
index 000000000..456422f9f
--- /dev/null
+++ b/kdecore/network/ksocketaddress.h
@@ -0,0 +1,912 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KSOCKETADDRESS_H
+#define KSOCKETADDRESS_H
+
+#include <qstring.h>
+#include <qcstring.h>
+
+#include <kdelibs_export.h>
+
+struct sockaddr;
+struct sockaddr_in;
+struct sockaddr_in6;
+struct sockaddr_un;
+
+namespace KNetwork {
+
+class KIpAddress;
+class KSocketAddress;
+class KInetSocketAddress;
+class KUnixSocketAddress;
+
+/** @class KIpAddress ksocketaddress.h ksocketaddress.h
+ * @brief An IP address.
+ *
+ * This class represents one IP address, version 4 or 6. This is only
+ * the address, not including port information or other data.
+ *
+ * It is not a good programming practice to create address from objects
+ * like this. Instead, prefer a more thorough function like
+ * @ref KResolver::resolve, which also handle extra information like scope
+ * ids.
+ *
+ * This is a light-weight class. Most of the member functions are inlined and
+ * there are no virtual functions. This object's size should be less than 20
+ * bytes. Also note that there is no sharing of data.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KIpAddress
+{
+public:
+ /**
+ * Default constructor. Creates an empty address.
+ * It defaults to IP version 4.
+ */
+ inline KIpAddress() : m_version(0)
+ { }
+
+ /**
+ * Copy constructor. Copies the data from the other
+ * object.
+ *
+ * Data is not shared.
+ *
+ * @param other the other
+ */
+ inline KIpAddress(const KIpAddress& other)
+ { *this = other; }
+
+ /**
+ * Creates an object from the given string representation.
+ *
+ * The IP version is guessed from the address format.
+ *
+ * @param addr the address
+ */
+ inline KIpAddress(const QString& addr)
+ { setAddress(addr); }
+
+ /**
+ * Creates an object from the given string representation.
+ *
+ * The IP version is guessed from the address format.
+ *
+ * @param addr the address
+ */
+ inline KIpAddress(const char* addr)
+ { setAddress(addr); }
+
+ /**
+ * Creates an object from the given raw data and IP version.
+ *
+ * @param addr the raw data
+ * @param version the IP version (4 or 6)
+ */
+ inline KIpAddress(const void* addr, int version = 4)
+ { setAddress(addr, version); }
+
+ /**
+ * This is a convenience constructor. Constructs an object
+ * from the given IPv4 address in the form of an integer.
+ *
+ * Note: do not write code to depend on IPv4 addresses being
+ * integer types. Instead, treat them as a special type, like
+ * a KIpAddress or the system's in_addr.
+ *
+ * @param ip4addr the IPv4 address
+ */
+ inline KIpAddress(Q_UINT32 ip4addr)
+ { setAddress(&ip4addr, 4); }
+
+ /**
+ * Destructor. This frees resources associated with this object.
+ *
+ * Note: destructor is non-virtual. The compiler will happily optimise it
+ * out of the way.
+ */
+ inline ~KIpAddress()
+ { }
+
+ /**
+ * Copy operator.
+ *
+ * Copies the data from the other object into this one.
+ *
+ * @param other the object to copy
+ */
+ KIpAddress& operator =(const KIpAddress& other);
+
+ /**
+ * Returns true if the two addresses match.
+ * This function performs a v4-mapped check.
+ * @see compare
+ */
+ inline bool operator ==(const KIpAddress& other) const
+ { return compare(other, true); }
+
+ /**
+ * Compares this address against the other, supplied one and return
+ * true if they match. The @p checkMapped parameter controls whether
+ * a check for an IPv6 v4-mapped address will be performed.
+ *
+ * An IPv6 v4-mapped address is an IPv6 address that is, for all purposes,
+ * equivalent to an IPv4 one. The default behaviour of this function
+ * is to take that into account. If you want a strict matching,
+ * pass @b false to the @p checkMapped parameter.
+ *
+ * @param other the other IP address
+ * @param checkMapped whether v4-mapped addresses will be taken into account
+ */
+ bool compare(const KIpAddress& other, bool checkMapped = true) const;
+
+ /**
+ * Retrieves the IP version in this object.
+ *
+ * @returns the version: 4 or 6
+ */
+ inline int version() const
+ { return m_version; }
+
+ /**
+ * Returns true if this is an IPv4 address.
+ */
+ inline bool isIPv4Addr() const
+ { return version() == 4; }
+
+ /**
+ * Returns true if this is an IPv6 address.
+ */
+ inline bool isIPv6Addr() const
+ { return version() == 6; }
+
+ /**
+ * Sets the address to the given string representation.
+ *
+ * @return true if the address was successfully parsed; otherwise returns
+ * false and leaves the object unchanged.
+ */
+ bool setAddress(const QString& address);
+
+ /**
+ * Sets the address to the given string representation.
+ *
+ * @return true if the address was successfully parsed; otherwise returns
+ * false and leaves the object unchanged.
+ */
+ bool setAddress(const char* address);
+
+ /**
+ * Sets the address to the given raw binary representation.
+ *
+ * @param raw a pointer to the raw binary data
+ * @param version the IP version
+ * @return true if the address was successfully parsed; otherwise returns
+ * false and leaves the object unchanged.
+ */
+ bool setAddress(const void* raw, int version = 4);
+
+ /**
+ * Returns the address as a string.
+ */
+ QString toString() const;
+
+ /**
+ * Returns a pointer to binary raw data representing the address.
+ */
+ inline const void *addr() const
+ { return m_data; }
+
+ /**
+ * This is a convenience function. Returns the IPv4 address in a
+ * 32-bit integer. The result is only valid if @ref isIPv4Addr returns
+ * true. Alternatively, if the contained IPv6 address is a v4-mapped one
+ * and the @p convertMapped parameter is true, the result will also be
+ * valid. The address returned is in network byte order.
+ *
+ * Note: you should not treat IP addresses as integers. Instead,
+ * use types defined for that purpose, such as KIpAddress or the
+ * system's in_addr type.
+ *
+ */
+ inline Q_UINT32 IPv4Addr(bool convertMapped = true) const
+ {
+ return (convertMapped && isV4Mapped()) ? m_data[3] : m_data[0];
+ }
+
+ /**
+ * This is a convenience function. Returns the IPv4 address in a
+ * 32-bit integer. The result is only valid if @ref isIPv4Addr returns
+ * true. Alternatively, if the contained IPv6 address is a v4-mapped one
+ * and the @p convertMapped parameter is true, the result will also be
+ * valid. The address returned is in host byte order.
+ *
+ */
+ Q_UINT32 hostIPv4Addr(bool convertMapped = true) const;
+
+public:
+ /*-- tests --*/
+
+ /**
+ * Returns true if this is the IPv4 or IPv6 unspecified address.
+ */
+ inline bool isUnspecified() const
+ { return version() == 0 ? true : (*this == anyhostV4 || *this == anyhostV6); }
+
+ /**
+ * Returns true if this is either the IPv4 or the IPv6 localhost address.
+ */
+ inline bool isLocalhost() const
+ { return version() == 0 ? false : (*this == localhostV4 || *this == localhostV6); }
+
+ /**
+ * This is an alias for @ref isLocalhost.
+ */
+ inline bool isLoopback() const
+ { return isLocalhost(); }
+
+ /**
+ * Returns true if this is an IPv4 class A address, i.e.,
+ * from 0.0.0.0 to 127.255.255.255.
+ *
+ * This function does not test for v4-mapped addresses.
+ */
+ inline bool isClassA() const
+ { return version() != 4 ? false : (hostIPv4Addr() & 0x80000000) == 0; }
+
+ /**
+ * Returns true if this is an IPv4 class B address, i.e., one from
+ * 128.0.0.0 to 191.255.255.255.
+ *
+ * This function does not test for v4-mapped addresses.
+ */
+ inline bool isClassB() const
+ { return version() != 4 ? false : (hostIPv4Addr() & 0xc0000000) == 0x80000000; }
+
+ /**
+ * Returns true if this is an IPv4 class C address, i.e., one from
+ * 192.0.0.0 to 223.255.255.255.
+ *
+ * This function does not test for v4-mapped addresses.
+ */
+ inline bool isClassC() const
+ { return version() != 4 ? false : (hostIPv4Addr() & 0xe0000000) == 0xc0000000; }
+
+ /**
+ * Returns true if this is an IPv4 class D (a.k.a. multicast) address.
+ *
+ * Note: this function is not the same as @ref isMulticast. isMulticast also
+ * tests for IPv6 multicast addresses.
+ */
+ inline bool isClassD() const
+ { return version() != 4 ? false : (hostIPv4Addr() & 0xf0000000) == 0xe0000000; }
+
+ /**
+ * Returns true if this is a multicast address, be it IPv4 or IPv6.
+ */
+ inline bool isMulticast() const
+ {
+ if (version() == 4) return isClassD();
+ if (version() == 6) return ((Q_UINT8*)addr())[0] == 0xff;
+ return false;
+ }
+
+ /**
+ * Returns true if this is an IPv6 link-local address.
+ */
+ inline bool isLinkLocal() const
+ {
+ if (version() != 6) return false;
+ Q_UINT8* addr = (Q_UINT8*)this->addr();
+ return (addr[0] & 0xff) == 0xfe &&
+ (addr[1] & 0xc0) == 0x80;
+ }
+
+ /**
+ * Returns true if this is an IPv6 site-local address.
+ */
+ inline bool isSiteLocal() const
+ {
+ if (version() != 6) return false;
+ Q_UINT8* addr = (Q_UINT8*)this->addr();
+ return (addr[0] & 0xff) == 0xfe &&
+ (addr[1] & 0xc0) == 0xc0;
+ }
+
+ /**
+ * Returns true if this is a global IPv6 address.
+ */
+ inline bool isGlobal() const
+ { return version() != 6 ? false : !(isMulticast() || isLinkLocal() || isSiteLocal()); }
+
+ /**
+ * Returns true if this is a v4-mapped IPv6 address.
+ */
+ inline bool isV4Mapped() const
+ {
+ if (version() != 6) return false;
+ Q_UINT32* addr = (Q_UINT32*)this->addr();
+ return addr[0] == 0 && addr[1] == 0 &&
+ ((Q_UINT16*)&addr[2])[0] == 0 &&
+ ((Q_UINT16*)&addr[2])[1] == 0xffff;
+ }
+
+ /**
+ * Returns true if this is a v4-compat IPv6 address.
+ */
+ inline bool isV4Compat() const
+ {
+ if (version() != 6 || isLocalhost()) return false;
+ Q_UINT32* addr = (Q_UINT32*)this->addr();
+ return addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] != 0;
+ }
+
+ /**
+ * Returns true if this is an IPv6 node-local multicast address.
+ */
+ inline bool isMulticastNodeLocal() const
+ { return version() == 6 && isMulticast() && (((Q_UINT32*)addr())[0] & 0xf) == 0x1; }
+
+ /**
+ * Returns true if this is an IPv6 link-local multicast address.
+ */
+ inline bool isMulticastLinkLocal() const
+ { return version() == 6 && isMulticast() && (((Q_UINT32*)addr())[0] & 0xf) == 0x2; }
+
+ /**
+ * Returns true if this is an IPv6 site-local multicast address.
+ */
+ inline bool isMulticastSiteLocal() const
+ { return version() == 6 && isMulticast() && (((Q_UINT32*)addr())[0] & 0xf) == 0x5; }
+
+ /**
+ * Returns true if this is an IPv6 organisational-local multicast address.
+ */
+ inline bool isMulticastOrgLocal() const
+ { return version() == 6 && isMulticast() && (((Q_UINT32*)addr())[0] & 0xf) == 0x8; }
+
+ /**
+ * Returns true if this is an IPv6 global multicast address.
+ */
+ inline bool isMulticastGlobal() const
+ { return version() == 6 && isMulticast() && (((Q_UINT32*)addr())[0] & 0xf) == 0xe; }
+
+protected:
+ Q_UINT32 m_data[4]; // 16 bytes, needed for an IPv6 address
+
+ char m_version;
+
+public:
+ /// localhost in IPv4 (127.0.0.1)
+ static const KIpAddress localhostV4;
+ /// the any host or undefined address in IPv4 (0.0.0.0)
+ static const KIpAddress anyhostV4;
+
+ /// localhost in IPv6 (::1)
+ static const KIpAddress localhostV6;
+ /// the any host or undefined address in IPv6 (::)
+ static const KIpAddress anyhostV6;
+};
+
+
+class KSocketAddressData;
+/** @class KSocketAddress ksocketaddress.h ksocketaddress.h
+ * @brief A generic socket address.
+ *
+ * This class holds one generic socket address.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KSocketAddress
+{
+public:
+ /**
+ * Default constructor.
+ *
+ * Creates an empty object
+ */
+ KSocketAddress();
+
+ /**
+ * Creates this object with the given data.
+ * The raw socket address is copied into this object.
+ *
+ * @param sa the socket address structure
+ * @param len the socket address length
+ */
+ KSocketAddress(const sockaddr* sa, Q_UINT16 len);
+
+ /**
+ * Copy constructor. This creates a copy of the other
+ * object.
+ *
+ * Data is not shared.
+ *
+ * @param other the object to copy from
+ */
+ KSocketAddress(const KSocketAddress& other);
+
+ /**
+ * Destructor. Frees any associated resources.
+ */
+ virtual ~KSocketAddress();
+
+ /**
+ * Performs a shallow copy of the other object into this one.
+ * Data will be copied.
+ *
+ * @param other the object to copy from
+ */
+ KSocketAddress& operator =(const KSocketAddress& other);
+
+ /**
+ * Returns the socket address structure, to be passed down to
+ * low level functions.
+ *
+ * Note that this function returns NULL for invalid or empty sockets,
+ * so you may use to to test for validity.
+ */
+ const sockaddr* address() const;
+
+ /**
+ * Returns the socket address structure, to be passed down to
+ * low level functions.
+ *
+ * Note that this function returns NULL for invalid or empty sockets,
+ * so you may use to to test for validity.
+ *
+ * The returned value, if not NULL, is an internal buffer which is guaranteed
+ * to be at least @ref length() bytes long.
+ */
+ sockaddr* address();
+
+ /**
+ * Sets the address to the given address.
+ * The raw socket address is copied into this object.
+ *
+ * @param sa the socket address structure
+ * @param len the socket address length
+ */
+ KSocketAddress& setAddress(const sockaddr *sa, Q_UINT16 len);
+
+ /**
+ * Returns the socket address structure, to be passed down to
+ * low level functions.
+ */
+ inline operator const sockaddr*() const
+ { return address(); }
+
+ /**
+ * Returns the length of this socket address structure.
+ */
+ Q_UINT16 length() const;
+
+ /**
+ * Sets the length of this socket structure.
+ *
+ * Use this function with care. It allows you to resize the internal
+ * buffer to fit needs. This function should not be used except for handling
+ * unknown socket address structures.
+ *
+ * Also note that this function may invalidate the socket if a known
+ * family is set (Internet or Unix socket) and the new length would be
+ * too small to hold the system's sockaddr_* structure. If unsure, reset
+ * the family:
+ *
+ * \code
+ * KSocketAddress qsa;
+ * [...]
+ * qsa.setFamily(AF_UNSPEC).setLength(newlen);
+ * \endcode
+ *
+ * @param len the new length
+ */
+ KSocketAddress& setLength(Q_UINT16 len);
+
+ /**
+ * Returns the family of this address.
+ * @return the family of this address, AF_UNSPEC if it's undefined
+ */
+ int family() const;
+
+ /**
+ * Sets the family of this object.
+ *
+ * Note: setting the family will probably invalidate any address data
+ * contained in this object. Use this function with care.
+ *
+ * @param family the new family to set
+ */
+ virtual KSocketAddress& setFamily(int family);
+
+ /**
+ * Returns the IANA family number of this address.
+ * @return the IANA family number of this address (1 for AF_INET.
+ * 2 for AF_INET6, otherwise 0)
+ */
+ inline int ianaFamily() const
+ { return ianaFamily(family()); }
+
+ /**
+ * Returns true if this equals the other socket.
+ *
+ * Socket addresses are considered matching if and only if all data is the same.
+ *
+ * @param other the other socket
+ * @return true if both sockets are equal
+ */
+ bool operator ==(const KSocketAddress& other) const;
+
+ /**
+ * Returns the node name of this socket.
+ *
+ * In the case of Internet sockets, this is string representation of the IP address.
+ * The default implementation returns QString::null.
+ *
+ * @return the node name, can be QString::null
+ * @bug use KResolver to resolve unknown families
+ */
+ virtual QString nodeName() const;
+
+ /**
+ * Returns the service name for this socket.
+ *
+ * In the case of Internet sockets, this is the port number.
+ * The default implementation returns QString::null.
+ *
+ * @return the service name, can be QString::null
+ * @bug use KResolver to resolve unknown families
+ */
+ virtual QString serviceName() const;
+
+ /**
+ * Returns this socket address as a string suitable for
+ * printing. Family, node and service are part of this address.
+ *
+ * @bug use KResolver to resolve unknown families
+ */
+ virtual QString toString() const;
+
+ /**
+ * Returns an object reference that can be used to manipulate this socket
+ * as an Internet socket address. Both objects share the same data.
+ */
+ KInetSocketAddress& asInet();
+
+ /**
+ * Returns an object is equal to this object's data, but they don't share it.
+ */
+ KInetSocketAddress asInet() const;
+
+ /**
+ * Returns an object reference that can be used to manipulate this socket
+ * as a Unix socket address. Both objects share the same data.
+ */
+ KUnixSocketAddress& asUnix();
+
+ /**
+ * Returns an object is equal to this object's data, but they don't share it.
+ */
+ KUnixSocketAddress asUnix() const;
+
+protected:
+ /// @internal
+ /// private data
+ KSocketAddressData *d;
+
+ /// @internal
+ /// extra constructor
+ KSocketAddress(KSocketAddressData* d);
+
+public: // static
+ /**
+ * Returns the IANA family number of the given address family.
+ * Returns 0 if there is no corresponding IANA family number.
+ * @param af the address family, in AF_* constants
+ * @return the IANA family number of this address (1 for AF_INET.
+ * 2 for AF_INET6, otherwise 0)
+ */
+ static int ianaFamily(int af);
+
+ /**
+ * Returns the address family of the given IANA family number.
+ * @return the address family, AF_UNSPEC for unknown IANA family numbers
+ */
+ static int fromIanaFamily(int iana);
+};
+
+
+/** @class KInetSocketAddress ksocketaddress.h ksocketaddress.h
+ * @brief an Internet socket address
+ *
+ * An Inet (IPv4 or IPv6) socket address
+ *
+ * This is an IPv4 or IPv6 address of the Internet.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KInetSocketAddress: public KSocketAddress
+{
+ friend class KSocketAddress;
+public:
+ /**
+ * Public constructor. Creates an empty object.
+ */
+ KInetSocketAddress();
+
+ /**
+ * Creates an object from raw data.
+ *
+ * Note: if the socket address @p sa does not contain a valid Internet
+ * socket (IPv4 or IPv6), this object will be empty.
+ *
+ * @param sa the sockaddr structure
+ * @param len the structure's length
+ */
+ KInetSocketAddress(const sockaddr* sa, Q_UINT16 len);
+
+ /**
+ * Creates an object from an IP address and port.
+ *
+ * @param host the IP address
+ * @param port the port number
+ */
+ KInetSocketAddress(const KIpAddress& host, Q_UINT16 port);
+
+ /**
+ * Copy constructor.
+ *
+ * Data is not shared.
+ *
+ * @param other the other object
+ */
+ KInetSocketAddress(const KInetSocketAddress& other);
+
+ /**
+ * Copy constructor.
+ *
+ * If the other, generic socket address contains an Internet address,
+ * it will be copied. Otherwise, this object will be empty.
+ *
+ * @param other the other object
+ */
+ KInetSocketAddress(const KSocketAddress& other);
+
+ /**
+ * Destroys this object.
+ */
+ virtual ~KInetSocketAddress();
+
+ /**
+ * Copy operator.
+ *
+ * Copies the other object into this one.
+ *
+ * @param other the other object
+ */
+ KInetSocketAddress& operator =(const KInetSocketAddress& other);
+
+ /**
+ * Cast operator to sockaddr_in.
+ */
+ inline operator const sockaddr_in*() const
+ { return (const sockaddr_in*)address(); }
+
+ /**
+ * Cast operator to sockaddr_in6.
+ */
+ inline operator const sockaddr_in6*() const
+ { return (const sockaddr_in6*)address(); }
+
+ /**
+ * Returns the IP version of the address this object holds.
+ *
+ * @return 4 or 6, if IPv4 or IPv6, respectively; 0 if this object is empty
+ */
+ int ipVersion() const;
+
+ /**
+ * Returns the IP address component.
+ */
+ KIpAddress ipAddress() const;
+
+ /**
+ * Sets the IP address to the given raw address.
+ *
+ * This call will preserve port numbers accross IP versions, but will lose
+ * IPv6 specific data if the address is set to IPv4.
+ *
+ * @param addr the address to set to
+ * @return a reference to itself
+ */
+ KInetSocketAddress& setHost(const KIpAddress& addr);
+
+ /**
+ * Retrieves the port number stored in this object.
+ *
+ * @return a port number in the range 0 to 65535, inclusive. An empty or
+ * invalid object will have a port number of 0.
+ */
+ Q_UINT16 port() const;
+
+ /**
+ * Sets the port number. If this object is empty, this function will default to
+ * creating an IPv4 address.
+ *
+ * @param port the port number to set
+ * @return a reference to itself
+ */
+ KInetSocketAddress& setPort(Q_UINT16 port);
+
+ /**
+ * Converts this object to an IPv4 socket address. It has no effect if the object
+ * is already an IPv4 socket address.
+ *
+ * If this object is an IPv6 address, the port number is preserved. All other information
+ * is lost.
+ *
+ * @return a reference to itself
+ */
+ KInetSocketAddress& makeIPv4();
+
+ /**
+ * Converts this object to an IPv6 socket address. It has no effect if the object
+ * is already an IPv6 socket address.
+ *
+ * If this object is an IPv4 address, the port number is preserved.
+ *
+ * @return a reference to itself
+ */
+ KInetSocketAddress& makeIPv6();
+
+ /**
+ * Returns the flowinfo information from the IPv6 socket address.
+ *
+ * @return the flowinfo information or 0 if this object is empty or IPv4
+ */
+ Q_UINT32 flowinfo() const;
+
+ /**
+ * Sets the flowinfo information for an IPv6 socket address. If this is not
+ * an IPv6 socket address, this function converts it to one. See makeIPv6.
+ *
+ * @param flowinfo the flowinfo to set
+ * @return a reference to itself
+ */
+ KInetSocketAddress& setFlowinfo(Q_UINT32 flowinfo);
+
+ /**
+ * Returns the scope id this IPv6 socket is bound to.
+ *
+ * @return the scope id, or 0 if this is not an IPv6 object
+ */
+ int scopeId() const;
+
+ /**
+ * Sets the scope id for this IPv6 object. If this is not an IPv6 socket
+ * address, this function converts it to one. See makeIPv6
+ *
+ * @param scopeid the scopeid to set
+ * @return a reference to itself
+ */
+ KInetSocketAddress& setScopeId(int scopeid);
+
+protected:
+ /// @internal
+ /// extra constructor
+ KInetSocketAddress(KSocketAddressData* d);
+
+private:
+ void update();
+};
+
+/*
+ * External definition
+ */
+
+/** @class KUnixSocketAddress ksocketaddress.h ksocketaddress.h
+ * @brief A Unix (local) socket address.
+ *
+ * This is a Unix socket address.
+ *
+ * Note that this class uses QStrings to represent filenames, which means
+ * the proper encoding is used to translate into valid filesystem file names.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KUnixSocketAddress: public KSocketAddress
+{
+ friend class KSocketAddress;
+public:
+ /**
+ * Default constructor. Creates an empty object.
+ */
+ KUnixSocketAddress();
+
+ /**
+ * Creates this object with the given raw data. If
+ * the sockaddr structure does not contain a Local namespace
+ * (Unix) socket, this object will be created empty.
+ *
+ * @param sa the socket address structure
+ * @param len the structure's length
+ */
+ KUnixSocketAddress(const sockaddr* sa, Q_UINT16 len);
+
+ /**
+ * Copy constructor. Creates a copy of the other object,
+ * sharing the data explicitly.
+ *
+ * @param other the other object
+ */
+ KUnixSocketAddress(const KUnixSocketAddress& other);
+
+ /**
+ * Constructs an object from the given pathname.
+ */
+ KUnixSocketAddress(const QString& pathname);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KUnixSocketAddress();
+
+ /**
+ * Copy operator. Copies the contents of the other object into
+ * this one. Data is explicitly shared.
+ *
+ * @param other the other
+ */
+ KUnixSocketAddress& operator =(const KUnixSocketAddress& other);
+
+ /**
+ * Cast operator to sockaddr_un.
+ */
+ inline operator const sockaddr_un*() const
+ { return (const sockaddr_un*)address(); }
+
+ /**
+ * Returns the pathname associated with this object. Will return
+ * QString::null if this object is empty.
+ */
+ QString pathname() const;
+
+ /**
+ * Sets the pathname for the object.
+ *
+ * @return a reference to itself
+ */
+ KUnixSocketAddress& setPathname(const QString& path);
+
+protected:
+ /// @internal
+ /// extra constructor
+ KUnixSocketAddress(KSocketAddressData* d);
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/ksocketbase.cpp b/kdecore/network/ksocketbase.cpp
new file mode 100644
index 000000000..f3bfa766c
--- /dev/null
+++ b/kdecore/network/ksocketbase.cpp
@@ -0,0 +1,327 @@
+/* -*- 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 <qmutex.h>
+#include "klocale.h"
+
+#include "ksocketbase.h"
+#include "ksocketdevice.h"
+
+using namespace KNetwork;
+
+class KNetwork::KSocketBasePrivate
+{
+public:
+ int socketOptions;
+ int socketError;
+ int capabilities;
+
+ mutable KSocketDevice* device;
+
+ QMutex mutex;
+
+ KSocketBasePrivate()
+ : mutex(true) // create recursive
+ { }
+};
+
+KSocketBase::KSocketBase()
+ : d(new KSocketBasePrivate)
+{
+ d->socketOptions = Blocking;
+ d->socketError = 0;
+ d->device = 0L;
+ d->capabilities = 0;
+}
+
+KSocketBase::~KSocketBase()
+{
+ delete d->device;
+ delete d;
+}
+
+bool KSocketBase::setSocketOptions(int opts)
+{
+ d->socketOptions = opts;
+ return true;
+}
+
+int KSocketBase::socketOptions() const
+{
+ return d->socketOptions;
+}
+
+bool KSocketBase::setBlocking(bool enable)
+{
+ return setSocketOptions((socketOptions() & ~Blocking) | (enable ? Blocking : 0));
+}
+
+bool KSocketBase::blocking() const
+{
+ return socketOptions() & Blocking;
+}
+
+bool KSocketBase::setAddressReuseable(bool enable)
+{
+ return setSocketOptions((socketOptions() & ~AddressReuseable) | (enable ? AddressReuseable : 0));
+}
+
+bool KSocketBase::addressReuseable() const
+{
+ return socketOptions() & AddressReuseable;
+}
+
+bool KSocketBase::setIPv6Only(bool enable)
+{
+ return setSocketOptions((socketOptions() & ~IPv6Only) | (enable ? IPv6Only : 0));
+}
+
+bool KSocketBase::isIPv6Only() const
+{
+ return socketOptions() & IPv6Only;
+}
+
+bool KSocketBase::setBroadcast(bool enable)
+{
+ return setSocketOptions((socketOptions() & ~Broadcast) | (enable ? Broadcast : 0));
+}
+
+bool KSocketBase::broadcast() const
+{
+ return socketOptions() & Broadcast;
+}
+
+KSocketDevice* KSocketBase::socketDevice() const
+{
+ if (d->device)
+ return d->device;
+
+ // it doesn't exist, so create it
+ QMutexLocker locker(mutex());
+ if (d->device)
+ return d->device;
+
+ KSocketBase* that = const_cast<KSocketBase*>(this);
+ KSocketDevice* dev = 0;
+ if (d->capabilities)
+ dev = KSocketDevice::createDefault(that, d->capabilities);
+ if (!dev)
+ dev = KSocketDevice::createDefault(that);
+ that->setSocketDevice(dev);
+ return d->device;
+}
+
+void KSocketBase::setSocketDevice(KSocketDevice* device)
+{
+ QMutexLocker locker(mutex());
+ if (d->device == 0L)
+ d->device = device;
+}
+
+int KSocketBase::setRequestedCapabilities(int add, int remove)
+{
+ d->capabilities |= add;
+ d->capabilities &= ~remove;
+ return d->capabilities;
+}
+
+bool KSocketBase::hasDevice() const
+{
+ return d->device != 0L;
+}
+
+void KSocketBase::setError(SocketError error)
+{
+ d->socketError = error;
+}
+
+KSocketBase::SocketError KSocketBase::error() const
+{
+ return static_cast<KSocketBase::SocketError>(d->socketError);
+}
+
+// static
+QString KSocketBase::errorString(KSocketBase::SocketError code)
+{
+ QString reason;
+ switch (code)
+ {
+ case NoError:
+ reason = i18n("Socket error code NoError", "no error");
+ break;
+
+ case LookupFailure:
+ reason = i18n("Socket error code LookupFailure",
+ "name lookup has failed");
+ break;
+
+ case AddressInUse:
+ reason = i18n("Socket error code AddressInUse",
+ "address already in use");
+ break;
+
+ case AlreadyBound:
+ reason = i18n("Socket error code AlreadyBound",
+ "socket is already bound");
+ break;
+
+ case AlreadyCreated:
+ reason = i18n("Socket error code AlreadyCreated",
+ "socket is already created");
+ break;
+
+ case NotBound:
+ reason = i18n("Socket error code NotBound",
+ "socket is not bound");
+ break;
+
+ case NotCreated:
+ reason = i18n("Socket error code NotCreated",
+ "socket has not been created");
+ break;
+
+ case WouldBlock:
+ reason = i18n("Socket error code WouldBlock",
+ "operation would block");
+ break;
+
+ case ConnectionRefused:
+ reason = i18n("Socket error code ConnectionRefused",
+ "connection actively refused");
+ break;
+
+ case ConnectionTimedOut:
+ reason = i18n("Socket error code ConnectionTimedOut",
+ "connection timed out");
+ break;
+
+ case InProgress:
+ reason = i18n("Socket error code InProgress",
+ "operation is already in progress");
+ break;
+
+ case NetFailure:
+ reason = i18n("Socket error code NetFailure",
+ "network failure occurred");
+ break;
+
+ case NotSupported:
+ reason = i18n("Socket error code NotSupported",
+ "operation is not supported");
+ break;
+
+ case Timeout:
+ reason = i18n("Socket error code Timeout",
+ "timed operation timed out");
+ break;
+
+ case UnknownError:
+ reason = i18n("Socket error code UnknownError",
+ "an unknown/unexpected error has happened");
+ break;
+
+ case RemotelyDisconnected:
+ reason = i18n("Socket error code RemotelyDisconnected",
+ "remote host closed connection");
+ break;
+
+ default:
+ reason = QString::null;
+ break;
+ }
+
+ return reason;
+}
+
+// static
+bool KSocketBase::isFatalError(int code)
+{
+ switch (code)
+ {
+ case WouldBlock:
+ case InProgress:
+ case NoError:
+ case RemotelyDisconnected:
+ return false;
+ }
+
+ return true;
+}
+
+void KSocketBase::unsetSocketDevice()
+{
+ d->device = 0L;
+}
+
+QMutex* KSocketBase::mutex() const
+{
+ return &d->mutex;
+}
+
+KActiveSocketBase::KActiveSocketBase()
+{
+}
+
+KActiveSocketBase::~KActiveSocketBase()
+{
+}
+
+int KActiveSocketBase::getch()
+{
+ unsigned char c;
+ if (readBlock((char*)&c, 1) != 1)
+ return -1;
+
+ return c;
+}
+
+int KActiveSocketBase::putch(int ch)
+{
+ unsigned char c = (unsigned char)ch;
+ if (writeBlock((char*)&c, 1) != 1)
+ return -1;
+
+ return c;
+}
+
+void KActiveSocketBase::setError(int status, SocketError error)
+{
+ KSocketBase::setError(error);
+ setStatus(status);
+}
+
+void KActiveSocketBase::resetError()
+{
+ KSocketBase::setError(NoError);
+ resetStatus();
+}
+
+KPassiveSocketBase::KPassiveSocketBase()
+{
+}
+
+KPassiveSocketBase::~KPassiveSocketBase()
+{
+}
diff --git a/kdecore/network/ksocketbase.h b/kdecore/network/ksocketbase.h
new file mode 100644
index 000000000..acbbdf9b9
--- /dev/null
+++ b/kdecore/network/ksocketbase.h
@@ -0,0 +1,762 @@
+/* -*- 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.
+ */
+
+/*
+ * Even before our #ifdef, clean up the namespace
+ */
+#ifdef socket
+#undef socket
+#endif
+
+#ifdef bind
+#undef bind
+#endif
+
+#ifdef listen
+#undef listen
+#endif
+
+#ifdef connect
+#undef connect
+#endif
+
+#ifdef accept
+#undef accept
+#endif
+
+#ifdef getpeername
+#undef getpeername
+#endif
+
+#ifdef getsockname
+#undef getsockname
+#endif
+
+#ifndef KSOCKETBASE_H
+#define KSOCKETBASE_H
+
+#include <qiodevice.h>
+#include <qstring.h>
+
+#include "ksocketaddress.h"
+#include <kdelibs_export.h>
+
+/*
+ * This is extending QIODevice's error codes
+ *
+ * According to qiodevice.h, the last error is IO_UnspecifiedError
+ * These errors will never occur in functions declared in QIODevice
+ * (except open, but you shouldn't call open)
+ */
+#define IO_ListenError (IO_UnspecifiedError+1)
+#define IO_AcceptError (IO_UnspecifiedError+2)
+#define IO_LookupError (IO_UnspecifiedError+3)
+#define IO_SocketCreateError (IO_UnspecifiedError+4)
+#define IO_BindError (IO_UnspecifiedError+5)
+
+class QMutex;
+
+namespace KNetwork {
+
+class KResolverEntry;
+class KSocketDevice;
+
+class KSocketBasePrivate;
+/** @class KSocketBase ksocketbase.h ksocketbase.h
+ * @brief Basic socket functionality.
+ *
+ * This class provides the basic socket functionlity for descended classes.
+ * Socket classes are thread-safe and provide a recursive mutex should it be
+ * needed.
+ *
+ * @note This class is abstract.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KSocketBase
+{
+public:
+ /**
+ * Possible socket options.
+ *
+ * These are the options that may be set on a socket:
+ * - Blocking: whether the socket shall operate in blocking
+ * or non-blocking mode. This flag defaults to on.
+ * See @ref setBlocking.
+ * - AddressReusable: whether the address used by this socket will
+ * be available for reuse by other sockets. This flag defaults to off.
+ * See @ref setAddressReuseable.
+ * - IPv6Only: whether an IPv6 socket will accept IPv4 connections
+ * through a mapped address. This flag defaults to off.
+ * See @ref setIPv6Only.
+ * - KeepAlive: whether TCP should send keepalive probes when a connection
+ * has gone idle for far too long.
+ * - Broadcast: whether this socket is allowed to send broadcast packets
+ * and will receive packets sent to broadcast.
+ */
+ enum SocketOptions
+ {
+ Blocking = 0x01,
+ AddressReuseable = 0x02,
+ IPv6Only = 0x04,
+ Keepalive = 0x08,
+ Broadcast = 0x10
+ };
+
+ /**
+ * Possible socket error codes.
+ *
+ * This is a list of possible error conditions that socket classes may
+ * be expected to find.
+ *
+ * - NoError: no error has been detected
+ * - LookupFailure: if a name lookup has failed
+ * - AddressInUse: address is already in use
+ * - AlreadyBound: cannot bind again
+ * - AlreadyCreated: cannot recreate the socket
+ * - NotBound: operation required socket to be bound and it isn't
+ * - NotCreated: operation required socket to exist and it doesn't
+ * - WouldBlock: requested I/O operation would block
+ * - ConnectionRefused: connection actively refused
+ * - ConnectionTimedOut: connection timed out
+ * - InProgress: operation (connection) is already in progress
+ * - NetFailure: a network failure occurred (no route, host down, host unreachable or similar)
+ * - NotSupported: requested operation is not supported
+ * - Timeout: a timed operation timed out
+ * - UnknownError: an unknown/unexpected error has happened
+ * - RemotelyDisconnected: when a connection is disconnected by the other end (since 3.4)
+ *
+ * @sa error, errorString
+ */
+ enum SocketError
+ {
+ NoError = 0,
+ LookupFailure,
+ AddressInUse,
+ AlreadyCreated,
+ AlreadyBound,
+ AlreadyConnected,
+ NotConnected,
+ NotBound,
+ NotCreated,
+ WouldBlock,
+ ConnectionRefused,
+ ConnectionTimedOut,
+ InProgress,
+ NetFailure,
+ NotSupported,
+ Timeout,
+ UnknownError,
+ RemotelyDisconnected
+ };
+
+public:
+ /**
+ * Default constructor.
+ */
+ KSocketBase();
+
+ /**
+ * Destructor.
+ */
+ virtual ~KSocketBase();
+
+ /*
+ * The following functions are shared by all descended classes and will have
+ * to be reimplemented.
+ */
+
+protected:
+ /**
+ * Set the given socket options.
+ *
+ * The default implementation does nothing but store the mask internally.
+ * Descended classes must override this function to achieve functionality and
+ * must also call this implementation.
+ *
+ * @param opts a mask of @ref SocketOptions or-ed bits of options to set
+ * or unset
+ * @returns true on success
+ * @note this function sets the options corresponding to the bits enabled in @p opts
+ * but will also unset the optiosn corresponding to the bits not set.
+ */
+ virtual bool setSocketOptions(int opts);
+
+ /**
+ * Retrieves the socket options that have been set.
+ *
+ * The default implementation just retrieves the mask from an internal variable.
+ * Descended classes may choose to override this function to read the values
+ * from the operating system.
+ *
+ * @returns the mask of the options set
+ */
+ virtual int socketOptions() const;
+
+public:
+ /**
+ * Sets this socket's blocking mode.
+ *
+ * In blocking operation, all I/O functions are susceptible to blocking --
+ * i.e., will not return unless the I/O can be satisfied. In non-blocking
+ * operation, if the I/O would block, the function will return an error
+ * and set the corresponding error code.
+ *
+ * The default implementation toggles the Blocking flag with the current
+ * socket options and calls @ref setSocketOptions.
+ *
+ * @param enable whether to set this socket to blocking mode
+ * @returns whether setting this value was successful; it is NOT the
+ * final blocking mode.
+ */
+ virtual bool setBlocking(bool enable);
+
+ /**
+ * Retrieves this socket's blocking mode.
+ *
+ * @returns true if this socket is/will be operated in blocking mode,
+ * false if non-blocking.
+ */
+ bool blocking() const;
+
+ /**
+ * Sets this socket's address reuseable flag.
+ *
+ * When the address reuseable flag is active, the address used by
+ * this socket is left reuseable for other sockets to bind. If
+ * the flag is not active, no other sockets may reuse the same
+ * address.
+ *
+ * The default implementation toggles the AddressReuseable flag with the current
+ * socket options and calls @ref setSocketOptions.
+ *
+ * @param enable whether to set the flag on or off
+ * @returns true if setting this flag was successful
+ */
+ virtual bool setAddressReuseable(bool enable);
+
+ /**
+ * Retrieves this socket's address reuseability flag.
+ *
+ * @returns true if this socket's address can be reused,
+ * false if it can't.
+ */
+ bool addressReuseable() const;
+
+ /**
+ * Sets this socket's IPv6 Only flag.
+ *
+ * When this flag is on, an IPv6 socket will only accept, connect, send to or
+ * receive from IPv6 addresses. When it is off, it will also talk to
+ * IPv4 addresses through v4-mapped addresses.
+ *
+ * This option has no effect on non-IPv6 sockets.
+ *
+ * The default implementation toggles the IPv6Only flag with the current
+ * socket options and calls @ref setSocketOptions.
+ *
+ * @param enable whether to set the flag on or off
+ * @returns true if setting this flag was successful
+ */
+ virtual bool setIPv6Only(bool enable);
+
+ /**
+ * Retrieves this socket's IPv6 Only flag.
+ *
+ * @returns true if this socket will ignore IPv4-compatible and IPv4-mapped
+ * addresses, false if it will accept them.
+ */
+ bool isIPv6Only() const;
+
+ /**
+ * Sets this socket Broadcast flag.
+ *
+ * Datagram-oriented sockets cannot normally send packets to broadcast
+ * addresses, nor will they receive packets that were sent to a broadcast
+ * address. To do so, you need to enable the Broadcast flag.
+ *
+ * This option has no effect on stream-oriented sockets.
+ *
+ * @returns true if setting this flag was successful.
+ */
+ virtual bool setBroadcast(bool enable);
+
+ /**
+ * Retrieves this socket's Broadcast flag.
+ *
+ * @returns true if this socket can send and receive broadcast packets,
+ * false if it can't.
+ */
+ bool broadcast() const;
+
+ /**
+ * Retrieves the socket implementation used on this socket.
+ *
+ * This function creates the device if none has been set
+ * using the default factory.
+ */
+ KSocketDevice* socketDevice() const;
+
+ /**
+ * Sets the socket implementation to be used on this socket.
+ *
+ * Note: it is an error to set this if the socket device has
+ * already been set once.
+ *
+ * This function is provided virtual so that derived classes can catch
+ * the setting of a device and properly set their own states and internal
+ * variables. The parent class must be called.
+ *
+ * This function is called by @ref socketDevice above when the socket is
+ * first created.
+ */
+ virtual void setSocketDevice(KSocketDevice* device);
+
+ /**
+ * Sets the internally requested capabilities for a socket device.
+ *
+ * Most socket classes can use any back-end implementation. However, a few
+ * may require specific capabilities not provided in the default
+ * implementation. By using this function, derived classes can request
+ * that a backend with those capabilities be created when necessary.
+ *
+ * For the possible flags, see @ref KSocketDevice::Capabilities. However, note
+ * that only the Can* flags make sense in this context.
+ *
+ * @note Since socketDevice must always return a valid backend object, it
+ * is is possible that the created device does not conform to all
+ * requirements requested. Implementations sensitive to this fact
+ * should test the object returned by @ref socketDevice (through
+ * @ref KSocketDevice::capabilities, for instance) the availability.
+ *
+ * @param add mask of @ref KSocketDevice::Capabilities to add
+ * @param remove mask of bits to remove from the requirements
+ * @return the current mask of requested capabilities
+ */
+ int setRequestedCapabilities(int add, int remove = 0);
+
+protected:
+ /**
+ * Returns true if the socket device has been initialised in this
+ * object, either by calling @ref socketDevice() or @ref setSocketDevice
+ */
+ bool hasDevice() const;
+
+ /**
+ * Sets the socket's error code.
+ *
+ * @param error the error code
+ */
+ void setError(SocketError error);
+
+public:
+ /**
+ * Retrieves the socket error code.
+ * @sa errorString
+ */
+ SocketError error() const;
+
+ /**
+ * Returns the error string corresponding to this error condition.
+ */
+ inline QString errorString() const
+ { return errorString(error()); }
+
+ /**
+ * Returns the internal mutex for this class.
+ *
+ * Note on multithreaded use of sockets:
+ * the socket classes are thread-safe by design, but you should be aware of
+ * problems regarding socket creation, connection and destruction in
+ * multi-threaded programs. The classes are guaranteed to work while
+ * the socket exists, but it's not wise to call connect in multiple
+ * threads.
+ *
+ * Also, this mutex must be unlocked before the object is destroyed, which
+ * means you cannot use it to guard against other threads accessing the object
+ * while destroying it. You must ensure there are no further references to this
+ * object when deleting it.
+ */
+ QMutex* mutex() const;
+
+public:
+ /**
+ * Returns the string describing the given error code, i18n'ed.
+ *
+ * @param code the error code
+ */
+ static QString errorString(SocketError code);
+
+ /**
+ * Returns true if the given error code is a fatal one, false
+ * otherwise. The parameter here is of type int so that
+ * casting isn't necessary when using the parameter to signal
+ * QClientSocketBase::gotError.
+ *
+ * @param code the code to test
+ */
+ static bool isFatalError(int code);
+
+private:
+ /// @internal
+ /// called by KSocketDevice
+ void unsetSocketDevice();
+
+ KSocketBase(const KSocketBase&);
+ KSocketBase& operator =(const KSocketBase&);
+
+ KSocketBasePrivate *d;
+
+ friend class KSocketDevice;
+};
+
+/**
+ * @class KActiveSocketBase ksocketbase.h ksocketbase.h
+ * @brief Abstract class for active sockets
+ *
+ * This class provides the standard interfaces for active sockets, i.e.,
+ * sockets that are used to connect to external addresses.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KActiveSocketBase: public QIODevice, virtual public KSocketBase
+{
+public:
+ /**
+ * Constructor.
+ */
+ KActiveSocketBase();
+
+ /**
+ * Destructor.
+ */
+ virtual ~KActiveSocketBase();
+
+ /**
+ * Binds this socket to the given address.
+ *
+ * The socket will be constructed with the address family,
+ * socket type and protocol as those given in the
+ * @p address object.
+ *
+ * @param address the address to bind to
+ * @returns true if the binding was successful, false otherwise
+ */
+ virtual bool bind(const KResolverEntry& address) = 0;
+
+ /**
+ * Connect to a remote host.
+ *
+ * This will make this socket try to connect to the remote host.
+ * If the socket is not yet created, it will be created using the
+ * address family, socket type and protocol specified in the
+ * @p address object.
+ *
+ * If this function returns with error InProgress, calling it
+ * again with the same address after a time will cause it to test
+ * if the connection has succeeded in the mean time.
+ *
+ * @param address the address to connect to
+ * @returns true if the connection was successful or has been successfully
+ * queued; false if an error occurred.
+ */
+ virtual bool connect(const KResolverEntry& address) = 0;
+
+ /**
+ * Disconnects this socket from a connection, if possible.
+ *
+ * If this socket was connected to an endpoint, the connection
+ * is severed, but the socket is not closed. If the socket wasn't
+ * connected, this function does nothing.
+ *
+ * If the socket hadn't yet been created, this function does nothing
+ * either.
+ *
+ * Not all socket types can disconnect. Most notably, only
+ * connectionless datagram protocols such as UDP support this operation.
+ *
+ * @return true if the socket is now disconnected or false on error.
+ */
+ virtual bool disconnect() = 0;
+
+ /**
+ * This call is not supported on sockets. Reimplemented from QIODevice.
+ * This will always return 0.
+ */
+ virtual Offset size() const
+ { return 0; }
+
+ /**
+ * This call is not supported on sockets. Reimplemented from QIODevice.
+ * This will always return 0.
+ */
+ virtual Offset at() const
+ { return 0; }
+
+ /**
+ * This call is not supported on sockets. Reimplemented from QIODevice.
+ * This will always return false.
+ */
+ virtual bool at(Offset)
+ { return false; }
+
+ /**
+ * This call is not supported on sockets. Reimplemented from QIODevice.
+ * This will always return true.
+ */
+ virtual bool atEnd() const
+ { return true; }
+
+ /**
+ * Returns the number of bytes available for reading without
+ * blocking.
+ */
+ virtual Q_LONG bytesAvailable() const = 0;
+
+ /**
+ * Waits up to @p msecs for more data to be available on this socket.
+ *
+ * If msecs is -1, this call will block indefinetely until more data
+ * is indeed available; if it's 0, this function returns immediately.
+ *
+ * If @p timeout is not NULL, this function will set it to indicate
+ * if a timeout occurred.
+ *
+ * @returns the number of bytes available
+ */
+ virtual Q_LONG waitForMore(int msecs, bool *timeout = 0L) = 0;
+
+ /**
+ * Reads data from the socket.
+ *
+ * Reimplemented from QIODevice. See QIODevice::readBlock for
+ * more information.
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG len) = 0;
+
+ /** @overload
+ * Receives data and the source address.
+ *
+ * This call will read data in the socket and will also
+ * place the sender's address in @p from object.
+ *
+ * @param data where to write the read data to
+ * @param maxlen the maximum number of bytes to read
+ * @param from the address of the sender will be stored here
+ * @returns the actual number of bytes read
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen, KSocketAddress& from) = 0;
+
+ /**
+ * Peeks the data in the socket.
+ *
+ * This call will allow you to peek the data to be received without actually
+ * receiving it -- that is, it will be available for further peekings and
+ * for the next read call.
+ *
+ * @param data where to write the peeked data to
+ * @param maxlen the maximum number of bytes to peek
+ * @returns the actual number of bytes copied into @p data
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen) = 0;
+
+ /** @overload
+ * Peeks the data in the socket and the source address.
+ *
+ * This call will allow you to peek the data to be received without actually
+ * receiving it -- that is, it will be available for further peekings and
+ * for the next read call.
+ *
+ * @param data where to write the peeked data to
+ * @param maxlen the maximum number of bytes to peek
+ * @param from the address of the sender will be stored here
+ * @returns the actual number of bytes copied into @p data
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen, KSocketAddress& from) = 0;
+
+ /**
+ * Writes the given data to the socket.
+ *
+ * Reimplemented from QIODevice. See QIODevice::writeBlock for
+ * more information.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len) = 0;
+
+ /** @overload
+ * Writes the given data to the destination address.
+ *
+ * Note that not all socket connections allow sending data to different
+ * addresses than the one the socket is connected to.
+ *
+ * @param data the data to write
+ * @param len the length of the data
+ * @param to the address to send to
+ * @returns the number of bytes actually sent
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to) = 0;
+
+ /**
+ * Reads one character from the socket.
+ * Reimplementation from QIODevice. See QIODevice::getch for more information.
+ */
+ virtual int getch();
+
+ /**
+ * Writes one character to the socket.
+ * Reimplementation from QIODevice. See QIODevice::putch for more information.
+ */
+ virtual int putch(int ch);
+
+ /**
+ * This call is not supported on sockets. Reimplemented from QIODevice.
+ * This will always return -1;
+ */
+ virtual int ungetch(int)
+ { return -1; }
+
+ /**
+ * Returns this socket's local address.
+ */
+ virtual KSocketAddress localAddress() const = 0;
+
+ /**
+ * Return this socket's peer address, if we are connected.
+ * If the address cannot be retrieved, the returned object will contain
+ * an invalid address.
+ */
+ virtual KSocketAddress peerAddress() const = 0;
+
+ // FIXME KDE 4.0:
+ // enable this function
+#if 0
+ /**
+ * Returns this socket's externally-visible address, if known.
+ */
+ virtual KSocketAddress externalAddress() const = 0;
+#endif
+
+protected:
+ /**
+ * Sets the socket's error code and the I/O Device's status.
+ *
+ * @param status the I/O Device status
+ * @param error the error code
+ */
+ void setError(int status, SocketError error);
+
+ /**
+ * Resets the socket error code and the I/O Device's status.
+ */
+ void resetError();
+};
+
+/**
+ * @class KPassiveSocketBase ksocketbase.h ksocketbase.h
+ * @brief Abstract base class for passive sockets.
+ *
+ * This socket provides the initial functionality for passive sockets,
+ * i.e., sockets that accept incoming connections.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KPassiveSocketBase: virtual public KSocketBase
+{
+public:
+ /**
+ * Constructor
+ */
+ KPassiveSocketBase();
+
+ /**
+ * Destructor
+ */
+ virtual ~KPassiveSocketBase();
+
+ /**
+ * Binds this socket to the given address.
+ *
+ * The socket will be constructed with the address family,
+ * socket type and protocol as those given in the
+ * @p address object.
+ *
+ * @param address the address to bind to
+ * @returns true if the binding was successful, false otherwise
+ */
+ virtual bool bind(const KResolverEntry& address) = 0;
+
+ /**
+ * Puts this socket into listening mode.
+ *
+ * Placing a socket in listening mode means that it will
+ * be allowed to receive incoming connections from
+ * remote hosts.
+ *
+ * Note that some socket types or protocols cannot be
+ * put in listening mode.
+ *
+ * @param backlog the number of accepted connections to
+ * hold before starting to refuse
+ * @returns true if the socket is now in listening mode
+ */
+ virtual bool listen(int backlog) = 0;
+
+ /**
+ * Closes this socket. All resources used are freed. Note that closing
+ * a passive socket does not close the connections accepted with it.
+ */
+ virtual void close() = 0;
+
+ /**
+ * Accepts a new incoming connection.
+ *
+ * If this socket was in listening mode, you can call this
+ * function to accept an incoming connection.
+ *
+ * If this function cannot accept a new connection (either
+ * because it is not listening for one or because the operation
+ * would block), it will return NULL.
+ *
+ * Also note that descended classes will override this function
+ * to return specialised socket classes.
+ */
+ virtual KActiveSocketBase* accept() = 0;
+
+ /**
+ * Returns this socket's local address.
+ */
+ virtual KSocketAddress localAddress() const = 0;
+
+ /**
+ * Returns this socket's externally-visible address if known.
+ */
+ virtual KSocketAddress externalAddress() const = 0;
+
+private:
+ KPassiveSocketBase(const KPassiveSocketBase&);
+ KPassiveSocketBase& operator = (const KPassiveSocketBase&);
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/ksocketbuffer.cpp b/kdecore/network/ksocketbuffer.cpp
new file mode 100644
index 000000000..d458a4f15
--- /dev/null
+++ b/kdecore/network/ksocketbuffer.cpp
@@ -0,0 +1,329 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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 <assert.h>
+#include <string.h>
+
+#include "ksocketbase.h"
+#include "ksocketbuffer_p.h"
+
+using namespace KNetwork;
+using namespace KNetwork::Internal;
+
+KSocketBuffer::KSocketBuffer(Q_LONG size)
+ : m_mutex(true), m_offset(0), m_size(size), m_length(0)
+{
+}
+
+KSocketBuffer::KSocketBuffer(const KSocketBuffer& other)
+ : KIOBufferBase(other), m_mutex(true)
+{
+ *this = other;
+}
+
+KSocketBuffer::~KSocketBuffer()
+{
+ // QValueList takes care of deallocating memory
+}
+
+KSocketBuffer& KSocketBuffer::operator=(const KSocketBuffer& other)
+{
+ QMutexLocker locker1(&m_mutex);
+ QMutexLocker locker2(&other.m_mutex);
+
+ KIOBufferBase::operator=(other);
+
+ m_list = other.m_list; // copy-on-write
+ m_offset = other.m_offset;
+ m_size = other.m_size;
+ m_length = other.m_length;
+
+ return *this;
+}
+
+bool KSocketBuffer::canReadLine() const
+{
+ QMutexLocker locker(&m_mutex);
+
+ QValueListConstIterator<QByteArray> it = m_list.constBegin(),
+ end = m_list.constEnd();
+ QIODevice::Offset offset = m_offset;
+
+ // walk the buffer
+ for ( ; it != end; ++it)
+ {
+ if ((*it).find('\n', offset) != -1)
+ return true;
+ if ((*it).find('\r', offset) != -1)
+ return true;
+ offset = 0;
+ }
+
+ return false; // not found
+}
+
+QCString KSocketBuffer::readLine()
+{
+ if (!canReadLine())
+ return QCString(); // empty
+
+ QMutexLocker locker(&m_mutex);
+
+ // find the offset of the newline in the buffer
+ int newline = 0;
+ QValueListConstIterator<QByteArray> it = m_list.constBegin(),
+ end = m_list.constEnd();
+ QIODevice::Offset offset = m_offset;
+
+ // walk the buffer
+ for ( ; it != end; ++it)
+ {
+ int posnl = (*it).find('\n', offset);
+ if (posnl == -1)
+ {
+ // not found in this one
+ newline += (*it).size();
+ offset = 0;
+ continue;
+ }
+
+ // we found it
+ newline += posnl;
+ break;
+ }
+
+ QCString result(newline + 2 - m_offset);
+ consumeBuffer(result.data(), newline + 1 - m_offset);
+ return result;
+}
+
+Q_LONG KSocketBuffer::length() const
+{
+ return m_length;
+}
+
+Q_LONG KSocketBuffer::size() const
+{
+ return m_size;
+}
+
+bool KSocketBuffer::setSize(Q_LONG size)
+{
+ m_size = size;
+ if (size == -1 || m_length < m_size)
+ return true;
+
+ // size is now smaller than length
+ QMutexLocker locker(&m_mutex);
+
+ // repeat the test
+ if (m_length < m_size)
+ return true;
+
+ // discard from the beginning
+ return (m_length - m_size) == consumeBuffer(0L, m_length - m_size, true);
+}
+
+Q_LONG KSocketBuffer::feedBuffer(const char *data, Q_LONG len)
+{
+ if (data == 0L || len == 0)
+ return 0; // nothing to write
+ if (isFull())
+ return -1; // can't write
+
+ QMutexLocker locker(&m_mutex);
+
+ // verify if we can add len bytes
+ if (m_size != -1 && (m_size - m_length) < len)
+ len = m_size - m_length;
+
+ QByteArray a(len);
+ a.duplicate(data, len);
+ m_list.append(a);
+
+ m_length += len;
+ return len;
+}
+
+Q_LONG KSocketBuffer::consumeBuffer(char *destbuffer, Q_LONG maxlen, bool discard)
+{
+ if (maxlen == 0 || isEmpty())
+ return 0;
+
+ QValueListIterator<QByteArray> it = m_list.begin(),
+ end = m_list.end();
+ QIODevice::Offset offset = m_offset;
+ Q_LONG copied = 0;
+
+ // walk the buffer
+ while (it != end && maxlen)
+ {
+ // calculate how much we'll copy
+ size_t to_copy = (*it).size() - offset;
+ if (to_copy > maxlen)
+ to_copy = maxlen;
+
+ // do the copying
+ if (destbuffer)
+ memcpy(destbuffer + copied, (*it).data() + offset, to_copy);
+ maxlen -= to_copy;
+ copied += to_copy;
+
+ if ((*it).size() - offset > to_copy)
+ {
+ // we did not copy everything
+ offset += to_copy;
+ break;
+ }
+ else
+ {
+ // we copied everything
+ // discard this element;
+ offset = 0;
+ if (discard)
+ it = m_list.remove(it);
+ else
+ ++it;
+ }
+ }
+
+ if (discard)
+ {
+ m_offset = offset;
+ m_length -= copied;
+ assert(m_length >= 0);
+ }
+
+ return copied;
+}
+
+void KSocketBuffer::clear()
+{
+ QMutexLocker locker(&m_mutex);
+ m_list.clear();
+ m_offset = 0;
+ m_length = 0;
+}
+
+Q_LONG KSocketBuffer::sendTo(KActiveSocketBase* dev, Q_LONG len)
+{
+ if (len == 0 || isEmpty())
+ return 0;
+
+ QMutexLocker locker(&m_mutex);
+
+ QValueListIterator<QByteArray> it = m_list.begin(),
+ end = m_list.end();
+ QIODevice::Offset offset = m_offset;
+ Q_LONG written = 0;
+
+ // walk the buffer
+ while (it != end && (len || len == -1))
+ {
+ // we have to write each element up to len bytes
+ // but since we can have several very small buffers, we can make things
+ // better by concatenating a few of them into a big buffer
+ // question is: how big should that buffer be? 2 kB should be enough
+
+ Q_ULONG bufsize = 1460;
+ if (len != -1 && len < bufsize)
+ bufsize = len;
+ QByteArray buf(bufsize);
+ Q_LONG count = 0;
+
+ while (it != end && count + ((*it).size() - offset) <= bufsize)
+ {
+ memcpy(buf.data() + count, (*it).data() + offset, (*it).size() - offset);
+ count += (*it).size() - offset;
+ offset = 0;
+ ++it;
+ }
+
+ // see if we can still fit more
+ if (count < bufsize && it != end)
+ {
+ // getting here means this buffer (*it) is larger than
+ // (bufsize - count) (even for count == 0).
+ memcpy(buf.data() + count, (*it).data() + offset, bufsize - count);
+ offset += bufsize - count;
+ count = bufsize;
+ }
+
+ // now try to write those bytes
+ Q_LONG wrote = dev->writeBlock(buf, count);
+
+ if (wrote == -1)
+ // error?
+ break;
+
+ written += wrote;
+ if (wrote != count)
+ // can't fit more?
+ break;
+ }
+
+ // discard data that has been written
+ // this updates m_length too
+ if (written)
+ consumeBuffer(0L, written);
+
+ return written;
+}
+
+Q_LONG KSocketBuffer::receiveFrom(KActiveSocketBase* dev, Q_LONG len)
+{
+ if (len == 0 || isFull())
+ return 0;
+
+ QMutexLocker locker(&m_mutex);
+
+ if (len == -1)
+ len = dev->bytesAvailable();
+ if (len <= 0)
+ // error or closing socket
+ return len;
+
+ // see if we can read that much
+ if (m_size != -1 && len > (m_size - m_length))
+ len = m_size - m_length;
+
+ // here, len contains just as many bytes as we're supposed to read
+
+ // now do the reading
+ QByteArray a(len);
+ len = dev->readBlock(a.data(), len);
+
+ if (len == -1)
+ // error?
+ return -1;
+
+ // success
+ // resize the buffer and add it
+ a.truncate(len);
+ m_list.append(a);
+ m_length += len;
+ return len;
+}
diff --git a/kdecore/network/ksocketbuffer_p.h b/kdecore/network/ksocketbuffer_p.h
new file mode 100644
index 000000000..ddcc692bd
--- /dev/null
+++ b/kdecore/network/ksocketbuffer_p.h
@@ -0,0 +1,164 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KSOCKETBUFFER_P_H
+#define KSOCKETBUFFER_P_H
+
+#include <qmutex.h>
+#include <qcstring.h>
+#include <qvaluelist.h>
+#include "kiobuffer.h"
+
+namespace KNetwork {
+
+class KActiveSocketBase;
+
+ namespace Internal {
+
+/**
+ * @internal
+ * @class KSocketBuffer ksocketbuffer_p.h ksocketbuffer_p.h
+ * @brief generic socket buffering code
+ *
+ * This class implements generic buffering used by @ref KBufferedSocket.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KSocketBuffer: public KIOBufferBase
+{
+public:
+ /**
+ * Default constructor.
+ *
+ * @param size the maximum size of the buffer
+ */
+ KSocketBuffer(Q_LONG size = -1);
+
+ /**
+ * Copy constructor.
+ */
+ KSocketBuffer(const KSocketBuffer& other);
+
+ /**
+ * Virtual destructor. Frees the buffer and discards its contents.
+ */
+ virtual ~KSocketBuffer();
+
+ /**
+ * Assignment operator.
+ */
+ KSocketBuffer& operator=(const KSocketBuffer& other);
+
+ /**
+ * Returns true if a line can be read from the buffer.
+ */
+ virtual bool canReadLine() const;
+
+ /**
+ * Reads a line from the buffer and discard it from the buffer.
+ */
+ virtual QCString readLine();
+
+ /**
+ * Returns the number of bytes in the buffer. Note that this is not
+ * the size of the buffer.
+ *
+ * @sa size
+ */
+ virtual Q_LONG length() const;
+
+ /**
+ * Retrieves the buffer size. The value of -1 indicates that
+ * the buffer has no defined upper limit.
+ *
+ * @sa length for the length of the data stored
+ */
+ virtual Q_LONG size() const;
+
+ /**
+ * Sets the size of the buffer, if allowed.
+ *
+ * @param size the maximum size, use -1 for unlimited.
+ * @returns true on success, false if an error occurred.
+ * @note if the new size is less than length(), the buffer will be truncated
+ */
+ virtual bool setSize(Q_LONG size);
+
+ /**
+ * Adds data to the end of the buffer.
+ *
+ * @param data the data to be added
+ * @param len the data length, in bytes
+ * @returns the number of bytes added to the end of the buffer.
+ */
+ virtual Q_LONG feedBuffer(const char *data, Q_LONG len);
+
+ /**
+ * Clears the buffer.
+ */
+ virtual void clear();
+
+ /**
+ * Consumes data from the beginning of the buffer.
+ *
+ * @param data where to copy the data to
+ * @param maxlen the maximum length to copy, in bytes
+ * @param discard if true, the bytes copied will be discarded
+ * @returns the number of bytes copied from the buffer
+ */
+ virtual Q_LONG consumeBuffer(char *data, Q_LONG maxlen, bool discard = true);
+
+ /**
+ * Sends at most @p len bytes of data to the I/O Device.
+ *
+ * @param device the device to which to send data
+ * @param len the amount of data to send; -1 to send everything
+ * @returns the number of bytes sent and discarded from the buffer, -1
+ * indicates an error.
+ */
+ virtual Q_LONG sendTo(KActiveSocketBase* device, Q_LONG len = -1);
+
+ /**
+ * Tries to receive @p len bytes of data from the I/O device.
+ *
+ * @param device the device to receive from
+ * @param len the number of bytes to receive; -1 to read as much
+ * as possible
+ * @returns the number of bytes received and copied into the buffer,
+ * -1 indicates an error.
+ */
+ virtual Q_LONG receiveFrom(KActiveSocketBase* device, Q_LONG len = -1);
+
+protected:
+ mutable QMutex m_mutex;
+ QValueList<QByteArray> m_list;
+ QIODevice::Offset m_offset; ///< offset of the start of data in the first element
+
+ Q_LONG m_size; ///< the maximum length of the buffer
+ mutable Q_LONG m_length;
+};
+
+} } // namespace KNetwork::Internal
+
+#endif
diff --git a/kdecore/network/ksocketdevice.cpp b/kdecore/network/ksocketdevice.cpp
new file mode 100644
index 000000000..b3004ccc2
--- /dev/null
+++ b/kdecore/network/ksocketdevice.cpp
@@ -0,0 +1,886 @@
+/* -*- 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 <qmap.h>
+
+#ifdef USE_SOLARIS
+# include <sys/filio.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+#ifdef HAVE_POLL
+# include <sys/poll.h>
+#else
+# ifdef HAVE_SYS_SELECT
+# include <sys/select.h>
+# endif
+#endif
+
+// Include syssocket before our local includes
+#include "syssocket.h"
+
+#include <qmutex.h>
+#include <qsocketnotifier.h>
+
+#include "kresolver.h"
+#include "ksocketaddress.h"
+#include "ksocketbase.h"
+#include "ksocketdevice.h"
+#include "ksockssocketdevice.h"
+
+using namespace KNetwork;
+
+class KNetwork::KSocketDevicePrivate
+{
+public:
+ mutable QSocketNotifier *input, *output, *exception;
+ KSocketAddress local, peer;
+ int af;
+
+ inline KSocketDevicePrivate()
+ {
+ input = output = exception = 0L;
+ af = 0;
+ }
+};
+
+
+KSocketDevice::KSocketDevice(const KSocketBase* parent)
+ : m_sockfd(-1), d(new KSocketDevicePrivate)
+{
+ setSocketDevice(this);
+ if (parent)
+ setSocketOptions(parent->socketOptions());
+}
+
+KSocketDevice::KSocketDevice(int fd)
+ : m_sockfd(fd), d(new KSocketDevicePrivate)
+{
+ setState(IO_Open);
+ setFlags(IO_Sequential | IO_Raw | IO_ReadWrite);
+ setSocketDevice(this);
+ d->af = localAddress().family();
+}
+
+KSocketDevice::KSocketDevice(bool, const KSocketBase* parent)
+ : m_sockfd(-1), d(new KSocketDevicePrivate)
+{
+ // do not set parent
+ if (parent)
+ setSocketOptions(parent->socketOptions());
+}
+
+KSocketDevice::~KSocketDevice()
+{
+ close(); // deletes the notifiers
+ unsetSocketDevice(); // prevent double deletion
+ delete d;
+}
+
+bool KSocketDevice::setSocketOptions(int opts)
+{
+ // must call parent
+ QMutexLocker locker(mutex());
+ KSocketBase::setSocketOptions(opts);
+
+ if (m_sockfd == -1)
+ return true; // flags are stored
+
+ {
+ int fdflags = fcntl(m_sockfd, F_GETFL, 0);
+ if (fdflags == -1)
+ {
+ setError(IO_UnspecifiedError, UnknownError);
+ return false; // error
+ }
+
+ if (opts & Blocking)
+ fdflags &= ~O_NONBLOCK;
+ else
+ fdflags |= O_NONBLOCK;
+
+ if (fcntl(m_sockfd, F_SETFL, fdflags) == -1)
+ {
+ setError(IO_UnspecifiedError, UnknownError);
+ return false; // error
+ }
+ }
+
+ {
+ int on = opts & AddressReuseable ? 1 : 0;
+ if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)) == -1)
+ {
+ setError(IO_UnspecifiedError, UnknownError);
+ return false; // error
+ }
+ }
+
+#if defined(IPV6_V6ONLY) && defined(AF_INET6)
+ if (d->af == AF_INET6)
+ {
+ // don't try this on non-IPv6 sockets, or we'll get an error
+
+ int on = opts & IPv6Only ? 1 : 0;
+ if (setsockopt(m_sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on, sizeof(on)) == -1)
+ {
+ setError(IO_UnspecifiedError, UnknownError);
+ return false; // error
+ }
+ }
+#endif
+
+ {
+ int on = opts & Broadcast ? 1 : 0;
+ if (setsockopt(m_sockfd, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on)) == -1)
+ {
+ setError(IO_UnspecifiedError, UnknownError);
+ return false; // error
+ }
+ }
+
+ return true; // all went well
+}
+
+bool KSocketDevice::open(int)
+{
+ resetError();
+ return false;
+}
+
+void KSocketDevice::close()
+{
+ resetError();
+ if (m_sockfd != -1)
+ {
+ delete d->input;
+ delete d->output;
+ delete d->exception;
+
+ d->input = d->output = d->exception = 0L;
+
+ d->local.setFamily(AF_UNSPEC);
+ d->peer.setFamily(AF_UNSPEC);
+
+ ::close(m_sockfd);
+ }
+ setState(0);
+
+ m_sockfd = -1;
+}
+
+bool KSocketDevice::create(int family, int type, int protocol)
+{
+ resetError();
+
+ if (m_sockfd != -1)
+ {
+ // it's already created!
+ setError(IO_SocketCreateError, AlreadyCreated);
+ return false;
+ }
+
+ // no socket yet; we have to create it
+ m_sockfd = kde_socket(family, type, protocol);
+
+ if (m_sockfd == -1)
+ {
+ setError(IO_SocketCreateError, NotSupported);
+ return false;
+ }
+
+ d->af = family;
+ setSocketOptions(socketOptions());
+ setState(IO_Open);
+ return true; // successfully created
+}
+
+bool KSocketDevice::create(const KResolverEntry& address)
+{
+ return create(address.family(), address.socketType(), address.protocol());
+}
+
+bool KSocketDevice::bind(const KResolverEntry& address)
+{
+ resetError();
+
+ if (m_sockfd == -1 && !create(address))
+ return false; // failed creating
+
+ // we have a socket, so try and bind
+ if (kde_bind(m_sockfd, address.address(), address.length()) == -1)
+ {
+ if (errno == EADDRINUSE)
+ setError(IO_BindError, AddressInUse);
+ else if (errno == EINVAL)
+ setError(IO_BindError, AlreadyBound);
+ else
+ // assume the address is the cause
+ setError(IO_BindError, NotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+bool KSocketDevice::listen(int backlog)
+{
+ if (m_sockfd != -1)
+ {
+ if (kde_listen(m_sockfd, backlog) == -1)
+ {
+ setError(IO_ListenError, NotSupported);
+ return false;
+ }
+
+ resetError();
+ setFlags(IO_Sequential | IO_Raw | IO_ReadWrite);
+ return true;
+ }
+
+ // we don't have a socket
+ // can't listen
+ setError(IO_ListenError, NotCreated);
+ return false;
+}
+
+bool KSocketDevice::connect(const KResolverEntry& address)
+{
+ resetError();
+
+ if (m_sockfd == -1 && !create(address))
+ return false; // failed creating!
+
+ if (kde_connect(m_sockfd, address.address(), address.length()) == -1)
+ {
+ if (errno == EISCONN)
+ return true; // we're already connected
+ else if (errno == EALREADY || errno == EINPROGRESS)
+ {
+ setError(IO_ConnectError, InProgress);
+ return true;
+ }
+ else if (errno == ECONNREFUSED)
+ setError(IO_ConnectError, ConnectionRefused);
+ else if (errno == ENETDOWN || errno == ENETUNREACH ||
+ errno == ENETRESET || errno == ECONNABORTED ||
+ errno == ECONNRESET || errno == EHOSTDOWN ||
+ errno == EHOSTUNREACH)
+ setError(IO_ConnectError, NetFailure);
+ else
+ setError(IO_ConnectError, NotSupported);
+
+ return false;
+ }
+
+ setFlags(IO_Sequential | IO_Raw | IO_ReadWrite);
+ return true; // all is well
+}
+
+KSocketDevice* KSocketDevice::accept()
+{
+ if (m_sockfd == -1)
+ {
+ // can't accept without a socket
+ setError(IO_AcceptError, NotCreated);
+ return 0L;
+ }
+
+ struct sockaddr sa;
+ socklen_t len = sizeof(sa);
+ int newfd = kde_accept(m_sockfd, &sa, &len);
+ if (newfd == -1)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ setError(IO_AcceptError, WouldBlock);
+ else
+ setError(IO_AcceptError, UnknownError);
+ return NULL;
+ }
+
+ return new KSocketDevice(newfd);
+}
+
+bool KSocketDevice::disconnect()
+{
+ resetError();
+
+ if (m_sockfd == -1)
+ return false; // can't create
+
+ KSocketAddress address;
+ address.setFamily(AF_UNSPEC);
+ if (kde_connect(m_sockfd, address.address(), address.length()) == -1)
+ {
+ if (errno == EALREADY || errno == EINPROGRESS)
+ {
+ setError(IO_ConnectError, InProgress);
+ return false;
+ }
+ else if (errno == ECONNREFUSED)
+ setError(IO_ConnectError, ConnectionRefused);
+ else if (errno == ENETDOWN || errno == ENETUNREACH ||
+ errno == ENETRESET || errno == ECONNABORTED ||
+ errno == ECONNRESET || errno == EHOSTDOWN ||
+ errno == EHOSTUNREACH)
+ setError(IO_ConnectError, NetFailure);
+ else
+ setError(IO_ConnectError, NotSupported);
+
+ return false;
+ }
+
+ setFlags(IO_Sequential | IO_Raw | IO_ReadWrite);
+ setState(IO_Open);
+ return true; // all is well
+}
+
+Q_LONG KSocketDevice::bytesAvailable() const
+{
+ if (m_sockfd == -1)
+ return -1; // there's nothing to read in a closed socket
+
+ int nchars;
+ if (ioctl(m_sockfd, FIONREAD, &nchars) == -1)
+ return -1; // error!
+
+ return nchars;
+}
+
+Q_LONG KSocketDevice::waitForMore(int msecs, bool *timeout)
+{
+ if (m_sockfd == -1)
+ return -1; // there won't ever be anything to read...
+
+ bool input;
+ if (!poll(&input, 0, 0, msecs, timeout))
+ return -1; // failed polling
+
+ return bytesAvailable();
+}
+
+static int do_read_common(int sockfd, char *data, Q_ULONG maxlen, KSocketAddress* from, ssize_t &retval, bool peek = false)
+{
+ socklen_t len;
+ if (from)
+ {
+ from->setLength(len = 128); // arbitrary length
+ retval = ::recvfrom(sockfd, data, maxlen, peek ? MSG_PEEK : 0, from->address(), &len);
+ }
+ else
+ retval = ::recvfrom(sockfd, data, maxlen, peek ? MSG_PEEK : 0, NULL, NULL);
+
+ if (retval == -1)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return KSocketDevice::WouldBlock;
+ else
+ return KSocketDevice::UnknownError;
+ }
+ if (retval == 0)
+ return KSocketDevice::RemotelyDisconnected;
+
+ if (from)
+ from->setLength(len);
+ return 0;
+}
+
+Q_LONG KSocketDevice::readBlock(char *data, Q_ULONG maxlen)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1;
+
+ if (maxlen == 0 || data == 0L)
+ return 0; // can't read
+
+ ssize_t retval;
+ int err = do_read_common(m_sockfd, data, maxlen, 0L, retval);
+
+ if (err)
+ {
+ setError(IO_ReadError, static_cast<SocketError>(err));
+ return -1;
+ }
+
+ return retval;
+}
+
+Q_LONG KSocketDevice::readBlock(char *data, Q_ULONG maxlen, KSocketAddress &from)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1; // nothing to do here
+
+ if (data == 0L || maxlen == 0)
+ return 0; // user doesn't want to read
+
+ ssize_t retval;
+ int err = do_read_common(m_sockfd, data, maxlen, &from, retval);
+
+ if (err)
+ {
+ setError(IO_ReadError, static_cast<SocketError>(err));
+ return -1;
+ }
+
+ return retval;
+}
+
+Q_LONG KSocketDevice::peekBlock(char *data, Q_ULONG maxlen)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1;
+
+ if (maxlen == 0 || data == 0L)
+ return 0; // can't read
+
+ ssize_t retval;
+ int err = do_read_common(m_sockfd, data, maxlen, 0L, retval, true);
+
+ if (err)
+ {
+ setError(IO_ReadError, static_cast<SocketError>(err));
+ return -1;
+ }
+
+ return retval;
+}
+
+Q_LONG KSocketDevice::peekBlock(char *data, Q_ULONG maxlen, KSocketAddress& from)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1; // nothing to do here
+
+ if (data == 0L || maxlen == 0)
+ return 0; // user doesn't want to read
+
+ ssize_t retval;
+ int err = do_read_common(m_sockfd, data, maxlen, &from, retval, true);
+
+ if (err)
+ {
+ setError(IO_ReadError, static_cast<SocketError>(err));
+ return -1;
+ }
+
+ return retval;
+}
+
+Q_LONG KSocketDevice::writeBlock(const char *data, Q_ULONG len)
+{
+ return writeBlock(data, len, KSocketAddress());
+}
+
+Q_LONG KSocketDevice::writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1; // can't write to unopen socket
+
+ if (data == 0L || len == 0)
+ return 0; // nothing to be written
+
+ ssize_t retval = ::sendto(m_sockfd, data, len, 0, to.address(), to.length());
+ if (retval == -1)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ setError(IO_WriteError, WouldBlock);
+ else
+ setError(IO_WriteError, UnknownError);
+ return -1; // nothing written
+ }
+ else if (retval == 0)
+ setError(IO_WriteError, RemotelyDisconnected);
+
+ return retval;
+}
+
+KSocketAddress KSocketDevice::localAddress() const
+{
+ if (m_sockfd == -1)
+ return KSocketAddress(); // not open, empty value
+
+ if (d->local.family() != AF_UNSPEC)
+ return d->local;
+
+ socklen_t len;
+ KSocketAddress localAddress;
+ localAddress.setLength(len = 32); // arbitrary value
+ if (kde_getsockname(m_sockfd, localAddress.address(), &len) == -1)
+ // error!
+ return d->local = KSocketAddress();
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ len = localAddress.address()->sa_len;
+#endif
+
+ if (len <= localAddress.length())
+ {
+ // it has fit already
+ localAddress.setLength(len);
+ return d->local = localAddress;
+ }
+
+ // no, the socket address is actually larger than we had anticipated
+ // call again
+ localAddress.setLength(len);
+ if (kde_getsockname(m_sockfd, localAddress.address(), &len) == -1)
+ // error!
+ return d->local = KSocketAddress();
+
+ return d->local = localAddress;
+}
+
+KSocketAddress KSocketDevice::peerAddress() const
+{
+ if (m_sockfd == -1)
+ return KSocketAddress(); // not open, empty value
+
+ if (d->peer.family() != AF_UNSPEC)
+ return d->peer;
+
+ socklen_t len;
+ KSocketAddress peerAddress;
+ peerAddress.setLength(len = 32); // arbitrary value
+ if (kde_getpeername(m_sockfd, peerAddress.address(), &len) == -1)
+ // error!
+ return d->peer = KSocketAddress();
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ len = peerAddress.address()->sa_len;
+#endif
+
+ if (len <= peerAddress.length())
+ {
+ // it has fit already
+ peerAddress.setLength(len);
+ return d->peer = peerAddress;
+ }
+
+ // no, the socket address is actually larger than we had anticipated
+ // call again
+ peerAddress.setLength(len);
+ if (kde_getpeername(m_sockfd, peerAddress.address(), &len) == -1)
+ // error!
+ return d->peer = KSocketAddress();
+
+ return d->peer = peerAddress;
+}
+
+KSocketAddress KSocketDevice::externalAddress() const
+{
+ // for normal sockets, the externally visible address is the same
+ // as the local address
+ return localAddress();
+}
+
+QSocketNotifier* KSocketDevice::readNotifier() const
+{
+ if (d->input)
+ return d->input;
+
+ QMutexLocker locker(mutex());
+ if (d->input)
+ return d->input;
+
+ if (m_sockfd == -1)
+ {
+ // socket doesn't exist; can't create notifier
+ return 0L;
+ }
+
+ return d->input = createNotifier(QSocketNotifier::Read);
+}
+
+QSocketNotifier* KSocketDevice::writeNotifier() const
+{
+ if (d->output)
+ return d->output;
+
+ QMutexLocker locker(mutex());
+ if (d->output)
+ return d->output;
+
+ if (m_sockfd == -1)
+ {
+ // socket doesn't exist; can't create notifier
+ return 0L;
+ }
+
+ return d->output = createNotifier(QSocketNotifier::Write);
+}
+
+QSocketNotifier* KSocketDevice::exceptionNotifier() const
+{
+ if (d->exception)
+ return d->exception;
+
+ QMutexLocker locker(mutex());
+ if (d->exception)
+ return d->exception;
+
+ if (m_sockfd == -1)
+ {
+ // socket doesn't exist; can't create notifier
+ return 0L;
+ }
+
+ return d->exception = createNotifier(QSocketNotifier::Exception);
+}
+
+bool KSocketDevice::poll(bool *input, bool *output, bool *exception,
+ int timeout, bool* timedout)
+{
+ if (m_sockfd == -1)
+ {
+ setError(IO_UnspecifiedError, NotCreated);
+ return false;
+ }
+
+ resetError();
+#ifdef HAVE_POLL
+ struct pollfd fds;
+ fds.fd = m_sockfd;
+ fds.events = 0;
+
+ if (input)
+ {
+ fds.events |= POLLIN;
+ *input = false;
+ }
+ if (output)
+ {
+ fds.events |= POLLOUT;
+ *output = false;
+ }
+ if (exception)
+ {
+ fds.events |= POLLPRI;
+ *exception = false;
+ }
+
+ int retval = ::poll(&fds, 1, timeout);
+ if (retval == -1)
+ {
+ setError(IO_UnspecifiedError, UnknownError);
+ return false;
+ }
+ if (retval == 0)
+ {
+ // timeout
+ if (timedout)
+ *timedout = true;
+ return true;
+ }
+
+ if (input && fds.revents & POLLIN)
+ *input = true;
+ if (output && fds.revents & POLLOUT)
+ *output = true;
+ if (exception && fds.revents & POLLPRI)
+ *exception = true;
+ if (timedout)
+ *timedout = false;
+
+ return true;
+#else
+ /*
+ * We don't have poll(2). We'll have to make do with select(2).
+ */
+
+ fd_set readfds, writefds, exceptfds;
+ fd_set *preadfds = 0L, *pwritefds = 0L, *pexceptfds = 0L;
+
+ if (input)
+ {
+ preadfds = &readfds;
+ FD_ZERO(preadfds);
+ FD_SET(m_sockfd, preadfds);
+ *input = false;
+ }
+ if (output)
+ {
+ pwritefds = &writefds;
+ FD_ZERO(pwritefds);
+ FD_SET(m_sockfd, pwritefds);
+ *output = false;
+ }
+ if (exception)
+ {
+ pexceptfds = &exceptfds;
+ FD_ZERO(pexceptfds);
+ FD_SET(m_sockfd, pexceptfds);
+ *exception = false;
+ }
+
+ int retval;
+ if (timeout < 0)
+ retval = select(m_sockfd + 1, preadfds, pwritefds, pexceptfds, 0L);
+ else
+ {
+ // convert the milliseconds to timeval
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = timeout % 1000 * 1000;
+
+ retval = select(m_sockfd + 1, preadfds, pwritefds, pexceptfds, &tv);
+ }
+
+ if (retval == -1)
+ {
+ setError(IO_UnspecifiedError, UnknownError);
+ return false;
+ }
+ if (retval == 0)
+ {
+ // timeout
+ if (timedout)
+ *timedout = true;
+ return true;
+ }
+
+ if (input && FD_ISSET(m_sockfd, preadfds))
+ *input = true;
+ if (output && FD_ISSET(m_sockfd, pwritefds))
+ *output = true;
+ if (exception && FD_ISSET(m_sockfd, pexceptfds))
+ *exception = true;
+
+ return true;
+#endif
+}
+
+bool KSocketDevice::poll(int timeout, bool *timedout)
+{
+ bool input, output, exception;
+ return poll(&input, &output, &exception, timeout, timedout);
+}
+
+QSocketNotifier* KSocketDevice::createNotifier(QSocketNotifier::Type type) const
+{
+ if (m_sockfd == -1)
+ return 0L;
+
+ return new QSocketNotifier(m_sockfd, type);
+}
+
+namespace
+{
+ // simple class to avoid pointer stuff
+ template<class T> class ptr
+ {
+ typedef T type;
+ type* obj;
+ public:
+ ptr() : obj(0)
+ { }
+
+ ptr(const ptr<T>& other) : obj(other.obj)
+ { }
+
+ ptr(type* _obj) : obj(_obj)
+ { }
+
+ ~ptr()
+ { }
+
+ ptr<T>& operator=(const ptr<T>& other)
+ { obj = other.obj; return *this; }
+
+ ptr<T>& operator=(T* _obj)
+ { obj = _obj; return *this; }
+
+ type* operator->() const { return obj; }
+
+ operator T*() const { return obj; }
+
+ bool isNull() const
+ { return obj == 0; }
+ };
+}
+
+static KSocketDeviceFactoryBase* defaultImplFactory;
+static QMutex defaultImplFactoryMutex;
+typedef QMap<int, KSocketDeviceFactoryBase* > factoryMap;
+static factoryMap factories;
+
+KSocketDevice* KSocketDevice::createDefault(KSocketBase* parent)
+{
+ KSocketDevice* device = dynamic_cast<KSocketDevice*>(parent);
+ if (device != 0L)
+ return device;
+
+ KSocksSocketDevice::initSocks();
+
+ if (defaultImplFactory)
+ return defaultImplFactory->create(parent);
+
+ // the really default
+ return new KSocketDevice(parent);
+}
+
+KSocketDevice* KSocketDevice::createDefault(KSocketBase* parent, int capabilities)
+{
+ KSocketDevice* device = dynamic_cast<KSocketDevice*>(parent);
+ if (device != 0L)
+ return device;
+
+ QMutexLocker locker(&defaultImplFactoryMutex);
+ factoryMap::ConstIterator it = factories.constBegin();
+ for ( ; it != factories.constEnd(); ++it)
+ if ((it.key() & capabilities) == capabilities)
+ // found a match
+ return it.data()->create(parent);
+
+ return 0L; // no default
+}
+
+KSocketDeviceFactoryBase*
+KSocketDevice::setDefaultImpl(KSocketDeviceFactoryBase* factory)
+{
+ QMutexLocker locker(&defaultImplFactoryMutex);
+ KSocketDeviceFactoryBase* old = defaultImplFactory;
+ defaultImplFactory = factory;
+ return old;
+}
+
+void KSocketDevice::addNewImpl(KSocketDeviceFactoryBase* factory, int capabilities)
+{
+ QMutexLocker locker(&defaultImplFactoryMutex);
+ if (factories.contains(capabilities))
+ delete factories[capabilities];
+ factories.insert(capabilities, factory);
+}
+
diff --git a/kdecore/network/ksocketdevice.h b/kdecore/network/ksocketdevice.h
new file mode 100644
index 000000000..301bc2b07
--- /dev/null
+++ b/kdecore/network/ksocketdevice.h
@@ -0,0 +1,428 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KSOCKETDEVICE_H
+#define KSOCKETDEVICE_H
+
+#include <qsocketnotifier.h>
+#include "ksocketbase.h"
+
+namespace KNetwork {
+
+class KSocketDevice;
+class KSocketDeviceFactoryBase;
+
+class KSocketDevicePrivate;
+/** @class KSocketDevice ksocketdevice.h ksocketdevice.h
+ * @brief Low-level socket functionality.
+ *
+ * This class provides low-level socket functionality.
+ *
+ * Most users will prefer "cooked" interfaces like those of @ref KStreamSocket or
+ * @ref KServerSocket.
+ *
+ * Descended classes from this one provide some other kinds of socket functionality,
+ * like proxying or specific socket types.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ */
+class KDECORE_EXPORT KSocketDevice: public KActiveSocketBase, public KPassiveSocketBase
+{
+public:
+ /**
+ * Capabilities for the socket implementation.
+ *
+ * KSocketDevice-derived classes can implement certain capabilities that are not
+ * available in the default class. These capabilities are described by these flags.
+ * The default KSocketDevice class has none of these capabilities.
+ *
+ * For the negative capabilities (inabilities, the CanNot* forms), when a capability
+ * is not present, the implementation will default to the original behaviour.
+ */
+ enum Capabilities
+ {
+ /** Can connect to hostnames.
+ * If this flag is present, the string form of @ref connect can be used. */
+ CanConnectString = 0x01,
+
+ /** Can bind to hostnames.
+ * If this flag is present, the string form of @ref bind can be used */
+ CanBindString = 0x02,
+
+ /** Can not bind.
+ * If this flag is present, this implementation cannot bind */
+ CanNotBind = 0x04,
+
+ /** Can not listen.
+ * If this flag is present, this implementation cannot listen */
+ CanNotListen = 0x08,
+
+ /**
+ * Can send multicast as well as join/leave multicast groups.
+ */
+ CanMulticast = 0x10,
+
+ /**
+ * Can not use datagrams.
+ * Note that this implies multicast capability not being available either.
+ */
+ CanNotUseDatagrams = 0x20
+ };
+protected:
+ /// The socket file descriptor. It is used throughout the implementation
+ /// and subclasses.
+ int m_sockfd;
+
+public:
+ /**
+ * Default constructor.
+ *
+ * The parameter is used to specify which socket this object is used as
+ * a device for.
+ */
+ explicit KSocketDevice(const KSocketBase* = 0L);
+
+ /**
+ * Constructs a new object around an already-open socket.
+ *
+ * Note: you should write programs that create sockets through
+ * the classes whenever possible.
+ */
+ explicit KSocketDevice(int fd);
+
+ /**
+ * Destructor. This closes the socket if it's open.
+ */
+ virtual ~KSocketDevice();
+
+ /**
+ * Returns the file descriptor for this socket.
+ */
+ inline int socket() const
+ { return m_sockfd; }
+
+ /**
+ * Returns the set of capabilities this socket class implements.
+ * The set of capabilities is defined as an OR-ed mask of
+ * @ref Capabilities bits.
+ *
+ * The default implementation is guaranteed to always return 0. That
+ * is, derived implementations always return bits where they differ
+ * from the system standard sockets.
+ */
+ virtual int capabilities() const
+ { return 0; }
+
+ /**
+ * This implementation sets the options on the socket.
+ */
+ virtual bool setSocketOptions(int opts);
+
+ /**
+ * Reimplementation from QIODevice. You should not call this function in sockets.
+ */
+ virtual bool open(int mode);
+
+ /**
+ * Closes the socket. Reimplemented from QIODevice.
+ *
+ * Use this function to close the socket this object is holding open.
+ */
+ virtual void close();
+
+ /**
+ * This call is not supported on sockets. Reimplemented from QIODevice.
+ */
+ virtual void flush()
+ { }
+
+ /**
+ * Creates a socket but don't connect or bind anywhere.
+ * This function is the equivalent of the system call socket(2).
+ */
+ virtual bool create(int family, int type, int protocol);
+
+ /**
+ * @overload
+ * Creates a socket but don't connect or bind anywhere.
+ */
+ bool create(const KResolverEntry& address);
+
+ /**
+ * Binds this socket to the given address.
+ */
+ virtual bool bind(const KResolverEntry& address);
+
+ /**
+ * Puts this socket into listening mode.
+ */
+ virtual bool listen(int backlog = 5); // 5 is arbitrary
+
+ /**
+ * Connect to a remote host.
+ */
+ virtual bool connect(const KResolverEntry& address);
+
+ /**
+ * Accepts a new incoming connection.
+ * Note: this function returns a socket of type KSocketDevice.
+ */
+ virtual KSocketDevice* accept();
+
+ /**
+ * Disconnects this socket.
+ */
+ virtual bool disconnect();
+
+ /**
+ * Returns the number of bytes available for reading without blocking.
+ */
+ virtual Q_LONG bytesAvailable() const;
+
+ /**
+ * Waits up to @p msecs for more data to be available on this socket.
+ *
+ * This function is a wrapper against @ref poll. This function will wait
+ * for any read events.
+ */
+ virtual Q_LONG waitForMore(int msecs, bool *timeout = 0L);
+
+ /**
+ * Reads data from this socket.
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen);
+
+ /**
+ * Reads data and the source address from this socket.
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen, KSocketAddress& from);
+
+ /**
+ * Peeks data in the socket.
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen);
+
+ /**
+ * Peeks the data in the socket and the source address.
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen, KSocketAddress& from);
+
+ /**
+ * Writes data to the socket.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len);
+
+ /**
+ * Writes the given data to the given destination address.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to);
+
+ /**
+ * Returns this socket's local address.
+ */
+ virtual KSocketAddress localAddress() const;
+
+ /**
+ * Returns this socket's peer address. If this implementation does proxying
+ * of some sort, this is the real external address, not the proxy's address.
+ */
+ virtual KSocketAddress peerAddress() const;
+
+ /**
+ * Returns this socket's externally visible local address.
+ *
+ * If this socket has a local address visible externally different
+ * from the normal local address (as returned by @ref localAddress), then
+ * return it.
+ *
+ * Certain implementations will use proxies and thus have externally visible
+ * addresses different from the local socket values. The default implementation
+ * returns the same value as @ref localAddress.
+ *
+ * @note This function may return an empty KSocketAddress. In that case, the
+ * externally visible address could/can not be determined.
+ */
+ virtual KSocketAddress externalAddress() const;
+
+ /**
+ * Returns a socket notifier for input on this socket.
+ * The notifier is created only when requested. Whether
+ * it is enabled or not depends on the implementation.
+ *
+ * This function might return NULL.
+ */
+ QSocketNotifier* readNotifier() const;
+
+ /**
+ * Returns a socket notifier for output on this socket.
+ * The is created only when requested.
+ *
+ * This function might return NULL.
+ */
+ QSocketNotifier* writeNotifier() const;
+
+ /**
+ * Returns a socket notifier for exceptional events on this socket.
+ * The is created only when requested.
+ *
+ * This function might return NULL.
+ */
+ QSocketNotifier* exceptionNotifier() const;
+
+ /**
+ * Executes a poll in the socket, via select(2) or poll(2).
+ * The events polled are returned in the parameters pointers.
+ * Set any of them to NULL to disable polling of that event.
+ *
+ * On exit, @p input, @p output and @p exception will contain
+ * true if an event of that kind is waiting on the socket or false
+ * if not. If a timeout occurred, set @p timedout to true (all other
+ * parameters are necessarily set to false).
+ *
+ * @param input if set, turns on polling for input events
+ * @param output if set, turns on polling for output events
+ * @param exception if set, turns on polling for exceptional events
+ * @param timeout the time in milliseconds to wait for an event;
+ * 0 for no wait and any negative value to wait forever
+ * @param timedout on exit, will contain true if the polling timed out
+ * @return true if the poll call succeeded and false if an error occurred
+ */
+ virtual bool poll(bool* input, bool* output, bool* exception = 0L,
+ int timeout = -1, bool* timedout = 0L);
+
+ /**
+ * Shorter version to poll for any events in a socket. This call
+ * polls for input, output and exceptional events in a socket but
+ * does not return their states. This is useful if you need to wait for
+ * any event, but don't need to know which; or for timeouts.
+ *
+ * @param timeout the time in milliseconds to wait for an event;
+ * 0 for no wait and any negative value to wait forever
+ * @param timedout on exit, will contain true if the polling timed out
+ * @return true if the poll call succeeded and false if an error occurred
+ */
+ bool poll(int timeout = -1, bool* timedout = 0L);
+
+protected:
+ /**
+ * Special constructor. This constructor will cause the internal
+ * socket device NOT to be set. Use this if your socket device class
+ * takes another underlying socket device.
+ *
+ * @param parent the parent, if any
+ */
+ KSocketDevice(bool, const KSocketBase* parent = 0L);
+
+ /**
+ * Creates a socket notifier of the given type.
+ *
+ * This function is called by @ref readNotifier, @ref writeNotifier and
+ * @ref exceptionNotifier when they need to create a socket notifier
+ * (i.e., the first call to those functions after the socket is open).
+ * After that call, those functions cache the socket notifier and will
+ * not need to call this function again.
+ *
+ * Reimplement this function in your derived class if your socket type
+ * requires a different kind of QSocketNotifier. The return value should
+ * be deleteable with delete. (@ref close deletes them).
+ *
+ * @param type the socket notifier type
+ */
+ virtual QSocketNotifier* createNotifier(QSocketNotifier::Type type) const;
+
+public:
+ /**
+ * Creates a new default KSocketDevice object given
+ * the parent object.
+ *
+ * The capabilities flag indicates the desired capabilities the object being
+ * created should possess. Those capabilities are not guaranteed: if no factory
+ * can provide such an object, a default object will be created.
+ *
+ * @param parent the KSocketBase parent
+ */
+ static KSocketDevice* createDefault(KSocketBase* parent);
+
+ /**
+ * @overload
+ *
+ * This will create an object only if the requested capabilities match.
+ *
+ * @param parent the parent
+ * @param capabilities the requested capabilities
+ */
+ static KSocketDevice* createDefault(KSocketBase* parent, int capabilities);
+
+ /**
+ * Sets the default KSocketDevice implementation to use and
+ * return the old factory.
+ *
+ * @param factory the factory object for the implementation
+ */
+ static KSocketDeviceFactoryBase* setDefaultImpl(KSocketDeviceFactoryBase* factory);
+
+ /**
+ * Adds a factory of KSocketDevice objects to the list, along with its
+ * capabilities flag.
+ */
+ static void addNewImpl(KSocketDeviceFactoryBase* factory, int capabilities);
+
+private:
+ KSocketDevice(const KSocketDevice&);
+ KSocketDevice& operator=(const KSocketDevice&);
+
+ KSocketDevicePrivate *d;
+};
+
+/** @internal
+ * This class provides functionality for creating and registering
+ * socket implementations.
+ */
+class KSocketDeviceFactoryBase
+{
+public:
+ KSocketDeviceFactoryBase() {}
+ virtual ~KSocketDeviceFactoryBase() {}
+
+ virtual KSocketDevice* create(KSocketBase*) const = 0;
+};
+
+/**
+ * This class provides functionality for creating and registering
+ * socket implementations.
+ */
+template<class Impl>
+class KSocketDeviceFactory: public KSocketDeviceFactoryBase
+{
+public:
+ KSocketDeviceFactory() {}
+ virtual ~KSocketDeviceFactory() {}
+
+ virtual KSocketDevice* create(KSocketBase* parent) const
+ { return new Impl(parent); }
+};
+
+} // namespaces
+
+#endif
diff --git a/kdecore/network/ksockssocketdevice.cpp b/kdecore/network/ksockssocketdevice.cpp
new file mode 100644
index 000000000..f67b90bc9
--- /dev/null
+++ b/kdecore/network/ksockssocketdevice.cpp
@@ -0,0 +1,487 @@
+/* -*- C++ -*-
+ * Copyright (C) 2004 Thiago Macieira <thiago.macieira@kdemail.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#ifdef __CYGWIN__
+#undef kde_socklen_t
+#define kde_socklen_t ksocklen_t
+#endif
+
+#include "kapplication.h"
+
+#include "ksocks.h"
+#include "ksocketaddress.h"
+#include "kresolver.h"
+#include "ksockssocketdevice.h"
+
+using namespace KNetwork;
+
+// constructor
+// nothing to do
+KSocksSocketDevice::KSocksSocketDevice(const KSocketBase* obj)
+ : KSocketDevice(obj)
+{
+}
+
+// constructor with argument
+// nothing to do
+KSocksSocketDevice::KSocksSocketDevice(int fd)
+ : KSocketDevice(fd)
+{
+}
+
+// destructor
+// also nothing to do
+KSocksSocketDevice::~KSocksSocketDevice()
+{
+}
+
+// returns the capabilities
+int KSocksSocketDevice::capabilities() const
+{
+ return 0; // can do everything!
+}
+
+// From here on, the code is almost exactly a copy of KSocketDevice
+// the differences are the use of KSocks where appropriate
+
+bool KSocksSocketDevice::bind(const KResolverEntry& address)
+{
+ resetError();
+
+ if (m_sockfd == -1 && !create(address))
+ return false; // failed creating
+
+ // we have a socket, so try and bind
+ if (KSocks::self()->bind(m_sockfd, address.address(), address.length()) == -1)
+ {
+ if (errno == EADDRINUSE)
+ setError(IO_BindError, AddressInUse);
+ else if (errno == EINVAL)
+ setError(IO_BindError, AlreadyBound);
+ else
+ // assume the address is the cause
+ setError(IO_BindError, NotSupported);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool KSocksSocketDevice::listen(int backlog)
+{
+ if (m_sockfd != -1)
+ {
+ if (KSocks::self()->listen(m_sockfd, backlog) == -1)
+ {
+ setError(IO_ListenError, NotSupported);
+ return false;
+ }
+
+ resetError();
+ setFlags(IO_Sequential | IO_Raw | IO_ReadWrite);
+ setState(IO_Open);
+ return true;
+ }
+
+ // we don't have a socket
+ // can't listen
+ setError(IO_ListenError, NotCreated);
+ return false;
+}
+
+bool KSocksSocketDevice::connect(const KResolverEntry& address)
+{
+ resetError();
+
+ if (m_sockfd == -1 && !create(address))
+ return false; // failed creating!
+
+ int retval;
+ if (KSocks::self()->hasWorkingAsyncConnect())
+ retval = KSocks::self()->connect(m_sockfd, address.address(),
+ address.length());
+ else
+ {
+ // work around some SOCKS implementation bugs
+ // we will do a *synchronous* connection here!
+ // FIXME: KDE4, write a proper SOCKS implementation
+ bool isBlocking = blocking();
+ setBlocking(true);
+ retval = KSocks::self()->connect(m_sockfd, address.address(),
+ address.length());
+ setBlocking(isBlocking);
+ }
+
+ if (retval == -1)
+ {
+ if (errno == EISCONN)
+ return true; // we're already connected
+ else if (errno == EALREADY || errno == EINPROGRESS)
+ {
+ setError(IO_ConnectError, InProgress);
+ return true;
+ }
+ else if (errno == ECONNREFUSED)
+ setError(IO_ConnectError, ConnectionRefused);
+ else if (errno == ENETDOWN || errno == ENETUNREACH ||
+ errno == ENETRESET || errno == ECONNABORTED ||
+ errno == ECONNRESET || errno == EHOSTDOWN ||
+ errno == EHOSTUNREACH)
+ setError(IO_ConnectError, NetFailure);
+ else
+ setError(IO_ConnectError, NotSupported);
+
+ return false;
+ }
+
+ setFlags(IO_Sequential | IO_Raw | IO_ReadWrite);
+ setState(IO_Open);
+ return true; // all is well
+}
+
+KSocksSocketDevice* KSocksSocketDevice::accept()
+{
+ if (m_sockfd == -1)
+ {
+ // can't accept without a socket
+ setError(IO_AcceptError, NotCreated);
+ return 0L;
+ }
+
+ struct sockaddr sa;
+ kde_socklen_t len = sizeof(sa);
+ int newfd = KSocks::self()->accept(m_sockfd, &sa, &len);
+ if (newfd == -1)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ setError(IO_AcceptError, WouldBlock);
+ else
+ setError(IO_AcceptError, UnknownError);
+ return NULL;
+ }
+
+ return new KSocksSocketDevice(newfd);
+}
+
+static int socks_read_common(int sockfd, char *data, Q_ULONG maxlen, KSocketAddress* from, ssize_t &retval, bool peek = false)
+{
+ kde_socklen_t len;
+ if (from)
+ {
+ from->setLength(len = 128); // arbitrary length
+ retval = KSocks::self()->recvfrom(sockfd, data, maxlen, peek ? MSG_PEEK : 0, from->address(), &len);
+ }
+ else
+ retval = KSocks::self()->recvfrom(sockfd, data, maxlen, peek ? MSG_PEEK : 0, NULL, NULL);
+
+ if (retval == -1)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return KSocketDevice::WouldBlock;
+ else
+ return KSocketDevice::UnknownError;
+ }
+
+ if (from)
+ from->setLength(len);
+ return 0;
+}
+
+Q_LONG KSocksSocketDevice::readBlock(char *data, Q_ULONG maxlen)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1;
+
+ if (maxlen == 0 || data == 0L)
+ return 0; // can't read
+
+ ssize_t retval;
+ int err = socks_read_common(m_sockfd, data, maxlen, 0L, retval);
+
+ if (err)
+ {
+ setError(IO_ReadError, static_cast<SocketError>(err));
+ return -1;
+ }
+
+ return retval;
+}
+
+Q_LONG KSocksSocketDevice::readBlock(char *data, Q_ULONG maxlen, KSocketAddress &from)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1; // nothing to do here
+
+ if (data == 0L || maxlen == 0)
+ return 0; // user doesn't want to read
+
+ ssize_t retval;
+ int err = socks_read_common(m_sockfd, data, maxlen, &from, retval);
+
+ if (err)
+ {
+ setError(IO_ReadError, static_cast<SocketError>(err));
+ return -1;
+ }
+
+ return retval;
+}
+
+Q_LONG KSocksSocketDevice::peekBlock(char *data, Q_ULONG maxlen)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1;
+
+ if (maxlen == 0 || data == 0L)
+ return 0; // can't read
+
+ ssize_t retval;
+ int err = socks_read_common(m_sockfd, data, maxlen, 0L, retval, true);
+
+ if (err)
+ {
+ setError(IO_ReadError, static_cast<SocketError>(err));
+ return -1;
+ }
+
+ return retval;
+}
+
+Q_LONG KSocksSocketDevice::peekBlock(char *data, Q_ULONG maxlen, KSocketAddress& from)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1; // nothing to do here
+
+ if (data == 0L || maxlen == 0)
+ return 0; // user doesn't want to read
+
+ ssize_t retval;
+ int err = socks_read_common(m_sockfd, data, maxlen, &from, retval, true);
+
+ if (err)
+ {
+ setError(IO_ReadError, static_cast<SocketError>(err));
+ return -1;
+ }
+
+ return retval;
+}
+
+Q_LONG KSocksSocketDevice::writeBlock(const char *data, Q_ULONG len)
+{
+ return writeBlock(data, len, KSocketAddress());
+}
+
+Q_LONG KSocksSocketDevice::writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to)
+{
+ resetError();
+ if (m_sockfd == -1)
+ return -1; // can't write to unopen socket
+
+ if (data == 0L || len == 0)
+ return 0; // nothing to be written
+
+ ssize_t retval = KSocks::self()->sendto(m_sockfd, data, len, 0, to.address(), to.length());
+ if (retval == -1)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ setError(IO_WriteError, WouldBlock);
+ else
+ setError(IO_WriteError, UnknownError);
+ return -1; // nothing written
+ }
+
+ return retval;
+}
+
+KSocketAddress KSocksSocketDevice::localAddress() const
+{
+ if (m_sockfd == -1)
+ return KSocketAddress(); // not open, empty value
+
+ kde_socklen_t len;
+ KSocketAddress localAddress;
+ localAddress.setLength(len = 32); // arbitrary value
+ if (KSocks::self()->getsockname(m_sockfd, localAddress.address(), &len) == -1)
+ // error!
+ return KSocketAddress();
+
+ if (len <= localAddress.length())
+ {
+ // it has fit already
+ localAddress.setLength(len);
+ return localAddress;
+ }
+
+ // no, the socket address is actually larger than we had anticipated
+ // call again
+ localAddress.setLength(len);
+ if (KSocks::self()->getsockname(m_sockfd, localAddress.address(), &len) == -1)
+ // error!
+ return KSocketAddress();
+
+ return localAddress;
+}
+
+KSocketAddress KSocksSocketDevice::peerAddress() const
+{
+ if (m_sockfd == -1)
+ return KSocketAddress(); // not open, empty value
+
+ kde_socklen_t len;
+ KSocketAddress peerAddress;
+ peerAddress.setLength(len = 32); // arbitrary value
+ if (KSocks::self()->getpeername(m_sockfd, peerAddress.address(), &len) == -1)
+ // error!
+ return KSocketAddress();
+
+ if (len <= peerAddress.length())
+ {
+ // it has fit already
+ peerAddress.setLength(len);
+ return peerAddress;
+ }
+
+ // no, the socket address is actually larger than we had anticipated
+ // call again
+ peerAddress.setLength(len);
+ if (KSocks::self()->getpeername(m_sockfd, peerAddress.address(), &len) == -1)
+ // error!
+ return KSocketAddress();
+
+ return peerAddress;
+}
+
+KSocketAddress KSocksSocketDevice::externalAddress() const
+{
+ // return empty, indicating unknown external address
+ return KSocketAddress();
+}
+
+bool KSocksSocketDevice::poll(bool *input, bool *output, bool *exception,
+ int timeout, bool *timedout)
+{
+ if (m_sockfd == -1)
+ {
+ setError(IO_UnspecifiedError, NotCreated);
+ return false;
+ }
+
+ resetError();
+ fd_set readfds, writefds, exceptfds;
+ fd_set *preadfds = 0L, *pwritefds = 0L, *pexceptfds = 0L;
+
+ if (input)
+ {
+ preadfds = &readfds;
+ FD_ZERO(preadfds);
+ FD_SET(m_sockfd, preadfds);
+ *input = false;
+ }
+ if (output)
+ {
+ pwritefds = &writefds;
+ FD_ZERO(pwritefds);
+ FD_SET(m_sockfd, pwritefds);
+ *output = false;
+ }
+ if (exception)
+ {
+ pexceptfds = &exceptfds;
+ FD_ZERO(pexceptfds);
+ FD_SET(m_sockfd, pexceptfds);
+ *exception = false;
+ }
+
+ int retval;
+ if (timeout < 0)
+ retval = KSocks::self()->select(m_sockfd + 1, preadfds, pwritefds, pexceptfds, 0L);
+ else
+ {
+ // convert the milliseconds to timeval
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = timeout % 1000 * 1000;
+
+ retval = select(m_sockfd + 1, preadfds, pwritefds, pexceptfds, &tv);
+ }
+
+ if (retval == -1)
+ {
+ setError(IO_UnspecifiedError, UnknownError);
+ return false;
+ }
+ if (retval == 0)
+ {
+ // timeout
+ if (timedout)
+ *timedout = true;
+ return true;
+ }
+
+ if (input && FD_ISSET(m_sockfd, preadfds))
+ *input = true;
+ if (output && FD_ISSET(m_sockfd, pwritefds))
+ *output = true;
+ if (exception && FD_ISSET(m_sockfd, pexceptfds))
+ *exception = true;
+
+ return true;
+}
+
+void KSocksSocketDevice::initSocks()
+{
+ static bool init = false;
+
+ if (init)
+ return;
+
+ if (kapp == 0L)
+ return; // no KApplication, so don't initialise
+ // this should, however, test for KInstance
+
+ init = true;
+
+ if (KSocks::self()->hasSocks())
+ delete KSocketDevice::setDefaultImpl(new KSocketDeviceFactory<KSocksSocketDevice>);
+}
+
+#if 0
+static bool register()
+{
+ KSocketDevice::addNewImpl(new KSocketDeviceFactory<KSocksSocketDevice>, 0);
+}
+
+static bool register = registered();
+#endif
diff --git a/kdecore/network/ksockssocketdevice.h b/kdecore/network/ksockssocketdevice.h
new file mode 100644
index 000000000..62de23f05
--- /dev/null
+++ b/kdecore/network/ksockssocketdevice.h
@@ -0,0 +1,129 @@
+/* -*- C++ -*-
+ * Copyright (C) 2004 Thiago Macieira <thiago.macieira@kdemail.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KSOCKSSOCKETDEVICE_H
+#define KSOCKSSOCKETDEVICE_H
+
+#include "ksocketdevice.h"
+
+namespace KNetwork {
+
+/**
+ * @class KSocksSocketDevice ksockssocketdevice.h ksockssocketdevice.h
+ * @brief The low-level class for SOCKS proxying.
+ *
+ * This class reimplements several functions from @ref KSocketDevice in order
+ * to implement SOCKS support.
+ *
+ * This works by using KSocks.
+ *
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ *
+ * @warning This code is untested!
+ */
+class KDECORE_EXPORT KSocksSocketDevice: public KSocketDevice
+{
+public:
+ /**
+ * Constructor.
+ */
+ KSocksSocketDevice(const KSocketBase* = 0L);
+
+ /**
+ * Construct from a file descriptor.
+ */
+ explicit KSocksSocketDevice(int fd);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KSocksSocketDevice();
+
+ /**
+ * Sets our capabilities.
+ */
+ virtual int capabilities() const;
+
+ /**
+ * Overrides binding.
+ */
+ virtual bool bind(const KResolverEntry& address);
+
+ /**
+ * Overrides listening.
+ */
+ virtual bool listen(int backlog);
+
+ /**
+ * Overrides connection.
+ */
+ virtual bool connect(const KResolverEntry& address);
+
+ /**
+ * Overrides accepting. The return type is specialised.
+ */
+ virtual KSocksSocketDevice* accept();
+
+ /**
+ * Overrides reading.
+ */
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen);
+ virtual Q_LONG readBlock(char *data, Q_ULONG maxlen, KSocketAddress& from);
+
+ /**
+ * Overrides peeking.
+ */
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen);
+ virtual Q_LONG peekBlock(char *data, Q_ULONG maxlen, KSocketAddress& from);
+
+ /**
+ * Overrides writing.
+ */
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len);
+ virtual Q_LONG writeBlock(const char *data, Q_ULONG len, const KSocketAddress& to);
+
+ /**
+ * Overrides getting socket address.
+ */
+ virtual KSocketAddress localAddress() const;
+
+ /**
+ * Overrides getting peer address.
+ */
+ virtual KSocketAddress peerAddress() const;
+
+ /**
+ * Overrides getting external address.
+ */
+ virtual KSocketAddress externalAddress() const;
+
+ /**
+ * Overrides polling.
+ */
+ virtual bool poll(bool* input, bool* output, bool* exception = 0L,
+ int timeout = -1, bool* timedout = 0L);
+
+private:
+ static void initSocks();
+ friend class KSocketDevice;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/ksrvresolverworker.cpp b/kdecore/network/ksrvresolverworker.cpp
new file mode 100644
index 000000000..bcfee405c
--- /dev/null
+++ b/kdecore/network/ksrvresolverworker.cpp
@@ -0,0 +1,257 @@
+/* -*- C++ -*-
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include "ksrvresolverworker_p.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+
+#include <qapplication.h>
+#include <qevent.h>
+
+using namespace KNetwork;
+using namespace KNetwork::Internal;
+
+namespace
+{
+ struct KSrvStartEvent: public QCustomEvent
+ {
+ inline KSrvStartEvent() : QCustomEvent(QEvent::User) { }
+ };
+}
+
+static void sortPriorityClass(KSrvResolverWorker::PriorityClass&)
+{
+ // do nothing
+}
+
+bool KSrvResolverWorker::preprocess()
+{
+ // check if the resolver flags mention SRV-based lookup
+ if ((flags() & (KResolver::NoSrv | KResolver::UseSrv)) != KResolver::UseSrv)
+ return false;
+
+ QString node = nodeName();
+ if (node.find('%') != -1)
+ node.truncate(node.find('%'));
+
+ if (node.isEmpty() || node == QString::fromLatin1("*") ||
+ node == QString::fromLatin1("localhost"))
+ return false; // empty == localhost
+
+ encodedName = KResolver::domainToAscii(node);
+ if (encodedName.isNull())
+ return false;
+
+ // we only work with Internet-based families
+ if ((familyMask() & KResolver::InternetFamily) == 0)
+ return false;
+
+ // SRV-based resolution only works if the service isn't numeric
+ bool ok;
+ serviceName().toUInt(&ok);
+ if (ok)
+ return false; // it is numeric
+
+ // check the protocol for something we know
+ QCString protoname;
+ int sockettype = socketType();
+ if (!protocolName().isEmpty())
+ protoname = protocolName();
+ else if (protocol() != 0)
+ {
+ QStrList names = KResolver::protocolName(protocol());
+ names.setAutoDelete(true);
+ if (names.isEmpty())
+ return false;
+
+ protoname = "_";
+ protoname += names.at(0);
+ }
+ else if (sockettype == SOCK_STREAM || sockettype == 0)
+ protoname = "_tcp";
+ else if (sockettype == SOCK_DGRAM)
+ protoname = "_udp";
+ else
+ return false; // unknown protocol and socket type
+
+ encodedName.prepend(".");
+ encodedName.prepend(protoname);
+ encodedName.prepend(".");
+ encodedName.prepend(serviceName().latin1());
+ encodedName.prepend("_");
+
+ // looks like something we could process
+ return true;
+}
+
+bool KSrvResolverWorker::run()
+{
+ sem = new QSemaphore(1);
+ // zero out
+ sem->tryAccess(sem->available());
+
+ QApplication::postEvent(this, new KSrvStartEvent);
+
+ // block
+ (*sem)++;
+ delete sem;
+ sem = 0L;
+
+ if (rawResults.isEmpty())
+ {
+ // normal lookup
+ KResolver *r = new KResolver(nodeName(), serviceName());
+ r->setFlags(flags() | KResolver::NoSrv);
+ r->setFamily(familyMask());
+ r->setSocketType(socketType());
+ r->setProtocol(protocol(), protocolName());
+
+ enqueue(r);
+
+ Entry e;
+ PriorityClass cl;
+ e.resolver = r;
+ cl.entries.append(e);
+ myResults[0] = cl;
+
+ return true;
+ }
+ else if (rawResults.count() == 1 && rawResults.first().name == ".")
+ {
+ // no name
+ setError(KResolver::NoName);
+ finished();
+ return true;
+ }
+ else
+ {
+ // now process the results
+ QValueList<QDns::Server>::ConstIterator it = rawResults.begin();
+ while (it != rawResults.end())
+ {
+ const QDns::Server& srv = *it;
+ PriorityClass& r = myResults[srv.priority];
+ r.totalWeight += srv.weight;
+
+ Entry e;
+ e.name = srv.name;
+ e.port = srv.port;
+ e.weight = srv.weight;
+ e.resolver = 0L;
+ r.entries.append(e);
+
+ ++it;
+ }
+ rawResults.clear(); // free memory
+
+ Results::Iterator mapit;
+ for (mapit = myResults.begin(); mapit != myResults.end(); ++mapit)
+ {
+ // sort the priority
+ sortPriorityClass(*mapit);
+
+ QValueList<Entry>& entries = (*mapit).entries;
+
+ // start the resolvers
+ for (QValueList<Entry>::Iterator it = entries.begin();
+ it != entries.end(); ++it)
+ {
+ Entry &e = *it;
+
+ KResolver* r = new KResolver(e.name, QString::number(e.port));
+ r->setFlags(flags() | KResolver::NoSrv);
+ r->setFamily(familyMask());
+ r->setSocketType(socketType());
+ r->setProtocol(protocol(), protocolName());
+
+ enqueue(r);
+ e.resolver = r;
+ }
+ }
+
+ return true;
+ }
+}
+
+bool KSrvResolverWorker::postprocess()
+{
+ setError(KResolver::NoName);
+ if (myResults.isEmpty())
+ return false;
+
+ Results::Iterator mapit, mapend;
+ for (mapit = myResults.begin(), mapend = myResults.end();
+ mapit != mapend; ++mapit)
+ {
+ QValueList<Entry>::Iterator it = (*mapit).entries.begin(),
+ end = (*mapit).entries.end();
+ for ( ; it != end; ++it)
+ {
+ Entry &e = *it;
+ KResolverResults r = e.resolver->results();
+ if (r.isEmpty() && results.isEmpty())
+ setError(r.error(), r.systemError());
+ else
+ {
+ setError(KResolver::NoError);
+ results += r;
+ }
+ }
+ }
+
+ finished();
+ return true;
+}
+
+void KSrvResolverWorker::customEvent(QCustomEvent*)
+{
+ dns = new QDns(QString::fromLatin1(encodedName), QDns::Srv);
+ QObject::connect(dns, SIGNAL(resultsReady()), this, SLOT(dnsResultsReady()));
+}
+
+void KSrvResolverWorker::dnsResultsReady()
+{
+ (*sem)--;
+ rawResults = dns->servers();
+ dns->deleteLater();
+ dns = 0L;
+}
+
+namespace KNetwork
+{
+ namespace Internal
+ {
+
+ void initSrvWorker() KDE_NO_EXPORT;
+ void initSrvWorker()
+ {
+ if (getenv("KDE_NO_SRV") != NULL)
+ return;
+
+ KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KSrvResolverWorker>);
+ }
+
+ }
+}
+
+#include "ksrvresolverworker_p.moc"
diff --git a/kdecore/network/ksrvresolverworker_p.h b/kdecore/network/ksrvresolverworker_p.h
new file mode 100644
index 000000000..143fb0d4b
--- /dev/null
+++ b/kdecore/network/ksrvresolverworker_p.h
@@ -0,0 +1,85 @@
+/* -*- C++ -*-
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KSRVRESOLVERWORKER_P_H
+#define KSRVRESOLVERWORKER_P_H
+
+#include <qobject.h>
+#include <qdns.h>
+#include <qsemaphore.h>
+#include <qvaluelist.h>
+#include <qdict.h>
+#include "kresolver.h"
+#include "kresolverworkerbase.h"
+
+class QCustomEvent;
+
+namespace KNetwork
+{
+ namespace Internal
+ {
+ /**
+ * @internal
+ * This class implements SRV-based resolution
+ */
+ class KSrvResolverWorker: public QObject,
+ public KNetwork::KResolverWorkerBase
+ {
+ Q_OBJECT
+
+ public:
+ struct Entry
+ {
+ QString name;
+ Q_UINT16 port;
+ Q_UINT16 weight;
+ KNetwork::KResolver* resolver;
+ };
+
+ struct PriorityClass
+ {
+ PriorityClass() : totalWeight(0) { }
+
+ QValueList<Entry> entries;
+ Q_UINT16 totalWeight;
+ };
+
+ private:
+ QDns *dns;
+ QValueList<QDns::Server> rawResults;
+ QCString encodedName;
+ QSemaphore *sem;
+
+ typedef QMap<Q_UINT16, PriorityClass> Results;
+ Results myResults;
+
+ public:
+ virtual bool preprocess();
+ virtual bool run();
+ virtual bool postprocess();
+
+ virtual void customEvent(QCustomEvent*);
+
+ public slots:
+ void dnsResultsReady();
+ };
+ }
+}
+
+#endif
diff --git a/kdecore/network/kstreamsocket.cpp b/kdecore/network/kstreamsocket.cpp
new file mode 100644
index 000000000..93c423804
--- /dev/null
+++ b/kdecore/network/kstreamsocket.cpp
@@ -0,0 +1,368 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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 <qdatetime.h>
+#include <qtimer.h>
+#include <qguardedptr.h>
+
+#include "ksocketaddress.h"
+#include "kresolver.h"
+#include "ksocketdevice.h"
+#include "kstreamsocket.h"
+
+using namespace KNetwork;
+
+class KNetwork::KStreamSocketPrivate
+{
+public:
+ KResolverResults::ConstIterator local, peer;
+ QTime startTime;
+ QTimer timer;
+
+ int timeout;
+
+ inline KStreamSocketPrivate()
+ : timeout(0)
+ { }
+};
+
+KStreamSocket::KStreamSocket(const QString& node, const QString& service,
+ QObject* parent, const char *name)
+ : KClientSocketBase(parent, name), d(new KStreamSocketPrivate)
+{
+ peerResolver().setNodeName(node);
+ peerResolver().setServiceName(service);
+ peerResolver().setFamily(KResolver::KnownFamily);
+ localResolver().setFamily(KResolver::KnownFamily);
+
+ setSocketOptions(socketOptions() & ~Blocking);
+
+ QObject::connect(&d->timer, SIGNAL(timeout()), this, SLOT(timeoutSlot()));
+}
+
+KStreamSocket::~KStreamSocket()
+{
+ delete d;
+ // KClientSocketBase's destructor closes the socket
+}
+
+int KStreamSocket::timeout() const
+{
+ return d->timeout;
+}
+
+int KStreamSocket::remainingTimeout() const
+{
+ if (state() != Connecting)
+ return timeout();
+ if (timeout() <= 0)
+ return 0;
+
+ return timeout() - d->startTime.elapsed();
+}
+
+void KStreamSocket::setTimeout(int msecs)
+{
+ d->timeout = msecs;
+
+ if (state() == Connecting)
+ d->timer.changeInterval(msecs);
+}
+
+bool KStreamSocket::bind(const QString& node, const QString& service)
+{
+ if (state() != Idle)
+ return false;
+
+ if (!node.isNull())
+ localResolver().setNodeName(node);
+ if (!service.isNull())
+ localResolver().setServiceName(service);
+ return true;
+}
+
+bool KStreamSocket::connect(const QString& node, const QString& service)
+{
+ if (state() == Connected)
+ return true; // already connected
+
+ if (state() > Connected)
+ return false; // can't do much here
+
+ if (!node.isNull())
+ peerResolver().setNodeName(node);
+ if (!service.isNull())
+ peerResolver().setServiceName(service);
+
+ if (state() == Connecting && !blocking())
+ {
+ setError(IO_ConnectError, InProgress);
+ emit gotError(InProgress);
+ return true; // we're already connecting
+ }
+
+ if (state() < HostFound)
+ {
+ // connection hasn't started yet
+ if (!blocking())
+ {
+ QObject::connect(this, SIGNAL(hostFound()), SLOT(hostFoundSlot()));
+ return lookup();
+ }
+
+ // blocking mode
+ if (!lookup())
+ return false; // lookup failure
+ }
+
+ /*
+ * lookup results are available here
+ */
+
+ if (timeout() > 0)
+ {
+ if (!blocking() && !d->timer.isActive())
+ d->timer.start(timeout(), true);
+ else
+ {
+ // blocking connection with timeout
+ // this must be handled as a special case because it requires a
+ // non-blocking socket
+
+ d->timer.stop(); // no need for a timer here
+
+ socketDevice()->setBlocking(false);
+ while (true)
+ {
+ connectionEvent();
+ if (state() < Connecting)
+ return false; // error connecting
+ if (state() == Connected)
+ return true; // connected!
+
+ if (remainingTimeout() <= 0)
+ {
+ // we've timed out
+ timeoutSlot();
+ return false;
+ }
+
+ if (socketDevice()->error() == InProgress)
+ {
+ bool timedout;
+ socketDevice()->poll(remainingTimeout(), &timedout);
+ if (timedout)
+ {
+ timeoutSlot();
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ connectionEvent();
+ return error() == NoError;
+}
+
+bool KStreamSocket::connect(const KResolverEntry& entry)
+{
+ return KClientSocketBase::connect(entry);
+}
+
+void KStreamSocket::hostFoundSlot()
+{
+ QObject::disconnect(this, SLOT(hostFoundSlot()));
+ if (timeout() > 0)
+ d->timer.start(timeout(), true);
+ QTimer::singleShot(0, this, SLOT(connectionEvent()));
+}
+
+void KStreamSocket::connectionEvent()
+{
+ if (state() != HostFound && state() != Connecting)
+ return; // nothing to do
+
+ const KResolverResults& peer = peerResults();
+ if (state() == HostFound)
+ {
+ d->startTime.start();
+
+ setState(Connecting);
+ emit stateChanged(Connecting);
+ d->peer = peer.begin();
+ d->local = localResults().begin(); // just to be on the safe side
+ }
+
+ while (d->peer != peer.end())
+ {
+ const KResolverEntry &r = *d->peer;
+
+ if (socketDevice()->socket() != -1)
+ {
+ // we have an existing file descriptor
+ // this means that we've got activity in it (connection result)
+ if (socketDevice()->connect(r) && socketDevice()->error() == NoError)
+ {
+ // yes, it did connect!
+ connectionSucceeded(r);
+ return;
+ }
+ else if (socketDevice()->error() == InProgress)
+ // nope, still in progress
+ return;
+
+ // no, the socket failed to connect
+ copyError();
+ socketDevice()->close();
+ ++d->peer;
+ continue;
+ }
+
+ // try to bind
+ if (!bindLocallyFor(r))
+ {
+ // could not find a matching family
+ ++d->peer;
+ continue;
+ }
+
+ {
+ bool skip = false;
+ emit aboutToConnect(r, skip);
+ if (skip)
+ {
+ ++d->peer;
+ continue;
+ }
+ }
+
+ if (socketDevice()->connect(r) || socketDevice()->error() == InProgress)
+ {
+ // socket is attempting to connect
+ if (socketDevice()->error() == InProgress)
+ {
+ QSocketNotifier *n = socketDevice()->readNotifier();
+ QObject::connect(n, SIGNAL(activated(int)),
+ this, SLOT(connectionEvent()));
+ n->setEnabled(true);
+
+ n = socketDevice()->writeNotifier();
+ QObject::connect(n, SIGNAL(activated(int)),
+ this, SLOT(connectionEvent()));
+ n->setEnabled(true);
+
+ return; // wait for activity
+ }
+
+ // socket has connected
+ connectionSucceeded(r);
+ return;
+ }
+
+ // connection failed
+ // try next
+ copyError();
+ socketDevice()->close();
+ ++d->peer;
+ }
+
+ // that was the last item
+ socketDevice()->setSocketOptions(socketOptions());
+ setState(Idle);
+ emit stateChanged(Idle);
+ emit gotError(error());
+ return;
+}
+
+void KStreamSocket::timeoutSlot()
+{
+ if (state() != Connecting)
+ return;
+
+ // halt the connections
+ socketDevice()->close(); // this also kills the notifiers
+
+ setError(IO_TimeOutError, Timeout);
+ setState(HostFound);
+ emit stateChanged(HostFound);
+
+ QGuardedPtr<KStreamSocket> that = this;
+ emit gotError(Timeout);
+ if (!that.isNull())
+ emit timedOut();
+}
+
+bool KStreamSocket::bindLocallyFor(const KResolverEntry& peer)
+{
+ const KResolverResults& local = localResults();
+
+ if (local.isEmpty())
+ // user doesn't want to bind to any specific local address
+ return true;
+
+ bool foundone = false;
+ // scan the local resolution for a matching family
+ for (d->local = local.begin(); d->local != local.end(); ++d->local)
+ if ((*d->local).family() == peer.family())
+ {
+ // found a suitable address!
+ foundone = true;
+
+ if (socketDevice()->bind(*d->local))
+ return true;
+ }
+
+ if (!foundone)
+ {
+ // found nothing
+ setError(IO_BindError, NotSupported);
+ emit gotError(NotSupported);
+ }
+ else
+ copyError();
+ return false;
+}
+
+void KStreamSocket::connectionSucceeded(const KResolverEntry& peer)
+{
+ QObject::disconnect(socketDevice()->readNotifier(), 0, this, SLOT(connectionEvent()));
+ QObject::disconnect(socketDevice()->writeNotifier(), 0, this, SLOT(connectionEvent()));
+
+ resetError();
+ setFlags(IO_Sequential | IO_Raw | IO_ReadWrite | IO_Open | IO_Async);
+ setState(Connected);
+ socketDevice()->setSocketOptions(socketOptions());
+ d->timer.stop();
+ emit stateChanged(Connected);
+
+ if (!localResults().isEmpty())
+ emit bound(*d->local);
+ emit connected(peer);
+}
+
+#include "kstreamsocket.moc"
diff --git a/kdecore/network/kstreamsocket.h b/kdecore/network/kstreamsocket.h
new file mode 100644
index 000000000..d9be79657
--- /dev/null
+++ b/kdecore/network/kstreamsocket.h
@@ -0,0 +1,249 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 Thiago Macieira <thiago@kde.org>
+ *
+ *
+ * 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.
+ */
+
+#ifndef KSTREAMSOCKET_H
+#define KSTREAMSOCKET_H
+
+#include <qstring.h>
+
+#include "kclientsocketbase.h"
+
+/** A namespace to store all networking-related (socket) classes. */
+namespace KNetwork {
+
+class KResolverEntry;
+class KResolverResults;
+class KServerSocket;
+class KBufferedSocket;
+
+class KStreamSocketPrivate;
+/** @class KStreamSocket kstreamsocket.h kstreamsocket.h
+ * @brief Simple stream socket
+ *
+ * This class provides functionality to creating unbuffered, stream
+ * sockets. In the case of Internet (IP) sockets, this class creates and
+ * uses TCP/IP sockets.
+ *
+ * Objects of this class start, by default, on non-blocking mode. Call
+ * setBlocking if you wish to change that.
+ *
+ * KStreamSocket objects are thread-safe and can be used in auxiliary
+ * threads (i.e., not the thread in which the Qt event loop runs in).
+ * Note that KBufferedSocket cannot be used reliably in an auxiliary thread.
+ *
+ * Sample usage:
+ * \code
+ * QByteArray httpGet(const QString& hostname)
+ * {
+ * KStreamSocket socket(hostname, "http");
+ * if (!socket.connect())
+ * return QByteArray();
+ * QByteArray data = socket.readAll();
+ * return data;
+ * }
+ * \endcode
+ *
+ * Here's another sample, showing asynchronous operation:
+ * \code
+ * DataRetriever::DataRetriever(const QString& hostname, const QString& port)
+ * : socket(hostname, port)
+ * {
+ * // connect signals to our slots
+ * QObject::connect(&socket, SIGNAL(connected(const KResolverEntry&)),
+ * this, SLOT(slotSocketConnected()));
+ * QObject::connect(&socket, SIGNAL(gotError(int)),
+ * this, SLOT(slotSocketError(int)));
+ * QObject::connect(&socket, SIGNAL(readyRead()),
+ * this, SLOT(slotSocketReadyToRead()));
+ * QObject::connect(&socket, SIGNAL(readyWrite()),
+ * this, SLOT(slotSocketReadyToWrite()));
+ *
+ * // set non-blocking mode in order to work asynchronously
+ * socket.setBlocking(false);
+ *
+ * // turn on signal emission
+ * socket.enableRead(true);
+ * socket.enableWrite(true);
+ *
+ * // start connecting
+ * socket.connect();
+ * }
+ * \endcode
+ *
+ * @see KNetwork::KBufferedSocket, KNetwork::KServerSocket
+ * @author Thiago Macieira <thiago@kde.org>
+ */
+class KDECORE_EXPORT KStreamSocket: public KClientSocketBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Default constructor.
+ *
+ * @param node destination host
+ * @param service destination service to connect to
+ * @param parent the parent QObject object
+ * @param name name for this object
+ */
+ KStreamSocket(const QString& node = QString::null, const QString& service = QString::null,
+ QObject* parent = 0L, const char *name = 0L);
+
+ /**
+ * Destructor. This closes the socket.
+ */
+ virtual ~KStreamSocket();
+
+ /**
+ * Retrieves the timeout value (in milliseconds).
+ */
+ int timeout() const;
+
+ /**
+ * Retrieves the remaining timeout time (in milliseconds). This value
+ * equals @ref timeout() if there's no connection in progress.
+ */
+ int remainingTimeout() const;
+
+ /**
+ * Sets the timeout value. Setting this value while a connection attempt
+ * is in progress will reset the timer.
+ *
+ * Please note that the timeout value is valid for the connection attempt
+ * only. No other operations are timed against this value -- including the
+ * name lookup associated.
+ *
+ * @param msecs the timeout value in milliseconds
+ */
+ void setTimeout(int msecs);
+
+ /**
+ * Binds this socket to the given nodename and service,
+ * or use the default ones if none are given. In order to bind to a service
+ * and allow the operating system to choose the interface, set @p node to
+ * QString::null.
+ *
+ * Reimplemented from KClientSocketBase.
+ *
+ * Upon successful binding, the @ref bound signal will be
+ * emitted. If an error is found, the @ref gotError
+ * signal will be emitted.
+ *
+ * @note Due to the internals of the name lookup and binding
+ * mechanism, some (if not most) implementations of this function
+ * do not actually bind the socket until the connection
+ * is requested (see @ref connect). They only set the values
+ * for future reference.
+ *
+ * This function returns true on success.
+ *
+ * @param node the nodename
+ * @param service the service
+ */
+ virtual bool bind(const QString& node = QString::null,
+ const QString& service = QString::null);
+
+ /**
+ * Reimplemented from KClientSocketBase. Connect this socket to this
+ * specific address.
+ *
+ * Unlike @ref bind(const QString&, const QString&) above, this function
+ * really does bind the socket. No lookup is performed. The @ref bound
+ * signal will be emitted.
+ */
+ virtual bool bind(const KResolverEntry& entry)
+ { return KClientSocketBase::bind(entry); }
+
+ /**
+ * Reimplemented from KClientSocketBase.
+ *
+ * Attempts to connect to the these hostname and service,
+ * or use the default ones if none are given. If a connection attempt
+ * is already in progress, check on its state and set the error status
+ * (NoError, meaning the connection is completed, or InProgress).
+ *
+ * If the blocking mode for this object is on, this function will only
+ * return when all the resolved peer addresses have been tried or when
+ * a connection is established.
+ *
+ * Upon successfully connecting, the @ref connected signal
+ * will be emitted. If an error is found, the @ref gotError
+ * signal will be emitted.
+ *
+ * This function also implements timeout handling.
+ *
+ * @param node the remote node to connect to
+ * @param service the service on the remote node to connect to
+ */
+ virtual bool connect(const QString& node = QString::null,
+ const QString& service = QString::null);
+
+ /**
+ * Unshadowing from KClientSocketBase.
+ */
+ virtual bool connect(const KResolverEntry& entry);
+
+signals:
+ /**
+ * This signal is emitted when a connection timeout occurs.
+ */
+ void timedOut();
+
+private slots:
+ void hostFoundSlot();
+ void connectionEvent();
+ void timeoutSlot();
+
+private:
+ /**
+ * @internal
+ * If the user requested local bind before connection, bind the socket to one
+ * suitable address and return true. Also sets d->local to the address used.
+ *
+ * Return false in case of error.
+ */
+ bool bindLocallyFor(const KResolverEntry& peer);
+
+ /**
+ * @internal
+ * Finishes the connection process by setting internal values and
+ * emitting the proper signals.
+ *
+ * Note: assumes d->local iterator points to the address that we bound
+ * to.
+ */
+ void connectionSucceeded(const KResolverEntry& peer);
+
+ KStreamSocket(const KStreamSocket&);
+ KStreamSocket& operator=(const KStreamSocket&);
+
+ KStreamSocketPrivate *d;
+
+ friend class KServerSocket;
+ friend class KBufferedSocket;
+};
+
+} // namespace KNetwork
+
+#endif
diff --git a/kdecore/network/syssocket.h b/kdecore/network/syssocket.h
new file mode 100644
index 000000000..e34db1bd1
--- /dev/null
+++ b/kdecore/network/syssocket.h
@@ -0,0 +1,93 @@
+/* -*- C++ -*-
+ * Copyright (C) 2003 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.
+ */
+
+#ifndef KDE_SYSSOCKET_H
+#define KDE_SYSSOCKET_H
+
+#ifdef KSOCKETBASE_H
+#error syssocket.h must be included before ksocketbase.h!
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+namespace {
+
+ /*
+ * These function here are just wrappers for the real system calls.
+ *
+ * Unfortunately, a number of systems out there work by redefining
+ * symbols through the preprocessor -- symbols that we need.
+ *
+ * So we write wrappers for all the low-level system calls.
+ *
+ * Qt has a very similar implementation. I got the idea from them, but
+ * I copied no code.
+ */
+
+ // socket
+ inline int kde_socket(int af, int style, int protocol)
+ {
+ return ::socket(af, style, protocol);
+ }
+
+ // bind
+ inline int kde_bind(int fd, const struct sockaddr* sa, socklen_t len)
+ {
+ return ::bind(fd, sa, len);
+ }
+
+ // listen
+ inline int kde_listen(int fd, int backlog)
+ {
+ return ::listen(fd, backlog);
+ }
+
+ // connect
+ inline int kde_connect(int fd, const struct sockaddr* sa, socklen_t len)
+ {
+ return ::connect(fd, (struct sockaddr*)sa, len);
+ }
+
+ // accept
+ inline int kde_accept(int fd, struct sockaddr* sa, socklen_t* len)
+ {
+ return ::accept(fd, sa, len);
+ }
+
+ // getpeername
+ inline int kde_getpeername(int fd, struct sockaddr* sa, socklen_t* len)
+ {
+ return ::getpeername(fd, sa, len);
+ }
+
+ // getsockname
+ inline int kde_getsockname(int fd, struct sockaddr* sa, socklen_t* len)
+ {
+ return ::getsockname(fd, sa, len);
+ }
+
+} // anonymous namespace
+
+#endif