You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/kdecore/network/kserversocket.cpp

414 lines
9.8 KiB

/* -*- 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"