
/****************************************************************************
* io_fltr [x|c][b][V][v][s] [V_size] [v_fname] [s_fname] 
*  flags:
*     x	  read data from stdin, if compressed, write uncompressed to stdout,
*	  otherwise copy stdin to stdout.
*      
*     c	  read data from stdin, if compressed copy stdin to stdout, 
*	  otherwise write compressed to stdout.
*
*     b	  buffer the output with nulls to make it divisible by 20b = 10240
*	   
*     V   output a '.' to stderr after every <V_size> kbytes have been copied
*	  to stdout.
*
*     v   append checksum into file <v_fname> upon completion
*
*     s	  use <s_fname> as file containing key to secure data. This option
*	  should only be used with the 'c' option.
*
*
****************************************************************************/
static char *usage = "Usage: \
io_fltr [x|c][b][V][v][s] [V_size] [v_fname] [s_fname]\n";


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
extern int fclose(), pclose();
#include <signal.h>

#ifndef TRUE
#define TRUE	1
#define FALSE	0
#endif				/*ifndef TRUE*/

#ifndef MIN
#define MIN(a,b)	(((a)<=(b))?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b)	(((a)>=(b))?(a):(b))
#endif

#include <memory.h>

#define ROLR16(i)	if( (i) & 0x1 )			\
			    i = ( (i) >> 1 ) | 0x8000;	\
			else				\
			    i = ( (i) >> 1 )

#ifdef nec_ews
#define COMPRESS	"/usr/bin/compress -f"
#define UNCOMPRESS	"/usr/bin/zcat"
#else
#ifdef __hpux
#define COMPRESS	"/usr/bin/compress -f"
#define UNCOMPRESS	"/usr/bin/zcat"
#else
#ifdef __sgi
#define COMPRESS	"/usr/bsd/compress -f"
#define UNCOMPRESS	"/usr/bsd/zcat"
#else
#ifdef sun4v
#define COMPRESS	"/usr/bin/compress -f"
#define UNCOMPRESS	"/usr/bin/zcat"
#else
#ifdef ibmrs
#define COMPRESS	"/usr/bin/compress -f"
#define COMPRESSF	"/usr/bin/compress"
#define UNCOMPRESS	"/usr/bin/zcat"
#define COMPRESS1	"/usr/ucb/compress -f"
#define COMPRESS1F	"/usr/ucb/compress"
#define UNCOMPRESS1	"/usr/ucb/zcat"
#else
#define COMPRESS	"/usr/ucb/compress -f"
#define UNCOMPRESS	"/usr/ucb/zcat"
#endif
#endif
#endif
#endif
#endif

#define MAX_KEYSIZE	 4096	/* for en/de-cryption */
#define IO_BLOCKSZ	10240

#define ZMAGIC_OFFSET	0
#define SMAGIC_OFFSET	0
u_char	zmagic[] = { '\037', '\235' },
	smagic[] = { '\037', '\010' };

#define	SFLNAMLEN	100
	

typedef enum { YAH, IGNORE, NAH } Opcode;

typedef int FilterFunc();

char	*vFile = (char *)0,	/* verification */
	*sFile = (char *)0;	/* security */

long	dotInterval = 0;	/* marching dots */

FILE 	*writeFp;



/****************************************************************************
*****************************************************************************
BEGIN security code
*****************************************************************************
****************************************************************************/

static u_char *secKey = (u_char *)0;
static int  secKeySz = 0;


int 
InitSecurity( sFile )
char	*sFile;
{
    struct stat statBuf;
    int	inFD,
	nbytes,
	numRead;

    if( stat( sFile, &statBuf ) < 0 ) {
	(void)fprintf( stderr, "Could not locate security key\n" );
	return -1;
    }
    if( !(secKeySz = (int)statBuf.st_size) )
	return -1;
    secKeySz = MIN( secKeySz, MAX_KEYSIZE );
    if( !(secKey = (u_char *)malloc( (unsigned)secKeySz )) )
	return -1;
    if( (inFD = open( sFile, O_RDONLY )) < 0 )
	return -1;

    for( numRead = 0;
	 numRead < secKeySz &&
	 (nbytes = read( inFD, &secKey[numRead], secKeySz-numRead )) > 0 ;
	 numRead += nbytes
       );

    if( nbytes < 0 ) {
	perror( "read" );
	return -1;
    }
    close( inFD );

    return 0;
}


int
SecureBuf( buf, bufSz )
u_char	*buf;
long	bufSz;
{
    static u_char *keyPos = (u_char *)0;
    static unsigned incr = 0;		/* used to init position in key */
    long    bufRemaining;

    u_char  *bufEnd = &buf[bufSz],
	    *keyEnd = &secKey[secKeySz];

    if( !keyPos )
	keyPos = secKey;

    while( (bufRemaining = bufEnd-buf) > 0 ) {
	long	keyRemaining;
	u_char	*limit;

	while( (keyRemaining = keyEnd-keyPos) == 0 )
	    keyPos = &secKey[ (incr++ & 0x01FF) % secKeySz ];

	for( limit = buf +MIN(keyRemaining,bufRemaining); buf < limit; buf++ ){
	    ROLR16( incr ); 
	    incr += *buf;
	    incr &= 0xFFFF;

	    if( (*buf) && ( *buf != *keyPos ) )	/* dont creat or destroy 0's */
		*buf ^= *keyPos++ ;
	}
    }
}

int
UnsecureBuf( buf, bufSz )
u_char	*buf;
long	bufSz;
{
    static u_char *keyPos = (u_char *)0;
    static unsigned incr = 0;		/* used to init position in key */
    long    bufRemaining;

    u_char  *bufEnd = &buf[bufSz],
	    *keyEnd = &secKey[secKeySz];

    if( !keyPos )
	keyPos = secKey;

    while( (bufRemaining = bufEnd-buf) > 0 ) {
	long	keyRemaining;
	u_char	*limit;

	while( (keyRemaining = keyEnd-keyPos) == 0 )
	    keyPos = &secKey[ (incr++ & 0x01FF) % secKeySz ];

	for( limit = buf +MIN(keyRemaining,bufRemaining); buf < limit; buf++ ){
	    if( (*buf) && ( *buf != *keyPos ) )	/* dont creat or destroy 0's */
		*buf ^= *keyPos++ ;

	    ROLR16( incr ); 
	    incr += *buf;
	    incr &= 0xFFFF;
	}
    }
}



/**********************************************************************
*   Routines to determine header info
**********************************************************************/
typedef u_char SmagicHeader[ SMAGIC_OFFSET + sizeof(smagic) + SFLNAMLEN ];
typedef u_char ZmagicHeader[ ZMAGIC_OFFSET + sizeof(zmagic) ];

static u_char header[ MAX( sizeof(SmagicHeader), sizeof(ZmagicHeader) ) ];

int
ReadHeader( buf )
u_char	*buf;
{
    int	    numRead,
	    nbytes;

    for( nbytes = 0;
	 nbytes < sizeof( header ) &&
	 (numRead = read( fileno(stdin), &buf[nbytes], IO_BLOCKSZ-nbytes)) > 0;
	 nbytes += numRead
       );
    if( numRead < 0 ) {
	perror( "read" );
	exit( -1 );
    }
    return nbytes;
}


void
Identify( buf, nbytes, isSecured, isCompressed )
u_char	*buf;
long	nbytes;
int	*isSecured, 
	*isCompressed;
{
    *isSecured = *isCompressed = FALSE;

    if( nbytes >= sizeof(SmagicHeader) )
        *isCompressed = *isSecured = 		/* secured implies compressed */
		(memcmp( &buf[SMAGIC_OFFSET], smagic, sizeof(smagic)) == 0 );

    if( !(*isCompressed) && (nbytes >= sizeof(ZmagicHeader) ) )
	*isCompressed =
		(memcmp( &buf[ZMAGIC_OFFSET], zmagic, sizeof(zmagic)) == 0 );
}



/****************************************************************************
*****************************************************************************
Begin Filters
*****************************************************************************
****************************************************************************/

int
SumFltr( buf, nbytes, filterChain )
u_char	*buf;
long	nbytes;
FilterFunc *filterChain[];
{
    static unsigned sum = 0;
    register int i;

    for( i = 0; i < nbytes; i++ ) {
	ROLR16( sum );
	sum += buf[i];
	sum &= 0xFFFF;
    }

    if( nbytes == 0 ) {			/* end of input */
	FILE    *sumOut;
	if( !(sumOut = fopen( vFile, "a" )) ) {
	    perror( vFile );
	    exit( -1 );
	} else {
	    (void)fprintf( sumOut, "%05u\n", sum );
	    fclose( sumOut );
	}
    }

    return( (*filterChain[0])( buf, nbytes, &filterChain[1] ) );
}




int
DotFltr( buf, nbytes, filterChain )
u_char	*buf;
long	nbytes;
FilterFunc *filterChain[];
{
    static long interv = 0;

    if( nbytes )
	for( interv += nbytes; interv >= dotInterval; interv -= dotInterval ) 
	    putc( '.', stderr );

    return( (*filterChain[0])( buf, nbytes, &filterChain[1] ) );
}




int
BlockFltr( buf, nbytes, filterChain )
u_char	*buf;
long	nbytes;
FilterFunc *filterChain[];
{
    static long numWritten = 0;

    if( nbytes ) {
	numWritten += nbytes;
    } else if( numWritten %= IO_BLOCKSZ ) {	/* pad output */
	u_char *tmpBuf;

	if( !(tmpBuf = (u_char *)calloc( IO_BLOCKSZ-numWritten, 1 ) ) ) {
	    (void)fprintf( stderr, "calloc failure\n" );
	    exit( 1 );
	}
	if( (*filterChain[0])( tmpBuf, IO_BLOCKSZ-numWritten, &filterChain[1]) )
	    return -1;
	free( (char *)tmpBuf );
    }
    return( (*filterChain[0])( buf, nbytes, &filterChain[1] ) );
}



/************************************************************************
* Security filters
************************************************************************/

int
SecureFltr( buf, nbytes, filterChain )
u_char	*buf;
long	nbytes;
FilterFunc *filterChain[];
{
    static int firstTime = TRUE;

    if( firstTime )
	if( InitSecurity( sFile ) != 0 )
	    return -1;
	else {				/* write out header */
	    SmagicHeader secHeader;
	    long   bytesCopied;

	    if( SMAGIC_OFFSET )
		memset( &secHeader[0],'\0', SMAGIC_OFFSET );
	    memcpy( &secHeader[SMAGIC_OFFSET], &smagic[0], sizeof(smagic) );
	    bytesCopied = SMAGIC_OFFSET+sizeof(smagic);
	    strcpy( (char *)&secHeader[bytesCopied], sFile );	/* terminate */
	    bytesCopied += strlen( sFile ) + 1;

	    if( (*filterChain[0])( &secHeader[0], bytesCopied, &filterChain[1]))
		return -1;

	    firstTime = FALSE;
	}
    
    if( nbytes )
	SecureBuf( buf, nbytes );
    return( (*filterChain[0])( buf, nbytes, &filterChain[1] ) );
}


int
UnSecureFltr( buf, nbytes, filterChain )
u_char	*buf;
long	nbytes;
FilterFunc *filterChain[];
{
    static int firstTime = TRUE;

    if( firstTime ) {
	char *sFile;
	long  numBytes;
					/* buf holds entire name, or CRASH */
	sFile = (char *)&buf[ SMAGIC_OFFSET+sizeof(smagic) ];
	if( InitSecurity( sFile ) != 0 )
	    return -1;
	numBytes = SMAGIC_OFFSET+sizeof(smagic)+strlen( sFile ) +1;
	buf += numBytes;
	nbytes -= numBytes;
	if( nbytes == 0 )
	    return 0;

	firstTime = FALSE;
    }

    if( nbytes )
	UnsecureBuf( buf, nbytes );
    
    return( (*filterChain[0])( buf, nbytes, &filterChain[1] ) );
}



/************************************************************************
* Begin/End of filterChain
************************************************************************/
int
FilterBeg( buf, nbytes, filterChain )
u_char	*buf;
long	nbytes;
FilterFunc *filterChain[];
{
    if( nbytes )
	if( (*filterChain[0])( buf, nbytes, &filterChain[1] ) != 0 )
	    return -1;

    while( (nbytes = read( fileno(stdin), &buf[0], IO_BLOCKSZ )) >= 0 ) {
	if( (*filterChain[0])( buf, nbytes, &filterChain[1] ) != 0 )
	    return -1;
	if( nbytes == 0 )	/* already sent out END_OF_DATA marker */
	    return 0;
    }
    perror( "read" );
    return -1;
}


int 
FilterEnd( buf, nbytes, filterChain )
u_char	*buf;
long	nbytes;
FilterFunc *filterChain[];
{
    (void)filterChain;
    if( (nbytes) && (write( fileno(writeFp), buf, nbytes ) != nbytes) )
	return -1;
    else
	return 0;
}


static void
SigPipeHdlr()
{
    extern int errno;

    if( errno )
	perror( (char *)0 );
    exit( 1 );
}


/****************************************************************************
*****************************************************************************
Main
*****************************************************************************
****************************************************************************/
main( argc, argv )
int	argc;
char	*argv[];
{
    u_char  buf[ IO_BLOCKSZ ];
    long    nbytes = 0;
    Opcode  secure   = IGNORE, 
	    compress = IGNORE,
	    block    = NAH,
	    verify   = NAH;
    char    *keys =	(char *)0;
    int	    (*closeFunc)();
    int	    argParsed = 0,
	    fltrCnt;
    FilterFunc	*filter[10];
#ifdef ibmrs
    struct stat statb;
    int compress1_flag = 0;
 
    if((stat(COMPRESSF,&statb) != 0) || (stat(UNCOMPRESS,&statb) != 0) ) {
      if((stat(COMPRESS1F,&statb) != 0) || (stat(UNCOMPRESS1,&statb) != 0) ) {
        (void)fprintf(stderr,"\nCannot find %s %s or %s %s\n",UNCOMPRESS,COMPRESSF,UNCOMPRESS1,COMPRESS1F);
        exit(1);
      }
      else {
        compress1_flag = 1;
      }
    }
    else {
        compress1_flag = 0;
    }
#endif
      
   

    if( !(keys = argv[++argParsed]) ) {
	(void)fprintf( stderr, usage );
	exit( 1 );
    }
    while( *keys )
	switch ( *keys++ ) {
	    case 'c' : 
		compress = YAH;
		break;
	    case 'x' : 
		compress = NAH;
		secure   = NAH;
		break;
	    case 'b' :
		block = YAH;
		break;
	    case 'v' :
		if( !(vFile = argv[++argParsed]) ) {
		    (void)fprintf( stderr, usage );
		    exit( 1 );
		}	
		verify = YAH;
		break;
	    case 'V' :
		if( (! argv[++argParsed]) || 
				 !(dotInterval = atoi( argv[argParsed] )) ) {
		    (void)fprintf( stderr, usage );
		    exit( 1 );
		}	
		break;
	    case 's' :
		if( !(sFile = argv[++argParsed]) ) {
		    (void)fprintf( stderr, usage );
		    exit( 1 );
		}	
		if( strlen( sFile ) >= SFLNAMLEN ) {
		    (void)fprintf( stderr, "s_fname too long\n" );
		    exit( 1 );
		}
		secure = YAH;
		break;
	    default:
		(void)fprintf( stderr, usage );
		exit( 1 );
	}
    if( argv[argParsed+1] ) {		/* extraneous argument */
	(void)fprintf( stderr, usage );
	exit( 1 );
    }	



    if( compress != IGNORE ) {		/* we know whether xtract or creat */
	int isSecured, isCompressed;

	nbytes = ReadHeader( &buf[0] );
	Identify( &buf[0], nbytes, &isSecured, &isCompressed );

	if ( (( isSecured) && (secure == YAH)) || 
	     ((!isSecured) && (secure == NAH)) )
	    secure = IGNORE;

	if ( (( isCompressed) && (compress == YAH)) || 
	     ((!isCompressed) && (compress == NAH)) )
	    compress = IGNORE;
    } else
	nbytes = 0;			/* header not read */

    if( compress != IGNORE ) {		/* need to fork a back end */
	char	cmnd[256];

	if( block == YAH || verify == YAH || dotInterval != 0 ||
					(secure==YAH)&&(compress==YAH) ) {
	    char     newKeys[10];
	    char     optArgs[256];

	    optArgs[0] = newKeys[0] = '\0';
	    if( block == YAH )
		strcat( &newKeys[0], "b" );
	    if( dotInterval ) {
		char	intStr[20];
		strcat( &newKeys[0], "V" );
		sprintf( &intStr[0], " %ld", dotInterval );
		strcat( &optArgs[0], &intStr[0] );
		dotInterval = 0;
	    }
	    if( verify == YAH ) {
		strcat( &newKeys[0], "v" );
		strcat( &optArgs[0], " " ); strcat( &optArgs[0], vFile );
	    }
	    if( secure == YAH && compress == YAH ) {
		strcat( &newKeys[0], "s" );
		strcat( &optArgs[0], " " ); strcat( &optArgs[0], sFile );
		secure = IGNORE;
	    }

#ifdef ibmrs
            if( compress1_flag == 1)
            {
	      sprintf( &cmnd[0], "%s | %s %s %s", 
			    (compress == YAH)?COMPRESS1:UNCOMPRESS1, 
			    argv[0], newKeys, optArgs );
            }
            else {
#endif
	    sprintf( &cmnd[0], "%s | %s %s %s", 
			    (compress == YAH)?COMPRESS:UNCOMPRESS, 
			    argv[0], newKeys, optArgs );
#ifdef ibmrs
            }
#endif
	    block = verify = NAH;
	    compress = IGNORE;
	} else {
#ifdef ibmrs
            if( compress1_flag == 1) {
	      strcpy( &cmnd[0], (compress == YAH)?COMPRESS1:UNCOMPRESS1 );
            }
            else
            {
#endif
	    strcpy( &cmnd[0], (compress == YAH)?COMPRESS:UNCOMPRESS );
#ifdef ibmrs
            }
#endif
        }

	writeFp = popen( cmnd, "w" );
	closeFunc = pclose;
	signal( SIGPIPE, SigPipeHdlr );
    } else {					/* copy stdin to stdout */
	writeFp = stdout;
	closeFunc = fclose;
    }


    fltrCnt = 0;
    if( secure == YAH )
	filter[fltrCnt++] = SecureFltr;
    else if ( secure == NAH )
	filter[fltrCnt++] = UnSecureFltr;

    if( block == YAH )
	filter[fltrCnt++] = BlockFltr;

    if( dotInterval ) {
	dotInterval *= 1024;			/* convert kbytes to bytes */
	filter[fltrCnt++] = DotFltr;
    }

    if( verify == YAH )
	filter[fltrCnt++] = SumFltr;
    
    filter[fltrCnt] = FilterEnd;

    if( FilterBeg( buf, nbytes, &filter[0] ) )
	(void)fprintf( stderr, "Failure detected\n" );

    exit( (*closeFunc)( writeFp ) );
}

