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