1 /**
2  * @file
3  * Command line processing
4  *
5  * @authors
6  * Copyright (C) 1996-2007,2010,2013 Michael R. Elkins <me@mutt.org>
7  * Copyright (C) 1999-2007 Thomas Roessler <roessler@does-not-exist.org>
8  * Copyright (C) 2004 g10 Code GmbH
9  * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch>
10  * Copyright (C) 2020 R Primus <rprimus@gmail.com>
11  *
12  * @copyright
13  * This program is free software: you can redistribute it and/or modify it under
14  * the terms of the GNU General Public License as published by the Free Software
15  * Foundation, either version 2 of the License, or (at your option) any later
16  * version.
17  *
18  * This program is distributed in the hope that it will be useful, but WITHOUT
19  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU General Public License along with
24  * this program.  If not, see <http://www.gnu.org/licenses/>.
25  */
26 
27 /**
28  * @mainpage Code Docs
29  *
30  * <img style="float: left; padding-right: 0.5em;" src="structs.svg">
31  * <img style="float: left; padding-right: 0.5em;" src="pages.svg">
32  * <img style="float: left; padding-right: 0.5em;" src="globals.svg">
33  * <img style="float: left; padding-right: 0.5em;" src="functions.svg">
34  * <img style="float: left; padding-right: 0.5em;" src="enums.svg">
35  * <img style="float: left; padding-right: 0.5em;" src="members.svg">
36  * <img style="float: left; padding-right: 0.5em;" src="defines.svg">
37  * <br>
38  *
39  * ## Libraries
40  *
41  * [Each library](pages.html) helps to untangle the code by grouping similar
42  * functions and reducing dependencies.
43  *
44  * The goal is that each library is:
45  * - Self-contained (it may rely on other libraries)
46  * - Independently testable (i.e. without using NeoMutt)
47  * - Fully documented
48  * - Robust
49  *
50  * Libraries:
51  * @ref lib_address, @ref lib_alias, @ref lib_attach, @ref lib_autocrypt,
52  * @ref lib_bcache, @ref lib_color, @ref lib_compmbox, @ref lib_compose,
53  * @ref lib_compress, @ref lib_config, @ref lib_conn, @ref lib_core,
54  * @ref lib_email, @ref lib_gui, @ref lib_hcache, @ref lib_helpbar,
55  * @ref lib_history, @ref lib_imap, @ref lib_index, @ref lib_maildir,
56  * @ref lib_mbox, @ref lib_menu, @ref lib_mutt, @ref lib_ncrypt, @ref lib_nntp,
57  * @ref lib_notmuch, @ref lib_pager, @ref lib_pattern, @ref lib_pop,
58  * @ref lib_progress, @ref lib_question, @ref lib_send, @ref lib_sidebar,
59  * @ref lib_store.
60  *
61  * ## Miscellaneous files
62  *
63  * These file form the main body of NeoMutt.
64  *
65  * | File            | Description                |
66  * | :-------------- | :------------------------- |
67  * | alternates.c    | @subpage neo_alternates    |
68  * | browser.c       | @subpage neo_browser       |
69  * | commands.c      | @subpage neo_commands      |
70  * | command_parse.c | @subpage neo_command_parse |
71  * | complete.c      | @subpage neo_complete      |
72  * | context.c       | @subpage neo_ctx           |
73  * | copy.c          | @subpage neo_copy          |
74  * | dlg_postpone.c  | @subpage neo_dlg_postpone  |
75  * | editmsg.c       | @subpage neo_editmsg       |
76  * | enriched.c      | @subpage neo_enriched      |
77  * | enter.c         | @subpage neo_enter         |
78  * | flags.c         | @subpage neo_flags         |
79  * | functions.c     | @subpage neo_functions     |
80  * | handler.c       | @subpage neo_handler       |
81  * | hdrline.c       | @subpage neo_hdrline       |
82  * | help.c          | @subpage neo_help          |
83  * | hook.c          | @subpage neo_hook          |
84  * | icommands.c     | @subpage neo_icommands     |
85  * | init.c          | @subpage neo_init          |
86  * | keymap.c        | @subpage neo_keymap        |
87  * | mailcap.c       | @subpage neo_mailcap       |
88  * | maillist.c      | @subpage neo_maillist      |
89  * | main.c          | @subpage neo_main          |
90  * | monitor.c       | @subpage neo_monitor       |
91  * | muttlib.c       | @subpage neo_muttlib       |
92  * | mutt_account.c  | @subpage neo_mutt_account  |
93  * | mutt_body.c     | @subpage neo_mutt_body     |
94  * | mutt_commands.c | @subpage neo_mutt_commands |
95  * | mutt_config.c   | @subpage neo_mutt_config   |
96  * | mutt_globals.h  | @subpage neo_mutt_globals  |
97  * | mutt_header.c   | @subpage neo_mutt_header   |
98  * | mutt_history.c  | @subpage neo_mutt_history  |
99  * | mutt_logging.c  | @subpage neo_mutt_logging  |
100  * | mutt_lua.c      | @subpage neo_mutt_lua      |
101  * | mutt_mailbox.c  | @subpage neo_mutt_mailbox  |
102  * | mutt_signal.c   | @subpage neo_mutt_signal   |
103  * | mutt_socket.c   | @subpage neo_mutt_socket   |
104  * | mutt_thread.c   | @subpage neo_mutt_thread   |
105  * | mx.c            | @subpage neo_mx            |
106  * | myvar.c         | @subpage neo_myvar         |
107  * | opcodes.c       | @subpage neo_opcode        |
108  * | options.h       | @subpage neo_options       |
109  * | postpone.c      | @subpage neo_postpone      |
110  * | recvcmd.c       | @subpage neo_recvcmd       |
111  * | remailer.c      | @subpage neo_remailer      |
112  * | resize.c        | @subpage neo_resize        |
113  * | rfc3676.c       | @subpage neo_rfc3676       |
114  * | score.c         | @subpage neo_score         |
115  * | sort.c          | @subpage neo_sort          |
116  * | status.c        | @subpage neo_status        |
117  * | subjectrx.c     | @subpage neo_subjrx        |
118  * | system.c        | @subpage neo_system        |
119  * | version.c       | @subpage neo_version       |
120  * | wcscasecmp.c    | @subpage neo_wcscasecmp    |
121  *
122  * ## Building these Docs
123  *
124  * The config for building the docs is in the main source repo.
125  *
126  * Everything possible is turned on in the config file, so you'll need to
127  * install a few dependencies like `dot` from the graphviz package.
128  *
129  * ## Installing the Docs
130  *
131  * These docs aren't in the main website repo -- they weigh in at 100MB.
132  * Instead, they're stored in the [code repo](https://github.com/neomutt/code)
133  */
134 
135 /**
136  * @page neo_main Command line processing
137  *
138  * Command line processing
139  */
140 
141 #define MAIN_C 1
142 #define GNULIB_defined_setlocale
143 
144 #include "config.h"
145 #include <errno.h>
146 #include <getopt.h>
147 #include <limits.h>
148 #include <locale.h>
149 #include <pwd.h>
150 #include <stdbool.h>
151 #include <stdint.h>
152 #include <stdio.h>
153 #include <stdlib.h>
154 #include <string.h>
155 #include <sys/stat.h>
156 #include <unistd.h>
157 #include "mutt/lib.h"
158 #include "address/lib.h"
159 #include "config/lib.h"
160 #include "email/lib.h"
161 #include "core/lib.h"
162 #include "alias/lib.h"
163 #include "conn/lib.h"
164 #include "gui/lib.h"
165 #include "attach/lib.h"
166 #include "color/lib.h"
167 #include "index/lib.h"
168 #include "menu/lib.h"
169 #include "ncrypt/lib.h"
170 #include "question/lib.h"
171 #include "send/lib.h"
172 #include "alternates.h"
173 #include "browser.h"
174 #include "commands.h"
175 #include "context.h"
176 #include "hook.h"
177 #include "init.h"
178 #include "keymap.h"
179 #include "mutt_globals.h"
180 #include "mutt_history.h"
181 #include "mutt_logging.h"
182 #include "mutt_mailbox.h"
183 #include "muttlib.h"
184 #include "mx.h"
185 #include "myvar.h"
186 #include "options.h"
187 #include "protos.h"
188 #include "subjectrx.h"
189 #include "version.h"
190 #ifdef ENABLE_NLS
191 #include <libintl.h>
192 #endif
193 #ifdef USE_IMAP
194 #include "imap/lib.h"
195 #endif
196 #ifdef USE_POP
197 #include "pop/lib.h"
198 #endif
199 #ifdef USE_NNTP
200 #include "nntp/lib.h"
201 #include "nntp/adata.h" // IWYU pragma: keep
202 #include "nntp/mdata.h" // IWYU pragma: keep
203 #endif
204 #ifdef USE_AUTOCRYPT
205 #include "autocrypt/lib.h"
206 #endif
207 #if defined(USE_DEBUG_NOTIFY) || defined(HAVE_LIBUNWIND)
208 #include "debug/lib.h"
209 #endif
210 
211 // clang-format off
212 typedef uint8_t CliFlags;         ///< Flags for command line options, e.g. #MUTT_CLI_IGNORE
213 #define MUTT_CLI_NO_FLAGS      0  ///< No flags are set
214 #define MUTT_CLI_IGNORE  (1 << 0) ///< -z Open first mailbox if it has mail
215 #define MUTT_CLI_MAILBOX (1 << 1) ///< -Z Open first mailbox if is has new mail
216 #define MUTT_CLI_NOSYSRC (1 << 2) ///< -n Do not read the system-wide config file
217 #define MUTT_CLI_RO      (1 << 3) ///< -R Open mailbox in read-only mode
218 #define MUTT_CLI_SELECT  (1 << 4) ///< -y Start with a list of all mailboxes
219 #ifdef USE_NNTP
220 #define MUTT_CLI_NEWS    (1 << 5) ///< -g/-G Start with a list of all newsgroups
221 #endif
222 // clang-format on
223 
224 /**
225  * reset_tilde - Temporary measure
226  * @param cs Config Set
227  */
reset_tilde(struct ConfigSet * cs)228 static void reset_tilde(struct ConfigSet *cs)
229 {
230   static const char *names[] = { "folder", "mbox", "postponed", "record" };
231 
232   struct Buffer value = mutt_buffer_make(256);
233   for (size_t i = 0; i < mutt_array_size(names); i++)
234   {
235     struct HashElem *he = cs_get_elem(cs, names[i]);
236     if (!he)
237       continue;
238     mutt_buffer_reset(&value);
239     cs_he_initial_get(cs, he, &value);
240     mutt_buffer_expand_path_regex(&value, false);
241     cs_he_initial_set(cs, he, value.data, NULL);
242     cs_he_reset(cs, he, NULL);
243   }
244   mutt_buffer_dealloc(&value);
245 }
246 
247 /**
248  * mutt_exit - Leave NeoMutt NOW
249  * @param code Value to return to the calling environment
250  */
mutt_exit(int code)251 void mutt_exit(int code)
252 {
253   mutt_endwin();
254 #ifdef HAVE_LIBUNWIND
255   if (code != 0)
256     show_backtrace();
257 #endif
258   exit(code);
259 }
260 
261 /**
262  * usage - Display NeoMutt command line
263  * @retval true Text displayed
264  */
usage(void)265 static bool usage(void)
266 {
267   puts(mutt_make_version());
268 
269   // clang-format off
270   /* L10N: Try to limit to 80 columns */
271   puts(_("usage:"));
272   puts(_("  neomutt [-Enx] [-e <command>] [-F <config>] [-H <draft>] [-i <include>]\n"
273          "          [-b <address>] [-c <address>] [-s <subject>] [-a <file> [...] --]\n"
274          "          <address> [...]"));
275   puts(_("  neomutt [-nx] [-e <command>] [-F <config>] [-b <address>] [-c <address>]\n"
276          "          [-s <subject>] [-a <file> [...] --] <address> [...] < message"));
277   puts(_("  neomutt [-nRy] [-e <command>] [-F <config>] [-f <mailbox>] [-m <type>]"));
278   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -A <alias>"));
279   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -B"));
280   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -D [-S] [-O]"));
281   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -d <level> -l <file>"));
282   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -G"));
283   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -g <server>"));
284   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -p"));
285   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -Q <variable> [-O]"));
286   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -Z"));
287   puts(_("  neomutt [-n] [-e <command>] [-F <config>] -z [-f <mailbox>]"));
288   puts(_("  neomutt -v[v]\n"));
289 
290   /* L10N: Try to limit to 80 columns.  If more space is needed add an indented line */
291   puts(_("options:"));
292   puts(_("  --            Special argument forces NeoMutt to stop option parsing and treat\n"
293          "                remaining arguments as addresses even if they start with a dash"));
294   puts(_("  -A <alias>    Print an expanded version of the given alias to stdout and exit"));
295   puts(_("  -a <file>     Attach one or more files to a message (must be the last option)\n"
296          "                Add any addresses after the '--' argument"));
297   puts(_("  -B            Run in batch mode (do not start the ncurses UI)"));
298   puts(_("  -b <address>  Specify a blind carbon copy (Bcc) recipient"));
299   puts(_("  -c <address>  Specify a carbon copy (Cc) recipient"));
300   puts(_("  -D            Dump all config variables as 'name=value' pairs to stdout"));
301   puts(_("  -D -O         Like -D, but show one-liner documentation"));
302   puts(_("  -D -S         Like -D, but hide the value of sensitive variables"));
303   puts(_("  -d <level>    Log debugging output to a file (default is \"~/.neomuttdebug0\")\n"
304          "                The level can range from 1-5 and affects verbosity"));
305   puts(_("  -E            Edit draft (-H) or include (-i) file during message composition"));
306   puts(_("  -e <command>  Specify a command to be run after reading the config files"));
307   puts(_("  -F <config>   Specify an alternative initialization file to read"));
308   puts(_("  -f <mailbox>  Specify a mailbox (as defined with 'mailboxes' command) to load"));
309   puts(_("  -G            Start NeoMutt with a listing of subscribed newsgroups"));
310   puts(_("  -g <server>   Like -G, but start at specified news server"));
311   puts(_("  -H <draft>    Specify a draft file with header and body for message composing"));
312   puts(_("  -h            Print this help message and exit"));
313   puts(_("  -i <include>  Specify an include file to be embedded in the body of a message"));
314   puts(_("  -l <file>     Specify a file for debugging output (default \"~/.neomuttdebug0\")"));
315   puts(_("  -m <type>     Specify a default mailbox format type for newly created folders\n"
316          "                The type is either MH, MMDF, Maildir or mbox (case-insensitive)"));
317   puts(_("  -n            Do not read the system-wide configuration file"));
318   puts(_("  -p            Resume a prior postponed message, if any"));
319   puts(_("  -Q <variable> Query a configuration variable and print its value to stdout\n"
320          "                (after the config has been read and any commands executed)\n"
321          "                Add -O for one-liner documentation"));
322   puts(_("  -R            Open mailbox in read-only mode"));
323   puts(_("  -s <subject>  Specify a subject (must be enclosed in quotes if it has spaces)"));
324   puts(_("  -v            Print the NeoMutt version and compile-time definitions and exit"));
325   puts(_("  -vv           Print the NeoMutt license and copyright information and exit"));
326   puts(_("  -y            Start NeoMutt with a listing of all defined mailboxes"));
327   puts(_("  -Z            Open the first mailbox with new message or exit immediately with\n"
328          "                exit code 1 if none is found in all defined mailboxes"));
329   puts(_("  -z            Open the first or specified (-f) mailbox if it holds any message\n"
330          "                or exit immediately with exit code 1 otherwise"));
331   // clang-format on
332 
333   fflush(stdout);
334   return !ferror(stdout);
335 }
336 
337 /**
338  * start_curses - Start the Curses UI
339  * @retval 0 Success
340  * @retval 1 Failure
341  */
start_curses(void)342 static int start_curses(void)
343 {
344   km_init(); /* must come before mutt_init */
345 
346   /* should come before initscr() so that ncurses 4.2 doesn't try to install
347    * its own SIGWINCH handler */
348   mutt_signal_init();
349 
350   if (!initscr())
351   {
352     mutt_error(_("Error initializing terminal"));
353     return 1;
354   }
355   mutt_signal_init();
356   mutt_colors_init();
357   keypad(stdscr, true);
358   cbreak();
359   noecho();
360   nonl();
361   typeahead(-1); /* simulate smooth scrolling */
362   meta(stdscr, true);
363   init_extended_keys();
364   /* Now that curses is set up, we drop back to normal screen mode.
365    * This simplifies displaying error messages to the user.
366    * The first call to refresh() will swap us back to curses screen mode. */
367   endwin();
368   return 0;
369 }
370 
371 /**
372  * init_locale - Initialise the Locale/NLS settings
373  */
init_locale(void)374 static void init_locale(void)
375 {
376   setlocale(LC_ALL, "");
377 
378 #ifdef ENABLE_NLS
379   const char *domdir = mutt_str_getenv("TEXTDOMAINDIR");
380   if (domdir)
381     bindtextdomain(PACKAGE, domdir);
382   else
383     bindtextdomain(PACKAGE, MUTTLOCALEDIR);
384   textdomain(PACKAGE);
385 #endif
386 #ifndef LOCALES_HACK
387   /* Do we have a locale definition? */
388   if (mutt_str_getenv("LC_ALL") || mutt_str_getenv("LANG") || mutt_str_getenv("LC_CTYPE"))
389   {
390     OptLocales = true;
391   }
392 #endif
393 }
394 
395 /**
396  * get_user_info - Find the user's name, home and shell
397  * @param cs Config Set
398  * @retval true Success
399  *
400  * Find the login name, real name, home directory and shell.
401  */
get_user_info(struct ConfigSet * cs)402 static bool get_user_info(struct ConfigSet *cs)
403 {
404   const char *shell = mutt_str_getenv("SHELL");
405   if (shell)
406     cs_str_initial_set(cs, "shell", shell, NULL);
407 
408   /* Get some information about the user */
409   struct passwd *pw = getpwuid(getuid());
410   if (pw)
411   {
412     if (!Username)
413       Username = mutt_str_dup(pw->pw_name);
414     if (!HomeDir)
415       HomeDir = mutt_str_dup(pw->pw_dir);
416     if (!shell)
417       cs_str_initial_set(cs, "shell", pw->pw_shell, NULL);
418   }
419 
420   if (!Username)
421   {
422     mutt_error(_("unable to determine username"));
423     return false; // TEST05: neomutt (unset $USER, delete user from /etc/passwd)
424   }
425 
426   if (!HomeDir)
427   {
428     mutt_error(_("unable to determine home directory"));
429     return false; // TEST06: neomutt (unset $HOME, delete user from /etc/passwd)
430   }
431 
432   cs_str_reset(cs, "shell", NULL);
433   return true;
434 }
435 
436 /**
437  * log_translation - Log the translation being used
438  *
439  * Read the header info from the translation file.
440  *
441  * @note Call bindtextdomain() first
442  */
log_translation(void)443 static void log_translation(void)
444 {
445   const char *header = ""; // Do not merge these two lines
446   header = _(header);      // otherwise the .po files will end up badly ordered
447   const char *lang = strcasestr(header, "Language:");
448   int len = 64;
449   if (lang)
450   {
451     lang += 9; // skip label
452     SKIPWS(lang);
453     char *nl = strchr(lang, '\n');
454     if (nl)
455       len = (nl - lang);
456   }
457   else
458   {
459     lang = "NONE";
460   }
461 
462   mutt_debug(LL_DEBUG1, "Translation: %.*s\n", len, lang);
463 }
464 
465 /**
466  * main - Start NeoMutt
467  * @param argc Number of command line arguments
468  * @param argv List of command line arguments
469  * @param envp Copy of the environment
470  * @retval 0 Success
471  * @retval 1 Error
472  */
main(int argc,char * argv[],char * envp[])473 int main(int argc, char *argv[], char *envp[])
474 {
475   char *subject = NULL;
476   char *include_file = NULL;
477   char *draft_file = NULL;
478   char *new_type = NULL;
479   char *dlevel = NULL;
480   char *dfile = NULL;
481 #ifdef USE_NNTP
482   const char *cli_nntp = NULL;
483 #endif
484   struct Email *e = NULL;
485   struct ListHead attach = STAILQ_HEAD_INITIALIZER(attach);
486   struct ListHead commands = STAILQ_HEAD_INITIALIZER(commands);
487   struct ListHead queries = STAILQ_HEAD_INITIALIZER(queries);
488   struct ListHead alias_queries = STAILQ_HEAD_INITIALIZER(alias_queries);
489   struct ListHead cc_list = STAILQ_HEAD_INITIALIZER(cc_list);
490   struct ListHead bcc_list = STAILQ_HEAD_INITIALIZER(bcc_list);
491   SendFlags sendflags = SEND_NO_FLAGS;
492   CliFlags flags = MUTT_CLI_NO_FLAGS;
493   int version = 0;
494   int i;
495   bool explicit_folder = false;
496   bool dump_variables = false;
497   bool one_liner = false;
498   bool hide_sensitive = false;
499   bool batch_mode = false;
500   bool edit_infile = false;
501 #ifdef USE_DEBUG_PARSE_TEST
502   bool test_config = false;
503 #endif
504   int double_dash = argc, nargc = 1;
505   int rc = 1;
506   bool repeat_error = false;
507   struct Buffer folder = mutt_buffer_make(0);
508   struct Buffer expanded_infile = mutt_buffer_make(0);
509   struct Buffer tempfile = mutt_buffer_make(0);
510   struct ConfigSet *cs = NULL;
511 
512   MuttLogger = log_disp_terminal;
513 
514   /* sanity check against stupid administrators */
515   if (getegid() != getgid())
516   {
517     mutt_error("%s: I don't want to run with privileges!", argv[0]);
518     goto main_exit; // TEST01: neomutt (as root, chgrp mail neomutt; chmod +s neomutt)
519   }
520 
521   init_locale();
522 
523   umask(077);
524 
525   mutt_envlist_init(envp);
526   for (optind = 1; optind < double_dash;)
527   {
528     /* We're getopt'ing POSIXLY, so we'll be here every time getopt()
529      * encounters a non-option.  That could be a file to attach
530      * (all non-options between -a and --) or it could be an address
531      * (which gets collapsed to the front of argv).  */
532     for (; optind < argc; optind++)
533     {
534       if ((argv[optind][0] == '-') && (argv[optind][1] != '\0'))
535       {
536         if ((argv[optind][1] == '-') && (argv[optind][2] == '\0'))
537           double_dash = optind; /* quit outer loop after getopt */
538         break;                  /* drop through to getopt */
539       }
540 
541       /* non-option, either an attachment or address */
542       if (!STAILQ_EMPTY(&attach))
543         mutt_list_insert_tail(&attach, mutt_str_dup(argv[optind]));
544       else
545         argv[nargc++] = argv[optind];
546     }
547 
548     /* USE_NNTP 'g:G' */
549     i = getopt(argc, argv, "+A:a:Bb:F:f:c:Dd:l:Ee:g:GH:i:hm:nOpQ:RSs:TvxyzZ");
550     if (i != EOF)
551     {
552       switch (i)
553       {
554         case 'A':
555           mutt_list_insert_tail(&alias_queries, mutt_str_dup(optarg));
556           break;
557         case 'a':
558           mutt_list_insert_tail(&attach, mutt_str_dup(optarg));
559           break;
560         case 'B':
561           batch_mode = true;
562           break;
563         case 'b':
564           mutt_list_insert_tail(&bcc_list, mutt_str_dup(optarg));
565           break;
566         case 'c':
567           mutt_list_insert_tail(&cc_list, mutt_str_dup(optarg));
568           break;
569         case 'D':
570           dump_variables = true;
571           break;
572         case 'd':
573           dlevel = optarg;
574           break;
575         case 'E':
576           edit_infile = true;
577           break;
578         case 'e':
579           mutt_list_insert_tail(&commands, mutt_str_dup(optarg));
580           break;
581         case 'F':
582           mutt_list_insert_tail(&Muttrc, mutt_str_dup(optarg));
583           break;
584         case 'f':
585           mutt_buffer_strcpy(&folder, optarg);
586           explicit_folder = true;
587           break;
588 #ifdef USE_NNTP
589         case 'g': /* Specify a news server */
590           cli_nntp = optarg;
591           /* fallthrough */
592         case 'G': /* List of newsgroups */
593           flags |= MUTT_CLI_SELECT | MUTT_CLI_NEWS;
594           break;
595 #endif
596         case 'H':
597           draft_file = optarg;
598           break;
599         case 'i':
600           include_file = optarg;
601           break;
602         case 'l':
603           dfile = optarg;
604           break;
605         case 'm':
606           new_type = optarg;
607           break;
608         case 'n':
609           flags |= MUTT_CLI_NOSYSRC;
610           break;
611         case 'O':
612           one_liner = true;
613           break;
614         case 'p':
615           sendflags |= SEND_POSTPONED;
616           break;
617         case 'Q':
618           mutt_list_insert_tail(&queries, mutt_str_dup(optarg));
619           break;
620         case 'R':
621           flags |= MUTT_CLI_RO; /* read-only mode */
622           break;
623         case 'S':
624           hide_sensitive = true;
625           break;
626         case 's':
627           subject = optarg;
628           break;
629 #ifdef USE_DEBUG_PARSE_TEST
630         case 'T':
631           test_config = true;
632           break;
633 #endif
634         case 'v':
635           version++;
636           break;
637         case 'y': /* My special hack mode */
638           flags |= MUTT_CLI_SELECT;
639           break;
640         case 'Z':
641           flags |= MUTT_CLI_MAILBOX | MUTT_CLI_IGNORE;
642           break;
643         case 'z':
644           flags |= MUTT_CLI_IGNORE;
645           break;
646         default:
647           OptNoCurses = true;
648           if (usage())
649             goto main_ok; // TEST03: neomutt -9
650           else
651             goto main_curses;
652       }
653     }
654   }
655 
656   /* collapse remaining argv */
657   while (optind < argc)
658     argv[nargc++] = argv[optind++];
659   optind = 1;
660   argc = nargc;
661 
662   if (version > 0)
663   {
664     log_queue_flush(log_disp_terminal);
665     bool done;
666     if (version == 1)
667       done = print_version(stdout);
668     else
669       done = print_copyright();
670     OptNoCurses = true;
671     if (done)
672       goto main_ok; // TEST04: neomutt -v
673     else
674       goto main_curses;
675   }
676 
677   mutt_str_replace(&Username, mutt_str_getenv("USER"));
678   mutt_str_replace(&HomeDir, mutt_str_getenv("HOME"));
679 
680   cs = cs_new(500);
681   if (!cs)
682     goto main_curses;
683 
684   NeoMutt = neomutt_new(cs);
685   init_config(cs);
686   subjrx_init();
687   attach_init();
688   alternates_init();
689 
690 #ifdef USE_DEBUG_NOTIFY
691   notify_observer_add(NeoMutt->notify, NT_ALL, debug_all_observer, NULL);
692 #endif
693 
694   if (!get_user_info(cs))
695     goto main_exit;
696 
697 #ifdef USE_DEBUG_PARSE_TEST
698   if (test_config)
699   {
700     cs_str_initial_set(cs, "from", "rich@flatcap.org", NULL);
701     cs_str_reset(cs, "from", NULL);
702     myvar_set("my_var", "foo");
703     test_parse_set();
704     goto main_ok;
705   }
706 #endif
707 
708   reset_tilde(cs);
709 
710   if (dfile)
711   {
712     cs_str_initial_set(cs, "debug_file", dfile, NULL);
713     cs_str_reset(cs, "debug_file", NULL);
714   }
715 
716   if (dlevel)
717   {
718     short num = 0;
719     if ((mutt_str_atos(dlevel, &num) < 0) || (num < LL_MESSAGE) || (num >= LL_MAX))
720     {
721       mutt_error(_("Error: value '%s' is invalid for -d"), dlevel);
722       goto main_exit; // TEST07: neomutt -d xyz
723     }
724     cs_str_initial_set(cs, "debug_level", dlevel, NULL);
725     cs_str_reset(cs, "debug_level", NULL);
726   }
727 
728   mutt_log_prep();
729   if (dlevel)
730     mutt_log_start();
731 
732   MuttLogger = log_disp_queue;
733 
734   log_translation();
735 
736   if (!STAILQ_EMPTY(&cc_list) || !STAILQ_EMPTY(&bcc_list))
737   {
738     e = email_new();
739     e->env = mutt_env_new();
740 
741     struct ListNode *np = NULL;
742     STAILQ_FOREACH(np, &bcc_list, entries)
743     {
744       mutt_addrlist_parse(&e->env->bcc, np->data);
745     }
746 
747     STAILQ_FOREACH(np, &cc_list, entries)
748     {
749       mutt_addrlist_parse(&e->env->cc, np->data);
750     }
751 
752     mutt_list_free(&bcc_list);
753     mutt_list_free(&cc_list);
754   }
755 
756   /* Check for a batch send. */
757   if (!isatty(0) || !STAILQ_EMPTY(&queries) || !STAILQ_EMPTY(&alias_queries) ||
758       dump_variables || batch_mode)
759   {
760     OptNoCurses = true;
761     sendflags = SEND_BATCH;
762     MuttLogger = log_disp_terminal;
763     log_queue_flush(log_disp_terminal);
764   }
765 
766   /* Check to make sure stdout is available in curses mode. */
767   if (!OptNoCurses && !isatty(1))
768     goto main_curses;
769 
770   /* Always create the mutt_windows because batch mode has some shared code
771    * paths that end up referencing them. */
772   rootwin_new();
773 
774   /* This must come before mutt_init() because curses needs to be started
775    * before calling the init_pair() function to set the color scheme.  */
776   if (!OptNoCurses)
777   {
778     int crc = start_curses();
779     if (crc != 0)
780       goto main_curses; // TEST08: can't test -- fake term?
781 
782     /* check whether terminal status is supported (must follow curses init) */
783     TsSupported = mutt_ts_capability();
784     rootwin_set_size(COLS, LINES);
785   }
786 
787   /* set defaults and read init files */
788   int rc2 = mutt_init(cs, flags & MUTT_CLI_NOSYSRC, &commands);
789   mutt_list_free(&commands);
790   if (rc2 != 0)
791     goto main_curses;
792 
793   mutt_init_abort_key();
794 
795   /* The command line overrides the config */
796   if (dlevel)
797     cs_str_reset(cs, "debug_level", NULL);
798   if (dfile)
799     cs_str_reset(cs, "debug_file", NULL);
800 
801   if (mutt_log_start() < 0)
802   {
803     mutt_perror("log file");
804     goto main_exit;
805   }
806 
807 #ifdef USE_NNTP
808   {
809     /* "$news_server" precedence: command line, config file, environment, system file */
810     if (!cli_nntp)
811       cli_nntp = cs_subset_string(NeoMutt->sub, "news_server");
812 
813     if (!cli_nntp)
814       cli_nntp = mutt_str_getenv("NNTPSERVER");
815 
816     char buf[1024] = { 0 };
817     if (!cli_nntp)
818       cli_nntp = mutt_file_read_keyword(SYSCONFDIR "/nntpserver", buf, sizeof(buf));
819 
820     if (cli_nntp)
821     {
822       cs_str_initial_set(cs, "news_server", cli_nntp, NULL);
823       cs_str_reset(cs, "news_server", NULL);
824     }
825   }
826 #endif
827 
828   /* Initialize crypto backends.  */
829   crypt_init();
830 
831   if (new_type)
832   {
833     struct Buffer err = mutt_buffer_make(0);
834     int r = cs_str_initial_set(cs, "mbox_type", new_type, &err);
835     if (CSR_RESULT(r) != CSR_SUCCESS)
836     {
837       mutt_error(err.data);
838       mutt_buffer_dealloc(&err);
839       goto main_curses;
840     }
841     cs_str_reset(cs, "mbox_type", NULL);
842   }
843 
844   if (!STAILQ_EMPTY(&queries))
845   {
846     rc = mutt_query_variables(&queries, one_liner);
847     goto main_curses;
848   }
849 
850   if (dump_variables)
851   {
852     ConfigDumpFlags cdflags = CS_DUMP_NO_FLAGS;
853     if (hide_sensitive)
854       cdflags |= CS_DUMP_HIDE_SENSITIVE;
855     if (one_liner)
856       cdflags |= CS_DUMP_SHOW_DOCS;
857     dump_config(cs, cdflags, stdout);
858     goto main_ok; // TEST18: neomutt -D
859   }
860 
861   if (!STAILQ_EMPTY(&alias_queries))
862   {
863     rc = 0;
864     for (; optind < argc; optind++)
865       mutt_list_insert_tail(&alias_queries, mutt_str_dup(argv[optind]));
866     struct ListNode *np = NULL;
867     STAILQ_FOREACH(np, &alias_queries, entries)
868     {
869       struct AddressList *al = alias_lookup(np->data);
870       if (al)
871       {
872         /* output in machine-readable form */
873         mutt_addrlist_to_intl(al, NULL);
874         mutt_addrlist_write_file(al, stdout, 0, false);
875       }
876       else
877       {
878         rc = 1;
879         printf("%s\n", np->data); // TEST19: neomutt -A unknown
880       }
881     }
882     mutt_list_free(&alias_queries);
883     goto main_curses; // TEST20: neomutt -A alias
884   }
885 
886   if (!OptNoCurses)
887   {
888     mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
889     clear();
890     MuttLogger = log_disp_curses;
891     log_queue_flush(log_disp_curses);
892     log_queue_set_max_size(100);
893   }
894 
895 #ifdef USE_AUTOCRYPT
896   /* Initialize autocrypt after curses messages are working,
897    * because of the initial account setup screens. */
898   const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
899   if (c_autocrypt)
900     mutt_autocrypt_init(!(sendflags & SEND_BATCH));
901 #endif
902 
903   /* Create the `$folder` directory if it doesn't exist. */
904   const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
905   if (!OptNoCurses && c_folder)
906   {
907     struct stat st = { 0 };
908     struct Buffer *fpath = mutt_buffer_pool_get();
909 
910     mutt_buffer_strcpy(fpath, c_folder);
911     mutt_buffer_expand_path(fpath);
912     bool skip = false;
913 #ifdef USE_IMAP
914     /* we're not connected yet - skip mail folder creation */
915     skip |= (imap_path_probe(mutt_buffer_string(fpath), NULL) == MUTT_IMAP);
916 #endif
917 #ifdef USE_POP
918     skip |= (pop_path_probe(mutt_buffer_string(fpath), NULL) == MUTT_POP);
919 #endif
920 #ifdef USE_NNTP
921     skip |= (nntp_path_probe(mutt_buffer_string(fpath), NULL) == MUTT_NNTP);
922 #endif
923     if (!skip && (stat(mutt_buffer_string(fpath), &st) == -1) && (errno == ENOENT))
924     {
925       char msg2[256];
926       snprintf(msg2, sizeof(msg2), _("%s does not exist. Create it?"), c_folder);
927       if (mutt_yesorno(msg2, MUTT_YES) == MUTT_YES)
928       {
929         if ((mkdir(mutt_buffer_string(fpath), 0700) == -1) && (errno != EEXIST))
930           mutt_error(_("Can't create %s: %s"), c_folder, strerror(errno)); // TEST21: neomutt -n -F /dev/null (and ~/Mail doesn't exist)
931       }
932     }
933     mutt_buffer_pool_release(&fpath);
934   }
935 
936   if (batch_mode)
937   {
938     goto main_ok; // TEST22: neomutt -B
939   }
940 
941   notify_observer_add(NeoMutt->notify, NT_CONFIG, main_hist_observer, NULL);
942   notify_observer_add(NeoMutt->notify, NT_CONFIG, main_log_observer, NULL);
943   notify_observer_add(NeoMutt->notify, NT_CONFIG, main_config_observer, NULL);
944 
945   if (sendflags & SEND_POSTPONED)
946   {
947     if (!OptNoCurses)
948       mutt_flushinp();
949     if (mutt_send_message(SEND_POSTPONED, NULL, NULL, NULL, NULL, NeoMutt->sub) == 0)
950       rc = 0;
951     // TEST23: neomutt -p (postponed message, cancel)
952     // TEST24: neomutt -p (no postponed message)
953     log_queue_empty();
954     repeat_error = true;
955     goto main_curses;
956   }
957   else if (subject || e || draft_file || include_file ||
958            !STAILQ_EMPTY(&attach) || (optind < argc))
959   {
960     FILE *fp_in = NULL;
961     FILE *fp_out = NULL;
962     char *infile = NULL;
963     char *bodytext = NULL;
964     const char *bodyfile = NULL;
965     int rv = 0;
966 
967     if (!OptNoCurses)
968       mutt_flushinp();
969 
970     if (!e)
971       e = email_new();
972     if (!e->env)
973       e->env = mutt_env_new();
974 
975     for (i = optind; i < argc; i++)
976     {
977       if (url_check_scheme(argv[i]) == U_MAILTO)
978       {
979         if (!mutt_parse_mailto(e->env, &bodytext, argv[i]))
980         {
981           mutt_error(_("Failed to parse mailto: link"));
982           email_free(&e);
983           goto main_curses; // TEST25: neomutt mailto:?
984         }
985       }
986       else
987         mutt_addrlist_parse(&e->env->to, argv[i]);
988     }
989 
990     const bool c_auto_edit = cs_subset_bool(NeoMutt->sub, "auto_edit");
991     if (!draft_file && c_auto_edit && TAILQ_EMPTY(&e->env->to) &&
992         TAILQ_EMPTY(&e->env->cc))
993     {
994       mutt_error(_("No recipients specified"));
995       email_free(&e);
996       goto main_curses; // TEST26: neomutt -s test (with auto_edit=yes)
997     }
998 
999     if (subject)
1000       e->env->subject = mutt_str_dup(subject);
1001 
1002     if (draft_file)
1003     {
1004       infile = draft_file;
1005       include_file = NULL;
1006     }
1007     else if (include_file)
1008       infile = include_file;
1009     else
1010       edit_infile = false;
1011 
1012     if (infile || bodytext)
1013     {
1014       /* Prepare fp_in and expanded_infile. */
1015       if (infile)
1016       {
1017         if (mutt_str_equal("-", infile))
1018         {
1019           if (edit_infile)
1020           {
1021             mutt_error(_("Can't use -E flag with stdin"));
1022             email_free(&e);
1023             goto main_curses; // TEST27: neomutt -E -H -
1024           }
1025           fp_in = stdin;
1026         }
1027         else
1028         {
1029           mutt_buffer_strcpy(&expanded_infile, infile);
1030           mutt_buffer_expand_path(&expanded_infile);
1031           fp_in = fopen(mutt_buffer_string(&expanded_infile), "r");
1032           if (!fp_in)
1033           {
1034             mutt_perror(mutt_buffer_string(&expanded_infile));
1035             email_free(&e);
1036             goto main_curses; // TEST28: neomutt -E -H missing
1037           }
1038         }
1039       }
1040 
1041       /* Copy input to a tempfile, and re-point fp_in to the tempfile.
1042        * Note: stdin is always copied to a tempfile, ensuring draft_file
1043        * can stat and get the correct st_size below.  */
1044       if (!edit_infile)
1045       {
1046         mutt_buffer_mktemp(&tempfile);
1047 
1048         fp_out = mutt_file_fopen(mutt_buffer_string(&tempfile), "w");
1049         if (!fp_out)
1050         {
1051           mutt_file_fclose(&fp_in);
1052           mutt_perror(mutt_buffer_string(&tempfile));
1053           email_free(&e);
1054           goto main_curses; // TEST29: neomutt -H existing-file (where tmpdir=/path/to/FILE blocking tmpdir)
1055         }
1056         if (fp_in)
1057         {
1058           mutt_file_copy_stream(fp_in, fp_out);
1059           if (fp_in != stdin)
1060             mutt_file_fclose(&fp_in);
1061         }
1062         else if (bodytext)
1063           fputs(bodytext, fp_out);
1064         mutt_file_fclose(&fp_out);
1065 
1066         fp_in = fopen(mutt_buffer_string(&tempfile), "r");
1067         if (!fp_in)
1068         {
1069           mutt_perror(mutt_buffer_string(&tempfile));
1070           email_free(&e);
1071           goto main_curses; // TEST30: can't test
1072         }
1073       }
1074       /* If editing the infile, keep it around afterwards so
1075        * it doesn't get unlinked, and we can rebuild the draft_file */
1076       else
1077         sendflags |= SEND_NO_FREE_HEADER;
1078 
1079       /* Parse the draft_file into the full Email/Body structure.
1080        * Set SEND_DRAFT_FILE so mutt_send_message doesn't overwrite
1081        * our e->body.  */
1082       if (draft_file)
1083       {
1084         struct Envelope *opts_env = e->env;
1085         struct stat st = { 0 };
1086 
1087         sendflags |= SEND_DRAFT_FILE;
1088 
1089         /* Set up a tmp Email with just enough information so that
1090          * mutt_prepare_template() can parse the message in fp_in.  */
1091         struct Email *e_tmp = email_new();
1092         e_tmp->offset = 0;
1093         e_tmp->body = mutt_body_new();
1094         if (fstat(fileno(fp_in), &st) != 0)
1095         {
1096           mutt_perror(draft_file);
1097           email_free(&e);
1098           email_free(&e_tmp);
1099           goto main_curses; // TEST31: can't test
1100         }
1101         e_tmp->body->length = st.st_size;
1102 
1103         if (mutt_prepare_template(fp_in, NULL, e, e_tmp, false) < 0)
1104         {
1105           mutt_error(_("Can't parse message template: %s"), draft_file);
1106           email_free(&e);
1107           email_free(&e_tmp);
1108           goto main_curses;
1109         }
1110 
1111         /* Scan for neomutt header to set `$resume_draft_files` */
1112         struct ListNode *np = NULL, *tmp = NULL;
1113         STAILQ_FOREACH_SAFE(np, &e->env->userhdrs, entries, tmp)
1114         {
1115           if (mutt_istr_startswith(np->data, "X-Mutt-Resume-Draft:"))
1116           {
1117             const bool c_resume_edited_draft_files =
1118                 cs_subset_bool(NeoMutt->sub, "resume_edited_draft_files");
1119             if (c_resume_edited_draft_files)
1120               cs_str_native_set(cs, "resume_draft_files", true, NULL);
1121 
1122             STAILQ_REMOVE(&e->env->userhdrs, np, ListNode, entries);
1123             FREE(&np->data);
1124             FREE(&np);
1125           }
1126         }
1127 
1128         mutt_addrlist_copy(&e->env->to, &opts_env->to, false);
1129         mutt_addrlist_copy(&e->env->cc, &opts_env->cc, false);
1130         mutt_addrlist_copy(&e->env->bcc, &opts_env->bcc, false);
1131         if (opts_env->subject)
1132           mutt_str_replace(&e->env->subject, opts_env->subject);
1133 
1134         mutt_env_free(&opts_env);
1135         email_free(&e_tmp);
1136       }
1137       /* Editing the include_file: pass it directly in.
1138        * Note that SEND_NO_FREE_HEADER is set above so it isn't unlinked.  */
1139       else if (edit_infile)
1140         bodyfile = mutt_buffer_string(&expanded_infile);
1141       // For bodytext and unedited include_file: use the tempfile.
1142       else
1143         bodyfile = mutt_buffer_string(&tempfile);
1144 
1145       mutt_file_fclose(&fp_in);
1146     }
1147 
1148     FREE(&bodytext);
1149 
1150     if (!STAILQ_EMPTY(&attach))
1151     {
1152       struct Body *b = e->body;
1153 
1154       while (b && b->next)
1155         b = b->next;
1156 
1157       struct ListNode *np = NULL;
1158       STAILQ_FOREACH(np, &attach, entries)
1159       {
1160         if (b)
1161         {
1162           b->next = mutt_make_file_attach(np->data, NeoMutt->sub);
1163           b = b->next;
1164         }
1165         else
1166         {
1167           b = mutt_make_file_attach(np->data, NeoMutt->sub);
1168           e->body = b;
1169         }
1170         if (!b)
1171         {
1172           mutt_error(_("%s: unable to attach file"), np->data);
1173           mutt_list_free(&attach);
1174           email_free(&e);
1175           goto main_curses; // TEST32: neomutt john@example.com -a missing
1176         }
1177       }
1178       mutt_list_free(&attach);
1179     }
1180 
1181     rv = mutt_send_message(sendflags, e, bodyfile, NULL, NULL, NeoMutt->sub);
1182     /* We WANT the "Mail sent." and any possible, later error */
1183     log_queue_empty();
1184     if (ErrorBufMessage)
1185       mutt_message("%s", ErrorBuf);
1186 
1187     if (edit_infile)
1188     {
1189       if (include_file)
1190         e->body->unlink = false;
1191       else if (draft_file)
1192       {
1193         if (truncate(mutt_buffer_string(&expanded_infile), 0) == -1)
1194         {
1195           mutt_perror(mutt_buffer_string(&expanded_infile));
1196           email_free(&e);
1197           goto main_curses; // TEST33: neomutt -H read-only -s test john@example.com -E
1198         }
1199         fp_out = mutt_file_fopen(mutt_buffer_string(&expanded_infile), "a");
1200         if (!fp_out)
1201         {
1202           mutt_perror(mutt_buffer_string(&expanded_infile));
1203           email_free(&e);
1204           goto main_curses; // TEST34: can't test
1205         }
1206 
1207         /* If the message was sent or postponed, these will already
1208          * have been done.  */
1209         if (rv < 0)
1210         {
1211           if (e->body->next)
1212             e->body = mutt_make_multipart(e->body);
1213           mutt_encode_descriptions(e->body, true, NeoMutt->sub);
1214           mutt_prepare_envelope(e->env, false, NeoMutt->sub);
1215           mutt_env_to_intl(e->env, NULL, NULL);
1216         }
1217 
1218         const bool c_crypt_protected_headers_read =
1219             cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_read");
1220         mutt_rfc822_write_header(
1221             fp_out, e->env, e->body, MUTT_WRITE_HEADER_POSTPONE, false,
1222             c_crypt_protected_headers_read && mutt_should_hide_protected_subject(e),
1223             NeoMutt->sub);
1224         const bool c_resume_edited_draft_files =
1225             cs_subset_bool(NeoMutt->sub, "resume_edited_draft_files");
1226         if (c_resume_edited_draft_files)
1227           fprintf(fp_out, "X-Mutt-Resume-Draft: 1\n");
1228         fputc('\n', fp_out);
1229         if ((mutt_write_mime_body(e->body, fp_out, NeoMutt->sub) == -1))
1230         {
1231           mutt_file_fclose(&fp_out);
1232           email_free(&e);
1233           goto main_curses; // TEST35: can't test
1234         }
1235         mutt_file_fclose(&fp_out);
1236       }
1237 
1238       email_free(&e);
1239     }
1240 
1241     /* !edit_infile && draft_file will leave the tempfile around */
1242     if (!mutt_buffer_is_empty(&tempfile))
1243       unlink(mutt_buffer_string(&tempfile));
1244 
1245     rootwin_free();
1246 
1247     if (rv != 0)
1248       goto main_curses; // TEST36: neomutt -H existing -s test john@example.com -E (cancel sending)
1249   }
1250   else if (sendflags & SEND_BATCH)
1251   {
1252     /* This guards against invoking `neomutt < /dev/null` and accidentally
1253      * sending an email due to a my_hdr or other setting.  */
1254     mutt_error(_("No recipients specified"));
1255     goto main_curses;
1256   }
1257   else
1258   {
1259     if (flags & MUTT_CLI_MAILBOX)
1260     {
1261 #ifdef USE_IMAP
1262       const bool c_imap_passive = cs_subset_bool(NeoMutt->sub, "imap_passive");
1263       cs_subset_str_native_set(NeoMutt->sub, "imap_passive", false, NULL);
1264 #endif
1265       if (mutt_mailbox_check(NULL, 0) == 0)
1266       {
1267         mutt_message(_("No mailbox with new mail"));
1268         goto main_curses; // TEST37: neomutt -Z (no new mail)
1269       }
1270       mutt_buffer_reset(&folder);
1271       mutt_mailbox_next(NULL, &folder);
1272 #ifdef USE_IMAP
1273       cs_subset_str_native_set(NeoMutt->sub, "imap_passive", c_imap_passive, NULL);
1274 #endif
1275     }
1276     else if (flags & MUTT_CLI_SELECT)
1277     {
1278 #ifdef USE_NNTP
1279       if (flags & MUTT_CLI_NEWS)
1280       {
1281         const char *const c_news_server =
1282             cs_subset_string(NeoMutt->sub, "news_server");
1283         OptNews = true;
1284         CurrentNewsSrv = nntp_select_server(Context ? Context->mailbox : NULL,
1285                                             c_news_server, false);
1286         if (!CurrentNewsSrv)
1287           goto main_curses; // TEST38: neomutt -G (unset news_server)
1288       }
1289       else
1290 #endif
1291           if (TAILQ_EMPTY(&NeoMutt->accounts))
1292       {
1293         mutt_error(_("No incoming mailboxes defined"));
1294         goto main_curses; // TEST39: neomutt -n -F /dev/null -y
1295       }
1296       mutt_buffer_reset(&folder);
1297       mutt_buffer_select_file(&folder, MUTT_SEL_FOLDER | MUTT_SEL_MAILBOX,
1298                               ctx_mailbox(Context), NULL, NULL);
1299       if (mutt_buffer_is_empty(&folder))
1300       {
1301         goto main_ok; // TEST40: neomutt -y (quit selection)
1302       }
1303     }
1304 
1305     if (mutt_buffer_is_empty(&folder))
1306     {
1307       const char *const c_spool_file =
1308           cs_subset_string(NeoMutt->sub, "spool_file");
1309       if (c_spool_file)
1310       {
1311         // Check if `$spool_file` corresponds a mailboxes' description.
1312         struct Mailbox *m_desc = mailbox_find_name(c_spool_file);
1313         if (m_desc)
1314           mutt_buffer_strcpy(&folder, m_desc->realpath);
1315         else
1316           mutt_buffer_strcpy(&folder, c_spool_file);
1317       }
1318       else if (c_folder)
1319         mutt_buffer_strcpy(&folder, c_folder);
1320       /* else no folder */
1321     }
1322 
1323 #ifdef USE_NNTP
1324     if (OptNews)
1325     {
1326       OptNews = false;
1327       mutt_buffer_alloc(&folder, PATH_MAX);
1328       nntp_expand_path(folder.data, folder.dsize, &CurrentNewsSrv->conn->account);
1329     }
1330     else
1331 #endif
1332       mutt_buffer_expand_path(&folder);
1333 
1334     mutt_str_replace(&CurrentFolder, mutt_buffer_string(&folder));
1335     mutt_str_replace(&LastFolder, mutt_buffer_string(&folder));
1336 
1337     if (flags & MUTT_CLI_IGNORE)
1338     {
1339       /* check to see if there are any messages in the folder */
1340       switch (mx_path_is_empty(mutt_buffer_string(&folder)))
1341       {
1342         case -1:
1343           mutt_perror(mutt_buffer_string(&folder));
1344           goto main_curses; // TEST41: neomutt -z -f missing
1345         case 1:
1346           mutt_error(_("Mailbox is empty"));
1347           goto main_curses; // TEST42: neomutt -z -f /dev/null
1348       }
1349     }
1350 
1351     mutt_folder_hook(mutt_buffer_string(&folder), NULL);
1352     mutt_startup_shutdown_hook(MUTT_STARTUP_HOOK);
1353     mutt_debug(LL_NOTIFY, "NT_GLOBAL_STARTUP\n");
1354     notify_send(NeoMutt->notify, NT_GLOBAL, NT_GLOBAL_STARTUP, NULL);
1355 
1356     repeat_error = true;
1357     struct Mailbox *m = mx_resolve(mutt_buffer_string(&folder));
1358     const bool c_read_only = cs_subset_bool(NeoMutt->sub, "read_only");
1359     if (!mx_mbox_open(m, ((flags & MUTT_CLI_RO) || c_read_only) ? MUTT_READONLY : MUTT_OPEN_NO_FLAGS))
1360     {
1361       if (m->account)
1362         account_mailbox_remove(m->account, m);
1363 
1364       mailbox_free(&m);
1365       mutt_error(_("Unable to open mailbox %s"), mutt_buffer_string(&folder));
1366       repeat_error = false;
1367     }
1368     if (m || !explicit_folder)
1369     {
1370       struct MuttWindow *dlg = index_pager_init();
1371       dialog_push(dlg);
1372 
1373       struct EventMailbox ev_m = { m };
1374       mutt_debug(LL_NOTIFY, "NT_MAILBOX_SWITCH: %p\n", m);
1375       notify_send(dlg->notify, NT_MAILBOX, NT_MAILBOX_SWITCH, &ev_m);
1376 
1377       m = mutt_index_menu(dlg, m);
1378       if (m && (m->flags == MB_HIDDEN))
1379         mailbox_free(&m);
1380 
1381       dialog_pop();
1382       mutt_window_free(&dlg);
1383       log_queue_empty();
1384       repeat_error = false;
1385     }
1386 #ifdef USE_IMAP
1387     imap_logout_all();
1388 #endif
1389 #ifdef USE_SASL
1390     mutt_sasl_done();
1391 #endif
1392 #ifdef USE_AUTOCRYPT
1393     mutt_autocrypt_cleanup();
1394 #endif
1395     // TEST43: neomutt (no change to mailbox)
1396     // TEST44: neomutt (change mailbox)
1397   }
1398 
1399 main_ok:
1400   rc = 0;
1401 main_curses:
1402   mutt_endwin();
1403   mutt_unlink_temp_attachments();
1404   /* Repeat the last message to the user */
1405   if (repeat_error && ErrorBufMessage)
1406     puts(ErrorBuf);
1407 main_exit:
1408   MuttLogger = log_disp_queue;
1409   mutt_buffer_dealloc(&folder);
1410   mutt_buffer_dealloc(&expanded_infile);
1411   mutt_buffer_dealloc(&tempfile);
1412   mutt_list_free(&queries);
1413   crypto_module_free();
1414   rootwin_free();
1415   mutt_buffer_pool_free();
1416   mutt_envlist_free();
1417   mutt_browser_cleanup();
1418   mutt_commands_cleanup();
1419   menu_cleanup();
1420   crypt_cleanup();
1421   mutt_opts_free();
1422   subjrx_free();
1423   attach_free();
1424   alternates_free();
1425   mutt_keys_free();
1426   myvarlist_free(&MyVars);
1427   mutt_prex_free();
1428   neomutt_free(&NeoMutt);
1429   cs_free(&cs);
1430   log_queue_flush(log_disp_terminal);
1431   log_queue_empty();
1432   mutt_log_stop();
1433   return rc;
1434 }
1435