/*  ModeSet.c

    A collaboration of
    Industrial Electronic Engineers: Kevin D. Quitt, Benjamin Javaherian
    Toys R Us: Bala Ramalingham

    This program can read and set all the various modes in the IEE ScanVue
    and ShopVue series products, by using the Product Information Protocol
    (SLOPPI) as sanctioned by IANA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.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>
#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


#define  BUFFER_SIZE 128*1024

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

typedef unsigned long   ulong;

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

#define TKNAME(a,b,c,d) (((d)<<24)|((c)<<16)|((b)<<8)|(a))
#include "sloppidef.h"

#define connectPort	1283
#define CONFIGFILE	"ModeSet.ini"
#define TARGETIPTOKEN	"TargetIP"

char    ConfigFile[30] = { 0 };

/*  Default IP Address
*/
char	    remoteHost[ 255 ];

int	tcpSocket=0;
int	NumberOfModes;

typedef	struct
  {
    char    Name[50];
    char    Type[20];
    char    Help[512];
    int	    Size;
    char    NVRamContent[200];
    char    CurrentContent[200];
    char    iniContent[200];
    BOOL    New;
  } MODE;


MODE mode[100];
int	verbose	  = 0;
int	modeset	  = 0;
int	commit	  = 0;
int	restart	  = 0;

 

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

int boolVal( char *TrueFalse, int defaultValue )
  {
    int	ch;

    if ( TrueFalse )
      {
	ch  = toupper( *TrueFalse );
	switch( ch )
	  {
	    case    'T':
	    case    'Y':
	    case    '1':
	      {
		return	TRUE;
	      }

	    case    'F':
	    case    'N':
	    case    '0':
	      {
		return	FALSE;
	      }

	    default:
	      {
		return	defaultValue;
	      }

	  }
      }

    return  defaultValue;
  }

void BuildModeArray( SLPACKET *pkt )
  {
    char   *p;
    int	    i = 0;
    int	    total;
    char    temp[ 50 ];

    p	= pkt->body.moderesponse.moderesponse;

    while ( *p )
    {
	total = strcspn( p, " =" );
	strncpy( mode[ i ].Name, p, total );
	mode[ i ].Name[ total ] = 0;

	p	+= total;
	p	+= strspn( p, "= " );
	total	= strcspn( p, " \t\n\r," );
	strncpy( mode[i].Type, p, total );
	mode[i].Type[ total ]	= 0;
	p	+= total;

	if ( stricmp( mode[ i ].Type, "STRING") == 0 )
	{
	    p	    += strspn( p, ", " );
	    total   = strcspn( p, " \t\n\r" );
	    strncpy( temp, p, total );
  	    temp[ total ]	= 0;
	    mode[ i ].Size	= atoi( temp );
	    p	+= total;
	}

	p += strspn( p, " \t" );
        total = strcspn( p, "\n\r" );
	strncpy( mode[i].Help, p, total );
        p += total;
	p += strspn( p, "\n\r" );
	i++;
    }

    NumberOfModes   = i;
}

int GetNameIndex( char *name )
  {
    int i;

    if ( !name			||
	 !*name			||
	 isspace( name[ 0 ])	||
	 (name[ 0 ]  ==  '#'))
	return	-1;

    for ( i = 0; i < NumberOfModes; i++ )
      {
	if( stricmp( name, mode[ i ].Name ) == 0 )
	    return  i;
      }

    return -1;
  }

void	ClearTrailingWhiteSpace( char *content )
  {
    int	i;

    for ( i = strlen( content ) - 1; i >= 0; i-- )
      {
	if ( isspace( content[ i ] ))
	    content[ i ]    = 0;
	else
	    break;
      }

    return;
  }

void	ReadNVRamContent( SLPACKET *pkt )
  {
    char   *p;
    int	    i	= 0;
    int	    total;
    char    name[ 50 ];

    p = pkt->body.moderesponse.moderesponse;

    while( *p )
      {
	total		= strcspn( p, " =\t" );
	strncpy( name, p, total );
	name[ total ]	= 0;

	p   += total;
	p   += strspn( p, "= \t" );
	total = strcspn( p, "\n\r\t" );

	i   = GetNameIndex( name );
	if( i >= 0 )
	  {
	    strncpy( mode[ i ].NVRamContent, p, total );
	    mode[ i ].NVRamContent[ total ] = 0;
	    ClearTrailingWhiteSpace( mode[ i ].NVRamContent );
	  }

	p   += total;
	p   += strspn( p, " \n\r\t" );
      }
  }


void ReadIniContent ( SLPACKET *pkt )
  {
    char    Buffer[ BUFFER_SIZE ];
    char   *p;
    int	    i	= 0;
    int	    total;
    char    name[ 50 ];

    FILE    *stream;

    memset( Buffer, 0, BUFFER_SIZE );

/*  Open file in text mode:
*/
    stream  = fopen( ConfigFile, "r+t" );
    if( !stream )
      {
	fprintf( stderr, "File %s could not be opened\n", ConfigFile );
	return;
      }

    fread( Buffer, sizeof( char ), BUFFER_SIZE, stream );
    fclose( stream );

    p	= Buffer;

    while( *p )
      {
/*  Get rid of white spaces
*/
	p   += strspn( p, "\t\n\r" );

	total = strcspn( p, " =\t" );
	strncpy( name, p, total );
	name[ total ] = 0;

	p	+= total;
	p	+= strspn( p, "= \t" );
	total	= strcspn( p, "\n\r\t" );

	i	= GetNameIndex( name );
	if ( i >= 0 )
	  {
	    if ( stricmp( p, "(Not Set)" ))
	      {
		strncpy( mode[ i ].iniContent, p, total );
		mode[ i ].iniContent[ total ]	= 0;
		ClearTrailingWhiteSpace( mode[ i ].iniContent );
		mode[ i ].New   = TRUE;
	      }
	  }

	p   += total;
	p   += strspn( p, " \n\r\t" );
      }
  }

void DisplayHelpAndExit( void )
  {
    fputs( "\nModeSet [options] [IPAddress]\n", stderr );
    fputs( "  -v verbose operation\n", stderr );
    fputs( "  -s set modes according to modeset.ini\n", stderr );
    fputs( "  -c commit to permanent storage\n", stderr );
    fputs( "  -r restart unit when complete\n", stderr );
    fputs( "  -f specify an .ini file, e.g. -fNewModes.ini \n", stderr );
    fputs( "  -i set a single mode, e.g. -iUnitID=ScanVue5\n", stderr );
    fputs( "  -g get a single mode, e.g. -gUnitID\n", stderr );
    fputs( "  -l send literal text\n", stderr );
    fputs( "  IP address of unit obtained from modeset.ini "
           "if not specified.\n", stderr );
    fputs( "\nDefault operation is to list the modes and values "
           "of the specified unit.\n\n", stderr );
    exit( EXIT_SUCCESS );
  }


void getTargetIP( char *remoteHost )
  {
    char    Buffer[ BUFFER_SIZE ];
    char   *p;
    int	    total;
    BOOL    GotIt = FALSE;

    FILE   *stream;

    memset( Buffer, 0, BUFFER_SIZE );

/*  Open file in text mode:
*/
    stream  = fopen( ConfigFile, "r+t" );
    if( !stream )
      {
	fprintf( stderr, "File %s could not be opened\n", ConfigFile );
        DisplayHelpAndExit( );
      }

    fread( Buffer, sizeof( char ), BUFFER_SIZE, stream );
    fclose( stream );

    p	= Buffer;
    while ( *p )
      {
	total	= strcspn( p, " =\t" );
	if ( strncmp( p, TARGETIPTOKEN, total ) == 0 )
	    GotIt   = TRUE;

	p	+= total;
	p	+= strspn( p, "= \t" );
	total	= strcspn( p, "\n\r\t," );

	if( GotIt )
	  {
	    strncpy( remoteHost, p, total );
	    remoteHost[ total ]	= 0;
	    ClearTrailingWhiteSpace( remoteHost );
	    break;
	  }

	p   += total;
	p   += strspn( p, " \n\r\t" );
      }

    if ( !GotIt ) 
    {
        fputs( "\nNo target IPAddress specified.\n", stderr );
        fputs( "Please specify the Target IPAddress either on the command line,\n", stderr );
        fputs( "or in the .ini file, e.g. TargetIP=10.0.0.227 \n\n", stderr );

        DisplayHelpAndExit( );
    }
  }



void ReadCurrentContent ( SLPACKET *pkt )
  {
    char   *p;
    int	    i	= 0;
    int	    total;
    char    name[ 50 ];

    p = (char *)pkt->body.moderesponse.moderesponse;

    while( *p )
      {
	total = strcspn( p, " =\t" );
	strncpy( name, p, total );
	name[total] = 0;
	p += total;
	p += strspn( p, "= \t" );
	total = strcspn( p, "\n\r\t" );
	i = GetNameIndex( name );
	if( i >= 0 )
	  {
	    strncpy( mode[i].CurrentContent, p, total );
	    mode[i].CurrentContent[ total ] = 0;
	    ClearTrailingWhiteSpace( mode[i].CurrentContent );
	  }
	p += total;
	p += strspn( p, " \n\r\t" );
      }
  }

void SendPacket( SLPACKET *pkt, char *ModeName )
  {
    int	total  = 0;
    int length = 0;
    int sent   = 0;

    if ( verbose )
	printf( "Sending MDSQ %s\n", ModeName);
    memset( pkt,0,BUFFER_SIZE);

    total = sizeof( pkt->header ) + strlen( ModeName ) + 1;
    pkt->header.token = SLT_MODESETQUERY;
    pkt->header.length = htonl( total);

    if( *ModeName )
	strcpy( pkt->body.modesetquery.modesetquery, ModeName );

    while( total != sent)
      {
	length = send( tcpSocket, (const char *)(pkt + sent), total-sent,0);
	if( length < 0)
	  {
	    fprintf( stderr, "\nError sending..\n");
	    exit( -1);
	  }
	sent += length;
      }
  }


void RecvPacket( SLPACKET *pkt )
  {
    int	    length=0,total=0;
    char    *p;
    char    msgBuffer[5];

    length = recv( tcpSocket, (char *)pkt, 4, 0);
    total = ntohl( pkt->header.length) - sizeof pkt->header.length;
    p = (char *)&pkt->header.token;
    while ( total > 0)
      {
	length = recv( tcpSocket, p, total, 0);
	if ( length  <=  0 )
	    continue;
	total	-= length;
	p += length;
      }
    memmove( msgBuffer, &(pkt->header.token), 4);
    msgBuffer[4]=0;
    if ( verbose )
	printf( "Received %s %s\n", msgBuffer,
		pkt->body.moderesponse.moderesponse );
  }

void	PrintResult( void )
  {
    int	    i, len, maxlen = 0;
    char    fmt[ 40 ];

    for ( i = 0; i < NumberOfModes; i++ )
      {
	len = strlen( mode[ i ].Name );
	if ( len  >  maxlen )
	    maxlen  = len;
      }
    maxlen  += 4;
    sprintf( fmt, "%%-%ds= %%s\n", maxlen );

    if ( verbose  ||  !modeset )
      {
	printf( "\n\n# ***  Current Content  ***\n\n" );
	printf( "TargetIP = %s\n", remoteHost );
	for( i = 0; i < NumberOfModes; i++ )
	    printf( fmt, mode[ i ].Name, mode[ i ].NVRamContent );
      }

    if ( verbose )
      {
	printf( "\n\n# ***  %s Content  ***\n\n", ConfigFile );
	for( i = 0; i < NumberOfModes; i++ )
	    if( mode[ i ].New )
		printf( fmt, mode[ i ].Name, mode[ i ].iniContent );
      }


    if ( modeset )
      {
	printf( "\n\n# ***  New Content  ***\n\n");
	for( i = 0; i < NumberOfModes; i++ )
	    printf( fmt, mode[ i ].Name, mode[ i ].CurrentContent );

	if ( verbose )
	  {
	    printf( "\n\n# ***  Changes  ***\n\n");
	    sprintf( fmt, "%%-%ds= %%s, %%s\n", maxlen );
	    printf( fmt, "Mode", "NVRam Content", "New Content\n");

	    for ( i = 0; i < NumberOfModes; i++ )
	      {
		if( mode[ i ].New                                                       &&
                    (stricmp( mode[i].Name,"DateTime" ))                             &&
		    (stricmp( mode[ i ].NVRamContent, mode[ i ].CurrentContent) != 0 )
                  )
		    printf( fmt, mode[ i ].Name,
			    mode[ i ].NVRamContent,
			    mode[ i ].CurrentContent );
	      }
	  }
      }
  }


BOOL setBoolean( char *name )
  {
    int i;

    i = GetNameIndex( name );

    if ( mode[ i ].New )
	return	boolVal( mode[ i ].iniContent, FALSE );

    return	boolVal( mode[ i ].NVRamContent, FALSE );
  }


int main( int argc, char *argv[] )
  {
    int		i, arg;
    int		status	    = 0;
    int		gotHost	    = FALSE;
    char	Mode[ 255 ]	    = { 0 };
    char	Immediate[ 255 ]    = { 0 };
    char	GetMode[ 255 ]      = { 0 };
    char	Literal[ 255 ]	    = { 0 };
    char        TextReset[ 30 ]	    = { 0 };
    char        ServerType[ 30 ]    = { 0 };

    SLPACKET   *pkt;

    struct hostent     *phe;
    struct sockaddr_in  remote_sin;

    strcpy( ConfigFile, CONFIGFILE );

/*    If an IP is provided as the command line argument,  or in the .ini file, use it.
*/
    arg	= 0;
    while ( --argc )
      {
	++arg;
	i   = argv[ arg ][ 0 ];
	if (( i   ==  '-' )  ||  ( i  ==  '/' ))
	  {
	    i	= toupper( argv[ arg ][ 1 ] );
	    if ( i  ==  'V' )
		verbose++;
	    if ( i  ==  'S' )
		modeset++;
	    if ( i  ==  'C' )
		commit++;
	    if ( i  ==  'R' )
		restart++;
	    if ( i  ==  'F' )
                strcpy( ConfigFile, &(argv[ arg ][ 2 ]) );
	    if ( i  ==  'I' )
		strcpy( Immediate, &(argv[ arg ][ 2 ]) );
	    if ( i  ==  'G' )
		strcpy( GetMode, &(argv[ arg ][ 2 ]) );
	    if ( i  ==  'L' )
	      {
		char *lit   = argv[ arg ] + 2;
		if ( *lit  ==  '"' )
		    lit++;
		strncpy( Literal, lit, sizeof Literal );
		lit = Literal + strlen( Literal ) - 1;
		if ( *lit  ==  '"' )
		    *lit    = 0;
	      }
	    if ( i  ==  '?' )
                DisplayHelpAndExit();
	  }
	else
	  {
	    strncpy( remoteHost, argv[ arg ], sizeof remoteHost );
	    gotHost = TRUE;
	  }
      }

    if ( !gotHost )
	getTargetIP( remoteHost );

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

/*  Create socket
*/
    tcpSocket	= socket( AF_INET, SOCK_STREAM, 0 );
    if( tcpSocket == -1 )
      {
	printf( "Error creating socket\n");
	exit( -1 );
      }

#ifdef REQD
/*
    Set Socket Options
*/
    status  = setsockopt( tcpSocket, IPPROTO_TCP,TCP_NODELAY,
			  (char *)&opt, optlen );
    if ( status == -1 )
      {
	perror( "Error setting socket option(TCP_NODELAY)\n" );
	exit( -1 );
      }

    opt	    = 4096;
    status  = setsockopt( tcpSocket, SOL_SOCKET, SO_SNDBUF, (char *)&opt,optlen);
    if ( status == -1)
	printf( "Error setting socket option(SO_SNDBUF)\n");

    status = setsockopt( tcpSocket,SOL_SOCKET,SO_RCVBUF,(char *)&opt,optlen);
    if( status == -1 )
	printf( "Error setting socket option(SO_RCVBUF)\n" );
#endif

    phe	= gethostbyname( remoteHost );

    if ( !phe )
      {
	printf( "DNS lookup failed for host :%s\n", remoteHost );
	CloseSocket( tcpSocket );
	exit( -1 );
      }

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

/*  talk to user provided port value
*/
    remote_sin.sin_port	    = htons( connectPort );
    remote_sin.sin_family =  AF_INET;

/*  connect
*/
    status  = connect( tcpSocket, (struct sockaddr *)&remote_sin,
		       sizeof remote_sin );
    if ( status  ==  -1 )
      {
	printf( "Error connecting to node %s\n", remoteHost );
	CloseSocket( tcpSocket );
	exit( EXIT_FAILURE );
      }


    pkt	= malloc( BUFFER_SIZE );
    if ( !pkt )
      {
	printf( "malloc failed\n" );
	exit( EXIT_FAILURE );
      }

    if ( *Literal )
      {
	send( tcpSocket, Literal, strlen( Literal ) + 1, 0 );
	CloseSocket( tcpSocket );
	free( pkt );
	exit( EXIT_SUCCESS );
      }

    if ( *Immediate )
      {
        SendPacket( pkt, Immediate );
        CloseSocket( tcpSocket );
        free( pkt );
        exit( EXIT_SUCCESS );
      }

    if ( *GetMode )
      {
        SendPacket( pkt, "ForceResponse=TRUE" );
        RecvPacket( pkt );
        SendPacket( pkt, GetMode );
        RecvPacket( pkt );
        printf("%s", pkt->body.moderesponse.moderesponse );
        CloseSocket( tcpSocket );
        free( pkt );
        exit( EXIT_SUCCESS );
      }



    SendPacket( pkt, "ForceResponse=TRUE" );
    RecvPacket( pkt );
    SendPacket( pkt, "ForceError=TRUE" );
    RecvPacket( pkt );
    SendPacket( pkt, "ForceHelpText=FALSE" );
    RecvPacket( pkt );


/*  ModeQuery
*/
    SendPacket( pkt, "" );
    RecvPacket( pkt );

    BuildModeArray ( pkt );

/*  Query the NVRam value
*/
    SendPacket( pkt, "*" );
    RecvPacket( pkt );

    ReadNVRamContent ( pkt );

    if ( modeset )
      {
	ReadIniContent( pkt );

/*  Send the new Modes
*/
	for ( i = 0; i < NumberOfModes; i++ )
	  {
	    if( mode[ i ].New )
	      {
                if ( !stricmp (mode[i].Name, "DateTime")     ||
                     !stricmp (mode[i].Name, "Commit")       ||
                     !stricmp (mode[i].Name, "Restart")      ||
                     !stricmp (mode[i].Name, "ProductName")  ||
                     !stricmp (mode[i].Name, "buildInfo")  
                   ) 
                    continue;

		if ( stricmp( mode[ i ].iniContent, "(Not Set)" ))
		  {
		    sprintf( Mode, "%s = %s", mode[ i ].Name, mode[ i ].iniContent );
                    if( !stricmp( mode[i].Name, "TextReset" ) ) 
                        strcpy( TextReset, Mode );
                    else if ( !stricmp( mode[i].Name, "ServerType" ) )
                        strcpy( ServerType, Mode );
                    else
                      {
		        SendPacket( pkt, Mode );
                        RecvPacket( pkt );
                      }
		  }
	      }
	  }

/*  Send TestReset and ServerType last
*/
        if( strlen(TextReset) != 0 )
          {
            SendPacket( pkt, TextReset );
            RecvPacket( pkt );
          }

        if( strlen(ServerType) != 0 )
          {
	    SendPacket( pkt, ServerType );
            RecvPacket( pkt );
          }
/*  Query the current value
*/
	SendPacket( pkt, "*" );
	RecvPacket( pkt );
	ReadCurrentContent( pkt );
      }

    PrintResult();

    if( commit )
      {
	SendPacket( pkt, "COMMIT=TRUE" );
	RecvPacket( pkt );
      }

    if( restart )
	SendPacket( pkt, "RESTART=TRUE" );

    CloseSocket( tcpSocket );
    free( pkt );

    return 0;
  }

