static char rcsid[] = "@(#)$Id: mime.c,v 1.50 2006/04/10 09:03:21 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.50 $ $State: Exp $
*
* Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI>
* (was hurtta+elm@ozone.FMI.FI)
******************************************************************************
* The Elm Mail System
*
* Copyright (c) 1988-1992 USENET Community Trust
* Copyright (c) 1986,1987 Dave Taylor
*****************************************************************************/
#include "def_elm.h"
#include "s_elm.h"
DEBUG_VAR(Debug,__FILE__,"mime");
#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif
static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str)
char *str;
{
return (unsigned char *)str;
}
void clear_mime_send_info(mime_info)
mime_send_t *mime_info;
{
mime_info->raw_level = mailer_7bit;
mime_info->encoding_top = ENCODING_7BIT;
mime_info->TYPE_opts_top = NULL;
mime_info->hdr_charset = text_charset;
/* FIXME: is this correct?
... we also should avoid ISO2022 charsets on here
*/
mime_info->encode_hdr = 1;
mime_info->top_parts_count = 0;
mime_info->top_parts = NULL;
/* These fields are not on correct place */
mime_info->cl_offset = mime_info->cl_start = mime_info->cl_end = 0;
}
static struct mime_send_part * new_part P_((mime_send_t *mime_info));
static struct mime_send_part * new_part(mime_info)
mime_send_t *mime_info;
{
int ptr = mime_info->top_parts_count;
mime_info->top_parts = safe_realloc(mime_info->top_parts,
(mime_info->top_parts_count +1) *
sizeof (struct mime_send_part));
mime_info->top_parts_count++;
mime_info->top_parts[ptr].encoding_part = ENCODING_7BIT;
mime_info->top_parts[ptr].encoding_part_text = NULL;
mime_info->top_parts[ptr].TYPE = 0;
mime_info->top_parts[ptr].TYPE_opts_part = NULL;
mime_info->top_parts[ptr].disposition = 0;
mime_info->top_parts[ptr].DISPOSITION_opts = NULL;
mime_info->top_parts[ptr].description = NULL;
mime_info->top_parts[ptr].is_text = 0;
mime_info->top_parts[ptr].save_it_on_copy = 0;
mime_info->top_parts[ptr].start_loc = 0;
mime_info->top_parts[ptr].end_loc = 0;
mime_info->top_parts[ptr].result_charset = NULL;
DPRINT(Debug,4,
(&Debug,
"Preparing mail for sending: new_part %d\n",
ptr));
/* Notice that returned pointer is valid only until
* new_part() is called again
*/
return &(mime_info->top_parts[ptr]);
}
static void reset_parts P_((mime_send_t *mime_info));
static void reset_parts(mime_info)
mime_send_t *mime_info;
{
int i;
for (i = 0; i < mime_info->top_parts_count; i++) {
mime_info->top_parts[i].encoding_part = 0;
if (mime_info->top_parts[i].encoding_part_text) {
free(mime_info->top_parts[i].encoding_part_text);
mime_info->top_parts[i].encoding_part_text = NULL;
}
mime_info->top_parts[i].TYPE = NULL;
if (mime_info->top_parts[i].TYPE_opts_part)
free_mime_param(&(mime_info->top_parts[i].TYPE_opts_part));
mime_info->top_parts[i].disposition = 0;
if (mime_info->top_parts[i].DISPOSITION_opts)
free_mime_param(&(mime_info->top_parts[i].DISPOSITION_opts));
if (mime_info->top_parts[i].description) {
free_string(&(mime_info->top_parts[i].description));
mime_info->top_parts[i].description = NULL;
}
mime_info->top_parts[i].is_text = 0;
mime_info->top_parts[i].save_it_on_copy = 0;
mime_info->top_parts[i].result_charset = NULL;
mime_info->top_parts[i].start_loc = 0;
mime_info->top_parts[i].end_loc = 0;
}
if (mime_info->top_parts) {
free(mime_info->top_parts);
mime_info->top_parts = NULL;
}
mime_info->top_parts_count = 0;
}
void free_mime_send_info(mime_info)
mime_send_t *mime_info;
{
reset_parts(mime_info);
mime_info->encoding_top = 0;
mime_info->mime_boundary[0] = '\0';
if (mime_info->TYPE_opts_top)
free_mime_param(&(mime_info->TYPE_opts_top));
mime_info->hdr_charset = NULL;
mime_info->encode_hdr = 1;
mime_info->msg_is_multipart = 0;
mime_info->cl_offset = 0;
mime_info->cl_start = 0;
mime_info->cl_end = 0;
}
void add_parameter_1(ptr,name,value,quoted)
char **ptr;
char *name;
char *value;
int quoted;
{
char buffer[1024];
buffer[0] = '\0';
if (*ptr)
strfcpy(buffer,*ptr,sizeof buffer);
add_parameter(buffer,name,value,sizeof buffer, quoted);
*ptr = strmcpy(*ptr,buffer);
}
static int write_one_part P_((mime_send_t *mime_info,
struct mime_send_part * X,
out_state_t *mailer,
FILE *tmp,
struct menu_context *page));
int write_one_part(mime_info,X,mailer,tmp, page)
mime_send_t *mime_info;
struct mime_send_part * X;
out_state_t *mailer;
FILE *tmp;
struct menu_context *page;
{
int need_enc, line_len;
char buffer[SLEN]; /* file reading buffer */
charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);
int LINES, COLUMNS;
menu_get_sizes(page,&LINES, &COLUMNS);
if (!ascii_ptr)
panic("MIME PANIC",__FILE__,__LINE__,"write_one_part",
"US-ASCII not found",0);
rewind(tmp);
/* Determine how this message should be MIME encoded:
* 7BIT, 8BIT, BINARY or QUOTED-PRINTABLE.
*/
need_enc = needs_encoding (tmp,NULL);
rewind(tmp);
if (need_enc & HAVE_BINARY) {
if (mime_info->raw_level >= mailer_binary)
X->encoding_part = ENCODING_BINARY;
else
/* no -BBINARYMIME option */
X->encoding_part = ENCODING_QUOTED;
}
else if (need_enc & HAVE_8BIT) {
if (mime_info->raw_level >= mailer_8bit)
/* Just send 8BIT anyway */
X->encoding_part = ENCODING_8BIT;
else
/* no -B8BITMIME option */
X->encoding_part = ENCODING_QUOTED;
}
/* Control characters can send with encoding 7BIT
* HAVE_BINARY takes care really severe stuff */
if ((need_enc & HAVE_CTRL) &&
X->encoding_part != ENCODING_QUOTED) {
if (batch_only)
X->encoding_part = ENCODING_QUOTED;
else {
int ch = prompt_letter(LINES-3,"",
*def_ans_yes,
PROMPT_yesno|PROMPT_cancel,
page,
CATGETS(elm_msg_cat, ElmSet,
ElmTextHaveControl,
"Text part %d have control characters. Encode text with quoted-printable? "),
(X - mime_info->top_parts) +1);
if (ch == *def_ans_yes)
X->encoding_part = ENCODING_QUOTED;
else if (ch != *def_ans_no) {
return 0; /* Go again to verify_transmission loop */
}
}
}
/* This update may be little late for previous body parts
but at least headers go correctly also for previous
body parts ...
*/
update_encoding(&(mime_info->encoding_top),X->encoding_part);
if (X->result_charset) {
int ascii_compat = charset_ok_p(X->result_charset);
/* Update Charset of part */
DPRINT(Debug,8,(&Debug,
"charset=%s (%p) 8bit=%s charset ascii compatible=%s\n",
X->result_charset->MIME_name ?
X->result_charset->MIME_name :
"???",
X->result_charset,
(need_enc & HAVE_8BIT) ? "yes" : "no",
ascii_compat ? "yes" : "no"));
/* 1) If charset can be replaced with US-ASCII, do it */
if (!(need_enc & HAVE_8BIT) &&
X->result_charset != ascii_ptr &&
ascii_compat) {
X->result_charset = ascii_ptr;
DPRINT(Debug,8,(&Debug, "Replacing charset with US-ASCII\n"));
}
/* 2) If charset claim to be US-ASCII, but there is 8-bit data
use UNKNOWN-8BIT instead (should not happen...)
*/
else if ((need_enc & HAVE_8BIT) &&
X->result_charset->MIME_name &&
istrcmp(X->result_charset->MIME_name,"US-ASCII") == 0) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmUsingUNKNOWN8BIT,
"Text has 8BIT data and charset=US-ASCII, using charset=UNKNOWN-8BIT instead."));
X->result_charset = MIME_name_to_charset("UNKNOWN-8BIT",1);
DPRINT(Debug,8,(&Debug, "Replacing charset with UNKNOWN-8BIT\n"));
}
/* Add charset to options */
mime_params_add_compat(&(X->TYPE_opts_part),
"charset",
X->result_charset->MIME_name ?
X->result_charset->MIME_name :
"UNKNOWN-8BIT");
}
state_putc('\n',mailer); /* filler */
X->start_loc = out_state_ftell(mailer);
while (0 < (line_len =
mail_gets(buffer, sizeof buffer -1, tmp))) {
if (mime_info->encoding_top == ENCODING_BINARY) {
/* It is better perhaps use canonical eol (CRLF) when mail have
* content transfer encoding BINARY somewhere (see notes about
* BINARYMIME)
*/
/* THIS assumes that
dest->EOLN_is_CRLF is set
*/
state_convert_EOLN(buffer,&line_len,sizeof buffer,mailer);
}
/* Do QUOTED-PRINTABLE conversion if necessary... */
if (X->encoding_part == ENCODING_QUOTED)
line_quoted_printable_encode(buffer,mailer,line_len,TRUE,
mime_info);
else
state_put(buffer, line_len, mailer);
}
X->end_loc = out_state_ftell(mailer);
/* !! */
if (ferror(out_state_FILE(mailer))) {
lib_error(CATGETS(elm_msg_cat, ElmSet,
ElmWriteFailedCopy,
"Write failed to temp file in copy"));
return 0; /* Just indicate failure */
}
DPRINT(Debug,4,
(&Debug,
"Preparing mail for sending: part %d is %d bytes (%s)\n",
X-mime_info->top_parts,
X->end_loc-X->start_loc,
X->encoding_part == ENCODING_QUOTED ?
"quoted-printable" : "Xbit"));
return 1;
}
static int Include_Part P_((FILE *dest,char *buffer,
mime_send_t *mime_info,
struct mime_send_part * X,
struct mailer_info *mailer_info,
charset_t from_charset));
/* Returns 1 if OK */
int convert_text(source,dest,mime_info,from_charset,to_charset,do_pgp,
attachments,mailer_info,page)
FILE *source;
FILE *dest;
mime_send_t *mime_info;
charset_t from_charset;
charset_t to_charset;
int do_pgp;
struct Attachments *attachments;
struct mailer_info *mailer_info;
struct menu_context *page;
{
int ok = 1;
int line_len;
char buffer[SLEN]; /* file reading buffer */
int no_save = 0;
int keyword_error = 0;
struct mime_send_part * X = NULL;
char * tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
if (!tmp)
tmp = "/tmp/";
reset_parts(mime_info);
if (do_pgp) {
#ifdef USE_PGP
X = new_part(mime_info);
X->save_it_on_copy = 1;
X->is_text = 0;
/* write_one_part will fix encoding */
X->encoding_part = ENCODING_7BIT;
X->encoding_part_text = NULL;
if (do_pgp & PGP_MESSAGE) {
int pe = give_dt_enumerate_as_int(&pgp_encrypt_type);
switch (pe) {
case 0:
default:
X -> TYPE = give_media_type2(MIME_TYPE_APPLICATION,"pgp",1);
break;
case 1:
X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
if (!X -> TYPE)
mime_panic(__FILE__,__LINE__,"convert_text",
"text/plain is not known");
break;
case 2:
X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"x-pgp",1);
}
} else {
int ps = give_dt_enumerate_as_int(&pgp_sign_type);
switch (ps) {
case 0:
default:
X -> TYPE = give_media_type2(MIME_TYPE_APPLICATION,"pgp",1);
break;
case 1:
X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
if (!X -> TYPE)
mime_panic(__FILE__,__LINE__,"convert_text",
"text/plain is not known");
break;
case 2:
X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"x-pgp",1);
}
}
if (do_pgp & PGP_PUBLIC_KEY)
mime_params_add_compat(&(X->TYPE_opts_part),"format","keys-only");
else {
mime_params_add_compat(&(X->TYPE_opts_part), "format", "text");
/* This next bit is a non-non-standard, but exmh does this and it
* can be very useful when parsing the message.
*/
if (pgp_status & PGP_MESSAGE) {
mime_params_add_compat(&(X->TYPE_opts_part), "x-action",
(do_pgp & PGP_SIGNED_MESSAGE) ?
"encryptsign" : "encrypt");
} else
mime_params_add_compat(&(X->TYPE_opts_part), "x-action", "sign");
}
/* If message is signed, it can not be character set converted! */
if (get_major_type_code(X -> TYPE) == MIME_TYPE_TEXT)
X -> result_charset = from_charset;
{
out_state_t A;
out_state_clear(&A,STATE_out_file);
set_out_state_file(dest,&A);
/* Let state routines be aware of encoding */
if (mime_info->encoding_top == ENCODING_BINARY)
A.EOLN_is_CRLF = 1;
/* Whole source -- no splitting ... */
if (!write_one_part(mime_info,X,&A,source, page))
ok = 0; /* Write failed or user canceled */
/* out_state_destroy does not fclose() dest */
out_state_destroy(&A);
}
#else
panic("PGP PANIC",__FILE__,__LINE__,"convert_text",
"do_pgp is set but PGP support not compiled in",0);
#endif
} else {
static int tmpcount = 0;
FILE * tmpfile = NULL;
int crypted = FALSE;
if (from_charset != to_charset &&
(!(CS_mapping & charset_properties(from_charset)) ||
!(CS_mapping & charset_properties(to_charset)))) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantConvertCharset,
"Can't convert text from charset %s to charset %s"),
from_charset -> MIME_name ?
from_charset -> MIME_name : "<no MIME name>",
to_charset -> MIME_name ?
to_charset -> MIME_name : "<no MIME name>");
to_charset = from_charset; /* No conversion */
sleep_message(); /* Needed ... */
}
if (from_charset != to_charset) {
DPRINT(Debug,4,
(&Debug,
"Preparing mail for sending: conversion from charset %s to charset %s\n",
from_charset -> MIME_name ?
from_charset -> MIME_name : "<no MIME name>",
to_charset -> MIME_name ?
to_charset -> MIME_name : "<no MIME name>"));
}
mime_info->hdr_charset = to_charset;
while (0 < (line_len =
mail_gets(buffer, sizeof buffer, source))) {
int change_part = 0;
char *ptr = buffer;
int gotten_key = 0;
int incomplete = 0;
int was_incomplete;
if (buffer[line_len-1] != '\n') {
DPRINT(Debug,7,
(&Debug,
"convert_text: Too long line (read %d bytes)\n",
line_len));
incomplete = 1;
}
if (!tmpfile)
change_part = 1;
if (mime_body_keywords && buffer[0] == '[') {
if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE)) ==0 &&
crypted) {
if (tmpfile) {
fputs(END_ENCODE,tmpfile);
fputc('\n',tmpfile);
}
change_part = 1;
} else if (strncmp(buffer, START_ENCODE,
strlen(START_ENCODE)) == 0 ||
strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0 ||
strncmp(buffer, DONT_SAVE2, strlen(DONT_SAVE2)) == 0 ||
strncmp(buffer, MIME_INCLUDE, strlen(MIME_INCLUDE)) ==0)
change_part = 1;
else if (strncmp(buffer,"[[",2) != 0 &&
strncmp(buffer,"[ ",2) != 0) {
if (keyword_error++ == 0) {
if (RawState())
lower_prompt(catgets(elm_msg_cat, ElmSet,
ElmBadKeywordHint,
"Use [[ to specify line starting with ["));
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmBadKeyword,
"Bad keyword on text: %.50s"),
buffer);
} else
lib_transient(CATGETS(elm_msg_cat, ElmSet,
ElmBadKeyword,
"Bad keyword on text: %.50s"),
buffer);
if (!batch_only)
ok = 0;
}
}
/* End previous part */
if (change_part) {
if (tmpfile) {
if (ferror(tmpfile)) {
lib_error(CATGETS(elm_msg_cat, ElmSet,
ElmWriteFailedCopy,
"Write failed to temp file in copy"));
ok = 0;
}
{
out_state_t A;
out_state_clear(&A,STATE_out_file);
set_out_state_file(dest,&A);
/* Let state routines be aware of encoding */
if (mime_info->encoding_top == ENCODING_BINARY)
A.EOLN_is_CRLF = 1;
if (!write_one_part(mime_info,X,&A,tmpfile,page))
ok = 0; /* Write failed or user canceled */
/* out_state_destroy does not fclose() dest */
out_state_destroy(&A);
}
fclose(tmpfile);
tmpfile = NULL;
}
}
if (mime_body_keywords && buffer[0] == '[') {
if (strncmp(buffer, MIME_INCLUDE, strlen(MIME_INCLUDE)) ==0) {
X = new_part(mime_info);
X->save_it_on_copy = !no_save;
X->is_text = 0;
if (Include_Part(dest,buffer,mime_info,X,mailer_info,
from_charset) < 0)
ok = 0;
continue;
} else if (strncmp(buffer, START_ENCODE,
strlen(START_ENCODE))==0) {
if (batch_only) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoEncryptInBatch,
"Sorry. Cannot send encrypted mail in \"batch mode\".\n"));
ok = 0;
} else {
crypted = TRUE;
if (! gotten_key) {
int zz = getkey(ON);
if (zz > 0)
gotten_key = 1;
else
ok = 0;
if (zz < 0) /* Ctrl-C */
break;
} else
get_key_no_prompt(); /* reinitialize.. */
}
continue;
} else if (strncmp(buffer, END_ENCODE,
strlen(END_ENCODE))==0) {
crypted = FALSE;
continue;
} else if ((strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0) ||
(strncmp(buffer, DONT_SAVE2, strlen(DONT_SAVE2))) == 0) {
no_save = 1;
continue;
}
}
/* Start new part */
if (change_part) {
int i;
for (i = 0; i < 10; i++) {
char * filename = elm_message(FRM("%selmsd-%d-%d"),
tmp, getpid (),
tmpcount++);
tmpfile = safeopen_rdwr(filename);
if (!tmpfile) {
int err = errno;
DPRINT(Debug,10,(&Debug,
"convert_text: safeopen_rdwr: %s: %s (errno %d)\n",
filename,
error_description(err),err));
} else {
unlink(filename);
DPRINT(Debug,10,(&Debug,
"convert_text: using temp file (%s)\n",
filename));
}
free(filename);
if (tmpfile)
break;
}
if (!tmpfile) {
lib_error(CATGETS(elm_msg_cat, ElmSet,
ElmOpenFailedCopy,
"Temp file open failed to in copy"));
ok = 0;
break; /* Can't continue without target file handle */
}
X = new_part(mime_info);
X -> save_it_on_copy = !no_save;
X -> is_text = 0;
/* write_one_part will fix encoding */
X -> encoding_part = ENCODING_7BIT;
X -> encoding_part_text = NULL;
X -> result_charset = to_charset;
if (crypted) {
X -> TYPE =
give_media_type2(MIME_TYPE_APPLICATION,"X-ELM-encode",
0);
if (!X -> TYPE)
mime_panic(__FILE__,__LINE__,
"convert_text",
"application/X-ELM-encode is not known");
fputs(START_ENCODE,tmpfile);
fputc('\n',tmpfile);
} else {
X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
if (!X -> TYPE)
mime_panic(__FILE__,__LINE__,
"convert_text",
"text/plain is not known");
}
/* write_one_part will add charset paramater */
X -> result_charset = to_charset;
}
/* Double [[ means that user want start line with [ */
if (strncmp(buffer,"[[",2) == 0) {
ptr = buffer+1;
line_len--;
}
do { /* Handle long lines ... */
struct string * in_str = new_string(from_charset);
struct string * out_str = NULL;
char * result = NULL;
int resultlen = 0;
/* Do conversions -- this does not handle binary files,
* but they should not occur on here anyway...
*/
if (add_streambytes_to_string(in_str,line_len,s2us(ptr),
NULL) != line_len) {
/* FIXME: check errors */
}
out_str = convert_string(to_charset,in_str,0);
bytestream_from_string(out_str,&result,&resultlen);
if (crypted) {
encode(result);
fputs(result,tmpfile);
} else {
/* write_one_part will do \n -> \r\n conversion
* for this part if needed
*/
if (resultlen != fwrite(result,1,resultlen,tmpfile)) {
/* FIXME: check errors */
}
}
free(result);
free_string(&out_str);
free_string(&in_str);
was_incomplete = incomplete;
if (incomplete) {
incomplete = 0;
ptr = buffer;
if (0 < (line_len =
mail_gets(buffer, sizeof buffer, source))) {
if (buffer[line_len-1] != '\n') {
DPRINT(Debug,15,(&Debug,
"convert_text: Long line continues (read %d bytes)\n",
line_len));
incomplete = 1;
} else {
DPRINT(Debug,15,(&Debug,
"convert_text: end of long line (read %d bytes)\n",
line_len));
incomplete = 0;
}
} else {
DPRINT(Debug,15,(&Debug,
"convert_text: failed read to end of long line\n"));
break;
}
}
} while (was_incomplete);
}
if (tmpfile) {
if (ferror(tmpfile)) {
lib_error(CATGETS(elm_msg_cat, ElmSet,
ElmWriteFailedCopy,
"Write failed to temp file in copy"));
ok = 0;
}
{
out_state_t A;
out_state_clear(&A,STATE_out_file);
set_out_state_file(dest,&A);
/* Let state routines be aware of encoding */
if (mime_info->encoding_top == ENCODING_BINARY)
A.EOLN_is_CRLF = 1;
if (!write_one_part(mime_info,X,&A,tmpfile,page))
ok = 0; /* Write failed or user canceled */
/* out_state_destroy does not fclose() dest */
out_state_destroy(&A);
}
fclose(tmpfile);
tmpfile = NULL;
}
}
if (attachments->attachment_count > 0) {
#ifdef USE_PGP
if (do_pgp) {
if (do_pgp & PGP_MESSAGE)
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoPgpAttachEncode,
"WARNING: I PGP encode only main message"));
else
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoPgpAttachSign,
"WARNING: I PGP sign only main message"));
}
#endif
{
int i;
out_state_t A;
out_state_clear(&A,STATE_out_file);
set_out_state_file(dest,&A);
/* Let state routines be aware of encoding */
if (mime_info->encoding_top == ENCODING_BINARY)
A.EOLN_is_CRLF = 1;
/* now add attachments */
for (i = 0; i < attachments->attachment_count; i++) {
X = new_part(mime_info);
X->save_it_on_copy = !no_save;
X->is_text = 0;
if (!attach_message (& (attachments->attach_files[i]),
&A, mime_info, X))
ok = 0;
}
/* out_state_destroy does not fclose() dest */
out_state_destroy(&A);
}
}
if (keyword_error > 1 && RawState())
error_sleep(sleepmsg *2);
return ok;
}
int check_for_multipart(filedesc, mime_info, mailer_info, from_charset)
FILE *filedesc;
mime_send_t *mime_info;
struct mailer_info *mailer_info;
charset_t from_charset;
{
char buffer[SLEN];
int Multi_Part = FALSE;
while (mail_gets(buffer, SLEN, filedesc))
if (mime_body_keywords && buffer[0] == '[') {
if (strncmp(buffer, MIME_INCLUDE, strlen(MIME_INCLUDE)) == 0) {
Multi_Part = TRUE;
if (Include_Part(NULL,buffer, mime_info, NULL,
mailer_info,
from_charset) == -1) {
return(-1);
}
}
}
rewind(filedesc);
return(Multi_Part);
}
static int Include_Part(dest, buffer, mime_info, X, mailer_info, from_charset)
FILE *dest;
char *buffer;
mime_send_t *mime_info;
struct mime_send_part * X;
struct mailer_info *mailer_info;
charset_t from_charset;
{
int check = (X == NULL);
char *ptr;
char *incptr;
char Include_Filename[SLEN];
struct string * orig_filename = NULL;
struct folder_browser * expanded_file = new_browser(selection_file);
char * expanded_2 = NULL;
int is_copy = 0;
int ret = 0;
char Primary_Type[SLEN];
char SubType[SLEN];
media_type_t Type;
char Params[STRING];
char Encoding[SLEN];
int Enc_Type;
FILE *incfile;
int is_text;
ptr = buffer + strlen(MIME_INCLUDE);
while ((*ptr != '\0') && (*ptr == ' '))
ptr++;
incptr = Include_Filename;
while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0') &&
(incptr < Include_Filename + sizeof(Include_Filename) -1))
*incptr++ = *ptr++;
*incptr = '\0';
while ((*ptr != '\0') && (*ptr == ' '))
ptr++;
incptr = Primary_Type;
while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0') && (*ptr!='/')
&& (*ptr != ';')
&& (incptr < Primary_Type + sizeof(Primary_Type) -1))
*incptr++ = *ptr++;
*incptr = '\0';
while ((*ptr != '\0') && (*ptr == ' '))
ptr++;
incptr = SubType;
if (*ptr == '/') {
ptr++;
while ((*ptr != '\0') && (*ptr == ' '))
ptr++;
while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0') && (*ptr!=';')
&& (incptr < SubType + sizeof(SubType) -1))
*incptr++ = *ptr++;
}
*incptr = '\0';
while ((*ptr != '\0') && (*ptr == ' '))
ptr++;
incptr = Params;
while (*ptr == ';') {
ptr++;
if (incptr > Params) {
*incptr++ = ';';
} else if (*ptr == ' ')
ptr++;
while ((*ptr != '\0') && (*ptr == ' ')
&& (incptr < Params + sizeof(Params) -1))
*incptr++ = *ptr++;
while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0') && (*ptr!=';')
&& (incptr < Params + sizeof(Params) -1))
*incptr++ = *ptr++;
while ((*ptr != '\0') && (*ptr == ' '))
ptr++;
}
*incptr = '\0';
while ((*ptr != '\0') && (*ptr == ' '))
ptr++;
incptr = Encoding;
while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0')
&& (incptr < Encoding + sizeof(Encoding) -1))
*incptr++ = *ptr++;
*incptr = '\0';
if (strlen(Include_Filename) == 0) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoIncludeFilename,
"No Filename given, include line ignored"));
ret = -1;
goto check_out;
}
orig_filename = new_string2(from_charset,s2us(Include_Filename));
if (! select_dir_item(expanded_file,&orig_filename)) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFailedToExpandInclude,
"Failed to expand include filename"));
ret = -1;
goto check_out;
}
if (strlen(Primary_Type) == 0 || strlen(SubType) == 0 ) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoContentTypeGiven,
"No Content-type given, include line ignored"));
ret = -1;
goto check_out;
}
Enc_Type = check_encoding(Encoding);
if (Enc_Type == ENCODING_ILLEGAL) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmEncodingIsIllegal,
"Encoding is illegal"));
ret = -1;
goto check_out;
}
Type = give_media_type(Primary_Type,SubType,1);
/* 1 if is text type (true)
* 0 if not text type
* -1 if can't be encoded (ie structured) Message/ or Multipart/
*/
/* 7bit and 8bit are only allowed for line orienteed types */
if (Enc_Type == ENCODING_NONE ||
Enc_Type == ENCODING_7BIT ||
Enc_Type == ENCODING_8BIT) {
DPRINT(Debug,11,(&Debug,
"Include_Part: textual encoding (%s)\n",
ENCODING(Enc_Type)));
is_text = 1;
} else
is_text = give_text_type_code(Type);
if (!dir_make_ref(expanded_file,&expanded_2,&is_copy, is_text)) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmIncludeCannotAccess,
"Include File can't be accessed"), 0);
ret = -1;
goto check_out;
}
/* Don't allow 7BIT if 8-bit charcters in any type,
* don't allow 8BIT if 'binary' characters - K E H */
if (Enc_Type == ENCODING_7BIT || Enc_Type == ENCODING_NONE
|| Enc_Type == ENCODING_8BIT) {
FILE * fp = fopen (expanded_2, "r");
if (fp) {
int tmp = needs_encoding (fp,NULL);
if (tmp & HAVE_BINARY) {
lib_error(CATGETS(elm_msg_cat, ElmSet,ElmIncludeBINARY,
"Include file has BINARY data."));
if (Enc_Type == ENCODING_7BIT || Enc_Type == ENCODING_8BIT) {
/* indicate failure */
ret = -1;
goto check_out;
}
Enc_Type = ENCODING_BINARY;
} else if ((tmp & HAVE_8BIT) && Enc_Type != ENCODING_8BIT) {
lib_error (CATGETS(elm_msg_cat, ElmSet,ElmInclude8BIT,
"Include file has 8BIT data."));
if (Enc_Type == ENCODING_7BIT) {
/* indicate failure */
ret = -1;
goto check_out;
}
Enc_Type = ENCODING_8BIT;
}
fclose(fp);
}
}
if (Enc_Type == ENCODING_8BIT) {
if (mime_info->raw_level < mailer_8bit) {
lib_error (CATGETS(elm_msg_cat, ElmSet,ElmDoesnt8BIT,
"Mailer (MTA) doesn't support 8BIT encoding."));
ret = -1; /* indicate failure */
goto check_out;
}
}
if (Enc_Type == ENCODING_BINARY) {
if (mime_info->raw_level < mailer_binary) {
lib_error (CATGETS(elm_msg_cat, ElmSet,ElmDoesntBINARY,
"Mailer (MTA) doesn't support BINARY encoding!"));
ret = -1;
goto check_out;
}
}
if (is_text < 0 && (Enc_Type == ENCODING_QUOTED ||
Enc_Type == ENCODING_BASE64)) {
lib_error(CATGETS(elm_msg_cat, ElmSet,ElmDontEncoding,
"Content-Type don't allow encoding -- ignoring this part."));
ret = -1;
goto check_out;
}
(void) update_encoding(&(mime_info->encoding_top),Enc_Type);
if (check) {
check_out:
if (orig_filename)
free_string(&orig_filename);
if (expanded_file)
free_dir(&expanded_file);
if (expanded_2) {
if (is_copy &&
0 == unlink(expanded_2)) {
DPRINT(Debug,4,(&Debug,
"Include_Part: '%s' unlinked\n",
expanded_2));
}
free(expanded_2);
}
return ret;
}
incfile = fopen (expanded_2, "r");
if (incfile) {
DPRINT(Debug,4,(&Debug,
"Include_Part: '%s' C-T=%s/%s Params=%s Enc=%d is_text=%d\n",
expanded_2,Primary_Type,SubType, Params, Enc_Type,
is_text));
X->encoding_part = Enc_Type;
if (Enc_Type >= ENCODING_EXPERIMENTAL) {
X->encoding_part_text = safe_strdup(Encoding);
}
X->TYPE = Type;
if (Params[0]) {
int mp = give_dt_enumerate_as_int(&mime_parameters);
/* 0 == plain
1 == encoded
2 == plain-and-encoded
*/
struct string * XX = new_string2(from_charset,s2us(Params));
switch(mp) {
case 0:
X->TYPE_opts_part = parse_mime_param_string(XX,0,1);
break;
case 1:
X->TYPE_opts_part = parse_mime_param_string(XX,0,0);
break;
default:
X->TYPE_opts_part = parse_mime_param_string(XX,
MAX_COMPAT_LEN,
0);
break;
}
free_string(&XX);
}
/* When user uses [include ...] it is better use disposition
* 'inline' instead of 'attachment'
*/
{
int mp = give_dt_enumerate_as_int(&mime_parameters);
/* 0 == plain
1 == encoded
2 == ascii-and-encoded
*/
int was_ok = 1;
char * compat = compat_filename(Include_Filename,&was_ok);
int have_encoded = 0;
if (mp > 0 &&
(!was_ok || !can_ascii_string(orig_filename))) {
struct string *p1 = pick_name(orig_filename);
if (p1) {
mime_params_add(& (X->DISPOSITION_opts),
"filename", p1);
free_string(&p1);
have_encoded ++;
}
}
DPRINT(Debug,9,
(&Debug,
"include: mime_parameters=%d have_encoded=%d was_ok=%d\n",
mp,have_encoded));
if (0 == mp ||
2 == mp ||
! have_encoded) {
mime_params_add_compat(&(X->DISPOSITION_opts),"filename",
compat);
}
free(compat); compat = NULL;
}
X->disposition = DISP_INLINE;
fputc('\n',dest); /* filler */
X->start_loc = ftell(dest);
/* Encoding rules are different for text and non-text.
* For text we must do \n -> \r\n before base64 encoding */
{
out_state_t A;
out_state_clear(&A,STATE_out_file);
set_out_state_file(dest,&A);
/* Let state routines be aware of encoding */
if (mime_info->encoding_top == ENCODING_BINARY)
A.EOLN_is_CRLF = 1;
/* For ENCODING_EXPERIMENTAL this is already supposed to be
encoded */
write_encoded(incfile,&A,Enc_Type,is_text,mime_info);
/* out_state_destroy does not fclose() dest */
out_state_destroy(&A);
}
X->end_loc = ftell(dest);
fclose(incfile);
if (ferror(dest)) {
lib_error(CATGETS(elm_msg_cat, ElmSet,
ElmWriteFailedCopy,
"Write failed to temp file in copy"));
/* Just indicate failure */
ret = -1;
goto fail_out;
}
DPRINT(Debug,4,(&Debug,
"Preparing mail for sending: part %d (include) is %d bytes (%s)\n",
X-mime_info->top_parts,
X->end_loc-X->start_loc,
ENCODING(X->encoding_part)));
} else {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantOpenIncludedFile,
"Can't open included File"));
ret = -1;
goto fail_out;
}
fail_out:
if (orig_filename)
free_string(&orig_filename);
if (expanded_file)
free_dir(&expanded_file);
if (expanded_2) {
if (is_copy &&
0 == unlink(expanded_2)) {
DPRINT(Debug,4,(&Debug,
"Include_Part: '%s' unlinked\n",
expanded_2));
}
free(expanded_2);
}
return ret;
}
/* Determine whether or not the data in "fp" needs to be QUOTED-PRINTABLE
encoded. Used when sending a message.
1 == have 8bit data (HAVE_8BIT)
4 == have control charcters (HAVE_CTRL)
8 == mime requires encoding BINARY (HAVE_BINARY)
*/
int needs_encoding (fp,list)
FILE *fp;
struct scan_list *list;
{
int ch;
int ret = FALSE;
int len = 0;
int printed = 0;
rewind (fp);
while ((ch = fgetc (fp)) != EOF) {
int need_more = 0;
if (list) {
need_more = scanlist_need_more(list,ch);
if (!need_more && !printed) {
DPRINT(Debug,9,(&Debug,
"\nneeds_encoding(): magic scan finished\n"));
printed++;
}
}
/* check for end of line */
if (ch == 13) { /* CR */
ch = fgetc(fp);
if (ch != 10) { /* Not CR LF */
DPRINT(Debug,9,(&Debug,
"\nneeds_encoding(): found CR without LF\n"));
ret |= HAVE_BINARY;
}
if (ch == EOF)
break;
if (list) {
need_more = scanlist_need_more(list,ch);
if (!need_more && !printed) {
DPRINT(Debug,9,(&Debug,
"\nneeds_encoding(): magic scan finished\n"));
printed++;
}
}
}
if (ch == 10) {
len = 0;
continue; /* skip newlines and tabs */
}
len++;
if (len > 990) {
DPRINT(Debug,9,(&Debug,
"\nneeds_encoding(): Line over 990 characters\n"));
ret |= HAVE_BINARY;
}
if (ch == 9) /* skip newlines and tabs */
continue;
if (ch < 32 || ch > 126) {
DPRINT(Debug,9,(&Debug,
"\nneeds_encoding(): found char decimal=%d\n", ch));
if (ch == 0)
ret |= HAVE_BINARY;
if (ch < 32 || ch == 127)
ret |= HAVE_CTRL;
if (ch > 127)
ret |= HAVE_8BIT;
}
if (ret == (HAVE_8BIT | HAVE_CTRL | HAVE_BINARY) &&
!need_more)
break;
}
rewind (fp);
DPRINT(Debug,4,(&Debug,
"needs_encoding()=%d%s%s%s\n",
ret,
ret & HAVE_8BIT ? " HAVE_8BIT" : "",
ret & HAVE_CTRL ? " HAVE_CTRL" : "",
ret & HAVE_BINARY ? " HAVE_BINARY" : ""));
return (ret);
}
char * have_metamail()
{
char * metamail_value = give_dt_estr_as_str(&metamail_path_e, "metamail");
char * return_value = metamail_value;
if (!metamail_value)
return NULL;
if (strcmp(metamail_value,"none") == 0 ||
metamail_value[0] == '\0') {
return_value = NULL;
} else if (metamail_value[0] == '/') {
if (-1 == access(metamail_value,EXECUTE_ACCESS)) {
int err = errno;
lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantMetamail,
"Can't execute metamail: %s.30: %.30s"),
metamail_value,
error_description(err));
DPRINT(Debug,6, (&Debug,
"have_metamail: no access %s: %s\n",
metamail_value,
error_description(err)));
sleep_message();
return_value = NULL;
}
}
DPRINT(Debug,5,(&Debug,
"have_metamail=%s\n",
return_value ? return_value : "<NULL>"));
return return_value;
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1