MISC.C

// 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 1993 - 1998 Microsoft Corporation. All Rights Reserved.
//
// MODULE: misc.c
//
// PURPOSE: Contains all helper functions "global" to the application.
//
// FUNCTIONS:
// CenterWindow - Center one window over another.
// AtoH - Converts ascii to network order hex
// BtoH - Converts ascii byte to numeric
// deregistername - Notifies "Available" peers of clients non-availability
// senddatamessage - Sends a message to a client node
// recvdatamessage - reads an entire message
// UpdateClientList - Updates Client Status Listbox
// MakeServSock - Attempts to initialize socket for listening
//
// COMMENTS:
//
//

#include <windows.h> // required for all Windows applications
#include <windowsx.h>
#include <nspapi.h>
#include <svcguid.h>
#include <wsipx.h>
#include <wsnetbs.h>
#include "globals.h" // prototypes specific to this application



//
// FUNCTION: CenterWindow(HWND, HWND)
//
// PURPOSE: Center one window over another.
//
// PARAMETERS:
// hwndChild - The handle of the window to be centered.
// hwndParent- The handle of the window to center on.
//
// RETURN VALUE:
//
// TRUE - Success
// FALSE - Failure
//
// COMMENTS:
//
// Dialog boxes take on the screen position that they were designed
// at, which is not always appropriate. Centering the dialog over a
// particular window usually results in a better position.
//

BOOL CenterWindow(HWND hwndChild, HWND hwndParent)
{
RECT rcChild, rcParent;
int cxChild, cyChild, cxParent, cyParent;
int cxScreen, cyScreen, xNew, yNew;
HDC hdc;

// Get the Height and Width of the child window
GetWindowRect(hwndChild, &rcChild);
cxChild = rcChild.right - rcChild.left;
cyChild = rcChild.bottom - rcChild.top;

// Get the Height and Width of the parent window
GetWindowRect(hwndParent, &rcParent);
cxParent = rcParent.right - rcParent.left;
cyParent = rcParent.bottom - rcParent.top;

// Get the display limits
hdc = GetDC(hwndChild);
cxScreen = GetDeviceCaps(hdc, HORZRES);
cyScreen = GetDeviceCaps(hdc, VERTRES);
ReleaseDC(hwndChild, hdc);

// Calculate new X position, then adjust for screen
xNew = rcParent.left + ((cxParent - cxChild) / 2);
if (xNew < 0)
{
xNew = 0;
}
else if ((xNew + cxChild) > cxScreen)
{
xNew = cxScreen - cxChild;
}

// Calculate new Y position, then adjust for screen
yNew = rcParent.top + ((cyParent - cyChild) / 2);
if (yNew < 0)
{
yNew = 0;
}
else if ((yNew + cyChild) > cyScreen)
{
yNew = cyScreen - cyChild;
}

// Set it, and return
return SetWindowPos(hwndChild,
NULL,
xNew, yNew,
0, 0,
SWP_NOSIZE | SWP_NOZORDER);
}

//
// FUNCTION: AtoH(char *, char *, int)
//
// PURPOSE: Converts ascii string to network order hex
//
// PARAMETERS:
// src - pointer to input ascii string
// dest - pointer to output hex
// destlen - size of dest
//
// COMMENTS:
//
// 2 ascii bytes make a hex byte so must put 1st ascii byte of pair
// into upper nibble and 2nd ascii byte of pair into lower nibble.
//

void AtoH(char * src, char * dest, int destlen)
{
char * srcptr;

srcptr = src;

while(destlen--)
{
*dest = BtoH(*srcptr++) << 4; // Put 1st ascii byte in upper nibble.
*dest++ += BtoH(*srcptr++); // Add 2nd ascii byte to above.
}
}

//
// FUNCTION: BtoH(char *, char *, int)
//
// PURPOSE: Converts ascii byte to numeric
//
// PARAMETERS:
// ch - ascii byte to convert
//
// RETURNS:
// associated numeric value
//
// COMMENTS:
//
// Will convert any hex ascii digit to its numeric counterpart.
// Puts in 0xff if not a valid hex digit.
//

unsigned char BtoH(char ch)
{
if (ch >= '0' && ch <= '9') return (ch - '0'); // Handle numerals
if (ch >= 'A' && ch <= 'F') return (ch - 'A' + 0xA); // Handle capitol hex digits
if (ch >= 'a' && ch <= 'f') return (ch - 'a' + 0xA); // Handle small hex digits
return(255);
}

//
// FUNCTION: deregistername(char *)
//
// PURPOSE: Removes client's availability from other "AVAILABLE" clients
//
// PARAMETERS:
// name - name to deregister
//
// RETURNS:
// nothing
//
// COMMENTS:
//
// Between the time when a client connects to the server and when it starts
// a chat, the client is in "AVAILABLE" state. It keeps of list of other
// available peers. deregistername removes a previously available client
// from the lists of all the other available clients
//

void deregistername(char * name)
{
int j;

// Set up message header
xferbuf.hdr.signature = MYSIGNATURE;
xferbuf.hdr.length = REALLEN(name) + HDRSIZE;
xferbuf.hdr.command = DEREGISTER_NAME;
// Message data is simply the name
lstrcpy(xferbuf.data, name);

// Send to every connected client whose status is SOCKET_AVAILABLE
for (j = 0; j < NextFree; j++)
{
if (ConnectedSockets[j].status == SOCKSTAT_AVAILABLE)
{
senddatamessage(ConnectedSockets[j].sock, &xferbuf);
}
}
return;
}

//
// FUNCTION: senddatamessage(SOCKET, LPDATAMSG)
//
// PURPOSE: Sends a message to a client
//
// PARAMETERS:
// sock - Client socket to send data on
// sendbuf - message to send
//
// RETURNS:
// TRUE - message sent
//
// COMMENTS:
//
// Performs send() until entire message is sent
//

BOOL senddatamessage (SOCKET sock, LPDATAMSG sendbuf)
{
int totalbytes, bytessent;
int thissendsize;

bytessent = 0; // start count of bytessent
totalbytes = sendbuf->hdr.length; // read total bytes from message header
while(bytessent < totalbytes)
{
// We may not be able to send the entire message with a single
// send. Therefore continue sending until the whole thing
// is gone.
if ((thissendsize = send(sock,
(char *)sendbuf + bytessent,
totalbytes - bytessent,
0)) == SOCKET_ERROR)
{
// Error
return FALSE;
}
bytessent += thissendsize;
}

return TRUE;
}

//
// FUNCTION: recvdatamessage(LPSOCKDATA, LPDATAMSG)
//
// PURPOSE: Receives a message from a client
//
// PARAMETERS:
// lpSockdat - pointer to sockdata struct of socket we are receiving data on.
// recvbuf - buffer to receive data into.
//
// RETURNS:
// TRUE - message received
// FALSE - message not received
//
// COMMENTS:
// Performs recv() until entire message is received
//

BOOL recvdatamessage (LPSOCKDATA lpSockdat, LPDATAMSG recvbuf)
{
int readsize, totalbytesread, msgsize, bytesread;

if (lpSockdat->type == SOCK_SEQPACKET)
{
// Message mode protocol!! Just post one big read.
readsize = sizeof(*recvbuf);
}
else
{
// Stream mode protocol!! Just read header...then read data (data size determined
// from header)
readsize = HDRSIZE;
}
if((totalbytesread = recv(lpSockdat->sock, (char *)recvbuf, readsize, 0)) == SOCKET_ERROR)
{
// ERROR -- just return dropping message
return FALSE;
}

// Check for my signature at the beginning of the message
if(recvbuf->hdr.signature != MYSIGNATURE)
{
// I've received some data that's in mid message--drop it
return FALSE;
}

// Read size of message
msgsize = recvbuf->hdr.length;
readsize = msgsize - totalbytesread;

while(totalbytesread < msgsize)
{
// we should only get hear for stream sockets
if((bytesread = recv(lpSockdat->sock,
(char *)recvbuf + totalbytesread,
readsize,
0)) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
// ERROR -- throw out message
return FALSE;
}
// If you got a WSAWOULDBLOCK error, just keep trying...it shouldn't take
// too much longer for the rest of the message to get here. Let's hope
// we don't hog the CPU so the data doesn't get to us.
}
totalbytesread += bytesread;
readsize -= bytesread;
}

return TRUE;
}

//
// FUNCTION: UpdateClientList(char *, int, char *)
//
// PURPOSE: Updates Client Status List box
//
// PARAMETERS:
// name - name of client to update
// status - status of client to display
// peername - connected peer for status = IN_SESSION
//
// RETURNS:
// nothing
//
// COMMENTS:
// Finds and updates client status in listbox, or adds
// it if client is new
//


void UpdateClientList(char * name, int status, char * peername)
{
int index;
char outtext[80];

// Find list box entry with name in it
index = SendMessage(hwndClientList,
LB_FINDSTRING,
(WPARAM)-1,
(LPARAM)name);

// Delete the entry--we'll add a new entry below
SendMessage(hwndClientList, LB_DELETESTRING, index, 0);

switch (status)
{
case SOCKSTAT_CLOSED: // No new entry required. Just return.
return;

case SOCKSTAT_AVAILABLE: // build available entry
wsprintf(outtext, GetStringRes(IDS_AVAILABLE), name);
break;

case SOCKSTAT_INSESSION: // build in_session entry
wsprintf(outtext, GetStringRes (IDS_SESSION), name, peername);
break;

case SOCKSTAT_REQSESSION: // build reqsession entry
wsprintf(outtext, GetStringRes(IDS_SESSION_SETUP), name, peername);
}
// Add the new list box entry to the client status list box
SendMessage(hwndClientList, LB_ADDSTRING, 0, (LPARAM)&outtext);
}

//
// FUNCTION: MakeServSock(HWND, LPSOCKDATA, LPPROTOCOL_INFO)
//
// PURPOSE: Attempts to initialize server side socket for listening
//
// PARAMETERS:
// hwnd - handle to main window
// ServSocks - SOCKDATA struct to hold socket specific information
// lpProtBuf - PROTOCOL_INFO struct describing protocol to
// attempt listen on
//
// RETURNS:
// TRUE - Listen successfully posted
// FALSE - Initialization failed
//
// COMMENTS:
// Given the protocol triple in the PROTOCOL_INFO struct, we call
// socket(), then GetAddressByName(), then bind(), listen(), and
// finally WSAAsyncSelect() to get socket it appropriate state
// for listening. If we fail at any point, then cleanup and do
// not use protocol
//

BOOL MakeServSock(HWND hwnd, LPSOCKDATA ServSocks, LPPROTOCOL_INFO lpProtBuf)
{
CSADDR_INFO CSABuf[10];
DWORD dwCSABufsize = sizeof(CSABuf);
GUID guidNW = SVCID_NETWARE(NWCHATID); // Macro generates GUID using hard coded NetWare Service Type
GUID guidDNS = SVCID_TCP(DNSCHATID); // Macro generates GUID using hard coded port
LPSOCKADDR_NB lpNBaddr;

// open socket using protocol defined by PROTOCOL_INFO structure
if ((ServSocks->sock = socket(lpProtBuf->iAddressFamily,
lpProtBuf->iSocketType,
lpProtBuf->iProtocol)) == SOCKET_ERROR)
{
// ERROR
return FALSE; // This will reuse the current ServSocks structure
}

// Populate SOCKDATA structure
ServSocks->status = SOCKSTAT_INIT;
ServSocks->type = lpProtBuf->iSocketType;
ServSocks->protocol = lpProtBuf->iProtocol;
ServSocks->currconnects = 0;
ServSocks->lpProtocolName = lpProtBuf->lpProtocol;

// ***********************************************************************
//
// Below we will be calling GetAddressByName with the RES_SERVICE
// option in order to find the necessary sockaddr structure to use
// for the following bind() call. At this point, GetAddressByName()
// only supports the DNS and the SAP/Bindery name spaces. Ultimately
// a single call to GetAddressByName will return information on
// all available protocols, but this requires a central database
// which is currently not available. In the mean time we will make
// name space specific calls to GetAddressByName.
//
// ***********************************************************************

switch (lpProtBuf->iAddressFamily)
{
case AF_IPX:
// Use SAP/Bindery Name Space
if (GetAddressByName(0, // Since GUID is name space specific, we don't need to specify distinct name space.
&guidNW, // GUID defined by NetWare service type.
"GLOBAL CHAT SERVER", // This parameter is actually not used for RES_SERVICE calls.
NULL, // Since GUID implies the protocol, we don't need to specify a distinct one.
RES_SERVICE, // Specifies that we are just looking for local address to bind to.
NULL, // Currently not supported
CSABuf, // Structure which will hold information for bind() call
&dwCSABufsize, // Size of CSABuf
NULL, // Currently not supported
NULL) // Currently not supported
< 1) // We need at least one address returned in order to bind()
{
// Error--cleanup
closesocket(ServSocks->sock);
return FALSE;
}

// Copy sockaddr info and addresslength
memcpy(&ServSocks->addr, CSABuf[0].LocalAddr.lpSockaddr, sizeof(struct sockaddr));
ServSocks->addrlen = CSABuf[0].LocalAddr.iSockaddrLength;
break;

case AF_INET:
// Use DNS Name Space
if (GetAddressByName(0, // Since GUID is name space specific, we don't need to specify
&guidDNS, // GUID defined by TCP port number
"globchat", // This parameter is actually not used for RES_SERVICE calls
NULL, // GUID implies protocol so no need to specify
RES_SERVICE, // Specifies that we are trying to find local address to bind to
NULL, // Currently not supported
CSABuf, // Results buffer
&dwCSABufsize, // Size of results buffer
NULL, // Not supported
NULL) // Not supported
< 1) // Need at least one address returned in order to bind()
{
// Error -- cleanup
closesocket(ServSocks->sock);
return FALSE;
}
// Copy sockaddr info and address length
memcpy(&ServSocks->addr, CSABuf[0].LocalAddr.lpSockaddr, sizeof(struct sockaddr));
ServSocks->addrlen = CSABuf[0].LocalAddr.iSockaddrLength;
break;

case AF_NETBIOS:
// no netbios name space provider so we will simulate GetAddressByName() results
lpNBaddr = (LPSOCKADDR_NB)&ServSocks->addr;
SET_NETBIOS_SOCKADDR(lpNBaddr,
NETBIOS_GROUP_NAME,
"GLOBSERV",
0);
ServSocks->addrlen = lpProtBuf->iMaxSockAddr;
break;

}

// We have an address to bind() to, so do it!
if (bind(ServSocks->sock,
&ServSocks->addr,
ServSocks->addrlen) == SOCKET_ERROR)
{
// Error -- cleanup
closesocket(ServSocks->sock);
return FALSE;
}

// Find out the specific local address
if (getsockname(ServSocks->sock,
&ServSocks->addr,
&ServSocks->addrlen) == SOCKET_ERROR)
{
// Error -- better cleanup
closesocket(ServSocks->sock);
return FALSE;
}

// Listen on the socket
if (listen(ServSocks->sock, 5) == SOCKET_ERROR)
{
// Error -- cleanup
closesocket(ServSocks->sock);
return FALSE;
}

// Set up window message to indicate connection
if (WSAAsyncSelect(ServSocks->sock,
hwnd,
MW_CONNECTED,
FD_ACCEPT) == SOCKET_ERROR)
{
// Error -- cleanup
closesocket(ServSocks->sock);
return FALSE;
}

// Success!!!
return TRUE;
}

//---------------------------------------------------------------------------
//
// FUNCTION: GetStringRes (int id INPUT ONLY)
//
// COMMENTS: Load the resource string with the ID given, and return a
// pointer to it. Notice that the buffer is common memory so
// the string must be used before this call is made a second time.
//
//---------------------------------------------------------------------------

LPTSTR GetStringRes (int id)
{
static TCHAR buffer[MAX_PATH];

buffer[0]=0;
LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
return buffer;
}