www.pudn.com > globchat.zip > GLOBCHAT.C, change:1997-10-05,size:30061b


// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE. 
// 
// Copyright (C) 1993-1997  Microsoft Corporation.  All Rights Reserved. 
// 
//  MODULE:   generic.c 
// 
//  PURPOSE:   Implement the windows procedure for the main application 
//    windows.  Also implement the generic message and command dispatchers. 
// 
//  FUNCTIONS: 
//    WndProc      - Processes messages for the main window. 
//    MsgCreate    - Handles the WM_CREATE message.  Sets up Display and 
//                   initializes listening sockets.  Also advertises the  
//                   service 
//    MsgSize      - Resizes the Listboxes to fit nicely in main window. 
//    MsgTimer     - Handles the WM_TIMER messages which alert us to the 
//                   fact that its time to send a SAP packet 
//    MsgConnected - Handles new connection notifications 
//    MsgDataready - Handles recv data notifications and close socket 
//                   notifications 
//    MsgCommand   - Handle the WM_COMMAND messages for the main window. 
//    MsgDestroy   - Handles the WM_DESTROY message by calling 
//                   PostQuitMessage(). 
//    CmdExit      - Handles the file exit command by calling destory 
//                   window on the main window. 
// 
//  COMMENTS: 
// 
 
#include <windows.h>            // required for all Windows applications 
#include <windowsx.h> 
#include <wsipx.h> 
#include <wsnetbs.h> 
#include <svcguid.h> 
#include "globals.h"            // prototypes specific to this application 
 
 
// Main window message table definition. 
MSD rgmsd[] = 
{ 
    {WM_CREATE,    MsgCreate}, 
    {WM_SIZE,      MsgSize}, 
    {WM_TIMER,     MsgTimer}, 
    {WM_COMMAND,   MsgCommand}, 
    {WM_DESTROY,   MsgDestroy}, 
    {MW_CONNECTED, MsgConnected}, 
    {MW_DATAREADY, MsgDataready} 
}; 
 
MSDI msdiMain = 
{ 
    sizeof(rgmsd) / sizeof(MSD), 
    rgmsd, 
    edwpWindow 
}; 
 
 
// Main window command table definition. 
CMD rgcmd[] = 
{ 
    {IDM_EXIT,  CmdExit}, 
    {IDM_ABOUT, CmdAbout} 
}; 
 
CMDI cmdiMain = 
{ 
    sizeof(rgcmd) / sizeof(CMD), 
    rgcmd, 
    edwpWindow 
}; 
 
 
// 
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE:  Processes messages for the main window. 
// 
//  PARAMETERS: 
//    hwnd     - window handle 
//    uMessage - message number 
//    wparam   - additional information (dependant on message number) 
//    lparam   - additional information (dependant on message number) 
// 
//  RETURN VALUE: 
//    The return value depends on the message number.  If the message 
//    is implemented in the message dispatch table, the return value is 
//    the value returned by the message handling function.  Otherwise, 
//    the return value is the value returned by the default window procedure. 
// 
//  COMMENTS: 
//    Call the DispMessage() function with the main window's message dispatch 
//    information (msdiMain) and the message specific information. 
// 
 
LRESULT CALLBACK WndProc(HWND   hwnd, 
                         UINT   uMessage, 
                         WPARAM wparam, 
                         LPARAM lparam) 
{ 
    return DispMessage(&msdiMain, hwnd, uMessage, wparam, lparam); 
} 
 
// 
//  FUNCTION: MsgCreate(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE:  Sets up child windows, initializes listening sockets, and advertises 
//            the existence of the service 
// 
//  PARAMETERS: 
// 
//    hwnd      - Window handle 
//    uMessage  - Message number (Unused) 
//    wparam    - Extra data     (Unused) 
//    lparam    - Extra data     (Unused) 
// 
//  RETURN VALUE: 
// 
//    Always returns 0 - Message handled 
// 
//  COMMENTS: 
// 
// 
 
LRESULT MsgCreate(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam) 
{ 
    int sizeProtBuf = 0;  
    int cNumProt; 
    int j, k; 
    int GoodProts[40]; 
    WORD wVersionRequested; 
    WSADATA wsaData; 
    char outtext[80]; 
    int tabwidth[1] = {100}; 
    BOOL bcaststat; 
    int addrlen; 
 
    // Create Protocol listbox 
    hwndProtocolList = CreateWindow("LISTBOX", 
                                    NULL, 
                                    WS_BORDER | WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_USETABSTOPS, 
                                    0, 0, 0, 0, 
                                    hwnd, 
                                    (HMENU)ID_PROTOCOLBOX, 
                                    (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), 
                                    0); 
    // Set tabs 
    SendMessage(hwndProtocolList, LB_SETTABSTOPS, 1, (LPARAM)tabwidth); 
 
    // Create Client List listbox 
    hwndClientList = CreateWindow("LISTBOX", 
                                  NULL, 
                                  WS_BORDER | WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_USETABSTOPS | LBS_HASSTRINGS, 
                                  0, 0, 0, 0, 
                                  hwnd, 
                                  (HMENU)ID_CLIENTBOX, 
                                  (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), 
                                  0); 
    // Set tab stops 
    SendMessage(hwndClientList, LB_SETTABSTOPS, 1, (LPARAM)tabwidth); 
 
    // First allocate Connected Sockets Heap 
    if((ConnectHeap = HeapCreate(0, sizeof(SOCKDATA) * 5, sizeof(SOCKDATA) * 100)) == NULL) 
    { 
        // ERROR - abort 
		return 0; 
    } 
    if(((LPVOID)ConnectedSockets = HeapAlloc(ConnectHeap,0, sizeof(SOCKDATA) * 5)) == NULL) 
    { 
        // ERROR - abort 
        return 0; 
    } 
    NextFree = 0;  // First available SOCKDATA struct 
    MaxConnects = 5; // Allocate only 5 structures at a time 
 
    // Load winsock dll 
    wVersionRequested = MAKEWORD(1, 1); 
    if (WSAStartup(wVersionRequested, &wsaData) != 0) 
    { 
        // ERROR -abort 
		return 0; 
    } 
 
    // Determine size of buffer needed 
    if (EnumProtocols(NULL, NULL, &sizeProtBuf) != SOCKET_ERROR) 
    { 
        // ERROR 
        return 0; 
    } 
 
    // Allocate buffer of appropriate size 
    if (((LPVOID)lpProtBuf = VirtualAlloc(NULL, 
                                          sizeProtBuf, 
                                          MEM_COMMIT, 
                                          PAGE_READWRITE)) == NULL) 
    { 
        // ERROR 
        return 0; 
    } 
 
    // Do the real EnumProtocols call--note that there is no GETNEXT mechanism here. 
	// We have to allocate a single buffer big enough to hold everything. 
    if ((cNumProt = EnumProtocols(NULL, lpProtBuf, &sizeProtBuf)) == SOCKET_ERROR) 
    { 
        // ERROR 
        return 0; 
    } 
 
    // Now that we know how many protocols we have, allocate array of globchat 
	// SOCKDATA structures to hold info on each. 
    if (((LPVOID)ServerSockets = VirtualAlloc(NULL, 
                                              cNumProt * sizeof(SOCKDATA), 
                                              MEM_COMMIT, 
                                              PAGE_READWRITE)) == NULL) 
    { 
        // ERROR 
        return 0; 
    } 
 
    // Walk through available protocols, j is index to list of all protocols, k is index to list of 
	// protocols we can use. 
    for (j = 0, k = 0; j < cNumProt; j++) 
    { 
        // Only want connection oriented protocols 
        if((lpProtBuf[j].dwServiceFlags & XP_CONNECTIONLESS) == 0)   
        { 
			// Attempt to setup listen and fill in SOCKDATA struct 
			if (MakeServSock(hwnd, &ServerSockets[k], &lpProtBuf[j])) 
			{ 
	            // Successfully using protocol...add it to our list box 
	            wsprintf(outtext, "%s\t%d", ServerSockets[k].lpProtocolName, ServerSockets[k].currconnects); 
	            SendMessage(hwndProtocolList, LB_ADDSTRING, 0, (LPARAM)&outtext); 
 
	            // Found a good protocol! 
	            GoodProts[k++] = j; 
			} 
        } 
    } 
 
    cNumProt = k;  // Number of good protocols 
 
 
 
 
    // Register with Name Spaces 
 
    // In the future this will be done transparently, but for now must do it 
    // manually for specific protocols.  The only NSPs we have are DNS and SAP. 
    // Since DNS is static, that just leaves us with SAP to do. 
 
     
    if((SAPSocket = socket(AF_IPX,                // IPX family 
                           SOCK_DGRAM, 			  // SAPs are broadcasted 
                           NSPROTO_IPX + 4)		  // Uses IPX packet type 4 
                           ) != INVALID_SOCKET) 
    { 
        // Find IPX address which we are bound to 
        for(k = 0;k < cNumProt; k++) 
        { 
            if(ServerSockets[k].protocol == NSPROTO_SPXII) 
            { 
		        // Initialize SAP data 
		        SAPData.operation = 0x200;         // 2 = SAP response(network order) 
		        SAPData.servicetype = 0x5607;  // 0756 Assigned by Novell specifically for GLOBCHAT only!!!! 
		        strcpy(SAPData.name, "GLOBCHAT");  // My service name 
		        memcpy((void *)SAPData.network,    // Copy address into SAP body 
		               (void *)(ServerSockets[k].addr.sa_data),  
		               12); 
		        SAPData.hops = 0;                 // Hop count starts at zero 
 
		        SAPSockAddr.sa_family = AF_IPX;   // SAP packets sent over IPX 
		        AtoH((char *)&SAPSockAddr.sa_socket, "0452", 2);  // SAP destination socket address is 0452. 
	         
				// Bind to SAP socket 
		        if (bind(SAPSocket, (PSOCKADDR) &SAPSockAddr, sizeof(SOCKADDR_IPX)) == SOCKET_ERROR) 
		        { 
		            // Error binding to SAP socket 
					return 0; 
		        } 
		        else 
		        { 
		            // build destination address for SAP's IPX header (broadcast) 
		            SAPDestSockAddr.sa_family = AF_IPX; 
		            AtoH("00000000",SAPDestSockAddr.sa_netnum, 4); 
		            AtoH("FFFFFFFFFFFF", SAPDestSockAddr.sa_nodenum, 6); 
		            AtoH("0452", (char *)&SAPDestSockAddr.sa_socket, 2); 
 
	                // Setup socket to allow broadcasts 
	                bcaststat = TRUE; 
	                addrlen = sizeof(bcaststat); 
	                setsockopt(SAPSocket, SOL_SOCKET, SO_BROADCAST, (char *)&bcaststat, sizeof(bcaststat)); 
		            sendto(SAPSocket, (char *)&SAPData, sizeof(SAPData), MSG_DONTROUTE, (struct sockaddr *)&SAPDestSockAddr, sizeof(SAPDestSockAddr)); 
 
		            // Set timer to remind us to send SAP packet every 60 seconds. 
		            SetTimer(hwnd, SAPTIMER, 60000, NULL); 
		        } 
                break; 
            } 
        } 
    } 
    return 0; 
} 
 
// 
//  FUNCTION: MsgSize(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE:  Resizes child listboxes to fit nicely within parent 
// 
//  PARAMETERS: 
// 
//    hwnd      - Window handle 
//    uMessage  - Message number (Unused) 
//    wparam    - Extra data	 (Unused) 
//    lparam    - Window dimensions 
// 
//  RETURN VALUE: 
// 
//    Always returns 0 - Message handled 
// 
//  COMMENTS: 
// 
// 
 
LRESULT MsgSize(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam) 
{ 
    MoveWindow(hwndProtocolList, 
               1, (HIWORD(lparam) / 4), 
               (LOWORD(lparam) / 2), 
               (HIWORD(lparam) * 3) / 4, 
               TRUE); 
 
    MoveWindow(hwndClientList, 
               LOWORD(lparam) /2, 
               HIWORD(lparam) / 4, 
               LOWORD(lparam) / 2, 
               (HIWORD(lparam) * 3) / 4, 
               TRUE); 
    return 0; 
} 
 
// 
//  FUNCTION: MsgTimer(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE: Sends out SAP packet every 60 seconds 
// 
//  PARAMETERS: 
// 
//    hwnd      - Window handle 
//    uMessage  - Message number (Unused) 
//    wparam    - Should be = SAPTIMER 
//    lparam    - Extra data     (Unused) 
// 
//  RETURN VALUE: 
// 
//    Always returns 0 - Message handled 
// 
//  COMMENTS: 
// 
// 
 
LRESULT MsgTimer(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam) 
{ 
    sendto(SAPSocket, (char *) &SAPData, sizeof(SAPData), MSG_DONTROUTE, (struct sockaddr *)&SAPDestSockAddr, sizeof(SAPDestSockAddr)); 
    return 0; 
} 
 
// 
//  FUNCTION: MsgConnected(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE: Handles new connection notifications 
// 
//  PARAMETERS: 
// 
//    hwnd      - Window handle 
//    uMessage  - Message number (Unused) 
//    wparam    - socket which has the connection 
//    lparam    - loword = event, hiword = error code 
// 
//  RETURN VALUE: 
// 
//    Always returns 0 - Message handled 
// 
//  COMMENTS: 
// 
// 
 
LRESULT MsgConnected(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam) 
{ 
    int k, addrlen; 
    int index; 
    char outtext[80]; 
 
    // May have to increase our heap size 
    if(NextFree >= MaxConnects) 
    { 
        if(HeapAlloc(ConnectHeap, 0, sizeof(SOCKDATA) * 5) == NULL) 
        { 
           // ERROR on heap allocation...don't accept new connections 
		   return 0; 
        } 
        MaxConnects += 5;  // We've got five more connect entries available 
    } 
 
    // find socket that connect is refering to 
    k = 0; 
    while(ServerSockets[k].sock != wparam) k++; 
 
    // Lets keep track of our progress 
    ServerSockets[k].status = SOCKSTAT_ACCEPTING; 
 
    // Some address our bigger than a sockaddr struct.  I threw in some reserved bytes to cover it. 
    addrlen = sizeof(ConnectedSockets[NextFree].addr) + sizeof(ConnectedSockets[NextFree].reserved); 
 
    // Accept the connect request 
    if ((ConnectedSockets[NextFree].sock = accept(ServerSockets[k].sock, 
                                                 &ConnectedSockets[NextFree].addr, 
                                                 &addrlen)) == INVALID_SOCKET) 
    { 
        // ERROR clean up ConnectedSockets (that is don't increment NextFree) and go on our way 
        return 0; 
    } 
    if(WSAAsyncSelect(ConnectedSockets[NextFree].sock, hwnd, MW_DATAREADY, FD_READ | FD_CLOSE) == SOCKET_ERROR) 
    { 
        // ERROR clean up connection 
		closesocket(ConnectedSockets[NextFree].sock); 
        return 0; 
    } 
    // Fill in SOCKDATA structure for client socket 
    ConnectedSockets[NextFree].protocol = ServerSockets[k].protocol; 
    ConnectedSockets[NextFree].type = ServerSockets[k].type; 
    ConnectedSockets[NextFree].servsockindex = k; 
    ConnectedSockets[NextFree++].status = SOCKSTAT_CONNECTED; 
	// Increment protocol connection count and display 
    ServerSockets[k].currconnects ++; 
    index = SendMessage(hwndProtocolList, LB_FINDSTRING, (WPARAM)-1, (LPARAM)ServerSockets[k].lpProtocolName); 
    SendMessage(hwndProtocolList, LB_DELETESTRING, index, 0); 
    wsprintf(outtext, "%s\t%d", ServerSockets[k].lpProtocolName, ServerSockets[k].currconnects); 
    SendMessage(hwndProtocolList, LB_ADDSTRING, 0, (LPARAM)&outtext); 
 
    // Don't forget to set server socket status back to listening 
    ServerSockets[k].status = SOCKSTAT_LISTENING; 
 
    return 0; 
} 
 
// 
//  FUNCTION: MsgDataready(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE: Handles incoming socket data notifications and close socket 
//           notifications 
// 
//  PARAMETERS: 
// 
//    hwnd      - Window handle 
//    uMessage  - Message number 
//    wparam    - socket which has the data 
//    lparam    - loword = event, hiword = error code 
// 
//  RETURN VALUE: 
// 
//    Always returns 0 - Message handled 
// 
//  COMMENTS: 
// 
// 
 
LRESULT MsgDataready(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam) 
{ 
    int k, j, l; 
    int totalbytesread; 
    int index; 
    char outtext[80]; 
 
    // Find the appropriate socket...allow for the reuse of a closed socket handle 
    for (k = 0; k < NextFree; k++) 
    { 
        if((ConnectedSockets[k].sock == wparam) && (ConnectedSockets[k].status != SOCKSTAT_CLOSED)) 
            break; 
    } 
 
    if(LOWORD(lparam) == FD_CLOSE) 
    { 
        // Socket closed notification--cleanup! 
        closesocket(ConnectedSockets[k].sock); 
 
        // Delete name from client list display 
        UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_CLOSED, NULL); 
 
        // Take name off of other clients' available list 
        if (ConnectedSockets[k].status == SOCKSTAT_AVAILABLE) 
        { 
            deregistername(ConnectedSockets[k].name); 
        } 
 
        // if this connection was in session with another peer...notify peer of disconnect 
 
        // First, find peer 
        for (j = 0; j < NextFree; j++) 
        { 
            if((ConnectedSockets[j].sock == ConnectedSockets[k].peer) && 
               (ConnectedSockets[j].status == SOCKSTAT_INSESSION)) 
            { 
                break; 
            } 
        } 
        if (j < NextFree)  // Did we find a peer? 
        { 
            // Yes, build message header 
            xferbuf.hdr.signature = MYSIGNATURE; 
            xferbuf.hdr.length = totalbytesread = HDRSIZE; 
            xferbuf.hdr.command = SESSION_CLOSE; 
            // Send session close message 
            senddatamessage(ConnectedSockets[j].sock, &xferbuf); 
			// Update Peer's displayed status 
            ConnectedSockets[j].status = SOCKSTAT_AVAILABLE; 
            UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_AVAILABLE, NULL); 
			// Propagate the fact that the peer is now available for other chats 
			// and give peer list of other available clients 
            for (l = 0; l < NextFree; l++) 
            { 
                if ((l != j) && (ConnectedSockets[l].status == SOCKSTAT_AVAILABLE)) 
                { 
                    xferbuf.hdr.command = REGISTER_NAME; 
                    lstrcpy(xferbuf.data, ConnectedSockets[l].name); 
                    xferbuf.hdr.length = REALLEN(ConnectedSockets[l].name) + HDRSIZE; 
                    senddatamessage(ConnectedSockets[j].sock, &xferbuf); 
 
                    lstrcpy(xferbuf.data, ConnectedSockets[j].name); 
                    xferbuf.hdr.length = REALLEN(ConnectedSockets[j].name) + HDRSIZE; 
                    senddatamessage(ConnectedSockets[l].sock, &xferbuf); 
                } 
            } 
        } 
        // Cleanup ConnectedSockets array 
        ConnectedSockets[k].status = SOCKSTAT_CLOSED; 
        j = ConnectedSockets[k].servsockindex; 
	    // Fix protocol connection count display 
	    ServerSockets[j].currconnects --; 
	    index = SendMessage(hwndProtocolList, LB_FINDSTRING, (WPARAM)-1, (LPARAM)ServerSockets[j].lpProtocolName); 
	    SendMessage(hwndProtocolList, LB_DELETESTRING, index, 0); 
	    wsprintf(outtext, "%s\t%d", ServerSockets[j].lpProtocolName, ServerSockets[j].currconnects); 
	    SendMessage(hwndProtocolList, LB_ADDSTRING, 0, (LPARAM)&outtext); 
        return 0; 
    } 
 
    // There's data to read...read it! 
 
    if(!recvdatamessage(&ConnectedSockets[k], &xferbuf)) 
    { 
        return 0; 
    } 
 
    // We've got our whole message!  Now switch() on the command flag 
 
    switch(xferbuf.hdr.command) 
    { 
        case REGISTER_NAME:  // First message we should receive on a connection 
            if(ConnectedSockets[k].status != SOCKSTAT_CONNECTED) 
            { 
                // ERROR -- we weren't expecting this...drop it 
                return 0; 
            } 
            // Get name and add to internal structs and display 
            lstrcpy(ConnectedSockets[k].name, xferbuf.data); 
            ConnectedSockets[k].status = SOCKSTAT_AVAILABLE; 
            UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_AVAILABLE, NULL); 
 
            // Send notification to other "AVAILABLE" sockets that we have a new peer available 
            for (j = 0; j < NextFree; j++) 
            { 
                if((j != k) && (ConnectedSockets[j].status == SOCKSTAT_AVAILABLE)) 
                { 
                    // message in xferbuf should be able to be sent just like it is 
                    senddatamessage(ConnectedSockets[j].sock, &xferbuf); 
                } 
            } 
 
            // Send notifications back to registering peer of all the currently available peers 
 
            for (j = 0; j < NextFree; j++) 
            { 
                if((j != k) && (ConnectedSockets[j].status == SOCKSTAT_AVAILABLE)) 
                { 
                    // found one...build message and send it 
                    xferbuf.hdr.signature = MYSIGNATURE; 
                    xferbuf.hdr.length = REALLEN(ConnectedSockets[j].name) + HDRSIZE; 
                    xferbuf.hdr.command = REGISTER_NAME; 
                    lstrcpy(xferbuf.data, ConnectedSockets[j].name); 
                    senddatamessage(ConnectedSockets[k].sock, &xferbuf); 
                } 
            } 
 
            return 0; 
 
        case XFER_DATA:   // For passing data between two insession peers 
            if(ConnectedSockets[k].status != SOCKSTAT_INSESSION) 
            { 
                // ERROR -- we weren't expecting this...drop data 
                return 0; 
            } 
 
            // forward the message to peer...should be able transfer 
            // message without modification 
            senddatamessage(ConnectedSockets[k].peer, &xferbuf); 
            return 0; 
 
        case REQUEST_SESSION:  // Client is asking another peer for a chat 
            if(ConnectedSockets[k].status != SOCKSTAT_AVAILABLE) 
            { 
                // ERROR -- we weren't expecting this...drop data 
                return 0; 
            } 
 
            ConnectedSockets[k].status = SOCKSTAT_REQSESSION; 
            UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_REQSESSION, xferbuf.data); 
 
            // Find the socket which corresponds to the name 
 
            for(j = 0; j < NextFree; j++) 
            { 
                if(lstrcmp(ConnectedSockets[j].name, xferbuf.data) == 0) 
                { 
                    if(ConnectedSockets[j].status == SOCKSTAT_AVAILABLE) 
                    { 
                        // Found It! 
                        break; 
                    } 
                } 
            } 
 
            if (j == NextFree) return 0; 
 
            // Copy requester's name into send data buffer 
            lstrcpy(xferbuf.data, ConnectedSockets[k].name); 
            xferbuf.hdr.length = HDRSIZE + REALLEN(ConnectedSockets[k].name); 
            senddatamessage(ConnectedSockets[j].sock, &xferbuf); 
 
            // Update connected sockets structures 
            ConnectedSockets[j].status = SOCKSTAT_REQSESSION; 
            UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_REQSESSION, ConnectedSockets[k].name); 
            ConnectedSockets[j].peer = ConnectedSockets[k].sock; 
            ConnectedSockets[k].peer = ConnectedSockets[j].sock; 
            return 0; 
 
        case SESSION_REQUEST_RESPONSE:  // Response to session request 
 
            if(ConnectedSockets[k].status != SOCKSTAT_REQSESSION) 
            { 
                // ERROR -- not expecting this...drop packet 
                return 0; 
            } 
 
            // find peer entry 
            for (j = 0; j < NextFree; j++) 
            { 
                if ((ConnectedSockets[j].sock == ConnectedSockets[k].peer) && 
                    (ConnectedSockets[j].status == SOCKSTAT_REQSESSION)) 
                { 
                    // Found it! 
                    break; 
                } 
            } 
 
            if (j == NextFree) 
            { 
                // ERROR -- couldn't find peer...drop packet 
                return 0; 
            } 
 
            // forward response to requester 
            senddatamessage(ConnectedSockets[k].peer, &xferbuf); 
 
            if(*(xferbuf.data) == 1) 
            { 
                // Session accepted, change status of sockets 
                ConnectedSockets[k].status = SOCKSTAT_INSESSION; 
                ConnectedSockets[j].status = SOCKSTAT_INSESSION; 
                UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_INSESSION, ConnectedSockets[j].name); 
                UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_INSESSION, ConnectedSockets[k].name); 
                deregistername(ConnectedSockets[k].name); 
                deregistername(ConnectedSockets[j].name); 
            } 
            else 
            { 
                // Session not accepted, make sockets available 
                ConnectedSockets[k].status = SOCKSTAT_AVAILABLE; 
                ConnectedSockets[j].status = SOCKSTAT_AVAILABLE; 
                UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_AVAILABLE, NULL); 
                UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_AVAILABLE, NULL); 
            } 
 
            return 0; 
 
        case SESSION_CLOSE:  // Insession client chose "End Chat" option 
            if (ConnectedSockets[k].status != SOCKSTAT_INSESSION) 
            { 
                // We weren't expecting this...drop packet 
                return 0; 
            } 
 
            // Find Peer 
            for (j = 0; j < NextFree; j++) 
            { 
                if((ConnectedSockets[j].sock == ConnectedSockets[k].peer) && 
                   (ConnectedSockets[j].status == SOCKSTAT_INSESSION)) 
                { 
                    // Found it 
                    break; 
                } 
            } 
 
            if(j == NextFree) 
            { 
                // ERROR - couldn't find peer...drop message 
                return 0; 
            } 
 
            // forward message 
            senddatamessage(ConnectedSockets[k].peer,&xferbuf); 
 
            // Change Status 
            ConnectedSockets[k].status = SOCKSTAT_AVAILABLE; 
            ConnectedSockets[j].status = SOCKSTAT_AVAILABLE; 
            UpdateClientList(ConnectedSockets[k].name, SOCKSTAT_AVAILABLE, NULL); 
            UpdateClientList(ConnectedSockets[j].name, SOCKSTAT_AVAILABLE, NULL); 
 
            // register names of both peers with other available clients.  Also 
			// provide current available client names to both peers 
 
            for (l = 0; l < NextFree; l++)  // l = index to all connected sockets 
			                                // k = index of current peer 
											// j = index of socket who generated message 
            { 
                if (ConnectedSockets[l].status == SOCKSTAT_AVAILABLE) 
                { 
                    if ( l != k) 
                    { 
                        xferbuf.hdr.signature = MYSIGNATURE; 
                        xferbuf.hdr.length = REALLEN(ConnectedSockets[k].name) + HDRSIZE; 
                        xferbuf.hdr.command = REGISTER_NAME; 
                        lstrcpy(xferbuf.data, ConnectedSockets[k].name); 
                        senddatamessage(ConnectedSockets[l].sock, &xferbuf); 
 
                        if (l != j) 
                        { 
                            xferbuf.hdr.length = REALLEN(ConnectedSockets[l].name) + HDRSIZE; 
                            lstrcpy(xferbuf.data, ConnectedSockets[l].name); 
                            senddatamessage(ConnectedSockets[k].sock, &xferbuf); 
                        } 
                    } 
                    if (l != j) 
                    { 
                        xferbuf.hdr.signature = MYSIGNATURE; 
                        xferbuf.hdr.length = REALLEN(ConnectedSockets[j].name) + HDRSIZE; 
                        xferbuf.hdr.command = REGISTER_NAME; 
                        lstrcpy(xferbuf.data, ConnectedSockets[j].name); 
                        senddatamessage(ConnectedSockets[l].sock, &xferbuf); 
 
                        if (l != k) 
                        { 
                            xferbuf.hdr.length = REALLEN(ConnectedSockets[l].name) + HDRSIZE; 
                            lstrcpy(xferbuf.data, ConnectedSockets[l].name); 
                            senddatamessage(ConnectedSockets[j].sock, &xferbuf); 
                        } 
                    } 
                } 
            } 
            return 0; 
 
    } // End message command switch 
 
    // ERROR -- If we get here, we had an invalid message... drop it 
    return 0; 
} 
 
// 
//  FUNCTION: MsgCommand(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE: Handle the WM_COMMAND messages for the main window. 
// 
//  PARAMETERS: 
//    hwnd     - window handle 
//    uMessage - WM_COMMAND (Unused) 
//    GET_WM_COMMAND_ID(wparam, lparam)   - Command identifier 
//    GET_WM_COMMAND_HWND(wparam, lparam) - Control handle 
// 
//  RETURN VALUE: 
//    The return value depends on the message number.  If the message 
//    is implemented in the message dispatch table, the return value is 
//    the value returned by the message handling function.  Otherwise, 
//    the return value is the value returned by the default window procedure. 
// 
//  COMMENTS: 
//    Call the DispCommand() function with the main window's command dispatch 
//    information (cmdiMain) and the command specific information. 
// 
 
LRESULT MsgCommand(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam) 
{ 
    return DispCommand(&cmdiMain, hwnd, wparam, lparam); 
} 
 
 
// 
//  FUNCTION: MsgDestroy(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE: Calls PostQuitMessage(). 
// 
//  PARAMETERS: 
// 
//    hwnd      - Window handle  (Unused) 
//    uMessage  - Message number (Unused) 
//    wparam    - Extra data     (Unused) 
//    lparam    - Extra data     (Unused) 
// 
//  RETURN VALUE: 
// 
//    Always returns 0 - Message handled 
// 
//  COMMENTS: 
// 
// 
 
LRESULT MsgDestroy(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam) 
{ 
    PostQuitMessage(0); 
    return 0; 
} 
 
// 
//  FUNCTION: CmdExit(HWND, WORD, WORD, HWND) 
// 
//  PURPOSE: Exit the application. 
// 
//  PARAMETERS: 
//    hwnd     - The window. 
//    wCommand - IDM_EXIT (unused) 
//    wNotify  - Notification number (unused) 
//    hwndCtrl - NULL (unused) 
// 
//  RETURN VALUE: 
//    Always returns 0 - command handled. 
// 
//  COMMENTS: 
// 
// 
 
LRESULT CmdExit(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl) 
{ 
    DestroyWindow(hwnd); 
    return 0; 
}