Download | Plain Text | Line Numbers


#include <assert.h>
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "../cipher.h"
 
#define BLOCKSIZE 8192
 
#define SET_LEN(sv, len) \
  do { SvPVX(sv)[len] = '\0'; SvCUR_set(sv, len); } while (0)
 
/* 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 CIPHER_GV(s)                  IoFMT_GV(s)
#define CIPHER_SV(s)                  ((SV *) CIPHER_GV(s))
#define CIPHER_BUFFER(s)              ((ctx_t *)SvPVX(CIPHER_SV(s)))
#define CLEAR_CIPHER_SV(s)            SvCUR_set(CIPHER_SV(s), 0)
 
#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(DECRYPT_SV(s))
#define SET_DECRYPT_BUFFER_LEN(s, n)  SvCUR_set(DECRYPT_SV(s), n)
 
static unsigned
decrypt(SV *in_cipher, SV *in_sv, SV *out_sv)
{
  /* here is where the actual decryption takes place */
  ctx_t *cipher = (ctx_t *)SvPVX(in_cipher);
  char *in_buffer = (char *)SvPVX(in_sv);
  char *out_buffer;
  size_t in_len = SvCUR(in_sv);
  size_t out_len;
 
  /* 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, in_len);
  out_buffer = (char *)SvPVX(out_sv);
 
  /* decrypt */
  out_len = CipherUpdate(cipher, in_buffer, in_len);
 
  /* input has been consumed, so set length to 0 */
  SET_LEN(in_sv, 0);
 
  /* set decrypt buffer length */
  assert(out_len <= BLOCKSIZE);
  SET_LEN(out_sv, out_len);
 
  /* return the size of the decrypt buffer */
  return out_len;
}
 
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);
    /* eof/error when nothing read so far */
    if (n <= 0 && i == size)
      return n;
    /* eof/error when something already read */
    if (n <= 0)
      return size - i;
    if (n == i)
      return size;
    i -= n;
  }
}
 
static void
pre_decrypt(int idx)
{}
 
static void
post_decrypt(int idx)
{}
 
static I32
filter_decrypt(pTHX_ int idx, SV *buf_sv, int maxlen)
{
  char *out_ptr;
  char *p;
  char *nl = "\n";
  int n;
  SV *my_sv = FILTER_DATA(idx);
 
  /* 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
     */
    pre_decrypt(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 DEBUG
  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 DEBUG
        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 DEBUG
          warn("recycle(%d): leaving %d, returning %d [%.999s]\n",
            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 at most BLOCKSIZE - 1 bytes from the file into the encrypt buffer:
     *  - 1 char ("#"-char) will be prepended during first call of CipherUpdate()
     *  - 1 char is needed for the string termination BUT perl already
     *    allocates +1 byte with newSV()
     */
    if ((n = readblock(idx + 1, ENCRYPT_SV(my_sv), BLOCKSIZE - 1)) <= 0)
    {
      /* either EOF or an error */
#ifdef DEBUG
      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
       */
      post_decrypt(idx);
      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 DEBUG
    else
      warn("filter_read(%d): read %d bytes\n", idx, n);
#endif
 
#ifdef DEBUG
    warn("filter_decrypt(%d): sub-filter returned %d: '%.999s'\n",
      idx, n, SvPV(my_sv, PL_na));
#endif
 
    /* now decrypt a block */
    n = decrypt(CIPHER_SV(my_sv), ENCRYPT_SV(my_sv), DECRYPT_SV(my_sv));
 
#ifdef DEBUG
    warn("decrypt(%d): returned %d [%.999s]\n", idx, n, SvPVX(DECRYPT_SV(my_sv)));
#endif
  }
}
 
 
MODULE = Confixx::Filter  PACKAGE = Confixx::Filter
 
PROTOTYPES: DISABLE
 
BOOT:
{
#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:
{
  int ret;
  SV *sv;
 
  /* main/decrypted SV */
  DECRYPT_SV(sv) = newSV(BLOCKSIZE);
  (void) SvPOK_only(DECRYPT_SV(sv));
  SET_LEN(DECRYPT_SV(sv), 0);
 
  /* add filter */
  filter_add(filter_decrypt, DECRYPT_SV(sv));
 
  /* cipher SV */
  CIPHER_GV(sv) = (GV *)newSV(sizeof(ctx_t));
  (void) SvPOK_only(CIPHER_GV(sv));
  (void) memset(CIPHER_BUFFER(sv), 0, sizeof(ctx_t));
  ret = CipherInit(CIPHER_BUFFER(sv));
  if (ret != 256)
    croak("unexpected return value of CipherInit(): %d\n", ret);
 
  /* copy pointer to output buffer inside ciphers first bytes */
  CIPHER_BUFFER(sv)->out = DECRYPT_BUFFER(sv);
 
  /* encrypted SV */
  ENCRYPT_GV(sv) = (GV *)newSV(BLOCKSIZE);
  (void) SvPOK_only(ENCRYPT_SV(sv));
  SET_LEN(ENCRYPT_SV(sv), 0);
 
  /* enable first time flag */
  FIRST_TIME(sv) = TRUE;
  /* 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);