1 /*******************************************************************************
2  *  The Elm Mail System  -  $Revision: 1.6 $   $State: Exp $
3  *
4  * This file and all associated files and documentation:
5  *                      Copyright (c) 1988-1995 USENET Community Trust
6  *			Copyright (c) 1986,1987 Dave Taylor
7  *******************************************************************************
8  * Bug reports, patches, comments, suggestions should be sent to:
9  *
10  *      Bill Pemberton, Elm Coordinator
11  *      flash@virginia.edu
12  *
13  *******************************************************************************
14  * $Log: sndmsg.c,v $
15  * Revision 1.6  1996/10/28  16:58:11  wfp5p
16  * Beta 1
17  *
18  * Revision 1.5  1996/08/08  19:49:31  wfp5p
19  * Alpha 11
20  *
21  * Revision 1.4  1996/05/09  15:51:28  wfp5p
22  * Alpha 10
23  *
24  * Revision 1.3  1996/03/14  17:29:55  wfp5p
25  * Alpha 9
26  *
27  * Revision 1.2  1996/03/13  14:38:03  wfp5p
28  * Alpha 9 before Chip's big changes
29  *
30  * Revision 1.1  1995/09/29  17:42:31  wfp5p
31  * Alpha 8 (Chip's big changes)
32  *
33  *
34  ******************************************************************************/
35 
36 #include "elm_defs.h"
37 #include "elm_globals.h"
38 #include "mime.h"
39 #include "sndhdrs.h"
40 #include "sndparts.h"
41 #include "s_elm.h"
42 #include <assert.h>
43 
44 extern char *bounce_off_remote();
45 extern char *strip_commas();
46 
47 static void display_subject P_((const char *));
48 static int get_subject P_((char *));
49 static int get_copies P_((char *, char *, char *, int));
50 static int verify_copy_msg P_((void));
51 static int recall_last_msg P_((const char *, int));
52 static int append_sig P_((FILE *, SEND_HEADER *));
53 static int verify_bounceback P_((void));
54 static void remove_hostbang P_((char *));
55 static int verify_transmission P_((const char *, SEND_HEADER *, SEND_MULTIPART **, int *, char *));
56 
57 
send_message(given_to,given_cc,given_subject,mssgtype)58 PUBLIC int send_message(given_to, given_cc, given_subject, mssgtype)
59 const char *given_to, *given_cc, *given_subject;
60 int mssgtype;
61 {
62     SEND_HEADER *shdr;		/* headers for this message		*/
63     SEND_BODYPART *mssg_parts;	/* contents of this message		*/
64     SEND_MULTIPART *attachments;/* add'l user-specified attachments	*/
65     SEND_MULTIPART *mp;         /* used to loop thru user attachments   */
66     char fname_mssgbody[SLEN];	/* message composition file		*/
67     char *fname_fullmssg; 	/* complete message to transmit		*/
68     char fname_savecopy[SLEN];	/* selected save copy folder		*/
69     FILE *fp_mssgbody;		/* file stream for fname_mssgbody[]	*/
70     FILE *fp_fullmssg;		/* file stream for fname_fullmssg[]	*/
71     int need_redraw;		/* have we scribbled on the scren?	*/
72     int copy_msg;		/* copy mssg from folder into body?	*/
73     int edit_msg;		/* edit the message body?		*/
74     int form;			/* is the message a form?		*/
75     int body_has_text;		/* any text been placed in mssg yet?	*/
76     int want_signature;		/* should .signature file be added?	*/
77     int send_attempts;		/* count of tries to send this message	*/
78     int rc;			/* final return status			*/
79     char tmpbuf[SLEN];
80     char bigbuf[VERY_LONG_STRING];
81     char *s;
82     int i;
83 
84     /* is there a cancelled message laying around from last time? */
85     static int cancelled_msg = FALSE;
86     static int saved_want_sig = FALSE;
87 
88     /* initialize */
89     shdr = sndhdr_new();
90     mssg_parts = NULL;
91     attachments = NULL;
92     fp_mssgbody = NULL;
93     fp_fullmssg = NULL;
94     fname_fullmssg = NULL;
95     need_redraw = FALSE;
96     body_has_text = FALSE;
97     want_signature = TRUE;
98     send_attempts = 0;
99     rc = -1;
100 
101     assert(mssgtype == SM_ORIGINAL || OPMODE_IS_READMODE(opmode));
102     switch (mssgtype) {
103     case SM_ORIGINAL:
104 	copy_msg = NO;
105 	edit_msg = TRUE;
106 	form = allow_forms;
107 	break;
108     case SM_REPLY:
109 	copy_msg = verify_copy_msg();
110 	edit_msg = TRUE;
111 	form = NO;
112 	generate_in_reply_to(shdr, curr_folder.curr_mssg-1);
113 	break;
114     case SM_FORWARD:
115 	copy_msg = YES;
116 	edit_msg = TRUE;
117 	form = NO;
118 	break;
119     case SM_FWDUNQUOTE:
120 	copy_msg = YES;
121 	edit_msg = FALSE;
122 	form = NO;
123 	break;
124     case SM_FORMRESP:
125 	copy_msg = FORM;
126 	edit_msg = FALSE;
127 	form = NO;
128 	generate_in_reply_to(shdr, curr_folder.curr_mssg-1);
129 	break;
130     default:
131 	error1("INTERNAL ERROR - bad mssgtype code %d in send_message().",
132 		    mssgtype);
133 	return FALSE;
134     }
135 
136     /*
137      * The tri-level logic (YES, NO, FORM) of some of the parameters
138      * is driving me absolutely bonkers.
139      */
140 
141     /* load argument values into headers */
142     if (given_to && *given_to)
143 	strfcpy(shdr->to, given_to, sizeof(shdr->to));
144     if (given_cc && *given_cc)
145 	strfcpy(shdr->cc, given_cc, sizeof(shdr->cc));
146     if (shdr->cc[0] != '\0')
147 	build_address(strip_commas(shdr->cc), shdr->expanded_cc);
148     if (given_subject && *given_subject) {
149 	strfcpy(shdr->subject, given_subject, sizeof(shdr->subject));
150 	/* in interactive mode, display Subject: if provided */
151 	if (OPMODE_IS_INTERACTIVE(opmode))
152 	    display_subject(given_subject);
153     }
154 
155     /* get To: field */
156     if (!get_to(shdr->to, shdr->expanded_to, mssgtype))
157 	goto done;
158 
159     /* prompt for other header information */
160     if (OPMODE_IS_INTERACTIVE(opmode) && mssgtype != SM_FORMRESP) {
161 	display_to(shdr->expanded_to);
162 	if (!get_subject(shdr->subject))
163 	    goto done;
164 	if (prompt_for_cc && !get_copies(shdr->cc, shdr->expanded_to,
165 		    shdr->expanded_cc, copy_msg))
166 	    goto done;
167 	MoveCursor(LINES,0);	/* so you know you've hit <return> ! */
168     }
169 
170     dprint(3, (debugfile, "\nsend_msg() ready to mail...\n"));
171     dprint(3, (debugfile, "to=\"%s\" expanded_to=\"%s\"\n",
172 		shdr->to, shdr->expanded_to));
173     dprint(4, (debugfile, "subject=\"%s\"\n",
174 		shdr->subject));
175     dprint(5, (debugfile, "cc=\"%s\" expanded_cc=\"%s\"\n",
176 		shdr->cc, shdr->expanded_cc));
177     dprint(5, (debugfile, "bcc=\"%s\" expanded_bcc=\"%s\"\n",
178 		shdr->bcc, shdr->expanded_bcc));
179 
180     /* initialize default for saved copy */
181     if (!auto_cc)
182 	fname_savecopy[0] = '\0';	/* no default saved copy */
183     else if (!(save_by_name || save_by_alias))
184 	strcpy(fname_savecopy, "<");	/* save to sentmail */
185     else if (!force_name)
186 	strcpy(fname_savecopy, "=?");	/* conditional save by 'to' */
187     else
188 	strcpy(fname_savecopy, "=");	/* save by 'to' logname */
189 
190     /* generate name of file used to compose message */
191     sprintf(fname_mssgbody, "%s%s%d", temp_dir, temp_file, getpid());
192 
193     /* grab the completed form as the response */
194     if (copy_msg == FORM) {
195 	sprintf(tmpbuf, "%s%s%d", temp_dir, temp_form_file, getpid());
196 	dprint(4, (debugfile,
197 		"-- renaming existing file %s to file %s --\n",
198 		tmpbuf, fname_mssgbody));
199 	if (file_rename(tmpbuf, fname_mssgbody) < 0)
200 	    goto done;
201 	want_signature = FALSE;
202 	body_has_text = TRUE;
203 	goto message_is_prepared;
204     }
205 
206     /* if there is a cancelled message layaround, see if he wants to use that */
207     if (cancelled_msg && user_level > 0) {
208 	cancelled_msg = FALSE;
209 	if (recall_last_msg(fname_mssgbody, copy_msg)) {
210 	    want_signature = saved_want_sig;
211 	    body_has_text = TRUE;
212 	    goto message_is_prepared;
213 	}
214     }
215 
216     /* create the file to hold the message body */
217     if ((fp_mssgbody = file_open(fname_mssgbody, "w")) == NULL)
218 	goto done;
219 
220     /* copy the message from standard input */
221     if (opmode == OPMODE_SEND_BATCH) {
222 	if (file_copy(stdin, fp_mssgbody,
223 		    "standard input", fname_mssgbody) < 0)
224 	    goto done;
225 	body_has_text = TRUE;
226     }
227 
228     /* add any included file specified on the command line */
229     if (included_file != NULL) {
230 	FILE *fp;
231 	if ((fp = file_open(included_file, "r")) == NULL)
232 	    goto done;
233 	if (file_copy(fp, fp_mssgbody, included_file, fname_mssgbody) < 0) {
234 	    (void) fclose(fp);
235 	    goto done;
236 	}
237 	if (file_close(fp, fname_mssgbody) < 0)
238 	    goto done;
239 	body_has_text = TRUE;
240     }
241 
242     /* retrieve copy of desired message */
243     if (copy_msg == YES) {
244 	i = CM_ATTRIBUTION;
245 #ifdef MMDF
246 	i |= CM_MMDF_HEAD;
247 #endif
248 	if (edit_msg)
249 	    i |= CM_DECODE;
250 	if (mssgtype != SM_FWDUNQUOTE)
251 	    i |= CM_PREFIX;
252 	if (mssgtype == SM_FORWARD)
253             i |= CM_FORWARDING;
254         if (mssgtype == SM_REPLY && noheader)
255 	    i |= CM_REMOVE_HEADER;
256 	copy_message(fp_mssgbody, curr_folder.curr_mssg, i);
257 	body_has_text = TRUE;
258     }
259 
260 message_is_prepared:
261 
262     /* cleanup on retries */
263     if (send_attempts++ > 0) {
264 	if (!OPMODE_IS_INTERACTIVE(opmode))
265 	    goto done;
266 	if (fp_fullmssg != NULL) {
267 	    (void) fclose(fp_fullmssg);
268 	    fp_fullmssg = NULL;
269 	}
270 	if (fname_fullmssg != NULL) {
271 	    (void) unlink(fname_fullmssg);
272 	    (void) free((malloc_t)fname_fullmssg);
273 	    fname_fullmssg = NULL;
274 	}
275 	edit_msg = FALSE;
276     }
277 
278     /* add sig now unless we are using builtin editor */
279     if (want_signature &&
280 	    !streq(editor, "builtin") && !streq(editor, "none")) {
281 	if (fp_mssgbody == NULL) {
282 	    if ((fp_mssgbody = file_open(fname_mssgbody, "a")) == NULL)
283 		goto done;
284 	}
285 
286 	if (append_sig(fp_mssgbody, shdr))
287  	    body_has_text = TRUE;
288 	want_signature = FALSE;
289     }
290 
291     /* close out the body so it can be edited */
292     if (fp_mssgbody != NULL) {
293 	if (file_close(fp_mssgbody, fname_mssgbody) < 0)
294 	    goto done;
295 	fp_mssgbody = NULL;
296 
297     }
298 
299     /* ask the user to confirm transmission of the message */
300     if (OPMODE_IS_INTERACTIVE(opmode)) {
301 	need_redraw = TRUE; /* we are about to trounce the display */
302 	if (edit_msg) {
303 	    s = (body_has_text ? (char *)NULL : editor);
304 	    if (edit_message(fname_mssgbody, shdr, s) < 0)
305 		goto done;
306 	    body_has_text = TRUE;
307 	}
308 	if (verify_transmission(fname_mssgbody, shdr, &attachments, &form,
309 		fname_savecopy) != 0)
310 	    goto done;
311 	body_has_text = TRUE; /* it had better by this point! */
312     }
313 
314     /* format the raw form for transmission */
315     if (form == YES && format_form(fname_mssgbody) < 1)
316 	goto message_is_prepared;
317 
318     /* tack the .signature onto the end if needed */
319     if (want_signature) {
320 	if ((fp_mssgbody = file_open(fname_mssgbody, "a")) == NULL)
321 	    goto done;
322 	(void) append_sig(fp_mssgbody, shdr);
323 	if (file_close(fp_mssgbody, fname_mssgbody) < 0)
324 	    goto done;
325 	fp_mssgbody = NULL;
326 	want_signature = FALSE;
327     }
328 
329     /* on retries, cleanup from last attempt */
330     if (mssg_parts != NULL) {
331 	bodypart_destroy(mssg_parts);
332 	mssg_parts = NULL;
333     }
334 
335     /* scan the message for attachments, determine MIME headers, and such */
336     if ((mssg_parts = newpart_mssgtext(fname_mssgbody)) == NULL)
337 	goto message_is_prepared;
338 
339     /* dup any user-specified attachments onto the message attachments */
340     if (attachments != NULL) {
341 	mp = NULL;
342 	while ((mp = multipart_next(attachments, mp)) != NULL) {
343 	    multipart_insertpart(mssg_parts->subparts,
344 			MULTIPART_TAIL(mssg_parts->subparts),
345 			MULTIPART_PART(mp), MP_ID_ATTACHMENT);
346 	}
347     }
348 
349     /* ask about bounceback if the user wants us to.... */
350     if (bounceback > 0 && uucp_hops(shdr->to) > bounceback
351 		&& copy_msg != FORM && verify_bounceback()) {
352 	if (shdr->expanded_bcc[0] != '\0')
353 	    strcat(shdr->expanded_bcc, ", ");
354 	strcat(shdr->expanded_bcc, bounce_off_remote(shdr->to));
355     }
356 
357     remove_hostbang(shdr->expanded_to);
358     remove_hostbang(shdr->expanded_cc);
359     remove_hostbang(shdr->expanded_bcc);
360 
361     /* create temp file in which to build entire message */
362     if ((fname_fullmssg = tempnam(temp_dir, "xmt.")) == NULL) {
363 	dprint(1, (debugfile, "couldn't make temp file nam! (mail)\n"));
364 	error(catgets(elm_msg_cat, ElmSet, ElmCouldNotMakeTemp,
365 		"Sorry - couldn't make temp file name."));
366 	goto done;
367     }
368 
369     dprint(2, (debugfile, "Composition file='%s' and mail buffer='%s'\n",
370 	    fname_mssgbody, fname_fullmssg));
371     dprint(2,(debugfile,"--\nTo: %s\nCc: %s\nBcc: %s\nSubject: %s\n---\n",
372 	    shdr->expanded_to, shdr->expanded_cc, shdr->expanded_bcc,
373 	    shdr->subject));
374 
375     /* generate full message (headers and body) to transmit */
376     if ((fp_fullmssg = file_open(fname_fullmssg, "w")) == NULL) /* 7! w+? */
377 	goto done;
378 #ifdef MMDF
379     if (streq(submitmail, mailer)) {
380 	do_mmdf_addresses(fp_fullmssg, shdr->expanded_to);
381 	do_mmdf_addresses(fp_fullmssg, shdr->expanded_cc);
382 	do_mmdf_addresses(fp_fullmssg, shdr->expanded_bcc);
383 	putc('\n', fp_fullmssg);
384     }
385 #endif
386     if (sndhdr_output(fp_fullmssg, shdr, (form == YES), FALSE) < 0)
387 	goto message_is_prepared;
388     if (form == YES)
389 	; /* Content-Type was generated by sndhdr_output() */
390     else if (emitpart_hdr(fp_fullmssg, mssg_parts) < 0)
391 	goto message_is_prepared;
392     putc('\n', fp_fullmssg);
393     if (emitpart_body(fp_fullmssg, mssg_parts) < 0)
394 	goto message_is_prepared;
395     if (file_close(fp_fullmssg, fname_fullmssg) < 0)
396 	goto done;
397     fp_fullmssg = NULL;
398 
399     /* ensure we have envelope recipients before trying to mail */
400     if ((shdr->expanded_to == NULL || *shdr->expanded_to == '\0') &&
401 	    (shdr->expanded_cc == NULL || *shdr->expanded_cc == '\0') &&
402 	    (shdr->expanded_bcc == NULL || *shdr->expanded_bcc == '\0')) {
403 	if (bytes(fname_mssgbody) > 0)
404 	    error(catgets(elm_msg_cat, ElmSet, ElmNoRecipientsKeptMessage,
405 		    "No recipients specified!  Message kept."));
406 	else
407 	    error(catgets(elm_msg_cat, ElmSet, ElmNoRecipients,
408 		    "No recipients specified!"));
409 	goto done;
410     } else {
411 	/* mail off the message */
412 	error(catgets(elm_msg_cat, ElmSet, ElmSendingMail, "Sending mail..."));
413 	(void) build_mailer_command(bigbuf, fname_fullmssg,
414 		shdr->expanded_to, shdr->expanded_cc, shdr->expanded_bcc);
415 	if ((i = system_call(bigbuf, SY_ENV_SHELL)) != 0) {
416 	    error1(catgets(elm_msg_cat, ElmSet, ElmMailerReturnedError,
417 		    "Mail failed!  [mailer exit status %d]"), i);
418 	    goto done;
419 	}
420     }
421 
422     /* grab a copy if the user so desires... */
423 /*  #ifdef SAVE_ATTACHMENT_NAMES this if we decide to make this optional */
424     /* append attachment names to message body to remind user what they did */
425         if (attachments != NULL) {
426       if ((fp_mssgbody = file_open(fname_mssgbody, "a")) == NULL)
427           goto done;
428 
429       fprintf(fp_mssgbody, "\n");  /* Blank line to separate */
430 
431       mp = NULL;
432       while ((mp = multipart_next(attachments, mp)) != NULL) {
433           fprintf(fp_mssgbody, "(%s)\n",
434                   mp->part->content_header[BP_CONT_DISPOSITION]);
435       }
436 
437       if (file_close(fp_mssgbody, fname_mssgbody) < 0)
438           goto done;
439       fp_mssgbody = NULL;
440     }
441 /* #endif */
442 
443     if (fname_savecopy[0] != '\0'
444 		&& !save_copy(fname_savecopy, fname_mssgbody, shdr, form)
445 		&& sleepmsg > 0) {
446 	FlushOutput();
447 	sleep(sleepmsg);
448     }
449 
450     /* mark the "replied" status of the message */
451     if (mssgtype == SM_REPLY && !(curr_folder.headers[curr_folder.curr_mssg-1]->status & REPLIED_TO)) {
452 	curr_folder.headers[curr_folder.curr_mssg-1]->status |= REPLIED_TO;
453 	curr_folder.headers[curr_folder.curr_mssg-1]->status_chgd = TRUE;
454     }
455 
456     /* prevent unlink below -- the background command will delete it */
457     if (fname_fullmssg != NULL)
458     {
459        free((malloc_t)fname_fullmssg);
460        fname_fullmssg = NULL;
461     }
462 
463 
464     set_error(catgets(elm_msg_cat, ElmSet, ElmMailSent, "Mail sent!"));
465     rc = 0;
466 
467 done:
468     if (fname_fullmssg != NULL) {
469 	(void) unlink(fname_fullmssg);
470 	free((malloc_t)fname_fullmssg);
471     }
472     if (fp_fullmssg != NULL)
473 	(void) fclose(fp_fullmssg);
474     if (fp_mssgbody != NULL)
475 	(void) fclose(fp_mssgbody);
476     if (attachments != NULL) {
477 	multipart_destroy(attachments);
478 	attachments = NULL;
479     }
480     if (mssg_parts != NULL) {
481 	bodypart_destroy(mssg_parts);
482 	mssg_parts = NULL;
483     }
484     sndhdr_destroy(shdr);
485 
486     if (rc == 0) {
487 	/*
488 	 * Message sent ok -- blow away the body.  Beside crapping up
489 	 * the tmp directory, it could contain the cleartext to an
490 	 * encrypted message.
491 	 */
492 	(void) unlink(fname_mssgbody);
493 	cancelled_msg = FALSE;
494     } else {
495 	/*
496 	 * The guy can try to recall this message next time.
497 	 */
498 	cancelled_msg = (copy_msg != FORM
499 		&& access(fname_mssgbody, EDIT_ACCESS) == 0
500 		&& bytes(fname_mssgbody) > 0);
501 	saved_want_sig = want_signature;
502     }
503 
504     return need_redraw;
505 }
506 
507 
display_to(address)508 PUBLIC void display_to(address)
509 char *address;
510 {
511     char *ap;
512     char *dp, dispbuf[SLEN], ret_addr[SLEN], ret_name[SLEN];
513     int to_line, to_col, first, displen, dispsiz, len;
514 
515     assert(OPMODE_IS_INTERACTIVE(opmode));
516     if (OPMODE_IS_READMODE(opmode)) {
517 	to_line = LINES-3;
518 	to_col = 20;
519     } else {
520 	to_line = 3;
521 	to_col = 0;
522     }
523 
524     (void) strcpy(dispbuf, "To: ");
525     displen = 4;
526     dp = dispbuf + displen;
527     dispsiz = ((COLS-2) - to_col) + 1;
528     first = TRUE;
529 
530     ap = address;
531     while (displen < dispsiz && *ap != '\0' && parse_arpa_mailbox(ap,
532 		ret_addr, sizeof(ret_addr), ret_name, sizeof(ret_name),
533 		&ap) == 0) {
534 	if (!first) {
535 	    (void) strcpy(dp, ", ");
536 	    dp += 2;
537 	    displen += 2;
538 	}
539 	first = FALSE;
540 
541 	if (*ret_name == '\0')
542 	    (void) strcpy(dp, ret_addr);
543 	else if (names_only)
544 	    (void) strcpy(dp, ret_name);
545 	else
546 	    sprintf(dp, "%s (%s)", ret_addr, ret_name);
547 	len = strlen(dp);
548 	displen += len;
549 	dp += len;
550     }
551 
552     if (displen > dispsiz) {
553 	(void) strcpy(dispbuf+dispsiz-4, " ...");
554 	displen = dispsiz;
555     }
556 
557     MoveCursor(to_line, to_col);
558     CleartoEOLN();
559     if (to_col > 0) {
560 	/* fixup column position to be right justified */
561 	MoveCursor(to_line, COLS - (displen+1));
562     }
563     PutLine0(-1, -1, dispbuf);
564 }
565 
566 
get_to(to_field,address,mssgtype)567 PUBLIC int get_to(to_field, address, mssgtype)
568 char *to_field, *address;
569 int mssgtype;
570 {
571     char *prompt;
572     int line, col;
573 
574     if (*to_field == '\0') {
575 	line = (OPMODE_IS_READMODE(opmode)) ? LINES - 2 : 3;
576 
577 	if (user_level < 2) {
578 	    prompt = catgets(elm_msg_cat, ElmSet,
579 			ElmSendTheMessageTo, "Send the message to: ");
580 	} else {
581 	    prompt = catgets(elm_msg_cat, ElmSet, ElmTo, "To: ");
582 	}
583 	PutLine0(line, 0, prompt);
584 	if (enter_string(to_field, LONG_STRING, -1, -1, ESTR_REPLACE) < 0
585 		    || *to_field == '\0') {
586 	    ClearLine(line);
587 	    return FALSE;
588 	}
589     }
590 
591     if (mssgtype == SM_REPLY || mssgtype == SM_FORMRESP) {
592 	/* use the literal address values */
593 	(void) strcpy(address, to_field);
594     } else {
595 	/* perform alias expansion of addresses */
596 	(void) build_address(strip_commas(to_field), address);
597 	if (*address == '\0') {	/* bad address!  Removed!! */
598 	    ClearLine(line);
599 	    return FALSE;
600 	}
601     }
602 
603     return TRUE;
604 }
605 
606 
display_subject(subject_field)607 static void display_subject(subject_field)
608 const char *subject_field;
609 {
610     int prompt_line;
611 
612     assert(OPMODE_IS_INTERACTIVE(opmode));
613     prompt_line = (OPMODE_IS_READMODE(opmode) ? LINES-2 : 4);
614 
615     if (user_level == 0) {
616 	PutLine0(prompt_line, 0,
617 		catgets(elm_msg_cat, ElmSet, ElmSubjectOfMessage,
618 		"Subject of message: "));
619     }
620     else
621 	PutLine0(prompt_line, 0,
622 		catgets(elm_msg_cat, ElmSet, ElmSubject, "Subject: "));
623 
624     PutLine0(-1, -1, subject_field);
625 }
626 
627 
get_subject(subject_field)628 static int get_subject(subject_field)
629 char *subject_field;
630 {
631 	char *msg;
632 
633 	/** get the subject and return non-zero if all okay... **/
634 	int prompt_line;
635 
636 	assert(OPMODE_IS_INTERACTIVE(opmode));
637 	prompt_line = (OPMODE_IS_READMODE(opmode) ? LINES-2 : 4);
638 
639 	if (user_level == 0) {
640 	  PutLine0(prompt_line,0, catgets(elm_msg_cat, ElmSet, ElmSubjectOfMessage,
641 		"Subject of message: "));
642 	}
643 	else
644 	  PutLine0(prompt_line,0, catgets(elm_msg_cat, ElmSet, ElmSubject, "Subject: "));
645 
646 	if (enter_string(subject_field, SLEN, -1, -1, ESTR_UPDATE) < 0) {
647 	  MoveCursor(prompt_line,0);
648 	  CleartoEOLN();
649 	  error(catgets(elm_msg_cat, ElmSet, ElmMailNotSent, "Mail not sent."));
650 	  return FALSE;
651 	}
652 
653 	if (strlen(subject_field) == 0) {	/* zero length subject?? */
654 	  msg = catgets(elm_msg_cat, ElmSet, ElmNoSubjectContinue,
655 	    "No subject - Continue with message?");
656 
657 	  if (!enter_yn(msg, FALSE, prompt_line, FALSE)) {
658 	    ClearLine(prompt_line);
659 	    error(catgets(elm_msg_cat, ElmSet, ElmMailNotSend, "Mail not sent."));
660 	    return FALSE;
661 	  }
662 	  PutLine0(prompt_line,0,"Subject: <none>");
663 	  CleartoEOLN();
664 	}
665 
666 	return TRUE;
667 }
668 
669 
get_copies(cc_field,address,addressII,copy_message)670 static int get_copies(cc_field, address, addressII, copy_message)
671 char *cc_field, *address, *addressII;
672 int   copy_message;
673 {
674 	/** Get the list of people that should be cc'd, returning ZERO if
675 	    any problems arise.  Address and AddressII are for expanding
676 	    the aliases out after entry!
677 	    If copy-message, that means that we're going to have to invoke
678 	    a screen editor, so we'll need to delay after displaying the
679 	    possibly rewritten Cc: line...
680 	**/
681 	int prompt_line;
682 
683 	assert(OPMODE_IS_INTERACTIVE(opmode));
684 	prompt_line = (OPMODE_IS_READMODE(opmode) ? LINES-1 : 5);
685 	PutLine0(prompt_line,0,
686 		catgets(elm_msg_cat, ElmSet, ElmCopiesTo, "Copies to: "));
687 
688 	if (enter_string(cc_field, VERY_LONG_STRING, -1, -1, ESTR_REPLACE) < 0) {
689 	  ClearLine(prompt_line-1);
690 	  ClearLine(prompt_line);
691 
692 	  error(catgets(elm_msg_cat, ElmSet, ElmMailNotSend, "Mail not sent."));
693 	  return FALSE;
694 	}
695 
696 	/** The following test is that if the build_address routine had
697 	    reason to rewrite the entry given, then, if we're mailing only
698 	    print the new Cc line below the old one.  If we're not, then
699 	    assume we're in screen mode and replace the incorrect entry on
700 	    the line above where we are (e.g. where we originally prompted
701 	    for the Cc: field).
702 	**/
703 
704 	if (build_address(strip_commas(cc_field), addressII)) {
705 	  PutLine1(prompt_line, 11, "%s", addressII);
706 	  if ((strcmp(editor, "builtin") != 0 && strcmp(editor, "none") != 0)
707 	      || copy_message)
708 	    if (sleepmsg > 0) {
709 		FlushOutput();
710 		sleep(sleepmsg);
711 	    }
712 	}
713 
714 	if (strlen(address) + strlen(addressII) > VERY_LONG_STRING) {
715 	  dprint(2, (debugfile,
716 		"String length of \"To:\" + \"Cc\" too long! (get_copies)\n"));
717 	  error(catgets(elm_msg_cat, ElmSet, ElmTooManyPeople, "Too many people. Copies ignored."));
718 	  if (sleepmsg > 0)
719 		sleep(sleepmsg);
720 	  cc_field[0] = '\0';
721 	}
722 
723 	return TRUE;
724 }
725 
726 
verify_copy_msg()727 static int verify_copy_msg()
728 {
729 	char *msg;
730 
731 	if (!ask_reply_copy)
732 	    return reply_copy;
733 
734 	if (user_level < 2) {
735 	    msg = catgets(elm_msg_cat, ElmSet, ElmCopyMessageIntoReplyYN,
736 		    "Copy message into reply?");
737 	} else {
738 	    msg = catgets(elm_msg_cat, ElmSet, ElmCopyMessageYN,
739 		    "Copy message?");
740 	}
741 	return enter_yn(msg, reply_copy, LINES-3, FALSE);
742 }
743 
744 
recall_last_msg(filename,copy_msg)745 static int recall_last_msg(filename, copy_msg)
746 const char *filename;
747 int copy_msg;
748 {
749 	char *msg;
750 
751 	if (access(filename, EDIT_ACCESS) != 0)
752 	  return FALSE;
753 
754 	if (copy_msg)
755 	    msg = catgets(elm_msg_cat, ElmSet, ElmRecallLastInstead,
756 		"Recall last kept message instead?");
757 	else
758 	    msg = catgets(elm_msg_cat, ElmSet, ElmRecallLastKept,
759 		"Recall last kept message?");
760 
761 	ClearLine(LINES-1);
762 	return enter_yn(msg, -1, LINES-1, FALSE);
763 
764 	/*NOTREACHED*/
765 }
766 
767 
append_sig(fp_mssg,shdr)768 static int append_sig(fp_mssg, shdr)
769 FILE *fp_mssg;
770 SEND_HEADER *shdr;
771 {
772     /* Append the correct signature file to file.  Return TRUE if
773     we append anything.  */
774 
775     /* Look at the to and cc list to determine which one to use */
776 
777     /* We could check the bcc list too, but we don't want people to
778     know about bcc, even indirectly */
779 
780     /* Some people claim that  user@anything.same_domain should be
781     considered local.  Since it's not the same machine, better be
782     safe and use the remote sig (presumably it has more complete
783     information).  You can't necessarily finger someone in the
784     same domain. */
785 
786     FILE *fp_sig;
787     char filename2[SLEN], *sig;
788 
789     if (!OPMODE_IS_INTERACTIVE(opmode))
790 	return FALSE;	/* FOO - why are we doing this?? */
791     if (local_signature[0] == '\0' && remote_signature[0] == '\0')
792 	return FALSE;
793 
794     if (index(shdr->expanded_to, '!') || index(shdr->expanded_cc,'!')) {
795 	sig = remote_signature;		/* ! always means remote */
796     } else {
797 	/* check each @ for @thissite.domain */
798 	/* if any one is different than this, then use remote sig */
799 	int len;
800 	char *ptr;
801 	char sitename[SLEN];
802 	sprintf(sitename, "@%s", host_fullname);
803 	len = strlen(sitename);
804 	sig = local_signature;
805 	for (ptr = index(shdr->expanded_to,'@'); ptr;  /* check To: list */
806 		    ptr = index(ptr+1,'@')) {
807 	    if (strincmp(ptr,sitename,len) != 0
808 			|| (*(ptr+len) != ',' && *(ptr+len) != 0
809 			&& *(ptr+len) != ' ')) {
810 		sig = remote_signature;
811 		break;
812 	    }
813 	}
814 	if (sig == local_signature) {		   /* still local? */
815 	    for (ptr = index(shdr->expanded_cc,'@'); ptr;   /* check Cc: */
816 			ptr = index(ptr+1,'@')) {
817 		if (strincmp(ptr,sitename,len) != 0
818 			    || (*(ptr+len) != ',' && *(ptr+len) != 0
819 			    && *(ptr+len) != ' ')) {
820 		    sig = remote_signature;
821 		    break;
822 		}
823 	    }
824 	}
825     }
826 
827     switch (sig[0]) {
828     case '\0':
829 	return FALSE;
830     case '/':
831 	(void) strcpy(filename2, sig);
832 	break;
833     default:
834 	sprintf(filename2, "%s/%s", user_home, sig);
835 	break;
836     }
837 
838     /*
839      * FOO - this displays an error if the .sig doesn't exist.
840      * Is that really what we want to do?
841      */
842     if ((fp_sig = file_open(filename2, "r")) == NULL)
843 	return FALSE;
844     if (sig_dashes)
845 	fputs("\n-- \n", fp_mssg);
846     if (file_copy(fp_sig, fp_mssg, filename2, "message file") < 0) {
847 	(void) fclose(fp_sig);
848 	return FALSE;
849     }
850     if (file_close(fp_sig, filename2) < 0)
851 	return FALSE;
852     return TRUE;
853 }
854 
855 
verify_bounceback()856 static int verify_bounceback()
857 {
858     char *msg;
859 
860     ClearLine(LINES);
861     msg = catgets(elm_msg_cat, ElmSet, ElmBounceOffRemote,
862 		"\"Bounce\" a copy off the remote machine?");
863     return enter_yn(msg, FALSE, LINES, FALSE);
864 }
865 
866 
867 /*
868  * remove_hostbang - Given an expanded list of addresses, remove all
869  * occurrences of "thishost!" at the beginning of addresses.
870  * This hack is useful in itself, but it is required now because of the
871  * kludge disallowing alias expansion on return addresses.
872  */
remove_hostbang(addrs)873 static void remove_hostbang(addrs)
874 char *addrs;
875 {
876     int hlen, flen;
877     char *src, *dest;
878 
879     if ((hlen = strlen(host_name)) < 1)
880 	return;
881     flen = strlen(host_fullname);
882     src = dest = addrs;
883 
884     while (*src != '\0') {
885 	if (strncmp(src, host_name, hlen) == 0 && src[hlen] == '!')
886 	    src += hlen+1;
887 	if (strncmp(src, host_fullname, flen) == 0 && src[flen] == '!')
888 	    src += flen+1;
889 	while (*src != '\0' && !isspace(*src))
890 	    *dest++ = *src++;
891 	if (isspace(*src)) {
892 	    while (isspace(*src))
893 		++src;
894 	    *dest++ = ' ';
895 	}
896     }
897 
898     *dest = '\0';
899 }
900 
901 
902 #define VT_REDRAW_NONE		0
903 #define VT_REDRAW_HEADER	(1<<0)
904 #define VT_REDRAW_FOOTER	(1<<1)
905 #define VT_REDRAW_ALL		(~0)
906 
907 /*
908  * verify_transmission() - Ask the user to confirm transmission of the
909  * message.  Returns 0 to send it, -1 to forget it.
910  */
verify_transmission(filename,shdr,attachments_p,form_p,copy_file)911 static int verify_transmission(filename, shdr, attachments_p, form_p, copy_file)
912 const char *filename;	/* pathname to mail mssg composition file	*/
913 SEND_HEADER *shdr;	/* headers for the message being sent		*/
914 SEND_MULTIPART **attachments_p; /* attachments to message being sent	*/
915 int  *form_p;		/* pointer to form message state		*/
916 char *copy_file;	/* pointer to buffer holding copy file name	*/
917 {
918     char *prompt_mssg;		/* message to display prompting for cmd	*/
919     char prompt_menu1[SLEN];	/* menu of available commands		*/
920     char prompt_menu2[SLEN];	/* menu of available commands		*/
921     int bad_cmd;		/* set TRUE to bitch about user's entry	*/
922     int prev_form;		/* "*form_p" value last time thru loop	*/
923     int do_redraw;		/* portions of display to update	*/
924     int cmd;			/* command to perform			*/
925     int max_hdrline;		/* bottommost line used in hdr display	*/
926     char lbuf[VERY_LONG_STRING], *s;
927     int curr_line, curr_col, i;
928 
929     bad_cmd = FALSE;		/* nothing to complain about yet	*/
930     prev_form = *form_p + 1;	/* force build of prompt strings	*/
931     do_redraw = VT_REDRAW_ALL;	/* force screen display first time	*/
932 
933     for (;;) {
934 
935 	/* see if the prompts need to be built */
936 	if (prev_form == *form_p) {
937 	    ; /* not changed - no need to rebuild the strings */
938 	} else {
939 	    if (user_level == 0) {
940 		prompt_mssg = catgets(elm_msg_cat, ElmSet,
941 			    ElmVfyPromptSendTheMsg,
942 "Send the message now? y");
943 		strcpy(prompt_menu1, catgets(elm_msg_cat, ElmSet,
944 			    ElmVfyMenu1User0,
945 "Select letter of header to edit, 'e' to edit the message,"));
946 		strcpy(prompt_menu2, catgets(elm_msg_cat, ElmSet,
947 			    ElmVfyMenu2User0,
948 "'a' to make attachments, 'y' to send message, or 'n' to cancel."));
949 	    } else {
950 		prompt_mssg = catgets(elm_msg_cat, ElmSet,
951 			    ElmVfyPromptSendMessage,
952 			    "Send message now? y");
953 		strcpy(prompt_menu1, catgets(elm_msg_cat, ElmSet,
954 			    ElmVfySelectLetter,
955 			    "Select letter of header to edit, "));
956 		*prompt_menu2 = '\0';
957 		switch (*form_p) {
958 		case PREFORMATTED:
959 		    break;
960 		case YES:
961 		    strcat(prompt_menu1, catgets(elm_msg_cat, ElmSet,
962 				ElmVfyMenuEditForm, "e)dit form,"));
963 		    break;
964 		case MAYBE:
965 		    strcat(prompt_menu1, catgets(elm_msg_cat, ElmSet,
966 				ElmVfyMenuEditMake, "e)dit msg, m)ake form,"));
967 		    break;
968 		default:
969 		    strcat(prompt_menu1, catgets(elm_msg_cat, ElmSet,
970 				ElmVfyMenuEditMsg, "e)dit message,"));
971 		    break;
972 		}
973 		strcat(prompt_menu2, catgets(elm_msg_cat, ElmSet,
974 			    ElmVfyMenuVfyCpy,
975 			    "all h)eaders, a)ttachments, co(p)y, "));
976 #ifdef ISPELL
977 		strcat(prompt_menu2, catgets(elm_msg_cat, ElmSet,
978 			    ElmVfyMenuIspell,
979 			    "i)spell, "));
980 #endif
981 #ifdef ALLOW_SUBSHELL
982 		strcat(prompt_menu2, catgets(elm_msg_cat, ElmSet,
983 			    ElmVfyMenuShell,
984 			    "!)shell, "));
985 #endif
986 		strcat(prompt_menu2, catgets(elm_msg_cat, ElmSet,
987 			    ElmVfyMenuForget,
988 			    "f)orget, or:"));
989 	    }
990 	    prev_form = *form_p;
991 	    do_redraw |= VT_REDRAW_FOOTER;
992 	}
993 
994 	/* complain if last entry was bad */
995 	if (bad_cmd) {
996 	    PutLine0(-1, -1, "\07??");	/* beep! */
997 	    if (sleepmsg > 0) {
998 		FlushOutput();
999 		sleep((sleepmsg + 1) / 2);
1000 	    }
1001 	    bad_cmd = FALSE;
1002 	}
1003 
1004 	/* update the screen */
1005 	if (do_redraw == VT_REDRAW_ALL)
1006 	    ClearScreen();
1007 	if (do_redraw & VT_REDRAW_HEADER) {
1008 	    if (do_redraw != VT_REDRAW_ALL) {
1009 		MoveCursor(0, 0);
1010 		CleartoEOLN();
1011 	    }
1012 	    CenterLine(0, catgets(elm_msg_cat, ElmSet, ElmVfyTitle,
1013 			"Send Message"));
1014 	}
1015 	if (do_redraw == VT_REDRAW_ALL)
1016 	    max_hdrline = show_msg_headers(shdr, "tcbsr");
1017 	if (do_redraw & VT_REDRAW_FOOTER) {
1018 	    if (do_redraw != VT_REDRAW_ALL) {
1019 		MoveCursor(max_hdrline+1, 0);
1020 		CleartoEOS();
1021 	    }
1022 	    CenterLine(LINES-5, prompt_menu1);
1023 	    CenterLine(LINES-4, prompt_menu2);
1024 	    show_last_error();
1025 	}
1026 	do_redraw = VT_REDRAW_NONE;
1027 
1028 	/* prompt for command */
1029 	PutLine0(LINES-2, 0, prompt_mssg);
1030 	GetCursorPos(&curr_line, &curr_col);
1031 	curr_col--; /* backspace over default answer */
1032 	CleartoEOLN();
1033 	MoveCursor(curr_line, curr_col);
1034 	if ((cmd = GetKey(0)) == KEY_REDRAW) {
1035 	    do_redraw = VT_REDRAW_ALL;
1036 	    continue;
1037 	}
1038 	clear_error();
1039 
1040 	/* handle command */
1041 	switch (cmd) {
1042 
1043 	case 'y':
1044 	case '\n':
1045 	case '\r':
1046 	    PutLine0(-1, -1, "Send");
1047 	    return 0;
1048 	    /*NOTREACHED*/
1049 
1050 	case 'n':
1051 	case 'f':		/* old menu used "f)orget" */
1052 	    PutLine0(-1, -1, "Forget");
1053 	    if (bytes(filename) <= 0) {
1054 		; /* forget about empty files */
1055 	    } else if (!OPMODE_IS_READMODE(opmode)) {
1056 		sprintf(lbuf, "%s/%s", user_home, dead_letter);
1057 		(void) save_mssg(lbuf, filename, shdr, *form_p);
1058 	    } else if (user_level > 0) {
1059 		set_error(catgets(elm_msg_cat, ElmSet, ElmVfyMessageKept,
1060 		    "Message kept.  Can be restored at next f)orward, m)ail or r)eply."));
1061 	    }
1062 	    return -1;
1063 	    /*NOTREACHED*/
1064 
1065 	case 'a':
1066 	    if (attachment_menu(attachments_p) < 0)
1067 		return -1;
1068 	    do_redraw = VT_REDRAW_ALL;
1069 	    break;
1070 
1071 	case 'p':
1072 	    PutLine0(-1, -1, "Copy file");
1073 	    do_redraw |= (name_copy_file(copy_file)
1074 			? VT_REDRAW_ALL : VT_REDRAW_FOOTER);
1075 	    break;
1076 
1077 	case 'e':
1078 	    PutLine0(-1, -1, "Edit");
1079 	    if (*form_p == PREFORMATTED) {
1080 		bad_cmd = TRUE;
1081 	    } else {
1082 		if (*form_p == YES) {
1083 		    *form_p = MAYBE;
1084 		    error(catgets(elm_msg_cat, ElmSet, ElmVfyFormToPlaintext,
1085 "Converting form back to plaintext.  Do \"m)ake form\" to re-make form."));
1086 		    if (sleepmsg > 0)
1087 			sleep(sleepmsg);
1088 		}
1089 		if (edit_message(filename, shdr, (char *)NULL) < 0)
1090 		    return -1;
1091 		do_redraw = VT_REDRAW_ALL;
1092 	    }
1093 	    break;
1094 
1095 	case 'h':
1096 	    PutLine0(-1, -1, "Headers");
1097 	    edit_headers(shdr);
1098 	    do_redraw |= (VT_REDRAW_HEADER|VT_REDRAW_FOOTER);
1099 	    break;
1100 
1101 	case 'm':
1102 	    if (*form_p != MAYBE) {
1103 		bad_cmd = TRUE;
1104 	    } else {
1105 		switch (check_form_file(filename)) {
1106 		case -1:
1107 		    /* couldn't open file??? */
1108 		    return -1;
1109 		case 0:
1110 		    PutLine0(-1, -1, catgets(elm_msg_cat, ElmSet,
1111 			ElmVfyNoFieldsInForm, "No fields in form!\007"));
1112 		    if (sleepmsg > 0) {
1113 			FlushOutput();
1114 			sleep(sleepmsg);
1115 		    }
1116 		    break;
1117 		default:
1118 		    /* looks like a good form */
1119 		    *form_p = YES;
1120 		    break;
1121 		}
1122 	    }
1123 	    break;
1124 
1125 #ifdef ISPELL
1126 	case 'i':
1127 	    PutLine0(-1, -1, "Ispell");
1128 	    if (*form_p == PREFORMATTED) {
1129 		bad_cmd = TRUE;
1130 	    } else {
1131 		if (*form_p == YES) {
1132 		    *form_p = MAYBE;
1133 		    error(catgets(elm_msg_cat, ElmSet, ElmVfyFormToPlaintext,
1134 "Converting form back to plaintext.  Do \"m)ake form\" to re-make form."));
1135 		    if (sleepmsg > 0)
1136 			sleep(sleepmsg);
1137 		}
1138 		sprintf(lbuf, "%s %s %s",
1139 		    ISPELL_PATH, ISPELL_OPTIONS, filename);
1140 		system_call(lbuf, SY_COOKED|SY_ENAB_SIGHUP);
1141 		do_redraw = VT_REDRAW_ALL;
1142 	    }
1143 	    break;
1144 #endif
1145 
1146 #ifdef ALLOW_SUBSHELL
1147 	case '!':
1148 	    if (subshell() != 0)
1149 		do_redraw = VT_REDRAW_ALL;
1150 	    break;
1151 #endif
1152 
1153 	/* these letters should match those given to show_msg_headers() */
1154 	/* And we allow uppercase also, since it's what's parenthesized
1155 	 * on the display. */
1156 	case 't': case 'T':	/* T)o */
1157 	case 'c': case 'C':	/* C)c */
1158 	case 'b': case 'B':	/* B)cc */
1159 	case 's': case 'S':	/* S)ubject */
1160 	case 'r': case 'R':	/* R)eply-To */
1161 	    MoveCursor(LINES-5, 0);
1162 	    CleartoEOS();
1163 	    if (!edit_header_char(shdr, tolower(cmd)))
1164 		bad_cmd = TRUE;
1165 	    do_redraw |= VT_REDRAW_FOOTER;
1166 	    break;
1167 
1168 	case ctrl('L'):
1169 	    do_redraw = VT_REDRAW_ALL;
1170 	    break;
1171 
1172 	default:
1173 	    bad_cmd = TRUE;
1174 	    break;
1175 
1176 	}
1177 
1178     }
1179 
1180 }
1181 
1182 
1183 #ifdef MMDF
do_mmdf_addresses(fp,buf)1184 PUBLIC void do_mmdf_addresses(fp, buf)
1185 FILE *fp;
1186 char *buf;
1187 {
1188     char *bufcopy[VERY_LONG_STRING], *bp, *addr;
1189 
1190     bp = strip_parens(strip_commas(strfcpy(bufcopy, buf, sizeof(bufcopy))));
1191     while ((addr = strtok(bp, " \t\r\n")) != NULL) {
1192 	bp = NULL;
1193 	fputs(addr, fp);
1194 	putc('\n', fp);
1195     }
1196 }
1197 #endif /* MMDF */
1198 
1199 
1200 /*
1201  * Construct a command string to mail a message.
1202  * This sanitizes the recipient lists as a side effect.
1203  */
build_mailer_command(cmdbuf,fname_mssg,to_recip,cc_recip,bcc_recip)1204 PUBLIC char *build_mailer_command(cmdbuf, fname_mssg, to_recip, cc_recip, bcc_recip)
1205 char *cmdbuf;
1206 const char *fname_mssg;
1207 char *to_recip, *cc_recip, *bcc_recip;
1208 {
1209     char recipients[VERY_LONG_STRING], mflags[SLEN], *cp;
1210 
1211     if (streq(sendmail, mailer)) {
1212 	strcpy(mflags, (sendmail_verbose ? smflagsv : smflags));
1213 	if (metoo)
1214 	    strcat(mflags, smflagmt);
1215     } else if (streq(submitmail, mailer)) {
1216 	strcpy(mflags, submitflags_s);
1217     } else if (streq(execmail, mailer)) {
1218 	strcpy(mflags, (sendmail_verbose ? emflagsv : emflags));
1219 	if (metoo)
1220 	    strcat(mflags, emflagmt);
1221     } else {
1222 	mflags[0] ='\0';
1223     }
1224 
1225     if (streq(submitmail, mailer)) {
1226 
1227 	strcpy(mflags, submitflags_s);
1228 	recipients[0] = '\0';
1229 
1230     } else {
1231 
1232 	if (streq(sendmail, mailer)) {
1233 	    strcpy(mflags, (sendmail_verbose ? smflagsv : smflags));
1234 	    if (metoo)
1235 		strcat(mflags, smflagmt);
1236 	} else if (streq(execmail, mailer)) {
1237 	    strcpy(mflags, (sendmail_verbose ? emflagsv : emflags));
1238 	    if (metoo)
1239 		strcat(mflags, emflagmt);
1240 	} else {
1241 	    mflags[0] ='\0';
1242 	}
1243 
1244 	*(cp = recipients) = '\0';
1245 	if (to_recip != NULL && *to_recip != '\0') {
1246 	    *cp++ = ' ';
1247 	    quote_args(cp, strip_commas(strip_parens(to_recip)));
1248 	    cp += strlen(cp);
1249 	}
1250 	if (cc_recip != NULL && *cc_recip != '\0') {
1251 	    *cp++ = ' ';
1252 	    quote_args(cp, strip_commas(strip_parens(cc_recip)));
1253 	    cp += strlen(cp);
1254 	}
1255 	if (bcc_recip != NULL && *bcc_recip != '\0') {
1256 	    *cp++ = ' ';
1257 	    quote_args(cp, strip_commas(strip_parens(bcc_recip)));
1258 	    cp += strlen(cp);
1259 	}
1260 
1261     }
1262 
1263     sprintf(cmdbuf, "(%s %s%s <%s;%s %s)&",
1264 	mailer, mflags, recipients, fname_mssg, remove_cmd, fname_mssg);
1265     return cmdbuf;
1266 }
1267 
1268