home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / netds / winsock / globchat / server / misc.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  18KB  |  556 lines

  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (C) 1993-1997  Microsoft Corporation.  All Rights Reserved.
  7. //
  8. //  MODULE:   misc.c
  9. //
  10. //  PURPOSE:  Contains all helper functions "global" to the application.
  11. //
  12. //  FUNCTIONS:
  13. //    CenterWindow - Center one window over another.
  14. //    AtoH - Converts ascii to network order hex
  15. //    BtoH - Converts ascii byte to numeric
  16. //    deregistername - Notifies "Available" peers of clients non-availability
  17. //    senddatamessage - Sends a message to a client node
  18. //    recvdatamessage - reads an entire message
  19. //    UpdateClientList - Updates Client Status Listbox
  20. //    MakeServSock - Attempts to initialize socket for listening
  21. //
  22. //  COMMENTS:
  23. //
  24. //
  25.  
  26. #include <windows.h>            // required for all Windows applications
  27. #include <windowsx.h>
  28. #include <nspapi.h>
  29. #include <svcguid.h>
  30. #include <wsipx.h>
  31. #include <wsnetbs.h>
  32. #include "globals.h"            // prototypes specific to this application
  33.  
  34.  
  35.  
  36. //
  37. //  FUNCTION: CenterWindow(HWND, HWND)
  38. //
  39. //  PURPOSE:  Center one window over another.
  40. //
  41. //  PARAMETERS:
  42. //    hwndChild - The handle of the window to be centered.
  43. //    hwndParent- The handle of the window to center on.
  44. //
  45. //  RETURN VALUE:
  46. //
  47. //    TRUE  - Success
  48. //    FALSE - Failure
  49. //
  50. //  COMMENTS:
  51. //
  52. //    Dialog boxes take on the screen position that they were designed
  53. //    at, which is not always appropriate. Centering the dialog over a
  54. //    particular window usually results in a better position.
  55. //
  56.  
  57. BOOL CenterWindow(HWND hwndChild, HWND hwndParent)
  58. {
  59.     RECT    rcChild, rcParent;
  60.     int     cxChild, cyChild, cxParent, cyParent;
  61.     int     cxScreen, cyScreen, xNew, yNew;
  62.     HDC     hdc;
  63.  
  64.     // Get the Height and Width of the child window
  65.     GetWindowRect(hwndChild, &rcChild);
  66.     cxChild = rcChild.right - rcChild.left;
  67.     cyChild = rcChild.bottom - rcChild.top;
  68.  
  69.     // Get the Height and Width of the parent window
  70.     GetWindowRect(hwndParent, &rcParent);
  71.     cxParent = rcParent.right - rcParent.left;
  72.     cyParent = rcParent.bottom - rcParent.top;
  73.  
  74.     // Get the display limits
  75.     hdc = GetDC(hwndChild);
  76.     cxScreen = GetDeviceCaps(hdc, HORZRES);
  77.     cyScreen = GetDeviceCaps(hdc, VERTRES);
  78.     ReleaseDC(hwndChild, hdc);
  79.  
  80.     // Calculate new X position, then adjust for screen
  81.     xNew = rcParent.left + ((cxParent - cxChild) / 2);
  82.     if (xNew < 0)
  83.     {
  84.         xNew = 0;
  85.     }
  86.     else if ((xNew + cxChild) > cxScreen)
  87.     {
  88.         xNew = cxScreen - cxChild;
  89.     }
  90.  
  91.     // Calculate new Y position, then adjust for screen
  92.     yNew = rcParent.top  + ((cyParent - cyChild) / 2);
  93.     if (yNew < 0)
  94.     {
  95.         yNew = 0;
  96.     }
  97.     else if ((yNew + cyChild) > cyScreen)
  98.     {
  99.         yNew = cyScreen - cyChild;
  100.     }
  101.  
  102.     // Set it, and return
  103.     return SetWindowPos(hwndChild,
  104.                         NULL,
  105.                         xNew, yNew,
  106.                         0, 0,
  107.                         SWP_NOSIZE | SWP_NOZORDER);
  108. }
  109.  
  110. //
  111. //  FUNCTION: AtoH(char *, char *, int)
  112. //
  113. //  PURPOSE:  Converts ascii string to network order hex
  114. //
  115. //  PARAMETERS:
  116. //    src    - pointer to input ascii string
  117. //    dest   - pointer to output hex
  118. //    destlen - size of dest
  119. //
  120. //  COMMENTS:
  121. //
  122. //    2 ascii bytes make a hex byte so must put 1st ascii byte of pair
  123. //    into upper nibble and 2nd ascii byte of pair into lower nibble.
  124. //
  125.  
  126. void AtoH(char * src, char * dest, int destlen)
  127. {
  128.     char * srcptr;
  129.  
  130.     srcptr = src;
  131.  
  132.     while(destlen--)
  133.     {
  134.     *dest = BtoH(*srcptr++) << 4;    // Put 1st ascii byte in upper nibble.
  135.     *dest++ += BtoH(*srcptr++);      // Add 2nd ascii byte to above.
  136.     }
  137. }
  138.  
  139. //
  140. //  FUNCTION: BtoH(char *, char *, int)
  141. //
  142. //  PURPOSE:  Converts ascii byte to numeric
  143. //
  144. //  PARAMETERS:
  145. //    ch - ascii byte to convert
  146. //
  147. //  RETURNS:
  148. //    associated numeric value
  149. //
  150. //  COMMENTS:
  151. //
  152. //    Will convert any hex ascii digit to its numeric counterpart.
  153. //    Puts in 0xff if not a valid hex digit.
  154. //
  155.  
  156. unsigned char BtoH(char ch)
  157. {
  158.     if (ch >= '0' && ch <= '9') return (ch - '0');        // Handle numerals
  159.     if (ch >= 'A' && ch <= 'F') return (ch - 'A' + 0xA);  // Handle capitol hex digits
  160.     if (ch >= 'a' && ch <= 'f') return (ch - 'a' + 0xA);  // Handle small hex digits
  161.     return(255);
  162. }
  163.  
  164. //
  165. //  FUNCTION: deregistername(char *)
  166. //
  167. //  PURPOSE:  Removes client's availability from other "AVAILABLE" clients
  168. //
  169. //  PARAMETERS:
  170. //    name - name to deregister
  171. //
  172. //  RETURNS:
  173. //    nothing
  174. //
  175. //  COMMENTS:
  176. //
  177. //    Between the time when a client connects to the server and when it starts
  178. //    a chat, the client is in "AVAILABLE" state.  It keeps of list of other
  179. //    available peers.  deregistername removes a previously available client
  180. //    from the lists of all the other available clients
  181. //
  182.  
  183. void deregistername(char * name)
  184. {
  185.     int j;
  186.  
  187.     // Set up message header
  188.     xferbuf.hdr.signature = MYSIGNATURE;
  189.     xferbuf.hdr.length = REALLEN(name) + HDRSIZE;
  190.     xferbuf.hdr.command = DEREGISTER_NAME;
  191.     // Message data is simply the name
  192.     lstrcpy(xferbuf.data, name);
  193.  
  194.     // Send to every connected client whose status is SOCKET_AVAILABLE
  195.     for (j = 0; j < NextFree; j++)
  196.     {
  197.         if (ConnectedSockets[j].status == SOCKSTAT_AVAILABLE)
  198.         {
  199.             senddatamessage(ConnectedSockets[j].sock, &xferbuf);
  200.         }
  201.     }
  202.     return;
  203. }
  204.  
  205. //
  206. //  FUNCTION: senddatamessage(SOCKET, LPDATAMSG)
  207. //
  208. //  PURPOSE:  Sends a message to a client
  209. //
  210. //  PARAMETERS:
  211. //    sock - Client socket to send data on
  212. //    sendbuf - message to send
  213. //
  214. //  RETURNS:
  215. //    TRUE - message sent
  216. //
  217. //  COMMENTS:
  218. //
  219. //    Performs send() until entire message is sent
  220. //
  221.  
  222. BOOL senddatamessage (SOCKET sock, LPDATAMSG sendbuf)
  223. {
  224.     int totalbytes, bytessent;
  225.    int thissendsize;
  226.  
  227.     bytessent = 0;                    // start count of bytessent
  228.     totalbytes = sendbuf->hdr.length; // read total bytes from message header
  229.     while(bytessent < totalbytes)
  230.     {
  231.         // We may not be able to send the entire message with a single
  232.       // send.  Therefore continue sending until the whole thing
  233.       // is gone.
  234.         if ((thissendsize = send(sock,
  235.                                 (char *)sendbuf + bytessent,
  236.                                 totalbytes - bytessent,
  237.                                 0)) == SOCKET_ERROR)
  238.       {
  239.            // Error
  240.           return FALSE;
  241.        }
  242.         bytessent += thissendsize;
  243.     }
  244.  
  245.     return TRUE;
  246. }
  247.  
  248. //
  249. //  FUNCTION: recvdatamessage(LPSOCKDATA, LPDATAMSG)
  250. //
  251. //  PURPOSE:  Receives a message from a client
  252. //
  253. //  PARAMETERS:
  254. //    lpSockdat - pointer to sockdata struct of socket we are receiving data on.
  255. //    recvbuf - buffer to receive data into.
  256. //
  257. //  RETURNS:
  258. //    TRUE - message received
  259. //    FALSE - message not received
  260. //
  261. //  COMMENTS:
  262. //    Performs recv() until entire message is received
  263. //
  264.  
  265. BOOL recvdatamessage (LPSOCKDATA lpSockdat, LPDATAMSG recvbuf)
  266. {
  267.     int readsize, totalbytesread, msgsize, bytesread;
  268.  
  269.     if (lpSockdat->type == SOCK_SEQPACKET)
  270.     {
  271.         // Message mode protocol!!  Just post one big read.
  272.         readsize = sizeof(*recvbuf);
  273.     }
  274.     else
  275.     {
  276.         // Stream mode protocol!!  Just read header...then read data (data size determined
  277.         // from header)
  278.         readsize = HDRSIZE;
  279.     }
  280.     if((totalbytesread = recv(lpSockdat->sock, (char *)recvbuf, readsize, 0)) == SOCKET_ERROR)
  281.     {
  282.         // ERROR  -- just return dropping message
  283.         return FALSE;
  284.     }
  285.  
  286.     // Check for my signature at the beginning of the message
  287.     if(recvbuf->hdr.signature != MYSIGNATURE)
  288.     {
  289.         // I've received some data that's in mid message--drop it
  290.         return FALSE;
  291.     }
  292.  
  293.     // Read size of message
  294.     msgsize = recvbuf->hdr.length;
  295.     readsize = msgsize - totalbytesread;
  296.  
  297.     while(totalbytesread < msgsize)
  298.     {
  299.         // we should only get hear for stream sockets
  300.         if((bytesread = recv(lpSockdat->sock,
  301.                              (char *)recvbuf + totalbytesread,
  302.                              readsize,
  303.                              0)) == SOCKET_ERROR)
  304.         {
  305.             if (WSAGetLastError() != WSAEWOULDBLOCK)
  306.             {
  307.                 // ERROR -- throw out message
  308.                 return FALSE;
  309.             }
  310.             // If you got a WSAWOULDBLOCK error, just keep trying...it shouldn't take
  311.             // too much longer for the rest of the message to get here.  Let's hope
  312.             // we don't hog the CPU so the data doesn't get to us.
  313.         }
  314.         totalbytesread += bytesread;
  315.         readsize -= bytesread;
  316.     }
  317.  
  318.     return TRUE;
  319. }
  320.  
  321. //
  322. //  FUNCTION: UpdateClientList(char *, int, char *)
  323. //
  324. //  PURPOSE:  Updates Client Status List box
  325. //
  326. //  PARAMETERS:
  327. //    name - name of client to update
  328. //    status - status of client to display
  329. //    peername - connected peer for status = IN_SESSION
  330. //
  331. //  RETURNS:
  332. //    nothing
  333. //
  334. //  COMMENTS:
  335. //    Finds and updates client status in listbox, or adds
  336. //    it if client is new
  337. //
  338.  
  339.  
  340. void UpdateClientList(char * name, int status, char * peername)
  341. {
  342.     int index;
  343.     char outtext[80];
  344.  
  345.     // Find list box entry with name in it
  346.     index = SendMessage(hwndClientList,
  347.                         LB_FINDSTRING,
  348.                         (WPARAM)-1,
  349.                         (LPARAM)name);
  350.  
  351.     // Delete the entry--we'll add a new entry below
  352.     SendMessage(hwndClientList, LB_DELETESTRING, index, 0);
  353.  
  354.     switch (status)
  355.     {
  356.         case SOCKSTAT_CLOSED:  // No new entry required.  Just return.
  357.             return;
  358.  
  359.        case SOCKSTAT_AVAILABLE:  // build available entry
  360.             wsprintf(outtext, GetStringRes(IDS_AVAILABLE), name);
  361.        break;
  362.  
  363.        case SOCKSTAT_INSESSION:  // build in_session entry
  364.             wsprintf(outtext, GetStringRes (IDS_SESSION), name, peername);
  365.        break;
  366.  
  367.        case SOCKSTAT_REQSESSION: // build reqsession entry
  368.             wsprintf(outtext, GetStringRes(IDS_SESSION_SETUP), name, peername);
  369.     }
  370.    // Add the new list box entry to the client status list box
  371.     SendMessage(hwndClientList, LB_ADDSTRING, 0, (LPARAM)&outtext);
  372. }
  373.  
  374. //
  375. //  FUNCTION: MakeServSock(HWND, LPSOCKDATA, LPPROTOCOL_INFO)
  376. //
  377. //  PURPOSE:  Attempts to initialize server side socket for listening
  378. //
  379. //  PARAMETERS:
  380. //    hwnd - handle to main window
  381. //    ServSocks - SOCKDATA struct to hold socket specific information
  382. //    lpProtBuf - PROTOCOL_INFO struct describing protocol to
  383. //                attempt listen on
  384. //
  385. //  RETURNS:
  386. //    TRUE - Listen successfully posted
  387. //    FALSE - Initialization failed
  388. //
  389. //  COMMENTS:
  390. //    Given the protocol triple in the PROTOCOL_INFO struct, we call
  391. //    socket(), then GetAddressByName(), then bind(), listen(), and
  392. //    finally WSAAsyncSelect() to get socket it appropriate state
  393. //    for listening.  If we fail at any point, then cleanup and do
  394. //    not use protocol
  395. //
  396.  
  397. BOOL MakeServSock(HWND hwnd, LPSOCKDATA ServSocks, LPPROTOCOL_INFO lpProtBuf)
  398. {
  399.    CSADDR_INFO CSABuf[10];
  400.    DWORD dwCSABufsize = sizeof(CSABuf);
  401.    GUID guidNW =  SVCID_NETWARE(NWCHATID);  // Macro generates GUID using hard coded NetWare Service Type
  402.    GUID guidDNS = SVCID_TCP(DNSCHATID);    // Macro generates GUID using hard coded port
  403.    LPSOCKADDR_NB lpNBaddr;
  404.  
  405.    // open socket using protocol defined by PROTOCOL_INFO structure
  406.    if ((ServSocks->sock = socket(lpProtBuf->iAddressFamily,
  407.                                        lpProtBuf->iSocketType,
  408.                                        lpProtBuf->iProtocol)) == SOCKET_ERROR)
  409.    {
  410.        // ERROR
  411.       return FALSE;  // This will reuse the current ServSocks structure
  412.    }
  413.  
  414.    // Populate SOCKDATA structure
  415.    ServSocks->status = SOCKSTAT_INIT;
  416.    ServSocks->type = lpProtBuf->iSocketType;
  417.    ServSocks->protocol = lpProtBuf->iProtocol;
  418.    ServSocks->currconnects = 0;
  419.    ServSocks->lpProtocolName = lpProtBuf->lpProtocol;
  420.  
  421.    // ***********************************************************************
  422.    //
  423.    //    Below we will be calling GetAddressByName with the RES_SERVICE
  424.    //    option in order to find the necessary sockaddr structure to use
  425.    //    for the following bind() call.  At this point, GetAddressByName()
  426.    //    only supports the DNS and the SAP/Bindery name spaces.  Ultimately
  427.    //    a single call to GetAddressByName will return information on
  428.    //    all available protocols, but this requires a central database
  429.    //    which is currently not available.  In the mean time we will make
  430.    //    name space specific calls to GetAddressByName.
  431.    //
  432.    // ***********************************************************************
  433.  
  434.    switch (lpProtBuf->iAddressFamily)
  435.    {
  436.        case AF_IPX:
  437.            // Use SAP/Bindery Name Space
  438.            if (GetAddressByName(0,    // Since GUID is name space specific, we don't need to specify distinct name space.
  439.                                 &guidNW,  // GUID defined by NetWare service type.
  440.                                 "GLOBAL CHAT SERVER",  // This parameter is actually not used for RES_SERVICE calls.
  441.                                 NULL,    // Since GUID implies the protocol, we don't need to specify a distinct one.
  442.                                 RES_SERVICE,  // Specifies that we are just looking for local address to bind to.
  443.                                 NULL,   // Currently not supported
  444.                                 CSABuf, // Structure which will hold information for bind() call
  445.                                 &dwCSABufsize,  // Size of CSABuf
  446.                                 NULL,   //  Currently not supported
  447.                                 NULL)   // Currently not supported
  448.                                 < 1)    // We need at least one address returned in order to bind()
  449.            {
  450.                // Error--cleanup
  451.             closesocket(ServSocks->sock);
  452.             return FALSE;
  453.            }
  454.  
  455.          // Copy sockaddr info and addresslength
  456.          memcpy(&ServSocks->addr, CSABuf[0].LocalAddr.lpSockaddr, sizeof(struct sockaddr));
  457.          ServSocks->addrlen = CSABuf[0].LocalAddr.iSockaddrLength;
  458.            break;
  459.  
  460.       case AF_INET:
  461.            // Use DNS Name Space
  462.            if (GetAddressByName(0,   // Since GUID is name space specific, we don't need to specify
  463.                                 &guidDNS,  // GUID defined by TCP port number
  464.                                 "globchat",  // This parameter is actually not used for RES_SERVICE calls
  465.                                 NULL,    // GUID implies protocol so no need to specify
  466.                                 RES_SERVICE,  // Specifies that we are trying to find local address to bind to
  467.                                 NULL,  // Currently not supported
  468.                                 CSABuf, // Results buffer
  469.                                 &dwCSABufsize,   // Size of results buffer
  470.                                 NULL,    // Not supported
  471.                                 NULL)    // Not supported
  472.                                 < 1)   // Need at least one address returned in order to bind()
  473.            {
  474.                // Error -- cleanup
  475.             closesocket(ServSocks->sock);
  476.             return FALSE;
  477.            }
  478.          // Copy sockaddr info and address length
  479.          memcpy(&ServSocks->addr, CSABuf[0].LocalAddr.lpSockaddr, sizeof(struct sockaddr));
  480.          ServSocks->addrlen = CSABuf[0].LocalAddr.iSockaddrLength;
  481.            break;
  482.  
  483.        case AF_NETBIOS:
  484.            // no netbios name space provider so we will simulate GetAddressByName() results
  485.          lpNBaddr = (LPSOCKADDR_NB)&ServSocks->addr;
  486.            SET_NETBIOS_SOCKADDR(lpNBaddr,
  487.                                 NETBIOS_GROUP_NAME,
  488.                                 "GLOBSERV",
  489.                                 0);
  490.          ServSocks->addrlen = lpProtBuf->iMaxSockAddr;
  491.            break;
  492.  
  493.    }
  494.  
  495.    // We have an address to bind() to, so do it!
  496.    if (bind(ServSocks->sock,
  497.             &ServSocks->addr,
  498.            ServSocks->addrlen) == SOCKET_ERROR)
  499.    {
  500.        // Error -- cleanup
  501.       closesocket(ServSocks->sock);
  502.       return FALSE;
  503.    }
  504.  
  505.    // Find out the specific local address
  506.    if (getsockname(ServSocks->sock,
  507.                    &ServSocks->addr,
  508.                    &ServSocks->addrlen) == SOCKET_ERROR)
  509.    {
  510.        // Error -- better cleanup
  511.        closesocket(ServSocks->sock);
  512.       return FALSE;
  513.    }
  514.  
  515.    // Listen on the socket
  516.    if (listen(ServSocks->sock, 5) == SOCKET_ERROR)
  517.    {
  518.        // Error -- cleanup
  519.        closesocket(ServSocks->sock);
  520.       return FALSE;
  521.    }
  522.  
  523.    // Set up window message to indicate connection
  524.    if (WSAAsyncSelect(ServSocks->sock,
  525.                       hwnd,
  526.                      MW_CONNECTED,
  527.                   FD_ACCEPT) == SOCKET_ERROR)
  528.    {
  529.        // Error -- cleanup
  530.       closesocket(ServSocks->sock);
  531.       return FALSE;
  532.    }
  533.  
  534.    // Success!!!
  535.    return TRUE;
  536. }
  537.  
  538. //---------------------------------------------------------------------------
  539. //
  540. // FUNCTION:    GetStringRes (int id INPUT ONLY)
  541. //
  542. // COMMENTS:    Load the resource string with the ID given, and return a
  543. //              pointer to it.  Notice that the buffer is common memory so
  544. //              the string must be used before this call is made a second time.
  545. //
  546. //---------------------------------------------------------------------------
  547.  
  548. LPTSTR GetStringRes (int id)
  549. {
  550.   static TCHAR buffer[MAX_PATH];
  551.  
  552.   buffer[0]=0;
  553.   LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  554.   return buffer;
  555. }
  556.