/************************************************************************
* WebClient.cpp                                                         *
*                                                                       *
* Purpose:                                                              *
*       This program will connect to the ICS device via Ethernet and    *
*       then manipulate an internal configuration variable via the ICS  *
*       port-80 web service.                                            *
*                                                                       *
*       First it performs a modify and read-back. It will modify the    *
*       configured static IP value and do a read-back of the value      *
*       in the same command. Note that it is not needed to do the read  *
*       after write. It is only done here to show that it can be done   *
*       and how to do multiple commands in a single communication.      *
*                                                                       *
*       It then does a simple query of a variable. Note that multiple   *
*       variables can be queried (and/or set) in a single command.      *
*                                                                       *
* NOTES:                                                                *
*       It is assumed that the reader is either able to do Windows      *
*       socket coding and/or able to translate the code into VISA RAW   *
*       socket function calls (or an equivilent I/O library).           *
*                                                                       *
*       All HTML services execute the same way and the socket is only   *
*       open for the duration of the send/read cycle. It is therefore   *
*       good programming to do all commands in a single connection      *
*       session rather than having multiple sessions. An HTML session   *
*       consists of the following states.                               *
*                                                                       *
*           Client establishes a connection                             *
*           Client sends the URL                                        *
*           Servince sends all requested data                           *
*           Socket is closed (by both sides)                            *
*                                                                       *
*       The ICS web service allows setting of in-memory configuration   *
*       parameters. This means that once the unit is rebooted, the      *
*       changes will no longer be set since the unit will revert back   *
*       to the current saved configuration parameters. If you wish to   *
*       save the changes then you must issue a FLASH command. After the *
*       save is performed, you may then reboot the unit using either    *
*       the REBOOT or SUBMIT commands. The REBOOT will immediately do   *
*       a reboot without sending any return data. The SUBMIT will also  *
*       do a reboot but will first send a short text message saying the *
*       reboot is about to happen. Following is the text message sent:  *
*       Rebooting in process. Please wait 5 seconds and then reconnect. *
*                                                                       *
*       Some configuration changes are immediate and some will only     *
*       take place following a reboot. It is recommended that a FLASH   *
*       and REBOOT is always done following any configuration changes.  *
*                                                                       *
*       Some models of the ICS Ethernet devices may add a 501.HTML page *
*       after the requested data. To use such a device look for the two *
*       blank lines (containing only CR/LF) sequences. The first will   *
*       preceed the requested data and the second will follow any       *
*       requested data.                                                 *
*                                                                       *
*       If you are executing a command and not doing a read-back then   *
*       it is not required to actually perform a read. You may simply   *
*       close the socket after you have sent the command/data. The same *
*       is true if your unit is one that does a 501.HTML page (see the  *
*       prior note) in that you are not required to actually read until *
*       the end of data.                                                *
*                                                                       *
*       If the URL sent is command only and does not contain a query,   *
*       then the returned data will be empty of actual data (but will   *
*       contain the HTML header information). In such a case the socket *
*       may be closed immediately following the sending of the data     *
*       without waiting for a reply.                                    *
*                                                                       *
*       Performing an excessive number of open/close cycles on the      *
*       Ethernet within a short time is not good programming practice.  *
*       This is especially true with embedded devices which have very   *
*       limited TCP/IP stack resources. If you need to perform more     *
*       than a couple of Ethernet sessions (socket open/close cycles),  *
*       then it is recommended to wait a period of time (15-20 seconds) *
*       after each half dozen sessions, or if you observe a lengthy     *
*       period waiting for a connection.                                *
*                                                                       *
* Normal Output:                                                        *
*   Sending command string to set the new value                         *
*   Receiving any/all data                                              *
*   =============                                                       *
*   HTTP/1.1 200 OK                                                     *
*   Content-Length: 13                                                  *
*   Content-Type: text/plain                                            *
*   Connection: close                                                   *
*                                                                       *
*   192.168.0.254                                                       *
*                                                                       *
*   =============                                                       *
*   All data received                                                   *
*   Sending command string to read the value                            *
*   Receiving any/all data                                              *
*   =============                                                       *
*   HTTP/1.1 200 OK                                                     *
*   Content-Length: 13                                                  *
*   Content-Type: text/plain                                            *
*   Connection: close                                                   *
*                                                                       *
*   192.168.0.254                                                       *
*                                                                       *
*   =============                                                       *
*   All data received                                                   *
*                                                                       *
************************************************************************/


#include "stdafx.h"



int     connected;



//
// Locate the actual server IP based on its IP/name
//
//  RETURNS:
//      actual IP of the IP/hostname string
//      0 if unable to resolve the IP/hostname
//
static  long  getAddr (char server[])

	{
	unsigned  long  ip;
	LPHOSTENT       host;


    ip = inet_addr (server);

    if (ip == INADDR_NONE)
        {
        host = gethostbyname (server);

        if (host)
            ip = *((unsigned long FAR *) (host->h_addr));
        else
            {
            // Unable to resolve the IP/hostname, so return a 0
            ip = 0;
			}
		}

	return (ip);
	}



//
// Initialize the socket layer and open a socket.
//
// RETURNS:
//      INVALID_SOCKET if error
//      socket if no error
//
static  SOCKET  openSocket (char server[], int port)

    {
    int     retval;
    struct	sockaddr_in socka;
    SOCKET  sock;


    sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (sock != INVALID_SOCKET)
        {
        memset (&socka, 0, sizeof(socka));
        socka.sin_family = AF_INET;
        socka.sin_port = ntohs ((unsigned short) port);
        socka.sin_addr.S_un.S_addr = getAddr (server);


        // Perform the initial connection attempt. This should generate a
        // WSAWOULDBLOCK error since the socket is set to non-blocking mode.
        retval = connect (sock, (struct sockaddr *) &socka, sizeof(socka));

        if (retval == SOCKET_ERROR)
            {
            WSACleanup ();
            sock = INVALID_SOCKET;
            }
        }

    return (sock);
    }



int  GetChar (SOCKET sock, char *c)

    {
    int     err, len, loop, retval;


    for (loop= retval= 0; !retval; )
        {
        len = recv (sock, (char *) c, 1, 0);

        if ((len == SOCKET_ERROR) || !len)
            {
            err = WSAGetLastError ();

            if ((err == WSAEWOULDBLOCK) || !len)
                {
                Sleep (10);

                ++loop;

                if (loop > 10)
                    retval = -1;
                }
            else
                {
                closesocket (sock);
                connected = 0;
                retval = -2;
                }
            }
        else if (len == 1)
            retval = 1;
        }

    if (retval < 0)
        {
        connected = 0;
        closesocket (sock);
        }

    return (retval);
    }



int  GetLine (SOCKET sock, char buf[], int maxlen)

    {
    int     i, err, done, retval;


    for (i= retval= done= 0; (i < maxlen) && !retval && !done; )
        {
        err = GetChar (sock, &buf[i]);

        if (err == 1)
            {
            if (i >= 1)
                {
                if (buf[i] == '\n')
                    {
                    done = 1;
                    buf[i+1] = '\0';

                    retval = (int) strlen (buf);
                    }
                }

            ++i;
            }
        else if (err < 1)
            {
            retval = -1;
            }
        }

    return (retval);
    }



void  FlushReceivePipe (SOCKET sock)

    {
    int     len;
    char    buf[256];


    memset (buf, 0, sizeof(buf));
    len = GetLine (sock, buf, sizeof(buf));

    if (len > 0)
        printf ("%s", buf);
    }



void  SendIt (SOCKET sock, char buf[])

    {
    int     i, j, len= (int) strlen (buf);


    if (len)
        {
        for (i= 0; i < len; )
            {
            j = send (sock, &buf[i], len-i, 0);

            if (j != (len-i))
                printf ("ERROR\n");
            else
                i += j;
            }
        }

    send (sock, "\r\n", 2, 0);
    }




int _tmain(int argc, _TCHAR* argv[])

    {
    WORD    vers;
    WSADATA WSAData;
    SOCKET  sock;


    // Initialize the Ethernet layer
    vers = MAKEWORD (2,0);

    if (WSAStartup(vers, &WSAData))
        return (0);

    // First we're going to set the variable to a known value. This is done by
    // first setting the value, then reading the value. If you wish only to read
    // the variable, that is done lower down in the next example.
    sock = openSocket ("192.168.0.254", 80);

    if (sock == INVALID_SOCKET)
        {
        printf ("Error connecting to 192.168.0.254:80\n");
        exit (1);
        }

    printf ("Sending command string to set the new value\n");
    SendIt (sock, "GET /cgi?IP=192.168.0.254&IP&\x0D\x0A");

    printf ("Receiving any/all data\n=============\n");

    // Receive any/all data sent to us until the socket is closed.
    connected = 1;

    do  {
        FlushReceivePipe (sock);
        }
    while (connected);

    printf ("=============\nAll data received\n");

    // Now we will query the example to see what the value is set to.
    sock = openSocket ("192.168.0.254", 80);

    if (sock == INVALID_SOCKET)
        {
        printf ("Error connecting to 192.168.0.254:80\n");
        exit (1);
        }

    printf ("Sending command string to read the value\n");

    SendIt (sock, "GET /cgi?ip&\x0D\x0A");

    printf ("Receiving any/all data\n=============\n");

    // Receive any/all data sent to us until the socket is closed.
    connected = 1;

    do  {
        FlushReceivePipe (sock);
        }
    while (connected);

    printf ("=============\nAll data received\n");

    return 0;
    }

