/*
renattach 1.2.2 - Filter that renames/deletes dangerous email attachments
Copyright (C) 2003, 2004 Jem E. Berkes <jberkes@pc-tools.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "utility.h"
/* Used by base64 routines */
const static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/*
Trim leading whitespace from a line
*/
void trim_leading(char* buf)
{
int i, len = strlen(buf);
char *tmpbuf = calloc(len+1, 1);
for (i=0; i<len; i++)
{
if (buf[i]!=' ' && buf[i]!='\t')
break;
}
strcpy(tmpbuf, buf+i);
strcpy(buf, tmpbuf);
free(tmpbuf);
}
/*
Trims trailing whitespace from a line
*/
void trim_trailing(char* buf)
{
char* end = buf+strlen(buf)-1;
while (end>=buf && (*end==' ' || *end=='\t'))
{
*end = 0;
end--;
}
}
/*
Trims specified trailing character from a line
*/
void trim_trailch(char* buf, char ch1, char ch2, char ch3)
{
char* end = buf+strlen(buf)-1;
while (end>=buf && (*end==ch1 || *end==ch2 || *end==ch3))
{
*end = 0;
end--;
}
}
/*
Expand the specified dynamic list with the additional text
If adding to existing, the new addition is delimited by the given string
*/
void expand_list(char** buffer, const char* addition, const char* delim)
{
char* newbuf;
if (*buffer)
{
newbuf = malloc(strlen(*buffer) + strlen(delim) + strlen(addition) + 1);
strcpy(newbuf, *buffer);
strcat(newbuf, delim);
}
else
newbuf = calloc(strlen(addition) + 1, 1);
strcat(newbuf, addition);
if (*buffer)
free(*buffer);
*buffer = newbuf;
}
/*
Case-insensitive version of strstr
Searches for needle within haystack, returning pointer
to start of the matching characters in haystack.
*/
char* stristr(const char* haystack, const char* needle)
{
const char* needlepoint = needle;
const char* matchpoint = NULL;
if (!haystack || !needle) /* check for bad input */
return NULL;
else if (*needle == '\0') /* strstr compliance */
return (char*)haystack;
while (*haystack != '\0')
{
if (tolower(*needlepoint) == tolower(*haystack))
{
if (!matchpoint)
matchpoint = haystack;
needlepoint++;
if (*needlepoint == '\0')
return (char*)matchpoint;
}
else if (matchpoint)
{ /* reset the search */
needlepoint = needle;
haystack = matchpoint; /* cure overlap problem */
matchpoint = NULL;
}
haystack++;
}
return NULL;
}
/*
Return integer (byte) equivalent of hexadecimal octet from ASCII form
*/
unsigned char hex2int(char* hexform)
{
int pos;
unsigned char value = 0;
for (pos=0; pos<=1; pos++)
{
unsigned char nibble = (unsigned char)hexform[pos];
if ((nibble >= '0') && (nibble <= '9'))
nibble -= '0';
else if ((nibble >= 'a') && (nibble <= 'f'))
nibble -= ('a' - 10);
else if ((nibble >= 'A') && (nibble <= 'F'))
nibble -= ('A' - 10);
else
return value;
value = (value << 4) | nibble;
}
return value;
}
/*
Decode quoted printable (or URL-style) encoding where 8-bit characters
are represented by hexadecimal octet. e.g. Hello%20world%2E
Specify the hexflag character
Set underscore=1 if underscores should be converted to spaces (RFC 2047)
optdest is optional; if NULL, the input text is replaced
*/
void decode_hex(char* text, char hexflag, int underscore, char* optdest)
{
int pos, inlen = strlen(text);
char* tmpbuf = calloc(inlen+1, 1);
for (pos=0; pos < inlen; pos++)
{
if (underscore && (text[pos]=='_'))
strcat(tmpbuf, " ");
else if ((text[pos]==hexflag) && (pos+2<inlen))
{
pos++;
if (isxdigit((int)text[pos]) && isxdigit((int)text[pos+1]))
sprintf(tmpbuf+strlen(tmpbuf), "%c", hex2int(text+pos++));
}
else
sprintf(tmpbuf+strlen(tmpbuf), "%c", text[pos]);
}
if (optdest)
strcpy(optdest, tmpbuf); /* optdest better be large enough */
else
strcpy(text, tmpbuf); /* decoded version can never be longer than original */
free(tmpbuf);
}
/*
Returns number of bytes written to dest, if this was valid base64 encoded data
Returns 0 if invalid format
Can decode non-text. Caller should completely clear dest
*/
int base64_decode_line(const char* input, char* dest)
{
char outbytes[3];
int len = strlen(input); /* input is plain text */
const char* endpoint = input + len;
int outpos = 0; /* required for binary output */
if (len % 4 != 0)
return 0; /* quit, invalid format */
while (input < endpoint)
{
int i, j, towrite=3;
for (i=2; i<4; i++) /* count deductions */
if (input[i] == '=') towrite--;
for (j=0; j<towrite; j++)
{
char *lookup1 = strchr(alphabet, input[j]);
char *lookup2 = strchr(alphabet, input[j+1]);
if (!lookup1 || !lookup2)
return 0; /* invalid situation */
outbytes[j] = (((lookup1-alphabet) << (2+j*2)) | ((lookup2 - alphabet) >> (4-2*j)));
}
memcpy(dest+outpos, outbytes, towrite);
outpos += towrite;
input += 4;
}
return outpos;
}
/*
base64 encoded the input data, of size insize
output buffer must hold: 1 + (1+insize/3)*4
*/
void base64_encode_line(const char* input, int insize, char* output)
{
int i;
unsigned char inthree[3];
unsigned char outfour[4];
for (i=0; i < (insize/3); i++)
{
memcpy(inthree, input+(3*i), 3);
outfour[0] = inthree[0]>>2;
outfour[1] = ((inthree[0]<<4) & 0x30) | (inthree[1]>>4);
outfour[2] = ((inthree[1]<<2) & 0x3F) | (inthree[2]>>6);
outfour[3] = inthree[2] & 0x3F;
outfour[0] = alphabet[outfour[0]];
outfour[1] = alphabet[outfour[1]];
outfour[2] = alphabet[outfour[2]];
outfour[3] = alphabet[outfour[3]];
memcpy(output+(4*i), outfour, 4);
}
if (insize % 3 == 1)
{
memcpy(inthree, input+(3*i), 1);
outfour[0] = inthree[0] >> 2;
outfour[1] = (inthree[0]<<4) & 0x30;
outfour[0] = alphabet[outfour[0]];
outfour[1] = alphabet[outfour[1]];
outfour[2] = '=';
outfour[3] = '=';
memcpy(output+(4*i), outfour, 4);
i++;
}
else if (insize % 3 == 2)
{
memcpy(inthree, input+(3*i), 2);
outfour[0] = inthree[0] >> 2;
outfour[1] = ((inthree[0]<<4) & 0x30) | (inthree[1]>>4);
outfour[2] = (inthree[1]<<2) & 0x3F;
outfour[0] = alphabet[outfour[0]];
outfour[1] = alphabet[outfour[1]];
outfour[2] = alphabet[outfour[2]];
outfour[3] = '=';
memcpy(output+(4*i), outfour, 4);
i++;
}
memcpy(output+(4*i), "\0", 1);
}
/*
You must initialize the zip_info structure before first use
*/
void init_zip(struct zip_info* zip)
{
memset(zip, 0, sizeof(struct zip_info));
zip->offset = -1;
}
/*
Search for and extract filenames from ZIP archives.
databuf is the input chunk of binary ZIP data, of databuf_len
zip_info is the context, and must have been initialized
Returns:
NULL No filename found in this data block
ptr Filename found. Repeat call with this pointer to continue
processing this data block after you grab the filename.
*/
char* unzip_filename(char* databuf, int databuf_len, struct zip_info* zip)
{
char* databuf_pos; /* Position within databuf */
char* databuf_end; /* Byte past end of databuf */
/* Sanity checking */
if (!databuf || !zip)
return NULL;
if (databuf_len < 1)
return NULL;
databuf_pos = databuf;
databuf_end = databuf + databuf_len;
while (databuf_pos < databuf_end)
{
/* Maybe we're skipping compresed data until next file */
if (zip->skipping)
{
if (zip->skipcount == 0)
init_zip(zip); /* Clears skipping flag too */
else
{
databuf_pos++;
zip->skipcount--;
}
continue;
}
if (zip->offset < 0) /* haven't yet found first ID byte */
{
void* id0 = memchr(databuf_pos, 0x50, databuf_end-databuf_pos);
if (id0)
{
zip->offset = 0; /* found 0x50, offset 0 ok */
databuf_pos = (char*)id0+1;
continue;
}
else
return NULL; /* no hope in this particular data block */
}
if (zip->offset < 3)
{
char needid = 0;
switch (zip->offset)
{
case 0:
needid = 0x4b;
break;
case 1:
needid = 0x03;
break;
case 2:
needid = 0x04;
break;
}
if (*databuf_pos == needid)
zip->offset++;
else
zip->offset = -1;
databuf_pos++;
continue;
}
zip->offset++;
if (zip->offset == 0x12)
zip->skipcount = (unsigned char)*databuf_pos;
else if (zip->offset == 0x13)
zip->skipcount |= ((unsigned char)*databuf_pos) << 8;
else if (zip->offset == 0x14)
zip->skipcount |= ((unsigned char)*databuf_pos) << 16;
else if (zip->offset == 0x15)
zip->skipcount |= ((unsigned char)*databuf_pos) << 24;
else if (zip->offset == 0x1a)
zip->namelen = (unsigned char)*databuf_pos;
else if (zip->offset == 0x1b)
{
zip->namelen |= ((unsigned char)*databuf_pos) << 8;
/* Bounds checking */
if ((zip->namelen + 1) > sizeof(zip->filename))
zip->namelen = sizeof(zip->filename) - 1;
}
else if (zip->offset == 0x1c)
zip->skipcount += (unsigned char)*databuf_pos;
else if (zip->offset == 0x1d)
{
zip->skipcount += ((unsigned char)*databuf_pos) << 8;
if (zip->skipcount > MAX_ZIPBYTES) /* Exceeded reasonable size */
{
init_zip(zip); /* Likely corrupt, reset state*/
continue;
}
}
else if (zip->offset >= 0x1e)
{
/* Transfer bytes of filename until end is reached */
if ((zip->offset - 0x1e) >= zip->namelen)
{
/* Next time, we'll start skipping bytes */
zip->skipping = 1;
return databuf_pos; /* Done */
}
zip->filename[zip->offset - 0x1e] = *databuf_pos;
}
databuf_pos++;
}
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1