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

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

#include "def_mailer.h"
#include "s_elm.h"
#include "mailer_imp.h"
#ifdef USE_DLOPEN
#include "shared_imp.h"
#endif



DEBUG_VAR(Debug,__FILE__,"mailer");

#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;
}

int MO_default_value (M,L,value,set)
     struct mailer_config      *M;
     struct mailer_option_list *L;
     char **value;
     int set;
{
    if (set) {
	if (!value) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmMailerOptionValue,
			      "Mailer option %s requires value"),
		      L->field);
	    return 0;
	}

	if (L->malloced) {
	    free(*(L->value));
	    *(L->value) = NULL;
	}

	*(L->value) = safe_strdup(*value);
	L->malloced = 1;	
    }
    else {
	*value = *(L->value);
    }
    return 1;
}


#if ANSI_C
#define S_(x) static x;
#else
#define S_(x)
#endif

S_(mailer_set_option default_set_option)
static int default_set_option(M,X,value) 
     struct mailer_config *M;
     struct mailer_option_list *X;
     char *value;
{
    int r ;

    r = X->value_func(M,X,&value,1);
    return r;
}

S_(mailer_init_hook default_mailer_init)
static int default_mailer_init P_((struct mailer_config *M,
				   struct mail_send_state *C,
				   struct mailer_info *I));

S_(mailer_init_hook submitmail_mailer_init)
static int submitmail_mailer_init P_((struct mailer_config *M,
				      struct mail_send_state *C,
				      struct mailer_info *I));



S_(mailer_close_hook default_mailer_close)
static void default_mailer_close P_((struct mailer_config *M,
				     struct mail_send_state *C));


S_(mailer_info_init_hook default_mailer_info_init)
static int default_mailer_info_init P_((struct mailer_config *M,
					struct mailer_info *I));


S_(mailer_info_close_hook null_mailer_info_close)
static void null_mailer_info_close(M,I)
     struct mailer_config *M;
     struct mailer_info *I;
{
    /* Nothing */
}

/* Return 
   -1 if caller should result with default data (return 1)
   -2 if caller should just test passwd
   0  if failure
   1  is succees
*/
S_(mailer_info_verify_addr default_mailer_info_verify_addr)
static int default_mailer_info_verify_addr(M,I,text,result)
     struct mailer_config *M;
     struct mailer_info  *I;
     CONST char *text;
     struct addr_item *result;
{
    return -2;   /* caller does job */
}

S_(mailer_info_gen_def_ef default_mailer_info_gen_def_ef)
static void default_mailer_info_gen_def_ef(M,I,X)
     struct mailer_config *M;
     struct mailer_info *I;
     struct mailer_env_from *X; 
{
    /* Nothing */
}

S_(mailer_info_set_ef default_mailer_info_set_ef)
static  void default_mailer_info_set_ef(M,I,X,value)
     struct mailer_config *M;
     struct mailer_info *I;
     struct mailer_env_from *X;
     CONST char *value;
{
    /* Sets env from without checking */
    X->mail_from = strmcpy(X->mail_from,value);
}

/* Return
   1 == mailer restarted, reconnect succeed -- reask info
   0 == mailer not disconnected
  -1 == mailer disconnected, reconnect failed 
*/
S_(mailer_info_restart_hook default_mailer_info_rs_hook)
static int default_mailer_info_rs_hook P_((struct mailer_config *M,
					 struct mailer_info *I));
static int default_mailer_info_rs_hook(M,I)
     struct mailer_config *M;
     struct mailer_info *I;
{
    return 0;  /* Assume not restarted */
}

S_(mailer_info_set_ef sendmail_mailer_info_set_ef)
static  void sendmail_mailer_info_set_ef P_((struct mailer_config *M,
					     struct mailer_info *I,
					     struct mailer_env_from *X,
					     const char *value));

S_(mailer_info_verify_addr sendmail_mailer_info_verify_addr)
static int sendmail_mailer_info_verify_addr P_((struct mailer_config *M,
						struct mailer_info  *I,
						const char *text,
						struct addr_item *result));

S_(mailer_info_query_hook default_mailer_info_query)
static int default_mailer_info_query P_((struct mailer_config *M,
					 struct mailer_info *I,
					 enum MI_query query));
    
S_(mailer_info_query_hook sendmail_mailer_info_query)
static int sendmail_mailer_info_query P_((struct mailer_config *M,
					  struct mailer_info *I,
					  enum MI_query query));

S_(mailer_backend_hook unknown_mailer_backend)
static int unknown_mailer_backend P_((struct mailer_config *M,
					  struct mail_send_state **C,
					  int encoding_top,
					  char * title,
					  sending_message_func *sm));

S_(mailer_backend_hook sendmail_mailer_backend)
static int sendmail_mailer_backend P_((struct mailer_config *M,
				       struct mail_send_state **C,
				       int encoding_top,
				       char * title,
				       sending_message_func *sm));

S_(mailer_backend_hook submitmail_mailer_backend)
static int submitmail_mailer_backend P_((struct mailer_config *M,
					 struct mail_send_state **C,
					 int encoding_top,
					 char * title,
					 sending_message_func *sm));

S_(mailer_backend_hook execmail_mailer_backend)
static int execmail_mailer_backend P_((struct mailer_config *M,
				       struct mail_send_state **C,
				       int encoding_top,
				       char * title,
				       sending_message_func *sm));

static struct mailer_config  * selected_mailer = NULL;

static char * unknown_mailer_path     = DEFAULT_MAILER_PATH;
static char * sendmail_mailer_path    = SENDMAIL_MAILER_PATH;
static char * submitmail_mailer_path  = SUBMITMAIL_MAILER_PATH;
static char * execmail_mailer_path    = EXECMAIL_MAILER_PATH;

static enum bodytypes {
    have_7bit = 0, have_8bit = 1, have_binary = 2 
} sendmail_supported_bodytype   = have_7bit;
static int sendmail_supports_dsn = 0;
static int sendmail_verify_address = 0;

static int MO_sendmail_bodytype P_((struct mailer_config      * M,
				    struct mailer_option_list * L,
				    char **value,int set));
static int MO_sendmail_bodytype (M,L,value,set)
     struct mailer_config      *M;
     struct mailer_option_list *L;
     char **value;
     int set;
{
    static char *sendmail_bodytypes[] = { "7bit", "8bit", "binary" };
    static int value_set  = 0;
    
    if (set) {
	int i;

	if (!*value) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmMailerOptionValue,
			      "Mailer option %s requires value"),
		      L->field);
	    return 0;
	}
	
	for (i = 0; 
	     i < sizeof sendmail_bodytypes / sizeof (sendmail_bodytypes[0]);
	     i++) {
	    if (0 == strcmp(sendmail_bodytypes[i],*value)) {
		sendmail_supported_bodytype = (enum bodytypes)i;	       
		value_set = 1;
		return 1;
	    }
	}
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSendmailBadBodytype,
			  "Bad supported-bodytype %s for sendmail"),
		  *value);
	return 0;
    }
    else {
	if (sendmail_supported_bodytype < have_7bit ||
	    sendmail_supported_bodytype > have_binary)
	    panic("MAILER PANIC",__FILE__,__LINE__,
		  "MO_sendmail_bodytype",
		  "Bad supported-bodytype",0);

	*value = sendmail_bodytypes[sendmail_supported_bodytype];
	if (sendmail_supported_bodytype > have_7bit)
	    return 1;  /* Give value always */
    }
    return value_set; /* Return calue only if set at lweast once */
}

static int MO_sendmail_dsn P_((struct mailer_config      * M,
			       struct mailer_option_list * L,
			       char **value,int set));
static int MO_sendmail_dsn (M,L,value,set)
     struct mailer_config      *M;
     struct mailer_option_list *L;
     char **value;
     int set;
{
    static char *no_yes[] = { "no", "yes" };
    static int value_set  = 0;

    if (set) {
	if (!*value)
	    sendmail_supports_dsn = 1;
	else if (0 == strcmp(*value,no_yes[0]))
	    sendmail_supports_dsn = 0;
	else if (0 == strcmp(*value,no_yes[1])) 
	    sendmail_supports_dsn = 1;
	else {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSendmailBadDsn,
			      "Bad supports-dsn=%s for sendmail"),
		      *value);
	    return 0;
	}	
	value_set = 1;
    } else {
	/* Do not return option if sendmail_supports_dsn is not supported */

	if (sendmail_supports_dsn) {
	    *value = no_yes[1];
	    return 1;
	}
	*value = no_yes[0]; /* Only show if supports-dsn=no or
			       supports-dsn=yes is given at least once 
			    */
    }    
    return value_set;
}

static int MO_sendmail_verify P_((struct mailer_config      * M,
			       struct mailer_option_list * L,
			       char **value,int set));
static int MO_sendmail_verify (M,L,value,set)
     struct mailer_config      *M;
     struct mailer_option_list *L;
     char **value;
     int set;
{
    static char *no_yes[] = { "no", "yes" };
    static int value_set  = 0;

    if (set) {
	if (!*value)
	     sendmail_verify_address = 1;
	else if (0 == strcmp(*value,no_yes[0]))
	    sendmail_verify_address = 0;
	else if (0 == strcmp(*value,no_yes[1])) 
	    sendmail_verify_address = 1;
	else {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSendmailBadVerify,
			      "Bad verify=%s for sendmail"),
		      *value);
	    return 0;
	}	
	value_set = 1;
    } else {
	if (sendmail_verify_address) {
	    *value = no_yes[1];
	    return 1;
	}
	*value = no_yes[0]; /* Only show if verify=no or
			       verify=yes is given at least once 
			    */
    }    
    return value_set;
}


static int MO_dont_add_from P_((struct mailer_config      * M,
			       struct mailer_option_list * L,
			       char **value,int set));
static int MO_dont_add_from (M,L,value,set)
     struct mailer_config      *M;
     struct mailer_option_list *L;
     char **value;
     int set;
{
    static char *yes_dont[] = { "yes", "dont" };
    static int value_set  = 0;

    if (set) {
	if (!*value)
	    M -> mailer_bits  &= ~MB_DONT_ADD_FROM;
	else if (0 == strcmp(*value,yes_dont[0]))
	    M -> mailer_bits  &= ~MB_DONT_ADD_FROM;
	else if (0 == strcmp(*value,yes_dont[1])) 
	    M -> mailer_bits  |= MB_DONT_ADD_FROM;
	else {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmBadAddFrom,
			      "Bad add-from=%s for %s"),
		      *value, M->mailer_type);
	    return 0;
	}
	value_set = 1;
    } else {
	
	*value = yes_dont[ (M -> mailer_bits & MB_DONT_ADD_FROM) ?
			 1 : 0 ];

	if (M -> mailer_bits & MB_DONT_ADD_FROM)
	    return 1;

	/* Do not show add-from=yes if it is not set explicity */
    }

    return value_set;
}

static int MO_use_domain P_((struct mailer_config      * M,
			     struct mailer_option_list * L,
			     char **value,int set));
static int MO_use_domain (M,L,value,set)
     struct mailer_config      *M;
     struct mailer_option_list *L;
     char **value;
     int set;
{
    static char *no_yes[] = { "no", "yes" };
    static int value_set  = 0;

    if (set) {
	if (!*value)
	    M -> mailer_bits  |= MB_USE_DOMAIN;
	else if (0 == strcmp(*value,no_yes[0]))
	    M -> mailer_bits  &= ~MB_USE_DOMAIN;
	else if (0 == strcmp(*value,no_yes[1])) 
	    M -> mailer_bits  |= MB_USE_DOMAIN;
	else {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmBadUseDomain,
			      "Bad use-domain=%s for %s"),
		      *value, M->mailer_type);
	    return 0;
	}
	value_set = 1;
    } else {
	
	*value = no_yes[ (M -> mailer_bits & MB_USE_DOMAIN) ?
			 1 : 0 ];
       
	if (!(M -> mailer_bits & MB_USE_DOMAIN))
	    return 1;

	/* Do not show use-domain=yes if it is not set explicity */
    }

    return value_set;
}

int MO_allow_set_sender (M,L,value,set)
     struct mailer_config      *M;
     struct mailer_option_list *L;
     char **value;
     int set;
{
    static char *no_yes[] = { "no", "yes" };
    static int value_set  = 0;

    if (set) {
	if (!*value)
	    M -> mailer_bits  |= MB_ALLOW_SET_SENDER;
	else if (0 == strcmp(*value,no_yes[0]))
	    M -> mailer_bits  &= ~MB_ALLOW_SET_SENDER;
	else if (0 == strcmp(*value,no_yes[1])) 
	    M -> mailer_bits  |= MB_ALLOW_SET_SENDER;
	else {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmBadAllowSetSender,
			      "Bad allow-set-sender=%s for %s"),
		      *value, M->mailer_type);
	    return 0;
	}
	value_set = 1;
    } else {
	
	*value = no_yes[ (M -> mailer_bits & MB_ALLOW_SET_SENDER) ?
			 1 : 0 ];
       
	if (M -> mailer_bits & MB_ALLOW_SET_SENDER)
	    return 1;

	/* Do not show use-domain=no if it is not set explicity */
    }

    return value_set;
}


static struct mailer_option_list UNKNOWN_options[] = {
    { "path",     &unknown_mailer_path,          MO_default_value, 0 },
    { "add-from", NULL,                          MO_dont_add_from, 0 },
    { "use-domain", NULL,                        MO_use_domain, 0 },
    { NULL, NULL,                                MO_default_value, 0 }
};

static struct mailer_option_list SENDMAIL_options[] = {
    { "path",     &sendmail_mailer_path,          MO_default_value, 0 },
    { "add-from", NULL,                           MO_dont_add_from, 0 },
    { "use-domain", NULL,                         MO_use_domain, 0 },
    { "supported-bodytype", NULL,                 MO_sendmail_bodytype, 0 },
    { "supports-dsn", NULL,                       MO_sendmail_dsn,      0 },
    { "verify", NULL,                             MO_sendmail_verify,      0 },
    { "allow-set-sender",NULL,                    MO_allow_set_sender,     0 },
    { NULL, NULL,                                 MO_default_value, 0 }
};

static struct mailer_option_list SUBMITMAIL_options[] = {
    { "path",     &submitmail_mailer_path,            MO_default_value, 0 },
    { "add-from", NULL,                          MO_dont_add_from, 0 },
    { "use-domain", NULL,                        MO_use_domain, 0 },
    { NULL, NULL,                                MO_default_value, 0 }
};
static struct mailer_option_list EXECMAIL_options[] = {
    { "path",     &execmail_mailer_path,         MO_default_value, 0 },
    { "add-from", NULL,                          MO_dont_add_from, 0 },
    { "use-domain", NULL,                        MO_use_domain, 0 },
    { NULL, NULL,                                MO_default_value, 0 }
};


static struct mailer_config MAILERS[] = {
    { "unknown", &(UNKNOWN_options[0]), default_set_option, 
      default_mailer_init, default_mailer_close, unknown_mailer_backend,
      &unknown_mailer_path,  
      default_mailer_info_init, null_mailer_info_close,
      default_mailer_info_query, default_mailer_info_verify_addr,
      default_mailer_info_gen_def_ef, default_mailer_info_set_ef,
      default_mailer_info_rs_hook,

      MB_USE_DOMAIN
    },
    { "sendmail", &(SENDMAIL_options[0]), default_set_option, 
      default_mailer_init, default_mailer_close, sendmail_mailer_backend,
      &sendmail_mailer_path, 
      default_mailer_info_init, null_mailer_info_close,
      sendmail_mailer_info_query, sendmail_mailer_info_verify_addr,
      default_mailer_info_gen_def_ef, sendmail_mailer_info_set_ef,  
      default_mailer_info_rs_hook,

      MB_DONT_ADD_FROM
    },
    { "submitmail", &(SUBMITMAIL_options[0]), default_set_option, 
      submitmail_mailer_init, default_mailer_close, submitmail_mailer_backend,
      &submitmail_mailer_path, 
      default_mailer_info_init, null_mailer_info_close,
      default_mailer_info_query, default_mailer_info_verify_addr,
      default_mailer_info_gen_def_ef, default_mailer_info_set_ef,
      default_mailer_info_rs_hook,

      MB_USE_DOMAIN
    },
    { "execmail", &(EXECMAIL_options[0]), default_set_option, 
      default_mailer_init, default_mailer_close, execmail_mailer_backend,
      &execmail_mailer_path, 
      default_mailer_info_init, null_mailer_info_close,
      default_mailer_info_query, default_mailer_info_verify_addr,
      default_mailer_info_gen_def_ef, default_mailer_info_set_ef,
      default_mailer_info_rs_hook,

      MB_DONT_ADD_FROM
    }
};

static int valid_mailer P_((struct mailer_config *T));
static int valid_mailer(T)
     struct mailer_config *T;
{
    int x;

    for (x = 0; x < (sizeof MAILERS) / sizeof (MAILERS[0]); x++)
	if (T == &MAILERS[x])
	    return 1;
#ifdef USE_DLOPEN
    for (x = 0; x < shared_MCF_type_count; x++)
	if (T == shared_MCF_types[x].T)
	    return 1;
#endif
    return 0;
}

static int default_mailer_info_init(M,I)
     struct mailer_config *M;
     struct mailer_info *I;
{

    if (M->mailer_path) {
	/* CHECK that mailer is executable ... */
	
	if (! *(M->mailer_path) ||
	    !(*(M->mailer_path))[0] ||
	    0 == strcmp(*(M->mailer_path),"none")
	    ) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoMailerPath,
			      "Mailer path for %s not specified"),
		  M->mailer_type);
	    return 0;
	}
	
	if (0 != access(*(M->mailer_path),EXECUTE_ACCESS)) {
	    int err = errno;
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNotExecutable,
			      "%s not executable: %s"),
		      *(M->mailer_path),error_description(err));
	    return 0;
	}
    }

    return 1;
}


static int default_mailer_info_query(M,I,query)
     struct mailer_config *M;
     struct mailer_info *I;
     enum MI_query query;
{
    /* Answer to universal queries */

    switch(query) {
    case MI_DONT_ADD_FROM:
	return (M -> mailer_bits & MB_DONT_ADD_FROM) ? 1 : 0;
    case MI_USE_DOMAIN:
	return (M -> mailer_bits & MB_USE_DOMAIN) ? 1 : 0;
    }

    return 0; /* Not available */
}

static int sendmail_mailer_info_query(M,I,query)
     struct mailer_config *M;
     struct mailer_info *I;
     enum MI_query query;
{
    switch(query) {

    case MI_HAVE_8BITMIME:
	return  sendmail_supported_bodytype >= have_8bit;

    case MI_HAVE_BINARYMIME:
	return  sendmail_supported_bodytype >= have_binary;

    case MI_HAVE_DSN:
	return sendmail_supports_dsn;
    }

    return default_mailer_info_query(M,I,query);
}


static char * delay_parsing_info = NULL;

static int check_mailer P_((void));
static int check_mailer()
{
    if (selected_mailer)
	return 1;
    if (delay_parsing_info) {
	int r = mailerfunc(&delay_parsing_info,-1,0,NULL);
	free(delay_parsing_info); delay_parsing_info = NULL;
	return r;
    }
    return 0;
}

#ifdef ANSI_C
option_func mailerfunc;
#endif
int mailerfunc(value,enter,lineno,filename)
     char **value; 
     int enter;
     int lineno; 
     char *filename;
{
    int ok = 1;

    if (enter) {
	int i;
	char * WALK;

	char * temp = safe_strdup(*value);
	
	char * f = mime_parse_content_opts(temp, &WALK);
	
	if (!f) {
	    free(temp);
	    return 0;
	}

	for (i = 0; i < (sizeof MAILERS) / sizeof (MAILERS[0]); i++)
	    if (0 == strcmp(f,MAILERS[i].mailer_type))
		break;
	if (i >= (sizeof MAILERS) / sizeof (MAILERS[0])) {
	    
#ifdef USE_DLOPEN
	    if (enter > 0) {
		DPRINT(Debug,8,(&Debug,"mailerfunc: Parsing delayed\n"));

		selected_mailer = NULL;
		delay_parsing_info = strmcpy(delay_parsing_info,*value);
		free(temp);
		return 1;		    
	    }

	    selected_mailer = loc_mailer_type(f);
	    if (selected_mailer)
		goto found_shared;
#endif	    

	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmUnknowMailerType,
			      "Unknown mailer type %s"),
		      f);

	    free(temp);

	    mark_flocal_changed(mailerfunc);

	    return 0;
	}
	selected_mailer = &(MAILERS[i]);

#ifdef USE_DLOPEN
    found_shared:
#endif

	DPRINT(Debug,8,(&Debug,
			"mailerfunc: Selected %s\n",
			selected_mailer->mailer_type));

	while (NULL != (f = mime_parse_content_opts(NULL, &WALK))) {
	    char * q = strchr(f,'=');

	    if (q) {
		char * x = q;
 
		while (x > f && ' ' == *(x-1))
		    x--;
		*x = '\0';

		q++;
	    
		while (' ' == *q)
		    q++;
	    }

	    for (i = 0; selected_mailer->list[i].field; i++) 
		if ( 0 == strcmp(f,selected_mailer->list[i].field))
		    break;
	    if (!selected_mailer->list[i].field) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmUnknowMailerOption,
				  "Unknown mailer option %s"),
			  f);
		ok = 0;
		continue;
	    }

	    if (q) {
		char * val = dequote_opt(q,strlen(q));

		if (!selected_mailer->m_set_option(selected_mailer,
                                                   &(selected_mailer->list[i]),
                                                   val))
		    ok = 0;
		free(val);
	    } else {
		if (!selected_mailer->m_set_option(selected_mailer,
						   &(selected_mailer->list[i]),
						   NULL))
		    ok = 0;
	    }
	}

	if (selected_mailer->mailer_path &&
	    (!*(selected_mailer -> mailer_path) ||
	     !(*(selected_mailer -> mailer_path))[0] ||
	     0 == strcmp(*(selected_mailer -> mailer_path),"none"))) {

            lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoMailerPath,
                              "Mailer path for %s not specified"),
		      selected_mailer->mailer_type);
	    ok = 0;
	}

	free(temp);
    } else {
	if (delay_parsing_info) {
	    *value = delay_parsing_info;
	    DPRINT(Debug,8,(&Debug,
			    "mailerfunc: Returning delayed parsing info\n"));
	} else {
	    /* static pointer to buffer accross invocations */
	    static char * return_buffer = NULL;
	    int i;
	    
	    if (!check_mailer())
		selected_mailer = &(MAILERS[0]);
	    
	    return_buffer = strmcpy(return_buffer,selected_mailer->mailer_type);  
	    
	    for (i = 0; selected_mailer->list[i].field; i++) {
		char * value = NULL;
		
		if (selected_mailer->
		    list[i].value_func(selected_mailer,
				       & (selected_mailer->list[i]),
				       &value,0)) {
		    
		    return_buffer = strmcat(return_buffer,"; ");
		    
		    if (value) {
			char *Q = elm_message(FRM("%s=%Q"),
					      selected_mailer->list[i].field,
					      value);
			return_buffer = strmcat(return_buffer,Q);
			free(Q);
		    } else
			return_buffer = strmcat(return_buffer,
						selected_mailer->list[i].field);
		}
	    }
	    *value = return_buffer;
	}
    }
    return ok;
}

void init_default_mailer()
{
    char * p;
    char * type;

    /* INIT default mailer if not given on configuration file */

    if (check_mailer())
	return;

    p = strrchr(DEFAULT_MAILER_PATH,'/');
    if (p)
	p++;
    else {
	DPRINT(Debug,1,(&Debug,
			"On mailer path %s have not / character!!",
			DEFAULT_MAILER_PATH));
	p = DEFAULT_MAILER_PATH;
    } 

    if (0 == strcmp(p,"sendmail")) 
	type = "sendmail";
    else if (0 == strcmp(p,"submit"))
	type = "submitmail";
    else if (0 == strcmp(p,"execmail"))
	type = "execmail";
    else
	type = "unknown";

    if (!mailerfunc(&type,1,0,NULL) ||
	!check_mailer()) {
	DPRINT(Debug,1,(&Debug,
			"Calling of mailerfunc for setting of default mailer failed"));
	return;
    }

    if (selected_mailer -> mailer_path) {
	*(selected_mailer -> mailer_path) = DEFAULT_MAILER_PATH;

	if (!*(selected_mailer -> mailer_path) ||
	    !(*(selected_mailer -> mailer_path))[0] ||
	    0 == strcmp(*(selected_mailer -> mailer_path),"none")) {
	
            lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoMailerPath1,
                              "Give mailer path for mailer = %s on global elm.rc"),
                      selected_mailer->mailer_type);
	    /* wait that message will be noticed ... */
#if POLL_METHOD
	    wait_for_timeout(2);
#else
	    sleep(2);
#endif
        }

    } else {
	DPRINT(Debug,1,(&Debug,"Mailer type %s do not have mailer path!\n",
			selected_mailer -> mailer_type));
    }

#if defined(DEFAULT_8BITMIME)
    sendmail_supported_bodytype = have_8bit;
#endif

#if defined(DEFAULT_BINARYMIME)
    sendmail_supported_bodytype = have_binary;
#endif

#if defined(DEFAULT_DSN)
    sendmail_supports_dsn = 1;
#endif

#if defined(DEFAULT_DONT_ADD_FROM)
    selected_mailer -> mailer_bits  |= MB_DONT_ADD_FROM;
#else
    selected_mailer -> mailer_bits  &= ~MB_DONT_ADD_FROM;
#endif

#if defined(DEFAULT_USE_DOMAIN)
    selected_mailer -> mailer_bits  |= MB_USE_DOMAIN;
#else
    selected_mailer -> mailer_bits  &= ~MB_USE_DOMAIN;
#endif

}


static void delete_mail_info P_((struct mailer_info *S));


struct mailer_info  *get_mailer_info() {
    struct mailer_info * ret;

    if (!check_mailer()) {
	DPRINT(Debug,7,(&Debug,"No mailer available\n"));
	return NULL;
    }

    DPRINT(Debug,8,(&Debug,
		    "get_mailer_info: Current mailer %s\n",
		    selected_mailer->mailer_type));



    ret = safe_malloc (sizeof (*ret));

    /* bzero is defined on hdrs/defs.h */
    bzero((void *)ret, sizeof (*ret));

    ret->magic         = MAILER_INFO_magic;
    ret->mailer_type   = selected_mailer;

    ret->delete_pending   = 0;
    ret->p.ptr            = NULL;
    ret->first            = NULL;
    ret->first_e          = NULL;
   
    DPRINT(Debug,7,(&Debug,
		    "Initialising info for %s mailer, info=%p\n",
		    ret->mailer_type->mailer_type,ret));

    if (! ret->mailer_type->mi_init_hook(ret->mailer_type,ret)) {
	/* FAILURE */
	ret->delete_pending = 1;

	delete_mail_info(ret);
	return NULL;
    }

    return ret;
}

static void delete_mail_info(S)
     struct mailer_info *S;
{

    if (S->magic != MAILER_INFO_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"delete_mail_info",
	      "Bad magic number (mailer info)",0);

    if (S->first)
    	panic("MAILER PANIC",__FILE__,__LINE__,"delete_mail_info",
	      "mailer send state list not empty",
	      0);

    if (S->first_e)
    	panic("MAILER PANIC",__FILE__,__LINE__,"delete_mail_info",
	      "mailer env from list not empty",
	      0);

    if (!valid_mailer(S->mailer_type))
    	panic("MAILER PANIC",__FILE__,__LINE__,"delete_mail_info",
	      "Bad mailer type",
	      0);

    DPRINT(Debug,7,(&Debug,
		    "Deleting info for %s mailer, info=%p\n",
		    S->mailer_type->mailer_type,S));

    S->mailer_type->mi_close_hook(S->mailer_type,S);

    if (S->p.ptr) {
	DPRINT(Debug,1,(&Debug,
			"delete_mail_info: WARNING: Private data (S->p.ptr) not free'ed\n"));		       
    }


    /* bzero is defined on hdrs/defs.h */
    bzero((void *)S, sizeof (*S));

    free(S);    
}

static int can_delete_mailer_info P_((struct mailer_info **S));
static int can_delete_mailer_info(S)
     struct mailer_info **S;
{
    if ((*S)->first)
	return 0;
    if ((*S)->first_e)
	return 0;
    delete_mail_info(*S);

    return 1;
}

void free_mailer_info(S)
     struct mailer_info **S;
{
    if (*S) {
	if ((*S)->magic != MAILER_INFO_magic)
	    panic("MAILER PANIC",__FILE__,__LINE__,"free_mail_info",
		  "Bad magic number (mailer info)",0);
		
	(*S)->delete_pending = 1;
	if (! can_delete_mailer_info(S)) {
	    DPRINT(Debug,7,(&Debug,
			    "delaying deleting of mail info -> delete_pending, info=%s\n",
			    *S));
	}
	*S = NULL;
    }
}

int query_mailer_info(I,query)
     struct mailer_info  *I;
     enum MI_query query;
{
    int ret;

    if (I->magic != MAILER_INFO_magic)
	panic("MAILER PANIC",__FILE__,__LINE__,"query_mailer_info",
	      "Bad magic number (mailer info)",0);

    if (!valid_mailer(I->mailer_type))
    	panic("MAILER PANIC",__FILE__,__LINE__,"query_mailer_info",
	      "Bad mailer type",
	      0);

    ret = I->mailer_type->mi_query_hook(I->mailer_type,I,query);

    return ret;
}


/* -1  if connection lost
    0  if OK
    1  if mailer reinitialized and  query_mailer_info()
          need to be called again
*/
int mailer_restarted(I)
     struct mailer_info  *I;
{
    int ret;

    if (I->magic != MAILER_INFO_magic)
	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_restarted",
	      "Bad magic number (mailer info)",0);

    if (!valid_mailer(I->mailer_type))
    	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_restarted",
	      "Bad mailer type",
	      0);

    ret = I->mailer_type->mi_restart_hook(I->mailer_type,I);

    return ret;
}

static int default_mailer_init(M,C,I)
     struct mailer_config *M;
     struct mail_send_state *C;
     struct mailer_info *I;
{
    FILE * F;
    char *tmp;

    if (C->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"default_mailer_init",
	      "Bad magic number (mail send state)",0);

    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
    if (!tmp)
	tmp = "/tmp/";

    C->fname = elm_message(FRM("%selm.snd-%d"), 
			tmp, getpid ());

    if (!C->fname) {
	DPRINT(Debug,1,(&Debug,   
			"couldn't make temp file nam! (mail)\n"));
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCouldNotMakeTemp,
			  "Sorry - couldn't make temp file name."));
	return 0;
    }

    F = safeopen_rdwr(C->fname);

    if (!F) {
	int err = errno;
	DPRINT(Debug,1, (&Debug,
			 "Attempt to open file %s for writing failed! (write_header_info)\n",
			 C->fname));
	DPRINT(Debug,1, (&Debug, "** %s **\n\n", error_description(err)));
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorTryingToWrite,
			  "Error %s encountered trying to write to %s."), 
		  error_description(err), C->fname);

	return 0;
    }

    out_state_clear(& (C->OUT), STATE_out_file);
    set_out_state_file(F,& (C->OUT));

    return 1;  /* OK */
}


static void default_mailer_close(M,C)
     struct mailer_config *M;
     struct mail_send_state *C;
{
    /* close assigned FD FD */

    if (C->OUT.magic) {
	FILE *F = out_state_FILE(& (C->OUT));

	if (F) {
	    out_state_destroy(& (C->OUT));
	    fclose(F);
	}
    }
}



void free_mail_send_state(S)
     struct mail_send_state **S;
{
    struct mail_send_state *X = *S;
    
    if (X) {
	int i;

	if (X->magic != MAILER_magic)
	    panic("MAILER PANIC",__FILE__,__LINE__,"free_mail_send_state",
		  "Bad magic number (mail send state)",0);

	if (!valid_mailer(X->mailer_type))
	    panic("MAILER PANIC",__FILE__,__LINE__,"free_mail_send_state",
		  "Bad mailer type",
		  0);

	X->mailer_type->m_close_hook(X->mailer_type,X);

	for (i = 0; i < X->addr_count; i++) {
	    if (X->addrs[i]) {
		free(X->addrs[i]);
		X->addrs[i] = NULL;
	    }
	}
	if (X->addrs) {
	    free(X->addrs);
	    X->addrs = NULL;
	    X->addr_count = 0;
	}

	if (X->fname) {
	    unlink(X->fname);
	    free(X->fname);
	    X->fname = NULL;
	}

	if (X->mail_from) {
	    free(X->mail_from);
	    X->mail_from = NULL;
	}

	/* destroy if assigned */
	if (X->OUT.magic) {	   
	    DPRINT(Debug,1,(&Debug,
			    "free_mail_send_state: out state is probably leaking\n"));

	    out_state_destroy(& (X->OUT));
	}

	if (X->head) {
	    struct mail_send_state * prev = NULL, *walk;

	    for (walk = X->head->first; walk; walk = walk -> next) {

		if (walk == X)
		    break;

		prev = walk;
	    }
	    if (walk != X)
		panic("MAILER PANIC",__FILE__,__LINE__,"free_mail_send_state",
		      "State not in list of mailer_info",0);
	    if (!prev)
		X->head->first = X->next; 
	    else
		prev->next        = X->next; 

	    if (X->head->delete_pending && can_delete_mailer_info(& (X->head))) {
		DPRINT(Debug,7,(&Debug,
				"delete_pending -> deleting mail info"));
	    }
	    X->head = NULL;
	    X->next = NULL;
	}

	/* bzero is defined on hdrs/defs.h */
	bzero((void *)X, sizeof (*X));
	free(X);
	X = NULL;
    }

    *S = X;
}

S_(end_handler no_end_handler)
static void no_end_handler(fd,title,rs,ret,exit_stat) 
     union any_fd fd; 
     char * title; 
     struct run_state *rs; 
     int ret; 
     int exit_stat;
{
    /* Nothing */
}


/* Return NULL if no editor (and no value either) */
struct mailer_env_from * mailer_get_env_from(I)
     struct mailer_info  *I;
{
    struct mailer_env_from * ret = NULL;
    int flags                    = 0;

    if (I->magic != MAILER_INFO_magic)
	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_env_from",
	      "Bad magic number (mailer info)",0);

    if (!valid_mailer(I->mailer_type))
    	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_env_from",
	      "Bad mailer type", 0);


    if (0 != (I->mailer_type->mailer_bits & MB_ALLOW_SET_SENDER))
	flags |= MAILER_ef_can_set;

    if (0 != (I->mailer_type->mailer_bits & MB_REQ_DEFAULT_SENDER))
	flags |= MAILER_ef_need_default;

    if (0 == flags)
	return NULL;

    ret = safe_malloc (sizeof (*ret));

    /* bzero is defined on hdrs/defs.h */
    bzero((void *)ret, sizeof (*ret));

    ret->magic      = MAILER_ef_magic;
    ret->mail_from  = NULL;
    ret->flags      = flags;
   
    ret->head       = I;
    ret->next       = I->first_e;
    I->first_e      = ret;

    if (MAILER_ef_need_default | ret->flags)
	I->mailer_type->mi_def_env_from(I->mailer_type,
					I,ret);



    return ret;
}

void mailer_free_env_from(X)
     struct mailer_env_from **X;
{
    struct mailer_env_from * E = *X;
    
    if (E) {
	if (MAILER_ef_magic != E->magic) 
	    panic("MAILER PANIC",__FILE__,__LINE__,"mailer_free_env_from",
		  "Bad magic number (env from)",0);

	if (E->mail_from) {
	    free(E->mail_from);
	    E->mail_from = NULL;
	}


	if (E->head) {
	    struct mailer_env_from * prev = NULL, *walk;

	    for (walk = E->head->first_e; walk; walk = walk -> next) {

		if (walk == E)
		    break;

		prev = walk;
	    }
	    if (walk != E)
		panic("MAILER PANIC",__FILE__,__LINE__,"mailer_free_env_from",
		      "Env from not in list of mailer_info",0);
	    if (!prev)
		E->head->first_e = E->next; 
	    else
		prev->next       = E->next; 

	    if (E->head->delete_pending && can_delete_mailer_info(& (E->head))) {
		DPRINT(Debug,7,(&Debug,
				"delete_pending -> deleting mail info"));
	    }
	    E->head = NULL;
	    E->next = NULL;
	}

	E->magic = 0;   /* Set bad value */

	free(E);
	E = NULL;
    }
    *X = E;
}

/* return temporary pointer  value -- do not free 
   return NULL if currently no value set (use implicit default)
  */
CONST char * mailer_env_from_value(X,can_edit)
     struct mailer_env_from *X;
     int *can_edit;
{
    if (MAILER_ef_magic != X->magic) 
	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_free_env_from",
	      "Bad magic number (env from)",0);

    if (can_edit)
	*can_edit = 0 != (X->flags & MAILER_ef_can_set);

    return X->mail_from;
}

int env_from_changed(X)
     struct mailer_env_from *X;
{
    if (MAILER_ef_magic != X->magic) 
	panic("MAILER PANIC",__FILE__,__LINE__,"env_from_changed",
	      "Bad magic number (env from)",0);

    return 0 != (X->flags & MAILER_ef_changed);
}

void mailer_env_from_change(X,value)
     struct mailer_env_from *X;
     CONST char * value;
{
    if (MAILER_ef_magic != X->magic) 
	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_env_from_change",
	      "Bad magic number (env from)",0);

    if (MAILER_INFO_magic != X->head->magic)
	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_env_from_change",
	      "Bad magic number (mailer info)",0);

    if (!valid_mailer(X->head->mailer_type))
	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_env_from_change",
	      "Bad mailer type",0);

    if (0 == (X->flags & MAILER_ef_can_set)) {
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmChangingEnvSenderNotAllowed,
			  "Changing of envelope sender for mailer %s not allowed: %s"),
		  X->head->mailer_type->mailer_type, value);
	return;
    }
    
    if (0 != (MB_USE_DOMAIN & X->head->mailer_type->mailer_bits) &&
	0 != strcmp(value,"<>") &&
	NULL == strchr(value,'@')) {
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmEnvSenderDomainRequired,
			  "Mailer %s requires domain part on envelope sender: %s"),
		  X->head->mailer_type->mailer_type, value);
	return;

    }

    X->head->mailer_type->mi_set_env_from(X->head->mailer_type,
					  X->head,X,value);
    X->flags |= MAILER_ef_changed;
}

struct mail_send_state * mailer_init(addr_args,dsn,verbose,info,env_from)
     char **addr_args;
     int dsn;
     int verbose;
     struct mailer_info *info;
     struct mailer_env_from *env_from; 
{
    struct mail_send_state *ret;
    int i;
    int count;

    ret = safe_malloc (sizeof (*ret));

    /* bzero is defined on hdrs/defs.h */
    bzero((void *)ret, sizeof (*ret));

    ret->magic         = MAILER_magic;

    if (info) {

	if (info->magic != MAILER_INFO_magic)
	    panic("MAILER PANIC",__FILE__,__LINE__,"mailer_init",
		  "Bad magic number (mailer info)",0);

	ret->mailer_type   = info->mailer_type;

	ret->head          = info;
	ret->next          = info->first;
	info->first        = ret;

    } else {
	ret->mailer_type   = selected_mailer;
	ret->head          = NULL;
	ret->next          = NULL;
    }
    for (count = 0; addr_args[count]; count++);
        
    ret->addrs   = safe_malloc(sizeof (* (ret->addrs)) * (count+1));

    for (i = 0; i < count; i++) 
	ret->addrs[i] = safe_strdup(addr_args[i]);
    ret->addrs[count] = NULL;
    ret->addr_count   = count;
    
    ret->dsn          = dsn;
    ret->verbose      = verbose;
    ret->fname        = NULL;
    ret->mail_from    = NULL;

    if (env_from) {
	if (MAILER_ef_magic != env_from->magic) 
	    panic("MAILER PANIC",__FILE__,__LINE__,"mailer_init",
		  "Bad magic number (env from)",0);

	if (env_from->mail_from)
	    ret->mail_from = safe_strdup(env_from->mail_from);
    }

    ret->OUT.magic    = 0;   /* Nothing set */
    ret->orig_func    = no_end_handler;

    DPRINT(Debug,5,(&Debug,
		    "Initialising sending via %s mailer, %d recipients\n",
		    ret->mailer_type->mailer_type,
		    ret->addr_count));

    if (!valid_mailer(ret->mailer_type))
    	panic("MAILER PANIC",__FILE__,__LINE__,"mailer_init",
	      "Bad mailer type",
	      0);

    if (!ret->mailer_type->m_init_hook(ret->mailer_type,
				       ret,info)) {
	free_mail_send_state(&ret);
    }

    return ret;
}

CONST char *get_mailer_path(X)
     struct mail_send_state *X;
{
    if (X->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"get_mailer_path",
	      "Bad magic number (mail send state)",0);

    if (X->mailer_type->mailer_path &&
	*(X->mailer_type->mailer_path) &&
	(*(X->mailer_type->mailer_path))[0])
	return *(X->mailer_type->mailer_path);

    return X->mailer_type->mailer_type;
}


out_state_t *get_mail_outfd(X)
     struct mail_send_state  *X;
{
    if (X->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"get_mail_outfd",
	      "Bad magic number (mail send state)",0);

    return &(X->OUT);
}

S_(end_handler call_end_handler)
static void call_end_handler(fd,title,rs,ret,exit_stat) 
     union any_fd fd; 
     char * title; 
     struct run_state *rs; 
     int ret; 
     int exit_stat;
{
    if (fd.mail_fd->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"call_end_handler",
	      "Bad magic number (mail send state)",0);

    DPRINT(Debug,7,(&Debug,
		    "Finished sending mail via %s mailer, ret=%d exit_stat=%d\n",
		    fd.mail_fd->mailer_type->mailer_type,
		    ret,exit_stat));

    fd.mail_fd->orig_func(fd,title,rs,ret,exit_stat);
    free_mail_send_state(&(fd.mail_fd));
}

static int backend_tail P_((struct mailer_config *M,
			    struct mail_send_state **C,
			    char * title,
			    sending_message_func *sm,
			    const char **argv));

static int backend_tail(M,C,title,sm,argv)
     struct mailer_config *M;
     struct mail_send_state **C;
     char * title;
     sending_message_func *sm;
     CONST char **argv;
{
    struct run_state RS;

    int options = SY_ENV_SHELL;
    int ret;
    union any_fd FD;

    if ((*C)->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"backend_tail",
	      "Bad magic number (mail send state)",0);

    FD.mail_fd         = *C;

    if (!(*C)->verbose)
	options |= SY_NOTTY;
    
    out_state_fseek (&((*C)->OUT), 0);
#ifdef _POSIX_VERSION
    /* Synzronize underlying file descriptor */
    fflush(out_state_FILE(&((*C)->OUT)));;  
#else
    seek(fileno(out_state_FILE(&((*C)->OUT))),0,0);
#endif

    ret=start_run(&RS, options, argv, fileno(out_state_FILE(&((*C)->OUT))),-1);
    
    if (ret) {
	int backgrounded = 0;
	int exit_code;

	ret = run_already_done(&RS,&exit_code);
	if (0 == ret) {
	    sm(0);
	    	    	      	    
#ifdef BACKGROUD_PROCESSES       /* We assume POSIX in here */
	    if (background_wait_time) {
 		int tmp;
 		DPRINT(Debug,4, (&Debug, 
				 "Sleeping ( %d seconds ) for completion!\n",
				 background_wait_time));
#if POLL_METHOD	  
		tmp = wait_for_timeout(background_wait_time);
		if (!tmp) {
		    DPRINT(Debug,4,(&Debug,  
				    " -- sleeping interrupted\n"));
		}
#else
 		tmp = sleep(background_wait_time);  
 		/* POSIX sleep returns time in left on
 		 * interrupt -- when sendmail terminates
		 * we will get interrupt (SIGCHLD signal)
		 */
		if (tmp > 0) {
		    DPRINT(Debug,4,(&Debug,  
				    " -- sleeping interrupted, %d seconds left!\n",
				    tmp));
		} else if (tmp < 0) {
		    DPRINT(Debug,4,(&Debug,  
				    " -- sleeping failed?\n"));
		}
#endif
		ret = run_already_done(&RS,&exit_code);
		if (0 == ret) 
		    ret = maybe_background(&RS,&exit_code,
					   FD,title,call_end_handler);
		if (0 == ret) {
		    sm(1);		    		    
		    backgrounded = 1;
		    *C = NULL;   /* call_end_handler may free 
				    mail_send_state 
				 */
		}
	    } else
#endif
		ret = wait_end(&RS,&exit_code);
	}
	if (!backgrounded) {
	    call_end_handler(FD,title,&RS,ret,exit_code);
	    *C = NULL;   /* call_end_handler free'ed mail_send_state 
			  */
	}
    } else {
	if (RS.save_errno)
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFailErrno,
			      "Failed: %.30s: %.40s"),
		      argv[0],error_description(RS.save_errno));
	else
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantStart,
			      "Can't start %.30s"),
		      argv[0]);
	free_mail_send_state(C);
    }

    return ret;
}


static int unknown_mailer_backend(M,C,encoding_top,title,sm)
     struct mailer_config *M;
     struct mail_send_state **C;
     int encoding_top;
     char * title;
     sending_message_func *sm;
{
    char *mailerflags[20];
    CONST char **argv;
    int mf_idx=0;
    int r;

    if ((*C)->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"unknown_mailer_backend",
	      "Bad magic number (mail send state)",0);


    mailerflags[mf_idx++] = unknown_mailer_path;
    mailerflags[mf_idx] = NULL;

    argv = join_argv(mailerflags,(*C)->addrs);

    r = backend_tail(M,C,title,sm,argv);
    
    free(argv);

    return r;
}

static int sendmail_mailer_backend(M,C,encoding_top,title,sm)
     struct mailer_config *M;
     struct mail_send_state **C;
     int encoding_top;
     char * title;
     sending_message_func *sm;
{
    char t[80];
    int r;

    char *mailerflags[22];
    CONST char **argv;
    int mf_idx=0;

    if ((*C)->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"sendmail_mailer_backend",
	      "Bad magic number (mail send state)",0);

    mailerflags[mf_idx++] = sendmail_mailer_path;

    mailerflags[mf_idx++] = "-oi";
    mailerflags[mf_idx++] = "-oem";

    if ((*C)->mail_from && 0 != ( M -> mailer_bits & MB_ALLOW_SET_SENDER)) {
	mailerflags[mf_idx++] = "-f";
	mailerflags[mf_idx++] = (*C)->mail_from;
    }

    if ((*C)->verbose)
	mailerflags[mf_idx++] = "-v";
    if (metoo) 
	mailerflags[mf_idx++] = "-om";

    if (sendmail_supported_bodytype >= have_8bit) {
	if (encoding_top == ENCODING_8BIT)
	    mailerflags[mf_idx++] = "-B8BITMIME";
    }

    if (sendmail_supported_bodytype >= have_binary) {
	if (encoding_top == ENCODING_BINARY)
	    /* With -BBINARYMIME lines must terminate with \r\n
	     * Unix's \n is _NOT_ sufficient - K E H              */
	    mailerflags[mf_idx++] = "-BBINARYMIME";
    }
    
    if (sendmail_supports_dsn) {
	if ((*C)->dsn & DSN_FULL) {
	    mailerflags[mf_idx++] = "-R";
	    mailerflags[mf_idx++] = "full";
	} else if ((*C)->dsn & DSN_HDRS) {
	    mailerflags[mf_idx++] = "-R";
	    mailerflags[mf_idx++] = "hdrs";
	}
	
	if ((*C)->dsn & DSN_NEVER) {
	    mailerflags[mf_idx++] = "-N";
	    mailerflags[mf_idx++] = "never";
	} else if ((*C)->dsn & (DSN_SUCCESS|DSN_FAILURE|DSN_DELAY)) {	    
	    t[0] = '\0';
	    if ((*C)->dsn & DSN_SUCCESS)
		strfcat(t,"success", sizeof t);
	    if ((*C)->dsn & DSN_FAILURE) {
		if (t[0]) strfcat(t,",", sizeof t);
		strfcat(t,"failure", sizeof t);
	    }
	    if ((*C)->dsn & DSN_DELAY) {
		if (t[0]) strfcat(t,",", sizeof t);
		strfcat(t,"delay", sizeof t);
	    }
	    mailerflags[mf_idx++] = "-N";
	    mailerflags[mf_idx++] = t;
	}
    }

    mailerflags[mf_idx++] = "--";

    mailerflags[mf_idx] = NULL;

    argv = join_argv(mailerflags,(*C)->addrs);
    
    r = backend_tail(M,C,title,sm,argv);
    
    free(argv);

    return r;
}

/* Return 
   -1 if caller should result with default data (return 1)
   -2 if caller should just test passwd
   0  if failure
   1  is succees
*/
static int sendmail_mailer_info_verify_addr(M,I,text,result)
     struct mailer_config *M;
     struct mailer_info  *I;
     CONST char *text;
     struct addr_item *result;
{

    CONST char *mailerflags[5];
    int mf_idx=0;
    struct run_state RS;
    int ret,exit_code;
 
    if (I->magic != MAILER_INFO_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,
	      "sendmail_mailer_info_verify_addr",
	      "Bad magic number (mailer info)",0);

    if (!sendmail_verify_address) {
	DPRINT(Debug,4,(&Debug,
			"Don't call %s for verify -- falling back to default addr verify\n",
			sendmail_mailer_path));
	return -2;   /* caller does job */
    }

    mailerflags[mf_idx++] = sendmail_mailer_path;

    mailerflags[mf_idx++] = "-bv";

    /* Do not treat -xyc as option */
    mailerflags[mf_idx++] = "--";

    mailerflags[mf_idx++] = text;

    mailerflags[mf_idx] = NULL;

    ret = start_run(&RS,SY_NOTTY|SY_ENV_SHELL,mailerflags,-1,-1);

    if (!ret) {
	DPRINT(Debug,4,(&Debug,
			"Can't run %s for address verify -- falling back to default addr verify\n",
			sendmail_mailer_path));
	return -2;   /* caller does job */
    }
    ret = wait_end(&RS,&exit_code);
    if (ret < 0) {
	DPRINT(Debug,4,(&Debug,
			"%s died on signal %d -- falling back to default addr verify\n",
			sendmail_mailer_path,-ret));
	return -2;   /* caller does job */	    
    }
    if (0 == ret) {
	DPRINT(Debug,4,(&Debug,
			"%s lost? -- falling back to default addr verify\n",
			sendmail_mailer_path));
	return -2;   /* caller does job */	    
    }

    /* -1 is success, but caller should fill fullname */
    return exit_code == 0 ? -1 : 0;
}

static  void sendmail_mailer_info_set_ef(M,I,X,value)
     struct mailer_config *M;
     struct mailer_info *I;
     struct mailer_env_from *X;
     CONST char *value;
{
    CONST char *mailerflags[5];
    int mf_idx=0;
    struct run_state RS;
    int ret,exit_code;

    if (0 == strcmp(value,"<>")) {
	/* Null envelope sender is only valid as sender (not as recipient)
	   so do not verify it
	*/
	X->mail_from = strmcpy(X->mail_from,value);
	return;
    }

    if (I->magic != MAILER_INFO_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,
	      "sendmail_mailer_info_set_ef",
	      "Bad magic number (mailer info)",0);

    mailerflags[mf_idx++] = sendmail_mailer_path;

    mailerflags[mf_idx++] = "-bv";

    /* Do not treat -xyc as option */
    mailerflags[mf_idx++] = "--";

    mailerflags[mf_idx++] = value;

    mailerflags[mf_idx] = NULL;

    ret = start_run(&RS,SY_NOTTY|SY_ENV_SHELL,mailerflags,-1,-1);

    if (!ret) {
	DPRINT(Debug,4,(&Debug,
			"Can't run %s for address verify -- sendmail_mailer_info_set_ef failed\n",
			sendmail_mailer_path));
	goto failure;
    }

    ret = wait_end(&RS,&exit_code);
    if (ret < 0) {
	DPRINT(Debug,4,(&Debug,
			"%s died on signal %d -- sendmail_mailer_info_set_ef failed\n",
			sendmail_mailer_path,-ret));
	goto failure;
    }

    if (0 == ret) {
	DPRINT(Debug,4,(&Debug,
			"%s lost? -- sendmail_mailer_info_set_ef failed\n",
			sendmail_mailer_path));
	goto failure;
    }


    if (exit_code == 0) {       
	X->mail_from = strmcpy(X->mail_from,value);
	DPRINT(Debug,8,(&Debug,
			" .. envelope sender=%s\n",
			X->mail_from));

    } else {
	DPRINT(Debug,4,(&Debug,
			"... exit code %d -- envelope sender address %s bad?\n",
			exit_code,value));

    failure:
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmChangingSendmEnvSenderFailed,
			  "Changing of envelope sender for sendmail failed: %s"),
		  value);
    }
}

static int submitmail_mailer_init(M,C,I)
     struct mailer_config *M;
     struct mail_send_state *C;
     struct mailer_info *I;
{
    int i;

    if (C->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"submitmail_mailer_init",
	      "Bad magic number (mail send state)",0);

    if (!default_mailer_init(M,C,I))
	return 0;

    /* WARNING: Untested  --
       code copied from do_mmdf_addresses() on src/reply.c 
    */
    for (i = 0; i < C -> addr_count; i++) {
	state_puts(C -> addrs[i],&(C->OUT));  state_putc('\n',&(C->OUT));
    }
    state_putc('\n',&(C->OUT));

    return 1;  /* OK */
}


static int submitmail_mailer_backend(M,C,encoding_top,title,sm)
     struct mailer_config *M;
     struct mail_send_state **C;
     int encoding_top;
     char * title;
     sending_message_func *sm;
{
    int r;
    CONST char *mailerflags[20];
    int mf_idx=0;

    if ((*C)->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"submitmail_mailer_backend",
	      "Bad magic number (mail send state)",0);

    /* WARNING: Untested  --
       code copied from mail_backend() on src/mailmsg2.c 
    */

    mailerflags[mf_idx++] = submitmail_mailer_path;

    mailerflags[mf_idx++] = "-mlrnv";

    mailerflags[mf_idx] = NULL;

    r = backend_tail(M,C,title,sm,mailerflags);
    
    return r;
}

static int execmail_mailer_backend(M,C,encoding_top,title,sm)
     struct mailer_config *M;
     struct mail_send_state **C;
     int encoding_top;
     char * title;
     sending_message_func *sm;
{
    int r;
    char *mailerflags[20];
    CONST char **argv;
    int mf_idx=0;

    if ((*C)->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"execmail_mailer_backend",
	      "Bad magic number (mail send state)",0);

    /* WARNING: Untested  --
       code copied from mail_backend() on src/mailmsg2.c 
    */

    mailerflags[mf_idx++] = execmail_mailer_path;

    if ((*C)->verbose)
	mailerflags[mf_idx++] = "-d";
    if (metoo)
	mailerflags[mf_idx++] = "-m";

    argv = join_argv(mailerflags,(*C)->addrs);
    
    r = backend_tail(M,C,title,sm,argv);
    
    free(argv);

    return r;
}


int mail_backend2(mail_fd,func,encoding_top,title,
		  sm)
     struct mail_send_state **mail_fd;
     end_handler *func;
     int encoding_top;
     char * title;
     sending_message_func *sm;
{    
    int r;

    if ((*mail_fd)->magic != MAILER_magic)
    	panic("MAILER PANIC",__FILE__,__LINE__,"mail_backend2",
	      "Bad magic number (mail send state)",0);

    DPRINT(Debug,7,(&Debug,
		    "Sending mail via %s mailer, %d recipients\n",
		    (*mail_fd)->mailer_type->mailer_type,
		    (*mail_fd)->addr_count));
    
    (*mail_fd)->orig_func = func;
    
    if (!valid_mailer((*mail_fd)->mailer_type))
	panic("MAILER PANIC",__FILE__,__LINE__,"mail_backend2",
	      "Bad mailer type",
	      0);

    r = (*mail_fd)->mailer_type->m_backend_hook((*mail_fd)->mailer_type,
						mail_fd,encoding_top,
						title,sm);        
    return r;
}

# ifdef PWDINSYS
#  include <sys/pwd.h>
# else
#  include <pwd.h>
# endif

/* Should return 1 if verify succees and fields of result to be filled 
   -- caller should free fields of result
   Returns 0 on failure
*/
int verify_mailer_addr(I,text,result, errcode)
     struct mailer_info  *I;
     CONST char *text;
     struct addr_item *result;
     enum mailer_errcode  * errcode;
{
    int ret = 0;
    
    if (I->magic != MAILER_INFO_magic)
	panic("MAILER PANIC",__FILE__,__LINE__,"verify_mailer_addr",
	      "Bad magic number (mailer info)",0);

    /* bzero is defined on hdrs/defs.h */
    bzero((void *)result, sizeof (*result));
    result -> addr      = NULL;
    result -> fullname  = NULL;
    result -> comment   = NULL;
    
    *errcode = MAILER_NOT_AVAIL;

    if (!valid_mailer(I->mailer_type))
	panic("MAILER PANIC",__FILE__,__LINE__,"verify_mailer_addr",
	      "Bad mailer type",
	      0);

    /* Returns 
       -1 if caller should result with default data (return 1)
       -2 if caller should just test passwd
       0  if failure
       1  is succees
    */
    ret = I->mailer_type->mi_verify_addr(I->mailer_type,I,text,result);

    DPRINT(Debug,7,(&Debug,"Address %s verify result %d%s\n",
		    text,ret,
		    ret < 0 ? " (use default processing)" : ""));
    if (result->addr) {
	DPRINT(Debug,7,(&Debug,
			"   ... resulting address %s\n",
			result->addr));	
    }

    if (0 == ret)
	*errcode = MAILER_NOT_EXIST;
    if (ret > 0)
	*errcode = MAILER_OK;

    if (ret < 0) {
	struct passwd * P = getpwnam(text);

	if (P)
	    *errcode = MAILER_OK;

	if (P && ! result -> fullname ) {
	    char * N = get_fullname1(P,text);

	    /* FIXME: Posible wrong charset */
	    if (N) {
		result -> fullname = new_string2(system_charset,s2us(N));
	    }

	    ret = 1;
	} else {
	    ret = ret == -1 ? 1 : 0;
	}
    }

    if (ret && !result->addr) {

	DPRINT(Debug,7,(&Debug,
			"   ... filling resulting address\n",
			text));
	

	result->addr = safe_strdup(text);
	

	if (I->mailer_type->mi_query_hook(I->mailer_type,I,
					  MI_USE_DOMAIN)) {
	    result->addr = strmcat(result->addr,"@");
	    result->addr = strmcat(result->addr,hostfullname);
	    DPRINT(Debug,7,(&Debug,
			    "   ... %s => %s\n",
			    text,result->addr));
	}
    }

    return ret;
}

/* Should return 1 if verify succees and fields of result to be filled 
   -- caller should free fields of result
   Returns 0 on failure
*/
int verify_mailer_domaddr(I,text,result, errcode)
     struct mailer_info  *I;
     CONST char *text;
     struct addr_item *result;
     enum mailer_errcode  * errcode;
{
    int ret = 0;
    
    if (I->magic != MAILER_INFO_magic)
	panic("MAILER PANIC",__FILE__,__LINE__,"verify_mailer_domaddr",
	      "Bad magic number (mailer info)",0);

    /* bzero is defined on hdrs/defs.h */
    bzero((void *)result, sizeof (*result));
    result -> addr      = NULL;
    result -> fullname  = NULL;
    result -> comment   = NULL;
    
    *errcode = MAILER_NOT_AVAIL;

    if (!valid_mailer(I->mailer_type))
	panic("MAILER PANIC",__FILE__,__LINE__,"verify_mailer_domaddr",
	      "Bad mailer type",
	      0);

    /* Returns 
       -1 if caller should result with default data (return 1)
       -2 if caller should just test passwd
       0  if failure
       1  is succees
    */
    ret = I->mailer_type->mi_verify_addr(I->mailer_type,I,text,result);

    DPRINT(Debug,7,(&Debug,"Address %s verify result %d%s\n",
		    text,ret,
		    ret < 0 ? " (use default processing)" : ""));
    if (result->addr) {
	DPRINT(Debug,7,(&Debug,
			"   ... resulting address %s\n",
			result->addr));	
    }

    if (0 == ret)
	*errcode = MAILER_NOT_EXIST;
    if (ret > 0)
	*errcode = MAILER_OK;


    if (-1 == ret)
	ret = 1;
    if (-2 == ret)
	ret = 0;

    if (ret && !result->addr) {

	DPRINT(Debug,7,(&Debug,
			"   ... filling resulting address\n",
			text));
	

	result->addr = safe_strdup(text);
    }

    return ret;
}


char * kludge_addr(char **addr) {
    char * ptr = *addr;
    int l = strlen(ptr);
    char *res;
    
    ptr = safe_realloc(ptr,l + 1 + 2 + l + 1);

    res = ptr + l + 1;

    res[0] = '<';
    strfcpy(res+1,ptr,l+1);
    res[l+1] = '>';
    res[l+2] = '\0';

    *addr = ptr;
    return res;
}

char **argv_from_headers (headers)
     struct mailing_headers * headers;
{
    int count = 
	headers->to.addrs_len + 
	headers->cc.addrs_len + headers->bcc.addrs_len;
    char **res;
    int idx = 0,i;
    struct addr_item *p;
        
    DPRINT(Debug,6,
	   (&Debug,  "argv_from_headers, count=%d\n",count));
    
    res = safe_malloc((count + 1) * sizeof (char *));
    
    dump_expanded_address(6,"argv_from_headers -- enter (to)",headers->to);

    for (p = headers->to.addrs; 
	 p < headers->to.addrs + headers->to.addrs_len;
	 p++) {
	if (p->addr[0] == '-' || p->addr[0] == '@' ||
	    p->addr[0] == '\0')
	    /* Modify p->addr so that there is space
	     * alloced also for <addr> form 
	     */
	    res[idx++] = kludge_addr(&p->addr);
	else
	    res[idx++] = p->addr;
    }

    dump_expanded_address(6,"argv_from_headers -- enter (cc)",headers->cc);

    for (p = headers->cc.addrs; 
	 p < headers->cc.addrs + headers->cc.addrs_len;
	 p++) {
	if (p->addr[0] == '-' || p->addr[0] == '@' ||
	    p->addr[0] == '\0')
	    /* Modify p->addrs so that there is space
	     * alloced also for <addr> form 
	     */
	    res[idx++] = kludge_addr(&p->addr);
	else
	    res[idx++] = p->addr;
    }

    dump_expanded_address(6,"argv_from_headers -- enter (bcc)",headers->bcc);

    for (p = headers->bcc.addrs; 
	 p < headers->bcc.addrs + headers->bcc.addrs_len;
	 p++) {
	if (p->addr[0] == '-' || p->addr[0] == '@' ||
	    p->addr[0] == '\0')
	    /* Modify p->addrs so that there is space
	     * alloced also for <addr> form 
	     */
	    res[idx++] = kludge_addr(&p->addr);
	else
	    res[idx++] = p->addr;
    }
    
    res[idx] = NULL;
    
    DPRINT(Debug,6,
	   (&Debug, "           idx=%d\n",idx));
    for (i = 0; i < idx; i++) 
	DPRINT(Debug,6,
	       (&Debug, "           [%d]=%s\n",i,res[i]));

    dump_expanded_address(7,"argv_from_headers -- leave (to)",headers->to);
    dump_expanded_address(7,"argv_from_headers -- leave (cc)",headers->cc);
    dump_expanded_address(7,"argv_from_headers -- leave (bcc)",headers->bcc);

    return res;
}

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


syntax highlighted by Code2HTML, v. 0.9.1