1621 lines
43 KiB
C
1621 lines
43 KiB
C
|
#include "generic/dp.h"
|
|||
|
#include <errno.h>
|
|||
|
#include <string.h>
|
|||
|
|
|||
|
/*
|
|||
|
* You will find below a description of the filter functions.
|
|||
|
*
|
|||
|
* PROTOTYPE: int Filter (char *inBuf, int inLength, char **outBuf,
|
|||
|
* int *outLength, void **data, Tcl_Interp *interp, int mode)
|
|||
|
*
|
|||
|
* ARGUMENTS: We provide below the "usual" interpretation of the parameters. For
|
|||
|
* the treatment of special cases, please refer to the mode parameter.
|
|||
|
*
|
|||
|
* (in) inBuf: If defined, it points to the input buffer. It is assumed that the
|
|||
|
* filter consumes the entire input. If it is not possible to generate output
|
|||
|
* for all input, part of it can be buffered internally.
|
|||
|
*
|
|||
|
* (in) inLength: The length of the input buffer.
|
|||
|
*
|
|||
|
* (out) outBuf: When the filter generates output, it allocates an output
|
|||
|
* buffer, and stores all the available output in it. The address of the
|
|||
|
* output buffer has to be stored in *outBuf. If no output is generated
|
|||
|
* (i.e. outLength is 0), this parameter is not used. Except when *output
|
|||
|
* contains the internal parameters of the filter functions (i.e. when mode
|
|||
|
* is DP_FILTER_GET), the filter function should not manage this space after
|
|||
|
* allocating it. The responsability for doing so belongs to the plugin channel
|
|||
|
* code. When mode is DP_FILTER_GET the string has to be managed by the filter
|
|||
|
* function; this is done to avoid unnecessary reconstruction of the parameter
|
|||
|
* string.
|
|||
|
*
|
|||
|
* (out) outLength: The filter must store here the length of the output
|
|||
|
* buffer. If there is no output, *outLength has to be set to 0.
|
|||
|
*
|
|||
|
* (in) data: The filter function can store a pointer to its local
|
|||
|
* datastructures in *data. The plugin channel code will initializa *data with
|
|||
|
* NULL. The argument for infilter will be different from the argument for the
|
|||
|
* outfilter. The filter function code has the responsability to manage data
|
|||
|
* referred to by *data. In particular, the filter should free all the allocated
|
|||
|
* space when mode is DP_FILTER_CLOSE.
|
|||
|
*
|
|||
|
* (in) interp: Pointer to the Tcl interpreter in which the filter was
|
|||
|
* created. It can be used to create filters that evaluate Tcl expressions (see,
|
|||
|
* for example. the tclfilter function).
|
|||
|
*
|
|||
|
* (int) mode: This parameter can have four distinct values. They are discussed
|
|||
|
* below:
|
|||
|
*
|
|||
|
* DP_FILTER_NORMAL: In this mode the interpretation of parameters is the
|
|||
|
* one given above. The filter can either generate output or not. This mode
|
|||
|
* corresponds to setting the "-buffering" argument of the channel to "full" or
|
|||
|
* "line".
|
|||
|
*
|
|||
|
* DP_FILTER_FLUSH: In this mode the interpretation of parameters is the
|
|||
|
* one given above. The filter is encouraged to generate output, even if this
|
|||
|
* would not be the case. The filter can ignore this argument and behave like
|
|||
|
* mode was DP_FILTER_NORMAL. This mode corresponds to setting the "-buffering"
|
|||
|
* argument of the channel to "none".
|
|||
|
*
|
|||
|
* DP_FILTER_CLOSE: In this mode the interpretation of parameters is the
|
|||
|
* one given above. This value of the parameter signals that the filter
|
|||
|
* channel is about to be closed, and this is the last call to the filter. The
|
|||
|
* filter should deallocate all its internal data structures, and should
|
|||
|
* generate all the output it can. NOTE: the filter should never try to
|
|||
|
* deallocate any *outBuf pointer that was returned to DP. These buffers are
|
|||
|
* managed and freed at the DP level.
|
|||
|
*
|
|||
|
* DP_FILTER_EOF: In this mode the interpretation of parameters is the
|
|||
|
* one given above. This value of the parameter signals that the underlying
|
|||
|
* channel reached eof, no more input is expected. It is important to
|
|||
|
* distinguish this case when the filter function buffers data; when the
|
|||
|
* channel is closed the output that would be generated based on the buffered data
|
|||
|
* will be lost if the channel is used for input (see the uuencode code for an
|
|||
|
* example).
|
|||
|
*
|
|||
|
* DP_FILTER_SET: All parameters are ignored, except for inBuf and
|
|||
|
* inLength. In this case the input buffer is a string that is passed "as is"
|
|||
|
* by TCL from the "fconfigure <channel> -[inset | outset] <string>" command.
|
|||
|
* The filter should use the string to set its internal variables. For example,
|
|||
|
* this mechanism could be used to set a different pgp key for each channel.
|
|||
|
*
|
|||
|
* DP_FILTER_GET: All parameters are ignored, except for outBuf. The filter
|
|||
|
* should store in *outBuf the address of a string that describes its internal
|
|||
|
* state. The string should end with a null character ('\0'). If the string is
|
|||
|
* not static, the filter has to take care of allocating/freeing it. The string
|
|||
|
* is used by DP only once, immediately after the filter returns. In order for
|
|||
|
* the "fconfigure <channel>" command to work with no other arguments, the
|
|||
|
* filter should return a non-empty string even if it has no internal state (see
|
|||
|
* the examples below.)
|
|||
|
*
|
|||
|
*
|
|||
|
* RETURN VALUE: If no error is detected, the return value must be zero. A
|
|||
|
* non-zero return value signals an error. The user should use POSIX error codes
|
|||
|
* to differentiate the possible error cases.
|
|||
|
*/
|
|||
|
|
|||
|
typedef struct {
|
|||
|
char *xorString; /* Pointer to the encoding string. */
|
|||
|
int xorStringLength; /* The length of the encoding string. */
|
|||
|
int counter; /* Position in the encoding string of the next */
|
|||
|
/* byte that will be used for encoding. */
|
|||
|
} xorFilterData;
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Plug1to2 --
|
|||
|
*
|
|||
|
* Given an input string X, this is filtered by replacing each character of
|
|||
|
* the string with two copies of itself.
|
|||
|
* Example: Plug1to2("abc") = "aabbcc".
|
|||
|
* Remark: Plug2to1(Plug1to2(X)) = X.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for the filtered output.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
Plug1to2 (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
static char internal[] = "{no internal arguments}";
|
|||
|
int i;
|
|||
|
char *iTmp, *oTmp;
|
|||
|
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
if(inLength == 0) {
|
|||
|
*outBuf = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if ((*outBuf = (char *)ckalloc(2 * inLength)) == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
for (i = inLength, oTmp = *outBuf, iTmp = inBuf; i; i--, oTmp++, iTmp++) {
|
|||
|
*oTmp++ = *iTmp;
|
|||
|
*oTmp = *iTmp;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = 2 * inLength;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET:
|
|||
|
|
|||
|
/* No argument can be set. */
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
case DP_FILTER_GET:
|
|||
|
|
|||
|
*outBuf = internal;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Plug2to1 --
|
|||
|
*
|
|||
|
* Given an input string X, this is filtered by selecting only the
|
|||
|
* characters that have even indices.
|
|||
|
* Example: Plug2to1("abcdef") = "ace"
|
|||
|
* Remark: Plug2to1(Plug1to2(X)) = X.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for the filtered output.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
Plug2to1 (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
static char internal[] = "{no internal arguments}";
|
|||
|
int i;
|
|||
|
char *iTmp, *oTmp;
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
if(inLength == 0) {
|
|||
|
*outBuf = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
if ((*outBuf = (char *)ckalloc(inLength / 2)) == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
};
|
|||
|
|
|||
|
for (i = inLength / 2, oTmp = *outBuf, iTmp = inBuf; i; i--, iTmp++) {
|
|||
|
*oTmp++ = *iTmp++;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = inLength / 2;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET:
|
|||
|
|
|||
|
/* No argument can be set. */
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
case DP_FILTER_GET:
|
|||
|
|
|||
|
*outBuf = internal;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Identity --
|
|||
|
*
|
|||
|
* Identity filter: given a string X Identity(X) = X.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for the filtered output.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
Identity (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
static char internal[] = "{no internal arguments}";
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
if(inLength == 0) {
|
|||
|
*outBuf = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if ((*outBuf = (char *)ckalloc(inLength)) == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
};
|
|||
|
|
|||
|
memcpy(*outBuf, inBuf, inLength);
|
|||
|
*outLength = inLength;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET:
|
|||
|
|
|||
|
/* No argument can be set. */
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
case DP_FILTER_GET:
|
|||
|
|
|||
|
*outBuf = internal;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* xor --
|
|||
|
*
|
|||
|
* A parameter string has to be provided before first using the
|
|||
|
* filter. The input bytes will be xor'd with the bytes in the parameter,
|
|||
|
* taken in circular order. This order will be preserved between succesive
|
|||
|
* calls. The same string must be used for both encoding and decoding.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for the filtered output. Manages dynamic local
|
|||
|
* datastructure (xoprFilterData).
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
Xor (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
xorFilterData *fD;
|
|||
|
|
|||
|
|
|||
|
if(*data == NULL) {
|
|||
|
|
|||
|
/* No filter specific data was allocated. */
|
|||
|
|
|||
|
fD = (xorFilterData *)ckalloc(sizeof(xorFilterData));
|
|||
|
|
|||
|
if(fD == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
*data = (void *)fD;
|
|||
|
|
|||
|
fD->xorString = NULL;
|
|||
|
fD->xorStringLength = 0;
|
|||
|
fD->counter = 0;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
fD = (xorFilterData *)(*data);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
if(fD->xorString == NULL) {
|
|||
|
/* Refuse encoding if no parameter string was specified. */
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
if(inLength > 0) {
|
|||
|
|
|||
|
int i;
|
|||
|
char *s, *d;
|
|||
|
|
|||
|
*outBuf = (char *)ckalloc(inLength);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
for(i = 0, s = inBuf, d = *outBuf; i < inLength; i++, s++, d++) {
|
|||
|
*d = *s ^ (fD->xorString[(fD->counter)++]);
|
|||
|
if((fD->counter) >= (fD->xorStringLength)) {
|
|||
|
fD->counter = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
*outBuf = NULL;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = inLength;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
/* Last call before the channel is closed. Since there is no buffering,
|
|||
|
* only the storage space for xorString has to be freed.
|
|||
|
*/
|
|||
|
|
|||
|
*outBuf = NULL;
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
if (fD->xorString != NULL) {
|
|||
|
ckfree(fD->xorString);
|
|||
|
fD->xorString = NULL;
|
|||
|
}
|
|||
|
ckfree((char *)fD);
|
|||
|
*data = NULL;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET: /* Set the string that will be used to xor data. */
|
|||
|
|
|||
|
if (fD->xorString != NULL) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
fD->xorString = (char *)ckalloc(inLength);
|
|||
|
if (fD->xorString == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
memcpy(fD->xorString, inBuf, inLength);
|
|||
|
fD->xorStringLength = inLength;
|
|||
|
|
|||
|
/* Start using the string from the beginning. */
|
|||
|
fD->counter = 0;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_GET: /* Return the string that is used to xor data. */
|
|||
|
|
|||
|
if (fD->xorString == NULL) {
|
|||
|
*outBuf = "{xor string not set}";
|
|||
|
} else {
|
|||
|
*outBuf = fD->xorString;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* packon --
|
|||
|
*
|
|||
|
* Adds a header of 6 bytes to each packet that is written to or read from
|
|||
|
* the underlying channel. The header contains a right-aligned nonnegative
|
|||
|
* integer in base 10; it is padded to the left with non-significant
|
|||
|
* zeroes. A packet is the amount of data that is written to (read
|
|||
|
* from) the underlying channel in one step. If there is more data than it
|
|||
|
* can fit in one of tcl's buffers (usually 4k), then one write (read) will
|
|||
|
* be translated into several packets. Though the filter will work both for
|
|||
|
* input and output, it is more likely that the filter will be used for
|
|||
|
* automatically attaching the packet length to outgoing (written)
|
|||
|
* messages. No internal parameters.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for the filtered output.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
PackOn (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
if(inLength > 1000000) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
*outBuf = (char *)ckalloc(inLength + 6);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
sprintf(*outBuf, "%.6d", inLength);
|
|||
|
memcpy((*outBuf) + 6, inBuf, inLength);
|
|||
|
*outLength = inLength + 6;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
/* Last call before the channel is closed. Since there is no
|
|||
|
* buffering, only the storage space for xorString has to be freed.
|
|||
|
*/
|
|||
|
|
|||
|
*outBuf = NULL;
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET: /* No setup is allowed for this filter. */
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
case DP_FILTER_GET: /* No filter option can be set here. */
|
|||
|
|
|||
|
*outBuf = "{no internal arguments}";
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This value of UU_LINE was chosen for compatibility with Unix. If you change it,
|
|||
|
* you should keep in mind that if _must_ be divisible by zero. Functions
|
|||
|
* uuencode and uudecode are unix compatible (i.e. you can uuencode or uudecode
|
|||
|
* either with these functions either with the corresponding Unix utilities, and
|
|||
|
* obtain the same result.
|
|||
|
*/
|
|||
|
|
|||
|
#define UU_LINE 45
|
|||
|
|
|||
|
|
|||
|
/* Prototypes of functions that do the actual uuencoding and decoding. */
|
|||
|
|
|||
|
static void uuencode _ANSI_ARGS_((unsigned char *from, unsigned char *to,
|
|||
|
int howMany));
|
|||
|
static void uudecode _ANSI_ARGS_((unsigned char *from, unsigned char *to,
|
|||
|
int howMany));
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* uuencode --
|
|||
|
*
|
|||
|
* Same functionality as Unix uuencode (see "man 5 uuencode" for
|
|||
|
* details). No internal parameters.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for the filtered output. Manages dynamic local
|
|||
|
* datastructure (encodeData).
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
Uuencode (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
/* "740" represents the acces rights a file that was encoded with the
|
|||
|
* dp uuencode function and decoded with the unix uudecode function will
|
|||
|
* have. The corresponding file name will be be uufilter.
|
|||
|
* Maximum care should be taken when modifying the first constant below; its
|
|||
|
* structure is used to recognize the start of the data part of an uuencoded
|
|||
|
* file (for details please refer to the corresponding comment in the code for
|
|||
|
* uudecode). The second constant can be modified freely; this form is added to
|
|||
|
* preserve Unix compatibility.
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
char *header = "begin 740 uufilter\n";
|
|||
|
char *trailer = " \nend\n";
|
|||
|
|
|||
|
typedef struct {
|
|||
|
char buffer [UU_LINE]; /* Stores incomplete lines between calls. */
|
|||
|
int used; /* Number of bytes used in the buffer. */
|
|||
|
int first; /* If 1, the header must be output first. */
|
|||
|
int trailerOutput; /* Used to avoid double output of trailer. */
|
|||
|
int firstEOFCall; /* Identifies first call with mode == DP_FILTER_EOF */
|
|||
|
} encodeData;
|
|||
|
|
|||
|
int headerLength, trailerLength, spaceNeeded;
|
|||
|
char *putHere;
|
|||
|
|
|||
|
encodeData *eD;
|
|||
|
|
|||
|
|
|||
|
if(*data == NULL) {
|
|||
|
|
|||
|
/* No filter specific data was allocated. */
|
|||
|
|
|||
|
eD = (encodeData *)ckalloc(sizeof(encodeData));
|
|||
|
|
|||
|
if(eD == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
*data = (void *)eD;
|
|||
|
eD->used = 0;
|
|||
|
eD->first = 1;
|
|||
|
eD->trailerOutput = 0;
|
|||
|
eD->firstEOFCall = 1;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
eD = (encodeData *)(*data);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
if(mode == DP_FILTER_EOF) {
|
|||
|
if((!(eD->firstEOFCall)) || (inLength == 0)) {
|
|||
|
eD->firstEOFCall = 0;
|
|||
|
goto jump_eof;
|
|||
|
}
|
|||
|
eD->firstEOFCall = 0;
|
|||
|
}
|
|||
|
|
|||
|
if(inLength == 0) {
|
|||
|
*outLength = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
spaceNeeded = ((4 * UU_LINE) / 3 + 2) * ((eD->used + inLength) / UU_LINE);
|
|||
|
|
|||
|
if(eD->first) {
|
|||
|
|
|||
|
/* If this is the first call, insert the header first. */
|
|||
|
|
|||
|
headerLength = strlen(header);
|
|||
|
|
|||
|
*outBuf = putHere = (char *)ckalloc(spaceNeeded + headerLength);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = spaceNeeded + headerLength;
|
|||
|
|
|||
|
eD->first = 0;
|
|||
|
|
|||
|
memcpy(putHere, header, headerLength);
|
|||
|
|
|||
|
putHere += headerLength;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
*outBuf = putHere = (char *)ckalloc(spaceNeeded);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = spaceNeeded;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/* eD->used is not zero when we first get here. */
|
|||
|
|
|||
|
/* Until we have at least one full line to encode, we go
|
|||
|
* ahead. Incomplete lines are not encoded at this point; either they
|
|||
|
* will be completed later, or they will encoded and output when the
|
|||
|
*channel is closed.
|
|||
|
*/
|
|||
|
|
|||
|
while(eD->used + inLength >= UU_LINE) {
|
|||
|
|
|||
|
memcpy(eD->buffer + eD->used, inBuf, UU_LINE - eD->used);
|
|||
|
uuencode(eD->buffer, putHere, UU_LINE);
|
|||
|
putHere += 2 + (4 * UU_LINE) / 3;
|
|||
|
inBuf += UU_LINE - eD->used;
|
|||
|
inLength -= UU_LINE -eD->used;
|
|||
|
eD->used = 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
memcpy(eD->buffer, inBuf, inLength);
|
|||
|
eD->used = inLength;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
|
|||
|
/* If there is an incomplete line whose encoding was postponed,
|
|||
|
* output it. Then output the trailer.
|
|||
|
*/
|
|||
|
|
|||
|
if(eD->first) {
|
|||
|
|
|||
|
/* The filtered was called only to close it; there was no other
|
|||
|
* activity.
|
|||
|
*/
|
|||
|
|
|||
|
*outBuf = NULL;
|
|||
|
*outLength = 0;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/* Continue with the case for DP_FILTER_EOF below! */
|
|||
|
|
|||
|
|
|||
|
jump_eof:
|
|||
|
if(!(eD->trailerOutput)) {
|
|||
|
trailerLength = strlen(trailer);
|
|||
|
|
|||
|
if(eD->used == 0) {
|
|||
|
|
|||
|
*outBuf = putHere = (char *)ckalloc(trailerLength);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = trailerLength;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
/* Establish how many bytes will be needed to encode the incomplete
|
|||
|
* line we are trying to output.
|
|||
|
*/
|
|||
|
|
|||
|
int needed = 2 + (4 * (((eD->used + 2) / 3) * 3)) / 3;
|
|||
|
|
|||
|
*outBuf = putHere = (char *)ckalloc(needed + trailerLength);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
uuencode(eD->buffer, putHere, eD->used);
|
|||
|
putHere += needed;
|
|||
|
*outLength = trailerLength + needed;
|
|||
|
}
|
|||
|
|
|||
|
memcpy(putHere, trailer, trailerLength);
|
|||
|
|
|||
|
eD->trailerOutput = 1;
|
|||
|
} else {
|
|||
|
if(mode == DP_FILTER_EOF) {
|
|||
|
*outLength = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(mode == DP_FILTER_CLOSE) {
|
|||
|
ckfree((void *)eD);
|
|||
|
*data = NULL;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET:
|
|||
|
|
|||
|
/* No option is accepted. */
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
|
|||
|
case DP_FILTER_GET:
|
|||
|
|
|||
|
*outBuf = "{no internal arguments}";
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This function implements the actual uuencode algorithm. For details
|
|||
|
* see "man 5 uuencode" .
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
uuencode (from, to, howMany)
|
|||
|
unsigned char *from; /* (in) Where to take the data from. */
|
|||
|
unsigned char *to; /* (in) Where to put the encoded data to. */
|
|||
|
int howMany; /* (in) Number of bytes to encode. */
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
*to++ = howMany + ' ';
|
|||
|
|
|||
|
for(i = 0; i < howMany; i += 3) {
|
|||
|
*to++ = (from[i] >> 2) + ' ';
|
|||
|
*to++ = (((from[i] & 0x03) << 4) | (from[i+1] >> 4)) + ' ';
|
|||
|
*to++ = (((from[i+1] & 0x0f) << 2) | (from[i+2] >> 6)) + ' ';
|
|||
|
*to++ = (from[i+2] & 0x3f) + ' ';
|
|||
|
}
|
|||
|
|
|||
|
*to = '\n';
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* uudecode --
|
|||
|
*
|
|||
|
* Same basic functionality as Unix uudecode (see "man 5 uuencode" for
|
|||
|
* details). No internal parameters.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for the filtered output. Manages dynamic local
|
|||
|
* datastructure (decodeData).
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
Uudecode (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
|
|||
|
#define my_min(x, y) (((x) < (y)) ? (x) : (y))
|
|||
|
#define isoctal(x) (((x) >= '0') && ((x) <= '7'))
|
|||
|
|
|||
|
|
|||
|
typedef struct {
|
|||
|
char buffer [(4 * UU_LINE) / 3 + 2]; /* Stores incomplete lines between
|
|||
|
calls. */
|
|||
|
int used; /* Number of bytes used in the buffer. */
|
|||
|
int headerSeen; /* The standard uuencode header was identified. */
|
|||
|
int endSeen; /* A uuencoded line with lenght zero was seen. */
|
|||
|
int matchOK; /* The "begin XXX " part of the header was matched. */
|
|||
|
int justWaitForEOL; /* Self explanatory. */
|
|||
|
}
|
|||
|
decodeData;
|
|||
|
|
|||
|
int maxSpaceNeeded;
|
|||
|
|
|||
|
decodeData *dD;
|
|||
|
|
|||
|
|
|||
|
if(*data == NULL) {
|
|||
|
|
|||
|
/* No filter specific data was allocated. */
|
|||
|
|
|||
|
dD = (decodeData *)ckalloc(sizeof(decodeData));
|
|||
|
|
|||
|
if(dD == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
*data = (void *)dD;
|
|||
|
dD->used = 0;
|
|||
|
dD->headerSeen = 0;
|
|||
|
dD->endSeen = 0;
|
|||
|
dD->matchOK = 0;
|
|||
|
dD->justWaitForEOL = 0;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
dD = (decodeData *)(*data);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
/* Here we identify the standard header of a uuencoded data stream. */
|
|||
|
|
|||
|
while(!(dD->headerSeen)) {
|
|||
|
|
|||
|
if(dD->justWaitForEOL) {
|
|||
|
|
|||
|
/* Either we already identified the "begin XXX " part header,
|
|||
|
* either we found a line that does not contain the header for
|
|||
|
* sure. In both cases consume input until '\n' is found.
|
|||
|
*/
|
|||
|
|
|||
|
for(/* empty */; inLength > 0; inBuf++, inLength--) {
|
|||
|
if(*inBuf == '\n') {
|
|||
|
|
|||
|
inBuf++;
|
|||
|
inLength--;
|
|||
|
|
|||
|
dD->used = 0;
|
|||
|
dD->justWaitForEOL = 0;
|
|||
|
|
|||
|
if(dD->matchOK) {
|
|||
|
/* Header found, the next line contains data. */
|
|||
|
dD->headerSeen = 1;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* No more input, but no output yet. */
|
|||
|
if(inLength == 0) {
|
|||
|
|
|||
|
*outBuf = NULL;
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
/* 10 below is strlen("begin ddd "), d is an octal digit. This
|
|||
|
* is related to the structure of the header defined in the
|
|||
|
* uuencode function above. Its structure reflects the Unix
|
|||
|
* standard.
|
|||
|
*/
|
|||
|
|
|||
|
int stillNeeded = 10 - dD->used;
|
|||
|
int canGet = my_min(stillNeeded, inLength);
|
|||
|
|
|||
|
memcpy(dD->buffer + dD->used, inBuf, canGet);
|
|||
|
|
|||
|
dD-> used += canGet;
|
|||
|
inBuf += canGet;
|
|||
|
inLength -= canGet;
|
|||
|
|
|||
|
if(stillNeeded == canGet) {
|
|||
|
|
|||
|
/* Try to match expected header pattern: "begin XXX " */
|
|||
|
|
|||
|
if(!strncmp(dD->buffer, "begin ", 6) &&
|
|||
|
isoctal((dD->buffer)[6]) &&
|
|||
|
isoctal((dD->buffer)[7]) &&
|
|||
|
isoctal((dD->buffer)[8]) &&
|
|||
|
((dD->buffer)[9] == ' ')) {
|
|||
|
|
|||
|
/* We found the header. */
|
|||
|
dD->matchOK = 1;
|
|||
|
dD->justWaitForEOL = 1;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
/* Bad news; this is not the header. Reset buffer,
|
|||
|
* wait for next '\n' and test again.
|
|||
|
*/
|
|||
|
dD->used = 0;
|
|||
|
dD->justWaitForEOL = 1;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* No output will be generated after the end marker was found. */
|
|||
|
if(dD->endSeen) {
|
|||
|
|
|||
|
*outBuf = NULL;
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/* Alloc all the space that might be needed in conversion: round up to
|
|||
|
* the next multiple of 4 for the input, and see how much output could
|
|||
|
* that produce.
|
|||
|
*/
|
|||
|
|
|||
|
maxSpaceNeeded = ((dD->used + inLength + 3) / 4) * 3;
|
|||
|
*outBuf = (char *)ckalloc(maxSpaceNeeded);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
while(inLength > 0) {
|
|||
|
|
|||
|
int i, eolSeen, lineLength;
|
|||
|
|
|||
|
/* This gives the maximum number of characters that could possibly
|
|||
|
* exist in a correctly uuencoded line.
|
|||
|
*/
|
|||
|
|
|||
|
int maxLookAhead = (4 * UU_LINE) / 3 + 2 - dD->used;
|
|||
|
|
|||
|
for(i = 0, eolSeen = 0; i < my_min(maxLookAhead, inLength); i++) {
|
|||
|
if(inBuf[i] == '\n') {
|
|||
|
eolSeen = 1;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(eolSeen == 1) {
|
|||
|
|
|||
|
/* Transfer the rest of the current line. */
|
|||
|
memcpy(dD->buffer + dD->used, inBuf, i + 1);
|
|||
|
|
|||
|
dD->used += i + 1;
|
|||
|
inLength -= i + 1;
|
|||
|
inBuf += i + 1;
|
|||
|
|
|||
|
lineLength = (dD->buffer)[0] - ' ';
|
|||
|
|
|||
|
/* Check the length of the line. */
|
|||
|
if(((lineLength + 2) / 3) * 4 != dD->used - 2) {
|
|||
|
/* The length of the string is specified incorrectly. */
|
|||
|
|
|||
|
/* Reset filter data, so that recovery is possible. */
|
|||
|
dD->used = 0;
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
/* Is this a logically empty line? */
|
|||
|
|
|||
|
if(lineLength == 0) {
|
|||
|
|
|||
|
/* Yes. This is the ending line of the uuencode file. */
|
|||
|
dD->endSeen = 1;
|
|||
|
|
|||
|
if(*outLength == 0) {
|
|||
|
ckfree(*outBuf);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/* No. This is a genuine data line. */
|
|||
|
uudecode(dD->buffer + 1, *outBuf + *outLength, dD->used - 2);
|
|||
|
*outLength += lineLength;
|
|||
|
dD->used = 0;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if(maxLookAhead < inLength) {
|
|||
|
|
|||
|
/* The line is too long; this is an error. */
|
|||
|
|
|||
|
ckfree(*outBuf);
|
|||
|
|
|||
|
/* Reset filter data, so that recovery is possible. */
|
|||
|
dD->used = 0;
|
|||
|
|
|||
|
*outBuf = 0;
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
/* The input is not long enough, copy it to buffer. */
|
|||
|
memcpy(dD->buffer + dD->used, inBuf, inLength);
|
|||
|
dD->used += inLength;
|
|||
|
inLength = 0;
|
|||
|
inBuf += inLength;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
|
|||
|
/* Last call before the channel is closed. The last line (which must
|
|||
|
* have been incomplete), if it exists in the buffer, is ignored.
|
|||
|
*/
|
|||
|
|
|||
|
*outBuf = NULL;
|
|||
|
*outLength = 0;
|
|||
|
|
|||
|
ckfree((void *)dD);
|
|||
|
*data = NULL;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET:
|
|||
|
|
|||
|
/* No option is accepted. */
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
|
|||
|
case DP_FILTER_GET:
|
|||
|
|
|||
|
*outBuf = "{no internal arguments}";
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
|
|||
|
#undef my_min
|
|||
|
#undef isoctal
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This function implements the actual uudecode algorithm. For details
|
|||
|
* see "man 5 uuencode" .
|
|||
|
*/
|
|||
|
static void
|
|||
|
uudecode (from, to, howMany)
|
|||
|
unsigned char *from; /* (in) Where to take the data from. */
|
|||
|
unsigned char *to; /* (in) Where to put the encoded data to. */
|
|||
|
int howMany; /* (in) Number of bytes to decode. */
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
for(i = 0; i < howMany; i += 4) {
|
|||
|
*to++ = ((from[i] - ' ') << 2) | ((from[i+1] - ' ') >> 4);
|
|||
|
*to++ = ((from[i+1] - ' ') << 4) | ((from[i+2] - ' ') >> 2);
|
|||
|
*to++ = ((from[i+2] - ' ') << 6) | (from[i+3] - ' ');
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TclFilter --
|
|||
|
*
|
|||
|
* Allows the user to use any tcl function to implement a filter. The tcl filter
|
|||
|
* is assumed to consume the whole input (and buffer it, if necessary). The
|
|||
|
* tcl procedure must take two arguments, the first is the input string
|
|||
|
* (possibly with length zero), and the second one indicates the mode. It
|
|||
|
* can have the values "normal", "flush", "close", "eof". The output value is a
|
|||
|
* string that will be returned as a result (the output can have length 0).
|
|||
|
* Arguments: the name of the tcl command must be setup before the first use.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for the filtered output. Manages dynamic local
|
|||
|
* datastructure (tclData).
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
TclFilter (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
|
|||
|
#define BUFFER_CHUNK 4096
|
|||
|
|
|||
|
typedef struct {
|
|||
|
char *buffer; /* Stores the name of the Tcl commmand. */
|
|||
|
char *cmd; /* Buffer for creating the Tcl comand. */
|
|||
|
int cmdLength; /* Length of command buffer. */
|
|||
|
} tclData;
|
|||
|
|
|||
|
char *modeStr;
|
|||
|
int temp, needed;
|
|||
|
tclData *tD;
|
|||
|
|
|||
|
if(*data == NULL) {
|
|||
|
|
|||
|
/* No filter specific data was allocated. */
|
|||
|
|
|||
|
tD = (tclData *)ckalloc(sizeof(tclData));
|
|||
|
if(tD == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
*data = (void *)tD;
|
|||
|
|
|||
|
tD->buffer = NULL;
|
|||
|
tD->cmd = NULL;
|
|||
|
tD->cmdLength = 0;
|
|||
|
|
|||
|
} else {
|
|||
|
tD = (tclData *)(*data);
|
|||
|
}
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
|
|||
|
modeStr = "normal";
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
|
|||
|
modeStr = "flush";
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
|
|||
|
modeStr = "close";
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
modeStr = "eof";
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET:
|
|||
|
if(tD->buffer != NULL) {
|
|||
|
/* We do not allow for the change of the tcl filter. */
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
if(inLength == 0) {
|
|||
|
/* We do not allow for null-length procedure name. */
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
tD->buffer = (char *)ckalloc(inLength + 1);
|
|||
|
memcpy(tD->buffer, inBuf, inLength);
|
|||
|
(tD->buffer)[inLength] = '\0';
|
|||
|
return 0;
|
|||
|
|
|||
|
case DP_FILTER_GET:
|
|||
|
if(tD->buffer == NULL) {
|
|||
|
*outBuf = "{tcl filter name not set}";
|
|||
|
} else {
|
|||
|
*outBuf = tD->buffer;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
default:
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
/* Refuse to do filtering if the name of the Tcl procedure was not given. */
|
|||
|
|
|||
|
if(tD->buffer == NULL) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
temp = strlen(tD->buffer);
|
|||
|
|
|||
|
/* 6 = max{strlen(modeStr)}; 3 = number of spaces; 1 = space for '\0' */
|
|||
|
needed = temp + inLength + 3 + 6 + 1;
|
|||
|
|
|||
|
/* Do we have enough place to create the command? */
|
|||
|
|
|||
|
if(tD->cmdLength < needed) {
|
|||
|
|
|||
|
/* No. */
|
|||
|
|
|||
|
if(tD->cmd != NULL) {
|
|||
|
ckfree(tD->cmd);
|
|||
|
}
|
|||
|
|
|||
|
tD->cmd = (char *)ckalloc(needed + BUFFER_CHUNK);
|
|||
|
|
|||
|
if(tD->cmd == NULL) {
|
|||
|
tD->cmdLength = 0;
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
tD->cmdLength = needed + BUFFER_CHUNK;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/* Create the Tcl command. */
|
|||
|
|
|||
|
memcpy(tD->cmd, tD->buffer, temp);
|
|||
|
(tD->cmd)[temp] = ' ';
|
|||
|
if(inLength == 0) {
|
|||
|
memcpy(tD->cmd + temp + 1, "\"\"", 2);
|
|||
|
inLength = 2;
|
|||
|
} else {
|
|||
|
memcpy(tD->cmd + temp + 1, inBuf, inLength);
|
|||
|
}
|
|||
|
(tD->cmd)[temp + 1 + inLength] = ' ';
|
|||
|
memcpy(tD->cmd + temp + 1 + inLength + 1, modeStr, strlen(modeStr));
|
|||
|
(tD->cmd)[temp + 1 + inLength + 1 + strlen(modeStr)] = '\0';
|
|||
|
|
|||
|
if(Tcl_GlobalEval(interp, tD->cmd) != TCL_OK) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
/* Reserve the space for the result. */
|
|||
|
needed = strlen(interp->result);
|
|||
|
|
|||
|
if(needed > 0) {
|
|||
|
*outBuf = (char *)ckalloc(needed);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
*outLength = needed;
|
|||
|
memcpy(*outBuf, interp->result, needed);
|
|||
|
}
|
|||
|
|
|||
|
if(mode == DP_FILTER_CLOSE) {
|
|||
|
/* This was the last call; free filter data. */
|
|||
|
if(tD->cmd != NULL) {
|
|||
|
ckfree(tD->cmd);
|
|||
|
}
|
|||
|
|
|||
|
if(tD->buffer != NULL) {
|
|||
|
ckfree(tD->buffer);
|
|||
|
}
|
|||
|
ckfree((void *)tD);
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
|
|||
|
#undef BUFFER_CHUNK
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* HexOut
|
|||
|
*
|
|||
|
* Converts a string containg characters that correspond to hexadecimal
|
|||
|
* digits, into a sequence of bytes consisting of the binary representation
|
|||
|
* of the given hexadecimal symbols. Each byte will contain two hexadecimal
|
|||
|
* codes; the length of the input string has to be even.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for output.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
HexOut (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
int i, j;
|
|||
|
unsigned char t1, t2;
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
if(inLength % 2 != 0) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
if(inLength == 0) {
|
|||
|
*outBuf = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
*outBuf = (char *)ckalloc(inLength / 2);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = inLength / 2;
|
|||
|
|
|||
|
for(i = 0, j = 0; i < inLength; i += 2, j++) {
|
|||
|
if(isxdigit(inBuf[i]) && isxdigit(inBuf[i+1])) {
|
|||
|
if(isdigit(inBuf[i])) {
|
|||
|
t1 = inBuf[i] - '0';
|
|||
|
} else {
|
|||
|
t1 = tolower(inBuf[i]) - 'a' + 10;
|
|||
|
}
|
|||
|
if(isdigit(inBuf[i + 1])) {
|
|||
|
t2 = inBuf[i + 1] - '0';
|
|||
|
} else {
|
|||
|
t2 = tolower(inBuf[i + 1]) - 'a' + 10;
|
|||
|
}
|
|||
|
|
|||
|
(*outBuf)[j] = (t1 << 4) | t2;
|
|||
|
|
|||
|
} else {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
|
|||
|
*outLength = 0;
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
case DP_FILTER_GET:
|
|||
|
|
|||
|
*outBuf = "{no internal arguments}";
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* HexIn
|
|||
|
*
|
|||
|
* The opposite of HexOut (see above).
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* 0 if everything is OK, a POSIX error code if there was an error.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Allocates memory for output.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
int
|
|||
|
HexIn (inBuf, inLength, outBuf, outLength, data, interp, mode)
|
|||
|
char *inBuf; /* (in) Data to be filtered. */
|
|||
|
int inLength; /* (in) Amount of data to be filtered in bytes. */
|
|||
|
char **outBuf; /* (out) Address where the filtered data is stored. */
|
|||
|
int *outLength; /* (out) Amount of filtered data. */
|
|||
|
void **data; /* (in) Place where filter function data is stored. */
|
|||
|
Tcl_Interp *interp;/* (in) Interpreter in which the filter was created. */
|
|||
|
int mode; /* (in) Distinguishes between normal, flush and */
|
|||
|
/* close modes. Ignored in this case. */
|
|||
|
{
|
|||
|
int i, j;
|
|||
|
unsigned char t1, t2;
|
|||
|
|
|||
|
char *TranslationTable = "0123456789abcdef";
|
|||
|
|
|||
|
|
|||
|
switch(mode) {
|
|||
|
|
|||
|
case DP_FILTER_NORMAL:
|
|||
|
case DP_FILTER_FLUSH:
|
|||
|
case DP_FILTER_EOF:
|
|||
|
|
|||
|
if(inLength == 0) {
|
|||
|
*outBuf = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
*outBuf = (char *)ckalloc(2 * inLength);
|
|||
|
|
|||
|
if(*outBuf == NULL) {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
*outLength = 2 * inLength;
|
|||
|
|
|||
|
for(i = 0, j = 0; i < inLength; i++, j += 2) {
|
|||
|
(*outBuf)[j] = TranslationTable[((unsigned char)(inBuf[i])) >> 4];
|
|||
|
(*outBuf)[j + 1] = TranslationTable[inBuf[i] & 0x0F];
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_CLOSE:
|
|||
|
|
|||
|
*outLength = 0;
|
|||
|
break;
|
|||
|
|
|||
|
case DP_FILTER_SET:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
case DP_FILTER_GET:
|
|||
|
|
|||
|
*outBuf = "{no internal arguments}";
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
return EINVAL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
|
|||
|
}
|
|||
|
|