The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* 
 * Filename : decrypt.xs
 * 
 * Author   : Paul Marquess 
 * Date     : 20th July 2000
 * Version  : 1.05
 *
 */

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "../Call/ppport.h"

#ifdef FDEBUG
static int fdebug = 0;
#endif

/* constants specific to the encryption format */
#define CRYPT_MAGIC_1	0xff
#define CRYPT_MAGIC_2	0x00

#define HEADERSIZE	2
#define BLOCKSIZE	4


#define SET_LEN(sv,len) \
        do { SvPVX(sv)[len] = '\0'; SvCUR_set(sv, len); } while (0)


static unsigned XOR [BLOCKSIZE] = {'P', 'e', 'r', 'l' } ;


/* Internal defines */
#ifdef PERL_FILTER_EXISTS
#  define CORE_FILTER_COUNT \
    (PL_parser && PL_parser->rsfp_filters ? av_len(PL_parser->rsfp_filters) : 0)
#else
#  define CORE_FILTER_COUNT \
    (PL_rsfp_filters ? av_len(PL_rsfp_filters) : 0)
#endif

#define FILTER_COUNT(s)		IoPAGE(s)
#define FILTER_LINE_NO(s)	IoLINES(s)
#define FIRST_TIME(s)		IoLINES_LEFT(s)

#define ENCRYPT_GV(s)		IoTOP_GV(s)
#define ENCRYPT_SV(s)		((SV*) ENCRYPT_GV(s))
#define ENCRYPT_BUFFER(s)	SvPVX(ENCRYPT_SV(s))
#define CLEAR_ENCRYPT_SV(s)	SvCUR_set(ENCRYPT_SV(s), 0)

#define DECRYPT_SV(s)		s
#define DECRYPT_BUFFER(s)	SvPVX(DECRYPT_SV(s))
#define CLEAR_DECRYPT_SV(s)	SvCUR_set(DECRYPT_SV(s), 0)
#define DECRYPT_BUFFER_LEN(s)	SvCUR(DECRYPT_SV(s))
#define DECRYPT_OFFSET(s) 	IoPAGE_LEN(s)
#define SET_DECRYPT_BUFFER_LEN(s,n)	SvCUR_set(DECRYPT_SV(s), n)

static unsigned
Decrypt(SV *in_sv, SV *out_sv)
{
	/* Here is where the actual decryption takes place */

    	unsigned char * in_buffer  = (unsigned char *) SvPVX(in_sv) ;
    	unsigned char * out_buffer ;
    	unsigned size = SvCUR(in_sv) ;
    	unsigned index = size ;
    	int i ;

	/* make certain that the output buffer is big enough 		*/
	/* as the output from the decryption can never be larger than	*/
	/* the input buffer, make it that size				*/
	SvGROW(out_sv, size) ;
	out_buffer = (unsigned char *) SvPVX(out_sv) ;

        /* XOR */
        for (i = 0 ; i < size ; ++i) 
            out_buffer[i] = (unsigned char)( XOR[i] ^ in_buffer[i] ) ;

	/* input has been consumed, so set length to 0 */
	SET_LEN(in_sv, 0) ;

	/* set decrypt buffer length */
	SET_LEN(out_sv, index) ;

	/* return the size of the decrypt buffer */
 	return (index) ;
}

static int
ReadBlock(int idx, SV *sv, unsigned size)
{   /* read *exactly* size bytes from the next filter */
    int i = size;
    while (1) {
        int n = FILTER_READ(idx, sv, i) ;
        if (n <= 0 && i==size)  /* eof/error when nothing read so far */
            return n ;
        if (n <= 0)             /* eof/error when something already read */
            return size - i;
        if (n == i)
            return size ;
        i -= n ;
    }
}

static void
preDecrypt(int idx)
{
    /*	If the encrypted data starts with a header or needs to do some
	initialisation it can be done here 

	In this case the encrypted data has to start with a fingerprint,
	so that is checked.
    */

    SV * sv = FILTER_DATA(idx) ;
    unsigned char * buffer ;


    /* read the header */
    if (ReadBlock(idx+1, sv, HEADERSIZE) != HEADERSIZE)
	croak("truncated file") ;

    buffer = (unsigned char *) SvPVX(sv) ;

    /* check for fingerprint of encrypted data */
    if (buffer[0] != CRYPT_MAGIC_1 || buffer[1] != CRYPT_MAGIC_2) 
            croak( "bad encryption format" );
}

static void
postDecrypt()
{
}

static I32
filter_decrypt(pTHX_ int idx, SV *buf_sv, int maxlen)
{
    SV   *my_sv = FILTER_DATA(idx);
    char *nl = "\n";
    char *p;
    char *out_ptr;
    int n;

    /* check if this is the first time through */
    if (FIRST_TIME(my_sv)) {

	/* Mild paranoia mode - make sure that no extra filters have 	*/
	/* been applied on the same line as the use Filter::decrypt	*/
        if (CORE_FILTER_COUNT > FILTER_COUNT(my_sv) )
	    croak("too many filters") ; 

	/* As this is the first time through, so deal with any 		*/
	/* initialisation required 					*/
        preDecrypt(idx) ;

	FIRST_TIME(my_sv) = FALSE ;
        SET_LEN(DECRYPT_SV(my_sv), 0) ;
        SET_LEN(ENCRYPT_SV(my_sv), 0) ;
        DECRYPT_OFFSET(my_sv)    = 0 ;
    }

#ifdef FDEBUG
    if (fdebug)
	warn("**** In filter_decrypt - maxlen = %d, len buf = %d idx = %d\n", 
		maxlen, SvCUR(buf_sv), idx ) ;
#endif

    while (1) {

	/* anything left from last time */
	if ((n = SvCUR(DECRYPT_SV(my_sv)))) {

	    out_ptr = SvPVX(DECRYPT_SV(my_sv)) + DECRYPT_OFFSET(my_sv) ;

	    if (maxlen) { 
		/* want a block */ 
#ifdef FDEBUG
		if (fdebug)
		    warn("BLOCK(%d): size = %d, maxlen = %d\n", 
			idx, n, maxlen) ;
#endif

	        sv_catpvn(buf_sv, out_ptr, maxlen > n ? n : maxlen );
		if(n <= maxlen) {
        	    DECRYPT_OFFSET(my_sv) = 0 ;
	            SET_LEN(DECRYPT_SV(my_sv), 0) ;
		}
		else {
        	    DECRYPT_OFFSET(my_sv) += maxlen ;
	            SvCUR_set(DECRYPT_SV(my_sv), n - maxlen) ;
		}
	        return SvCUR(buf_sv);
	    }
	    else {
		/* want lines */
                if ((p = ninstr(out_ptr, out_ptr + n, nl, nl + 1))) {

	            sv_catpvn(buf_sv, out_ptr, p - out_ptr + 1);

	            n = n - (p - out_ptr + 1);
		    DECRYPT_OFFSET(my_sv) += (p - out_ptr + 1) ;
	            SvCUR_set(DECRYPT_SV(my_sv), n) ;
#ifdef FDEBUG 
	            if (fdebug)
		        warn("recycle %d - leaving %d, returning %d [%.999s]", 
				idx, n, SvCUR(buf_sv), SvPVX(buf_sv)) ;
#endif

	            return SvCUR(buf_sv);
	        }
	        else /* no EOL, so append the complete buffer */
	            sv_catpvn(buf_sv, out_ptr, n) ;
	    }
	    
	}


	SET_LEN(DECRYPT_SV(my_sv), 0) ;
        DECRYPT_OFFSET(my_sv) = 0 ;

	/* read from the file into the encrypt buffer */
 	if ( (n = ReadBlock(idx+1, ENCRYPT_SV(my_sv), BLOCKSIZE)) <= 0)
	{
	    /* Either EOF or an error */

#ifdef FDEBUG
	    if (fdebug)
	        warn ("filter_read %d returned %d , returning %d\n", idx, n,
	            (SvCUR(buf_sv)>0) ? SvCUR(buf_sv) : n);
#endif

	    /* If the decrypt code needs to tidy up on EOF/error, 
		now is the time  - here is a hook */
	    postDecrypt() ; 

	    filter_del(filter_decrypt);  

 
            /* If error, return the code */
            if (n < 0)
                return n ;

	    /* return what we have so far else signal eof */
	    return (SvCUR(buf_sv)>0) ? SvCUR(buf_sv) : n;
	}

#ifdef FDEBUG
	if (fdebug)
	    warn("  filter_decrypt(%d): sub-filter returned %d: '%.999s'",
		idx, n, SvPV(my_sv,PL_na));
#endif

	/* Now decrypt a block */
	n = Decrypt(ENCRYPT_SV(my_sv), DECRYPT_SV(my_sv)) ;

#ifdef FDEBUG 
	if (fdebug) 
	    warn("Decrypt (%d) returned %d [%.999s]\n", idx, n, SvPVX(DECRYPT_SV(my_sv)) ) ;
#endif 

    }
}


MODULE = Filter::decrypt	PACKAGE = Filter::decrypt

PROTOTYPES:	DISABLE

BOOT:
    /* Check for the presence of the Perl Compiler */
    if (get_av("B::NULL::ISA",0)) {
        croak("Aborting, Compiler detected") ;
    }
#ifndef BYPASS
    /* Don't run if this module is dynamically linked */
    if (!isALPHA(SvPV(GvSV(CvFILEGV(cv)), PL_na)[0]))
	croak("module is dynamically linked. Recompile as a static module") ;
#ifdef DEBUGGING
	/* Don't run if compiled with DEBUGGING */
	croak("recompile without -DDEBUGGING") ;
#endif
        
	/* Double check that DEBUGGING hasn't been enabled */
	if (PL_debug)
	    croak("debugging flags detected") ;
#endif


void
import(module)
    SV *	module
    PPCODE:
    {

        SV * sv = newSV(BLOCKSIZE) ;

	/* make sure the Perl debugger isn't enabled */
	if( PL_perldb )
	    croak("debugger disabled") ;

        filter_add(filter_decrypt, sv) ;
	FIRST_TIME(sv) = TRUE ;

        ENCRYPT_GV(sv) = (GV*) newSV(BLOCKSIZE) ;
        (void)SvPOK_only(DECRYPT_SV(sv));
        (void)SvPOK_only(ENCRYPT_SV(sv));
        SET_LEN(DECRYPT_SV(sv), 0) ;
        SET_LEN(ENCRYPT_SV(sv), 0) ;


        /* remember how many filters are enabled */
        FILTER_COUNT(sv) = CORE_FILTER_COUNT ;
	/* and the line number */
	FILTER_LINE_NO(sv) = PL_curcop->cop_line ;

    }

void
unimport(...)
    PPCODE:
    /* filter_del(filter_decrypt); */