/********************************************************************************************
	Clean OS Windows library module version 1.2.1.
	This module is part of the Clean Object I/O library, version 1.2.1,
	for the Windows platform.
********************************************************************************************/

/********************************************************************************************
	About this module:
	Routines related to printing.
********************************************************************************************/
//#define FD_SETSIZE "maximal number of sockets to select on" (default is 64) 

#include <winsock.h>
#include "util_121.h"
#include "Clean.h"
#include "cTCP_121.h"
#define trace(x)

/*
------------------------ THE FUNCTION PROTOTYPES ------------------------------------------

Naming convention: functions, which are called from Clean end with a "C". Function's that
*/

//************************************************
// functions, which are called from Clean (semantic is explained in tcp.icl or ostcp.icl)

int os_eom(SOCKET endpointRef);
int os_disconnected(SOCKET endpointRef);
int os_connectrequestavailable(SOCKET endpointRef);
void abortedHost_syncC(CleanString inetAddr, int *errCode, int *ipAddr);
void abortedHost_asyncC(CleanString inetAddr, int *errCode, HANDLE *endpointRef);
void openTCP_ListenerC(int portNum, int *pErrCode, SOCKET *pEndpointRef);
void acceptC(SOCKET endpointRef, int *pErrCode, int *pInetHost, SOCKET *pEndpointRef);
void os_connectTCPC(int isIOProg, int block, int doTimeout, unsigned int stopTime,
						   int ipAddr, int portnum,
						   int *errCode, int *timeoutExpiredP, int *endpointRefP
						  );
void sendC(SOCKET endpointRef, CleanString data, int begin, int nBytes, int *pErrCode, int *pSentBytes);
void receiveC(SOCKET endpointRef, int maxSize, CleanString *data);
int getRcvBuffSizeC();
void resizeStringC(CleanString string, int newSize);
int data_availableC(SOCKET endpointRef);
void disconnectGracefulC(SOCKET endpointRef);
void disconnectBrutalC(SOCKET endpointRef);
void garbageCollectEndpointC(SOCKET endpointRef);
void os_select_inetevents(SOCKET endpointRef, int receiverCategory,
								 int referenceCount, int getReceiveEvents, int getSendEvents,
								 int aborted
								);
void selectChC(int justForMac, int nonBlocking, int doTimeout, unsigned int stopTime, 
					  SOCKET *pRChannels, int *justForMac2, SOCKET *pSChannels,
					  int *pErrCode
					 );
int tcpPossibleC(void);

//************************************************
// other functions

void StartUp(int abort);
	// initialize winsock (if not started yet. uses global "tcpStartedUp")
	// if succesful: tcpStartedUp==TRUE afterwards
	// if not succesful && abort: aborts
	// if not succesful && !abort: tcpStartedUp==FALSE afterwards
void CleanUp(void);

//************************************************
// functions to deal with the endpoint dictionary:

int insertNewDictionaryItem(SOCKET endpointRef);
	// allocates memory for new dictionary item, initializes it as far as possible and
	// adds it to the dictionary. returns error code: 0==ok, 1==not ok
dictitem* lookup(SOCKET endpointRef);
	// lookup entry (CFN)
void setEndpointDataC(int endpointRef, int referenceCount,
							 int hasReceiveNotifier, int hasSendableNotifier, int aborted);
	// set the corresponding fields of the entry 
void getEndpointDataC(int endpointRef, int *referenceCount,
							 int *hasReceiveNotifier, int *hasSendableNotifier, int *aborted);
	// returns the corresponding fields of the entry 
void removeDictionaryItem(SOCKET endpointRef);
	// remove one item via pointer manipulations (must not be called from notifier)

//--------------------- GLOBAL VARIABLES ------------------------------------------

//	The inetEventQueue: To append easily a new item to the end of the list, "toLastNext" points
//	to the "next" field of the last list element (or to &inetEventQueue)


dictitem	*endpointDict = NULL;

int			tcpStartedUp = FALSE;
int			rcvBuffSize;	// contains the size of the sockets internal buffer for receiving data.
CleanString	pRcvBuff;

extern	DNSInfo	*DNSInfoList;
extern  HWND    ghMainWindow;
extern	void (*exit_tcpip_function)();	// the function will be called, when the Clean
										// program terminates 

//--------------------- FUNCTION IMPLEMENTATION -----------------------------------

int tcpPossibleC(void)
{
	StartUp(FALSE);
	return tcpStartedUp;
}

int os_eom(SOCKET endpointRef)
{
	int			err, err2;
	char		dummyBuffer[1];
	dictitem	*pDictitem;

	err		= recv(	endpointRef, dummyBuffer, 1, MSG_PEEK);
	err2	= WSAGetLastError();
	if (err>0)
		return FALSE;
	if (err<0 && err2==WSAEWOULDBLOCK)
		return FALSE;
	pDictitem	= lookup(endpointRef);
	trace(	if (!pDictitem)
				rMessageBox(NULL, MB_APPLMODAL, "in os_eom", "ERROR");)
	if (pDictitem->availByteValid)
		return FALSE;
	if (err==0)
		return TRUE;
	return TRUE;
}

int os_disconnected(SOCKET endpointRef)
{
	int		err;
	char	string[1];
	dictitem	*pDictitem;

	pDictitem	= lookup(endpointRef);
	trace(	if (!pDictitem)
				rMessageBox(NULL, MB_APPLMODAL, "in os_disconnected", "ERROR");)
	if (pDictitem->disconnected)
		return TRUE;
	err	= send(	endpointRef, string, 0, 0);
	if (err!=SOCKET_ERROR)
		return FALSE;
	  else
		return WSAGetLastError()!=WSAEWOULDBLOCK;
				// only this error can happen with sockets that can still send
}

int os_connectrequestavailable(SOCKET endpointRef)
{
	FD_SET	readSet;
	TIMEVAL timeout;
	int		nr;

	FD_ZERO(&readSet);
	FD_SET(endpointRef,&readSet);

	timeout.tv_sec	= 0;			// Timeout in sec's
	timeout.tv_usec	= 0;			// Timeout in microsec's
	nr = select(0,&readSet,NULL,NULL,&timeout);
	return nr>0 && FD_ISSET(endpointRef,&readSet);
}

#define majorVrs 1
#define minorVrs 1

void StartUp(int abort)
{
	if (!tcpStartedUp)
		{
		WORD wVersionRequested; 
		WSADATA wsaData; 
		int err, four=4;
		SOCKET s;

		wVersionRequested = MAKEWORD(majorVrs, minorVrs); 
 		err = WSAStartup(wVersionRequested, &wsaData); 
 		if (err != 0) {
			if (abort) {
				rMessageBox(NULL, MB_APPLMODAL, "ERROR", "can't start up winsock"
							"\nprogram aborts");
				ExitProcess (255);
				}
			  else
				return;
			}

		/* Confirm that the Windows Sockets DLL supports version mj.mi.*/ 
		/* Note that if the DLL supports versions greater */ 
		/* than mj.mi in addition to mj.mi, it will still return */ 
		/* mj.mi in wVersion since that is the version we */ 
		/* requested. */ 
 
		if ( LOBYTE( wsaData.wVersion ) != majorVrs || 
			HIBYTE( wsaData.wVersion ) != minorVrs ) {
			WSACleanup(); 
			if (abort) {
				rMessageBox(NULL, MB_APPLMODAL, "ERROR", "winsock 1.1 or higher not available"
							"\nprogram aborts");
				ExitProcess (255);
				}
			  else
				return;
			};
		
		// initialize rcvBuffSize
		s = socket(PF_INET, SOCK_STREAM, 0);
		if (s==INVALID_SOCKET) {
			rMessageBox(NULL, MB_APPLMODAL, "ERROR", "can't create a socket"
						"\nprogram aborts");
			ExitProcess (255);
			};
		if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*) &rcvBuffSize, &four)) {
			rMessageBox(NULL, MB_APPLMODAL, "ERROR", "can't call getsockopt"
						"\nprogram aborts");
			ExitProcess (255);
			};
		
		pRcvBuff	= (CleanString) LocalAlloc(LMEM_FIXED, rcvBuffSize+4);
		if (pRcvBuff==NULL) {
			rMessageBox(NULL, MB_APPLMODAL, "ERROR", "out of memory"
						"\nprogram aborts");
			ExitProcess (255);
			};
		exit_tcpip_function	= CleanUp;
		tcpStartedUp	= TRUE;
		};
}
	

void lookupHost_syncC(CleanString inetAddr, int *errCode, int *ipAddrP)
// error code: 0 ok, 1 error (also: addr doesn't exist)
{
	HOSTENT *hostentP;
	unsigned long ipAddr;
	
	StartUp(TRUE);
	ipAddr	= inet_addr(CleanStringCharacters(inetAddr));
	if (ipAddr!=INADDR_NONE)
		{
		*errCode	= 0;
		*ipAddrP	= ntohl(ipAddr);
		return;
		};
	*errCode = 1;
	hostentP = gethostbyname(CleanStringCharacters(inetAddr));	// string is alphanumerical 
	if (hostentP!=NULL)
		{ *ipAddrP = ntohl(((DWORD *)(*(hostentP->h_addr_list)))[0]);	
		  if (*ipAddrP!=0)
		    *errCode = 0;
	    };
}

void lookupHost_asyncC(CleanString inetAddr, int *errCode, HANDLE *endpointRef)
// errCode: 0 ok, 1 not ok
{
	DNSInfo		*newPtr;
	HANDLE		dnsHdl;

	StartUp(TRUE);

	*errCode	= 1;
	newPtr = (DNSInfo*) LocalAlloc(LMEM_FIXED,sizeof(DNSInfo));
	if (newPtr==NULL) {
		*errCode	= 1;
		return;
		};

	newPtr->next = DNSInfoList;
	DNSInfoList = newPtr;

	// and fill the fields and initiate DNS lookup.
	dnsHdl = WSAAsyncGetHostByName(ghMainWindow,PM_DNS_EVENT,CleanStringCharacters(inetAddr),
									DNSInfoList->junion.freeSpace,
									MAXGETHOSTSTRUCT);
	// this will cause the sending of a PM_DNS_EVENT message to the main window.
	// The wParam value of that message will be equal to dnsHdl so that
	// the ipAdress of the lookedup host can be retrieved then.
	// The element of the List should be deallocated then
	if (dnsHdl==0)
		return;

	DNSInfoList->dnsHdl = dnsHdl;
	*errCode			= 0;
	*endpointRef		= dnsHdl;
}

void openTCP_ListenerC(int portNum, int *pErrCode, SOCKET *pEndpointRef)
// errCode: 0:ok;	otherwise:not ok
{
	SOCKET		s;
	SOCKADDR_IN	srvAdr;

	StartUp(TRUE);

	*pErrCode = 1;
	
	s = socket(PF_INET, SOCK_STREAM, 0);
	if (s==INVALID_SOCKET)
	  return;

	srvAdr.sin_family = AF_INET;			// of course internet adress family
	srvAdr.sin_addr.s_addr = INADDR_ANY;	// internet address will be given after "accept" 
	srvAdr.sin_port = htons((short int)portNum);
	
	*pErrCode = bind(s, (LPSOCKADDR) &srvAdr, sizeof(srvAdr));
	if (*pErrCode) {
		closesocket(s);
		return;
		};

	*pErrCode = listen(s,5);
	if (*pErrCode) {
		closesocket(s);
		return;
		};
	*pEndpointRef = s;

	*pErrCode	= insertNewDictionaryItem(s);
	if (*pErrCode)
		return;
	
	setEndpointDataC(s, 1,0,0,0);
}

void acceptC(SOCKET listener, int *pErrCode, int *pInetHost, SOCKET *pEndpointRef)
// errCode: 0:ok;	otherwise:not ok
{
	SOCKET		endpointRef;
	SOCKADDR_IN	clientAdr;
	int			clientAdrSize, tru;
	
	clientAdrSize	= sizeof(clientAdr);
	endpointRef		= accept(listener,(LPSOCKADDR) &clientAdr, &clientAdrSize);
	tru				= TRUE;
	ioctlsocket(endpointRef, FIONBIO, &tru);	// set mode to non blocking
	*pErrCode		= endpointRef==INVALID_SOCKET;
	if (*pErrCode)
		return;

	*pInetHost		= ntohl(clientAdr.sin_addr.s_addr);
	*pEndpointRef	= endpointRef;

	*pErrCode		= insertNewDictionaryItem(endpointRef);
	if (*pErrCode)
		return;

	setEndpointDataC(endpointRef,2,0,0,0);
}

void os_connectTCPC(int onlyForMac, int block, int doTimeout, unsigned int stopTime,
					int ipAddr, int portnum,
					int *errCodeP, int *timeoutExpiredP, int *endpointRefP)
// errCode: 0 ok;	1 not ok
{
	SOCKET client;
	SOCKADDR_IN	srvAdr,clientAdr;
	int err, tru;

	*errCodeP = 1;
	*timeoutExpiredP	= FALSE;
		
	client = socket(PF_INET, SOCK_STREAM, 0);
	if (client==INVALID_SOCKET)
		return;

	clientAdr.sin_family = AF_INET;			// of course internet adress family
	clientAdr.sin_addr.s_addr = INADDR_ANY;	// internet adress will be given after "connect" 
	clientAdr.sin_port = 0;					// the winsock library will choose a free number between 1024 and 5000
	
	err = bind(client, (LPSOCKADDR) &clientAdr, sizeof(clientAdr));
	if (err)
		{
		closesocket(client);
		return;
		};

	srvAdr.sin_family = AF_INET;			// of course internet adress family
	srvAdr.sin_addr.s_addr = htonl(ipAddr);
	srvAdr.sin_port = htons((short int)portnum);	
	
	tru				= TRUE;

	//////////////////////////////////////////////////////////////////////////
	if (block && doTimeout)
		{
		ioctlsocket(client, FIONBIO, &tru);		// set mode to non blocking
		err = connect(client, (LPSOCKADDR) &srvAdr, sizeof(srvAdr));
		if (!err) {
			*errCodeP = 0;
			*timeoutExpiredP	= FALSE;
			*endpointRefP = client;
			}
		  else if (WSAGetLastError()!=WSAEWOULDBLOCK) {
				closesocket(client);
				return;
				}
			else
				{
				FD_SET writeSet, exptnSet;
				TIMEVAL timeout;
				unsigned int	now;
				int				noOfWritableSockets, timeoutTicks;		
				FD_ZERO(&writeSet);
				FD_SET(client,&writeSet);
				FD_ZERO(&exptnSet);
				FD_SET(client,&exptnSet);

				now	= GetTickCount();
				timeoutTicks	= ((int)stopTime) - ((int)now);
				if (timeoutTicks<=0)
					{						// timeout expired
					closesocket(client);
					*timeoutExpiredP	= TRUE;
					return;
					};
				timeout.tv_sec	= timeoutTicks / 1000;			// Timeout in sec's
				timeout.tv_usec	= (timeoutTicks % 1000)*1000;	// Timeout in microsec's
				noOfWritableSockets = select(0,NULL,&writeSet,&exptnSet,&timeout);
				*errCodeP =		noOfWritableSockets<0
							||	(noOfWritableSockets>0 && FD_ISSET(client,&exptnSet));
				*timeoutExpiredP	= noOfWritableSockets==0;
				*endpointRefP		= client;
				if (*errCodeP || *timeoutExpiredP) {
					closesocket(client);
					return;
					};
				};
		};
	///////////////////////////////////////////////////////////////////////////
	if (block && !doTimeout)
		{
		err = connect(client, (LPSOCKADDR) &srvAdr, sizeof(srvAdr));
		if (err)
			{
			closesocket(client);
			return;
			};

		ioctlsocket(client, FIONBIO, &tru);		// set mode to non blocking
		
		*errCodeP = 0;
		*timeoutExpiredP	= FALSE;
		*endpointRefP = client;
		};
	////////////////////////////////////////////////////////////////////////////
	if (!block)
		{
	
		err = WSAAsyncSelect(client,ghMainWindow,PM_SOCKET_EVENT,FD_CONNECT);
		if (err)
			{
			closesocket(client);
			return;
			};
		
		err = connect(client, (LPSOCKADDR) &srvAdr, sizeof(srvAdr));
		if (err==SOCKET_ERROR)
			{
			err = WSAGetLastError();		// a WSAEWOULDBLOCK error is a pretty harmless thing
			if (err!=WSAEWOULDBLOCK)	
				{
				closesocket(client);
				return;
				};
			};
		*errCodeP			= 0;
		*timeoutExpiredP	= FALSE;
		*endpointRefP		= client;
		};
	//////////////////////////////////////////////////////////////////////////////

	*errCodeP	= insertNewDictionaryItem(client);
	if (*errCodeP)
		{
		closesocket(client);
		return;
		};
	if (block)
		setEndpointDataC(client,2,0,0,0);
	  else
		{	
		dictitem *ptr;
		ptr	= lookup(client);
		ptr->referenceCount			= 1;
		ptr->hasReceiveNotifier		= 0;
		ptr->hasSendableNotifier	= 1;
		ptr->aborted				= 0;
		};
}

void sendC(SOCKET endpointRef, CleanString data, int begin, int nBytes,
		 int *pErrCode, int *pSentBytes)
{
	int				sentBytes;

	*pErrCode	= 0;
	sentBytes = send(endpointRef, CleanStringCharacters(data)+begin,nBytes, 0);
	if (sentBytes==SOCKET_ERROR) {
		int err;
		sentBytes	= 0;
		err	= WSAGetLastError();
		if (err!=WSAEWOULDBLOCK) {
			dictitem	*pDictitem;
			pDictitem	= lookup(endpointRef);
			trace(	if (!pDictitem)
						rMessageBox(NULL, MB_APPLMODAL, "in sendC", "ERROR");)
			pDictitem->disconnected	=1;
			*pErrCode	= 1;
			}
		};
	*pSentBytes	= sentBytes;
}


void receiveC(SOCKET endpointRef, int maxSize, CleanString *pReceived)
{
	int			size, received;
	dictitem	*pDictitem;

	*pReceived	= (CleanString) pRcvBuff;
	size		= maxSize<=0 ? rcvBuffSize : maxSize;
	received	= recv(	endpointRef, CleanStringCharacters(pRcvBuff), size, 0);
	pDictitem	= lookup(endpointRef);
	trace(	if (!pDictitem)
				rMessageBox(NULL, MB_APPLMODAL, "in receiveC", "ERROR");)
	if (received>0) {
		pDictitem->availByteValid = 0;
		CleanStringLength(pRcvBuff)	= received;
		}
	  else if (pDictitem->availByteValid) {
			CleanStringCharacters(pRcvBuff)[0]	= pDictitem->availByte;
			pDictitem->availByteValid = 0;
			CleanStringLength(pRcvBuff) = 1;
			}
		else
			CleanStringLength(pRcvBuff) = 0;
}

int data_availableC(SOCKET endpointRef)
{
	dictitem	*pDictitem;
	int			err;

	pDictitem	= lookup(endpointRef);
	trace(	if (!pDictitem)
				rMessageBox(NULL, MB_APPLMODAL, "in data_availableC", 
							"ERROR\nendpoint %i not found", endpointRef);)
	if (pDictitem->availByteValid)
		return TRUE;
	err	= recv(	endpointRef, &pDictitem->availByte, 1, MSG_PEEK);
	if (err>0) {
		pDictitem->availByteValid = 1;
		return TRUE;
		};
	return FALSE;
}

void disconnectGracefulC(SOCKET endpointRef)
{
	shutdown(endpointRef,1);	// 1: graceful
}

void disconnectBrutalC(SOCKET endpointRef)
{
	LINGER	linger;
	linger.l_onoff	= 1;
	linger.l_linger	= 0;
	setsockopt(endpointRef, SOL_SOCKET, SO_LINGER, (char*) &linger, sizeof(linger));
}

void garbageCollectEndpointC(SOCKET endpointRef)
{
	dictitem	*pDictitem;

	pDictitem	= lookup(endpointRef);
	if (pDictitem!=NULL && pDictitem->referenceCount==0) {
		closesocket(endpointRef);
		removeDictionaryItem(endpointRef);
		};
}


void os_select_inetevents(	SOCKET endpointRef, int receiverCategory,
							int referenceCount, int getReceiveEvents, int getSendEvents, 
							int aborted)
{
	setEndpointDataC(endpointRef, referenceCount, getReceiveEvents, getSendEvents, aborted);
}

void initFD_SET(FD_SET **ppSet, SOCKET sockets[], int n)
{
	int	i;
	FD_SET	*pSet;

	pSet	= (FD_SET*) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, n*sizeof(SOCKET)+sizeof(u_int));
	for(i=0; i<n; i++)
		FD_SET(sockets[i], pSet);
	*ppSet	= pSet;
}
			
void selectChC(	int justForMac, int nonBlocking, int doTimeout, unsigned int stopTime, 
			    SOCKET *pRChannels, int *justForMac2, SOCKET *pSChannels,
				int *pErrCode)
// error code: 0=ok; 1=timeout expired, 3=other errors
{
	int				nRChannels, nSChannels, i;
	FD_SET			*pReadSet, *pWriteSet;
	TIMEVAL			timeout;
	unsigned int	now;
	int				n, timeoutTicks;		

	nRChannels	= (int) pRChannels[-2];
	nSChannels	= (int) pSChannels[-2];
	if (doTimeout)
		{
		now	= GetTickCount();
		timeoutTicks	= nonBlocking ? 0 : ((int)stopTime) - ((int)now);
		if (timeoutTicks<0)
			{
			*pErrCode	= 1;
			return;
			};
		timeout.tv_sec	= timeoutTicks / 1000;			// Timeout in sec's
		timeout.tv_usec	= (timeoutTicks % 1000)*1000;	// Timeout in microsec's
		};
	initFD_SET(&pReadSet,  pRChannels, nRChannels);
	initFD_SET(&pWriteSet, pSChannels, nSChannels);
	n = select(0,pReadSet,pWriteSet,NULL, doTimeout ? &timeout : NULL);
	if (n==0)
		{	// timeout expired
		*pErrCode	= 1;
		return;
		};
	if (n<0)
		{
		*pErrCode	= 3;
		return;
		};
	for(i=0; i<nRChannels; i++)
		if (FD_ISSET(pRChannels[i], pReadSet))
			pRChannels[i]	= 0;
	for(i=0; i<nSChannels; i++)
		if (FD_ISSET(pSChannels[i], pWriteSet))
			pSChannels[i]	= 0;
	*pErrCode	= 0;
}

// this function is called from Cleans runtime system via *exit_tcpip_function
void CleanUp(void)
{
	dictitem	*pDictitem, *pTemp;
	int			referenceCount;
	pDictitem	= endpointDict;
	while (pDictitem) {
		referenceCount	= pDictitem->referenceCount;
		if (referenceCount!=0) {
			if (referenceCount==1 && pDictitem->aborted) {
				disconnectBrutalC(pDictitem->endpointRef);
				}
			else
				disconnectGracefulC(pDictitem->endpointRef);
			closesocket(pDictitem->endpointRef);
			};
		pTemp	= pDictitem;
		pDictitem	= pDictitem->next;
		GlobalFree((char*) pTemp);
		};
	WSACleanup();
}


//------------------------ FUNCTION IMPLEMENTATIONS FOR THE ENDPOINT DICTIONARY -------

int insertNewDictionaryItem(SOCKET endpointRef)
{
	dictitem	*newItem			= (dictitem*) GlobalAlloc(LMEM_FIXED, sizeof(dictitem));
	if (newItem==NULL)
		return 1;
		
	newItem->endpointRef			= endpointRef;
	newItem->next					= endpointDict;
	newItem->availByteValid			= 0;
	newItem->aborted				= 0;
	newItem->disconnected			= 0;
	endpointDict					= newItem;

	return 0;
}

dictitem* lookup(SOCKET endpointRef)
{
	dictitem	*ptr=endpointDict;
	while (ptr!=NULL && (ptr->endpointRef!=endpointRef))
		ptr	= ptr->next;
	
	return ptr;
}

void setEndpointDataC(	int endpointRef, int referenceCount,
						int hasReceiveNotifier, int hasSendableNotifier, int aborted)
{
	dictitem	*ptr			= lookup((SOCKET) endpointRef);
	
	if (ptr!=NULL)
		{	ptr->referenceCount			= referenceCount;
			ptr->hasReceiveNotifier		= hasReceiveNotifier ? 1 : 0;
			ptr->hasSendableNotifier	= hasSendableNotifier ? 1 : 0;
			ptr->aborted				= aborted ? 1 : 0;
		};
	WSAAsyncSelect(endpointRef, ghMainWindow, PM_SOCKET_EVENT, 
						(hasReceiveNotifier ? FD_READ | FD_OOB | FD_ACCEPT | FD_CLOSE : 0)
					  |	(hasSendableNotifier ? FD_WRITE | FD_CLOSE : 0));
}


void getEndpointDataC(	int endpointRef, int *referenceCount,
						int *hasReceiveNotifier, int *hasSendableNotifier, int *aborted)
{
	dictitem	*ptr			= lookup((SOCKET) endpointRef);
	
	if (ptr!=NULL)
		{	*referenceCount			= ptr->referenceCount;
			*hasReceiveNotifier		= ptr->hasReceiveNotifier!=0;
			*hasSendableNotifier	= ptr->hasSendableNotifier!=0;
			*aborted				= ptr->aborted!=0;
		};
}


void removeDictionaryItem(SOCKET endpointRef)
// the dictionary MUST contain a valid item with the endpointRef
{
	dictitem	**ptr, *temp;
	int			notRemoved;

	ptr	= &endpointDict;
	notRemoved	= TRUE;
	while(notRemoved)
		if ((*ptr)->endpointRef==endpointRef)
			{
				temp	= *ptr;
				*ptr	= (*ptr)->next;
				GlobalFree((char*) temp);
				notRemoved	= FALSE;
			}
		  else
			ptr	= &((*ptr)->next);
}
