/*  QFXClnt.c	 8-Jan-03 10:47:43 by Kevin D. Quitt

    QFX command-line client.
*/
#include    <stdio.h>
#include    <string.h>
#include    <stdlib.h>
#include    <time.h>
#include    <sys/stat.h>

#ifdef	WIN32
#undef	WIN32
#define	WIN32	1
#endif

#ifdef	VMS
#undef	VMS
#define	VMS	1
#endif


#if	WIN32
#include <fcntl.h>
#include <io.h>
#include <conio.h>
#include <ctype.h>

typedef unsigned long   ulong;
typedef unsigned int	uint;
typedef unsigned char	uchar;

#elif	VMS
#include <in.h>
#include <inet.h>
#include <netdb.h>
#include <types.h>
#include <socket.h>
#include <tcp.h>
#include <unixio.h>
#include <ucx$inetdef.h>
#include <iodef.h>
#include <unixlib.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ctype.h>
#endif



/*  Include Windows specific include files
*/
#ifdef WIN32
#include <windows.h>
#include <winsock.h>
#else
typedef int BOOL;
#endif


#ifndef	TRUE
#define	TRUE (1==1)
#define	FALSE (1==0)
#endif

#ifdef	MAC
#define	BIGENDIAN
#endif

#ifdef	BIGENDIAN
#define TKNAME(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d))
#else
#define TKNAME(a,b,c,d) (((d)<<24)|((c)<<16)|((b)<<8)|(a))
#endif


#if WIN32
#define	CloseSocket(s)	closesocket(s)
#else
#define	CloseSocket(s)	close(s)
#endif

typedef unsigned long	ulong;

#include    "qfxdef.h"


void DisplayHelpAndExit( void )
  {
    fputs( "\nqfxclnt TargetIPAddress {INFO|SEND) filespec\n", stderr );
    exit( EXIT_SUCCESS );
  }


int establish( char *host )
  {
    int			status	= 0;
    struct hostent     *phe;
    struct sockaddr_in  remote_sin;
    int			sock;

#ifdef WIN32
    {
      WSADATA	WSAData;
      status	= WSAStartup( MAKEWORD( 2,0 ), &WSAData );
      if ( status )
	{
	  fprintf( stderr, "Requested socket version not supported : %d\n",
		   status );
	  exit( EXIT_FAILURE );
	}
    }
#endif

    sock    = socket( AF_INET, SOCK_STREAM, 0 );
    if ( sock  ==  -1 )
      {
	fputs( "Error creating socket\n", stderr );
	exit( EXIT_FAILURE );
      }

    phe	= gethostbyname( host);

    if ( !phe )
      {
	fprintf( stderr, "DNS lookup failed for host: %s\n", host);
	CloseSocket( sock );
	exit( EXIT_FAILURE );
      }

    memmove((void *)&remote_sin.sin_addr, phe->h_addr, phe->h_length );

    remote_sin.sin_port	    = htons( QFXPORT );
    remote_sin.sin_family   = AF_INET;

    status  = connect( sock, (struct sockaddr *)&remote_sin,
		       sizeof remote_sin );
    if ( status  ==  -1 )
      {
	fprintf( stderr, "Error connecting to %s\n", host );
	CloseSocket( sock );
	exit( EXIT_FAILURE );
      }

    return  sock;
  }

void SendPacket( int sock, char *pkt, size_t size )
  {
    int length = 0;
    size_t sent   = 0;
    int tosend;

    while( size  !=  sent )
      {
	tosend	= size - sent;
	length	= send( sock, (const char *)(pkt + sent), tosend, 0 );
	if( length  <  0 )
	  {
	    printf( "\nError sending..\n");
	    return;
	  }
	sent += length;
      }
  }


/*  Recvpacket is broken too - you fix it.  It should read the first four
    bytes into a local buffer to get the length of the packet, then
    allocate that packet and move the count over.
*/
void RecvPacket( int sock, QFXPACKET **pkt )
  {
    int	    length  = 0;
    int	    total   = 0;
    char   *p;
    QFXHEADER	qh;
    QFXPACKET  *pq;

    if ( !pkt )
      {
	fputs( "RecvPacket with NULL packet pointer", stderr );
	exit( EXIT_FAILURE );
      }

    if ( *pkt )				/* Already allocated?		    */
	free( *pkt );			/* free it first		    */

    length  = recv( sock, (char *)&qh, 4, 0 );
    total   = ntohl( qh.length );
    p	    = malloc( total );
    if ( !p )
      {
	fprintf( stderr, "RecvPacket malloc for %d bytes failed\n", total );
	exit( EXIT_FAILURE );
      }

    *pkt    = (QFXPACKET *)p;
    pq	    = (QFXPACKET *)p;

    pq->header.length	= qh.length;

    total   -= sizeof pq->header.length;
    p	    += sizeof pq->header.length;
    while ( total  >  0 )
      {
	length	= recv( sock, p, total, 0 );
	if ( length  <=  0 )
	    continue;
	total	-= length;
	p	+= length;
      }
  }


int main( int argc, char *argv[] )
  {
    int	    sock;
    int	    i, len, ret;
    char   *outname = "foo.bar";
    char   *ipname  = "127.0.0.1";
    char   *inname  = "foo.bar";
    char   *tkname  = "INFO";

    QFXTOKEN	qt;
    char	mktkn[ 5 ]  = {0};
    QFXPACKET   *qpkt	= NULL;

/*  arg1 is the IP
    arg2 is the command "INFO" or "SEND"
    arg3 is the remote file path and name
    arg4 is the local file path and name
*/
    if ((argc  <  3)  ||  (strlen( argv[ 2 ] )  !=  4))
	DisplayHelpAndExit();


//  Someday maybe we'll allow options and such.

    ipname  = argv[ 1 ];
    tkname  = argv[ 2 ];
    inname  = argv[ 3 ];
    outname = argv[ argc - 1 ];


//  Need a legit token.

    for ( i = 0; i < 4; i++ )
	mktkn[ i ]  = (char)toupper( tkname[ i ]);
    qt	= *(QFXTOKEN *)&mktkn[ 0 ];


//  INFO takes 4 params, SEND takes 4 or 5

    if ( !(argc  ==  4)	 &&
	 !((qt  ==  QFX_SEND)  &&  (argc  ==  5)))
	DisplayHelpAndExit();


//  Allocate the packet

    len	    = strlen( inname ) + 1;	// Include the NUL
    len	    += sizeof qpkt->header;
    qpkt    = malloc( len );
    if ( !qpkt )
      {
	fprintf( stderr, "malloc of %d bytes failed\n", len );
	exit( EXIT_FAILURE );
      }


//  Fill out the particulars

    qpkt->header.token	= qt;
    qpkt->header.length	= htonl( len );
    strcpy( qpkt->body.info.name, inname );


//  Get a socket and send the packet

    sock    = establish( ipname );
    SendPacket( sock, (char *)qpkt, len );

    RecvPacket( sock, &qpkt );

    CloseSocket( sock );

    ret	= EXIT_SUCCESS;
    if ( qpkt->header.token  !=  qt )
	fprintf( stderr, "receive failure\n" );

    switch  ( qpkt->header.token )
      {
	case	QFX_INFO:
	  {
	    printf( "%s\n", qpkt->body.info.name );
	    break;
	  }

	case	QFX_SEND:
	  {
	    FILE   *f;
	    long	got, put;
	    char   *b;

	    got = ntohl( qpkt->header.length );
	    if ( got  <=  8 )
	      {
		fprintf( stderr, "Received length is too short: %ld\n", got );
		ret = EXIT_FAILURE;
		break;
	      }
	    got -= sizeof qpkt->header;

	    fprintf( stdout, "Writing %d byte%s to %s\n",
		     got, (got == 1) ? "" : "s", outname );

	    f   = fopen( outname, "wb" );
	    if ( !f )
	      {
		fprintf( stderr, "Can't open %s for output\n", outname );
		ret = EXIT_FAILURE;
		break;
	      }

	    b   = (char *)qpkt;
	    put = fwrite( b + (sizeof qpkt->header), 1, got, f );
	    fclose( f );
	    if ( put  !=  got )
	      {
		fprintf( stderr, "Wrote %ld of %ld bytes\n", put, got );
		ret = EXIT_FAILURE;
		break;
	      }
	    break;
	  }

	default:
	  {
	    ret = EXIT_FAILURE;
	    break;
	  }
      }

    free( qpkt );

    return ret;
  }
