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