/src/neomutt/postpone/postpone.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * Postponed Email Selection Dialog |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 1996-2002,2012-2013 Michael R. Elkins <me@mutt.org> |
7 | | * Copyright (C) 1999-2002,2004 Thomas Roessler <roessler@does-not-exist.org> |
8 | | * |
9 | | * @copyright |
10 | | * This program is free software: you can redistribute it and/or modify it under |
11 | | * the terms of the GNU General Public License as published by the Free Software |
12 | | * Foundation, either version 2 of the License, or (at your option) any later |
13 | | * version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
16 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
17 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
18 | | * details. |
19 | | * |
20 | | * You should have received a copy of the GNU General Public License along with |
21 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
22 | | */ |
23 | | |
24 | | /** |
25 | | * @page postpone_postpone Postponed Email |
26 | | * |
27 | | * Functions to deal with Postponed Emails. |
28 | | */ |
29 | | |
30 | | #include "config.h" |
31 | | #include <stdbool.h> |
32 | | #include <stdio.h> |
33 | | #include <string.h> |
34 | | #include <sys/stat.h> |
35 | | #include <unistd.h> |
36 | | #include "mutt/lib.h" |
37 | | #include "address/lib.h" |
38 | | #include "config/lib.h" |
39 | | #include "email/lib.h" |
40 | | #include "core/lib.h" |
41 | | #include "mutt.h" |
42 | | #include "lib.h" |
43 | | #include "ncrypt/lib.h" |
44 | | #include "send/lib.h" |
45 | | #include "globals.h" |
46 | | #include "handler.h" |
47 | | #include "mutt_logging.h" |
48 | | #include "mutt_thread.h" |
49 | | #include "muttlib.h" |
50 | | #include "mx.h" |
51 | | #include "protos.h" |
52 | | #include "rfc3676.h" |
53 | | #ifdef USE_IMAP |
54 | | #include "imap/lib.h" |
55 | | #endif |
56 | | |
57 | | /// Number of postponed (draft) emails |
58 | | short PostCount = 0; |
59 | | /// When true, force a recount of the postponed (draft) emails |
60 | | static bool UpdateNumPostponed = false; |
61 | | |
62 | | /** |
63 | | * mutt_num_postponed - Return the number of postponed messages |
64 | | * @param m currently selected mailbox |
65 | | * @param force |
66 | | * * false Use a cached value if costly to get a fresh count (IMAP) |
67 | | * * true Force check |
68 | | * @retval num Postponed messages |
69 | | */ |
70 | | int mutt_num_postponed(struct Mailbox *m, bool force) |
71 | 0 | { |
72 | 0 | struct stat st = { 0 }; |
73 | |
|
74 | 0 | static time_t LastModify = 0; |
75 | 0 | static char *OldPostponed = NULL; |
76 | |
|
77 | 0 | if (UpdateNumPostponed) |
78 | 0 | { |
79 | 0 | UpdateNumPostponed = false; |
80 | 0 | force = true; |
81 | 0 | } |
82 | |
|
83 | 0 | const char *const c_postponed = cs_subset_string(NeoMutt->sub, "postponed"); |
84 | 0 | if (!mutt_str_equal(c_postponed, OldPostponed)) |
85 | 0 | { |
86 | 0 | FREE(&OldPostponed); |
87 | 0 | OldPostponed = mutt_str_dup(c_postponed); |
88 | 0 | LastModify = 0; |
89 | 0 | force = true; |
90 | 0 | } |
91 | |
|
92 | 0 | if (!c_postponed) |
93 | 0 | return 0; |
94 | | |
95 | | // We currently are in the `$postponed` mailbox so just pick the current status |
96 | 0 | if (m && mutt_str_equal(c_postponed, m->realpath)) |
97 | 0 | { |
98 | 0 | PostCount = m->msg_count - m->msg_deleted; |
99 | 0 | return PostCount; |
100 | 0 | } |
101 | | |
102 | 0 | #ifdef USE_IMAP |
103 | | /* LastModify is useless for IMAP */ |
104 | 0 | if (imap_path_probe(c_postponed, NULL) == MUTT_IMAP) |
105 | 0 | { |
106 | 0 | if (force) |
107 | 0 | { |
108 | 0 | short newpc; |
109 | |
|
110 | 0 | newpc = imap_path_status(c_postponed, false); |
111 | 0 | if (newpc >= 0) |
112 | 0 | { |
113 | 0 | PostCount = newpc; |
114 | 0 | mutt_debug(LL_DEBUG3, "%d postponed IMAP messages found\n", PostCount); |
115 | 0 | } |
116 | 0 | else |
117 | 0 | { |
118 | 0 | mutt_debug(LL_DEBUG3, "using old IMAP postponed count\n"); |
119 | 0 | } |
120 | 0 | } |
121 | 0 | return PostCount; |
122 | 0 | } |
123 | 0 | #endif |
124 | | |
125 | 0 | if (stat(c_postponed, &st) == -1) |
126 | 0 | { |
127 | 0 | PostCount = 0; |
128 | 0 | LastModify = 0; |
129 | 0 | return 0; |
130 | 0 | } |
131 | | |
132 | 0 | if (S_ISDIR(st.st_mode)) |
133 | 0 | { |
134 | | /* if we have a maildir mailbox, we need to stat the "new" dir */ |
135 | 0 | struct Buffer *buf = buf_pool_get(); |
136 | |
|
137 | 0 | buf_printf(buf, "%s/new", c_postponed); |
138 | 0 | if ((access(buf_string(buf), F_OK) == 0) && (stat(buf_string(buf), &st) == -1)) |
139 | 0 | { |
140 | 0 | PostCount = 0; |
141 | 0 | LastModify = 0; |
142 | 0 | buf_pool_release(&buf); |
143 | 0 | return 0; |
144 | 0 | } |
145 | 0 | buf_pool_release(&buf); |
146 | 0 | } |
147 | | |
148 | 0 | if (LastModify < st.st_mtime) |
149 | 0 | { |
150 | 0 | #ifdef USE_NNTP |
151 | 0 | int optnews = OptNews; |
152 | 0 | #endif |
153 | 0 | LastModify = st.st_mtime; |
154 | |
|
155 | 0 | if (access(c_postponed, R_OK | F_OK) != 0) |
156 | 0 | return PostCount = 0; |
157 | 0 | #ifdef USE_NNTP |
158 | 0 | if (optnews) |
159 | 0 | OptNews = false; |
160 | 0 | #endif |
161 | 0 | struct Mailbox *m_post = mx_path_resolve(c_postponed); |
162 | 0 | if (mx_mbox_open(m_post, MUTT_NOSORT | MUTT_QUIET)) |
163 | 0 | { |
164 | 0 | PostCount = m_post->msg_count; |
165 | 0 | mx_fastclose_mailbox(m_post, false); |
166 | 0 | } |
167 | 0 | else |
168 | 0 | { |
169 | 0 | PostCount = 0; |
170 | 0 | } |
171 | 0 | mailbox_free(&m_post); |
172 | |
|
173 | 0 | #ifdef USE_NNTP |
174 | 0 | if (optnews) |
175 | 0 | OptNews = true; |
176 | 0 | #endif |
177 | 0 | } |
178 | | |
179 | 0 | return PostCount; |
180 | 0 | } |
181 | | |
182 | | /** |
183 | | * mutt_update_num_postponed - Force the update of the number of postponed messages |
184 | | */ |
185 | | void mutt_update_num_postponed(void) |
186 | 0 | { |
187 | 0 | UpdateNumPostponed = true; |
188 | 0 | } |
189 | | |
190 | | /** |
191 | | * hardclose - Try hard to close a mailbox |
192 | | * @param m Mailbox to close |
193 | | */ |
194 | | static void hardclose(struct Mailbox *m) |
195 | 0 | { |
196 | | /* messages might have been marked for deletion. |
197 | | * try once more on reopen before giving up. */ |
198 | 0 | enum MxStatus rc = mx_mbox_close(m); |
199 | 0 | if (rc != MX_STATUS_ERROR && rc != MX_STATUS_OK) |
200 | 0 | rc = mx_mbox_close(m); |
201 | 0 | if (rc != MX_STATUS_OK) |
202 | 0 | mx_fastclose_mailbox(m, false); |
203 | 0 | } |
204 | | |
205 | | /** |
206 | | * mutt_parse_crypt_hdr - Parse a crypto header string |
207 | | * @param p Header string to parse |
208 | | * @param set_empty_signas Allow an empty "Sign as" |
209 | | * @param crypt_app App, e.g. #APPLICATION_PGP |
210 | | * @retval num SecurityFlags, see #SecurityFlags |
211 | | */ |
212 | | SecurityFlags mutt_parse_crypt_hdr(const char *p, bool set_empty_signas, SecurityFlags crypt_app) |
213 | 0 | { |
214 | 0 | char smime_cryptalg[1024] = { 0 }; |
215 | 0 | char sign_as[1024] = { 0 }; |
216 | 0 | char *q = NULL; |
217 | 0 | SecurityFlags flags = SEC_NO_FLAGS; |
218 | |
|
219 | 0 | if (!WithCrypto) |
220 | 0 | return SEC_NO_FLAGS; |
221 | | |
222 | 0 | p = mutt_str_skip_email_wsp(p); |
223 | 0 | for (; p[0] != '\0'; p++) |
224 | 0 | { |
225 | 0 | switch (p[0]) |
226 | 0 | { |
227 | 0 | case 'c': |
228 | 0 | case 'C': |
229 | 0 | q = smime_cryptalg; |
230 | |
|
231 | 0 | if (p[1] == '<') |
232 | 0 | { |
233 | 0 | for (p += 2; (p[0] != '\0') && (p[0] != '>') && |
234 | 0 | (q < (smime_cryptalg + sizeof(smime_cryptalg) - 1)); |
235 | 0 | *q++ = *p++) |
236 | 0 | { |
237 | 0 | } |
238 | |
|
239 | 0 | if (p[0] != '>') |
240 | 0 | { |
241 | 0 | mutt_error(_("Illegal S/MIME header")); |
242 | 0 | return SEC_NO_FLAGS; |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | 0 | *q = '\0'; |
247 | 0 | break; |
248 | | |
249 | 0 | case 'e': |
250 | 0 | case 'E': |
251 | 0 | flags |= SEC_ENCRYPT; |
252 | 0 | break; |
253 | | |
254 | 0 | case 'i': |
255 | 0 | case 'I': |
256 | 0 | flags |= SEC_INLINE; |
257 | 0 | break; |
258 | | |
259 | | /* This used to be the micalg parameter. |
260 | | * |
261 | | * It's no longer needed, so we just skip the parameter in order |
262 | | * to be able to recall old messages. */ |
263 | 0 | case 'm': |
264 | 0 | case 'M': |
265 | 0 | if (p[1] != '<') |
266 | 0 | break; |
267 | | |
268 | 0 | for (p += 2; (p[0] != '\0') && (p[0] != '>'); p++) |
269 | 0 | ; // do nothing |
270 | |
|
271 | 0 | if (p[0] != '>') |
272 | 0 | { |
273 | 0 | mutt_error(_("Illegal crypto header")); |
274 | 0 | return SEC_NO_FLAGS; |
275 | 0 | } |
276 | 0 | break; |
277 | | |
278 | 0 | case 'o': |
279 | 0 | case 'O': |
280 | 0 | flags |= SEC_OPPENCRYPT; |
281 | 0 | break; |
282 | | |
283 | 0 | case 'a': |
284 | 0 | case 'A': |
285 | | #ifdef USE_AUTOCRYPT |
286 | | flags |= SEC_AUTOCRYPT; |
287 | | #endif |
288 | 0 | break; |
289 | | |
290 | 0 | case 'z': |
291 | 0 | case 'Z': |
292 | | #ifdef USE_AUTOCRYPT |
293 | | flags |= SEC_AUTOCRYPT_OVERRIDE; |
294 | | #endif |
295 | 0 | break; |
296 | | |
297 | 0 | case 's': |
298 | 0 | case 'S': |
299 | 0 | flags |= SEC_SIGN; |
300 | 0 | q = sign_as; |
301 | |
|
302 | 0 | if (p[1] == '<') |
303 | 0 | { |
304 | 0 | for (p += 2; |
305 | 0 | (p[0] != '\0') && (*p != '>') && (q < (sign_as + sizeof(sign_as) - 1)); |
306 | 0 | *q++ = *p++) |
307 | 0 | { |
308 | 0 | } |
309 | |
|
310 | 0 | if (p[0] != '>') |
311 | 0 | { |
312 | 0 | mutt_error(_("Illegal crypto header")); |
313 | 0 | return SEC_NO_FLAGS; |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | 0 | q[0] = '\0'; |
318 | 0 | break; |
319 | | |
320 | 0 | default: |
321 | 0 | mutt_error(_("Illegal crypto header")); |
322 | 0 | return SEC_NO_FLAGS; |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | /* the cryptalg field must not be empty */ |
327 | 0 | if (((WithCrypto & APPLICATION_SMIME) != 0) && *smime_cryptalg) |
328 | 0 | { |
329 | 0 | struct Buffer errmsg = buf_make(0); |
330 | 0 | int rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with", |
331 | 0 | smime_cryptalg, &errmsg); |
332 | |
|
333 | 0 | if ((CSR_RESULT(rc) != CSR_SUCCESS) && !buf_is_empty(&errmsg)) |
334 | 0 | mutt_error("%s", buf_string(&errmsg)); |
335 | |
|
336 | 0 | buf_dealloc(&errmsg); |
337 | 0 | } |
338 | | |
339 | | /* Set {Smime,Pgp}SignAs, if desired. */ |
340 | |
|
341 | 0 | if (((WithCrypto & APPLICATION_PGP) != 0) && (crypt_app == APPLICATION_PGP) && |
342 | 0 | (flags & SEC_SIGN) && (set_empty_signas || *sign_as)) |
343 | 0 | { |
344 | 0 | cs_subset_str_string_set(NeoMutt->sub, "pgp_sign_as", sign_as, NULL); |
345 | 0 | } |
346 | |
|
347 | 0 | if (((WithCrypto & APPLICATION_SMIME) != 0) && (crypt_app == APPLICATION_SMIME) && |
348 | 0 | (flags & SEC_SIGN) && (set_empty_signas || *sign_as)) |
349 | 0 | { |
350 | 0 | cs_subset_str_string_set(NeoMutt->sub, "smime_sign_as", sign_as, NULL); |
351 | 0 | } |
352 | |
|
353 | 0 | return flags; |
354 | 0 | } |
355 | | |
356 | | /** |
357 | | * create_tmp_files_for_attachments - Create temporary files for all attachments |
358 | | * @param fp_body file containing the template |
359 | | * @param file Allocated buffer for temporary file name |
360 | | * @param e_new The new email template header |
361 | | * @param body First body in email or group |
362 | | * @param protected_headers MIME headers for email template |
363 | | * @retval 0 Success |
364 | | * @retval -1 Error |
365 | | */ |
366 | | static int create_tmp_files_for_attachments(FILE *fp_body, struct Buffer *file, |
367 | | struct Email *e_new, struct Body *body, |
368 | | struct Envelope *protected_headers) |
369 | 0 | { |
370 | 0 | struct Body *b = NULL; |
371 | 0 | struct State state = { 0 }; |
372 | |
|
373 | 0 | state.fp_in = fp_body; |
374 | |
|
375 | 0 | for (b = body; b; b = b->next) |
376 | 0 | { |
377 | 0 | if (b->type == TYPE_MULTIPART) |
378 | 0 | { |
379 | 0 | if (create_tmp_files_for_attachments(fp_body, file, e_new, b->parts, protected_headers) < 0) |
380 | 0 | { |
381 | 0 | return -1; |
382 | 0 | } |
383 | 0 | } |
384 | 0 | else |
385 | 0 | { |
386 | 0 | buf_reset(file); |
387 | 0 | if (b->filename) |
388 | 0 | { |
389 | 0 | buf_strcpy(file, b->filename); |
390 | 0 | b->d_filename = mutt_str_dup(b->filename); |
391 | 0 | } |
392 | 0 | else |
393 | 0 | { |
394 | | /* avoid Content-Disposition: header with temporary filename */ |
395 | 0 | b->use_disp = false; |
396 | 0 | } |
397 | | |
398 | | /* set up state flags */ |
399 | |
|
400 | 0 | state.flags = 0; |
401 | |
|
402 | 0 | if (b->type == TYPE_TEXT) |
403 | 0 | { |
404 | 0 | if (mutt_istr_equal("yes", mutt_param_get(&b->parameter, "x-mutt-noconv"))) |
405 | 0 | { |
406 | 0 | b->noconv = true; |
407 | 0 | } |
408 | 0 | else |
409 | 0 | { |
410 | 0 | state.flags |= STATE_CHARCONV; |
411 | 0 | b->noconv = false; |
412 | 0 | } |
413 | |
|
414 | 0 | mutt_param_delete(&b->parameter, "x-mutt-noconv"); |
415 | 0 | } |
416 | |
|
417 | 0 | mutt_adv_mktemp(file); |
418 | 0 | state.fp_out = mutt_file_fopen(buf_string(file), "w"); |
419 | 0 | if (!state.fp_out) |
420 | 0 | return -1; |
421 | | |
422 | 0 | SecurityFlags sec_type = SEC_NO_FLAGS; |
423 | 0 | if (((WithCrypto & APPLICATION_PGP) != 0) && sec_type == SEC_NO_FLAGS) |
424 | 0 | sec_type = mutt_is_application_pgp(b); |
425 | 0 | if (((WithCrypto & APPLICATION_SMIME) != 0) && sec_type == SEC_NO_FLAGS) |
426 | 0 | sec_type = mutt_is_application_smime(b); |
427 | 0 | if (sec_type & (SEC_ENCRYPT | SEC_SIGN)) |
428 | 0 | { |
429 | 0 | if (sec_type & SEC_ENCRYPT) |
430 | 0 | { |
431 | 0 | if (!crypt_valid_passphrase(sec_type)) |
432 | 0 | return -1; |
433 | 0 | if (sec_type & APPLICATION_SMIME) |
434 | 0 | crypt_smime_getkeys(e_new->env); |
435 | 0 | mutt_message(_("Decrypting message...")); |
436 | 0 | } |
437 | | |
438 | 0 | if (mutt_body_handler(b, &state) < 0) |
439 | 0 | { |
440 | 0 | mutt_error(_("Decryption failed")); |
441 | 0 | return -1; |
442 | 0 | } |
443 | | |
444 | | /* Is this the first body part? Then save the headers. */ |
445 | 0 | if ((b == body) && !protected_headers) |
446 | 0 | { |
447 | 0 | protected_headers = b->mime_headers; |
448 | 0 | b->mime_headers = NULL; |
449 | 0 | } |
450 | |
|
451 | 0 | e_new->security |= sec_type; |
452 | 0 | b->type = TYPE_TEXT; |
453 | 0 | mutt_str_replace(&b->subtype, "plain"); |
454 | 0 | if (sec_type & APPLICATION_PGP) |
455 | 0 | mutt_param_delete(&b->parameter, "x-action"); |
456 | 0 | } |
457 | 0 | else |
458 | 0 | { |
459 | 0 | mutt_decode_attachment(b, &state); |
460 | 0 | } |
461 | | |
462 | 0 | if (mutt_file_fclose(&state.fp_out) != 0) |
463 | 0 | return -1; |
464 | | |
465 | 0 | mutt_str_replace(&b->filename, buf_string(file)); |
466 | 0 | b->unlink = true; |
467 | |
|
468 | 0 | mutt_stamp_attachment(b); |
469 | |
|
470 | 0 | mutt_body_free(&b->parts); |
471 | 0 | if (b->email) |
472 | 0 | b->email->body = NULL; /* avoid dangling pointer */ |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | 0 | return 0; |
477 | 0 | } |
478 | | |
479 | | /** |
480 | | * mutt_prepare_template - Prepare a message template |
481 | | * @param fp If not NULL, file containing the template |
482 | | * @param m If fp is NULL, the Mailbox containing the header with the template |
483 | | * @param e_new The template is read into this Header |
484 | | * @param e Email to recall/resend |
485 | | * @param resend Set if resending (as opposed to recalling a postponed msg) |
486 | | * Resent messages enable header weeding, and also |
487 | | * discard any existing Message-ID and Mail-Followup-To |
488 | | * @retval 0 Success |
489 | | * @retval -1 Error |
490 | | */ |
491 | | int mutt_prepare_template(FILE *fp, struct Mailbox *m, struct Email *e_new, |
492 | | struct Email *e, bool resend) |
493 | 0 | { |
494 | 0 | struct Message *msg = NULL; |
495 | 0 | struct Body *b = NULL; |
496 | 0 | FILE *fp_body = NULL; |
497 | 0 | int rc = -1; |
498 | 0 | struct Envelope *protected_headers = NULL; |
499 | 0 | struct Buffer *file = NULL; |
500 | |
|
501 | 0 | if (!fp && !(msg = mx_msg_open(m, e))) |
502 | 0 | return -1; |
503 | | |
504 | 0 | if (!fp) |
505 | 0 | fp = msg->fp; |
506 | |
|
507 | 0 | fp_body = fp; |
508 | | |
509 | | /* parse the message header and MIME structure */ |
510 | |
|
511 | 0 | if (!mutt_file_seek(fp, e->offset, SEEK_SET)) |
512 | 0 | { |
513 | 0 | return -1; |
514 | 0 | } |
515 | 0 | e_new->offset = e->offset; |
516 | | /* enable header weeding for resent messages */ |
517 | 0 | e_new->env = mutt_rfc822_read_header(fp, e_new, true, resend); |
518 | 0 | e_new->body->length = e->body->length; |
519 | 0 | mutt_parse_part(fp, e_new->body); |
520 | | |
521 | | /* If resending a message, don't keep message_id or mail_followup_to. |
522 | | * Otherwise, we are resuming a postponed message, and want to keep those |
523 | | * headers if they exist. */ |
524 | 0 | if (resend) |
525 | 0 | { |
526 | 0 | FREE(&e_new->env->message_id); |
527 | 0 | mutt_addrlist_clear(&e_new->env->mail_followup_to); |
528 | 0 | } |
529 | |
|
530 | 0 | SecurityFlags sec_type = SEC_NO_FLAGS; |
531 | 0 | if (((WithCrypto & APPLICATION_PGP) != 0) && sec_type == SEC_NO_FLAGS) |
532 | 0 | sec_type = mutt_is_multipart_encrypted(e_new->body); |
533 | 0 | if (((WithCrypto & APPLICATION_SMIME) != 0) && sec_type == SEC_NO_FLAGS) |
534 | 0 | sec_type = mutt_is_application_smime(e_new->body); |
535 | 0 | if (sec_type != SEC_NO_FLAGS) |
536 | 0 | { |
537 | 0 | e_new->security |= sec_type; |
538 | 0 | if (!crypt_valid_passphrase(sec_type)) |
539 | 0 | goto bail; |
540 | | |
541 | 0 | mutt_message(_("Decrypting message...")); |
542 | 0 | int ret = -1; |
543 | 0 | if (sec_type & APPLICATION_PGP) |
544 | 0 | ret = crypt_pgp_decrypt_mime(fp, &fp_body, e_new->body, &b); |
545 | 0 | else if (sec_type & APPLICATION_SMIME) |
546 | 0 | ret = crypt_smime_decrypt_mime(fp, &fp_body, e_new->body, &b); |
547 | 0 | if ((ret == -1) || !b) |
548 | 0 | { |
549 | 0 | mutt_error(_("Could not decrypt postponed message")); |
550 | 0 | goto bail; |
551 | 0 | } |
552 | | |
553 | | /* throw away the outer layer and keep only the (now decrypted) inner part |
554 | | * with its headers. */ |
555 | 0 | mutt_body_free(&e_new->body); |
556 | 0 | e_new->body = b; |
557 | |
|
558 | 0 | if (b->mime_headers) |
559 | 0 | { |
560 | 0 | protected_headers = b->mime_headers; |
561 | 0 | b->mime_headers = NULL; |
562 | 0 | } |
563 | |
|
564 | 0 | mutt_clear_error(); |
565 | 0 | } |
566 | | |
567 | | /* remove a potential multipart/signed layer - useful when |
568 | | * resending messages */ |
569 | 0 | if ((WithCrypto != 0) && mutt_is_multipart_signed(e_new->body)) |
570 | 0 | { |
571 | 0 | e_new->security |= SEC_SIGN; |
572 | 0 | if (((WithCrypto & APPLICATION_PGP) != 0) && |
573 | 0 | mutt_istr_equal(mutt_param_get(&e_new->body->parameter, "protocol"), "application/pgp-signature")) |
574 | 0 | { |
575 | 0 | e_new->security |= APPLICATION_PGP; |
576 | 0 | } |
577 | 0 | else if (WithCrypto & APPLICATION_SMIME) |
578 | 0 | { |
579 | 0 | e_new->security |= APPLICATION_SMIME; |
580 | 0 | } |
581 | | |
582 | | /* destroy the signature */ |
583 | 0 | mutt_body_free(&e_new->body->parts->next); |
584 | 0 | e_new->body = mutt_remove_multipart(e_new->body); |
585 | |
|
586 | 0 | if (e_new->body->mime_headers) |
587 | 0 | { |
588 | 0 | mutt_env_free(&protected_headers); |
589 | 0 | protected_headers = e_new->body->mime_headers; |
590 | 0 | e_new->body->mime_headers = NULL; |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | | /* We don't need no primary multipart/mixed. */ |
595 | 0 | if ((e_new->body->type == TYPE_MULTIPART) && mutt_istr_equal(e_new->body->subtype, "mixed")) |
596 | 0 | e_new->body = mutt_remove_multipart(e_new->body); |
597 | |
|
598 | 0 | file = buf_pool_get(); |
599 | | |
600 | | /* create temporary files for all attachments */ |
601 | 0 | if (create_tmp_files_for_attachments(fp_body, file, e_new, e_new->body, protected_headers) < 0) |
602 | 0 | { |
603 | 0 | goto bail; |
604 | 0 | } |
605 | | |
606 | 0 | const bool c_crypt_protected_headers_read = cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_read"); |
607 | 0 | if (c_crypt_protected_headers_read && protected_headers && protected_headers->subject && |
608 | 0 | !mutt_str_equal(e_new->env->subject, protected_headers->subject)) |
609 | 0 | { |
610 | 0 | mutt_str_replace(&e_new->env->subject, protected_headers->subject); |
611 | 0 | } |
612 | 0 | mutt_env_free(&protected_headers); |
613 | | |
614 | | /* Fix encryption flags. */ |
615 | | |
616 | | /* No inline if multipart. */ |
617 | 0 | if ((WithCrypto != 0) && (e_new->security & SEC_INLINE) && e_new->body->next) |
618 | 0 | e_new->security &= ~SEC_INLINE; |
619 | | |
620 | | /* Do we even support multiple mechanisms? */ |
621 | 0 | e_new->security &= WithCrypto | ~(APPLICATION_PGP | APPLICATION_SMIME); |
622 | | |
623 | | /* Theoretically, both could be set. Take the one the user wants to set by default. */ |
624 | 0 | if ((e_new->security & APPLICATION_PGP) && (e_new->security & APPLICATION_SMIME)) |
625 | 0 | { |
626 | 0 | const bool c_smime_is_default = cs_subset_bool(NeoMutt->sub, "smime_is_default"); |
627 | 0 | if (c_smime_is_default) |
628 | 0 | e_new->security &= ~APPLICATION_PGP; |
629 | 0 | else |
630 | 0 | e_new->security &= ~APPLICATION_SMIME; |
631 | 0 | } |
632 | |
|
633 | 0 | mutt_rfc3676_space_unstuff(e_new); |
634 | |
|
635 | 0 | rc = 0; |
636 | |
|
637 | 0 | bail: |
638 | | |
639 | | /* that's it. */ |
640 | 0 | buf_pool_release(&file); |
641 | 0 | if (fp_body != fp) |
642 | 0 | mutt_file_fclose(&fp_body); |
643 | 0 | if (msg) |
644 | 0 | mx_msg_close(m, &msg); |
645 | |
|
646 | 0 | if (rc == -1) |
647 | 0 | { |
648 | 0 | mutt_env_free(&e_new->env); |
649 | 0 | mutt_body_free(&e_new->body); |
650 | 0 | } |
651 | |
|
652 | 0 | return rc; |
653 | 0 | } |
654 | | |
655 | | /** |
656 | | * mutt_get_postponed - Recall a postponed message |
657 | | * @param[in] m_cur Current mailbox |
658 | | * @param[in] hdr envelope/attachment info for recalled message |
659 | | * @param[out] cur if message was a reply, 'cur' is set to the message which 'hdr' is in reply to |
660 | | * @param[in] fcc fcc for the recalled message |
661 | | * @retval -1 Error/no messages |
662 | | * @retval 0 Normal exit |
663 | | * @retval #SEND_REPLY Recalled message is a reply |
664 | | */ |
665 | | int mutt_get_postponed(struct Mailbox *m_cur, struct Email *hdr, |
666 | | struct Email **cur, struct Buffer *fcc) |
667 | 0 | { |
668 | 0 | const char *const c_postponed = cs_subset_string(NeoMutt->sub, "postponed"); |
669 | 0 | if (!c_postponed) |
670 | 0 | return -1; |
671 | | |
672 | 0 | struct Email *e = NULL; |
673 | 0 | int rc = SEND_POSTPONED; |
674 | 0 | const char *p = NULL; |
675 | |
|
676 | 0 | struct Mailbox *m = mx_path_resolve(c_postponed); |
677 | 0 | if (m_cur != m) |
678 | 0 | { |
679 | 0 | if (!mx_mbox_open(m, MUTT_NOSORT)) |
680 | 0 | { |
681 | 0 | PostCount = 0; |
682 | 0 | mutt_error(_("No postponed messages")); |
683 | 0 | mailbox_free(&m); |
684 | 0 | return -1; |
685 | 0 | } |
686 | 0 | } |
687 | | |
688 | 0 | mx_mbox_check(m); |
689 | |
|
690 | 0 | if (m->msg_count == 0) |
691 | 0 | { |
692 | 0 | PostCount = 0; |
693 | 0 | mutt_error(_("No postponed messages")); |
694 | 0 | if (m_cur != m) |
695 | 0 | { |
696 | 0 | mx_fastclose_mailbox(m, false); |
697 | 0 | mailbox_free(&m); |
698 | 0 | } |
699 | 0 | return -1; |
700 | 0 | } |
701 | | |
702 | | /* avoid the "purge deleted messages" prompt */ |
703 | 0 | const enum QuadOption c_delete = cs_subset_quad(NeoMutt->sub, "delete"); |
704 | 0 | cs_subset_str_native_set(NeoMutt->sub, "delete", MUTT_YES, NULL); |
705 | |
|
706 | 0 | if (m->msg_count == 1) |
707 | 0 | { |
708 | | /* only one message, so just use that one. */ |
709 | 0 | e = m->emails[0]; |
710 | 0 | } |
711 | 0 | else if (!(e = dlg_select_postponed_email(m))) |
712 | 0 | { |
713 | 0 | rc = -1; |
714 | 0 | goto cleanup; |
715 | 0 | } |
716 | | |
717 | 0 | if (mutt_prepare_template(NULL, m, hdr, e, false) < 0) |
718 | 0 | { |
719 | 0 | rc = -1; |
720 | 0 | goto cleanup; |
721 | 0 | } |
722 | | |
723 | | /* finished with this message, so delete it. */ |
724 | 0 | mutt_set_flag(m, e, MUTT_DELETE, true, true); |
725 | 0 | mutt_set_flag(m, e, MUTT_PURGE, true, true); |
726 | | |
727 | | /* update the count for the status display */ |
728 | 0 | PostCount = m->msg_count - m->msg_deleted; |
729 | |
|
730 | 0 | struct ListNode *np = NULL, *tmp = NULL; |
731 | 0 | STAILQ_FOREACH_SAFE(np, &hdr->env->userhdrs, entries, tmp) |
732 | 0 | { |
733 | 0 | size_t plen = 0; |
734 | | // Check for header names: most specific first |
735 | 0 | if ((plen = mutt_istr_startswith(np->data, "X-Mutt-References:")) || |
736 | 0 | (plen = mutt_istr_startswith(np->data, "Mutt-References:"))) |
737 | 0 | { |
738 | | /* if a mailbox is currently open, look to see if the original message |
739 | | * the user attempted to reply to is in this mailbox */ |
740 | 0 | if (m_cur) |
741 | 0 | { |
742 | 0 | p = mutt_str_skip_email_wsp(np->data + plen); |
743 | 0 | if (!m_cur->id_hash) |
744 | 0 | m_cur->id_hash = mutt_make_id_hash(m_cur); |
745 | 0 | *cur = mutt_hash_find(m_cur->id_hash, p); |
746 | |
|
747 | 0 | if (*cur) |
748 | 0 | rc |= SEND_REPLY; |
749 | 0 | } |
750 | 0 | } |
751 | | // Check for header names: most specific first |
752 | 0 | else if ((plen = mutt_istr_startswith(np->data, "X-Mutt-Fcc:")) || |
753 | 0 | (plen = mutt_istr_startswith(np->data, "Mutt-Fcc:"))) |
754 | 0 | { |
755 | 0 | p = mutt_str_skip_email_wsp(np->data + plen); |
756 | 0 | buf_strcpy(fcc, p); |
757 | 0 | buf_pretty_mailbox(fcc); |
758 | | |
759 | | /* note that mutt-fcc was present. we do this because we want to add a |
760 | | * default fcc if the header was missing, but preserve the request of the |
761 | | * user to not make a copy if the header field is present, but empty. */ |
762 | 0 | rc |= SEND_POSTPONED_FCC; |
763 | 0 | } |
764 | | // Check for header names: most specific first |
765 | 0 | else if (((WithCrypto & APPLICATION_PGP) != 0) && |
766 | 0 | ((plen = mutt_istr_startswith(np->data, "X-Mutt-PGP:")) || |
767 | 0 | (plen = mutt_istr_startswith(np->data, "Mutt-PGP:")) || |
768 | 0 | (plen = mutt_istr_startswith(np->data, "Pgp:")))) |
769 | 0 | { |
770 | 0 | hdr->security = mutt_parse_crypt_hdr(np->data + plen, true, APPLICATION_PGP); |
771 | 0 | hdr->security |= APPLICATION_PGP; |
772 | 0 | } |
773 | | // Check for header names: most specific first |
774 | 0 | else if (((WithCrypto & APPLICATION_SMIME) != 0) && |
775 | 0 | ((plen = mutt_istr_startswith(np->data, "X-Mutt-SMIME:")) || |
776 | 0 | (plen = mutt_istr_startswith(np->data, "Mutt-SMIME:")))) |
777 | 0 | { |
778 | 0 | hdr->security = mutt_parse_crypt_hdr(np->data + plen, true, APPLICATION_SMIME); |
779 | 0 | hdr->security |= APPLICATION_SMIME; |
780 | 0 | } |
781 | | #ifdef MIXMASTER |
782 | | // Check for header names: most specific first |
783 | | else if ((plen = mutt_istr_startswith(np->data, "X-Mutt-Mix:")) || |
784 | | (plen = mutt_istr_startswith(np->data, "Mutt-Mix:"))) |
785 | | { |
786 | | mutt_list_free(&hdr->chain); |
787 | | |
788 | | char *t = strtok(np->data + plen, " \t\n"); |
789 | | while (t) |
790 | | { |
791 | | mutt_list_insert_tail(&hdr->chain, mutt_str_dup(t)); |
792 | | t = strtok(NULL, " \t\n"); |
793 | | } |
794 | | } |
795 | | #endif |
796 | 0 | else |
797 | 0 | { |
798 | | // skip header removal |
799 | 0 | continue; |
800 | 0 | } |
801 | | |
802 | | // remove the header |
803 | 0 | STAILQ_REMOVE(&hdr->env->userhdrs, np, ListNode, entries); |
804 | 0 | FREE(&np->data); |
805 | 0 | FREE(&np); |
806 | 0 | } |
807 | |
|
808 | 0 | const bool c_crypt_opportunistic_encrypt = cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt"); |
809 | 0 | if (c_crypt_opportunistic_encrypt) |
810 | 0 | crypt_opportunistic_encrypt(hdr); |
811 | |
|
812 | 0 | cleanup: |
813 | 0 | if (m_cur != m) |
814 | 0 | { |
815 | 0 | hardclose(m); |
816 | 0 | mailbox_free(&m); |
817 | 0 | } |
818 | |
|
819 | 0 | cs_subset_str_native_set(NeoMutt->sub, "delete", c_delete, NULL); |
820 | 0 | return rc; |
821 | 0 | } |