prefixwrite(), TODO: change file tracking..
[s-mailx.git] / names.c
blob289a45944d299af4a8a62bd9aeadc38b93d92460
1 /*
2 * S-nail - 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.
41 * Mail -- a mail program
43 * Handle name lists.
46 #include "rcv.h"
47 #include "extern.h"
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #include <time.h>
51 #include <unistd.h>
53 static struct name * tailof(struct name *name);
54 static struct name * extract1(char *line, enum gfield ntype,
55 char *separators, int copypfx);
56 static char * yankword(char *ap, char *wbuf, char *separators,
57 int copypfx);
58 static int same_name(char *n1, char *n2);
59 static struct name * gexpand(struct name *nlist, struct grouphead *gh,
60 int metoo, int ntype);
61 static struct name * put(struct name *list, struct name *node);
62 static struct name * delname(struct name *np, char *name);
65 * Allocate a single element of a name list,
66 * initialize its name field to the passed
67 * name and return it.
69 struct name *
70 nalloc(char *str, enum gfield ntype)
72 struct addrguts ag;
73 struct str in, out;
74 struct name *np;
76 np = (struct name*)salloc(sizeof *np);
77 np->n_flink = NULL;
78 np->n_blink = NULL;
79 np->n_type = ntype;
80 np->n_flags = 0;
82 (void)addrspec_with_guts((ntype & (GFULL|GSKIN|GREF)) != 0, str, &ag);
83 if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) {
84 ag.ag_n_flags |= NAME_NAME_SALLOC;
85 ag.ag_skinned = savestr(ag.ag_skinned);
87 np->n_fullname = np->n_name = ag.ag_skinned;
88 np->n_flags = ag.ag_n_flags;
90 if (ntype & GFULL) {
91 if (ag.ag_ilen != ag.ag_slen) {
92 in.s = str;
93 in.l = ag.ag_ilen;
94 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
95 np->n_fullname = savestr(out.s);
96 free(out.s);
97 np->n_flags |= NAME_FULLNAME_SALLOC;
99 } else if (ntype & GREF) { /* TODO LEGACY */
100 /* TODO Unfortunately we had to skin GREFerences i.e. the
101 * TODO surrounding angle brackets have been stripped away.
102 * TODO Necessarily since otherwise the plain address check
103 * TODO fails due to them; insert them back so that valid
104 * TODO headers will be created */
105 np->n_fullname = np->n_name = str = salloc(ag.ag_slen + 2 + 1);
106 *(str++) = '<';
107 memcpy(str, ag.ag_skinned, ag.ag_slen);
108 str += ag.ag_slen;
109 *(str++) = '>';
110 *str = '\0';
112 return (np);
115 struct name *
116 ndup(struct name *np, enum gfield ntype)
118 struct name *nnp;
120 if ((ntype & (GFULL|GSKIN)) && (np->n_flags & NAME_SKINNED) == 0) {
121 nnp = nalloc(np->n_name, ntype);
122 goto jleave;
125 nnp = (struct name*)salloc(sizeof *np);
126 nnp->n_flink = nnp->n_blink = NULL;
127 nnp->n_type = ntype;
128 nnp->n_flags = (np->n_flags &
129 ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) |
130 NAME_NAME_SALLOC;
131 nnp->n_name = savestr(np->n_name);
132 if (np->n_name == np->n_fullname || (ntype & (GFULL|GSKIN)) == 0)
133 nnp->n_fullname = nnp->n_name;
134 else {
135 nnp->n_flags |= NAME_FULLNAME_SALLOC;
136 nnp->n_fullname = savestr(np->n_fullname);
138 jleave:
139 return (nnp);
143 * Find the tail of a list and return it.
145 static struct name *
146 tailof(struct name *name)
148 struct name *np;
150 np = name;
151 if (np == NULL)
152 return(NULL);
153 while (np->n_flink != NULL)
154 np = np->n_flink;
155 return(np);
159 * Extract a list of names from a line,
160 * and make a list of names from it.
161 * Return the list or NULL if none found.
163 struct name *
164 extract(char *line, enum gfield ntype)
166 return extract1(line, ntype, " \t,(", 0);
169 struct name *
170 sextract(char *line, enum gfield ntype)
172 if (line && strpbrk(line, ",\"\\(<"))
173 return extract1(line, ntype, ",", 1);
174 else
175 return extract(line, ntype);
178 struct name *
179 lextract(char *line, enum gfield ntype)
181 return (extract1(line, ntype, ",", 1));
184 static struct name *
185 extract1(char *line, enum gfield ntype, char *separators, int copypfx)
187 char *cp, *nbuf;
188 struct name *top, *np, *t;
190 if (line == NULL || *line == '\0')
191 return NULL;
192 top = NULL;
193 np = NULL;
194 cp = line;
195 nbuf = ac_alloc(strlen(line) + 1);
196 while ((cp = yankword(cp, nbuf, separators, copypfx)) != NULL) {
197 t = nalloc(nbuf, ntype);
198 if (top == NULL)
199 top = t;
200 else
201 np->n_flink = t;
202 t->n_blink = np;
203 np = t;
205 ac_free(nbuf);
206 return top;
210 * Turn a list of names into a string of the same names.
212 char *
213 detract(struct name *np, enum gfield ntype)
215 int s;
216 char *cp, *top;
217 struct name *p;
218 int comma;
220 comma = ntype & GCOMMA;
221 if (np == NULL)
222 return(NULL);
223 ntype &= ~GCOMMA;
224 s = 0;
225 if ((debug || value("debug")) && comma)
226 fprintf(stderr, catgets(catd, CATSET, 145,
227 "detract asked to insert commas\n"));
228 for (p = np; p != NULL; p = p->n_flink) {
229 if (ntype && (p->n_type & GMASK) != ntype)
230 continue;
231 s += strlen(p->n_fullname) + 1;
232 if (comma)
233 s++;
235 if (s == 0)
236 return(NULL);
237 s += 2;
238 top = salloc(s);
239 cp = top;
240 for (p = np; p != NULL; p = p->n_flink) {
241 if (ntype && (p->n_type & GMASK) != ntype)
242 continue;
243 cp = sstpcpy(cp, p->n_fullname);
244 if (comma && p->n_flink != NULL)
245 *cp++ = ',';
246 *cp++ = ' ';
248 *--cp = 0;
249 if (comma && *--cp == ',')
250 *cp = 0;
251 return(top);
255 * Grab a single word (liberal word)
256 * Throw away things between ()'s, and take anything between <>.
257 * Strip trailing whitespace as *ap* may come directly from user.
259 static char *
260 yankword(char *ap, char *wbuf, char *separators, int copypfx)
262 char *cp, *pp, *wp;
264 cp = ap;
265 wp = wbuf;
266 while (blankspacechar(*cp) || *cp == ',')
267 ++cp;
268 pp = cp;
269 if ((cp = nexttoken(cp)) == NULL)
270 return NULL;
271 if (copypfx)
272 while (pp < cp)
273 *wp++ = *pp++;
274 if (*cp == '<')
275 while (*cp && (*wp++ = *cp++) != '>');
276 else {
277 int incomm = 0;
279 while (*cp && (incomm || !strchr(separators, *cp))) {
280 if (*cp == '\"') {
281 if (cp == ap || *(cp - 1) != '\\') {
282 if (incomm)
283 incomm--;
284 else
285 incomm++;
286 *wp++ = '\"';
287 } else if (cp != ap) {
288 *(wp - 1) = '\"';
290 cp++;
291 continue;
293 *wp++ = *cp++;
296 while (wp > wbuf && blankspacechar(wp[-1]))
297 --wp;
298 *wp = '\0';
299 return cp;
303 * Check all addresses in np and delete invalid ones.
305 struct name *
306 checkaddrs(struct name *np)
308 struct name *n;
310 for (n = np; n != NULL;) {
311 if (is_addr_invalid(n, 1)) {
312 if (n->n_blink)
313 n->n_blink->n_flink = n->n_flink;
314 if (n->n_flink)
315 n->n_flink->n_blink = n->n_blink;
316 if (n == np)
317 np = n->n_flink;
319 n = n->n_flink;
321 return (np);
325 * For each recipient in the passed name list with a /
326 * in the name, append the message to the end of the named file
327 * and remove him from the recipient list.
329 * Recipients whose name begins with | are piped through the given
330 * program and removed.
332 struct name *
333 outof(struct name *names, FILE *fo, struct header *hp)
335 int pipecnt, xcnt, *fda, i;
336 char *shell, *date;
337 struct name *np;
338 time_t now;
339 FILE *fin = NULL, *fout;
340 (void)hp;
343 * Look through all recipients and do a quick return if no file or pipe
344 * addressee is found.
346 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
347 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
348 case NAME_ADDRSPEC_ISFILE:
349 ++xcnt;
350 break;
351 case NAME_ADDRSPEC_ISPIPE:
352 ++pipecnt;
353 break;
355 if (pipecnt == 0 && xcnt == 0)
356 goto jleave;
359 * Otherwise create an array of file descriptors for each found pipe
360 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
361 * each pipe subprocess needs its very own file descriptor, and we need
362 * to deal with that.
363 * To make our life a bit easier let's just use the auto-reclaimed
364 * string storage.
366 if (pipecnt == 0) {
367 fda = NULL;
368 shell = NULL;
369 } else {
370 fda = (int*)salloc(sizeof(int) * pipecnt);
371 for (i = 0; i < pipecnt; ++i)
372 fda[i] = -1;
373 if ((shell = value("SHELL")) == NULL)
374 shell = SHELL;
377 time(&now);
378 date = ctime(&now);
380 for (np = names; np != NULL;) {
381 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
382 == 0) {
383 np = np->n_flink;
384 continue;
388 * See if we have copied the complete message out yet.
389 * If not, do so.
391 if (image < 0) {
392 int c;
393 char *tempEdit;
395 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
396 == NULL) {
397 perror(tr(146, "Creation of temporary image"));
398 ++senderr;
399 goto jcant;
401 image = open(tempEdit, O_RDWR);
402 if (image >= 0)
403 for (i = 0; i < pipecnt; ++i) {
404 int fd = open(tempEdit, O_RDONLY);
405 if (fd < 0) {
406 (void)close(image);
407 image = -1;
408 pipecnt = i;
409 break;
411 fda[i] = fd;
412 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
414 unlink(tempEdit);
415 Ftfree(&tempEdit);
416 if (image < 0) {
417 perror(tr(147, "Creating descriptor duplicate "
418 "of temporary image"));
419 ++senderr;
420 Fclose(fout);
421 goto jcant;
423 fcntl(image, F_SETFD, FD_CLOEXEC);
425 fprintf(fout, "From %s %s", myname, date);
426 c = EOF;
427 while (i = c, (c = getc(fo)) != EOF)
428 putc(c, fout);
429 rewind(fo);
430 if (i != '\n')
431 putc('\n', fout);
432 putc('\n', fout);
433 fflush(fout);
434 if (ferror(fout))
435 perror(tr(148, "Finalizing write of temporary "
436 "image"));
437 Fclose(fout);
439 /* If we have to serve file addressees, open reader */
440 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
441 perror(tr(149, "Failed to open a duplicate of "
442 "the temporary image"));
443 ++senderr;
444 (void)close(image);
445 image = -1;
446 goto jcant;
449 /* From now on use xcnt as a counter for pipecnt */
450 xcnt = 0;
454 * Now either copy "image" to the desired file
455 * or give it as the standard input to the desired
456 * program as appropriate.
459 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
460 int pid;
461 sigset_t nset;
463 sigemptyset(&nset);
464 sigaddset(&nset, SIGHUP);
465 sigaddset(&nset, SIGINT);
466 sigaddset(&nset, SIGQUIT);
467 pid = start_command(shell, &nset,
468 fda[xcnt++], -1, "-c", np->n_name + 1, NULL);
469 if (pid < 0) {
470 ++senderr;
471 goto jcant;
473 free_child(pid);
474 } else {
475 char *fname = expand(np->n_name);
476 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
477 perror(fname);
478 ++senderr;
479 goto jcant;
481 rewind(fin);
482 while ((i = getc(fin)) != EOF)
483 putc(i, fout);
484 if (ferror(fout)) {
485 ++senderr;
486 perror(fname);
488 Fclose(fout);
490 jcant:
492 * In days of old we removed the entry from the
493 * the list; now for sake of header expansion
494 * we leave it in and mark it as deleted.
496 np->n_type |= GDEL;
497 np = np->n_flink;
498 if (image < 0)
499 goto jdelall;
501 jleave:
502 if (fin != NULL)
503 Fclose(fin);
504 for (i = 0; i < pipecnt; ++i)
505 (void)close(fda[i]);
506 if (image >= 0) {
507 close(image);
508 image = -1;
510 return (names);
512 jdelall:
513 while (np != NULL) {
514 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
515 != 0)
516 np->n_type |= GDEL;
517 np = np->n_flink;
519 goto jleave;
522 static int
523 same_name(char *n1, char *n2)
525 int c1, c2;
527 if (value("allnet") != NULL) {
528 do {
529 c1 = (*n1++ & 0377);
530 c2 = (*n2++ & 0377);
531 c1 = lowerconv(c1);
532 c2 = lowerconv(c2);
533 if (c1 != c2)
534 return 0;
535 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
536 return 1;
537 } else
538 return asccasecmp(n1, n2) == 0;
542 * Map all of the aliased users in the invoker's mailrc
543 * file and insert them into the list.
544 * Changed after all these months of service to recursively
545 * expand names (2/14/80).
548 struct name *
549 usermap(struct name *names)
551 struct name *new, *np, *cp;
552 struct grouphead *gh;
553 int metoo;
555 new = NULL;
556 np = names;
557 metoo = (value("metoo") != NULL);
558 while (np != NULL) {
559 if (np->n_name[0] == '\\') {
560 cp = np->n_flink;
561 new = put(new, np);
562 np = cp;
563 continue;
565 gh = findgroup(np->n_name);
566 cp = np->n_flink;
567 if (gh != NULL)
568 new = gexpand(new, gh, metoo, np->n_type);
569 else
570 new = put(new, np);
571 np = cp;
573 return(new);
577 * Recursively expand a group name. We limit the expansion to some
578 * fixed level to keep things from going haywire.
579 * Direct recursion is not expanded for convenience.
582 static struct name *
583 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
585 struct group *gp;
586 struct grouphead *ngh;
587 struct name *np;
588 static int depth;
589 char *cp;
591 if (depth > MAXEXP) {
592 printf(catgets(catd, CATSET, 150,
593 "Expanding alias to depth larger than %d\n"), MAXEXP);
594 return(nlist);
596 depth++;
597 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
598 cp = gp->ge_name;
599 if (*cp == '\\')
600 goto quote;
601 if (strcmp(cp, gh->g_name) == 0)
602 goto quote;
603 if ((ngh = findgroup(cp)) != NULL) {
604 nlist = gexpand(nlist, ngh, metoo, ntype);
605 continue;
607 quote:
608 np = nalloc(cp, ntype|GFULL);
610 * At this point should allow to expand
611 * to self if only person in group
613 if (gp == gh->g_list && gp->ge_link == NULL)
614 goto skip;
615 if (!metoo && same_name(cp, myname))
616 np->n_type |= GDEL;
617 skip:
618 nlist = put(nlist, np);
620 depth--;
621 return(nlist);
625 * Concatenate the two passed name lists, return the result.
627 struct name *
628 cat(struct name *n1, struct name *n2)
630 struct name *tail;
632 if (n1 == NULL)
633 return(n2);
634 if (n2 == NULL)
635 return(n1);
636 tail = tailof(n1);
637 tail->n_flink = n2;
638 n2->n_blink = tail;
639 return(n1);
643 * Unpack the name list onto a vector of strings.
644 * Return an error if the name list won't fit.
646 char **
647 unpack(struct name *np)
649 char **ap, **top;
650 struct name *n;
651 int t, extra, metoo, verbose;
653 n = np;
654 if ((t = count(n)) == 0)
655 panic(catgets(catd, CATSET, 151, "No names to unpack"));
657 * Compute the number of extra arguments we will need.
658 * We need at least two extra -- one for "mail" and one for
659 * the terminating 0 pointer. Additional spots may be needed
660 * to pass along -f to the host mailer.
662 extra = 2;
663 extra++;
664 metoo = value("metoo") != NULL;
665 if (metoo)
666 extra++;
667 verbose = value("verbose") != NULL;
668 if (verbose)
669 extra++;
670 /*LINTED*/
671 top = (char **)salloc((t + extra) * sizeof *top);
672 ap = top;
673 *ap++ = "send-mail";
674 *ap++ = "-i";
675 if (metoo)
676 *ap++ = "-m";
677 if (verbose)
678 *ap++ = "-v";
679 for (; n != NULL; n = n->n_flink)
680 if ((n->n_type & GDEL) == 0)
681 *ap++ = n->n_name;
682 *ap = NULL;
683 return(top);
687 * Remove all of the duplicates from the passed name list by
688 * insertion sorting them, then checking for dups.
689 * Return the head of the new list.
691 struct name *
692 elide(struct name *names)
694 struct name *np, *t, *newn, *x;
696 if (names == NULL)
697 return (NULL);
698 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
699 for (np = NULL; names != NULL; names = names->n_flink)
700 if ((names->n_type & GDEL) == 0) {
701 names->n_blink = np;
702 if (np)
703 np->n_flink = names;
704 else
705 newn = names;
706 np = names;
708 if (newn == NULL)
709 return (NULL);
711 np = newn->n_flink;
712 if (np != NULL)
713 np->n_blink = NULL;
714 newn->n_flink = NULL;
716 while (np != NULL) {
717 t = newn;
718 while (asccasecmp(t->n_name, np->n_name) < 0) {
719 if (t->n_flink == NULL)
720 break;
721 t = t->n_flink;
725 * If we ran out of t's, put the new entry after
726 * the current value of t.
729 if (asccasecmp(t->n_name, np->n_name) < 0) {
730 t->n_flink = np;
731 np->n_blink = t;
732 t = np;
733 np = np->n_flink;
734 t->n_flink = NULL;
735 continue;
739 * Otherwise, put the new entry in front of the
740 * current t. If at the front of the list,
741 * the new guy becomes the new head of the list.
744 if (t == newn) {
745 t = np;
746 np = np->n_flink;
747 t->n_flink = newn;
748 newn->n_blink = t;
749 t->n_blink = NULL;
750 newn = t;
751 continue;
755 * The normal case -- we are inserting into the
756 * middle of the list.
759 x = np;
760 np = np->n_flink;
761 x->n_flink = t;
762 x->n_blink = t->n_blink;
763 t->n_blink->n_flink = x;
764 t->n_blink = x;
768 * Now the list headed up by new is sorted.
769 * Go through it and remove duplicates.
772 np = newn;
773 while (np != NULL) {
774 t = np;
775 while (t->n_flink != NULL &&
776 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
777 t = t->n_flink;
778 if (t == np || t == NULL) {
779 np = np->n_flink;
780 continue;
784 * Now t points to the last entry with the same name
785 * as np. Make np point beyond t.
788 np->n_flink = t->n_flink;
789 if (t->n_flink != NULL)
790 t->n_flink->n_blink = np;
791 np = np->n_flink;
793 return (newn);
797 * Put another node onto a list of names and return
798 * the list.
800 static struct name *
801 put(struct name *list, struct name *node)
803 node->n_flink = list;
804 node->n_blink = NULL;
805 if (list != NULL)
806 list->n_blink = node;
807 return(node);
811 * Determine the number of undeleted elements in
812 * a name list and return it.
814 int
815 count(struct name *np)
817 int c;
819 for (c = 0; np != NULL; np = np->n_flink)
820 if ((np->n_type & GDEL) == 0)
821 c++;
822 return c;
826 * Delete the given name from a namelist.
828 static struct name *
829 delname(struct name *np, char *name)
831 struct name *p;
833 for (p = np; p != NULL; p = p->n_flink)
834 if (same_name(p->n_name, name)) {
835 if (p->n_blink == NULL) {
836 if (p->n_flink != NULL)
837 p->n_flink->n_blink = NULL;
838 np = p->n_flink;
839 continue;
841 if (p->n_flink == NULL) {
842 if (p->n_blink != NULL)
843 p->n_blink->n_flink = NULL;
844 continue;
846 p->n_blink->n_flink = p->n_flink;
847 p->n_flink->n_blink = p->n_blink;
849 return np;
853 * Pretty print a name list
854 * Uncomment it if you need it.
858 void
859 prettyprint(struct name *name)
861 struct name *np;
863 np = name;
864 while (np != NULL) {
865 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
866 np = np->n_flink;
868 fprintf(stderr, "\n");
872 struct name *
873 delete_alternates(struct name *np)
875 struct name *xp;
876 char **ap;
878 np = delname(np, myname);
879 if (altnames)
880 for (ap = altnames; *ap; ap++)
881 np = delname(np, *ap);
882 if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL)
883 while (xp) {
884 np = delname(np, xp->n_name);
885 xp = xp->n_flink;
887 if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
888 while (xp) {
889 np = delname(np, xp->n_name);
890 xp = xp->n_flink;
892 if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL)
893 while (xp) {
894 np = delname(np, xp->n_name);
895 xp = xp->n_flink;
897 return np;
901 is_myname(char *name)
903 struct name *xp;
904 char **ap;
906 if (same_name(myname, name))
907 return 1;
908 if (altnames)
909 for (ap = altnames; *ap; ap++)
910 if (same_name(*ap, name))
911 return 1;
912 if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL)
913 while (xp) {
914 if (same_name(xp->n_name, name))
915 return 1;
916 xp = xp->n_flink;
918 if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
919 while (xp) {
920 if (same_name(xp->n_name, name))
921 return 1;
922 xp = xp->n_flink;
924 if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL)
925 while (xp) {
926 if (same_name(xp->n_name, name))
927 return 1;
928 xp = xp->n_flink;
930 return 0;