1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Names (read: addresses), including `alternates' and `alias' lists.
3 *@ TODO It should be solely that, parsing etc. should be in header.c,
4 *@ TODO or rfc5322.c or something like this.
5 *
6 * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7 * SPDX-License-Identifier: ISC
8 *
9 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21 #ifndef mx_NAMES_H
22 #define mx_NAMES_H /* XXX a lie - it is rather n_ yet */
23
24 #include <mx/nail.h>
25
26 #define mx_HEADER
27 #include <su/code-in.h>
28
29 struct mx_name;
30
31 enum mx_name_flags{
32 mx_NAME_SKINNED = 1u<<0, /* Has been skin()ned */
33 mx_NAME_IDNA = 1u<<1, /* IDNA has been applied */
34 mx_NAME_NAME_SALLOC = 1u<<2, /* .n_name in detached memory */
35
36 mx_NAME_ADDRSPEC_ISFILE = 1u<<3, /* ..is a file path */
37 mx_NAME_ADDRSPEC_ISPIPE = 1u<<4, /* ..is a command for piping */
38 mx_NAME_ADDRSPEC_ISFILEORPIPE = mx_NAME_ADDRSPEC_ISFILE |
39 mx_NAME_ADDRSPEC_ISPIPE,
40 mx_NAME_ADDRSPEC_ISNAME = 1u<<5, /* ..is an alias name */
41 mx_NAME_ADDRSPEC_ISADDR = 1u<<6, /* ..is a mail network address.. */
42 mx_NAME_ADDRSPEC_ISMASK = su_BITENUM_MASK(3,6),
43 mx_NAME_ADDRSPEC_WITHOUT_DOMAIN = 1u<<7, /* ISADDR: without domain name */
44
45 /* Bits not values for easy & testing */
46 mx_NAME_ADDRSPEC_ERR_EMPTY = 1u<<9, /* An empty string (or NIL) */
47 mx_NAME_ADDRSPEC_ERR_ATSEQ = 1u<<10, /* Weird @ sequence */
48 mx_NAME_ADDRSPEC_ERR_CHAR = 1u<<11, /* Invalid character */
49 mx_NAME_ADDRSPEC_ERR_IDNA = 1u<<12, /* IDNA conversion failed */
50 mx_NAME_ADDRSPEC_ERR_NAME = 1u<<13, /* Alias with invalid content */
51 mx_NAME_ADDRSPEC_INVALID = mx_NAME_ADDRSPEC_ERR_EMPTY |
52 mx_NAME_ADDRSPEC_ERR_ATSEQ | mx_NAME_ADDRSPEC_ERR_CHAR |
53 mx_NAME_ADDRSPEC_ERR_IDNA | mx_NAME_ADDRSPEC_ERR_NAME,
54
55 /* Error storage (we must fit in 31-bit!) */
56 mx__NAME_SHIFTWC = 14,
57 mx__NAME_MAXWC = 0x1FFFF,
58 mx__NAME_MASKWC = mx__NAME_MAXWC << mx__NAME_SHIFTWC
59 /* Bit 31 (32) == S32_MIN temporarily used */
60 };
61
62 struct mx_name{
63 struct mx_name *n_flink;
64 struct mx_name *n_blink;
65 enum gfield n_type; /* Header field this comes from */
66 u32 n_flags; /* enum mx_name_flags */
67 char *n_name;
68 char *n_fullname; /* .n_name, unless GFULL: +comments, etc */
69 char *n_fullextra; /* GFULL, without address */
70 };
71
72 /* In the !_ERR_EMPTY case, the failing character can be queried */
mx_name_flags_get_err_wc(u32 flags)73 INLINE s32 mx_name_flags_get_err_wc(u32 flags){
74 return (((flags & mx__NAME_MASKWC) >> mx__NAME_SHIFTWC) & mx__NAME_MAXWC);
75 }
76
77 /* ..where err is mx_name_flags (mix) */
mx_name_flags_set_err(u32 flags,u32 err,s32 e_wc)78 INLINE u32 mx_name_flags_set_err(u32 flags, u32 err, s32 e_wc){
79 return ((flags & ~(mx_NAME_ADDRSPEC_INVALID | mx__NAME_MASKWC)) |
80 S(u32,err) | ((S(u32,e_wc) & mx__NAME_MAXWC) << mx__NAME_SHIFTWC));
81 }
82
83 /* Allocate a single element of a name list, initialize its name field to the
84 * passed name and return it.
85 * May return NULL with GNULL_OK (only, unfortunately) */
86 EXPORT struct mx_name *nalloc(char const *str, enum gfield ntype);
87
88 /* Alloc an Fcc: entry TODO temporary only i hope */
89 EXPORT struct mx_name *nalloc_fcc(char const *file);
90
91 /* Like nalloc(), but initialize from content of np */
92 EXPORT struct mx_name *ndup(struct mx_name *np, enum gfield ntype);
93
94 /* Concatenate the two passed name lists, return the result */
95 EXPORT struct mx_name *cat(struct mx_name *n1, struct mx_name *n2);
96
97 /* Duplicate np */
98 EXPORT struct mx_name *n_namelist_dup(struct mx_name const *np,
99 enum gfield ntype);
100
101 /* Determine the number of undeleted elements in a name list and return it;
102 * the latter also doesn't count file and pipe addressees in addition */
103 EXPORT u32 count(struct mx_name const *np);
104 EXPORT u32 count_nonlocal(struct mx_name const *np);
105
106 /* Extract a list of names from a line, and make a list of names from it.
107 * Return the list or NULL if none found */
108 EXPORT struct mx_name *extract(char const *line, enum gfield ntype);
109
110 /* Like extract() unless line contains anyof ",\"\\(<|", in which case
111 * comma-separated list extraction is used instead */
112 EXPORT struct mx_name *lextract(char const *line, enum gfield ntype);
113
114 /* Interprets the entire line as one address: identical to extract() and
115 * lextract() but only returns one (or none) name.
116 * GSKIN will be added to ntype as well as GNULL_OK: may return NULL! */
117 EXPORT struct mx_name *n_extract_single(char const *line, enum gfield ntype);
118
119 /* Turn a list of names into a string of the same names */
120 EXPORT char *detract(struct mx_name *np, enum gfield ntype);
121
122 /* Get a lextract() list via n_go_input_cp(), reassigning to *np* */
123 EXPORT struct mx_name *grab_names(enum n_go_input_flags gif, char const *field,
124 struct mx_name *np, int comma, enum gfield gflags);
125
126 /* Check whether n1 & n2 are the same address, effectively.
127 * Takes *allnet* into account */
128 EXPORT boole mx_name_is_same_address(struct mx_name const *n1,
129 struct mx_name const *n2);
130
131 /* Check whether n1 & n2 share the domain name */
132 EXPORT boole mx_name_is_same_domain(struct mx_name const *n1,
133 struct mx_name const *n2);
134
135 /* Check all addresses in np and delete invalid ones; if set_on_error is not
136 * NULL it'll be set to TRU1 for error or -1 for "hard fail" error */
137 EXPORT struct mx_name *checkaddrs(struct mx_name *np,
138 enum expand_addr_check_mode eacm, s8 *set_on_error);
139
140 /* Vaporise all duplicate addresses in hp (.h_(to|cc|bcc)) so that an address
141 * that "first" occurs in To: is solely in there, ditto Cc:, after expanding
142 * aliases etc. eacm and set_on_error are passed to checkaddrs().
143 * After updating hp to the new state this returns a flat list of all
144 * addressees, which may be NIL */
145 EXPORT struct mx_name *n_namelist_vaporise_head(struct header *hp,
146 boole metoo, boole strip_alternates,
147 enum expand_addr_check_mode eacm, s8 *set_on_error);
148
149 /* Map all of the aliased users in the invoker's mailrc file and insert them
150 * into the list */
151 EXPORT struct mx_name *usermap(struct mx_name *names, boole force_metoo);
152
153 /* Remove all of the duplicates from the passed name list by insertion sorting
154 * them, then checking for dups. Return the head of the new list */
155 EXPORT struct mx_name *elide(struct mx_name *names);
156
157 /* `(un)?alias' */
158 EXPORT int c_alias(void *vp);
159 EXPORT int c_unalias(void *vp);
160
161 /* Is name a valid alias name (as opposed to: "is an alias") */
162 EXPORT boole mx_alias_is_valid_name(char const *name);
163
164 /* `(un)?alternates' deal with the list of alternate names */
165 EXPORT int c_alternates(void *vp);
166 EXPORT int c_unalternates(void *vp);
167
168 /* If keep_single is set one alternates member will be allowed in np */
169 EXPORT struct mx_name *mx_alternates_remove(struct mx_name *np,
170 boole keep_single);
171
172 /* Likewise, is name an alternate in broadest sense? */
173 EXPORT boole mx_name_is_mine(char const *name);
174
175 #include <su/code-ou.h>
176 #endif /* mx_NAMES_H */
177 /* s-it-mode */
178