1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2  *@ Implementation of names.h.
3  *@ XXX Use a su_cs_set for alternates stuff?
4  *
5  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6  * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7  * SPDX-License-Identifier: BSD-3-Clause XXX ISC once yank stuff+ changed
8  */
9 /*
10  * Copyright (c) 1980, 1993
11  *      The Regents of the University of California.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 #undef su_FILE
38 #define su_FILE names
39 #define mx_SOURCE
40 #define mx_SOURCE_NAMES /* XXX a lie - it is rather n_ yet */
41 
42 #ifndef mx_HAVE_AMALGAMATION
43 # include "mx/nail.h"
44 #endif
45 
46 #include <su/cs.h>
47 #include <su/cs-dict.h>
48 #include <su/mem.h>
49 #include <su/sort.h>
50 
51 #include "mx/iconv.h"
52 #include "mx/mta-aliases.h"
53 
54 #include "mx/names.h"
55 #include "su/code-in.h"
56 
57 /* ..of a_nm_alias_dp.
58  * We rely on resorting, and use has_key()...lookup() (a_nm_alias_expand()).
59  * The value is a n_strlist*, which we manage directly (no toolbox).
60  * name::n_name, after .sl_dat[.sl_len] one boole that indicates
61  * recursion-allowed, thereafter name::n_fullname (empty if EQ n_name) */
62 #define a_NM_ALIAS_FLAGS (su_CS_DICT_POW2_SPACED |\
63       su_CS_DICT_HEAD_RESORT | su_CS_DICT_AUTO_SHRINK | su_CS_DICT_ERR_PASS)
64 #define a_NM_ALIAS_TRESHOLD_SHIFT 2
65 
66 /* ..of a_nm_a8s_dp */
67 #define a_NM_A8S_FLAGS (su_CS_DICT_CASE |\
68       su_CS_DICT_AUTO_SHRINK | su_CS_DICT_ERR_PASS)
69 #define a_NM_A8S_TRESHOLD_SHIFT 2
70 
71 static struct su_cs_dict *a_nm_alias_dp, a_nm_alias__d; /* XXX atexit gut()..*/
72 static struct su_cs_dict *a_nm_a8s_dp, a_nm_a8s__d; /* XXX .. (DVL()) */
73 
74 /* Same name, while taking care for *allnet*? */
75 static boole a_nm_is_same_name(char const *n1, char const *n2,
76       boole *isall_or_nil);
77 
78 /* Mark all (!file, !pipe) nodes with the given name */
79 static struct mx_name *a_nm_namelist_mark_name(struct mx_name *np,
80       char const *name);
81 
82 /* Grab a single name (liberal name) */
83 static char const *a_nm_yankname(char const *ap, char *wbuf,
84       char const *separators, int keepcomms);
85 
86 /* Extraction multiplexer that splits an input line to names */
87 static struct mx_name *a_nm_extract1(char const *line, enum gfield ntype,
88       char const *separators, boole keepcomms);
89 
90 /* elide() helper */
91 static su_sz a_nm_elide_sort(void const *s1, void const *s2);
92 
93 /* Recursively expand an alias name, adjust nlist for result and return it;
94  * limit expansion to some fixed level.
95  * metoo=*metoo*, logname=$LOGNAME == optimization */
96 static struct mx_name *a_nm_alias_expand(uz level, struct mx_name *nlist,
97       char const *name, int ntype, boole metoo, char const *logname);
98 
99 /* */
100 static struct n_strlist *a_nm_alias_dump(char const *cmdname, char const *key,
101       void const *dat);
102 
103 /* */
104 static struct n_strlist *a_nm_a8s_dump(char const *cmdname, char const *key,
105       void const *dat);
106 
107 static boole
a_nm_is_same_name(char const * n1,char const * n2,boole * isall_or_nil)108 a_nm_is_same_name(char const *n1, char const *n2, boole *isall_or_nil){
109    char c1, c2, c1r, c2r;
110    boole rv;
111    NYD2_IN;
112 
113    rv = ok_blook(allnet);
114 
115    if(isall_or_nil != NIL)
116       *isall_or_nil = rv;
117 
118    if(!rv)
119       rv = !su_cs_cmp_case(n1, n2);
120    else{
121       for(;; ++n1, ++n2){
122          c1 = *n1;
123          c1 = su_cs_to_lower(c1);
124          c1r = (c1 == '\0' || c1 == '@');
125          c2 = *n2;
126          c2 = su_cs_to_lower(c2);
127          c2r = (c2 == '\0' || c2 == '@');
128 
129          if(c1r || c2r){
130             rv = (c1r == c2r);
131             break;
132          }else if(c1 != c2){
133             rv = FAL0;
134             break;
135          }
136       }
137    }
138 
139    NYD2_OU;
140    return rv;
141 }
142 
143 static struct mx_name *
a_nm_namelist_mark_name(struct mx_name * np,char const * name)144 a_nm_namelist_mark_name(struct mx_name *np, char const *name){
145    struct mx_name *p;
146    NYD2_IN;
147 
148    for(p = np; p != NULL; p = p->n_flink)
149       if(!(p->n_type & GDEL) &&
150             !(p->n_flags & (S(u32,S32_MIN) | mx_NAME_ADDRSPEC_ISFILE |
151                mx_NAME_ADDRSPEC_ISPIPE)) &&
152             a_nm_is_same_name(p->n_name, name, NIL))
153          p->n_flags |= S(u32,S32_MIN);
154    NYD2_OU;
155    return np;
156 }
157 
158 static char const *
a_nm_yankname(char const * ap,char * wbuf,char const * separators,int keepcomms)159 a_nm_yankname(char const *ap, char *wbuf, char const *separators,
160    int keepcomms)
161 {
162    char const *cp;
163    char *wp, c, inquote, lc, lastsp;
164    NYD_IN;
165 
166    *(wp = wbuf) = '\0';
167 
168    /* Skip over intermediate list trash, as in ".org>  ,  <xy@zz.org>" */
169    for (c = *ap; su_cs_is_blank(c) || c == ','; c = *++ap)
170       ;
171    if (c == '\0') {
172       cp = NULL;
173       goto jleave;
174    }
175 
176    /* Parse a full name: TODO RFC 5322
177     * - Keep everything in quotes, liberal handle *quoted-pair*s therein
178     * - Skip entire (nested) comments
179     * - In non-quote, non-comment, join adjacent space to a single SP
180     * - Understand separators only in non-quote, non-comment context,
181     *   and only if not part of a *quoted-pair* (XXX too liberal) */
182    cp = ap;
183    for (inquote = lc = lastsp = 0;; lc = c, ++cp) {
184       c = *cp;
185       if (c == '\0')
186          break;
187       if (c == '\\')
188          goto jwpwc;
189       if (c == '"') {
190          if (lc != '\\')
191             inquote = !inquote;
192 #if 0 /* TODO when doing real RFC 5322 parsers - why have i done this? */
193          else
194             --wp;
195 #endif
196          goto jwpwc;
197       }
198       if (inquote || lc == '\\') {
199 jwpwc:
200          *wp++ = c;
201          lastsp = 0;
202          continue;
203       }
204       if (c == '(') {
205          ap = cp;
206          cp = skip_comment(cp + 1);
207          if (keepcomms)
208             while (ap < cp)
209                *wp++ = *ap++;
210          --cp;
211          lastsp = 0;
212          continue;
213       }
214       if (su_cs_find_c(separators, c) != NULL)
215          break;
216 
217       lc = lastsp;
218       lastsp = su_cs_is_blank(c);
219       if (!lastsp || !lc)
220          *wp++ = c;
221    }
222    if (su_cs_is_blank(lc))
223       --wp;
224 
225    *wp = '\0';
226 jleave:
227    NYD_OU;
228    return cp;
229 }
230 
231 static struct mx_name *
a_nm_extract1(char const * line,enum gfield ntype,char const * seps,boole keepcomms)232 a_nm_extract1(char const *line, enum gfield ntype, char const *seps,
233       boole keepcomms){
234    struct mx_name *headp, *tailp, *np;
235    NYD_IN;
236 
237    headp = NULL;
238 
239    if(line == NULL || *line == '\0')
240       ;
241    else if(ntype & GNOT_A_LIST)
242       /* Not GNULL_OK! (yet: see below) */
243       headp = nalloc(line, ntype | GSKIN | GNOT_A_LIST);
244    else{
245       char const *cp;
246       char *nbuf;
247 
248       nbuf = n_lofi_alloc(su_cs_len(cp = line) +1);
249 
250       for(tailp = headp;
251             ((cp = a_nm_yankname(cp, nbuf, seps, keepcomms)) != NULL);){
252          /* TODO Cannot set GNULL_OK because otherwise this software blows up.
253           * TODO We will need a completely new way of reporting the errors of
254           * TODO is_addr_invalid() ... */
255          if((np = nalloc(nbuf, ntype /*| GNULL_OK*/)) != NULL){
256             if((np->n_blink = tailp) != NULL)
257                tailp->n_flink = np;
258             else
259                headp = np;
260             tailp = np;
261          }
262       }
263 
264       n_lofi_free(nbuf);
265    }
266 
267    NYD_OU;
268    return headp;
269 }
270 
271 static su_sz
a_nm_elide_sort(void const * s1,void const * s2)272 a_nm_elide_sort(void const *s1, void const *s2){
273    struct mx_name const *np1, *np2;
274    su_sz rv;
275    NYD2_IN;
276 
277    np1 = s1;
278    np2 = s2;
279    if(!(rv = su_cs_cmp_case(np1->n_name, np2->n_name))){
280       LCTAV(GTO < GCC && GCC < GBCC);
281       rv = (np1->n_type & (GTO | GCC | GBCC)) -
282             (np2->n_type & (GTO | GCC | GBCC));
283    }
284    NYD2_OU;
285    return rv;
286 }
287 
288 static struct mx_name *
a_nm_alias_expand(uz level,struct mx_name * nlist,char const * name,int ntype,boole metoo,char const * logname)289 a_nm_alias_expand(uz level, struct mx_name *nlist, char const *name, int ntype,
290       boole metoo, char const *logname){
291    struct mx_name *np, *nlist_tail;
292    char const *ccp;
293    struct n_strlist const *slp, *slp_base, *slp_next;
294    NYD2_IN;
295    ASSERT_NYD(a_nm_alias_dp != NIL);
296    ASSERT(mx_alias_is_valid_name(name));
297 
298    UNINIT(slp_base, NIL);
299 
300    if(UCMP(z, level++, ==, n_ALIAS_MAXEXP)){ /* TODO not a real error!! */
301       n_err(_("alias: stopping recursion at depth %d\n"), n_ALIAS_MAXEXP);
302       slp_next = NIL;
303       ccp = name;
304       goto jlinkin;
305    }
306 
307    slp_next = slp_base =
308    slp = S(struct n_strlist const*,su_cs_dict_lookup(a_nm_alias_dp, name));
309 
310    if(slp == NIL){
311       ccp = name;
312       goto jlinkin;
313    }
314    do{ /* while(slp != NIL); */
315       slp_next = slp->sl_next;
316 
317       if(slp->sl_len == 0)
318          continue;
319 
320       /* Cannot shadow itself.  Recursion allowed for target? */
321       if(su_cs_cmp(name, slp->sl_dat) && slp->sl_dat[slp->sl_len + 1] != FAL0){
322          /* For S-nail(1), the "alias" may *be* the sender in that a name
323           * to a full address specification */
324          nlist = a_nm_alias_expand(level, nlist, slp->sl_dat, ntype, metoo,
325                logname);
326          continue;
327       }
328 
329       /* Here we should allow to expand to itself if only person in alias */
330       if(metoo || slp_base->sl_next == NIL ||
331             !a_nm_is_same_name(slp->sl_dat, logname, NIL)){
332          /* Use .n_name if .n_fullname is not set */
333          if(*(ccp = &slp->sl_dat[slp->sl_len + 2]) == '\0')
334             ccp = slp->sl_dat;
335 
336 jlinkin:
337          if((np = n_extract_single(ccp, ntype | GFULL)) != NIL){
338             if((nlist_tail = nlist) != NIL){ /* XXX su_list_push()! */
339                while(nlist_tail->n_flink != NIL)
340                   nlist_tail = nlist_tail->n_flink;
341                nlist_tail->n_flink = np;
342                np->n_blink = nlist_tail;
343             }else
344                nlist = np;
345          }
346       }
347    }while((slp = slp_next) != NIL);
348 
349    NYD2_OU;
350    return nlist;
351 }
352 
353 static struct n_strlist *
a_nm_alias_dump(char const * cmdname,char const * key,void const * dat)354 a_nm_alias_dump(char const *cmdname, char const *key, void const *dat){
355    struct n_string s_b, *s;
356    struct n_strlist *slp;
357    NYD2_IN;
358 
359    s = n_string_creat_auto(&s_b);
360    s = n_string_resize(s, 511);
361    s = n_string_trunc(s, VSTRUCT_SIZEOF(struct n_strlist, sl_dat)); /* gross */
362 
363    s = n_string_push_cp(s, cmdname);
364    s = n_string_push_c(s, ' ');
365    s = n_string_push_cp(s, key); /*n_shexp_quote_cp(key, TRU1); valid alias */
366 
367    for(slp = UNCONST(struct n_strlist*,dat); slp != NIL; slp = slp->sl_next){
368       s = n_string_push_c(s, ' ');
369       /* Use .n_fullname if available, fall back to .n_name */
370       key = &slp->sl_dat[slp->sl_len + 2];
371       if(*key == '\0')
372          key = slp->sl_dat;
373       s = n_string_push_cp(s, n_shexp_quote_cp(key, TRU1));
374    }
375 
376    slp = C(struct n_strlist*,S(void const*,n_string_cp(s)));
377    slp->sl_next = NIL;
378    slp->sl_len = s->s_len - VSTRUCT_SIZEOF(struct n_strlist, sl_dat);
379 
380    NYD2_OU;
381    return slp;
382 }
383 
384 static struct n_strlist *
a_nm_a8s_dump(char const * cmdname,char const * key,void const * dat)385 a_nm_a8s_dump(char const *cmdname, char const *key, void const *dat){
386    /* XXX real strlist + str_to_fmt() */
387    struct n_strlist *slp;
388    uz kl;
389    NYD2_IN;
390    UNUSED(cmdname);
391    UNUSED(dat);
392 
393    /*key = n_shexp_quote_cp(key, TRU1); plain address: not needed */
394    kl = su_cs_len(key);
395 
396    slp = n_STRLIST_AUTO_ALLOC(kl +1);
397    slp->sl_next = NIL;
398    slp->sl_len = kl;
399    su_mem_copy(slp->sl_dat, key, kl +1);
400    NYD2_OU;
401    return slp;
402 }
403 
404 struct mx_name *
nalloc(char const * str,enum gfield ntype)405 nalloc(char const *str, enum gfield ntype)
406 {
407    struct n_addrguts ag;
408    struct str in, out;
409    struct mx_name *np;
410    NYD_IN;
411    ASSERT(!(ntype & GFULLEXTRA) || (ntype & GFULL) != 0);
412 
413    str = n_addrspec_with_guts(&ag, str, ntype);
414    if(str == NULL){
415       /* TODO this may not return NULL but for new-style callers */
416       if(ntype & GNULL_OK){
417          np = NULL;
418          goto jleave;
419       }
420    }
421    ntype &= ~(GNOT_A_LIST | GNULL_OK); /* (all this a hack is) */
422    str = ag.ag_input; /* Take the possibly reordered thing */
423 
424    if (!(ag.ag_n_flags & mx_NAME_NAME_SALLOC)) {
425       ag.ag_n_flags |= mx_NAME_NAME_SALLOC;
426       np = n_autorec_alloc(sizeof(*np) + ag.ag_slen +1);
427       su_mem_copy(np + 1, ag.ag_skinned, ag.ag_slen +1);
428       ag.ag_skinned = (char*)(np + 1);
429    } else
430       np = n_autorec_alloc(sizeof *np);
431 
432    np->n_flink = NULL;
433    np->n_blink = NULL;
434    np->n_type = ntype;
435    np->n_fullname = np->n_name = ag.ag_skinned;
436    np->n_fullextra = NULL;
437    np->n_flags = ag.ag_n_flags;
438 
439    if (ntype & GFULL) {
440       if (ag.ag_ilen == ag.ag_slen
441 #ifdef mx_HAVE_IDNA
442             && !(ag.ag_n_flags & mx_NAME_IDNA)
443 #endif
444       )
445          goto jleave;
446       if (ag.ag_n_flags & mx_NAME_ADDRSPEC_ISFILEORPIPE)
447          goto jleave;
448 
449       /* n_fullextra is only the complete name part without address.
450        * Beware of "-r '<abc@def>'", don't treat that as FULLEXTRA */
451       if ((ntype & GFULLEXTRA) && ag.ag_ilen > ag.ag_slen + 2) {
452          uz s = ag.ag_iaddr_start, e = ag.ag_iaddr_aend, i;
453          char const *cp;
454 
455          if (s == 0 || str[--s] != '<' || str[e++] != '>')
456             goto jskipfullextra;
457          i = ag.ag_ilen - e;
458          in.s = n_lofi_alloc(s + 1 + i +1);
459          while(s > 0 && su_cs_is_blank(str[s - 1]))
460             --s;
461          su_mem_copy(in.s, str, s);
462          if (i > 0) {
463             in.s[s++] = ' ';
464             while (su_cs_is_blank(str[e])) {
465                ++e;
466                if (--i == 0)
467                   break;
468             }
469             if (i > 0)
470                su_mem_copy(&in.s[s], &str[e], i);
471          }
472          s += i;
473          in.s[in.l = s] = '\0';
474          mime_fromhdr(&in, &out, /* TODO TD_ISPR |*/ TD_ICONV);
475 
476          for (cp = out.s, i = out.l; i > 0 && su_cs_is_space(*cp); --i, ++cp)
477             ;
478          while (i > 0 && su_cs_is_space(cp[i - 1]))
479             --i;
480          np->n_fullextra = savestrbuf(cp, i);
481 
482          n_lofi_free(in.s);
483          n_free(out.s);
484       }
485 jskipfullextra:
486 
487       /* n_fullname depends on IDNA conversion */
488 #ifdef mx_HAVE_IDNA
489       if (!(ag.ag_n_flags & mx_NAME_IDNA)) {
490 #endif
491          in.s = UNCONST(char*,str);
492          in.l = ag.ag_ilen;
493 #ifdef mx_HAVE_IDNA
494       } else {
495          /* The domain name was IDNA and has been converted.  We also have to
496           * ensure that the domain name in .n_fullname is replaced with the
497           * converted version, since MIME doesn't perform encoding of addrs */
498          /* TODO This definitily doesn't belong here! */
499          uz l = ag.ag_iaddr_start,
500             lsuff = ag.ag_ilen - ag.ag_iaddr_aend;
501          in.s = n_lofi_alloc(l + ag.ag_slen + lsuff +1);
502          su_mem_copy(in.s, str, l);
503          su_mem_copy(in.s + l, ag.ag_skinned, ag.ag_slen);
504          l += ag.ag_slen;
505          su_mem_copy(in.s + l, str + ag.ag_iaddr_aend, lsuff);
506          l += lsuff;
507          in.s[l] = '\0';
508          in.l = l;
509       }
510 #endif
511       mime_fromhdr(&in, &out, /* TODO TD_ISPR |*/ TD_ICONV);
512       np->n_fullname = savestr(out.s);
513       n_free(out.s);
514 #ifdef mx_HAVE_IDNA
515       if (ag.ag_n_flags & mx_NAME_IDNA)
516          n_lofi_free(in.s);
517 #endif
518    }
519 
520 jleave:
521    NYD_OU;
522    return np;
523 }
524 
525 struct mx_name *
nalloc_fcc(char const * file)526 nalloc_fcc(char const *file){
527    struct mx_name *nnp;
528    NYD_IN;
529 
530    nnp = n_autorec_alloc(sizeof *nnp);
531    nnp->n_flink = nnp->n_blink = NULL;
532    nnp->n_type = GBCC | GBCC_IS_FCC; /* xxx Bcc: <- namelist_vaporise_head */
533    nnp->n_flags = mx_NAME_NAME_SALLOC | mx_NAME_SKINNED |
534          mx_NAME_ADDRSPEC_ISFILE;
535    nnp->n_fullname = nnp->n_name = savestr(file);
536    nnp->n_fullextra = NULL;
537    NYD_OU;
538    return nnp;
539 }
540 
541 struct mx_name *
ndup(struct mx_name * np,enum gfield ntype)542 ndup(struct mx_name *np, enum gfield ntype)
543 {
544    struct mx_name *nnp;
545    NYD_IN;
546 
547    if ((ntype & (GFULL | GSKIN)) && !(np->n_flags & mx_NAME_SKINNED)) {
548       nnp = nalloc(np->n_name, ntype);
549       goto jleave;
550    }
551 
552    nnp = n_autorec_alloc(sizeof *np);
553    nnp->n_flink = nnp->n_blink = NULL;
554    nnp->n_type = ntype & ~(GFULL | GFULLEXTRA);
555    nnp->n_flags = np->n_flags | mx_NAME_NAME_SALLOC;
556    nnp->n_name = savestr(np->n_name);
557 
558    if(np->n_name == np->n_fullname || !(ntype & GFULL)){
559       ASSERT((ntype & GFULL) || !(ntype & GFULLEXTRA));
560       nnp->n_fullname = nnp->n_name;
561       nnp->n_fullextra = NULL;
562    }else{
563       nnp->n_fullname = savestr(np->n_fullname);
564       nnp->n_fullextra = (!(ntype & GFULLEXTRA) || np->n_fullextra == NIL)
565             ? NIL : savestr(np->n_fullextra);
566       nnp->n_type = ntype;
567    }
568 
569 jleave:
570    NYD_OU;
571    return nnp;
572 }
573 
574 struct mx_name *
cat(struct mx_name * n1,struct mx_name * n2)575 cat(struct mx_name *n1, struct mx_name *n2){
576    struct mx_name *tail;
577    NYD_IN;
578 
579    tail = n2;
580    if(n1 == NULL)
581       goto jleave;
582 
583    tail = n1;
584    if(n2 == NULL || (n2->n_type & GDEL))
585       goto jleave;
586 
587    while(tail->n_flink != NULL)
588       tail = tail->n_flink;
589    tail->n_flink = n2;
590    n2->n_blink = tail;
591    tail = n1;
592 
593 jleave:
594    NYD_OU;
595    return tail;
596 }
597 
598 struct mx_name *
n_namelist_dup(struct mx_name const * np,enum gfield ntype)599 n_namelist_dup(struct mx_name const *np, enum gfield ntype){
600    struct mx_name *nlist, *xnp;
601    NYD_IN;
602 
603    for(nlist = xnp = NULL; np != NULL; np = np->n_flink){
604       struct mx_name *x;
605 
606       if(!(np->n_type & GDEL)){
607          x = ndup(UNCONST(struct mx_name*,np), (np->n_type & ~GMASK) | ntype);
608          if((x->n_blink = xnp) == NULL)
609             nlist = x;
610          else
611             xnp->n_flink = x;
612          xnp = x;
613       }
614    }
615    NYD_OU;
616    return nlist;
617 }
618 
619 u32
count(struct mx_name const * np)620 count(struct mx_name const *np){
621    u32 c;
622    NYD_IN;
623 
624    for(c = 0; np != NIL; np = np->n_flink)
625       if(!(np->n_type & GDEL))
626          ++c;
627    NYD_OU;
628    return c;
629 }
630 
631 u32
count_nonlocal(struct mx_name const * np)632 count_nonlocal(struct mx_name const *np){
633    u32 c;
634    NYD_IN;
635 
636    for(c = 0; np != NIL; np = np->n_flink)
637       if(!(np->n_type & GDEL) &&
638             !(np->n_flags & mx_NAME_ADDRSPEC_ISFILEORPIPE))
639          ++c;
640    NYD_OU;
641    return c;
642 }
643 
644 struct mx_name *
extract(char const * line,enum gfield ntype)645 extract(char const *line, enum gfield ntype)
646 {
647    struct mx_name *rv;
648    NYD_IN;
649 
650    rv = a_nm_extract1(line, ntype, " \t,", 0);
651    NYD_OU;
652    return rv;
653 }
654 
655 struct mx_name *
lextract(char const * line,enum gfield ntype)656 lextract(char const *line, enum gfield ntype)
657 {
658    char *cp;
659    struct mx_name *rv;
660    NYD_IN;
661 
662    if(!(ntype & GSHEXP_PARSE_HACK) || !(expandaddr_to_eaf() & EAF_SHEXP_PARSE))
663       cp = NULL;
664    else{
665       struct str sin;
666       struct n_string s_b, *s;
667       BITENUM_IS(u32,n_shexp_state) shs;
668 
669       n_autorec_relax_create();
670       s = n_string_creat_auto(&s_b);
671       sin.s = UNCONST(char*,line); /* logical */
672       sin.l = UZ_MAX;
673       shs = n_shexp_parse_token((n_SHEXP_PARSE_LOG |
674             n_SHEXP_PARSE_IGNORE_EMPTY | n_SHEXP_PARSE_QUOTE_AUTO_FIXED |
675             n_SHEXP_PARSE_QUOTE_AUTO_DSQ), s, &sin, NULL);
676       if(!(shs & n_SHEXP_STATE_ERR_MASK) && (shs & n_SHEXP_STATE_STOP)){
677          line = cp = n_lofi_alloc(s->s_len +1);
678          su_mem_copy(cp, n_string_cp(s), s->s_len +1);
679       }else
680          line = cp = su_NIL;
681       n_autorec_relax_gut();
682    }
683 
684    if(line == su_NIL)
685       rv = su_NIL;
686    else if((ntype & GNOT_A_LIST) || strpbrk(line, ",\"\\(<|"))
687       rv = a_nm_extract1(line, ntype, ",", 1);
688    else
689       rv = extract(line, ntype);
690 
691    if(cp != su_NIL)
692       n_lofi_free(cp);
693    NYD_OU;
694    return rv;
695 }
696 
697 struct mx_name *
n_extract_single(char const * line,enum gfield ntype)698 n_extract_single(char const *line, enum gfield ntype){
699    struct mx_name *rv;
700    NYD_IN;
701 
702    rv = nalloc(line, ntype | GSKIN | GNOT_A_LIST | GNULL_OK);
703    NYD_OU;
704    return rv;
705 }
706 
707 char *
detract(struct mx_name * np,enum gfield ntype)708 detract(struct mx_name *np, enum gfield ntype)
709 {
710    char *topp, *cp;
711    struct mx_name *p;
712    int flags, s;
713    NYD_IN;
714 
715    topp = NULL;
716    if (np == NULL)
717       goto jleave;
718 
719    flags = ntype & (GCOMMA | GNAMEONLY);
720    ntype &= ~(GCOMMA | GNAMEONLY);
721    s = 0;
722 
723    for (p = np; p != NULL; p = p->n_flink) {
724       if (ntype && (p->n_type & GMASK) != ntype)
725          continue;
726       s += su_cs_len(flags & GNAMEONLY ? p->n_name : p->n_fullname) +1;
727       if (flags & GCOMMA)
728          ++s;
729    }
730    if (s == 0)
731       goto jleave;
732 
733    s += 2;
734    topp = n_autorec_alloc(s);
735    cp = topp;
736    for (p = np; p != NULL; p = p->n_flink) {
737       if (ntype && (p->n_type & GMASK) != ntype)
738          continue;
739       cp = su_cs_pcopy(cp, (flags & GNAMEONLY ? p->n_name : p->n_fullname));
740       if ((flags & GCOMMA) && p->n_flink != NULL)
741          *cp++ = ',';
742       *cp++ = ' ';
743    }
744    *--cp = 0;
745    if ((flags & GCOMMA) && *--cp == ',')
746       *cp = 0;
747 jleave:
748    NYD_OU;
749    return topp;
750 }
751 
752 struct mx_name *
grab_names(enum n_go_input_flags gif,char const * field,struct mx_name * np,int comma,enum gfield gflags)753 grab_names(enum n_go_input_flags gif, char const *field, struct mx_name *np,
754       int comma, enum gfield gflags)
755 {
756    struct mx_name *nq;
757    NYD_IN;
758 
759 jloop:
760    np = lextract(n_go_input_cp(gif, field, detract(np, comma)), gflags);
761    for (nq = np; nq != NULL; nq = nq->n_flink)
762       if (is_addr_invalid(nq, EACM_NONE))
763          goto jloop;
764    NYD_OU;
765    return np;
766 }
767 
768 boole
mx_name_is_same_address(struct mx_name const * n1,struct mx_name const * n2)769 mx_name_is_same_address(struct mx_name const *n1, struct mx_name const *n2){
770    boole isall, rv;
771    NYD2_IN;
772 
773    rv = (a_nm_is_same_name(n1->n_name, n2->n_name, &isall) &&
774          (isall || mx_name_is_same_domain(n1, n2)));
775 
776    NYD2_OU;
777    return rv;
778 }
779 
780 boole
mx_name_is_same_domain(struct mx_name const * n1,struct mx_name const * n2)781 mx_name_is_same_domain(struct mx_name const *n1, struct mx_name const *n2){
782    boole rv;
783    NYD2_IN;
784 
785    if((rv = (n1 != NIL && n2 != NIL))){
786       char const *d1, *d2;
787 
788       d1 = su_cs_rfind_c(n1->n_name, '@');
789       d2 = su_cs_rfind_c(n2->n_name, '@');
790 
791       rv = (d1 != NIL && d2 != NIL) ? !su_cs_cmp_case(++d1, ++d2) : FAL0;
792    }
793 
794    NYD2_OU;
795    return rv;
796 }
797 
798 struct mx_name *
checkaddrs(struct mx_name * np,enum expand_addr_check_mode eacm,s8 * set_on_error)799 checkaddrs(struct mx_name *np, enum expand_addr_check_mode eacm,
800    s8 *set_on_error)
801 {
802    struct mx_name *n;
803    NYD_IN;
804 
805    for (n = np; n != NULL; n = n->n_flink) {
806       s8 rv;
807 
808       if ((rv = is_addr_invalid(n, eacm)) != 0) {
809          if (set_on_error != NULL)
810             *set_on_error |= rv; /* don't loose -1! */
811          if (eacm & EAF_MAYKEEP) /* TODO HACK!  See definition! */
812             continue;
813          if (n->n_blink)
814             n->n_blink->n_flink = n->n_flink;
815          if (n->n_flink)
816             n->n_flink->n_blink = n->n_blink;
817          if (n == np)
818             np = n->n_flink;
819       }
820    }
821    NYD_OU;
822    return np;
823 }
824 
825 struct mx_name *
n_namelist_vaporise_head(struct header * hp,boole metoo,boole strip_alternates,enum expand_addr_check_mode eacm,s8 * set_on_error)826 n_namelist_vaporise_head(struct header *hp, boole metoo,
827    boole strip_alternates, enum expand_addr_check_mode eacm, s8 *set_on_error)
828 {
829    /* TODO namelist_vaporise_head() is incredibly expensive and redundant */
830    struct mx_name *tolist, *np, **npp;
831    NYD_IN;
832 
833    tolist = cat(hp->h_to, cat(hp->h_cc, cat(hp->h_bcc, hp->h_fcc)));
834    hp->h_to = hp->h_cc = hp->h_bcc = hp->h_fcc = NULL;
835 
836    tolist = usermap(tolist, metoo);
837 
838    /* MTA aliases are resolved last */
839 #ifdef mx_HAVE_MTA_ALIASES
840    switch(mx_mta_aliases_expand(&tolist)){
841    case su_ERR_DESTADDRREQ:
842    case su_ERR_NONE:
843    case su_ERR_NOENT:
844       break;
845    default:
846       *set_on_error |= TRU1;
847       break;
848    }
849 #endif
850 
851    if(strip_alternates)
852       tolist = mx_alternates_remove(tolist, TRU1);
853 
854    tolist = elide(tolist);
855 
856    tolist = checkaddrs(tolist, eacm, set_on_error);
857 
858    for (np = tolist; np != NULL; np = np->n_flink) {
859       switch (np->n_type & (GDEL | GMASK)) {
860       case GTO:   npp = &hp->h_to; break;
861       case GCC:   npp = &hp->h_cc; break;
862       case GBCC:  npp = &hp->h_bcc; break;
863       default:    continue;
864       }
865       *npp = cat(*npp, ndup(np, np->n_type | GFULL));
866    }
867 
868    NYD_OU;
869    return tolist;
870 }
871 
872 struct mx_name *
usermap(struct mx_name * names,boole force_metoo)873 usermap(struct mx_name *names, boole force_metoo){
874    struct su_cs_dict_view dv;
875    struct mx_name *nlist, **nlist_tail, *np, *nxtnp;
876    int metoo;
877    char const *logname;
878    NYD_IN;
879 
880    logname = ok_vlook(LOGNAME);
881    metoo = (force_metoo || ok_blook(metoo));
882    nlist = NIL;
883    nlist_tail = &nlist;
884    np = names;
885 
886    if(a_nm_alias_dp != NIL)
887       su_cs_dict_view_setup(&dv, a_nm_alias_dp);
888 
889    for(; np != NULL; np = nxtnp){
890       ASSERT(!(np->n_type & GDEL)); /* TODO legacy */
891       nxtnp = np->n_flink;
892 
893       /* Only valid alias names may enter expansion; even so GFULL may cause
894        * .n_fullname to be different memory, it will be bitwise equal) */
895       if(is_fileorpipe_addr(np) || (np->n_name != np->n_fullname &&
896                su_cs_cmp(np->n_name, np->n_fullname)) ||
897             a_nm_alias_dp == NIL || !su_cs_dict_view_find(&dv, np->n_name)){
898          np->n_blink = *nlist_tail;
899          np->n_flink = NIL;
900          *nlist_tail = np;
901          nlist_tail = &np->n_flink;
902       }else{
903          nlist = a_nm_alias_expand(0, nlist, np->n_name, np->n_type, metoo,
904                logname);
905          if((np = nlist) == NIL)
906             nlist_tail = &nlist;
907          else for(;; np = np->n_flink)
908             if(np->n_flink == NIL){
909                nlist_tail = &np->n_flink;
910                break;
911             }
912       }
913    }
914 
915    NYD_OU;
916    return nlist;
917 }
918 
919 struct mx_name *
elide(struct mx_name * names)920 elide(struct mx_name *names)
921 {
922    uz i, j, k;
923    struct mx_name *nlist, *np, **nparr;
924    NYD_IN;
925 
926    nlist = NULL;
927 
928    if(names == NULL)
929       goto jleave;
930 
931    /* Throw away all deleted nodes */
932    for(np = NULL, i = 0; names != NULL; names = names->n_flink)
933       if(!(names->n_type & GDEL)){
934          names->n_blink = np;
935          if(np != NULL)
936             np->n_flink = names;
937          else
938             nlist = names;
939          np = names;
940          ++i;
941       }
942    if(nlist == NULL || i == 1)
943       goto jleave;
944    np->n_flink = NULL;
945 
946    /* Create a temporary array and sort that */
947    nparr = n_lofi_alloc(sizeof(*nparr) * i);
948 
949    for(i = 0, np = nlist; np != NULL; np = np->n_flink)
950       nparr[i++] = np;
951 
952    su_sort_shell_vpp(su_S(void const**,nparr), i, &a_nm_elide_sort);
953 
954    /* Remove duplicates XXX speedup, or list_uniq()! */
955    for(j = 0, --i; j < i;){
956       if(su_cs_cmp_case(nparr[j]->n_name, nparr[k = j + 1]->n_name))
957          ++j;
958       else{
959          for(; k < i; ++k)
960             nparr[k] = nparr[k + 1];
961          --i;
962       }
963    }
964 
965    /* Throw away all list members which are not part of the array.
966     * Note this keeps the original, possibly carefully crafted, order of the
967     * addressees, thus.. */
968    for(np = nlist; np != NULL; np = np->n_flink){
969       for(j = 0; j <= i; ++j)
970          /* The order of pointers depends on the sorting algorithm, and
971           * therefore our desire to keep the original order of addessees cannot
972           * be guaranteed when there are multiple equal names (ham zebra ham)
973           * of equal weight: we need to compare names _once_again_ :( */
974          if(nparr[j] != NULL && !su_cs_cmp_case(np->n_name, nparr[j]->n_name)){
975             nparr[j] = NULL;
976             goto jiter;
977          }
978       /* Drop it */
979       if(np == nlist){
980          nlist = np->n_flink;
981          np->n_blink = NULL;
982       }else
983          np->n_blink->n_flink = np->n_flink;
984       if(np->n_flink != NULL)
985          np->n_flink->n_blink = np->n_blink;
986 jiter:;
987    }
988 
989    n_lofi_free(nparr);
990 jleave:
991    NYD_OU;
992    return nlist;
993 }
994 
995 int
c_alias(void * vp)996 c_alias(void *vp){
997    struct su_cs_dict_view dv;
998    union {void const *cvp; boole haskey; struct n_strlist *slp;} dat;
999    int rv;
1000    char const **argv, *key;
1001    NYD_IN;
1002 
1003    if((key = *(argv = S(char const**,vp))) == NIL){
1004       dat.slp = NIL;
1005       rv = !(mx_xy_dump_dict("alias", a_nm_alias_dp, &dat.slp, NIL,
1006                &a_nm_alias_dump) &&
1007             mx_page_or_print_strlist("alias", dat.slp, FAL0));
1008       goto jleave;
1009    }
1010 
1011    if(argv[1] != NIL && argv[2] == NIL && key[0] == '-' && key[1] == '\0')
1012       key = argv[1];
1013 
1014    if(!mx_alias_is_valid_name(key)){
1015       n_err(_("alias: not a valid name: %s\n"), n_shexp_quote_cp(key, FAL0));
1016       rv = 1;
1017       goto jleave;
1018    }
1019 
1020    if(a_nm_alias_dp != NIL && su_cs_dict_view_find(
1021             su_cs_dict_view_setup(&dv, a_nm_alias_dp), key))
1022       dat.cvp = su_cs_dict_view_data(&dv);
1023    else
1024       dat.cvp = NIL;
1025 
1026    if(argv[1] == NIL || key == argv[1]){
1027       if(dat.cvp != NIL){
1028          if(argv[1] == NIL){
1029             dat.slp = a_nm_alias_dump("alias", key, dat.cvp);
1030             rv = (fputs(dat.slp->sl_dat, n_stdout) == EOF);
1031             rv |= (putc('\n', n_stdout) == EOF);
1032          }else{
1033             struct mx_name *np;
1034 
1035             np = a_nm_alias_expand(0, NIL, key, 0, TRU1, ok_vlook(LOGNAME));
1036             np = elide(np);
1037             rv = (fprintf(n_stdout, "alias %s", key) < 0);
1038             if(!rv){
1039                for(; np != NIL; np = np->n_flink){
1040                   rv |= (putc(' ', n_stdout) == EOF);
1041                   rv |= (fputs(n_shexp_quote_cp(np->n_fullname, TRU1),
1042                         n_stdout) == EOF);
1043                }
1044                rv |= (putc('\n', n_stdout) == EOF);
1045             }
1046          }
1047       }else{
1048          n_err(_("No such alias: %s\n"), n_shexp_quote_cp(key, FAL0));
1049          rv = 1;
1050       }
1051    }else{
1052       struct n_strlist *head, **tailp;
1053       boole exists;
1054       char const *val1, *val2;
1055 
1056       if(a_nm_alias_dp == NIL)
1057          a_nm_alias_dp = su_cs_dict_set_treshold_shift(
1058                su_cs_dict_create(&a_nm_alias__d, a_NM_ALIAS_FLAGS, NIL),
1059                a_NM_ALIAS_TRESHOLD_SHIFT);
1060 
1061       if((exists = (head = dat.slp) != NIL)){
1062          while(dat.slp->sl_next != NIL)
1063             dat.slp = dat.slp->sl_next;
1064          tailp = &dat.slp->sl_next;
1065       }else
1066          head = NIL, tailp = &head;
1067 
1068       while((val1 = *++argv) != NIL){
1069          uz l1, l2;
1070          struct mx_name *np;
1071          boole norecur, name_eq_fullname;
1072 
1073          if((norecur = (*val1 == '\\')))
1074             ++val1;
1075 
1076          /* We need to allow empty targets */
1077          name_eq_fullname = TRU1;
1078          if(*val1 == '\0')
1079             val2 = val1;
1080          else if((np = n_extract_single(val1, GFULL)) != NIL){
1081             val1 = np->n_name;
1082             val2 = np->n_fullname;
1083             if((name_eq_fullname = !su_cs_cmp(val1, val2)))
1084                val2 = su_empty;
1085          }else{
1086             n_err(_("alias: %s: invalid argument: %s\n"),
1087                key, n_shexp_quote_cp(val1, FAL0));
1088             /*rv = 1;*/
1089             continue;
1090          }
1091 
1092          l1 = su_cs_len(val1) +1;
1093          l2 = su_cs_len(val2) +1;
1094          dat.slp = n_STRLIST_ALLOC(l1 + 1 + l2);
1095          *tailp = dat.slp;
1096          dat.slp->sl_next = NIL;
1097          tailp = &dat.slp->sl_next;
1098          dat.slp->sl_len = l1 -1;
1099          su_mem_copy(dat.slp->sl_dat, val1, l1);
1100          dat.slp->sl_dat[l1++] = (!norecur && name_eq_fullname &&
1101                mx_alias_is_valid_name(val1));
1102          su_mem_copy(&dat.slp->sl_dat[l1], val2, l2);
1103       }
1104 
1105       if(exists){
1106          su_cs_dict_view_set_data(&dv, head);
1107          rv = !TRU1;
1108       }else
1109          rv = !(su_cs_dict_insert(a_nm_alias_dp, key, head) == 0);
1110    }
1111 
1112 jleave:
1113    NYD_OU;
1114    return rv;
1115 }
1116 
1117 int
c_unalias(void * vp)1118 c_unalias(void *vp){ /* XXX how about toolbox and generic unxy_dict()? */
1119    struct su_cs_dict_view dv;
1120    struct n_strlist *slp;
1121    char const **argv, *key;
1122    int rv;
1123    NYD_IN;
1124 
1125    rv = 0;
1126    key = (argv = vp)[0];
1127 
1128    if(a_nm_alias_dp != NIL)
1129       su_cs_dict_view_setup(&dv, a_nm_alias_dp);
1130 
1131    do{
1132       if(key[1] == '\0' && key[0] == '*'){
1133          if(a_nm_alias_dp != NIL){
1134             su_CS_DICT_VIEW_FOREACH(&dv){
1135                slp = S(struct n_strlist*,su_cs_dict_view_data(&dv));
1136                do{
1137                   vp = slp;
1138                   slp = slp->sl_next;
1139                   n_free(vp);
1140                }while(slp != NIL);
1141             }
1142             su_cs_dict_clear(a_nm_alias_dp);
1143          }
1144       }else if(!su_cs_dict_view_find(&dv, key)){
1145          n_err(_("No such `alias': %s\n"), n_shexp_quote_cp(key, FAL0));
1146          rv = 1;
1147       }else{
1148          slp = S(struct n_strlist*,su_cs_dict_view_data(&dv));
1149          do{
1150             vp = slp;
1151             slp = slp->sl_next;
1152             n_free(vp);
1153          }while(slp != NIL);
1154          su_cs_dict_view_remove(&dv);
1155       }
1156    }while((key = *++argv) != NIL);
1157 
1158    NYD_OU;
1159    return rv;
1160 }
1161 
1162 boole
mx_alias_is_valid_name(char const * name)1163 mx_alias_is_valid_name(char const *name){
1164    char c;
1165    char const *cp;
1166    boole rv;
1167    NYD2_IN;
1168 
1169    for(rv = TRU1, cp = name; (c = *cp) != '\0'; ++cp){
1170       /* User names, plus things explicitly mentioned in Postfix aliases(5).
1171        * Plus extensions.  On change adjust *mta-aliases* and impl., too */
1172       /* TODO alias_is_valid_name(): locale dependent validity check,
1173        * TODO with Unicode prefix valid UTF-8! */
1174       if(!su_cs_is_alnum(c) && c != '_'){
1175          if(cp == name ||
1176                (c != '-' &&
1177                /* Extensions, but mentioned by Postfix */
1178                c != '#' && c != ':' && c != '@' &&
1179                /* Extensions */
1180                c != '!' && c != '.' && !(S(u8,c) & 0x80) &&
1181                !(c == '$' && cp[1] == '\0'))){
1182             rv = FAL0;
1183             break;
1184          }
1185       }
1186    }
1187    NYD2_OU;
1188    return rv;
1189 }
1190 
1191 int
c_alternates(void * vp)1192 c_alternates(void *vp){
1193    struct n_string s_b, *s;
1194    struct n_strlist *slp;
1195    int rv;
1196    char const **argv, *varname, *key;
1197    NYD_IN;
1198 
1199    n_pstate_err_no = su_ERR_NONE;
1200 
1201    argv = S(char const**,vp);
1202    varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NIL;
1203 
1204    if((key = *argv) == NIL){
1205       slp = NIL;
1206       rv = !mx_xy_dump_dict("alternates", a_nm_a8s_dp, &slp, NIL,
1207                &a_nm_a8s_dump);
1208       if(!rv){
1209          s = n_string_creat_auto(&s_b);
1210          s = n_string_book(s, 500); /* xxx */
1211 
1212          for(; slp != NIL; slp = slp->sl_next){
1213             if(s->s_len > 0)
1214                s = n_string_push_c(s, ' ');
1215             s = n_string_push_buf(s, slp->sl_dat, slp->sl_len);
1216          }
1217          key = n_string_cp(s);
1218 
1219          if(varname != NIL){
1220             if(!n_var_vset(varname, S(up,key))){
1221                n_pstate_err_no = su_ERR_NOTSUP;
1222                rv = 1;
1223             }
1224          }else if(*key != '\0')
1225             rv = !(fprintf(n_stdout, "alternates %s\n", key) >= 0);
1226          else
1227             rv = !(fprintf(n_stdout, _("# no alternates registered\n")) >= 0);
1228       }
1229    }else{
1230       if(varname != NULL)
1231          n_err(_("alternates: `vput' only supported in \"show\" mode\n"));
1232 
1233       if(a_nm_a8s_dp == NIL)
1234          a_nm_a8s_dp = su_cs_dict_set_treshold_shift(
1235                su_cs_dict_create(&a_nm_a8s__d, a_NM_A8S_FLAGS, NIL),
1236                a_NM_A8S_TRESHOLD_SHIFT);
1237       /* In POSIX mode this command declares a, not appends to a list */
1238       else if(ok_blook(posix))
1239          su_cs_dict_clear_elems(a_nm_a8s_dp);
1240 
1241       for(rv = 0; (key = *argv++) != NIL;){
1242          struct mx_name *np;
1243 
1244          if((np = n_extract_single(key, 0)) == NIL ||
1245                (np = checkaddrs(np, EACM_STRICT, NIL)) == NIL){
1246             n_err(_("Invalid `alternates' argument: %s\n"),
1247                n_shexp_quote_cp(key, FAL0));
1248             n_pstate_err_no = su_ERR_INVAL;
1249             rv = 1;
1250             continue;
1251          }
1252          key = np->n_name;
1253 
1254          if(su_cs_dict_replace(a_nm_a8s_dp, key, NIL) > 0){
1255             n_err(_("Failed to create `alternates' storage: %s\n"),
1256                n_shexp_quote_cp(key, FAL0));
1257             n_pstate_err_no = su_ERR_INVAL;
1258             rv = 1;
1259          }
1260       }
1261    }
1262 
1263    NYD_OU;
1264    return rv;
1265 }
1266 
1267 int
c_unalternates(void * vp)1268 c_unalternates(void *vp){
1269    int rv;
1270    NYD_IN;
1271 
1272    rv = !mx_unxy_dict("alternates", a_nm_a8s_dp, vp);
1273    NYD_OU;
1274    return rv;
1275 }
1276 
1277 struct mx_name *
mx_alternates_remove(struct mx_name * np,boole keep_single)1278 mx_alternates_remove(struct mx_name *np, boole keep_single){
1279    /* XXX keep a single pointer, initial null, and immediate remove nodes
1280     * XXX on successful match unless keep single and that pointer null! */
1281    struct su_cs_dict_view dv;
1282    struct mx_name *xp, *newnp;
1283    NYD_IN;
1284 
1285    /* Delete the temporary bit from all */
1286    for(xp = np; xp != NULL; xp = xp->n_flink)
1287       xp->n_flags &= ~S(u32,S32_MIN);
1288 
1289    /* Mark all possible alternate names (xxx sic: instead walk over namelist
1290     * and hash-lookup alternate instead (unless *allnet*) */
1291    if(a_nm_a8s_dp != NIL)
1292       su_CS_DICT_FOREACH(a_nm_a8s_dp, &dv)
1293          np = a_nm_namelist_mark_name(np, su_cs_dict_view_key(&dv));
1294 
1295    np = a_nm_namelist_mark_name(np, ok_vlook(LOGNAME));
1296 
1297    if((xp = n_extract_single(ok_vlook(sender), GEXTRA)) != NIL)
1298       np = a_nm_namelist_mark_name(np, xp->n_name);
1299    else for(xp = lextract(ok_vlook(from), GEXTRA | GSKIN); xp != NULL;
1300          xp = xp->n_flink)
1301       np = a_nm_namelist_mark_name(np, xp->n_name);
1302 
1303    /* C99 */{
1304       char const *v15compat;
1305 
1306       if((v15compat = ok_vlook(replyto)) != NULL){
1307          n_OBSOLETE(_("please use *reply-to*, not *replyto*"));
1308          for(xp = lextract(v15compat, GEXTRA | GSKIN); xp != NULL;
1309                xp = xp->n_flink)
1310             np = a_nm_namelist_mark_name(np, xp->n_name);
1311       }
1312    }
1313 
1314    for(xp = lextract(ok_vlook(reply_to), GEXTRA | GSKIN); xp != NULL;
1315          xp = xp->n_flink)
1316       np = a_nm_namelist_mark_name(np, xp->n_name);
1317 
1318    /* Clean the list by throwing away all deleted or marked (but one) nodes */
1319    for(xp = newnp = NULL; np != NULL; np = np->n_flink){
1320       if(np->n_type & GDEL)
1321          continue;
1322       if(np->n_flags & S(u32,S32_MIN)){
1323          if(!keep_single)
1324             continue;
1325          keep_single = FAL0;
1326       }
1327 
1328       np->n_blink = xp;
1329       if(xp != NULL)
1330          xp->n_flink = np;
1331       else
1332          newnp = np;
1333       xp = np;
1334       xp->n_flags &= ~S(u32,S32_MIN);
1335    }
1336    if(xp != NULL)
1337       xp->n_flink = NULL;
1338    np = newnp;
1339 
1340    NYD_OU;
1341    return np;
1342 }
1343 
1344 boole
mx_name_is_mine(char const * name)1345 mx_name_is_mine(char const *name){
1346    struct su_cs_dict_view dv;
1347    struct mx_name *xp;
1348    NYD_IN;
1349 
1350    if(a_nm_is_same_name(ok_vlook(LOGNAME), name, NIL))
1351       goto jleave;
1352 
1353    if(a_nm_a8s_dp != NIL){
1354       if(!ok_blook(allnet)){
1355          if(su_cs_dict_has_key(a_nm_a8s_dp, name))
1356             goto jleave;
1357       }else su_CS_DICT_FOREACH(a_nm_a8s_dp, &dv)
1358          if(a_nm_is_same_name(name, su_cs_dict_view_key(&dv), NIL))
1359             goto jleave;
1360    }
1361 
1362    for(xp = lextract(ok_vlook(from), GEXTRA | GSKIN); xp != NULL;
1363          xp = xp->n_flink)
1364       if(a_nm_is_same_name(xp->n_name, name, NIL))
1365          goto jleave;
1366 
1367    /* C99 */{
1368       char const *v15compat;
1369 
1370       if((v15compat = ok_vlook(replyto)) != NULL){
1371          n_OBSOLETE(_("please use *reply-to*, not *replyto*"));
1372          for(xp = lextract(v15compat, GEXTRA | GSKIN); xp != NULL;
1373                xp = xp->n_flink)
1374             if(a_nm_is_same_name(xp->n_name, name, NIL))
1375                goto jleave;
1376       }
1377    }
1378 
1379    for(xp = lextract(ok_vlook(reply_to), GEXTRA | GSKIN); xp != NULL;
1380          xp = xp->n_flink)
1381       if(a_nm_is_same_name(xp->n_name, name, NIL))
1382          goto jleave;
1383 
1384    if((xp = n_extract_single(ok_vlook(sender), GEXTRA)) != NIL &&
1385          a_nm_is_same_name(xp->n_name, name, NIL))
1386       goto jleave;
1387 
1388    name = NIL;
1389 jleave:
1390    NYD_OU;
1391    return (name != NIL);
1392 }
1393 
1394 #include "su/code-ou.h"
1395 /* s-it-mode */
1396