NeoMutt  2025-12-11-872-g385a04
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
send.c File Reference

Prepare and send an email. More...

#include "config.h"
#include <errno.h>
#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "alias/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "send.h"
#include "attach/lib.h"
#include "browser/lib.h"
#include "compose/lib.h"
#include "editor/lib.h"
#include "expando/lib.h"
#include "history/lib.h"
#include "hooks/lib.h"
#include "imap/lib.h"
#include "index/lib.h"
#include "ncrypt/lib.h"
#include "pager/lib.h"
#include "pattern/lib.h"
#include "postpone/lib.h"
#include "question/lib.h"
#include "body.h"
#include "expando_greeting.h"
#include "expando_msgid.h"
#include "globals.h"
#include "header.h"
#include "module_data.h"
#include "multipart.h"
#include "mutt_logging.h"
#include "muttlib.h"
#include "mx.h"
#include "nntp/mdata.h"
#include "sendlib.h"
#include "sendmail.h"
#include "smtp.h"
#include "notmuch/lib.h"
#include "autocrypt/lib.h"
#include "autocrypt/module_data.h"
+ Include dependency graph for send.c:

Go to the source code of this file.

Functions

static void append_signature (FILE *fp, struct ConfigSubset *sub)
 Append a signature to an email.
 
static void remove_user (struct AddressList *al, bool leave_only)
 Remove any address which matches the current user.
 
static void add_mailing_lists (struct AddressList *out, const struct AddressList *t, const struct AddressList *c)
 Search Address lists for mailing lists.
 
int mutt_edit_address (struct AddressList *al, const char *field, bool expand_aliases)
 Edit an email address.
 
static int edit_envelope (struct Envelope *en, SendFlags flags, struct ConfigSubset *sub)
 Edit Envelope fields.
 
static char * nntp_get_header (const char *s)
 Get the trimmed header.
 
static void process_user_recips (struct Envelope *env)
 Process the user headers.
 
static void process_user_header (struct Envelope *env)
 Process the user headers.
 
void mutt_forward_intro (struct Email *e, FILE *fp, struct ConfigSubset *sub)
 Add the "start of forwarded message" text.
 
void mutt_forward_trailer (struct Email *e, FILE *fp, struct ConfigSubset *sub)
 Add a "end of forwarded message" text.
 
static int include_forward (struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
 Write out a forwarded message.
 
static int inline_forward_attachments (struct Mailbox *m, struct Email *e, struct Body ***plast, enum QuadOption *forwardq, struct ConfigSubset *sub)
 Add attachments to an email, inline.
 
static void format_attribution (const struct Expando *exp, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
 Format an attribution prefix/suffix.
 
void mutt_make_attribution_intro (struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
 Add "on DATE, PERSON wrote" header.
 
void mutt_make_attribution_trailer (struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
 Add suffix to replied email text.
 
static void mutt_make_greeting (struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
 Add greetings string.
 
static int include_reply (struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
 Generate the reply text for an email.
 
static const struct AddressList * choose_default_to (const struct Address *from, const struct Envelope *env, struct ConfigSubset *sub)
 Pick the best 'to:' value.
 
static int default_to (struct AddressList *to, struct Envelope *env, SendFlags flags, int hmfupto, struct ConfigSubset *sub)
 Generate default email addresses.
 
int mutt_fetch_recips (struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
 Generate recpients for a reply email.
 
static void add_references (struct ListHead *head, struct Envelope *env)
 Add the email's references to a list.
 
static void add_message_id (struct ListHead *head, struct Envelope *env)
 Add the email's message ID to a list.
 
void mutt_fix_reply_recipients (struct Envelope *env, struct ConfigSubset *sub)
 Remove duplicate recipients.
 
void mutt_make_forward_subject (struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
 Create a subject for a forwarded email.
 
void mutt_make_misc_reply_headers (struct Envelope *env, struct Envelope *env_cur, struct ConfigSubset *sub)
 Set subject for a reply.
 
void mutt_add_to_reference_headers (struct Envelope *env, struct Envelope *env_cur, struct ConfigSubset *sub)
 Generate references for a reply email.
 
static void make_reference_headers (struct EmailArray *ea, struct Envelope *env, struct ConfigSubset *sub)
 Generate reference headers for an email.
 
static int envelope_defaults (struct Envelope *env, struct EmailArray *ea, SendFlags flags, struct ConfigSubset *sub)
 Fill in some defaults for a new email.
 
static int generate_body (FILE *fp_tmp, struct Email *e, SendFlags flags, struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
 Create a new email body.
 
void mutt_set_followup_to (struct Envelope *env, struct ConfigSubset *sub)
 Set followup-to field.
 
static void set_reverse_name (struct AddressList *al, struct Envelope *env, struct ConfigSubset *sub)
 Try to set the 'from' field from the recipients.
 
struct Addressmutt_default_from (struct ConfigSubset *sub)
 Get a default 'from' Address.
 
static int invoke_mta (struct Mailbox *m, struct Email *e, struct ConfigSubset *sub)
 Send an email.
 
void mutt_encode_descriptions (struct Body *b, bool recurse, struct ConfigSubset *sub)
 RFC2047 encode the content-descriptions.
 
static void decode_descriptions (struct Body *b)
 RFC2047 decode them in case of an error.
 
static void fix_end_of_file (const char *data)
 Ensure a file ends with a linefeed.
 
int mutt_resend_message (FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
 Resend an email.
 
static bool is_reply (struct Email *reply, struct Email *orig)
 Is one email a reply to another?
 
static bool search_attach_keyword (char *filename, struct ConfigSubset *sub)
 Search an email for 'attachment' keywords.
 
static int save_fcc (struct Mailbox *m, struct Email *e, struct Buffer *fcc, struct Body *clear_content, char *pgpkeylist, SendFlags flags, char **finalpath, struct ConfigSubset *sub)
 Save an Email to a 'sent mail' folder.
 
static int postpone_message (struct Email *e_post, struct Email *e_cur, const char *fcc, SendFlags flags, struct ConfigSubset *sub)
 Save an Email for another day.
 
static bool is_text_plain (const struct Body *b)
 Is a Body a text/plain MIME part?
 
static bool abort_for_missing_attachments (const struct Body *b, struct ConfigSubset *sub)
 Should we abort sending because of missing attachments?
 
int mutt_send_message (SendFlags flags, struct Email *e_templ, const char *tempfile, struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
 Send an email.
 
static bool send_simple_email (struct Mailbox *m, struct EmailArray *ea, const char *mailto, const char *subj, const char *body)
 Compose an email given a few basic ingredients.
 
bool mutt_send_list_subscribe (struct Mailbox *m, struct Email *e)
 Send a mailing-list subscription email.
 
bool mutt_send_list_unsubscribe (struct Mailbox *m, struct Email *e)
 Send a mailing-list unsubscription email.
 

Detailed Description

Prepare and send an email.

Authors
  • Richard Russon
  • Pietro Cerutti
  • Jakub Jindra
  • Thomas Sanchez
  • Ashish Panigrahi
  • Charalampos Kardaris
  • Thomas Bracht Laumann Jespersen
  • Viktor Cheburkin
  • David Purton
  • Dennis Schön
  • Whitney Cumber
  • наб
  • Alejandro Colomar
  • Tóth János

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file send.c.

Function Documentation

◆ append_signature()

static void append_signature ( FILE * fp,
struct ConfigSubset * sub )
static

Append a signature to an email.

Parameters
fpFile to write to
subConfig Subset

Definition at line 101 of file send.c.

102{
103 const char *const c_signature = cs_subset_path(sub, "signature");
104 if (!c_signature)
105 return;
106
107 // If the user hasn't set $signature, don't warn them if it doesn't exist
108 struct Buffer *def_sig = buf_pool_get();
109 cs_str_initial_get(sub->cs, "signature", def_sig);
110 mutt_path_canon(def_sig, NeoMutt->home_dir, false);
111 bool notify_missing = !mutt_str_equal(c_signature, buf_string(def_sig));
112 buf_pool_release(&def_sig);
113
114 pid_t pid = 0;
115 FILE *fp_tmp = mutt_open_read(c_signature, &pid);
116 if (!fp_tmp)
117 {
118 if (notify_missing)
119 mutt_perror("%s", c_signature);
120 return;
121 }
122
123 const bool c_sig_dashes = cs_subset_bool(sub, "sig_dashes");
124 if (c_sig_dashes)
125 fputs("\n-- \n", fp);
126 mutt_file_copy_stream(fp_tmp, fp);
127 mutt_file_fclose(&fp_tmp);
128 if (pid != -1)
129 filter_wait(pid);
130}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition helpers.c:168
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
int cs_str_initial_get(const struct ConfigSet *cs, const char *name, struct Buffer *result)
Get the initial, or parent, value of a config item.
Definition set.c:594
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition file.c:224
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_perror(...)
Definition logging2.h:95
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition filter.c:228
bool mutt_path_canon(struct Buffer *path, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition path.c:248
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition muttlib.c:645
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
String manipulation buffer.
Definition buffer.h:36
struct ConfigSet * cs
Parent ConfigSet.
Definition subset.h:50
Container for Accounts, Notifications.
Definition neomutt.h:41
char * home_dir
User's home directory.
Definition neomutt.h:55
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ remove_user()

static void remove_user ( struct AddressList * al,
bool leave_only )
static

Remove any address which matches the current user.

Parameters
alList of addresses
leave_onlyIf set, don't remove the user's address if it it the only one in the list

Definition at line 138 of file send.c.

139{
140 struct Address *a = NULL, *tmp = NULL;
141 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
142 {
143 if (mutt_addr_is_user(a) && (!leave_only || TAILQ_NEXT(a, entries)))
144 {
145 TAILQ_REMOVE(al, a, entries);
146 mutt_addr_free(&a);
147 }
148 }
149}
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition address.c:462
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition alias.c:600
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:792
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:901
#define TAILQ_NEXT(elm, field)
Definition queue.h:889
An email address.
Definition address.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ add_mailing_lists()

static void add_mailing_lists ( struct AddressList * out,
const struct AddressList * t,
const struct AddressList * c )
static

Search Address lists for mailing lists.

Parameters
outAddress list where to append matching mailing lists
t'To' Address list
c'Cc' Address list

Definition at line 157 of file send.c.

159{
160 const struct AddressList *const als[] = { t, c };
161
162 for (size_t i = 0; i < countof(als); i++)
163 {
164 const struct AddressList *al = als[i];
165 struct Address *a = NULL;
166 TAILQ_FOREACH(a, al, entries)
167 {
168 if (!a->group && mutt_is_mail_list(a))
169 {
171 }
172 }
173 }
174}
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition address.c:1489
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition address.c:754
bool mutt_is_mail_list(const struct Address *addr)
Is this the email address of a mailing list?
Definition maillist.c:46
#define countof(x)
Definition memory.h:49
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
bool group
Group mailbox?
Definition address.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_edit_address()

int mutt_edit_address ( struct AddressList * al,
const char * field,
bool expand_aliases )

Edit an email address.

Parameters
[in,out]alAddressList to edit
[in]fieldPrompt for user
[in]expand_aliasesIf true, expand Address aliases
Return values
0Success
-1Failure

Definition at line 184 of file send.c.

185{
186 int rc = 0;
187 struct Buffer *buf = buf_pool_get();
188 buf_alloc(buf, 8192);
189 char *err = NULL;
190 int idna_ok = 0;
191
192 do
193 {
195 buf_reset(buf);
196 mutt_addrlist_write(al, buf, false);
197 if (!buf_is_empty(buf))
198 buf_addstr(buf, ", ");
199
200 if (mw_get_field(field, buf, MUTT_COMP_NONE, HC_ALIAS, &CompleteAliasOps, NULL) != 0)
201 {
202 rc = -1;
203 goto done;
204 }
207 if (expand_aliases)
209 idna_ok = mutt_addrlist_to_intl(al, &err);
210 if (idna_ok != 0)
211 {
212 mutt_error(_("Bad IDN: '%s'"), err);
213 FREE(&err);
214 }
215 } while (idna_ok != 0);
216
217done:
218 buf_pool_release(&buf);
219 return rc;
220}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1469
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition address.c:1387
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1215
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:649
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition address.c:1302
const struct CompleteOps CompleteAliasOps
Auto-Completion of Aliases.
Definition complete.c:108
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition alias.c:296
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
@ MUTT_COMP_NONE
No flags are set.
Definition wdata.h:46
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition window.c:467
#define mutt_error(...)
Definition logging2.h:94
@ HC_ALIAS
Aliases.
Definition lib.h:57
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define _(a)
Definition message.h:28
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ edit_envelope()

static int edit_envelope ( struct Envelope * en,
SendFlags flags,
struct ConfigSubset * sub )
static

Edit Envelope fields.

Parameters
enEnvelope to edit
flagsFlags, see SendFlags
subConfig Subset
Return values
0Success
-1Failure

Definition at line 230 of file send.c.

231{
232 int rc = -1;
233 struct Buffer *buf = buf_pool_get();
234 buf_alloc(buf, 8192);
235
236 /* For NNTP posts, prompt for Newsgroups, Followup-To, and X-Comment-To */
237 if (OptNewsSend)
238 {
239 if (en->newsgroups)
240 buf_strcpy(buf, en->newsgroups);
241 else
242 buf_reset(buf);
243
244 if (mw_get_field("Newsgroups: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) != 0)
245 {
246 goto done;
247 }
249
250 if (en->followup_to)
251 buf_strcpy(buf, en->followup_to);
252 else
253 buf_reset(buf);
254
255 const bool c_ask_followup_to = cs_subset_bool(sub, "ask_followup_to");
256 if (c_ask_followup_to &&
257 (mw_get_field("Followup-To: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) != 0))
258 {
259 goto done;
260 }
262
263 if (en->x_comment_to)
264 buf_strcpy(buf, en->x_comment_to);
265 else
266 buf_reset(buf);
267
268 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
269 const bool c_ask_x_comment_to = cs_subset_bool(sub, "ask_x_comment_to");
270 if (c_x_comment_to && c_ask_x_comment_to &&
271 (mw_get_field("X-Comment-To: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) != 0))
272 {
273 goto done;
274 }
276 }
277 else
278 {
279 /* For email: prompt for To, Cc, Bcc addresses (skipped with fast_reply) */
280 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
281 if (TAILQ_EMPTY(&en->to) || !c_fast_reply || (flags & SEND_REVIEW_TO))
282 {
283 if ((mutt_edit_address(&en->to, _("To: "), true) == -1))
284 goto done;
285 }
286
287 const bool c_ask_cc = cs_subset_bool(sub, "ask_cc");
288 if (TAILQ_EMPTY(&en->cc) || !c_fast_reply)
289 {
290 if (c_ask_cc && (mutt_edit_address(&en->cc, _("Cc: "), true) == -1))
291 goto done;
292 }
293
294 const bool c_ask_bcc = cs_subset_bool(sub, "ask_bcc");
295 if (TAILQ_EMPTY(&en->bcc) || !c_fast_reply)
296 {
297 if (c_ask_bcc && (mutt_edit_address(&en->bcc, _("Bcc: "), true) == -1))
298 goto done;
299 }
300
301 if (TAILQ_EMPTY(&en->to) && TAILQ_EMPTY(&en->cc) && TAILQ_EMPTY(&en->bcc))
302 {
303 mutt_warning(_("No recipients specified"));
304 goto done;
305 }
306
307 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
308 if (c_reply_with_xorig && (flags & (SEND_REPLY | SEND_LIST_REPLY | SEND_GROUP_REPLY)) &&
309 (mutt_edit_address(&en->from, "From: ", true) == -1))
310 {
311 goto done;
312 }
313 }
314
316 ASSERT(mod_data);
317
318 /* Handle the Subject line: fast_reply skips the prompt if subject already set.
319 * Check user_header for any "Subject:" override from send-hooks. */
320 if (en->subject)
321 {
322 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
323 if (c_fast_reply)
324 {
325 rc = 0;
326 goto done;
327 }
328 buf_strcpy(buf, en->subject);
329 }
330 else
331 {
332 const char *p = NULL;
333
334 buf_reset(buf);
335 struct ListNode *uh = NULL;
336 STAILQ_FOREACH(uh, &mod_data->user_header, entries)
337 {
338 size_t plen = mutt_istr_startswith(uh->data, "subject:");
339 if (plen)
340 {
341 p = mutt_str_skip_email_wsp(uh->data + plen);
342 buf_strcpy(buf, p);
343 }
344 }
345 }
346
347 if ((mw_get_field(_("Subject: "), buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) != 0) ||
348 (buf_is_empty(buf) &&
349 (query_quadoption(_("No subject, abort?"), sub, "abort_nosubject") != MUTT_NO)))
350 {
351 mutt_message(_("No subject, aborting"));
352 goto done;
353 }
355 rc = 0;
356
357done:
358 buf_pool_release(&buf);
359 return rc;
360}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
void mutt_env_set_subject(struct Envelope *env, const char *subj)
Set both subject and real_subj to subj.
Definition envelope.c:68
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition globals.c:54
#define mutt_warning(...)
Definition logging2.h:92
#define mutt_message(...)
Definition logging2.h:93
@ HC_OTHER
Miscellaneous strings.
Definition lib.h:61
@ MODULE_ID_SEND
ModuleSend, Send
Definition module_api.h:90
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:613
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:384
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define TAILQ_EMPTY(head)
Definition queue.h:778
int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
Edit an email address.
Definition send.c:184
@ SEND_REPLY
Reply to sender.
Definition send.h:46
@ SEND_GROUP_REPLY
Reply to all.
Definition send.h:47
@ SEND_REVIEW_TO
Allow the user to edit the To field.
Definition send.h:60
@ SEND_LIST_REPLY
Reply to mailing list.
Definition send.h:48
#define ASSERT(COND)
Definition signal2.h:59
char *const subject
Email's subject.
Definition envelope.h:70
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition envelope.h:80
char * x_comment_to
List of 'X-comment-to' fields.
Definition envelope.h:81
char * newsgroups
List of newsgroups.
Definition envelope.h:78
struct AddressList cc
Email's 'Cc' list.
Definition envelope.h:61
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
Send private Module data.
Definition module_data.h:33
struct ListHead user_header
Custom headers to add to outgoing emails.
Definition module_data.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nntp_get_header()

static char * nntp_get_header ( const char * s)
static

Get the trimmed header.

Parameters
sHeader line with leading whitespace
Return values
ptrCopy of string
Note
The caller should free the returned string.

Definition at line 369 of file send.c.

370{
371 SKIPWS(s);
372 return mutt_str_dup(s);
373}
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
#define SKIPWS(ch)
Definition string2.h:52
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ process_user_recips()

static void process_user_recips ( struct Envelope * env)
static

Process the user headers.

Parameters
envEnvelope to populate

Definition at line 379 of file send.c.

380{
382 ASSERT(mod_data);
383
384 struct ListNode *uh = NULL;
385 STAILQ_FOREACH(uh, &mod_data->user_header, entries)
386 {
387 size_t plen;
388 if ((plen = mutt_istr_startswith(uh->data, "to:")))
389 mutt_addrlist_parse(&env->to, uh->data + plen);
390 else if ((plen = mutt_istr_startswith(uh->data, "cc:")))
391 mutt_addrlist_parse(&env->cc, uh->data + plen);
392 else if ((plen = mutt_istr_startswith(uh->data, "bcc:")))
393 mutt_addrlist_parse(&env->bcc, uh->data + plen);
394 else if ((plen = mutt_istr_startswith(uh->data, "newsgroups:")))
395 env->newsgroups = nntp_get_header(uh->data + plen);
396 else if ((plen = mutt_istr_startswith(uh->data, "followup-to:")))
397 env->followup_to = nntp_get_header(uh->data + plen);
398 else if ((plen = mutt_istr_startswith(uh->data, "x-comment-to:")))
399 env->x_comment_to = nntp_get_header(uh->data + plen);
400 }
401}
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:480
static char * nntp_get_header(const char *s)
Get the trimmed header.
Definition send.c:369
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ process_user_header()

static void process_user_header ( struct Envelope * env)
static

Process the user headers.

Parameters
envEnvelope to populate

Definition at line 407 of file send.c.

408{
410 ASSERT(mod_data);
411
412 struct ListNode *uh = NULL;
413 STAILQ_FOREACH(uh, &mod_data->user_header, entries)
414 {
415 size_t plen;
416 if ((plen = mutt_istr_startswith(uh->data, "from:")))
417 {
418 /* User has specified a default From: address. Remove default address */
420 mutt_addrlist_parse(&env->from, uh->data + plen);
421 }
422 else if ((plen = mutt_istr_startswith(uh->data, "reply-to:")))
423 {
425 mutt_addrlist_parse(&env->reply_to, uh->data + plen);
426 }
427 else if ((plen = mutt_istr_startswith(uh->data, "message-id:")))
428 {
429 char *tmp = mutt_extract_message_id(uh->data + plen, NULL);
430 if (mutt_addr_valid_msgid(tmp))
431 {
432 FREE(&env->message_id);
433 env->message_id = tmp;
434 }
435 else
436 {
437 FREE(&tmp);
438 }
439 }
440 else if (!mutt_istr_startswith(uh->data, "to:") &&
441 !mutt_istr_startswith(uh->data, "cc:") &&
442 !mutt_istr_startswith(uh->data, "bcc:") &&
443 !mutt_istr_startswith(uh->data, "newsgroups:") &&
444 !mutt_istr_startswith(uh->data, "followup-to:") &&
445 !mutt_istr_startswith(uh->data, "x-comment-to:") &&
446 !mutt_istr_startswith(uh->data, "supersedes:") &&
447 !mutt_istr_startswith(uh->data, "subject:") &&
448 !mutt_istr_startswith(uh->data, "return-path:"))
449 {
451 }
452 }
453}
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition address.c:801
char * mutt_extract_message_id(const char *s, size_t *len)
Find a Message-ID.
Definition parse.c:368
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
struct ListHead userhdrs
user defined headers
Definition envelope.h:85
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
char * message_id
Message ID.
Definition envelope.h:73
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_forward_intro()

void mutt_forward_intro ( struct Email * e,
FILE * fp,
struct ConfigSubset * sub )

Add the "start of forwarded message" text.

Parameters
eEmail
subConfig Subset
fpFile to write to

Definition at line 461 of file send.c.

462{
463 const struct Expando *c_forward_attribution_intro = cs_subset_expando(sub, "forward_attribution_intro");
464 if (!c_forward_attribution_intro || !fp)
465 return;
466
467 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
468
469 struct Buffer *buf = buf_pool_get();
470 setlocale(LC_TIME, NONULL(c_attribution_locale));
471 mutt_make_string(buf, -1, c_forward_attribution_intro, NULL, -1, e, MUTT_FORMAT_NONE, NULL);
472 setlocale(LC_TIME, "");
473 fputs(buf_string(buf), fp);
474 fputs("\n\n", fp);
475 buf_pool_release(&buf);
476}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
const struct Expando * cs_subset_expando(const struct ConfigSubset *sub, const char *name)
Get an Expando config item by name.
int mutt_make_string(struct Buffer *buf, size_t max_cols, const struct Expando *exp, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition dlg_index.c:824
@ MUTT_FORMAT_NONE
No flags are set.
Definition render.h:37
#define NONULL(x)
Definition string2.h:44
Parsed Expando trees.
Definition expando.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_forward_trailer()

void mutt_forward_trailer ( struct Email * e,
FILE * fp,
struct ConfigSubset * sub )

Add a "end of forwarded message" text.

Parameters
eEmail
subConfig Subset
fpFile to write to

Definition at line 484 of file send.c.

485{
486 const struct Expando *c_forward_attribution_trailer = cs_subset_expando(sub, "forward_attribution_trailer");
487 if (!c_forward_attribution_trailer || !fp)
488 return;
489
490 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
491
492 struct Buffer *buf = buf_pool_get();
493 setlocale(LC_TIME, NONULL(c_attribution_locale));
494 mutt_make_string(buf, -1, c_forward_attribution_trailer, NULL, -1, e,
495 MUTT_FORMAT_NONE, NULL);
496 setlocale(LC_TIME, "");
497 fputc('\n', fp);
498 fputs(buf_string(buf), fp);
499 fputc('\n', fp);
500 buf_pool_release(&buf);
501}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ include_forward()

static int include_forward ( struct Mailbox * m,
struct Email * e,
FILE * fp_out,
struct ConfigSubset * sub )
static

Write out a forwarded message.

Parameters
mMailbox
eEmail
fp_outFile to write to
subConfig Subset
Return values
0Success
-1Failure

Definition at line 512 of file send.c.

514{
515 CopyHeaderFlags chflags = CH_DECODE;
517
518 struct Message *msg = mx_msg_open(m, e);
519 if (!msg)
520 {
521 return -1;
522 }
525
526 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
527 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT) && c_forward_decode)
528 {
529 /* make sure we have the user's passphrase before proceeding... */
531 {
532 mx_msg_close(m, &msg);
533 return -1;
534 }
535 }
536
537 mutt_forward_intro(e, fp_out, sub);
538
539 if (c_forward_decode)
540 {
541 cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
542
543 const bool c_weed = cs_subset_bool(sub, "weed");
544 if (c_weed)
545 {
546 chflags |= CH_WEED | CH_REORDER;
547 cmflags |= MUTT_CM_WEED;
548 }
549 }
550
551 const bool c_forward_quote = cs_subset_bool(sub, "forward_quote");
552 if (c_forward_quote)
553 cmflags |= MUTT_CM_PREFIX;
554
555 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
556 mx_msg_close(m, &msg);
557 mutt_forward_trailer(e, fp_out, sub);
558 return 0;
559}
void mutt_parse_mime_message(struct Email *e, FILE *fp)
Parse a MIME email.
Definition commands.c:627
@ CMD_MESSAGE_HOOK
:message-hook
Definition command.h:97
int mutt_copy_message(FILE *fp_out, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition copy_email.c:917
@ MUTT_CM_PREFIX
Quote the header and body.
Definition copy_email.h:43
@ MUTT_CM_DECODE
Decode the message body into text/plain.
Definition copy_email.h:44
@ MUTT_CM_CHARCONV
Perform character set conversions.
Definition copy_email.h:48
@ MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition copy_email.h:47
@ MUTT_CM_NONE
No flags are set.
Definition copy_email.h:41
uint32_t CopyHeaderFlags
Definition copy_email.h:89
@ CH_WEED
Weed the headers?
Definition copy_email.h:67
@ CH_DECODE
Do RFC2047 header decoding.
Definition copy_email.h:68
@ CH_REORDER
Re-order output of headers (specified by 'header-order')
Definition copy_email.h:73
uint16_t CopyMessageFlags
Definition copy_email.h:55
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition crypt.c:131
void exec_message_hook(struct Mailbox *m, struct Email *e, enum CommandId id)
Perform a message hook.
Definition exec.c:137
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition mx.c:1182
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition mx.c:1136
@ SEC_ENCRYPT
Email is encrypted.
Definition lib.h:92
#define WithCrypto
Definition lib.h:132
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition send.c:461
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition send.c:484
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition email.h:43
A local copy of an email.
Definition message.h:34
FILE * fp
pointer to the message data
Definition message.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ inline_forward_attachments()

static int inline_forward_attachments ( struct Mailbox * m,
struct Email * e,
struct Body *** plast,
enum QuadOption * forwardq,
struct ConfigSubset * sub )
static

Add attachments to an email, inline.

Parameters
[in]mMailbox
[in]eCurrent Email
[out]plastPointer to the last Attachment
[out]forwardqResult of asking the user to forward the attachments, e.g. MUTT_YES
[in]subConfig Subset
Return values
0Success
-1Error

Definition at line 571 of file send.c.

574{
575 struct Body **last = *plast;
576 struct Body *body = NULL;
577 struct AttachCtx *actx = NULL;
578 int rc = 0, i;
579
580 struct Message *msg = mx_msg_open(m, e);
581 if (!msg)
582 {
583 return -1;
584 }
585
588
589 actx = MUTT_MEM_CALLOC(1, struct AttachCtx);
590 actx->email = e;
591 actx->fp_root = msg->fp;
592
593 mutt_generate_recvattach_list(actx, actx->email, actx->email->body,
594 actx->fp_root, -1, 0, 0);
595
596 for (i = 0; i < actx->idxlen; i++)
597 {
598 body = actx->idx[i]->body;
599 if ((body->type != TYPE_MULTIPART) && mutt_prefer_as_attachment(body) &&
600 !((body->type == TYPE_APPLICATION) &&
601 (mutt_istr_equal(body->subtype, "pgp-signature") ||
602 mutt_istr_equal(body->subtype, "x-pkcs7-signature") ||
603 mutt_istr_equal(body->subtype, "pkcs7-signature"))))
604 {
605 /* Ask the quadoption only once */
606 if (*forwardq == MUTT_ABORT)
607 {
608 /* L10N: This is the prompt for $forward_attachments.
609 When inline forwarding ($mime_forward answered "no"), this prompts
610 whether to add non-decodable attachments from the original email.
611 Text/plain parts and the like will already be included in the
612 message contents, but other attachment, such as PDF files, will also
613 be added as attachments to the new mail, if this is answered yes. */
614 *forwardq = query_quadoption(_("Forward attachments?"), sub, "forward_attachments");
615 if (*forwardq != MUTT_YES)
616 {
617 if (*forwardq == -1)
618 rc = -1;
619 goto cleanup;
620 }
621 }
622 if (mutt_body_copy(actx->idx[i]->fp, last, body) == -1)
623 {
624 rc = -1;
625 goto cleanup;
626 }
627 last = &((*last)->next);
628 }
629 }
630
631cleanup:
632 *plast = last;
633 mx_msg_close(m, &msg);
634 mutt_actx_free(&actx);
635 return rc;
636}
void mutt_actx_free(struct AttachCtx **ptr)
Free an Attachment Context.
Definition attach.c:198
int mutt_body_copy(FILE *fp, struct Body **b_dst, struct Body *b_src)
Create a send-mode duplicate from a receive-mode body.
Definition copy_body.c:50
bool mutt_prefer_as_attachment(struct Body *b)
Do we want this part as an attachment?
Definition handler.c:1882
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition mime.h:33
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:677
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition quad.h:37
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
void mutt_generate_recvattach_list(struct AttachCtx *actx, struct Email *e, struct Body *b, FILE *fp, int parent_type, int level, bool decrypted)
Create a list of attachments.
A set of attachments.
Definition attach.h:65
FILE * fp_root
Used by recvattach for updating.
Definition attach.h:67
struct Email * email
Used by recvattach for updating.
Definition attach.h:66
struct AttachPtr ** idx
Array of attachments.
Definition attach.h:69
short idxlen
Number of attachmentes.
Definition attach.h:70
struct Body * body
Attachment.
Definition attach.h:37
FILE * fp
Used in the recvattach menu.
Definition attach.h:38
The body of an email.
Definition body.h:36
char * subtype
content-type subtype
Definition body.h:61
unsigned int type
content-type primary type, ContentType
Definition body.h:40
struct Body * body
List of MIME parts.
Definition email.h:69
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ format_attribution()

static void format_attribution ( const struct Expando * exp,
struct Email * e,
FILE * fp_out,
struct ConfigSubset * sub )
static

Format an attribution prefix/suffix.

Parameters
expExpando to format
eEmail
fp_outFile to write to
subConfig Subset

Definition at line 645 of file send.c.

647{
648 if (!exp || !fp_out)
649 return;
650
651 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
652
653 struct Buffer *buf = buf_pool_get();
654 setlocale(LC_TIME, NONULL(c_attribution_locale));
655 mutt_make_string(buf, -1, exp, NULL, -1, e, MUTT_FORMAT_NONE, NULL);
656 setlocale(LC_TIME, "");
657 fputs(buf_string(buf), fp_out);
658 fputc('\n', fp_out);
659 buf_pool_release(&buf);
660}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_make_attribution_intro()

void mutt_make_attribution_intro ( struct Email * e,
FILE * fp_out,
struct ConfigSubset * sub )

Add "on DATE, PERSON wrote" header.

Parameters
eEmail
fp_outFile to write to
subConfig Subset

Definition at line 668 of file send.c.

669{
670 format_attribution(cs_subset_expando(sub, "attribution_intro"), e, fp_out, sub);
671}
static void format_attribution(const struct Expando *exp, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Format an attribution prefix/suffix.
Definition send.c:645
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_make_attribution_trailer()

void mutt_make_attribution_trailer ( struct Email * e,
FILE * fp_out,
struct ConfigSubset * sub )

Add suffix to replied email text.

Parameters
eEmail
fp_outFile to write to
subConfig Subset

Definition at line 679 of file send.c.

680{
681 format_attribution(cs_subset_expando(sub, "attribution_trailer"), e, fp_out, sub);
682}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_make_greeting()

static void mutt_make_greeting ( struct Email * e,
FILE * fp_out,
struct ConfigSubset * sub )
static

Add greetings string.

Parameters
eEmail
fp_outFile to write to
subConfig Subset
See also
$greeting

Definition at line 692 of file send.c.

693{
694 const struct Expando *c_greeting = cs_subset_expando(sub, "greeting");
695 if (!c_greeting || !fp_out)
696 return;
697
698 struct Buffer *buf = buf_pool_get();
699
701 buf->dsize, NeoMutt->env, buf);
702
703 fputs(buf_string(buf), fp_out);
704 fputc('\n', fp_out);
705 buf_pool_release(&buf);
706}
int expando_filter(const struct Expando *exp, const struct ExpandoRenderCallback *erc, void *data, MuttFormatFlags flags, int max_cols, char **env_list, struct Buffer *buf)
Render an Expando and run the result through a filter.
Definition filter.c:139
const struct ExpandoRenderCallback GreetingRenderCallbacks[]
Callbacks for Greeting Expandos.
size_t dsize
Length of data.
Definition buffer.h:39
char ** env
Private copy of the environment variables.
Definition neomutt.h:57
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ include_reply()

static int include_reply ( struct Mailbox * m,
struct Email * e,
FILE * fp_out,
struct ConfigSubset * sub )
static

Generate the reply text for an email.

Parameters
mMailbox
eEmail
fp_outFile to write to
subConfig Subset
Return values
0Success
-1Failure

Definition at line 717 of file send.c.

719{
721 CopyHeaderFlags chflags = CH_DECODE;
722
723 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
724 {
725 /* make sure we have the user's passphrase before proceeding... */
727 return -1;
728 }
729
730 struct Message *msg = mx_msg_open(m, e);
731 if (!msg)
732 {
733 return -1;
734 }
737
738 mutt_make_attribution_intro(e, fp_out, sub);
739
740 const bool c_header = cs_subset_bool(sub, "header");
741 if (!c_header)
742 cmflags |= MUTT_CM_NOHEADER;
743
744 const bool c_weed = cs_subset_bool(sub, "weed");
745 if (c_weed)
746 {
747 chflags |= CH_WEED | CH_REORDER;
748 cmflags |= MUTT_CM_WEED;
749 }
750
751 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
752 mx_msg_close(m, &msg);
753
754 mutt_make_attribution_trailer(e, fp_out, sub);
755
756 return 0;
757}
@ MUTT_CM_REPLYING
Replying the message.
Definition copy_email.h:50
@ MUTT_CM_NOHEADER
Don't copy the message header.
Definition copy_email.h:42
void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition send.c:668
void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition send.c:679
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ choose_default_to()

static const struct AddressList * choose_default_to ( const struct Address * from,
const struct Envelope * env,
struct ConfigSubset * sub )
static

Pick the best 'to:' value.

Parameters
fromFrom Address
envEnvelope
subConfig Subset
Return values
ptrAddresses to use

Definition at line 766 of file send.c.

769{
770 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
771 if (!c_reply_self && mutt_addr_is_user(from))
772 {
773 /* mail is from the user, assume replying to recipients */
774 return &env->to;
775 }
776 else
777 {
778 return &env->from;
779 }
780}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ default_to()

static int default_to ( struct AddressList * to,
struct Envelope * env,
SendFlags flags,
int hmfupto,
struct ConfigSubset * sub )
static

Generate default email addresses.

Parameters
[in,out]to'To' address
[in]envEnvelope to populate
[in]flagsFlags, see SendFlags
[in]hmfuptoIf true, add 'followup-to' address to 'to' address
[in]subConfig Subset
Return values
0Success
-1Aborted

Definition at line 792 of file send.c.

794{
795 const struct Address *from = TAILQ_FIRST(&env->from);
796 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
797
798 if (flags && !TAILQ_EMPTY(&env->mail_followup_to) && (hmfupto == MUTT_YES))
799 {
800 mutt_addrlist_copy(to, &env->mail_followup_to, true);
801 return 0;
802 }
803
804 /* Exit now if we're setting up the default Cc list for list-reply
805 * (only set if Mail-Followup-To is present and honoured). */
806 if (flags & SEND_LIST_REPLY)
807 return 0;
808
809 const struct AddressList *default_to = choose_default_to(from, env, sub);
810
811 if (reply_to)
812 {
813 const bool from_is_reply_to = mutt_addr_cmp(from, reply_to);
814 const bool multiple_reply_to = reply_to &&
815 TAILQ_NEXT(TAILQ_FIRST(&env->reply_to), entries);
816
817 const bool c_ignore_list_reply_to = cs_subset_bool(sub, "ignore_list_reply_to");
818 const enum QuadOption c_reply_to = cs_subset_quad(sub, "reply_to");
819 if ((from_is_reply_to && !multiple_reply_to && !reply_to->personal) ||
820 (c_ignore_list_reply_to && mutt_is_mail_list(reply_to) &&
821 (mutt_addrlist_search(&env->to, reply_to) || mutt_addrlist_search(&env->cc, reply_to))))
822 {
823 /* If the Reply-To: address is a mailing list, assume that it was
824 * put there by the mailing list, and use the From: address
825 *
826 * We also take the from header if our correspondent has a reply-to
827 * header which is identical to the electronic mail address given
828 * in his From header, and the reply-to has no display-name. */
829 mutt_addrlist_copy(to, &env->from, false);
830 }
831 else if (!(from_is_reply_to && !multiple_reply_to) && (c_reply_to != MUTT_YES))
832 {
833 char prompt[256] = { 0 };
834 /* There are quite a few mailing lists which set the Reply-To:
835 * header field to the list address, which makes it quite impossible
836 * to send a message to only the sender of the message. This
837 * provides a way to do that. */
838 /* L10N: Asks whether the user respects the reply-to header.
839 If she says no, neomutt will reply to the from header's address instead. */
840 snprintf(prompt, sizeof(prompt), _("Reply to %s%s?"),
841 buf_string(reply_to->mailbox), multiple_reply_to ? ",..." : "");
842 switch (query_quadoption(prompt, sub, "reply_to"))
843 {
844 case MUTT_YES:
845 mutt_addrlist_copy(to, &env->reply_to, false);
846 break;
847
848 case MUTT_NO:
849 mutt_addrlist_copy(to, default_to, false);
850 break;
851
852 default:
853 return -1; /* abort */
854 }
855 }
856 else
857 {
858 mutt_addrlist_copy(to, &env->reply_to, false);
859 }
860 }
861 else
862 {
863 mutt_addrlist_copy(to, default_to, false);
864 }
865
866 return 0;
867}
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition address.c:774
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition address.c:901
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition address.c:918
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition helpers.c:192
QuadOption
Possible values for a quad-option.
Definition quad.h:36
#define TAILQ_FIRST(head)
Definition queue.h:780
static const struct AddressList * choose_default_to(const struct Address *from, const struct Envelope *env, struct ConfigSubset *sub)
Pick the best 'to:' value.
Definition send.c:766
static int default_to(struct AddressList *to, struct Envelope *env, SendFlags flags, int hmfupto, struct ConfigSubset *sub)
Generate default email addresses.
Definition send.c:792
struct Buffer * personal
Real name of address.
Definition address.h:36
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition envelope.h:65
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_fetch_recips()

int mutt_fetch_recips ( struct Envelope * out,
struct Envelope * in,
SendFlags flags,
struct ConfigSubset * sub )

Generate recpients for a reply email.

Parameters
outEnvelope to populate
inEnvelope of source email
flagsFlags, see SendFlags
subConfig Subset
Return values
0Success
-1Failure

Definition at line 878 of file send.c.

880{
881 enum QuadOption hmfupto = MUTT_ABORT;
882 const struct Address *followup_to = TAILQ_FIRST(&in->mail_followup_to);
883
884 if ((flags & (SEND_LIST_REPLY | SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) && followup_to)
885 {
886 char prompt[256] = { 0 };
887 snprintf(prompt, sizeof(prompt), _("Follow-up to %s%s?"),
888 buf_string(followup_to->mailbox),
889 TAILQ_NEXT(TAILQ_FIRST(&in->mail_followup_to), entries) ? ",..." : "");
890
891 hmfupto = query_quadoption(prompt, sub, "honor_followup_to");
892 if (hmfupto == MUTT_ABORT)
893 return -1;
894 }
895
896 if (flags & SEND_LIST_REPLY)
897 {
898 add_mailing_lists(&out->to, &in->to, &in->cc);
899
900 if (followup_to && (hmfupto == MUTT_YES) &&
901 (default_to(&out->cc, in, flags & SEND_LIST_REPLY, (hmfupto == MUTT_YES), sub) == MUTT_ABORT))
902 {
903 return -1; /* abort */
904 }
905 }
906 else if (flags & SEND_TO_SENDER)
907 {
908 mutt_addrlist_copy(&out->to, &in->from, false);
909 }
910 else
911 {
912 if (default_to(&out->to, in, flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY),
913 (hmfupto == MUTT_YES), sub) == -1)
914 {
915 return -1; /* abort */
916 }
917
918 if ((flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) &&
919 (!followup_to || (hmfupto != MUTT_YES)))
920 {
921 /* if(!mutt_addr_is_user(in->to)) */
922 if (flags & SEND_GROUP_REPLY)
923 mutt_addrlist_copy(&out->cc, &in->to, true);
924 else
925 mutt_addrlist_copy(&out->to, &in->to, true);
926 mutt_addrlist_copy(&out->cc, &in->cc, true);
927 }
928 }
929 return 0;
930}
static void add_mailing_lists(struct AddressList *out, const struct AddressList *t, const struct AddressList *c)
Search Address lists for mailing lists.
Definition send.c:157
@ SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition send.h:58
@ SEND_TO_SENDER
Compose new email to sender.
Definition send.h:57
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ add_references()

static void add_references ( struct ListHead * head,
struct Envelope * env )
static

Add the email's references to a list.

Parameters
headList of references
envEnvelope of message

Definition at line 937 of file send.c.

938{
939 struct ListHead *src = STAILQ_EMPTY(&env->references) ? &env->in_reply_to : &env->references;
940 mutt_list_copy_tail(head, src);
941}
void mutt_list_copy_tail(struct ListHead *dst, const struct ListHead *src)
Copy a list into another list.
Definition list.c:275
#define STAILQ_EMPTY(head)
Definition queue.h:382
struct ListHead references
message references (in reverse order)
Definition envelope.h:83
struct ListHead in_reply_to
in-reply-to header content
Definition envelope.h:84
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ add_message_id()

static void add_message_id ( struct ListHead * head,
struct Envelope * env )
static

Add the email's message ID to a list.

Parameters
headList of message IDs
envEnvelope of message

Definition at line 948 of file send.c.

949{
950 if (env->message_id)
951 {
953 }
954}
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition list.c:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_fix_reply_recipients()

void mutt_fix_reply_recipients ( struct Envelope * env,
struct ConfigSubset * sub )

Remove duplicate recipients.

Parameters
envEnvelope to fix
subConfig Subset

Definition at line 961 of file send.c.

962{
963 const bool c_me_too = cs_subset_bool(sub, "me_too");
964 if (!c_me_too)
965 {
966 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
967
968 /* the order is important here. do the CC: first so that if the
969 * the user is the only recipient, it ends up on the TO: field */
970 remove_user(&env->cc, TAILQ_EMPTY(&env->to));
971 remove_user(&env->to, TAILQ_EMPTY(&env->cc) || c_reply_self);
972 }
973
974 /* the CC field can get cluttered, especially with lists */
977 mutt_addrlist_remove_xrefs(&env->to, &env->cc);
978
979 if (!TAILQ_EMPTY(&env->cc) && TAILQ_EMPTY(&env->to))
980 {
981 TAILQ_SWAP(&env->to, &env->cc, Address, entries);
982 }
983}
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition address.c:1442
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition address.c:1406
#define TAILQ_SWAP(head1, head2, type, field)
Definition queue.h:937
static void remove_user(struct AddressList *al, bool leave_only)
Remove any address which matches the current user.
Definition send.c:138
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_make_forward_subject()

void mutt_make_forward_subject ( struct Envelope * env,
struct Email * e,
struct ConfigSubset * sub )

Create a subject for a forwarded email.

Parameters
envEnvelope for result
eEmail
subConfig Subset

Definition at line 991 of file send.c.

992{
993 if (!env)
994 return;
995
996 const struct Expando *c_forward_format = cs_subset_expando(sub, "forward_format");
997
998 struct Buffer *buf = buf_pool_get();
999 /* set the default subject for the message. */
1000 mutt_make_string(buf, -1, c_forward_format, NULL, -1, e, MUTT_FORMAT_NONE, NULL);
1002 buf_pool_release(&buf);
1003}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_make_misc_reply_headers()

void mutt_make_misc_reply_headers ( struct Envelope * env,
struct Envelope * env_cur,
struct ConfigSubset * sub )

Set subject for a reply.

Parameters
envEnvelope for result
env_curEnvelope of source email
subConfig Subset

Definition at line 1011 of file send.c.

1013{
1014 if (!env || !env_cur)
1015 return;
1016
1017 /* This takes precedence over a subject that might have
1018 * been taken from a List-Post header. Is that correct? */
1019 if (env_cur->real_subj)
1020 {
1021 char *subj = NULL;
1022 mutt_str_asprintf(&subj, "Re: %s", env_cur->real_subj);
1023 mutt_env_set_subject(env, subj);
1024 FREE(&subj);
1025 }
1026 else if (!env->subject)
1027 {
1028 const char *const c_empty_subject = cs_subset_string(sub, "empty_subject");
1029 mutt_env_set_subject(env, c_empty_subject);
1030 }
1031}
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:808
char *const real_subj
Offset of the real subject.
Definition envelope.h:71
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_add_to_reference_headers()

void mutt_add_to_reference_headers ( struct Envelope * env,
struct Envelope * env_cur,
struct ConfigSubset * sub )

Generate references for a reply email.

Parameters
envEnvelope for result
env_curEnvelope of source email
subConfig Subset

Definition at line 1039 of file send.c.

1041{
1042 add_references(&env->references, env_cur);
1043 add_message_id(&env->references, env_cur);
1044 add_message_id(&env->in_reply_to, env_cur);
1045
1046 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
1047 if (OptNewsSend && c_x_comment_to && !TAILQ_EMPTY(&env_cur->from))
1049}
const char * mutt_get_name(const struct Address *a)
Pick the best name to display from an address.
Definition sort.c:138
static void add_message_id(struct ListHead *head, struct Envelope *env)
Add the email's message ID to a list.
Definition send.c:948
static void add_references(struct ListHead *head, struct Envelope *env)
Add the email's references to a list.
Definition send.c:937
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ make_reference_headers()

static void make_reference_headers ( struct EmailArray * ea,
struct Envelope * env,
struct ConfigSubset * sub )
static

Generate reference headers for an email.

Parameters
eaArray of source Emails
envEnvelope for result
subConfig Subset

Definition at line 1057 of file send.c.

1059{
1060 if (!ea || !env || ARRAY_EMPTY(ea))
1061 return;
1062
1063 struct Email **ep = NULL;
1064 ARRAY_FOREACH(ep, ea)
1065 {
1066 struct Email *e = *ep;
1068 }
1069
1070 /* if there's more than entry in In-Reply-To (i.e. message has multiple
1071 * parents), don't generate a References: header as it's discouraged by
1072 * RFC2822, sect. 3.6.4 */
1073 if ((ARRAY_SIZE(ea) > 1) && !STAILQ_EMPTY(&env->in_reply_to) &&
1075 {
1077 }
1078}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
#define STAILQ_FIRST(head)
Definition queue.h:388
#define STAILQ_NEXT(elm, field)
Definition queue.h:439
void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *env_cur, struct ConfigSubset *sub)
Generate references for a reply email.
Definition send.c:1039
The envelope/body of an email.
Definition email.h:39
struct Envelope * env
Envelope information.
Definition email.h:68
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ envelope_defaults()

static int envelope_defaults ( struct Envelope * env,
struct EmailArray * ea,
SendFlags flags,
struct ConfigSubset * sub )
static

Fill in some defaults for a new email.

Parameters
envEnvelope for result
eaArray of Emails to use
flagsFlags, see SendFlags
subConfig Subset
Return values
0Success
-1Failure

Definition at line 1089 of file send.c.

1091{
1092 if (!ea || ARRAY_EMPTY(ea))
1093 return -1;
1094
1095 struct Email *e_cur = *ARRAY_GET(ea, 0);
1096 bool single = (ARRAY_SIZE(ea) == 1);
1097
1098 struct Envelope *env_cur = e_cur->env;
1099 if (!env_cur)
1100 return -1;
1101
1102 if (flags & (SEND_REPLY | SEND_TO_SENDER))
1103 {
1104 if ((flags & SEND_NEWS))
1105 {
1106 /* in case followup set Newsgroups: with Followup-To: if it present */
1107 if (!env->newsgroups && !mutt_istr_equal(env_cur->followup_to, "poster"))
1108 {
1109 env->newsgroups = mutt_str_dup(env_cur->followup_to);
1110 }
1111 }
1112 else if (!single)
1113 {
1114 struct Email **ep = NULL;
1115 ARRAY_FOREACH(ep, ea)
1116 {
1117 struct Email *e = *ep;
1118 if (mutt_fetch_recips(env, e->env, flags, sub) == -1)
1119 return -1;
1120 }
1121 }
1122 else if (mutt_fetch_recips(env, env_cur, flags, sub) == -1)
1123 {
1124 return -1;
1125 }
1126
1127 if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
1128 {
1129 mutt_error(_("No mailing lists found"));
1130 return -1;
1131 }
1132
1133 if (flags & SEND_REPLY)
1134 {
1135 mutt_make_misc_reply_headers(env, env_cur, sub);
1136 make_reference_headers(ea, env, sub);
1137 }
1138 }
1139 else if (flags & SEND_FORWARD)
1140 {
1141 mutt_make_forward_subject(env, e_cur, sub);
1142
1143 const bool c_forward_references = cs_subset_bool(sub, "forward_references");
1144 if (c_forward_references)
1145 make_reference_headers(ea, env, sub);
1146 }
1147
1148 return 0;
1149}
#define ARRAY_GET(head, idx)
Return the element at index.
Definition array.h:109
void mutt_make_misc_reply_headers(struct Envelope *env, struct Envelope *env_cur, struct ConfigSubset *sub)
Set subject for a reply.
Definition send.c:1011
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition send.c:991
static void make_reference_headers(struct EmailArray *ea, struct Envelope *env, struct ConfigSubset *sub)
Generate reference headers for an email.
Definition send.c:1057
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition send.c:878
@ SEND_FORWARD
Forward email.
Definition send.h:49
@ SEND_NEWS
Reply to a news article.
Definition send.h:59
The header of an Email.
Definition envelope.h:57
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ generate_body()

static int generate_body ( FILE * fp_tmp,
struct Email * e,
SendFlags flags,
struct Mailbox * m,
struct EmailArray * ea,
struct ConfigSubset * sub )
static

Create a new email body.

Parameters
fp_tmpStream for outgoing message
eEmail for outgoing message
flagsCompose mode, see SendFlags
mMailbox
eaArray of Emails to use
subConfig Subset
Return values
0Success
-1Error

Definition at line 1162 of file send.c.

1164{
1165 /* An EmailList is required for replying and forwarding */
1166 if (!ea && (flags & (SEND_REPLY | SEND_FORWARD)))
1167 return -1;
1168
1169 /* Handle reply: optionally include quoted original message(s) */
1170 if (flags & SEND_REPLY)
1171 {
1172 enum QuadOption ans = query_quadoption(_("Include message in reply?"), sub, "include");
1173 if (ans == MUTT_ABORT)
1174 return -1;
1175
1176 if (ans == MUTT_YES)
1177 {
1178 mutt_message(_("Including quoted message..."));
1179 struct Email **ep = NULL;
1180 size_t count = ARRAY_SIZE(ea) - 1;
1181 ARRAY_FOREACH(ep, ea)
1182 {
1183 if (include_reply(m, *ep, fp_tmp, sub) == -1)
1184 {
1185 mutt_error(_("Could not include all requested messages"));
1186 return -1;
1187 }
1188 if (ARRAY_FOREACH_IDX_ep < count)
1189 {
1190 fputc('\n', fp_tmp);
1191 }
1192 }
1193 }
1194 }
1195 else if (flags & SEND_FORWARD)
1196 {
1197 /* Handle forward: either as message/rfc822 MIME attachment
1198 * or inline with optional attachment forwarding */
1199 enum QuadOption ans = query_quadoption(_("Forward as attachment?"), sub, "mime_forward");
1200 if (ans == MUTT_YES)
1201 {
1202 struct Body *last = e->body;
1203
1204 mutt_message(_("Preparing forwarded message..."));
1205
1206 while (last && last->next)
1207 last = last->next;
1208
1209 struct Email **ep = NULL;
1210 ARRAY_FOREACH(ep, ea)
1211 {
1212 struct Body *tmp = mutt_make_message_attach(m, *ep, false, sub);
1213 if (last)
1214 {
1215 last->next = tmp;
1216 last = tmp;
1217 }
1218 else
1219 {
1220 last = tmp;
1221 e->body = tmp;
1222 }
1223 }
1224 }
1225 else if (ans != MUTT_ABORT)
1226 {
1227 enum QuadOption forwardq = MUTT_ABORT;
1228 struct Body **last = NULL;
1229
1230 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
1231 const enum QuadOption c_forward_attachments = cs_subset_quad(sub, "forward_attachments");
1232 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1233 {
1234 last = &e->body;
1235 while (*last)
1236 last = &((*last)->next);
1237 }
1238
1239 struct Email **ep = NULL;
1240 ARRAY_FOREACH(ep, ea)
1241 {
1242 struct Email *e_cur = *ep;
1243 include_forward(m, e_cur, fp_tmp, sub);
1244 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1245 {
1246 if (inline_forward_attachments(m, e_cur, &last, &forwardq, sub) != 0)
1247 return -1;
1248 }
1249 }
1250 }
1251 else
1252 {
1253 return -1;
1254 }
1255 }
1256 else if (((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY))
1257 {
1258 /* Attach the user's PGP public key to the message */
1259 struct Body *b = NULL;
1260
1261 if (((WithCrypto & APPLICATION_PGP) != 0) && !(b = crypt_pgp_make_key_attachment()))
1262 {
1263 return -1;
1264 }
1265
1266 b->next = e->body;
1267 e->body = b;
1268 }
1269
1271
1272 return 0;
1273}
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition cryptglue.c:349
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition lib.h:106
static int include_reply(struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Generate the reply text for an email.
Definition send.c:717
static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Write out a forwarded message.
Definition send.c:512
static int inline_forward_attachments(struct Mailbox *m, struct Email *e, struct Body ***plast, enum QuadOption *forwardq, struct ConfigSubset *sub)
Add attachments to an email, inline.
Definition send.c:571
@ SEND_KEY
Mail a PGP public key.
Definition send.h:52
struct Body * mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg, struct ConfigSubset *sub)
Create a message attachment.
Definition sendlib.c:453
struct Body * next
next attachment in the list
Definition body.h:72
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_set_followup_to()

void mutt_set_followup_to ( struct Envelope * env,
struct ConfigSubset * sub )

Set followup-to field.

Parameters
envEnvelope to modify
subConfig Subset

Definition at line 1280 of file send.c.

1281{
1282 /* Only generate the Mail-Followup-To if the user has requested it, and
1283 * it hasn't already been set */
1284
1285 const bool c_followup_to = cs_subset_bool(sub, "followup_to");
1286 if (!c_followup_to)
1287 return;
1288 if (OptNewsSend)
1289 {
1290 if (!env->followup_to && env->newsgroups && (strrchr(env->newsgroups, ',')))
1291 env->followup_to = mutt_str_dup(env->newsgroups);
1292 return;
1293 }
1294
1295 if (TAILQ_EMPTY(&env->mail_followup_to))
1296 {
1297 if (mutt_is_list_recipient(false, env))
1298 {
1299 /* this message goes to known mailing lists, so create a proper
1300 * mail-followup-to header */
1301
1302 mutt_addrlist_copy(&env->mail_followup_to, &env->to, false);
1303 mutt_addrlist_copy(&env->mail_followup_to, &env->cc, true);
1304 }
1305
1306 /* remove ourselves from the mail-followup-to header */
1307 remove_user(&env->mail_followup_to, false);
1308
1309 /* If we are not subscribed to any of the lists in question, re-add
1310 * ourselves to the mail-followup-to header. The mail-followup-to header
1311 * generated is a no-op with group-reply, but makes sure list-reply has the
1312 * desired effect. */
1313
1314 if (!TAILQ_EMPTY(&env->mail_followup_to) &&
1316 {
1317 struct AddressList *al = NULL;
1318 if (!TAILQ_EMPTY(&env->reply_to))
1319 al = &env->reply_to;
1320 else if (!TAILQ_EMPTY(&env->from))
1321 al = &env->from;
1322
1323 if (al)
1324 {
1325 struct Address *a = NULL;
1326 TAILQ_FOREACH_REVERSE(a, al, AddressList, entries)
1327 {
1329 }
1330 }
1331 else
1332 {
1334 }
1335 }
1336
1338 }
1339}
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition address.c:1500
bool mutt_is_subscribed_list_recipient(bool all_addr, struct Envelope *env)
Matches subscribed mailing lists.
Definition exec.c:494
bool mutt_is_list_recipient(bool all_addr, struct Envelope *env)
Matches known mailing lists.
Definition exec.c:507
#define TAILQ_FOREACH_REVERSE(var, head, headname, field)
Definition queue.h:802
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition send.c:1404
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_reverse_name()

static void set_reverse_name ( struct AddressList * al,
struct Envelope * env,
struct ConfigSubset * sub )
static

Try to set the 'from' field from the recipients.

Parameters
alAddressList to prepend the found address
envEnvelope to use
subConfig Subset

Look through the recipients of the message we are replying to, and if we find an address that matches $alternates, we use that as the default from field

Definition at line 1351 of file send.c.

1353{
1354 struct Address *a = NULL;
1355 if (TAILQ_EMPTY(al))
1356 {
1357 TAILQ_FOREACH(a, &env->to, entries)
1358 {
1359 if (mutt_addr_is_user(a))
1360 {
1362 break;
1363 }
1364 }
1365 }
1366
1367 if (TAILQ_EMPTY(al))
1368 {
1369 TAILQ_FOREACH(a, &env->cc, entries)
1370 {
1371 if (mutt_addr_is_user(a))
1372 {
1374 break;
1375 }
1376 }
1377 }
1378
1379 if (TAILQ_EMPTY(al))
1380 {
1381 struct Address *from = TAILQ_FIRST(&env->from);
1382 if (from && mutt_addr_is_user(from))
1383 {
1385 }
1386 }
1387
1388 if (!TAILQ_EMPTY(al))
1389 {
1390 /* when $reverse_real_name is not set, clear the personal name so that it
1391 * may be set via a reply- or send-hook. */
1392
1393 const bool c_reverse_real_name = cs_subset_bool(sub, "reverse_real_name");
1394 if (!c_reverse_real_name)
1395 FREE(&TAILQ_FIRST(al)->personal);
1396 }
1397}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_default_from()

struct Address * mutt_default_from ( struct ConfigSubset * sub)

Get a default 'from' Address.

Parameters
subConfig Subset
Return values
ptrNewly allocated Address

Definition at line 1404 of file send.c.

1405{
1406 /* Note: We let $from override $real_name here.
1407 * Is this the right thing to do?
1408 */
1409
1410 const struct Address *c_from = cs_subset_address(sub, "from");
1411 if (c_from)
1412 {
1413 return mutt_addr_copy(c_from);
1414 }
1415
1416 char domain[1024] = { 0 };
1417 const char *mailbox = NeoMutt->username;
1418 const bool c_use_domain = cs_subset_bool(sub, "use_domain");
1419 if (c_use_domain)
1420 {
1421 snprintf(domain, sizeof(domain), "%s@%s", NONULL(NeoMutt->username),
1422 NONULL(mutt_fqdn(true, sub)));
1423 mailbox = domain;
1424 }
1425
1426 return mutt_addr_create(NULL, mailbox);
1427}
struct Address * mutt_addr_create(const char *personal, const char *mailbox)
Create and populate a new Address.
Definition address.c:414
const struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition sendlib.c:713
char * username
User's login name.
Definition neomutt.h:56
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ invoke_mta()

static int invoke_mta ( struct Mailbox * m,
struct Email * e,
struct ConfigSubset * sub )
static

Send an email.

Parameters
mMailbox
eEmail
subConfig Subset
Return values
0Success
-1Failure

Definition at line 1437 of file send.c.

1438{
1439 struct Buffer *tempfile = NULL;
1440 int rc = -1;
1441
1442 /* Write out the message in MIME form. */
1443 tempfile = buf_pool_get();
1444 buf_mktemp(tempfile);
1445 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
1446 if (!fp_tmp)
1447 goto cleanup;
1448
1449 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
1450 const char *const c_smtp_url = cs_subset_string(sub, "smtp_url");
1451 if (c_smtp_url)
1452 cs_subset_str_native_set(sub, "write_bcc", false, NULL);
1453
1455 false, mutt_should_hide_protected_subject(e), sub);
1456
1457 cs_subset_str_native_set(sub, "write_bcc", c_write_bcc, NULL);
1458
1459 fputc('\n', fp_tmp); /* tie off the header. */
1460
1461 if ((mutt_write_mime_body(e->body, fp_tmp, sub) == -1))
1462 goto cleanup;
1463
1464 if (mutt_file_fclose(&fp_tmp) != 0)
1465 {
1466 mutt_perror("%s", buf_string(tempfile));
1467 unlink(buf_string(tempfile));
1468 goto cleanup;
1469 }
1470
1471 if (OptNewsSend)
1472 goto sendmail;
1473
1474 if (c_smtp_url)
1475 {
1476 rc = mutt_smtp_send(&e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1477 buf_string(tempfile), (e->body->encoding == ENC_8BIT), sub);
1478 goto cleanup;
1479 }
1480
1481sendmail:
1482 rc = mutt_invoke_sendmail(m, &e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1483 buf_string(tempfile), (e->body->encoding == ENC_8BIT), sub);
1484cleanup:
1485 if (fp_tmp)
1486 {
1487 mutt_file_fclose(&fp_tmp);
1488 unlink(buf_string(tempfile));
1489 }
1490 buf_pool_release(&tempfile);
1491 return rc;
1492}
bool mutt_should_hide_protected_subject(struct Email *e)
Should NeoMutt hide the protected subject?
Definition crypt.c:1105
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
@ ENC_8BIT
8-bit text
Definition mime.h:50
int mutt_write_mime_body(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition body.c:302
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *b, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
Write out one RFC822 header line.
Definition header.c:578
@ MUTT_WRITE_HEADER_NORMAL
A normal Email, write full header + MIME headers.
Definition header.h:38
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:299
int mutt_smtp_send(const struct AddressList *from, const struct AddressList *to, const struct AddressList *cc, const struct AddressList *bcc, const char *msgfile, bool eightbit, struct ConfigSubset *sub)
Send a message using SMTP.
Definition smtp.c:1132
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition subset.c:303
#define buf_mktemp(buf)
Definition tmp.h:33
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_encode_descriptions()

void mutt_encode_descriptions ( struct Body * b,
bool recurse,
struct ConfigSubset * sub )

RFC2047 encode the content-descriptions.

Parameters
bBody of email
recurseIf true, encode children parts
subConfig Subset

Definition at line 1500 of file send.c.

1501{
1502 const struct Slist *const c_send_charset = cs_subset_slist(sub, "send_charset");
1503 for (struct Body *t = b; t; t = t->next)
1504 {
1505 if (t->description)
1506 {
1507 rfc2047_encode(&t->description, NULL, sizeof("Content-Description:"), c_send_charset);
1508 }
1509 if (recurse && t->parts)
1510 mutt_encode_descriptions(t->parts, recurse, sub);
1511 }
1512}
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition helpers.c:242
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition rfc2047.c:636
void mutt_encode_descriptions(struct Body *b, bool recurse, struct ConfigSubset *sub)
RFC2047 encode the content-descriptions.
Definition send.c:1500
String list.
Definition slist.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ decode_descriptions()

static void decode_descriptions ( struct Body * b)
static

RFC2047 decode them in case of an error.

Parameters
bMIME parts to decode

Definition at line 1518 of file send.c.

1519{
1520 for (struct Body *t = b; t; t = t->next)
1521 {
1522 if (t->description)
1523 {
1524 rfc2047_decode(&t->description);
1525 }
1526 if (t->parts)
1527 decode_descriptions(t->parts);
1528 }
1529}
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:669
static void decode_descriptions(struct Body *b)
RFC2047 decode them in case of an error.
Definition send.c:1518
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fix_end_of_file()

static void fix_end_of_file ( const char * data)
static

Ensure a file ends with a linefeed.

Parameters
dataName of file to fix

Definition at line 1535 of file send.c.

1536{
1537 FILE *fp = mutt_file_fopen(data, "a+");
1538 if (!fp)
1539 return;
1540
1541 if ((mutt_file_get_size_fp(fp) > 0) && mutt_file_seek(fp, -1, SEEK_END))
1542 {
1543 int c = fgetc(fp);
1544 if (c != '\n')
1545 fputc('\n', fp);
1546 }
1547 mutt_file_fclose(&fp);
1548}
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1432
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_resend_message()

int mutt_resend_message ( FILE * fp,
struct Mailbox * m,
struct Email * e_cur,
struct ConfigSubset * sub )

Resend an email.

Parameters
fpFile containing email
mMailbox
e_curEmail to resend
subConfig Subset
Return values
0Message was successfully sent
-1Message was aborted or an error occurred
1Message was postponed

Definition at line 1560 of file send.c.

1562{
1563 struct Email *e_new = email_new();
1564
1565 if (mutt_prepare_template(fp, m, e_new, e_cur, true) < 0)
1566 {
1567 email_free(&e_new);
1568 return -1;
1569 }
1570
1571 if (WithCrypto)
1572 {
1573 /* mutt_prepare_template doesn't always flip on an application bit.
1574 * so fix that here */
1575 if (!(e_new->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1576 {
1577 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
1578 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_smime_is_default)
1579 e_new->security |= APPLICATION_SMIME;
1580 else if (WithCrypto & APPLICATION_PGP)
1581 e_new->security |= APPLICATION_PGP;
1582 else
1583 e_new->security |= APPLICATION_SMIME;
1584 }
1585
1586 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
1587 if (c_crypt_opportunistic_encrypt)
1588 {
1589 e_new->security |= SEC_OPPENCRYPT;
1591 }
1592 }
1593
1594 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
1595 ARRAY_ADD(&ea, e_cur);
1596 int rc = mutt_send_message(SEND_RESEND, e_new, NULL, m, &ea, sub);
1597 ARRAY_FREE(&ea);
1598
1599 return rc;
1600}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
void crypt_opportunistic_encrypt(struct Email *e)
Can all recipients be determined.
Definition crypt.c:1050
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
@ SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition lib.h:100
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition lib.h:107
int mutt_prepare_template(FILE *fp, struct Mailbox *m, struct Email *e_new, struct Email *e, bool resend)
Prepare a message template.
Definition postpone.c:490
int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
Send an email.
Definition send.c:2030
@ SEND_RESEND
Reply using the current email as a template.
Definition send.h:53
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ is_reply()

static bool is_reply ( struct Email * reply,
struct Email * orig )
static

Is one email a reply to another?

Parameters
replyEmail to test
origOriginal email
Return values
trueIt is a reply
falseIt is not a reply

Definition at line 1609 of file send.c.

1610{
1611 if (!reply || !reply->env || !orig || !orig->env)
1612 return false;
1613 return mutt_list_find(&orig->env->references, reply->env->message_id) ||
1614 mutt_list_find(&orig->env->in_reply_to, reply->env->message_id);
1615}
struct ListNode * mutt_list_find(const struct ListHead *h, const char *data)
Find a string in a List.
Definition list.c:103
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ search_attach_keyword()

static bool search_attach_keyword ( char * filename,
struct ConfigSubset * sub )
static

Search an email for 'attachment' keywords.

Parameters
filenameFilename
subConfig Subset
Return values
trueThe regex matches in the email

Search an email for the regex in $abort_noattach_regex. A match might indicate that the user should have attached something.

Note
Quoted lines (as defined by $quote_regex) are ignored

Definition at line 1628 of file send.c.

1629{
1630 const struct Regex *c_abort_noattach_regex = cs_subset_regex(sub, "abort_noattach_regex");
1631 const struct Regex *c_quote_regex = cs_subset_regex(sub, "quote_regex");
1632
1633 /* Search for the regex in `$abort_noattach_regex` within a file */
1634 if (!c_abort_noattach_regex || !c_abort_noattach_regex->regex ||
1635 !c_quote_regex || !c_quote_regex->regex)
1636 {
1637 return false;
1638 }
1639
1640 FILE *fp_att = mutt_file_fopen(filename, "r");
1641 if (!fp_att)
1642 return false;
1643
1644 char *inputline = MUTT_MEM_MALLOC(1024, char);
1645 bool found = false;
1646 while (!feof(fp_att) && fgets(inputline, 1024, fp_att))
1647 {
1648 if (!mutt_is_quote_line(inputline, NULL) &&
1649 mutt_regex_match(c_abort_noattach_regex, inputline))
1650 {
1651 found = true;
1652 break;
1653 }
1654 }
1655 FREE(&inputline);
1656 mutt_file_fclose(&fp_att);
1657 return found;
1658}
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition helpers.c:217
bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition display.c:324
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition regex.c:614
Cached regular expression.
Definition regex3.h:85
regex_t * regex
compiled expression
Definition regex3.h:87
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ save_fcc()

static int save_fcc ( struct Mailbox * m,
struct Email * e,
struct Buffer * fcc,
struct Body * clear_content,
char * pgpkeylist,
SendFlags flags,
char ** finalpath,
struct ConfigSubset * sub )
static

Save an Email to a 'sent mail' folder.

Parameters
[in]mCurrent Mailbox
[in]eEmail to save
[in]fccFolder to save to (can be comma-separated list)
[in]clear_contentCleartext content of Email
[in]pgpkeylistList of pgp keys
[in]flagsSend mode, see SendFlags
[out]finalpathPath of final folder
[in]subConfig Subset
Return values
0Success
-1Error

Definition at line 1673 of file send.c.

1676{
1677 int rc = 0;
1678 struct Body *save_content = NULL;
1679
1680 expand_path(fcc, false);
1681
1682 if (buf_is_empty(fcc) || mutt_str_equal("/dev/null", buf_string(fcc)))
1683 return rc;
1684
1685 struct Body *tmpbody = e->body;
1686 struct Body *save_sig = NULL;
1687 struct Body *save_parts = NULL;
1688
1689 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
1690 /* Before sending, we don't allow message manipulation because it
1691 * will break message signatures. This is especially complicated by
1692 * Protected Headers. */
1693 if (!c_fcc_before_send)
1694 {
1695 const bool c_fcc_clear = cs_subset_bool(sub, "fcc_clear");
1696 if ((WithCrypto != 0) &&
1697 (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) && c_fcc_clear)
1698 {
1699 e->body = clear_content;
1702 mutt_param_delete(&e->body->parameter, "protected-headers");
1703 }
1704
1705 const enum QuadOption c_fcc_attach = cs_subset_quad(sub, "fcc_attach");
1706
1707 /* check to see if the user wants copies of all attachments */
1708 bool save_atts = true;
1709 if (e->body->type == TYPE_MULTIPART)
1710 {
1711 /* In batch mode, save attachments if the quadoption is yes or ask-yes */
1712 if (flags & SEND_BATCH)
1713 {
1714 if ((c_fcc_attach == MUTT_NO) || (c_fcc_attach == MUTT_ASKNO))
1715 save_atts = false;
1716 }
1717 else if (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES)
1718 {
1719 save_atts = false;
1720 }
1721 }
1722 if (!save_atts)
1723 {
1724 if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) &&
1725 (mutt_str_equal(e->body->subtype, "encrypted") ||
1726 mutt_str_equal(e->body->subtype, "signed")))
1727 {
1728 if ((clear_content->type == TYPE_MULTIPART) &&
1729 (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES))
1730 {
1731 if (!(e->security & SEC_ENCRYPT) && (e->security & SEC_SIGN))
1732 {
1733 /* save initial signature and attachments */
1734 save_sig = e->body->parts->next;
1735 save_parts = clear_content->parts->next;
1736 }
1737
1738 /* this means writing only the main part */
1739 e->body = clear_content->parts;
1740
1741 if (mutt_protect(e, pgpkeylist, false) == -1)
1742 {
1743 /* we can't do much about it at this point, so
1744 * fallback to saving the whole thing to fcc */
1745 e->body = tmpbody;
1746 save_sig = NULL;
1747 goto full_fcc;
1748 }
1749
1750 save_content = e->body;
1751 }
1752 }
1753 else
1754 {
1755 if (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES)
1756 e->body = e->body->parts;
1757 }
1758 }
1759 }
1760
1761full_fcc:
1762 if (e->body)
1763 {
1764 /* update received time so that when storing to a mbox-style folder
1765 * the From_ line contains the current time instead of when the
1766 * message was first postponed. */
1767 e->received = mutt_date_now();
1768 rc = mutt_write_multiple_fcc(buf_string(fcc), e, NULL, false, NULL, finalpath, sub);
1769 if (rc && (flags & SEND_BATCH))
1770 {
1771 /* Printed when an Fcc in batch mode fails. */
1772 mutt_error(_("Warning: Fcc to %s failed"), buf_string(fcc));
1773 return rc;
1774 }
1775
1776 while (rc && !(flags & SEND_BATCH))
1777 {
1779 int choice = mw_multi_choice(
1780 /* L10N: Called when saving to $record or Fcc failed after sending.
1781 (r)etry tries the same mailbox again.
1782 alternate (m)ailbox prompts for a different mailbox to try.
1783 (s)kip aborts saving. */
1784 _("Fcc failed. (r)etry, alternate (m)ailbox, or (s)kip?"),
1785 /* L10N: These correspond to the "Fcc failed" multi-choice prompt
1786 (r)etry, alternate (m)ailbox, or (s)kip.
1787 Any similarity to famous leaders of the FSF is coincidental. */
1788 _("rms"));
1789 switch (choice)
1790 {
1791 case 2: /* alternate (m)ailbox */
1792 /* L10N: This is the prompt to enter an "alternate (m)ailbox" when the
1793 initial Fcc fails. */
1794 rc = mw_enter_fname(_("Fcc mailbox"), fcc, true, m, false, NULL, NULL, MUTT_SEL_NONE);
1795 if ((rc == -1) || buf_is_empty(fcc))
1796 {
1797 rc = 0;
1798 break;
1799 }
1801
1802 case 1: /* (r)etry */
1803 rc = mutt_write_multiple_fcc(buf_string(fcc), e, NULL, false, NULL, finalpath, sub);
1804 break;
1805
1806 case -1: /* abort */
1807 case 3: /* (s)kip */
1808 rc = 0;
1809 break;
1810 }
1811 }
1812 }
1813
1814 if (!c_fcc_before_send)
1815 {
1816 e->body = tmpbody;
1817
1818 if ((WithCrypto != 0) && save_sig)
1819 {
1820 /* cleanup the second signature structures */
1821 if (save_content->parts)
1822 {
1823 mutt_body_free(&save_content->parts->next);
1824 save_content->parts = NULL;
1825 }
1826 mutt_body_free(&save_content);
1827
1828 /* restore old signature and attachments */
1829 e->body->parts->next = save_sig;
1830 e->body->parts->parts->next = save_parts;
1831 }
1832 else if ((WithCrypto != 0) && save_content)
1833 {
1834 /* destroy the new encrypted body. */
1835 mutt_body_free(&save_content);
1836 }
1837 }
1838
1839 return 0;
1840}
@ MUTT_SEL_NONE
No flags are set.
Definition lib.h:59
int mutt_protect(struct Email *e, char *keylist, bool postpone)
Encrypt and/or sign a message.
Definition crypt.c:156
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition body.c:58
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition envelope.c:125
int mw_enter_fname(const char *prompt, struct Buffer *fname, bool mailbox, struct Mailbox *m, bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
Ask the user to select a file -.
Definition curs_lib.c:238
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition question.c:62
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
#define FALLTHROUGH
Definition lib.h:117
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
@ SEC_SIGN
Email is signed.
Definition lib.h:93
@ SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition lib.h:101
void mutt_param_delete(struct ParameterList *pl, const char *attribute)
Delete a matching Parameter.
Definition parameter.c:143
@ MUTT_ASKNO
Ask the user, defaulting to 'No'.
Definition quad.h:40
@ SEND_BATCH
Send email in batch mode (without user interaction)
Definition send.h:51
int mutt_write_multiple_fcc(const char *path, struct Email *e, const char *msgid, bool post, char *fcc, char **finalpath, struct ConfigSubset *sub)
Handle FCC with multiple, comma separated entries.
Definition sendlib.c:971
struct Body * parts
parts of a multipart or message/rfc822
Definition body.h:73
struct Envelope * mime_headers
Memory hole protected headers.
Definition body.h:76
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
time_t received
Time when the message was placed in the mailbox.
Definition email.h:61
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ postpone_message()

static int postpone_message ( struct Email * e_post,
struct Email * e_cur,
const char * fcc,
SendFlags flags,
struct ConfigSubset * sub )
static

Save an Email for another day.

Parameters
e_postEmail to postpone
e_curCurrent Email in the index
fccFolder for 'sent mail'
flagsSend mode, see SendFlags
subConfig Subset
Return values
0Success
-1Error

Definition at line 1852 of file send.c.

1854{
1855 char *pgpkeylist = NULL;
1856 const char *encrypt_as = NULL;
1857 struct Body *clear_content = NULL;
1858
1859 const char *const c_postponed = cs_subset_string(sub, "postponed");
1860 if (!c_postponed)
1861 {
1862 mutt_error(_("Can't postpone. $postponed is unset"));
1863 return -1;
1864 }
1865
1866 if (e_post->body->next)
1867 e_post->body = mutt_make_multipart(e_post->body);
1868
1869 mutt_encode_descriptions(e_post->body, true, sub);
1870
1871 const bool c_postpone_encrypt = cs_subset_bool(sub, "postpone_encrypt");
1872 if ((WithCrypto != 0) && c_postpone_encrypt &&
1873 (e_post->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
1874 {
1875 if (((WithCrypto & APPLICATION_PGP) != 0) && (e_post->security & APPLICATION_PGP))
1876 {
1877 const char *const c_pgp_default_key = cs_subset_string(sub, "pgp_default_key");
1878 encrypt_as = c_pgp_default_key;
1879 }
1880 else if (((WithCrypto & APPLICATION_SMIME) != 0) && (e_post->security & APPLICATION_SMIME))
1881 {
1882 const char *const c_smime_default_key = cs_subset_string(sub, "smime_default_key");
1883 encrypt_as = c_smime_default_key;
1884 }
1885 if (!encrypt_as)
1886 {
1887 const char *const c_postpone_encrypt_as = cs_subset_string(sub, "postpone_encrypt_as");
1888 encrypt_as = c_postpone_encrypt_as;
1889 }
1890
1891#ifdef USE_AUTOCRYPT
1892 if (e_post->security & SEC_AUTOCRYPT)
1893 {
1895 {
1896 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1897 e_post->body = mutt_remove_multipart(e_post->body);
1898 decode_descriptions(e_post->body);
1899 mutt_error(_("Error encrypting message. Check your crypt settings."));
1900 return -1;
1901 }
1903 encrypt_as = mod_data->autocrypt_default_key;
1904 }
1905#endif
1906
1907 if (encrypt_as)
1908 {
1909 pgpkeylist = mutt_str_dup(encrypt_as);
1910 clear_content = e_post->body;
1911 if (mutt_protect(e_post, pgpkeylist, true) == -1)
1912 {
1913 FREE(&pgpkeylist);
1914 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1915 e_post->body = mutt_remove_multipart(e_post->body);
1916 decode_descriptions(e_post->body);
1917 mutt_error(_("Error encrypting message. Check your crypt settings."));
1918 return -1;
1919 }
1920
1921 FREE(&pgpkeylist);
1922
1923 mutt_encode_descriptions(e_post->body, false, sub);
1924 }
1925 }
1926
1927 /* make sure the message is written to the right part of a maildir
1928 * postponed folder. */
1929 e_post->read = false;
1930 e_post->old = false;
1931
1932 mutt_prepare_envelope(e_post->env, false, sub);
1933 mutt_env_to_intl(e_post->env, NULL, NULL); /* Handle bad IDNAs the next time. */
1934
1935 if (mutt_write_fcc(NONULL(c_postponed), e_post,
1936 (e_cur && (flags & SEND_REPLY)) ? e_cur->env->message_id : NULL,
1937 true, fcc, NULL, sub) < 0)
1938 {
1939 if (clear_content)
1940 {
1941 mutt_body_free(&e_post->body);
1942 e_post->body = clear_content;
1943 }
1944 mutt_env_free(&e_post->body->mime_headers); /* protected headers */
1945 mutt_param_delete(&e_post->body->parameter, "protected-headers");
1946 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1947 e_post->body = mutt_remove_multipart(e_post->body);
1948 decode_descriptions(e_post->body);
1950 return -1;
1951 }
1952
1954
1955 if (clear_content)
1956 mutt_body_free(&clear_content);
1957
1958 return 0;
1959}
int mutt_autocrypt_set_sign_as_default_key(struct Email *e)
Set the Autocrypt default key for signing.
Definition autocrypt.c:717
int mutt_env_to_intl(struct Envelope *env, const char **tag, char **err)
Convert an Envelope's Address fields to Punycode format.
Definition envelope.c:350
@ MODULE_ID_AUTOCRYPT
ModuleAutocrypt, Autocrypt
Definition module_api.h:50
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition multipart.c:133
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition multipart.c:107
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition postpone.c:171
void mutt_unprepare_envelope(struct Envelope *env)
Undo the encodings of mutt_prepare_envelope()
Definition sendlib.c:785
void mutt_prepare_envelope(struct Envelope *env, bool final, struct ConfigSubset *sub)
Prepare an email header.
Definition sendlib.c:746
int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, bool post, const char *fcc, char **finalpath, struct ConfigSubset *sub)
Write email to FCC mailbox.
Definition sendlib.c:1024
Autocrypt private Module data.
Definition module_data.h:32
char * autocrypt_default_key
Autocrypt default key id (used for postponing messages)
Definition module_data.h:35
bool read
Email is read.
Definition email.h:50
bool old
Email is seen, but unread.
Definition email.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ is_text_plain()

static bool is_text_plain ( const struct Body * b)
static

Is a Body a text/plain MIME part?

Parameters
bBody to check
Return values
trueBody is text/plain
falseBody is not

Definition at line 1967 of file send.c.

1968{
1969 return (b->type == TYPE_TEXT) && mutt_istr_equal(b->subtype, "plain");
1970}
@ TYPE_TEXT
Type: 'text/*'.
Definition mime.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ abort_for_missing_attachments()

static bool abort_for_missing_attachments ( const struct Body * b,
struct ConfigSubset * sub )
static

Should we abort sending because of missing attachments?

Parameters
bBody
subConfig Subset
Return values
trueAbort because of missing attachments

Definition at line 1978 of file send.c.

1979{
1980 const enum QuadOption c_abort_noattach = cs_subset_quad(sub, "abort_noattach");
1981
1982 if (c_abort_noattach == MUTT_NO)
1983 return false;
1984
1985 if (b->next)
1986 return false;
1987
1988 bool has_keyword = false;
1989
1990 /* search text/plain parts, whether they are main or alternative parts */
1991 if (is_text_plain(b))
1992 {
1993 has_keyword |= search_attach_keyword(b->filename, sub);
1994 }
1995 else
1996 {
1997 for (b = b->parts; b; b = b->next)
1998 {
1999 if (is_text_plain(b))
2000 {
2001 has_keyword |= search_attach_keyword(b->filename, sub);
2002 }
2003 }
2004 }
2005
2006 if (!has_keyword)
2007 return false;
2008
2009 if (c_abort_noattach == MUTT_YES)
2010 {
2011 mutt_error(_("Message contains text matching \"$abort_noattach_regex\". Not sending."));
2012 return true;
2013 }
2014
2015 return query_quadoption(_("No attachments, cancel sending?"), sub, "abort_noattach") != MUTT_NO;
2016}
static bool search_attach_keyword(char *filename, struct ConfigSubset *sub)
Search an email for 'attachment' keywords.
Definition send.c:1628
static bool is_text_plain(const struct Body *b)
Is a Body a text/plain MIME part?
Definition send.c:1967
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_send_message()

int mutt_send_message ( SendFlags flags,
struct Email * e_templ,
const char * tempfile,
struct Mailbox * m,
struct EmailArray * ea,
struct ConfigSubset * sub )

Send an email.

Parameters
flagsSend mode, see SendFlags
e_templTemplate to use for new message
tempfileFile specified by -i or -H
mCurrent mailbox
eaArray of Emails to send
subConfig Subset
Return values
0Message was successfully sent
-1Message was aborted or an error occurred
1Message was postponed

Definition at line 2030 of file send.c.

2032{
2033 struct Buffer *fcc = buf_pool_get(); /* where to copy this message */
2034 FILE *fp_tmp = NULL;
2035 struct Body *pbody = NULL;
2036 int i;
2037 bool free_clear_content = false;
2038
2039 struct Body *clear_content = NULL;
2040 char *pgpkeylist = NULL;
2041 /* save current value of "pgp_sign_as" and "smime_default_key" */
2042 char *pgp_sign_as = NULL;
2043 char *smime_sign_as = NULL;
2044 const char *tag = NULL;
2045 char *err = NULL;
2046 char *finalpath = NULL;
2047 struct Email *e_cur = NULL;
2048
2049 if (ea && (ARRAY_SIZE(ea) == 1))
2050 e_cur = *ARRAY_GET(ea, 0);
2051
2052 int rc = -1;
2053
2054 if (flags & SEND_NEWS)
2055 OptNewsSend = true;
2056 else
2057 OptNewsSend = false;
2058
2059 const enum QuadOption c_recall = cs_subset_quad(sub, "recall");
2060
2061 if (!flags && !e_templ && (c_recall != MUTT_NO) && mutt_num_postponed(m, true))
2062 {
2063 /* If the user is composing a new message, check to see if there
2064 * are any postponed messages first. */
2065 enum QuadOption ans = query_quadoption(_("Recall postponed message?"), sub, "recall");
2066 if (ans == MUTT_ABORT)
2067 return rc;
2068
2069 if (ans == MUTT_YES)
2070 flags |= SEND_POSTPONED;
2071 }
2072
2073 if (flags & SEND_POSTPONED)
2074 {
2076 {
2077 const char *const c_pgp_sign_as = cs_subset_string(sub, "pgp_sign_as");
2078 pgp_sign_as = mutt_str_dup(c_pgp_sign_as);
2079 }
2081 {
2082 const char *const c_smime_sign_as = cs_subset_string(sub, "smime_sign_as");
2083 smime_sign_as = mutt_str_dup(c_smime_sign_as);
2084 }
2085 }
2086
2087 /* Delay expansion of aliases until absolutely necessary--shouldn't
2088 * be necessary unless we are prompting the user or about to execute a
2089 * send-hook. */
2090
2091 if (!e_templ)
2092 {
2093 e_templ = email_new();
2094
2095 if (flags == SEND_POSTPONED)
2096 {
2097 rc = mutt_get_postponed(m, e_templ, &e_cur, fcc);
2098 if (rc < 0)
2099 {
2100 flags = SEND_POSTPONED;
2101 goto cleanup;
2102 }
2103 flags = rc;
2104 /* If postponed message is a news article, it have
2105 * a "Newsgroups:" header line, then set appropriate flag. */
2106 if (e_templ->env->newsgroups)
2107 {
2108 flags |= SEND_NEWS;
2109 OptNewsSend = true;
2110 }
2111 else
2112 {
2113 flags &= ~SEND_NEWS;
2114 OptNewsSend = false;
2115 }
2116 }
2117
2118 if (flags & (SEND_POSTPONED | SEND_RESEND))
2119 {
2120 struct Body *b = e_templ->body;
2121 while (b->parts)
2122 b = b->parts;
2123 fp_tmp = mutt_file_fopen(b->filename, "a+");
2124 if (!fp_tmp)
2125 {
2126 mutt_perror("%s", b->filename);
2127 goto cleanup;
2128 }
2129 }
2130
2131 if (!e_templ->env)
2132 e_templ->env = mutt_env_new();
2133 }
2134
2135 /* Parse and use an eventual list-post header */
2136 if ((flags & SEND_LIST_REPLY) && e_cur && e_cur->env && e_cur->env->list_post)
2137 {
2138 /* Use any list-post header as a template */
2139 mutt_parse_mailto(e_templ->env, NULL, e_cur->env->list_post);
2140 /* We don't let them set the sender's address. */
2141 mutt_addrlist_clear(&e_templ->env->from);
2142 }
2143
2144 if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND)))
2145 {
2146 /* When SEND_DRAFT_FILE is set, the caller has already
2147 * created the "parent" body structure. */
2148 if (!(flags & SEND_DRAFT_FILE))
2149 {
2150 pbody = mutt_body_new();
2151 pbody->next = e_templ->body; /* don't kill command-line attachments */
2152 e_templ->body = pbody;
2153
2154 const char *const c_content_type = cs_subset_string(sub, "content_type");
2155 const char *ctype = mutt_str_dup(c_content_type ? c_content_type : "text/plain");
2156 mutt_parse_content_type(ctype, e_templ->body);
2157 FREE(&ctype);
2158 e_templ->body->unlink = true;
2159 e_templ->body->use_disp = false;
2160 e_templ->body->disposition = DISP_INLINE;
2161
2162 if (tempfile)
2163 {
2164 fp_tmp = mutt_file_fopen(tempfile, "a+");
2165 e_templ->body->filename = mutt_str_dup(tempfile);
2166 if (flags & SEND_NO_FREE_HEADER)
2167 e_templ->body->unlink = false;
2168 }
2169 else
2170 {
2171 struct Buffer *buf = buf_pool_get();
2172 buf_mktemp_draft(buf);
2173 fp_tmp = mutt_file_fopen(buf_string(buf), "w+");
2174 e_templ->body->filename = buf_strdup(buf);
2175 buf_pool_release(&buf);
2176 }
2177 }
2178 else
2179 {
2180 struct Body *b = e_templ->body;
2181 while (b->parts)
2182 b = b->parts;
2183 fp_tmp = mutt_file_fopen(b->filename, "a+");
2184 }
2185
2186 if (!fp_tmp)
2187 {
2188 mutt_debug(LL_DEBUG1, "can't create tempfile %s (errno=%d)\n",
2189 e_templ->body->filename, errno);
2190 mutt_perror("%s", e_templ->body->filename);
2191 goto cleanup;
2192 }
2193 }
2194
2195 const bool c_reverse_name = cs_subset_bool(sub, "reverse_name");
2196 /* this is handled here so that the user can match ~f in send-hook */
2197 if (e_cur && c_reverse_name && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2198 {
2199 /* We shouldn't have to worry about alias expansion here since we are
2200 * either replying to a real or postponed message, therefore no aliases
2201 * should exist since the user has not had the opportunity to add
2202 * addresses to the list. We just have to ensure the postponed messages
2203 * have their aliases expanded. */
2204
2205 if (!TAILQ_EMPTY(&e_templ->env->from))
2206 {
2207 mutt_debug(LL_DEBUG5, "e_templ->env->from before set_reverse_name: %s\n",
2208 buf_string(TAILQ_FIRST(&e_templ->env->from)->mailbox));
2209 mutt_addrlist_clear(&e_templ->env->from);
2210 }
2211 set_reverse_name(&e_templ->env->from, e_cur->env, sub);
2212 }
2213
2214 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
2215 if (e_cur && c_reply_with_xorig && !(flags & (SEND_POSTPONED | SEND_RESEND | SEND_FORWARD)))
2216 {
2217 /* We shouldn't have to worry about freeing 'e_templ->env->from' before
2218 * setting it here since this code will only execute when doing some
2219 * sort of reply. The pointer will only be set when using the -H command
2220 * line option.
2221 *
2222 * If there is already a from address recorded in 'e_templ->env->from',
2223 * then it theoretically comes from `$reverse_name` handling, and we don't use
2224 * the 'X-Original-To header'. */
2225 if (!TAILQ_EMPTY(&e_cur->env->x_original_to) && TAILQ_EMPTY(&e_templ->env->from))
2226 {
2227 mutt_addrlist_copy(&e_templ->env->from, &e_cur->env->x_original_to, false);
2228 mutt_debug(LL_DEBUG5, "e_templ->env->from extracted from X-Original-To: header: %s\n",
2229 buf_string(TAILQ_FIRST(&e_templ->env->from)->mailbox));
2230 }
2231 }
2232
2233 if (!e_templ->env->message_id)
2234 e_templ->env->message_id = msgid_generate();
2235
2236 const bool c_resume_draft_files = cs_subset_bool(sub, "resume_draft_files");
2237 if (!(flags & (SEND_POSTPONED | SEND_RESEND)) &&
2238 !((flags & SEND_DRAFT_FILE) && c_resume_draft_files))
2239 {
2240 if ((flags & (SEND_REPLY | SEND_FORWARD | SEND_TO_SENDER)) &&
2241 (envelope_defaults(e_templ->env, ea, flags, sub) == -1))
2242 {
2243 goto cleanup;
2244 }
2245
2246 const bool c_hdrs = cs_subset_bool(sub, "hdrs");
2247 if (c_hdrs)
2248 process_user_recips(e_templ->env);
2249
2250 /* Expand aliases and remove duplicates/crossrefs */
2251 mutt_expand_aliases_env(e_templ->env);
2252
2253 if (flags & SEND_REPLY)
2254 mutt_fix_reply_recipients(e_templ->env, sub);
2255
2256 if ((flags & SEND_NEWS) && (m && m->type == MUTT_NNTP) && !e_templ->env->newsgroups)
2257 {
2258 e_templ->env->newsgroups = mutt_str_dup(((struct NntpMboxData *) m->mdata)->group);
2259 }
2260
2261 const bool c_auto_edit = cs_subset_bool(sub, "auto_edit");
2262 const bool c_edit_headers = cs_subset_bool(sub, "edit_headers");
2263 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
2264 if (!(flags & SEND_BATCH) && !(c_auto_edit && c_edit_headers) &&
2265 !((flags & SEND_REPLY) && c_fast_reply))
2266 {
2267 if (edit_envelope(e_templ->env, flags, sub) == -1)
2268 goto cleanup;
2269 }
2270
2271 /* the from address must be set here regardless of whether or not
2272 * $use_from is set so that the '~P' (from you) operator in send-hook
2273 * patterns will work. if $use_from is unset, the from address is killed
2274 * after send-hooks are evaluated */
2275
2276 const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2277 if (killfrom)
2278 {
2280 }
2281
2282 if ((flags & SEND_REPLY) && e_cur)
2283 {
2284 /* change setting based upon message we are replying to */
2286
2287 /* set the replied flag for the message we are generating so that the
2288 * user can use ~Q in a send-hook to know when reply-hook's are also
2289 * being used. */
2290 e_templ->replied = true;
2291 }
2292
2293 /* change settings based upon recipients */
2294
2295 exec_message_hook(NULL, e_templ, CMD_SEND_HOOK);
2296
2297 /* Unset the replied flag from the message we are composing since it is
2298 * no longer required. This is done here because the FCC'd copy of
2299 * this message was erroneously get the 'R'eplied flag when stored in
2300 * a maildir-style mailbox. */
2301 e_templ->replied = false;
2302
2303 /* $use_from and/or $from might have changed in a send-hook */
2304 if (killfrom)
2305 {
2306 mutt_addrlist_clear(&e_templ->env->from);
2307
2308 const bool c_use_from = cs_subset_bool(sub, "use_from");
2309 if (c_use_from && !(flags & (SEND_POSTPONED | SEND_RESEND)))
2311 }
2312
2313 if (c_hdrs)
2314 process_user_header(e_templ->env);
2315
2316 if ((flags & SEND_BATCH) && !(flags & SEND_CONSUMED_STDIN))
2317 {
2318 if (mutt_file_copy_stream(stdin, fp_tmp) < 0)
2319 {
2320 mutt_error(_("Error sending message"));
2321 goto cleanup;
2322 }
2323 }
2324
2325 if (!(flags & SEND_BATCH))
2326 mutt_make_greeting(e_templ, fp_tmp, sub);
2327
2328 const bool c_sig_on_top = cs_subset_bool(sub, "sig_on_top");
2329 const char *const c_editor = cs_subset_string(sub, "editor");
2330 if (c_sig_on_top && !(flags & (SEND_KEY | SEND_BATCH)) && c_editor)
2331 {
2332 append_signature(fp_tmp, sub);
2333 }
2334
2335 /* include replies/forwarded messages, unless we are given a template */
2336 if (!tempfile && (m || !(flags & (SEND_REPLY | SEND_FORWARD))) &&
2337 (generate_body(fp_tmp, e_templ, flags, m, ea, sub) == -1))
2338 {
2339 goto cleanup;
2340 }
2341
2342 if (!c_sig_on_top && !(flags & (SEND_KEY | SEND_BATCH)) && c_editor)
2343 {
2344 append_signature(fp_tmp, sub);
2345 }
2346 }
2347
2348 /* Only set format=flowed for new messages. postponed/resent/draftfiles
2349 * should respect the original email.
2350 *
2351 * This is set here so that send-hook can be used to turn the option on. */
2352 if (!(flags & (SEND_KEY | SEND_POSTPONED | SEND_RESEND | SEND_DRAFT_FILE)))
2353 {
2354 const bool c_text_flowed = cs_subset_bool(sub, "text_flowed");
2355 if (c_text_flowed && is_text_plain(e_templ->body))
2356 {
2357 mutt_param_set(&e_templ->body->parameter, "format", "flowed");
2358 }
2359 }
2360
2361 /* This hook is even called for postponed messages, and can, e.g., be used
2362 * for setting the editor, the sendmail path, or the envelope sender. */
2363 exec_message_hook(NULL, e_templ, CMD_SEND2_HOOK);
2364
2365 /* wait until now to set the real name portion of our return address so
2366 * that $real_name can be set in a send-hook */
2367 {
2368 struct Address *from = TAILQ_FIRST(&e_templ->env->from);
2369 if (from && !from->personal && !(flags & (SEND_RESEND | SEND_POSTPONED)))
2370 {
2371 const char *const c_real_name = cs_subset_string(sub, "real_name");
2372 if (c_real_name)
2373 from->personal = buf_new(c_real_name);
2374 }
2375 }
2376
2377 if (!(((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY)))
2378 mutt_file_fclose(&fp_tmp);
2379
2380 if (!(flags & SEND_BATCH))
2381 {
2382 struct stat st = { 0 };
2383 time_t mtime;
2384 struct Body *b = e_templ->body;
2385 while (b->parts)
2386 b = b->parts;
2387 mtime = mutt_file_decrease_mtime(b->filename, NULL);
2388 if (mtime == (time_t) -1)
2389 {
2390 mutt_perror("%s", b->filename);
2391 goto cleanup;
2392 }
2393
2394 mutt_update_encoding(b, sub);
2395
2396 const bool c_edit_headers = cs_subset_bool(sub, "edit_headers");
2397 const bool c_auto_edit = cs_subset_bool(sub, "auto_edit");
2398
2399 /* Select whether or not the user's editor should be called now. We
2400 * don't want to do this when:
2401 * 1) we are sending a key/cert
2402 * 2) we are forwarding a message and the user doesn't want to edit it.
2403 * This is controlled by the quadoption $forward_edit. However, if
2404 * both $edit_headers and $auto_edit are set, we want to ignore the
2405 * setting of $forward_edit because the user probably needs to add the
2406 * recipients. */
2407 if (!(flags & SEND_KEY) &&
2408 (((flags & SEND_FORWARD) == 0) || (c_edit_headers && c_auto_edit) ||
2409 (query_quadoption(_("Edit forwarded message?"), sub, "forward_edit") == MUTT_YES)))
2410 {
2411 /* If the this isn't a text message, look for a mailcap edit command */
2412 const char *const c_editor = cs_subset_string(sub, "editor");
2413 b = e_templ->body;
2414 while (b->parts)
2415 b = b->parts;
2416 if (mutt_needs_mailcap(b))
2417 {
2418 if (!mutt_edit_attachment(b))
2419 goto cleanup;
2420 }
2421 else if (c_edit_headers)
2422 {
2423 mutt_env_to_local(e_templ->env);
2424 mutt_edit_headers(c_editor, b->filename, e_templ, fcc);
2425 mutt_env_to_intl(e_templ->env, NULL, NULL);
2426 }
2427 else
2428 {
2429 mutt_edit_file(c_editor, b->filename);
2430 if (stat(b->filename, &st) == 0)
2431 {
2432 if (mtime != st.st_mtime)
2434 }
2435 else
2436 {
2437 mutt_perror("%s", b->filename);
2438 }
2439 }
2440
2441 exec_message_hook(NULL, e_templ, CMD_SEND2_HOOK);
2442 }
2443
2445 {
2446 if (stat(e_templ->body->filename, &st) == 0)
2447 {
2448 /* if the file was not modified, bail out now */
2449 if ((mtime == st.st_mtime) && !e_templ->body->next &&
2450 (query_quadoption(_("Abort unmodified message?"), sub, "abort_unmodified") == MUTT_YES))
2451 {
2452 mutt_message(_("Aborted unmodified message"));
2453 goto cleanup;
2454 }
2455 }
2456 else
2457 {
2458 mutt_perror("%s", e_templ->body->filename);
2459 }
2460 }
2461 }
2462
2463 /* Set the message security unless:
2464 * 1) crypto support is not enabled (WithCrypto==0)
2465 * 2) pgp: header field was present during message editing with $edit_headers (e_templ->security != 0)
2466 * 3) we are resending a message
2467 * 4) we are recalling a postponed message (don't override the user's saved settings)
2468 * 5) we are in batch mode
2469 * But 3, 4, and 5, can be overridden with '-C' in the command line (flags & SEND_CLI_CRYPTO)
2470 *
2471 * This is done after allowing the user to edit the message so that security
2472 * settings can be configured with send2-hook and $edit_headers. */
2473 if ((WithCrypto != 0) && (e_templ->security == 0) &&
2474 ((flags & SEND_CLI_CRYPTO) || !(flags & (SEND_BATCH | SEND_POSTPONED | SEND_RESEND))))
2475 {
2476 bool c_autocrypt = false;
2477 bool c_autocrypt_reply = false;
2478
2479#ifdef USE_AUTOCRYPT
2480 c_autocrypt = cs_subset_bool(sub, "autocrypt");
2481 c_autocrypt_reply = cs_subset_bool(sub, "autocrypt_reply");
2482#endif
2483
2484 if (c_autocrypt && c_autocrypt_reply && e_cur && (e_cur->security & SEC_AUTOCRYPT))
2485 {
2487 }
2488 else
2489 {
2490 const bool c_crypt_auto_sign = cs_subset_bool(sub, "crypt_auto_sign");
2491 const bool c_crypt_auto_encrypt = cs_subset_bool(sub, "crypt_auto_encrypt");
2492 const bool c_crypt_reply_encrypt = cs_subset_bool(sub, "crypt_reply_encrypt");
2493 const bool c_crypt_reply_sign = cs_subset_bool(sub, "crypt_reply_sign");
2494 const bool c_crypt_reply_sign_encrypted = cs_subset_bool(sub, "crypt_reply_sign_encrypted");
2495
2496 if (c_crypt_auto_sign)
2497 e_templ->security |= SEC_SIGN;
2498 if (c_crypt_auto_encrypt)
2499 e_templ->security |= SEC_ENCRYPT;
2500 if (c_crypt_reply_encrypt && e_cur && (e_cur->security & SEC_ENCRYPT))
2501 e_templ->security |= SEC_ENCRYPT;
2502 if (c_crypt_reply_sign && e_cur && (e_cur->security & SEC_SIGN))
2503 e_templ->security |= SEC_SIGN;
2504 if (c_crypt_reply_sign_encrypted && e_cur && (e_cur->security & SEC_ENCRYPT))
2505 e_templ->security |= SEC_SIGN;
2506
2507 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
2508
2509 if (((WithCrypto & APPLICATION_PGP) != 0) &&
2510 ((e_templ->security & (SEC_ENCRYPT | SEC_SIGN)) || c_crypt_opportunistic_encrypt))
2511 {
2512 const bool c_pgp_auto_inline = cs_subset_bool(sub, "pgp_auto_inline");
2513 const bool c_pgp_reply_inline = cs_subset_bool(sub, "pgp_reply_inline");
2514
2515 if (c_pgp_auto_inline)
2516 e_templ->security |= SEC_INLINE;
2517 if (c_pgp_reply_inline && e_cur && (e_cur->security & SEC_INLINE))
2518 e_templ->security |= SEC_INLINE;
2519 }
2520 }
2521
2522 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
2523
2524 if (e_templ->security || c_crypt_opportunistic_encrypt)
2525 {
2526 const bool c_crypt_auto_pgp = cs_subset_bool(sub, "crypt_auto_pgp");
2527 const bool c_crypt_auto_smime = cs_subset_bool(sub, "crypt_auto_smime");
2528
2529 /* When replying / forwarding, use the original message's
2530 * crypto system. According to the documentation,
2531 * smime_is_default should be disregarded here.
2532 *
2533 * Problem: At least with forwarding, this doesn't really
2534 * make much sense. Should we have an option to completely
2535 * disable individual mechanisms at run-time? */
2536 if (e_cur)
2537 {
2538 if (((WithCrypto & APPLICATION_PGP) != 0) && c_crypt_auto_pgp &&
2539 (e_cur->security & APPLICATION_PGP))
2540 {
2541 e_templ->security |= APPLICATION_PGP;
2542 }
2543 else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
2544 c_crypt_auto_smime && (e_cur->security & APPLICATION_SMIME))
2545 {
2546 e_templ->security |= APPLICATION_SMIME;
2547 }
2548 }
2549
2550 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
2551
2552 /* No crypto mechanism selected? Use availability + smime_is_default
2553 * for the decision. */
2554 if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2555 {
2556 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_crypt_auto_smime && c_smime_is_default)
2557 {
2558 e_templ->security |= APPLICATION_SMIME;
2559 }
2560 else if (((WithCrypto & APPLICATION_PGP) != 0) && c_crypt_auto_pgp)
2561 {
2562 e_templ->security |= APPLICATION_PGP;
2563 }
2564 else if (((WithCrypto & APPLICATION_SMIME) != 0) && c_crypt_auto_smime)
2565 {
2566 e_templ->security |= APPLICATION_SMIME;
2567 }
2568 }
2569 }
2570
2571 /* opportunistic encrypt relies on SMIME or PGP already being selected */
2572 if (c_crypt_opportunistic_encrypt)
2573 {
2574 /* If something has already enabled encryption, e.g. `$crypt_auto_encrypt`
2575 * or `$crypt_reply_encrypt`, then don't enable opportunistic encrypt for
2576 * the message. */
2577 if (!(e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
2578 {
2579 e_templ->security |= SEC_OPPENCRYPT;
2581 }
2582 }
2583
2584 /* No permissible mechanisms found. Don't sign or encrypt. */
2585 if (!(e_templ->security & (APPLICATION_SMIME | APPLICATION_PGP)))
2586 e_templ->security = SEC_NONE;
2587 }
2588
2589 /* Deal with the corner case where the crypto module backend is not available.
2590 * This can happen if configured without PGP/SMIME and with GPGME, but
2591 * $crypt_use_gpgme is unset. */
2592 if (e_templ->security && !crypt_has_module_backend(e_templ->security))
2593 {
2594 mutt_error(_("No crypto backend configured. Disabling message security setting."));
2595 e_templ->security = SEC_NONE;
2596 }
2597
2598 /* specify a default fcc. if we are in batchmode, only save a copy of
2599 * the message if the value of $copy is yes or ask-yes */
2600
2601 const enum QuadOption c_copy = cs_subset_quad(sub, "copy");
2602
2603 if (buf_is_empty(fcc) && !(flags & SEND_POSTPONED_FCC) &&
2604 (!(flags & SEND_BATCH) || (c_copy & 0x1)))
2605 {
2606 /* set the default FCC */
2607 const bool killfrom = TAILQ_EMPTY(&e_templ->env->from);
2608 if (killfrom)
2609 {
2611 }
2612 mutt_select_fcc(fcc, e_templ);
2613 if (killfrom)
2614 {
2615 mutt_addrlist_clear(&e_templ->env->from);
2616 }
2617 }
2618
2619 mutt_rfc3676_space_stuff(e_templ);
2620
2621 mutt_update_encoding(e_templ->body, sub);
2622
2623 if (!(flags & SEND_BATCH))
2624 {
2625 main_loop:
2626
2627 pretty_mailbox(fcc);
2628 i = dlg_compose(e_templ, fcc,
2629 ((flags & SEND_NO_FREE_HEADER) ? MUTT_COMPOSE_NOFREEHEADER : 0), sub);
2630 if (i == -1)
2631 {
2632 /* abort */
2633 if (flags & SEND_NEWS)
2634 mutt_message(_("Article not posted"));
2635 else
2636 mutt_message(_("Mail not sent"));
2637 goto cleanup;
2638 }
2639 else if (i == 1)
2640 {
2641 if (postpone_message(e_templ, e_cur, buf_string(fcc), flags, sub) != 0)
2642 goto main_loop;
2643 mutt_message(_("Message postponed"));
2644 rc = 1;
2645 goto cleanup;
2646 }
2647 }
2648
2649 if (!(flags & SEND_NEWS))
2650 {
2651 if ((mutt_addrlist_count_recips(&e_templ->env->to) == 0) &&
2652 (mutt_addrlist_count_recips(&e_templ->env->cc) == 0) &&
2653 (mutt_addrlist_count_recips(&e_templ->env->bcc) == 0))
2654 {
2655 if (flags & SEND_BATCH)
2656 {
2657 puts(_("No recipients specified"));
2658 goto cleanup;
2659 }
2660
2661 mutt_warning(_("No recipients specified"));
2662 goto main_loop;
2663 }
2664 }
2665
2666 if (mutt_env_to_intl(e_templ->env, &tag, &err))
2667 {
2668 mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
2669 FREE(&err);
2670 if (flags & SEND_BATCH)
2671 goto cleanup;
2672 goto main_loop;
2673 }
2674
2675 const enum QuadOption c_abort_nosubject = cs_subset_quad(sub, "abort_nosubject");
2676
2677 if (!e_templ->env->subject && !(flags & SEND_BATCH) &&
2678 (query_quadoption(_("No subject, abort sending?"), sub, "abort_nosubject") != MUTT_NO))
2679 {
2680 /* if the abort is automatic, print an error message */
2681 if (c_abort_nosubject == MUTT_YES)
2682 mutt_error(_("No subject specified"));
2683 goto main_loop;
2684 }
2685
2686 if ((flags & SEND_NEWS) && !e_templ->env->subject)
2687 {
2688 mutt_error(_("No subject specified"));
2689 goto main_loop;
2690 }
2691
2692 if ((flags & SEND_NEWS) && !e_templ->env->newsgroups)
2693 {
2694 mutt_error(_("No newsgroup specified"));
2695 goto main_loop;
2696 }
2697
2698 if (!(flags & SEND_BATCH) && abort_for_missing_attachments(e_templ->body, sub))
2699 {
2700 goto main_loop;
2701 }
2702
2703 const bool c_confirm_empty_to = cs_subset_bool(sub, "confirm_empty_to");
2704 if (c_confirm_empty_to && TAILQ_EMPTY(&e_templ->env->to) &&
2705 (query_yesorno(_("No recipients specified in To. Send anyway?"), MUTT_NO) == MUTT_NO))
2706 {
2707 goto main_loop;
2708 }
2709
2710 if (e_templ->body->next)
2711 e_templ->body = mutt_make_multipart(e_templ->body);
2712
2713 /* Ok, we need to do it this way instead of handling all fcc stuff in
2714 * one place in order to avoid going to main_loop with encoded "env"
2715 * in case of error. Ugh. */
2716
2717 mutt_encode_descriptions(e_templ->body, true, sub);
2718
2719 /* Make sure that clear_content and free_clear_content are
2720 * properly initialized -- we may visit this particular place in
2721 * the code multiple times, including after a failed call to
2722 * mutt_protect(). */
2723
2724 clear_content = NULL;
2725 free_clear_content = false;
2726
2727 if (WithCrypto)
2728 {
2729 if (e_templ->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT))
2730 {
2731 /* save the decrypted attachments */
2732 clear_content = e_templ->body;
2733
2734 if ((crypt_get_keys(e_templ, &pgpkeylist, false) == -1) ||
2735 (mutt_protect(e_templ, pgpkeylist, false) == -1))
2736 {
2737 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2738 e_templ->body = mutt_remove_multipart(e_templ->body);
2739
2740 FREE(&pgpkeylist);
2741
2742 decode_descriptions(e_templ->body);
2743
2744 if (flags & SEND_BATCH)
2745 {
2746 mutt_message(_("Missing encryption key; mail not sent"));
2747 rc = -1;
2748 goto cleanup;
2749 }
2750
2751 goto main_loop;
2752 }
2753 mutt_encode_descriptions(e_templ->body, false, sub);
2754 }
2755
2756 /* at this point, e_templ->body is one of the following three things:
2757 * - multipart/signed. In this case, clear_content is a child
2758 * - multipart/encrypted. In this case, clear_content exists independently
2759 * - application/pgp. In this case, clear_content exists independently
2760 * - something else. In this case, it's the same as clear_content */
2761
2762 /* This is ugly -- lack of "reporting back" from mutt_protect(). */
2763
2764 if (clear_content && (e_templ->body != clear_content) &&
2765 (e_templ->body->parts != clear_content))
2766 free_clear_content = true;
2767 }
2768
2769 if (OptGui)
2770 mutt_message(_("Sending message..."));
2771
2772 mutt_prepare_envelope(e_templ->env, true, sub);
2773
2774 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
2775 if (c_fcc_before_send)
2776 {
2777 if (save_fcc(m, e_templ, fcc, clear_content, pgpkeylist, flags, &finalpath, sub) &&
2778 (flags & SEND_BATCH))
2779 {
2780 /* In batch mode with $fcc_before_send set, abort sending if Fcc fails. */
2781 puts(_("Fcc failed. Aborting sending."));
2782 goto cleanup;
2783 }
2784 }
2785
2786 i = invoke_mta(m, e_templ, sub);
2787 if (i < 0)
2788 {
2789 if (!(flags & SEND_BATCH))
2790 {
2791 if (!WithCrypto)
2792 ; // do nothing
2793 else if ((e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) ||
2794 ((e_templ->security & SEC_SIGN) && (e_templ->body->type == TYPE_APPLICATION)))
2795 {
2796 if (e_templ->body != clear_content)
2797 {
2798 mutt_body_free(&e_templ->body); /* destroy PGP data */
2799 e_templ->body = clear_content; /* restore clear text. */
2800 }
2801 }
2802 else if ((e_templ->security & SEC_SIGN) && (e_templ->body->type == TYPE_MULTIPART))
2803 {
2804 mutt_body_free(&e_templ->body->parts->next); /* destroy sig */
2805 if (mutt_istr_equal(e_templ->body->subtype, "mixed") ||
2806 mutt_istr_equal(e_templ->body->subtype, "signed"))
2807 {
2808 e_templ->body = mutt_remove_multipart(e_templ->body);
2809 }
2810 }
2811
2812 FREE(&pgpkeylist);
2813 mutt_env_free(&e_templ->body->mime_headers); /* protected headers */
2814 mutt_param_delete(&e_templ->body->parameter, "protected-headers");
2815 if (mutt_istr_equal(e_templ->body->subtype, "mixed"))
2816 e_templ->body = mutt_remove_multipart(e_templ->body);
2817 decode_descriptions(e_templ->body);
2818 mutt_unprepare_envelope(e_templ->env);
2819 FREE(&finalpath);
2820 goto main_loop;
2821 }
2822 else
2823 {
2824 puts(_("Could not send the message"));
2825 goto cleanup;
2826 }
2827 }
2828
2829 if (!c_fcc_before_send)
2830 save_fcc(m, e_templ, fcc, clear_content, pgpkeylist, flags, &finalpath, sub);
2831
2832 if (OptGui)
2833 {
2834 mutt_message((i != 0) ? _("Sending in background") :
2835 (flags & SEND_NEWS) ? _("Article posted") :
2836 _("Mail sent"));
2837#ifdef USE_NOTMUCH
2838 const bool c_nm_record = cs_subset_bool(sub, "nm_record");
2839 if (c_nm_record)
2840 nm_record_message(m, finalpath, e_cur);
2841#endif
2842 mutt_sleep(0);
2843 }
2844
2845 if (WithCrypto)
2846 FREE(&pgpkeylist);
2847
2848 if ((WithCrypto != 0) && free_clear_content)
2849 mutt_body_free(&clear_content);
2850
2851 /* set 'replied' flag only if the user didn't change/remove
2852 * In-Reply-To: and References: headers during edit */
2853 if (flags & SEND_REPLY)
2854 {
2855 if (!(flags & SEND_POSTPONED) && m)
2856 {
2857 struct Email **ep = NULL;
2858 ARRAY_FOREACH(ep, ea)
2859 {
2860 struct Email *e = *ep;
2861 mutt_set_flag(m, e, MUTT_REPLIED, is_reply(e, e_templ), true);
2862 }
2863 }
2864 }
2865
2866 rc = 0;
2867
2868cleanup:
2869 buf_pool_release(&fcc);
2870
2871 if (flags & SEND_POSTPONED)
2872 {
2874 {
2875 cs_subset_str_string_set(sub, "pgp_sign_as", pgp_sign_as, NULL);
2876 FREE(&pgp_sign_as);
2877 }
2879 {
2880 cs_subset_str_string_set(sub, "smime_sign_as", smime_sign_as, NULL);
2881 FREE(&smime_sign_as);
2882 }
2883 }
2884
2885 mutt_file_fclose(&fp_tmp);
2886 if (!(flags & SEND_NO_FREE_HEADER))
2887 email_free(&e_templ);
2888
2889 FREE(&finalpath);
2890 return rc;
2891}
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition address.c:881
void mutt_expand_aliases_env(struct Envelope *env)
Expand aliases in all the fields of an Envelope.
Definition alias.c:310
struct Buffer * buf_new(const char *str)
Allocate a new Buffer.
Definition buffer.c:304
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
@ CMD_SEND_HOOK
:send-hook
Definition command.h:110
@ CMD_SEND2_HOOK
:send2-hook
Definition command.h:109
@ CMD_REPLY_HOOK
:reply-hook
Definition command.h:105
#define MUTT_COMPOSE_NOFREEHEADER
Don't free the header when closing compose dialog.
Definition lib.h:55
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:48
int crypt_get_keys(struct Email *e, char **keylist, bool oppenc_mode)
Check we have all the keys we need.
Definition crypt.c:966
bool crypt_has_module_backend(SecurityFlags type)
Is there a crypto backend for a given type?
Definition cryptglue.c:188
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition curs_lib.c:118
struct Body * mutt_body_new(void)
Create a new Body.
Definition body.c:44
void mutt_edit_headers(const char *editor, const char *body, struct Email *e, struct Buffer *fcc)
Let the user edit the message header and body.
Definition header.c:181
void mutt_parse_content_type(const char *s, struct Body *b)
Parse a content type.
Definition parse.c:433
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition parse.c:1726
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope's Address fields to local format.
Definition envelope.c:316
char * msgid_generate(void)
Generate a Message-ID.
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file's modification time by 1 second.
Definition file.c:898
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition flags.c:54
bool OptGui
(pseudo) when the gui (and curses) are started
Definition globals.c:48
int dlg_compose(struct Email *e, struct Buffer *fcc, uint8_t flags, struct ConfigSubset *sub)
Allow the user to edit the message envelope -.
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
void mutt_select_fcc(struct Buffer *path, struct Email *e)
Select the FCC path for an email.
Definition exec.c:256
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
@ DISP_INLINE
Content is inline.
Definition mime.h:62
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:91
bool mutt_edit_attachment(struct Body *b)
Edit an attachment.
void mutt_sleep(short s)
Sleep for a while.
Definition muttlib.c:787
void pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:428
bool mutt_needs_mailcap(struct Body *b)
Does this type need a mailcap entry do display.
Definition muttlib.c:368
@ SEC_NONE
No flags are set.
Definition lib.h:91
@ SEC_INLINE
Email has an inline signature.
Definition lib.h:99
@ SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition lib.h:102
int nm_record_message(struct Mailbox *m, char *path, struct Email *e)
Add a message to the Notmuch database.
Definition notmuch.c:1956
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition parameter.c:111
int mutt_num_postponed(struct Mailbox *m, bool force)
Return the number of postponed messages.
Definition postpone.c:63
int mutt_get_postponed(struct Mailbox *m_cur, struct Email *hdr, struct Email **cur, struct Buffer *fcc)
Recall a postponed message.
Definition postpone.c:665
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
void mutt_rfc3676_space_stuff(struct Email *e)
Perform RFC3676 space stuffing on an Email.
Definition rfc3676.c:490
static int postpone_message(struct Email *e_post, struct Email *e_cur, const char *fcc, SendFlags flags, struct ConfigSubset *sub)
Save an Email for another day.
Definition send.c:1852
static bool is_reply(struct Email *reply, struct Email *orig)
Is one email a reply to another?
Definition send.c:1609
static int save_fcc(struct Mailbox *m, struct Email *e, struct Buffer *fcc, struct Body *clear_content, char *pgpkeylist, SendFlags flags, char **finalpath, struct ConfigSubset *sub)
Save an Email to a 'sent mail' folder.
Definition send.c:1673
static int envelope_defaults(struct Envelope *env, struct EmailArray *ea, SendFlags flags, struct ConfigSubset *sub)
Fill in some defaults for a new email.
Definition send.c:1089
void mutt_fix_reply_recipients(struct Envelope *env, struct ConfigSubset *sub)
Remove duplicate recipients.
Definition send.c:961
static int generate_body(FILE *fp_tmp, struct Email *e, SendFlags flags, struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
Create a new email body.
Definition send.c:1162
static void mutt_make_greeting(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add greetings string.
Definition send.c:692
static int invoke_mta(struct Mailbox *m, struct Email *e, struct ConfigSubset *sub)
Send an email.
Definition send.c:1437
static void process_user_recips(struct Envelope *env)
Process the user headers.
Definition send.c:379
static void process_user_header(struct Envelope *env)
Process the user headers.
Definition send.c:407
static int edit_envelope(struct Envelope *en, SendFlags flags, struct ConfigSubset *sub)
Edit Envelope fields.
Definition send.c:230
static bool abort_for_missing_attachments(const struct Body *b, struct ConfigSubset *sub)
Should we abort sending because of missing attachments?
Definition send.c:1978
static void set_reverse_name(struct AddressList *al, struct Envelope *env, struct ConfigSubset *sub)
Try to set the 'from' field from the recipients.
Definition send.c:1351
static void fix_end_of_file(const char *data)
Ensure a file ends with a linefeed.
Definition send.c:1535
static void append_signature(FILE *fp, struct ConfigSubset *sub)
Append a signature to an email.
Definition send.c:101
@ SEND_CONSUMED_STDIN
stdin has been read; don't read it twice
Definition send.h:61
@ SEND_NO_FREE_HEADER
Used by the -E flag.
Definition send.h:55
@ SEND_CLI_CRYPTO
Enable message security in modes that by default don't enable it.
Definition send.h:62
@ SEND_DRAFT_FILE
Used by the -H flag.
Definition send.h:56
@ SEND_POSTPONED_FCC
Used by mutt_get_postponed() to signal that the Mutt-Fcc header field was present.
Definition send.h:54
@ SEND_POSTPONED
Recall a postponed email.
Definition send.h:50
void mutt_update_encoding(struct Body *b, struct ConfigSubset *sub)
Update the encoding type.
Definition sendlib.c:421
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition body.h:68
bool use_disp
Content-Disposition uses filename= ?
Definition body.h:47
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
bool replied
Email has been replied to.
Definition email.h:51
struct AddressList x_original_to
Email's 'X-Original-to'.
Definition envelope.h:66
char * list_post
This stores a mailto URL, or nothing.
Definition envelope.h:67
enum MailboxType type
Mailbox type.
Definition mailbox.h:104
void * mdata
Driver specific data.
Definition mailbox.h:134
NNTP-specific Mailbox data -.
Definition mdata.h:34
int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition subset.c:392
#define buf_mktemp_draft(buf)
Definition tmp.h:34
+ Here is the caller graph for this function:

◆ send_simple_email()

static bool send_simple_email ( struct Mailbox * m,
struct EmailArray * ea,
const char * mailto,
const char * subj,
const char * body )
static

Compose an email given a few basic ingredients.

Parameters
mMailbox
eaArray of source Emails
mailtomailto address to parse (can include fields such as subject)
subjSubject, if not overridden by mailto
bodytext/plain body
Return values
trueSuccess
falseFailure

Definition at line 2903 of file send.c.

2905{
2906 struct Email *e = email_new();
2907
2908 /* envelope */
2909 e->env = mutt_env_new();
2910 mutt_parse_mailto(e->env, NULL, mailto);
2911 if (!e->env->subject)
2912 {
2913 mutt_env_set_subject(e->env, subj);
2914 }
2915 if (TAILQ_EMPTY(&e->env->to) && !mutt_addrlist_parse(&e->env->to, NULL))
2916 {
2917 mutt_warning(_("No recipient specified"));
2918 }
2919
2920 /* body */
2921 e->body = mutt_body_new();
2922 char ctype[] = "text/plain";
2923 mutt_parse_content_type(ctype, e->body);
2924
2925 struct Buffer *tempfile = buf_pool_get();
2926 buf_mktemp_draft(tempfile);
2927 if (body)
2928 {
2929 FILE *fp = mutt_file_fopen(buf_string(tempfile), "w+");
2930 if (!fp)
2931 {
2932 email_free(&e);
2933 buf_pool_release(&tempfile);
2934 return false;
2935 }
2936 fprintf(fp, "%s\n", body);
2937 mutt_file_fclose(&fp);
2938 }
2939 e->body->filename = buf_strdup(tempfile);
2940 e->body->unlink = true;
2941 buf_pool_release(&tempfile);
2942
2943 const int rc = mutt_send_message(SEND_DRAFT_FILE, e, NULL, m, ea, NeoMutt->sub);
2944 return rc >= 0;
2945}
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_send_list_subscribe()

bool mutt_send_list_subscribe ( struct Mailbox * m,
struct Email * e )

Send a mailing-list subscription email.

Parameters
mMailbox
eEmail carrying mailing-list subscription headers
Return values
trueSuccess
falseFailure

Definition at line 2954 of file send.c.

2955{
2956 if (!e || !e->env)
2957 {
2958 return false;
2959 }
2960
2961 const char *mailto = e->env->list_subscribe;
2962 if (!mailto)
2963 {
2964 mutt_warning(_("No List-Subscribe header found"));
2965 return false;
2966 }
2967
2968 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
2969 ARRAY_ADD(&ea, e);
2970 bool rc = send_simple_email(m, &ea, mailto, "Subscribe", "subscribe");
2971 ARRAY_FREE(&ea);
2972
2973 return rc;
2974}
static bool send_simple_email(struct Mailbox *m, struct EmailArray *ea, const char *mailto, const char *subj, const char *body)
Compose an email given a few basic ingredients.
Definition send.c:2903
char * list_subscribe
This stores a mailto URL, or nothing.
Definition envelope.h:68
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_send_list_unsubscribe()

bool mutt_send_list_unsubscribe ( struct Mailbox * m,
struct Email * e )

Send a mailing-list unsubscription email.

Parameters
mMailbox
eEmail carrying mailing-list unsubscription headers
Return values
trueSuccess
falseFailure

Definition at line 2983 of file send.c.

2984{
2985 if (!e || !e->env)
2986 {
2987 return false;
2988 }
2989
2990 const char *mailto = e->env->list_unsubscribe;
2991 if (!mailto)
2992 {
2993 mutt_warning(_("No List-Unsubscribe header found"));
2994 return false;
2995 }
2996
2997 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
2998 ARRAY_ADD(&ea, e);
2999 bool rc = send_simple_email(m, &ea, mailto, "Unsubscribe", "unsubscribe");
3000 ARRAY_FREE(&ea);
3001
3002 return rc;
3003}
char * list_unsubscribe
This stores a mailto URL, or nothing.
Definition envelope.h:69
+ Here is the call graph for this function:
+ Here is the caller graph for this function: