NeoMutt  2020-03-20-65-g141838
Teaching an old dog new tricks
DOXYGEN
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 "email/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "context.h"
#include "copy.h"
#include "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_b2s(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_b2s(fname), &sb);
99  if (rc == -1)
100  {
101  mutt_error(_("Can't stat %s: %s"), mutt_b2s(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_b2s(fname), sb.st_size - 1) == -1))
111  {
112  mutt_error(_("could not truncate temporary mail folder: %s"), strerror(errno));
113  goto bail;
114  }
115 
116  if (action == EVM_VIEW)
117  {
118  /* remove write permissions */
119  rc = mutt_file_chmod_rm_stat(mutt_b2s(fname), S_IWUSR | S_IWGRP | S_IWOTH, &sb);
120  if (rc == -1)
121  {
122  mutt_debug(LL_DEBUG1, "Could not remove write permissions of %s: %s",
123  mutt_b2s(fname), strerror(errno));
124  /* Do not bail out here as we are checking afterwards if we should adopt
125  * changes of the temporary file. */
126  }
127  }
128 
129  /* Do not reuse the stat sb here as it is outdated. */
130  time_t mtime = mutt_file_decrease_mtime(mutt_b2s(fname), NULL);
131 
133 
134  rc = stat(mutt_b2s(fname), &sb);
135  if (rc == -1)
136  {
137  mutt_error(_("Can't stat %s: %s"), mutt_b2s(fname), strerror(errno));
138  goto bail;
139  }
140 
141  if (sb.st_size == 0)
142  {
143  mutt_message(_("Message file is empty"));
144  rc = 1;
145  goto bail;
146  }
147 
148  if ((action == EVM_EDIT) && (sb.st_mtime == mtime))
149  {
150  mutt_message(_("Message not modified"));
151  rc = 1;
152  goto bail;
153  }
154 
155  if ((action == EVM_VIEW) && (sb.st_mtime != mtime))
156  {
157  mutt_message(_("Message of read-only mailbox modified! Ignoring changes."));
158  rc = 1;
159  goto bail;
160  }
161 
162  if (action == EVM_VIEW)
163  {
164  /* stop processing here and skip right to the end */
165  rc = 1;
166  goto bail;
167  }
168 
169  fp = fopen(mutt_b2s(fname), "r");
170  if (!fp)
171  {
172  rc = -1;
173  mutt_error(_("Can't open message file: %s"), strerror(errno));
174  goto bail;
175  }
176 
177  struct Context *ctx_app = mx_mbox_open(m, MUTT_APPEND | MUTT_QUIET);
178  if (!ctx_app)
179  {
180  rc = -1;
181  /* L10N: %s is from strerror(errno) */
182  mutt_error(_("Can't append to folder: %s"), strerror(errno));
183  goto bail;
184  }
185 
187  CopyHeaderFlags cf =
188  (((ctx_app->mailbox->type == MUTT_MBOX) || (ctx_app->mailbox->type == MUTT_MMDF)) ?
189  CH_NO_FLAGS :
190  CH_NOSTATUS);
191 
192  if (fgets(buf, sizeof(buf), fp) && is_from(buf, NULL, 0, NULL))
193  {
194  if ((ctx_app->mailbox->type == MUTT_MBOX) || (ctx_app->mailbox->type == MUTT_MMDF))
195  cf = CH_FROM | CH_FORCE_FROM;
196  }
197  else
198  of = MUTT_ADD_FROM;
199 
200  /* XXX - we have to play games with the message flags to avoid
201  * problematic behavior with maildir folders. */
202 
203  bool o_read = e->read;
204  bool o_old = e->old;
205  e->read = false;
206  e->old = false;
207  struct Message *msg = mx_msg_open_new(ctx_app->mailbox, e, of);
208  e->read = o_read;
209  e->old = o_old;
210 
211  if (!msg)
212  {
213  mutt_error(_("Can't append to folder: %s"), strerror(errno));
214  mx_mbox_close(&ctx_app);
215  goto bail;
216  }
217 
218  rc = mutt_copy_hdr(fp, msg->fp, 0, sb.st_size, CH_NOLEN | cf, NULL, 0);
219  if (rc == 0)
220  {
221  fputc('\n', msg->fp);
222  mutt_file_copy_stream(fp, msg->fp);
223  }
224 
225  rc = mx_msg_commit(ctx_app->mailbox, msg);
226  mx_msg_close(ctx_app->mailbox, &msg);
227 
228  mx_mbox_close(&ctx_app);
229 
230 bail:
231  mutt_file_fclose(&fp);
232 
233  if (rc >= 0)
234  unlink(mutt_b2s(fname));
235 
236  if (rc == 0)
237  {
238  mutt_set_flag(m, e, MUTT_DELETE, true);
239  mutt_set_flag(m, e, MUTT_PURGE, true);
240  mutt_set_flag(m, e, MUTT_READ, true);
241 
242  if (C_DeleteUntag)
243  mutt_set_flag(m, e, MUTT_TAG, false);
244  }
245  else if (rc == -1)
246  mutt_message(_("Error. Preserving temporary file: %s"), mutt_b2s(fname));
247 
248  m->append = old_append;
249 
250  mutt_buffer_pool_release(&fname);
251  return rc;
252 }
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mx.h:54
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:65
The "current" mailbox.
Definition: context.h:37
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:81
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
#define NONULL(x)
Definition: string2.h:37
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:70
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
int mx_mbox_close(struct Context **ptr)
Save changes and close mailbox.
Definition: mx.c:593
#define mutt_message(...)
Definition: logging.h:83
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition: file.c:1140
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
String manipulation buffer.
Definition: buffer.h:33
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:55
#define _(a)
Definition: message.h:28
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition: copy.h:57
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:255
Messages to be purged (bypass trash)
Definition: mutt.h:98
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:34
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1171
bool read
Email is read.
Definition: email.h:51
struct Mailbox * mailbox
Definition: context.h:51
bool old
Email is seen, but unread.
Definition: email.h:50
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:60
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
struct Message * mx_msg_open_new(struct Mailbox *m, struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1036
#define CH_FORCE_FROM
Give CH_FROM precedence over CH_WEED?
Definition: copy.h:65
#define mutt_b2s(buf)
Definition: buffer.h:41
View the message.
Definition: protos.h:58
Edit the message.
Definition: protos.h:59
A local copy of an email.
Definition: mx.h:83
Messages to be deleted.
Definition: mutt.h:96
A mailbox.
Definition: mailbox.h:81
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:50
Tagged messages.
Definition: mutt.h:101
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:49
Messages that have been read.
Definition: mutt.h:94
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:113
unsigned char C_MboxType
Config: Default type for creating new mailboxes.
Definition: mx.c:85
#define MUTT_QUIET
Do not print any messages.
Definition: mx.h:56
#define CH_NOLEN
Don&#39;t write Content-Length: and Lines:
Definition: copy.h:63
uint8_t MsgOpenFlags
Flags for mx_msg_open_new(), e.g. MUTT_ADD_FROM.
Definition: mx.h:64
&#39;mbox&#39; Mailbox type
Definition: mailbox.h:48
WHERE bool C_DeleteUntag
Config: Untag messages when they are marked for deletion.
Definition: globals.h:211
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
Log at debug level 1.
Definition: logging.h:40
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:49
#define mutt_error(...)
Definition: logging.h:84
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1150
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:77
WHERE char * C_Editor
Config: External command to use as an email editor.
Definition: globals.h:111
int mutt_append_message(struct Mailbox *dest, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Append a message.
Definition: copy.c:882
FILE * fp
pointer to the message data
Definition: mx.h:85
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file&#39;s modification time by 1 second.
Definition: file.c:964
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1655
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a &#39;From&#39; header line?
Definition: from.c:50
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
#define MUTT_NEWFOLDER
Create a new folder - same as MUTT_APPEND,.
Definition: mx.h:57
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:66
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:354
+ 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 263 of file editmsg.c.

264 {
265  struct EmailNode *en = NULL;
266  STAILQ_FOREACH(en, el, entries)
267  {
268  if (ev_message(action, m, en->email) == -1)
269  return -1;
270  }
271 
272  return 0;
273 }
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 STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
struct Email * email
Email in the list.
Definition: email.h:116
List of Emails.
Definition: email.h:114
+ Here is the call graph for this function:
+ Here is the caller graph for this function: