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