static char rcsid[] = "@(#)$Id: outheaders.c,v 1.28 2006/04/09 07:37:08 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.28 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
 *****************************************************************************/

#include "headers.h"
#include "hdr_imp.h"
#include "s_me.h"

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif         

DEBUG_VAR(Debug,__FILE__,"header");


static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str) 
     char *str;
{
    return (unsigned char *)str;
}

int add_expanded_addr_ (x,ADR,FN,COM)
     struct expanded_address *x;
     CONST char *ADR;
     CONST struct string *FN;
     CONST struct string *COM;
{
    if (x->magic   != EXP_ADDR_magic) 
    	panic("HEADERS PANIC",__FILE__,__LINE__,"add_expanded_addr_",
	      "Bad magic number",0);

    x->addrs = safe_realloc(x->addrs,(x->addrs_len + 2) * 
			    sizeof (struct addr_item));
    x->addrs[x->addrs_len].addr     = safe_strdup(ADR);
    x->addrs[x->addrs_len].fullname = dup_string(FN);
    x->addrs[x->addrs_len].comment  = dup_string(COM);
    x->addrs[x->addrs_len+1].addr     = NULL;
    x->addrs[x->addrs_len+1].fullname = NULL;
    x->addrs[x->addrs_len+1].comment  = NULL;
    return x->addrs_len++;
}

int add_textual_addr_ (x,TEXTUAL,POS,LEN)
     struct expanded_address *x;
     CONST struct string *TEXTUAL;
     int POS, LEN;
{
    if (x->magic   != EXP_ADDR_magic) 
    	panic("HEADERS PANIC",__FILE__,__LINE__,"add_textual_addr_",
	      "Bad magic number",0);

    x->surface = safe_realloc(x->surface,(x->surface_len + 2) * 
			      sizeof (struct textual));
    if (!TEXTUAL)
	x->surface[x->surface_len].Textual     = NULL;
    else
	x->surface[x->surface_len].Textual     = dup_string(TEXTUAL);
    x->surface[x->surface_len].pos         = POS;
    x->surface[x->surface_len].len         = LEN;
    x->surface[x->surface_len+1].Textual   = NULL;
    x->surface[x->surface_len+1].pos       = POS+LEN;
    x->surface[x->surface_len+1].len       = 0;
    return x->surface_len++;
}

void zero_expanded_address (x)
     struct expanded_address *x;
{
    /* bzero is defined hdrs/defs.h */
    bzero((void *)x,sizeof (*x));
    x->magic        = EXP_ADDR_magic;

    x->addrs        = NULL;
    x->addrs_len    = 0;
    x->surface      = NULL;
    x->surface_len  = 0;
}

void copy_expanded_address (result,source) 
     struct expanded_address *result;
     struct expanded_address source;
{
    int i;
    free_expanded_address(result);

    if (source.magic   != EXP_ADDR_magic) 
    	panic("HEADERS PANIC",__FILE__,__LINE__,"copy_expanded_address",
	      "Bad magic number",0);
    
    for (i = 0; i < source.surface_len; i++) {
	int count = 0;
	int pos = result->addrs_len;
	int j;
	for (j = 0; j < source.surface[i].len; j++) {
	    CONST struct addr_item *ptr1 = & 
		(source.addrs[source.surface[i].pos+j]);
	    int p = add_expanded_addr_(result,ptr1->addr,ptr1->fullname,
				       ptr1->comment);
	    if (0 == count)
		pos = p;
	    count++;
	}
	add_textual_addr_(result,source.surface[i].Textual,pos,count);      
    }
}

void free_expanded_address(x)
     struct expanded_address *x;
{
    int i;
    
    if (x->magic   != EXP_ADDR_magic) 
    	panic("HEADERS PANIC",__FILE__,__LINE__,"free_expanded_address",
	      "Bad magic number",0);

    if (x->addrs) {
	free_addr_items(x->addrs);
	x->addrs = NULL;
    }
    x->addrs_len = 0;
    
    if (x->surface) {
	for (i = 0; i < x->surface_len; i++) {
	    if (x->surface[i].Textual) {
		free_string (&(x->surface[i].Textual));	     
	    }
	}
	free(x->surface);
	x->surface = NULL;
    }
    x->surface_len = 0;
}

struct string * make_surface_addr(ITEM)
     struct addr_item ITEM; 
{
    struct string * result = new_string(display_charset);
    int fl,cl;

    if ((fl = string_len(ITEM.fullname))) {
	int i;

	add_ascii_to_string(result,s2us("\""));
       
	for (i = 0; i < fl; i++) {
	    uint16 code = give_unicode_from_string(ITEM.fullname,i);

	    /* No fancy quotation stuff on textual form for editing */
	    
	    switch(code) {
	    case 0x0022  /* '"'  */: 
	    case 0x005C  /* '\\' */: 
		code = 0x005F /* '_' */;
		break;
	    }
	    add_unicode_to_string(result,1,&code);
	}

       	add_ascii_to_string(result,s2us("\" "));
    }

    add_ascii_to_string(result,s2us("<"));
    add_ascii_to_string(result,s2us(ITEM.addr));
    add_ascii_to_string(result,s2us(">"));
    
    if ((cl = string_len(ITEM.comment))) {
	int i;

	add_ascii_to_string(result,s2us(" ("));

	for (i = 0; i < cl; i++) {
	    uint16 code = give_unicode_from_string(ITEM.comment,i);

	    /* No fancy quotation stuff on textual form for editing */

	    switch(code) {
	    case 0x0022 /* '"'  */: 
	    case 0x005C /* '\\' */: 
	    case 0x0028 /* '('  */: 
	    case 0x0029 /* ')'  */: 
	    case 0x003A /* ':'  */:
	    case 0x003C /* '<'  */: 
	    case 0x003E /* '>'  */:
		code = '_';
		break;
	    }
	    add_unicode_to_string(result,1,&code);
	}

	add_ascii_to_string(result,s2us(")"));

		
    }

    return result;
}

void expanded_address_from_items(result,TMP)
     struct expanded_address *result;
     struct addr_item *TMP;
{
    int j;
   
    if (!TMP)
	return;

    for (j = 0; TMP[j].addr && TMP[j].fullname; j++) {
	int pos = add_expanded_addr_(result,
				     TMP[j].addr,
				     TMP[j].fullname,
				     TMP[j].comment);
	struct string *s = make_surface_addr(TMP[j]);
	add_textual_addr_(result,s,pos,1);
	free_string(&s);
    }
}


#ifdef ANSI_C
hdr_add_to_mailing_hdr add_from_hdr;
#endif
int add_from_hdr(headers,X,value,demime,defcharset,replace)
     struct mailing_headers * headers;
     header_ptr X;
     CONST char *value;
     int demime;
     charset_t defcharset;
     int replace;
{
    struct addr_item * TMP;

    TMP = break_down_address(value,demime,defcharset);

    if (replace)
	free_expanded_address(&(headers->from));

    expanded_address_from_items(&(headers->from), TMP);

    free_addr_items(TMP);

    return 1;
}


#ifdef ANSI_C
hdr_add_to_mailing_hdr add_to_hdr;
#endif
int add_to_hdr(headers,X,value,demime,defcharset,replace)
     struct mailing_headers * headers;
     header_ptr X;
     CONST char *value;
     int demime;
     charset_t defcharset;
     int replace;
{    
    struct addr_item * TMP;

    TMP = break_down_address(value,demime,defcharset);

    if (replace)
	free_expanded_address(&(headers->to));

    expanded_address_from_items(&(headers->to), TMP);

    free_addr_items(TMP);

    return 1;
}


#ifdef ANSI_C
hdr_add_to_mailing_hdr add_cc_hdr;
#endif
int add_cc_hdr(headers,X,value,demime,defcharset,replace)
     struct mailing_headers * headers;
     header_ptr X;
     CONST char *value;
     int demime;
     charset_t defcharset;
     int replace;
{
    struct addr_item * TMP;

    TMP = break_down_address(value,demime,defcharset);

    if (replace)
	free_expanded_address(&(headers->cc));

    expanded_address_from_items(&(headers->cc), TMP);

    free_addr_items(TMP);

    return 1;
}


#ifdef ANSI_C
hdr_add_to_mailing_hdr add_bcc_hdr;
#endif
int add_bcc_hdr(headers,X,value,demime,defcharset,replace)
     struct mailing_headers * headers;
     header_ptr X;
     CONST char *value;
     int demime;
     charset_t defcharset;
     int replace;
{
    int j;

    struct addr_item * TMP;

    TMP = break_down_address(value,demime,defcharset);

    if (replace)
	free_expanded_address(&(headers->bcc));

    expanded_address_from_items(&(headers->bcc), TMP);

    free_addr_items(TMP);

    return 1;
}



#ifdef ANSI_C
hdr_add_to_mailing_hdr add_replyto_hdr;
#endif
int add_replyto_hdr(headers,X,value,demime,defcharset,replace)
     struct mailing_headers * headers;
     header_ptr X;
     CONST char *value;
     int demime;
     charset_t defcharset;
     int replace;
{
    int j;

    struct addr_item * TMP;

    TMP = break_down_address(value,demime,defcharset);

    if (replace)
	free_expanded_address(&(headers->reply_to));

    expanded_address_from_items(&(headers->reply_to), TMP);

    free_addr_items(TMP);

    return 1;
}

#if ANSI_C
static env_from_change_hook dummy_env_from_change;
#endif
static int dummy_env_from_change P_((struct mailer_env_from *X,
				     const char * value));

static int dummy_env_from_change(X,value)
     struct mailer_env_from *X;
     const char * value;
{
    return 0;

}

static env_from_change_hook  * ef_hook = &dummy_env_from_change;

void set_env_from_change_hook(hook)
     env_from_change_hook *hook;
{
    ef_hook = hook;
}

#ifdef ANSI_C
hdr_add_to_mailing_hdr add_env_from_hdr;
#endif
int add_env_from_hdr(headers,X,value,demime,defcharset,replace)
     struct mailing_headers * headers;
     header_ptr X;
     CONST char *value;
     int demime;
     charset_t defcharset;
     int replace;
{
    int ret = 0;

    if (0 == strcmp(value,"<>")) {
	ret = ef_hook(headers->env_from,value);
    } else {
	struct addr_item * TMP;
	
	TMP = break_down_address(value,demime,defcharset);
	
	if (TMP[0].addr && headers->env_from) {
	    char * t = elm_message(FRM("<%s>"),TMP[0].addr);
	    ret = ef_hook(headers->env_from,t);	    
	    free(t);
	}

	free_addr_items(TMP);
    }

    return ret;
}


/* This replaces always */
#ifdef ANSI_C
hdr_add_to_mailing_hdr add_subject_hdr;
#endif

int add_subject_hdr(headers,X,value,demime,defcharset,replace)
     struct mailing_headers * headers;
     header_ptr X;
     CONST char *value;
     int demime;
     charset_t defcharset;
     int replace;
{
    if (headers->subject)
	free_string( & (headers->subject)  );

    headers->subject = 
	hdr_to_string(HDR_TEXT,value,defcharset,demime);

    return 1;
}



#ifdef ANSI_C
hdr_add_to_mailing_hdr add_user_hdr;
#endif

int add_user_hdr(headers,X,value,demime,defcharset,replace)
     struct mailing_headers * headers;
     header_ptr X;
     CONST char *value;
     int demime;
     charset_t defcharset;
     int replace;
{
    if (replace) {

	/* Only replace header with same name */

	int i;
	int count = 0;

	for (i = 0; i < headers->user_header_count; i++) {

	    if (X == headers->user_header[i].name) {

		if (headers->user_header[i].value)
		    free_string(&(headers->user_header[i].value));
		headers->user_header[i].name = NULL;
	    } else {
		headers->user_header[count] = headers->user_header[i];

		if (count != i) {
		    headers->user_header[i].value = NULL;
		    headers->user_header[i].name = NULL;
		}
		count++;
	    }

	}
	headers->user_header_count = count;
    }

    headers->user_header = safe_realloc(headers->user_header,
				     (headers->user_header_count +1) *
				     sizeof (headers->user_header[0]));
    headers->user_header[headers->user_header_count].name = X;
    headers->user_header[headers->user_header_count].value = 
	hdr_to_string(HDR_TEXT,value,defcharset,demime);
    headers->user_header_count++;

    return 1;
}

void dump_expanded_address(debuglevel,text,expanded)
     int debuglevel; 
     CONST char *text;
     struct expanded_address expanded;

{
    struct textual *ptr;
    int l = strlen(text);
    int LASTPOS = -1;

    DPRINT(Debug,debuglevel,
	   (&Debug, 
	    "%s: %d surfaces, %d addresses\n",
	    text, expanded.surface_len, expanded.addrs_len));
 
    for (ptr = expanded.surface; 
	 ptr < expanded.surface + expanded.surface_len; 
	 ptr++) {
	int j;
	if (!ptr->Textual) {
	    DPRINT(Debug,debuglevel,
		   (&Debug, 
		    "%*s [%d] surface NULL! \t(%d addresses)\n",
		    l,"",ptr-expanded.surface,ptr->len));
	} else {
	    DPRINT(Debug,debuglevel,
		   (&Debug, 
		    "%*s [%d] surface=%S \t(%d addresses)\n",
		    l,"",ptr-expanded.surface,ptr->Textual,
		    ptr->len));
	}
	if (ptr->pos != LASTPOS + 1) {     
	    DPRINT(Debug,debuglevel,
		   (&Debug, 
		    "%*s [%d] ERROR!  address pos = %d != %d (lastpost +1)\n",
		    l,"",ptr-expanded.surface, ptr->pos, LASTPOS+1));			
	}
	for (j = 0; j < ptr->len; j++) {
	    if (ptr->pos+j < 0 || ptr->pos+j >= expanded.addrs_len) {
		DPRINT(Debug,debuglevel,
		       (&Debug, 
			"%*s    %d [%d] ERROR, not range 0 - %d\n",
			l,"",j,ptr->pos,
			expanded.addrs_len-1));
	    } else {
		CONST struct addr_item *ptr1 = & (expanded.addrs[ptr->pos+j]);
		if (!ptr1->addr || !ptr1->fullname || !ptr1->comment) {
		    DPRINT(Debug,debuglevel,
			   (&Debug, 
			    "%*s    %d [%d] ERROR! NULL element!\n",
			    l,"",j,ptr->pos+j));
		} else {
		    CONST char *A, *B;

		    DPRINT(Debug,debuglevel,
			   (&Debug, 			    
			    "%*s    %d [%d] addr=%s \tfullname=%S\tcomment=%S\n",
			    l,"",j,ptr->pos+j,
			    ptr1->addr,ptr1->fullname,
			    ptr1->comment));

		    A = get_string_MIME_name(ptr1->fullname);
		    B = get_string_lang(ptr1->fullname);

		    DPRINT(Debug,debuglevel,
			   (&Debug, 			    
			    "%*s         fullname cs=%s lang=%s, ",
			    l,"",
			    A ? A : "<none>",
			    B ? B : "<none>"));
		    
		    A = get_string_MIME_name(ptr1->comment);
		    B = get_string_lang(ptr1->comment);
		    
		    DPRINT(Debug,debuglevel,
			   (&Debug,"comment cs=%s lang=%s\n",
			    A ? A : "<none>",
			    B ? B : "<none>"));

		}
		LASTPOS = ptr->pos+j;
	    }
	}
    }
    DPRINT(Debug,debuglevel,
	   (&Debug, 
	    "%*s === end\n",l,""));
}


/* Returns either backquote replaced string or original
   if return value != buffer, value is malloced 
*/

static char * eval_backquote P_((char *buffer));
static char * eval_backquote(buffer)
     char *buffer;
{
    char *ptr ;
    char *result  = NULL;
    char *x1      = buffer;
    char *cptr    = NULL;
    int quitflag  = 0;

    DPRINT(Debug,8,(&Debug, "eval_backquote: %s\n",
		    buffer));


    for (ptr = buffer; (*ptr || result) && !quitflag; ptr++) {

	if ('`' == *ptr || !*ptr) {
	    
	    char * start = cptr ? cptr : x1;
	    int len;
	    char * clip;

	    quitflag = !*ptr;

	    if (!start)
		panic("HEADERS PANIC",__FILE__,__LINE__,"eval_backquote",
		      "No start pointer",0);
	    
	    len = ptr - start;
	    if (len < 0)
		panic("HEADERS PANIC",__FILE__,__LINE__,"eval_backquote",
		      "Bad start pointer",0);
		
	    clip = safe_malloc(len+1);
	    memcpy(clip,start,len);
	    clip[len] = '\0';

	    DPRINT(Debug,11,(&Debug, "eval_backquote: clip(len=%d)=%s\n",
			     len,clip));

	    if (!cptr) {
		/* Got start of command */

		result = strmcat(result,clip);
		cptr = ptr +1;
		x1 = NULL;
		free(clip);
		continue;
	    }

	    if (len > 0) {
		struct run_state RS;
		CONST char * argv[4];
		int retcode;

		char local_buffer[80];
		char * sh = give_dt_estr_as_str(&shell_e,"shell");
		int n;

		int res;

		if (!sh)
		    sh = "/bin/sh";

		DPRINT(Debug,5,(&Debug, "eval_backquote: command: %s\n",
				clip));

		argv[0] = sh;
		argv[1] = "-c";
		argv[2] = clip;
		argv[3] = NULL;
		
		res = start_run(&RS,
				SY_ENAB_SIGHUP|SY_ENAB_SIGINT|
				SY_RUN_STATE_OPIPE,argv,-1,-1);
		if (0 == res) {
		    lib_error(CATGETS(elm_msg_cat, MeSet, 
				      MeBackquoteCmdFailed,
				      "Backquoted command \"%s\" in elmheaders failed."),
			      clip);
		    free(clip);
		    free(result);

		    DPRINT(Debug,5,(&Debug, 
				    "eval_backquote: start_run failed\n"));
		    return NULL;
		}

		while (0 < (n = 
			    mail_gets(local_buffer, sizeof local_buffer, 
				      RS.pfd))) {	
		    DPRINT(Debug,5,(&Debug, 
				    "eval_backquote: output: %s",
				    local_buffer));
		    if (local_buffer[n-1] != '\n') {
			DPRINT(Debug,5,(&Debug, 
				    "<-- no NEWLINE\n"));
		    }

		    result = strmcat(result,local_buffer);
		    
		}

		if (result && 0 < (n = strlen(result)) &&
		    '\n' == result[n-1]) {
		    result[n-1] = '\0';
		    DPRINT(Debug,5,(&Debug, 
				    "... Removed newline from end of result\n"));
		}

		res = wait_end(&RS,&retcode);
		if (res < 0 || retcode != 0) {
		    lib_error(CATGETS(elm_msg_cat, MeSet, 
				      MeBackquoteCmdFailed,
				      "Backquoted command \"%s\" in elmheaders failed."),
			      clip);
		    free(clip);
		    free(result);
		    
		    DPRINT(Debug,5,(&Debug, 
				    "eval_backquote: command failed: res=%d, retcode=%d\n",
				    res,retcode));
		    return NULL;

		}
	    }

	    x1 = ptr +1;
	    cptr = NULL;
	    free(clip);
	}
    }

    if (result) {
	DPRINT(Debug,8,(&Debug, "eval_backquote: result=%s\n",
			result));
    }
	
    return result ? result : buffer;
}


void import_mailheaders(headers)
     struct mailing_headers * headers;
{
    int err;
    FILE *F;
    char buffer[32*1024];

    if (0 != (err = can_open(user_mailheaders,"r"))) {
	DPRINT(Debug,5,(&Debug, "%s not available: %s (errno=%d)\n",
			user_mailheaders,error_description(err),err));
	return;
    }

    F = fopen(user_mailheaders,"r");
    if (!F) {
	/* In than point opening should have succeed */
	int err = errno;
	lib_error(FRM("%s: %s"),user_mailheaders,err);
	
	return;
    }

    while (0 < read_header_line(F,buffer,sizeof buffer,0)) {

	header_ptr header_name;

	char *k;
	char * value1 = NULL;

	value1 = eval_backquote(buffer);

	if (!value1)
	    continue;

	k = strchr(value1,':');
	if (!k)
	    break;
	*k = '\0';
	k++;
	
	while (whitespace(*k))
	    k++;
	
	header_name = find_header(value1,1);
	
	if (!add_to_mailing_header(headers,header_name,k,
				   !allow_no_hdrencoding,
				   system_charset,0)) {
	    /* TODO: Notice failure ... */
	}

	if (value1 != buffer)
	    free(value1);

    }

    fclose(F);
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1