Thanks to visit codestin.com
Credit goes to code.neomutt.org

NeoMutt  2025-12-11-189-gceedb6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
editmsg.c
Go to the documentation of this file.
1
25
31
32#include "config.h"
33#include <errno.h>
34#include <stdbool.h>
35#include <stdio.h>
36#include <string.h>
37#include <sys/stat.h>
38#include <time.h>
39#include <unistd.h>
40#include "mutt/lib.h"
41#include "config/lib.h"
42#include "email/lib.h"
43#include "core/lib.h"
44#include "gui/lib.h"
45#include "mutt.h"
46#include "mx.h"
47
57static int ev_message(enum EvMessage action, struct Mailbox *m, struct Email *e)
58{
59 char buf[256] = { 0 };
60 int rc;
61 FILE *fp = NULL;
62 struct stat st = { 0 };
63 bool old_append = m->append;
64
65 struct Buffer *fname = buf_pool_get();
66 buf_mktemp(fname);
67
68 // Temporarily force $mbox_type to be MUTT_MBOX
69 const unsigned char c_mbox_type = cs_subset_enum(NeoMutt->sub, "mbox_type");
70 cs_subset_str_native_set(NeoMutt->sub, "mbox_type", MUTT_MBOX, NULL);
71
72 struct Mailbox *m_fname = mx_path_resolve(buf_string(fname));
73 if (!mx_mbox_open(m_fname, MUTT_APPEND))
74 {
75 mutt_error(_("could not create temporary folder: %s"), strerror(errno));
76 buf_pool_release(&fname);
77 mailbox_free(&m_fname);
78 return -1;
79 }
80
81 cs_subset_str_native_set(NeoMutt->sub, "mbox_type", c_mbox_type, NULL);
82
83 const CopyHeaderFlags chflags = CH_NOLEN |
84 (((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) ?
87 rc = mutt_append_message(m_fname, m, e, NULL, MUTT_CM_NO_FLAGS, chflags);
88 int oerrno = errno;
89
90 mx_mbox_close(m_fname);
91 mailbox_free(&m_fname);
92
93 if (rc == -1)
94 {
95 mutt_error(_("could not write temporary mail folder: %s"), strerror(oerrno));
96 goto bail;
97 }
98
99 rc = stat(buf_string(fname), &st);
100 if (rc == -1)
101 {
102 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
103 goto bail;
104 }
105
106 /* The file the user is going to edit is not a real mbox, so we need to
107 * truncate the last newline in the temp file, which is logically part of
108 * the message separator, and not the body of the message. If we fail to
109 * remove it, the message will grow by one line each time the user edits
110 * the message. */
111 if ((st.st_size != 0) && (truncate(buf_string(fname), st.st_size - 1) == -1))
112 {
113 rc = -1;
114 mutt_error(_("could not truncate temporary mail folder: %s"), strerror(errno));
115 goto bail;
116 }
117
118 if (action == EVM_VIEW)
119 {
120 /* remove write permissions */
121 rc = mutt_file_chmod_rm_stat(buf_string(fname), S_IWUSR | S_IWGRP | S_IWOTH, &st);
122 if (rc == -1)
123 {
124 mutt_debug(LL_DEBUG1, "Could not remove write permissions of %s: %s\n",
125 buf_string(fname), strerror(errno));
126 /* Do not bail out here as we are checking afterwards if we should adopt
127 * changes of the temporary file. */
128 }
129 }
130
131 /* re-stat after the truncate, to avoid false "modified" bugs */
132 rc = stat(buf_string(fname), &st);
133 if (rc == -1)
134 {
135 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
136 goto bail;
137 }
138
139 /* Do not reuse the stat st here as it is outdated. */
140 time_t mtime = mutt_file_decrease_mtime(buf_string(fname), NULL);
141 if (mtime == (time_t) -1)
142 {
143 rc = -1;
144 mutt_perror("%s", buf_string(fname));
145 goto bail;
146 }
147
148 const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
149 mutt_edit_file(NONULL(c_editor), buf_string(fname));
150
151 rc = stat(buf_string(fname), &st);
152 if (rc == -1)
153 {
154 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
155 goto bail;
156 }
157
158 if (st.st_size == 0)
159 {
160 mutt_message(_("Message file is empty"));
161 rc = 1;
162 goto bail;
163 }
164
165 if ((action == EVM_EDIT) && (st.st_mtime == mtime))
166 {
167 mutt_message(_("Message not modified"));
168 rc = 1;
169 goto bail;
170 }
171
172 if ((action == EVM_VIEW) && (st.st_mtime != mtime))
173 {
174 mutt_message(_("Message of read-only mailbox modified! Ignoring changes."));
175 rc = 1;
176 goto bail;
177 }
178
179 if (action == EVM_VIEW)
180 {
181 /* stop processing here and skip right to the end */
182 rc = 1;
183 goto bail;
184 }
185
186 fp = mutt_file_fopen(buf_string(fname), "r");
187 if (!fp)
188 {
189 rc = -1;
190 mutt_error(_("Can't open message file: %s"), strerror(errno));
191 goto bail;
192 }
193
195 {
196 rc = -1;
197 /* L10N: %s is from strerror(errno) */
198 mutt_error(_("Can't append to folder: %s"), strerror(errno));
199 goto bail;
200 }
202 CopyHeaderFlags cf = (((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) ? CH_NO_FLAGS : CH_NOSTATUS);
203
204 if (fgets(buf, sizeof(buf), fp) && is_from(buf, NULL, 0, NULL))
205 {
206 if ((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF))
207 cf = CH_FROM | CH_FORCE_FROM;
208 }
209 else
210 {
211 of = MUTT_ADD_FROM;
212 }
213
214 /* XXX - we have to play games with the message flags to avoid
215 * problematic behavior with maildir folders. */
216
217 bool o_read = e->read;
218 bool o_old = e->old;
219 e->read = false;
220 e->old = false;
221 struct Message *msg = mx_msg_open_new(m, e, of);
222 e->read = o_read;
223 e->old = o_old;
224
225 if (!msg)
226 {
227 rc = -1;
228 mutt_error(_("Can't append to folder: %s"), strerror(errno));
229 mx_mbox_close(m);
230 goto bail;
231 }
232
233 rc = mutt_copy_hdr(fp, msg->fp, 0, st.st_size, CH_NOLEN | cf, NULL, 0);
234 if (rc == 0)
235 {
236 fputc('\n', msg->fp);
238 }
239
240 rc = mx_msg_commit(m, msg);
241 mx_msg_close(m, &msg);
242
243 mx_mbox_close(m);
244 m->last_checked = 0; // force a check on the next mx_mbox_check() call
245
246bail:
248
249 if (rc >= 0)
250 unlink(buf_string(fname));
251
252 if (rc == 0)
253 {
254 mutt_set_flag(m, e, MUTT_DELETE, true, true);
255 mutt_set_flag(m, e, MUTT_PURGE, true, true);
256 mutt_set_flag(m, e, MUTT_READ, true, true);
257
258 const bool c_delete_untag = cs_subset_bool(NeoMutt->sub, "delete_untag");
259 if (c_delete_untag)
260 mutt_set_flag(m, e, MUTT_TAG, false, true);
261 }
262 else if (rc == -1)
263 {
264 mutt_message(_("Error. Preserving temporary file: %s"), buf_string(fname));
265 }
266
267 m->append = old_append;
268
269 buf_pool_release(&fname);
270 return rc;
271}
272
282int mutt_ev_message(struct Mailbox *m, struct EmailArray *ea, enum EvMessage action)
283{
284 struct Email **ep = NULL;
285 ARRAY_FOREACH(ep, ea)
286 {
287 struct Email *e = *ep;
288 if (ev_message(action, m, e) == -1)
289 return -1;
290 }
291
292 return 0;
293}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
unsigned char cs_subset_enum(const struct ConfigSubset *sub, const char *name)
Get a enumeration config item by name.
Definition helpers.c:71
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
Convenience wrapper for the config headers.
int mutt_append_message(struct Mailbox *m_dst, struct Mailbox *m_src, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Append a message.
Definition copy_email.c:991
int mutt_copy_hdr(FILE *fp_in, FILE *fp_out, LOFF_T off_start, LOFF_T off_end, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy header from one file to another.
Definition copy_email.c:112
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition copy_email.h:60
#define CH_FROM
Retain the "From " message separator?
Definition copy_email.h:58
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition copy_email.h:52
#define CH_FORCE_FROM
Give CH_FROM precedence over CH_WEED?
Definition copy_email.h:68
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition copy_email.h:37
#define CH_NO_FLAGS
No flags are set.
Definition copy_email.h:53
#define CH_NOLEN
Don't write Content-Length: and Lines:
Definition copy_email.h:66
Convenience wrapper for the core headers.
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition mailbox.c:90
@ MUTT_MMDF
'mmdf' Mailbox type
Definition mailbox.h:46
@ MUTT_MBOX
'mbox' Mailbox type
Definition mailbox.h:45
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition curs_lib.c:116
static int ev_message(enum EvMessage action, struct Mailbox *m, struct Email *e)
Edit an email or view it in an external editor.
Definition editmsg.c:57
int mutt_ev_message(struct Mailbox *m, struct EmailArray *ea, enum EvMessage action)
Edit or view a message.
Definition editmsg.c:282
Structs that make up an email.
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition file.c:226
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file's modification time by 1 second.
Definition file.c:906
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition file.c:1067
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition flags.c:54
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition from.c:49
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
Convenience wrapper for the gui headers.
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
Many unsorted constants and some structs.
EvMessage
Edit or View a message.
Definition mutt.h:75
@ EVM_VIEW
View the message.
Definition mutt.h:76
@ EVM_EDIT
Edit the message.
Definition mutt.h:77
@ MUTT_READ
Messages that have been read.
Definition mutt.h:92
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition mutt.h:96
@ MUTT_TAG
Tagged messages.
Definition mutt.h:99
@ MUTT_DELETE
Messages to be deleted.
Definition mutt.h:94
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition mx.c:1182
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition mx.c:285
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition mx.c:1041
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition mx.c:1161
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1647
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition mx.c:595
API for mailboxes.
uint8_t MsgOpenFlags
Flags for mx_msg_open_new(), e.g. MUTT_ADD_FROM.
Definition mx.h:37
#define MUTT_ADD_FROM
add a From_ line
Definition mx.h:39
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition mx.h:38
#define MUTT_APPEND
Open mailbox for appending messages.
Definition mxapi.h:42
#define MUTT_QUIET
Do not print any messages.
Definition mxapi.h:44
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:96
#define NONULL(x)
Definition string2.h:44
String manipulation buffer.
Definition buffer.h:36
The envelope/body of an email.
Definition email.h:39
bool read
Email is read.
Definition email.h:50
bool old
Email is seen, but unread.
Definition email.h:49
A mailbox.
Definition mailbox.h:79
bool append
Mailbox is opened in append mode.
Definition mailbox.h:109
time_t last_checked
Last time we checked this mailbox for new mail.
Definition mailbox.h:105
enum MailboxType type
Mailbox type.
Definition mailbox.h:102
A local copy of an email.
Definition message.h:34
FILE * fp
pointer to the message data
Definition message.h:35
Container for Accounts, Notifications.
Definition neomutt.h:128
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:134
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition subset.c:303
#define buf_mktemp(buf)
Definition tmp.h:33