// Implementation of WSock


#include <stdio.h>


#ifndef GLOBAL_DECL

#include "global.h"     // Global data types and variables

#endif


// Needed for some versions of compiler:

// enum bool { false, true };


// Constructor - Initialise WinSock and Bind Socket

wsock::wsock(config* setts)
{
    settings = setts;

    crlf[0] = 13;
    crlf[1] = 10;
    crlf[2] = 13;
    crlf[3] = 10;
    crlf[4] = NULL;
    error[0] = NULL;
    
    // Set timeout and retry settings

    timeout_val = settings -> timeout;  // Timeout in seconds

    retry_val = settings -> retries;
    // Set proxy settings

    use_proxy = settings -> use_proxy;
    strcpy(proxy_addr, settings -> proxy_addr);
    proxy_port = settings -> proxy_port;

    wsock::initialise();
}


// Initialise Winsock

void wsock::initialise()
{
    WORD    requested;
    WSADATA data;
    int     wserror;
    
    requested = MAKEWORD( 2, 0 ); 
    wserror = WSAStartup( requested, &data );
    if ( wserror != 0 )
    {
      strcpy( error, "ERROR - Couldn't find a WinSock DLL." );
    } 
    // Confirm that the WinSock DLL supports 2.0.

    if ( LOBYTE( data.wVersion ) != 2 ||
            HIBYTE( data.wVersion ) != 0 )
    {
        strcpy( error, "ERROR - Couldn't find a usable WinSock DLL." );
        WSACleanup();
    }
}

// Destructor - Close socket and shut down Winsock

wsock::~wsock()
{   
    WSACleanup();
}

// Check that domain is valid

bool wsock::check_valid( char* address )
{
    LPHOSTENT host_info;        // Information about host


    if ( ( serv_addr.sin_addr.s_addr = inet_addr( address ) ) == INADDR_NONE)
        if ( ( host_info = gethostbyname( address ) ) == NULL )
        {
            strcpy( error, "ERROR - Server does not have a DNS entry." );
            return false;
        }
        else
        {
            if (use_proxy)
                if ( ( host_info = gethostbyname( proxy_addr ) ) == NULL )
                {
                    strcpy( error, "ERROR - Proxy Server does not have a DNS entry." );
                    return false;
                }
            memcpy ( &( serv_addr.sin_addr ), host_info -> h_addr,
                        host_info -> h_length );
        }

    return true;
}

// Create and bind socket

bool wsock::create_socket( void )
{
    SOCKADDR_IN cli_addr;       // Client address

    
    // Set up client-side address

    cli_addr.sin_family = AF_INET;
    cli_addr.sin_port = 0;
    cli_addr.sin_addr.s_addr = INADDR_ANY;

    // Create socket

    my_socket = socket ( AF_INET, SOCK_STREAM, 0 );
    if ( my_socket == INVALID_SOCKET )
    {
        strcpy( error, "ERROR - Couldn't create socket." );
        return false;
    }

    // Bind socket to client address

    if ( bind ( my_socket, (LPSOCKADDR) &cli_addr,
                sizeof( cli_addr ) ) == SOCKET_ERROR )
    {
        strcpy( error, "ERROR - Unable to bind socket." );
        return false;
    }
    return true;
}

// Connect to server

bool wsock::make_connection( void )
{
    serv_addr.sin_family = AF_INET;
    if (use_proxy)
        serv_addr.sin_port = htons( proxy_port );
    else
        serv_addr.sin_port = htons( 80 );

    if ( connect ( my_socket, (LPSOCKADDR) &serv_addr,
                                sizeof( serv_addr ) ) == SOCKET_ERROR )
        {
            strcpy( error, "ERROR - Unable to connect to server." );
            return false;
        }
    return true;
}

bool wsock::get_page( char* address, char* fname )
{
    char domain_name[100];              // Just the domain part of address

    int characters;                     // Number of characters in domain

    char command[200];                  // Command to send to server


    // Create socket and bind it locally

    if ( !wsock::create_socket() )
        return false;

    // Seperate domain name from user input

    sscanf( address, "http://%s", domain_name );
    characters = strcspn( domain_name, "/" );
    domain_name[characters] = NULL;

    if ( !wsock::check_valid( domain_name ) ) // Check that domain is valid

        return false;

    if ( !wsock::make_connection() )                    // Connect to server

        return false;

    // Create string to send to server

    strcpy( command, "GET " );
    strcat( command, address );
    strcat( command, " HTTP/1.0" );
    strcat( command, crlf );

    if ( !wsock::send_command( command ) )      // Send a string to the server

        return false;

    wsock::get_reply( fname );                      // Get reply from server


    closesocket( my_socket );
    
    return true;
}

// Send a string to the server

bool wsock::send_command( char* command )
{
    char*   ptr;            // Pointer to current position in string

    int     bytes_sent;     // # of bytes sent

    int     length;         // Length of string to send

    
    ptr = command;
    length = strlen( command );

    do
    {
        bytes_sent = send ( my_socket, ptr, length, 0 );
        if ( bytes_sent == SOCKET_ERROR )
        {
            strcpy( error, "ERROR - Error while sending data." );
            return false;
        }
        ptr += bytes_sent;
        length -= bytes_sent;
    } while ( length > 0 );
    return true;
}

// Get the reply from the server

bool wsock::get_reply( char* fname )
{
    char    character;                  // Character recieved

    char    prev_chars[5];              // Previous characters received

    int     bytes_rcvd;                 // # of bytes received

    int     result;                     // Result of select statement

    fd_set  iadd_set;                   // Set of sockets used in select

    timeval timeout;                    // Timeout variable

    FILE    *fp;                        // File pointer

    bool    is_header;                  // true if receiving header, else false

    int     retries;                    // Retries remaining

    int     loop;

    FD_ZERO ( &iadd_set );              // Initialise empty set of ports

    FD_SET ( my_socket, &iadd_set );    // Add to set of sockets

    timeout.tv_sec = timeout_val;       // Set timeout value

    timeout.tv_usec = 0;
    retries = retry_val;                // Set number of retries

    is_header = true;                   // Receiving header flag


    do
    {
        result = select ( 0, &iadd_set, 0, 0, &timeout );
    
    } while ((result == 0) && (retries-- > 0));
    
    if (result != 0)
    {
        // Open file to save downloaded data to

        if ( ( fp = fopen(fname, "w") ) == NULL )
        {
            strcpy( error, "ERROR - Unable to create file." );
            return false;
        }

        // Receive a character from server

        bytes_rcvd = recv ( my_socket, &character, 1, 0 );

        while ( bytes_rcvd != 0 )
        {
            // Buffer characters and check if at end of header

            for ( loop=0; loop<3; loop++)
                prev_chars[loop] = prev_chars[loop+1];
            prev_chars[3] = character;
            prev_chars[4] = NULL;
            if ( !strcmp(prev_chars, crlf) )
                is_header = false;
    
            if ( !is_header )                   // Not In header

                fputc( character, fp );

            if ( bytes_rcvd == SOCKET_ERROR )
            {
                strcpy( error, "ERROR - Error while receiving data." );
                return false;
            }
            
            // Receive a character from server

            bytes_rcvd = recv ( my_socket, &character, 1, 0 );
        }

        fclose( fp );   // Close file

    }
    else
    {
        strcpy( error, "ERROR - Connection timed out." );
        return false;
    }

    return true;
}

syntax highlighted by Code2HTML, v. 0.8.11