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