]> granicus.if.org Git - neomutt/blob - editmsg.c
merge: Kill Buffy
[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/email.h"
41 #include "mutt.h"
42 #include "context.h"
43 #include "copy.h"
44 #include "curs_lib.h"
45 #include "globals.h"
46 #include "muttlib.h"
47 #include "mx.h"
48 #include "protos.h"
49
50 /**
51  * edit_or_view_one_message - Edit an email or view it in an external editor
52  * @param edit true if the message should be editable. If false, changes
53  *            to the message (in the editor) will be ignored.
54  * @param ctx Context
55  * @param cur Header of email
56  * @retval 1  Message not modified
57  * @retval 0  Message edited successfully
58  * @retval -1 Error
59  */
60 static int edit_or_view_one_message(bool edit, struct Context *ctx, struct Header *cur)
61 {
62   char tmp[PATH_MAX];
63   char buf[STRING];
64   int omagic;
65   int oerrno;
66   int rc;
67
68   bool o_read;
69   bool o_old;
70
71   int of, cf;
72
73   struct Context tmpctx;
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   rc = (mx_mbox_open(tmp, MUTT_NEWFOLDER, &tmpctx) == NULL) ? -1 : 0;
87
88   MboxType = omagic;
89
90   if (rc == -1)
91   {
92     mutt_error(_("could not create temporary folder: %s"), strerror(errno));
93     return -1;
94   }
95
96   rc = mutt_append_message(
97       &tmpctx, ctx, cur, 0,
98       CH_NOLEN | ((ctx->magic == MUTT_MBOX || ctx->magic == MUTT_MMDF) ? 0 : CH_NOSTATUS));
99   oerrno = errno;
100
101   mx_mbox_close(&tmpctx, NULL);
102
103   if (rc == -1)
104   {
105     mutt_error(_("could not write temporary mail folder: %s"), strerror(oerrno));
106     goto bail;
107   }
108
109   rc = stat(tmp, &sb);
110   if (rc == -1)
111   {
112     mutt_error(_("Can't stat %s: %s"), tmp, strerror(errno));
113     goto bail;
114   }
115
116   /* The file the user is going to edit is not a real mbox, so we need to
117    * truncate the last newline in the temp file, which is logically part of
118    * the message separator, and not the body of the message.  If we fail to
119    * remove it, the message will grow by one line each time the user edits
120    * the message.
121    */
122   if (sb.st_size != 0 && truncate(tmp, sb.st_size - 1) == -1)
123   {
124     mutt_error(_("could not truncate temporary mail folder: %s"), strerror(errno));
125     goto bail;
126   }
127
128   if (!edit)
129   {
130     /* remove write permissions */
131     rc = mutt_file_chmod_rm_stat(tmp, S_IWUSR | S_IWGRP | S_IWOTH, &sb);
132     if (rc == -1)
133     {
134       mutt_debug(1, "Could not remove write permissions of %s: %s", tmp, strerror(errno));
135       /* Do not bail out here as we are checking afterwards if we should adopt
136        * changes of the temporary file. */
137     }
138   }
139
140   /* Do not reuse the stat sb here as it is outdated. */
141   mtime = mutt_file_decrease_mtime(tmp, NULL);
142
143   mutt_edit_file(NONULL(Editor), tmp);
144
145   rc = stat(tmp, &sb);
146   if (rc == -1)
147   {
148     mutt_error(_("Can't stat %s: %s"), tmp, strerror(errno));
149     goto bail;
150   }
151
152   if (sb.st_size == 0)
153   {
154     mutt_message(_("Message file is empty!"));
155     rc = 1;
156     goto bail;
157   }
158
159   if (edit && sb.st_mtime == mtime)
160   {
161     mutt_message(_("Message not modified!"));
162     rc = 1;
163     goto bail;
164   }
165
166   if (!edit && sb.st_mtime != mtime)
167   {
168     mutt_message(_("Message of read-only mailbox modified! Ignoring changes."));
169     rc = 1;
170     goto bail;
171   }
172
173   if (!edit)
174   {
175     /* stop processing here and skip right to the end */
176     rc = 1;
177     goto bail;
178   }
179
180   fp = fopen(tmp, "r");
181   if (!fp)
182   {
183     rc = -1;
184     mutt_error(_("Can't open message file: %s"), strerror(errno));
185     goto bail;
186   }
187
188   if (mx_mbox_open(ctx->path, MUTT_APPEND, &tmpctx) == NULL)
189   {
190     rc = -1;
191     /* L10N: %s is from strerror(errno) */
192     mutt_error(_("Can't append to folder: %s"), strerror(errno));
193     goto bail;
194   }
195
196   of = 0;
197   cf = ((tmpctx.magic == MUTT_MBOX || tmpctx.magic == MUTT_MMDF) ? 0 : CH_NOSTATUS);
198
199   if (fgets(buf, sizeof(buf), fp) && is_from(buf, NULL, 0, NULL))
200   {
201     if (tmpctx.magic == MUTT_MBOX || tmpctx.magic == MUTT_MMDF)
202       cf = CH_FROM | CH_FORCE_FROM;
203   }
204   else
205     of = MUTT_ADD_FROM;
206
207   /* XXX - we have to play games with the message flags to avoid
208    * problematic behavior with maildir folders.  */
209
210   o_read = cur->read;
211   o_old = cur->old;
212   cur->read = cur->old = false;
213   msg = mx_msg_open_new(&tmpctx, cur, of);
214   cur->read = o_read;
215   cur->old = o_old;
216
217   if (!msg)
218   {
219     mutt_error(_("Can't append to folder: %s"), strerror(errno));
220     mx_mbox_close(&tmpctx, NULL);
221     goto bail;
222   }
223
224   rc = mutt_copy_hdr(fp, msg->fp, 0, sb.st_size, CH_NOLEN | cf, NULL);
225   if (rc == 0)
226   {
227     fputc('\n', msg->fp);
228     mutt_file_copy_stream(fp, msg->fp);
229   }
230
231   rc = mx_msg_commit(&tmpctx, msg);
232   mx_msg_close(&tmpctx, &msg);
233
234   mx_mbox_close(&tmpctx, NULL);
235
236 bail:
237   if (fp)
238     mutt_file_fclose(&fp);
239
240   if (rc >= 0)
241     unlink(tmp);
242
243   if (rc == 0)
244   {
245     mutt_set_flag(Context, cur, MUTT_DELETE, 1);
246     mutt_set_flag(Context, cur, MUTT_PURGE, 1);
247     mutt_set_flag(Context, cur, MUTT_READ, 1);
248
249     if (DeleteUntag)
250       mutt_set_flag(Context, cur, MUTT_TAG, 0);
251   }
252   else if (rc == -1)
253     mutt_message(_("Error. Preserving temporary file: %s"), tmp);
254
255   return rc;
256 }
257
258 /**
259  * edit_or_view_message - Edit an email or view it in an external editor
260  * @param edit true: Edit the email; false: view the email
261  * @param ctx  Mailbox Context
262  * @param hdr  Email Header
263  * @retval 1  Message not modified
264  * @retval 0  Message edited successfully
265  * @retval -1 Error
266  */
267 int edit_or_view_message(bool edit, struct Context *ctx, struct Header *hdr)
268 {
269   if (hdr)
270     return edit_or_view_one_message(edit, ctx, hdr);
271
272   for (int i = 0; i < ctx->msgcount; i++)
273   {
274     if (!message_is_tagged(ctx, i))
275       continue;
276
277     if (edit_or_view_one_message(edit, ctx, ctx->hdrs[i]) == -1)
278       return -1;
279   }
280
281   return 0;
282 }
283
284 /**
285  * mutt_edit_message - Edit a message
286  * @param ctx Mailbox Context
287  * @param hdr Email Header
288  * @retval 1  Message not modified
289  * @retval 0  Message edited successfully
290  * @retval -1 Error
291  */
292 int mutt_edit_message(struct Context *ctx, struct Header *hdr)
293 {
294   return edit_or_view_message(true, ctx, hdr); /* true means edit */
295 }
296
297 /**
298  * mutt_view_message - Edit a message
299  * @param ctx Mailbox Context
300  * @param hdr Email Header
301  * @retval 1  Message not modified
302  * @retval 0  Message edited successfully
303  * @retval -1 Error
304  */
305 int mutt_view_message(struct Context *ctx, struct Header *hdr)
306 {
307   return edit_or_view_message(false, ctx, hdr); /* false means only view */
308 }