mail(1): call sendmail, not send-mail
[dragonfly.git] / usr.bin / mail / names.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)names.c  8.1 (Berkeley) 6/6/93
30  * $FreeBSD: src/usr.bin/mail/names.c,v 1.4.6.3 2003/01/06 05:46:03 mikeh Exp $
31  */
32
33 /*
34  * Mail -- a mail program
35  *
36  * Handle name lists.
37  */
38
39 #include "rcv.h"
40 #include <fcntl.h>
41 #include "extern.h"
42
43 /*
44  * Allocate a single element of a name list,
45  * initialize its name field to the passed
46  * name and return it.
47  */
48 struct name *
49 nalloc(char *str, int ntype)
50 {
51         struct name *np;
52
53         np = (struct name *)salloc(sizeof(*np));
54         np->n_flink = NULL;
55         np->n_blink = NULL;
56         np->n_type = ntype;
57         np->n_name = savestr(str);
58         return (np);
59 }
60
61 /*
62  * Find the tail of a list and return it.
63  */
64 struct name *
65 tailof(struct name *name)
66 {
67         struct name *np;
68
69         np = name;
70         if (np == NULL)
71                 return (NULL);
72         while (np->n_flink != NULL)
73                 np = np->n_flink;
74         return (np);
75 }
76
77 /*
78  * Extract a list of names from a line,
79  * and make a list of names from it.
80  * Return the list or NULL if none found.
81  */
82 struct name *
83 extract(char *line, int ntype)
84 {
85         char *cp, *nbuf;
86         struct name *top, *np, *t;
87
88         if (line == NULL || *line == '\0')
89                 return (NULL);
90         if ((nbuf = malloc(strlen(line) + 1)) == NULL)
91                 err(1, "Out of memory");
92         top = NULL;
93         np = NULL;
94         cp = line;
95         while ((cp = yankword(cp, nbuf)) != NULL) {
96                 t = nalloc(nbuf, ntype);
97                 if (top == NULL)
98                         top = t;
99                 else
100                         np->n_flink = t;
101                 t->n_blink = np;
102                 np = t;
103         }
104         free(nbuf);
105         return (top);
106 }
107
108 /*
109  * Turn a list of names into a string of the same names.
110  */
111 char *
112 detract(struct name *np, int ntype)
113 {
114         int s, comma;
115         char *cp, *top;
116         struct name *p;
117
118         comma = ntype & GCOMMA;
119         if (np == NULL)
120                 return (NULL);
121         ntype &= ~GCOMMA;
122         s = 0;
123         if (debug && comma)
124                 fprintf(stderr, "detract asked to insert commas\n");
125         for (p = np; p != NULL; p = p->n_flink) {
126                 if (ntype && (p->n_type & GMASK) != ntype)
127                         continue;
128                 s += strlen(p->n_name) + 1;
129                 if (comma)
130                         s++;
131         }
132         if (s == 0)
133                 return (NULL);
134         s += 2;
135         top = salloc(s);
136         cp = top;
137         for (p = np; p != NULL; p = p->n_flink) {
138                 if (ntype && (p->n_type & GMASK) != ntype)
139                         continue;
140                 cp += strlcpy(cp, p->n_name, strlen(p->n_name) + 1);
141                 if (comma && p->n_flink != NULL)
142                         *cp++ = ',';
143                 *cp++ = ' ';
144         }
145         *--cp = '\0';
146         if (comma && *--cp == ',')
147                 *cp = '\0';
148         return (top);
149 }
150
151 /*
152  * Grab a single word (liberal word)
153  * Throw away things between ()'s, and take anything between <>.
154  */
155 char *
156 yankword(char *ap, char *wbuf)
157 {
158         char *cp, *cp2;
159
160         cp = ap;
161         for (;;) {
162                 if (*cp == '\0')
163                         return (NULL);
164                 if (*cp == '(') {
165                         int nesting = 0;
166
167                         while (*cp != '\0') {
168                                 switch (*cp++) {
169                                 case '(':
170                                         nesting++;
171                                         break;
172                                 case ')':
173                                         --nesting;
174                                         break;
175                                 }
176                                 if (nesting <= 0)
177                                         break;
178                         }
179                 } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
180                         cp++;
181                 else
182                         break;
183         }
184         if (*cp ==  '<')
185                 for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
186                         ;
187         else
188                 for (cp2 = wbuf; *cp != '\0' && strchr(" \t,(", *cp) == NULL;
189                     *cp2++ = *cp++)
190                         ;
191         *cp2 = '\0';
192         return (cp);
193 }
194
195 /*
196  * Grab a single login name (liberal word).
197  * Throw away things between ()'s, take anything between <>,
198  * and look for words before metacharacters %, @, !.
199  */
200 char *
201 yanklogin(char *ap, char *wbuf)
202 {
203         char *cp, *cp2, *cp_temp;
204         int n;
205
206         cp = ap;
207         for (;;) {
208                 if (*cp == '\0')
209                         return (NULL);
210                 if (*cp == '(') {
211                         int nesting = 0;
212
213                         while (*cp != '\0') {
214                                 switch (*cp++) {
215                                 case '(':
216                                         nesting++;
217                                         break;
218                                 case ')':
219                                         nesting--;
220                                         break;
221                                 }
222                                 if (nesting <= 0)
223                                         break;
224                         }
225                 } else if (*cp == ' ' || *cp == '\t' || *cp == ',') {
226                         cp++;
227                 } else {
228                         break;
229                 }
230         }
231
232         /*
233          * Now, let's go forward till we meet the needed character,
234          * and step one word back.
235          */
236
237         /* First, remember current point. */
238         cp_temp = cp;
239         n = 0;
240
241         /*
242          * Note that we look ahead in a cycle. This is safe, since
243          * non-end of string is checked first.
244          */
245         while (*cp != '\0' && strchr("@%!", *(cp + 1)) == NULL)
246                 cp ++;
247
248         /*
249          * Now, start stepping back to the first non-word character,
250          * while counting the number of symbols in a word.
251          */
252         while (cp != cp_temp && strchr(" \t,<>", *(cp - 1)) == NULL) {
253                 n++;
254                 cp--;
255         }
256
257         /* Finally, grab the word forward. */
258         cp2 = wbuf;
259         while (n >= 0) {
260                 *cp2++ = *cp++;
261                 n--;
262         }
263
264         *cp2 = '\0';
265         return (cp);
266 }
267
268 /*
269  * For each recipient in the passed name list with a /
270  * in the name, append the message to the end of the named file
271  * and remove him from the recipient list.
272  *
273  * Recipients whose name begins with | are piped through the given
274  * program and removed.
275  */
276 struct name *
277 outof(struct name *names, FILE *fo, struct header *hp)
278 {
279         int c, ispipe;
280         struct name *np, *top;
281         time_t now;
282         char *date, *fname;
283         FILE *fout, *fin;
284
285         top = names;
286         np = names;
287         time(&now);
288         date = ctime(&now);
289         while (np != NULL) {
290                 if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
291                         np = np->n_flink;
292                         continue;
293                 }
294                 ispipe = np->n_name[0] == '|';
295                 if (ispipe)
296                         fname = np->n_name+1;
297                 else
298                         fname = expand(np->n_name);
299
300                 /*
301                  * See if we have copied the complete message out yet.
302                  * If not, do so.
303                  */
304
305                 if (image < 0) {
306                         int fd;
307                         char tempname[PATHSIZE];
308
309                         snprintf(tempname, sizeof(tempname),
310                                  "%s/mail.ReXXXXXXXXXX", tmpdir);
311                         if ((fd = mkstemp(tempname)) == -1 ||
312                             (fout = Fdopen(fd, "a")) == NULL) {
313                                 warn("%s", tempname);
314                                 senderr++;
315                                 goto cant;
316                         }
317                         image = open(tempname, O_RDWR);
318                         rm(tempname);
319                         if (image < 0) {
320                                 warn("%s", tempname);
321                                 senderr++;
322                                 Fclose(fout);
323                                 goto cant;
324                         }
325                         fcntl(image, F_SETFD, 1);
326                         fprintf(fout, "From %s %s", myname, date);
327                         puthead(hp, fout,
328                             GTO|GSUBJECT|GCC|GREPLYTO|GINREPLYTO|GNL);
329                         while ((c = getc(fo)) != EOF)
330                                 putc(c, fout);
331                         rewind(fo);
332                         fprintf(fout, "\n");
333                         fflush(fout);
334                         if (ferror(fout)) {
335                                 warn("%s", tempname);
336                                 senderr++;
337                                 Fclose(fout);
338                                 goto cant;
339                         }
340                         Fclose(fout);
341                 }
342
343                 /*
344                  * Now either copy "image" to the desired file
345                  * or give it as the standard input to the desired
346                  * program as appropriate.
347                  */
348
349                 if (ispipe) {
350                         int pid;
351                         char *sh;
352                         sigset_t nset;
353
354                         /*
355                          * XXX
356                          * We can't really reuse the same image file,
357                          * because multiple piped recipients will
358                          * share the same lseek location and trample
359                          * on one another.
360                          */
361                         if ((sh = value("SHELL")) == NULL)
362                                 sh = _PATH_CSHELL;
363                         sigemptyset(&nset);
364                         sigaddset(&nset, SIGHUP);
365                         sigaddset(&nset, SIGINT);
366                         sigaddset(&nset, SIGQUIT);
367                         pid = start_command(sh, &nset, image, -1, "-c", fname,
368                             NULL);
369                         if (pid < 0) {
370                                 senderr++;
371                                 goto cant;
372                         }
373                         free_child(pid);
374                 } else {
375                         int f;
376                         if ((fout = Fopen(fname, "a")) == NULL) {
377                                 warn("%s", fname);
378                                 senderr++;
379                                 goto cant;
380                         }
381                         if ((f = dup(image)) < 0) {
382                                 warn("dup");
383                                 fin = NULL;
384                         } else
385                                 fin = Fdopen(f, "r");
386                         if (fin == NULL) {
387                                 fprintf(stderr, "Can't reopen image\n");
388                                 Fclose(fout);
389                                 senderr++;
390                                 goto cant;
391                         }
392                         rewind(fin);
393                         while ((c = getc(fin)) != EOF)
394                                 putc(c, fout);
395                         if (ferror(fout)) {
396                                 warnx("%s", fname);
397                                 senderr++;
398                                 Fclose(fout);
399                                 Fclose(fin);
400                                 goto cant;
401                         }
402                         Fclose(fout);
403                         Fclose(fin);
404                 }
405 cant:
406                 /*
407                  * In days of old we removed the entry from the
408                  * the list; now for sake of header expansion
409                  * we leave it in and mark it as deleted.
410                  */
411                 np->n_type |= GDEL;
412                 np = np->n_flink;
413         }
414         if (image >= 0) {
415                 close(image);
416                 image = -1;
417         }
418         return (top);
419 }
420
421 /*
422  * Determine if the passed address is a local "send to file" address.
423  * If any of the network metacharacters precedes any slashes, it can't
424  * be a filename.  We cheat with .'s to allow path names like ./...
425  */
426 int
427 isfileaddr(char *name)
428 {
429         char *cp;
430
431         if (*name == '+')
432                 return (1);
433         for (cp = name; *cp != '\0'; cp++) {
434                 if (*cp == '!' || *cp == '%' || *cp == '@')
435                         return (0);
436                 if (*cp == '/')
437                         return (1);
438         }
439         return (0);
440 }
441
442 /*
443  * Map all of the aliased users in the invoker's mailrc
444  * file and insert them into the list.
445  * Changed after all these months of service to recursively
446  * expand names (2/14/80).
447  */
448
449 struct name *
450 usermap(struct name *names)
451 {
452         struct name *new, *np, *cp;
453         struct grouphead *gh;
454         int metoo;
455
456         new = NULL;
457         np = names;
458         metoo = (value("metoo") != NULL);
459         while (np != NULL) {
460                 if (np->n_name[0] == '\\') {
461                         cp = np->n_flink;
462                         new = put(new, np);
463                         np = cp;
464                         continue;
465                 }
466                 gh = findgroup(np->n_name);
467                 cp = np->n_flink;
468                 if (gh != NULL)
469                         new = gexpand(new, gh, metoo, np->n_type);
470                 else
471                         new = put(new, np);
472                 np = cp;
473         }
474         return (new);
475 }
476
477 /*
478  * Recursively expand a group name.  We limit the expansion to some
479  * fixed level to keep things from going haywire.
480  * Direct recursion is not expanded for convenience.
481  */
482
483 struct name *
484 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
485 {
486         struct group *gp;
487         struct grouphead *ngh;
488         struct name *np;
489         static int depth;
490         char *cp;
491
492         if (depth > MAXEXP) {
493                 printf("Expanding alias to depth larger than %d\n", MAXEXP);
494                 return (nlist);
495         }
496         depth++;
497         for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
498                 cp = gp->ge_name;
499                 if (*cp == '\\')
500                         goto quote;
501                 if (strcmp(cp, gh->g_name) == 0)
502                         goto quote;
503                 if ((ngh = findgroup(cp)) != NULL) {
504                         nlist = gexpand(nlist, ngh, metoo, ntype);
505                         continue;
506                 }
507 quote:
508                 np = nalloc(cp, ntype);
509                 /*
510                  * At this point should allow to expand
511                  * to self if only person in group
512                  */
513                 if (gp == gh->g_list && gp->ge_link == NULL)
514                         goto skip;
515                 if (!metoo && strcmp(cp, myname) == 0)
516                         np->n_type |= GDEL;
517 skip:
518                 nlist = put(nlist, np);
519         }
520         depth--;
521         return (nlist);
522 }
523
524 /*
525  * Concatenate the two passed name lists, return the result.
526  */
527 struct name *
528 cat(struct name *n1, struct name *n2)
529 {
530         struct name *tail;
531
532         if (n1 == NULL)
533                 return (n2);
534         if (n2 == NULL)
535                 return (n1);
536         tail = tailof(n1);
537         tail->n_flink = n2;
538         n2->n_blink = tail;
539         return (n1);
540 }
541
542 /*
543  * Unpack the name list onto a vector of strings.
544  * Return an error if the name list won't fit.
545  */
546 char **
547 unpack(struct name *np)
548 {
549         char **ap, **top;
550         struct name *n;
551         int t, extra, metoo, verbose;
552
553         n = np;
554         if ((t = count(n)) == 0)
555                 errx(1, "No names to unpack");
556         /*
557          * Compute the number of extra arguments we will need.
558          * We need at least two extra -- one for "mail" and one for
559          * the terminating 0 pointer.  Additional spots may be needed
560          * to pass along -f to the host mailer.
561          */
562         extra = 2;
563         extra++;
564         metoo = value("metoo") != NULL;
565         if (metoo)
566                 extra++;
567         verbose = value("verbose") != NULL;
568         if (verbose)
569                 extra++;
570         top = (char **)salloc((t + extra) * sizeof(*top));
571         ap = top;
572         *ap++ = "sendmail";
573         *ap++ = "-i";
574         if (metoo)
575                 *ap++ = "-m";
576         if (verbose)
577                 *ap++ = "-v";
578         for (; n != NULL; n = n->n_flink)
579                 if ((n->n_type & GDEL) == 0)
580                         *ap++ = n->n_name;
581         *ap = NULL;
582         return (top);
583 }
584
585 /*
586  * Remove all of the duplicates from the passed name list by
587  * insertion sorting them, then checking for dups.
588  * Return the head of the new list.
589  */
590 struct name *
591 elide(struct name *names)
592 {
593         struct name *np, *t, *new;
594         struct name *x;
595
596         if (names == NULL)
597                 return (NULL);
598         new = names;
599         np = names;
600         np = np->n_flink;
601         if (np != NULL)
602                 np->n_blink = NULL;
603         new->n_flink = NULL;
604         while (np != NULL) {
605                 t = new;
606                 while (strcasecmp(t->n_name, np->n_name) < 0) {
607                         if (t->n_flink == NULL)
608                                 break;
609                         t = t->n_flink;
610                 }
611
612                 /*
613                  * If we ran out of t's, put the new entry after
614                  * the current value of t.
615                  */
616
617                 if (strcasecmp(t->n_name, np->n_name) < 0) {
618                         t->n_flink = np;
619                         np->n_blink = t;
620                         t = np;
621                         np = np->n_flink;
622                         t->n_flink = NULL;
623                         continue;
624                 }
625
626                 /*
627                  * Otherwise, put the new entry in front of the
628                  * current t.  If at the front of the list,
629                  * the new guy becomes the new head of the list.
630                  */
631
632                 if (t == new) {
633                         t = np;
634                         np = np->n_flink;
635                         t->n_flink = new;
636                         new->n_blink = t;
637                         t->n_blink = NULL;
638                         new = t;
639                         continue;
640                 }
641
642                 /*
643                  * The normal case -- we are inserting into the
644                  * middle of the list.
645                  */
646
647                 x = np;
648                 np = np->n_flink;
649                 x->n_flink = t;
650                 x->n_blink = t->n_blink;
651                 t->n_blink->n_flink = x;
652                 t->n_blink = x;
653         }
654
655         /*
656          * Now the list headed up by new is sorted.
657          * Go through it and remove duplicates.
658          */
659
660         np = new;
661         while (np != NULL) {
662                 t = np;
663                 while (t->n_flink != NULL &&
664                     strcasecmp(np->n_name, t->n_flink->n_name) == 0)
665                         t = t->n_flink;
666                 if (t == np || t == NULL) {
667                         np = np->n_flink;
668                         continue;
669                 }
670
671                 /*
672                  * Now t points to the last entry with the same name
673                  * as np.  Make np point beyond t.
674                  */
675
676                 np->n_flink = t->n_flink;
677                 if (t->n_flink != NULL)
678                         t->n_flink->n_blink = np;
679                 np = np->n_flink;
680         }
681         return (new);
682 }
683
684 /*
685  * Put another node onto a list of names and return
686  * the list.
687  */
688 struct name *
689 put(struct name *list, struct name *node)
690 {
691         node->n_flink = list;
692         node->n_blink = NULL;
693         if (list != NULL)
694                 list->n_blink = node;
695         return (node);
696 }
697
698 /*
699  * Determine the number of undeleted elements in
700  * a name list and return it.
701  */
702 int
703 count(struct name *np)
704 {
705         int c;
706
707         for (c = 0; np != NULL; np = np->n_flink)
708                 if ((np->n_type & GDEL) == 0)
709                         c++;
710         return (c);
711 }
712
713 /*
714  * Delete the given name from a namelist.
715  */
716 struct name *
717 delname(struct name *np, char *name)
718 {
719         struct name *p;
720
721         for (p = np; p != NULL; p = p->n_flink)
722                 if (strcasecmp(p->n_name, name) == 0) {
723                         if (p->n_blink == NULL) {
724                                 if (p->n_flink != NULL)
725                                         p->n_flink->n_blink = NULL;
726                                 np = p->n_flink;
727                                 continue;
728                         }
729                         if (p->n_flink == NULL) {
730                                 if (p->n_blink != NULL)
731                                         p->n_blink->n_flink = NULL;
732                                 continue;
733                         }
734                         p->n_blink->n_flink = p->n_flink;
735                         p->n_flink->n_blink = p->n_blink;
736                 }
737         return (np);
738 }
739
740 /*
741  * Pretty print a name list
742  * Uncomment it if you need it.
743  */
744
745 /*
746 void
747 prettyprint(struct name *name)
748 {
749         struct name *np;
750
751         np = name;
752         while (np != NULL) {
753                 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
754                 np = np->n_flink;
755         }
756         fprintf(stderr, "\n");
757 }
758 */