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