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