names.c: fix compiler warnings
[s-mailx.git] / names.c
blobe4677c44918f2240d20c6659f6927fcc51fe0d3c
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)names.c 2.22 (gritter) 3/4/06";
43 #endif
44 #endif /* not lint */
47 * Mail -- a mail program
49 * Handle name lists.
52 #include "rcv.h"
53 #include "extern.h"
54 #include <sys/stat.h>
55 #include <fcntl.h>
56 #include <time.h>
57 #include <unistd.h>
59 static struct name *tailof(struct name *name);
60 static struct name *extract1(char *line, enum gfield ntype, char *separators,
61 int copypfx);
62 static char *yankword(char *ap, char *wbuf, char *separators, int copypfx);
63 static int same_name(char *n1, char *n2);
64 static struct name *gexpand(struct name *nlist, struct grouphead *gh,
65 int metoo, int ntype);
66 static struct name *put(struct name *list, struct name *node);
67 static struct name *delname(struct name *np, char *name);
70 * Allocate a single element of a name list,
71 * initialize its name field to the passed
72 * name and return it.
74 struct name *
75 nalloc(char *str, enum gfield ntype)
77 struct name *np;
78 struct str in, out;
80 /*LINTED*/
81 np = (struct name *)salloc(sizeof *np);
82 np->n_flink = NULL;
83 np->n_blink = NULL;
84 np->n_type = ntype;
85 if (ntype & GFULL) {
86 np->n_name = savestr(skin(str));
87 if (strcmp(np->n_name, str)) {
88 in.s = str;
89 in.l = strlen(str);
90 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
91 np->n_fullname = savestr(out.s);
92 free(out.s);
93 } else
94 np->n_fullname = np->n_name;
95 } else if (ntype & GSKIN)
96 np->n_fullname = np->n_name = savestr(skin(str));
97 else
98 np->n_fullname = np->n_name = savestr(str);
99 return(np);
103 * Find the tail of a list and return it.
105 static struct name *
106 tailof(struct name *name)
108 struct name *np;
110 np = name;
111 if (np == NULL)
112 return(NULL);
113 while (np->n_flink != NULL)
114 np = np->n_flink;
115 return(np);
119 * Extract a list of names from a line,
120 * and make a list of names from it.
121 * Return the list or NULL if none found.
123 struct name *
124 extract(char *line, enum gfield ntype)
126 return extract1(line, ntype, " \t,(", 0);
129 struct name *
130 sextract(char *line, enum gfield ntype)
132 if (line && strpbrk(line, ",\"\\(<"))
133 return extract1(line, ntype, ",", 1);
134 else
135 return extract(line, ntype);
138 static struct name *
139 extract1(char *line, enum gfield ntype, char *separators, int copypfx)
141 char *cp, *nbuf;
142 struct name *top, *np, *t;
144 if (line == NULL || *line == '\0')
145 return NULL;
146 top = NULL;
147 np = NULL;
148 cp = line;
149 nbuf = ac_alloc(strlen(line) + 1);
150 while ((cp = yankword(cp, nbuf, separators, copypfx)) != NULL) {
151 t = nalloc(nbuf, ntype);
152 if (top == NULL)
153 top = t;
154 else
155 np->n_flink = t;
156 t->n_blink = np;
157 np = t;
159 ac_free(nbuf);
160 return top;
164 * Turn a list of names into a string of the same names.
166 char *
167 detract(struct name *np, enum gfield ntype)
169 int s;
170 char *cp, *top;
171 struct name *p;
172 int comma;
174 comma = ntype & GCOMMA;
175 if (np == NULL)
176 return(NULL);
177 ntype &= ~GCOMMA;
178 s = 0;
179 if ((debug || value("debug")) && comma)
180 fprintf(stderr, catgets(catd, CATSET, 145,
181 "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_fullname) + 1;
186 if (comma)
187 s++;
189 if (s == 0)
190 return(NULL);
191 s += 2;
192 top = salloc(s);
193 cp = top;
194 for (p = np; p != NULL; p = p->n_flink) {
195 if (ntype && (p->n_type & GMASK) != ntype)
196 continue;
197 cp = sstpcpy(cp, p->n_fullname);
198 if (comma && p->n_flink != NULL)
199 *cp++ = ',';
200 *cp++ = ' ';
202 *--cp = 0;
203 if (comma && *--cp == ',')
204 *cp = 0;
205 return(top);
209 * Grab a single word (liberal word)
210 * Throw away things between ()'s, and take anything between <>.
212 static char *
213 yankword(char *ap, char *wbuf, char *separators, int copypfx)
215 char *cp, *pp, *wp;
217 cp = ap;
218 wp = wbuf;
219 while (blankchar(*cp & 0377) || *cp == ',')
220 cp++;
221 pp = cp;
222 if ((cp = nexttoken(cp)) == NULL)
223 return NULL;
224 if (copypfx)
225 while (pp < cp)
226 *wp++ = *pp++;
227 if (*cp == '<')
228 while (*cp && (*wp++ = *cp++) != '>');
229 else {
230 int incomm = 0;
232 while (*cp && (incomm || !strchr(separators, *cp))) {
233 if (*cp == '\"') {
234 if (cp == ap || *(cp - 1) != '\\') {
235 if (incomm)
236 incomm--;
237 else
238 incomm++;
239 *wp++ = '\"';
240 } else if (cp != ap) {
241 *(wp - 1) = '\"';
243 cp++;
244 continue;
246 *wp++ = *cp++;
249 *wp = '\0';
250 return cp;
254 * For each recipient in the passed name list with a /
255 * in the name, append the message to the end of the named file
256 * and remove him from the recipient list.
258 * Recipients whose name begins with | are piped through the given
259 * program and removed.
261 /*ARGSUSED 3*/
262 struct name *
263 outof(struct name *names, FILE *fo, struct header *hp)
265 int c, lastc;
266 struct name *np, *top;
267 time_t now;
268 char *date, *fname;
269 FILE *fout, *fin;
270 int ispipe;
271 (void)hp;
273 top = names;
274 np = names;
275 time(&now);
276 date = ctime(&now);
277 while (np != NULL) {
278 if (!is_fileaddr(np->n_name) && np->n_name[0] != '|') {
279 np = np->n_flink;
280 continue;
282 ispipe = np->n_name[0] == '|';
283 if (ispipe)
284 fname = np->n_name+1;
285 else
286 fname = expand(np->n_name);
289 * See if we have copied the complete message out yet.
290 * If not, do so.
293 if (image < 0) {
294 char *tempEdit;
296 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
297 == NULL) {
298 perror(catgets(catd, CATSET, 146,
299 "temporary edit file"));
300 senderr++;
301 goto cant;
303 image = open(tempEdit, O_RDWR);
304 unlink(tempEdit);
305 Ftfree(&tempEdit);
306 if (image < 0) {
307 perror(catgets(catd, CATSET, 147,
308 "temporary edit file"));
309 senderr++;
310 Fclose(fout);
311 goto cant;
313 fcntl(image, F_SETFD, FD_CLOEXEC);
314 fprintf(fout, "From %s %s", myname, date);
315 c = EOF;
316 while (lastc = c, (c = getc(fo)) != EOF)
317 putc(c, fout);
318 rewind(fo);
319 if (lastc != '\n')
320 putc('\n', fout);
321 putc('\n', fout);
322 fflush(fout);
323 if (ferror(fout))
324 perror(catgets(catd, CATSET, 148,
325 "temporary edit file"));
326 Fclose(fout);
330 * Now either copy "image" to the desired file
331 * or give it as the standard input to the desired
332 * program as appropriate.
335 if (ispipe) {
336 int pid;
337 char *shell;
338 sigset_t nset;
341 * XXX
342 * We can't really reuse the same image file,
343 * because multiple piped recipients will
344 * share the same lseek location and trample
345 * on one another.
347 if ((shell = value("SHELL")) == NULL)
348 shell = SHELL;
349 sigemptyset(&nset);
350 sigaddset(&nset, SIGHUP);
351 sigaddset(&nset, SIGINT);
352 sigaddset(&nset, SIGQUIT);
353 pid = start_command(shell, &nset,
354 image, -1, "-c", fname, NULL);
355 if (pid < 0) {
356 senderr++;
357 goto cant;
359 free_child(pid);
360 } else {
361 int f;
362 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
363 perror(fname);
364 senderr++;
365 goto cant;
367 if ((f = dup(image)) < 0) {
368 perror("dup");
369 fin = NULL;
370 } else
371 fin = Fdopen(f, "r");
372 if (fin == NULL) {
373 fprintf(stderr, catgets(catd, CATSET, 149,
374 "Can't reopen image\n"));
375 Fclose(fout);
376 senderr++;
377 goto cant;
379 rewind(fin);
380 while ((c = getc(fin)) != EOF)
381 putc(c, fout);
382 if (ferror(fout))
383 senderr++, perror(fname);
384 Fclose(fout);
385 Fclose(fin);
387 cant:
389 * In days of old we removed the entry from the
390 * the list; now for sake of header expansion
391 * we leave it in and mark it as deleted.
393 np->n_type |= GDEL;
394 np = np->n_flink;
396 if (image >= 0) {
397 close(image);
398 image = -1;
400 return(top);
404 * Determine if the passed address is a local "send to file" address.
405 * If any of the network metacharacters precedes any slashes, it can't
406 * be a filename. We cheat with .'s to allow path names like ./...
408 int
409 is_fileaddr(char *name)
411 char *cp;
413 if (strchr(name, '@') != NULL)
414 return 0;
415 if (*name == '+')
416 return 1;
417 for (cp = name; *cp; cp++) {
418 if (*cp == '!' || *cp == '%')
419 return 0;
420 if (*cp == '/')
421 return 1;
423 return 0;
426 static int
427 same_name(char *n1, char *n2)
429 int c1, c2;
431 if (value("allnet") != NULL) {
432 do {
433 c1 = (*n1++ & 0377);
434 c2 = (*n2++ & 0377);
435 c1 = lowerconv(c1);
436 c2 = lowerconv(c2);
437 if (c1 != c2)
438 return 0;
439 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
440 return 1;
441 } else
442 return asccasecmp(n1, n2) == 0;
446 * Map all of the aliased users in the invoker's mailrc
447 * file and insert them into the list.
448 * Changed after all these months of service to recursively
449 * expand names (2/14/80).
452 struct name *
453 usermap(struct name *names)
455 struct name *new, *np, *cp;
456 struct grouphead *gh;
457 int metoo;
459 new = NULL;
460 np = names;
461 metoo = (value("metoo") != NULL);
462 while (np != NULL) {
463 if (np->n_name[0] == '\\') {
464 cp = np->n_flink;
465 new = put(new, np);
466 np = cp;
467 continue;
469 gh = findgroup(np->n_name);
470 cp = np->n_flink;
471 if (gh != NULL)
472 new = gexpand(new, gh, metoo, np->n_type);
473 else
474 new = put(new, np);
475 np = cp;
477 return(new);
481 * Recursively expand a group name. We limit the expansion to some
482 * fixed level to keep things from going haywire.
483 * Direct recursion is not expanded for convenience.
486 static struct name *
487 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
489 struct group *gp;
490 struct grouphead *ngh;
491 struct name *np;
492 static int depth;
493 char *cp;
495 if (depth > MAXEXP) {
496 printf(catgets(catd, CATSET, 150,
497 "Expanding alias to depth larger than %d\n"), MAXEXP);
498 return(nlist);
500 depth++;
501 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
502 cp = gp->ge_name;
503 if (*cp == '\\')
504 goto quote;
505 if (strcmp(cp, gh->g_name) == 0)
506 goto quote;
507 if ((ngh = findgroup(cp)) != NULL) {
508 nlist = gexpand(nlist, ngh, metoo, ntype);
509 continue;
511 quote:
512 np = nalloc(cp, ntype|GFULL);
514 * At this point should allow to expand
515 * to self if only person in group
517 if (gp == gh->g_list && gp->ge_link == NULL)
518 goto skip;
519 if (!metoo && same_name(cp, myname))
520 np->n_type |= GDEL;
521 skip:
522 nlist = put(nlist, np);
524 depth--;
525 return(nlist);
529 * Concatenate the two passed name lists, return the result.
531 struct name *
532 cat(struct name *n1, struct name *n2)
534 struct name *tail;
536 if (n1 == NULL)
537 return(n2);
538 if (n2 == NULL)
539 return(n1);
540 tail = tailof(n1);
541 tail->n_flink = n2;
542 n2->n_blink = tail;
543 return(n1);
547 * Unpack the name list onto a vector of strings.
548 * Return an error if the name list won't fit.
550 char **
551 unpack(struct name *np)
553 char **ap, **top;
554 struct name *n;
555 int t, extra, metoo, verbose;
557 n = np;
558 if ((t = count(n)) == 0)
559 panic(catgets(catd, CATSET, 151, "No names to unpack"));
561 * Compute the number of extra arguments we will need.
562 * We need at least two extra -- one for "mail" and one for
563 * the terminating 0 pointer. Additional spots may be needed
564 * to pass along -f to the host mailer.
566 extra = 2;
567 extra++;
568 metoo = value("metoo") != NULL;
569 if (metoo)
570 extra++;
571 verbose = value("verbose") != NULL;
572 if (verbose)
573 extra++;
574 /*LINTED*/
575 top = (char **)salloc((t + extra) * sizeof *top);
576 ap = top;
577 *ap++ = "send-mail";
578 *ap++ = "-i";
579 if (metoo)
580 *ap++ = "-m";
581 if (verbose)
582 *ap++ = "-v";
583 for (; n != NULL; n = n->n_flink)
584 if ((n->n_type & GDEL) == 0)
585 *ap++ = n->n_name;
586 *ap = NULL;
587 return(top);
591 * Remove all of the duplicates from the passed name list by
592 * insertion sorting them, then checking for dups.
593 * Return the head of the new list.
595 struct name *
596 elide(struct name *names)
598 struct name *np, *t, *new;
599 struct name *x;
601 if (names == NULL)
602 return(NULL);
603 new = names;
604 np = names;
605 np = np->n_flink;
606 if (np != NULL)
607 np->n_blink = NULL;
608 new->n_flink = NULL;
609 while (np != NULL) {
610 t = new;
611 while (asccasecmp(t->n_name, np->n_name) < 0) {
612 if (t->n_flink == NULL)
613 break;
614 t = t->n_flink;
618 * If we ran out of t's, put the new entry after
619 * the current value of t.
622 if (asccasecmp(t->n_name, np->n_name) < 0) {
623 t->n_flink = np;
624 np->n_blink = t;
625 t = np;
626 np = np->n_flink;
627 t->n_flink = NULL;
628 continue;
632 * Otherwise, put the new entry in front of the
633 * current t. If at the front of the list,
634 * the new guy becomes the new head of the list.
637 if (t == new) {
638 t = np;
639 np = np->n_flink;
640 t->n_flink = new;
641 new->n_blink = t;
642 t->n_blink = NULL;
643 new = t;
644 continue;
648 * The normal case -- we are inserting into the
649 * middle of the list.
652 x = np;
653 np = np->n_flink;
654 x->n_flink = t;
655 x->n_blink = t->n_blink;
656 t->n_blink->n_flink = x;
657 t->n_blink = x;
661 * Now the list headed up by new is sorted.
662 * Go through it and remove duplicates.
665 np = new;
666 while (np != NULL) {
667 t = np;
668 while (t->n_flink != NULL &&
669 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
670 t = t->n_flink;
671 if (t == np || t == NULL) {
672 np = np->n_flink;
673 continue;
677 * Now t points to the last entry with the same name
678 * as np. Make np point beyond t.
681 np->n_flink = t->n_flink;
682 if (t->n_flink != NULL)
683 t->n_flink->n_blink = np;
684 np = np->n_flink;
686 return(new);
690 * Put another node onto a list of names and return
691 * the list.
693 static struct name *
694 put(struct name *list, struct name *node)
696 node->n_flink = list;
697 node->n_blink = NULL;
698 if (list != NULL)
699 list->n_blink = node;
700 return(node);
704 * Determine the number of undeleted elements in
705 * a name list and return it.
707 int
708 count(struct name *np)
710 int c;
712 for (c = 0; np != NULL; np = np->n_flink)
713 if ((np->n_type & GDEL) == 0)
714 c++;
715 return c;
719 * Delete the given name from a namelist.
721 static struct name *
722 delname(struct name *np, char *name)
724 struct name *p;
726 for (p = np; p != NULL; p = p->n_flink)
727 if (same_name(p->n_name, name)) {
728 if (p->n_blink == NULL) {
729 if (p->n_flink != NULL)
730 p->n_flink->n_blink = NULL;
731 np = p->n_flink;
732 continue;
734 if (p->n_flink == NULL) {
735 if (p->n_blink != NULL)
736 p->n_blink->n_flink = NULL;
737 continue;
739 p->n_blink->n_flink = p->n_flink;
740 p->n_flink->n_blink = p->n_blink;
742 return np;
746 * Pretty print a name list
747 * Uncomment it if you need it.
751 void
752 prettyprint(struct name *name)
754 struct name *np;
756 np = name;
757 while (np != NULL) {
758 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
759 np = np->n_flink;
761 fprintf(stderr, "\n");
765 struct name *
766 delete_alternates(struct name *np)
768 struct name *xp;
769 char **ap;
771 np = delname(np, myname);
772 if (altnames)
773 for (ap = altnames; *ap; ap++)
774 np = delname(np, *ap);
775 if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL)
776 while (xp) {
777 np = delname(np, xp->n_name);
778 xp = xp->n_flink;
780 if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
781 while (xp) {
782 np = delname(np, xp->n_name);
783 xp = xp->n_flink;
785 if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL)
786 while (xp) {
787 np = delname(np, xp->n_name);
788 xp = xp->n_flink;
790 return np;
794 is_myname(char *name)
796 struct name *xp;
797 char **ap;
799 if (same_name(myname, name))
800 return 1;
801 if (altnames)
802 for (ap = altnames; *ap; ap++)
803 if (same_name(*ap, name))
804 return 1;
805 if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL)
806 while (xp) {
807 if (same_name(xp->n_name, name))
808 return 1;
809 xp = xp->n_flink;
811 if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
812 while (xp) {
813 if (same_name(xp->n_name, name))
814 return 1;
815 xp = xp->n_flink;
817 if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL)
818 while (xp) {
819 if (same_name(xp->n_name, name))
820 return 1;
821 xp = xp->n_flink;
823 return 0;