Coverage Report

Created: 2023-06-07 06:15

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