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