static char rcsid[] = "@(#)$Id: hdrconfg.c,v 1.55 2006/05/22 19:17:14 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.55 $   $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
 *****************************************************************************/

/**   This file contains the routines necessary to be able to modify
      the mail headers of messages on the way off the machine.  The
      headers currently supported for modification are:

	Subject:
	To:
	Cc:
	Bcc:
	Reply-To:
	Expires:
	Priority:
	Precedence:
	In-Reply-To:
	Action:

	<user defined>
**/

#include "def_elm.h"
#include "s_elm.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;
}
static char *us2s P_((unsigned char *str));
static char *us2s(str) 
     unsigned char *str;
{
    return (char *)str;
}


/*
 * Option flags for the fields in a (struct hdr_menu_item).
 */
#define HF_DISP_1ROW	0001	/* field is displayed on one line	*/
#define HF_DISP_2ROW	0002	/* field display spans two lines	*/
#define HF_DISP_3ROW	0003	/* field display spans three lines	*/
#define HF_DISP_LEFT	0004	/* field occupies left half of a line	*/
#define HF_DISP_RIGHT	0005	/* field occupies right half of a line	*/
#define HF_DISP_MASK	0007	/* -- mask to pull out display option	*/
#define HF_PROMPT_EXP	0010	/* prompt for expires data entry	*/
#define HF_PROMPT_USR	0020	/* prompt for user defined hdr entry	*/
#define HF_PROMPT_MASK	0070	/* -- mask to pull out prompt option	*/
#define HF_APPENDENTRY	0100	/* append user entry to existing value	*/
#define HF_ADD_COMMA    0200    /* add comma if already data            */
#define HF_ONPRE_SENDMENU 0400

struct hdr_menu_item;
typedef void inpval_proc_t P_((struct hdr_menu_item *h,
			       struct mailing_headers *headers,
			       struct string **ptr,
			       charset_t hdr_charset
			       ));

typedef struct string * expval_proc_t P_((struct hdr_menu_item *h,
					  struct mailing_headers *headers,
					  charset_t hdr_charset
					  ));
typedef int hdrproc_t  P_((struct hdr_menu_item *h,
			   struct mailing_headers *headers,
			   struct string **ptr, int free_only,
			   struct mailer_info  *mailer_info,
			   charset_t hdr_charset,
			   struct AliasView *aview,
			   struct menu_context  *page,
			   struct menu_context  *prompt_area
			   ));

/*
 * Structure to describe a header which can be edited in this menu.
 */
struct hdr_menu_item {
    int menucmd;       /* The single keystroke (lower-case letter) the	*
			*   user strikes to edit this menu item.	*/

    char *hdrname;	/* Header name to display in the menu.  Parens	*
			 *   should be used to bracket the "menucmd"	*
			 *   char in the name, e.g. "S)ubject".  This	*
			 *   will be NULL for the user-defined header.	*/

    int lineno;		/* Screen line at which the field is displayed.	*/

    int flags;		/* Various flags which effect the display and	*
			 *   user entry of this item.			*/

    inpval_proc_t *inpval_proc;  
                        /* Returns pointer to the buffer to hold the    *
                         *  value entered by the user. Needs to be      *
                         * dealloced with hdrproc			*/

    expval_proc_t *expval_proc;  
                        /* Returns to tointer to the expanded header    *
			 * value to display.  Returned valued need to   *
			 * be free()ed.                                 */

    hdrproc_t *hdrproc;	/* Pointer to a procedure which verifies the	*
			 *   user data entry, and if required converts	*
			 *   the "inpval" value to "expval" value. Does *
                         * needed deallocation of buffer returned by    *
                         * inpval_proc                                  */
};

/*
 * Local procedures.
 */
static int hdrmenu_get P_((struct hdr_menu_item *h,
			   struct mailing_headers *headers,
			   struct mailer_info  *mailer_info,
			   charset_t hdr_charset,
			   struct AliasView *aview,     
			   struct menu_context  *page,
			   struct menu_context  *prompt_area));
static void hdrmenu_put P_((struct hdr_menu_item *h,
			    int already_clear,
			    struct mailing_headers *headers,
			    charset_t hdr_charset,
			    struct menu_context  *page));


/* From: -header -------------------------------------------------------- */

static void inpval_from P_((struct hdr_menu_item *h,
			    struct mailing_headers *headers,
			    struct string **ptr,
			    charset_t hdr_charset));
static struct string * expval_from P_((struct hdr_menu_item *h,
				       struct mailing_headers *headers,
				       charset_t hdr_charset));
static int hdrproc_from  P_((struct hdr_menu_item *h,
			     struct mailing_headers *headers,
			     struct string **ptr, int free_only,
			     struct mailer_info  *mailer_info,
			     charset_t hdr_charset,
			     struct AliasView *aview,
			     struct menu_context  *page,
			     struct menu_context  *prompt_area
			     ));


/* To: -header -------------------------------------------------------- */

static void inpval_to P_((struct hdr_menu_item *h,
			  struct mailing_headers *headers,
			  struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_to P_((struct hdr_menu_item *h,
				     struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_to  P_((struct hdr_menu_item *h,
			   struct mailing_headers *headers,
			   struct string **ptr, int free_only,
			   struct mailer_info  *mailer_info,
			   charset_t hdr_charset,
			   struct AliasView *aview,
			   struct menu_context  *page,
			   struct menu_context  *prompt_area));

/* CC: -header -------------------------------------------------------- */

static void inpval_cc P_((struct hdr_menu_item *h,
			  struct mailing_headers *headers,
			  struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_cc P_((struct hdr_menu_item *h,
				     struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_cc  P_((struct hdr_menu_item *h,
			   struct mailing_headers *headers,
			   struct string **ptr, int free_only,
			   struct mailer_info  *mailer_info,
			   charset_t hdr_charset,
			   struct AliasView *aview,
			   struct menu_context  *page,
			   struct menu_context  *prompt_area));

/* BCC: -header -------------------------------------------------------- */

static void inpval_bcc P_((struct hdr_menu_item *h,
			   struct mailing_headers *headers,
			   struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_bcc P_((struct hdr_menu_item *h,
				      struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_bcc  P_((struct hdr_menu_item *h,
			    struct mailing_headers *headers,
			    struct string **ptr, int free_only,
			    struct mailer_info  *mailer_info,
			    charset_t hdr_charset,
			    struct AliasView *aview,
			    struct menu_context  *page,
			    struct menu_context  *prompt_area));


/* Subject: -header -------------------------------------------------------- */

static void inpval_subject P_((struct hdr_menu_item *h,
			       struct mailing_headers *headers,
			       struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_subject P_((struct hdr_menu_item *h,
					  struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_subject  P_((struct hdr_menu_item *h,
				struct mailing_headers *headers,
				struct string **ptr, int free_only,
				struct mailer_info  *mailer_info,
				charset_t hdr_charset,
				struct AliasView *aview,
				struct menu_context  *page,
				struct menu_context  *prompt_area));


/* Reply-To: -header -------------------------------------------------- */

static void inpval_reply_to P_((struct hdr_menu_item *h,
				struct mailing_headers *headers,
				struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_reply_to P_((struct hdr_menu_item *h,
					   struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_reply_to  P_((struct hdr_menu_item *h,
				 struct mailing_headers *headers,
				 struct string **ptr, int free_only,
				 struct mailer_info  *mailer_info,
				 charset_t hdr_charset,
				 struct AliasView *aview,
				 struct menu_context  *page,
				 struct menu_context  *prompt_area));


/* Action: -header ---------------------------------------------------- */

static void inpval_action P_((struct hdr_menu_item *h,
			      struct mailing_headers *headers,
			      struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_action P_((struct hdr_menu_item *h,
					 struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_action  P_((struct hdr_menu_item *h,
			       struct mailing_headers *headers,
			       struct string **ptr, int free_only,
			       struct mailer_info  *mailer_info,
			       charset_t hdr_charset,
			       struct AliasView *aview,
			       struct menu_context  *page,
			       struct menu_context  *prompt_area));

/* Expires: -header ---------------------------------------------------- */

static void inpval_expires P_((struct hdr_menu_item *h,
			       struct mailing_headers *headers,
			       struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_expires P_((struct hdr_menu_item *h,
					  struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_expires  P_((struct hdr_menu_item *h,
				struct mailing_headers *headers,
				struct string **ptr, int free_only,
				struct mailer_info  *mailer_info,
				charset_t hdr_charset,
				struct AliasView *aview,
				struct menu_context  *page,
				struct menu_context  *prompt_area));

/* Priority: -header ---------------------------------------------------- */

static void inpval_priority P_((struct hdr_menu_item *h,
				struct mailing_headers *headers,
				struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_priority P_((struct hdr_menu_item *h,
					   struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_priority  P_((struct hdr_menu_item *h,
				 struct mailing_headers *headers,
				 struct string **ptr, int free_only,
				 struct mailer_info  *mailer_info,
				 charset_t hdr_charset,
				 struct AliasView *aview,
				 struct menu_context  *page,
				 struct menu_context  *prompt_area));

/* Precedence: -header ---------------------------------------------------- */

static void inpval_precedence P_((struct hdr_menu_item *h,
				  struct mailing_headers *headers,
				  struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_precedence P_((struct hdr_menu_item *h,
					     struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_precedence  P_((struct hdr_menu_item *h,
				   struct mailing_headers *headers,
				   struct string **ptr, int free_only,
				   struct mailer_info  *mailer_info,
				   charset_t hdr_charset,
				   struct AliasView *aview,
				   struct menu_context  *page,
				   struct menu_context  *prompt_area));

/* In-Reply-To: -header ---------------------------------------------------- */

static void inpval_in_reply_to P_((struct hdr_menu_item *h,
				   struct mailing_headers *headers,
				   struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_in_reply_to P_((struct hdr_menu_item *h,
					      struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_in_reply_to  P_((struct hdr_menu_item *h,
				    struct mailing_headers *headers,
				    struct string **ptr, int free_only,
				    struct mailer_info  *mailer_info,
				    charset_t hdr_charset,
				    struct AliasView *aview,
				    struct menu_context  *page,
				    struct menu_context  *prompt_area));

/* User defined header ---------------------------------------------------- */

static void inpval_userhdr P_((struct hdr_menu_item *h,
			       struct mailing_headers *headers,
			       struct string **ptr,
			     charset_t hdr_charset));
static struct string * expval_userhdr P_((struct hdr_menu_item *h,
					  struct mailing_headers *headers,
			     charset_t hdr_charset));
static int hdrproc_userhdr  P_((struct hdr_menu_item *h,
				struct mailing_headers *headers,
				struct string **ptr,
				int free_only,
				struct mailer_info  *mailer_info,
				charset_t hdr_charset,
				struct AliasView *aview,
				struct menu_context  *page,
				struct menu_context  *prompt_area));

/*
 * Definition of all the header editing menu fields.
 */
struct hdr_menu_item hmenu_item_list[] = {
    { 't', "T)o",		 2, HF_DISP_3ROW|HF_APPENDENTRY|HF_ADD_COMMA|HF_ONPRE_SENDMENU,
      inpval_to, expval_to, hdrproc_to },
    { 'c', "C)c",		 5, HF_DISP_3ROW|HF_APPENDENTRY|HF_ADD_COMMA,
      inpval_cc, expval_cc, hdrproc_cc },
    { 'b', "B)cc",		 8, HF_DISP_2ROW|HF_APPENDENTRY|HF_ADD_COMMA,
      inpval_bcc, expval_bcc, hdrproc_bcc },
    { 's', "S)ubject",		10, HF_DISP_2ROW, 
      inpval_subject, expval_subject, hdrproc_subject },
    { 'r', "R)eply-to",		12, HF_DISP_1ROW, 
      inpval_reply_to, expval_reply_to, hdrproc_reply_to },
    { 'a', "A)ction",		13, HF_DISP_LEFT, 
      inpval_action, expval_action, hdrproc_action },
    { 'e', "E)xpires",		13, HF_DISP_RIGHT|HF_PROMPT_EXP, 
      inpval_expires, expval_expires, hdrproc_expires },
    { 'p', "P)riority",		14, HF_DISP_LEFT, 
      inpval_priority, expval_priority, hdrproc_priority },

    { 'n', "Precede(n)ce",	14, HF_DISP_RIGHT, 
      inpval_precedence, expval_precedence, hdrproc_precedence },

    { 'i', "I)n-reply-to",	15, HF_DISP_2ROW,
      inpval_in_reply_to, expval_in_reply_to, hdrproc_in_reply_to },

    { 'u', NULL,		17, HF_DISP_1ROW|HF_PROMPT_USR,
      inpval_userhdr, expval_userhdr, hdrproc_userhdr },
    { 'f', "F)rom",		-1, 0,
      inpval_from, expval_from, hdrproc_from },
    { -1, NULL, -1, -1, NULL, NULL, NULL },
};

/*
 * Selection of individual fields.  The indices *must* correspond
 * to the above "hmenu_item_list[]" list.
 */
#define hmenu_to		(hmenu_item_list[0])
#define hmenu_cc		(hmenu_item_list[1])
#define hmenu_bcc		(hmenu_item_list[2])
#define hmenu_subject		(hmenu_item_list[3])
#define hmenu_replyto		(hmenu_item_list[4])
#define hmenu_action		(hmenu_item_list[5])
#define hmenu_expires		(hmenu_item_list[6])
#define hmenu_priority		(hmenu_item_list[7])
#define hmenu_precedence	(hmenu_item_list[8])
#define hmenu_inreplyto		(hmenu_item_list[9])
#define hmenu_userdef		(hmenu_item_list[10])
#define hmenu_from		(hmenu_item_list[11])

void show_presend_headers(headers,hdr_charset,page)
     struct mailing_headers *headers;
     charset_t hdr_charset;
     struct menu_context  *page;
{
    struct hdr_menu_item *h;

    for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
	if (h->lineno > 0 && 0 != (h->flags & HF_ONPRE_SENDMENU))
	    hdrmenu_put(h, TRUE, headers,hdr_charset,page);
    }

}

/* returns 0 if command not found
           1 if found 
	   REDRAW_MARK is redraw required 
*/
int presend_action(headers,mailer_info,c,hdr_charset,aview,page,
		   prompt_area)
     struct mailing_headers *headers;    
     struct mailer_info  *mailer_info;
     int c;
     charset_t hdr_charset;
     struct AliasView *aview;
     struct menu_context *page;
     struct menu_context *prompt_area;
{
   struct hdr_menu_item *h;
   int LINES, COLUMNS;

   menu_get_sizes(page, &LINES, &COLUMNS);   


   for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
       if (0 != (h->flags & HF_ONPRE_SENDMENU)) {
	   if (h->menucmd == c) {
	       int status;

	       MoveCursor(LINES-4,0);
	       CleartoEOS();

	       status = hdrmenu_get(h,headers,mailer_info,hdr_charset,aview,
				    page,prompt_area);
	       if (REDRAW_MARK == status) {
		   return REDRAW_MARK;
	       }
	   
	       if (status != 0) {
		   Writechar('\007');
		   return 1;
	       }
	       if (h->lineno > 0)
		   hdrmenu_put(h, FALSE,headers,hdr_charset,page);
	       return 1;		
	   }	   
       }
   }
   return 0;
}

enum mode_xx { mode_plain, mode_phrase, mode_comment };

static void print_edit_string P_((enum mode_xx mode,FILE *F,charset_t edit_charset,
			   int encondig_supported,struct string *s));

static void print_edit_string(mode,F,edit_charset,encoding_supported,s)
     enum mode_xx mode;
     FILE *F;
     charset_t edit_charset;
     int encoding_supported;
     struct string *s;
{

    int mime_code = encoding_supported &&
	s->string_type != edit_charset;
		
    char * s1 = NULL;

    switch (mode) {
    case mode_phrase:
	s1 = string_to_hdr(HDR_PHRASE,s, edit_charset,mime_code, NULL);
	break;
    case mode_comment: putc('(',F); 
	s1 = string_to_hdr(HDR_COMMENT,s, edit_charset,mime_code, NULL);
	break;
    default:
	s1 = string_to_hdr(HDR_TEXT,s, edit_charset,mime_code, NULL);
	break;
    }
    
    fputs(s1,F); 
        
    switch (mode) {
    case mode_comment: putc(')',F); break;
    }
    
    free(s1);

}

static void  add_addr_header P_((FILE * F,charset_t edit_charset,
				 int encoding_supported,char * name, 
				 struct expanded_address *a));

static void  add_addr_header(F,edit_charset,encoding_supported,name,a)
     FILE * F;
     charset_t edit_charset;
     int encoding_supported;
     char * name; 
     struct expanded_address *a;
{
    int i;
    int l = 0;

    fprintf(F,"%s: ",name);

    for (i = 0; i < a->addrs_len; i++) {
	struct string * f = a->addrs[i].fullname;
	char * ad         = a->addrs[i].addr;
	struct string * c = a->addrs[i].comment;

	if (f && string_len(f) > 0) {
	    print_edit_string (mode_phrase,F,edit_charset,encoding_supported,f);
	    putc(' ',F);
	    l += string_len(f);
	}
	putc('<',F);
	fputs(ad,F);  l += strlen(ad);
	putc('>',F);
	if (c && string_len(c) > 0) {
	    putc(' ',F);
	    print_edit_string (mode_comment,F,edit_charset,encoding_supported,c);
	    l += string_len(c);
	}
	    
	if (i < a->addrs_len-1) {
	    putc(',',F);
	    if (l > 60) {
		putc('\n',F);
		l = 0;
	    }
	    putc(' ',F);
	}
    }
    putc('\n',F);

}

static void  add_string_header P_((FILE * F,charset_t edit_charset,
				 int encoding_supported,char * name, 
				 struct string *s));

static void  add_string_header(F,edit_charset,encoding_supported,name,s)
     FILE * F;
     charset_t edit_charset;
     int encoding_supported;
     char * name; 
     struct string *s;
{
    fprintf(F,"%s: ",name);
    print_edit_string (mode_plain,F,edit_charset,encoding_supported,s);
    putc('\n',F);

}


static void  edit_headers_on_editor P_((struct mailing_headers *headers,
					char *editor, int encoding_supported,
					struct menu_context *page));
static void  edit_headers_on_editor(headers,editor, encoding_supported,
				    page)
     struct mailing_headers *headers;    
     char *editor;
     int encoding_supported;
     struct menu_context *page;
{
    charset_t   edit_charset = system_charset;
    FILE * F = NULL; 
    char *    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");


    char * filename = elm_message(FRM("%s%sHDR%d"), 
				  tmp ? tmp : "/tmp/", 
				  temp_file, getpid());
    char * buffer = NULL;
    char buffer1[32*1024];
    int stat;
    int c;
    int err;
    int LINES, COLUMNS;

    menu_get_sizes(page, &LINES, &COLUMNS);   

    if (in_string(editor, "%s"))
	buffer = elm_message(FRM(editor), 
			     filename);
    else
	buffer = elm_message(FRM("%s %s"), 
			     editor, filename);


    F = safeopen_rdwr(filename);
    if (!F) 
	goto fail1;

    PutLineX(LINES-1, 0, CATGETS(elm_msg_cat, ElmSet, ElmInvokeEditor,
				   "Invoking editor..."));
    FlushBuffer();


    /* FIX: Following prints with display_charset and
            not with edit_charset (system_charset)
    */

    elm_fprintf(F,
		CATGETS(elm_msg_cat, ElmSet, ElmHdrEditComment,
			"# Headers are automatically converted from %s charset and not need encoded\n"),
		edit_charset->MIME_name ? edit_charset->MIME_name : "<no MIME name>");
			
    if (encoding_supported) {
	elm_fprintf(F,
		    CATGETS(elm_msg_cat, ElmSet, ElmHdrEditCommentEnc,
			    "# MIME encoding of headers is supported\n"));

    } else {
	elm_fprintf(F,
		    CATGETS(elm_msg_cat, ElmSet, ElmHdrEditCommentNo,
			    "# MIME encoding of headers is NOT supported\n"));

    }

    add_addr_header(F,edit_charset,encoding_supported,
		    "From", & headers->from);
    add_addr_header(F,edit_charset,encoding_supported,
		    "To", & headers->to);
    add_addr_header(F,edit_charset,encoding_supported,
		    "Cc", & headers->cc);
    add_addr_header(F,edit_charset,encoding_supported,
		    "Bcc", & headers->bcc);
    add_addr_header(F,edit_charset,encoding_supported,
		    "Reply-To", & headers->reply_to);
    add_string_header(F,edit_charset,encoding_supported,
		      "Subject", headers->subject);

    fclose(F);

    (void) elm_chown(filename, userid, groupid);
    
    Raw(OFF);
    /* FIX: SY_DUMPSTATE should perhaps be supported? */
    if ((stat = system_call(buffer, SY_ENAB_SIGHUP,
			    NULL)) == -1) {
	int err = errno;

	Raw(ON);
	DPRINT(Debug,1,(&Debug,
			"System call failed with stat %d (edit_the_message)\n", 
			stat));
	DPRINT(Debug,1,(&Debug,
		   "** %s **\n", error_description(err)));
	ClearLine(LINES-2);
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantInvokeEditor,
			  "Can't invoke editor '%s' for composition."), 
		  editor);
	sleep_message();

	goto fail2;
    }
    Raw(ON);

    err = can_open(filename,"r");
    if (err) {
	lib_error(FRM("%s: %s"),filename,err);
	
	goto fail1;
    }

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

    /* Skip comment block */
    
    while ('#' == (c = getc(F))) {
	
	while ('\n' != (c = getc(F))) {
	    if (EOF == c)
		break;
	}
    }
    
    if (EOF != c)
	ungetc(c,F);


    /* Read headers */
    while (0 < read_header_line(F,buffer1,sizeof buffer1,0)) {

	header_ptr header_name;
	char *k;

	if ('#' == buffer[0])
	    continue;

	k = strchr(buffer1,':');
	if (!k)
	    break;
	*k = '\0';
	k++;
	
	while (whitespace(*k))
	    k++;
	
	header_name = find_header(buffer1,1);
	
	if (!add_to_mailing_header(headers,header_name,k,
				   encoding_supported,
				   edit_charset,1 /* REPLACE */ )) {

	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmHdrNotSupported,
			      "Editing of %s header is not supported."),
		      buffer1);
	}
    }

    if (!feof(F))
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmHdrGarbage,
			  "Garbage on header file"));

    fclose(F);

 fail2:
    unlink(filename);

 fail1:

    free(filename); filename = NULL;
    free(buffer);   buffer = NULL;

}


void edit_headers(headers,mailer_info,hdr_charset, hdr_encoding_supported,
		  aview)
     struct mailing_headers *headers;    
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;
     int hdr_encoding_supported;
     struct AliasView *aview;
{
    int c, do_redraw;
    struct hdr_menu_item *h;
    int precmd = 0;

    char * editor_val = give_dt_estr_as_str(&editor_e,"editor");
    int editor_available = 
	editor_val &&
	strcmp(editor_val, "builtin") != 0 && 
	strcmp(editor_val, "none") != 0;
    int ul = give_dt_enumerate_as_int(&user_level);

    int LINES, COLUMNS;

    struct menu_context  *page = new_menu_context();

    struct menu_context  * prompt_area = NULL;

    menu_get_sizes(page, &LINES, &COLUMNS);   

    /*
     * Placement of prompts and messages at the bottom of the screen.
     */
    
    prompt_area = 
	new_menu_subpage(page,LINES-5,5,
			 subpage_simple_noredraw,NULL);

    /* expand out all of the header values */
    /* menu displays expanded values, user edits unexpended versions */

    do_redraw = TRUE;
    while (TRUE) {	/* forever */
	menu_set_default(page);

    resize_mark:
	if (menu_resized(page)) {
	    
	    menu_get_sizes(page,&LINES, &COLUMNS);
	    
	    menu_subpage_relocate(prompt_area,page,LINES-5,5);

	    do_redraw = 1;

	} else if (menu_need_redraw(page))
	    do_redraw = 1;


	if (headers->from.addrs_len > 0 &&
	    hmenu_from.lineno == -1 && 
	    (hmenu_to.flags & HF_DISP_MASK) == HF_DISP_3ROW) {
	    /* Hack: 
	       From: -header take space from To: -header ... 
	    */

	    hmenu_from.lineno = hmenu_to.lineno++;
	    hmenu_to.flags = hmenu_to.flags & ~HF_DISP_MASK |
		             HF_DISP_2ROW;
	    hmenu_from.flags = HF_DISP_1ROW;
	    do_redraw = 1;
	}

	/* redraw the entire display if required */
	if (do_redraw) {
	    menu_ClearScreen(page);
	    print_format_center(0, 
				CATGETS(elm_msg_cat, ElmSet,
					ElmHdrmenuScreenTitle, 
					"Message Header Edit Screen"));
	    for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
		if (h->lineno > 0)
		    hdrmenu_put(h, TRUE, headers, hdr_charset, page);
	    }
	    do_redraw = FALSE;

	    menu_redraw_children(page);
	}

	if (menu_resized(prompt_area) ||
	    menu_need_redraw(prompt_area)) {
	
	    menu_ClearScreen(prompt_area);
	    
	    /* display the instructions */
	    if (ul > 0 && editor_available) {
		
#ifdef ALLOW_SUBSHELL
		menu_print_format_center(prompt_area,0, 
					 CATGETS(elm_msg_cat, ElmSet,
						 ElmHdrmenuInstruct1,
						 "Choose header, u)ser defined header, !)shell, invoke e(d)itor, or <return>."));
#else
		menu_print_format_center(prompt_area,0
					 CATGETS(elm_msg_cat, ElmSet,
						 ElmHdrmenuInstruct1NoShell,
						 "Choose header, u)ser defined header, invoke e(d)itor, or <return>."));
#endif
		
	    } else {
		
#ifdef ALLOW_SUBSHELL
		menu_print_format_center(prompt_area,0,
					 CATGETS(elm_msg_cat, ElmSet,
						 ElmHdrmenuInstruct,
						 "Choose header, u)ser defined header, !)shell, or <return>."));
#else
		menu_print_format_center(prompt_area,0,
					 CATGETS(elm_msg_cat, ElmSet,
						 ElmHdrmenuInstructNoShell,
						 "Choose header, u)ser defined header, or <return>."));
#endif
		
	    }	    	    	    
	}
	    
       

	/* prompt for command */
	menu_PutLineX(prompt_area,2, 0, 
		      CATGETS(elm_msg_cat, ElmSet,
			      ElmHdrmenuPrompt, "Choice: "));
	if (precmd) {
	    c = precmd;
	    precmd = 0;
	} else {
	    c = menu_ReadCh(prompt_area,REDRAW_MARK|READCH_resize|READCH_sig_char);
	    if (c == REDRAW_MARK) {
		menu_ClearScreen(page);   /* Clear possible redraw mark */
		
		/* Call refresh routines of children */
		menu_redraw_children(page);

		if (menu_need_redraw(prompt_area))		    
		    menu_ClearScreen(prompt_area);   /* Clear redraw mark from prompt_area*/

		do_redraw = TRUE;
		continue;
	    }

	    if (c == TERMCH_interrupt_char)
		goto OUT;

	    if (c == RESIZE_MARK) {
		DPRINT(Debug,4, (&Debug, "  ... resizing\n"));
		goto resize_mark;
	    }

	}

#ifdef ASCII_CTYPE
	if (isascii(c))
#endif
	  c = tolower(c);

	menu_ClearScreen(prompt_area);

	/* execute the command */
	switch (c) {

	case EOF:
	case RETURN:
	case LINE_FEED:
	case 'q':
	    goto OUT;

#ifdef ALLOW_SUBSHELL
	case '!':
	   
	    subshell(NULL, page, prompt_area);   /* !! */

	    if (menu_need_redraw(page))
		do_redraw = TRUE;
	    break;
#endif

	case ctrl('L'):
	    do_redraw = TRUE;
	    break;

	case 'd':
	    if (editor_val) {
		editor_available &=  have_editor(editor_val);
		
		if (editor_available)
		    edit_headers_on_editor(headers,editor_val, 
					   hdr_encoding_supported,
					   page);
	    }

	    do_redraw = TRUE;
	    break;


	default:
	    for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
		if (h->menucmd == c) {
		    int status = hdrmenu_get(h,headers,mailer_info,
					     hdr_charset,aview,
					     page,prompt_area);
		    if (REDRAW_MARK == status) {
			precmd = c;
			do_redraw = TRUE;
			break;
		    }

		    if (status != 0) {
			Writechar('\007');
			break;
		    }
		    if (h->lineno > 0)
			hdrmenu_put(h, FALSE,headers, hdr_charset, page);
		    break;
		}
	    }
	    if (h->menucmd <= 0) {
		menu_print_format_center(prompt_area,3, 
					 CATGETS(elm_msg_cat, ElmSet,
						 ElmHdrmenuBadChoice, 
						 "No such header!"));
		Writechar('\007');
	    }
	    break;
	}

    }

 OUT:
    erase_menu_context(&prompt_area);

    erase_menu_context(&page);
    return;
}




/*
 * Prompt the user for a header value, and do any required post-processing.
 */
static int hdrmenu_get(h,headers,mailer_info,hdr_charset,aview,page,
		       prompt_area)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;  
     struct mailer_info  *mailer_info;     
     charset_t hdr_charset;
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;
{
    char *s;
    int plen, ret, do_append, do_comma;
    struct string *buffer = NULL;
    char header_name[20];
    int LINES, COLUMNS;

    menu_get_sizes(page,&LINES, &COLUMNS);

    menu_ClearLine(prompt_area,0);

    /* display the instructions */
    switch (h->flags & HF_PROMPT_MASK) {
    case HF_PROMPT_EXP:
	menu_print_format_center(prompt_area,0,
				 CATGETS(elm_msg_cat, ElmSet,
					 ElmHdrmenuGetExpiresInstruct,
					 "In how many days should this message expire? "));
	break;
    case HF_PROMPT_USR:
	menu_print_format_center(prompt_area,0,
				 CATGETS(elm_msg_cat, ElmSet,
					 ElmHdrmenuGetUserdefInstruct,
					 "Enter in the format \"HeaderName: HeaderValue\"."));
	break;
    default:
	menu_print_format_center(prompt_area,0,
				 CATGETS(elm_msg_cat, ElmSet,
					 ElmHdrmenuGetInstruct, 
					 "Enter value for the header."));
	break;
    }

    /* display a prompt */
    plen = 0;

    if (h->hdrname != NULL) {
	menu_MoveCursor(prompt_area,2,0);
	for (s = h->hdrname ; *s != '\0' ; ++s) {
	    if (*s != '(' && *s != ')') {
		if (plen > sizeof header_name -4)
		    break;
		header_name[plen] = *s;
		++plen;
	    }
	}
	header_name[plen++] = ':';
	header_name[plen++] = ' ';
    }
    header_name[plen] = '\0';

    /* get input from the user */
    do_append = ((h->flags & HF_APPENDENTRY) != 0);
    h->inpval_proc(h,headers,&buffer,hdr_charset);
    do_comma = ((h->flags & HF_ADD_COMMA) != 0 &&
		string_len(buffer));

    {
	/* FIXME --optionally_enter*  should use prompt_area */
	int line1 = menu_GetAbsLine(prompt_area,2);

	ret = optionally_enter2(page, &buffer, line1, 0, 
				(do_append ? OE_APPEND_CURRENT : 0) | 
				(do_comma ? OE_ADD_COMMA : 0) |
				OE_REDRAW_MARK|OE_SIG_CHAR /* Ctrl-C */, 
				FRM("%s"),header_name);      
    }
    if (ret == REDRAW_MARK) {
	h->hdrproc(h,headers,&buffer,TRUE,mailer_info,hdr_charset,aview,page,
		   prompt_area);
	return REDRAW_MARK;
    }

    menu_ClearScreen(prompt_area);

    /* bail out on error */
    if (ret != 0) {
	if (buffer)
	    h->hdrproc(h,headers,&buffer,TRUE,mailer_info,hdr_charset,aview,
		       page,prompt_area);
	return -1;
    }
    /* see if there is some processing required on this value */
    return h->hdrproc(h,headers,&buffer,FALSE,mailer_info,hdr_charset,
		      aview,page,prompt_area);
}


/*
 * Dispay a header and its value in the appropriate field.
 */
static void hdrmenu_put(h, already_clear, headers, hdr_charset,
			page)
     struct hdr_menu_item *h;
     int already_clear;
     struct mailing_headers *headers; 
     charset_t hdr_charset;
     struct menu_context  *page;
{
    struct string * buffer = NULL;
    char    *p;
    int     start_row, max_row, start_col, max_col, row, col;
    int X;
    int Bl;
    int LINES, COLUMNS;

    menu_get_sizes(page,&LINES, &COLUMNS);

    /* figure out the dimensions of the field */
    switch (h->flags & HF_DISP_MASK) {
    case HF_DISP_LEFT:
	start_row = h->lineno;		max_row = h->lineno;
	start_col = 0;			max_col = COLUMNS/2 - 2;
	break;
    case HF_DISP_RIGHT:
	start_row = h->lineno;		max_row = h->lineno;
	start_col = COLUMNS/2 + 1;	max_col = COLUMNS-1;
	break;
    case HF_DISP_3ROW:
	start_row = h->lineno;		max_row = h->lineno+2;
	start_col = 0;			max_col = COLUMNS-1;
	break;
    case HF_DISP_2ROW:
	start_row = h->lineno;		max_row = h->lineno+1;
	start_col = 0;			max_col = COLUMNS-1;
	break;
    default:
	start_row = h->lineno;		max_row = h->lineno;
	start_col = 0;			max_col = COLUMNS-1;
	break;
    }

    /* display the header name */
    MoveCursor(start_row, start_col);
    if (h->hdrname != NULL) {
	for (p = h->hdrname ; *p != '\0' ; ++p)
	    Writechar(*p);
	Writechar(':');
	Writechar(' ');
    }

    /* display the header value */
    GetXYLocation(&row, &col);

    buffer=h->expval_proc(h,headers,hdr_charset);          
    
    Bl = buffer ? string_len(buffer) : 0;
    for (X = 0; buffer && X < Bl && row <= max_row; row++) {
	struct string * str = NULL;
	int visible_len;
	
	int space_left;
	int cur_col = col;

    retry:
	if (row == max_row) /* neat hack alert */
	    space_left = max_col-cur_col-4;
	else 
	    space_left = max_col-cur_col;

	str = curses_printable_clip(buffer,&X,Bl,&visible_len,space_left);

	if (str) {
	    MoveCursor(row, col);
	    Write_to_screen(FRM("%S"),str);
	
	    if (row == max_row && X < string_len(buffer)) {
		Write_to_screen(FRM(" ..."));
		cur_col += 4;
	    }

	    free_string(&str);
	    cur_col += visible_len;
	} else {
	    /* Treat next character as control character */
	    StartBold();
	    Writechar('?');		    
	    EndBold();
	    cur_col++;
	    if (cur_col < max_col && X < Bl)
		goto retry;
	}

	/* If we are not end of string -- clear rest on case of double with characters */
	if (X < Bl && cur_col < max_col && !already_clear) {
	    while (cur_col++ <= max_col)
		Writechar(' ');
	}

    }
    GetXYLocation(&row, &col);

    /* save some drawing if we know the screen is already empty */
    if (!already_clear) {
	
	/* clear out remaining space in this line of the field */
	if (max_col == COLUMNS-1) {
	    /* people on slow terminals might appreciate doing it this way */
	    CleartoEOLN();
	} else {
	    while (col++ <= max_col)
		Writechar(' ');
	}
	
	/* for multi-line fields, clear out any unused lines */
	/* this assumes that multi-line fields span the entire screen width */
	while (++row <= max_row) {
	    /* grrrrrr -- this is a multi-statement macro */
	    ClearLine(row);
	}

    }

    if (buffer)
	free_string(&buffer);
}

void hdr_to_buffer(addrs,ptr)
     struct expanded_address addrs;
     struct string **ptr; 
{
    expanded_to_edit_buffer(ptr,addrs);
}

struct string *hdr_to_expval(addrs)
     struct expanded_address addrs;
{
    struct addr_item *p;
    struct string * buffer = NULL;
    
    for (p = addrs.addrs; p < addrs.addrs + addrs.addrs_len; p++) {
	if (buffer)
	    add_ascii_to_string(buffer,s2us(", "));
	else
	    buffer = new_string(display_charset);
	
	if (string_len(p->fullname) || ! p->addr[0] || '@' == p->addr[0]) {
	    int pd = give_dt_enumerate_as_int(&phrase_display_mode);

	    /* We do not quote displayed (only) address */
	    struct string * temp  = NULL;

	    switch (pd) {
	    case 0: 
		temp = cat_strings(buffer,p->fullname,1);
		break;
	    default:
		/* We do not (backslash) quote displayed (only) address */
		
		add_ascii_to_string(buffer,s2us("\""));
		temp = cat_strings(buffer,p->fullname,1);
		add_ascii_to_string(temp,s2us("\""));
		break;
	    }
	    add_ascii_to_string(temp,s2us(" <"));
	    add_ascii_to_string(temp,s2us(p->addr));
	    add_ascii_to_string(temp,s2us(">"));
	    free_string(&buffer);
	    buffer = temp;
	    
	} else
	    add_ascii_to_string(buffer,s2us(p->addr));
	if (string_len(p->comment)) {
	    /* We do not quote displayed (only) address */
	    struct string * temp;

	    add_ascii_to_string(buffer,s2us(" ("));
	    temp = cat_strings(buffer,p->comment,1);
	    add_ascii_to_string(temp,s2us(")"));
	    free_string(&buffer);
            buffer = temp;
	}
    } 
    return buffer;
}

/*
 * Process the to, cc, and bcc headers.  The value entered by the
 * user is expanded.  A successful status is always returned.
 */

int buffer_to_header(addrs,ptr,free_only,mailer_info,aview)
     struct expanded_address *addrs;
     struct string **ptr;
     int free_only;
     struct mailer_info *mailer_info;
     struct AliasView *aview;
{
    if (!free_only) {
	update_expanded_from_edit_buffer(addrs,*ptr,mailer_info, aview);
    }
    free_string(ptr);    
    return 0;
}

static void text_to_buffer P_((char *text,struct string **ptr,
			       charset_t hdr_charset));
static void text_to_buffer(text,ptr,hdr_charset)
     char *text;
     struct string **ptr;
     charset_t hdr_charset;
{
    if (text)
	*ptr = new_string2(hdr_charset,s2us(text));
    else 
	*ptr = new_string(hdr_charset);
}

static struct string * text_to_expval P_((char *text,
					  charset_t hdr_charset));
static struct string * text_to_expval(text,hdr_charset)
     char *text;
     charset_t hdr_charset;
{
    if (!text)
	return new_string(hdr_charset);
    else
	return new_string2(hdr_charset,s2us(text));
}

static int buffer_to_text P_((char **text,struct string **ptr,int free_only,
			      charset_t hdr_charset));
static int buffer_to_text(text,ptr,free_only,hdr_charset)
     char **text;
     struct string **ptr;
     int free_only;
     charset_t hdr_charset;
{

    if (!free_only) {
	struct string * X;

	if (can_ascii_string(*ptr))
	    X = ascify_string(*ptr);
	else
	    X = convert_string(hdr_charset,
			       *ptr,0);

	if (*text)
	    free(*text);
	*text = us2s(stream_from_string(X,0,NULL));
	free_string(&X);
    }
    free_string(ptr);
    return 0;
}

/* From: -header -------------------------------------------------------- */

static void inpval_from (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;  /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;    /* NOT USED */
{
    hdr_to_buffer(headers->from,ptr);
}

static struct string * expval_from (h,headers,hdr_charset)
     struct hdr_menu_item *h;    /* NOT USED */
     struct mailing_headers *headers;
     charset_t hdr_charset;      /* NOT USED */
{
    return hdr_to_expval(headers->from);
}

static int hdrproc_from  (h,headers,ptr,free_only,mailer_info,hdr_charset,
			  aview,page,prompt_area)
     struct hdr_menu_item *h;              /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr;
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;                /* NOT USED */
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;    /* NOT USED */
{
    return buffer_to_header(&headers->from,ptr,free_only,mailer_info,
			    aview);
}

/* To: -header -------------------------------------------------------- */

static void inpval_to (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;                   /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;                     /* NOT USED */
{
    hdr_to_buffer(headers->to,ptr);
}

static struct string * expval_to (h,headers,hdr_charset)
     struct hdr_menu_item *h;                   /* NOT USED */
     struct mailing_headers *headers;
     charset_t hdr_charset;                     /* NOT USED */
{
    return hdr_to_expval(headers->to);
}

static int hdrproc_to  (h,headers,ptr,free_only,mailer_info,hdr_charset,
			aview,page,prompt_area)
     struct hdr_menu_item *h;                  /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr;
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;                    /* NOT USED */
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;        /* NOT USED */
{
    return buffer_to_header(&headers->to,ptr,free_only,mailer_info,
			    aview);
}

/* CC: -header -------------------------------------------------------- */

static void inpval_cc (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;                      /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;                        /* NOT USED */
{
    hdr_to_buffer(headers->cc,ptr);
}

static struct string * expval_cc (h,headers,hdr_charset)
     struct hdr_menu_item *h;                      /* NOT USED */
     struct mailing_headers *headers;
     charset_t hdr_charset;                        /* NOT USED */
{
    return hdr_to_expval(headers->cc);
}

static int hdrproc_cc  (h,headers,ptr,free_only,mailer_info,hdr_charset,
			aview,page,prompt_area)
     struct hdr_menu_item *h;                       /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr;
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;                         /* NOT USED */
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;        /* NOT USED */
{
    return buffer_to_header(&headers->cc,ptr,free_only,mailer_info,
			    aview);
}

/* BCC: -header -------------------------------------------------------- */

static void inpval_bcc (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;                      /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;                        /* NOT USED */
{
    hdr_to_buffer(headers->bcc,ptr);
}

static struct string * expval_bcc (h,headers,hdr_charset)
     struct hdr_menu_item *h;                      /* NOT USED */
     struct mailing_headers *headers;
     charset_t hdr_charset;                        /* NOT USED */
{
    return hdr_to_expval(headers->bcc);
}

static int hdrproc_bcc  (h,headers,ptr,free_only,mailer_info,hdr_charset,
			 aview,page,prompt_area)
     struct hdr_menu_item *h;                      /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr;
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;                        /* NOT USED */
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;        /* NOT USED */
{
    return buffer_to_header(&headers->bcc,ptr,free_only,mailer_info,
			    aview);
}

/* Subject: -header -------------------------------------------------------- */

static void inpval_subject (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;                          /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;
{
    if (*ptr)
	free_string(ptr);
    
    if (headers->subject) {
	*ptr = dup_string(headers->subject);
    } else
	*ptr = new_string(hdr_charset);
}

static struct string * expval_subject (h,headers,hdr_charset)
     struct hdr_menu_item *h;                            /* NOT USED */
     struct mailing_headers *headers;
     charset_t hdr_charset;
{
    struct string * r;

    if (headers->subject)
	r = dup_string(headers->subject);
    else
	r = new_string(hdr_charset);
    return r;
}

static int hdrproc_subject  (h,headers,ptr,free_only,mailer_info,hdr_charset,
			     aview,page,prompt_area)
     struct hdr_menu_item *h;                          /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     int free_only;
     struct mailer_info  *mailer_info;                 /* NOT USED */
     charset_t hdr_charset;                            /* NOT USED */
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;        /* NOT USED */
{
    if (!free_only) {
	charset_t utf7;
	if (headers->subject)
	    free_string(&(headers->subject));

	if (convert_utf_header && (*ptr)->string_type->MIME_name &&
	    0 == istrcmp((*ptr)->string_type->MIME_name,"UTF-8") &&
	    0 != (CS_mapping & charset_properties((*ptr)->string_type)) &&
	    (utf7 =  MIME_name_to_charset("UTF-7",0)) &&
	    0 != (CS_mapping & charset_properties(utf7))) {

	    headers->subject = convert_string(utf7,*ptr,1);
	    free_string(ptr);
	    
	} else {	   
	    headers->subject = *ptr;
	    *ptr = NULL;
	}
    } else
	free_string(ptr);

    return 0;
}

/* Reply-To: -header -------------------------------------------------- */

static void inpval_reply_to (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;                       /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr;
     charset_t hdr_charset;                         /* NOT USED */
{
    hdr_to_buffer(headers->reply_to,ptr);
}

static struct string * expval_reply_to (h,headers,hdr_charset)
     struct hdr_menu_item *h;                       /* NOT USED */
     struct mailing_headers *headers;
     charset_t hdr_charset;                         /* NOT USED */
{
    return hdr_to_expval(headers->reply_to);
}

static int hdrproc_reply_to  (h,headers,ptr,free_only,mailer_info,hdr_charset,
			      aview,page,prompt_area)
     struct hdr_menu_item *h;                       /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;                        /* NOT USED */
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;        /* NOT USED */
{
    return buffer_to_header(&headers->reply_to,ptr,free_only,mailer_info,
			    aview);
}

/* Action: -header ---------------------------------------------------- */

static void inpval_action (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;                      /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;
{
    text_to_buffer(headers->action,ptr,hdr_charset); 
}

static struct string * expval_action (h,headers,hdr_charset)
     struct hdr_menu_item *h;                      /* NOT USED */
     struct mailing_headers *headers;
     charset_t hdr_charset;
{
    return text_to_expval(headers->action,hdr_charset);
}

static int hdrproc_action  (h,headers,ptr,free_only,mailer_info,hdr_charset,
			    aview,page,prompt_area)
     struct hdr_menu_item *h;                       /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;        /* NOT USED */
{
    return buffer_to_text(&headers->action,ptr,free_only,hdr_charset);
}

/* Expires: -header ---------------------------------------------------- */

static void inpval_expires (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;                         /* NOT USED */
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;
{
    text_to_buffer(headers->expires_days,ptr,hdr_charset); 
}

static struct string * expval_expires (h,headers,hdr_charset)
     struct hdr_menu_item *h;                        /* NOT USED */
     struct mailing_headers *headers;
     charset_t hdr_charset;
{
    return text_to_expval(headers->expires,hdr_charset);
}

/*
 * Process the expires header.  The value entered by the user is interpreted
 * as a number of days, and is expanded out to a date specification.  If
 * an error occurs a message is printed, the expanded value is cleared
 * out, and a -1 is returned.
 */

static int hdrproc_expires  (h,headers,ptr,free_only,mailer_info,hdr_charset,
			     aview,page,prompt_area)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr; 
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;
{
    int ret_val = 0;

    if (!free_only) {

	char buffer[STRING];
	int days;
	int fail_pos;

	struct string * X = NULL;
	int LINES, COLUMNS;

	menu_get_sizes(page,&LINES, &COLUMNS);




	/* initialize expanded date spec to empty */
	if (headers->expires)
	    free(headers->expires);
	headers->expires = NULL;

	/* blank is ok */
	if (ptr[0] == '\0') {
	    if (headers->expires_days)
		free(headers->expires_days);
	    headers->expires_days = NULL;
	    goto free_it;
	}

	/* verify the number of days is valid and in range */
	days = string_to_long(*ptr,&fail_pos);
	if (days < 1 || fail_pos >= 0) {
	    menu_print_format_center(prompt_area,3, 
				     CATGETS(elm_msg_cat, ElmSet,
					     ElmHdrmenuExpiresNotNumber,
					     "Expiration must be specified as a number of days."));
	    ret_val = -1;
	    goto free_it;
	}	

	if (days > 8*7) {
	    menu_print_format_center(prompt_area,3, 
				     CATGETS(elm_msg_cat, ElmSet,
					     ElmHdrmenuExpiresOutOfRange,
					     "Expiration date must be within eight weeks of today."));
	    ret_val = -1;
	    goto free_it;
	}
	/* convert number of days to a date */
	days_ahead(days, buffer, sizeof buffer);

	headers->expires = strmcpy(headers->expires,buffer);
	
	if (can_ascii_string(*ptr))
	    X = ascify_string(*ptr);
	else
	    X = convert_string(hdr_charset,
			       *ptr,0);

	if (headers->expires_days)
	    free(headers->expires_days);
	headers->expires_days = us2s(stream_from_string(X,0,NULL));
	free_string(&X);
 
	ret_val = 0;
    }

 free_it:
    free_string(ptr);
    return ret_val;
}


/* Priority: -header ---------------------------------------------------- */

static void inpval_priority (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;
{
    text_to_buffer(headers->priority,ptr,hdr_charset); 
}

static struct string * expval_priority (h,headers,hdr_charset)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     charset_t hdr_charset;
{
    return text_to_expval(headers->priority,hdr_charset);
}

static int hdrproc_priority(h,headers,ptr,free_only,mailer_info,hdr_charset,
			    aview,page,prompt_area)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr;
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;        /* NOT USED */
{
    return buffer_to_text(&headers->priority,ptr,free_only,hdr_charset);
}

/* Precedence: -header ---------------------------------------------------- */

static void inpval_precedence (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;
{
    text_to_buffer(headers->precedence,ptr,hdr_charset); 
}

static struct string * expval_precedence (h,headers,hdr_charset)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     charset_t hdr_charset;
{
    return text_to_expval(headers->precedence,hdr_charset);
}

/*
 * Process the precedence header.  The value entered by the user is
 * checked against the list of allowed precedences, if one exists.  If
 * the precedence has a priority assigned to it, then an empty priority
 * field will be filled in with that value.  If an error occurs a message
 * is printed, the precedence value is cleared out, and a -1 is returned.
 */

static int hdrproc_precedence(h,headers,ptr,free_only,mailer_info,
			      hdr_charset,aview,page,prompt_area)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr; 
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;
{
    int ret_val = 0;

    if (!free_only) {
	char *buf, *bp;
	char *prec = NULL, *prio = NULL;
	int Lptr = string_len(*ptr);
	struct string * X = NULL;
	int LINES, COLUMNS;

	menu_get_sizes(page,&LINES, &COLUMNS);

	/* empty is ok */
	if (0 == Lptr) {
	    if (headers->precedence)
		free(headers->precedence);
	    headers->precedence = NULL;
	    goto free_it;
	}

	if (can_ascii_string(*ptr))
	    X = ascify_string(*ptr);
	else
	    X = convert_string(hdr_charset,
			       *ptr,0);

	/* if there are no restrictions on precedence then anything is ok */
	if (allowed_precedences[0] == '\0') {
	    if (headers->precedence)
		free(headers->precedence);
	    headers->precedence = us2s(stream_from_string(X,0,NULL));
	    free_string(&X);

	    goto free_it;
	}

	/* the "allowed_precedences[]" format is: */
	/*   precedence[:priority-value] precedence[:priority-value] ... */
	bp = buf = safe_strdup(allowed_precedences);

	while ((prec = strtok(bp, " \t\n")) != NULL) {
	    struct string *T = NULL;
	    int r;

	    bp = NULL;
	    if ((prio = index(prec, ':')) != NULL)
		*prio++ = '\0';

	    /* TODO: This is ineffective 
	       TODO: Should be case-insensitive
	    */
	    T = new_string2(system_charset,s2us(prec));
	    r = string_cmp(*ptr,T,
			   999 /* == Not equal if not comparable */ );

	    free_string(&T);

	    if (0 == r)
		break;
	}

	/* Do not 	
	   free(buf);
	   here because prio pointer must be valid!
	*/

	/* see if we reached the end of the list without a match */
	if (prec == NULL) {
	    menu_print_format_center(prompt_area,3, 
				     CATGETS(elm_msg_cat, ElmSet,
					     ElmHdrmenuPrecedenceBadValue,
					     "Unknown precedence value specified."));	    
	    ret_val = -1;
	    free_string(&X);
	    free(buf);
	    goto free_it;
	}

	if (headers->precedence)
	    free(headers->precedence);
	headers->precedence = us2s(stream_from_string(X,0,NULL));

	/* see if this precedence has an associated priority */
	if (prio != NULL && headers->priority == NULL) {
	    headers->priority = strmcpy(headers->priority,prio);
	    hdrmenu_put(&hmenu_priority, FALSE,headers,hdr_charset,page);
	}

	free_string(&X);
	free(buf);

	ret_val = 0;
    }

 free_it:
    free_string(ptr);
    return ret_val;
}

/* In-Reply-To: -header ---------------------------------------------------- */

static void inpval_in_reply_to (h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr;
     charset_t hdr_charset;
{
    if (headers->in_reply_to.id && headers->in_reply_to.text)
	*ptr = format_string(FRM("%s%S"),
			     headers->in_reply_to.id,
			     headers->in_reply_to.text);
    else if (headers->in_reply_to.text)
	*ptr = dup_string(headers->in_reply_to.text);
    else if (headers->in_reply_to.id)
	*ptr = new_string2(hdr_charset,
			   s2us(headers->in_reply_to.id));
    else
	*ptr = new_string(hdr_charset);
}

static struct string * expval_in_reply_to (h,headers,hdr_charset)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     charset_t hdr_charset;
{
    struct string * m = new_string(hdr_charset);
    struct string * r;

    if (headers->in_reply_to.id)
	add_ascii_to_string(m,s2us(headers->in_reply_to.id));

    if (headers->in_reply_to.text) {
	int pd = give_dt_enumerate_as_int(&phrase_display_mode);

	switch (pd) {
	    int X;
	    struct string * temp;

	case 0:
	    r = cat_strings(m,headers->in_reply_to.text,1);
	    break;
	default:
	    /* We do not (backslash) quote displayed (only) values */

	    X = 0;
	    
	    if (string_len(headers->in_reply_to.text) > 0 &&
		0x0020 == give_unicode_from_string(headers->in_reply_to.text,
						   0)) {
		add_ascii_to_string(m,s2us(" "));
		X = 1;
	    }

	    temp = clip_from_string(headers->in_reply_to.text,
				    &X, 
				    string_len(headers->in_reply_to.text));

	    add_ascii_to_string(m,s2us("\""));
	    r = cat_strings(m,temp,1);
	    add_ascii_to_string(r,s2us("\""));
	    free_string(&temp);

	    break;
	}
    } else
	r = dup_string(m);
    free_string(&m);

    return r;
}

static int hdrproc_in_reply_to (h,headers,ptr,free_only,mailer_info,
				hdr_charset,aview,page,prompt_area)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr; 
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;        
{
    int ret_val = 0;
  
    if (!free_only) {
	int Xl = string_len(*ptr);
	int p  = 0;
	int p1 = 0;
	int LINES, COLUMNS;

	menu_get_sizes(page,&LINES, &COLUMNS);

	while (Xl > p &&
	       0x0020 == give_unicode_from_string(*ptr,p))
	    p++;
	    
	if (Xl > p &&
	    0x003C  /* < */ == give_unicode_from_string(*ptr,p)) { 

	    struct string * X = NULL;
	    int x;

	    for (x = p; x < Xl; x++) {
		if (0x003E  /* > */ == give_unicode_from_string(*ptr,x)) {
		    p1 = x+1;   /* past '>' */
		    break;
		}
	    } 

	    if (!p1) {
		menu_print_format_center(prompt_area,3, 
					 CATGETS(elm_msg_cat, ElmSet,
						 ElmHdrmenuMissAngle,
						 "Missing > after < on in-reply-to header."));
		
		ret_val = -1;
		goto free_it;
	    }

	    if (can_ascii_string(*ptr))
		X = ascify_string(*ptr);
	    else
		X = convert_string(hdr_charset,
				   *ptr,0);

	    if (headers->in_reply_to.id)
		free(headers->in_reply_to.id);
	    headers->in_reply_to.id = 
		us2s(streamclip_from_string(X,&p,p1-p,NULL,NULL));
	     
	    free_string(&X);

	} else {
	    if (headers->in_reply_to.id)
		free(headers->in_reply_to.id);
	    headers->in_reply_to.id = NULL;
	}

	if (headers->in_reply_to.text)
	    free_string(&(headers->in_reply_to.text));

	if (p < Xl) {
	    
	    if (!add_irt_phrase)
		menu_print_format_center(prompt_area,3, 
					 CATGETS(elm_msg_cat, ElmSet,
						 ElmHdrmenuPhraseIRT,
						 "In-reply-to header should include only message-id"));

	    headers->in_reply_to.text = clip_from_string(*ptr,&p,Xl);
	}
    }

 free_it:
    free_string(ptr);
    return ret_val;
}

/* User defined header ---------------------------------------------------- */

static void inpval_userhdr(h,headers,ptr,hdr_charset)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr; 
     charset_t hdr_charset;
{
    if (headers->user_header_count > 0 &&
	headers->user_header[0].value) {

	*ptr = format_string(FRM("%s: %S"),
			     give_header_name(headers->user_header[0].name),
			     headers->user_header[0].value);
    } else
	*ptr = new_string(hdr_charset);
}

static struct string * expval_userhdr(h,headers,hdr_charset)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     charset_t hdr_charset;
{
    if (headers->user_header_count > 0 &&
	headers->user_header[0].value) 
	return format_string(FRM("%s: %S"),
			     give_header_name(headers->user_header[0].name),
			     headers->user_header[0].value);
    return new_string(hdr_charset);
}

/*
 * Process the user-defined header.  The value entered by the user is
 * verified for proper format.  If an error occurs a message is printed,
 * the expanded value is cleared out, and a -1 is returned.
 */

static int hdrproc_userhdr(h,headers,ptr,free_only,mailer_info,hdr_charset,
			   aview,page,prompt_area)
     struct hdr_menu_item *h;
     struct mailing_headers *headers;
     struct string **ptr; 
     int free_only;
     struct mailer_info  *mailer_info;
     charset_t hdr_charset;
     struct AliasView *aview;
     struct menu_context  *page;
     struct menu_context  *prompt_area;       
{
    int ret_val = 0;

    if (!free_only) {
	header_ptr header_name;	
	int Xl = string_len(*ptr);

	charset_t utf8 = MIME_name_to_charset("UTF-8",0);
	struct string * utf8_temp  = NULL;
	unsigned char * utf8_value = NULL;
	char * s;
	int LINES, COLUMNS;

	menu_get_sizes(page,&LINES, &COLUMNS);

	/* empty is ok */
	if (0 == Xl) {
	    int i;
	    for (i = 0; i < headers->user_header_count; i++) {
		if (headers->user_header[i].value)
		    free_string(&(headers->user_header[i].value));
		headers->user_header[i].name = NULL;
	    }
	    headers->user_header_count = 0;

	    goto free_it;
	}


	/* HACK: We use UTF-8 version of header value .... */
	
	if (!utf8) 
	    panic("CHARSET PANIC",__FILE__,__LINE__,"hdrproc_userhdr",
		  "UTF-8 not found",0);

	utf8_temp    = convert_string(utf8,*ptr,0);
	utf8_value = stream_from_string(utf8_temp,0,NULL);

	
	/* make sure the header name doesn't begin with some strange 
	 * character -- Note that only US-ASCII is allowed according
	 * of standards on headers
	 */

	if (!isascii(utf8_value[0]) &&
	    !isalnum(utf8_value[0])) {
	    menu_print_format_center(prompt_area,3, 
				     CATGETS(elm_msg_cat, ElmSet,
					     ElmHdrmenuUserdefNotAlnum,
					     "The user-defined header must begin with a letter or number."));
	    ret_val = -1;

	    free(utf8_value);
	    free_string(&utf8_temp);

	    goto free_it;
	}
	
	/* locate the end of the header name */
	for (s = us2s(utf8_value) ; 
	     *s != ':' && isascii(*s) && isprint(*s) && !isspace(*s) ; 
	     ++s)
	    continue;
	
	    /* there needs to be a colon at the end of the header name */
	if (*s != ':') {
	    menu_print_format_center(prompt_area,3, 
				     CATGETS(elm_msg_cat, ElmSet,
					     ElmHdrmenuUserdefMissingColon,
					     "The user-defined header must have a colon after the field name."));
	    ret_val = -1;

	    free(utf8_value);
	    free_string(&utf8_temp);

	    goto free_it;
	}

	*s = '\0';
	*s++;

	while (whitespace(*s))
	    s++;

	header_name = find_header(us2s(utf8_value),1);
		
	/* Allow user to paste mime encoded words to buffer ... */
	if (!add_to_mailing_header(headers,header_name,s,
				   1,utf8,1)) 
	    ret_val = -1;
	else
	    ret_val = 0;
    }

 free_it:
    free_string(ptr);
    return ret_val;
}

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



syntax highlighted by Code2HTML, v. 0.9.1