]> granicus.if.org Git - neomutt/blob - editmsg.c
factor out Context from mutt_append_message()
[neomutt] / editmsg.c
1 /**
2  * @file
3  * Prepare an email to be edited
4  *
5  * @authors
6  * Copyright (C) 1999-2002 Thomas Roessler <roessler@does-not-exist.org>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 /**
24  * @page editmsg Prepare an email to be edited
25  *
26  * Prepare an email to be edited
27  */
28
29 #include "config.h"
30 #include <errno.h>
31 #include <limits.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <time.h>
37 #include <unistd.h>
38 #include "mutt/mutt.h"
39 #include "config/lib.h"
40 #include "email/lib.h"
41 #include "mutt.h"
42 #include "context.h"
43 #include "copy.h"
44 #include "curs_lib.h"
45 #include "globals.h"
46 #include "mailbox.h"
47 #include "muttlib.h"
48 #include "mx.h"
49 #include "protos.h"
50
51 /**
52  * edit_or_view_one_message - Edit an email or view it in an external editor
53  * @param edit true if the message should be editable. If false, changes
54  *            to the message (in the editor) will be ignored.
55  * @param ctx Context
56  * @param cur Email
57  * @retval 1  Message not modified
58  * @retval 0  Message edited successfully
59  * @retval -1 Error
60  */
61 static int edit_or_view_one_message(bool edit, struct Context *ctx, struct Email *cur)
62 {
63   char tmp[PATH_MAX];
64   char buf[STRING];
65   enum MailboxType omagic;
66   int oerrno;
67   int rc;
68
69   bool o_read;
70   bool o_old;
71
72   int of, cf;
73
74   struct Message *msg = NULL;
75
76   FILE *fp = NULL;
77
78   struct stat sb;
79   time_t mtime = 0;
80
81   mutt_mktemp(tmp, sizeof(tmp));
82
83   omagic = MboxType;
84   MboxType = MUTT_MBOX;
85
86   struct Context *tmpctx = mx_mbox_open(NULL, tmp, MUTT_NEWFOLDER);
87
88   MboxType = omagic;
89
90   if (!tmpctx)
91   {
92     mutt_error(_("could not create temporary folder: %s"), strerror(errno));
93     return -1;
94   }
95
96   const int chflags =
97       CH_NOLEN |
98       ((ctx->mailbox->magic == MUTT_MBOX || ctx->mailbox->magic == MUTT_MMDF) ? 0 : CH_NOSTATUS);
99   rc = mutt_append_message(tmpctx->mailbox, ctx->mailbox, cur, 0, chflags);
100   oerrno = errno;
101
102   mx_mbox_close(&tmpctx, NULL);
103
104   if (rc == -1)
105   {
106     mutt_error(_("could not write temporary mail folder: %s"), strerror(oerrno));
107     goto bail;
108   }
109
110   rc = stat(tmp, &sb);
111   if (rc == -1)
112   {
113     mutt_error(_("Can't stat %s: %s"), tmp, strerror(errno));
114     goto bail;
115   }
116
117   /* The file the user is going to edit is not a real mbox, so we need to
118    * truncate the last newline in the temp file, which is logically part of
119    * the message separator, and not the body of the message.  If we fail to
120    * remove it, the message will grow by one line each time the user edits
121    * the message.
122    */
123   if (sb.st_size != 0 && truncate(tmp, sb.st_size - 1) == -1)
124   {
125     mutt_error(_("could not truncate temporary mail folder: %s"), strerror(errno));
126     goto bail;
127   }
128
129   if (!edit)
130   {
131     /* remove write permissions */
132     rc = mutt_file_chmod_rm_stat(tmp, S_IWUSR | S_IWGRP | S_IWOTH, &sb);
133     if (rc == -1)
134     {
135       mutt_debug(1, "Could not remove write permissions of %s: %s", tmp, strerror(errno));
136       /* Do not bail out here as we are checking afterwards if we should adopt
137        * changes of the temporary file. */
138     }
139   }
140
141   /* Do not reuse the stat sb here as it is outdated. */
142   mtime = mutt_file_decrease_mtime(tmp, NULL);
143
144   mutt_edit_file(NONULL(Editor), tmp);
145
146   rc = stat(tmp, &sb);
147   if (rc == -1)
148   {
149     mutt_error(_("Can't stat %s: %s"), tmp, strerror(errno));
150     goto bail;
151   }
152
153   if (sb.st_size == 0)
154   {
155     mutt_message(_("Message file is empty"));
156     rc = 1;
157     goto bail;
158   }
159
160   if (edit && sb.st_mtime == mtime)
161   {
162     mutt_message(_("Message not modified"));
163     rc = 1;
164     goto bail;
165   }
166
167   if (!edit && sb.st_mtime != mtime)
168   {
169     mutt_message(_("Message of read-only mailbox modified! Ignoring changes."));
170     rc = 1;
171     goto bail;
172   }
173
174   if (!edit)
175   {
176     /* stop processing here and skip right to the end */
177     rc = 1;
178     goto bail;
179   }
180
181   fp = fopen(tmp, "r");
182   if (!fp)
183   {
184     rc = -1;
185     mutt_error(_("Can't open message file: %s"), strerror(errno));
186     goto bail;
187   }
188
189   tmpctx = mx_mbox_open(ctx->mailbox, NULL, MUTT_APPEND);
190   if (!tmpctx)
191   {
192     rc = -1;
193     /* L10N: %s is from strerror(errno) */
194     mutt_error(_("Can't append to folder: %s"), strerror(errno));
195     goto bail;
196   }
197
198   of = 0;
199   cf = (((tmpctx->mailbox->magic == MUTT_MBOX) || (tmpctx->mailbox->magic == MUTT_MMDF)) ?
200             0 :
201             CH_NOSTATUS);
202
203   if (fgets(buf, sizeof(buf), fp) && is_from(buf, NULL, 0, NULL))
204   {
205     if ((tmpctx->mailbox->magic == MUTT_MBOX) || (tmpctx->mailbox->magic == MUTT_MMDF))
206       cf = CH_FROM | CH_FORCE_FROM;
207   }
208   else
209     of = MUTT_ADD_FROM;
210
211   /* XXX - we have to play games with the message flags to avoid
212    * problematic behavior with maildir folders.  */
213
214   o_read = cur->read;
215   o_old = cur->old;
216   cur->read = false;
217   cur->old = false;
218   msg = mx_msg_open_new(tmpctx->mailbox, cur, of);
219   cur->read = o_read;
220   cur->old = o_old;
221
222   if (!msg)
223   {
224     mutt_error(_("Can't append to folder: %s"), strerror(errno));
225     mx_mbox_close(&tmpctx, NULL);
226     goto bail;
227   }
228
229   rc = mutt_copy_hdr(fp, msg->fp, 0, sb.st_size, CH_NOLEN | cf, NULL);
230   if (rc == 0)
231   {
232     fputc('\n', msg->fp);
233     mutt_file_copy_stream(fp, msg->fp);
234   }
235
236   rc = mx_msg_commit(tmpctx->mailbox, msg);
237   mx_msg_close(tmpctx->mailbox, &msg);
238
239   mx_mbox_close(&tmpctx, NULL);
240
241 bail:
242   if (fp)
243     mutt_file_fclose(&fp);
244
245   if (rc >= 0)
246     unlink(tmp);
247
248   if (rc == 0)
249   {
250     mutt_set_flag(Context->mailbox, cur, MUTT_DELETE, 1);
251     mutt_set_flag(Context->mailbox, cur, MUTT_PURGE, 1);
252     mutt_set_flag(Context->mailbox, cur, MUTT_READ, 1);
253
254     if (DeleteUntag)
255       mutt_set_flag(Context->mailbox, cur, MUTT_TAG, 0);
256   }
257   else if (rc == -1)
258     mutt_message(_("Error. Preserving temporary file: %s"), tmp);
259
260   return rc;
261 }
262
263 /**
264  * edit_or_view_message - Edit an email or view it in an external editor
265  * @param edit true: Edit the email; false: view the email
266  * @param ctx  Mailbox Context
267  * @param e   Email
268  * @retval 1  Message not modified
269  * @retval 0  Message edited successfully
270  * @retval -1 Error
271  */
272 int edit_or_view_message(bool edit, struct Context *ctx, struct Email *e)
273 {
274   if (e)
275     return edit_or_view_one_message(edit, ctx, e);
276
277   for (int i = 0; i < ctx->mailbox->msg_count; i++)
278   {
279     if (!message_is_tagged(ctx, i))
280       continue;
281
282     if (edit_or_view_one_message(edit, ctx, ctx->mailbox->hdrs[i]) == -1)
283       return -1;
284   }
285
286   return 0;
287 }
288
289 /**
290  * mutt_edit_message - Edit a message
291  * @param ctx Mailbox Context
292  * @param e Email
293  * @retval 1  Message not modified
294  * @retval 0  Message edited successfully
295  * @retval -1 Error
296  */
297 int mutt_edit_message(struct Context *ctx, struct Email *e)
298 {
299   return edit_or_view_message(true, ctx, e); /* true means edit */
300 }
301
302 /**
303  * mutt_view_message - Edit a message
304  * @param ctx Mailbox Context
305  * @param e Email
306  * @retval 1  Message not modified
307  * @retval 0  Message edited successfully
308  * @retval -1 Error
309  */
310 int mutt_view_message(struct Context *ctx, struct Email *e)
311 {
312   return edit_or_view_message(false, ctx, e); /* false means only view */
313 }