1
2 static char rcsid[] = "@(#)$Id: hdrconfg.c,v 1.5 1996/05/09 15:51:19 wfp5p Exp $";
3
4 /*******************************************************************************
5 * The Elm Mail System - $Revision: 1.5 $ $State: Exp $
6 *
7 * Copyright (c) 1988-1995 USENET Community Trust
8 * Copyright (c) 1986,1987 Dave Taylor
9 *******************************************************************************
10 * Bug reports, patches, comments, suggestions should be sent to:
11 *
12 * Bill Pemberton, Elm Coordinator
13 * flash@virginia.edu
14 *
15 *******************************************************************************
16 * $Log: hdrconfg.c,v $
17 * Revision 1.5 1996/05/09 15:51:19 wfp5p
18 * Alpha 10
19 *
20 * Revision 1.4 1996/03/14 17:29:36 wfp5p
21 * Alpha 9
22 *
23 * Revision 1.3 1995/09/29 17:42:12 wfp5p
24 * Alpha 8 (Chip's big changes)
25 *
26 * Revision 1.2 1995/09/11 15:19:08 wfp5p
27 * Alpha 7
28 *
29 * Revision 1.1.1.1 1995/04/19 20:38:36 wfp5p
30 * Initial import of elm 2.4 PL0 as base for elm 2.5.
31 *
32 ******************************************************************************/
33
34 /** This file contains the routines necessary to be able to modify
35 the mail headers of messages on the way off the machine. The
36 headers currently supported for modification are:
37
38 Subject:
39 To:
40 Cc:
41 Bcc:
42 Reply-To:
43 Priority:
44 Precedence:
45 In-Reply-To:
46 Action:
47
48 <user defined>
49 **/
50
51 #include "elm_defs.h"
52 #include "elm_globals.h"
53 #include "sndhdrs.h"
54 #include "s_elm.h"
55
56 /*
57 * Placement of prompts and messages at the bottom of the screen.
58 */
59 #define INSTRUCT_LINE (LINES-4)
60 #define INPUT_LINE (LINES-2)
61 #define ERROR_LINE (LINES-1)
62 #define TOPMOST_PROMPTAREA_LINE INSTRUCT_LINE
63
64 /*
65 * Option flags for the fields in a (struct hdr_menu_item).
66 */
67 #define HF_DISP_1ROW 0001 /* field is displayed on one line */
68 #define HF_DISP_2ROW 0002 /* field display spans two lines */
69 #define HF_DISP_3ROW 0003 /* field display spans three lines */
70 #define HF_DISP_LEFT 0004 /* field occupies left half of a line */
71 #define HF_DISP_RIGHT 0005 /* field occupies right half of a line */
72 #define HF_DISP_MASK 0007 /* -- mask to pull out display option */
73 #define HF_PROMPT_USR 0020 /* prompt for user defined hdr entry */
74 #define HF_PROMPT_MASK 0070 /* -- mask to pull out prompt option */
75 #define HF_APPENDENTRY 0100 /* append user entry to existing value */
76
77 /*
78 * Structure to describe a header which can be edited in this menu.
79 */
80 struct hdr_menu_item {
81 int menucmd; /* The single keystroke (lower-case letter) the */
82 /* user strikes to edit this menu item. */
83 char *hdrname; /* Header name to display in the menu. Parens */
84 /* should be used to bracket the "menucmd" */
85 /* char in the name, e.g. "S)ubject". This */
86 /* will be NULL for the user-defined header. */
87 int lineno; /* Screen line at which the field is displayed. */
88 int flags; /* Various flags which effect the display and */
89 /* user entry of this item. */
90 char *inpval; /* Pointer to the buffer to hold the value */
91 /* entered by the user. */
92 char *expval; /* Pointer to the expanded header value to */
93 /* display. If no special expansions are */
94 /* required then this will point to "inpval". */
95 int (*hdrproc)(); /* Pointer to a procedure which verifies the */
96 /* user data entry, and if required converts */
97 /* the "inpval" value to "expval" value. If */
98 /* no verification or expansion is needed */
99 /* then this will be NULL. */
100 };
101
102 /*
103 * Local procedures.
104 */
105 static void hdrmenu_clear_promptarea();
106 static int hdrmenu_get();
107 static void hdrmenu_put();
108 static int hdrproc_addr();
109 static int hdrproc_precedence();
110 static int hdrproc_userhdr();
111 static void domainize_submenu();
112 static void domainize();
113 static void domainize_addr();
114
115 /*
116 * Buffer to hold the message header during editing.
117 *
118 * I'm sorry to report this buffer is not for clever reasons. (Although
119 * it could be used as the basis of an "undo" capability.) This buffer
120 * exists because this routine originally was written to work on global
121 * strings. Using this buffer just made it easy to allow this routine
122 * to work now that sending header information now is contained in
123 * a SEND_HEADER datatype.
124 */
125 static SEND_HEADER H;
126
127 /*
128 * Definition of all the header editing menu fields.
129 */
130 struct hdr_menu_item hmenu_item_list[] = {
131 { 't', "T)o", 2, HF_DISP_3ROW|HF_APPENDENTRY,
132 H.to, H.expanded_to, hdrproc_addr },
133 { 'c', "C)c", 5, HF_DISP_3ROW|HF_APPENDENTRY,
134 H.cc, H.expanded_cc, hdrproc_addr },
135 { 'b', "B)cc", 8, HF_DISP_2ROW|HF_APPENDENTRY,
136 H.bcc, H.expanded_bcc, hdrproc_addr },
137 { 's', "S)ubject", 10, HF_DISP_2ROW, H.subject, H.subject, NULL },
138 { 'r', "R)eply-to", 12, HF_DISP_1ROW, H.reply_to,
139 H.expanded_reply_to, hdrproc_addr },
140 { 'a', "A)ction", 13, HF_DISP_LEFT, H.action, H.action, NULL },
141 { 'p', "P)riority", 14, HF_DISP_LEFT,
142 H.priority, H.priority, NULL },
143 { 'n', "Precede(n)ce", 14, HF_DISP_RIGHT, H.precedence,
144 H.precedence, hdrproc_precedence },
145 { 'i', "I)n-reply-to", 15, HF_DISP_2ROW,
146 H.in_reply_to, H.in_reply_to, NULL },
147 { 'u', NULL, 17, HF_DISP_1ROW|HF_PROMPT_USR,
148 H.user_defined_header,
149 H.user_defined_header,
150 hdrproc_userhdr },
151 { -1, NULL, -1, -1, NULL, NULL, NULL },
152 };
153
154 /*
155 * Selection of individual fields. The indices *must* correspond
156 * to the above "hmenu_item_list[]" list.
157 */
158 #define hmenu_to (hmenu_item_list[0])
159 #define hmenu_cc (hmenu_item_list[1])
160 #define hmenu_bcc (hmenu_item_list[2])
161 #define hmenu_subject (hmenu_item_list[3])
162 #define hmenu_replyto (hmenu_item_list[4])
163 #define hmenu_action (hmenu_item_list[5])
164 #define hmenu_priority (hmenu_item_list[7])
165 #define hmenu_precedence (hmenu_item_list[8])
166 #define hmenu_inreplyto (hmenu_item_list[9])
167 #define hmenu_userdef (hmenu_item_list[10])
168
169
edit_headers(shdr)170 PUBLIC void edit_headers(shdr)
171 SEND_HEADER *shdr;
172 {
173 int c, i, do_redraw;
174 struct hdr_menu_item *h;
175
176 /* copy message header into private buffer */
177 bcopy((char *)shdr, (char *)&H, sizeof(SEND_HEADER));
178
179 /* expand out all of the header values */
180 /* menu displays expanded values, user edits unexpended versions */
181 for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
182 if (h->hdrproc != NULL)
183 (*h->hdrproc)(h);
184 }
185
186 clearerr(stdin);
187 do_redraw = TRUE;
188 while (TRUE) { /* forever */
189
190 /* redraw the entire display if required */
191 if (do_redraw) {
192 ClearScreen();
193 CenterLine(0, catgets(elm_msg_cat, ElmSet,
194 ElmHdrmenuScreenTitle, "Message Header Edit Screen"));
195 for (h = hmenu_item_list ; h->menucmd > 0 ; ++h)
196 hdrmenu_put(h, TRUE);
197 do_redraw = FALSE;
198 }
199
200 /* display the instructions */
201 #ifdef ALLOW_SUBSHELL
202 CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
203 ElmHdrmenuInstruct,
204 "Choose header, u)ser defined header, d)omainize, !)shell, or <return>."));
205 #else
206 CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
207 ElmHdrmenuInstructNoShell,
208 "Choose header, u)ser defined header, d)omainize, or <return>."));
209 #endif
210
211 /* prompt for command */
212 PutLine0(INPUT_LINE, 0, catgets(elm_msg_cat, ElmSet,
213 ElmHdrmenuPrompt, "Choice: "));
214 if ((c = GetKey(0)) == KEY_REDRAW) {
215 do_redraw = TRUE;
216 continue;
217 }
218 hdrmenu_clear_promptarea();
219
220 /* execute the command */
221 switch (c) {
222
223 case RETURN:
224 case LINE_FEED:
225 case 'q':
226 goto done;
227
228 case 'd':
229 domainize_submenu();
230 break;
231
232 #ifdef ALLOW_SUBSHELL
233 case '!':
234 if (subshell())
235 do_redraw = TRUE;
236 break;
237 #endif
238
239 case ctrl('L'):
240 do_redraw = TRUE;
241 break;
242
243 default:
244 /* Since headers are displayed with parenthesized letter
245 * capitalized, allow user to hit the capital letter, too.
246 * Shhh--don't tell anyone about Precede(n)ce. */
247 c = tolower(c);
248
249 for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
250 if (h->menucmd == c) {
251 if (hdrmenu_get(h) != 0)
252 goto done;
253 hdrmenu_put(h, FALSE);
254 break;
255 }
256 }
257 if (h->menucmd <= 0) {
258 CenterLine(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
259 ElmHdrmenuBadChoice, "No such header!"));
260 Beep();
261 }
262 break;
263
264 }
265
266 }
267
268 done:
269 /* copy private buffer out to message header */
270 bcopy((char *)&H, (char *)shdr, sizeof(SEND_HEADER));
271 }
272
273
274 /*
275 * Erase instructions, user input, left-over errors, etc.
276 * This should be run after every user input command in this module.
277 */
hdrmenu_clear_promptarea()278 static void hdrmenu_clear_promptarea()
279 {
280 clear_error();
281 MoveCursor(TOPMOST_PROMPTAREA_LINE, 0);
282 CleartoEOS();
283 }
284
285
286 /*
287 * Prompt the user for a header value, and do any required post-processing.
288 */
hdrmenu_get(h)289 static int hdrmenu_get(h)
290 struct hdr_menu_item *h;
291 {
292 char inpbuf[SLEN], *s;
293 int ret, emode;
294
295 /* display the instructions */
296 switch (h->flags & HF_PROMPT_MASK) {
297 case HF_PROMPT_USR:
298 CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
299 ElmHdrmenuGetUserdefInstruct,
300 "Enter in the format \"HeaderName: HeaderValue\"."));
301 break;
302 default:
303 CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
304 ElmHdrmenuGetInstruct, "Enter value for the header."));
305 break;
306 }
307
308 /* display a prompt */
309 MoveCursor(INPUT_LINE, 0);
310 if (h->hdrname != NULL) {
311 for (s = h->hdrname ; *s != '\0' ; ++s) {
312 if (*s != '(' && *s != ')')
313 WriteChar(*s);
314 }
315 WriteChar(':');
316 WriteChar(' ');
317 }
318
319 /* get input from the user */
320 (void) strfcpy(inpbuf, h->inpval, sizeof(inpbuf));
321 emode = ((h->flags & HF_APPENDENTRY) ? ESTR_UPDATE : ESTR_REPLACE);
322 if (enter_string(inpbuf, sizeof(inpbuf), -1, -1, emode) == 0)
323 (void) strcpy(h->inpval, inpbuf);
324 hdrmenu_clear_promptarea();
325
326 /* see if there is some processing required on this value */
327 if (h->hdrproc != NULL)
328 (void) (*h->hdrproc)(h);
329
330 return 0;
331 }
332
333
334 /*
335 * Dispay a header and its value in the appropriate field.
336 */
hdrmenu_put(h,already_clear)337 static void hdrmenu_put(h, already_clear)
338 struct hdr_menu_item *h;
339 int already_clear;
340 {
341 char *p;
342 int start_row, max_row, start_col, max_col, row, col;
343
344 /* figure out the dimensions of the field */
345 switch (h->flags & HF_DISP_MASK) {
346 case HF_DISP_LEFT:
347 start_row = h->lineno; max_row = h->lineno;
348 start_col = 0; max_col = COLS/2 - 2;
349 break;
350 case HF_DISP_RIGHT:
351 start_row = h->lineno; max_row = h->lineno;
352 start_col = COLS/2 + 1; max_col = COLS-1;
353 break;
354 case HF_DISP_3ROW:
355 start_row = h->lineno; max_row = h->lineno+2;
356 start_col = 0; max_col = COLS-1;
357 break;
358 case HF_DISP_2ROW:
359 start_row = h->lineno; max_row = h->lineno+1;
360 start_col = 0; max_col = COLS-1;
361 break;
362 default:
363 start_row = h->lineno; max_row = h->lineno;
364 start_col = 0; max_col = COLS-1;
365 break;
366 }
367
368 /* display the header name */
369 MoveCursor(start_row, start_col);
370 if (h->hdrname != NULL) {
371 PutLine0(-1, -1, h->hdrname);
372 WriteChar(':');
373 WriteChar(' ');
374 }
375
376 /* display the header value */
377 GetCursorPos(&row, &col);
378 for (p = h->expval ; *p != '\0' && row <= max_row ; ++p) {
379 if (row == max_row && col == max_col-4 && strlen(p) > 4)
380 p = " ..."; /* neat hack alert */
381 WriteChar(*p);
382 if (!isprint(*p) || ++col > max_col)
383 GetCursorPos(&row, &col);
384 }
385
386 /* save some drawing if we know the screen is already empty */
387 if (!already_clear) {
388
389 /* clear out remaining space in this line of the field */
390 if (max_col == COLS-1) {
391 /* people on slow terminals might appreciate doing it this way */
392 CleartoEOLN();
393 } else {
394 while (col++ <= max_col)
395 WriteChar(' ');
396 }
397
398 /* for multi-line fields, clear out any unused lines */
399 /* this assumes that multi-line fields span the entire screen width */
400 while (++row <= max_row) {
401 /* grrrrrr -- this is a multi-statement macro */
402 ClearLine(row);
403 }
404
405 }
406
407 }
408
409
410 /*
411 * Process the to, cc, and bcc headers. The value entered by the
412 * user is expanded. A successful status is always returned.
413 */
hdrproc_addr(h)414 static int hdrproc_addr(h)
415 struct hdr_menu_item *h;
416 {
417 (void) build_address(strip_commas(h->inpval), h->expval);
418 return 0;
419 }
420
421
422
423 /*
424 * Process the precedence header. The value entered by the user is
425 * checked against the list of allowed precedences, if one exists. If
426 * the precedence has a priority assigned to it, then an empty priority
427 * field will be filled in with that value. If an error occurs a message
428 * is printed, the precedence value is cleared out, and a -1 is returned.
429 */
hdrproc_precedence(h)430 static int hdrproc_precedence(h)
431 struct hdr_menu_item *h;
432 {
433 char buf[SLEN]; /* assumes sizeof(allowed_precedences) <= SLEN */
434 char *bp, *prec, *prio;
435
436 /* empty is ok */
437 if (h->inpval[0] == '\0')
438 return 0;
439
440 /* if there are no restrictions on precedence then anything is ok */
441 if (allowed_precedences[0] == '\0')
442 return 0;
443
444 /* the "allowed_precedences[]" format is: */
445 /* precedence[:priority-value] precedence[:priority-value] ... */
446 bp = strcpy(buf, allowed_precedences);
447 while ((prec = strtok(bp, " \t\n")) != NULL) {
448 bp = NULL;
449 if ((prio = index(prec, ':')) != NULL)
450 *prio++ = '\0';
451 if (istrcmp(prec, h->inpval) == 0)
452 break;
453 }
454
455 /* see if we reached the end of the list without a match */
456 if (prec == NULL) {
457 CenterLine(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
458 ElmHdrmenuPrecedenceBadValue,
459 "Unknown precedence value specified."));
460 h->inpval[0] = '\0';
461 return -1;
462 }
463
464 /* see if this precedence has an associated priority */
465 if (prio != NULL && hmenu_priority.inpval[0] == '\0') {
466 (void) strcpy(hmenu_priority.inpval, prio);
467 hdrmenu_put(&hmenu_priority, FALSE);
468 }
469
470 return 0;
471 }
472
473
474 /*
475 * Process the user-defined header. The value entered by the user is
476 * verified for proper format. If an error occurs a message is printed,
477 * the expanded value is cleared out, and a -1 is returned.
478 */
hdrproc_userhdr(h)479 static int hdrproc_userhdr(h)
480 struct hdr_menu_item *h;
481 {
482 char *s;
483
484 /* empty is ok */
485 if (h->inpval[0] == '\0')
486 return 0;
487
488 /* make sure the header name doesn't begin with some strange character */
489 if (!isalnum(h->inpval[0])) {
490 CenterLine(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
491 ElmHdrmenuUserdefNotAlnum,
492 "The user-defined header must begin with a letter or number."));
493 h->inpval[0] = '\0';
494 return -1;
495 }
496
497 /* locate the end of the header name */
498 for (s = h->inpval ; *s != ':' && isprint(*s) && !isspace(*s) ; ++s)
499 ;
500
501 /* there needs to be a colon at the end of the header name */
502 if (*s != ':') {
503 CenterLine(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
504 ElmHdrmenuUserdefMissingColon,
505 "The user-defined header must have a colon after the field name."));
506 h->inpval[0] = '\0';
507 return -1;
508 }
509
510 return 0;
511 }
512
513
514 /*
515 * Prompt the user to domainize a header.
516 */
domainize_submenu()517 static void domainize_submenu()
518 {
519 int c;
520 struct hdr_menu_item *h;
521
522 CenterLine(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
523 ElmHdrmenuDomInstruct,
524 "Select header to domainize: T)o, C)c, B)cc, or <return>."));
525 PutLine0(INPUT_LINE, 0, catgets(elm_msg_cat, ElmSet,
526 ElmHdrmenuDomPrompt, "Domainize choice: "));
527
528 for (;;) {
529
530 c = ReadCh();
531
532 switch (tolower(c)) {
533 case 't':
534 h = &hmenu_to;
535 break;
536 case 'c':
537 h = &hmenu_cc;
538 break;
539 case 'b':
540 h = &hmenu_bcc;
541 break;
542 case '\r':
543 case '\n':
544 h = NULL;
545 break;
546 default:
547 Beep();
548 continue;
549 }
550
551 if (h != NULL) {
552 domainize(h->expval);
553 hdrmenu_put(h, FALSE);
554 }
555 hdrmenu_clear_promptarea();
556 break;
557
558 }
559
560 }
561
562
563
domainize(addresses)564 static void domainize(addresses)
565 char *addresses;
566 {
567 /*** Convert the given addresses from bang paths to domain format.
568 This policy amounts to Rabid Rerouting. However, since it's
569 under the sender's control, I don't mind. ***/
570
571 char buffer[VERY_LONG_STRING];
572 char *a, *d;
573 int how_many;
574
575 strcpy(buffer, addresses);
576 a = buffer;
577 d = addresses;
578 how_many = 0;
579
580 for (;;) {
581 while (*a == ' ' || *a == ',')
582 ++a;
583 if (*a == '\0')
584 break;
585
586 if (*a == '(' || *a == '"') {
587 int endch;
588
589 if (d != addresses)
590 *d++ = ' ';
591 endch = (*a == '(') ? ')' : '"';
592 while (*a && *a != endch) {
593 if (*a == '\\' && *(a + 1))
594 *d++ = *a++;
595 *d++ = *a++;
596 }
597 if (*a)
598 *d++ = *a++;
599 }
600 else {
601 char *addr;
602
603 if (how_many) {
604 *d++ = ',';
605 *d++ = ' ';
606 }
607 ++how_many;
608
609 if (*a == '<') {
610 *d++ = *a++;
611 addr = a;
612 while (*a && *a != '>') {
613 if (*a == '\\' && *(a + 1))
614 ++a;
615 ++a;
616 }
617 if (*a)
618 *a++ = '\0';
619 domainize_addr(addr, d);
620 d += strlen(d);
621 *d++ = '>';
622 }
623 else {
624 addr = a;
625 while (*a && *a != ' ' && *a != ',')
626 ++a;
627 if (*a)
628 *a++ = '\0';
629 domainize_addr(addr, d);
630 d += strlen(d);
631 }
632 }
633 }
634
635 *d = '\0';
636 }
637
domainize_addr(src,dest)638 static void domainize_addr(src, dest)
639 char *src, *dest;
640 {
641 /*** Convert one address to domain form. ***/
642
643 char *locpart, *host;
644
645 if (index(src, '@') != NULL
646 || (locpart = rindex(src, '!')) == NULL) {
647 strcpy(dest, src);
648 return;
649 }
650
651 *locpart++ = '\0';
652
653 if ((host = rindex(src, '!')) != NULL)
654 ++host;
655 else
656 host = src;
657 sprintf(dest, "%s@%s", locpart, host);
658 if (!index(host, '.'))
659 strcat(dest, ".uucp");
660
661 *--locpart = '!';
662 }
663
664
show_msg_headers(shdr,cmds)665 PUBLIC int show_msg_headers(shdr, cmds)
666 const SEND_HEADER *shdr;
667 const char *cmds;
668 {
669 int max_lineno, c, i;
670 struct hdr_menu_item *h;
671
672 max_lineno = 0;
673
674 /* copy message header into private buffer */
675 bcopy((char *)shdr, (char *)&H, sizeof(SEND_HEADER));
676
677 /* expand out all of the header values */
678 /* menu displays expanded values, user edits unexpended versions */
679 for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
680 if (h->hdrproc != NULL)
681 (*h->hdrproc)(h);
682 }
683
684 for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
685 if (index(cmds, h->menucmd) != NULL) {
686 hdrmenu_put(h, TRUE);
687 if (h->lineno > max_lineno)
688 max_lineno = h->lineno;
689 }
690 }
691
692 return max_lineno;
693 }
694
695
edit_header_char(shdr,c)696 PUBLIC int edit_header_char(shdr, c)
697 SEND_HEADER *shdr;
698 int c;
699 {
700 struct hdr_menu_item *h;
701
702 for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
703 if (h->menucmd == c) {
704 bcopy((char *)shdr, (char *)&H, sizeof(SEND_HEADER));
705 if (hdrmenu_get(h) != 0)
706 return FALSE;
707 hdrmenu_put(h, FALSE);
708 bcopy((char *)&H, (char *)shdr, sizeof(SEND_HEADER));
709 return TRUE;
710 }
711 }
712 return FALSE;
713 }
714