ftp.nice.ch/pub/next/unix/mail/elm2.4/elm.2.4pl17.s.tar.gz#/src/hdrconfg.c

This is hdrconfg.c in view mode; [Download] [Up]

static char rcsid[] = "@(#)$Id: hdrconfg.c,v 5.2 1992/11/22 01:15:15 syd Exp $";

/*******************************************************************************
 *  The Elm Mail System  -  $Revision: 5.2 $   $State: Exp $
 *
 *			Copyright (c) 1988-1992 USENET Community Trust
 *			Copyright (c) 1986,1987 Dave Taylor
 *******************************************************************************
 * Bug reports, patches, comments, suggestions should be sent to:
 *
 *	Syd Weinstein, Elm Coordinator
 *	elm@DSI.COM			dsinc!elm
 *
 *******************************************************************************
 * $Log: hdrconfg.c,v $
 * Revision 5.2  1992/11/22  01:15:15  syd
 * Add on initial display or display where the entire screen is being
 * drawn, we should not output the trailing blanks that clear the old
 * value.
 * From: chip@chinacat.unicom.com (Chip Rosenthal)
 *
 * Revision 5.1  1992/10/03  22:58:40  syd
 * Initial checkin as of 2.4 Release at PL0
 *
 *
 ******************************************************************************/

/**   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 "headers.h"
#include "s_elm.h"

#include <ctype.h>

#ifdef BSD
#undef toupper
#undef tolower
#endif

/*
 * Placement of prompts and messages at the bottom of the screen.
 */
#define INSTRUCT_LINE           (LINES-4)
#define INPUT_LINE		(LINES-2)
#define ERROR_LINE		(LINES-1)
#define TOPMOST_PROMPTAREA_LINE INSTRUCT_LINE

/*
 * 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	*/

/*
 * 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.			*/
    char *inpval;	/* Pointer to the buffer to hold the value	*/
			/*   entered by the user.			*/
    char *expval;	/* Pointer to the expanded header value to	*/
			/*   display.  If no special expansions are	*/
			/*   required then this will point to "inpval".	*/
    int (*hdrproc)();	/* Pointer to a procedure which verifies the	*/
			/*   user data entry, and if required converts	*/
			/*   the "inpval" value to "expval" value.  If	*/
			/*   no verification or expansion is needed	*/
			/*   then this will be NULL.			*/
};

/*
 * These are all defined in the mailmsg file.
 */
extern char subject[SLEN], in_reply_to[SLEN], expires[SLEN],
            action[SLEN], priority[SLEN], reply_to[SLEN], to[VERY_LONG_STRING],
	    cc[VERY_LONG_STRING], expanded_to[VERY_LONG_STRING],
	    expanded_reply_to[LONG_STRING],
	    expanded_cc[VERY_LONG_STRING], user_defined_header[SLEN],
	    bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING],
	    precedence[SLEN], expires_days[SLEN];

/*
 * Local procedures.
 */
static void hdrmenu_clear_promptarea();
static int hdrmenu_get();
static void hdrmenu_put();
static int hdrproc_addr();
static int hdrproc_expires();
static int hdrproc_precedence();
static int hdrproc_userhdr();
static int domainize_submenu();
static void domainize();
static void domainize_addr();

/*
 * Definition of all the header editing menu fields.
 */
struct hdr_menu_item hmenu_item_list[] = {
    { 't', "T)o",		 2, HF_DISP_3ROW|HF_APPENDENTRY,
					to, expanded_to, hdrproc_addr },
    { 'c', "C)c",		 5, HF_DISP_3ROW|HF_APPENDENTRY,
					cc, expanded_cc, hdrproc_addr },
    { 'b', "B)cc",		 8, HF_DISP_2ROW|HF_APPENDENTRY,
					bcc, expanded_bcc, hdrproc_addr },
    { 's', "S)ubject",		10, HF_DISP_2ROW, subject, subject, NULL },
    { 'r', "R)eply-to",		12, HF_DISP_1ROW, reply_to, expanded_reply_to,
					hdrproc_addr },
    { 'a', "A)ction",		13, HF_DISP_LEFT, action, action, NULL },
    { 'e', "E)xpires",		13, HF_DISP_RIGHT|HF_PROMPT_EXP, expires_days,
					expires, hdrproc_expires },
    { 'p', "P)riority",		14, HF_DISP_LEFT, priority, priority, NULL },
    { 'n', "Precede(n)ce",	14, HF_DISP_RIGHT, precedence,
					precedence, hdrproc_precedence },
    { 'i', "I)n-reply-to",	15, HF_DISP_2ROW,
					in_reply_to, in_reply_to, NULL },
    { 'u', NULL,		17, HF_DISP_1ROW|HF_PROMPT_USR,
					user_defined_header,
					user_defined_header, hdrproc_userhdr },
    { -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])


edit_headers()
{
    int c, i, do_redraw;
    struct hdr_menu_item *h;
    
    /* expand out all of the header values */
    /* menu displays expanded values, user edits unexpended versions */
    for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
	if (h->hdrproc != NULL)
	    (*h->hdrproc)(h);
    }

    clearerr(stdin);
    do_redraw = TRUE;
    while (TRUE) {	/* forever */

	/* redraw the entire display if required */
	if (do_redraw) {
	    ClearScreen();
	    Centerline(0, catgets(elm_msg_cat, ElmSet,
		ElmHdrmenuScreenTitle, "Message Header Edit Screen"));
	    for (h = hmenu_item_list ; h->menucmd > 0 ; ++h)
		hdrmenu_put(h, TRUE);
	    do_redraw = FALSE;
	}

	/* display the instructions */
#ifdef ALLOW_SUBSHELL
	Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
	    ElmHdrmenuInstruct,
	    "Choose header, u)ser defined header, d)omainize, !)shell, or <return>."));
#else
	Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
	    ElmHdrmenuInstructNoShell,
	    "Choose header, u)ser defined header, d)omainize, or <return>."));
#endif

	/* prompt for command */
	PutLine0(INPUT_LINE, 0, catgets(elm_msg_cat, ElmSet,
	    ElmHdrmenuPrompt, "Choice: "));
	c = getchar();
	if (isupper(c))
	    c = tolower(c);
	hdrmenu_clear_promptarea();

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

	case EOF:
	case RETURN:
	case LINE_FEED:
	case 'q':
	    return 0;

	case 'd':
	    if (domainize_submenu() != 0)
		return 0;
	    break;

#ifdef ALLOW_SUBSHELL
	case '!':
	    if (subshell())
		do_redraw = TRUE;
	    break;
#endif

	case ctrl('L'):
	    do_redraw = TRUE;
	    break;
    
	default:
	    for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
		if (h->menucmd == c) {
		    if (hdrmenu_get(h) != 0)
			return 0;
		    hdrmenu_put(h, FALSE);
		    break;
		}
	    }
	    if (h->menucmd <= 0) {
		Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
		    ElmHdrmenuBadChoice, "No such header!"));
		Writechar('\007');
	    }
	    break;

	}

    }

    /*NOTREACHED*/
}


/*
 * Erase instructions, user input, left-over errors, etc.
 * This should be run after every user input command in this module.
 */
static void hdrmenu_clear_promptarea()
{
    clear_error();
    MoveCursor(TOPMOST_PROMPTAREA_LINE, 0);
    CleartoEOS();
}


/*
 * Prompt the user for a header value, and do any required post-processing.
 */
static int hdrmenu_get(h)
struct hdr_menu_item *h;
{
    char *s;
    int plen, ret, do_append;

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

    /* display a prompt */
    plen = 0;
    if (h->hdrname != NULL) {
	MoveCursor(INPUT_LINE, 0);
	for (s = h->hdrname ; *s != '\0' ; ++s) {
	    if (*s != '(' && *s != ')') {
		Writechar(*s);
		++plen;
	    }
	}
	Writechar(':');
	Writechar(' ');
	plen += 2;
    }

    /* get input from the user */
    do_append = ((h->flags & HF_APPENDENTRY) != 0);
    ret = optionally_enter(h->inpval, INPUT_LINE, plen, do_append, FALSE);
    hdrmenu_clear_promptarea();

    /* bail out on error */
    if (ret < 0)
	return -1;

    /* see if there is some processing required on this value */
    if (h->hdrproc != NULL)
	(void) (*h->hdrproc)(h);

    return 0;
}


/*
 * Dispay a header and its value in the appropriate field.
 */
static void hdrmenu_put(h, already_clear)
struct hdr_menu_item *h;
int already_clear;
{
    char    *p;
    int     start_row, max_row, start_col, max_col, row, col;

    /* 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);
    for (p = h->expval ; *p != '\0' && row <= max_row ; ++p) {
	if (row == max_row && col == max_col-4 && strlen(p) > 4)
	    p = " ...";		/* neat hack alert */
	Writechar(*p);
	if (!isprint(*p) || ++col > max_col)
	    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);
	}

    }

}


/*
 * Process the to, cc, and bcc headers.  The value entered by the
 * user is expanded.  A successful status is always returned.
 */
static int hdrproc_addr(h)
struct hdr_menu_item *h;
{
    (void) build_address(strip_commas(h->inpval), h->expval);
    return 0;
}


/*
 * 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)
struct hdr_menu_item *h;
{
    int days;

    /* initialize expanded date spec to empty */
    h->expval[0] = '\0';

    /* blank is ok */
    if (h->inpval[0] == '\0')
	return 0;

    /* verify the number of days is valid and in range */
    days = atoi(h->inpval);
    if (days < 1) {
	Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
	    ElmHdrmenuExpiresNotNumber,
	    "Expiration must be specified as a number of days."));
	return -1;
    }
    if (days > 8*7) {
	Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
	    ElmHdrmenuExpiresOutOfRange,
	    "Expiration date must be within eight weeks of today."));
	return -1;
    }

    /* convert number of days to a date */
    days_ahead(days, h->expval);
    return 0;
}


/*
 * 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)
struct hdr_menu_item *h;
{
    char buf[SLEN];	/* assumes sizeof(allowed_precedences) <= SLEN */
    char *bp, *prec, *prio;

    /* empty is ok */
    if (h->inpval[0] == '\0')
	return 0;

    /* if there are no restrictions on precedence then anything is ok */
    if (allowed_precedences[0] == '\0')
	return 0;

    /* the "allowed_precedences[]" format is: */
    /*   precedence[:priority-value] precedence[:priority-value] ... */
    bp = strcpy(buf, allowed_precedences);
    while ((prec = strtok(bp, " \t\n")) != NULL) {
	bp = NULL;
	if ((prio = index(prec, ':')) != NULL)
	    *prio++ = '\0';
	if (istrcmp(prec, h->inpval) == 0)
	    break;
    }

    /* see if we reached the end of the list without a match */
    if (prec == NULL) {
	Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
	    ElmHdrmenuPrecedenceBadValue,
	    "Unknown precedence value specified."));
	h->inpval[0] = '\0';
	return -1;
    }

    /* see if this precedence has an associated priority */
    if (prio != NULL && hmenu_priority.inpval[0] == '\0') {
	(void) strcpy(hmenu_priority.inpval, prio);
	hdrmenu_put(&hmenu_priority, FALSE);
    }

    return 0;
}


/*
 * 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)
struct hdr_menu_item *h;
{
    char *s;

    /* empty is ok */
    if (h->inpval[0] == '\0')
	return 0;

    /* make sure the header name doesn't begin with some strange character */
    if (!isalnum(h->inpval[0])) {
	Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
	    ElmHdrmenuUserdefNotAlnum,
	    "The user-defined header must begin with a letter or number."));
	h->inpval[0] = '\0';
	return -1;
    }

    /* locate the end of the header name */
    for (s = h->inpval ; *s != ':' && isprint(*s) && !isspace(*s) ; ++s)
	;

    /* there needs to be a colon at the end of the header name */
    if (*s != ':') {
	Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
	    ElmHdrmenuUserdefMissingColon,
	    "The user-defined header must have a colon after the field name."));
	h->inpval[0] = '\0';
	return -1;
    }

    return 0;
}


/*
 * Prompt the user to domainize a header.
 */
static int domainize_submenu()
{
    int c;
    struct hdr_menu_item *h;

    Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
	ElmHdrmenuDomInstruct,
	"Select header to domainize:  T)o, C)c, B)cc, or <return>."));
    PutLine0(INPUT_LINE, 0, catgets(elm_msg_cat, ElmSet,
	ElmHdrmenuDomPrompt, "Domainize choice: "));

    for (;;) {

	c = getchar();

	switch ((int)(isupper(c) ? tolower(c) : c)) {
	case 't':
	    h = &hmenu_to;
	    break;
	case 'c':
	    h = &hmenu_cc;
	    break;
	case 'b':
	    h = &hmenu_bcc;
	    break;
	case '\r':
	case '\n':
	case EOF:
	    h = NULL;
	    break;
	default:
	    Writechar('\007');
	    continue;
	}

	if (h != NULL) {
	    domainize(h->expval);
	    hdrmenu_put(h, FALSE);
	}

	hdrmenu_clear_promptarea();
	return (c == EOF ? -1 : 0);

    }

    /*NOTREACHED*/
}



static void domainize(addresses)
char *addresses;
{
	/*** Convert the given addresses from bang paths to domain format.
	     This policy amounts to Rabid Rerouting.  However, since it's
	     under the sender's control, I don't mind. ***/

	char buffer[VERY_LONG_STRING];
	char *a, *d;
	int how_many;

	strcpy(buffer, addresses);
	a = buffer;
	d = addresses;
	how_many = 0;

	for (;;) {
	  while (*a == ' ' || *a == ',')
	    ++a;
	  if (*a == '\0')
	    break;

	  if (*a == '(' || *a == '"') {
	    int endch;

	    if (d != addresses)
	      *d++ = ' ';
	    endch = (*a == '(') ? ')' : '"';
	    while (*a && *a != endch) {
	      if (*a == '\\' && *(a + 1))
		*d++ = *a++;
	      *d++ = *a++;
	    }
	    if (*a)
	      *d++ = *a++;
	  }
	  else {
	    char *addr;

	    if (how_many) {
	      *d++ = ',';
	      *d++ = ' ';
	    }
	    ++how_many;

	    if (*a == '<') {
	      *d++ = *a++;
	      addr = a;
	      while (*a && *a != '>') {
		if (*a == '\\' && *(a + 1))
		  ++a;
		++a;
	      }
	      if (*a)
		*a++ = '\0';
	      domainize_addr(addr, d);
	      d += strlen(d);
	      *d++ = '>';
	    }
	    else {
	      addr = a;
	      while (*a && *a != ' ' && *a != ',')
		++a;
	      if (*a)
		*a++ = '\0';
	      domainize_addr(addr, d);
	      d += strlen(d);
	    }
	  }
	}

	*d = '\0';
}

static void domainize_addr(src, dest)
char *src, *dest;
{
	/*** Convert one address to domain form. ***/

	char *locpart, *host;

	if (index(src, '@') != NULL
	 || (locpart = rindex(src, '!')) == NULL) {
	  strcpy(dest, src);
	  return;
	}

	*locpart++ = '\0';

	if ((host = rindex(src, '!')) != NULL)
	  ++host;
	else
	  host = src;
	sprintf(dest, "%s@%s", locpart, host);
	if (!index(host, '.'))
	  strcat(dest, ".uucp");

	*--locpart = '!';
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.