1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2  *@ `headerpick', `retain' and `ignore', and `un..' variants.
3  *
4  * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
5  * SPDX-License-Identifier: ISC
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #undef su_FILE
20 #define su_FILE ignore
21 #define mx_SOURCE
22 
23 #ifndef mx_HAVE_AMALGAMATION
24 # include "mx/nail.h"
25 #endif
26 
27 #include <su/cs.h>
28 #include <su/mem.h>
29 #include <su/sort.h>
30 
31 #include "mx/termios.h"
32 
33 /* TODO fake */
34 #include "su/code-in.h"
35 
36 struct a_ignore_type{
37    u32 it_count; /* Entries in .it_ht (and .it_re) */
38    boole it_all; /* _All_ fields ought to be _type_ (ignore/retain) */
39    u8 it__dummy[3];
40    struct a_ignore_field{
41       struct a_ignore_field *if_next;
42       char if_field[VFIELD_SIZE(0)]; /* Header field */
43    } *it_ht[3]; /* TODO make hashmap dynamic */
44 #ifdef mx_HAVE_REGEX
45    struct a_ignore_re{
46       struct a_ignore_re *ir_next;
47       regex_t ir_regex;
48       char ir_input[VFIELD_SIZE(0)]; /* Regex input text (for showing it) */
49    } *it_re, *it_re_tail;
50 #endif
51 };
52 
53 struct n_ignore{
54    struct a_ignore_type i_retain;
55    struct a_ignore_type i_ignore;
56    boole i_auto; /* In auto-reclaimed, not heap memory */
57    boole i_bltin; /* Is a built-in n_IGNORE* type */
58    u8 i_ibm_idx; /* If .i_bltin: a_ignore_bltin_map[] idx */
59    u8 i__dummy[5];
60 };
61 
62 struct a_ignore_bltin_map{
63    struct n_ignore *ibm_ip;
64    char const ibm_name[8];
65 };
66 
67 static struct a_ignore_bltin_map const a_ignore_bltin_map[] = {
68    {n_IGNORE_TYPE, "type"},
69    {n_IGNORE_SAVE, "save"},
70    {n_IGNORE_FWD, "forward\0"},
71    {n_IGNORE_TOP, "top"},
72 
73    {n_IGNORE_TYPE, "print\0"},
74    {n_IGNORE_FWD, "fwd"}
75 };
76 #ifdef mx_HAVE_DEVEL /* Avoid gcc warn cascade "n_ignore is defined locally" */
77 CTAV(-n__IGNORE_TYPE - n__IGNORE_ADJUST == 0);
78 CTAV(-n__IGNORE_SAVE - n__IGNORE_ADJUST == 1);
79 CTAV(-n__IGNORE_FWD - n__IGNORE_ADJUST == 2);
80 CTAV(-n__IGNORE_TOP - n__IGNORE_ADJUST == 3);
81 CTAV(n__IGNORE_MAX == 3);
82 #endif
83 
84 static struct n_ignore *a_ignore_bltin[n__IGNORE_MAX + 1];
85 
86 /* Almost everyone uses `ignore'/`retain', put _TYPE in BSS */
87 static struct n_ignore a_ignore_type;
88 
89 /* Return real self, which is xself unless that is a built-in special,
90  * in which case NIL is returned if nonexistent and docreate is false.
91  * The other statics assume self has been resolved (unless noted) */
92 static struct n_ignore *a_ignore_resolve_self(struct n_ignore *xself,
93       boole docreate);
94 
95 /* Lookup whether a mapping is contained: TRU1=retained, TRUM1=ignored.
96  * If retain is _not_ TRUM1 then only the retained/ignored slot is inspected,
97  * and regular expressions are not executed but instead their .ir_input is
98  * text-compared against len bytes of dat.
99  * Note it doesn't handle the .it_all "all fields" condition */
100 static boole a_ignore_lookup(struct n_ignore const *self, boole retain,
101       char const *dat, uz len);
102 
103 /* Delete all retain( else ignor)ed members */
104 static void a_ignore_del_allof(struct n_ignore *ip, boole retain);
105 
106 /* Try to map a string to one of the built-in types */
107 static struct a_ignore_bltin_map const *a_ignore_resolve_bltin(char const *cp);
108 
109 /* Logic behind `headerpick T T' (a.k.a. `retain'+) */
110 static boole a_ignore_addcmd_mux(struct n_ignore *ip, char const **list,
111       boole retain);
112 
113 static void a_ignore__show(struct n_ignore const *ip, boole retain);
114 
115 /* Logic behind `unheaderpick T T' (a.k.a. `unretain'+) */
116 static boole a_ignore_delcmd_mux(struct n_ignore *ip, char const **list,
117       boole retain);
118 
119 static boole a_ignore__delone(struct n_ignore *ip, boole retain,
120       char const *field);
121 
122 static struct n_ignore *
a_ignore_resolve_self(struct n_ignore * xself,boole docreate)123 a_ignore_resolve_self(struct n_ignore *xself, boole docreate){
124    up suip;
125    struct n_ignore *self;
126    NYD2_IN;
127 
128    self = xself;
129    suip = -R(up,self) - n__IGNORE_ADJUST;
130 
131    if(suip <= n__IGNORE_MAX){
132       if((self = a_ignore_bltin[suip]) == NIL && docreate){
133          if(xself == n_IGNORE_TYPE){
134             self = &a_ignore_type;
135             /* LIB: su_mem_set(self, 0, sizeof *self);*/
136          }else
137             self = n_ignore_new(FAL0);
138          self->i_bltin = TRU1;
139          self->i_ibm_idx = S(u8,suip);
140          a_ignore_bltin[suip] = self;
141       }
142    }
143 
144    NYD2_OU;
145    return self;
146 }
147 
148 static boole
a_ignore_lookup(struct n_ignore const * self,boole retain,char const * dat,uz len)149 a_ignore_lookup(struct n_ignore const *self, boole retain,
150       char const *dat, uz len){
151    boole rv;
152 #ifdef mx_HAVE_REGEX
153    struct a_ignore_re *irp;
154 #endif
155    struct a_ignore_field *ifp;
156    u32 hi;
157    NYD2_IN;
158 
159    if(len == UZ_MAX)
160       len = su_cs_len(dat);
161    hi = su_cs_hash_case_cbuf(dat, len) % NELEM(self->i_retain.it_ht);
162 
163    /* Again: does not handle .it_all conditions! */
164    /* (Inner functions would be nice, again) */
165    if(retain && self->i_retain.it_count > 0){
166       rv = TRU1;
167       for(ifp = self->i_retain.it_ht[hi]; ifp != NIL; ifp = ifp->if_next)
168          if(!su_cs_cmp_case_n(ifp->if_field, dat, len) &&
169                ifp->if_field[len] == '\0')
170             goto jleave;
171 #ifdef mx_HAVE_REGEX
172       if(dat[len - 1] != '\0')
173          dat = savestrbuf(dat, len);
174       for(irp = self->i_retain.it_re; irp != NIL; irp = irp->ir_next)
175          if((retain == TRUM1
176                ? (regexec(&irp->ir_regex, dat, 0,NIL, 0) != REG_NOMATCH)
177                : (!su_cs_cmp_n(irp->ir_input, dat, len) &&
178                   irp->ir_input[len] == '\0')))
179             goto jleave;
180 #endif
181       rv = (retain == TRUM1) ? TRUM1 : FAL0;
182    }else if((retain == TRUM1 || !retain) && self->i_ignore.it_count > 0){
183       rv = TRUM1;
184       for(ifp = self->i_ignore.it_ht[hi]; ifp != NIL; ifp = ifp->if_next)
185          if(!su_cs_cmp_case_n(ifp->if_field, dat, len) &&
186                ifp->if_field[len] == '\0')
187             goto jleave;
188 #ifdef mx_HAVE_REGEX
189       if(dat[len - 1] != '\0')
190          dat = savestrbuf(dat, len);
191       for(irp = self->i_ignore.it_re; irp != NIL; irp = irp->ir_next)
192          if((retain == TRUM1
193                ? (regexec(&irp->ir_regex, dat, 0,NIL, 0) != REG_NOMATCH)
194                : (!su_cs_cmp_n(irp->ir_input, dat, len) &&
195                   irp->ir_input[len] == '\0')))
196             goto jleave;
197 #endif
198       rv = (retain == TRUM1) ? TRU1 : FAL0;
199    }else
200       rv = FAL0;
201 
202 jleave:
203    NYD2_OU;
204    return rv;
205 }
206 
207 static void
a_ignore_del_allof(struct n_ignore * ip,boole retain)208 a_ignore_del_allof(struct n_ignore *ip, boole retain){
209 #ifdef mx_HAVE_REGEX
210    struct a_ignore_re *irp;
211 #endif
212    struct a_ignore_field *ifp;
213    struct a_ignore_type *itp;
214    NYD2_IN;
215 
216    itp = retain ? &ip->i_retain : &ip->i_ignore;
217 
218    if(!ip->i_auto){
219       uz i;
220 
221       for(i = 0; i < NELEM(itp->it_ht); ++i)
222          for(ifp = itp->it_ht[i]; ifp != NIL;){
223             struct a_ignore_field *x;
224 
225             x = ifp;
226             ifp = ifp->if_next;
227             su_FREE(x);
228          }
229    }
230 
231 #ifdef mx_HAVE_REGEX
232    for(irp = itp->it_re; irp != NIL;){
233       struct a_ignore_re *x;
234 
235       x = irp;
236       irp = irp->ir_next;
237       regfree(&x->ir_regex);
238       if(!ip->i_auto)
239          su_FREE(x);
240    }
241 #endif
242 
243    su_mem_set(itp, 0, sizeof *itp);
244    NYD2_OU;
245 }
246 
247 static struct a_ignore_bltin_map const *
a_ignore_resolve_bltin(char const * cp)248 a_ignore_resolve_bltin(char const *cp){
249    struct a_ignore_bltin_map const *ibmp;
250    NYD2_IN;
251 
252    for(ibmp = &a_ignore_bltin_map[0];;)
253       if(!su_cs_cmp_case(cp, ibmp->ibm_name))
254          break;
255       else if(++ibmp == &a_ignore_bltin_map[NELEM(a_ignore_bltin_map)]){
256          ibmp = NIL;
257          break;
258       }
259 
260    NYD2_OU;
261    return ibmp;
262 }
263 
264 static boole
a_ignore_addcmd_mux(struct n_ignore * ip,char const ** list,boole retain)265 a_ignore_addcmd_mux(struct n_ignore *ip, char const **list, boole retain){
266    char const **ap;
267    boole rv;
268    NYD2_IN;
269 
270    ip = a_ignore_resolve_self(ip, rv = (*list != NIL));
271 
272    if(!rv){
273       if(ip != NIL && ip->i_bltin)
274          a_ignore__show(ip, retain);
275       rv = TRU1;
276    }else{
277       for(ap = list; *ap != 0; ++ap)
278          switch(n_ignore_insert_cp(ip, retain, *ap)){
279          case FAL0:
280             n_err(_("Invalid field name cannot be %s: %s\n"),
281                (retain ? _("retained") : _("ignored")), *ap);
282             rv = FAL0;
283             break;
284          case TRUM1:
285             if(n_poption & n_PO_D_V)
286                n_err(_("Field already %s: %s\n"),
287                   (retain ? _("retained") : _("ignored")), *ap);
288             /* FALLTHRU */
289          case TRU1:
290             break;
291          }
292    }
293 
294    NYD2_OU;
295    return rv;
296 }
297 
298 static void
a_ignore__show(struct n_ignore const * ip,boole retain)299 a_ignore__show(struct n_ignore const *ip, boole retain){
300 #ifdef mx_HAVE_REGEX
301    struct a_ignore_re *irp;
302 #endif
303    struct a_ignore_field *ifp;
304    uz i, sw;
305    char const **ap, **ring;
306    struct a_ignore_type const *itp;
307    NYD2_IN;
308 
309    itp = retain ? &ip->i_retain : &ip->i_ignore;
310 
311    do{
312       char const *pre, *attr;
313 
314       if(itp->it_all)
315          pre = n_empty, attr = n_star;
316       else if(itp->it_count == 0)
317          pre = n_ns, attr = _("currently covers no fields");
318       else
319          break;
320       fprintf(n_stdout, _("%sheaderpick %s %s %s\n"),
321          pre, a_ignore_bltin_map[ip->i_ibm_idx].ibm_name,
322          (retain ? "retain" : "ignore"), attr);
323       goto jleave;
324    }while(0);
325 
326    ring = n_autorec_alloc((itp->it_count +1) * sizeof *ring);
327    for(ap = ring, i = 0; i < NELEM(itp->it_ht); ++i)
328       for(ifp = itp->it_ht[i]; ifp != NIL; ifp = ifp->if_next)
329          *ap++ = ifp->if_field;
330    *ap = NIL;
331 
332    su_sort_shell_vpp(su_S(void const**,ring), P2UZ(ap - ring),
333       su_cs_toolbox_case.tb_compare);
334 
335    i = fprintf(n_stdout, "headerpick %s %s",
336       a_ignore_bltin_map[ip->i_ibm_idx].ibm_name,
337       (retain ? "retain" : "ignore"));
338    sw = mx_termios_dimen.tiosd_width;
339 
340    for(ap = ring; *ap != NIL; ++ap){
341       /* These fields are all ASCII, no visual width needed */
342       uz len;
343       char const *cp;
344 
345       cp = n_shexp_quote_cp(*ap, FAL0);
346       len = su_cs_len(cp) + 1;
347       if(UCMP(z, len, >=, sw - i)){
348          fputs(" \\\n ", n_stdout);
349          i = 1;
350       }
351       i += len;
352       putc(' ', n_stdout);
353       fputs(cp, n_stdout);
354    }
355 
356    /* Regular expression in FIFO order */
357 #ifdef mx_HAVE_REGEX
358    for(irp = itp->it_re; irp != NIL; irp = irp->ir_next){
359       uz len;
360       char const *cp;
361 
362       cp = n_shexp_quote_cp(irp->ir_input, FAL0);
363       len = su_cs_len(cp) + 1;
364       if(UCMP(z, len, >=, sw - i)){
365          fputs(" \\\n ", n_stdout);
366          i = 1;
367       }
368       i += len;
369       putc(' ', n_stdout);
370       fputs(cp, n_stdout);
371    }
372 #endif
373 
374    putc('\n', n_stdout);
375 
376 jleave:
377    fflush(n_stdout);
378    NYD2_OU;
379 }
380 
381 static boole
a_ignore_delcmd_mux(struct n_ignore * ip,char const ** list,boole retain)382 a_ignore_delcmd_mux(struct n_ignore *ip, char const **list, boole retain){
383    char const *cp;
384    struct a_ignore_type *itp;
385    boole rv;
386    NYD2_IN;
387 
388    ip = a_ignore_resolve_self(ip, rv = (*list != NIL));
389    itp = retain ? &ip->i_retain : &ip->i_ignore;
390 
391    if(itp->it_count == 0 && !itp->it_all)
392       n_err(_("No fields currently being %s\n"),
393          (retain ? _("retained") : _("ignored")));
394    else{
395       while((cp = *list++) != NIL)
396          if(cp[0] == '*' && cp[1] == '\0')
397             a_ignore_del_allof(ip, retain);
398          else if(!a_ignore__delone(ip, retain, cp)){
399             n_err(_("Field not %s: %s\n"),
400                (retain ? _("retained") : _("ignored")), cp);
401             rv = FAL0;
402          }
403    }
404 
405    NYD2_OU;
406    return rv;
407 }
408 
409 static boole
a_ignore__delone(struct n_ignore * ip,boole retain,char const * field)410 a_ignore__delone(struct n_ignore *ip, boole retain, char const *field){
411    struct a_ignore_type *itp;
412    NYD_IN;
413 
414    itp = retain ? &ip->i_retain : &ip->i_ignore;
415 
416 #ifdef mx_HAVE_REGEX
417    if(n_is_maybe_regex(field)){
418       struct a_ignore_re **lirp, *irp;
419 
420       for(irp = *(lirp = &itp->it_re); irp != NIL;
421             lirp = &irp->ir_next, irp = irp->ir_next)
422          if(!su_cs_cmp(field, irp->ir_input)){
423             *lirp = irp->ir_next;
424             if(irp == itp->it_re_tail)
425                itp->it_re_tail = irp->ir_next;
426 
427             regfree(&irp->ir_regex);
428             if(!ip->i_auto)
429                su_FREE(irp);
430             --itp->it_count;
431             goto jleave;
432          }
433    }else
434 #endif /* mx_HAVE_REGEX */
435         {
436       struct a_ignore_field **ifpp, *ifp;
437       u32 hi;
438 
439       hi = su_cs_hash_case_cbuf(field, UZ_MAX) % NELEM(itp->it_ht);
440 
441       for(ifp = *(ifpp = &itp->it_ht[hi]); ifp != NIL;
442             ifpp = &ifp->if_next, ifp = ifp->if_next)
443          if(!su_cs_cmp_case(ifp->if_field, field)){
444             *ifpp = ifp->if_next;
445             if(!ip->i_auto)
446                su_FREE(ifp);
447             --itp->it_count;
448            goto jleave;
449          }
450    }
451 
452    ip = NIL;
453 jleave:
454    NYD_OU;
455    return (ip != NIL);
456 }
457 
458 FL int
c_headerpick(void * vp)459 c_headerpick(void *vp){
460    boole retain;
461    struct a_ignore_bltin_map const *ibmp;
462    char const **argv;
463    int rv;
464    NYD_IN;
465 
466    rv = 1;
467    argv = vp;
468 
469    /* Without arguments, show all settings of all contexts */
470    if(*argv == NIL){
471       rv = 0;
472       for(ibmp = &a_ignore_bltin_map[0];
473             ibmp <= &a_ignore_bltin_map[n__IGNORE_MAX]; ++ibmp){
474          rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
475          rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
476       }
477       goto jleave;
478    }
479 
480    if((ibmp = a_ignore_resolve_bltin(*argv)) == NIL){
481       n_err(_("headerpick: invalid context: %s\n"), *argv);
482       goto jleave;
483    }
484    ++argv;
485 
486    /* With only <context>, show all settings of it */
487    if(*argv == NIL){
488       rv = 0;
489       rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
490       rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
491       goto jleave;
492    }
493 
494    if(su_cs_starts_with_case("retain", *argv))
495       retain = TRU1;
496    else if(su_cs_starts_with_case("ignore", *argv))
497       retain = FAL0;
498    else{
499       n_err(_("headerpick: invalid type (retain, ignore): %s\n"), *argv);
500       goto jleave;
501    }
502    ++argv;
503 
504    /* With only <context> and <type>, show its settings */
505    if(*argv == NIL){
506       rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
507       goto jleave;
508    }
509 
510    rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
511 
512 jleave:
513    NYD_OU;
514    return rv;
515 }
516 
517 FL int
c_unheaderpick(void * vp)518 c_unheaderpick(void *vp){
519    boole retain;
520    struct a_ignore_bltin_map const *ibmp;
521    char const **argv;
522    int rv;
523    NYD_IN;
524 
525    rv = 1;
526    argv = vp;
527 
528    if((ibmp = a_ignore_resolve_bltin(*argv)) == NIL){
529       n_err(_("unheaderpick: invalid context: %s\n"), *argv);
530       goto jleave;
531    }
532    ++argv;
533 
534    if(su_cs_starts_with_case("retain", *argv))
535       retain = TRU1;
536    else if(su_cs_starts_with_case("ignore", *argv))
537       retain = FAL0;
538    else{
539       n_err(_("unheaderpick: invalid type (retain, ignore): %s\n"), *argv);
540       goto jleave;
541    }
542    ++argv;
543 
544    rv = !a_ignore_delcmd_mux(ibmp->ibm_ip, argv, retain);
545 
546 jleave:
547    NYD_OU;
548    return rv;
549 }
550 
551 FL int
c_retain(void * vp)552 c_retain(void *vp){
553    int rv;
554    NYD_IN;
555 
556    rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, vp, TRU1);
557 
558    NYD_OU;
559    return rv;
560 }
561 
562 FL int
c_ignore(void * vp)563 c_ignore(void *vp){
564    int rv;
565    NYD_IN;
566 
567    rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, vp, FAL0);
568 
569    NYD_OU;
570    return rv;
571 }
572 
573 FL int
c_unretain(void * vp)574 c_unretain(void *vp){
575    int rv;
576    NYD_IN;
577 
578    rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, vp, TRU1);
579 
580    NYD_OU;
581    return rv;
582 }
583 
584 FL int
c_unignore(void * vp)585 c_unignore(void *vp){
586    int rv;
587    NYD_IN;
588 
589    rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, vp, FAL0);
590 
591    NYD_OU;
592    return rv;
593 }
594 
595 FL int
c_saveretain(void * v)596 c_saveretain(void *v){ /* TODO v15 drop */
597    int rv;
598    NYD_IN;
599 
600    rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, TRU1);
601 
602    NYD_OU;
603    return rv;
604 }
605 
606 FL int
c_saveignore(void * v)607 c_saveignore(void *v){ /* TODO v15 drop */
608    int rv;
609    NYD_IN;
610 
611    rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, FAL0);
612 
613    NYD_OU;
614    return rv;
615 }
616 
617 FL int
c_unsaveretain(void * v)618 c_unsaveretain(void *v){ /* TODO v15 drop */
619    int rv;
620    NYD_IN;
621 
622    rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, TRU1);
623 
624    NYD_OU;
625    return rv;
626 }
627 
628 FL int
c_unsaveignore(void * v)629 c_unsaveignore(void *v){ /* TODO v15 drop */
630    int rv;
631    NYD_IN;
632 
633    rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, FAL0);
634 
635    NYD_OU;
636    return rv;
637 }
638 
639 FL int
c_fwdretain(void * v)640 c_fwdretain(void *v){ /* TODO v15 drop */
641    int rv;
642    NYD_IN;
643 
644    rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, TRU1);
645 
646    NYD_OU;
647    return rv;
648 }
649 
650 FL int
c_fwdignore(void * v)651 c_fwdignore(void *v){ /* TODO v15 drop */
652    int rv;
653    NYD_IN;
654 
655    rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, FAL0);
656 
657    NYD_OU;
658    return rv;
659 }
660 
661 FL int
c_unfwdretain(void * v)662 c_unfwdretain(void *v){ /* TODO v15 drop */
663    int rv;
664    NYD_IN;
665 
666    rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, TRU1);
667 
668    NYD_OU;
669    return rv;
670 }
671 
672 FL int
c_unfwdignore(void * v)673 c_unfwdignore(void *v){ /* TODO v15 drop */
674    int rv;
675    NYD_IN;
676 
677    rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, FAL0);
678 
679    NYD_OU;
680    return rv;
681 }
682 
683 FL struct n_ignore *
n_ignore_new(boole isauto)684 n_ignore_new(boole isauto){
685    struct n_ignore *self;
686    NYD_IN;
687 
688    self = isauto ? n_autorec_calloc(1, sizeof *self)
689          : su_CALLOC_N(1, sizeof *self);
690    self->i_auto = isauto;
691 
692    NYD_OU;
693    return self;
694 }
695 
696 FL void
n_ignore_del(struct n_ignore * self)697 n_ignore_del(struct n_ignore *self){
698    NYD_IN;
699 
700    a_ignore_del_allof(self, TRU1);
701    a_ignore_del_allof(self, FAL0);
702    if(!self->i_auto)
703       su_FREE(self);
704 
705    NYD_OU;
706 }
707 
708 FL boole
n_ignore_is_any(struct n_ignore const * self)709 n_ignore_is_any(struct n_ignore const *self){
710    boole rv;
711    NYD_IN;
712 
713    self = a_ignore_resolve_self(n_UNCONST(self), FAL0);
714    rv = (self != NIL &&
715          (self->i_retain.it_count != 0 || self->i_retain.it_all ||
716           self->i_ignore.it_count != 0 || self->i_ignore.it_all));
717 
718    NYD_OU;
719    return rv;
720 }
721 
722 FL boole
n_ignore_insert(struct n_ignore * self,boole retain,char const * dat,uz len)723 n_ignore_insert(struct n_ignore *self, boole retain,
724       char const *dat, uz len){
725 #ifdef mx_HAVE_REGEX
726    struct a_ignore_re *irp;
727    boole isre;
728 #endif
729    struct a_ignore_field *ifp;
730    struct a_ignore_type *itp;
731    boole rv;
732    NYD_IN;
733 
734    retain = !!retain; /* Make true bool, TRUM1 has special _lookup meaning */
735    rv = FAL0;
736    self = a_ignore_resolve_self(self, TRU1);
737 
738    if(len == UZ_MAX)
739       len = su_cs_len(dat);
740 
741    /* Request to ignore or retain _anything_?  That is special-treated */
742    if(len == 1 && dat[0] == '*'){
743       itp = retain ? &self->i_retain : &self->i_ignore;
744       if(itp->it_all)
745          rv = TRUM1;
746       else{
747          itp->it_all = TRU1;
748          a_ignore_del_allof(self, retain);
749          rv = TRU1;
750       }
751       goto jleave;
752    }
753 
754    /* Check for regular expression or valid fieldname */
755 #ifdef mx_HAVE_REGEX
756    if(!(isre = n_is_maybe_regex_buf(dat, len)))
757 #endif
758    {
759       char c;
760       uz i;
761 
762       for(i = 0; i < len; ++i){
763          c = dat[i];
764          if(!fieldnamechar(c))
765             goto jleave;
766       }
767    }
768 
769    rv = TRUM1;
770    if(a_ignore_lookup(self, retain, dat, len) == (retain ? TRU1 : TRUM1))
771       goto jleave;
772 
773    itp = retain ? &self->i_retain : &self->i_ignore;
774 
775    if(itp->it_count == U32_MAX){
776       n_err(_("Header selection size limit reached, cannot insert: %.*s\n"),
777          S(int,MIN(len, S32_MAX)), dat);
778       rv = FAL0;
779       goto jleave;
780    }
781 
782    rv = TRU1;
783 #ifdef mx_HAVE_REGEX
784    if(isre){
785       struct a_ignore_re *x;
786       int s;
787       uz i;
788 
789       i = VSTRUCT_SIZEOF(struct a_ignore_re, ir_input) + ++len;
790       irp = self->i_auto ? n_autorec_alloc(i) : su_ALLOC(i);
791       su_mem_copy(irp->ir_input, dat, --len);
792       irp->ir_input[len] = '\0';
793 
794       if((s = regcomp(&irp->ir_regex, irp->ir_input,
795             REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0){
796          n_err(_("Invalid regular expression: %s: %s\n"),
797             n_shexp_quote_cp(irp->ir_input, FAL0),
798             n_regex_err_to_doc(NIL, s));
799          if(!self->i_auto)
800             su_FREE(irp);
801          rv = FAL0;
802          goto jleave;
803       }
804 
805       irp->ir_next = NIL;
806       if((x = itp->it_re_tail) != NIL)
807          x->ir_next = irp;
808       else
809          itp->it_re = irp;
810       itp->it_re_tail = irp;
811    }else
812 #endif /* mx_HAVE_REGEX */
813         {
814       u32 hi;
815       uz i;
816 
817       i = VSTRUCT_SIZEOF(struct a_ignore_field, if_field) + len + 1;
818       ifp = self->i_auto ? n_autorec_alloc(i) : su_ALLOC(i);
819       su_mem_copy(ifp->if_field, dat, len);
820       ifp->if_field[len] = '\0';
821       hi = su_cs_hash_case_cbuf(dat, len) % NELEM(itp->it_ht);
822       ifp->if_next = itp->it_ht[hi];
823       itp->it_ht[hi] = ifp;
824    }
825    ++itp->it_count;
826 
827 jleave:
828    NYD_OU;
829    return rv;
830 }
831 
832 FL boole
n_ignore_lookup(struct n_ignore const * self,char const * dat,uz len)833 n_ignore_lookup(struct n_ignore const *self, char const *dat, uz len){
834    boole rv;
835    NYD_IN;
836 
837    if(self == n_IGNORE_ALL)
838       rv = TRUM1;
839    else if(len == 0 ||
840          (self = a_ignore_resolve_self(n_UNCONST(self), FAL0)) == NIL)
841       rv = FAL0;
842    else if(self->i_retain.it_all)
843       rv = TRU1;
844    else if(self->i_retain.it_count == 0 && self->i_ignore.it_all)
845       rv = TRUM1;
846    else
847       rv = a_ignore_lookup(self, TRUM1, dat, len);
848 
849    NYD_OU;
850    return rv;
851 }
852 
853 #include "su/code-ou.h"
854 /* s-it-mode */
855