base64.c: fix compiler warnings
[s-mailx.git] / names.c
blob66e976bcced1c2909cd96f5c3fa9de03f143f0ae
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)names.c 2.22 (gritter) 3/4/06";
42 #endif
43 #endif /* not lint */
46 * Mail -- a mail program
48 * Handle name lists.
51 #include "rcv.h"
52 #include "extern.h"
53 #include <sys/stat.h>
54 #include <fcntl.h>
55 #include <time.h>
56 #include <unistd.h>
58 static struct name *tailof(struct name *name);
59 static struct name *extract1(char *line, enum gfield ntype, char *separators,
60 int copypfx);
61 static char *yankword(char *ap, char *wbuf, char *separators, int copypfx);
62 static int same_name(char *n1, char *n2);
63 static struct name *gexpand(struct name *nlist, struct grouphead *gh,
64 int metoo, int ntype);
65 static struct name *put(struct name *list, struct name *node);
66 static struct name *delname(struct name *np, char *name);
69 * Allocate a single element of a name list,
70 * initialize its name field to the passed
71 * name and return it.
73 struct name *
74 nalloc(char *str, enum gfield ntype)
76 struct name *np;
77 struct str in, out;
79 /*LINTED*/
80 np = (struct name *)salloc(sizeof *np);
81 np->n_flink = NULL;
82 np->n_blink = NULL;
83 np->n_type = ntype;
84 if (ntype & GFULL) {
85 np->n_name = savestr(skin(str));
86 if (strcmp(np->n_name, str)) {
87 in.s = str;
88 in.l = strlen(str);
89 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
90 np->n_fullname = savestr(out.s);
91 free(out.s);
92 } else
93 np->n_fullname = np->n_name;
94 } else if (ntype & GSKIN)
95 np->n_fullname = np->n_name = savestr(skin(str));
96 else
97 np->n_fullname = np->n_name = savestr(str);
98 return(np);
102 * Find the tail of a list and return it.
104 static struct name *
105 tailof(struct name *name)
107 struct name *np;
109 np = name;
110 if (np == NULL)
111 return(NULL);
112 while (np->n_flink != NULL)
113 np = np->n_flink;
114 return(np);
118 * Extract a list of names from a line,
119 * and make a list of names from it.
120 * Return the list or NULL if none found.
122 struct name *
123 extract(char *line, enum gfield ntype)
125 return extract1(line, ntype, " \t,(", 0);
128 struct name *
129 sextract(char *line, enum gfield ntype)
131 if (line && strpbrk(line, ",\"\\(<"))
132 return extract1(line, ntype, ",", 1);
133 else
134 return extract(line, ntype);
137 static struct name *
138 extract1(char *line, enum gfield ntype, char *separators, int copypfx)
140 char *cp, *nbuf;
141 struct name *top, *np, *t;
143 if (line == NULL || *line == '\0')
144 return NULL;
145 top = NULL;
146 np = NULL;
147 cp = line;
148 nbuf = ac_alloc(strlen(line) + 1);
149 while ((cp = yankword(cp, nbuf, separators, copypfx)) != NULL) {
150 t = nalloc(nbuf, ntype);
151 if (top == NULL)
152 top = t;
153 else
154 np->n_flink = t;
155 t->n_blink = np;
156 np = t;
158 ac_free(nbuf);
159 return top;
163 * Turn a list of names into a string of the same names.
165 char *
166 detract(struct name *np, enum gfield ntype)
168 int s;
169 char *cp, *top;
170 struct name *p;
171 int comma;
173 comma = ntype & GCOMMA;
174 if (np == NULL)
175 return(NULL);
176 ntype &= ~GCOMMA;
177 s = 0;
178 if ((debug || value("debug")) && comma)
179 fprintf(stderr, catgets(catd, CATSET, 145,
180 "detract asked to insert commas\n"));
181 for (p = np; p != NULL; p = p->n_flink) {
182 if (ntype && (p->n_type & GMASK) != ntype)
183 continue;
184 s += strlen(p->n_fullname) + 1;
185 if (comma)
186 s++;
188 if (s == 0)
189 return(NULL);
190 s += 2;
191 top = salloc(s);
192 cp = top;
193 for (p = np; p != NULL; p = p->n_flink) {
194 if (ntype && (p->n_type & GMASK) != ntype)
195 continue;
196 cp = sstpcpy(cp, p->n_fullname);
197 if (comma && p->n_flink != NULL)
198 *cp++ = ',';
199 *cp++ = ' ';
201 *--cp = 0;
202 if (comma && *--cp == ',')
203 *cp = 0;
204 return(top);
208 * Grab a single word (liberal word)
209 * Throw away things between ()'s, and take anything between <>.
211 static char *
212 yankword(char *ap, char *wbuf, char *separators, int copypfx)
214 char *cp, *pp, *wp;
216 cp = ap;
217 wp = wbuf;
218 while (blankchar(*cp & 0377) || *cp == ',')
219 cp++;
220 pp = cp;
221 if ((cp = nexttoken(cp)) == NULL)
222 return NULL;
223 if (copypfx)
224 while (pp < cp)
225 *wp++ = *pp++;
226 if (*cp == '<')
227 while (*cp && (*wp++ = *cp++) != '>');
228 else {
229 int incomm = 0;
231 while (*cp && (incomm || !strchr(separators, *cp))) {
232 if (*cp == '\"') {
233 if (cp == ap || *(cp - 1) != '\\') {
234 if (incomm)
235 incomm--;
236 else
237 incomm++;
238 *wp++ = '\"';
239 } else if (cp != ap) {
240 *(wp - 1) = '\"';
242 cp++;
243 continue;
245 *wp++ = *cp++;
248 *wp = '\0';
249 return cp;
253 * For each recipient in the passed name list with a /
254 * in the name, append the message to the end of the named file
255 * and remove him from the recipient list.
257 * Recipients whose name begins with | are piped through the given
258 * program and removed.
260 /*ARGSUSED 3*/
261 struct name *
262 outof(struct name *names, FILE *fo, struct header *hp)
264 int c, lastc;
265 struct name *np, *top;
266 time_t now;
267 char *date, *fname;
268 FILE *fout, *fin;
269 int ispipe;
271 top = names;
272 np = names;
273 time(&now);
274 date = ctime(&now);
275 while (np != NULL) {
276 if (!is_fileaddr(np->n_name) && np->n_name[0] != '|') {
277 np = np->n_flink;
278 continue;
280 ispipe = np->n_name[0] == '|';
281 if (ispipe)
282 fname = np->n_name+1;
283 else
284 fname = expand(np->n_name);
287 * See if we have copied the complete message out yet.
288 * If not, do so.
291 if (image < 0) {
292 char *tempEdit;
294 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
295 == NULL) {
296 perror(catgets(catd, CATSET, 146,
297 "temporary edit file"));
298 senderr++;
299 goto cant;
301 image = open(tempEdit, O_RDWR);
302 unlink(tempEdit);
303 Ftfree(&tempEdit);
304 if (image < 0) {
305 perror(catgets(catd, CATSET, 147,
306 "temporary edit file"));
307 senderr++;
308 Fclose(fout);
309 goto cant;
311 fcntl(image, F_SETFD, FD_CLOEXEC);
312 fprintf(fout, "From %s %s", myname, date);
313 c = EOF;
314 while (lastc = c, (c = getc(fo)) != EOF)
315 putc(c, fout);
316 rewind(fo);
317 if (lastc != '\n')
318 putc('\n', fout);
319 putc('\n', fout);
320 fflush(fout);
321 if (ferror(fout))
322 perror(catgets(catd, CATSET, 148,
323 "temporary edit file"));
324 Fclose(fout);
328 * Now either copy "image" to the desired file
329 * or give it as the standard input to the desired
330 * program as appropriate.
333 if (ispipe) {
334 int pid;
335 char *shell;
336 sigset_t nset;
339 * XXX
340 * We can't really reuse the same image file,
341 * because multiple piped recipients will
342 * share the same lseek location and trample
343 * on one another.
345 if ((shell = value("SHELL")) == NULL)
346 shell = SHELL;
347 sigemptyset(&nset);
348 sigaddset(&nset, SIGHUP);
349 sigaddset(&nset, SIGINT);
350 sigaddset(&nset, SIGQUIT);
351 pid = start_command(shell, &nset,
352 image, -1, "-c", fname, NULL);
353 if (pid < 0) {
354 senderr++;
355 goto cant;
357 free_child(pid);
358 } else {
359 int f;
360 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
361 perror(fname);
362 senderr++;
363 goto cant;
365 if ((f = dup(image)) < 0) {
366 perror("dup");
367 fin = NULL;
368 } else
369 fin = Fdopen(f, "r");
370 if (fin == NULL) {
371 fprintf(stderr, catgets(catd, CATSET, 149,
372 "Can't reopen image\n"));
373 Fclose(fout);
374 senderr++;
375 goto cant;
377 rewind(fin);
378 while ((c = getc(fin)) != EOF)
379 putc(c, fout);
380 if (ferror(fout))
381 senderr++, perror(fname);
382 Fclose(fout);
383 Fclose(fin);
385 cant:
387 * In days of old we removed the entry from the
388 * the list; now for sake of header expansion
389 * we leave it in and mark it as deleted.
391 np->n_type |= GDEL;
392 np = np->n_flink;
394 if (image >= 0) {
395 close(image);
396 image = -1;
398 return(top);
402 * Determine if the passed address is a local "send to file" address.
403 * If any of the network metacharacters precedes any slashes, it can't
404 * be a filename. We cheat with .'s to allow path names like ./...
406 int
407 is_fileaddr(char *name)
409 char *cp;
411 if (strchr(name, '@') != NULL)
412 return 0;
413 if (*name == '+')
414 return 1;
415 for (cp = name; *cp; cp++) {
416 if (*cp == '!' || *cp == '%')
417 return 0;
418 if (*cp == '/')
419 return 1;
421 return 0;
424 static int
425 same_name(char *n1, char *n2)
427 int c1, c2;
429 if (value("allnet") != NULL) {
430 do {
431 c1 = (*n1++ & 0377);
432 c2 = (*n2++ & 0377);
433 c1 = lowerconv(c1);
434 c2 = lowerconv(c2);
435 if (c1 != c2)
436 return 0;
437 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
438 return 1;
439 } else
440 return asccasecmp(n1, n2) == 0;
444 * Map all of the aliased users in the invoker's mailrc
445 * file and insert them into the list.
446 * Changed after all these months of service to recursively
447 * expand names (2/14/80).
450 struct name *
451 usermap(struct name *names)
453 struct name *new, *np, *cp;
454 struct grouphead *gh;
455 int metoo;
457 new = NULL;
458 np = names;
459 metoo = (value("metoo") != NULL);
460 while (np != NULL) {
461 if (np->n_name[0] == '\\') {
462 cp = np->n_flink;
463 new = put(new, np);
464 np = cp;
465 continue;
467 gh = findgroup(np->n_name);
468 cp = np->n_flink;
469 if (gh != NULL)
470 new = gexpand(new, gh, metoo, np->n_type);
471 else
472 new = put(new, np);
473 np = cp;
475 return(new);
479 * Recursively expand a group name. We limit the expansion to some
480 * fixed level to keep things from going haywire.
481 * Direct recursion is not expanded for convenience.
484 static struct name *
485 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
487 struct group *gp;
488 struct grouphead *ngh;
489 struct name *np;
490 static int depth;
491 char *cp;
493 if (depth > MAXEXP) {
494 printf(catgets(catd, CATSET, 150,
495 "Expanding alias to depth larger than %d\n"), MAXEXP);
496 return(nlist);
498 depth++;
499 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
500 cp = gp->ge_name;
501 if (*cp == '\\')
502 goto quote;
503 if (strcmp(cp, gh->g_name) == 0)
504 goto quote;
505 if ((ngh = findgroup(cp)) != NULL) {
506 nlist = gexpand(nlist, ngh, metoo, ntype);
507 continue;
509 quote:
510 np = nalloc(cp, ntype|GFULL);
512 * At this point should allow to expand
513 * to self if only person in group
515 if (gp == gh->g_list && gp->ge_link == NULL)
516 goto skip;
517 if (!metoo && same_name(cp, myname))
518 np->n_type |= GDEL;
519 skip:
520 nlist = put(nlist, np);
522 depth--;
523 return(nlist);
527 * Concatenate the two passed name lists, return the result.
529 struct name *
530 cat(struct name *n1, struct name *n2)
532 struct name *tail;
534 if (n1 == NULL)
535 return(n2);
536 if (n2 == NULL)
537 return(n1);
538 tail = tailof(n1);
539 tail->n_flink = n2;
540 n2->n_blink = tail;
541 return(n1);
545 * Unpack the name list onto a vector of strings.
546 * Return an error if the name list won't fit.
548 char **
549 unpack(struct name *np)
551 char **ap, **top;
552 struct name *n;
553 int t, extra, metoo, verbose;
555 n = np;
556 if ((t = count(n)) == 0)
557 panic(catgets(catd, CATSET, 151, "No names to unpack"));
559 * Compute the number of extra arguments we will need.
560 * We need at least two extra -- one for "mail" and one for
561 * the terminating 0 pointer. Additional spots may be needed
562 * to pass along -f to the host mailer.
564 extra = 2;
565 extra++;
566 metoo = value("metoo") != NULL;
567 if (metoo)
568 extra++;
569 verbose = value("verbose") != NULL;
570 if (verbose)
571 extra++;
572 /*LINTED*/
573 top = (char **)salloc((t + extra) * sizeof *top);
574 ap = top;
575 *ap++ = "send-mail";
576 *ap++ = "-i";
577 if (metoo)
578 *ap++ = "-m";
579 if (verbose)
580 *ap++ = "-v";
581 for (; n != NULL; n = n->n_flink)
582 if ((n->n_type & GDEL) == 0)
583 *ap++ = n->n_name;
584 *ap = NULL;
585 return(top);
589 * Remove all of the duplicates from the passed name list by
590 * insertion sorting them, then checking for dups.
591 * Return the head of the new list.
593 struct name *
594 elide(struct name *names)
596 struct name *np, *t, *new;
597 struct name *x;
599 if (names == NULL)
600 return(NULL);
601 new = names;
602 np = names;
603 np = np->n_flink;
604 if (np != NULL)
605 np->n_blink = NULL;
606 new->n_flink = NULL;
607 while (np != NULL) {
608 t = new;
609 while (asccasecmp(t->n_name, np->n_name) < 0) {
610 if (t->n_flink == NULL)
611 break;
612 t = t->n_flink;
616 * If we ran out of t's, put the new entry after
617 * the current value of t.
620 if (asccasecmp(t->n_name, np->n_name) < 0) {
621 t->n_flink = np;
622 np->n_blink = t;
623 t = np;
624 np = np->n_flink;
625 t->n_flink = NULL;
626 continue;
630 * Otherwise, put the new entry in front of the
631 * current t. If at the front of the list,
632 * the new guy becomes the new head of the list.
635 if (t == new) {
636 t = np;
637 np = np->n_flink;
638 t->n_flink = new;
639 new->n_blink = t;
640 t->n_blink = NULL;
641 new = t;
642 continue;
646 * The normal case -- we are inserting into the
647 * middle of the list.
650 x = np;
651 np = np->n_flink;
652 x->n_flink = t;
653 x->n_blink = t->n_blink;
654 t->n_blink->n_flink = x;
655 t->n_blink = x;
659 * Now the list headed up by new is sorted.
660 * Go through it and remove duplicates.
663 np = new;
664 while (np != NULL) {
665 t = np;
666 while (t->n_flink != NULL &&
667 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
668 t = t->n_flink;
669 if (t == np || t == NULL) {
670 np = np->n_flink;
671 continue;
675 * Now t points to the last entry with the same name
676 * as np. Make np point beyond t.
679 np->n_flink = t->n_flink;
680 if (t->n_flink != NULL)
681 t->n_flink->n_blink = np;
682 np = np->n_flink;
684 return(new);
688 * Put another node onto a list of names and return
689 * the list.
691 static struct name *
692 put(struct name *list, struct name *node)
694 node->n_flink = list;
695 node->n_blink = NULL;
696 if (list != NULL)
697 list->n_blink = node;
698 return(node);
702 * Determine the number of undeleted elements in
703 * a name list and return it.
705 int
706 count(struct name *np)
708 int c;
710 for (c = 0; np != NULL; np = np->n_flink)
711 if ((np->n_type & GDEL) == 0)
712 c++;
713 return c;
717 * Delete the given name from a namelist.
719 static struct name *
720 delname(struct name *np, char *name)
722 struct name *p;
724 for (p = np; p != NULL; p = p->n_flink)
725 if (same_name(p->n_name, name)) {
726 if (p->n_blink == NULL) {
727 if (p->n_flink != NULL)
728 p->n_flink->n_blink = NULL;
729 np = p->n_flink;
730 continue;
732 if (p->n_flink == NULL) {
733 if (p->n_blink != NULL)
734 p->n_blink->n_flink = NULL;
735 continue;
737 p->n_blink->n_flink = p->n_flink;
738 p->n_flink->n_blink = p->n_blink;
740 return np;
744 * Pretty print a name list
745 * Uncomment it if you need it.
749 void
750 prettyprint(struct name *name)
752 struct name *np;
754 np = name;
755 while (np != NULL) {
756 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
757 np = np->n_flink;
759 fprintf(stderr, "\n");
763 struct name *
764 delete_alternates(struct name *np)
766 struct name *xp;
767 char **ap;
769 np = delname(np, myname);
770 if (altnames)
771 for (ap = altnames; *ap; ap++)
772 np = delname(np, *ap);
773 if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL)
774 while (xp) {
775 np = delname(np, xp->n_name);
776 xp = xp->n_flink;
778 if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
779 while (xp) {
780 np = delname(np, xp->n_name);
781 xp = xp->n_flink;
783 if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL)
784 while (xp) {
785 np = delname(np, xp->n_name);
786 xp = xp->n_flink;
788 return np;
792 is_myname(char *name)
794 struct name *xp;
795 char **ap;
797 if (same_name(myname, name))
798 return 1;
799 if (altnames)
800 for (ap = altnames; *ap; ap++)
801 if (same_name(*ap, name))
802 return 1;
803 if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL)
804 while (xp) {
805 if (same_name(xp->n_name, name))
806 return 1;
807 xp = xp->n_flink;
809 if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
810 while (xp) {
811 if (same_name(xp->n_name, name))
812 return 1;
813 xp = xp->n_flink;
815 if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL)
816 while (xp) {
817 if (same_name(xp->n_name, name))
818 return 1;
819 xp = xp->n_flink;
821 return 0;