/*
 * Sock.c -- Windows sockets sample application
 *
 * Implements psuedo finger client and server.
 * Should work with any UNIX finger daemon
 *
 */



#include "windows.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <winsock.h>
#include "dlg.h"


#define PROMPT_STRING       130
#define PROMPT_TEXT         131

#define CONN_MSG        (WM_USER+25)
#define	FNGR_READ		(WM_USER+26)

extern void errormsg(LPSTR cp);
extern int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int);
extern BOOL InitApplication(HANDLE);
extern BOOL InitInstance(HANDLE, int);
extern long FAR PASCAL MainWndProc(HWND, unsigned, WORD, LONG);
extern BOOL FAR PASCAL About(HWND, unsigned, WORD, LONG);
extern long FAR PASCAL SockWndProc(HWND hWnd, unsigned message, WORD wParam, LONG lParam);
extern char *prompt(HWND hWnd, HANDLE hInst, char *message, char *buf);


/*  Global buffers  */
char buff[4096];
char msg[80], name[125];

HANDLE hInst;
HWND hWnd;
struct sockaddr_in anaddr;
int whoport, fngrport;		/*  Well known port numbers  */
int app_closing;

/*  Sockets  */
SOCKET fclient = INVALID_SOCKET;
SOCKET fserver = INVALID_SOCKET;
SOCKET fconnect = INVALID_SOCKET;


/*
 * standard windows initialization routines
 */
BOOL InitApplication(HANDLE hInstance)
    {
    WNDCLASS  wc;

   	buff[0] = '\0';

    wc.style = NULL;
    wc.lpfnWndProc = SockWndProc;

    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "SockMenu";
    wc.lpszClassName = "SockWClass";

    return(RegisterClass(&wc));
    }

BOOL InitInstance(HANDLE hInstance, int nCmdShow)
    {

    hInst = hInstance;

    /* Create a main window for this application instance.  */

    hWnd = CreateWindow("SockWClass", "Sockets Sample Application",
    						WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
    						CW_USEDEFAULT, CW_USEDEFAULT,
    						CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

    if (!hWnd)
    	return (FALSE);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return (TRUE);
    }


int PASCAL
WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
    MSG msg;
    FARPROC lpfn;
    WSADATA wskData;

    if (!hPrevInstance)
		if (!InitApplication(hInstance))
			return(FALSE);

    if (!InitInstance(hInstance, nCmdShow))
		return(FALSE);

    /*
     * register application with windows sockets dll
     */

	if(!WSAStartup(1, &wskData) && (wskData.wVersion == 1))
		{

	    while (GetMessage(&msg, NULL, NULL, NULL))
			{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			if (app_closing)
				{
				/* The application has been requested to shut down. Continue processing
				* the close request by calling DestroyWindow  */
				DestroyWindow(hWnd);
				}
			}

		/* Close any open sockets */
		if(fclient != INVALID_SOCKET)
			closesocket(fclient);
		if(fserver != INVALID_SOCKET)
			closesocket(fserver);
		if(fconnect != INVALID_SOCKET)
			closesocket(fconnect);

		/*  Let windows sockets know we're done */
	    WSACleanup();
		return(msg.wParam);
	    }

    return(0);
    }


BOOL FAR PASCAL About(HWND hDlg, unsigned message, WORD wParam, LONG lParam)
    {
    switch (message)
	    {
		    case WM_INITDIALOG:
				return (TRUE);

		    case WM_COMMAND:
				if (wParam == IDOK || wParam == IDCANCEL)
					{
					EndDialog(hDlg, TRUE);
					return (TRUE);
					}
				break;
	    }
    return (FALSE);
    }


long FAR PASCAL
SockWndProc(HWND hWnd, unsigned message, WORD wParam, LONG lParam)
    {
    int len, cnt;
    RECT wRect;
    PAINTSTRUCT ps;
    HDC hdc;
    struct hostent FAR *hp;
    static HANDLE arrow, hrGlass, hMenu;

    switch (message)
		{
		case WM_CREATE:
			hMenu = GetMenu(hWnd);
			arrow = LoadCursor(NULL, IDC_ARROW);
			hrGlass = LoadCursor(NULL, IDC_WAIT);
			SetCursor(arrow);
			break;

	    /*
	     * If user presses control-c, interpret as an interrupt request.
	     * Could also provide a menu item or a dialogbox with a "cancel"
	     * button.
	     *
	     */
	    case WM_CHAR:
			if (wParam == 3/* ctrl-c */)
				{
				WSACancelBlockingCall();
				strcpy(buff, "Call cancelled!!");
				InvalidateRect(hWnd, NULL, 1);
				}
			else
				return(DefWindowProc(hWnd, message, wParam, lParam));
			break;

        /* Update the window */
        case WM_PAINT:
        	hdc = BeginPaint(hWnd, &ps);

        	/* Use fixed fontand expand tabs so tabs align */
        	SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
        	GetClientRect(hWnd, &wRect);
        	DrawText(hdc, buff, -1, &wRect, DT_LEFT | DT_EXPANDTABS);
        	EndPaint(hWnd, &ps);
        	break;

        /* Menu item selected, wParam == menu item ID  */
	    case WM_COMMAND:
			switch (wParam)
				{
				/* Display About Box */
				case IDM_ABOUT:
				    {
				    FARPROC lpfn;

				    lpfn = MakeProcInstance(About, hInst);
				    DialogBox(hInst, "AboutBox", hWnd, lpfn);
				    FreeProcInstance(lpfn);
				    break;
				    }

                /* Finger client utility */
				case FINGER:
                	/*  Get user name  */
                	if(prompt(hWnd, hInst, "Hostname:", msg) == NULL)
                		break;

                    /*  Look up the host address  */
                    if((hp = gethostbyname(msg)) == NULL)
                    	{
                    	errormsg("Unknown host");
                    	break;
                    	}
					_fmemcpy((char FAR *) &anaddr.sin_addr.s_addr, hp->h_addr, hp->h_length);

                    /*  Get username to finger */
                    if(!prompt(hWnd, hInst, "Finger User:", msg))
                    	strcpy(msg, "");

					/*  Look up port number */
					if(fngrport == 0)
						{
						struct servent FAR *sp;

						if((sp = getservbyname("finger", "tcp")) == NULL)
							{
							errormsg("Cannot determine port number for finger daemon.");
							break;
							}
						fngrport = htons(sp->s_port);
						}

                    /* create socket */
					if((fclient = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
						{
						errormsg("Unable to create a socket");
						}
					else
						{
						/*  Fill in address to which we will connect */
						anaddr.sin_family = PF_INET;
						anaddr.sin_port = htons(fngrport);

						/*  try to connect */
						if(connect(fclient, (struct sockaddr FAR *) &anaddr, sizeof(struct sockaddr_in)))
							{
							errormsg("Unable to connect to finger daemon.");
							closesocket(fclient);
							fclient = INVALID_SOCKET;
							}
						else
							{
							strcat(msg, "\r\n");
							if(send(fclient, msg, strlen(msg), 0) < (int) strlen(msg))
								{
								errormsg("Error sending string to finger daemon");
								}
							else
								if((cnt = recv(fclient, buff, sizeof(buff), 0)) == SOCKET_ERROR)
									{
									errormsg("Error reading data from daemon");
									}
								else
									if(cnt == 0)
										errormsg("No data received from finger daemon");
                                	else
                                		{
										buff[cnt] = '\0';
										InvalidateRect(hWnd, NULL, 1);
										}
							}
						closesocket(fclient);
						fclient = INVALID_SOCKET;
						}
					break;

				case FINGER_SRV:
					/*  Look up port number */
					if(fngrport == 0)
						{
						struct servent FAR *sp;

						if((sp = getservbyname("finger", "tcp")) == NULL)
							{
							errormsg("Cannot determine port number for finger daemon.");
							break;
							}
						fngrport = htons(sp->s_port);
						}

                    /* if not acting as server, start */
					if(fserver == INVALID_SOCKET)
						{
	                	/*  Get user name  */
	                	if(prompt(hWnd, hInst, "Real Name:", msg) == NULL)
	                		break;
	                	else
	                		wsprintf(name, "In Real Life: %s\r\n", (LPSTR) msg);

						/*  allocate socket  */
						if((fserver = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
							{
							errormsg("Cannot allocate socket for finger server");
							break;
							}

						/* Bind our socket to the finger port number */
						anaddr.sin_port = htons(fngrport);
						anaddr.sin_addr.s_addr = INADDR_ANY;
						anaddr.sin_family = PF_INET;
						if(bind(fserver, (struct sockaddr FAR *) &anaddr, sizeof(anaddr)))
							{
							errormsg("Error binding to finger daemon port");
							closesocket(fserver);
							fserver = INVALID_SOCKET;
							break;
							}

					    /*  listen for connections and ask for a message when they come in */
						if(listen(fserver, 5) || WSAAsyncSelect(fserver, hWnd, CONN_MSG, FD_ACCEPT))
							{
							errormsg("Error trying to listen on finger server socket");
							closesocket(fserver);
							fserver = INVALID_SOCKET;
							}
						else
							CheckMenuItem(hMenu, FINGER_SRV, MF_BYCOMMAND | MF_CHECKED);
						}
					else 	/*  Already acting as server, stop  */
						{
						closesocket(fserver);
						fserver = INVALID_SOCKET;
						CheckMenuItem(hMenu, FINGER_SRV, MF_BYCOMMAND | MF_UNCHECKED);
						}
					break;

                case CANCEL:	/* Cancel any outstanding blocking call (send or recv) */
                	WSACancelBlockingCall();
					strcpy(buff, "Call cancelled!!");
					InvalidateRect(hWnd, NULL, 1);
                	break;

				case QUIT:
					SendMessage(hWnd, WM_CLOSE, 0, 0L);
					break;

				default:
				    return (DefWindowProc(hWnd, message, wParam, lParam));
				}
		break;

        case CONN_MSG:		/* Connection on finger server  */
        	if(WSAGETSELECTERROR(lParam))
        		errormsg("Error listening for finger connections");
        	else
        		{
	        	/* accept connection */
	        	len = sizeof(struct sockaddr_in);
	        	fconnect = accept(fserver, (struct sockaddr FAR *) &anaddr, &len);
	        	if(fconnect == INVALID_SOCKET)
	        		errormsg("Error accepting connection to finger server");
	        	else
	        		WSAAsyncSelect(fconnect, hWnd, FNGR_READ, FD_READ);
	        	}
	        break;

	     case FNGR_READ:	/*  Data available for reading  */
    		{
    		/* The finger client will send us a string but we will ignore it */
    		cnt = recv(fconnect, buff, sizeof(buff), 0);
    		buff[0] = '\0';
    		if(cnt == SOCKET_ERROR)
    			errormsg("Error reading from finger client");
    		else
    			if(send(fconnect, name, strlen(name), 0) < (int) strlen(name))
    				errormsg("Error sending data to finger client");

    		/* Close up the accepted connection */
    		closesocket(fconnect);
    		fconnect = INVALID_SOCKET;
    		}
        	break;

	    case WM_CLOSE:
			/* Application's main window is closing. Set flag to notify main loop
			 * The main function is responsible for handling the close
			 */
			app_closing = 1;
			break;

	    case WM_DESTROY:
			PostQuitMessage(0);
			break;

	    default:
			return (DefWindowProc(hWnd, message, wParam, lParam));
	    }
    return (NULL);
    }



char PromptText[41];       /* prompt message text for prompt dlg */
char PromptString[81];     /* user's input from prompt dlg */

/*
 * PromptDlgProc - Processes the "prompt" dialog box
 *
 * globals:  PromptText - prompt message text
 *           PromptString - user's input
 * return: IDOK or IDCANCEL
 */
BOOL FAR PASCAL
PromptDlgProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam)
    {
    switch (message)
	    {
	    case WM_COMMAND:
			if (wParam == IDOK)
				{
				GetDlgItemText(hDlg, PROMPT_STRING, PromptString, 80);
				EndDialog(hDlg, IDOK);
				return TRUE;
				}
			else if (wParam == IDCANCEL)
				{
				EndDialog(hDlg, IDCANCEL);
				return TRUE;
				}
				return FALSE;
			break;

	    case WM_INITDIALOG:
			SetDlgItemText(hDlg, PROMPT_TEXT, PromptText);
			return(TRUE);
			break;

	    default:
			return FALSE;
	    }
    }

/*
 * prompt - displays message, copies user response into buf
 *
 * return: NULL for cancel, or FAR *  to buf
 */
char *prompt(HWND hWnd, HANDLE hInst, char *message, char *buf)
    {
    int rc;
    FARPROC   lpDlgProc;

    lstrcpy(PromptText, message);

    lpDlgProc = MakeProcInstance(PromptDlgProc, hInst);
    rc = DialogBox(hInst, "PROMPT", hWnd, lpDlgProc);
    FreeProcInstance(lpDlgProc);

    if (rc == IDOK)
    	lstrcpy(buf, PromptString);
    else
    	buf = NULL;
    return buf;
    }

/*
 * errormsg - displays an error message and the last error
 *
 */
void errormsg(LPSTR cp)
{
    char buf[128];

    wsprintf(buf, "%s (%d)", (LPSTR)cp, WSAGetLastError());
    MessageBox(hWnd, buf, NULL, MB_ICONHAND);
}
