//////////////////////////////////////////////////////////////////
//
// yasocket.cxx
//
// Copyright (c) Citron Network Inc. 2002-2003
//
// This work is published under the GNU Public License (GPL)
// see file COPYING for details.
// We also explicitely grant the right to link this code
// with the OpenH323 library.
//
// initial author: Chih-Wei Huang <cwhuang@linux.org.tw>
// initial version: 03/14/2003
//
//////////////////////////////////////////////////////////////////

#if defined(_WIN32) && (_MSC_VER <= 1200)
#pragma warning(disable:4284)
#endif

#include <ptlib.h>
#include "h323util.h"
#include "stl_supp.h"
#include "rwlock.h"
#include "yasocket.h"

using std::mem_fun;
using std::bind1st;
using std::partition;
using std::distance;
using std::copy;
using std::back_inserter;
using std::find;

namespace {
/// time to recheck state of closed sockets owned by a proxy handler
const long SOCKETSREADER_IDLE_TIMEOUT = 1000;
}

#ifdef LARGE_FDSET

bool YaSelectList::Select(SelectType t, const PTimeInterval & timeout)
{
	large_fd_set fdset;
	// add handles to the fdset
	const_iterator i = fds.begin();
	const_iterator endIter = fds.end();
	while (i != endIter)
		fdset.add((*i++)->GetHandle());
	
	fd_set *readfds, *writefds;
	if (t == Read)
		readfds = fdset, writefds = 0;
	else
		writefds = fdset, readfds = 0;

	const unsigned long msec = timeout.GetInterval();
	struct timeval tval;
	tval.tv_sec  = msec / 1000;
	tval.tv_usec = (msec - tval.tv_sec * 1000) * 1000;
	int r = ::select(maxfd + 1, readfds, writefds, 0, &tval);
	if (r > 0) {
#if 1
		std::vector<YaSocket*>::iterator last = remove_if(
			fds.begin(), fds.end(),
			not1(compose1(
				bind1st(mem_fun(&large_fd_set::has), &fdset), 
				mem_fun(&YaSocket::GetHandle)
				)));
		fds.erase(last, fds.end());
#else
		/* This unrolled implementation of the above code may give
		   another 10-15% of performance gain. As it is not much under normal
		   conditions, I leave it for thouse who want to squeeze a few more
		   calls from their proxies;-)
		   
		   I did some performance tests (Duron 1.1GHz) with simulation of
		   various fds selected sockets coverage (10%, 33%, 50%, 75%, 90%):
		   
		   LARGE_FDSET=1024  - 12% performance gain
		   LARGE_FDSET=4096  - 15% performance gain
		   LARGE_FDSET=16384 - 13% performance gain
		   
		   For LARGE_FDSET=4096 it took less than 1ms to manipulate the fdset
		   and this grows in a linear fashion (LARGE_FDSET=16384 takes a few
		   milliseconds to perform the same task).
		*/
		std::vector<YaSocket*>::reverse_iterator j = fds.rbegin();
		std::vector<YaSocket*>::iterator k = fds.end();
		const std::vector<YaSocket*>::reverse_iterator rendIter = fds.rend();
		bool hasfd = false;
		
		// start from the end of the list, skip consecutive sockets 
		// that were not selected (find the first one selected)
		while (j != rendIter) {
			k--;
			if (fdset.has((*j)->GetHandle())) {
				hasfd = true;
				break;
			} else
				++j;
		}
		// reorder remaining sockets, so non-selected sockets 
		// are moved to the end of the vector
		if (hasfd) {
			while (++j != rendIter) {
				if (!fdset.has((*j)->GetHandle()))
					*j = *k--;
			}
			// at this point the vector [begin(),k] should contain
			// all selected sockets, so erase the remaining vector elements
			fds.erase(++k, fds.end());
		} else
			fds.clear();
#endif
	} else if (r < 0)
		PTRACE(3, GetName() << "\tSelect " << (t == Read ? "read" : "write") << " error - errno: " << errno);
	return r > 0;
}


// class YaSocket
YaSocket::YaSocket() : os_handle(-1)
{
	lastReadCount = lastWriteCount = 0;
}

YaSocket::~YaSocket()
{
	Close();
}

bool YaSocket::Close()
{
	if (!IsOpen())
		return false;

	// send a shutdown to the other end
	int handle = os_handle;
	os_handle = -1;
	::shutdown(handle, SHUT_RDWR);
#ifdef _WIN32
	::closesocket(handle);
#else
	::close(handle);
#endif
	return true;
}

bool YaSocket::Read(void *buf, int sz)
{
	int r = os_recv(buf, sz);
	lastReadCount = ConvertOSError(r, PSocket::LastReadError) ? r : 0;
	return lastReadCount > 0;
}

bool YaSocket::ReadBlock(void *buf, int len)
{
	// lazy implementation, but it is enough for us...
	return Read(buf, len) && lastReadCount == len;
}

bool YaSocket::CanRead(
	long timeout
	) const
{
	const int h = os_handle;
	if (h < 0)
		return false;

	YaSelectList::large_fd_set fdset;
	fdset.add(h);

	struct timeval tval;
	tval.tv_sec  = timeout / 1000;
	tval.tv_usec = (timeout - tval.tv_sec * 1000) * 1000;
	return ::select(h + 1, (fd_set*)fdset, NULL, NULL, &tval) > 0;
}

bool YaSocket::CanWrite(
	long timeout
	) const
{
	const int h = os_handle;
	if (h < 0)
		return false;
		
	YaSelectList::large_fd_set fdset;
	fdset.add(h);

	struct timeval tval;
	tval.tv_sec  = timeout / 1000;
	tval.tv_usec = (timeout - tval.tv_sec * 1000) * 1000;
	return ::select(h + 1, NULL, (fd_set*)fdset, NULL, &tval) > 0;
}

bool YaSocket::Write(const void *buf, int sz)
{
	lastWriteCount = 0;
	if (!CanWrite(writeTimeout.GetInterval())) {
		errno = EAGAIN;
		return ConvertOSError(-1, PSocket::LastWriteError);
	}
	int r = os_send(buf, sz);
	if (ConvertOSError(r, PSocket::LastWriteError))
		lastWriteCount = r;
	return lastWriteCount == sz;
}

void YaSocket::GetLocalAddress(Address & addr) const
{
	WORD pt;
	GetLocalAddress(addr, pt);
}

void YaSocket::GetLocalAddress(Address & addr, WORD & pt) const
{
	sockaddr_in inaddr;
	socklen_t insize = sizeof(inaddr);
	if (::getsockname(os_handle, (struct sockaddr*)&inaddr, &insize) == 0) {
		addr = inaddr.sin_addr;
		pt = ntohs(inaddr.sin_port);
	}
}

bool YaSocket::SetOption(int option, int value, int level)
{
	return ConvertOSError(::setsockopt(os_handle, level, option, (char *)&value, sizeof(int)));
}

bool YaSocket::SetOption(int option, const void *value, int size, int level)
{
	return ConvertOSError(::setsockopt(os_handle, level, option, (char *)value, size));
}

bool YaSocket::GetOption(int option, int & value, int level)
{
	socklen_t valSize = sizeof(value);
	return ConvertOSError(::getsockopt(os_handle, level, option, (char *)&value, &valSize));
}

bool YaSocket::GetOption(int option, void * valuePtr, PINDEX valueSize, int level)
{
	return ConvertOSError(::getsockopt(os_handle, level, option,
		(char *)valuePtr, (socklen_t *)&valueSize)
		);
}

PString YaSocket::GetErrorText(PSocket::ErrorGroup group) const
{
	return PSocket::GetErrorText(GetErrorCode(group));
}

bool YaSocket::ConvertOSError(int libReturnValue, PSocket::ErrorGroup group)
{
	if (libReturnValue < 0 && errno == EAGAIN) {
		lastErrorCode[group] = PSocket::Timeout;
		lastErrorNumber[group] = errno;
		return false;
	}
	return PSocket::ConvertOSError(libReturnValue, lastErrorCode[group], lastErrorNumber[group]);
}

bool YaSocket::SetNonBlockingMode()
{
	if (!IsOpen())
		return false;
	// is call to F_SETFD with F_CLOEXEC really neccessary?
	int cmd = 1;
	if (ConvertOSError(::ioctl(os_handle, FIONBIO, &cmd))
			&& ConvertOSError(::fcntl(os_handle, F_SETFD, 1)))
		return true;
	Close();
	return false;

}

bool YaSocket::Bind(const Address & addr, WORD pt)
{
	if (IsOpen()) {
		sockaddr_in inaddr;
		memset(&inaddr, 0, sizeof(inaddr));
		inaddr.sin_family = AF_INET;
		inaddr.sin_addr.s_addr = addr;
		inaddr.sin_port = htons(pt);
		if (ConvertOSError(::bind(os_handle, (struct sockaddr *)&inaddr, sizeof(inaddr)))) {
			socklen_t insize = sizeof(inaddr);
			if (::getsockname(os_handle, (struct sockaddr *)&inaddr, &insize) == 0) {
				port = ntohs(inaddr.sin_port);
				return true;
			}
		}
	}
	return false;
}


// class YaTCPSocket
YaTCPSocket::YaTCPSocket(WORD pt)
{
	peeraddr.sin_family = AF_INET;
	SetPort(pt);
}

void YaTCPSocket::GetPeerAddress(Address & addr) const
{
	addr = peeraddr.sin_addr;
}

void YaTCPSocket::GetPeerAddress(Address & addr, WORD & pt) const
{
	addr = peeraddr.sin_addr;
	pt = ntohs(peeraddr.sin_port);
}

bool YaTCPSocket::SetLinger()
{
	SetOption(TCP_NODELAY, 1, IPPROTO_TCP);
	const linger ling = { 1, 3 };
	return SetOption(SO_LINGER, &ling, sizeof(ling));
}

bool YaTCPSocket::Listen(unsigned qs, WORD pt, PSocket::Reusability reuse)
{
	return Listen(INADDR_ANY, qs, pt, reuse);
}

bool YaTCPSocket::Listen(const Address & addr, unsigned qs, WORD pt, PSocket::Reusability reuse)
{
	os_handle = ::socket(PF_INET, SOCK_STREAM, 0);
	if (!ConvertOSError(os_handle))
		return false;

	if (!SetOption(SO_REUSEADDR, reuse == PSocket::CanReuseAddress ? 1 : 0))
		return false;
	
//	SetNonBlockingMode();
	if (Bind(addr, pt) && ConvertOSError(::listen(os_handle, qs)))
		return true;
	Close();
	return false;
}

bool YaTCPSocket::Accept(YaTCPSocket & socket)
{
	while (true) {
		int fd = socket.GetHandle();
		if (fd < 0) { // socket closed
			errno = ENOTSOCK;
			break;
		}

		YaSelectList::large_fd_set fdset;
		fdset.add(fd);
		struct timeval tval = { 1, 0 };
		int r = ::select(fd + 1, fdset, 0, 0, &tval);
		if (r < 0)
			break;
		else if (r == 0)
			continue;

		socklen_t addrsize = sizeof(peeraddr);
		os_handle = ::accept(fd, (struct sockaddr *)&peeraddr, &addrsize);
		if (os_handle < 0)
			break;

		SetLinger();
		SetNonBlockingMode();
		SetWriteTimeout(PTimeInterval(10));
		SetName(AsString(peeraddr.sin_addr, ntohs(peeraddr.sin_port)));
		port = socket.GetPort();
		return true;
	}
	return ConvertOSError(-1);
}

bool YaTCPSocket::Connect(const Address & iface, WORD localPort, const Address & addr)
{
	if (os_handle < 0) {
		os_handle = ::socket(PF_INET, SOCK_STREAM, 0);
		if (!ConvertOSError(os_handle))
			return false;
	}

	SetOption(SO_REUSEADDR, 0);
	
	int optval;
	socklen_t optlen = sizeof(optval);

	WORD peerPort = port;
	// bind local interface and port
	if (iface != INADDR_ANY || localPort != 0)
		if (!Bind(iface, localPort))
			return false;

	// connect in non-blocking mode
	SetNonBlockingMode();
	SetWriteTimeout(PTimeInterval(10));
	peeraddr.sin_addr = addr;
	peeraddr.sin_port = htons(port = peerPort);
	SetName(AsString(addr, port));

	int r = ::connect(os_handle, (struct sockaddr *)&peeraddr, sizeof(peeraddr));
#ifdef _WIN32
	if ((r != 0) && (WSAGetLastError() != WSAEWOULDBLOCK))
#else
	if (r == 0 || errno != EINPROGRESS)
#endif
		return ConvertOSError(r);

	YaSelectList::large_fd_set fdset;
	fdset.add(os_handle);
	YaSelectList::large_fd_set exset = fdset;
	struct timeval tval = { 6, 0 }; // TODO: read from config...
	if (::select(os_handle + 1, 0, fdset, exset, &tval) > 0) {
		optval = -1;
		::getsockopt(os_handle, SOL_SOCKET, SO_ERROR, &optval, &optlen);
		if (optval == 0) // connected
			return SetLinger();
		errno = optval;
	}
	return ConvertOSError(-1);
}

bool YaTCPSocket::Connect(const Address & addr)
{
	return YaTCPSocket::Connect(INADDR_ANY, 0, addr);
}

int YaTCPSocket::os_recv(void *buf, int sz)
{
#if HAS_MSG_NOSIGNAL
	return ::recv(os_handle, buf, sz, MSG_NOSIGNAL);
#else
	return ::recv(os_handle, buf, sz, 0);
#endif
}

int YaTCPSocket::os_send(const void *buf, int sz)
{
#if HAS_MSG_NOSIGNAL
	return ::send(os_handle, buf, sz, MSG_NOSIGNAL);
#else
	return ::send(os_handle, buf, sz, 0);
#endif
}


// class YaUDPSocket
YaUDPSocket::YaUDPSocket()
{
	sendaddr.sin_family = AF_INET;
	sendaddr.sin_port = 0;
}

bool YaUDPSocket::Listen(unsigned, WORD pt, PSocket::Reusability reuse)
{
	return Listen(INADDR_ANY, 0, pt, reuse);
}

bool YaUDPSocket::Listen(const Address & addr, unsigned, WORD pt, PSocket::Reusability reuse)
{
	os_handle = ::socket(PF_INET, SOCK_DGRAM, 0);
	if (!ConvertOSError(os_handle))
		return false;

	if (!SetNonBlockingMode())
		return false;
	if (!SetOption(SO_REUSEADDR, reuse == PSocket::CanReuseAddress ? 1 : 0))
		return false;
	return Bind(addr, pt);
}

void YaUDPSocket::GetLastReceiveAddress(Address & addr, WORD & pt) const
{
	addr = recvaddr.sin_addr;
	pt = ntohs(recvaddr.sin_port);
}

void YaUDPSocket::SetSendAddress(const Address & addr, WORD pt)
{
	sendaddr.sin_addr = addr;
	sendaddr.sin_port = htons(pt);
}

void YaUDPSocket::GetSendAddress(
	Address& address, /// IP address to send packets.
	WORD& port /// Port to send packets.
	)
{
	address = sendaddr.sin_addr;
	port = ntohs(sendaddr.sin_port);
}

bool YaUDPSocket::ReadFrom(void *buf, PINDEX len, Address & addr, WORD pt)
{
	bool result = Read(buf, len);
	if (result)
		GetLastReceiveAddress(addr, pt);
	return result;
}

bool YaUDPSocket::WriteTo(const void *buf, PINDEX len, const Address & addr, WORD pt)
{
	SetSendAddress(addr, pt);
	return Write(buf, len);
}

int YaUDPSocket::os_recv(void *buf, int sz)
{
	socklen_t addrlen = sizeof(recvaddr);
	return ::recvfrom(os_handle, buf, sz, 0, (struct sockaddr *)&recvaddr, &addrlen);
}

int YaUDPSocket::os_send(const void *buf, int sz)
{
	return ::sendto(os_handle, buf, sz, 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
}

#else // LARGE_FDSET

bool SocketSelectList::Select(SelectType t, const PTimeInterval & timeout)
{
	if (IsEmpty())
		return false;
	SocketSelectList dumb, *rlist, *wlist;
	if (t == Read)
		rlist = this, wlist = &dumb;
	else
		wlist = this, rlist = &dumb;
	const PSocket::Errors r = PSocket::Select(*rlist, *wlist, timeout);
	if (r != PSocket::NoError) {
		PTRACE(3, GetName() << "\tSelect " << (t == Read ? "read" : "write") << " error: " << r);
		return false;
	} else
		return !IsEmpty();
}

PSocket *SocketSelectList::operator[](int i) const
{
	typedef PSocket::SelectList PSocketSelectList; // stupid VC...
	return &PSocketSelectList::operator[](i);
}

#endif // LARGE_FDSET


// class USocket
USocket::USocket(IPSocket *s, const char *t) 
	: self(s), qsize(0), blocked(false), type(t)
{
}

USocket::~USocket()
{
	//PWaitAndSignal lock(writeMutex);
	{
		PWaitAndSignal lock(queueMutex);
		DeleteObjectsInContainer(queue);
		queue.clear();
		qsize = 0;
	}
	PIPSocket::Address addr(0);
	WORD port = 0;
	self->GetLocalAddress(addr, port);
	PTRACE(3, type << "\tDelete socket " << Name());
}

bool USocket::TransmitData(const PBYTEArray & buf)
{
	return WriteData(buf, buf.GetSize());
}

bool USocket::TransmitData(const PString & str)
{
	return WriteData(str, str.GetLength());
}

bool USocket::TransmitData(const BYTE *buf, int len)
{
	return WriteData(buf, len);
}

bool USocket::Flush()
{
	bool result = true;
	PWaitAndSignal lock(writeMutex);
	while (result && qsize > 0) {
		PBYTEArray* const pdata = PopQueuedPacket();
		if (pdata) {
			result = InternalWriteData(*pdata, pdata->GetSize());
			PTRACE_IF(4, result, type << '\t' << pdata->GetSize() << " bytes flushed to " << Name());
			delete pdata;
		} else
			break;
	}
	return result;
}

bool USocket::WriteData(const BYTE *buf, int len)
{
	if (IsSocketOpen()) {
		if (qsize == 0 && !writeMutex.WillBlock()) {
			PWaitAndSignal lock(writeMutex);
			return InternalWriteData(buf, len);
		}
		if (qsize > 100) { // to be justitied
			PTRACE(2, type << '\t' << Name() << " is dead and closed");
			CloseSocket();
		} else {
			PTRACE(3, type << '\t' << Name() << " is busy, " << len << " bytes queued");
			QueuePacket(buf, len);
		}
	}
	return false;
}

bool USocket::ErrorHandler(PSocket::ErrorGroup group)
{
	PSocket::Errors e = self->GetErrorCode(group);

	PString msg(PString(type) + "\t" + Name());
	switch (e)
	{
		case PSocket::Timeout:
			PTRACE(4, msg << " Error(" << group << "): Timeout");
			break;
		case PSocket::NoError:
			if (group == PSocket::LastReadError) {
				PTRACE(5, msg << " closed by remote");
				CloseSocket();
				break;
			}
		default:
			PTRACE(3, msg << " Error(" << group << "): " 
				<< PSocket::GetErrorText(e) << " (" << e << ':'
				<< self->GetErrorNumber(group) << ')'
				);
			CloseSocket();
			break;
	}
	return false;
}

bool USocket::InternalWriteData(const BYTE *buf, int len)
{
	if (self->Write(buf, len)) {
		PTRACE(6, Name() << ' ' << len << " bytes sent");
		return true;
	}

	int wcount = self->GetLastWriteCount();
	buf += wcount, len -= wcount;

	if (wcount == 0)
		ErrorHandler(PSocket::LastWriteError);
	if (IsSocketOpen()) {
		PTRACE(4, type << '\t' << Name() << " blocked, " << wcount << " bytes written, " << len << " bytes queued");
		// push_front used intentionally, as InternalWriteData can be called
		// either when flushing the queue (so any remaining unflushed data
		// should be put back at the queue front) or when the queue is epmty
		PWaitAndSignal lock(queueMutex);
		queue.push_front(new PBYTEArray(buf, len));
		++qsize;
	}
	return false;
}

void USocket::ClearQueue()
{
	queueMutex.Wait();
	DeleteObjectsInContainer(queue);
	queue.clear();
	qsize = 0;
	queueMutex.Signal();
}


// class SocketsReader
SocketsReader::SocketsReader(int t) : m_timeout(t), m_socksize(0), m_rmsize(0)
{
	SetName("SockRdr");
}

SocketsReader::~SocketsReader()
{
	RemoveClosed(false);
	SocketsReader::CleanUp();
	//DeleteObjectsInContainer(m_removed);
	//DeleteObjectsInContainer(m_sockets);
}

void SocketsReader::Stop()
{
	PWaitAndSignal lock(m_deletionPreventer);
	ReadLock llock(m_listmutex);
	ForEachInContainer(m_sockets, mem_fun(&IPSocket::Close));
	RegularJob::Stop();
}

void SocketsReader::AddSocket(IPSocket *socket)
{
	m_listmutex.StartWrite();
	iterator iter = find(m_sockets.begin(), m_sockets.end(), socket);
	if (iter == m_sockets.end()) {
		m_sockets.push_back(socket);
		++m_socksize;
	} else
		PTRACE(1, GetName() << "\tTrying to add an already existing socket to the handler");
	m_listmutex.EndWrite();
	Signal();
	PTRACE(5, GetName() << "\tTotal sockets: " << m_socksize);
}

bool SocketsReader::BuildSelectList(SocketSelectList & slist)
{
	ReadLock lock(m_listmutex);
	ForEachInContainer(m_sockets, bind1st(mem_fun(&SocketSelectList::Append), &slist));
	return !slist.IsEmpty();
}

void SocketsReader::CleanUp()
{
	PWaitAndSignal lock(m_rmutex);
	DeleteObjectsInContainer(m_removed);
	m_removed.clear();
	m_rmsize = 0;
}

bool SocketsReader::SelectSockets(SocketSelectList & slist)
{
#if PTRACING
	int ss = slist.GetSize();
#endif
	ConfigReloadMutex.EndRead();
	if (!slist.Select(SocketSelectList::Read, m_timeout)) {
		ConfigReloadMutex.StartRead();
		return false;
	}
	ConfigReloadMutex.StartRead();
#if PTRACING
	PString msg(PString::Printf, "\t%u sockets selected from %u, total %u/%u", slist.GetSize(), ss, m_socksize, m_rmsize);
	PTRACE(5, GetName() << msg);
#endif
	return true;
}

void SocketsReader::RemoveClosed(bool bDeleteImmediately)
{
	WriteLock lock(m_listmutex);
	iterator iter = partition(m_sockets.begin(), m_sockets.end(), mem_fun(&IPSocket::IsOpen));
	if (ptrdiff_t rmsize = distance(iter, m_sockets.end())) {
		if (bDeleteImmediately)
			DeleteObjects(iter, m_sockets.end());
		else {
			PWaitAndSignal lock(m_rmutex);
			copy(iter, m_sockets.end(), back_inserter(m_removed));
			m_rmsize += rmsize;
		}
		m_sockets.erase(iter, m_sockets.end());
		m_socksize -= rmsize;
	}
}
/*
void SocketsReader::RemoveSocket(iterator i)
{
	m_sockets.erase(i);
	--m_socksize;
	PWaitAndSignal lock(m_rmutex);
	m_removed.push_back(*i);
	++m_rmsize;
}

void SocketsReader::RemoveSocket(IPSocket *s)
{
	m_sockets.remove(s);
	--m_socksize;
	PWaitAndSignal lock(m_rmutex);
	m_removed.push_back(s);
	++m_rmsize;
}
*/
void SocketsReader::Exec()
{
	ReadLock cfglock(ConfigReloadMutex);
	SocketSelectList slist(GetName());

	if (BuildSelectList(slist)) {
		if (SelectSockets(slist)) {
			int ss = slist.GetSize();
			for (int i = 0; i < ss; ++i)
#ifdef LARGE_FDSET
				ReadSocket(slist[i]);
#else
				ReadSocket(dynamic_cast<IPSocket *>(slist[i]));
#endif
		}
		CleanUp();
	} else {
		CleanUp();
		ConfigReloadMutex.EndRead();
		PTRACE(6, GetName() << " waiting...");
		Wait(SOCKETSREADER_IDLE_TIMEOUT);
		ConfigReloadMutex.StartRead();
	}
}


// class TCPListenSocket
TCPListenSocket::TCPListenSocket(int timeout)
{
	if (timeout > 0)
		SetReadTimeout(timeout * 1000);
}

TCPListenSocket::~TCPListenSocket()
{
	PTRACE(3, "TCP\tDelete listener " << GetName());
}

bool TCPListenSocket::IsTimeout(const PTime *now) const
{
	if (readTimeout < PMaxTimeInterval)
		return IsOpen() ? ((readTimeout > 0) ? ((*now - start) > readTimeout) : false) : true;
	else
		return !IsOpen();
}


// class TCPServer
TCPServer::TCPServer()
{
	SetName("TCPSrv");
	Execute();
}

bool TCPServer::CloseListener(TCPListenSocket *socket)
{
	ReadLock lock(m_listmutex);
	iterator iter = find(m_sockets.begin(), m_sockets.end(), socket);
	if (iter != m_sockets.end()) {
		PTRACE(6, GetName() << "\tListener " << (*iter)->GetName() << " closed");
		(*iter)->Close();
		return true;
	} else
		return false;
}

void TCPServer::ReadSocket(IPSocket *socket)
{
	PTRACE(4, GetName() << "\tAccept request on " << socket->GetName());
	TCPListenSocket *listener = dynamic_cast<TCPListenSocket *>(socket);
	ServerSocket *acceptor = listener->CreateAcceptor();
	if (acceptor->Accept(*listener)) {
		PTRACE(6, GetName() << "\tAccepted new connection on " << socket->GetName() << " from " << acceptor->GetName());
		CreateJob(acceptor, &ServerSocket::Dispatch, "Acceptor");
	} else {
		PTRACE(4, GetName() << "\tAccept failed on " << socket->GetName());
		delete acceptor;
	}
}

void TCPServer::CleanUp()
{
	PTime now;
	WriteLock lock(m_listmutex);
	iterator iter = m_sockets.begin(), eiter = m_sockets.end();
	while (iter != eiter) {
		iterator i = iter++;
		TCPListenSocket *listener = dynamic_cast<TCPListenSocket *>(*i);
		if (listener && listener->IsTimeout(&now)) {
			m_sockets.erase(i);
			--m_socksize;
			delete listener;
		}
	}
}


syntax highlighted by Code2HTML, v. 0.9.1