NeoMutt  2024-04-25-76-g20fe7b
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
editmsg.c File Reference

Prepare an email to be edited. More...

#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "copy.h"
#include "mx.h"
#include "protos.h"
+ Include dependency graph for editmsg.c:

Go to the source code of this file.

Functions

static int ev_message (enum EvMessage action, struct Mailbox *m, struct Email *e)
 Edit an email or view it in an external editor.
 
int mutt_ev_message (struct Mailbox *m, struct EmailArray *ea, enum EvMessage action)
 Edit or view a message.
 

Detailed Description

Prepare an email to be edited.

Authors
  • Thomas Roessler
  • Reis Radomil
  • Richard Russon
  • Pietro Cerutti

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 editmsg.c.

Function Documentation

◆ ev_message()

static int ev_message ( enum EvMessage  action,
struct Mailbox m,
struct Email e 
)
static

Edit an email or view it in an external editor.

Parameters
actionAction to perform, e.g. EVM_EDIT
mMailbox
eEmail
Return values
1Message not modified
0Message edited successfully
-1Error

Definition at line 59 of file editmsg.c.

60{
61 char buf[256] = { 0 };
62 int rc;
63 FILE *fp = NULL;
64 struct stat st = { 0 };
65 bool old_append = m->append;
66
67 struct Buffer *fname = buf_pool_get();
68 buf_mktemp(fname);
69
70 // Temporarily force $mbox_type to be MUTT_MBOX
71 const unsigned char c_mbox_type = cs_subset_enum(NeoMutt->sub, "mbox_type");
72 cs_subset_str_native_set(NeoMutt->sub, "mbox_type", MUTT_MBOX, NULL);
73
74 struct Mailbox *m_fname = mx_path_resolve(buf_string(fname));
75 if (!mx_mbox_open(m_fname, MUTT_NEWFOLDER))
76 {
77 mutt_error(_("could not create temporary folder: %s"), strerror(errno));
78 buf_pool_release(&fname);
79 mailbox_free(&m_fname);
80 return -1;
81 }
82
83 cs_subset_str_native_set(NeoMutt->sub, "mbox_type", c_mbox_type, NULL);
84
85 const CopyHeaderFlags chflags = CH_NOLEN |
86 (((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) ?
89 rc = mutt_append_message(m_fname, m, e, NULL, MUTT_CM_NO_FLAGS, chflags);
90 int oerrno = errno;
91
92 mx_mbox_close(m_fname);
93 mailbox_free(&m_fname);
94
95 if (rc == -1)
96 {
97 mutt_error(_("could not write temporary mail folder: %s"), strerror(oerrno));
98 goto bail;
99 }
100
101 rc = stat(buf_string(fname), &st);
102 if (rc == -1)
103 {
104 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
105 goto bail;
106 }
107
108 /* The file the user is going to edit is not a real mbox, so we need to
109 * truncate the last newline in the temp file, which is logically part of
110 * the message separator, and not the body of the message. If we fail to
111 * remove it, the message will grow by one line each time the user edits
112 * the message. */
113 if ((st.st_size != 0) && (truncate(buf_string(fname), st.st_size - 1) == -1))
114 {
115 rc = -1;
116 mutt_error(_("could not truncate temporary mail folder: %s"), strerror(errno));
117 goto bail;
118 }
119
120 if (action == EVM_VIEW)
121 {
122 /* remove write permissions */
123 rc = mutt_file_chmod_rm_stat(buf_string(fname), S_IWUSR | S_IWGRP | S_IWOTH, &st);
124 if (rc == -1)
125 {
126 mutt_debug(LL_DEBUG1, "Could not remove write permissions of %s: %s",
127 buf_string(fname), strerror(errno));
128 /* Do not bail out here as we are checking afterwards if we should adopt
129 * changes of the temporary file. */
130 }
131 }
132
133 /* re-stat after the truncate, to avoid false "modified" bugs */
134 rc = stat(buf_string(fname), &st);
135 if (rc == -1)
136 {
137 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
138 goto bail;
139 }
140
141 /* Do not reuse the stat st here as it is outdated. */
142 time_t mtime = mutt_file_decrease_mtime(buf_string(fname), NULL);
143 if (mtime == (time_t) -1)
144 {
145 rc = -1;
146 mutt_perror("%s", buf_string(fname));
147 goto bail;
148 }
149
150 const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
151 mutt_edit_file(NONULL(c_editor), buf_string(fname));
152
153 rc = stat(buf_string(fname), &st);
154 if (rc == -1)
155 {
156 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
157 goto bail;
158 }
159
160 if (st.st_size == 0)
161 {
162 mutt_message(_("Message file is empty"));
163 rc = 1;
164 goto bail;
165 }
166
167 if ((action == EVM_EDIT) && (st.st_mtime == mtime))
168 {
169 mutt_message(_("Message not modified"));
170 rc = 1;
171 goto bail;
172 }
173
174 if ((action == EVM_VIEW) && (st.st_mtime != mtime))
175 {
176 mutt_message(_("Message of read-only mailbox modified! Ignoring changes."));
177 rc = 1;
178 goto bail;
179 }
180
181 if (action == EVM_VIEW)
182 {
183 /* stop processing here and skip right to the end */
184 rc = 1;
185 goto bail;
186 }
187
188 fp = mutt_file_fopen(buf_string(fname), "r");
189 if (!fp)
190 {
191 rc = -1;
192 mutt_error(_("Can't open message file: %s"), strerror(errno));
193 goto bail;
194 }
195
197 {
198 rc = -1;
199 /* L10N: %s is from strerror(errno) */
200 mutt_error(_("Can't append to folder: %s"), strerror(errno));
201 goto bail;
202 }
204 CopyHeaderFlags cf = (((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) ? CH_NO_FLAGS : CH_NOSTATUS);
205
206 if (fgets(buf, sizeof(buf), fp) && is_from(buf, NULL, 0, NULL))
207 {
208 if ((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF))
209 cf = CH_FROM | CH_FORCE_FROM;
210 }
211 else
212 {
213 of = MUTT_ADD_FROM;
214 }
215
216 /* XXX - we have to play games with the message flags to avoid
217 * problematic behavior with maildir folders. */
218
219 bool o_read = e->read;
220 bool o_old = e->old;
221 e->read = false;
222 e->old = false;
223 struct Message *msg = mx_msg_open_new(m, e, of);
224 e->read = o_read;
225 e->old = o_old;
226
227 if (!msg)
228 {
229 rc = -1;
230 mutt_error(_("Can't append to folder: %s"), strerror(errno));
231 mx_mbox_close(m);
232 goto bail;
233 }
234
235 rc = mutt_copy_hdr(fp, msg->fp, 0, st.st_size, CH_NOLEN | cf, NULL, 0);
236 if (rc == 0)
237 {
238 fputc('\n', msg->fp);
240 }
241
242 rc = mx_msg_commit(m, msg);
243 mx_msg_close(m, &msg);
244
245 mx_mbox_close(m);
246 m->last_checked = 0; // force a check on the next mx_mbox_check() call
247
248bail:
250
251 if (rc >= 0)
252 unlink(buf_string(fname));
253
254 if (rc == 0)
255 {
256 mutt_set_flag(m, e, MUTT_DELETE, true, true);
257 mutt_set_flag(m, e, MUTT_PURGE, true, true);
258 mutt_set_flag(m, e, MUTT_READ, true, true);
259
260 const bool c_delete_untag = cs_subset_bool(NeoMutt->sub, "delete_untag");
261 if (c_delete_untag)
262 mutt_set_flag(m, e, MUTT_TAG, false, true);
263 }
264 else if (rc == -1)
265 {
266 mutt_message(_("Error. Preserving temporary file: %s"), buf_string(fname));
267 }
268
269 m->append = old_append;
270
271 buf_pool_release(&fname);
272 return rc;
273}
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
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.c:982
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.c:108
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition: copy.h:60
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:58
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:52
#define CH_FORCE_FROM
Give CH_FROM precedence over CH_WEED?
Definition: copy.h:68
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:37
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:53
#define CH_NOLEN
Don't write Content-Length: and Lines:
Definition: copy.h:66
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:89
@ 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
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:287
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file's modification time by 1 second.
Definition: file.c:1028
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition: file.c:1173
#define mutt_file_fclose(FP)
Definition: file.h:149
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:148
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 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:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define _(a)
Definition: message.h:28
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:73
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:77
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:80
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:75
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition: mx.c:1180
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:288
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1040
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1159
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1636
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:598
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_NEWFOLDER
Create a new folder - same as MUTT_APPEND, but uses mutt_file_fopen() with mode "w" for mbox-style fo...
Definition: mxapi.h:45
#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:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
@ EVM_VIEW
View the message.
Definition: protos.h:53
@ EVM_EDIT
Edit the message.
Definition: protos.h:54
#define NONULL(x)
Definition: string2.h:37
String manipulation buffer.
Definition: buffer.h:36
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:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
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_ev_message()

int mutt_ev_message ( struct Mailbox m,
struct EmailArray *  ea,
enum EvMessage  action 
)

Edit or view a message.

Parameters
mMailbox
eaArray of Emails
actionAction to perform, e.g. EVM_EDIT
Return values
1Message not modified
0Message edited successfully
-1Error

Definition at line 284 of file editmsg.c.

285{
286 struct Email **ep = NULL;
287 ARRAY_FOREACH(ep, ea)
288 {
289 struct Email *e = *ep;
290 if (ev_message(action, m, e) == -1)
291 return -1;
292 }
293
294 return 0;
295}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:212
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:59
The envelope/body of an email.
Definition: email.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function: