nail.h: struct message.m_spamscore: ui_it -> ui32_t
[s-mailx.git] / names.c
blob5f3eb38055d3de4ae14e8c5e9e5cb33e703293e9
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Handle name lists, alias expansion; outof(): serve file / pipe addresses
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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 HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <fcntl.h>
46 #ifndef O_CLOEXEC
47 # define _OUR_CLOEXEC
48 # define O_CLOEXEC 0
49 # define _SET_CLOEXEC(FD) fcntl((FD), F_SETFD, FD_CLOEXEC)
50 #else
51 # define _SET_CLOEXEC(FD)
52 #endif
54 /* Same name, taking care for *allnet*? */
55 static bool_t _same_name(char const *n1, char const *n2);
57 /* Delete the given name from a namelist */
58 static struct name * delname(struct name *np, char const *name);
60 /* Put another node onto a list of names and return the list */
61 static struct name * put(struct name *list, struct name *node);
63 /* Grab a single name (liberal name) */
64 static char const * yankname(char const *ap, char *wbuf,
65 char const *separators, int keepcomms);
67 /* Extraction multiplexer that splits an input line to names */
68 static struct name * _extract1(char const *line, enum gfield ntype,
69 char const *separators, bool_t keepcomms);
71 /* Recursively expand a group name. Limit expansion to some fixed level.
72 * Direct recursion is not expanded for convenience */
73 static struct name * _gexpand(size_t level, struct name *nlist,
74 struct grouphead *gh, bool_t metoo, int ntype);
76 static void _remove_grouplist(struct grouphead *gh);
78 static bool_t
79 _same_name(char const *n1, char const *n2)
81 bool_t rv = FAL0;
82 char c1, c2;
83 NYD_ENTER;
85 if (ok_blook(allnet)) {
86 do {
87 c1 = *n1++;
88 c2 = *n2++;
89 c1 = lowerconv(c1);
90 c2 = lowerconv(c2);
91 if (c1 != c2)
92 goto jleave;
93 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
94 rv = 1;
95 } else
96 rv = !asccasecmp(n1, n2);
97 jleave:
98 NYD_LEAVE;
99 return rv;
102 static struct name *
103 delname(struct name *np, char const *name)
105 struct name *p;
106 NYD_ENTER;
108 for (p = np; p != NULL; p = p->n_flink)
109 if (_same_name(p->n_name, name)) {
110 if (p->n_blink == NULL) {
111 if (p->n_flink != NULL)
112 p->n_flink->n_blink = NULL;
113 np = p->n_flink;
114 continue;
116 if (p->n_flink == NULL) {
117 if (p->n_blink != NULL)
118 p->n_blink->n_flink = NULL;
119 continue;
121 p->n_blink->n_flink = p->n_flink;
122 p->n_flink->n_blink = p->n_blink;
124 NYD_LEAVE;
125 return np;
128 static struct name *
129 put(struct name *list, struct name *node)
131 NYD_ENTER;
132 node->n_flink = list;
133 node->n_blink = NULL;
134 if (list != NULL)
135 list->n_blink = node;
136 NYD_LEAVE;
137 return node;
140 static char const *
141 yankname(char const *ap, char *wbuf, char const *separators, int keepcomms)
143 char const *cp;
144 char *wp, c, inquote, lc, lastsp;
145 NYD_ENTER;
147 *(wp = wbuf) = '\0';
149 /* Skip over intermediate list trash, as in ".org> , <xy@zz.org>" */
150 for (c = *ap; blankchar(c) || c == ','; c = *++ap)
152 if (c == '\0') {
153 cp = NULL;
154 goto jleave;
157 /* Parse a full name: TODO RFC 5322
158 * - Keep everything in quotes, liberal handle *quoted-pair*s therein
159 * - Skip entire (nested) comments
160 * - In non-quote, non-comment, join adjacent space to a single SP
161 * - Understand separators only in non-quote, non-comment context,
162 * and only if not part of a *quoted-pair* (XXX too liberal) */
163 cp = ap;
164 for (inquote = lc = lastsp = 0;; lc = c, ++cp) {
165 c = *cp;
166 if (c == '\0')
167 break;
168 if (c == '\\') {
169 lastsp = 0;
170 continue;
172 if (c == '"') {
173 if (lc != '\\')
174 inquote = !inquote;
175 #if 0 /* TODO when doing real RFC 5322 parsers - why have i done this? */
176 else
177 --wp;
178 #endif
179 goto jwpwc;
181 if (inquote || lc == '\\') {
182 jwpwc:
183 *wp++ = c;
184 lastsp = 0;
185 continue;
187 if (c == '(') {
188 ap = cp;
189 cp = skip_comment(cp + 1);
190 if (keepcomms)
191 while (ap < cp)
192 *wp++ = *ap++;
193 --cp;
194 lastsp = 0;
195 continue;
197 if (strchr(separators, c) != NULL)
198 break;
200 lc = lastsp;
201 lastsp = blankchar(c);
202 if (!lastsp || !lc)
203 *wp++ = c;
205 if (blankchar(lc))
206 --wp;
208 *wp = '\0';
209 jleave:
210 NYD_LEAVE;
211 return cp;
214 static struct name *
215 _extract1(char const *line, enum gfield ntype, char const *separators,
216 bool_t keepcomms)
218 struct name *topp, *np, *t;
219 char const *cp;
220 char *nbuf;
221 NYD_ENTER;
223 topp = NULL;
224 if (line == NULL || *line == '\0')
225 goto jleave;
227 np = NULL;
228 cp = line;
229 nbuf = ac_alloc(strlen(line) + 1);
230 while ((cp = yankname(cp, nbuf, separators, keepcomms)) != NULL) {
231 t = nalloc(nbuf, ntype);
232 if (topp == NULL)
233 topp = t;
234 else
235 np->n_flink = t;
236 t->n_blink = np;
237 np = t;
239 ac_free(nbuf);
240 jleave:
241 NYD_LEAVE;
242 return topp;
245 static struct name *
246 _gexpand(size_t level, struct name *nlist, struct grouphead *gh, bool_t metoo,
247 int ntype)
249 struct group *gp;
250 struct grouphead *ngh;
251 struct name *np;
252 char *cp;
253 NYD_ENTER;
255 if (UICMP(z, level++, >, MAXEXP)) {
256 printf(tr(150, "Expanding alias to depth larger than %d\n"), MAXEXP);
257 goto jleave;
260 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
261 cp = gp->ge_name;
262 if (*cp == '\\')
263 goto jquote;
264 if (!strcmp(cp, gh->g_name))
265 goto jquote;
266 if ((ngh = findgroup(cp)) != NULL) {
267 /* For S-nail(1), the "group" may *be* the sender in that a name maps
268 * to a full address specification */
269 if (!metoo && ngh->g_list->ge_link == NULL && _same_name(cp, myname))
270 continue;
271 nlist = _gexpand(level, nlist, ngh, metoo, ntype);
272 continue;
274 jquote:
275 np = nalloc(cp, ntype | GFULL);
276 /* At this point should allow to expand itself if only person in group */
277 if (gp == gh->g_list && gp->ge_link == NULL)
278 goto jskip;
279 if (!metoo && _same_name(cp, myname))
280 np->n_type |= GDEL;
281 jskip:
282 nlist = put(nlist, np);
284 jleave:
285 NYD_LEAVE;
286 return nlist;
289 static void
290 _remove_grouplist(struct grouphead *gh)
292 struct group *gp, *gq;
293 NYD_ENTER;
295 if ((gp = gh->g_list) != NULL) {
296 for (; gp; gp = gq) {
297 gq = gp->ge_link;
298 free(gp->ge_name);
299 free(gp);
302 NYD_LEAVE;
305 FL struct name *
306 nalloc(char *str, enum gfield ntype)
308 struct addrguts ag;
309 struct str in, out;
310 struct name *np;
311 NYD_ENTER;
313 np = salloc(sizeof *np);
314 np->n_flink = NULL;
315 np->n_blink = NULL;
316 np->n_type = ntype;
317 np->n_flags = 0;
319 addrspec_with_guts((ntype & (GFULL | GSKIN | GREF)) != 0, str, &ag);
320 if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) {
321 ag.ag_n_flags |= NAME_NAME_SALLOC;
322 ag.ag_skinned = savestrbuf(ag.ag_skinned, ag.ag_slen);
324 np->n_fullname = np->n_name = ag.ag_skinned;
325 np->n_flags = ag.ag_n_flags;
327 if (ntype & GFULL) {
328 if (ag.ag_ilen == ag.ag_slen
329 #ifdef HAVE_IDNA
330 && (ag.ag_n_flags & NAME_IDNA) == 0
331 #endif
333 goto jleave;
334 if (ag.ag_n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
335 goto jleave;
336 #ifdef HAVE_IDNA
337 if ((ag.ag_n_flags & NAME_IDNA) == 0) {
338 #endif
339 in.s = str;
340 in.l = ag.ag_ilen;
341 #ifdef HAVE_IDNA
342 } else {
343 /* The domain name was IDNA and has been converted. We also have to
344 * ensure that the domain name in .n_fullname is replaced with the
345 * converted version, since MIME doesn't perform encoding of addrs */
346 size_t l = ag.ag_iaddr_start,
347 lsuff = ag.ag_ilen - ag.ag_iaddr_aend;
348 in.s = ac_alloc(l + ag.ag_slen + lsuff + 1);
349 memcpy(in.s, str, l);
350 memcpy(in.s + l, ag.ag_skinned, ag.ag_slen);
351 l += ag.ag_slen;
352 memcpy(in.s + l, str + ag.ag_iaddr_aend, lsuff);
353 l += lsuff;
354 in.s[l] = '\0';
355 in.l = l;
357 #endif
358 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
359 np->n_fullname = savestr(out.s);
360 free(out.s);
361 #ifdef HAVE_IDNA
362 if (ag.ag_n_flags & NAME_IDNA)
363 ac_free(in.s);
364 #endif
365 np->n_flags |= NAME_FULLNAME_SALLOC;
366 } else if (ntype & GREF) { /* TODO LEGACY */
367 /* TODO Unfortunately we had to skin GREFerences i.e. the
368 * TODO surrounding angle brackets have been stripped away.
369 * TODO Necessarily since otherwise the plain address check
370 * TODO fails due to them; insert them back so that valid
371 * TODO headers will be created */
372 np->n_fullname = np->n_name = str = salloc(ag.ag_slen + 2 +1);
373 *(str++) = '<';
374 memcpy(str, ag.ag_skinned, ag.ag_slen);
375 str += ag.ag_slen;
376 *(str++) = '>';
377 *str = '\0';
379 jleave:
380 NYD_LEAVE;
381 return np;
384 FL struct name *
385 ndup(struct name *np, enum gfield ntype)
387 struct name *nnp;
388 NYD_ENTER;
390 if ((ntype & (GFULL | GSKIN)) && !(np->n_flags & NAME_SKINNED)) {
391 nnp = nalloc(np->n_name, ntype);
392 goto jleave;
395 nnp = salloc(sizeof *np);
396 nnp->n_flink = nnp->n_blink = NULL;
397 nnp->n_type = ntype;
398 nnp->n_flags = (np->n_flags & ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) |
399 NAME_NAME_SALLOC;
400 nnp->n_name = savestr(np->n_name);
401 if (np->n_name == np->n_fullname || !(ntype & (GFULL | GSKIN)))
402 nnp->n_fullname = nnp->n_name;
403 else {
404 nnp->n_flags |= NAME_FULLNAME_SALLOC;
405 nnp->n_fullname = savestr(np->n_fullname);
407 jleave:
408 NYD_LEAVE;
409 return nnp;
412 FL struct name *
413 cat(struct name *n1, struct name *n2)
415 struct name *tail;
416 NYD_ENTER;
418 tail = n2;
419 if (n1 == NULL)
420 goto jleave;
421 tail = n1;
422 if (n2 == NULL)
423 goto jleave;
425 while (tail->n_flink != NULL)
426 tail = tail->n_flink;
427 tail->n_flink = n2;
428 n2->n_blink = tail;
429 tail = n1;
430 jleave:
431 NYD_LEAVE;
432 return tail;
435 FL ui32_t
436 count(struct name const*np)
438 ui32_t c;
439 NYD_ENTER;
441 for (c = 0; np != NULL; np = np->n_flink)
442 if (!(np->n_type & GDEL))
443 ++c;
444 NYD_LEAVE;
445 return c;
448 FL struct name *
449 extract(char const *line, enum gfield ntype)
451 struct name *rv;
452 NYD_ENTER;
454 rv = _extract1(line, ntype, " \t,", 0);
455 NYD_LEAVE;
456 return rv;
459 FL struct name *
460 lextract(char const *line, enum gfield ntype)
462 struct name *rv;
463 NYD_ENTER;
465 rv = ((line != NULL && strpbrk(line, ",\"\\(<|"))
466 ? _extract1(line, ntype, ",", 1) : extract(line, ntype));
467 NYD_LEAVE;
468 return rv;
471 FL char *
472 detract(struct name *np, enum gfield ntype)
474 char *topp, *cp;
475 struct name *p;
476 int comma, s;
477 NYD_ENTER;
479 topp = NULL;
480 if (np == NULL)
481 goto jleave;
483 comma = ntype & GCOMMA;
484 ntype &= ~GCOMMA;
485 s = 0;
486 if ((options & OPT_DEBUG) && comma)
487 fprintf(stderr, tr(145, "detract asked to insert commas\n"));
488 for (p = np; p != NULL; p = p->n_flink) {
489 if (ntype && (p->n_type & GMASK) != ntype)
490 continue;
491 s += strlen(p->n_fullname) + 1;
492 if (comma)
493 s++;
495 if (s == 0)
496 goto jleave;
498 s += 2;
499 topp = salloc(s);
500 cp = topp;
501 for (p = np; p != NULL; p = p->n_flink) {
502 if (ntype && (p->n_type & GMASK) != ntype)
503 continue;
504 cp = sstpcpy(cp, p->n_fullname);
505 if (comma && p->n_flink != NULL)
506 *cp++ = ',';
507 *cp++ = ' ';
509 *--cp = 0;
510 if (comma && *--cp == ',')
511 *cp = 0;
512 jleave:
513 NYD_LEAVE;
514 return topp;
517 FL struct name *
518 grab_names(char const *field, struct name *np, int comma, enum gfield gflags)
520 struct name *nq;
521 NYD_ENTER;
522 jloop:
523 np = lextract(readstr_input(field, detract(np, comma)), gflags);
524 for (nq = np; nq != NULL; nq = nq->n_flink)
525 if (is_addr_invalid(nq, 1))
526 goto jloop;
527 NYD_LEAVE;
528 return np;
531 FL struct name *
532 checkaddrs(struct name *np)
534 struct name *n;
535 NYD_ENTER;
537 for (n = np; n != NULL;) {
538 if (is_addr_invalid(n, 1)) {
539 if (n->n_blink)
540 n->n_blink->n_flink = n->n_flink;
541 if (n->n_flink)
542 n->n_flink->n_blink = n->n_blink;
543 if (n == np)
544 np = n->n_flink;
546 n = n->n_flink;
548 NYD_LEAVE;
549 return np;
552 FL struct name *
553 usermap(struct name *names, bool_t force_metoo)
555 struct name *new, *np, *cp;
556 struct grouphead *gh;
557 int metoo;
558 NYD_ENTER;
560 new = NULL;
561 np = names;
562 metoo = (force_metoo || ok_blook(metoo));
563 while (np != NULL) {
564 assert((np->n_type & GDEL) == 0); /* TODO legacy */
565 if (is_fileorpipe_addr(np) || np->n_name[0] == '\\') {
566 cp = np->n_flink;
567 new = put(new, np);
568 np = cp;
569 continue;
571 gh = findgroup(np->n_name);
572 cp = np->n_flink;
573 if (gh != NULL)
574 new = _gexpand(0, new, gh, metoo, np->n_type);
575 else
576 new = put(new, np);
577 np = cp;
579 NYD_LEAVE;
580 return new;
583 FL struct name *
584 elide(struct name *names)
586 struct name *np, *t, *newn, *x;
587 NYD_ENTER;
589 newn = NULL;
590 if (names == NULL)
591 goto jleave;
593 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
594 for (np = NULL; names != NULL; names = names->n_flink)
595 if (!(names->n_type & GDEL)) {
596 names->n_blink = np;
597 if (np)
598 np->n_flink = names;
599 else
600 newn = names;
601 np = names;
603 if (newn == NULL)
604 goto jleave;
606 np = newn->n_flink;
607 if (np != NULL)
608 np->n_blink = NULL;
609 newn->n_flink = NULL;
611 while (np != NULL) {
612 t = newn;
613 while (asccasecmp(t->n_name, np->n_name) < 0) {
614 if (t->n_flink == NULL)
615 break;
616 t = t->n_flink;
619 /* If we ran out of t's, put new entry after 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;
629 /* Otherwise, put the new entry in front of the current t. If at the
630 * front of the list, the new guy becomes the new head of the list */
631 if (t == newn) {
632 t = np;
633 np = np->n_flink;
634 t->n_flink = newn;
635 newn->n_blink = t;
636 t->n_blink = NULL;
637 newn = t;
638 continue;
641 /* The normal case -- we are inserting into the middle of the list */
642 x = np;
643 np = np->n_flink;
644 x->n_flink = t;
645 x->n_blink = t->n_blink;
646 t->n_blink->n_flink = x;
647 t->n_blink = x;
650 /* Now the list headed up by new is sorted. Remove duplicates */
651 np = newn;
652 while (np != NULL) {
653 t = np;
654 while (t->n_flink != NULL && !asccasecmp(np->n_name, t->n_flink->n_name))
655 t = t->n_flink;
656 if (t == np) {
657 np = np->n_flink;
658 continue;
661 /* Now t points to the last entry with the same name as np.
662 * Make np point beyond t */
663 np->n_flink = t->n_flink;
664 if (t->n_flink != NULL)
665 t->n_flink->n_blink = np;
666 np = np->n_flink;
668 jleave:
669 NYD_LEAVE;
670 return newn;
673 FL struct name *
674 delete_alternates(struct name *np)
676 struct name *xp;
677 char **ap;
678 NYD_ENTER;
680 np = delname(np, myname);
681 if (altnames)
682 for (ap = altnames; *ap != '\0'; ++ap)
683 np = delname(np, *ap);
685 if ((xp = lextract(ok_vlook(from), GEXTRA | GSKIN)) != NULL)
686 while (xp) {
687 np = delname(np, xp->n_name);
688 xp = xp->n_flink;
691 if ((xp = lextract(ok_vlook(replyto), GEXTRA | GSKIN)) != NULL)
692 while (xp) {
693 np = delname(np, xp->n_name);
694 xp = xp->n_flink;
697 if ((xp = extract(ok_vlook(sender), GEXTRA | GSKIN)) != NULL)
698 while (xp) {
699 np = delname(np, xp->n_name);
700 xp = xp->n_flink;
702 NYD_LEAVE;
703 return np;
706 FL int
707 is_myname(char const *name)
709 int rv = 1;
710 struct name *xp;
711 char **ap;
712 NYD_ENTER;
714 if (_same_name(myname, name))
715 goto jleave;
716 if (altnames)
717 for (ap = altnames; *ap != NULL; ++ap)
718 if (_same_name(*ap, name))
719 goto jleave;
721 if ((xp = lextract(ok_vlook(from), GEXTRA | GSKIN)) != NULL)
722 while (xp) {
723 if (_same_name(xp->n_name, name))
724 goto jleave;
725 xp = xp->n_flink;
728 if ((xp = lextract(ok_vlook(replyto), GEXTRA | GSKIN)) != NULL)
729 while (xp) {
730 if (_same_name(xp->n_name, name))
731 goto jleave;
732 xp = xp->n_flink;
735 if ((xp = extract(ok_vlook(sender), GEXTRA | GSKIN)) != NULL)
736 while (xp) {
737 if (_same_name(xp->n_name, name))
738 goto jleave;
739 xp = xp->n_flink;
741 rv = 0;
742 jleave:
743 NYD_LEAVE;
744 return rv;
747 FL struct name *
748 outof(struct name *names, FILE *fo, struct header *hp, bool_t *senderror)
750 ui32_t pipecnt, xcnt, i;
751 int *fda;
752 char const *sh;
753 struct name *np;
754 FILE *fin = NULL, *fout;
755 NYD_ENTER;
756 UNUSED(hp);
758 /* Look through all recipients and do a quick return if no file or pipe
759 * addressee is found */
760 fda = NULL; /* Silence cc */
761 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
762 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
763 case NAME_ADDRSPEC_ISFILE:
764 ++xcnt;
765 break;
766 case NAME_ADDRSPEC_ISPIPE:
767 ++pipecnt;
768 break;
770 if (pipecnt == 0 && xcnt == 0)
771 goto jleave;
773 /* Otherwise create an array of file descriptors for each found pipe
774 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
775 * each pipe subprocess needs its very own file descriptor, and we need
776 * to deal with that.
777 * To make our life a bit easier let's just use the auto-reclaimed
778 * string storage */
779 if (pipecnt == 0) {
780 fda = NULL;
781 sh = NULL;
782 } else {
783 fda = salloc(sizeof(int) * pipecnt);
784 for (i = 0; i < pipecnt; ++i)
785 fda[i] = -1;
786 if ((sh = ok_vlook(SHELL)) == NULL)
787 sh = XSHELL;
790 for (np = names; np != NULL;) {
791 if (!(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)) {
792 np = np->n_flink;
793 continue;
796 /* See if we have copied the complete message out yet. If not, do so */
797 if (image < 0) {
798 int c;
799 char *tempEdit;
801 if ((fout = Ftmp(&tempEdit, "outof",
802 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
803 perror(tr(146, "Creation of temporary image"));
804 *senderror = TRU1;
805 goto jcant;
807 if ((image = open(tempEdit, O_RDWR | O_CLOEXEC)) >= 0) {
808 _SET_CLOEXEC(image);
809 for (i = 0; i < pipecnt; ++i) {
810 int fd = open(tempEdit, O_RDONLY | O_CLOEXEC);
811 if (fd < 0) {
812 close(image);
813 image = -1;
814 pipecnt = i;
815 break;
817 fda[i] = fd;
818 _SET_CLOEXEC(fd);
821 Ftmp_release(&tempEdit);
823 if (image < 0) {
824 perror(tr(147, "Creating descriptor duplicate of temporary image"));
825 *senderror = TRU1;
826 Fclose(fout);
827 goto jcant;
830 fprintf(fout, "From %s %s", myname, time_current.tc_ctime);
831 c = EOF;
832 while (i = c, (c = getc(fo)) != EOF)
833 putc(c, fout);
834 rewind(fo);
835 if ((int)i != '\n')
836 putc('\n', fout);
837 putc('\n', fout);
838 fflush(fout);
839 if (ferror(fout)) {
840 perror(tr(148, "Finalizing write of temporary image"));
841 Fclose(fout);
842 goto jcantfout;
844 Fclose(fout);
846 /* If we have to serve file addressees, open reader */
847 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
848 perror(tr(149,
849 "Failed to open a duplicate of the temporary image"));
850 jcantfout:
851 *senderror = TRU1;
852 close(image);
853 image = -1;
854 goto jcant;
857 /* From now on use xcnt as a counter for pipecnt */
858 xcnt = 0;
861 /* Now either copy "image" to the desired file or give it as the standard
862 * input to the desired program as appropriate */
863 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
864 int pid;
865 sigset_t nset;
867 sigemptyset(&nset);
868 sigaddset(&nset, SIGHUP);
869 sigaddset(&nset, SIGINT);
870 sigaddset(&nset, SIGQUIT);
871 pid = start_command(sh, &nset, fda[xcnt++], -1, "-c",
872 np->n_name + 1, NULL);
873 if (pid < 0) {
874 fprintf(stderr, tr(281, "Message piping to <%s> failed\n"),
875 np->n_name);
876 *senderror = TRU1;
877 goto jcant;
879 free_child(pid);
880 } else {
881 char c, *fname = file_expand(np->n_name);
882 if (fname == NULL) {
883 *senderror = TRU1;
884 goto jcant;
887 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
888 fprintf(stderr, tr(282, "Message writing to <%s> failed: %s\n"),
889 fname, strerror(errno));
890 *senderror = TRU1;
891 goto jcant;
893 rewind(fin);
894 while ((c = getc(fin)) != EOF)
895 putc(c, fout);
896 if (ferror(fout)) {
897 fprintf(stderr, tr(282, "Message writing to <%s> failed: %s\n"),
898 fname, tr(283, "write error"));
899 *senderror = TRU1;
901 Fclose(fout);
903 jcant:
904 /* In days of old we removed the entry from the the list; now for sake of
905 * header expansion we leave it in and mark it as deleted */
906 np->n_type |= GDEL;
907 np = np->n_flink;
908 if (image < 0)
909 goto jdelall;
911 jleave:
912 if (fin != NULL)
913 Fclose(fin);
914 for (i = 0; i < pipecnt; ++i)
915 close(fda[i]);
916 if (image >= 0) {
917 close(image);
918 image = -1;
920 NYD_LEAVE;
921 return names;
923 jdelall:
924 while (np != NULL) {
925 if (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
926 np->n_type |= GDEL;
927 np = np->n_flink;
929 goto jleave;
932 FL struct grouphead *
933 findgroup(char *name)
935 struct grouphead *gh;
936 NYD_ENTER;
938 for (gh = groups[hash(name)]; gh != NULL; gh = gh->g_link)
939 if (*gh->g_name == *name && !strcmp(gh->g_name, name))
940 break;
941 NYD_LEAVE;
942 return gh;
945 FL void
946 printgroup(char *name)
948 struct grouphead *gh;
949 struct group *gp;
950 NYD_ENTER;
952 if ((gh = findgroup(name)) == NULL) {
953 fprintf(stderr, tr(202, "\"%s\": no such alias\n"), name);
954 goto jleave;
957 printf("%s\t", gh->g_name);
958 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link)
959 printf(" %s", gp->ge_name);
960 putchar('\n');
961 jleave:
962 NYD_LEAVE;
965 FL void
966 remove_group(char const *name)
968 ui32_t h;
969 struct grouphead *gh, *gp;
970 NYD_ENTER;
972 h = hash(name);
974 for (gp = NULL, gh = groups[h]; gh != NULL; gh = gh->g_link) {
975 if (*gh->g_name == *name && !strcmp(gh->g_name, name)) {
976 _remove_grouplist(gh);
977 free(gh->g_name);
978 if (gp != NULL)
979 gp->g_link = gh->g_link;
980 else
981 groups[h] = NULL;
982 free(gh);
983 break;
985 gp = gh;
987 NYD_LEAVE;
990 #ifdef _OUR_CLOEXEC
991 # undef O_CLOEXEC
992 # undef _OUR_CLOEXEC
993 #endif
994 #undef _SET_CLOEXEC
996 /* vim:set fenc=utf-8:s-it-mode */