/* * 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 #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #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; int 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 */ 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; int sock, on; int size; 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; int 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; } }