www.pudn.com > speakfs72.zip > CONNECT.C, change:2002-01-05,size:80104b


/* 
 
	Window procedure for connection MDI window 
	 
*/ 
 
#include "netfone.h" 
 
#define ASYNC_OUTPUT 
                             
static struct { 
	char header[4]; 
	unsigned short len, ilen; 
	soundbuf sbm; 
} mb = {{1, 2, 3, 4}}; 
#define sb	mb.sbm 
 
soundbuf ebuf;				  		  // Utility sound buffer 
static LONG pktlen;					  // Packet length to transmit 
static struct adpcm_state adpcm;	  // ADPCM compression state 
static int squelched = FALSE;		  // Squelched by VOX ? 
static int sqpacket = FALSE;		  // Is this packet squelched 
static char ourSendingHost[16];		  // Host name for sound buffer sendinghost 
static int xseq = 0;				  // Robust mode transmit sequence number 
 
extern long outputPending; 
char blankit[] = "                                                              " 
				 "                                                              " 
				 "                                                              "; 
 
/*  GSMCOMP  --  Compress the contents of a sound buffer using GSM.  */ 
 
static void gsmcomp(soundbuf *asb) 
{ 
    gsm_signal src[160]; 
    gsm_frame dst; 
    int i, j, l = 0; 
    char *dp = (asb->buffer.buffer_val) + sizeof(short); 
    long ldata = asb->buffer.buffer_len;  
     
    for (i = 0; i < ldata; i += 160) { 
        for (j = 0; j < 160; j++) { 
            if ((i + j) < asb->buffer.buffer_len) { 
                src[j] = audio_u2s(asb->buffer.buffer_val[i + j]); 
            } else { 
                src[j] = 0; 
            } 
        } 
        gsm_encode(gsmh, src, dst); 
        _fmemcpy(dp, dst, sizeof dst); 
        dp += sizeof dst; 
        l += sizeof dst; 
    } 
 
    /* Hide original uncompressed buffer length in first 2 bytes of buffer. */ 
     
    *((short *) asb->buffer.buffer_val) = (short) ldata; 
    revshort((short *) asb->buffer.buffer_val); 
    asb->buffer.buffer_len = l + sizeof(short); 
} 
 
/*	ADPCMCOMP  --  Compress the contents of a sound buffer using ADPCM.  */ 
 
static void adpcmcomp(soundbuf *asb) 
{ 
	unsigned char *dp = (unsigned char *) asb->buffer.buffer_val; 
	struct adpcm_state istate; 
 
	istate = adpcm; 
	adpcm_coder_u(dp, (char *) dp, (int) asb->buffer.buffer_len, &adpcm); 
	asb->buffer.buffer_len /= 2; 
 
	/* Hide the ADPCM encoder state at the end of this buffer. 
	   The shifting and anding makes the code byte-order 
	   insensitive. */ 
 
	dp += asb->buffer.buffer_len; 
	*dp++ = ((unsigned int) istate.valprev) >> 8; 
	*dp++ = istate.valprev & 0xFF; 
	*dp = istate.index; 
	asb->buffer.buffer_len += 3; 
} 
 
/*	LPCCOMP  --  Compress the contents of a sound buffer using LPC.  */ 
 
static void lpccomp(asb) 
  soundbuf *asb; 
{ 
	int i, l = 0; 
	char *dp = ((char *) asb->buffer.buffer_val) + sizeof(short); 
	unsigned char *src = ((unsigned char *) asb->buffer.buffer_val); 
	lpcparams_t lp; 
 
	asb->compression |= fCompLPC; 
	for (i = 0; i < asb->buffer.buffer_len; i += LPC_FRAME_SIZE) { 
		lpc_analyze(src + i, &lp); 
		_fmemcpy(dp, &lp, sizeof lp); 
		dp += sizeof lp; 
		l += sizeof lp; 
	} 
 
	// Hide original uncompressed buffer length in first 2 bytes of buffer. 
 
	*((short *) asb->buffer.buffer_val) = (short) asb->buffer.buffer_len; 
	revshort((short *) asb->buffer.buffer_val); 
	asb->buffer.buffer_len = l + sizeof(short); 
} 
 
/*	LPC10COMP  --  Compress the contents of a sound buffer using LPC-10.  */ 
 
static void lpc10comp(soundbuf *asb) 
{ 
	char *dp = (char *) asb->buffer.buffer_val; 
	unsigned char *src = ((unsigned char *) asb->buffer.buffer_val); 
 
	asb->compression |= fCompLPC10; 
	asb->buffer.buffer_len = lpc10encode(src, dp, asb->buffer.buffer_len); 
} 
 
/*	LPC10STUFF	--	Stuff last 16 bytes of LPC10 packet in sendinghost. */ 
 
static int lpc10stuff(struct soundbuf FAR *asb, int pktlen) 
{ 
	if ((asb->compression & fCompLPC10) && (asb->buffer.buffer_len > 16)) { 
		_fmemcpy(asb->sendinghost,  
			((char *) asb) + (pktlen - (sizeof asb->sendinghost)), 
			sizeof asb->sendinghost); 
		pktlen -= sizeof asb->sendinghost; 
		if (robust > 1) { 
			asb->compression |= fCompRobust; 
			asb->buffer.buffer_len |= ((long) xseq) << 24; 
			xseq = (xseq + 1) & 0xFF; 
		} 
	} 
	return pktlen; 
} 
 
/*	LPC10UNSTUFF  --  Reverse the effect of lpc10stuff. */ 
 
static void lpc10unstuff(struct soundbuf FAR *asb) 
{ 
	if ((asb->compression & fCompLPC10) && (asb->buffer.buffer_len > 16)) { 
		pktlen += sizeof asb->sendinghost; 
		if (asb->compression & fCompRobust) { 
			asb->compression &= ~fCompRobust; 
			asb->buffer.buffer_len &= 0xFFFFFFL; 
		} 
	} 
} 
 
/*  COMPRESS2X  --  Compress a sound buffer with Simple (discard every 
					other sample) compression.  If you're doing both 
					Simple and GSM compression, Simple compression must be 
					done first.  */ 
					 
void compress2X(soundbuf *asb) 
{ 
#ifdef OLD2X 
    LONG i; 
	          
    asb->buffer.buffer_len /= 2; 
    for (i = 1; i < asb->buffer.buffer_len; i++) { 
        asb->buffer.buffer_val[i] = asb->buffer.buffer_val[i * 2]; 
    } 
#else 
	int li = (int) asb->buffer.buffer_len; 
	int lo = li / 2; 
	 
	rate_flow(&xrate, (unsigned char *) asb->buffer.buffer_val, 
			  (unsigned char *) asb->buffer.buffer_val, 
			  &li, &lo); 
	asb->buffer.buffer_len = lo;  
#endif 
} 
 
/*	MONITORPORT  --  Obtain an auxiliary socket to receive input 
					 from a given port.  Existing sockets are reused 
					 and their reference count incremented.  */ 
 
static struct auxSocket FAR *monitorPort(unsigned short port) 
{ 
	struct auxSocket FAR *as = asList; 
	WORD serr = 0; 
 
	while (as != NULL) { 
		if (port == as->asport) { 
			break; 
		} 
		as = as->asnext; 
	} 
	 
	if (as == NULL) { 
#ifdef TRACE_AUX_SOCKET 
OutputDebugString("Allocating aux socket.\r\n"); 
#endif	 
		as = (struct auxSocket FAR *) GlobalAllocPtr(GPTR, sizeof(struct auxSocket)); 
		if (as != NULL) { 
			as->asnext = asList; 
			asList = as; 
			as->asrefc = 0; 
			as->asport = port; 
			as->asdata = as->asctrl = INVALID_SOCKET; 
		} 
	} 
	if (as != NULL) { 
		if (as->asrefc == 0) { 
		    serr = CreateSocket(&(as->asdata), SOCK_DGRAM, 
		    		htonl(INADDR_ANY), htons(as->asport)); 
			if (serr != 0) { 
		        MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(73), 
		                port, serr, SockerrToString(serr)); 
		        goto bang; 
			} 
		    serr = CreateSocket(&(as->asctrl), SOCK_DGRAM, 
		    		htonl(INADDR_ANY), htons((short) (as->asport + 1))); 
			if (serr != 0) { 
		        MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(73), 
		                port + 1, serr, SockerrToString(serr)); 
		        goto bang; 
			} 
#ifdef ASYNC_OUTPUT			 
		    if (WSAAsyncSelect(as->asdata, hwndMDIFrame, WM_SOCKET_SELECT, FD_READ) != 0) { 
		        serr = WSAGetLastError(); 
		    } 
			if (serr != 0) { 
		        MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(73), 
		                port, serr, SockerrToString(serr)); 
		        goto bang; 
			} 
		    if (WSAAsyncSelect(as->asctrl, hwndMDIFrame, WM_SOCKET_CONTROL, FD_READ) != 0) { 
		        serr = WSAGetLastError(); 
		    } 
			if (serr != 0) { 
		        MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(73), 
		                port + 1, serr, SockerrToString(serr)); 
		        goto bang; 
			} 
#endif			 
#ifdef TRACE_AUX_SOCKET 
OutputDebugString("Opened aux socket.\r\n"); 
#endif 
#ifdef IP_MAX_MEMBERSHIPS			 
			multicastJoin(hwndMDIFrame, FALSE); 
			multicastJoin(hwndMDIFrame, TRUE); 
#endif						 
		} 
		as->asrefc++; 
	} 
	return as; 
	 
bang: 
	if (as->asdata != INVALID_SOCKET) { 
		closesocket(as->asdata); 
		as->asdata = INVALID_SOCKET; 
	} 
	if (as->asctrl != INVALID_SOCKET) { 
		closesocket(as->asctrl); 
		as->asctrl = INVALID_SOCKET; 
	} 
	return NULL; 
} 
 
//	WRITEOUTPUT  --  Transmit output buffer to destination 
 
static int writeOutput(LPCLIENT_DATA d, LPSTR buf, int buflen) 
{ 
#ifdef MODEM 
	if (d->modemConnection) { 
		if (modemHandle != -1) { 
			unsigned short bcrc; 
			COMSTAT cs; 
			int wrl, err; 
		 
			mb.len = (unsigned short) buflen; 
			mb.ilen = ~mb.len; 
			revshort(&mb.len); 
			revshort(&mb.ilen); 
			bcrc = crc((LPSTR) &mb, buflen + 4 + 2 * sizeof(unsigned short)); 
			revshort(&bcrc); 
			err = GetCommError(modemHandle, &cs); 
			if (err != 0) {  
				MsgBox(hwndMDIFrame, MB_ICONSTOP | MB_OK, Format(20), err); 
				errorRant(hwndMDIFrame); 
				return -1; 
			}  
			wrl = WriteComm(modemHandle, &mb, buflen + 4 + 2 * sizeof(unsigned short)); 
			err = GetCommError(modemHandle, &cs); 
			if (err != 0) {  
				MsgBox(hwndMDIFrame, MB_ICONSTOP | MB_OK, Format(21), err); 
				errorRant(hwndMDIFrame); 
				return -1; 
			}  
			if (wrl > 0) { 
				wrl = WriteComm(modemHandle, &bcrc, sizeof(short)); 
			} 
			err = GetCommError(modemHandle, &cs); 
			if (err != 0) {  
				MsgBox(hwndMDIFrame, MB_ICONSTOP | MB_OK, Format(22), err); 
				errorRant(hwndMDIFrame); 
				return -1; 
			} 
			return buflen; 
		} 
	} else 
#endif	 
	if (d->outputSocketBusy) { 
		propeller(IDC_PH_OUTPUT_LOST, ++outputPacketsLost); 
#ifdef OVERLOAD			 
OutputDebugString("Socket busy--packet discarded.\r\n"); 
#endif 
		return buflen;					 
	} else { 
		int stat, wasSentTo = FALSE; 
		 
#ifdef OVERLOAD 
	int OL; 
	for (OL = 0; OL < OVERLOAD; OL++) { 
#endif			 
		if (d->localLoopback || ((!useSendNotSendto || waNetNoConnect) && 
									(!waNetUseSend))) { 
			if (d->localLoopback) { 
				stat = loop_sendto(d, buf, buflen, 
						(LPSOCKADDR) &(d->name), sizeof d->name); 
			} else {									 
				stat = sendto(d->sReply, buf, buflen, 0, 
						(LPSOCKADDR) &(d->name), sizeof d->name); 
			} 
			if (stat < 0) { 
				if (!waNetNoConnect) { 
					useSendNotSendto = TRUE; 
					if (hDlgPropeller != NULL) { 
						SetDlgItemText(hDlgPropeller, IDC_PH_SENDTO, rstring(IDS_T_SEND)); 
					} 
				} 
			} else { 
				wasSentTo = TRUE; 
			} 
		} 
		/*	Careful!  Don't "optimise" this to "else if"; we have to be 
			able to switch-hit when the first sendto() fails above. */ 
		if (!wasSentTo) { 
			stat = send(d->sReply, buf, buflen, 0); 
		} 
		if (stat == buflen) { 
			propeller(IDC_PH_PACKETS_SENT, ++packetsSent); 
#ifdef OVERLOAD			 
if ((rand() & 0x3F) == 0) { d->outputSocketBusy = TRUE; OutputDebugString("Dooooh!\r\n"); }   
#endif			 
		} else { 
			propeller(IDC_PH_OUTPUT_LOST, ++outputPacketsLost); 
 
			if (!waNetNoOutOverflow) { 
 
				/* Great leap of faith.  Treat a "WSAEWOULDBLOCK" error as 
				   a truncated buffer and do nothing other than mark the 
				   socket busy. */ 
				    
				d->outputSocketBusy = TRUE; 
				if (stat == SOCKET_ERROR && 
					WSAGetLastError() == WSAEWOULDBLOCK) { 
					stat = 0; 
				} 
			}  
#ifdef OVERLOAD			 
OutputDebugString("Socket write failed.\r\n"); 
#endif 
		} 
#ifdef OVERLOAD 
	} 
#endif				 
		return stat; 
	} 
} 
 
//	SOCKETERRORBOX  --  Show message box for socket error 
 
static void socketerrorbox(HWND hwnd, LPCLIENT_DATA d) 
{ 
	if (d->modemConnection) { 
	    MessageBox(hwnd, rstring(IDS_T_MODEM_WRITE_ERR), NULL, 
	    	MB_ICONEXCLAMATION | MB_OK); 
	} else { 
	    MessageBox(hwnd, SockerrToString(WSAGetLastError()), rstring(IDS_T_SOCKET_WRITE_ERR), 
	    	MB_ICONEXCLAMATION | MB_OK); 
    } 
}  
 
/*  SENDPKT  --  Send a message to the destination.  */ 
 
static int sendpkt(HWND hwnd, LPCLIENT_DATA d, struct soundbuf *asb) 
{ 
    LONG lts = asb->buffer.buffer_len; 
    LONG pkl = pktlen; 
    struct soundbuf *osb = asb; 
 
#ifdef CRYPTO 
    if ((d->deskey[0] || d->ideakey[0] || d->opgpkey[0] || 
		d->blowfish_spec || d->otpFileName[0])) { 
        int i; 
        LONG slen; 
 
        _fmemcpy(&ebuf, asb, (int) pktlen); 
        slen = lts; 
        osb = &ebuf; 
 
        /* DES encryption. */ 
 
        if (d->deskey[0]) { 
			if (protocolSent == PROTOCOL_RTP || protocolSent == PROTOCOL_VAT) { 
				LONG vlen = pktlen; 
				des_key_schedule sched; 
				des_cblock ivec; 
 
                /* If we're DES encrypting we must round the size of 
				   the data to be sent to be a multiple of 8 so that 
				   the entire DES frame is sent.  If this is an RTP 
				   packet, we may have to set the Pad bit in the 
				   header and include a count of pad bytes at the end 
				   of the packet. */ 
 
				_fmemset(ivec, 0, 8); 
				pkl = (pktlen + 7) & (~7); 
				if (pkl > pktlen) { 
					_fmemset(((char *) &ebuf) + vlen, 0, (int) (pkl - vlen)); 
				} 
				if ((protocolSent == PROTOCOL_RTP) && (pkl > vlen)) { 
					char *p = (char *) &ebuf; 
 
					p[0] |= 0x20; /* Set pad bytes present bit */ 
					p[pkl - 1] = (unsigned char) (pkl - vlen); /* Set pad count at end */ 
				} 
				des_set_key((des_cblock FAR *) (((protocolSent == PROTOCOL_RTP) ? d->rtpdeskey : 
								d->vatdeskey) + 1), sched); 
				des_ncbc_encrypt((des_cblock *) &ebuf, 
					(des_cblock *) &ebuf, pkl, sched, 
					(des_cblock *) ivec, DES_ENCRYPT); 
				slen = pkl - (sizeof(struct soundbuf) - BUFL);  
			} else { 
	        	char twibble[8]; 
	        	 
	        	_fmemcpy(twibble, d->deskey + 1, 8); 
	            setkey(twibble); 
	 
	            /* If we're DES encrypting we must round the size of 
	               the data to be sent to be a multiple of 8 so that 
	               the entire DES frame is sent. */ 
	 
	            slen = (slen + 7) & (~7); 
	            for (i = 0; i < slen; i += 8) { 
	 
	                /* Apply cipher block chaining within the packet. */ 
	 
	                if (i > 0) { 
	                    int j; 
	 
	                    for (j = 0; j < 8; j++) { 
	                        ebuf.buffer.buffer_val[(i + j)] ^= 
	                            ebuf.buffer.buffer_val[(i + j) - 8]; 
	                    } 
	                } 
	                endes(ebuf.buffer.buffer_val + i); 
	            } 
	            ebuf.compression |= fEncDES; 
	        } 
        } 
 
        /* IDEA encryption. */ 
 
        if ((protocolSent == PROTOCOL_SPEAKFREE) && d->ideakey[0]) { 
            unsigned short iv[4]; 
	        char twibble[16]; 
	         
	        _fmemcpy(twibble, d->ideakey + 1, 16); 
            memset(iv, 0, sizeof(iv)); 
            initcfb_idea(iv, twibble, FALSE); 
 
            /* If we're IDEA encrypting we must round the size of 
               the data to be sent to be a multiple of 8 so that 
               the entire IDEA frame is sent. */ 
 
            slen = (slen + 7) & (~7);  
            ideacfb(ebuf.buffer.buffer_val, (int) slen); 
            close_idea(); 
            ebuf.compression |= fEncIDEA; 
        } 
 
        /* Blowfish encryption. */ 
 
        if ((protocolSent == PROTOCOL_SPEAKFREE) && d->blowfish_spec) { 
            unsigned char iv[8]; 
	         
            memset(iv, 0, sizeof(iv)); 
 
            /* If we're Blowfish encrypting we must round the size of 
               the data to be sent to be a multiple of 8 so that 
               the entire Blowfish frame is sent. */ 
 
            slen = (slen + 7) & (~7);  
            BF_cbc_encrypt((unsigned char *) ebuf.buffer.buffer_val, 
						   (unsigned char *) ebuf.buffer.buffer_val, 
						   slen, &(d->blowfishkey), iv, BF_ENCRYPT); 
            ebuf.compression |= fEncBF; 
        } 
 
        /* PGP session key encryption. */ 
 
        if ((protocolSent == PROTOCOL_SPEAKFREE) && d->opgpkey[0]) { 
            unsigned short iv[4]; 
	        char twibble[16]; 
	         
	        _fmemcpy(twibble, d->opgpkey + 1, 16); 
            memset(iv, 0, sizeof(iv)); 
            initcfb_idea(iv, twibble, FALSE); 
 
            /* If we're IDEA encrypting we must round the size of 
               the data to be sent to be a multiple of 8 so that 
               the entire IDEA frame is sent. */ 
 
            slen = (slen + 7) & (~7);  
            ideacfb(ebuf.buffer.buffer_val, (int) slen); 
            close_idea(); 
            ebuf.compression |= fEncPGP; 
        } 
 
        /* One-time pad encryption. */ 
 
        if ((protocolSent == PROTOCOL_SPEAKFREE) && d->otpFileName[0]) { 
            for (i = 0; i < slen; i++) { 
                ebuf.buffer.buffer_val[i] ^= d->otp[i]; 
            } 
            ebuf.compression |= fEncOTP; 
        } 
        pkl = slen + (sizeof(struct soundbuf) - BUFL); 
    } 
#endif 
      
    { 
    	int stat, nr; 
 
		if (protocolSent == PROTOCOL_SPEAKFREE) {    	 
			pkl = lpc10stuff(osb, pkl); 
	        revlong(&osb->compression); 
	        revlong(&osb->buffer.buffer_len); 
	    } 
		for (nr = 0; nr < ((lpc10compress && (robust > 1)) ? robust : 1); nr++) { 
			stat = writeOutput(d, (LPSTR) osb, (int) pkl); 
			if (stat < 0) { 
				break; 
			} 
		} 
        if (protocolSent == PROTOCOL_SPEAKFREE) { 
	        revlong(&osb->compression); 
	        revlong(&osb->buffer.buffer_len); 
			lpc10unstuff(osb); 
	    } 
        if (stat < 0 
&& WSAGetLastError() != WSAEINPROGRESS        ) { 
            d->state = d->wantsInput ? SendingLiveAudio : Idle; 
            d->wantsInput = FALSE; 
            if (d->hFile != HFILE_ERROR) { 
            	KillTimer(hwnd, 2); 
	            _lclose(d->hFile); 
	            d->hFile = HFILE_ERROR; 
	        } 
            socketerrorbox(hwnd, d); 
            return FALSE; 
        } 
    } 
    return TRUE; 
} 
 
/*	CHANGEAUDIOSTATE  --  Change transmitting / receiving state.  */ 
 
static void changeAudioState(HWND hwnd, LPCLIENT_DATA pClientData) 
{ 
	if (pClientData->face_shown) { 
		LPSTR hname = ((pClientData->uname != NULL) && (pClientData->uname[0]) && 
					   ((pClientData->uname[0] & 0xFF) != 0xFF)) ? 
						pClientData->uname : 
						(pClientData->localLoopback ? rstring(IDS_T_LOOPBACK) :  
						 (pClientData->modemConnection ? 
		    			  rstring(IDS_T_MODEM_CONNECTION) : pClientData->szHost)); 
    	if (pClientData->wantsInput) { 
    		char s[MAX_HOST + 20]; 
    		 
    		strcpy(s, "==> "); 
    		_fstrcat(s, hname); 
    		SetWindowText(hwnd, s);  
    	} else { 
    		SetWindowText(hwnd, hname); 
    	}  
	} else { 
		InvalidateRect(hwnd, NULL, FALSE); 
		UpdateWindow(hwnd); 
	} 
} 
 
/*  SHIPSOUNDBUFFER  --  Output sound buffer to connection.  */ 
 
void shipSoundBuffer(HWND hwnd, LPCLIENT_DATA pClientData) 
{ 
	if (sqpacket != squelched) { 
		RECT wr; 
		POINT cp; 
	 
		squelched = sqpacket; 
		GetWindowRect(hwnd, &wr); 
		GetCursorPos(&cp); 
		if (PtInRect(&wr, cp)) { 
	    	SetCursor((pClientData->wantsInput || broadcasting || bConferencing) ?  
	    				(squelched ? boltCursor : earCursor) : 
	    				phoneCursor); 
			UpdateWindow(hwnd);			// Change cursor to indicate squelch state 
		} 
	} 
 
	if (squelched) { 
		return; 
	} 
     
    if (protocolSent == PROTOCOL_SPEAKFREE) { 
	    sb.compression = fProtocol | (pClientData->ring ? (fSetDest | fDestSpkr) : 0); 
	    pClientData->ring = FALSE; 
	    sb.compression |= pClientData->debugging ? fDebug : 0; 
	    sb.compression |= pClientData->loopback ? fLoopBack : 0; 
	    sb.compression |= compression ? fComp2X : 0; 
	    sb.compression |= gsmcompress ? fCompGSM : 0; 
	    sb.compression |= adpcmcompress ? fCompADPCM : 0; 
	    sb.compression |= lpccompress ? fCompLPC : 0; 
	    sb.compression |= lpc10compress ? fCompLPC10 : 0; 
	    /* Never offer face data to a multicast address.  It might be 
	       nice, but the possibility of screw-ups is just too great 
	       given the flakiness of multicast implementations. */ 
	    sb.compression |= ((faceFile != HFILE_ERROR) && 
	    	(!IN_MULTICAST(pClientData->inetSock.sin_addr.s_addr))) ? fFaceOffer : 0; 
	    strcpy(sb.sendinghost, ourSendingHost); 
	} 
    sendpkt(hwnd, pClientData, &sb); 
} 
 
/*	SENDSESSIONCTRL  --  Send an RTP or VAT session control packet 
						 on the control channel.  */ 
						  
static void sendSessionCtrl(LPCLIENT_DATA pClientData, char *msg, int msgl)  
{ 
	char *aux = (char *) &ebuf; 
	int stat; 
 
#ifdef CRYPTO			    			 
	if ((!(protocolSent == PROTOCOL_SPEAKFREE)) && pClientData->rtpdeskey[0] && 
			!((protocolSent == PROTOCOL_RTP) && waProtNoRTCPCrypt)) { 
		int vlen; 
		des_key_schedule sched; 
		des_cblock ivec; 
						 
		_fmemset(ivec, 0, 8); 
						 
		if (protocolSent == PROTOCOL_RTP) { 
						 
			/* Encrypted RTCP messages are prefixed with 4 random 
			   bytes to prevent known plaintext attacks. */ 
						 
			_fmemcpy(aux, &rtpdesrand, 4); 
			_fmemcpy(aux + 4, msg, msgl); 
			msgl += 4; 
		} else { 
			_fmemcpy(aux, msg, msgl); 
		} 
						 
        /* If we're DES encrypting we must round the size of 
		   the data to be sent to be a multiple of 8 so that 
		   the entire DES frame is sent.  This applies only to 
		   VAT, as the code that creates RTCP packets guarantees 
           they're already padded to a multiple of 8 bytes. */ 
						 
		vlen = msgl; 
		msgl = (msgl + 7) & (~7); 
		if (msgl > vlen) { 
			_fmemset(aux + vlen, 0, msgl - vlen); 
		} 
		des_set_key((des_cblock FAR *) (((protocolSent == PROTOCOL_RTP) ? 
						pClientData->rtpdeskey : pClientData->vatdeskey) + 1), sched); 
		des_ncbc_encrypt((des_cblock *) aux, 
			(des_cblock *) aux, msgl, sched, 
			(des_cblock *) ivec, DES_ENCRYPT); 
		msg = aux; 
	} 
#endif 
	 
	if (pClientData->localLoopback) { 
		stat = loop_sendto(pClientData, msg, msgl, 
				(LPSOCKADDR) &(pClientData->ctrl), sizeof pClientData->ctrl); 
	} else {									 
		if ((!useSendNotSendto || waNetNoConnect) && (!waNetUseSend)) { 
			stat = sendto(pClientData->sControl, msg, msgl, 0, 
					(LPSOCKADDR) &(pClientData->ctrl), sizeof pClientData->ctrl); 
			if (stat < 0) { 
				if (!waNetNoConnect) { 
					useSendNotSendto = TRUE; 
					if (hDlgPropeller != NULL) { 
						SetDlgItemText(hDlgPropeller, IDC_PH_SENDTO, rstring(IDS_T_SEND)); 
					} 
				} 
			} 
		} 
		/*	Careful!  Don't "optimise" this to "else if"; we have to be 
			able to switch-hit when the first sendto() fails above. */ 
		if (useSendNotSendto) { 
			stat = send(pClientData->sControl, msg, msgl, 0); 
		} 
	} 
	propeller(IDC_PH_PACKETS_SENT, ++packetsSent); 
}						  
 
/*  CREATESOUNDBUFFER  --  Create a standard format sound buffer 
						   with selected compression modes from a 
						   set of raw samples received from the audio 
						   input port.  Special gimmick: align==0 means 
						   the data are already in place in mu-law 
						   encoding (rate==8000 only).  This allows fast 
						   processing of mu-law encoded sound files.  */ 
						 
void createSoundBuffer(LPSTR buffer, WORD buflen, DWORD channels, 
					   DWORD rate, DWORD bytesec, WORD align) 
{ 
	int knownFormat = FALSE; 
 
	//	If the spectrum display is open, let it see the raw samples 
	spectrumUpdate(buffer, buflen, channels, rate, bytesec, align, FALSE); 
 
	if (rate == 8000) { 
		if (align == 2) { 
			LONG i; 
			int j; 
			 
			for (i = j = 0; i < (LONG) buflen / align; i++) { 
				sb.buffer.buffer_val[j++] = audio_s2u((((WORD FAR *) buffer)[i])); 
			}  
		} else if (align == 1) {	// align == 1 
			LONG i; 
			int j; 
			 
			for (i = j = 0; i < (LONG) buflen; i++) { 
				sb.buffer.buffer_val[j++] = audio_c2u((((BYTE FAR *) buffer)[i])); 
			}  
		} else  {	//	(align == 0) already stored in mu-law encoding 
			align = 1; 
		} 
		sb.buffer.buffer_len = buflen / align; 
		knownFormat = TRUE; 
	} else if (rate == 11025 && align == 2) { 
		LONG i; 
		int j, k; 
		 
		for (i = j = k = 0; i < (LONG) (buflen / align); i++) { 
			if ((k & 3) != 2  && ((i % 580) != 579)) { 
				sb.buffer.buffer_val[j++] = audio_s2u((((WORD FAR *) buffer)[i])); 
			} 
			k = (k + 1) % 11; 
		}  
		sb.buffer.buffer_len = j; 
		knownFormat = TRUE; 
	} else if (rate == 11025 && align == 1) { 
		LONG i; 
		int j, k; 
		 
		for (i = j = k = 0; i < (LONG) (buflen / align); i++) { 
			if ((k & 3) != 2  && ((i % 580) != 579)) { 
				sb.buffer.buffer_val[j++] = audio_c2u((((BYTE FAR *) buffer)[i])); 
			} 
			k = (k + 1) % 11; 
		}  
		sb.buffer.buffer_len = j; 
		knownFormat = TRUE; 
	} 
	 
	sqpacket = FALSE; 
	if (knownFormat) { 
		if (voxmode != IDM_VOX_NONE) { 
			LONG i; 
			long alevel = 0; 
			static long voxSampleCountdown; 
			int j, thresh; 
			 
			thresh = (int) exp(log(32767.0) * ((1000 - noise_threshold) / 1000.0));  
			for (i = 0, j = 0; i < sb.buffer.buffer_len; i++, j++) { 
				int samp = audio_u2s(sb.buffer.buffer_val[j]); 
				 
				if (samp < 0) { 
					samp = -samp; 
				} 
				alevel += samp; 
			} 
			alevel /= sb.buffer.buffer_len; 
//{char s[132]; sprintf(s, "Nt = %d, Thresh = %d, Max = %d, Alevel = %ld, VU = %.2g\r\n", 
//noise_threshold, thresh, maxsamp, alevel, 
//log((double) alevel) / log(32767.0)); OutputDebugString(s);}			 
			voxMonitorUpdate(alevel, 0); 
			if (alevel < thresh) { 
				if (voxSampleCountdown <= 0) { 
					sqpacket = TRUE; 
					sb.buffer.buffer_len = 0; 
					return; 
				} 
				voxSampleCountdown -= sb.buffer.buffer_len; 
			} else { 
				switch (voxmode) { 
					case IDM_VOX_FAST: 
						voxSampleCountdown = 4000; 
						break; 
					case IDM_VOX_MEDIUM: 
						voxSampleCountdown = 8000; 
						break; 
					case IDM_VOX_SLOW: 
						voxSampleCountdown = 12000; 
						break; 
				} 
			}			 	         
		} 
 
	    if (compression) { 
	    	compress2X(&sb); 
	    } 
     
	    if (gsmcompress) { 
	        gsmcomp(&sb); 
	    } 
 
		if (adpcmcompress) { 
// Sledgehammer to verify that lockup-prevention code works OK 
//long l; l = GetTickCount(); 
//while (GetTickCount() - l < 350) ; 
			adpcmcomp(&sb); 
		} 
 
		if (lpccompress) { 
			lpccomp(&sb); 
		} 
 
		if (lpc10compress) { 
			lpc10comp(&sb); 
		} 
         
        pktlen = sb.buffer.buffer_len + (sizeof(struct soundbuf) - BUFL); 
        //	Ugly, ugly ugly.  RTP and VAT need compression modes here. 
        sb.compression = fProtocol;  
	    sb.compression |= gsmcompress ? fCompGSM : 0; 
	    sb.compression |= adpcmcompress ? fCompADPCM : 0; 
	    sb.compression |= lpccompress ? fCompLPC : 0; 
		if (protocolSent == PROTOCOL_RTP) { 
			pktlen = rtpout(&sb, ssrc, timestamp, seq, spurt); 
			seq++; 
			timestamp += currentInputSamples; 
		} else if (protocolSent == PROTOCOL_VAT) { 
			pktlen = vatout(&sb, 0L, timestamp, spurt); 
			timestamp += currentInputSamples; 
		} 
	 
		spurt = FALSE;					  /* Not the start of a talk spurt */ 
	} 
}											 
 
/*  CREATENEWCONNECTION  --  Create a new connection MDI window.  */ 
 
HWND createNewConnection(LPCLIENT_DATA pClientData) 
{ 
    MDICREATESTRUCT mcs; 
    HWND hwnd; 
    SOCKERR serr = 0; 
    int vover; 
 
#ifdef TRACE_FACE 
	OutputDebugString("createNewConnection()\r\n"); 
#endif 
	 
	vover = (GetSystemMetrics(SM_CYFRAME) * 2) + GetSystemMetrics(SM_CYCAPTION) - 1;                     
    mcs.szClass = pszClientClass; 
    mcs.szTitle = pClientData->localLoopback ? 
			   	  rstring(IDS_T_LOOPBACK) : pClientData->szHost, 
    mcs.hOwner = hInst; 
    mcs.x = CW_USEDEFAULT; 
    mcs.y = CW_USEDEFAULT; 
    mcs.cx = tmAveCharWidth * 50; 
    mcs.cy = ((4 * tmHeight) + 5) + vover; 
    mcs.style = 0; 
 
    hwnd = FORWARD_WM_MDICREATE(hwndMDIClient, (LPMDICREATESTRUCT) &mcs, SendMessage); 
 
    if (hwnd == NULL) { 
        return NULL; 
    } 
     
    pClientData->state = Idle; 
    SetWindowLong(hwnd, GWL_CLIENT, (LONG) pClientData); 
    if (!pClientData->modemConnection) { 
	    serr = CreateSocket(&(pClientData->sReply), SOCK_DGRAM, 
	                         htonl(INADDR_ANY), 0); 
	 
	    if (serr == 0) { 
#ifdef ASYNC_OUTPUT	     
		    if (WSAAsyncSelect(pClientData->sReply, hwnd, WM_SOCKET_SELECT, FD_WRITE) != 0) { 
		        serr = WSAGetLastError(); 
		    } 
#endif 
			if (serr == 0) { 
		        pClientData->name.sin_family = PF_INET; 
		        pClientData->name.sin_addr = pClientData->inetSock.sin_addr; 
		        pClientData->name.sin_port = htons(pClientData->port); 
		         
		        if (!waNetNoConnect) { 
			        if (connect(pClientData->sReply, (LPSOCKADDR) &(pClientData->name), 
			        	 sizeof(pClientData->name)) != 0) { 
			            serr = WSAGetLastError(); 
			        } 
		        } 
		    } 
   	    } 
	    if (serr != 0) { 
	       MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(23), 
	                (LPSTR) pClientData->szHost, serr, SockerrToString(serr)); 
	        return NULL; 
	    } 
	     
	    //	Create control socket for RTP and VAT protocol connections 
	     
	    serr = CreateSocket(&(pClientData->sControl), SOCK_DGRAM, 
	                         htonl(INADDR_ANY), 0); 
	 
	    if (serr == 0) { 
#ifdef ASYNC_OUTPUT	     
		    if (WSAAsyncSelect(pClientData->sReply, hwnd, WM_SOCKET_CONTROL, FD_WRITE) != 0) { 
		        serr = WSAGetLastError(); 
		    } 
#endif 
			if (serr == 0) { 
		        pClientData->ctrl.sin_family = PF_INET; 
		        pClientData->ctrl.sin_addr = pClientData->inetSock.sin_addr; 
		        pClientData->ctrl.sin_port = htons((short) (pClientData->port + 1)); 
		         
		        if (!waNetNoConnect) { 
			        if (connect(pClientData->sControl, (LPSOCKADDR) &(pClientData->ctrl), 
			        	 sizeof(pClientData->ctrl)) != 0) { 
			            serr = WSAGetLastError(); 
			        } 
		        } 
			} 
   	    } 
	    if (serr != 0) { 
	       MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(67), 
	                (LPSTR) pClientData->szHost, serr, SockerrToString(serr)); 
	        return NULL; 
	    } 
	     
	    /*	If this is a nonstandard port, create auxiliary sockets to 
	    	receive input from that port.  */ 
	    	 
	    if (pClientData->port != NETFONE_COMMAND_PORT) { 
	    	pClientData->auxSock = monitorPort(pClientData->port); 
	    } 
    } 
 
    pClientData->gsmh = gsm_create(); 
	pClientData->rseq = -1;				// Initialise receive sequence number 
	rate_start(&pClientData->rateconv, 4000, 8000);	// Initialise 2X decompression 
     
    /* Save the sending host for Speak Freely protocol packets. */ 
     
    { 
    	char gh[MAX_HOST]; 
    	 
    	gethostname(gh, sizeof gh); 
    	if (strlen(gh) > ((sizeof ourSendingHost) - 1)) { 
    		gh[(sizeof ourSendingHost) - 1] = 0; 
    	} 
    	strcpy(ourSendingHost, gh); 
    	strcpy(sb.sendinghost, ourSendingHost); 
    } 
 
#ifdef MODEM     
    if (pClientData->modemConnection) { 
    	SetWindowText(hwnd, rstring(IDS_T_MODEM_CONNECTION)); 
		modemSessions++; 
    } else 
#endif     
    if (pClientData->szHost[0] == '(' && !pClientData->localLoopback) { 
     
    	/* I'm sure by now you're shocked and stunned to discover that 
    	   some Winsock implementations, Sun PC-NFS 5.1, to name one, 
    	   don't correctly implement the WSAAsyncGetHostByAddr function. 
    	   Oh, you can make the call, and you even get back a valid host 
    	   name.  But doing so plants a time bomb which will kill you 
    	   (at least under the debug kernel) much, much later when you call 
    	   WSACleanup() right before exiting the program.  At that time, 
    	   depending on where the random pointer inside their so-called 
    	   WSHELPER points, you get either two invalid global pointer 
    	   errors or a fatal error due to an object usage count underflow in the 
    	   (bogus) global block.  If the waNetSynchronousGetHostnameAction is 
    	   set, we eschew the asynchronous request and make the user 
    	   wait for a blocking gethostbyaddr() which has the merit, at 
    	   least, of not blowing us away at program termination time. */   
     
		if (!waNetSynchronousGetHostnameAction) { 
		    	    
	    	/* This is a temporary connection initiated from the remote site. 
	    	   Schedule a lookup to obtain the full domain name of the host, 
	    	   not just the hostname included in the sound packet. */ 
	    	    
		    pClientData->getNameTask = WSAAsyncGetHostByAddr(hwnd, WM_SOCKET_ASYNC, 
		                                    (CHAR FAR *) &pClientData->inetSock.sin_addr, 
		                                    sizeof(pClientData->inetSock.sin_addr), 
		                                    PF_INET, pClientData->hostBuffer, 
		                                    sizeof(pClientData->hostBuffer)); 
			if (pClientData->getNameTask == NULL) { 
				int serr = WSAGetLastError(); 
				MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(24), 
				        (LPSTR) pClientData->szHost, serr, SockerrToString(serr)); 
			} 
		} else{ 
			LPHOSTENT h = gethostbyaddr((CHAR FAR *) &pClientData->inetSock.sin_addr, 
		                                 sizeof(pClientData->inetSock.sin_addr), 
		                                 PF_INET); 
					 
			if (h == NULL) { 
#ifdef SHOW_GET_HOST_NAME_ERROR			 
	            int serr = WSAGetLastError(); 
								             
				MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(27), 
				        pClientData->szHost, serr, 
				        SockerrToString(serr)); 
#endif				         
			} else { 
				_fstrcpy(pClientData->szHost, h->h_name); 
			    SetWindowText(hwnd, pClientData->localLoopback ? 
			    	rstring(IDS_T_LOOPBACK) : pClientData->szHost); 
	    		changeAudioState(hwnd, pClientData); 
			} 
			pClientData->getNameTask = NULL;			                              
		}	 
	} 
 
    DragAcceptFiles(hwnd, TRUE); 
    ShowWindow(hwnd, SW_SHOW); 
    openConnections++; 
	propUpdateAudio(); 
 
  // Save start time for calculating call duration later. 
  pClientData->dwStartTime = GetTickCount(); 
    return hwnd; 
} 
 
//	CONNFETCHFACE  --  Begin retrieval of a face image. 
 
void connFetchFace(HWND hwndClient, LPCLIENT_DATA pClientData) 
{ 
//	pClientData->face_stat = FSabandoned; 
//	if (pClientData->face_stat != -1) { 
#ifdef TRACE_FACE 
		OutputDebugString("connFetchFace()\r\n"); 
#endif 
		pClientData->face_address = 0L; 
		pClientData->face_timeout = 0; 
		pClientData->face_retry = 0; 
		pClientData->face_stat = FSreply;   	// Activate request from timeout 
		SetTimer(hwndClient, 5, (UINT) FaceFetchInterval, NULL); 
//	} 
} 
 
//	STARTSOUNDFILE  --  Begin playing a sound file. 
 
VOID startSoundFile(HWND hwnd, LPSTR pszFile) 
{ 
    LPCLIENT_DATA pClientData; 
    HFILE hFile = HFILE_ERROR; 
    char magic[4]; 
 
    pClientData = CLIENTPTR(hwnd); 
     
    pClientData->quitSoundFile = FALSE; 
    pClientData->hFile = HFILE_ERROR; 
    if (pClientData->timeout > 0) { 
    	pClientData->timeout = 0; 
    } 
    pClientData->cbSent = 0L; 
 
    //  Try to open it 
 
    hFile = _lopen(pszFile, OF_READ | OF_SHARE_DENY_WRITE); 
 
    if (hFile == HFILE_ERROR) { 
        //  Error opening file 
 
        MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(25), 
                pszFile, (LPSTR) pClientData->szHost); 
        goto FatalError; 
    } 
    _lread(hFile, magic, sizeof(long)); 
     
    /* See if it's a chunky, wavy RIFF.  If so, delegate 
       handling of the file to the multimedia I/O package. */ 
        
	if (memcmp(magic, "RIFF", 4) == 0) { 
		_lclose(hFile); 
		if (!readWaveInit(hwnd, pClientData, pszFile)) { 
			return; 
		} 
	} else { 
	 
	    /* If the file has a Sun .au file header, skip it. 
	       Note that we still blithely assume the file is 
	       8-bit ISDN u-law encoded at 8000 samples per 
	       second. */ 
	 
	    if (memcmp(magic, ".snd", 4) == 0) { 
	        long startpos; 
	 
	        _lread(hFile, &startpos, sizeof(long)); 
	        revlong(&startpos); 
	        _llseek(hFile, startpos, 0); 
	    } else { 
	        _llseek(hFile, 0L, 0); 
	    } 
	    pClientData->hFile = hFile; 
    } 
     
    pClientData->state = Transferring; 
    spurt = TRUE;						// Consider sound file a "talk spurt" 
    if (pClientData->timeout > 0) { 
    	pClientData->timeout = 0; 
    } 
 
#ifdef SHOW_STATE 
    InvalidateRect(hwnd, NULL, TRUE); 
    UpdateWindow(hwnd); 
#endif     
    DragAcceptFiles(hwnd, FALSE); 
    if (SetTimer(hwnd, 2, 200, NULL) == 0) { 
        MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(26)); 
    } 
    return; 
 
FatalError: 
    if (hFile != HFILE_ERROR) { 
        _lclose(hFile); 
    } 
} 
 
/*  FILEDROPPED  --  Handle file dropped in connection window.  */ 
 
static VOID fileDropped(HWND hwnd, HDROP hdrop) 
{ 
    LPCLIENT_DATA pClientData; 
     
    pClientData = CLIENTPTR(hwnd); 
 
    //  Retrieve the dropped file 
 
    DragQueryFile(hdrop, 0, pClientData->szFile, sizeof(pClientData->szFile)); 
    DragFinish(hdrop); 
     
    //	Start output 
     
    startSoundFile(hwnd, pClientData->szFile);  
} 
 
/*  STATETOSTRING  --  Convert state value to string.  */ 
 
static LPSTR stateToString(CLIENT_STATE state) 
{ 
    LPSTR pszResult; 
 
    switch (state) { 
	    case Embryonic: 
	        pszResult = rstring(IDS_T_INITIALISING); 
	        break; 
	 
	    case Idle: 
	        pszResult = rstring(IDS_T_IDLE); 
	        break; 
	 
	    case SendingLiveAudio: 
	        pszResult = rstring(IDS_T_SENDING_LIVE); 
	        break; 
	 
	    case Transferring: 
	        pszResult = rstring(IDS_T_SENDING_FILE); 
	        break; 
	 
	    case PlayingReceivedAudio: 
	        pszResult = rstring(IDS_T_PLAYING_AUDIO); 
	        break; 
	 
	    default: 
	        pszResult = rstring(IDS_T_UNKNOWN); 
	        break; 
    } 
    return pszResult; 
} 
 
/*  CONNECT_WNDPROC  --  Connection main window procedure.  */ 
 
LRESULT CALLBACK connectWndProc(HWND hwnd, UINT nMessage, WPARAM wParam, LPARAM lParam) 
{ 
    LPCLIENT_DATA pClientData; 
 
    pClientData = CLIENTPTR(hwnd); 
     
    switch (nMessage) { 
     
//    	case WM_MDIACTIVATE: 
//    		if (wParam) { 
//    			InvalidateRect(hwnd, NULL, FALSE); 
//    			UpdateWindow(hwnd); 
//    		} 
//    		break; 
     
    	case WM_CHAR: 
    		if (wParam == ' ') { 
    			if (pClientData != NULL) { 
    				if (!pClientData->wantsInput) { 
    					goto spacebarOn; 
    				} else { 
    					goto spacebarOff; 
    				} 
    			}	 
    		} 
    		return 0; 
         
        case WM_CLOSE: 
        	/*	Mark the window closed at the request of the user. 
        		We set the "Lazarus timer" running which prevents 
        		the connection being remotely re-opened until the 
        		timer expires.  */ 
        	if (pClientData != NULL) { 
				Lazarus = pClientData->inetSock.sin_addr.s_addr; 
				LazarusLong = LazarusLength; 
			} 
		    FORWARD_WM_CLOSE(hwnd, DefMDIChildProc); 
        	return 0; 
        	 
        case WM_CREATE: 
#ifdef TRACE_FACE 
			OutputDebugString("Connection WM_CREATE()\r\n"); 
#endif 
        	SetFocus(hwnd); 
        	break; 
         
        case WM_DESTROY: 
        	if (pClientData != NULL) { 
        		SendMessage(hwnd, WM_CLEAN_UP_YOUR_ACT, 0, 0L); 
			}         
        	return 0; 
         
        case WM_CLEAN_UP_YOUR_ACT: 
        	if (pClientData != NULL) { 
    			if (pClientData->wantsInput && --listeners <= 0) { 
		        	terminateWaveInput(); 
					inputPaused = FALSE; 
		        	listeners = 0; 
		        } 
        		gsm_destroy(pClientData->gsmh); 
        		if (pClientData->hFile != HFILE_ERROR || 
			    	pClientData->mmioHandle != NULL) { 
	        		KillTimer(hwnd, 2); 
	        		if (pClientData->hFile != HFILE_ERROR) { 
	        			_lclose(pClientData->hFile); 
	        		} 
	    			readWaveTerm(pClientData); 
	        	} 
	        	 
	        	if (pClientData->pgpFileName[0] != 0) { 
	        		KillTimer(hwnd, 3);				// Kill timer for incomplete PGP poll 
	        		// T'would be nice to clean up the temp files here as well. 
	        	} 
	        	 
	        	if (pClientData->opgpFileName[0] != 0) { 
	        		KillTimer(hwnd, 4);				// Kill timer for incomplete PGP poll 
	        		// T'would be nice to clean up the temp files here as well. 
	        	} 
	        	 
	        	if (pClientData->face_stat == FSrequest || 
	        		pClientData->face_stat == FSreply) { 
	        		KillTimer(hwnd, 5);				// Kill timer for incomplete face image 
	        	} 
	        	if (pClientData->face_bmp != NULL) { 
	        		GlobalFreePtr(pClientData->face_bmp);	// Release face bitmap 
	        	} 
	        	 
	        	/*	If we're transmitting in RTP or VAT protocol, send a 
	        		Bye/Done message to indicate the connection's been 
	        		closed.  */ 
	        	 
	        	if (pClientData->sControl != INVALID_SOCKET) { 
	        		unsigned char byebye[128]; 
	        		int byel; 
	        		 
	        		pClientData->sendSDEStimer = TIMEOUT_RESEND_SDES; 
	        		if (protocolSent == PROTOCOL_RTP || protocolSent == PROTOCOL_SPEAKFREE) { 
	        			byel = rtp_make_bye(byebye, ssrc, rstring(IDS_T_RAISON_CLOSING), TRUE); 
	        			if (protocolSent == PROTOCOL_SPEAKFREE) { 
	        				byebye[0] = (unsigned char) ((byebye[0] & 0x3F) | (1 << 6)); 
	        			} 
	        		} else { 
	        			byel = makevatdone(byebye, 0L); 
	        		} 
	        		if (!((protocolSent == PROTOCOL_SPEAKFREE) && waProtNoHeartbeat)) { 
	        			sendSessionCtrl(pClientData, byebye, byel); 
	        		} 
	        	} 
			    if (pClientData->sReply != INVALID_SOCKET) { 
			        ResetSocket(pClientData->sReply); 
			        pClientData->sReply = INVALID_SOCKET; 
			    } 
			    if (pClientData->sControl != INVALID_SOCKET) { 
				    LINGER linger; 
				 
				    /*  Enable linger with a timeout of one second.  This  
				  		should, in theory, allow the BYE message to go out 
				  		before the socket is closed.  Why not use the 
				  		"graceful disconnect" mechanism?  Because there are 
				  		WINSOCKs out there that don't do it right.  */ 
				 
				    linger.l_onoff  = TRUE; 
				    linger.l_linger = 1; 
				 
				    setsockopt(pClientData->sControl, SOL_SOCKET, SO_LINGER, (CHAR FAR *) &linger, sizeof(linger)); 
				    closesocket(pClientData->sControl); 
			        pClientData->sControl = INVALID_SOCKET; 
			    } 
 
				/*	If we're listening on a nonstandard channel, decrement 
					the reference count on the auxiliary socket pair and 
					close if it's zero.  */ 
					 
				if (pClientData->auxSock != NULL) { 
					if (--pClientData->auxSock->asrefc == 0) { 
#ifdef TRACE_AUX_SOCKET 
OutputDebugString("Closing aux socket.\r\n"); 
#endif					 
				        ResetSocket(pClientData->auxSock->asdata); 
				        pClientData->auxSock->asdata = INVALID_SOCKET; 
				        ResetSocket(pClientData->auxSock->asctrl); 
				        pClientData->auxSock->asctrl = INVALID_SOCKET; 
					} 
				} 
			 
			    if (pClientData->hFile != HFILE_ERROR) { 
			        _lclose(pClientData->hFile); 
			        pClientData->hFile = HFILE_ERROR; 
			    } 
			 
#ifdef MODEM 
	        	if (pClientData->modemConnection) { 
	        		modemSessions--; 
	        	} 
#endif	        	 
	        	 
	        	if (pClientData->getNameTask != NULL) { 
	        		WSACancelAsyncRequest(pClientData->getNameTask); 
	        		pClientData->getNameTask = NULL;	 
	        	} 
	        	 
				if (pClientData->uname != NULL) { 
					GlobalFreePtr(pClientData->uname); 
					pClientData->uname = NULL; 
				} 
					        	 
            // Add this call to the total money saved since Speak Freely was installed. 
            fTotalMoneySaved += pClientData->fMoneySaved; 
 
        		loop_flush(pClientData);				// Flush any queued local loopback data 
	        	 
        		GlobalFreePtr(pClientData); 
        		SetWindowLong(hwnd, GWL_CLIENT, 0L); 
			      openConnections--; 
				    propUpdateAudio(); 
	        } 
        	return 0; 
        	 
        case WM_DROPFILES: 
        	if (pClientData != NULL && !broadcasting) 
          { 
			      pClientData->timeout = -1;	// Send file immortalises connection 
	        	fileDropped(hwnd, (HDROP) wParam); 
        	} 
        	break; 
 
#ifdef GATES_OF_HELL 
 
		/* Well, golly, I though it would be kinda nice to keep 
		   the user from inflating the connection window larger than 
		   the face image in it--doing so only wastes screen 
		   real estate, after all.  Little did I know that by trying 
		   to do so (at least with an MDI window), I stuck my toe 
		   back into the tree chipper and caused all kinds of 
		   things to let go--window size changing when iconised 
		   and reactivated, the "disappearing minimise button 
		   syndrome", etc. etc.  The Developer CD serves up the 
		   usual crop of incoherent blithering.  I give up--go 
		   ahead and make the bloody window as big as Siberia 
		   if you like. */ 
        	 
        case WM_GETMINMAXINFO: 
    		if (pClientData != NULL && pClientData->face_shown) { 
    			MINMAXINFO FAR *mm = (MINMAXINFO FAR *) lParam; 
				BITMAPINFOHEADER FAR *bmi; 
			    RECT cr, wr; 
            			 
			    GetWindowRect(hwnd, &wr); 
			    GetClientRect(hwnd, &cr); 
	                         
	            bmi = (BITMAPINFOHEADER FAR *) (pClientData->face_bmp + sizeof(BITMAPFILEHEADER)); 
    			 
    			//	Don't allow resize of window larger than face 
 
    			mm->ptMaxSize.x = mm->ptMaxTrackSize.x = ((int) bmi->biWidth) + ((wr.right - wr.left) - cr.right);  
    			mm->ptMaxSize.x = mm->ptMaxTrackSize.y = ((int) bmi->biHeight) + ((wr.bottom - wr.top) - cr.bottom);  
    		}  
        	break; 
#endif        	 
        	 
        case WM_LBUTTONDBLCLK: 
			if (pClientData != NULL && pClientData->wantsInput == TRUE) { 
				pClientData->wantsInput = 2; 
	        	if (pClientData->buttonUpTimer) { 
	        		KillTimer(hwnd, 8); 
	        		pClientData->buttonUpTimer = FALSE; 
	        	} 
				break; 
			}         
        case WM_LBUTTONDOWN: 
spacebarOn: if (pClientData != NULL && !pClientData->wantsInput && !broadcasting && !bConferencing) 
            { 
	        	if (listeners == 0) { 
					inputPaused = FALSE; 
		        	if (!startWaveInput(hwnd)) { 
		        		//	Couldn't turn on wave audio input 
		        		break; 
		        	} 
		        } 
	        	if (pClientData->buttonUpTimer) { 
	        		KillTimer(hwnd, 8); 
	        		pClientData->buttonUpTimer = FALSE; 
	        	} 
		        spurt = TRUE;				// Mark start of talk spurt 
		        loop_flush(pClientData);	// Flush any unplayed local loop packets 
	        	pClientData->wantsInput = (nMessage == WM_LBUTTONDBLCLK) ? 2 : TRUE; 
		        listeners++; 
		        pClientData->timeout = -1;	// Send audio immortalises connection 
		        pClientData->state = SendingLiveAudio; 
	        	SetCursor(earCursor); 
	        	changeAudioState(hwnd, pClientData); 
	        } 
        	break; 
        	 
        case WM_LBUTTONUP: 
        	if (pClientData != NULL && !broadcasting && !bConferencing && !pClientData->buttonUpTimer) 
          { 
        		SetTimer(hwnd, 8, GetDoubleClickTime(), NULL); 
        		pClientData->buttonUpTimer = TRUE; 
        		break; 
        	} 
         
spacebarOff:if (pClientData != NULL && !broadcasting && !bConferencing) 
            { 
	        	if (pClientData->wantsInput == TRUE) { 
		        	pClientData->wantsInput = FALSE; 
			        pClientData->state = pClientData->hFile != HFILE_ERROR ? 
			        	Transferring : Idle; 
		        	SetCursor(phoneCursor); 
	        		changeAudioState(hwnd, pClientData); 
	    			if (--listeners <= 0) { 
			        	terminateWaveInput(); 
						inputPaused = FALSE; 
			        	listeners = 0; 
			        } 
 
			    /* If this the button-up following a double click, don't 
			       turn off listening.  This allows a double click to latch 
			       input mode for a window. */ 
		        } else if (pClientData->wantsInput == 2) { 
		        	pClientData->wantsInput = TRUE; 
		        	if (pClientData->buttonUpTimer) { 
		        		pClientData->buttonUpTimer = FALSE; 
		        		KillTimer(hwnd, 8); 
		        	} 
		        	SetCursor(earCursor); 
	    			UpdateWindow(hwnd); 
		        } 
	        }  
        	break; 
        	 
        case WM_MOUSEMOVE: 
        	if (pClientData != NULL) { 
	        	SetCursor((pClientData->wantsInput || broadcasting || bConferencing) ?  
	        				(squelched ? boltCursor : earCursor) : 
	        				phoneCursor); 
	        } 
        	break; 
         
        case WM_PAINT: 
			{ 
#define DCOL	11			 
			    PAINTSTRUCT psPaint; 
			    HDC hdc; 
			    HBITMAP ibmap = NULL; 
          DWORD dwCryptoMode = 0; 
			    int active = hwnd == ((HWND) LOWORD(SendMessage(hwndMDIClient, WM_MDIGETACTIVE, 0, 0L))), 
			    	resized = FALSE; 
          DWORD dwElapsedTime; 
#ifdef MONEY_SAVED_INDICATOR 
          char szMoneySaved[15]; 
#endif 
                 
			    hdc = BeginPaint(hwnd, &psPaint); 
			    if (pClientData != NULL) { 
			    	if (pClientData->face_stat == FScomplete && 
			    		pClientData->face_bmp != NULL) { 
		    			int bx, by; 
					    RECT cr, wr; 
	            		BITMAPFILEHEADER FAR *bfh; 
	            		BITMAPINFOHEADER FAR *bmi; 
	            		char _huge *bits; 
					    HPALETTE bpal = NULL, opal; 
        				int i; 
        				LPLOGPALETTE lp; 
        				BITMAPINFO FAR *bh; 
        				unsigned short FAR *palidx; 
        				LPSTR lpalette; 
        				unsigned short FAR *savepal; 
#ifdef TRACE_FACE 
{	char s[256]; 
						 
    wsprintf(s, "Paint %04X: %s\r\n", hwnd, pClientData->szHost); 
	OutputDebugString(s); 
} 
#endif						 
					     
					    pClientData->face_shown = FALSE; 
					    savepal = (unsigned short FAR *) GlobalAllocPtr(GPTR, 
					    			sizeof(LOGPALETTE) + 
					    			sizeof(PALETTEENTRY) * 256 + 
					    			sizeof(short) * 256); 
					    if (savepal == NULL) { 
					    	goto face_failed; 
					    } 
					    lpalette = ((LPSTR) savepal) + 256 * sizeof(short);  
					    GetWindowRect(hwnd, &wr); 
					    GetClientRect(hwnd, &cr); 
                         
                        bfh = (BITMAPFILEHEADER FAR *) pClientData->face_bmp; 
                        bmi = (BITMAPINFOHEADER FAR *) (pClientData->face_bmp + sizeof(BITMAPFILEHEADER)); 
                        bh = (BITMAPINFO FAR *) bmi;  
            			bx = (int) bmi->biWidth; 
            			by = (int) bmi->biHeight; 
            			 
            			if (bmi->biClrUsed == 0) { 
            				bmi->biClrUsed = 1L << bmi->biBitCount; 
            			} 
 
	            		bits = (char FAR *) (pClientData->face_bmp + bfh->bfOffBits); 
 
        				lp = (LOGPALETTE FAR *) lpalette; 
        				palidx = (unsigned short FAR *) bh->bmiColors; 
        				_fmemcpy(savepal, palidx, 256 * sizeof(short)); 
        				lp->palVersion = 0x0300; 
        				lp->palNumEntries = (WORD) bmi->biClrUsed; 
        				for (i = 0; i < ((int) bmi->biClrUsed); i++) { 
        					lp->palPalEntry[i].peRed = bh->bmiColors[i].rgbRed; 
        					lp->palPalEntry[i].peGreen = bh->bmiColors[i].rgbGreen; 
        					lp->palPalEntry[i].peBlue = bh->bmiColors[i].rgbBlue; 
        					lp->palPalEntry[i].peFlags = PC_NOCOLLAPSE; 
//        					if (active) { 
//								palidx[i] = (unsigned short) i; 
//							} 
        				} 
        				bpal = CreatePalette(lp); 
        				if (active && bpal != NULL) { 
							opal = SelectPalette(hdc, bpal, FALSE); 
							RealizePalette(hdc); 
        				} 
 
	            		ibmap = CreateDIBitmap(hdc, bmi, CBM_INIT, 
	            					bits, (BITMAPINFO FAR *) bmi, DIB_RGB_COLORS); 
        				_fmemcpy(palidx, savepal, 256 * sizeof(short)); 
			            if (ibmap != NULL) { 
			            	HDC hMemDC; 
			            	HBITMAP obmap; 
			            	HPALETTE mopal; 
		 
							hMemDC = CreateCompatibleDC(hdc); 
							if (bpal != NULL) { 
								mopal = SelectPalette(hMemDC, bpal, FALSE); 
							} 
							obmap = SelectObject(hMemDC, ibmap); 
							if (bx <= cr.right && by <= cr.bottom) { 
			            		BitBlt(hdc, (cr.right - bx) / 2, (cr.bottom - by) / 2, 
			            			bx, by, hMemDC, 0, 0, SRCCOPY); 
							} else { 
								int nx, ny; 
								double xshrink = ((double) bx) / cr.right, 
									   yshrink = ((double) by) / cr.bottom; 
		 
#ifdef TRACE_FACE 
								OutputDebugString("Yick!!!  Had to stretch face bitmap.\r\n"); 
#endif 
								if (xshrink > yshrink) { 
									nx = cr.right; 
									ny = (int) (by / xshrink); 
								} else { 
									ny = cr.bottom; 
									nx = (int) (bx / yshrink); 
								} 
								SetStretchBltMode(hdc, STRETCH_DELETESCANS); 
								StretchBlt(hdc, (cr.right - nx) / 2, (cr.bottom - ny) / 2, nx, ny, 
									hMemDC, 0, 0, bx, by, SRCCOPY); 
							} 
			            	SelectObject(hMemDC, obmap); 
			            	DeleteObject(ibmap); 
			            	if (bpal != NULL) { 
			            		if (active) { 
				            		SelectPalette(hdc, opal, FALSE); 
				            	} 
				            	SelectPalette(hMemDC, mopal, FALSE); 
			            		DeleteObject(bpal); 
			            	} 
			            	DeleteDC(hMemDC); 
			            	pClientData->face_shown = TRUE; 
			            } 
			            GlobalFreePtr(savepal);			    		 
			    	} 
face_failed:			    	 
			    	if (!pClientData->face_shown) { 
			    		int n = 0; 
			    		RECT cr; 
						HFONT ofont; 
			    		 
						ofont = SelectObject(hdc, GetStockObject(ANSI_VAR_FONT)); 
						SetBkColor(hdc, GetSysColor(COLOR_MENU)); 
						SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT)); 
				        WinPrintf(hdc, n, 1, pClientData->modemConnection ? 
				        	rstring(IDS_T_DIAL_STRING) : rstring(IDS_T_HOST)); 
				        WinPrintf(hdc, n++, DCOL, pClientData->szHost); 
				         
				        if (pClientData->modemConnection) { 
					        WinPrintf(hdc, n, 1, rstring(IDS_T_MODEM_CONNECTION_L)); 
				        } else { 
					        WinPrintf(hdc, n, 1, rstring(IDS_T_ADDRESS)); 
					        WinPrintf(hdc, n++, DCOL, Format(48), 
					        	inet_ntoa(pClientData->inetSock.sin_addr), 
					        	pClientData->port); 
	                    } 
	                     
	                    WinPrintf(hdc, n++, 1, pClientData->wantsInput ? rstring(IDS_T_TRANSMITTING) : 
	                    	rstring(IDS_T_BLANKTRANSMIT)); 
	                     
	                    WinPrintf(hdc, n, 1, Format(71)); 
                      dwCryptoMode = IDS_CRYPTO_NONE; 
                       
                      if(pClientData->deskey[0]) 
                        dwCryptoMode = IDS_CRYPTO_DES; 
                      else if(pClientData->ideakey[0]) 
                        dwCryptoMode = IDS_CRYPTO_IDEA; 
                      else if(pClientData->pgpkey[0]) 
                        dwCryptoMode = IDS_CRYPTO_PGP; 
                      else if(pClientData->blowfish_spec) 
                        dwCryptoMode = IDS_CRYPTO_BLOWFISH; 
 
	                    WinPrintf(hdc, n++, DCOL, "%s (%s)          ", rstring(IDS_PROTOCOL_TYPES + 
	                    	pClientData->protocol), rstring(dwCryptoMode)); 
	                    if ((pClientData->uname != NULL) && 
	                    	((pClientData->uname[0] & 0xFF) == 0xFF)) { 
	                    	LPSTR sp, np; 
	                    	int nl = 0; 
	                    	 
	                    	WinPrintf(hdc, n, 1, Format(74)); 
	                    	sp = pClientData->uname + 1; 
	                    	while (TRUE) { 
	                    		np = _fstrstr(sp, ", "); 
	                    		if (np != NULL) { 
	                    			*np = 0; 
	                    		} 
	                    		WinPrintf(hdc, n++, DCOL, "%s", sp); 
	                    		if (np != NULL) { 
	                    			*np = ','; 
	                    			sp = np + 2; 
	                    		} else { 
	                    			break; 
	                    		} 
	                    		nl++; 
/*	Too many in conference causes a crash I cannot reproduce 
	Limit to 8 names shown for now.  */	                    		 
if (nl >= 8) { 
	WinPrintf(hdc, n++, DCOL, "..."); 
	break;  
} 
	                    	} 
	                    } else { 
		                    if ((pClientData->uname != NULL) && (pClientData->uname[0])) { 
						        WinPrintf(hdc, n, 1, Format(69)); 
						        WinPrintf(hdc, n++, DCOL, "%s", pClientData->uname); 
		                    } 
		                    if (pClientData->email[0]) { 
						        WinPrintf(hdc, n, 1, Format(70)); 
						        WinPrintf(hdc, n++, DCOL, "%s", pClientData->email); 
		                    } 
                        if(pClientData->szRemoteProgram[0]) 
                        { 
						        WinPrintf(hdc, n, 1, Format(77)); 
						        WinPrintf(hdc, n++, DCOL, "%s", pClientData->szRemoteProgram); 
                        } 
						         
#define LONG_DISTANCE_RATE 0.10f // How much charged for one minute of long distance. 
 
                    if(pClientData->protocol != PROTOCOL_UNKNOWN) 
                    { 
                      dwElapsedTime = GetTickCount() - pClientData->dwStartTime; 
                      pClientData->fMoneySaved = (float) dwElapsedTime; 
                      pClientData->fMoneySaved /= 60000; 
                      pClientData->fMoneySaved = (LONG_DISTANCE_RATE * pClientData->fMoneySaved); 
                    } 
 
#ifdef MONEY_SAVED_INDICATOR 
					/* The money saved indicator was confusing a lot of people (mainly 
					   non-USA residents) because they thought Speak Freely was 
					   billing them for the call.  Plus, 10c/minute is getting to be 
					   a rather outdated long distance rate, so this should be 
					   rewritten to allow customization of the rate and whether or not 
					   to show it.  Hence the #ifdef here. */ 
                    sprintf(szMoneySaved, "$%.2f", pClientData->fMoneySaved); 
                    WinPrintf(hdc, n, 1, Format(78)); 
						        WinPrintf(hdc, n++, DCOL, "%s", szMoneySaved); 
#endif 
	                    } 
	 
#ifdef SHOW_STATE 
						/* It's nice to show the state, but costly to update on 
						   every packet. */ 
						   			 
				        WinPrintf(hdc, n, 1, "State:"); 
				        WinPrintf(hdc, n++, DCOL, "%s", stateToString(pClientData->state)); 
				 
				        switch (pClientData->state) { 
				        	case Idle: 
					            WinPrintf(hdc, n++, 1, blankit); 
					            WinPrintf(hdc, n++, 1, blankit); 
				        		break; 
				        		 
				        	case Transferring: 
					            WinPrintf(hdc, n, 1, "File: "); 
					            WinPrintf(hdc, n++, DCOL, "%s", pClientData->szFile); 
					            WinPrintf(hdc, n, 1, "Bytes sent:"); 
					            WinPrintf(hdc, n++, DCOL, "%lu", pClientData->cbSent); 
					            break; 
					             
					        case PlayingReceivedAudio: 
					            WinPrintf(hdc, n, 1, "Bytes received:"); 
					            WinPrintf(hdc, n++, DCOL, "%lu", pClientData->cbReceived); 
								break;				        	 
				        } 
#endif 
 
						SelectObject(hdc, ofont); 
	                    GetClientRect(hwnd, &cr); 
	                    if (cr.bottom != (n * tmHeight + 5)) { 
	                    	RECT wr; 
	                    	 
	                    	GetWindowRect(hwnd, &wr); 
							SetWindowPos(hwnd, HWND_TOP, 
								0, 0, wr.right - wr.left,  
								((wr.bottom - wr.top) - cr.bottom) + 
								(n * tmHeight + 5), SWP_NOMOVE | SWP_NOZORDER); 
							resized = TRUE; 
//OutputDebugString("Resized connection window.\r\n");								 
	                    } 
				    } 
#undef DCOL 
				}			     
			 
			    EndPaint(hwnd, &psPaint); 
			    if (resized) { 
			    	/* Oops--we resized the window after finishing the paint. 
			    	   Now we have to invalidate it again so it will be 
			    	   repainted in the new size. */ 
			    	InvalidateRect(hwnd, NULL, TRUE); 
			    	UpdateWindow(hwnd); 
			    }			 
			} 
        	break; 
        	 
		case WM_MDIACTIVATE: 
			if (wParam == FALSE || IsIconic(hwnd)) { 
				break; 
			} 
	    	if (pClientData != NULL && pClientData->face_shown) { 
			    HDC hdc; 
        		BITMAPINFOHEADER FAR *bmi; 
			    HPALETTE bpal = NULL, opal; 
				int i; 
				LPLOGPALETTE lp; 
				BITMAPINFO FAR *bh; 
				LPSTR lpalette; 
#ifdef TRACE_FACE 
{	char s[256]; 
						 
    wsprintf(s, "MDI activate %04X: %s\r\n", hwnd, pClientData->szHost); 
	OutputDebugString(s); 
} 
#endif						 
					     
			    lpalette =  GlobalAllocPtr(GPTR, sizeof(LOGPALETTE) + 
			    			sizeof(PALETTEENTRY) * 256); 
			    if (lpalette != NULL) { 
                    bmi = (BITMAPINFOHEADER FAR *) (pClientData->face_bmp + sizeof(BITMAPFILEHEADER)); 
                    bh = (BITMAPINFO FAR *) bmi;  
    				lp = (LOGPALETTE FAR *) lpalette; 
    				lp->palVersion = 0x0300; 
    				lp->palNumEntries = (WORD) bmi->biClrUsed; 
    				for (i = 0; i < ((int) bmi->biClrUsed); i++) { 
    					lp->palPalEntry[i].peRed = bh->bmiColors[i].rgbRed; 
    					lp->palPalEntry[i].peGreen = bh->bmiColors[i].rgbGreen; 
    					lp->palPalEntry[i].peBlue = bh->bmiColors[i].rgbBlue; 
    					lp->palPalEntry[i].peFlags = PC_NOCOLLAPSE; 
    				} 
    				bpal = CreatePalette(lp); 
    				if (bpal != NULL) { 
			   			hdc = GetDC(hwnd); 
						opal = SelectPalette(hdc, bpal, FALSE); 
						i = RealizePalette(hdc); 
						SelectPalette(hdc, opal, FALSE); 
			            DeleteObject(bpal);						 
    					ReleaseDC(hwnd, hdc); 
    					if (i > 0) { 
    						HWND oldwin; 
    						 
    						InvalidateRect(hwnd, NULL, TRUE); 
    						UpdateWindow(hwnd); 
							oldwin = (HWND) wParam; 
    						if (oldwin != NULL) { 
	    						InvalidateRect(oldwin, NULL, TRUE); 
	    						UpdateWindow(oldwin); 
    						} 
    					} 
    					return i; 
    				} 
    				GlobalFreePtr(lpalette); 
    			} 
    		} 
        	break; 
 
#ifdef ASYNC_OUTPUT        	 
        case WM_SOCKET_SELECT: 
        	if (pClientData != NULL) { 
        		pClientData->outputSocketBusy = FALSE; 
        	} 
#ifdef OVERLOAD			 
{ char s[80]; 
  wsprintf(s, "WM_SOCKET_SELECT Event = %d, Error = %d\r\n", 
  	WSAGETSELECTEVENT(lParam), WSAGETSELECTERROR(lParam)); 
OutputDebugString(s); 
} 
#endif 
		case WM_SOCKET_CONTROL:        	 
        	return 0; 
#endif        	 
        	 
        case WM_SOCKET_ASYNC: 
        	if (pClientData != NULL) { 
       			if (WSAGETASYNCERROR(lParam) == 0) { 
				    LPHOSTENT host; 
				 
				    host = (LPHOSTENT) pClientData->hostBuffer; 
				    SetWindowText(hwnd, pClientData->localLoopback ? 
				    	rstring(IDS_T_LOOPBACK) : host->h_name); 
				    _fstrcpy(pClientData->szHost, host->h_name); 
	        		changeAudioState(hwnd, pClientData); 
#ifdef SHOW_GET_HOST_NAME_ERROR	        		 
			    } else { 
					MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(27), 
					        pClientData->szHost, WSAGETASYNCERROR(lParam), 
					        SockerrToString(WSAGETASYNCERROR(lParam))); 
#endif					         
			    } 
        	} 
			pClientData->getNameTask = NULL; 
        	break; 
 
		/*	Send text chat message as an RTCP APP packet on the control 
			channel.  This is available only if the user is sending in 
			Speak Freely protocol.  */ 
 
		case WM_CHAT_TEXT_SEND: 
			if (protocolSent == PROTOCOL_SPEAKFREE) { 
				char v[256]; 
 
				LPSTR chat_text = (LPSTR) lParam; 
				int l, n = strlen(chat_text); 
 
				l = rtp_make_app(v, ssrc, TRUE, RTCP_APP_TEXT_CHAT, chat_text); 
				// Set Speak Freely protocol flag in packet 
				v[0] = (char) ((v[0] & 0x3F) | (1 << 6)); 
				sendSessionCtrl(pClientData, v, l); 
			} 
			break; 
			        	 
        case WM_TIMER: 
        	{ 
    			DWORD startTicks = GetTickCount(); 
          DWORD dwElapsedTime = GetTickCount(); 
 
			    if (pClientData == NULL) { 
			         break; 
			    } 
 
				/* If there are no buffers pending, advance the timeout 
				   counter.  When it reaches TIMEOUT_CONNECTION, close the 
				   connection. */ 
			 
			    if (wParam == FRAME_TIMER_ID && !broadcasting && !bConferencing && 
			    	pClientData->timeout >= 0 && outputPending == 0) { 
					if ((pClientData->timeout++) >= TIMEOUT_CONNECTION) { 
        				FORWARD_WM_MDIDESTROY(hwndMDIClient, hwnd, SendMessage); 
						return 0; 
			        } 
			        if (pClientData->timeout == 5) { 
						if (!IsIconic(hwnd)) { 
							pClientData->cbReceived = 0; 
							pClientData->state = Idle; 
if (pClientData->face_stat == -1) { 
	pClientData->face_stat = FSinit;	 
}							 
#ifdef SHOW_STATE							 
			    			InvalidateRect(hwnd, NULL, TRUE); 
			    			UpdateWindow(hwnd); 
#endif 			 
						} 
			        } 
			    } 
			     
			    /*	If it's time to resend the session identity 
			    	packet, go do it.  */ 
			    	 
			    if (wParam == FRAME_TIMER_ID) { 
		    		if (--(pClientData->sendSDEStimer) <= 0) { 
		    			char *msg = NULL; 
		    			int msgl; 
		    			char pid; 
			    			 
		    			pClientData->sendSDEStimer = TIMEOUT_RESEND_SDES; 
		    			switch (protocolSent) { 
		    				case PROTOCOL_RTP: 
		    				case PROTOCOL_SPEAKFREE: 
		    					msg = rtpsdes; 
		    					msgl = rtpsdesl; 
		    					break; 
			    					 
		    				case PROTOCOL_VAT: 
		    					msg = vatid; 
		    					msgl = vatidl; 
		    					break; 
		    			} 
                        if (msg != NULL) { 
							pid = msg[0]; 
							if (protocolSent == PROTOCOL_SPEAKFREE) { 
                        		msg[0] = (char) ((msg[0] & 0x3F) | (1 << 6)); 
							} 
							if (!((protocolSent == PROTOCOL_SPEAKFREE) && waProtNoHeartbeat)) { 
								sendSessionCtrl(pClientData, msg, msgl); 
							} 
							msg[0] = pid; 
						} 
			    	} 
			    }  
			     
			    /* If a broadcast is underway and the site has requested 
			       to unsubscribe, close the connection after a decent 
			       interval has elapsed to avoid toggling due to multiple 
			       packets. */ 
			        
				if (wParam == FRAME_TIMER_ID && broadcasting && 
					pClientData->broadcastEnd && 
					((GetTickCount() - pClientData->broadcastBeginTime) > 
					 (BroadcastUnsubscribe * 1000L))) { 
    				FORWARD_WM_MDIDESTROY(hwndMDIClient, hwnd, SendMessage); 
					return 0; 
				}     
				 
				/* Here's a little bit of paranoia.  What if the socket 
				   blocks or otherwise gets stuck and we don't get the 
				   WM_SOCKET_SELECT/FD_WRITE message like we're guaranteed 
				   to (right, tell me another) in the Winsock specification. 
				   Well, if the socket's marked as busy, why not call select() 
				   on every timer tick just to see if it's gotten unstuck 
				   without bothering to let us know.  Since all we do is 
				   clear the busy flag, this won't interfere with the 
				   notification message even if it's on the way at the 
				   time we arrive here. */ 
				    
				if (wParam == FRAME_TIMER_ID && pClientData->outputSocketBusy) { 
					fd_set fds; 
					struct timeval t; 
					 
					t.tv_sec = t.tv_usec = 0; 
					FD_ZERO(&fds); 
					FD_SET(pClientData->sReply, &fds); 
					if (select(1, NULL, &fds, NULL, &t) > 0) { 
						pClientData->outputSocketBusy = FALSE; 
#ifdef OVERLOAD 
						OutputDebugString("Timer cleared socket busy after select()\r\n"); 
#endif							 
					} 
				} 
				 
				/* If loopback packets are queued, we're not still 
				   recording them, but we haven't yet started to play 
				   them back (and aren't otherwise occupied with a sound file), 
				   start the playback process. */ 
				 
				if (wParam == FRAME_TIMER_ID && 
					(pClientData->localLoopback & LOOPBACK_ENABLED) && 
					!(pClientData->localLoopback & LOOPBACK_PLAYING) && 
					(pClientData->llhead != NULL) && 
					(pClientData->hFile == HFILE_ERROR && 
			    		pClientData->mmioHandle == NULL) && 
			    		!pClientData->wantsInput) { 
			    	pClientData->localLoopback |= LOOPBACK_PLAYING; 
			    	SetTimer(hwnd, 2, (UINT) 1000, NULL); 
				} 
				 
				/* Button up timer has expired.  If we're still transmitting, 
				   cease transmission. */ 
				    
				if (wParam == 8) { 
					KillTimer(hwnd, 8); 
					pClientData->buttonUpTimer = FALSE; 
					if (pClientData->wantsInput) { 
						SendMessage(hwnd, WM_CHAR, ' ', 0L); 
					} 
				}     
			      
			    /* Cadence timer indicating it's time to send the 
			       next block of a sound file.  Read it in and send 
			       it on its way. */ 
			 
			    if (wParam == 2 && (pClientData->hFile != HFILE_ERROR || 
			    				    pClientData->mmioHandle != NULL || 
			    				    pClientData->localLoopback & LOOPBACK_PLAYING)) { 
			    		long et; 
			    		UINT bread = 0; 
 
#ifdef MODEM			    		 
			    		if (pClientData->modemConnection) { 
			    			int err; 
			    			COMSTAT cs; 
			    			 
			    			err = GetCommError(modemHandle, &cs); 
			    			if (cs.cbOutQue > 1000) { 
				    			/* If modem connection and modem's backed up 
				    			   with output, spin until it goes idle. */ 
				            	SetTimer(hwnd, 2, 10, NULL); 
			    				break; 
			            	} 
			    		} 
#endif			    	 
						if (pClientData->localLoopback & LOOPBACK_PLAYING) { 
							while (outputPending < 10) { 
								bread = loop_samples(pClientData); 
								 
								if (bread == 0) { 
									pClientData->localLoopback &= ~LOOPBACK_PLAYING; 
									KillTimer(hwnd, 2); 
//OutputDebugString("End loopback replay.\r\n");									 
									return 0; 
								} 
								SendMessage(hwndMDIFrame, loop_control_port(pClientData) ? 
												WM_SOCKET_CONTROL : WM_SOCKET_SELECT, 
												INVALID_SOCKET, (LPARAM) pClientData); 
							}					 
				            SetTimer(hwnd, 2, (UINT) 10, NULL); 
				            return 0; 
			    		} else if (pClientData->mmioHandle != NULL) { 
			    			//	Queue next packet from .WAV input file 
			    			if (!pClientData->quitSoundFile) { 
			    				bread = readWaveNext(hwnd, pClientData); 
			    			} 
			    			if (bread == 0) { 
			    				readWaveTerm(pClientData); 
			    			} else { 
			    				bread = (UINT) ((bread * 8000L) / 11025L); 
			    			}  
			    		} else { 
			    			//	Queue next packet from .AU input file 
			    			if (!pClientData->quitSoundFile) { 
					            bread = _lread(pClientData->hFile, 
										sb.buffer.buffer_val, currentInputSamples); 
					        } 
				            if (bread == 0) { 
				            	_lclose(pClientData->hFile); 
				            	pClientData->hFile = HFILE_ERROR; 
				            } else { 
								sb.buffer.buffer_len = bread; 
								/* Since we're manufacturing our own sound buffer 
								   in place rather than calling CreateSoundBuffer(), 
								   we need to apply whatever compression is requested 
								   here before shipping the buffer. */ 
    							isWaveSound = TRUE; 
    							sqpacket = FALSE; 
 
	        					if (sb.buffer.buffer_len > 0) { 
#ifdef OBSOLETE 
							    	if (compression) { 
							    		compress2X(&sb); 
							    	} 
							    	if (gsmcompress) { 
							        	gsmcomp(&sb); 
							    	} 
							    	if (adpcmcompress) { 
							    		adpcmcomp(&sb); 
							    	} 
							    	if (lpccompress) { 
							    		lpccomp(&sb); 
							    	} 
									if (lpc10compress) { 
										lpc10comp(&sb); 
									} 
							        pktlen = sb.buffer.buffer_len + (sizeof(struct soundbuf) - BUFL);  
							        sb.compression = fProtocol;  
								    sb.compression |= gsmcompress ? fCompGSM : 0; 
								    sb.compression |= adpcmcompress ? fCompADPCM : 0; 
								    sb.compression |= lpccompress ? fCompLPC : 0; 
								    sb.compression |= lpc10compress ? fCompLPC10 : 0; 
								    strcpy(sb.sendinghost, ourSendingHost); 
									if (protocolSent == PROTOCOL_RTP) { 
										pktlen = rtpout(&sb, ssrc, timestamp, seq, spurt); 
										seq++; 
										timestamp += currentInputSamples; 
									} else if (protocolSent == PROTOCOL_VAT) { 
										pktlen = vatout(&sb, 0L, timestamp, spurt); 
										timestamp += currentInputSamples; 
									} 
#else 
									createSoundBuffer(sb.buffer.buffer_val, (WORD) sb.buffer.buffer_len, 
													  1, 8000, 8000, 0); 
#endif 
									shipSoundBuffer(hwnd, pClientData); 
								} else { 
									sqpacket = TRUE; 
								} 
				            } 
			            }		 
 
			            if (bread == 0) { 
			            	KillTimer(hwnd, 2); 
			            	pClientData->state = pClientData->wantsInput ? SendingLiveAudio : Idle; 
			            	DragAcceptFiles(hwnd, TRUE); 
#ifdef SHOW_STATE			            	 
			    			InvalidateRect(hwnd, NULL, TRUE); 
			    			UpdateWindow(hwnd); 
#endif			    			 
			    			return 0; 
			            } 
				        pClientData->cbSent += bread; 
#ifdef SHOW_STATE 
						InvalidateRect(hwnd, NULL, TRUE); 
						UpdateWindow(hwnd); 
#endif						 
 
						// If window isn't immortal, reset the timeout 
						if (pClientData->timeout > 0) { 
							pClientData->timeout = 0; 
						} 
			 
			            /* The following code is needed because when we're reading 
			               sound from a file, as opposed to receiving it in real 
			               time from the CODEC, we must meter out the samples 
			               at the rate they will actually be played by the destination 
			               machine.  For 8000 samples per second, this amounts 
			               to 125 microseconds per sample, minus the time we spent 
			               compressing the data (which is substantial for GSM) and 
			               a fudge factor, kOverhead, which accounts for the time 
			               spent in executing the delay itself and getting control 
			               back after it's done.  If sound files pause periodically 
			               (when the sending machine isn't loaded), you may need 
			               to reduce the delay parameters.  If they're too low, 
			               however, data will be lost when sending long sound files. */ 
			 
#define kOverhead 25000 
			            et = ((bread * 125L) - kOverhead) - 
			            	((GetTickCount() - startTicks) * 1000); 
			            if (et <= 0) { 
			            	et = 10; 
			            } 
			            SetTimer(hwnd, 2, (UINT) (et / 1000), NULL); 
#ifdef DBT             
if (!IsIconic(hwnd)) { 
	HDC hdc = GetDC(hwnd); 
							 
	WinPrintf(hdc, 6, 1, "Timer reset to %d ms.", (UINT) (et / 1000)); 
	ReleaseDC(hwnd, hdc);		 
} 
#endif			 
			    } 
			     
			    /*	Timer indicating a periodic PGP poll is underway. 
			       	If PGP has finished writing the decoded session key 
			       	file, read it into the connection structure and put 
			       	it into effect, then sweep up after PGP.  */ 
			       	 
			    if (wParam == 3) { 
			    	HFILE kfile = _lopen(pClientData->pgpFileName, 
			    						OF_READWRITE | OF_SHARE_EXCLUSIVE); 
			    	 
			    	if (kfile == HFILE_ERROR) { 
			    		//	Still not done.  Reset the timer 
			    		SetTimer(hwnd, 3, 1000, NULL); 
			    	} else { 
			    		if (_lread(kfile, pClientData->pgpkey, 17) == 17) { 
			    			char yfn[MAX_PATH]; 
		    				int i; 
		    				unsigned char ow[16]; 
			    			 
			    			pClientData->pgpkey[0] = TRUE; 
			    			 
			    			//	Overwrite the session key on disc 
			    			 
		    				_llseek(kfile, 0L, 0); 
		    				for (i = 0; i < 16; i++) { 
		    					ow[i] = 0xFF; 
		    				} 
		    				_lwrite(kfile, ow, 16); 
		    				_llseek(kfile, 0L, 0); 
		    				for (i = 0; i < 16; i++) { 
		    					ow[i] = 0; 
		    				} 
		    				_lwrite(kfile, ow, 16); 
			    			 
			    			_lclose(kfile); 
			    			_fstrcpy(yfn, pClientData->pgpFileName); 
			    			_unlink(yfn); 
			    			_fstrcat(yfn, ".TMP"); 
			    			_unlink(yfn); 
			    			pClientData->pgpFileName[0] = 0; 
			    			KillTimer(hwnd, 3);  
			    		} else { 
			    			pClientData->pgpkey[0] = FALSE; 
			    			SetTimer(hwnd, 3, 1000, NULL); 
			    			_lclose(kfile); 
			    		} 
			    	} 
			    } 
			     
			    /*	Timer indicating a periodic PGP poll is underway. 
			       	If PGP has finished writing the encoded session key 
			       	file, read it into the connection structure and put 
			       	it into effect, then sweep up after PGP.  */ 
			       	 
			    if (wParam == 4) { 
			    	HFILE kfile = _lopen(pClientData->opgpFileName, 
			    					OF_READ | OF_SHARE_EXCLUSIVE); 
			    	 
			    	if (kfile == HFILE_ERROR) { 
			    		//	Still not done.  Reset the timer 
			    		SetTimer(hwnd, 4, 1000, NULL); 
			    	} else { 
			    		int len; 
			    		 
			    		if ((len = _lread(kfile, ebuf.buffer.buffer_val, BUFL)) > 0) { 
			    			int i; 
			    			char yfn[MAX_PATH]; 
			    			 
			    			ebuf.buffer.buffer_len = len; 
			    			pClientData->opgpkey[0] = TRUE;		// Activate outbound PGP key 
			    			ebuf.compression = fProtocol | fKeyPGP; 
			    			strcpy(ebuf.sendinghost, ourSendingHost); 
					        revlong(&ebuf.compression); 
					        revlong(&ebuf.buffer.buffer_len); 
					         
					        for (i = 0; i < 3; i++) { 
						        if (writeOutput(pClientData, (LPSTR) &ebuf, 
						        		(int) ((sizeof(struct soundbuf) - BUFL) + 
						        		len)) < 0) { 
						        	break; 
						        } 
						    } 
			    			_lclose(kfile); 
			    			 
			    			_fstrcpy(yfn, pClientData->opgpFileName); 
			    			_unlink(yfn); 
				            yfn[_fstrlen(yfn) - 3] = 0; 
			    			_fstrcat(yfn, "TMP"); 
			    			 
			    			//	Overwrite the session key on disc 
 
			    			kfile = _lopen(yfn, OF_READWRITE | OF_SHARE_EXCLUSIVE); 
			    			if (kfile != HFILE_ERROR) { 
			    				int i; 
			    				unsigned char ow[16]; 
			    				 
			    				for (i = 0; i < 16; i++) { 
			    					ow[i] = 0xFF; 
			    				} 
			    				_lwrite(kfile, ow, 16); 
			    				_llseek(kfile, 0L, 0); 
			    				for (i = 0; i < 16; i++) { 
			    					ow[i] = 0; 
			    				} 
			    				_lwrite(kfile, ow, 16); 
			    				_lclose(kfile); 
			    			} 
			    			_unlink(yfn); 
			    			pClientData->opgpFileName[0] = 0; 
			    			KillTimer(hwnd, 4);  
			    		} else { 
			    			SetTimer(hwnd, 4, 1000, NULL); 
			    			_lclose(kfile); 
			    		} 
			    	} 
			    } 
			     
			    /*	Cadence timer regulating retrieval of a face 
			    	image from the connected host.  */ 
			     
			    if (wParam == 5) { 
					int makereq = FALSE; 
		 
					/* If a face file transfer is in progress, request 
		               the next block or, if it's time, re-issue the last 
					   request if the timeout has expired. */ 
		 
					if (pClientData->face_stat == FSreply) { 
						makereq = TRUE; 
						pClientData->face_retry = 0; 
#ifdef TRACE_FACE 
						{	char s[MAX_HOST + 80]; 
						 
		                    wsprintf(s, "Request face data at %ld from %s\r\n", 
								pClientData->face_address, pClientData->szHost); 
							OutputDebugString(s); 
						} 
#endif						 
					} else if (pClientData->face_stat == FSrequest) { 
						pClientData->face_timeout++; 
						if (pClientData->face_timeout >= FaceTimeout) { 
#ifdef TRACE_FACE 
							{	char s[MAX_HOST + 80]; 
							 
			                    wsprintf(s, "Retry %d reissue face data request at %ld from %s\r\n", 
										pClientData->face_retry, pClientData->face_address, 
										pClientData->szHost); 
								OutputDebugString(s); 
							} 
#endif						 
							if (pClientData->face_retry > FaceMaxRetries) { 
/*							 
								if (pClientData->face_file != NULL) { 
									fclose(c->face_file); 
									c->face_file = NULL; 
								} 
								unlink(c->face_filename); 
								c->face_filename[0] = 0; 
*/								 
								pClientData->face_stat = FSabandoned; 
								if (pClientData->face_bmp != NULL) { 
									GlobalFreePtr(pClientData->face_bmp); 
									pClientData->face_bmp = NULL; 
								} 
								KillTimer(hwnd, 5); 
#ifdef TRACE_FACE 
								{	char s[MAX_HOST + 80]; 
								 
				                    wsprintf(s, "Timeout, no face image available for %s\r\n", 
											pClientData->szHost); 
									OutputDebugString(s); 
								} 
#endif						 
							} else { 
								makereq = TRUE; 
								pClientData->face_retry++; 
							} 
						} 
					} else { 
						KillTimer(hwnd, 5);			// Terminated due to data format error 
#ifdef TRACE_FACE 
						OutputDebugString("Face timer shut down.\r\n"); 
#endif						 
					} 
		 
					if (makereq) { 
						pClientData->face_stat = FSrequest; 
						pClientData->face_timeout = 0; 
						sb.compression = htonl(fProtocol | fFaceData | faceRequest); 
						strcpy(sb.sendinghost, ourSendingHost); 
						sb.buffer.buffer_len = htonl(pClientData->face_address); 
						if (writeOutput(pClientData, (LPSTR) &sb, sizeof(soundbuf) - BUFL) < 0) { 
		                 	pClientData->face_stat = FSabandoned; 
// *** CLEAN UP DEBRIS		                 	    
						} 
					} 
			    } 
        dwElapsedTime -= pClientData->dwStartTime; 
        dwElapsedTime /= 1000; 
 
        if((dwElapsedTime % 6) == 0)  // Only paint every 6 seconds for timer. 
        { 
			      InvalidateRect(hwnd, NULL, TRUE); 
			      UpdateWindow(hwnd); 
        } 
			} 
        	return 0; 
    } 
    return DefMDIChildProc(hwnd, nMessage, wParam, lParam); 
}