NeoMutt  2024-02-01-35-geee02f
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 <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 "history/lib.h"
#include "imap/lib.h"
#include "ncrypt/lib.h"
#include "pager/lib.h"
#include "parse/lib.h"
#include "pattern/lib.h"
#include "postpone/lib.h"
#include "question/lib.h"
#include "body.h"
#include "copy.h"
#include "format_flags.h"
#include "globals.h"
#include "handler.h"
#include "hdrline.h"
#include "header.h"
#include "hook.h"
#include "maillist.h"
#include "multipart.h"
#include "mutt_body.h"
#include "mutt_header.h"
#include "mutt_logging.h"
#include "muttlib.h"
#include "mx.h"
#include "nntp/mdata.h"
#include "protos.h"
#include "rfc3676.h"
#include "sendlib.h"
#include "sendmail.h"
#include "smtp.h"
#include "sort.h"
#include "mixmaster/lib.h"
#include "notmuch/lib.h"
#include "autocrypt/lib.h"

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 char *s, 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 const char * greeting_format_str (char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
 Format a greetings string - Implements format_t -.
 
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

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 107 of file send.c.

108{
109 const char *const c_signature = cs_subset_path(sub, "signature");
110 if (!c_signature)
111 return;
112
113 // If the user hasn't set $signature, don't warn them if it doesn't exist
114 struct Buffer *def_sig = buf_pool_get();
115 cs_str_initial_get(sub->cs, "signature", def_sig);
116 mutt_path_canon(def_sig, HomeDir, false);
117 bool notify_missing = !mutt_str_equal(c_signature, buf_string(def_sig));
118 buf_pool_release(&def_sig);
119
120 pid_t pid = 0;
121 FILE *fp_tmp = mutt_open_read(c_signature, &pid);
122 if (!fp_tmp)
123 {
124 if (notify_missing)
125 mutt_perror("%s", c_signature);
126 return;
127 }
128
129 const bool c_sig_dashes = cs_subset_bool(sub, "sig_dashes");
130 if (c_sig_dashes)
131 fputs("\n-- \n", fp);
132 mutt_file_copy_stream(fp_tmp, fp);
133 mutt_file_fclose(&fp_tmp);
134 if (pid != -1)
135 filter_wait(pid);
136}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:97
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
char * HomeDir
User's home directory.
Definition: globals.c:38
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:565
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:282
#define mutt_file_fclose(FP)
Definition: file.h:148
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:219
#define mutt_perror(...)
Definition: logging2.h:93
bool mutt_path_canon(struct Buffer *path, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition: path.c:247
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:709
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1279
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
String manipulation buffer.
Definition: buffer.h:36
struct ConfigSet * cs
Parent ConfigSet.
Definition: subset.h:51
+ 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 144 of file send.c.

145{
146 struct Address *a = NULL, *tmp = NULL;
147 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
148 {
149 if (mutt_addr_is_user(a) && (!leave_only || TAILQ_NEXT(a, entries)))
150 {
151 TAILQ_REMOVE(al, a, entries);
152 mutt_addr_free(&a);
153 }
154 }
155}
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:605
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
#define TAILQ_NEXT(elm, field)
Definition: queue.h:832
An email address.
Definition: address.h:36
+ 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 163 of file send.c.

165{
166 const struct AddressList *const als[] = { t, c };
167
168 for (size_t i = 0; i < mutt_array_size(als); ++i)
169 {
170 const struct AddressList *al = als[i];
171 struct Address *a = NULL;
172 TAILQ_FOREACH(a, al, entries)
173 {
174 if (!a->group && mutt_is_mail_list(a))
175 {
177 }
178 }
179 }
180}
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1484
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition: address.c:745
bool mutt_is_mail_list(const struct Address *addr)
Is this the email address of a mailing list? - Implements addr_predicate_t -.
Definition: maillist.c:45
#define mutt_array_size(x)
Definition: memory.h:38
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
bool group
Group mailbox?
Definition: address.h:39
+ 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 190 of file send.c.

191{
192 int rc = 0;
193 struct Buffer *buf = buf_pool_get();
194 buf_alloc(buf, 8192);
195 char *err = NULL;
196 int idna_ok = 0;
197
198 do
199 {
201 buf_reset(buf);
202 mutt_addrlist_write(al, buf, false);
203 if (mw_get_field(field, buf, MUTT_COMP_NO_FLAGS, HC_ALIAS, &CompleteAliasOps, NULL) != 0)
204 {
205 rc = -1;
206 goto done;
207 }
210 if (expand_aliases)
212 idna_ok = mutt_addrlist_to_intl(al, &err);
213 if (idna_ok != 0)
214 {
215 mutt_error(_("Bad IDN: '%s'"), err);
216 FREE(&err);
217 }
218 } while (idna_ok != 0);
219
220done:
221 buf_pool_release(&buf);
222 return rc;
223}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1464
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1382
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition: address.c:1210
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:644
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1297
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:300
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:93
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:354
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:274
#define mutt_error(...)
Definition: logging2.h:92
@ HC_ALIAS
Aliases.
Definition: lib.h:52
#define FREE(x)
Definition: memory.h:45
#define _(a)
Definition: message.h:28
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:56
+ 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 233 of file send.c.

234{
235 int rc = -1;
236 struct Buffer *buf = buf_pool_get();
237 buf_alloc(buf, 8192);
238
239 if (OptNewsSend)
240 {
241 if (en->newsgroups)
242 buf_strcpy(buf, en->newsgroups);
243 else
244 buf_reset(buf);
245
246 if (mw_get_field("Newsgroups: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
247 {
248 goto done;
249 }
251
252 if (en->followup_to)
253 buf_strcpy(buf, en->followup_to);
254 else
255 buf_reset(buf);
256
257 const bool c_ask_followup_to = cs_subset_bool(sub, "ask_followup_to");
258 if (c_ask_followup_to && (mw_get_field("Followup-To: ", buf, MUTT_COMP_NO_FLAGS,
259 HC_OTHER, NULL, NULL) != 0))
260 {
261 goto done;
262 }
264
265 if (en->x_comment_to)
266 buf_strcpy(buf, en->x_comment_to);
267 else
268 buf_reset(buf);
269
270 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
271 const bool c_ask_x_comment_to = cs_subset_bool(sub, "ask_x_comment_to");
272 if (c_x_comment_to && c_ask_x_comment_to &&
273 (mw_get_field("X-Comment-To: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0))
274 {
275 goto done;
276 }
278 }
279 else
280 {
281 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
282 if (TAILQ_EMPTY(&en->to) || !c_fast_reply || (flags & SEND_REVIEW_TO))
283 {
284 if ((mutt_edit_address(&en->to, _("To: "), true) == -1))
285 goto done;
286 }
287
288 const bool c_ask_cc = cs_subset_bool(sub, "ask_cc");
289 if (TAILQ_EMPTY(&en->cc) || !c_fast_reply)
290 {
291 if (c_ask_cc && (mutt_edit_address(&en->cc, _("Cc: "), true) == -1))
292 goto done;
293 }
294
295 const bool c_ask_bcc = cs_subset_bool(sub, "ask_bcc");
296 if (TAILQ_EMPTY(&en->bcc) || !c_fast_reply)
297 {
298 if (c_ask_bcc && (mutt_edit_address(&en->bcc, _("Bcc: "), true) == -1))
299 goto done;
300 }
301
302 if (TAILQ_EMPTY(&en->to) && TAILQ_EMPTY(&en->cc) && TAILQ_EMPTY(&en->bcc))
303 {
304 mutt_warning(_("No recipients specified"));
305 goto done;
306 }
307
308 const bool c_reply_with_xorig = cs_subset_bool(sub, "reply_with_xorig");
309 if (c_reply_with_xorig && (flags & (SEND_REPLY | SEND_LIST_REPLY | SEND_GROUP_REPLY)) &&
310 (mutt_edit_address(&en->from, "From: ", true) == -1))
311 {
312 goto done;
313 }
314 }
315
316 if (en->subject)
317 {
318 const bool c_fast_reply = cs_subset_bool(sub, "fast_reply");
319 if (c_fast_reply)
320 {
321 rc = 0;
322 goto done;
323 }
324 buf_strcpy(buf, en->subject);
325 }
326 else
327 {
328 const char *p = NULL;
329
330 buf_reset(buf);
331 struct ListNode *uh = NULL;
332 STAILQ_FOREACH(uh, &UserHeader, entries)
333 {
334 size_t plen = mutt_istr_startswith(uh->data, "subject:");
335 if (plen)
336 {
337 p = mutt_str_skip_email_wsp(uh->data + plen);
338 buf_strcpy(buf, p);
339 }
340 }
341 }
342
343 if ((mw_get_field(_("Subject: "), buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0) ||
344 (buf_is_empty(buf) &&
345 (query_quadoption(_("No subject, abort?"), sub, "abort_nosubject") != MUTT_NO)))
346 {
347 mutt_message(_("No subject, aborting"));
348 goto done;
349 }
351 rc = 0;
352
353done:
354 buf_pool_release(&buf);
355 return rc;
356}
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:308
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:412
void mutt_env_set_subject(struct Envelope *env, const char *subj)
Set both subject and real_subj to subj.
Definition: envelope.c:69
struct ListHead UserHeader
List of custom headers to add to outgoing emails.
Definition: globals.c:54
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: globals.c:71
#define mutt_warning(...)
Definition: logging2.h:90
#define mutt_message(...)
Definition: logging2.h:91
@ HC_OTHER
Miscellaneous strings.
Definition: lib.h:56
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:657
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:242
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:329
@ 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:373
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define TAILQ_EMPTY(head)
Definition: queue.h:721
int mutt_edit_address(struct AddressList *al, const char *field, bool expand_aliases)
Edit an email address.
Definition: send.c:190
#define SEND_GROUP_REPLY
Reply to all.
Definition: send.h:43
#define SEND_LIST_REPLY
Reply to mailing list.
Definition: send.h:44
#define SEND_REPLY
Reply to sender.
Definition: send.h:42
#define SEND_REVIEW_TO
Allow the user to edit the To field.
Definition: send.h:56
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:35
char * data
String.
Definition: list.h:36
+ 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 365 of file send.c.

366{
367 SKIPWS(s);
368 return mutt_str_dup(s);
369}
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
#define SKIPWS(ch)
Definition: string2.h:45
+ 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 375 of file send.c.

376{
377 struct ListNode *uh = NULL;
378 STAILQ_FOREACH(uh, &UserHeader, entries)
379 {
380 size_t plen;
381 if ((plen = mutt_istr_startswith(uh->data, "to:")))
382 mutt_addrlist_parse(&env->to, uh->data + plen);
383 else if ((plen = mutt_istr_startswith(uh->data, "cc:")))
384 mutt_addrlist_parse(&env->cc, uh->data + plen);
385 else if ((plen = mutt_istr_startswith(uh->data, "bcc:")))
386 mutt_addrlist_parse(&env->bcc, uh->data + plen);
387 else if ((plen = mutt_istr_startswith(uh->data, "newsgroups:")))
388 env->newsgroups = nntp_get_header(uh->data + plen);
389 else if ((plen = mutt_istr_startswith(uh->data, "followup-to:")))
390 env->followup_to = nntp_get_header(uh->data + plen);
391 else if ((plen = mutt_istr_startswith(uh->data, "x-comment-to:")))
392 env->x_comment_to = nntp_get_header(uh->data + plen);
393 }
394}
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:365
+ 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 400 of file send.c.

401{
402 struct ListNode *uh = NULL;
403 STAILQ_FOREACH(uh, &UserHeader, entries)
404 {
405 size_t plen;
406 if ((plen = mutt_istr_startswith(uh->data, "from:")))
407 {
408 /* User has specified a default From: address. Remove default address */
410 mutt_addrlist_parse(&env->from, uh->data + plen);
411 }
412 else if ((plen = mutt_istr_startswith(uh->data, "reply-to:")))
413 {
415 mutt_addrlist_parse(&env->reply_to, uh->data + plen);
416 }
417 else if ((plen = mutt_istr_startswith(uh->data, "message-id:")))
418 {
419 char *tmp = mutt_extract_message_id(uh->data + plen, NULL);
420 if (mutt_addr_valid_msgid(tmp))
421 {
422 FREE(&env->message_id);
423 env->message_id = tmp;
424 }
425 else
426 {
427 FREE(&tmp);
428 }
429 }
430 else if (!mutt_istr_startswith(uh->data, "to:") &&
431 !mutt_istr_startswith(uh->data, "cc:") &&
432 !mutt_istr_startswith(uh->data, "bcc:") &&
433 !mutt_istr_startswith(uh->data, "newsgroups:") &&
434 !mutt_istr_startswith(uh->data, "followup-to:") &&
435 !mutt_istr_startswith(uh->data, "x-comment-to:") &&
436 !mutt_istr_startswith(uh->data, "supersedes:") &&
437 !mutt_istr_startswith(uh->data, "subject:") &&
438 !mutt_istr_startswith(uh->data, "return-path:"))
439 {
441 }
442 }
443}
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition: address.c:792
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition: parse.c:365
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
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 451 of file send.c.

452{
453 const char *const c_forward_attribution_intro = cs_subset_string(sub, "forward_attribution_intro");
454 if (!c_forward_attribution_intro || !fp)
455 return;
456
457 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
458
459 struct Buffer *buf = buf_pool_get();
460 setlocale(LC_TIME, NONULL(c_attribution_locale));
461 mutt_make_string(buf, 0, c_forward_attribution_intro, NULL, -1, e,
463 setlocale(LC_TIME, "");
464 fputs(buf_string(buf), fp);
465 fputs("\n\n", fp);
466 buf_pool_release(&buf);
467}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:292
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
void mutt_make_string(struct Buffer *buf, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1440
#define NONULL(x)
Definition: string2.h:37
+ 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 475 of file send.c.

476{
477 const char *const c_forward_attribution_trailer = cs_subset_string(sub, "forward_attribution_trailer");
478 if (!c_forward_attribution_trailer || !fp)
479 return;
480
481 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
482
483 struct Buffer *buf = buf_pool_get();
484 setlocale(LC_TIME, NONULL(c_attribution_locale));
485 mutt_make_string(buf, 0, c_forward_attribution_trailer, NULL, -1, e,
487 setlocale(LC_TIME, "");
488 fputc('\n', fp);
489 fputs(buf_string(buf), fp);
490 fputc('\n', fp);
491 buf_pool_release(&buf);
492}
+ 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 503 of file send.c.

505{
506 CopyHeaderFlags chflags = CH_DECODE;
508
509 struct Message *msg = mx_msg_open(m, e);
510 if (!msg)
511 {
512 return -1;
513 }
516
517 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
518 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT) && c_forward_decode)
519 {
520 /* make sure we have the user's passphrase before proceeding... */
522 {
523 mx_msg_close(m, &msg);
524 return -1;
525 }
526 }
527
528 mutt_forward_intro(e, fp_out, sub);
529
530 if (c_forward_decode)
531 {
532 cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
533
534 const bool c_weed = cs_subset_bool(sub, "weed");
535 if (c_weed)
536 {
537 chflags |= CH_WEED | CH_REORDER;
538 cmflags |= MUTT_CM_WEED;
539 }
540 }
541
542 const bool c_forward_quote = cs_subset_bool(sub, "forward_quote");
543 if (c_forward_quote)
544 cmflags |= MUTT_CM_PREFIX;
545
546 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
547 mx_msg_close(m, &msg);
548 mutt_forward_trailer(e, fp_out, sub);
549 return 0;
550}
void mutt_parse_mime_message(struct Email *e, FILE *fp)
Parse a MIME email.
Definition: attachments.c:597
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.c:908
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:56
#define MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition: copy.h:43
#define MUTT_CM_PREFIX
Quote the header and body.
Definition: copy.h:39
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition: copy.h:40
#define CH_WEED
Weed the headers?
Definition: copy.h:55
#define CH_REORDER
Re-order output of headers (specified by 'hdr_order')
Definition: copy.h:61
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition: copy.h:44
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:52
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:37
uint16_t CopyMessageFlags
Flags for mutt_copy_message(), e.g. MUTT_CM_NOHEADER.
Definition: copy.h:36
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:132
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition: hook.c:671
#define MUTT_MESSAGE_HOOK
message-hook: run before displaying a message
Definition: hook.h:44
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition: mx.c:1178
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition: mx.c:1132
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:78
#define WithCrypto
Definition: lib.h:116
void mutt_forward_intro(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add the "start of forwarded message" text.
Definition: send.c:451
void mutt_forward_trailer(struct Email *e, FILE *fp, struct ConfigSubset *sub)
Add a "end of forwarded message" text.
Definition: send.c:475
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 562 of file send.c.

565{
566 struct Body **last = *plast;
567 struct Body *body = NULL;
568 struct AttachCtx *actx = NULL;
569 int rc = 0, i;
570
571 struct Message *msg = mx_msg_open(m, e);
572 if (!msg)
573 {
574 return -1;
575 }
576
579
580 actx = mutt_mem_calloc(1, sizeof(*actx));
581 actx->email = e;
582 actx->fp_root = msg->fp;
583
584 mutt_generate_recvattach_list(actx, actx->email, actx->email->body,
585 actx->fp_root, -1, 0, 0);
586
587 for (i = 0; i < actx->idxlen; i++)
588 {
589 body = actx->idx[i]->body;
590 if ((body->type != TYPE_MULTIPART) && mutt_prefer_as_attachment(body) &&
591 !((body->type == TYPE_APPLICATION) &&
592 (mutt_istr_equal(body->subtype, "pgp-signature") ||
593 mutt_istr_equal(body->subtype, "x-pkcs7-signature") ||
594 mutt_istr_equal(body->subtype, "pkcs7-signature"))))
595 {
596 /* Ask the quadoption only once */
597 if (*forwardq == MUTT_ABORT)
598 {
599 /* L10N: This is the prompt for $forward_attachments.
600 When inline forwarding ($mime_forward answered "no"), this prompts
601 whether to add non-decodable attachments from the original email.
602 Text/plain parts and the like will already be included in the
603 message contents, but other attachment, such as PDF files, will also
604 be added as attachments to the new mail, if this is answered yes. */
605 *forwardq = query_quadoption(_("Forward attachments?"), sub, "forward_attachments");
606 if (*forwardq != MUTT_YES)
607 {
608 if (*forwardq == -1)
609 rc = -1;
610 goto cleanup;
611 }
612 }
613 if (mutt_body_copy(actx->idx[i]->fp, last, body) == -1)
614 {
615 rc = -1;
616 goto cleanup;
617 }
618 last = &((*last)->next);
619 }
620 }
621
622cleanup:
623 *plast = last;
624 mx_msg_close(m, &msg);
625 mutt_actx_free(&actx);
626 return rc;
627}
void mutt_actx_free(struct AttachCtx **ptr)
Free an Attachment Context.
Definition: attach.c:198
bool mutt_prefer_as_attachment(struct Body *b)
Do we want this part as an attachment?
Definition: handler.c:1849
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
@ 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:721
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: mutt_body.c:49
@ 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.
Definition: recvattach.c:1091
A set of attachments.
Definition: attach.h:51
FILE * fp_root
Used by recvattach for updating.
Definition: attach.h:53
struct Email * email
Used by recvattach for updating.
Definition: attach.h:52
struct AttachPtr ** idx
Array of attachments.
Definition: attach.h:55
short idxlen
Number of attachmentes.
Definition: attach.h:56
struct Body * body
Attachment.
Definition: attach.h:36
FILE * fp
Used in the recvattach menu.
Definition: attach.h:37
The body of an email.
Definition: body.h:36
char * subtype
content-type subtype
Definition: body.h:60
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 char *  s,
struct Email e,
FILE *  fp_out,
struct ConfigSubset sub 
)
static

Format an attribution prefix/suffix.

Parameters
sString to format
eEmail
fp_outFile to write to
subConfig Subset

Definition at line 636 of file send.c.

638{
639 if (!s || !fp_out)
640 return;
641
642 const char *const c_attribution_locale = cs_subset_string(sub, "attribution_locale");
643
644 struct Buffer *buf = buf_pool_get();
645 setlocale(LC_TIME, NONULL(c_attribution_locale));
646 mutt_make_string(buf, 0, s, NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
647 setlocale(LC_TIME, "");
648 fputs(buf_string(buf), fp_out);
649 fputc('\n', fp_out);
650 buf_pool_release(&buf);
651}
+ 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 659 of file send.c.

660{
661 format_attribution(cs_subset_string(sub, "attribution_intro"), e, fp_out, sub);
662}
static void format_attribution(const char *s, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Format an attribution prefix/suffix.
Definition: send.c:636
+ 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 670 of file send.c.

671{
672 format_attribution(cs_subset_string(sub, "attribution_trailer"), e, fp_out, sub);
673}
+ 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, greeting_format_str()

Definition at line 748 of file send.c.

749{
750 const char *const c_greeting = cs_subset_string(sub, "greeting");
751 if (!c_greeting || !fp_out)
752 return;
753
754 char buf[1024] = { 0 };
755
756 mutt_expando_format(buf, sizeof(buf), 0, 0, c_greeting, greeting_format_str,
757 (intptr_t) e, TOKEN_NO_FLAGS);
758
759 fputs(buf, fp_out);
760 fputc('\n', fp_out);
761}
#define TOKEN_NO_FLAGS
No flags are set.
Definition: extract.h:46
static const char * greeting_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Format a greetings string - Implements format_t -.
Definition: send.c:684
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string -.
Definition: muttlib.c:740
+ 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 772 of file send.c.

774{
776 CopyHeaderFlags chflags = CH_DECODE;
777
778 if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT))
779 {
780 /* make sure we have the user's passphrase before proceeding... */
782 return -1;
783 }
784
785 struct Message *msg = mx_msg_open(m, e);
786 if (!msg)
787 {
788 return -1;
789 }
792
793 mutt_make_attribution_intro(e, fp_out, sub);
794
795 const bool c_header = cs_subset_bool(sub, "header");
796 if (!c_header)
797 cmflags |= MUTT_CM_NOHEADER;
798
799 const bool c_weed = cs_subset_bool(sub, "weed");
800 if (c_weed)
801 {
802 chflags |= CH_WEED | CH_REORDER;
803 cmflags |= MUTT_CM_WEED;
804 }
805
806 mutt_copy_message(fp_out, e, msg, cmflags, chflags, 0);
807 mx_msg_close(m, &msg);
808
809 mutt_make_attribution_trailer(e, fp_out, sub);
810
811 return 0;
812}
#define MUTT_CM_REPLYING
Replying the message.
Definition: copy.h:46
#define MUTT_CM_NOHEADER
Don't copy the message header.
Definition: copy.h:38
void mutt_make_attribution_intro(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add "on DATE, PERSON wrote" header.
Definition: send.c:659
void mutt_make_attribution_trailer(struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Add suffix to replied email text.
Definition: send.c:670
+ 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 821 of file send.c.

824{
825 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
826 if (!c_reply_self && mutt_addr_is_user(from))
827 {
828 /* mail is from the user, assume replying to recipients */
829 return &env->to;
830 }
831 else
832 {
833 return &env->from;
834 }
835}
+ 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 847 of file send.c.

849{
850 const struct Address *from = TAILQ_FIRST(&env->from);
851 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
852
853 if (flags && !TAILQ_EMPTY(&env->mail_followup_to) && (hmfupto == MUTT_YES))
854 {
855 mutt_addrlist_copy(to, &env->mail_followup_to, true);
856 return 0;
857 }
858
859 /* Exit now if we're setting up the default Cc list for list-reply
860 * (only set if Mail-Followup-To is present and honoured). */
861 if (flags & SEND_LIST_REPLY)
862 return 0;
863
864 const struct AddressList *default_to = choose_default_to(from, env, sub);
865
866 if (reply_to)
867 {
868 const bool from_is_reply_to = mutt_addr_cmp(from, reply_to);
869 const bool multiple_reply_to = reply_to &&
870 TAILQ_NEXT(TAILQ_FIRST(&env->reply_to), entries);
871
872 const bool c_ignore_list_reply_to = cs_subset_bool(sub, "ignore_list_reply_to");
873 const enum QuadOption c_reply_to = cs_subset_quad(sub, "reply_to");
874 if ((from_is_reply_to && !multiple_reply_to && !reply_to->personal) ||
875 (c_ignore_list_reply_to && mutt_is_mail_list(reply_to) &&
876 (mutt_addrlist_search(&env->to, reply_to) || mutt_addrlist_search(&env->cc, reply_to))))
877 {
878 /* If the Reply-To: address is a mailing list, assume that it was
879 * put there by the mailing list, and use the From: address
880 *
881 * We also take the from header if our correspondent has a reply-to
882 * header which is identical to the electronic mail address given
883 * in his From header, and the reply-to has no display-name. */
884 mutt_addrlist_copy(to, &env->from, false);
885 }
886 else if (!(from_is_reply_to && !multiple_reply_to) && (c_reply_to != MUTT_YES))
887 {
888 char prompt[256] = { 0 };
889 /* There are quite a few mailing lists which set the Reply-To:
890 * header field to the list address, which makes it quite impossible
891 * to send a message to only the sender of the message. This
892 * provides a way to do that. */
893 /* L10N: Asks whether the user respects the reply-to header.
894 If she says no, neomutt will reply to the from header's address instead. */
895 snprintf(prompt, sizeof(prompt), _("Reply to %s%s?"),
896 buf_string(reply_to->mailbox), multiple_reply_to ? ",..." : "");
897 switch (query_quadoption(prompt, sub, "reply_to"))
898 {
899 case MUTT_YES:
900 mutt_addrlist_copy(to, &env->reply_to, false);
901 break;
902
903 case MUTT_NO:
904 mutt_addrlist_copy(to, default_to, false);
905 break;
906
907 default:
908 return -1; /* abort */
909 }
910 }
911 else
912 {
913 mutt_addrlist_copy(to, &env->reply_to, false);
914 }
915 }
916 else
917 {
918 mutt_addrlist_copy(to, default_to, false);
919 }
920
921 return 0;
922}
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:765
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition: address.c:892
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition: address.c:909
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:193
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
#define TAILQ_FIRST(head)
Definition: queue.h:723
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:821
static int default_to(struct AddressList *to, struct Envelope *env, SendFlags flags, int hmfupto, struct ConfigSubset *sub)
Generate default email addresses.
Definition: send.c:847
struct Buffer * personal
Real name of address.
Definition: address.h:37
struct Buffer * mailbox
Mailbox and host address.
Definition: address.h:38
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 933 of file send.c.

935{
936 enum QuadOption hmfupto = MUTT_ABORT;
937 const struct Address *followup_to = TAILQ_FIRST(&in->mail_followup_to);
938
939 if ((flags & (SEND_LIST_REPLY | SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) && followup_to)
940 {
941 char prompt[256] = { 0 };
942 snprintf(prompt, sizeof(prompt), _("Follow-up to %s%s?"),
943 buf_string(followup_to->mailbox),
944 TAILQ_NEXT(TAILQ_FIRST(&in->mail_followup_to), entries) ? ",..." : "");
945
946 hmfupto = query_quadoption(prompt, sub, "honor_followup_to");
947 if (hmfupto == MUTT_ABORT)
948 return -1;
949 }
950
951 if (flags & SEND_LIST_REPLY)
952 {
953 add_mailing_lists(&out->to, &in->to, &in->cc);
954
955 if (followup_to && (hmfupto == MUTT_YES) &&
956 (default_to(&out->cc, in, flags & SEND_LIST_REPLY, (hmfupto == MUTT_YES), sub) == MUTT_ABORT))
957 {
958 return -1; /* abort */
959 }
960 }
961 else if (flags & SEND_TO_SENDER)
962 {
963 mutt_addrlist_copy(&out->to, &in->from, false);
964 }
965 else
966 {
967 if (default_to(&out->to, in, flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY),
968 (hmfupto == MUTT_YES), sub) == -1)
969 {
970 return -1; /* abort */
971 }
972
973 if ((flags & (SEND_GROUP_REPLY | SEND_GROUP_CHAT_REPLY)) &&
974 (!followup_to || (hmfupto != MUTT_YES)))
975 {
976 /* if(!mutt_addr_is_user(in->to)) */
977 if (flags & SEND_GROUP_REPLY)
978 mutt_addrlist_copy(&out->cc, &in->to, true);
979 else
980 mutt_addrlist_copy(&out->to, &in->to, true);
981 mutt_addrlist_copy(&out->cc, &in->cc, true);
982 }
983 }
984 return 0;
985}
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:163
#define SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition: send.h:54
#define SEND_TO_SENDER
Compose new email to sender.
Definition: send.h:53
+ 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 992 of file send.c.

993{
994 struct ListNode *np = NULL;
995
996 struct ListHead *src = STAILQ_EMPTY(&env->references) ? &env->in_reply_to : &env->references;
997 STAILQ_FOREACH(np, src, entries)
998 {
1000 }
1001}
#define STAILQ_EMPTY(head)
Definition: queue.h:348
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 1008 of file send.c.

1009{
1010 if (env->message_id)
1011 {
1013 }
1014}
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:45
+ 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 1021 of file send.c.

1022{
1023 const bool c_me_too = cs_subset_bool(sub, "me_too");
1024 if (!c_me_too)
1025 {
1026 const bool c_reply_self = cs_subset_bool(sub, "reply_self");
1027
1028 /* the order is important here. do the CC: first so that if the
1029 * the user is the only recipient, it ends up on the TO: field */
1030 remove_user(&env->cc, TAILQ_EMPTY(&env->to));
1031 remove_user(&env->to, TAILQ_EMPTY(&env->cc) || c_reply_self);
1032 }
1033
1034 /* the CC field can get cluttered, especially with lists */
1035 mutt_addrlist_dedupe(&env->to);
1036 mutt_addrlist_dedupe(&env->cc);
1037 mutt_addrlist_remove_xrefs(&env->to, &env->cc);
1038
1039 if (!TAILQ_EMPTY(&env->cc) && TAILQ_EMPTY(&env->to))
1040 {
1041 TAILQ_SWAP(&env->to, &env->cc, Address, entries);
1042 }
1043}
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1437
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition: address.c:1401
#define TAILQ_SWAP(head1, head2, type, field)
Definition: queue.h:859
static void remove_user(struct AddressList *al, bool leave_only)
Remove any address which matches the current user.
Definition: send.c:144
+ 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 1051 of file send.c.

1052{
1053 if (!env)
1054 return;
1055
1056 const char *const c_forward_format = cs_subset_string(sub, "forward_format");
1057
1058 struct Buffer *buf = buf_pool_get();
1059 /* set the default subject for the message. */
1060 mutt_make_string(buf, 0, NONULL(c_forward_format), NULL, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
1062 buf_pool_release(&buf);
1063}
+ 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 1071 of file send.c.

1073{
1074 if (!env || !env_cur)
1075 return;
1076
1077 /* This takes precedence over a subject that might have
1078 * been taken from a List-Post header. Is that correct? */
1079 if (env_cur->real_subj)
1080 {
1081 char *subj = NULL;
1082 mutt_str_asprintf(&subj, "Re: %s", env_cur->real_subj);
1083 mutt_env_set_subject(env, subj);
1084 FREE(&subj);
1085 }
1086 else if (!env->subject)
1087 {
1088 const char *const c_empty_subject = cs_subset_string(sub, "empty_subject");
1089 mutt_env_set_subject(env, c_empty_subject);
1090 }
1091}
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:852
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 1099 of file send.c.

1101{
1102 add_references(&env->references, env_cur);
1103 add_message_id(&env->references, env_cur);
1104 add_message_id(&env->in_reply_to, env_cur);
1105
1106 const bool c_x_comment_to = cs_subset_bool(sub, "x_comment_to");
1107 if (OptNewsSend && c_x_comment_to && !TAILQ_EMPTY(&env_cur->from))
1109}
static void add_message_id(struct ListHead *head, struct Envelope *env)
Add the email's message ID to a list.
Definition: send.c:1008
static void add_references(struct ListHead *head, struct Envelope *env)
Add the email's references to a list.
Definition: send.c:992
const char * mutt_get_name(const struct Address *a)
Pick the best name to display from an address.
Definition: sort.c:134
+ 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 1117 of file send.c.

1119{
1120 if (!ea || !env || ARRAY_EMPTY(ea))
1121 return;
1122
1123 struct Email **ep = NULL;
1124 ARRAY_FOREACH(ep, ea)
1125 {
1126 struct Email *e = *ep;
1128 }
1129
1130 /* if there's more than entry in In-Reply-To (i.e. message has multiple
1131 * parents), don't generate a References: header as it's discouraged by
1132 * RFC2822, sect. 3.6.4 */
1133 if ((ARRAY_SIZE(ea) > 1) && !STAILQ_EMPTY(&env->in_reply_to) &&
1135 {
1137 }
1138}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:212
#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:122
#define STAILQ_FIRST(head)
Definition: queue.h:350
#define STAILQ_NEXT(elm, field)
Definition: queue.h:400
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:1099
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 1149 of file send.c.

1151{
1152 if (!ea || ARRAY_EMPTY(ea))
1153 return -1;
1154
1155 struct Email *e_cur = *ARRAY_GET(ea, 0);
1156 bool single = (ARRAY_SIZE(ea) == 1);
1157
1158 struct Envelope *env_cur = e_cur->env;
1159 if (!env_cur)
1160 return -1;
1161
1162 if (flags & (SEND_REPLY | SEND_TO_SENDER))
1163 {
1164 if ((flags & SEND_NEWS))
1165 {
1166 /* in case followup set Newsgroups: with Followup-To: if it present */
1167 if (!env->newsgroups && !mutt_istr_equal(env_cur->followup_to, "poster"))
1168 {
1169 env->newsgroups = mutt_str_dup(env_cur->followup_to);
1170 }
1171 }
1172 else if (!single)
1173 {
1174 struct Email **ep = NULL;
1175 ARRAY_FOREACH(ep, ea)
1176 {
1177 struct Email *e = *ep;
1178 if (mutt_fetch_recips(env, e->env, flags, sub) == -1)
1179 return -1;
1180 }
1181 }
1182 else if (mutt_fetch_recips(env, env_cur, flags, sub) == -1)
1183 {
1184 return -1;
1185 }
1186
1187 if ((flags & SEND_LIST_REPLY) && TAILQ_EMPTY(&env->to))
1188 {
1189 mutt_error(_("No mailing lists found"));
1190 return -1;
1191 }
1192
1193 if (flags & SEND_REPLY)
1194 {
1195 mutt_make_misc_reply_headers(env, env_cur, sub);
1196 make_reference_headers(ea, env, sub);
1197 }
1198 }
1199 else if (flags & SEND_FORWARD)
1200 {
1201 mutt_make_forward_subject(env, e_cur, sub);
1202
1203 const bool c_forward_references = cs_subset_bool(sub, "forward_references");
1204 if (c_forward_references)
1205 make_reference_headers(ea, env, sub);
1206 }
1207
1208 return 0;
1209}
#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:1071
void mutt_make_forward_subject(struct Envelope *env, struct Email *e, struct ConfigSubset *sub)
Create a subject for a forwarded email.
Definition: send.c:1051
static void make_reference_headers(struct EmailArray *ea, struct Envelope *env, struct ConfigSubset *sub)
Generate reference headers for an email.
Definition: send.c:1117
int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, SendFlags flags, struct ConfigSubset *sub)
Generate recpients for a reply email.
Definition: send.c:933
#define SEND_NEWS
Reply to a news article.
Definition: send.h:55
#define SEND_FORWARD
Forward email.
Definition: send.h:45
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 1222 of file send.c.

1224{
1225 /* An EmailList is required for replying and forwarding */
1226 if (!ea && (flags & (SEND_REPLY | SEND_FORWARD)))
1227 return -1;
1228
1229 if (flags & SEND_REPLY)
1230 {
1231 enum QuadOption ans = query_quadoption(_("Include message in reply?"), sub, "include");
1232 if (ans == MUTT_ABORT)
1233 return -1;
1234
1235 if (ans == MUTT_YES)
1236 {
1237 mutt_message(_("Including quoted message..."));
1238 struct Email **ep = NULL;
1239 size_t count = ARRAY_SIZE(ea) - 1;
1240 ARRAY_FOREACH(ep, ea)
1241 {
1242 if (include_reply(m, *ep, fp_tmp, sub) == -1)
1243 {
1244 mutt_error(_("Could not include all requested messages"));
1245 return -1;
1246 }
1247 if (ARRAY_FOREACH_IDX < count)
1248 {
1249 fputc('\n', fp_tmp);
1250 }
1251 }
1252 }
1253 }
1254 else if (flags & SEND_FORWARD)
1255 {
1256 enum QuadOption ans = query_quadoption(_("Forward as attachment?"), sub, "mime_forward");
1257 if (ans == MUTT_YES)
1258 {
1259 struct Body *last = e->body;
1260
1261 mutt_message(_("Preparing forwarded message..."));
1262
1263 while (last && last->next)
1264 last = last->next;
1265
1266 struct Email **ep = NULL;
1267 ARRAY_FOREACH(ep, ea)
1268 {
1269 struct Body *tmp = mutt_make_message_attach(m, *ep, false, sub);
1270 if (last)
1271 {
1272 last->next = tmp;
1273 last = tmp;
1274 }
1275 else
1276 {
1277 last = tmp;
1278 e->body = tmp;
1279 }
1280 }
1281 }
1282 else if (ans != MUTT_ABORT)
1283 {
1284 enum QuadOption forwardq = MUTT_ABORT;
1285 struct Body **last = NULL;
1286
1287 const bool c_forward_decode = cs_subset_bool(sub, "forward_decode");
1288 const enum QuadOption c_forward_attachments = cs_subset_quad(sub, "forward_attachments");
1289 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1290 {
1291 last = &e->body;
1292 while (*last)
1293 last = &((*last)->next);
1294 }
1295
1296 struct Email **ep = NULL;
1297 ARRAY_FOREACH(ep, ea)
1298 {
1299 struct Email *e_cur = *ep;
1300 include_forward(m, e_cur, fp_tmp, sub);
1301 if (c_forward_decode && (c_forward_attachments != MUTT_NO))
1302 {
1303 if (inline_forward_attachments(m, e_cur, &last, &forwardq, sub) != 0)
1304 return -1;
1305 }
1306 }
1307 }
1308 else
1309 {
1310 return -1;
1311 }
1312 }
1313 else if (((WithCrypto & APPLICATION_PGP) != 0) && (flags & SEND_KEY))
1314 {
1315 struct Body *b = NULL;
1316
1317 if (((WithCrypto & APPLICATION_PGP) != 0) && !(b = crypt_pgp_make_key_attachment()))
1318 {
1319 return -1;
1320 }
1321
1322 b->next = e->body;
1323 e->body = b;
1324 }
1325
1327
1328 return 0;
1329}
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition: cryptglue.c:304
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
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:772
static int include_forward(struct Mailbox *m, struct Email *e, FILE *fp_out, struct ConfigSubset *sub)
Write out a forwarded message.
Definition: send.c:503
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:562
#define SEND_KEY
Mail a PGP public key.
Definition: send.h:48
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:71
+ 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 1336 of file send.c.

1337{
1338 /* Only generate the Mail-Followup-To if the user has requested it, and
1339 * it hasn't already been set */
1340
1341 const bool c_followup_to = cs_subset_bool(sub, "followup_to");
1342 if (!c_followup_to)
1343 return;
1344 if (OptNewsSend)
1345 {
1346 if (!env->followup_to && env->newsgroups && (strrchr(env->newsgroups, ',')))
1347 env->followup_to = mutt_str_dup(env->newsgroups);
1348 return;
1349 }
1350
1351 if (TAILQ_EMPTY(&env->mail_followup_to))
1352 {
1353 if (mutt_is_list_recipient(false, env))
1354 {
1355 /* this message goes to known mailing lists, so create a proper
1356 * mail-followup-to header */
1357
1358 mutt_addrlist_copy(&env->mail_followup_to, &env->to, false);
1359 mutt_addrlist_copy(&env->mail_followup_to, &env->cc, true);
1360 }
1361
1362 /* remove ourselves from the mail-followup-to header */
1363 remove_user(&env->mail_followup_to, false);
1364
1365 /* If we are not subscribed to any of the lists in question, re-add
1366 * ourselves to the mail-followup-to header. The mail-followup-to header
1367 * generated is a no-op with group-reply, but makes sure list-reply has the
1368 * desired effect. */
1369
1370 if (!TAILQ_EMPTY(&env->mail_followup_to) &&
1372 {
1373 struct AddressList *al = NULL;
1374 if (!TAILQ_EMPTY(&env->reply_to))
1375 al = &env->reply_to;
1376 else if (!TAILQ_EMPTY(&env->from))
1377 al = &env->from;
1378
1379 if (al)
1380 {
1381 struct Address *a = NULL;
1382 TAILQ_FOREACH_REVERSE(a, al, AddressList, entries)
1383 {
1385 }
1386 }
1387 else
1388 {
1390 }
1391 }
1392
1394 }
1395}
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition: address.c:1495
bool mutt_is_subscribed_list_recipient(bool all_addr, struct Envelope *env)
Matches subscribed mailing lists.
Definition: exec.c:479
bool mutt_is_list_recipient(bool all_addr, struct Envelope *env)
Matches known mailing lists.
Definition: exec.c:492
#define TAILQ_FOREACH_REVERSE(var, head, headname, field)
Definition: queue.h:745
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition: send.c:1460
+ 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 1407 of file send.c.

1409{
1410 struct Address *a = NULL;
1411 if (TAILQ_EMPTY(al))
1412 {
1413 TAILQ_FOREACH(a, &env->to, entries)
1414 {
1415 if (mutt_addr_is_user(a))
1416 {
1418 break;
1419 }
1420 }
1421 }
1422
1423 if (TAILQ_EMPTY(al))
1424 {
1425 TAILQ_FOREACH(a, &env->cc, entries)
1426 {
1427 if (mutt_addr_is_user(a))
1428 {
1430 break;
1431 }
1432 }
1433 }
1434
1435 if (TAILQ_EMPTY(al))
1436 {
1437 struct Address *from = TAILQ_FIRST(&env->from);
1438 if (from && mutt_addr_is_user(from))
1439 {
1441 }
1442 }
1443
1444 if (!TAILQ_EMPTY(al))
1445 {
1446 /* when $reverse_real_name is not set, clear the personal name so that it
1447 * may be set via a reply- or send-hook. */
1448
1449 const bool c_reverse_real_name = cs_subset_bool(sub, "reverse_real_name");
1450 if (!c_reverse_real_name)
1451 FREE(&TAILQ_FIRST(al)->personal);
1452 }
1453}
+ 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 1460 of file send.c.

1461{
1462 /* Note: We let $from override $real_name here.
1463 * Is this the right thing to do?
1464 */
1465
1466 const struct Address *c_from = cs_subset_address(sub, "from");
1467 if (c_from)
1468 {
1469 return mutt_addr_copy(c_from);
1470 }
1471
1472 char domain[1024] = { 0 };
1473 const char *mailbox = Username;
1474 const bool c_use_domain = cs_subset_bool(sub, "use_domain");
1475 if (c_use_domain)
1476 {
1477 snprintf(domain, sizeof(domain), "%s@%s", NONULL(Username),
1478 NONULL(mutt_fqdn(true, sub)));
1479 mailbox = domain;
1480 }
1481
1482 return mutt_addr_create(NULL, mailbox);
1483}
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.
Definition: config_type.c:273
char * Username
User's login name.
Definition: globals.c:41
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition: sendlib.c:706
+ 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 1493 of file send.c.

1494{
1495 struct Buffer *tempfile = NULL;
1496 int rc = -1;
1497
1498 /* Write out the message in MIME form. */
1499 tempfile = buf_pool_get();
1500 buf_mktemp(tempfile);
1501 FILE *fp_tmp = mutt_file_fopen(buf_string(tempfile), "w");
1502 if (!fp_tmp)
1503 goto cleanup;
1504
1505 const bool c_write_bcc = cs_subset_bool(sub, "write_bcc");
1506 const char *const c_smtp_url = cs_subset_string(sub, "smtp_url");
1507 if (c_smtp_url)
1508 cs_subset_str_native_set(sub, "write_bcc", false, NULL);
1509
1510#ifdef MIXMASTER
1512 !STAILQ_EMPTY(&e->chain),
1514#else
1516 false, mutt_should_hide_protected_subject(e), sub);
1517#endif
1518
1519 cs_subset_str_native_set(sub, "write_bcc", c_write_bcc, NULL);
1520
1521 fputc('\n', fp_tmp); /* tie off the header. */
1522
1523 if ((mutt_write_mime_body(e->body, fp_tmp, sub) == -1))
1524 goto cleanup;
1525
1526 if (mutt_file_fclose(&fp_tmp) != 0)
1527 {
1528 mutt_perror("%s", buf_string(tempfile));
1529 unlink(buf_string(tempfile));
1530 goto cleanup;
1531 }
1532
1533#ifdef MIXMASTER
1534 if (!STAILQ_EMPTY(&e->chain))
1535 {
1536 rc = mix_send_message(&e->chain, buf_string(tempfile));
1537 goto cleanup;
1538 }
1539#endif
1540
1541 if (OptNewsSend)
1542 goto sendmail;
1543
1544 if (c_smtp_url)
1545 {
1546 rc = mutt_smtp_send(&e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1547 buf_string(tempfile), (e->body->encoding == ENC_8BIT), sub);
1548 goto cleanup;
1549 }
1550
1551sendmail:
1552 rc = mutt_invoke_sendmail(m, &e->env->from, &e->env->to, &e->env->cc, &e->env->bcc,
1553 buf_string(tempfile), (e->body->encoding == ENC_8BIT), sub);
1554cleanup:
1555 if (fp_tmp)
1556 {
1557 mutt_file_fclose(&fp_tmp);
1558 unlink(buf_string(tempfile));
1559 }
1560 buf_pool_release(&tempfile);
1561 return rc;
1562}
bool mutt_should_hide_protected_subject(struct Email *e)
Should NeoMutt hide the protected subject?
Definition: crypt.c:1086
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:147
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:577
@ MUTT_WRITE_HEADER_NORMAL
A normal Email, write full header + MIME headers.
Definition: header.h:40
@ ENC_8BIT
8-bit text
Definition: mime.h:50
int mix_send_message(struct ListHead *chain, const char *tempfile)
Send an email via Mixmaster.
Definition: mixmaster.c:101
int mutt_write_mime_body(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:300
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:297
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:1108
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:41
struct ListHead chain
Mixmaster chain.
Definition: email.h:93
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:297
#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 1570 of file send.c.

1571{
1572 const struct Slist *const c_send_charset = cs_subset_slist(sub, "send_charset");
1573 for (struct Body *t = b; t; t = t->next)
1574 {
1575 if (t->description)
1576 {
1577 rfc2047_encode(&t->description, NULL, sizeof("Content-Description:"), c_send_charset);
1578 }
1579 if (recurse && t->parts)
1580 mutt_encode_descriptions(t->parts, recurse, sub);
1581 }
1582}
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:243
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition: rfc2047.c:628
void mutt_encode_descriptions(struct Body *b, bool recurse, struct ConfigSubset *sub)
RFC2047 encode the content-descriptions.
Definition: send.c:1570
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 1588 of file send.c.

1589{
1590 for (struct Body *t = b; t; t = t->next)
1591 {
1592 if (t->description)
1593 {
1594 rfc2047_decode(&t->description);
1595 }
1596 if (t->parts)
1597 decode_descriptions(t->parts);
1598 }
1599}
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:661
static void decode_descriptions(struct Body *b)
RFC2047 decode them in case of an error.
Definition: send.c:1588
+ 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 1605 of file send.c.

1606{
1607 FILE *fp = mutt_file_fopen(data, "a+");
1608 if (!fp)
1609 return;
1610
1611 if ((mutt_file_get_size_fp(fp) > 0) && mutt_file_seek(fp, -1, SEEK_END))
1612 {
1613 int c = fgetc(fp);
1614 if (c != '\n')
1615 fputc('\n', fp);
1616 }
1617 mutt_file_fclose(&fp);
1618}
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1578
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:771
+ 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 1630 of file send.c.

1632{
1633 struct Email *e_new = email_new();
1634
1635 if (mutt_prepare_template(fp, m, e_new, e_cur, true) < 0)
1636 {
1637 email_free(&e_new);
1638 return -1;
1639 }
1640
1641 if (WithCrypto)
1642 {
1643 /* mutt_prepare_template doesn't always flip on an application bit.
1644 * so fix that here */
1645 if (!(e_new->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1646 {
1647 const bool c_smime_is_default = cs_subset_bool(sub, "smime_is_default");
1648 if (((WithCrypto & APPLICATION_SMIME) != 0) && c_smime_is_default)
1649 e_new->security |= APPLICATION_SMIME;
1650 else if (WithCrypto & APPLICATION_PGP)
1651 e_new->security |= APPLICATION_PGP;
1652 else
1653 e_new->security |= APPLICATION_SMIME;
1654 }
1655
1656 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(sub, "crypt_opportunistic_encrypt");
1657 if (c_crypt_opportunistic_encrypt)
1658 {
1659 e_new->security |= SEC_OPPENCRYPT;
1661 }
1662 }
1663
1664 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
1665 ARRAY_ADD(&ea, e_cur);
1666 int rc = mutt_send_message(SEND_RESEND, e_new, NULL, m, &ea, sub);
1667 ARRAY_FREE(&ea);
1668
1669 return rc;
1670}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:156
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:204
#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:1031
struct Email * email_new(void)
Create a new Email.
Definition: email.c:80
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:46
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:86
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:91
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:483
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:2115
#define SEND_RESEND
Reply using the current email as a template.
Definition: send.h:49
+ 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 1679 of file send.c.

1680{
1681 if (!reply || !reply->env || !orig || !orig->env)
1682 return false;
1683 return mutt_list_find(&orig->env->references, reply->env->message_id) ||
1684 mutt_list_find(&orig->env->in_reply_to, reply->env->message_id);
1685}
struct ListNode * mutt_list_find(const struct ListHead *h, const char *data)
Find a string in a List.
Definition: list.c:102
+ 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 1698 of file send.c.

1699{
1700 const struct Regex *c_abort_noattach_regex = cs_subset_regex(sub, "abort_noattach_regex");
1701 const struct Regex *c_quote_regex = cs_subset_regex(sub, "quote_regex");
1702
1703 /* Search for the regex in `$abort_noattach_regex` within a file */
1704 if (!c_abort_noattach_regex || !c_abort_noattach_regex->regex ||
1705 !c_quote_regex || !c_quote_regex->regex)
1706 {
1707 return false;
1708 }
1709
1710 FILE *fp_att = mutt_file_fopen(filename, "r");
1711 if (!fp_att)
1712 return false;
1713
1714 char *inputline = mutt_mem_malloc(1024);
1715 bool found = false;
1716 while (!feof(fp_att) && fgets(inputline, 1024, fp_att))
1717 {
1718 if (!mutt_is_quote_line(inputline, NULL) &&
1719 mutt_regex_match(c_abort_noattach_regex, inputline))
1720 {
1721 found = true;
1722 break;
1723 }
1724 }
1725 FREE(&inputline);
1726 mutt_file_fclose(&fp_att);
1727 return found;
1728}
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:218
bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: display.c:323
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition: regex.c:639
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 1743 of file send.c.

1746{
1747 int rc = 0;
1748 struct Body *save_content = NULL;
1749
1750 buf_expand_path(fcc);
1751
1752 /* Don't save a copy when we are in batch-mode, and the FCC
1753 * folder is on an IMAP server: This would involve possibly lots
1754 * of user interaction, which is not available in batch mode.
1755 *
1756 * Note: A patch to fix the problems with the use of IMAP servers
1757 * from non-curses mode is available from Brendan Cully. However,
1758 * I'd like to think a bit more about this before including it. */
1759
1760 if ((flags & SEND_BATCH) && !buf_is_empty(fcc) &&
1761 (imap_path_probe(buf_string(fcc), NULL) == MUTT_IMAP))
1762 {
1763 mutt_error(_("Warning: Fcc to an IMAP mailbox is not supported in batch mode"));
1764 /* L10N: Printed after the "Fcc to an IMAP mailbox is not supported" message.
1765 To make it clearer that the message doesn't mean NeoMutt is aborting
1766 sending the mail too.
1767 %s is the full mailbox URL, including imap(s)://
1768 */
1769 mutt_error(_("Skipping Fcc to %s"), buf_string(fcc));
1770 buf_reset(fcc);
1771 return rc;
1772 }
1773
1774 if (buf_is_empty(fcc) || mutt_str_equal("/dev/null", buf_string(fcc)))
1775 return rc;
1776
1777 struct Body *tmpbody = e->body;
1778 struct Body *save_sig = NULL;
1779 struct Body *save_parts = NULL;
1780
1781 const bool c_fcc_before_send = cs_subset_bool(sub, "fcc_before_send");
1782 /* Before sending, we don't allow message manipulation because it
1783 * will break message signatures. This is especially complicated by
1784 * Protected Headers. */
1785 if (!c_fcc_before_send)
1786 {
1787 const bool c_fcc_clear = cs_subset_bool(sub, "fcc_clear");
1788 if ((WithCrypto != 0) &&
1789 (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) && c_fcc_clear)
1790 {
1791 e->body = clear_content;
1794 mutt_param_delete(&e->body->parameter, "protected-headers");
1795 }
1796
1797 const enum QuadOption c_fcc_attach = cs_subset_quad(sub, "fcc_attach");
1798
1799 /* check to see if the user wants copies of all attachments */
1800 bool save_atts = true;
1801 if (e->body->type == TYPE_MULTIPART)
1802 {
1803 /* In batch mode, save attachments if the quadoption is yes or ask-yes */
1804 if (flags & SEND_BATCH)
1805 {
1806 if ((c_fcc_attach == MUTT_NO) || (c_fcc_attach == MUTT_ASKNO))
1807 save_atts = false;
1808 }
1809 else if (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES)
1810 {
1811 save_atts = false;
1812 }
1813 }
1814 if (!save_atts)
1815 {
1816 if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) &&
1817 (mutt_str_equal(e->body->subtype, "encrypted") ||
1818 mutt_str_equal(e->body->subtype, "signed")))
1819 {
1820 if ((clear_content->type == TYPE_MULTIPART) &&
1821 (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES))
1822 {
1823 if (!(e->security & SEC_ENCRYPT) && (e->security & SEC_SIGN))
1824 {
1825 /* save initial signature and attachments */
1826 save_sig = e->body->parts->next;
1827 save_parts = clear_content->parts->next;
1828 }
1829
1830 /* this means writing only the main part */
1831 e->body = clear_content->parts;
1832
1833 if (mutt_protect(e, pgpkeylist, false) == -1)
1834 {
1835 /* we can't do much about it at this point, so
1836 * fallback to saving the whole thing to fcc */
1837 e->body = tmpbody;
1838 save_sig = NULL;
1839 goto full_fcc;
1840 }
1841
1842 save_content = e->body;
1843 }
1844 }
1845 else
1846 {
1847 if (query_quadoption(_("Save attachments in Fcc?"), sub, "fcc_attach") != MUTT_YES)
1848 e->body = e->body->parts;
1849 }
1850 }
1851 }
1852
1853full_fcc:
1854 if (e->body)
1855 {
1856 /* update received time so that when storing to a mbox-style folder
1857 * the From_ line contains the current time instead of when the
1858 * message was first postponed. */
1859 e->received = mutt_date_now();
1860 rc = mutt_write_multiple_fcc(buf_string(fcc), e, NULL, false, NULL, finalpath, sub);
1861 while (rc && !(flags & SEND_BATCH))
1862 {
1864 int choice = mw_multi_choice(
1865 /* L10N: Called when saving to $record or Fcc failed after sending.
1866 (r)etry tries the same mailbox again.
1867 alternate (m)ailbox prompts for a different mailbox to try.
1868 (s)kip aborts saving. */
1869 _("Fcc failed. (r)etry, alternate (m)ailbox, or (s)kip?"),
1870 /* L10N: These correspond to the "Fcc failed" multi-choice prompt
1871 (r)etry, alternate (m)ailbox, or (s)kip.
1872 Any similarity to famous leaders of the FSF is coincidental. */
1873 _("rms"));
1874 switch (choice)
1875 {
1876 case 2: /* alternate (m)ailbox */
1877 /* L10N: This is the prompt to enter an "alternate (m)ailbox" when the
1878 initial Fcc fails. */
1879 rc = mw_enter_fname(_("Fcc mailbox"), fcc, true, m, false, NULL, NULL,
1881 if ((rc == -1) || buf_is_empty(fcc))
1882 {
1883 rc = 0;
1884 break;
1885 }
1887
1888 case 1: /* (r)etry */
1889 rc = mutt_write_multiple_fcc(buf_string(fcc), e, NULL, false, NULL, finalpath, sub);
1890 break;
1891
1892 case -1: /* abort */
1893 case 3: /* (s)kip */
1894 rc = 0;
1895 break;
1896 }
1897 }
1898 }
1899
1900 if (!c_fcc_before_send)
1901 {
1902 e->body = tmpbody;
1903
1904 if ((WithCrypto != 0) && save_sig)
1905 {
1906 /* cleanup the second signature structures */
1907 if (save_content->parts)
1908 {
1909 mutt_body_free(&save_content->parts->next);
1910 save_content->parts = NULL;
1911 }
1912 mutt_body_free(&save_content);
1913
1914 /* restore old signature and attachments */
1915 e->body->parts->next = save_sig;
1916 e->body->parts->parts->next = save_parts;
1917 }
1918 else if ((WithCrypto != 0) && save_content)
1919 {
1920 /* destroy the new encrypted body. */
1921 mutt_body_free(&save_content);
1922 }
1923 }
1924
1925 return 0;
1926}
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition: lib.h:57
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
int mutt_protect(struct Email *e, char *keylist, bool postpone)
Encrypt and/or sign a message.
Definition: crypt.c:157
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:126
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:64
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2342
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:455
#define FALLTHROUGH
Definition: lib.h:111
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:331
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition: lib.h:87
#define SEC_SIGN
Email is signed.
Definition: lib.h:79
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
#define SEND_BATCH
Send email in batch mode (without user interaction)
Definition: send.h:47
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:1004
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:75
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
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 1938 of file send.c.

1940{
1941 char *pgpkeylist = NULL;
1942 const char *encrypt_as = NULL;
1943 struct Body *clear_content = NULL;
1944
1945 const char *const c_postponed = cs_subset_string(sub, "postponed");
1946 if (!c_postponed)
1947 {
1948 mutt_error(_("Can't postpone. $postponed is unset"));
1949 return -1;
1950 }
1951
1952 if (e_post->body->next)
1953 e_post->body = mutt_make_multipart(e_post->body);
1954
1955 mutt_encode_descriptions(e_post->body, true, sub);
1956
1957 const bool c_postpone_encrypt = cs_subset_bool(sub, "postpone_encrypt");
1958 if ((WithCrypto != 0) && c_postpone_encrypt &&
1959 (e_post->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)))
1960 {
1961 if (((WithCrypto & APPLICATION_PGP) != 0) && (e_post->security & APPLICATION_PGP))
1962 {
1963 const char *const c_pgp_default_key = cs_subset_string(sub, "pgp_default_key");
1964 encrypt_as = c_pgp_default_key;
1965 }
1966 else if (((WithCrypto & APPLICATION_SMIME) != 0) && (e_post->security & APPLICATION_SMIME))
1967 {
1968 const char *const c_smime_default_key = cs_subset_string(sub, "smime_default_key");
1969 encrypt_as = c_smime_default_key;
1970 }
1971 if (!encrypt_as)
1972 {
1973 const char *const c_postpone_encrypt_as = cs_subset_string(sub, "postpone_encrypt_as");
1974 encrypt_as = c_postpone_encrypt_as;
1975 }
1976
1977#ifdef USE_AUTOCRYPT
1978 if (e_post->security & SEC_AUTOCRYPT)
1979 {
1981 {
1982 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
1983 e_post->body = mutt_remove_multipart(e_post->body);
1984 decode_descriptions(e_post->body);
1985 mutt_error(_("Error encrypting message. Check your crypt settings."));
1986 return -1;
1987 }
1988 encrypt_as = AutocryptDefaultKey;
1989 }
1990#endif
1991
1992 if (encrypt_as)
1993 {
1994 pgpkeylist = mutt_str_dup(encrypt_as);
1995 clear_content = e_post->body;
1996 if (mutt_protect(e_post, pgpkeylist, true) == -1)
1997 {
1998 FREE(&pgpkeylist);
1999 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
2000 e_post->body = mutt_remove_multipart(e_post->body);
2001 decode_descriptions(e_post->body);
2002 mutt_error(_("Error encrypting message. Check your crypt settings."));
2003 return -1;
2004 }
2005
2006 FREE(&pgpkeylist);
2007
2008 mutt_encode_descriptions(e_post->body, false, sub);
2009 }
2010 }
2011
2012 /* make sure the message is written to the right part of a maildir
2013 * postponed folder. */
2014 e_post->read = false;
2015 e_post->old = false;
2016
2017 mutt_prepare_envelope(e_post->env, false, sub);
2018 mutt_env_to_intl(e_post->env, NULL, NULL); /* Handle bad IDNAs the next time. */
2019
2020 if (mutt_write_fcc(NONULL(c_postponed), e_post,
2021 (e_cur && (flags & SEND_REPLY)) ? e_cur->env->message_id : NULL,
2022 true, fcc, NULL, sub) < 0)
2023 {
2024 if (clear_content)
2025 {
2026 mutt_body_free(&e_post->body);
2027 e_post->body = clear_content;
2028 }
2029 mutt_env_free(&e_post->body->mime_headers); /* protected headers */
2030 mutt_param_delete(&e_post->body->parameter, "protected-headers");
2031 if (mutt_istr_equal(e_post->body->subtype, "mixed"))
2032 e_post->body = mutt_remove_multipart(e_post->body);
2033 decode_descriptions(e_post->body);
2035 return -1;
2036 }
2037
2039
2040 if (clear_content)
2041 mutt_body_free(&clear_content);
2042
2043 return 0;
2044}
char * AutocryptDefaultKey
Autocrypt default key id (used for postponing messages)
Definition: config.c:36
int mutt_autocrypt_set_sign_as_default_key(struct Email *e)
Set the Autocrypt default key for signing.
Definition: autocrypt.c:699
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:355
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition: multipart.c:126
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition: multipart.c:100
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition: postpone.c:177
void mutt_unprepare_envelope(struct Envelope *env)
Undo the encodings of mutt_prepare_envelope()
Definition: sendlib.c:818
void mutt_prepare_envelope(struct Envelope *env, bool final, struct ConfigSubset *sub)
Prepare an email header.
Definition: sendlib.c:779
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:1052
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 2052 of file send.c.

2053{
2054 return (b->type == TYPE_TEXT) && mutt_istr_equal(b->subtype, "plain");
2055}
@ 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 2063 of file send.c.

2064{
2065 const enum QuadOption c_abort_noattach = cs_subset_quad(sub, "abort_noattach");
2066
2067 if (c_abort_noattach == MUTT_NO)
2068 return false;
2069
2070 if (b->next)
2071 return false;
2072
2073 bool has_keyword = false;
2074
2075 /* search text/plain parts, whether they are main or alternative parts */
2076 if (is_text_plain(b))
2077 {
2078 has_keyword |= search_attach_keyword(b->filename, sub);
2079 }
2080 else
2081 {
2082 for (b = b->parts; b; b = b->next)
2083 {
2084 if (is_text_plain(b))
2085 {
2086 has_keyword |= search_attach_keyword(b->filename, sub);
2087 }
2088 }
2089 }
2090
2091 if (!has_keyword)
2092 return false;
2093
2094 if (c_abort_noattach == MUTT_YES)
2095 {
2096 mutt_error(_("Message contains text matching \"$abort_noattach_regex\". Not sending."));
2097 return true;
2098 }
2099
2100 return query_quadoption(_("No attachments, cancel sending?"), sub, "abort_noattach") != MUTT_NO;
2101}
static bool search_attach_keyword(char *filename, struct ConfigSubset *sub)
Search an email for 'attachment' keywords.
Definition: send.c:1698
static bool is_text_plain(const struct Body *b)
Is a Body a text/plain MIME part?
Definition: send.c:2052
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:58
+ 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 2115 of file send.c.

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

2978{
2979 struct Email *e = email_new();
2980
2981 /* envelope */
2982 e->env = mutt_env_new();
2983 mutt_parse_mailto(e->env, NULL, mailto);
2984 if (!e->env->subject)
2985 {
2986 mutt_env_set_subject(e->env, subj);
2987 }
2988 if (TAILQ_EMPTY(&e->env->to) && !mutt_addrlist_parse(&e->env->to, NULL))
2989 {
2990 mutt_warning(_("No recipient specified"));
2991 }
2992
2993 /* body */
2994 e->body = mutt_body_new();
2995 char ctype[] = "text/plain";
2996 mutt_parse_content_type(ctype, e->body);
2997
2998 struct Buffer *tempfile = buf_pool_get();
2999 buf_mktemp(tempfile);
3000 if (body)
3001 {
3002 FILE *fp = mutt_file_fopen(buf_string(tempfile), "w+");
3003 if (!fp)
3004 {
3005 email_free(&e);
3006 buf_pool_release(&tempfile);
3007 return false;
3008 }
3009 fprintf(fp, "%s\n", body);
3010 mutt_file_fclose(&fp);
3011 }
3012 e->body->filename = buf_strdup(tempfile);
3013 e->body->unlink = true;
3014 buf_pool_release(&tempfile);
3015
3016 const int rc = mutt_send_message(SEND_DRAFT_FILE, e, NULL, m, ea, NeoMutt->sub);
3017 return rc >= 0;
3018}
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
+ 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 3027 of file send.c.

3028{
3029 if (!e || !e->env)
3030 {
3031 return false;
3032 }
3033
3034 const char *mailto = e->env->list_subscribe;
3035 if (!mailto)
3036 {
3037 mutt_warning(_("No List-Subscribe header found"));
3038 return false;
3039 }
3040
3041 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
3042 ARRAY_ADD(&ea, e);
3043 bool rc = send_simple_email(m, &ea, mailto, "Subscribe", "subscribe");
3044 ARRAY_FREE(&ea);
3045
3046 return rc;
3047}
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:2976
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 3056 of file send.c.

3057{
3058 if (!e || !e->env)
3059 {
3060 return false;
3061 }
3062
3063 const char *mailto = e->env->list_unsubscribe;
3064 if (!mailto)
3065 {
3066 mutt_warning(_("No List-Unsubscribe header found"));
3067 return false;
3068 }
3069
3070 struct EmailArray ea = ARRAY_HEAD_INITIALIZER;
3071 ARRAY_ADD(&ea, e);
3072 bool rc = send_simple_email(m, &ea, mailto, "Unsubscribe", "unsubscribe");
3073 ARRAY_FREE(&ea);
3074
3075 return rc;
3076}
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: