]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/mail/send.c
amd64: use register macros for gdb_cpu_getreg()
[FreeBSD/FreeBSD.git] / usr.bin / mail / send.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #ifndef lint
33 #if 0
34 static char sccsid[] = "@(#)send.c      8.1 (Berkeley) 6/6/93";
35 #endif
36 #endif /* not lint */
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include "rcv.h"
41 #include "extern.h"
42
43 /*
44  * Mail -- a mail program
45  *
46  * Mail to others.
47  */
48
49 /*
50  * Send message described by the passed pointer to the
51  * passed output buffer.  Return -1 on error.
52  * Adjust the status: field if need be.
53  * If doign is given, suppress ignored header fields.
54  * prefix is a string to prepend to each output line.
55  */
56 int
57 sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign,
58         char *prefix)
59 {
60         long count;
61         FILE *ibuf;
62         char *cp, *cp2, line[LINESIZE];
63         int ishead, infld, ignoring, dostat, firstline;
64         int c = 0, length, prefixlen;
65
66         /*
67          * Compute the prefix string, without trailing whitespace
68          */
69         if (prefix != NULL) {
70                 cp2 = 0;
71                 for (cp = prefix; *cp != '\0'; cp++)
72                         if (*cp != ' ' && *cp != '\t')
73                                 cp2 = cp;
74                 prefixlen = cp2 == NULL ? 0 : cp2 - prefix + 1;
75         }
76         ibuf = setinput(mp);
77         count = mp->m_size;
78         ishead = 1;
79         dostat = doign == 0 || !isign("status", doign);
80         infld = 0;
81         firstline = 1;
82         /*
83          * Process headers first
84          */
85         while (count > 0 && ishead) {
86                 if (fgets(line, sizeof(line), ibuf) == NULL)
87                         break;
88                 count -= length = strlen(line);
89                 if (firstline) {
90                         /*
91                          * First line is the From line, so no headers
92                          * there to worry about
93                          */
94                         firstline = 0;
95                         ignoring = doign == ignoreall;
96                 } else if (line[0] == '\n') {
97                         /*
98                          * If line is blank, we've reached end of
99                          * headers, so force out status: field
100                          * and note that we are no longer in header
101                          * fields
102                          */
103                         if (dostat) {
104                                 statusput(mp, obuf, prefix);
105                                 dostat = 0;
106                         }
107                         ishead = 0;
108                         ignoring = doign == ignoreall;
109                 } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
110                         /*
111                          * If this line is a continuation (via space or tab)
112                          * of a previous header field, just echo it
113                          * (unless the field should be ignored).
114                          * In other words, nothing to do.
115                          */
116                 } else {
117                         /*
118                          * Pick up the header field if we have one.
119                          */
120                         for (cp = line; (c = *cp++) != '\0' && c != ':' &&
121                             !isspace((unsigned char)c);)
122                                 ;
123                         cp2 = --cp;
124                         while (isspace((unsigned char)*cp++))
125                                 ;
126                         if (cp[-1] != ':') {
127                                 /*
128                                  * Not a header line, force out status:
129                                  * This happens in uucp style mail where
130                                  * there are no headers at all.
131                                  */
132                                 if (dostat) {
133                                         statusput(mp, obuf, prefix);
134                                         dostat = 0;
135                                 }
136                                 if (doign != ignoreall)
137                                         /* add blank line */
138                                         (void)putc('\n', obuf);
139                                 ishead = 0;
140                                 ignoring = 0;
141                         } else {
142                                 /*
143                                  * If it is an ignored field and
144                                  * we care about such things, skip it.
145                                  */
146                                 *cp2 = '\0';    /* temporarily null terminate */
147                                 if (doign && isign(line, doign))
148                                         ignoring = 1;
149                                 else if ((line[0] == 's' || line[0] == 'S') &&
150                                          strcasecmp(line, "status") == 0) {
151                                         /*
152                                          * If the field is "status," go compute
153                                          * and print the real Status: field
154                                          */
155                                         if (dostat) {
156                                                 statusput(mp, obuf, prefix);
157                                                 dostat = 0;
158                                         }
159                                         ignoring = 1;
160                                 } else {
161                                         ignoring = 0;
162                                         *cp2 = c;       /* restore */
163                                 }
164                                 infld = 1;
165                         }
166                 }
167                 if (!ignoring) {
168                         /*
169                          * Strip trailing whitespace from prefix
170                          * if line is blank.
171                          */
172                         if (prefix != NULL) {
173                                 if (length > 1)
174                                         fputs(prefix, obuf);
175                                 else
176                                         (void)fwrite(prefix, sizeof(*prefix),
177                                             prefixlen, obuf);
178                         }
179                         (void)fwrite(line, sizeof(*line), length, obuf);
180                         if (ferror(obuf))
181                                 return (-1);
182                 }
183         }
184         /*
185          * Copy out message body
186          */
187         if (doign == ignoreall)
188                 count--;                /* skip final blank line */
189         if (prefix != NULL)
190                 while (count > 0) {
191                         if (fgets(line, sizeof(line), ibuf) == NULL) {
192                                 c = 0;
193                                 break;
194                         }
195                         count -= c = strlen(line);
196                         /*
197                          * Strip trailing whitespace from prefix
198                          * if line is blank.
199                          */
200                         if (c > 1)
201                                 fputs(prefix, obuf);
202                         else
203                                 (void)fwrite(prefix, sizeof(*prefix),
204                                     prefixlen, obuf);
205                         (void)fwrite(line, sizeof(*line), c, obuf);
206                         if (ferror(obuf))
207                                 return (-1);
208                 }
209         else
210                 while (count > 0) {
211                         c = count < LINESIZE ? count : LINESIZE;
212                         if ((c = fread(line, sizeof(*line), c, ibuf)) <= 0)
213                                 break;
214                         count -= c;
215                         if (fwrite(line, sizeof(*line), c, obuf) != c)
216                                 return (-1);
217                 }
218         if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
219                 /* no final blank line */
220                 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
221                         return (-1);
222         return (0);
223 }
224
225 /*
226  * Output a reasonable looking status field.
227  */
228 void
229 statusput(struct message *mp, FILE *obuf, char *prefix)
230 {
231         char statout[3];
232         char *cp = statout;
233
234         if (mp->m_flag & MREAD)
235                 *cp++ = 'R';
236         if ((mp->m_flag & MNEW) == 0)
237                 *cp++ = 'O';
238         *cp = '\0';
239         if (statout[0] != '\0')
240                 fprintf(obuf, "%sStatus: %s\n",
241                         prefix == NULL ? "" : prefix, statout);
242 }
243
244 /*
245  * Interface between the argument list and the mail1 routine
246  * which does all the dirty work.
247  */
248 int
249 mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts,
250         char *subject, char *replyto)
251 {
252         struct header head;
253
254         head.h_to = to;
255         head.h_subject = subject;
256         head.h_cc = cc;
257         head.h_bcc = bcc;
258         head.h_smopts = smopts;
259         head.h_replyto = replyto;
260         head.h_inreplyto = NULL;
261         mail1(&head, 0);
262         return (0);
263 }
264
265
266 /*
267  * Send mail to a bunch of user names.  The interface is through
268  * the mail routine below.
269  */
270 int
271 sendmail(char *str)
272 {
273         struct header head;
274
275         head.h_to = extract(str, GTO);
276         head.h_subject = NULL;
277         head.h_cc = NULL;
278         head.h_bcc = NULL;
279         head.h_smopts = NULL;
280         head.h_replyto = value("REPLYTO");
281         head.h_inreplyto = NULL;
282         mail1(&head, 0);
283         return (0);
284 }
285
286 /*
287  * Mail a message on standard input to the people indicated
288  * in the passed header.  (Internal interface).
289  */
290 void
291 mail1(struct header *hp, int printheaders)
292 {
293         char *cp;
294         char *nbuf;
295         int pid;
296         char **namelist;
297         struct name *to, *nsto;
298         FILE *mtf;
299
300         /*
301          * Collect user's mail from standard input.
302          * Get the result as mtf.
303          */
304         if ((mtf = collect(hp, printheaders)) == NULL)
305                 return;
306         if (value("interactive") != NULL) {
307                 if (value("askcc") != NULL || value("askbcc") != NULL) {
308                         if (value("askcc") != NULL)
309                                 grabh(hp, GCC);
310                         if (value("askbcc") != NULL)
311                                 grabh(hp, GBCC);
312                 } else {
313                         printf("EOT\n");
314                         (void)fflush(stdout);
315                 }
316         }
317         if (fsize(mtf) == 0) {
318                 if (value("dontsendempty") != NULL)
319                         goto out;
320                 if (hp->h_subject == NULL)
321                         printf("No message, no subject; hope that's ok\n");
322                 else
323                         printf("Null message body; hope that's ok\n");
324         }
325         /*
326          * Now, take the user names from the combined
327          * to and cc lists and do all the alias
328          * processing.
329          */
330         senderr = 0;
331         to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
332         if (to == NULL) {
333                 printf("No recipients specified\n");
334                 senderr++;
335         }
336         /*
337          * Look through the recipient list for names with /'s
338          * in them which we write to as files directly.
339          */
340         to = outof(to, mtf, hp);
341         if (senderr)
342                 savedeadletter(mtf);
343         to = elide(to);
344         if (count(to) == 0)
345                 goto out;
346         if (value("recordrecip") != NULL) {
347                 /*
348                  * Before fixing the header, save old To:.
349                  * We do this because elide above has sorted To: list, and
350                  * we would like to save message in a file named by the first
351                  * recipient the user has entered, not the one being the first
352                  * after sorting happened.
353                  */
354                 if ((nsto = malloc(sizeof(struct name))) == NULL)
355                         err(1, "Out of memory");
356                 bcopy(hp->h_to, nsto, sizeof(struct name));
357         }
358         fixhead(hp, to);
359         if ((mtf = infix(hp, mtf)) == NULL) {
360                 fprintf(stderr, ". . . message lost, sorry.\n");
361                 return;
362         }
363         namelist = unpack(cat(hp->h_smopts, to));
364         if (debug) {
365                 char **t;
366
367                 printf("Sendmail arguments:");
368                 for (t = namelist; *t != NULL; t++)
369                         printf(" \"%s\"", *t);
370                 printf("\n");
371                 goto out;
372         }
373         if (value("recordrecip") != NULL) {
374                 /*
375                  * Extract first recipient username from saved To: and use it
376                  * as a filename.
377                  */
378                 if ((nbuf = malloc(strlen(detract(nsto, 0)) + 1)) == NULL)
379                         err(1, "Out of memory");
380                 if ((cp = yanklogin(detract(nsto, 0), nbuf)) != NULL)
381                         (void)savemail(expand(nbuf), mtf);
382                 free(nbuf);
383                 free(nsto);
384         } else if ((cp = value("record")) != NULL)
385                 (void)savemail(expand(cp), mtf);
386         /*
387          * Fork, set up the temporary mail file as standard
388          * input for "mail", and exec with the user list we generated
389          * far above.
390          */
391         pid = fork();
392         if (pid == -1) {
393                 warn("fork");
394                 savedeadletter(mtf);
395                 goto out;
396         }
397         if (pid == 0) {
398                 sigset_t nset;
399                 (void)sigemptyset(&nset);
400                 (void)sigaddset(&nset, SIGHUP);
401                 (void)sigaddset(&nset, SIGINT);
402                 (void)sigaddset(&nset, SIGQUIT);
403                 (void)sigaddset(&nset, SIGTSTP);
404                 (void)sigaddset(&nset, SIGTTIN);
405                 (void)sigaddset(&nset, SIGTTOU);
406                 prepare_child(&nset, fileno(mtf), -1);
407                 if ((cp = value("sendmail")) != NULL)
408                         cp = expand(cp);
409                 else
410                         cp = _PATH_SENDMAIL;
411                 execv(cp, namelist);
412                 warn("%s", cp);
413                 _exit(1);
414         }
415         if (value("verbose") != NULL)
416                 (void)wait_child(pid);
417         else
418                 free_child(pid);
419 out:
420         (void)Fclose(mtf);
421 }
422
423 /*
424  * Fix the header by glopping all of the expanded names from
425  * the distribution list into the appropriate fields.
426  */
427 void
428 fixhead(struct header *hp, struct name *tolist)
429 {
430         struct name *np;
431
432         hp->h_to = NULL;
433         hp->h_cc = NULL;
434         hp->h_bcc = NULL;
435         for (np = tolist; np != NULL; np = np->n_flink) {
436                 /* Don't copy deleted addresses to the header */
437                 if (np->n_type & GDEL)
438                         continue;
439                 if ((np->n_type & GMASK) == GTO)
440                         hp->h_to =
441                             cat(hp->h_to, nalloc(np->n_name, np->n_type));
442                 else if ((np->n_type & GMASK) == GCC)
443                         hp->h_cc =
444                             cat(hp->h_cc, nalloc(np->n_name, np->n_type));
445                 else if ((np->n_type & GMASK) == GBCC)
446                         hp->h_bcc =
447                             cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
448         }
449 }
450
451 /*
452  * Prepend a header in front of the collected stuff
453  * and return the new file.
454  */
455 FILE *
456 infix(struct header *hp, FILE *fi)
457 {
458         FILE *nfo, *nfi;
459         int c, fd;
460         char tempname[PATHSIZE];
461
462         (void)snprintf(tempname, sizeof(tempname),
463             "%s/mail.RsXXXXXXXXXX", tmpdir);
464         if ((fd = mkstemp(tempname)) == -1 ||
465             (nfo = Fdopen(fd, "w")) == NULL) {
466                 warn("%s", tempname);
467                 return (fi);
468         }
469         if ((nfi = Fopen(tempname, "r")) == NULL) {
470                 warn("%s", tempname);
471                 (void)Fclose(nfo);
472                 (void)rm(tempname);
473                 return (fi);
474         }
475         (void)rm(tempname);
476         (void)puthead(hp, nfo,
477             GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA);
478         c = getc(fi);
479         while (c != EOF) {
480                 (void)putc(c, nfo);
481                 c = getc(fi);
482         }
483         if (ferror(fi)) {
484                 warnx("read");
485                 rewind(fi);
486                 return (fi);
487         }
488         (void)fflush(nfo);
489         if (ferror(nfo)) {
490                 warn("%s", tempname);
491                 (void)Fclose(nfo);
492                 (void)Fclose(nfi);
493                 rewind(fi);
494                 return (fi);
495         }
496         (void)Fclose(nfo);
497         (void)Fclose(fi);
498         rewind(nfi);
499         return (nfi);
500 }
501
502 /*
503  * Dump the to, subject, cc header on the
504  * passed file buffer.
505  */
506 int
507 puthead(struct header *hp, FILE *fo, int w)
508 {
509         int gotcha;
510
511         gotcha = 0;
512         if (hp->h_to != NULL && w & GTO)
513                 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
514         if (hp->h_subject != NULL && w & GSUBJECT)
515                 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
516         if (hp->h_cc != NULL && w & GCC)
517                 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
518         if (hp->h_bcc != NULL && w & GBCC)
519                 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
520         if (hp->h_replyto != NULL && w & GREPLYTO)
521                 fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++;
522         if (hp->h_inreplyto != NULL && w & GINREPLYTO)
523                 fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++;
524         if (gotcha && w & GNL)
525                 (void)putc('\n', fo);
526         return (0);
527 }
528
529 /*
530  * Format the given header line to not exceed 72 characters.
531  */
532 void
533 fmt(const char *str, struct name *np, FILE *fo, int comma)
534 {
535         int col, len;
536
537         comma = comma ? 1 : 0;
538         col = strlen(str);
539         if (col)
540                 fputs(str, fo);
541         for (; np != NULL; np = np->n_flink) {
542                 if (np->n_flink == NULL)
543                         comma = 0;
544                 len = strlen(np->n_name);
545                 col++;          /* for the space */
546                 if (col + len + comma > 72 && col > 4) {
547                         fprintf(fo, "\n    ");
548                         col = 4;
549                 } else
550                         fprintf(fo, " ");
551                 fputs(np->n_name, fo);
552                 if (comma)
553                         fprintf(fo, ",");
554                 col += len + comma;
555         }
556         fprintf(fo, "\n");
557 }
558
559 /*
560  * Save the outgoing mail on the passed file.
561  */
562
563 /*ARGSUSED*/
564 int
565 savemail(char name[], FILE *fi)
566 {
567         FILE *fo;
568         char buf[BUFSIZ];
569         int i;
570         time_t now;
571         mode_t saved_umask;
572
573         saved_umask = umask(077);
574         fo = Fopen(name, "a");
575         umask(saved_umask);
576
577         if (fo == NULL) {
578                 warn("%s", name);
579                 return (-1);
580         }
581         (void)time(&now);
582         fprintf(fo, "From %s %s", myname, ctime(&now));
583         while ((i = fread(buf, 1, sizeof(buf), fi)) > 0)
584                 (void)fwrite(buf, 1, i, fo);
585         fprintf(fo, "\n");
586         (void)fflush(fo);
587         if (ferror(fo))
588                 warn("%s", name);
589         (void)Fclose(fo);
590         rewind(fi);
591         return (0);
592 }