1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2  *@ Header inclusion, macros, constants, types and the global var declarations.
3  *@ TODO Should be split in myriads of FEATURE-GROUP.h headers.  Sort.  def.h.
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 TODO ISC
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 #ifndef n_NAIL_H
38 # define n_NAIL_H
39 
40 #include <mx/gen-config.h>
41 
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 
45 #ifdef mx_HAVE_GETTIMEOFDAY
46 # include <sys/time.h>
47 #endif
48 
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <inttypes.h>
52 #include <limits.h>
53 #include <setjmp.h>
54 #include <signal.h>
55 #include <stdarg.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <time.h>
60 #include <unistd.h>
61 
62 #ifdef mx_HAVE_REGEX
63 # include <regex.h>
64 #endif
65 
66 /* Many things possibly of interest for adjustments have been outsourced */
67 #include <mx/config.h>
68 
69 #include <su/code.h>
70 #include <su/mem-bag.h> /* TODO should not be needed */
71 
72 /* TODO fake */
73 #include "su/code-in.h"
74 
75 struct mx_dig_msg_ctx;
76 struct mx_mimetype_handler;
77 
78 /*  */
79 #define n_FROM_DATEBUF 64 /* Size of RFC 4155 From_ line date */
80 #define n_DATE_DAYSYEAR 365u
81 #define n_DATE_NANOSSEC (n_DATE_MICROSSEC * 1000)
82 #define n_DATE_MICROSSEC (n_DATE_MILLISSEC * 1000)
83 #define n_DATE_MILLISSEC 1000u
84 #define n_DATE_SECSMIN 60u
85 #define n_DATE_MINSHOUR 60u
86 #define n_DATE_HOURSDAY 24u
87 #define n_DATE_SECSHOUR (n_DATE_SECSMIN * n_DATE_MINSHOUR)
88 #define n_DATE_SECSDAY (n_DATE_SECSHOUR * n_DATE_HOURSDAY)
89 
90 /* Network protocol newline */
91 #define NETNL "\015\012"
92 #define NETLINE(X) X NETNL
93 
94 /*
95  * OS, CC support, generic macros etc. TODO remove -> SU!
96  */
97 
98 /* CC */
99 
100 #undef mx_HAVE_NATCH_CHAR
101 #if defined mx_HAVE_SETLOCALE && defined mx_HAVE_C90AMEND1 && \
102       defined mx_HAVE_WCWIDTH
103 # define mx_HAVE_NATCH_CHAR
104 # define n_NATCH_CHAR(X) X
105 #else
106 # define n_NATCH_CHAR(X)
107 #endif
108 
109 #define n_UNCONST(X) su_UNCONST(void*,X) /* TODO */
110 
111 /*
112  * Types
113  */
114 
115 enum n_announce_flags{
116    n_ANNOUNCE_NONE = 0, /* Only housekeeping */
117    n_ANNOUNCE_MAIN_CALL = 1u<<0, /* POSIX covered startup call */
118    n_ANNOUNCE_STATUS = 1u<<1, /* Only print status */
119    n_ANNOUNCE_CHANGE = 1u<<2, /* Folder changed */
120 
121    n__ANNOUNCE_HEADER = 1u<<6,
122    n__ANNOUNCE_ANY = 1u<<7
123 };
124 
125 enum expand_addr_flags{
126    EAF_NONE = 0, /* -> EAF_NOFILE | EAF_NOPIPE */
127    EAF_RESTRICT = 1u<<0, /* "restrict" (do unless interactive / -[~#]) */
128    EAF_FAIL = 1u<<1, /* "fail" */
129    EAF_FAILINVADDR = 1u<<2, /* "failinvaddr" */
130    EAF_DOMAINCHECK = 1u<<3, /* "domaincheck" <-> *expandaddr-domaincheck* */
131    EAF_NAMETOADDR = 1u<<4, /* "nametoaddr": expand valid name to NAME@HOST */
132    EAF_SHEXP_PARSE = 1u<<5, /* shexp_parse() the address first is allowed */
133    /* Bits reused by enum expand_addr_check_mode! */
134    EAF_FCC = 1u<<8, /* +"fcc" umbrella */
135    EAF_FILE = 1u<<9, /* +"file" targets */
136    EAF_PIPE = 1u<<10, /* +"pipe" command pipe targets */
137    EAF_NAME = 1u<<11, /* +"name"s (non-address) names / MTA aliases */
138    EAF_ADDR = 1u<<12, /* +"addr" network address (contain "@") */
139 
140    EAF_TARGET_MASK = EAF_FCC | EAF_FILE | EAF_PIPE | EAF_NAME | EAF_ADDR,
141    EAF_RESTRICT_TARGETS = EAF_NAME | EAF_ADDR /* (default set if not set) */
142    /* TODO HACK!  In pre-v15 we have a control flow problem (it is a general
143     * TODO design problem): if n_collect() calls makeheader(), e.g., for -t or
144     * TODO because of ~e diting, then that will checkaddr() and that will
145     * TODO remove invalid headers.  However, this code path does not know
146     * TODO about keeping track of senderrors unless a pointer has been passed,
147     * TODO but which it doesn't for ~e, and shall not, too.  Thus, invalid
148     * TODO addresses may be automatically removed, silently, and no one will
149     * TODO ever know, in particular not regarding "failinvaddr".
150     * TODO The hacky solution is this bit -- which can ONLY be used for fields
151     * TODO which will be subject to namelist_vaporise_head() later on!! --,
152     * TODO if it is set (by n_header_extract()) then checkaddr() will NOT strip
153     * TODO invalid headers off IF it deals with a NULL senderror pointer */
154    ,EAF_MAYKEEP = 1u<<15
155 };
156 
157 enum expand_addr_check_mode{
158    EACM_NONE = 0u, /* Don't care about *expandaddr* */
159    EACM_NORMAL = 1u<<0, /* Use our normal *expandaddr* checking */
160    EACM_STRICT = 1u<<1, /* Never allow any file or pipe addressee */
161    EACM_MODE_MASK = 0x3u, /* _NORMAL and _STRICT are mutual! */
162 
163    EACM_NOLOG = 1u<<2, /* Do not log check errors */
164 
165    /* Some special overwrites of EAF_TARGETs.
166     * May NOT clash with EAF_* bits which may be ORd to these here! */
167 
168    EACM_NONAME = 1u<<16,
169    EACM_NONAME_OR_FAIL = 1u<<17,
170    EACM_DOMAINCHECK = 1u<<18 /* Honour it! */
171 };
172 
173 enum conversion{
174    CONV_NONE, /* no conversion */
175    CONV_7BIT, /* no conversion, is 7bit */
176    CONV_FROMQP, /* convert from quoted-printable */
177    CONV_TOQP, /* convert to quoted-printable */
178    CONV_8BIT, /* convert to 8bit (iconv) */
179    CONV_FROMB64, /* convert from base64 */
180    CONV_FROMB64_T, /* convert from base64/text */
181    CONV_TOB64, /* convert to base64 */
182    CONV_FROMHDR, /* convert from RFC1522 format */
183    CONV_TOHDR, /* convert to RFC1522 format */
184    CONV_TOHDR_A /* convert addresses for header */
185 };
186 
187 enum cproto{
188    CPROTO_NONE, /* Invalid.  But sometimes used to be able to parse an URL */
189 CPROTO_IMAP,
190    CPROTO_POP3,
191    CPROTO_SMTP,
192    CPROTO_CCRED, /* Special dummy credential proto (S/MIME etc.) */
193    CPROTO_CERTINFO, /* Special dummy proto for TLS certificate info xxx */
194    CPROTO_SOCKS /* Special dummy SOCKS5 proxy proto */
195    /* We need a _DEDUCE, as default, for normal URL object */
196 };
197 
198 /* enum n_err_number from gen-config.h, which is in sync with
199  * su_err_doc(), su_err_name() and su_err_from_name() */
200 
201 enum n_exit_status{
202    n_EXIT_OK = EXIT_SUCCESS,
203    n_EXIT_ERR = EXIT_FAILURE,
204    n_EXIT_USE = 64, /* sysexits.h:EX_USAGE */
205    n_EXIT_NOUSER = 67, /* :EX_NOUSER */
206    n_EXIT_IOERR = 74, /* :EX_IOERR */
207    n_EXIT_COLL_ABORT = 1<<1, /* Message collection was aborted */
208    n_EXIT_SEND_ERROR = 1<<2 /* Unspecified send error occurred */
209 };
210 
211 enum fedit_mode{
212    FEDIT_NONE = 0,
213    FEDIT_SYSBOX = 1u<<0, /* %: prefix */
214    FEDIT_RDONLY = 1u<<1, /* Readonly (per-box, n_OPT_R_FLAG is global) */
215    FEDIT_NEWMAIL = 1u<<2, /* `newmail' operation TODO OBSOLETE THIS! */
216    FEDIT_ACCOUNT = 1u<<3 /* setfile() called by `account' */
217 };
218 
219 enum fexp_mode{
220    FEXP_MOST,
221    FEXP_NOPROTO = 1u<<0, /* TODO no which_protocol() to decide sh expansion */
222    FEXP_SILENT = 1u<<1, /* Do not print but only return errors */
223    FEXP_MULTIOK = 1u<<2, /* Expansion to many entries is ok */
224    FEXP_LOCAL = 1u<<3, /* Result must be local file/maildir */
225    FEXP_LOCAL_FILE = 1u<<4, /* ..must be a local file: strips protocol://! */
226    FEXP_SHORTCUT = 1u<<5, /* Do expand shortcuts */
227    FEXP_NSPECIAL = 1u<<6, /* No %,#,& specials */
228    FEXP_NFOLDER = 1u<<7, /* NSPECIAL and no + folder, too */
229    FEXP_NSHELL = 1u<<8, /* Do not do shell word exp. (but ~/, $VAR) */
230    FEXP_NVAR = 1u<<9, /* ..not even $VAR expansion */
231 
232    /* Actually does expand ~/ etc. */
233    FEXP_NONE = FEXP_NOPROTO | FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NVAR,
234    FEXP_FULL = FEXP_SHORTCUT /* Full expansion */
235 };
236 
237 enum n_go_input_flags{
238    n_GO_INPUT_NONE,
239    n_GO_INPUT_CTX_BASE = 0, /* Generic shared base: don't use! */
240    n_GO_INPUT_CTX_DEFAULT = 1, /* Default input */
241    n_GO_INPUT_CTX_COMPOSE = 2, /* Compose mode input */
242    n__GO_INPUT_CTX_MASK = 3,
243    /* _MASK is not a valid index here, but the lower bits are not misused,
244     * therefore -- to save space! -- indexing is performed via "& _MASK".
245     * This is CTA()d!  For actual spacing of arrays we use _MAX1 instead */
246    n__GO_INPUT_CTX_MAX1 = n_GO_INPUT_CTX_COMPOSE + 1,
247 
248    n_GO_INPUT_HOLDALLSIGS = 1u<<8, /* sigs_all_hold() active TODO */
249    /* `xcall' is `call' (at the level where this is set): to be set when
250     * teardown of top level has undesired effects, e.g., for `account's and
251     * folder hooks etc., if we do not want to loose `localopts' unroll list */
252    n_GO_INPUT_NO_XCALL = 1u<<9,
253 
254    n_GO_INPUT_FORCE_STDIN = 1u<<10, /* Even in macro, use stdin (`read')! */
255    n_GO_INPUT_DELAY_INJECTIONS = 1u<<11, /* Skip go_input_inject()ions */
256    n_GO_INPUT_NL_ESC = 1u<<12, /* Support "\\$" line continuation */
257    n_GO_INPUT_NL_FOLLOW = 1u<<13, /* ..on such a follow line */
258    n_GO_INPUT_PROMPT_NONE = 1u<<14, /* Do not print prompt */
259    n_GO_INPUT_PROMPT_EVAL = 1u<<15, /* Instead, evaluate *prompt* */
260 
261    /* XXX The remains are mostly hacks */
262 
263    n_GO_INPUT_HIST_ADD = 1u<<16, /* Add the result to history list */
264    n_GO_INPUT_HIST_GABBY = 1u<<17, /* Consider history entry as gabby */
265    /* Command was erroneous; only in combination with _HIST_GABBY! */
266    n_GO_INPUT_HIST_ERROR = 1u<<18,
267 
268    n_GO_INPUT_IGNERR = 1u<<19, /* Imply `ignerr' command modifier */
269 
270    n__GO_FREEBIT = 24
271 };
272 
273 enum n_go_input_inject_flags{
274    n_GO_INPUT_INJECT_NONE = 0,
275    n_GO_INPUT_INJECT_COMMIT = 1u<<0, /* Auto-commit input */
276    n_GO_INPUT_INJECT_HISTORY = 1u<<1 /* Allow history addition */
277 };
278 
279 enum n_header_extract_flags{
280    n_HEADER_EXTRACT_NONE,
281    n_HEADER_EXTRACT_EXTENDED = 1u<<0,
282    n_HEADER_EXTRACT_FULL = 2u<<0,
283    n_HEADER_EXTRACT__MODE_MASK = n_HEADER_EXTRACT_EXTENDED |
284          n_HEADER_EXTRACT_FULL,
285 
286    /* Prefill the receivers with the already existing content of the given
287     * struct header arguent */
288    n_HEADER_EXTRACT_PREFILL_RECEIVERS = 1u<<8,
289    /* Understand and ignore shell-style comments */
290    n_HEADER_EXTRACT_IGNORE_SHELL_COMMENTS = 1u<<9,
291    /* Ignore a MBOX From_ line _silently */
292    n_HEADER_EXTRACT_IGNORE_FROM_ = 1u<<10
293 };
294 
295 /* Special ignore (where _TYPE is covered by POSIX `ignore' / `retain').
296  * _ALL is very special in that it doesn't have a backing object.
297  * Go over enum to avoid cascads of (different) CC warnings for used CTA()s */
298 #define n_IGNORE_ALL ((struct n_ignore*)n__IGNORE_ALL)
299 #define n_IGNORE_TYPE ((struct n_ignore*)n__IGNORE_TYPE)
300 #define n_IGNORE_SAVE ((struct n_ignore*)n__IGNORE_SAVE)
301 #define n_IGNORE_FWD ((struct n_ignore*)n__IGNORE_FWD)
302 #define n_IGNORE_TOP ((struct n_ignore*)n__IGNORE_TOP)
303 
304 enum{
305    n__IGNORE_ALL = -2,
306    n__IGNORE_TYPE = -3,
307    n__IGNORE_SAVE = -4,
308    n__IGNORE_FWD = -5,
309    n__IGNORE_TOP = -6,
310    n__IGNORE_ADJUST = 3,
311    n__IGNORE_MAX = 6 - n__IGNORE_ADJUST
312 };
313 
314 enum n_mailsend_flags{
315    n_MAILSEND_NONE,
316    n_MAILSEND_IS_FWD = 1u<<0,
317    n_MAILSEND_HEADERS_PRINT = 1u<<2,
318    n_MAILSEND_RECORD_RECIPIENT = 1u<<3,
319    n_MAILSEND_ALTERNATES_NOSTRIP = 1u<<4,
320 
321    n_MAILSEND_ALL = n_MAILSEND_IS_FWD | n_MAILSEND_HEADERS_PRINT |
322          n_MAILSEND_RECORD_RECIPIENT | n_MAILSEND_ALTERNATES_NOSTRIP
323 };
324 
325 /* Content-Transfer-Encodings as defined in RFC 2045:
326  * - Quoted-Printable, section 6.7
327  * - Base64, section 6.8 */
328 #define QP_LINESIZE (4 * 19) /* Max. compliant QP linesize */
329 
330 #define B64_LINESIZE (4 * 19) /* Max. compliant Base64 linesize */
331 #define B64_ENCODE_INPUT_PER_LINE ((B64_LINESIZE / 4) * 3)
332 
333 enum mime_enc{
334    MIMEE_NONE, /* message is not in MIME format */
335    MIMEE_BIN, /* message is in binary encoding */
336    MIMEE_8B, /* message is in 8bit encoding */
337    MIMEE_7B, /* message is in 7bit encoding */
338    MIMEE_QP, /* message is quoted-printable */
339    MIMEE_B64 /* message is in base64 encoding */
340 };
341 
342 /* xxx QP came later, maybe rewrite all to use mime_enc_flags directly? */
343 enum mime_enc_flags{
344    MIMEEF_NONE,
345    MIMEEF_SALLOC = 1u<<0, /* Use n_autorec_alloc(), not n_realloc().. */
346    /* ..result .s,.l point to user buffer of *_LINESIZE+[+[+]] bytes instead */
347    MIMEEF_BUF = 1u<<1,
348    MIMEEF_CRLF = 1u<<2, /* (encode) Append "\r\n" to lines */
349    MIMEEF_LF = 1u<<3, /* (encode) Append "\n" to lines */
350    /* (encode) If one of _CRLF/_LF is set, honour *_LINESIZE+[+[+]] and
351     * inject the desired line-ending whenever a linewrap is desired */
352    MIMEEF_MULTILINE = 1u<<4,
353    /* (encode) Quote with header rules, do not generate soft NL breaks?
354     * For mustquote(), specifies whether special RFC 2047 header rules
355     * should be used instead */
356    MIMEEF_ISHEAD = 1u<<5,
357    /* (encode) Ditto; for mustquote() this furtherly fine-tunes behaviour in
358     * that characters which would not be reported as "must-quote" when
359     * detecting whether quoting is necessary at all will be reported as
360     * "must-quote" if they have to be encoded in an encoded word */
361    MIMEEF_ISENCWORD = 1u<<6,
362    __MIMEEF_LAST = 6u
363 };
364 
365 enum qpflags{
366    QP_NONE = MIMEEF_NONE,
367    QP_SALLOC = MIMEEF_SALLOC,
368    QP_BUF = MIMEEF_BUF,
369    QP_ISHEAD = MIMEEF_ISHEAD,
370    QP_ISENCWORD = MIMEEF_ISENCWORD
371 };
372 
373 enum b64flags{
374    B64_NONE = MIMEEF_NONE,
375    B64_SALLOC = MIMEEF_SALLOC,
376    B64_BUF = MIMEEF_BUF,
377    B64_CRLF = MIMEEF_CRLF,
378    B64_LF = MIMEEF_LF,
379    B64_MULTILINE = MIMEEF_MULTILINE,
380    /* Not used, but for clarity only */
381    B64_ISHEAD = MIMEEF_ISHEAD,
382    B64_ISENCWORD = MIMEEF_ISENCWORD,
383    /* Special version of Base64, "Base64URL", according to RFC 4648.
384     * Only supported for encoding! */
385    B64_RFC4648URL = 1u<<(__MIMEEF_LAST+1),
386    /* Don't use any ("=") padding;
387     * may NOT be used with any of _CRLF, _LF or _MULTILINE */
388    B64_NOPAD = 1u<<(__MIMEEF_LAST+2)
389 };
390 
391 enum mime_parse_flags{
392    MIME_PARSE_NONE,
393    MIME_PARSE_DECRYPT = 1u<<0,
394    MIME_PARSE_PARTS = 1u<<1,
395    MIME_PARSE_SHALLOW = 1u<<2,
396    /* In effect we parse this message for user display or quoting purposes, so
397     * relaxed rules regarding content inspection may be applicable */
398    MIME_PARSE_FOR_USER_CONTEXT = 1u<<3
399 };
400 
401 enum okay{
402    STOP = 0,
403    OKAY = 1
404 };
405 
406 enum okey_xlook_mode{
407    OXM_PLAIN = 1u<<0, /* Plain key always tested */
408    OXM_H_P = 1u<<1, /* Check PLAIN-.url_h_p */
409    OXM_U_H_P = 1u<<2, /* Check PLAIN-.url_u_h_p */
410    OXM_ALL = 0x7u
411 };
412 
413 /* <0 means "stop" unless *prompt* extensions are enabled. */
414 enum prompt_exp{
415    PROMPT_STOP = -1, /* \c */
416    /* *prompt* extensions: \$, \@ etc. */
417    PROMPT_DOLLAR = -2,
418    PROMPT_AT = -3
419 };
420 
421 enum protocol{
422    n_PROTO_NONE,
423    n_PROTO_EML, /* Local electronic mail file (single message, rdonly) */
424    n_PROTO_FILE, /* refers to a local file */
425 PROTO_FILE = n_PROTO_FILE,
426    n_PROTO_POP3, /* is a pop3 server string */
427 PROTO_POP3 = n_PROTO_POP3,
428 n_PROTO_IMAP,
429 PROTO_IMAP = n_PROTO_IMAP,
430    n_PROTO_MAILDIR, /* refers to a maildir folder */
431 PROTO_MAILDIR = n_PROTO_MAILDIR,
432    n_PROTO_UNKNOWN, /* unknown protocol */
433 PROTO_UNKNOWN = n_PROTO_UNKNOWN,
434 
435    n_PROTO_MASK = (1u << 5) - 1
436 };
437 
438 enum sendaction{
439    SEND_MBOX, /* no conversion to perform */
440    SEND_RFC822, /* no conversion, no From_ line */
441    SEND_TODISP, /* convert to displayable form */
442    SEND_TODISP_ALL, /* same, include all MIME parts */
443    SEND_TODISP_PARTS, /* same, but only interactive, user-selected parts */
444    SEND_SHOW, /* convert to 'show' command form */
445    SEND_TOSRCH, /* convert for IMAP SEARCH */
446    SEND_TOFILE, /* convert for saving body to a file */
447    SEND_TOPIPE, /* convert for pipe-content/subc. */
448    SEND_QUOTE, /* convert for quoting */
449    SEND_QUOTE_ALL, /* same, include all MIME parts */
450    SEND_DECRYPT /* decrypt */
451 };
452 
453 enum n_shexp_parse_flags{
454    n_SHEXP_PARSE_NONE,
455    /* Don't perform expansions or interpret reverse solidus escape sequences.
456     * Output may be NULL, otherwise the possibly trimmed non-expanded input is
457     * used as output (implies _PARSE_META_KEEP) */
458    n_SHEXP_PARSE_DRYRUN = 1u<<0,
459    n_SHEXP_PARSE_TRUNC = 1u<<1, /* Truncate result storage on entry */
460    n_SHEXP_PARSE_TRIM_SPACE = 1u<<2, /* ..surrounding tokens */
461    n_SHEXP_PARSE_TRIM_IFSSPACE = 1u<<3, /* " */
462    n_SHEXP_PARSE_LOG = 1u<<4, /* Log errors */
463    n_SHEXP_PARSE_LOG_D_V = 1u<<5, /* Log errors if n_PO_D_V */
464    n_SHEXP_PARSE_IFS_VAR = 1u<<6, /* IFS is *ifs*, not su_cs_is_blank() */
465    n_SHEXP_PARSE_IFS_ADD_COMMA = 1u<<7, /* Add comma , to normal "IFS" */
466    n_SHEXP_PARSE_IFS_IS_COMMA = 1u<<8, /* Let comma , be the sole "IFS" */
467    n_SHEXP_PARSE_IGNORE_EMPTY = 1u<<9, /* Ignore empty tokens, start over */
468 
469    /* Implicitly open quotes, and ditto closing.  _AUTO_FIXED may only be used
470     * if an auto-quote-mode is enabled, implies _AUTO_CLOSE and causes the
471     * quote mode to be permanently active (cannot be closed) */
472    n_SHEXP_PARSE_QUOTE_AUTO_FIXED = 1u<<16,
473    n_SHEXP_PARSE_QUOTE_AUTO_SQ = 1u<<17,
474    n_SHEXP_PARSE_QUOTE_AUTO_DQ = 1u<<18,
475    n_SHEXP_PARSE_QUOTE_AUTO_DSQ = 1u<<19,
476    n_SHEXP_PARSE_QUOTE_AUTO_CLOSE = 1u<<20, /* Ignore an open quote at EOS */
477    n__SHEXP_PARSE_QUOTE_AUTO_MASK = n_SHEXP_PARSE_QUOTE_AUTO_SQ |
478          n_SHEXP_PARSE_QUOTE_AUTO_DQ | n_SHEXP_PARSE_QUOTE_AUTO_DSQ,
479 
480    /* Recognize metacharacters to separate tokens */
481    n_SHEXP_PARSE_META_VERTBAR = 1u<<21,
482    n_SHEXP_PARSE_META_AMPERSAND = 1u<<22,
483    /* Interpret ; as a sequencing operator, go_input_inject() remainder */
484    n_SHEXP_PARSE_META_SEMICOLON = 1u<<23,
485    /* LPAREN, RPAREN, LESSTHAN, GREATERTHAN */
486 
487    n_SHEXP_PARSE_META_MASK = n_SHEXP_PARSE_META_VERTBAR |
488          n_SHEXP_PARSE_META_AMPERSAND | n_SHEXP_PARSE_META_SEMICOLON,
489 
490    /* Keep the metacharacter (or IFS character), do not skip over it */
491    n_SHEXP_PARSE_META_KEEP = 1u<<24,
492 
493    n__SHEXP_PARSE_LAST = 24
494 };
495 
496 enum n_shexp_state{
497    n_SHEXP_STATE_NONE,
498    /* We have produced some output (or would have, with _PARSE_DRYRUN).
499     * Note that empty quotes like '' produce no output but set this bit */
500    n_SHEXP_STATE_OUTPUT = 1u<<0,
501    /* Don't call the parser again (# comment seen; out of input).
502     * Not (necessarily) mutual with _OUTPUT) */
503    n_SHEXP_STATE_STOP = 1u<<1,
504    n_SHEXP_STATE_UNICODE = 1u<<2, /* \[Uu] used */
505    n_SHEXP_STATE_CONTROL = 1u<<3, /* Control characters seen */
506    n_SHEXP_STATE_QUOTE = 1u<<4, /* Any quotes seen */
507    n_SHEXP_STATE_WS_LEAD = 1u<<5, /* _TRIM_{IFS,}SPACE: seen.. */
508    n_SHEXP_STATE_WS_TRAIL = 1u<<6, /* .. leading / trailing WS */
509    n_SHEXP_STATE_META_VERTBAR = 1u<<7, /* Metacharacter | follows/ed */
510    n_SHEXP_STATE_META_AMPERSAND = 1u<<8, /* Metacharacter & follows/ed */
511    n_SHEXP_STATE_META_SEMICOLON = 1u<<9, /* Metacharacter ; follows/ed */
512 
513    n_SHEXP_STATE_META_MASK = n_SHEXP_STATE_META_VERTBAR |
514          n_SHEXP_STATE_META_AMPERSAND | n_SHEXP_STATE_META_SEMICOLON,
515 
516    n_SHEXP_STATE_ERR_CONTROL = 1u<<16, /* \c notation with invalid arg. */
517    n_SHEXP_STATE_ERR_UNICODE = 1u<<17, /* Valid \[Uu] and !n_PSO_UNICODE */
518    n_SHEXP_STATE_ERR_NUMBER = 1u<<18, /* Bad number (\[UuXx]) */
519    n_SHEXP_STATE_ERR_IDENTIFIER = 1u<<19, /* Invalid identifier */
520    n_SHEXP_STATE_ERR_BADSUB = 1u<<20, /* Empty/bad ${}/[] substitution */
521    n_SHEXP_STATE_ERR_GROUPOPEN = 1u<<21, /* _QUOTEOPEN + no }/]/)/ 4 ${/[/( */
522    n_SHEXP_STATE_ERR_QUOTEOPEN = 1u<<22, /* Quote remains open at EOS */
523 
524    n_SHEXP_STATE_ERR_MASK = su_BITENUM_MASK(16, 22)
525 };
526 
527 enum n_sigman_flags{
528    n_SIGMAN_NONE = 0,
529    n_SIGMAN_HUP = 1<<0,
530    n_SIGMAN_INT = 1<<1,
531    n_SIGMAN_QUIT = 1<<2,
532    n_SIGMAN_TERM = 1<<3,
533    n_SIGMAN_PIPE = 1<<4,
534 
535    n_SIGMAN_IGN_HUP = 1<<5,
536    n_SIGMAN_IGN_INT = 1<<6,
537    n_SIGMAN_IGN_QUIT = 1<<7,
538    n_SIGMAN_IGN_TERM = 1<<8,
539 
540    n_SIGMAN_ALL = 0xFF,
541    /* Mostly for _leave() reraise flags */
542    n_SIGMAN_VIPSIGS = n_SIGMAN_HUP | n_SIGMAN_INT | n_SIGMAN_QUIT |
543          n_SIGMAN_TERM,
544    n_SIGMAN_NTTYOUT_PIPE = 1<<16,
545    n_SIGMAN_VIPSIGS_NTTYOUT = n_SIGMAN_HUP | n_SIGMAN_INT | n_SIGMAN_QUIT |
546          n_SIGMAN_TERM | n_SIGMAN_NTTYOUT_PIPE,
547 
548    n__SIGMAN_PING = 1<<17
549 };
550 
551 enum n_str_trim_flags{
552    n_STR_TRIM_FRONT = 1u<<0,
553    n_STR_TRIM_END = 1u<<1,
554    n_STR_TRIM_BOTH = n_STR_TRIM_FRONT | n_STR_TRIM_END
555 };
556 
557 #ifdef mx_HAVE_TLS
558 enum n_tls_verify_level{
559    n_TLS_VERIFY_IGNORE,
560    n_TLS_VERIFY_WARN,
561    n_TLS_VERIFY_ASK,
562    n_TLS_VERIFY_STRICT
563 };
564 #endif
565 
566 enum tdflags{
567    TD_NONE, /* no display conversion */
568    TD_ISPR = 1<<0, /* use isprint() checks */
569    TD_ICONV = 1<<1, /* use iconv() */
570    TD_DELCTRL = 1<<2, /* delete control characters */
571 
572    /* NOTE: _TD_EOF and _TD_BUFCOPY may be ORd with enum conversion and
573     * enum sendaction, and may thus NOT clash with their bit range! */
574    _TD_EOF = 1<<14, /* EOF seen, last round! */
575    _TD_BUFCOPY = 1<<15 /* Buffer may be constant, copy it */
576 };
577 
578 enum n_visual_info_flags{
579    n_VISUAL_INFO_NONE,
580    n_VISUAL_INFO_ONE_CHAR = 1u<<0, /* Step only one char, then return */
581    n_VISUAL_INFO_SKIP_ERRORS = 1u<<1, /* Treat via replacement, step byte */
582    n_VISUAL_INFO_WIDTH_QUERY = 1u<<2, /* Detect visual character widths */
583 
584    /* Rest only with mx_HAVE_C90AMEND1, mutual with _ONE_CHAR */
585    n_VISUAL_INFO_WOUT_CREATE = 1u<<8, /* Use/create .vic_woudat */
586    n_VISUAL_INFO_WOUT_SALLOC = 1u<<9, /* ..autorec_alloc() it first */
587    /* Only visuals into .vic_woudat - implies _WIDTH_QUERY */
588    n_VISUAL_INFO_WOUT_PRINTABLE = 1u<<10,
589    n__VISUAL_INFO_FLAGS = n_VISUAL_INFO_WOUT_CREATE |
590          n_VISUAL_INFO_WOUT_SALLOC | n_VISUAL_INFO_WOUT_PRINTABLE
591 };
592 
593 enum n_program_option{
594    n_PO_D = 1u<<0, /* -d / *debug* */
595    n_PO_V = 1u<<1, /* -v / *verbose* */
596    n_PO_VV = 1u<<2, /* .. more verbosity */
597    n_PO_VVV = 1u<<3, /* .. most verbosity */
598    n_PO_EXISTONLY = 1u<<4, /* -e */
599    n_PO_HEADERSONLY = 1u<<5, /* -H */
600    n_PO_HEADERLIST = 1u<<6, /* -L */
601    n_PO_QUICKRUN_MASK = n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST,
602    n_PO_E_FLAG = 1u<<7, /* -E / *skipemptybody* */
603    n_PO_F_FLAG = 1u<<8, /* -F */
604    n_PO_f_FLAG = 1u<<9, /* -f [and file on command line] */
605    n_PO_Mm_FLAG = 1u<<10, /* -M or -m (plus n_poption_arg_Mm) */
606    n_PO_R_FLAG = 1u<<11, /* -R */
607    n_PO_r_FLAG = 1u<<12, /* -r (plus n_poption_arg_r) */
608    n_PO_S_FLAG_TEMPORARY = 1u<<13, /* -S about to set a variable */
609    n_PO_t_FLAG = 1u<<14, /* -t */
610    n_PO_TILDE_FLAG = 1u<<15, /* -~ */
611    n_PO_BATCH_FLAG = 1u<<16, /* -# */
612 
613    /* Some easy-access shortcut; the V bits must be contiguous! */
614    n_PO_V_MASK = n_PO_V | n_PO_VV | n_PO_VVV,
615    n_PO_D_V = n_PO_D | n_PO_V_MASK,
616    n_PO_D_VV = n_PO_D | n_PO_VV | n_PO_VVV,
617    n_PO_D_VVV = n_PO_D | n_PO_VVV
618 };
619 MCTA(n_PO_V << 1 == n_PO_VV, "PO_V* must be successive")
620 MCTA(n_PO_VV << 1 == n_PO_VVV, "PO_V* must be successive")
621 
622 #define n_OBSOLETE(X) \
623 do if(!su_state_has(su_STATE_REPRODUCIBLE)){\
624    static boole su_CONCAT(a__warned__, __LINE__);\
625    if(!su_CONCAT(a__warned__, __LINE__)){\
626       su_CONCAT(a__warned__, __LINE__) = TRU1;\
627       n_err("%s: %s\n", _("Obsoletion warning"), X);\
628    }\
629 }while(0)
630 #define n_OBSOLETE2(X,Y) \
631 do if(!su_state_has(su_STATE_REPRODUCIBLE)){\
632    static boole su_CONCAT(a__warned__, __LINE__);\
633    if(!su_CONCAT(a__warned__, __LINE__)){\
634       su_CONCAT(a__warned__, __LINE__) = TRU1;\
635       n_err("%s: %s: %s\n", _("Obsoletion warning"), X, Y);\
636    }\
637 }while(0)
638 
639 /* Program state bits which may regularly fluctuate */
640 enum n_program_state{
641    n_PS_ROOT = 1u<<30, /* Temporary "bypass any checks" bit */
642 #define n_PS_ROOT_BLOCK(ACT) \
643 do{\
644    boole a___reset___ = !(n_pstate & n_PS_ROOT);\
645    n_pstate |= n_PS_ROOT;\
646    ACT;\
647    if(a___reset___)\
648       n_pstate &= ~n_PS_ROOT;\
649 }while(0)
650 
651    /* XXX These are internal to the state machine and do not belong here,
652     * XXX yet this was the easiest (accessible) approach */
653    n_PS_ERR_XIT = 1u<<0, /* Unless `ignerr' seen -> n_PSO_XIT */
654    n_PS_ERR_QUIT = 1u<<1, /* ..ditto: -> n_PSO_QUIT */
655    n_PS_ERR_EXIT_MASK = n_PS_ERR_XIT | n_PS_ERR_QUIT,
656 
657    n_PS_SOURCING = 1u<<2, /* During load() or `source' */
658    n_PS_ROBOT = 1u<<3, /* .. even more robotic */
659    n_PS_COMPOSE_MODE = 1u<<4, /* State machine recursed */
660    n_PS_COMPOSE_FORKHOOK = 1u<<5, /* A hook running in a subprocess */
661 
662    n_PS_HOOK_NEWMAIL = 1u<<7,
663    n_PS_HOOK = 1u<<8,
664    n_PS_HOOK_MASK = n_PS_HOOK_NEWMAIL | n_PS_HOOK,
665 
666    n_PS_EDIT = 1u<<9, /* Current mailbox no "system mailbox" */
667    n_PS_SETFILE_OPENED = 1u<<10, /* (hack) setfile() opened a new box */
668    n_PS_SAW_COMMAND = 1u<<11, /* ..after mailbox switch */
669    n_PS_DID_PRINT_DOT = 1u<<12, /* Current message has been printed */
670 
671    n_PS_SIGWINCH_PEND = 1u<<13, /* Need update of $COLUMNS/$LINES */
672    n_PS_PSTATE_PENDMASK = n_PS_SIGWINCH_PEND, /* pstate housekeeping needed */
673 
674    n_PS_ARGLIST_MASK = su_BITENUM_MASK(14, 16),
675    n_PS_ARGMOD_LOCAL = 1u<<14, /* "local" modifier TODO struct CmdCtx */
676    n_PS_ARGMOD_VPUT = 1u<<15, /* "vput" modifier TODO struct CmdCtx */
677    n_PS_ARGMOD_WYSH = 1u<<16, /* "wysh" modifier TODO struct CmdCtx */
678    n_PS_MSGLIST_GABBY = 1u<<14, /* n_getmsglist() saw something gabby */
679    n_PS_MSGLIST_DIRECT = 1u<<15, /* A msg was directly chosen by number */
680 
681    n_PS_EXPAND_MULTIRESULT = 1u<<17, /* Last fexpand() with MULTIOK had .. */
682    /* In the interactive mainloop, we want any error to appear once for each
683     * tick, even if it is the same as in the tick before and would normally be
684     * suppressed */
685    n_PS_ERRORS_NEED_PRINT_ONCE = 1u<<19,
686 
687    /* Bad hacks */
688    n_PS_HEADER_NEEDED_MIME = 1u<<24, /* mime_write_tohdr() not ASCII clean */
689    n_PS_READLINE_NL = 1u<<25, /* readline_input()+ saw a \n */
690    n_PS_BASE64_STRIP_CR = 1u<<26, /* Go for text output, strip CR's */
691    n_PS_SIGALARM = 1u<<27 /* Some network timer has alarm(2) installed */
692 };
693 
694 /* Various states set once, and first time messages or initializers */
695 enum n_program_state_once{
696    /* We have four program states: (0) pre getopt() done, (_GETOPT) pre rcfile
697     * loaded etc., (_CONFIG) only -X evaluation missing still, followed by
698     * _STARTED when we are fully setup */
699    n_PSO_STARTED_GETOPT = 1u<<0,
700    n_PSO_STARTED_CONFIG = 1u<<1,
701    n_PSO_STARTED = 1u<<2,
702 
703    /* Exit request pending (quick) */
704    n_PSO_XIT = 1u<<3,
705    n_PSO_QUIT = 1u<<4,
706    n_PSO_EXIT_MASK = n_PSO_XIT | n_PSO_QUIT,
707    /* *posix* requires us to exit with error if sending any mail failed */
708    n_PSO_SEND_ERROR = 1u<<5,
709 
710    /* Pre _STARTED */
711    /* 1u<<5, */
712    n_PSO_UNICODE = 1u<<6,
713    n_PSO_ENC_MBSTATE = 1u<<7,
714 
715    n_PSO_SENDMODE = 1u<<9,
716    n_PSO_INTERACTIVE = 1u<<10,
717    n_PSO_TTYIN = 1u<<11,
718    n_PSO_TTYOUT = 1u<<12, /* TODO should be TTYERR! */
719    n_PSO_TTYANY = n_PSO_TTYIN | n_PSO_TTYOUT, /* mx_tty_fp = TTY */
720    n_PSO_TTYERR = 1u<<13,
721 
722    /* "Later" */
723    n_PSO_t_FLAG_DONE = 1u<<15,
724    n_PSO_GETFILENAME_QUOTE_NOTED = 1u<<16,
725    n_PSO_ERRORS_NOTED = 1u<<17,
726    n_PSO_LINE_EDITOR_INIT = 1u<<18,
727    n_PSO_RANDOM_INIT = 1u<<19,
728    n_PSO_TERMCAP_DISABLE = 1u<<20,
729    n_PSO_TERMCAP_CA_MODE = 1u<<21,
730    n_PSO_TERMCAP_FULLWIDTH = 1u<<22, /* !am or am+xn (right margin wrap) */
731    n_PSO_PS_DOTLOCK_NOTED = 1u<<23
732 };
733 
734 /* {{{ A large enum with all the boolean and value options a.k.a their keys.
735  * Only the constant keys are in here, to be looked up via ok_[bv]look(),
736  * ok_[bv]set() and ok_[bv]clear().
737  * Variable properties are placed in {PROP=VALUE[:,PROP=VALUE:]} comments,
738  * a {\} comment causes the next line to be read for (overlong) properties.
739  * Notes:
740  * - see the introductional source comments before changing *anything* in here!
741  * - virt= implies rdonly,nodel
742  * - import= implies env
743  * - num and posnum are mutual exclusive
744  * - most default VAL_ues come from in from build system via ./make.rc
745  * - Other i3val=s and/or defval=s are imposed by POSIX: we do not (or only
746  *   additionally "trust" the system-wide RC file to establish the settings
747  * - code assumes (in conjunction with make-okey-map.pl) case-insensitive sort!
748  * (Keep in SYNC: nail.h:okeys, nail.rc, nail.1:"Initial settings") */
749 enum okeys {
750    /* This is used for all macro(-local) variables etc., i.e.,
751     * [*@#]|[1-9][0-9]*, in order to have something with correct properties.
752     * It is also used for the ${^.+} multiplexer */
753    ok_v___special_param, /* {nolopts=1,rdonly=1,nodel=1} */
754    /*__qm/__em aka ?/! should be num=1 but that more expensive than what now */
755    ok_v___qm, /* {name=?,nolopts=1,rdonly=1,nodel=1} */
756    ok_v___em, /* {name=!,nolopts=1,rdonly=1,nodel=1} */
757 
758    ok_v_account, /* {nolopts=1,rdonly=1,nodel=1} */
759    ok_b_add_file_recipients,
760 ok_v_agent_shell_lookup, /* {obsolete=1} */
761    ok_b_allnet,
762    ok_b_append,
763    /* *ask* is auto-mapped to *asksub* as imposed by standard! */
764    ok_b_ask, /* {vip=1} */
765    ok_b_askatend,
766    ok_b_askattach,
767    ok_b_askbcc,
768    ok_b_askcc,
769    ok_b_asksign,
770    ok_b_asksend,
771    ok_b_asksub, /* {i3val=TRU1} */
772    ok_v_attrlist,
773    ok_v_autobcc,
774    ok_v_autocc,
775    ok_b_autocollapse,
776    ok_b_autoprint,
777 ok_b_autothread, /* {obsolete=1} */
778    ok_v_autosort,
779 
780    ok_b_bang,
781 ok_b_batch_exit_on_error, /* {obsolete=1} */
782 ok_v_bind_timeout, /* {vip=1,obsolete=1,notempty=1,posnum=1} */
783    ok_v_bind_inter_byte_timeout, /* {\ } */
784       /* {notempty=1,posnum=1,defval=mx_BIND_INTER_BYTE_TIMEOUT} */
785    ok_v_bind_inter_key_timeout, /* {notempty=1,posnum=1} */
786 ok_b_bsdannounce, /* {obsolete=1} */
787    ok_b_bsdcompat,
788    ok_b_bsdflags,
789    ok_b_bsdheadline,
790    ok_b_bsdmsgs,
791    ok_b_bsdorder,
792    ok_v_build_cc, /* {virt=VAL_BUILD_CC_ARRAY} */
793    ok_v_build_ld, /* {virt=VAL_BUILD_LD_ARRAY} */
794    ok_v_build_os, /* {virt=VAL_BUILD_OS} */
795    ok_v_build_rest, /* {virt=VAL_BUILD_REST_ARRAY} */
796 
797    ok_v_COLUMNS, /* {notempty=1,posnum=1,env=1} */
798    /* Charset lowercase conversion handled via vip= */
799    ok_v_charset_7bit, /* {vip=1,notempty=1,defval=CHARSET_7BIT} */
800    /* But unused without mx_HAVE_ICONV, we use ok_vlook(CHARSET_8BIT_OKEY)! */
801    ok_v_charset_8bit, /* {vip=1,notempty=1,defval=CHARSET_8BIT} */
802    ok_v_charset_unknown_8bit, /* {vip=1} */
803    ok_v_cmd,
804    ok_b_colour_disable,
805    ok_b_colour_pager,
806    ok_v_contact_mail, /* {virt=VAL_CONTACT_MAIL} */
807    ok_v_contact_web, /* {virt=VAL_CONTACT_WEB} */
808    ok_v_content_description_forwarded_message, /* {\ } */
809       /* {defval=mx_CONTENT_DESC_FORWARDED_MESSAGE} */
810    ok_v_content_description_quote_attachment, /* {\ } */
811       /* {defval=mx_CONTENT_DESC_QUOTE_ATTACHMENT} */
812    ok_v_content_description_smime_message, /* {\ } */
813       /* {defval=mx_CONTENT_DESC_SMIME_MESSAGE} */
814    ok_v_content_description_smime_signature, /* {\ } */
815       /* {defval=mx_CONTENT_DESC_SMIME_SIG} */
816 
817    ok_v_crt, /* {posnum=1} */
818    ok_v_customhdr, /* {vip=1} */
819 
820    ok_v_DEAD, /* {notempty=1,env=1,defval=VAL_DEAD} */
821    ok_v_datefield, /* {i3val="%Y-%m-%d %H:%M"} */
822    ok_v_datefield_markout_older, /* {i3val="%Y-%m-%d"} */
823    ok_b_debug, /* {vip=1} */
824    ok_b_disposition_notification_send,
825    ok_b_dot,
826    ok_b_dotlock_disable,
827 ok_b_dotlock_ignore_error, /* {obsolete=1} */
828 
829    ok_v_EDITOR, /* {env=1,notempty=1,defval=VAL_EDITOR} */
830    ok_v_editalong,
831    ok_b_editheaders,
832    ok_b_emptystart,
833 ok_v_encoding, /* {obsolete=1} */
834    ok_b_errexit,
835    ok_v_errors_limit, /* {notempty=1,posnum=1,defval=VAL_ERRORS_LIMIT} */
836    ok_v_escape, /* {defval=n_ESCAPE} */
837    ok_v_expandaddr,
838    ok_v_expandaddr_domaincheck, /* {notempty=1} */
839    ok_v_expandargv,
840 
841    ok_v_features, /* {virt=VAL_FEATURES} */
842    ok_b_flipr,
843    ok_v_folder, /* {vip=1} */
844    ok_v_folder_resolved, /* {rdonly=1,nodel=1} */
845    ok_v_folder_hook,
846    ok_b_followup_to,
847    ok_b_followup_to_add_cc,
848    ok_v_followup_to_honour,
849    ok_b_forward_add_cc,
850    ok_b_forward_as_attachment,
851    ok_v_forward_inject_head,
852    ok_v_forward_inject_tail,
853    ok_v_from, /* {vip=1} */
854    ok_b_fullnames,
855 ok_v_fwdheading, /* {obsolete=1} */
856 
857    ok_v_HOME, /* {vip=1,nodel=1,notempty=1,import=1} */
858    ok_b_header, /* {i3val=TRU1} */
859    ok_v_headline,
860    ok_v_headline_bidi,
861    ok_b_headline_plain,
862    ok_v_history_file,
863    ok_v_history_gabby,
864    ok_b_history_gabby_persist,
865    ok_v_history_size, /* {notempty=1,posnum=1} */
866    ok_b_hold,
867    ok_v_hostname, /* {vip=1} */
868 
869    ok_b_idna_disable,
870    ok_v_ifs, /* {vip=1,defval=" \t\n"} */
871    ok_v_ifs_ws, /* {vip=1,rdonly=1,nodel=1,i3val=" \t\n"} */
872    ok_b_ignore,
873    ok_b_ignoreeof,
874    ok_v_inbox,
875    ok_v_indentprefix, /* {defval="\t"} */
876 
877    ok_b_keep,
878    ok_b_keep_content_length,
879    ok_b_keepsave,
880 
881    ok_v_LANG, /* {vip=1,env=1,notempty=1} */
882    ok_v_LC_ALL, /* {name=LC_ALL,vip=1,env=1,notempty=1} */
883    ok_v_LC_CTYPE, /* {name=LC_CTYPE,vip=1,env=1,notempty=1} */
884    ok_v_LINES, /* {notempty=1,posnum=1,env=1} */
885    ok_v_LISTER, /* {env=1,notempty=1,defval=VAL_LISTER} */
886    ok_v_LOGNAME, /* {rdonly=1,import=1} */
887    ok_v_line_editor_cpl_word_breaks, /* {\ } */
888       /* {defval=n_LINE_EDITOR_CPL_WORD_BREAKS} */
889    ok_b_line_editor_disable,
890    ok_b_line_editor_no_defaults,
891    ok_v_log_prefix, /* {nodel=1,i3val=VAL_UAGENT ": "} */
892 
893    ok_v_MAIL, /* {env=1} */
894    ok_v_MAILCAPS, /* {import=1,defval=VAL_MAILCAPS} */
895    ok_v_MAILRC, /* {import=1,notempty=1,defval=VAL_MAILRC} */
896    ok_b_MAILX_NO_SYSTEM_RC, /* {name=MAILX_NO_SYSTEM_RC,import=1} */
897    ok_v_MBOX, /* {env=1,notempty=1,defval=VAL_MBOX} */
898    ok_v_mailbox_resolved, /* {nolopts=1,rdonly=1,nodel=1} */
899    ok_v_mailbox_display, /* {nolopts=1,rdonly=1,nodel=1} */
900    ok_b_mailcap_disable,
901    ok_v_mailx_extra_rc, /* {notempty=1} */
902    /* TODO drop all those _v_mailx which are now accessible via `digmsg'!
903     * TODO Documentation yet removed, n_temporary_compose_hook_varset() not */
904 ok_v_mailx_command, /* {rdonly=1,nodel=1} */
905 ok_v_mailx_subject, /* {rdonly=1,nodel=1} */
906 ok_v_mailx_from, /* {rdonly=1,nodel=1} */
907 ok_v_mailx_sender, /* {rdonly=1,nodel=1} */
908 ok_v_mailx_to, /* {rdonly=1,nodel=1} */
909 ok_v_mailx_cc, /* {rdonly=1,nodel=1} */
910 ok_v_mailx_bcc, /* {rdonly=1,nodel=1} */
911 ok_v_mailx_raw_to, /* {rdonly=1,nodel=1} */
912 ok_v_mailx_raw_cc, /* {rdonly=1,nodel=1} */
913 ok_v_mailx_raw_bcc, /* {rdonly=1,nodel=1} */
914 ok_v_mailx_orig_from, /* {rdonly=1,nodel=1} */
915 ok_v_mailx_orig_to, /* {rdonly=1,nodel=1} */
916 ok_v_mailx_orig_cc, /* {rdonly=1,nodel=1} */
917 ok_v_mailx_orig_bcc, /* {rdonly=1,nodel=1} */
918    ok_b_markanswered,
919    ok_b_mbox_fcc_and_pcc, /* {i3val=1} */
920    ok_b_mbox_rfc4155,
921    ok_b_memdebug, /* {vip=1} */
922    ok_b_message_id_disable,
923    ok_v_message_inject_head,
924    ok_v_message_inject_tail,
925    ok_b_metoo,
926    ok_b_mime_allow_text_controls,
927    ok_b_mime_alternative_favour_rich,
928    ok_v_mime_counter_evidence, /* {posnum=1} */
929    ok_v_mime_encoding,
930    ok_b_mime_force_sendout,
931    ok_v_mimetypes_load_control,
932    ok_v_mta, /* {notempty=1,defval=VAL_MTA} */
933    ok_v_mta_aliases, /* {notempty=1} */
934    ok_v_mta_arguments,
935    ok_b_mta_no_default_arguments,
936    ok_b_mta_no_receiver_arguments,
937    ok_v_mta_argv0, /* {notempty=1,defval=VAL_MTA_ARGV0} */
938    ok_b_mta_bcc_ok,
939 
940 ok_v_NAIL_EXTRA_RC, /* {name=NAIL_EXTRA_RC,env=1,notempty=1,obsolete=1} */
941 ok_b_NAIL_NO_SYSTEM_RC, /* {name=NAIL_NO_SYSTEM_RC,import=1,obsolete=1} */
942 ok_v_NAIL_HEAD, /* {name=NAIL_HEAD,obsolete=1} */
943 ok_v_NAIL_HISTFILE, /* {name=NAIL_HISTFILE,obsolete=1} */
944 ok_v_NAIL_HISTSIZE, /* {name=NAIL_HISTSIZE,notempty=1,num=1,obsolete=1} */
945 ok_v_NAIL_TAIL, /* {name=NAIL_TAIL,obsolete=1} */
946    ok_v_NETRC, /* {env=1,notempty=1,defval=VAL_NETRC} */
947    ok_b_netrc_lookup, /* {chain=1} */
948    ok_v_netrc_pipe,
949    ok_v_newfolders,
950    ok_v_newmail,
951 
952    ok_v_on_account_cleanup, /* {notempty=1} */
953    ok_v_on_compose_cleanup, /* {notempty=1} */
954    ok_v_on_compose_enter, /* {notempty=1} */
955    ok_v_on_compose_leave, /* {notempty=1} */
956    ok_v_on_compose_splice, /* {notempty=1} */
957    ok_v_on_compose_splice_shell, /* {notempty=1} */
958    ok_v_on_history_addition, /* {notempty=1} */
959    ok_v_on_main_loop_tick, /* {notempty=1} */
960    ok_v_on_program_exit, /* {notempty=1} */
961    ok_v_on_resend_cleanup, /* {notempty=1} */
962    ok_v_on_resend_enter, /* {notempty=1} */
963    ok_b_outfolder,
964 
965    ok_v_PAGER, /* {env=1,notempty=1,defval=VAL_PAGER} */
966    ok_v_PATH, /* {nodel=1,import=1} */
967    /* XXX POSIXLY_CORRECT->posix: needs initial call via main()! */
968    ok_b_POSIXLY_CORRECT, /* {vip=1,import=1,name=POSIXLY_CORRECT} */
969    ok_b_page,
970    ok_v_password, /* {chain=1} */
971    ok_b_piperaw,
972    ok_v_pop3_auth, /* {chain=1} */
973    ok_b_pop3_bulk_load,
974    ok_v_pop3_keepalive, /* {notempty=1,posnum=1} */
975    ok_b_pop3_no_apop, /* {chain=1} */
976    ok_b_pop3_use_starttls, /* {chain=1} */
977    ok_b_posix, /* {vip=1} */
978    ok_b_print_alternatives,
979    ok_v_prompt, /* {i3val="? "} */
980    ok_v_prompt2, /* {i3val=".. "} */
981 
982    ok_b_quiet,
983    ok_v_quote,
984    ok_b_quote_add_cc,
985    ok_b_quote_as_attachment,
986    ok_v_quote_chars, /* {vip=1,notempty=1,defval=">|}:"} */
987    ok_v_quote_fold,
988    ok_v_quote_inject_head,
989    ok_v_quote_inject_tail,
990 
991    ok_b_r_option_implicit,
992    ok_b_recipients_in_cc,
993    ok_v_record,
994    ok_b_record_files,
995    ok_b_record_resent,
996    ok_b_reply_in_same_charset,
997    ok_v_reply_strings,
998 ok_v_replyto, /* {obsolete=1,notempty=1} */
999    ok_v_reply_to, /* {notempty=1} */
1000    ok_v_reply_to_honour,
1001    ok_v_reply_to_swap_in,
1002    ok_b_rfc822_body_from_, /* {name=rfc822-body-from_} */
1003 
1004    ok_v_SHELL, /* {import=1,notempty=1,defval=VAL_SHELL} */
1005 ok_b_SYSV3, /* {env=1,obsolete=1} */
1006    ok_b_save, /* {i3val=TRU1} */
1007    ok_v_screen, /* {notempty=1,posnum=1} */
1008    ok_b_searchheaders,
1009    /* Charset lowercase conversion handled via vip= */
1010    ok_v_sendcharsets, /* {vip=1} */
1011    ok_b_sendcharsets_else_ttycharset,
1012    ok_v_sender, /* {vip=1} */
1013 ok_v_sendmail, /* {obsolete=1} */
1014 ok_v_sendmail_arguments, /* {obsolete=1} */
1015 ok_b_sendmail_no_default_arguments, /* {obsolete=1} */
1016 ok_v_sendmail_progname, /* {obsolete=1} */
1017    ok_v_sendwait, /* {i3val=""} */
1018    ok_b_showlast,
1019    ok_b_showname,
1020    ok_b_showto,
1021    ok_v_Sign,
1022    ok_v_sign,
1023 ok_v_signature, /* {obsolete=1} */
1024    ok_b_skipemptybody, /* {vip=1} */
1025    ok_v_smime_ca_dir,
1026    ok_v_smime_ca_file,
1027    ok_v_smime_ca_flags,
1028    ok_b_smime_ca_no_defaults,
1029    ok_v_smime_cipher, /* {chain=1} */
1030    ok_v_smime_crl_dir,
1031    ok_v_smime_crl_file,
1032    ok_v_smime_encrypt, /* {chain=1} */
1033    ok_b_smime_force_encryption,
1034 ok_b_smime_no_default_ca, /* {obsolete=1} */
1035    ok_b_smime_sign,
1036    ok_v_smime_sign_cert, /* {chain=1} */
1037    ok_v_smime_sign_digest, /* {chain=1} */
1038    ok_v_smime_sign_include_certs, /* {chain=1} */
1039 ok_v_smime_sign_message_digest, /* {chain=1,obsolete=1} */
1040 ok_v_smtp, /* {obsolete=1} */
1041    ok_v_smtp_auth,                     /* {chain=1} */
1042 ok_v_smtp_auth_password, /* {obsolete=1} */
1043 ok_v_smtp_auth_user, /* {obsolete=1} */
1044    ok_v_smtp_hostname,                 /* {vip=1} */
1045    ok_b_smtp_use_starttls,             /* {chain=1} */
1046    ok_v_SOCKS5_PROXY, /* {vip=1,import=1,notempty=1,name=SOCKS5_PROXY} */
1047    ok_v_SOURCE_DATE_EPOCH,             /* {\ } */
1048       /* {name=SOURCE_DATE_EPOCH,rdonly=1,import=1,notempty=1,posnum=1} */
1049    ok_v_socket_connect_timeout, /* {posnum=1} */
1050    ok_v_socks_proxy, /* {vip=1,chain=1,notempty=1} */
1051    ok_v_spam_interface,
1052    ok_v_spam_maxsize, /* {notempty=1,posnum=1} */
1053    ok_v_spamc_command,
1054    ok_v_spamc_arguments,
1055    ok_v_spamc_user,
1056    ok_v_spamfilter_ham,
1057    ok_v_spamfilter_noham,
1058    ok_v_spamfilter_nospam,
1059    ok_v_spamfilter_rate,
1060    ok_v_spamfilter_rate_scanscore,
1061    ok_v_spamfilter_spam,
1062 ok_v_ssl_ca_dir, /* {chain=1,obsolete=1} */
1063 ok_v_ssl_ca_file, /* {chain=1,obsolete=1} */
1064 ok_v_ssl_ca_flags, /* {chain=1,obsolete=1} */
1065 ok_b_ssl_ca_no_defaults, /* {chain=1,obsolete=1} */
1066 ok_v_ssl_cert, /* {chain=1,obsolete=1} */
1067 ok_v_ssl_cipher_list, /* {chain=1,obsolete=1} */
1068 ok_v_ssl_config_file, /* {obsolete=1} */
1069 ok_v_ssl_config_module, /* {chain=1,obsolete=1} */
1070 ok_v_ssl_config_pairs, /* {chain=1,obsolete=1} */
1071 ok_v_ssl_curves, /* {chain=1,obsolete=1} */
1072 ok_v_ssl_crl_dir, /* {obsolete=1} */
1073 ok_v_ssl_crl_file, /* {obsolete=1} */
1074 ok_v_ssl_features, /* {virt=VAL_TLS_FEATURES,obsolete=1} */
1075 ok_v_ssl_key, /* {chain=1,obsolete=1} */
1076 ok_v_ssl_method, /* {chain=1,obsolete=1} */
1077 ok_b_ssl_no_default_ca, /* {obsolete=1} */
1078 ok_v_ssl_protocol, /* {chain=1,obsolete=1} */
1079 ok_v_ssl_rand_egd, /* {obsolete=1} */
1080 ok_v_ssl_rand_file, /* {obsolete=1}*/
1081 ok_v_ssl_verify, /* {chain=1,obsolete=1} */
1082    ok_v_stealthmua,
1083    ok_v_system_mailrc, /* {virt=VAL_SYSCONFDIR "/" VAL_SYSCONFRC} */
1084 
1085    ok_v_TERM, /* {env=1} */
1086    ok_v_TMPDIR, /* {import=1,vip=1,notempty=1,defval=VAL_TMPDIR} */
1087    ok_v_termcap,
1088    ok_b_termcap_ca_mode,
1089    ok_b_termcap_disable,
1090    ok_v_tls_ca_dir, /* {chain=1} */
1091    ok_v_tls_ca_file, /* {chain=1} */
1092    ok_v_tls_ca_flags, /* {chain=1} */
1093    ok_b_tls_ca_no_defaults, /* {chain=1} */
1094    ok_v_tls_config_file,
1095    ok_v_tls_config_module, /* {chain=1} */
1096    ok_v_tls_config_pairs, /* {chain=1} */
1097    ok_v_tls_crl_dir,
1098    ok_v_tls_crl_file,
1099    ok_v_tls_features, /* {virt=VAL_TLS_FEATURES} */
1100    ok_v_tls_fingerprint, /* {chain=1} */
1101    ok_v_tls_fingerprint_digest, /* {chain=1} */
1102    ok_v_tls_rand_file,
1103    ok_v_tls_verify, /* {chain=1} */
1104    ok_v_toplines, /* {notempty=1,num=1,defval="5"} */
1105    ok_b_topsqueeze,
1106    /* Charset lowercase conversion handled via vip= */
1107    ok_v_ttycharset, /* {vip=1,notempty=1,defval=CHARSET_8BIT} */
1108    ok_b_typescript_mode, /* {vip=1} */
1109 
1110    ok_v_USER, /* {rdonly=1,import=1} */
1111    ok_v_umask, /* {vip=1,nodel=1,posnum=1,i3val="0077"} */
1112    ok_v_user, /* {notempty=1,chain=1} */
1113 
1114    ok_v_VISUAL, /* {env=1,notempty=1,defval=VAL_VISUAL} */
1115    ok_v_v15_compat,
1116    ok_v_verbose, /* {vip=1,posnum=1} */
1117    ok_v_version, /* {virt=mx_VERSION} */
1118    ok_v_version_date, /* {virt=mx_VERSION_DATE} */
1119    ok_v_version_hexnum, /* {virt=mx_VERSION_HEXNUM,posnum=1} */
1120    ok_v_version_major, /* {virt=mx_VERSION_MAJOR,posnum=1} */
1121    ok_v_version_minor, /* {virt=mx_VERSION_MINOR,posnum=1} */
1122    ok_v_version_update, /* {virt=mx_VERSION_UPDATE,posnum=1} */
1123 
1124    ok_b_writebackedited
1125 
1126 ,  /* Obsolete IMAP related non-sorted */
1127 ok_b_disconnected, /* {chain=1} */
1128 ok_v_imap_auth, /* {chain=1} */
1129 ok_v_imap_cache,
1130 ok_v_imap_delim, /* {chain=1} */
1131 ok_v_imap_keepalive, /* {chain=1} */
1132 ok_v_imap_list_depth,
1133 ok_b_imap_use_starttls /* {chain=1} */
1134 }; /* }}} */
1135 enum {n_OKEYS_MAX = ok_b_imap_use_starttls};
1136 
1137 /* Forwards */
1138 struct mx_attachment;
1139 struct mx_name;
1140 
1141 struct str{
1142    char *s; /* the string's content */
1143    uz l; /* the stings's length */
1144 };
1145 
1146 struct n_string{
1147    char *s_dat; /*@ May contain NULs, not automatically terminated */
1148    u32 s_len; /*@ gth of string */
1149    u32 s_auto : 1; /* Stored in auto-reclaimed storage? */
1150    u32 s_size : 31; /* of .s_dat, -1 */
1151 };
1152 
1153 struct n_strlist{
1154    struct n_strlist *sl_next;
1155    uz sl_len;
1156    char sl_dat[VFIELD_SIZE(0)];
1157 };
1158 #define n_STRLIST_ALLOC(SZ) /* XXX -> nailfuns.h (and pimp interface) */\
1159    n_alloc(VSTRUCT_SIZEOF(struct n_strlist, sl_dat) + (SZ) +1)
1160 #define n_STRLIST_AUTO_ALLOC(SZ) \
1161    n_autorec_alloc(VSTRUCT_SIZEOF(struct n_strlist, sl_dat) + (SZ) +1)
1162 #define n_STRLIST_LOFI_ALLOC(SZ) \
1163    n_lofi_alloc(VSTRUCT_SIZEOF(struct n_strlist, sl_dat) + (SZ) +1)
1164 #define n_STRLIST_PLAIN_SIZE() VSTRUCT_SIZEOF(struct n_strlist, sl_dat)
1165 
1166 struct n_go_data_ctx{
1167    struct su_mem_bag *gdc_membag;
1168    void *gdc_ifcond; /* Saved state of conditional stack */
1169 #ifdef mx_HAVE_COLOUR
1170    struct mx_colour_env *gdc_colour;
1171    boole gdc_colour_active;
1172    u8 gdc__colour_pad[7];
1173 # define mx_COLOUR_IS_ACTIVE() \
1174    (/*n_go_data->gc_data.gdc_colour != su_NIL &&*/\
1175     /*n_go_data->gc_data.gdc_colour->ce_enabled*/ n_go_data->gdc_colour_active)
1176 #endif
1177    struct su_mem_bag gdc__membag_buf[1];
1178 };
1179 
1180 struct search_expr{
1181    /* XXX Type of search should not be evaluated but be enum */
1182    boole ss_field_exists; /* Only check whether field spec. exists */
1183    boole ss_skin; /* Shall work on (skin()ned) addresses */
1184    u8 ss__pad[6];
1185    char const *ss_field; /* Field spec. where to search (not always used) */
1186    char const *ss_body; /* Field body search expression */
1187 #ifdef mx_HAVE_REGEX
1188    regex_t *ss_fieldre; /* Could be instead of .ss_field */
1189    regex_t *ss_bodyre; /* Ditto, .ss_body */
1190    regex_t ss__fieldre_buf;
1191    regex_t ss__bodyre_buf;
1192 #endif
1193 };
1194 
1195 struct n_timespec{
1196    s64 ts_sec;
1197    sz ts_nsec;
1198 };
1199 
1200 struct time_current{ /* TODO s64, etc. */
1201    time_t tc_time;
1202    struct tm tc_gm;
1203    struct tm tc_local;
1204    char tc_ctime[32];
1205 };
1206 
1207 struct mailbox{
1208    enum{
1209       MB_NONE = 000, /* no reply expected */
1210       MB_COMD = 001, /* command reply expected */
1211       MB_MULT = 002, /* multiline reply expected */
1212       MB_PREAUTH = 004, /* not in authenticated state */
1213       MB_BYE = 010, /* may accept a BYE state */
1214       MB_BAD_FROM_ = 1<<4 /* MBOX with invalid From_ seen & logged */
1215    } mb_active;
1216    FILE *mb_itf; /* temp file with messages, read open */
1217    FILE *mb_otf; /* same, write open */
1218    char *mb_sorted; /* sort method */
1219    enum{
1220       MB_VOID, /* no type (e. g. connection failed) */
1221       MB_FILE, /* local file */
1222       MB_POP3, /* POP3 mailbox */
1223 MB_IMAP, /* IMAP mailbox */
1224 MB_CACHE, /* IMAP cache */
1225       MB_MAILDIR /* maildir folder */
1226    } mb_type; /* type of mailbox */
1227    enum{
1228       MB_DELE = 01, /* may delete messages in mailbox */
1229       MB_EDIT = 02 /* may edit messages in mailbox */
1230    } mb_perm;
1231    int mb_threaded; /* mailbox has been threaded */
1232 #ifdef mx_HAVE_IMAP
1233    enum mbflags{
1234       MB_NOFLAGS = 000,
1235       MB_UIDPLUS = 001, /* supports IMAP UIDPLUS */
1236       MB_SASL_IR = 002  /* supports RFC 4959 SASL-IR */
1237    } mb_flags;
1238    u64 mb_uidvalidity; /* IMAP unique identifier validity */
1239    char *mb_imap_account; /* name of current IMAP account */
1240    char *mb_imap_pass; /* xxx v15-compat URL workaround */
1241    char *mb_imap_mailbox; /* name of current IMAP mailbox */
1242    char *mb_cache_directory; /* name of cache directory */
1243    char mb_imap_delim[8]; /* Directory separator(s), [0] += replacer */
1244 #endif
1245    /* XXX mailbox.mb_accmsg is a hack in so far as the mailbox object should
1246     * XXX have an on_close event to which that machinery should connect */
1247    struct mx_dig_msg_ctx *mb_digmsg; /* Open `digmsg' connections */
1248    struct mx_socket *mb_sock; /* socket structure */
1249 };
1250 
1251 enum needspec{
1252    NEED_UNSPEC, /* unspecified need, don't fetch */
1253    NEED_HEADER, /* need the header of a message */
1254    NEED_BODY /* need header and body of a message */
1255 };
1256 
1257 enum content_info{
1258    CI_NOTHING, /* Nothing downloaded yet */
1259    CI_HAVE_HEADER = 1u<<0, /* Header is downloaded */
1260    CI_HAVE_BODY = 1u<<1, /* Entire message is downloaded */
1261    CI_HAVE_MASK = CI_HAVE_HEADER | CI_HAVE_BODY,
1262    CI_MIME_ERRORS = 1u<<2, /* Defective MIME structure */
1263    CI_EXPANDED = 1u<<3, /* Container part (pk7m) exploded into X */
1264    CI_SIGNED = 1u<<4, /* Has a signature.. */
1265    CI_SIGNED_OK = 1u<<5, /* ..verified ok.. */
1266    CI_SIGNED_BAD = 1u<<6, /* ..verified bad (missing key).. */
1267    CI_ENCRYPTED = 1u<<7, /* Is encrypted.. */
1268    CI_ENCRYPTED_OK = 1u<<8, /* ..decryption possible/ok.. */
1269    CI_ENCRYPTED_BAD = 1u<<9 /* ..not possible/ok */
1270 };
1271 
1272 /* Note: flags that are used in obs-imap-cache.c may not change */
1273 enum mflag{
1274    MUSED = 1u<<0, /* entry is used, but this bit isn't */
1275    MDELETED = 1u<<1, /* entry has been deleted */
1276    MSAVED = 1u<<2, /* entry has been saved */
1277    MTOUCH = 1u<<3, /* entry has been noticed */
1278    MPRESERVE = 1u<<4, /* keep entry in sys mailbox */
1279    MMARK = 1u<<5, /* message is marked! */
1280    MODIFY = 1u<<6, /* message has been modified */
1281    MNEW = 1u<<7, /* message has never been seen */
1282    MREAD = 1u<<8, /* message has been read sometime. */
1283    MSTATUS = 1u<<9, /* message status has changed */
1284    MBOX = 1u<<10, /* Send this to mbox, regardless */
1285    MNOFROM = 1u<<11, /* no From line */
1286    MHIDDEN = 1u<<12, /* message is hidden to user */
1287 MFULLYCACHED = 1u<<13, /* IMAP cached */
1288    MBOXED = 1u<<14, /* message has been sent to mbox */
1289 MUNLINKED = 1u<<15, /* Unlinked from IMAP cache */
1290    MNEWEST = 1u<<16, /* message is very new (newmail) */
1291    MFLAG = 1u<<17, /* message has been flagged recently */
1292    MUNFLAG = 1u<<18, /* message has been unflagged */
1293    MFLAGGED = 1u<<19, /* message is `flagged' */
1294    MANSWER = 1u<<20, /* message has been answered recently */
1295    MUNANSWER = 1u<<21, /* message has been unanswered */
1296    MANSWERED = 1u<<22, /* message is `answered' */
1297    MDRAFT = 1u<<23, /* message has been drafted recently */
1298    MUNDRAFT = 1u<<24, /* message has been undrafted */
1299    MDRAFTED = 1u<<25, /* message is marked as `draft' */
1300    MOLDMARK = 1u<<26, /* messages was marked previously */
1301    MSPAM = 1u<<27, /* message is classified as spam */
1302    MSPAMUNSURE = 1u<<28, /* message may be spam, but it is unsure */
1303 
1304    /* The following are hacks in so far as they let imagine what the future
1305     * will bring, without doing this already today */
1306    MBADFROM_ = 1u<<29, /* From_ line must be replaced */
1307    MDISPLAY = 1u<<30 /* Display content of this part */
1308 };
1309 #define MMNORM (MDELETED | MSAVED | MHIDDEN)
1310 #define MMNDEL (MDELETED | MHIDDEN)
1311 
1312 #define visible(mp) (((mp)->m_flag & MMNDEL) == 0)
1313 
1314 struct mimepart{
1315    enum mflag m_flag;
1316    enum content_info m_content_info;
1317 #ifdef mx_HAVE_SPAM
1318    u32 m_spamscore; /* Spam score as int, 24:8 bits */
1319 #else
1320    u8 m__pad1[4];
1321 #endif
1322    int m_block; /* Block number of this part */
1323    uz m_offset; /* Offset in block of part */
1324    uz m_size; /* Bytes in the part */
1325    uz m_xsize; /* Bytes in the full part */
1326    long m_lines; /* Lines in the message; wire format! */
1327    long m_xlines; /* Lines in the full message; ditto */
1328    time_t m_time; /* Time the message was sent */
1329    char const *m_from; /* Message sender */
1330    struct mimepart *m_nextpart; /* Next part at same level */
1331    struct mimepart *m_multipart; /* Parts of multipart */
1332    struct mimepart *m_parent; /* Enclosing multipart part */
1333    char const *m_ct_type; /* Content-type */
1334    char const *m_ct_type_plain; /* Content-type without specs */
1335    char const *m_ct_type_usr_ovwr; /* Forcefully overwritten one */
1336    char const *m_charset;
1337    char const *m_ct_enc; /* Content-Transfer-Encoding */
1338    u32 m_mimetype; /* enum mx_mimetype */
1339    enum mime_enc m_mime_enc; /* ..in enum */
1340    char *m_partstring; /* Part level string */
1341    char *m_filename; /* ..of attachment */
1342    char const *m_content_description;
1343    char const *m_external_body_url; /* message/external-body:access-type=URL */
1344    struct mx_mimetype_handler *m_handler; /* MIME handler if yet classified */
1345 };
1346 
1347 struct message{
1348    enum mflag m_flag; /* flags */
1349    enum content_info m_content_info;
1350 #ifdef mx_HAVE_SPAM
1351    u32 m_spamscore; /* Spam score as int, 24:8 bits */
1352 #else
1353    u8 m__pad1[4];
1354 #endif
1355    int m_block; /* block number of this message */
1356    uz m_offset; /* offset in block of message */
1357    uz m_size; /* Bytes in the message */
1358    uz m_xsize; /* Bytes in the full message */
1359    long m_lines; /* Lines in the message */
1360    long m_xlines; /* Lines in the full message */
1361    time_t m_time; /* time the message was sent */
1362    time_t m_date; /* time in the 'Date' field */
1363 #ifdef mx_HAVE_IMAP
1364    u64 m_uid; /* IMAP unique identifier */
1365 #endif
1366 #ifdef mx_HAVE_MAILDIR
1367    char const *m_maildir_file; /* original maildir file of msg */
1368    u32 m_maildir_hash; /* hash of file name in maildir sub */
1369 #endif
1370    int m_collapsed; /* collapsed thread information */
1371    unsigned m_idhash; /* hash on Message-ID for threads */
1372    unsigned m_level; /* thread level of message */
1373    long m_threadpos; /* position in threaded display */
1374    struct message *m_child; /* first child of this message */
1375    struct message *m_younger; /* younger brother of this message */
1376    struct message *m_elder; /* elder brother of this message */
1377    struct message *m_parent; /* parent of this message */
1378 };
1379 
1380 /* Given a file address, determine the block number it represents */
1381 #define mailx_blockof(off) S(int,(off) / 4096)
1382 #define mailx_offsetof(off) S(int,(off) % 4096)
1383 #define mailx_positionof(block, offset) (S(off_t,block) * 4096 + (offset))
1384 
1385 enum gfield{ /* TODO -> enum m_grab_head, m_GH_xy */
1386    GNONE,
1387    GTO = 1u<<0, /* Grab To: line */
1388    GSUBJECT = 1u<<1, /* Likewise, Subject: line */
1389    GCC = 1u<<2, /* And the Cc: line */
1390    GBCC = 1u<<3, /* And also the Bcc: line */
1391 
1392    GNL = 1u<<4, /* Print blank line after */
1393    GDEL = 1u<<5, /* Entity removed from list */
1394    GCOMMA = 1u<<6, /* detract() puts in commas */
1395    GUA = 1u<<7, /* User-Agent field */
1396    GMIME = 1u<<8, /* MIME 1.0 fields */
1397    GMSGID = 1u<<9, /* a Message-ID */
1398    GNAMEONLY = 1u<<10, /* detract() does NOT use fullnames */
1399 
1400    GIDENT = 1u<<11, /* From:, Reply-To:, MFT: (user headers) */
1401    GREF = 1u<<12, /* References:, In-Reply-To:, (Message-ID:) */
1402    GREF_IRT = 1u<<30, /* XXX Hack; only In-Reply-To: -> n_run_editor() */
1403    GDATE = 1u<<13, /* Date: field */
1404    GFULL = 1u<<14, /* Include full names, comments etc. */
1405    GSKIN = 1u<<15, /* Skin names */
1406    GEXTRA = 1u<<16, /* Extra fields (mostly like GIDENT XXX) */
1407    GFILES = 1u<<17, /* Include filename and pipe addresses */
1408    GFULLEXTRA = 1u<<18, /* Only with GFULL: GFULL less address */
1409    GBCC_IS_FCC = 1u<<19, /* This GBCC is (or was) indeed a Fcc: */
1410    GSHEXP_PARSE_HACK = 1u<<20, /* lextract()+: *expandaddr*=shquote */
1411    /* All given input (nalloc() etc.) to be interpreted as a single address */
1412    GNOT_A_LIST = 1u<<21,
1413    GNULL_OK = 1u<<22, /* NULL return OK for nalloc()+ */
1414    /* HACK: support "|bla", i.e., anything enclosed in quotes; e.g., used for
1415     * MTA alias parsing */
1416    GQUOTE_ENCLOSED_OK = 1u<<23
1417 };
1418 #define GMASK (GTO | GSUBJECT | GCC | GBCC)
1419 
1420 enum header_flags{
1421    HF_NONE = 0,
1422    HF_LIST_REPLY = 1u<<0,
1423    HF_MFT_SENDER = 1u<<1, /* Add ourselves to Mail-Followup-To: */
1424    HF_RECIPIENT_RECORD = 1u<<10, /* Save message in file named after rec. */
1425    HF_USER_EDITED = 1u<<11,
1426    HF__NEXT_SHIFT = 16u
1427 };
1428 
1429 /* Structure used to pass about the current state of a message (header) */
1430 struct n_header_field{
1431    struct n_header_field *hf_next;
1432    u32 hf_nl; /* Field-name length */
1433    u32 hf_bl; /* Field-body length*/
1434    char hf_dat[VFIELD_SIZE(0)];
1435 };
1436 
1437 struct header{
1438    u32 h_flags; /* enum header_flags bits */
1439    u32 h_dummy;
1440    char *h_subject; /* Subject string */
1441    char const *h_charset; /* preferred charset */
1442    struct mx_name *h_from; /* overridden "From:" field */
1443    struct mx_name *h_sender; /* overridden "Sender:" field */
1444    struct mx_name *h_to; /* Dynamic "To:" string */
1445    struct mx_name *h_cc; /* Carbon copies string */
1446    struct mx_name *h_bcc; /* Blind carbon copies */
1447    struct mx_name *h_fcc; /* Fcc: file carbon copies to */
1448    struct mx_name *h_ref; /* References (possibly overridden) */
1449    struct mx_attachment *h_attach; /* MIME attachments */
1450    struct mx_name *h_reply_to; /* overridden "Reply-To:" field */
1451    struct mx_name *h_message_id; /* overridden "Message-ID:" field */
1452    struct mx_name *h_in_reply_to;/* overridden "In-Reply-To:" field */
1453    struct mx_name *h_mft; /* Mail-Followup-To */
1454    char const *h_list_post; /* Address from List-Post:, for `Lreply' */
1455    struct n_header_field *h_user_headers;
1456    struct n_header_field *h_custom_headers; /* (Cached result) */
1457    /* Raw/original versions of the header(s). If any */
1458    char const *h_mailx_command;
1459    struct mx_name *h_mailx_raw_to;
1460    struct mx_name *h_mailx_raw_cc;
1461    struct mx_name *h_mailx_raw_bcc;
1462    struct mx_name *h_mailx_orig_sender;
1463    struct mx_name *h_mailx_orig_from;
1464    struct mx_name *h_mailx_orig_to;
1465    struct mx_name *h_mailx_orig_cc;
1466    struct mx_name *h_mailx_orig_bcc;
1467 };
1468 
1469 struct n_addrguts{
1470    /* Input string as given (maybe replaced with a fixed one!) */
1471    char const *ag_input;
1472    uz ag_ilen; /* su_cs_len() of input */
1473    uz ag_iaddr_start; /* Start of *addr-spec* in .ag_input */
1474    uz ag_iaddr_aend; /* ..and one past its end */
1475    char *ag_skinned; /* Output (alloced if !=.ag_input) */
1476    uz ag_slen; /* su_cs_len() of .ag_skinned */
1477    uz ag_sdom_start; /* Start of domain in .ag_skinned, */
1478    u32 ag_n_flags; /* enum mx_name_flags of .ag_skinned */
1479 };
1480 
1481 struct sendbundle{
1482    struct header *sb_hp;
1483    struct mx_name *sb_to;
1484    FILE *sb_input;
1485    struct mx_url *sb_urlp; /* Or NIL for file-based MTA */
1486    struct mx_cred_ctx *sb_credp; /* cred-auth.h not included */
1487    struct str sb_signer; /* USER@HOST for signing+ */
1488 };
1489 
1490 /* For saving the current directory and later returning */
1491 struct cw{
1492 #ifdef mx_HAVE_FCHDIR
1493    int cw_fd;
1494 #else
1495    char cw_wd[PATH_MAX];
1496 #endif
1497 };
1498 
1499 /*
1500  * Global variable declarations
1501  *
1502  * These become instantiated in main.c.
1503  */
1504 
1505 #undef VL
1506 #ifdef mx_SOURCE_MASTER
1507 # ifndef mx_HAVE_AMALGAMATION
1508 #  define VL
1509 # else
1510 #  define VL static
1511 # endif
1512 #else
1513 # define VL extern
1514 #endif
1515 
1516 #define n_empty su_empty
1517 #ifndef mx_HAVE_AMALGAMATION
1518 VL char const n_month_names[12 + 1][4];
1519 VL char const n_weekday_names[7 + 1][4];
1520 
1521 VL char const n_uagent[sizeof VAL_UAGENT];
1522 # ifdef mx_HAVE_UISTRINGS
1523 VL char const n_error[sizeof n_ERROR];
1524 # endif
1525 VL char const n_path_devnull[sizeof n_PATH_DEVNULL];
1526 VL char const n_0[2];
1527 VL char const n_1[2];
1528 VL char const n_m1[3]; /* -1 */
1529 VL char const n_em[2]; /* Exclamation-mark ! */
1530 VL char const n_ns[2]; /* Number sign # */
1531 VL char const n_star[2]; /* Asterisk * */
1532 VL char const n_hy[2]; /* Hyphen-Minus - */
1533 VL char const n_qm[2]; /* Question-mark ? */
1534 VL char const n_at[2]; /* Commercial at @ */
1535 #endif /* mx_HAVE_AMALGAMATION */
1536 
1537 VL FILE *n_stdin;
1538 VL FILE *n_stdout;
1539 VL FILE *n_stderr;
1540 /* XXX Plus mx_tty_fp in tty.h */
1541 /* XXX *_read_overlay and dig_msg_compose_ctx are hacks caused by missing
1542  * XXX event driven nature of individual program parts */
1543 VL void *n_readctl_read_overlay; /* `readctl' XXX HACK */
1544 
1545 VL u32 n_mb_cur_max; /* Value of MB_CUR_MAX */
1546 
1547 VL gid_t n_group_id; /* getgid() and getuid() */
1548 VL uid_t n_user_id;
1549 VL pid_t n_pid; /* getpid() (lazy initialized) */
1550 
1551 VL int n_exit_status; /* Program exit status TODO long term: ex_no */
1552 VL u32 n_poption; /* Bits of enum n_program_option */
1553 VL struct n_header_field *n_poption_arg_C; /* -C custom header list */
1554 VL char const *n_poption_arg_Mm; /* Argument for -[Mm] aka n_PO_[Mm]_FLAG */
1555 VL struct mx_name *n_poption_arg_r; /* Argument to -r option */
1556 VL char const **n_smopts; /* MTA options from command line */
1557 VL uz n_smopts_cnt; /* Entries in n_smopts */
1558 
1559 /* The current execution data context */
1560 VL struct n_go_data_ctx *n_go_data;
1561 VL u32 n_psonce; /* Bits of enum n_program_state_once */
1562 VL u32 n_pstate; /* Bits of enum n_program_state */
1563 /* TODO "cmd_tab.h ARG_EM set"-storage (n_[01..]) as long as we don't have a
1564  * TODO struct CmdCtx where each command has its own ARGC/ARGV, errno and exit
1565  * TODO status and may-place-in-history bit, need to manage global bypass.. */
1566 
1567 #ifdef mx_HAVE_ERRORS
1568 VL u32 n_pstate_err_cnt; /* What backs $^ERRQUEUE-xy */
1569 #endif
1570 
1571 /* TODO n_pstate_err_no: this should contain the error number in the lower
1572  * TODO bits, and a suberror in the high bits: offer accessor/setter macros.
1573  * TODO Like this we could use $^ERR-SUBNO or so to access these from outer
1574  * TODO space, and could perform much better testing; e.g., too many failures
1575  * TODO simply result in _INVAL, but what has it been exactly?
1576  * TODO This will furthermore allow better testing, in that even without
1577  * TODO uistrings we can test error conditions _exactly_!
1578  * TODO And change the tests accordingly, even support a mode where our
1579  * TODO error output is entirely suppressed, so that we _really_ can test
1580  * TODO and only based upon the subnumber!! */
1581 VL s32 n_pstate_err_no; /* What backs $! su_ERR_* TODO ..HACK */
1582 VL s32 n_pstate_ex_no; /* What backs $? n_EX_* TODO ..HACK ->64-bit */
1583 
1584 /* XXX stylish sorting */
1585 VL int msgCount; /* Count of messages read in */
1586 VL struct mailbox mb; /* Current mailbox */
1587 VL char mailname[PATH_MAX]; /* Name of current file TODO URL/object*/
1588 VL char displayname[80 - 16]; /* Prettyfied for display TODO URL/obj*/
1589 VL char prevfile[PATH_MAX]; /* Name of previous file TODO URL/obj */
1590 VL off_t mailsize; /* Size of system mailbox */
1591 VL struct message *dot; /* Pointer to current message */
1592 VL struct message *prevdot; /* Previous current message */
1593 VL struct message *message; /* The actual message structure */
1594 VL struct message *threadroot; /* first threaded message */
1595 /* getmsglist() 1st marked (for e.g. `Reply') HACK TODO (should be in a ctx) */
1596 VL struct message *n_msgmark1;
1597 VL int *n_msgvec; /* Folder setmsize(), list.c res. store */
1598 #ifdef mx_HAVE_IMAP
1599 VL int imap_created_mailbox; /* hack to get feedback from imap */
1600 #endif
1601 
1602 VL struct n_header_field *n_customhdr_list; /* *customhdr* list */
1603 
1604 VL struct time_current time_current; /* time(3); send: mail1() XXXcarrier */
1605 
1606 #ifdef mx_HAVE_TLS
1607 VL enum n_tls_verify_level n_tls_verify_level; /* TODO local per-context! */
1608 #endif
1609 
1610 VL volatile int interrupts; /* TODO rid! */
1611 
1612 /*
1613  * Finally, let's include the function prototypes XXX embed
1614  */
1615 
1616 #ifndef mx_SOURCE_PS_DOTLOCK_MAIN
1617 # include "mx/nailfuns.h"
1618 #endif
1619 
1620 #include "su/code-ou.h"
1621 #endif /* n_NAIL_H */
1622 /* s-it-mode */
1623