NeoMutt  2021-02-05-89-gabe350
Teaching an old dog new tricks
DOXYGEN
sendmail.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <limits.h>
33 #include <signal.h>
34 #include <stdbool.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39 #include "mutt/lib.h"
40 #include "address/lib.h"
41 #include "config/lib.h"
42 #include "gui/lib.h"
43 #include "lib.h"
44 #include "pager/lib.h"
45 #include "context.h"
46 #include "format_flags.h"
47 #include "mutt_globals.h"
48 #include "muttlib.h"
49 #include "options.h"
50 #ifdef USE_NNTP
51 #include "nntp/lib.h"
52 #endif
53 #ifdef HAVE_SYSEXITS_H
54 #include <sysexits.h>
55 #else
56 #define EX_OK 0
57 #endif
58 
59 SIG_ATOMIC_VOLATILE_T SigAlrm;
60 
61 ARRAY_HEAD(SendmailArgs, const char *);
62 
67 static void alarm_handler(int sig)
68 {
69  SigAlrm = 1;
70 }
71 
85 static int send_msg(const char *path, struct SendmailArgs *args,
86  const char *msg, char **tempfile, int wait_time)
87 {
88  sigset_t set;
89  int st;
90 
92 
93  sigemptyset(&set);
94  /* we also don't want to be stopped right now */
95  sigaddset(&set, SIGTSTP);
96  sigprocmask(SIG_BLOCK, &set, NULL);
97 
98  if ((wait_time >= 0) && tempfile)
99  {
100  struct Buffer *tmp = mutt_buffer_pool_get();
101  mutt_buffer_mktemp(tmp);
102  *tempfile = mutt_buffer_strdup(tmp);
104  }
105 
106  pid_t pid = fork();
107  if (pid == 0)
108  {
109  struct sigaction act, oldalrm;
110 
111  /* save parent's ID before setsid() */
112  pid_t ppid = getppid();
113 
114  /* we want the delivery to continue even after the main process dies,
115  * so we put ourselves into another session right away */
116  setsid();
117 
118  /* next we close all open files */
119  close(0);
120 #ifdef OPEN_MAX
121  for (int fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
122  close(fd);
123 #elif defined(_POSIX_OPEN_MAX)
124  for (int fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
125  close(fd);
126 #else
127  if (tempfile)
128  {
129  close(1);
130  close(2);
131  }
132 #endif
133 
134  /* now the second fork() */
135  pid = fork();
136  if (pid == 0)
137  {
138  /* "msg" will be opened as stdin */
139  if (open(msg, O_RDONLY, 0) < 0)
140  {
141  unlink(msg);
142  _exit(S_ERR);
143  }
144  unlink(msg);
145 
146  if ((wait_time >= 0) && tempfile && *tempfile)
147  {
148  /* *tempfile will be opened as stdout */
149  if (open(*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
150  _exit(S_ERR);
151  /* redirect stderr to *tempfile too */
152  if (dup(1) < 0)
153  _exit(S_ERR);
154  }
155  else if (tempfile)
156  {
157  if (open("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
158  _exit(S_ERR);
159  if (open("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
160  _exit(S_ERR);
161  }
162 
163  /* execvpe is a glibc extension */
164  /* execvpe (path, args, mutt_envlist_getlist()); */
165  execvp(path, (char **) args->entries);
166  _exit(S_ERR);
167  }
168  else if (pid == -1)
169  {
170  unlink(msg);
171  FREE(tempfile);
172  _exit(S_ERR);
173  }
174 
175  /* wait_time > 0: interrupt waitpid() after wait_time seconds
176  * wait_time = 0: wait forever
177  * wait_time < 0: don't wait */
178  if (wait_time > 0)
179  {
180  SigAlrm = 0;
181  act.sa_handler = alarm_handler;
182 #ifdef SA_INTERRUPT
183  /* need to make sure waitpid() is interrupted on SIGALRM */
184  act.sa_flags = SA_INTERRUPT;
185 #else
186  act.sa_flags = 0;
187 #endif
188  sigemptyset(&act.sa_mask);
189  sigaction(SIGALRM, &act, &oldalrm);
190  alarm(wait_time);
191  }
192  else if (wait_time < 0)
193  _exit(0xff & EX_OK);
194 
195  if (waitpid(pid, &st, 0) > 0)
196  {
197  st = WIFEXITED(st) ? WEXITSTATUS(st) : S_ERR;
198  if (wait_time && (st == (0xff & EX_OK)) && tempfile && *tempfile)
199  {
200  unlink(*tempfile); /* no longer needed */
201  FREE(tempfile);
202  }
203  }
204  else
205  {
206  st = ((wait_time > 0) && (errno == EINTR) && SigAlrm) ? S_BKG : S_ERR;
207  if ((wait_time > 0) && tempfile && *tempfile)
208  {
209  unlink(*tempfile);
210  FREE(tempfile);
211  }
212  }
213 
214  if (wait_time > 0)
215  {
216  /* reset alarm; not really needed, but... */
217  alarm(0);
218  sigaction(SIGALRM, &oldalrm, NULL);
219  }
220 
221  if ((kill(ppid, 0) == -1) && (errno == ESRCH) && tempfile && *tempfile)
222  {
223  /* the parent is already dead */
224  unlink(*tempfile);
225  FREE(tempfile);
226  }
227 
228  _exit(st);
229  }
230 
231  sigprocmask(SIG_UNBLOCK, &set, NULL);
232 
233  if ((pid != -1) && (waitpid(pid, &st, 0) > 0))
234  st = WIFEXITED(st) ? WEXITSTATUS(st) : S_ERR; /* return child status */
235  else
236  st = S_ERR; /* error */
237 
239 
240  return st;
241 }
242 
248 static void add_args_one(struct SendmailArgs *args, const struct Address *addr)
249 {
250  /* weed out group mailboxes, since those are for display only */
251  if (addr->mailbox && !addr->group)
252  {
253  ARRAY_ADD(args, addr->mailbox);
254  }
255 }
256 
262 static void add_args(struct SendmailArgs *args, struct AddressList *al)
263 {
264  if (!al)
265  return;
266 
267  struct Address *a = NULL;
268  TAILQ_FOREACH(a, al, entries)
269  {
270  add_args_one(args, a);
271  }
272 }
273 
287 int mutt_invoke_sendmail(struct Mailbox *m, struct AddressList *from,
288  struct AddressList *to, struct AddressList *cc,
289  struct AddressList *bcc, const char *msg,
290  bool eightbit, struct ConfigSubset *sub)
291 {
292  char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
293  struct SendmailArgs args = ARRAY_HEAD_INITIALIZER;
294  struct SendmailArgs extra_args = ARRAY_HEAD_INITIALIZER;
295  int i;
296 
297 #ifdef USE_NNTP
298  if (OptNewsSend)
299  {
300  char cmd[1024];
301 
302  const char *c_inews = cs_subset_string(sub, "inews");
303  mutt_expando_format(cmd, sizeof(cmd), 0, sizeof(cmd), NONULL(c_inews),
305  if (*cmd == '\0')
306  {
307  i = nntp_post(m, msg);
308  unlink(msg);
309  return i;
310  }
311 
312  s = mutt_str_dup(cmd);
313  }
314  else
315 #endif
316  {
317  const char *c_sendmail = cs_subset_string(sub, "sendmail");
318  s = mutt_str_dup(c_sendmail);
319  }
320 
321  /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
322  if (!s)
323  {
324  mutt_error(_("$sendmail must be set in order to send mail"));
325  return -1;
326  }
327 
328  ps = s;
329  i = 0;
330  while ((ps = strtok(ps, " ")))
331  {
332  if (i)
333  {
334  if (mutt_str_equal(ps, "--"))
335  break;
336  ARRAY_ADD(&args, ps);
337  }
338  else
339  {
340  path = mutt_str_dup(ps);
341  ps = strrchr(ps, '/');
342  if (ps)
343  ps++;
344  else
345  ps = path;
346  ARRAY_ADD(&args, ps);
347  }
348  ps = NULL;
349  i++;
350  }
351 
352 #ifdef USE_NNTP
353  if (!OptNewsSend)
354  {
355 #endif
356  /* If $sendmail contained a "--", we save the recipients to append to
357  * args after other possible options added below. */
358  if (ps)
359  {
360  ps = NULL;
361  while ((ps = strtok(ps, " ")))
362  {
363  ARRAY_ADD(&extra_args, ps);
364  }
365  }
366 
367  const bool c_use_8bit_mime = cs_subset_bool(sub, "use_8bit_mime");
368  if (eightbit && c_use_8bit_mime)
369  ARRAY_ADD(&args, "-B8BITMIME");
370 
371  const bool c_use_envelope_from = cs_subset_bool(sub, "use_envelope_from");
372  if (c_use_envelope_from)
373  {
374  const struct Address *c_envelope_from_address =
375  cs_subset_address(sub, "envelope_from_address");
376  if (c_envelope_from_address)
377  {
378  ARRAY_ADD(&args, "-f");
379  add_args_one(&args, c_envelope_from_address);
380  }
381  else if (!TAILQ_EMPTY(from) && !TAILQ_NEXT(TAILQ_FIRST(from), entries))
382  {
383  ARRAY_ADD(&args, "-f");
384  add_args(&args, from);
385  }
386  }
387 
388  const char *c_dsn_notify = cs_subset_string(sub, "dsn_notify");
389  if (c_dsn_notify)
390  {
391  ARRAY_ADD(&args, "-N");
392  ARRAY_ADD(&args, c_dsn_notify);
393  }
394 
395  const char *c_dsn_return = cs_subset_string(sub, "dsn_return");
396  if (c_dsn_return)
397  {
398  ARRAY_ADD(&args, "-R");
399  ARRAY_ADD(&args, c_dsn_return);
400  }
401  ARRAY_ADD(&args, "--");
402  const char **e = NULL;
403  ARRAY_FOREACH(e, &extra_args)
404  {
405  ARRAY_ADD(&args, *e);
406  }
407  add_args(&args, to);
408  add_args(&args, cc);
409  add_args(&args, bcc);
410 #ifdef USE_NNTP
411  }
412 #endif
413 
414  ARRAY_ADD(&args, NULL);
415 
416  /* Some user's $sendmail command uses gpg for password decryption,
417  * and is set up to prompt using ncurses pinentry. If we
418  * mutt_endwin() it leaves other users staring at a blank screen.
419  * So instead, just force a hard redraw on the next refresh. */
420  if (!OptNoCurses)
422 
423  const short c_sendmail_wait = cs_subset_number(sub, "sendmail_wait");
424  i = send_msg(path, &args, msg, OptNoCurses ? NULL : &childout, c_sendmail_wait);
425  if (i != (EX_OK & 0xff))
426  {
427  if (i != S_BKG)
428  {
429  const char *e = mutt_str_sysexit(i);
430  mutt_error(_("Error sending message, child exited %d (%s)"), i, NONULL(e));
431  if (childout)
432  {
433  struct stat st;
434 
435  if ((stat(childout, &st) == 0) && (st.st_size > 0))
436  {
437  mutt_do_pager(_("Output of the delivery process"), childout,
438  MUTT_PAGER_NO_FLAGS, NULL);
439  }
440  }
441  }
442  }
443  else if (childout)
444  unlink(childout);
445 
446  FREE(&childout);
447  FREE(&path);
448  FREE(&s);
449  ARRAY_FREE(&args);
450  ARRAY_FREE(&extra_args);
451 
452  if (i == (EX_OK & 0xff))
453  i = 0;
454  else if (i == S_BKG)
455  i = 1;
456  else
457  i = -1;
458  return i;
459 }
lib.h
lib.h
S_BKG
#define S_BKG
Definition: string2.h:42
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
Mailbox
A mailbox.
Definition: mailbox.h:81
ARRAY_ADD
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:152
Buffer
String manipulation buffer.
Definition: buffer.h:33
S_ERR
#define S_ERR
Definition: string2.h:41
format_flags.h
mutt_buffer_mktemp
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
cs_subset_bool
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:69
TAILQ_EMPTY
#define TAILQ_EMPTY(head)
Definition: queue.h:714
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
mutt_globals.h
FREE
#define FREE(x)
Definition: memory.h:40
SigAlrm
SIG_ATOMIC_VOLATILE_T SigAlrm
true after SIGALRM is received
Definition: sendmail.c:59
options.h
send_msg
static int send_msg(const char *path, struct SendmailArgs *args, const char *msg, char **tempfile, int wait_time)
invoke sendmail in a subshell
Definition: sendmail.c:85
OptNoCurses
WHERE bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:48
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
MUTT_FORMAT_NO_FLAGS
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
ARRAY_HEAD
ARRAY_HEAD(SendmailArgs, const char *)
mutt_invoke_sendmail
int mutt_invoke_sendmail(struct Mailbox *m, struct AddressList *from, struct AddressList *to, struct AddressList *cc, struct AddressList *bcc, const char *msg, bool eightbit, struct ConfigSubset *sub)
Run sendmail.
Definition: sendmail.c:287
mutt_str_equal
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
lib.h
mutt_sig_unblock_system
void mutt_sig_unblock_system(bool restore)
Restore previously blocked signals.
Definition: signal.c:207
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:716
cs_subset_address
const struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
Definition: helpers.c:48
mutt_need_hard_redraw
void mutt_need_hard_redraw(void)
Force a hard refresh.
Definition: curs_lib.c:130
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
ARRAY_FOREACH
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:206
cs_subset_number
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:111
mutt_do_pager
int mutt_do_pager(const char *banner, const char *tempfile, PagerFlags do_color, struct Pager *info)
Display some page-able text to the user.
Definition: curs_lib.c:691
lib.h
muttlib.h
ARRAY_HEAD_INITIALIZER
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:54
Address::group
bool group
Group mailbox?
Definition: address.h:38
lib.h
MUTT_PAGER_NO_FLAGS
#define MUTT_PAGER_NO_FLAGS
No flags are set.
Definition: lib.h:42
ARRAY_FREE
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:198
EX_OK
#define EX_OK
Definition: sendmail.c:56
Address::mailbox
char * mailbox
Mailbox and host address.
Definition: address.h:37
cs_subset_string
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:241
TAILQ_NEXT
#define TAILQ_NEXT(elm, field)
Definition: queue.h:825
mutt_expando_format
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:774
nntp_post
int nntp_post(struct Mailbox *m, const char *msg)
Post article.
Definition: nntp.c:1866
add_args
static void add_args(struct SendmailArgs *args, struct AddressList *al)
Add a list of Addresses to a dynamic array.
Definition: sendmail.c:262
mutt_str_sysexit
const char * mutt_str_sysexit(int err_num)
Return a string matching an error code.
Definition: string.c:112
context.h
OptNewsSend
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:46
mutt_buffer_strdup
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:432
ConfigSubset
A set of inherited config items.
Definition: subset.h:46
lib.h
alarm_handler
static void alarm_handler(int sig)
Async notification of an alarm signal.
Definition: sendmail.c:67
nntp_format_str
const char * nntp_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Expand the newsrc filename - Implements format_t.
Definition: newsrc.c:920
mutt_sig_block_system
void mutt_sig_block_system(void)
Block signals before calling exec()
Definition: signal.c:183
add_args_one
static void add_args_one(struct SendmailArgs *args, const struct Address *addr)
Add an Address to a dynamic array.
Definition: sendmail.c:248
Address
An email address.
Definition: address.h:34
mutt_error
#define mutt_error(...)
Definition: logging.h:84