NeoMutt  2019-12-07-60-g0cfa53
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/mutt.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 omagic = 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 = omagic;
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 |
87  (((m->magic == MUTT_MBOX) || (m->magic == MUTT_MMDF)) ? CH_NO_FLAGS : CH_NOSTATUS);
88  rc = mutt_append_message(ctx_tmp->mailbox, m, e, MUTT_CM_NO_FLAGS, chflags);
89  int oerrno = errno;
90 
91  mx_mbox_close(&ctx_tmp);
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(mutt_b2s(fname), &sb);
100  if (rc == -1)
101  {
102  mutt_error(_("Can't stat %s: %s"), mutt_b2s(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 ((sb.st_size != 0) && (truncate(mutt_b2s(fname), sb.st_size - 1) == -1))
112  {
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 */
120  rc = mutt_file_chmod_rm_stat(mutt_b2s(fname), S_IWUSR | S_IWGRP | S_IWOTH, &sb);
121  if (rc == -1)
122  {
123  mutt_debug(LL_DEBUG1, "Could not remove write permissions of %s: %s",
124  mutt_b2s(fname), strerror(errno));
125  /* Do not bail out here as we are checking afterwards if we should adopt
126  * changes of the temporary file. */
127  }
128  }
129 
130  /* Do not reuse the stat sb here as it is outdated. */
131  time_t mtime = mutt_file_decrease_mtime(mutt_b2s(fname), NULL);
132 
134 
135  rc = stat(mutt_b2s(fname), &sb);
136  if (rc == -1)
137  {
138  mutt_error(_("Can't stat %s: %s"), mutt_b2s(fname), strerror(errno));
139  goto bail;
140  }
141 
142  if (sb.st_size == 0)
143  {
144  mutt_message(_("Message file is empty"));
145  rc = 1;
146  goto bail;
147  }
148 
149  if ((action == EVM_EDIT) && (sb.st_mtime == mtime))
150  {
151  mutt_message(_("Message not modified"));
152  rc = 1;
153  goto bail;
154  }
155 
156  if ((action == EVM_VIEW) && (sb.st_mtime != mtime))
157  {
158  mutt_message(_("Message of read-only mailbox modified! Ignoring changes."));
159  rc = 1;
160  goto bail;
161  }
162 
163  if (action == EVM_VIEW)
164  {
165  /* stop processing here and skip right to the end */
166  rc = 1;
167  goto bail;
168  }
169 
170  fp = fopen(mutt_b2s(fname), "r");
171  if (!fp)
172  {
173  rc = -1;
174  mutt_error(_("Can't open message file: %s"), strerror(errno));
175  goto bail;
176  }
177 
178  struct Context *ctx_app = mx_mbox_open(m, MUTT_APPEND | MUTT_QUIET);
179  if (!ctx_app)
180  {
181  rc = -1;
182  /* L10N: %s is from strerror(errno) */
183  mutt_error(_("Can't append to folder: %s"), strerror(errno));
184  goto bail;
185  }
186 
188  CopyHeaderFlags cf =
189  (((ctx_app->mailbox->magic == MUTT_MBOX) || (ctx_app->mailbox->magic == MUTT_MMDF)) ?
190  CH_NO_FLAGS :
191  CH_NOSTATUS);
192 
193  if (fgets(buf, sizeof(buf), fp) && is_from(buf, NULL, 0, NULL))
194  {
195  if ((ctx_app->mailbox->magic == MUTT_MBOX) || (ctx_app->mailbox->magic == MUTT_MMDF))
196  cf = CH_FROM | CH_FORCE_FROM;
197  }
198  else
199  of = MUTT_ADD_FROM;
200 
201  /* XXX - we have to play games with the message flags to avoid
202  * problematic behavior with maildir folders. */
203 
204  bool o_read = e->read;
205  bool o_old = e->old;
206  e->read = false;
207  e->old = false;
208  struct Message *msg = mx_msg_open_new(ctx_app->mailbox, e, of);
209  e->read = o_read;
210  e->old = o_old;
211 
212  if (!msg)
213  {
214  mutt_error(_("Can't append to folder: %s"), strerror(errno));
215  mx_mbox_close(&ctx_app);
216  goto bail;
217  }
218 
219  rc = mutt_copy_hdr(fp, msg->fp, 0, sb.st_size, CH_NOLEN | cf, NULL, 0);
220  if (rc == 0)
221  {
222  fputc('\n', msg->fp);
223  mutt_file_copy_stream(fp, msg->fp);
224  }
225 
226  rc = mx_msg_commit(ctx_app->mailbox, msg);
227  mx_msg_close(ctx_app->mailbox, &msg);
228 
229  mx_mbox_close(&ctx_app);
230 
231 bail:
232  mutt_file_fclose(&fp);
233 
234  if (rc >= 0)
235  unlink(mutt_b2s(fname));
236 
237  if (rc == 0)
238  {
239  mutt_set_flag(m, e, MUTT_DELETE, true);
240  mutt_set_flag(m, e, MUTT_PURGE, true);
241  mutt_set_flag(m, e, MUTT_READ, true);
242 
243  if (C_DeleteUntag)
244  mutt_set_flag(m, e, MUTT_TAG, false);
245  }
246  else if (rc == -1)
247  mutt_message(_("Error. Preserving temporary file: %s"), mutt_b2s(fname));
248 
249  m->append = old_append;
250 
251  mutt_buffer_pool_release(&fname);
252  return rc;
253 }
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mx.h:52
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:63
The "current" mailbox.
Definition: context.h:36
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:78
#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:583
#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:1137
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:253
Messages to be purged (bypass trash)
Definition: mutt.h:104
#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:1140
bool read
Email is read.
Definition: email.h:51
struct Mailbox * mailbox
Definition: context.h:50
bool old
Email is seen, but unread.
Definition: email.h:50
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:60
enum MailboxType magic
Mailbox type.
Definition: mailbox.h:104
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
struct Message * mx_msg_open_new(struct Mailbox *m, struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1009
#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:81
Messages to be deleted.
Definition: mutt.h:102
A mailbox.
Definition: mailbox.h:80
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:50
Tagged messages.
Definition: mutt.h:107
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:48
Messages that have been read.
Definition: mutt.h:100
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:112
unsigned char C_MboxType
Config: Default type for creating new mailboxes.
Definition: mx.c:83
#define MUTT_QUIET
Do not print any messages.
Definition: mx.h:54
#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:62
&#39;mbox&#39; Mailbox type
Definition: mailbox.h:47
WHERE bool C_DeleteUntag
Config: Untag messages when they are marked for deletion.
Definition: globals.h:209
MailboxType
Supported mailbox formats.
Definition: mailbox.h:42
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:270
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:1119
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:109
int mutt_append_message(struct Mailbox *dest, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Append a message.
Definition: copy.c:880
FILE * fp
pointer to the message data
Definition: mx.h:83
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:961
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1603
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:55
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:64
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:352
+ 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 264 of file editmsg.c.

265 {
266  struct EmailNode *en = NULL;
267  STAILQ_FOREACH(en, el, entries)
268  {
269  if (ev_message(action, m, en->email) == -1)
270  return -1;
271  }
272 
273  return 0;
274 }
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: