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