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