Commit ba5f7a3e ba5f7a3e5ed31151a7097fff50871621652e752c by Alain Magloire

attachment.c file_stream.c trans_stream.c

misc function done by D.I.
1 parent bee00121
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
the Free Software Foundation; either version 2, 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <message.h>
#include <io.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_HDR_LEN 256
#define BUF_SIZE 2048
struct _msg_info {
char *buf;
size_t nbytes;
char *header_buf;
int header_len;
int header_size;
header_t hdr;
message_t msg;
int ioffset;
int ooffset;
char line[MAX_HDR_LEN];
int line_ndx;
};
#define MSG_HDR "Content-Type: %s\nContent-Transfer-Encoding: %s\n\n"
int message_create_attachment(const char *content_type, const char *encoding, const char *filename, message_t *newmsg)
{
header_t hdr;
body_t body;
stream_t fstream = NULL, tstream = NULL;
char *header;
int ret;
if ( ( ret = message_create(newmsg, NULL) ) == 0 ) {
if ( content_type == NULL )
content_type = "text/plain";
if ( encoding == NULL )
encoding = "7bit";
if ( ( header = alloca(strlen(MSG_HDR) + strlen(content_type) + strlen(encoding)) ) == NULL )
ret = ENOMEM;
else {
sprintf(header, MSG_HDR, content_type, encoding);
if ( ( ret = header_create( &hdr, header, strlen(header), *newmsg ) ) == 0 ) {
message_get_body(*newmsg, &body);
if ( ( ret = file_stream_create(&fstream, filename, MU_STREAM_READ) ) == 0 ) {
if ( ( ret = encoder_stream_create(&tstream, fstream, encoding) ) == 0 ) {
body_set_stream(body, tstream, *newmsg);
message_set_header(*newmsg, hdr, NULL);
}
}
}
}
}
if ( ret ) {
if ( *newmsg )
message_destroy(newmsg, NULL);
if ( hdr )
header_destroy(&hdr, NULL);
if ( fstream )
stream_destroy(&fstream, NULL);
}
return ret;
}
static int _attachment_setup(struct _msg_info **info, message_t msg, stream_t *stream, void **data)
{
int sfl, ret;
body_t body;
if ( ( ret = message_get_body(msg, &body) ) != 0 ||
( ret = body_get_stream(body, stream) ) != 0 )
return ret;
stream_get_flags(*stream, &sfl);
if ( data == NULL && (sfl & MU_STREAM_NONBLOCK) )
return EINVAL;
if ( data )
*info = *data;
if ( *info == NULL ) {
if ( ( *info = calloc(1, sizeof(struct _msg_info)) ) == NULL )
return ENOMEM;
}
if ( ( (*info)->buf = malloc(BUF_SIZE) ) == NULL ) {
free(*info);
return ENOMEM;
}
return 0;
}
static void _attachment_free(struct _msg_info *info, int free_message) {
if ( info->buf )
free(info->buf);
if ( info->header_buf )
free(info->header_buf);
if ( free_message ) {
if ( info->msg )
message_destroy(&(info->msg), NULL);
else if ( info->hdr )
header_destroy(&(info->hdr), NULL);
}
free(info);
}
int message_save_attachment(message_t msg, const char *filename, void **data)
{
stream_t stream;
header_t hdr;
struct _msg_info *info = NULL;
int ret;
size_t size;
char *content_encoding;
if ( msg == NULL || filename == NULL)
return EINVAL;
if ( ( data == NULL || *data == NULL) && ( ret = message_get_header(msg, &hdr) ) == 0 ) {
header_get_value(hdr, "Content-Transfer-Encoding", NULL, 0, &size);
if ( size ) {
if ( ( content_encoding = alloca(size+1) ) == NULL )
ret = ENOMEM;
header_get_value(hdr, "Content-Transfer-Encoding", content_encoding, size+1, 0);
}
}
if ( ret == 0 && ( ret = _attachment_setup( &info, msg, &stream, data) ) != 0 )
return ret;
if ( ret != EAGAIN && info )
_attachment_free(info, ret);
return ret;
}
#if 0
int message_encapsulate(message_t msg, message_t *newmsg, void **data)
{
stream_t stream;
char *header;
struct _msg_info *info = NULL;
int ret;
if ( msg == NULL || newmsg == NULL)
return EINVAL;
if ( ( ret = message_create(&(info->msg), NULL) ) == 0 ) {
header = "Content-Type: message/rfc822\nContent-Transfer-Encoding: 7bit\n\n";
if ( ( ret = header_create( &(info->hdr), header, strlen(header), msg ) ) == 0 ) {
message_set_header(info->msg, info->hdr, NULL);
}
}
return ret;
}
#endif
/* If the message interface parsed headers on write this would be easy */
int message_unencapsulate(message_t msg, message_t *newmsg, void **data)
{
size_t size, nbytes;
int ret = 0, header_done = 0;
char *content_type, *cp;
header_t hdr;
body_t body;
stream_t istream, ostream;
struct _msg_info *info = NULL;
if ( msg == NULL || newmsg == NULL)
return EINVAL;
if ( (data == NULL || *data == NULL ) && ( ret = message_get_header(msg, &hdr) ) == 0 ) {
header_get_value(hdr, "Content-Type", NULL, 0, &size);
if ( size ) {
if ( ( content_type = alloca(size+1) ) == NULL )
ret = ENOMEM;
header_get_value(hdr, "Content-Type", content_type, size+1, 0);
if ( strncasecmp(content_type, "message/rfc822", strlen(content_type)) != 0 )
ret = EINVAL;
} else
return EINVAL;
}
if ( ret == 0 && ( ret = _attachment_setup( &info, msg, &istream, data) ) != 0 )
return ret;
if ( ret == 0 && info->hdr == NULL ) {
while ( !header_done && ( ret = stream_read(istream, info->buf, BUF_SIZE, info->ioffset, &info->nbytes) ) == 0 && info->nbytes ) {
cp = info->buf;
while ( info->nbytes && !header_done ) {
info->line[info->line_ndx] = *cp;
info->line_ndx++;
if ( *cp == '\n' ) {
if ( info->header_len + info->line_ndx > info->header_size) {
char *nhb;
if ( ( nhb = realloc( info->header_buf, info->header_len + info->line_ndx + 128 ) ) == NULL ) {
header_done = 1;
ret = ENOMEM;
break;
}
info->header_buf = nhb;
info->header_size = info->header_len + info->line_ndx + 128;
}
info->header_len += info->line_ndx;
memcpy(info->header_buf, info->line, info->line_ndx);
if ( info->line_ndx == 1 ) {
header_done = 1;
break;
}
info->line_ndx = 0;
}
info->ioffset++;
info->nbytes--;
cp++;
}
}
}
if ( ret == 0 && info->msg == NULL ) {
if ( ( ret = message_create(&(info->msg), NULL) ) == 0)
if ( ( ret = header_create(&(info->hdr), info->header_buf, info->header_len, info->msg) ) == 0 )
ret = message_set_header(info->msg, hdr, NULL);
}
if ( ret == 0 ) {
message_get_body(info->msg, &body);
body_get_stream( body, &ostream);
if ( info->nbytes )
memmove( info->buf, info->buf + (BUF_SIZE - info->nbytes), info->nbytes);
while ( info->nbytes || ( ( ret = stream_read(istream, info->buf, BUF_SIZE, info->ioffset, &info->nbytes) ) == 0 && info->nbytes ) ) {
info->ioffset += info->nbytes;
while( info->nbytes ) {
if ( ( ret = stream_write(ostream, info->buf, info->nbytes, info->ooffset, &nbytes ) ) != 0 )
break;
info->nbytes -= nbytes;
info->ooffset += nbytes;
}
}
}
if ( ret != EAGAIN && info )
_attachment_free(info, ret);
return ret;
}
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
the Free Software Foundation; either version 2, 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io0.h>
struct _file_stream
{
FILE *file;
int offset;
};
static void _file_destroy(stream_t stream)
{
struct _file_stream *fs = stream->owner;
if ( fs->file )
fclose(fs->file);
free(fs);
}
static int _file_read(stream_t stream, char *optr, size_t osize, off_t offset, size_t *nbytes)
{
struct _file_stream *fs = stream->owner;
if ( fs->offset != offset ) {
fseek( fs->file, offset, SEEK_SET );
fs->offset = offset;
}
*nbytes = fread( optr, osize, 1, fs->file);
if ( *nbytes == 0 ) {
if ( ferror( fs->file ) )
return errno;
} else
fs->offset += *nbytes;
return 0;
}
static int _file_write(stream_t stream, const char *iptr, size_t isize, off_t offset, size_t *nbytes)
{
struct _file_stream *fs = stream->owner;
if ( fs->offset != offset ) {
fseek( fs->file, offset, SEEK_SET );
fs->offset = offset;
}
*nbytes = fwrite( iptr, isize, 1, fs->file);
if ( *nbytes == 0 ) {
if ( ferror( fs->file ) )
return errno;
} else
fs->offset += *nbytes;
return 0;
}
int file_stream_create(stream_t *stream, const char *filename, int flags)
{
struct _file_stream *fs;
char *mode;
int ret;
if ( stream == NULL || filename == NULL )
return EINVAL;
if ( ( fs = calloc(sizeof(struct _file_stream), 1) ) == NULL )
return ENOMEM;
if ( ( flags & ( MU_STREAM_READ|MU_STREAM_WRITE ) ) == ( MU_STREAM_READ|MU_STREAM_WRITE ) )
mode = "r+b";
else if ( flags & MU_STREAM_READ )
mode = "rb";
else if ( flags & MU_STREAM_WRITE )
mode = "wb";
else
return EINVAL;
if ( ( fs->file = fopen(filename, mode) ) == NULL ) {
ret = errno;
free( fs );
return ret;
}
if ( ( ret = stream_create(stream, flags, fs) ) != 0 ) {
fclose( fs->file );
free( fs );
return ret;
}
stream_set_read(*stream, _file_read, fs );
stream_set_write(*stream, _file_write, fs );
stream_set_destroy(*stream, _file_destroy, fs );
return 0;
}
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Library Public License as published by
the Free Software Foundation; either version 2, 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io0.h>
struct _ts_desc {
const char *encoding;
int (*decode)(const char *iptr, size_t isize, char *optr, size_t *nbytes);
int (*encode)(const char *iptr, size_t isize, char *optr, size_t *nbytes);
};
struct _trans_stream
{
stream_t stream; /* encoder/decoder read/writes data to here */
int cur_offset;
int offset;
char *leftover;
int llen;
int (*transcoder)(const char *iptr, size_t isize, char *optr, size_t *nbytes);
};
int _base64_decode(const char *iptr, size_t isize, char *optr, size_t *nbytes);
int _base64_encode(const char *iptr, size_t isize, char *optr, size_t *nbytes);
int _qp_decode(const char *iptr, size_t isize, char *optr, size_t *nbytes);
int _qp_encode(const char *iptr, size_t isize, char *optr, size_t *nbytes);
#define NUM_TRANSCODERS 5
struct _ts_desc tslist[NUM_TRANSCODERS] = { { "base64", _base64_decode, _base64_encode},
{ "quoted-printable", _qp_decode, _qp_encode},
{ "7bit", NULL, NULL},
{ "8bit", NULL, NULL},
{ "binary", NULL, NULL}
};
static void _trans_destroy(stream_t stream)
{
struct _trans_stream *ts = stream->owner;
stream_destroy(&(ts->stream), NULL);
if ( ts->leftover )
free(ts->leftover);
free(ts);
}
static int _trans_read(stream_t stream, char *optr, size_t osize, off_t offset, size_t *nbytes)
{
struct _trans_stream *ts = stream->owner;
size_t isize = osize;
char *iptr;
int consumed, ret;
if ( nbytes == NULL || optr == NULL || osize == 0 )
return EINVAL;
*nbytes = 0;
if ( offset && ts->offset != offset )
return ESPIPE;
if ( offset == 0 )
ts->cur_offset = 0;
if ( ( iptr = alloca(isize) ) == NULL )
return ENOMEM;
if ( ts->leftover ) {
memcpy( iptr, ts->leftover, ts->llen);
free( ts->leftover );
ts->leftover = NULL;
ts->offset = 0; // encase of error;
}
if ( ( ret = stream_read(ts->stream, iptr + ts->llen, isize - ts->llen, ts->cur_offset, &osize) ) != 0 )
return ret;
ts->cur_offset += osize;
consumed = ts->transcoder(iptr, isize, optr, nbytes);
if ( ts->llen = (isize - consumed ) ) {
if ( ( ts->leftover = malloc(ts->llen) ) == NULL )
return ENOMEM;
memcpy(ts->leftover, iptr + consumed, ts->llen);
}
ts->offset = offset;
return 0;
}
static int _trans_write(stream_t stream, const char *iptr, size_t isize, off_t offset, size_t *nbytes)
{
struct _trans_stream *ts = stream->owner;
size_t osize = isize;
char *optr;
int ret;
if ( nbytes == NULL || iptr == NULL || isize == 0 )
return EINVAL;
*nbytes = 0;
if ( offset && ts->offset != offset )
return ESPIPE;
if ( offset == 0 )
ts->cur_offset = 0;
if ( ( optr = alloca(osize) ) == NULL )
return ENOMEM;
*nbytes = ts->transcoder(iptr, isize, optr, &osize);
if ( ( ret = stream_write(ts->stream, optr, osize, ts->cur_offset, &osize) ) != 0 )
return ret;
ts->cur_offset += osize;
return 0;
}
int encoder_stream_create(stream_t *stream, stream_t iostream, const char *encoding)
{
struct _trans_stream *ts;
int i, ret;
if ( stream == NULL || iostream == NULL || encoding == NULL )
return EINVAL;
if ( ( ts = calloc(sizeof(struct _trans_stream), 1) ) == NULL )
return ENOMEM;
for( i = 0; i < NUM_TRANSCODERS; i++ ) {
if ( strcasecmp( encoding, tslist[i].encoding ) == 0 )
break;
}
if ( i == NUM_TRANSCODERS )
return ENOTSUP;
if ( ( ret = stream_create(stream, MU_STREAM_RDWR, ts) ) != 0 )
return ret;
ts->transcoder = tslist[i].encode;
stream_set_read(*stream, _trans_read, ts );
stream_set_write(*stream, _trans_write, ts );
stream_set_destroy(*stream, _trans_destroy, ts );
ts->stream = iostream;
return 0;
}
int decoder_stream_create(stream_t *stream, stream_t iostream, const char *encoding)
{
struct _trans_stream *ts;
int i, ret;
if ( stream == NULL || iostream == NULL || encoding == NULL )
return EINVAL;
if ( ( ts = calloc(sizeof(struct _trans_stream), 1) ) == NULL )
return ENOMEM;
for( i = 0; i < NUM_TRANSCODERS; i++ ) {
if ( strcasecmp( encoding, tslist[i].encoding ) == 0 )
break;
}
if ( i == NUM_TRANSCODERS )
return ENOTSUP;
if ( ( ret = stream_create(stream, MU_STREAM_RDWR, ts) ) != 0 )
return ret;
ts->transcoder = tslist[i].decode;
stream_set_read(*stream, _trans_read, ts );
stream_set_write(*stream, _trans_write, ts );
stream_set_destroy(*stream, _trans_destroy, ts );
ts->stream = iostream;
return 0;
}
/*------------------------------------------------------
* base64 encode/decode
*----------------------------------------------------*/
static int _b64_input(char c)
{
const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int i;
for (i = 0; i < 64; i++) {
if (table[i] == c)
return i;
}
return -1;
}
static int _b64_output(int index)
{
const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
if (index < 64)
return table[index];
return -1;
}
int _base64_decode(const char *iptr, size_t isize, char *optr, size_t *nbytes)
{
int i, tmp = 0;
int consumed = 0;
char data[4];
while ( consumed < isize ) {
while ( ( i < 4 ) && ( consumed < isize ) ) {
tmp = _b64_input(*iptr++);
consumed++;
if ( tmp != -1 )
data[i++] = tmp;
}
if ( i == 4 ) { // I have a entire block of data 32 bits
// get the output data
*optr++ = ( data[0] << 2 ) | ( ( data[1] & 0x30 ) >> 4 );
*optr++ = ( ( data[1] & 0xf ) << 4 ) | ( ( data[2] & 0x3c ) >> 2 );
*optr++ = ( ( data[2] & 0x3 ) << 6 ) | data[3];
(*nbytes) += 3;
}
else // I did not get all the data
return consumed;
i = 0;
}
return consumed;
}
int _base64_encode(const char *iptr, size_t isize, char *optr, size_t *nbytes)
{
int consumed = 0;
while (consumed < (isize - 3) && (*nbytes + 4) < isize) {
*optr++ = _b64_output(*iptr >> 2);
*optr++ = _b64_output(((*iptr++ & 0x3) << 4) | ((*iptr & 0xf0) >> 4));
*optr++ = _b64_output(((*iptr++ & 0xf) << 2) | ((*iptr & 0xc0) >> 6));
*optr++ = _b64_output(*iptr++ & 0x3f);
consumed += 3;
(*nbytes) += 4;
}
return consumed;
}
/*------------------------------------------------------
* quoted-printable decoder/encoder
*------------------------------------------------------*/
static const char _hexdigits[16] = "0123456789ABCDEF";
static int _ishex(int c)
{
int i;
if ((c == 0x0a) || (c == 0x0d))
return 1;
for (i = 0; i < 16; i++)
if (c == _hexdigits[i])
return 1;
return 0;
}
int _qp_decode(const char *iptr, size_t isize, char *optr, size_t *nbytes)
{
char c;
int last_char = 0, consumed = 0;
while (consumed < isize) {
c = *iptr++;
if ( ((c >= 33) && (c <= 60)) || ((c >= 62) && (c <= 126)) || ((c == '=') && !_ishex(*iptr)) ) {
*optr++ = c;
(*nbytes)++;
consumed++;
}
else if (c == '=') {
// there must be 2 more characters before I consume this
if ((isize - consumed) < 3) {
return consumed;
}
else {
// you get =XX where XX are hex characters
char chr[2];
int new_c;
chr[0] = *iptr++;
if (chr[0] != 0x0a) { // LF after = means soft line break, ignore it
chr[1] = *iptr++;
new_c = strtoul(chr, NULL, 16);
if (new_c == '\r')
new_c = '\n';
*optr++ = new_c;
(*nbytes)++;
consumed += 3;
}
else
consumed += 2;
}
}
else if (c == 0x0d) { // CR character
// there must be at least 1 more character before I consume this
if ((isize - consumed) < 2)
return (consumed);
else {
iptr++; // skip the LF character
*optr++ = '\n';
(*nbytes)++;
consumed += 2;
}
}
else if ((c == 9) || (c == 32)) {
if ((last_char == 9) || (last_char == 32))
consumed++;
else {
*optr++ = c;
(*nbytes)++;
consumed++;
}
}
last_char = c;
}
return consumed;
}
#define QP_LINE_MAX 76
int _qp_encode(const char *iptr, size_t isize, char *optr, size_t *nbytes)
{
int count = 0;
int consumed = 0, c;
while (consumed < isize && (*nbytes + 4) < isize) {
if (count == QP_LINE_MAX) {
*optr++ = '=';
*optr++ = '\n';
(*nbytes) += 2;
count = 0;
}
c = *iptr++;
consumed++;
if ( ((c >= 32) && (c <= 60)) || ((c >= 62) && (c <= 126)) || (c == 9)) {
*optr++ = c;
(*nbytes)++;
count++;
}
else {
if (count >= (QP_LINE_MAX - 3)) {
// add spaces
while (count < QP_LINE_MAX) {
*optr++ = ' ';
(*nbytes)++;
count++;
}
consumed--;
iptr--;
}
else {
*optr++ = '=';
*optr++ = _hexdigits[c & 0xf];
*optr++ = _hexdigits[(c/16) & 0xf];
(*nbytes) += 3;
count += 3;
}
}
}
return consumed;
}