Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / connection.c
dissimilarity index 85%
index ec5e92e..cfbc093 100644 (file)
-/*
- *  WINGs WMConnection function library
- *
- *  Copyright (c) 1999-2003 Dan Pascu
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-
-/*
- * TODO:
- * - decide if we want to support connections with external sockets, else
- *   clean up the structure of the unneeded members.
- * - decide what to do with all wwarning() calls that are still there.
- *
- */
-
-
-#include "wconfig.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <signal.h>
-#ifdef __FreeBSD__
-#include <sys/signal.h>
-#endif
-
-#include "WINGs.h"
-
-
-/* Some older systems does not define this (linux libc5, maybe others too) */
-#ifndef SHUT_RDWR
-# define SHUT_RDWR              2
-#endif
-
-/* For SunOS */
-#ifndef SA_RESTART
-# define SA_RESTART             0
-#endif
-
-/* For Solaris */
-#ifndef INADDR_NONE
-# define INADDR_NONE           -1
-#endif
-
-/* Stuff for setting the sockets into non-blocking mode. */
-/*
-#ifdef __POSIX_SOURCE
-# define NONBLOCK_OPT           O_NONBLOCK
-#else
-# define NONBLOCK_OPT           FNDELAY
-#endif
-*/
-
-#define NONBLOCK_OPT            O_NONBLOCK
-
-#define NETBUF_SIZE             4096
-
-#define DEF_TIMEOUT             600   /* 600 seconds == 10 minutes */
-
-
-
-int WCErrorCode = 0;
-
-static Bool SigInitialized = False;
-
-static unsigned int DefaultTimeout = DEF_TIMEOUT;
-static unsigned int OpenTimeout = DEF_TIMEOUT;
-
-
-
-typedef struct TimeoutData {
-    unsigned timeout;
-    WMHandlerID *handler;
-} TimeoutData;
-
-
-typedef struct W_Connection {
-    int sock;                     /* the socket we speak through */
-
-    struct {
-        WMHandlerID *read;        /* the input read handler */
-        WMHandlerID *write;       /* the input write handler */
-        WMHandlerID *exception;   /* the input exception handler */
-    } handler;
-
-    ConnectionDelegate *delegate; /* client delegates */
-    void *clientData;             /* client data */
-    unsigned int uflags;          /* flags for the client */
-
-    WMArray *outputQueue;
-    unsigned bufPos;
-
-    TimeoutData sendTimeout;
-    TimeoutData openTimeout;
-
-    WMConnectionState state;
-    WMConnectionTimeoutState timeoutState;
-
-    char *address;
-    char *service;
-    char *protocol;
-
-    Bool closeOnRelease;
-    Bool shutdownOnClose;
-    Bool wasNonBlocking;
-    Bool isNonBlocking;
-
-} W_Connection;
-
-
-
-static void
-clearOutputQueue(WMConnection *cPtr)
-{
-    cPtr->bufPos = 0;
-    WMEmptyArray(cPtr->outputQueue);
-}
-
-
-static void
-openTimeout(void *cdata)
-{
-    WMConnection *cPtr = (WMConnection*) cdata;
-
-    cPtr->openTimeout.handler = NULL;
-    if (cPtr->handler.write) {
-        WMDeleteInputHandler(cPtr->handler.write);
-        cPtr->handler.write = NULL;
-    }
-    if (cPtr->state != WCConnected) {
-        cPtr->state = WCTimedOut;
-        cPtr->timeoutState = WCTWhileOpening;
-        if (cPtr->delegate && cPtr->delegate->didTimeout) {
-            (*cPtr->delegate->didTimeout)(cPtr->delegate, cPtr);
-        } else {
-            WMCloseConnection(cPtr);
-            cPtr->state = WCTimedOut; /* the above set state to WCClosed */
-        }
-    }
-}
-
-
-static void
-sendTimeout(void *cdata)
-{
-    WMConnection *cPtr = (WMConnection*) cdata;
-
-    cPtr->sendTimeout.handler = NULL;
-    if (cPtr->handler.write) {
-        WMDeleteInputHandler(cPtr->handler.write);
-        cPtr->handler.write = NULL;
-    }
-    if (WMGetArrayItemCount(cPtr->outputQueue)>0) {
-        clearOutputQueue(cPtr);
-        cPtr->state = WCTimedOut;
-        cPtr->timeoutState = WCTWhileSending;
-        /* // should we close it no matter what (after calling the didTimeout
-           // delegate)? -Dan */
-        if (cPtr->delegate && cPtr->delegate->didTimeout) {
-            (*cPtr->delegate->didTimeout)(cPtr->delegate, cPtr);
-        } else {
-            WMCloseConnection(cPtr);
-            cPtr->state = WCTimedOut; /* the above set state to WCClosed */
-        }
-    }
-}
-
-
-static void
-inputHandler(int fd, int mask, void *clientData)
-{
-    WMConnection *cPtr = (WMConnection*)clientData;
-
-    if (cPtr->state==WCClosed || cPtr->state==WCDied)
-        return;
-
-    if ((mask & WIWriteMask)) {
-        int result;
-
-        if (cPtr->state == WCInProgress) {
-            Bool failed;
-            socklen_t len = sizeof(result);
-
-            WCErrorCode = 0;
-            if (getsockopt(cPtr->sock, SOL_SOCKET, SO_ERROR,
-                           (void*)&result, &len) == 0 && result != 0) {
-                cPtr->state = WCFailed;
-                WCErrorCode = result;
-                failed = True;
-                /* should call wsyserrorwithcode(result, ...) here? */
-            } else {
-                cPtr->state = WCConnected;
-                failed = False;
-            }
-
-            if (cPtr->handler.write) {
-                WMDeleteInputHandler(cPtr->handler.write);
-                cPtr->handler.write = NULL;
-            }
-
-            if (cPtr->openTimeout.handler) {
-                WMDeleteTimerHandler(cPtr->openTimeout.handler);
-                cPtr->openTimeout.handler = NULL;
-            }
-
-            if (cPtr->delegate && cPtr->delegate->didInitialize)
-                (*cPtr->delegate->didInitialize)(cPtr->delegate, cPtr);
-
-            /* we use failed and not cPtr->state here, because cPtr may be
-             * destroyed by the delegate called above if the connection failed
-             */
-            if (failed)
-                return;
-        } else if (cPtr->state == WCConnected) {
-            result = WMFlushConnection(cPtr);
-            if (result>0 && cPtr->delegate && cPtr->delegate->canResumeSending) {
-                (*cPtr->delegate->canResumeSending)(cPtr->delegate, cPtr);
-            }
-        }
-    }
-
-    if (!cPtr->delegate)
-        return;
-
-    /* if the connection died, may get destroyed in the delegate, so retain */
-    wretain(cPtr);
-
-    if ((mask & WIReadMask) && cPtr->delegate->didReceiveInput)
-        (*cPtr->delegate->didReceiveInput)(cPtr->delegate, cPtr);
-
-    if ((mask & WIExceptMask) && cPtr->delegate->didCatchException)
-        (*cPtr->delegate->didCatchException)(cPtr->delegate, cPtr);
-
-    wrelease(cPtr);
-}
-
-
-static Bool
-setSocketNonBlocking(int sock, Bool flag)
-{
-    int state;
-    Bool isNonBlock;
-
-    state = fcntl(sock, F_GETFL, 0);
-
-    if (state < 0) {
-        /* set WCErrorCode here? -Dan*/
-        return False;
-    }
-
-    isNonBlock = (state & NONBLOCK_OPT) != 0;
-
-    if (flag) {
-        if (isNonBlock)
-            return True;
-        state |= NONBLOCK_OPT;
-    } else {
-        if (!isNonBlock)
-            return True;
-        state &= ~NONBLOCK_OPT;
-    }
-
-    if (fcntl(sock, F_SETFL, state) < 0) {
-        /* set WCErrorCode here? -Dan*/
-        return False;
-    }
-
-    return True;
-}
-
-
-static void
-setConnectionAddress(WMConnection *cPtr, struct sockaddr_in *socketaddr)
-{
-    wassertr(cPtr->address==NULL);
-
-    cPtr->address = wstrdup(inet_ntoa(socketaddr->sin_addr));
-    cPtr->service = wmalloc(16);
-    sprintf(cPtr->service, "%hu", ntohs(socketaddr->sin_port));
-    cPtr->protocol = wstrdup("tcp");
-}
-
-
-static struct sockaddr_in*
-getSocketAddress(char* name, char* service, char* protocol)
-{
-    static struct sockaddr_in socketaddr;
-    struct servent *sp;
-
-    if (!protocol || protocol[0]==0)
-        protocol = "tcp";
-
-    memset(&socketaddr, 0, sizeof(struct sockaddr_in));
-    socketaddr.sin_family = AF_INET;
-
-    /*
-     * If we were given a hostname, we use any address for that host.
-     * Otherwise we expect the given name to be an address unless it is
-     * NULL (any address).
-     */
-    if (name && name[0]!=0) {
-        WMHost *host = WMGetHostWithName(name);
-
-        if (!host)
-            return NULL; /* name is not a hostname nor a number and dot adr */
-
-        name = WMGetHostAddress(host);
-#ifndef        HAVE_INET_ATON
-        if ((socketaddr.sin_addr.s_addr = inet_addr(name)) == INADDR_NONE) {
-#else
-            if (inet_aton(name, &socketaddr.sin_addr) == 0) {
-#endif
-            WMReleaseHost(host);
-            return NULL;
-        }
-        WMReleaseHost(host);
-    } else {
-        socketaddr.sin_addr.s_addr = htonl(INADDR_ANY);
-    }
-
-    if (!service || service[0]==0) {
-        socketaddr.sin_port = 0;
-    } else if ((sp = getservbyname(service, protocol))==0) {
-        char *endptr;
-        unsigned portNumber;
-
-        portNumber = strtoul(service, &endptr, 10);
-
-        if (service[0]!=0 && *endptr==0 && portNumber<65536) {
-            socketaddr.sin_port = htons(portNumber);
-        } else {
-            return NULL;
-        }
-    } else {
-        socketaddr.sin_port = sp->s_port;
-    }
-
-    return &socketaddr;
-}
-
-
-static void
-dummyHandler(int signum)
-{
-}
-
-
-static WMConnection*
-createConnectionWithSocket(int sock, Bool closeOnRelease)
-{
-    WMConnection *cPtr;
-    struct sigaction sig_action;
-
-    cPtr = wmalloc(sizeof(WMConnection));
-    wretain(cPtr);
-    memset(cPtr, 0, sizeof(WMConnection));
-
-    fcntl(sock, F_SETFD, FD_CLOEXEC); /* by default close on exec */
-
-    cPtr->sock = sock;
-    cPtr->openTimeout.timeout = OpenTimeout;
-    cPtr->openTimeout.handler = NULL;
-    cPtr->sendTimeout.timeout = DefaultTimeout;
-    cPtr->sendTimeout.handler = NULL;
-    cPtr->closeOnRelease = closeOnRelease;
-    cPtr->shutdownOnClose = 1;
-    cPtr->outputQueue =
-        WMCreateArrayWithDestructor(16, (WMFreeDataProc*)WMReleaseData);
-    cPtr->state = WCNotConnected;
-    cPtr->timeoutState = WCTNone;
-
-    /* ignore dead pipe */
-    if (!SigInitialized) {
-        /* Because POSIX mandates that only signal with handlers are reset
-         * accross an exec*(), we do not want to propagate ignoring SIGPIPEs
-         * to children. Hence the dummy handler. Philippe Troin <phil@fifi.org>
-         */
-        sig_action.sa_handler = &dummyHandler;
-        sig_action.sa_flags = SA_RESTART;
-        sigaction(SIGPIPE, &sig_action, NULL);
-        SigInitialized = True;
-    }
-
-    return cPtr;
-}
-
-
-#if 0
-WMConnection*
-WMCreateConnectionWithSocket(int sock, Bool closeOnRelease)
-{
-    WMConnection *cPtr;
-    struct sockaddr_in clientname;
-    int size;
-    int n;
-
-    cPtr = createConnectionWithSocket(sock, closeOnRelease);
-    cPtr->wasNonBlocking = WMIsConnectionNonBlocking(cPtr);
-    cPtr->isNonBlocking = cPtr->wasNonBlocking;
-
-    /* some way to find out if it is connected, and binded. can't find
-     if it listens though!!!
-     */
-
-    size = sizeof(clientname);
-    n = getpeername(sock, (struct sockaddr*) &clientname, &size);
-    if (n==0) {
-        /* Since we have a peer, it means we are connected */
-        cPtr->state = WCConnected;
-    } else {
-        size = sizeof(clientname);
-        n = getsockname(sock, (struct sockaddr*) &clientname, &size);
-        if (n==0) {
-            /* We don't have a peer, but we are binded to an address.
-             * Assume we are listening on it (we don't know that for sure!)
-             */
-            cPtr->state = WCListening;
-        } else {
-            cPtr->state = WCNotConnected;
-        }
-    }
-
-    return cPtr;
-}
-#endif
-
-
-/*
- * host     is the name on which we want to listen for incoming connections,
- *          and it must be a name of this host, or NULL if we want to listen
- *          on any incoming address.
- * service  is either a service name as present in /etc/services, or the port
- *          number we want to listen on. If NULL, a random port between
- *          1024 and 65535 will be assigned to us.
- * protocol is one of "tcp" or "udp". If NULL, "tcp" will be used by default.
- *          currently only "tcp" is supported.
- */
-WMConnection*
-WMCreateConnectionAsServerAtAddress(char *host, char *service, char *protocol)
-{
-    WMConnection *cPtr;
-    struct sockaddr_in *socketaddr;
-    socklen_t size;
-    int sock, on;
-
-    WCErrorCode = 0;
-
-    if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
-        wwarning(_("Bad address-service-protocol combination"));
-        return NULL;
-    }
-
-    /* Create the actual socket */
-    sock = socket(PF_INET, SOCK_STREAM, 0);
-    if (sock<0) {
-        WCErrorCode = errno;
-        return NULL;
-    }
-
-    /*
-     * Set socket options. We try to make the port reusable and have it
-     * close as fast as possible without waiting in unnecessary wait states
-     * on close.
-     */
-    on = 1;
-    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
-
-    if (bind(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
-        WCErrorCode = errno;
-        close(sock);
-        return NULL;
-    }
-
-    if (listen(sock, 10) < 0) {
-        WCErrorCode = errno;
-        close(sock);
-        return NULL;
-    }
-
-    /* Find out what is the address/service/protocol we get */
-    /* In case some of address/service/protocol were NULL */
-    size = sizeof(*socketaddr);
-    if (getsockname(sock, (struct sockaddr*)socketaddr, &size) < 0) {
-        WCErrorCode = errno;
-        close(sock);
-        return NULL;
-    }
-
-    cPtr = createConnectionWithSocket(sock, True);
-    cPtr->state = WCListening;
-    WMSetConnectionNonBlocking(cPtr, True);
-
-    setConnectionAddress(cPtr, socketaddr);
-
-    return cPtr;
-}
-
-
-WMConnection*
-WMCreateConnectionToAddress(char *host, char *service, char *protocol)
-{
-    WMConnection *cPtr;
-    struct sockaddr_in *socketaddr;
-    int sock;
-
-    WCErrorCode = 0;
-
-    wassertrv(service!=NULL && service[0]!=0, NULL);
-
-    if (host==NULL || host[0]==0)
-        host = "localhost";
-
-    if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
-        wwarning(_("Bad address-service-protocol combination"));
-        return NULL;
-    }
-
-    /* Create the actual socket */
-    sock = socket(PF_INET, SOCK_STREAM, 0);
-    if (sock<0) {
-        WCErrorCode = errno;
-        return NULL;
-    }
-    /* make socket blocking while we connect. */
-    setSocketNonBlocking(sock, False);
-    if (connect(sock, (struct sockaddr*)socketaddr, sizeof(*socketaddr)) < 0) {
-        WCErrorCode = errno;
-        close(sock);
-        return NULL;
-    }
-
-    cPtr = createConnectionWithSocket(sock, True);
-    cPtr->state = WCConnected;
-    WMSetConnectionNonBlocking(cPtr, True);
-    setConnectionAddress(cPtr, socketaddr);
-
-    return cPtr;
-}
-
-
-WMConnection*
-WMCreateConnectionToAddressAndNotify(char *host, char *service, char *protocol)
-{
-    WMConnection *cPtr;
-    struct sockaddr_in *socketaddr;
-    int sock;
-    Bool isNonBlocking;
-
-    WCErrorCode = 0;
-
-    wassertrv(service!=NULL && service[0]!=0, NULL);
-
-    if (host==NULL || host[0]==0)
-        host = "localhost";
-
-    if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
-        wwarning(_("Bad address-service-protocol combination"));
-        return NULL;
-    }
-
-    /* Create the actual socket */
-    sock = socket(PF_INET, SOCK_STREAM, 0);
-    if (sock<0) {
-        WCErrorCode = errno;
-        return NULL;
-    }
-    isNonBlocking = setSocketNonBlocking(sock, True);
-    if (connect(sock, (struct sockaddr*)socketaddr, sizeof(*socketaddr)) < 0) {
-        if (errno!=EINPROGRESS) {
-            WCErrorCode = errno;
-            close(sock);
-            return NULL;
-        }
-    }
-
-    cPtr = createConnectionWithSocket(sock, True);
-    cPtr->state = WCInProgress;
-    cPtr->isNonBlocking = isNonBlocking;
-
-    cPtr->handler.write = WMAddInputHandler(cPtr->sock, WIWriteMask,
-                                            inputHandler, cPtr);
-
-    cPtr->openTimeout.handler =
-        WMAddTimerHandler(cPtr->openTimeout.timeout*1000, openTimeout, cPtr);
-
-    setConnectionAddress(cPtr, socketaddr);
-
-    return cPtr;
-}
-
-
-static void
-removeAllHandlers(WMConnection *cPtr)
-{
-    if (cPtr->handler.read)
-        WMDeleteInputHandler(cPtr->handler.read);
-    if (cPtr->handler.write)
-        WMDeleteInputHandler(cPtr->handler.write);
-    if (cPtr->handler.exception)
-        WMDeleteInputHandler(cPtr->handler.exception);
-    if (cPtr->openTimeout.handler)
-        WMDeleteTimerHandler(cPtr->openTimeout.handler);
-    if (cPtr->sendTimeout.handler)
-        WMDeleteTimerHandler(cPtr->sendTimeout.handler);
-
-    cPtr->handler.read = NULL;
-    cPtr->handler.write = NULL;
-    cPtr->handler.exception = NULL;
-    cPtr->openTimeout.handler = NULL;
-    cPtr->sendTimeout.handler = NULL;
-}
-
-
-void
-WMDestroyConnection(WMConnection *cPtr)
-{
-    if (cPtr->closeOnRelease && cPtr->sock>=0) {
-        if (cPtr->shutdownOnClose) {
-            shutdown(cPtr->sock, SHUT_RDWR);
-        }
-        close(cPtr->sock);
-    }
-
-    removeAllHandlers(cPtr);
-    WMFreeArray(cPtr->outputQueue); /* will also free the items with the destructor */
-
-    if (cPtr->address) {
-        wfree(cPtr->address);
-        wfree(cPtr->service);
-        wfree(cPtr->protocol);
-    }
-
-    wrelease(cPtr);
-}
-
-
-void
-WMCloseConnection(WMConnection *cPtr)
-{
-    if (cPtr->sock>=0) {
-        if (cPtr->shutdownOnClose) {
-            shutdown(cPtr->sock, SHUT_RDWR);
-        }
-        close(cPtr->sock);
-        cPtr->sock = -1;
-    }
-
-    removeAllHandlers(cPtr);
-    clearOutputQueue(cPtr);
-
-    cPtr->state = WCClosed;
-}
-
-
-WMConnection*
-WMAcceptConnection(WMConnection *listener)
-{
-    struct sockaddr_in clientname;
-    socklen_t size;
-    int newSock;
-    WMConnection *newConnection;
-
-    WCErrorCode = 0;
-    wassertrv(listener && listener->state==WCListening, NULL);
-
-    size = sizeof(clientname);
-    newSock = accept(listener->sock, (struct sockaddr*) &clientname, &size);
-    if (newSock<0) {
-        WCErrorCode = ((errno!=EAGAIN && errno!=EWOULDBLOCK) ? errno : 0);
-        return NULL;
-    }
-
-    newConnection = createConnectionWithSocket(newSock, True);
-    WMSetConnectionNonBlocking(newConnection, True);
-    newConnection->state = WCConnected;
-    setConnectionAddress(newConnection, &clientname);
-
-    return newConnection;
-}
-
-
-char*
-WMGetConnectionAddress(WMConnection *cPtr)
-{
-    return cPtr->address;
-}
-
-
-char*
-WMGetConnectionService(WMConnection *cPtr)
-{
-    return cPtr->service;
-}
-
-
-char*
-WMGetConnectionProtocol(WMConnection *cPtr)
-{
-    return cPtr->protocol;
-}
-
-
-int
-WMGetConnectionSocket(WMConnection *cPtr)
-{
-    return cPtr->sock;
-}
-
-
-WMConnectionState
-WMGetConnectionState(WMConnection *cPtr)
-{
-    return cPtr->state;
-}
-
-
-WMConnectionTimeoutState
-WMGetConnectionTimeoutState(WMConnection *cPtr)
-{
-    return cPtr->timeoutState;
-}
-
-
-Bool
-WMEnqueueConnectionData(WMConnection *cPtr, WMData *data)
-{
-    wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, False);
-    wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, False);
-
-    if (cPtr->state!=WCConnected)
-        return False;
-
-    WMAddToArray(cPtr->outputQueue, WMRetainData(data));
-    return True;
-}
-
-
-/*
- * Return value:
- * -1 - not connected or connection died while sending
- *  0 - couldn't send the data (or part of it). data is saved in a queue
- *      and will be sent when possible. after it is sent the canResumeSending
- *      callback will be called.
- *  1 - data was succesfully sent
- */
-int
-WMSendConnectionData(WMConnection *cPtr, WMData *data)
-{
-    int bytes, pos, len;
-    TimeoutData *tPtr = &cPtr->sendTimeout;
-    const unsigned char *dataBytes;
-
-    wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, -1);
-    wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, -1);
-
-    if (cPtr->state!=WCConnected)
-        return -1;
-
-    /* If we have no data just flush the queue, else try to send data */
-    if (data && WMGetDataLength(data)>0) {
-        WMAddToArray(cPtr->outputQueue, WMRetainData(data));
-        /* If there already was something in queue, and also a write input
-         * handler is established, it means we were unable to send, so
-         * return and let the write handler notify us when we can send.
-         */
-        if (WMGetArrayItemCount(cPtr->outputQueue)>1 && cPtr->handler.write)
-            return 0;
-    }
-
-    while (WMGetArrayItemCount(cPtr->outputQueue) > 0) {
-        data = WMGetFromArray(cPtr->outputQueue, 0);
-        dataBytes = (const unsigned char *)WMDataBytes(data);
-        len = WMGetDataLength(data);
-        pos = cPtr->bufPos; /* where we're left last time */
-        while(pos < len) {
-        again:
-            bytes = write(cPtr->sock, dataBytes+pos, len - pos);
-            if(bytes<0) {
-                switch (errno) {
-                case EINTR:
-                    goto again;
-                case EWOULDBLOCK:
-                    /* save the position where we're left and add a timeout */
-                    cPtr->bufPos = pos;
-                    if (!tPtr->handler) {
-                        tPtr->handler = WMAddTimerHandler(tPtr->timeout*1000,
-                                                          sendTimeout, cPtr);
-                    }
-                    if (!cPtr->handler.write) {
-                        cPtr->handler.write =
-                            WMAddInputHandler(cPtr->sock, WIWriteMask,
-                                              inputHandler, cPtr);
-                    }
-                    return 0;
-                default:
-                    WCErrorCode = errno;
-                    cPtr->state = WCDied;
-                    removeAllHandlers(cPtr);
-                    if (cPtr->delegate && cPtr->delegate->didDie)
-                        (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
-                    return -1;
-                }
-            }
-            pos += bytes;
-        }
-        WMDeleteFromArray(cPtr->outputQueue, 0);
-        cPtr->bufPos = 0;
-        if (tPtr->handler) {
-            WMDeleteTimerHandler(tPtr->handler);
-            tPtr->handler = NULL;
-        }
-        /*if (cPtr->handler.write) {
-         WMDeleteInputHandler(cPtr->handler.write);
-         cPtr->handler.write = NULL;
-         }*/
-    }
-
-    if (cPtr->handler.write) {
-        WMDeleteInputHandler(cPtr->handler.write);
-        cPtr->handler.write = NULL;
-    }
-
-    return 1;
-}
-
-
-/*
- * WMGetConnectionAvailableData(connection):
- *
- * will return a WMData structure containing the available data on the
- * specified connection. If connection is non-blocking (default) and no data
- * is available when this function is called, an empty WMData is returned.
- *
- * If an error occurs while reading or the other side closed connection,
- * it will return NULL.
- * Also trying to read from an already died or closed connection is
- * considered to be an error condition, and will return NULL.
- */
-WMData*
-WMGetConnectionAvailableData(WMConnection *cPtr)
-{
-    char buffer[NETBUF_SIZE];
-    int nbytes;
-    WMData *aData;
-
-    wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, NULL);
-    wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, NULL);
-
-    if (cPtr->state!=WCConnected)
-        return NULL;
-
-    aData = NULL;
-
-again:
-    nbytes = read(cPtr->sock, buffer, NETBUF_SIZE);
-    if (nbytes<0) {
-        switch (errno) {
-        case EINTR:
-            goto again;
-        case EWOULDBLOCK:
-            aData = WMCreateDataWithCapacity(0);
-            break;
-        default:
-            WCErrorCode = errno;
-            cPtr->state = WCDied;
-            removeAllHandlers(cPtr);
-            if (cPtr->delegate && cPtr->delegate->didDie)
-                (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
-            break;
-        }
-    } else if (nbytes==0) {      /* the other side has closed connection */
-        cPtr->state = WCClosed;
-        removeAllHandlers(cPtr);
-        if (cPtr->delegate && cPtr->delegate->didDie)
-            (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
-    } else {
-        aData = WMCreateDataWithBytes(buffer, nbytes);
-    }
-
-    return aData;
-}
-
-
-void
-WMSetConnectionDelegate(WMConnection *cPtr, ConnectionDelegate *delegate)
-{
-    wassertr(cPtr->sock >= 0);
-    /* Don't try to set the delegate multiple times */
-    wassertr(cPtr->delegate == NULL);
-
-    cPtr->delegate = delegate;
-    if (delegate && delegate->didReceiveInput && !cPtr->handler.read)
-        cPtr->handler.read = WMAddInputHandler(cPtr->sock, WIReadMask,
-                                               inputHandler, cPtr);
-    if (delegate && delegate->didCatchException && !cPtr->handler.exception)
-        cPtr->handler.exception = WMAddInputHandler(cPtr->sock, WIExceptMask,
-                                                    inputHandler, cPtr);
-}
-
-
-#if 0
-Bool
-WMIsConnectionNonBlocking(WMConnection *cPtr)
-{
-#if 1
-    int state;
-
-    state = fcntl(cPtr->sock, F_GETFL, 0);
-
-    if (state < 0) {
-        /* If we can't use fcntl on socket, this probably also means we could
-         * not use fcntl to set non-blocking mode, and since a socket defaults
-         * to blocking when created, return False as the best assumption */
-        return False;
-    }
-
-    return ((state & NONBLOCK_OPT)!=0);
-#else
-    return cPtr->isNonBlocking;
-#endif
-}
-#endif
-
-
-Bool
-WMSetConnectionNonBlocking(WMConnection *cPtr, Bool flag)
-{
-    wassertrv(cPtr!=NULL && cPtr->sock>=0, False);
-
-    flag = ((flag==0) ? 0 : 1);
-
-    if (cPtr->isNonBlocking == flag)
-        return True;
-
-    if (setSocketNonBlocking(cPtr->sock, flag)==True) {
-        cPtr->isNonBlocking = flag;
-        return True;
-    }
-
-    return False;
-}
-
-
-Bool
-WMSetConnectionCloseOnExec(WMConnection *cPtr, Bool flag)
-{
-    wassertrv(cPtr!=NULL && cPtr->sock>=0, False);
-
-    if (fcntl(cPtr->sock, F_SETFD, ((flag==0) ? 0 : FD_CLOEXEC)) < 0) {
-        return False;
-    }
-
-    return True;
-}
-
-
-void
-WMSetConnectionShutdownOnClose(WMConnection *cPtr, Bool flag)
-{
-    cPtr->shutdownOnClose = ((flag==0) ? 0 : 1);
-}
-
-
-void*
-WMGetConnectionClientData(WMConnection *cPtr)
-{
-    return cPtr->clientData;
-}
-
-
-void
-WMSetConnectionClientData(WMConnection *cPtr, void *data)
-{
-    cPtr->clientData = data;
-}
-
-
-unsigned int
-WMGetConnectionFlags(WMConnection *cPtr)
-{
-    return cPtr->uflags;
-}
-
-
-void
-WMSetConnectionFlags(WMConnection *cPtr, unsigned int flags)
-{
-    cPtr->uflags = flags;
-}
-
-
-WMArray*
-WMGetConnectionUnsentData(WMConnection *cPtr)
-{
-    return cPtr->outputQueue;
-}
-
-
-void
-WMSetConnectionDefaultTimeout(unsigned int timeout)
-{
-    if (timeout == 0) {
-        DefaultTimeout = DEF_TIMEOUT;
-    } else {
-        DefaultTimeout = timeout;
-    }
-}
-
-
-void
-WMSetConnectionOpenTimeout(unsigned int timeout)
-{
-    if (timeout == 0) {
-        OpenTimeout = DefaultTimeout;
-    } else {
-        OpenTimeout = timeout;
-    }
-}
-
-
-void
-WMSetConnectionSendTimeout(WMConnection *cPtr, unsigned int timeout)
-{
-    if (timeout == 0) {
-        cPtr->sendTimeout.timeout = DefaultTimeout;
-    } else {
-        cPtr->sendTimeout.timeout = timeout;
-    }
-}
-
-
+/*
+ *  WINGs WMConnection function library
+ *
+ *  Copyright (c) 1999-2003 Dan Pascu
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * TODO:
+ * - decide if we want to support connections with external sockets, else
+ *   clean up the structure of the unneeded members.
+ * - decide what to do with all wwarning() calls that are still there.
+ *
+ */
+
+#include "wconfig.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <signal.h>
+#ifdef __FreeBSD__
+#include <sys/signal.h>
+#endif
+
+#include "WINGs.h"
+
+/* Some older systems does not define this (linux libc5, maybe others too) */
+#ifndef SHUT_RDWR
+# define SHUT_RDWR              2
+#endif
+
+/* For SunOS */
+#ifndef SA_RESTART
+# define SA_RESTART             0
+#endif
+
+/* For Solaris */
+#ifndef INADDR_NONE
+# define INADDR_NONE           -1
+#endif
+
+/* Stuff for setting the sockets into non-blocking mode. */
+/*
+#ifdef __POSIX_SOURCE
+# define NONBLOCK_OPT           O_NONBLOCK
+#else
+# define NONBLOCK_OPT           FNDELAY
+#endif
+*/
+
+#define NONBLOCK_OPT            O_NONBLOCK
+
+#define NETBUF_SIZE             4096
+
+#define DEF_TIMEOUT             600    /* 600 seconds == 10 minutes */
+
+int WCErrorCode = 0;
+
+static Bool SigInitialized = False;
+
+static unsigned int DefaultTimeout = DEF_TIMEOUT;
+static unsigned int OpenTimeout = DEF_TIMEOUT;
+
+typedef struct TimeoutData {
+       unsigned timeout;
+       WMHandlerID *handler;
+} TimeoutData;
+
+typedef struct W_Connection {
+       int sock;               /* the socket we speak through */
+
+       struct {
+               WMHandlerID *read;      /* the input read handler */
+               WMHandlerID *write;     /* the input write handler */
+               WMHandlerID *exception; /* the input exception handler */
+       } handler;
+
+       ConnectionDelegate *delegate;   /* client delegates */
+       void *clientData;       /* client data */
+       unsigned int uflags;    /* flags for the client */
+
+       WMArray *outputQueue;
+       unsigned bufPos;
+
+       TimeoutData sendTimeout;
+       TimeoutData openTimeout;
+
+       WMConnectionState state;
+       WMConnectionTimeoutState timeoutState;
+
+       char *address;
+       char *service;
+       char *protocol;
+
+       Bool closeOnRelease;
+       Bool shutdownOnClose;
+       Bool wasNonBlocking;
+       Bool isNonBlocking;
+
+} W_Connection;
+
+static void clearOutputQueue(WMConnection * cPtr)
+{
+       cPtr->bufPos = 0;
+       WMEmptyArray(cPtr->outputQueue);
+}
+
+static void openTimeout(void *cdata)
+{
+       WMConnection *cPtr = (WMConnection *) cdata;
+
+       cPtr->openTimeout.handler = NULL;
+       if (cPtr->handler.write) {
+               WMDeleteInputHandler(cPtr->handler.write);
+               cPtr->handler.write = NULL;
+       }
+       if (cPtr->state != WCConnected) {
+               cPtr->state = WCTimedOut;
+               cPtr->timeoutState = WCTWhileOpening;
+               if (cPtr->delegate && cPtr->delegate->didTimeout) {
+                       (*cPtr->delegate->didTimeout) (cPtr->delegate, cPtr);
+               } else {
+                       WMCloseConnection(cPtr);
+                       cPtr->state = WCTimedOut;       /* the above set state to WCClosed */
+               }
+       }
+}
+
+static void sendTimeout(void *cdata)
+{
+       WMConnection *cPtr = (WMConnection *) cdata;
+
+       cPtr->sendTimeout.handler = NULL;
+       if (cPtr->handler.write) {
+               WMDeleteInputHandler(cPtr->handler.write);
+               cPtr->handler.write = NULL;
+       }
+       if (WMGetArrayItemCount(cPtr->outputQueue) > 0) {
+               clearOutputQueue(cPtr);
+               cPtr->state = WCTimedOut;
+               cPtr->timeoutState = WCTWhileSending;
+               /* // should we close it no matter what (after calling the didTimeout
+                  // delegate)? -Dan */
+               if (cPtr->delegate && cPtr->delegate->didTimeout) {
+                       (*cPtr->delegate->didTimeout) (cPtr->delegate, cPtr);
+               } else {
+                       WMCloseConnection(cPtr);
+                       cPtr->state = WCTimedOut;       /* the above set state to WCClosed */
+               }
+       }
+}
+
+static void inputHandler(int fd, int mask, void *clientData)
+{
+       WMConnection *cPtr = (WMConnection *) clientData;
+
+       if (cPtr->state == WCClosed || cPtr->state == WCDied)
+               return;
+
+       if ((mask & WIWriteMask)) {
+               int result;
+
+               if (cPtr->state == WCInProgress) {
+                       Bool failed;
+                       socklen_t len = sizeof(result);
+
+                       WCErrorCode = 0;
+                       if (getsockopt(cPtr->sock, SOL_SOCKET, SO_ERROR,
+                                      (void *)&result, &len) == 0 && result != 0) {
+                               cPtr->state = WCFailed;
+                               WCErrorCode = result;
+                               failed = True;
+                               /* should call wsyserrorwithcode(result, ...) here? */
+                       } else {
+                               cPtr->state = WCConnected;
+                               failed = False;
+                       }
+
+                       if (cPtr->handler.write) {
+                               WMDeleteInputHandler(cPtr->handler.write);
+                               cPtr->handler.write = NULL;
+                       }
+
+                       if (cPtr->openTimeout.handler) {
+                               WMDeleteTimerHandler(cPtr->openTimeout.handler);
+                               cPtr->openTimeout.handler = NULL;
+                       }
+
+                       if (cPtr->delegate && cPtr->delegate->didInitialize)
+                               (*cPtr->delegate->didInitialize) (cPtr->delegate, cPtr);
+
+                       /* we use failed and not cPtr->state here, because cPtr may be
+                        * destroyed by the delegate called above if the connection failed
+                        */
+                       if (failed)
+                               return;
+               } else if (cPtr->state == WCConnected) {
+                       result = WMFlushConnection(cPtr);
+                       if (result > 0 && cPtr->delegate && cPtr->delegate->canResumeSending) {
+                               (*cPtr->delegate->canResumeSending) (cPtr->delegate, cPtr);
+                       }
+               }
+       }
+
+       if (!cPtr->delegate)
+               return;
+
+       /* if the connection died, may get destroyed in the delegate, so retain */
+       wretain(cPtr);
+
+       if ((mask & WIReadMask) && cPtr->delegate->didReceiveInput)
+               (*cPtr->delegate->didReceiveInput) (cPtr->delegate, cPtr);
+
+       if ((mask & WIExceptMask) && cPtr->delegate->didCatchException)
+               (*cPtr->delegate->didCatchException) (cPtr->delegate, cPtr);
+
+       wrelease(cPtr);
+}
+
+static Bool setSocketNonBlocking(int sock, Bool flag)
+{
+       int state;
+       Bool isNonBlock;
+
+       state = fcntl(sock, F_GETFL, 0);
+
+       if (state < 0) {
+               /* set WCErrorCode here? -Dan */
+               return False;
+       }
+
+       isNonBlock = (state & NONBLOCK_OPT) != 0;
+
+       if (flag) {
+               if (isNonBlock)
+                       return True;
+               state |= NONBLOCK_OPT;
+       } else {
+               if (!isNonBlock)
+                       return True;
+               state &= ~NONBLOCK_OPT;
+       }
+
+       if (fcntl(sock, F_SETFL, state) < 0) {
+               /* set WCErrorCode here? -Dan */
+               return False;
+       }
+
+       return True;
+}
+
+static void setConnectionAddress(WMConnection * cPtr, struct sockaddr_in *socketaddr)
+{
+       wassertr(cPtr->address == NULL);
+
+       cPtr->address = wstrdup(inet_ntoa(socketaddr->sin_addr));
+       cPtr->service = wmalloc(16);
+       sprintf(cPtr->service, "%hu", ntohs(socketaddr->sin_port));
+       cPtr->protocol = wstrdup("tcp");
+}
+
+static struct sockaddr_in *getSocketAddress(char *name, char *service, char *protocol)
+{
+       static struct sockaddr_in socketaddr;
+       struct servent *sp;
+
+       if (!protocol || protocol[0] == 0)
+               protocol = "tcp";
+
+       memset(&socketaddr, 0, sizeof(struct sockaddr_in));
+       socketaddr.sin_family = AF_INET;
+
+       /*
+        * If we were given a hostname, we use any address for that host.
+        * Otherwise we expect the given name to be an address unless it is
+        * NULL (any address).
+        */
+       if (name && name[0] != 0) {
+               WMHost *host = WMGetHostWithName(name);
+
+               if (!host)
+                       return NULL;    /* name is not a hostname nor a number and dot adr */
+
+               name = WMGetHostAddress(host);
+#ifndef        HAVE_INET_ATON
+               if ((socketaddr.sin_addr.s_addr = inet_addr(name)) == INADDR_NONE) {
+#else
+               if (inet_aton(name, &socketaddr.sin_addr) == 0) {
+#endif
+                       WMReleaseHost(host);
+                       return NULL;
+               }
+               WMReleaseHost(host);
+       } else {
+               socketaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+       }
+
+       if (!service || service[0] == 0) {
+               socketaddr.sin_port = 0;
+       } else if ((sp = getservbyname(service, protocol)) == 0) {
+               char *endptr;
+               unsigned portNumber;
+
+               portNumber = strtoul(service, &endptr, 10);
+
+               if (service[0] != 0 && *endptr == 0 && portNumber < 65536) {
+                       socketaddr.sin_port = htons(portNumber);
+               } else {
+                       return NULL;
+               }
+       } else {
+               socketaddr.sin_port = sp->s_port;
+       }
+
+       return &socketaddr;
+}
+
+static void dummyHandler(int signum)
+{
+}
+
+static WMConnection *createConnectionWithSocket(int sock, Bool closeOnRelease)
+{
+       WMConnection *cPtr;
+       struct sigaction sig_action;
+
+       cPtr = wmalloc(sizeof(WMConnection));
+       wretain(cPtr);
+       memset(cPtr, 0, sizeof(WMConnection));
+
+       fcntl(sock, F_SETFD, FD_CLOEXEC);       /* by default close on exec */
+
+       cPtr->sock = sock;
+       cPtr->openTimeout.timeout = OpenTimeout;
+       cPtr->openTimeout.handler = NULL;
+       cPtr->sendTimeout.timeout = DefaultTimeout;
+       cPtr->sendTimeout.handler = NULL;
+       cPtr->closeOnRelease = closeOnRelease;
+       cPtr->shutdownOnClose = 1;
+       cPtr->outputQueue = WMCreateArrayWithDestructor(16, (WMFreeDataProc *) WMReleaseData);
+       cPtr->state = WCNotConnected;
+       cPtr->timeoutState = WCTNone;
+
+       /* ignore dead pipe */
+       if (!SigInitialized) {
+               /* Because POSIX mandates that only signal with handlers are reset
+                * accross an exec*(), we do not want to propagate ignoring SIGPIPEs
+                * to children. Hence the dummy handler. Philippe Troin <phil@fifi.org>
+                */
+               sig_action.sa_handler = &dummyHandler;
+               sig_action.sa_flags = SA_RESTART;
+               sigaction(SIGPIPE, &sig_action, NULL);
+               SigInitialized = True;
+       }
+
+       return cPtr;
+}
+
+#if 0
+WMConnection *WMCreateConnectionWithSocket(int sock, Bool closeOnRelease)
+{
+       WMConnection *cPtr;
+       struct sockaddr_in clientname;
+       int size;
+       int n;
+
+       cPtr = createConnectionWithSocket(sock, closeOnRelease);
+       cPtr->wasNonBlocking = WMIsConnectionNonBlocking(cPtr);
+       cPtr->isNonBlocking = cPtr->wasNonBlocking;
+
+       /* some way to find out if it is connected, and binded. can't find
+          if it listens though!!!
+        */
+
+       size = sizeof(clientname);
+       n = getpeername(sock, (struct sockaddr *)&clientname, &size);
+       if (n == 0) {
+               /* Since we have a peer, it means we are connected */
+               cPtr->state = WCConnected;
+       } else {
+               size = sizeof(clientname);
+               n = getsockname(sock, (struct sockaddr *)&clientname, &size);
+               if (n == 0) {
+                       /* We don't have a peer, but we are binded to an address.
+                        * Assume we are listening on it (we don't know that for sure!)
+                        */
+                       cPtr->state = WCListening;
+               } else {
+                       cPtr->state = WCNotConnected;
+               }
+       }
+
+       return cPtr;
+}
+#endif
+
+/*
+ * host     is the name on which we want to listen for incoming connections,
+ *          and it must be a name of this host, or NULL if we want to listen
+ *          on any incoming address.
+ * service  is either a service name as present in /etc/services, or the port
+ *          number we want to listen on. If NULL, a random port between
+ *          1024 and 65535 will be assigned to us.
+ * protocol is one of "tcp" or "udp". If NULL, "tcp" will be used by default.
+ *          currently only "tcp" is supported.
+ */
+WMConnection *WMCreateConnectionAsServerAtAddress(char *host, char *service, char *protocol)
+{
+       WMConnection *cPtr;
+       struct sockaddr_in *socketaddr;
+       socklen_t size;
+       int sock, on;
+
+       WCErrorCode = 0;
+
+       if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
+               wwarning(_("Bad address-service-protocol combination"));
+               return NULL;
+       }
+
+       /* Create the actual socket */
+       sock = socket(PF_INET, SOCK_STREAM, 0);
+       if (sock < 0) {
+               WCErrorCode = errno;
+               return NULL;
+       }
+
+       /*
+        * Set socket options. We try to make the port reusable and have it
+        * close as fast as possible without waiting in unnecessary wait states
+        * on close.
+        */
+       on = 1;
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
+
+       if (bind(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
+               WCErrorCode = errno;
+               close(sock);
+               return NULL;
+       }
+
+       if (listen(sock, 10) < 0) {
+               WCErrorCode = errno;
+               close(sock);
+               return NULL;
+       }
+
+       /* Find out what is the address/service/protocol we get */
+       /* In case some of address/service/protocol were NULL */
+       size = sizeof(*socketaddr);
+       if (getsockname(sock, (struct sockaddr *)socketaddr, &size) < 0) {
+               WCErrorCode = errno;
+               close(sock);
+               return NULL;
+       }
+
+       cPtr = createConnectionWithSocket(sock, True);
+       cPtr->state = WCListening;
+       WMSetConnectionNonBlocking(cPtr, True);
+
+       setConnectionAddress(cPtr, socketaddr);
+
+       return cPtr;
+}
+
+WMConnection *WMCreateConnectionToAddress(char *host, char *service, char *protocol)
+{
+       WMConnection *cPtr;
+       struct sockaddr_in *socketaddr;
+       int sock;
+
+       WCErrorCode = 0;
+
+       wassertrv(service != NULL && service[0] != 0, NULL);
+
+       if (host == NULL || host[0] == 0)
+               host = "localhost";
+
+       if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
+               wwarning(_("Bad address-service-protocol combination"));
+               return NULL;
+       }
+
+       /* Create the actual socket */
+       sock = socket(PF_INET, SOCK_STREAM, 0);
+       if (sock < 0) {
+               WCErrorCode = errno;
+               return NULL;
+       }
+       /* make socket blocking while we connect. */
+       setSocketNonBlocking(sock, False);
+       if (connect(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
+               WCErrorCode = errno;
+               close(sock);
+               return NULL;
+       }
+
+       cPtr = createConnectionWithSocket(sock, True);
+       cPtr->state = WCConnected;
+       WMSetConnectionNonBlocking(cPtr, True);
+       setConnectionAddress(cPtr, socketaddr);
+
+       return cPtr;
+}
+
+WMConnection *WMCreateConnectionToAddressAndNotify(char *host, char *service, char *protocol)
+{
+       WMConnection *cPtr;
+       struct sockaddr_in *socketaddr;
+       int sock;
+       Bool isNonBlocking;
+
+       WCErrorCode = 0;
+
+       wassertrv(service != NULL && service[0] != 0, NULL);
+
+       if (host == NULL || host[0] == 0)
+               host = "localhost";
+
+       if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
+               wwarning(_("Bad address-service-protocol combination"));
+               return NULL;
+       }
+
+       /* Create the actual socket */
+       sock = socket(PF_INET, SOCK_STREAM, 0);
+       if (sock < 0) {
+               WCErrorCode = errno;
+               return NULL;
+       }
+       isNonBlocking = setSocketNonBlocking(sock, True);
+       if (connect(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
+               if (errno != EINPROGRESS) {
+                       WCErrorCode = errno;
+                       close(sock);
+                       return NULL;
+               }
+       }
+
+       cPtr = createConnectionWithSocket(sock, True);
+       cPtr->state = WCInProgress;
+       cPtr->isNonBlocking = isNonBlocking;
+
+       cPtr->handler.write = WMAddInputHandler(cPtr->sock, WIWriteMask, inputHandler, cPtr);
+
+       cPtr->openTimeout.handler = WMAddTimerHandler(cPtr->openTimeout.timeout * 1000, openTimeout, cPtr);
+
+       setConnectionAddress(cPtr, socketaddr);
+
+       return cPtr;
+}
+
+static void removeAllHandlers(WMConnection * cPtr)
+{
+       if (cPtr->handler.read)
+               WMDeleteInputHandler(cPtr->handler.read);
+       if (cPtr->handler.write)
+               WMDeleteInputHandler(cPtr->handler.write);
+       if (cPtr->handler.exception)
+               WMDeleteInputHandler(cPtr->handler.exception);
+       if (cPtr->openTimeout.handler)
+               WMDeleteTimerHandler(cPtr->openTimeout.handler);
+       if (cPtr->sendTimeout.handler)
+               WMDeleteTimerHandler(cPtr->sendTimeout.handler);
+
+       cPtr->handler.read = NULL;
+       cPtr->handler.write = NULL;
+       cPtr->handler.exception = NULL;
+       cPtr->openTimeout.handler = NULL;
+       cPtr->sendTimeout.handler = NULL;
+}
+
+void WMDestroyConnection(WMConnection * cPtr)
+{
+       if (cPtr->closeOnRelease && cPtr->sock >= 0) {
+               if (cPtr->shutdownOnClose) {
+                       shutdown(cPtr->sock, SHUT_RDWR);
+               }
+               close(cPtr->sock);
+       }
+
+       removeAllHandlers(cPtr);
+       WMFreeArray(cPtr->outputQueue); /* will also free the items with the destructor */
+
+       if (cPtr->address) {
+               wfree(cPtr->address);
+               wfree(cPtr->service);
+               wfree(cPtr->protocol);
+       }
+
+       wrelease(cPtr);
+}
+
+void WMCloseConnection(WMConnection * cPtr)
+{
+       if (cPtr->sock >= 0) {
+               if (cPtr->shutdownOnClose) {
+                       shutdown(cPtr->sock, SHUT_RDWR);
+               }
+               close(cPtr->sock);
+               cPtr->sock = -1;
+       }
+
+       removeAllHandlers(cPtr);
+       clearOutputQueue(cPtr);
+
+       cPtr->state = WCClosed;
+}
+
+WMConnection *WMAcceptConnection(WMConnection * listener)
+{
+       struct sockaddr_in clientname;
+       socklen_t size;
+       int newSock;
+       WMConnection *newConnection;
+
+       WCErrorCode = 0;
+       wassertrv(listener && listener->state == WCListening, NULL);
+
+       size = sizeof(clientname);
+       newSock = accept(listener->sock, (struct sockaddr *)&clientname, &size);
+       if (newSock < 0) {
+               WCErrorCode = ((errno != EAGAIN && errno != EWOULDBLOCK) ? errno : 0);
+               return NULL;
+       }
+
+       newConnection = createConnectionWithSocket(newSock, True);
+       WMSetConnectionNonBlocking(newConnection, True);
+       newConnection->state = WCConnected;
+       setConnectionAddress(newConnection, &clientname);
+
+       return newConnection;
+}
+
+char *WMGetConnectionAddress(WMConnection * cPtr)
+{
+       return cPtr->address;
+}
+
+char *WMGetConnectionService(WMConnection * cPtr)
+{
+       return cPtr->service;
+}
+
+char *WMGetConnectionProtocol(WMConnection * cPtr)
+{
+       return cPtr->protocol;
+}
+
+int WMGetConnectionSocket(WMConnection * cPtr)
+{
+       return cPtr->sock;
+}
+
+WMConnectionState WMGetConnectionState(WMConnection * cPtr)
+{
+       return cPtr->state;
+}
+
+WMConnectionTimeoutState WMGetConnectionTimeoutState(WMConnection * cPtr)
+{
+       return cPtr->timeoutState;
+}
+
+Bool WMEnqueueConnectionData(WMConnection * cPtr, WMData * data)
+{
+       wassertrv(cPtr->state != WCNotConnected && cPtr->state != WCListening, False);
+       wassertrv(cPtr->state != WCInProgress && cPtr->state != WCFailed, False);
+
+       if (cPtr->state != WCConnected)
+               return False;
+
+       WMAddToArray(cPtr->outputQueue, WMRetainData(data));
+       return True;
+}
+
+/*
+ * Return value:
+ * -1 - not connected or connection died while sending
+ *  0 - couldn't send the data (or part of it). data is saved in a queue
+ *      and will be sent when possible. after it is sent the canResumeSending
+ *      callback will be called.
+ *  1 - data was succesfully sent
+ */
+int WMSendConnectionData(WMConnection * cPtr, WMData * data)
+{
+       int bytes, pos, len;
+       TimeoutData *tPtr = &cPtr->sendTimeout;
+       const unsigned char *dataBytes;
+
+       wassertrv(cPtr->state != WCNotConnected && cPtr->state != WCListening, -1);
+       wassertrv(cPtr->state != WCInProgress && cPtr->state != WCFailed, -1);
+
+       if (cPtr->state != WCConnected)
+               return -1;
+
+       /* If we have no data just flush the queue, else try to send data */
+       if (data && WMGetDataLength(data) > 0) {
+               WMAddToArray(cPtr->outputQueue, WMRetainData(data));
+               /* If there already was something in queue, and also a write input
+                * handler is established, it means we were unable to send, so
+                * return and let the write handler notify us when we can send.
+                */
+               if (WMGetArrayItemCount(cPtr->outputQueue) > 1 && cPtr->handler.write)
+                       return 0;
+       }
+
+       while (WMGetArrayItemCount(cPtr->outputQueue) > 0) {
+               data = WMGetFromArray(cPtr->outputQueue, 0);
+               dataBytes = (const unsigned char *)WMDataBytes(data);
+               len = WMGetDataLength(data);
+               pos = cPtr->bufPos;     /* where we're left last time */
+               while (pos < len) {
+ again:
+                       bytes = write(cPtr->sock, dataBytes + pos, len - pos);
+                       if (bytes < 0) {
+                               switch (errno) {
+                               case EINTR:
+                                       goto again;
+                               case EWOULDBLOCK:
+                                       /* save the position where we're left and add a timeout */
+                                       cPtr->bufPos = pos;
+                                       if (!tPtr->handler) {
+                                               tPtr->handler = WMAddTimerHandler(tPtr->timeout * 1000,
+                                                                                 sendTimeout, cPtr);
+                                       }
+                                       if (!cPtr->handler.write) {
+                                               cPtr->handler.write =
+                                                   WMAddInputHandler(cPtr->sock, WIWriteMask, inputHandler, cPtr);
+                                       }
+                                       return 0;
+                               default:
+                                       WCErrorCode = errno;
+                                       cPtr->state = WCDied;
+                                       removeAllHandlers(cPtr);
+                                       if (cPtr->delegate && cPtr->delegate->didDie)
+                                               (*cPtr->delegate->didDie) (cPtr->delegate, cPtr);
+                                       return -1;
+                               }
+                       }
+                       pos += bytes;
+               }
+               WMDeleteFromArray(cPtr->outputQueue, 0);
+               cPtr->bufPos = 0;
+               if (tPtr->handler) {
+                       WMDeleteTimerHandler(tPtr->handler);
+                       tPtr->handler = NULL;
+               }
+               /*if (cPtr->handler.write) {
+                  WMDeleteInputHandler(cPtr->handler.write);
+                  cPtr->handler.write = NULL;
+                  } */
+       }
+
+       if (cPtr->handler.write) {
+               WMDeleteInputHandler(cPtr->handler.write);
+               cPtr->handler.write = NULL;
+       }
+
+       return 1;
+}
+
+/*
+ * WMGetConnectionAvailableData(connection):
+ *
+ * will return a WMData structure containing the available data on the
+ * specified connection. If connection is non-blocking (default) and no data
+ * is available when this function is called, an empty WMData is returned.
+ *
+ * If an error occurs while reading or the other side closed connection,
+ * it will return NULL.
+ * Also trying to read from an already died or closed connection is
+ * considered to be an error condition, and will return NULL.
+ */
+WMData *WMGetConnectionAvailableData(WMConnection * cPtr)
+{
+       char buffer[NETBUF_SIZE];
+       int nbytes;
+       WMData *aData;
+
+       wassertrv(cPtr->state != WCNotConnected && cPtr->state != WCListening, NULL);
+       wassertrv(cPtr->state != WCInProgress && cPtr->state != WCFailed, NULL);
+
+       if (cPtr->state != WCConnected)
+               return NULL;
+
+       aData = NULL;
+
+ again:
+       nbytes = read(cPtr->sock, buffer, NETBUF_SIZE);
+       if (nbytes < 0) {
+               switch (errno) {
+               case EINTR:
+                       goto again;
+               case EWOULDBLOCK:
+                       aData = WMCreateDataWithCapacity(0);
+                       break;
+               default:
+                       WCErrorCode = errno;
+                       cPtr->state = WCDied;
+                       removeAllHandlers(cPtr);
+                       if (cPtr->delegate && cPtr->delegate->didDie)
+                               (*cPtr->delegate->didDie) (cPtr->delegate, cPtr);
+                       break;
+               }
+       } else if (nbytes == 0) {       /* the other side has closed connection */
+               cPtr->state = WCClosed;
+               removeAllHandlers(cPtr);
+               if (cPtr->delegate && cPtr->delegate->didDie)
+                       (*cPtr->delegate->didDie) (cPtr->delegate, cPtr);
+       } else {
+               aData = WMCreateDataWithBytes(buffer, nbytes);
+       }
+
+       return aData;
+}
+
+void WMSetConnectionDelegate(WMConnection * cPtr, ConnectionDelegate * delegate)
+{
+       wassertr(cPtr->sock >= 0);
+       /* Don't try to set the delegate multiple times */
+       wassertr(cPtr->delegate == NULL);
+
+       cPtr->delegate = delegate;
+       if (delegate && delegate->didReceiveInput && !cPtr->handler.read)
+               cPtr->handler.read = WMAddInputHandler(cPtr->sock, WIReadMask, inputHandler, cPtr);
+       if (delegate && delegate->didCatchException && !cPtr->handler.exception)
+               cPtr->handler.exception = WMAddInputHandler(cPtr->sock, WIExceptMask, inputHandler, cPtr);
+}
+
+#if 0
+Bool WMIsConnectionNonBlocking(WMConnection * cPtr)
+{
+#if 1
+       int state;
+
+       state = fcntl(cPtr->sock, F_GETFL, 0);
+
+       if (state < 0) {
+               /* If we can't use fcntl on socket, this probably also means we could
+                * not use fcntl to set non-blocking mode, and since a socket defaults
+                * to blocking when created, return False as the best assumption */
+               return False;
+       }
+
+       return ((state & NONBLOCK_OPT) != 0);
+#else
+       return cPtr->isNonBlocking;
+#endif
+}
+#endif
+
+Bool WMSetConnectionNonBlocking(WMConnection * cPtr, Bool flag)
+{
+       wassertrv(cPtr != NULL && cPtr->sock >= 0, False);
+
+       flag = ((flag == 0) ? 0 : 1);
+
+       if (cPtr->isNonBlocking == flag)
+               return True;
+
+       if (setSocketNonBlocking(cPtr->sock, flag) == True) {
+               cPtr->isNonBlocking = flag;
+               return True;
+       }
+
+       return False;
+}
+
+Bool WMSetConnectionCloseOnExec(WMConnection * cPtr, Bool flag)
+{
+       wassertrv(cPtr != NULL && cPtr->sock >= 0, False);
+
+       if (fcntl(cPtr->sock, F_SETFD, ((flag == 0) ? 0 : FD_CLOEXEC)) < 0) {
+               return False;
+       }
+
+       return True;
+}
+
+void WMSetConnectionShutdownOnClose(WMConnection * cPtr, Bool flag)
+{
+       cPtr->shutdownOnClose = ((flag == 0) ? 0 : 1);
+}
+
+void *WMGetConnectionClientData(WMConnection * cPtr)
+{
+       return cPtr->clientData;
+}
+
+void WMSetConnectionClientData(WMConnection * cPtr, void *data)
+{
+       cPtr->clientData = data;
+}
+
+unsigned int WMGetConnectionFlags(WMConnection * cPtr)
+{
+       return cPtr->uflags;
+}
+
+void WMSetConnectionFlags(WMConnection * cPtr, unsigned int flags)
+{
+       cPtr->uflags = flags;
+}
+
+WMArray *WMGetConnectionUnsentData(WMConnection * cPtr)
+{
+       return cPtr->outputQueue;
+}
+
+void WMSetConnectionDefaultTimeout(unsigned int timeout)
+{
+       if (timeout == 0) {
+               DefaultTimeout = DEF_TIMEOUT;
+       } else {
+               DefaultTimeout = timeout;
+       }
+}
+
+void WMSetConnectionOpenTimeout(unsigned int timeout)
+{
+       if (timeout == 0) {
+               OpenTimeout = DefaultTimeout;
+       } else {
+               OpenTimeout = timeout;
+       }
+}
+
+void WMSetConnectionSendTimeout(WMConnection * cPtr, unsigned int timeout)
+{
+       if (timeout == 0) {
+               cPtr->sendTimeout.timeout = DefaultTimeout;
+       } else {
+               cPtr->sendTimeout.timeout = timeout;
+       }
+}