diff options
Diffstat (limited to 'rfc822/encode.c')
| -rw-r--r-- | rfc822/encode.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/rfc822/encode.c b/rfc822/encode.c new file mode 100644 index 0000000..a6f791b --- /dev/null +++ b/rfc822/encode.c @@ -0,0 +1,255 @@ +/* +** Copyright 2003-2004 Double Precision, Inc. See COPYING for +** distribution information. +*/ + +/* +*/ +#include "encode.h" +#include <string.h> +#include <stdlib.h> + +static int quoted_printable(struct libmail_encode_info *, + const char *, size_t); +static int base64(struct libmail_encode_info *, + const char *, size_t); +static int eflush(struct libmail_encode_info *, + const char *, size_t); + +void libmail_encode_start(struct libmail_encode_info *info, + const char *transfer_encoding, + int (*callback_func)(const char *, size_t, void *), + void *callback_arg) +{ + info->output_buf_cnt=0; + info->input_buf_cnt=0; + + switch (*transfer_encoding) { + case 'q': + case 'Q': + info->encoding_func=quoted_printable; + info->input_buffer[0]=0; /* Recycle for qp encoding */ + break; + case 'b': + case 'B': + info->encoding_func=base64; + break; + default: + info->encoding_func=eflush; + break; + } + info->callback_func=callback_func; + info->callback_arg=callback_arg; +} + +int libmail_encode(struct libmail_encode_info *info, + const char *ptr, + size_t cnt) +{ + return ((*info->encoding_func)(info, ptr, cnt)); +} + +int libmail_encode_end(struct libmail_encode_info *info) +{ + int rc=(*info->encoding_func)(info, NULL, 0); + + if (rc == 0 && info->output_buf_cnt > 0) + { + rc= (*info->callback_func)(info->output_buffer, + info->output_buf_cnt, + info->callback_arg); + info->output_buf_cnt=0; + } + + return rc; +} + +static int eflush(struct libmail_encode_info *info, const char *ptr, size_t n) +{ + while (n > 0) + { + size_t i; + + if (info->output_buf_cnt == sizeof(info->output_buffer)) + { + int rc= (*info->callback_func)(info->output_buffer, + info->output_buf_cnt, + info->callback_arg); + + info->output_buf_cnt=0; + if (rc) + return rc; + } + + i=n; + + if (i > sizeof(info->output_buffer) - info->output_buf_cnt) + i=sizeof(info->output_buffer) - info->output_buf_cnt; + + memcpy(info->output_buffer + info->output_buf_cnt, ptr, i); + info->output_buf_cnt += i; + ptr += i; + n -= i; + } + return 0; +} + +static int base64_flush(struct libmail_encode_info *); + +static int base64(struct libmail_encode_info *info, + const char *buf, size_t n) +{ + if (!buf) + { + int rc=0; + + if (info->input_buf_cnt > 0) + rc=base64_flush(info); + + return rc; + } + + while (n) + { + size_t i; + + if (info->input_buf_cnt == sizeof(info->input_buffer)) + { + int rc=base64_flush(info); + + if (rc != 0) + return rc; + } + + i=n; + if (i > sizeof(info->input_buffer) - info->input_buf_cnt) + i=sizeof(info->input_buffer) - info->input_buf_cnt; + + memcpy(info->input_buffer + info->input_buf_cnt, + buf, i); + info->input_buf_cnt += i; + buf += i; + n -= i; + } + return 0; +} + +static const char base64tab[]= +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int base64_flush(struct libmail_encode_info *info) +{ + int a=0,b=0,c=0; + int i, j; + int d, e, f, g; + char output_buf[ sizeof(info->input_buffer) / 3 * 4+1]; + + for (j=i=0; i<info->input_buf_cnt; i += 3) + { + a=(unsigned char)info->input_buffer[i]; + b= i+1 < info->input_buf_cnt ? + (unsigned char)info->input_buffer[i+1]:0; + c= i+2 < info->input_buf_cnt ? + (unsigned char)info->input_buffer[i+2]:0; + + d=base64tab[ a >> 2 ]; + e=base64tab[ ((a & 3 ) << 4) | (b >> 4)]; + f=base64tab[ ((b & 15) << 2) | (c >> 6)]; + g=base64tab[ c & 63 ]; + if (i + 1 >= info->input_buf_cnt) f='='; + if (i + 2 >= info->input_buf_cnt) g='='; + output_buf[j++]=d; + output_buf[j++]=e; + output_buf[j++]=f; + output_buf[j++]=g; + } + + info->input_buf_cnt=0; + + output_buf[j++]='\n'; + return eflush(info, output_buf, j); +} + +static const char xdigit[]="0123456789ABCDEF"; + +static int quoted_printable(struct libmail_encode_info *info, + const char *p, size_t n) +{ + char local_buf[256]; + int local_buf_cnt=0; + +#define QPUT(c) do { if (local_buf_cnt == sizeof(local_buf)) \ + { int rc=eflush(info, local_buf, local_buf_cnt); \ + local_buf_cnt=0; if (rc) return (rc); } \ + local_buf[local_buf_cnt]=(c); ++local_buf_cnt; } while(0) + + if (!p) + return (0); + + while (n) + { + + + /* + ** Repurpose input_buffer[0] as a flag whether the previous + ** character was a space. + ** + ** A space before a newline gets escaped. + */ + + if (info->input_buffer[0]) + { + if (*p == '\n') + { + QPUT('='); + QPUT('2'); + QPUT('0'); + } + else + { + QPUT(' '); + } + ++info->input_buf_cnt; + } + + info->input_buffer[0]=0; + + if (*p == ' ') + { + info->input_buffer[0]=1; + p++; + --n; + continue; + } + + if (info->input_buf_cnt > 72 && *p != '\n') + { + QPUT('='); + QPUT('\n'); + info->input_buf_cnt=0; + } + + if ( *p == '\n') + info->input_buf_cnt=0; + else if (*p < ' ' || *p == '=' || *p >= 0x7F) + { + QPUT('='); + QPUT(xdigit[ (*p >> 4) & 15]); + QPUT(xdigit[ *p & 15 ]); + info->input_buf_cnt += 3; + p++; + --n; + continue; + } + else info->input_buf_cnt++; + + QPUT( *p); + p++; + --n; + } + + if (local_buf_cnt > 0) + return eflush(info, local_buf, local_buf_cnt); + + return 0; +} |
