NeoMutt  2021-02-05-89-gabe350
Teaching an old dog new tricks
DOXYGEN
editmsg.c File Reference
#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 "email/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "context.h"
#include "copy.h"
#include "mutt_globals.h"
#include "muttlib.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. More...
 
int mutt_ev_message (struct Mailbox *m, struct EmailList *el, enum EvMessage action)
 Edit or view a message. More...
 

Detailed Description

Prepare an email to be edited

Authors
  • Thomas Roessler

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 58 of file editmsg.c.

59 {
60  char buf[256];
61  int rc;
62  FILE *fp = NULL;
63  struct stat sb;
64  bool old_append = m->append;
65 
66  struct Buffer *fname = mutt_buffer_pool_get();
67  mutt_buffer_mktemp(fname);
68 
69  enum MailboxType otype = C_MboxType;
71 
72  struct Mailbox *m_fname = mx_path_resolve(mutt_buffer_string(fname));
73  struct Context *ctx_tmp = mx_mbox_open(m_fname, MUTT_NEWFOLDER);
74 
75  C_MboxType = otype;
76 
77  if (!ctx_tmp)
78  {
79  mutt_error(_("could not create temporary folder: %s"), strerror(errno));
81  mailbox_free(&m_fname);
82  return -1;
83  }
84 
85  const CopyHeaderFlags chflags =
86  CH_NOLEN | (((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) ? CH_NO_FLAGS : CH_NOSTATUS);
87  rc = mutt_append_message(ctx_tmp->mailbox, m, e, MUTT_CM_NO_FLAGS, chflags);
88  int oerrno = errno;
89 
90  mx_mbox_close(&ctx_tmp);
91 
92  if (rc == -1)
93  {
94  mutt_error(_("could not write temporary mail folder: %s"), strerror(oerrno));
95  goto bail;
96  }
97 
98  rc = stat(mutt_buffer_string(fname), &sb);
99  if (rc == -1)
100  {
101  mutt_error(_("Can't stat %s: %s"), mutt_buffer_string(fname), strerror(errno));
102  goto bail;
103  }
104 
105  /* The file the user is going to edit is not a real mbox, so we need to
106  * truncate the last newline in the temp file, which is logically part of
107  * the message separator, and not the body of the message. If we fail to
108  * remove it, the message will grow by one line each time the user edits
109  * the message. */
110  if ((sb.st_size != 0) && (truncate(mutt_buffer_string(fname), sb.st_size - 1) == -1))
111  {
112  rc = -1;
113  mutt_error(_("could not truncate temporary mail folder: %s"), strerror(errno));
114  goto bail;
115  }
116 
117  if (action == EVM_VIEW)
118  {
119  /* remove write permissions */
121  S_IWUSR | S_IWGRP | S_IWOTH, &sb);
122  if (rc == -1)
123  {
124  mutt_debug(LL_DEBUG1, "Could not remove write permissions of %s: %s",
125  mutt_buffer_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(mutt_buffer_string(fname), &sb);
133  if (rc == -1)
134  {
135  mutt_error(_("Can't stat %s: %s"), mutt_buffer_string(fname), strerror(errno));
136  goto bail;
137  }
138 
139  /* Do not reuse the stat sb here as it is outdated. */
140  time_t mtime = mutt_file_decrease_mtime(mutt_buffer_string(fname), NULL);
141  if (mtime == (time_t) -1)
142  {
143  rc = -1;
145  goto bail;
146  }
147 
149 
150  rc = stat(mutt_buffer_string(fname), &sb);
151  if (rc == -1)
152  {
153  mutt_error(_("Can't stat %s: %s"), mutt_buffer_string(fname), strerror(errno));
154  goto bail;
155  }
156 
157  if (sb.st_size == 0)
158  {
159  mutt_message(_("Message file is empty"));
160  rc = 1;
161  goto bail;
162  }
163 
164  if ((action == EVM_EDIT) && (sb.st_mtime == mtime))
165  {
166  mutt_message(_("Message not modified"));
167  rc = 1;
168  goto bail;
169  }
170 
171  if ((action == EVM_VIEW) && (sb.st_mtime != mtime))
172  {
173  mutt_message(_("Message of read-only mailbox modified! Ignoring changes."));
174  rc = 1;
175  goto bail;
176  }
177 
178  if (action == EVM_VIEW)
179  {
180  /* stop processing here and skip right to the end */
181  rc = 1;
182  goto bail;
183  }
184 
185  fp = fopen(mutt_buffer_string(fname), "r");
186  if (!fp)
187  {
188  rc = -1;
189  mutt_error(_("Can't open message file: %s"), strerror(errno));
190  goto bail;
191  }
192 
193  struct Context *ctx_app = mx_mbox_open(m, MUTT_APPEND | MUTT_QUIET);
194  if (!ctx_app)
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  }
201 
203  CopyHeaderFlags cf =
204  (((ctx_app->mailbox->type == MUTT_MBOX) || (ctx_app->mailbox->type == MUTT_MMDF)) ?
205  CH_NO_FLAGS :
206  CH_NOSTATUS);
207 
208  if (fgets(buf, sizeof(buf), fp) && is_from(buf, NULL, 0, NULL))
209  {
210  if ((ctx_app->mailbox->type == MUTT_MBOX) || (ctx_app->mailbox->type == MUTT_MMDF))
211  cf = CH_FROM | CH_FORCE_FROM;
212  }
213  else
214  of = MUTT_ADD_FROM;
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(ctx_app->mailbox, 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(&ctx_app);
232  goto bail;
233  }
234 
235  rc = mutt_copy_hdr(fp, msg->fp, 0, sb.st_size, CH_NOLEN | cf, NULL, 0);
236  if (rc == 0)
237  {
238  fputc('\n', msg->fp);
239  mutt_file_copy_stream(fp, msg->fp);
240  }
241 
242  rc = mx_msg_commit(ctx_app->mailbox, msg);
243  mx_msg_close(ctx_app->mailbox, &msg);
244 
245  mx_mbox_close(&ctx_app);
246 
247 bail:
249 
250  if (rc >= 0)
251  unlink(mutt_buffer_string(fname));
252 
253  if (rc == 0)
254  {
255  mutt_set_flag(m, e, MUTT_DELETE, true);
256  mutt_set_flag(m, e, MUTT_PURGE, true);
257  mutt_set_flag(m, e, MUTT_READ, true);
258 
259  if (C_DeleteUntag)
260  mutt_set_flag(m, e, MUTT_TAG, false);
261  }
262  else if (rc == -1)
263  mutt_message(_("Error. Preserving temporary file: %s"), mutt_buffer_string(fname));
264 
265  m->append = old_append;
266 
267  mutt_buffer_pool_release(&fname);
268  return rc;
269 }
+ 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 EmailList *  el,
enum EvMessage  action 
)

Edit or view a message.

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

Definition at line 280 of file editmsg.c.

281 {
282  struct EmailNode *en = NULL;
283  STAILQ_FOREACH(en, el, entries)
284  {
285  if (ev_message(action, m, en->email) == -1)
286  return -1;
287  }
288 
289  return 0;
290 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:
CH_FROM
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:55
CopyHeaderFlags
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:49
MUTT_MSG_NO_FLAGS
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:64
MUTT_MMDF
@ MUTT_MMDF
'mmdf' Mailbox type
Definition: mailbox.h:49
ev_message
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:58
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
Mailbox
A mailbox.
Definition: mailbox.h:81
mutt_file_decrease_mtime
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file's modification time by 1 second.
Definition: file.c:963
Mailbox::append
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:113
Buffer
String manipulation buffer.
Definition: buffer.h:33
C_DeleteUntag
WHERE bool C_DeleteUntag
Config: Untag messages when they are marked for deletion.
Definition: mutt_globals.h:145
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
CH_NOLEN
#define CH_NOLEN
Don't write Content-Length: and Lines:
Definition: copy.h:63
mutt_buffer_mktemp
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
EVM_VIEW
@ EVM_VIEW
View the message.
Definition: protos.h:56
EmailNode::email
struct Email * email
Email in the list.
Definition: email.h:127
MUTT_NEWFOLDER
#define MUTT_NEWFOLDER
Create a new folder - same as MUTT_APPEND,.
Definition: mx.h:56
Context
The "current" mailbox.
Definition: context.h:38
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
EmailNode
List of Emails.
Definition: email.h:125
mutt_perror
#define mutt_perror(...)
Definition: logging.h:85
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
mailbox_free
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:71
mx_path_resolve
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1681
STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
MUTT_READ
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:96
C_Editor
WHERE char * C_Editor
Config: External command to use as an email editor.
Definition: mutt_globals.h:90
Email::old
bool old
Email is seen, but unread.
Definition: email.h:50
MUTT_PURGE
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:100
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
mutt_edit_file
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:356
Mailbox::type
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
mx_msg_commit
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1183
mutt_file_copy_stream
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
mx_mbox_close
enum MxStatus mx_mbox_close(struct Context **ptr)
Save changes and close mailbox.
Definition: mx.c:632
MUTT_CM_NO_FLAGS
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:34
is_from
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:48
CH_NO_FLAGS
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:50
CH_NOSTATUS
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition: copy.h:57
mutt_copy_hdr
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:103
Context::mailbox
struct Mailbox * mailbox
Definition: context.h:50
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
MUTT_ADD_FROM
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:65
Message::fp
FILE * fp
pointer to the message data
Definition: mx.h:96
MUTT_QUIET
#define MUTT_QUIET
Do not print any messages.
Definition: mx.h:55
mutt_append_message
int mutt_append_message(struct Mailbox *dest, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Append a message.
Definition: copy.c:905
mutt_buffer_string
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
mutt_file_chmod_rm_stat
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition: file.c:1146
CH_FORCE_FROM
#define CH_FORCE_FROM
Give CH_FROM precedence over CH_WEED?
Definition: copy.h:65
MUTT_DELETE
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:98
MsgOpenFlags
uint8_t MsgOpenFlags
Flags for mx_msg_open_new(), e.g. MUTT_ADD_FROM.
Definition: mx.h:63
MUTT_TAG
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:103
EVM_EDIT
@ EVM_EDIT
Edit the message.
Definition: protos.h:57
mx_mbox_open
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:309
MailboxType
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
MUTT_APPEND
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mx.h:53
mx_msg_close
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1204
mutt_message
#define mutt_message(...)
Definition: logging.h:83
mx_msg_open_new
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1072
mutt_set_flag
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:67
Email::read
bool read
Email is read.
Definition: email.h:51
MUTT_MBOX
@ MUTT_MBOX
'mbox' Mailbox type
Definition: mailbox.h:48
C_MboxType
unsigned char C_MboxType
Config: Default type for creating new mailboxes.
Definition: mx.c:90
Message
A local copy of an email.
Definition: mx.h:94
mutt_error
#define mutt_error(...)
Definition: logging.h:84