xref: /dragonfly/usr.bin/mail/names.c (revision 82c94b79bf89c02cf6a1b0cd528be2e0340facfc)
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 *
nalloc(char * str,int ntype)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 *
tailof(struct name * 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 *
extract(char * line,int ntype)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 *
detract(struct name * np,int ntype)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 *
yankword(char * ap,char * wbuf)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 *
yanklogin(char * ap,char * wbuf)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 *
outof(struct name * names,FILE * fo,struct header * hp)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
isfileaddr(char * name)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 *
usermap(struct name * names)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 *
gexpand(struct name * nlist,struct grouphead * gh,int metoo,int ntype)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 *
cat(struct name * n1,struct name * n2)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 **
unpack(struct name * np)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 *
elide(struct name * names)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 *
put(struct name * list,struct name * node)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
count(struct name * np)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 *
delname(struct name * np,char * 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 */
759