root/commands.c

Revision 5443:8b783c916893, 24.2 kB (checked in by Rocco Rutte <pdmef@…>, 8 weeks ago)

Use realpath() in mutt_pretty_mailbox() for paths that may need it.
This requires to add the buffer size as parameter since the result may
be longer than the original but still fit in the buffer. Closes #2948.

Line 
1/*
2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 2000 Thomas Roessler <roessler@does-not-exist.org>
4 *
5 *     This program is free software; you can redistribute it and/or modify
6 *     it under the terms of the GNU General Public License as published by
7 *     the Free Software Foundation; either version 2 of the License, or
8 *     (at your option) any later version.
9 *
10 *     This program is distributed in the hope that it will be useful,
11 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *     GNU General Public License for more details.
14 *
15 *     You should have received a copy of the GNU General Public License
16 *     along with this program; if not, write to the Free Software
17 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 */ 
19
20#if HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "mutt.h"
25#include "mutt_curses.h"
26#include "mutt_menu.h"
27#include "mime.h"
28#include "sort.h"
29#include "mailbox.h"
30#include "copy.h"
31#include "mx.h"
32#include "pager.h"
33#include "mutt_crypt.h"
34#include "mutt_idna.h"
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38
39#ifdef USE_IMAP
40#include "imap.h"
41#endif
42
43#include "buffy.h"
44
45#include <errno.h>
46#include <unistd.h>
47#include <stdlib.h>
48#include <string.h>
49#include <sys/wait.h>
50#include <sys/stat.h>
51#include <sys/types.h>
52#include <utime.h>
53
54static const char *ExtPagerProgress = "all";
55
56/* The folder the user last saved to.  Used by ci_save_message() */
57static char LastSaveFolder[_POSIX_PATH_MAX] = "";
58
59int mutt_display_message (HEADER *cur)
60{
61  char tempfile[_POSIX_PATH_MAX], buf[LONG_STRING];
62  int rc = 0, builtin = 0;
63  int cmflags = M_CM_DECODE | M_CM_DISPLAY | M_CM_CHARCONV;
64  FILE *fpout = NULL;
65  FILE *fpfilterout = NULL;
66  pid_t filterpid = -1;
67  int res;
68
69  snprintf (buf, sizeof (buf), "%s/%s", TYPE (cur->content),
70            cur->content->subtype);
71
72  mutt_parse_mime_message (Context, cur);
73  mutt_message_hook (Context, cur, M_MESSAGEHOOK);
74
75  /* see if crytpo is needed for this message.  if so, we should exit curses */
76  if (WithCrypto && cur->security)
77  {
78    if (cur->security & ENCRYPT)
79    {
80      if (cur->security & APPLICATION_SMIME)
81        crypt_smime_getkeys (cur->env);
82      if(!crypt_valid_passphrase(cur->security))
83        return 0;
84
85      cmflags |= M_CM_VERIFY;
86    }
87    else if (cur->security & SIGN)
88    {
89      /* find out whether or not the verify signature */
90      if (query_quadoption (OPT_VERIFYSIG, _("Verify PGP signature?")) == M_YES)
91      {
92        cmflags |= M_CM_VERIFY;
93      }
94    }
95  }
96 
97  if (cmflags & M_CM_VERIFY || cur->security & ENCRYPT)
98  {
99    if (cur->security & APPLICATION_PGP)
100    {
101      if (cur->env->from)
102        crypt_pgp_invoke_getkeys (cur->env->from);
103     
104      crypt_invoke_message (APPLICATION_PGP);
105    }
106
107    if (cur->security & APPLICATION_SMIME)
108      crypt_invoke_message (APPLICATION_SMIME);
109  }
110
111
112  mutt_mktemp (tempfile);
113  if ((fpout = safe_fopen (tempfile, "w")) == NULL)
114  {
115    mutt_error _("Could not create temporary file!");
116    return (0);
117  }
118
119  if (DisplayFilter && *DisplayFilter) 
120  {
121    fpfilterout = fpout;
122    fpout = NULL;
123    /* mutt_endwin (NULL); */
124    filterpid = mutt_create_filter_fd (DisplayFilter, &fpout, NULL, NULL,
125                                       -1, fileno(fpfilterout), -1);
126    if (filterpid < 0)
127    {
128      mutt_error (_("Cannot create display filter"));
129      safe_fclose (&fpfilterout);
130      unlink (tempfile);
131      return 0;
132    }
133  }
134
135  if (!Pager || mutt_strcmp (Pager, "builtin") == 0)
136    builtin = 1;
137  else
138  {
139    struct hdr_format_info hfi;
140    hfi.ctx = Context;
141    hfi.pager_progress = ExtPagerProgress;
142    hfi.hdr = cur;
143    mutt_make_string_info (buf, sizeof (buf), NONULL(PagerFmt), &hfi, M_FORMAT_MAKEPRINT);
144    fputs (buf, fpout);
145    fputs ("\n\n", fpout);
146  }
147
148  res = mutt_copy_message (fpout, Context, cur, cmflags,
149        (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | CH_DECODE | CH_FROM);
150  if ((safe_fclose (&fpout) != 0 && errno != EPIPE) || res < 0)
151  {
152    mutt_error (_("Could not copy message"));
153    if (fpfilterout != NULL)
154    {
155      mutt_wait_filter (filterpid);
156      safe_fclose (&fpfilterout);
157    }
158    mutt_unlink (tempfile);
159    return 0;
160  }
161
162  if (fpfilterout != NULL && mutt_wait_filter (filterpid) != 0)
163    mutt_any_key_to_continue (NULL);
164
165  safe_fclose (&fpfilterout);   /* XXX - check result? */
166
167 
168  if (WithCrypto)
169  {
170    /* update crypto information for this message */
171    cur->security &= ~(GOODSIGN|BADSIGN);
172    cur->security |= crypt_query (cur->content);
173 
174    /* Remove color cache for this message, in case there
175       are color patterns for both ~g and ~V */
176    cur->pair = 0;
177  }
178
179  if (builtin)
180  {
181    pager_t info;
182
183    if (WithCrypto
184        && (cur->security & APPLICATION_SMIME) && (cmflags & M_CM_VERIFY))
185    {
186      if (cur->security & GOODSIGN)
187      {
188        if (!crypt_smime_verify_sender(cur))
189          mutt_message ( _("S/MIME signature successfully verified."));
190        else
191          mutt_error ( _("S/MIME certificate owner does not match sender."));
192      }
193      else if (cur->security & PARTSIGN)
194        mutt_message (_("Warning: Part of this message has not been signed."));
195      else if (cur->security & SIGN || cur->security & BADSIGN)
196        mutt_error ( _("S/MIME signature could NOT be verified."));
197    }
198
199    if (WithCrypto
200        && (cur->security & APPLICATION_PGP) && (cmflags & M_CM_VERIFY))
201    {
202      if (cur->security & GOODSIGN)
203        mutt_message (_("PGP signature successfully verified."));
204      else if (cur->security & PARTSIGN)
205        mutt_message (_("Warning: Part of this message has not been signed."));
206      else if (cur->security & SIGN)
207        mutt_message (_("PGP signature could NOT be verified."));
208    }
209
210    /* Invoke the builtin pager */
211    memset (&info, 0, sizeof (pager_t));
212    info.hdr = cur;
213    info.ctx = Context;
214    rc = mutt_pager (NULL, tempfile, M_PAGER_MESSAGE, &info);
215  }
216  else
217  {
218    int r;
219
220    mutt_endwin (NULL);
221    snprintf (buf, sizeof (buf), "%s %s", NONULL(Pager), tempfile);
222    if ((r = mutt_system (buf)) == -1)
223      mutt_error (_("Error running \"%s\"!"), buf);
224    unlink (tempfile);
225    keypad (stdscr, TRUE);
226    if (r != -1)
227      mutt_set_flag (Context, cur, M_READ, 1);
228    if (r != -1 && option (OPTPROMPTAFTER))
229    {
230      mutt_ungetch (mutt_any_key_to_continue _("Command: "), 0);
231      rc = km_dokey (MENU_PAGER);
232    }
233    else
234      rc = 0;
235  }
236
237  return rc;
238}
239
240void ci_bounce_message (HEADER *h, int *redraw)
241{
242  char prompt[SHORT_STRING];
243  char scratch[SHORT_STRING];
244  char buf[HUGE_STRING] = { 0 };
245  ADDRESS *adr = NULL;
246  char *err = NULL;
247  int rc;
248
249  if(h)
250    strfcpy(prompt, _("Bounce message to: "), sizeof(prompt));
251  else
252    strfcpy(prompt, _("Bounce tagged messages to: "), sizeof(prompt));
253 
254  rc = mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS);
255
256  if (option (OPTNEEDREDRAW))
257  {
258    unset_option (OPTNEEDREDRAW);
259    *redraw = REDRAW_FULL;
260  }
261
262  if (rc || !buf[0])
263    return;
264
265  if (!(adr = rfc822_parse_adrlist (adr, buf)))
266  {
267    mutt_error _("Error parsing address!");
268    return;
269  }
270
271  adr = mutt_expand_aliases (adr);
272
273  if (mutt_addrlist_to_idna (adr, &err) < 0)
274  {
275    mutt_error (_("Bad IDN: '%s'"), err);
276    FREE (&err);
277    rfc822_free_address (&adr);
278    return;
279  }
280
281  buf[0] = 0;
282  rfc822_write_address (buf, sizeof (buf), adr, 1);
283
284#define extra_space (15 + 7 + 2)
285  snprintf (scratch, sizeof (scratch),
286           (h ? _("Bounce message to %s") : _("Bounce messages to %s")), buf);
287
288  if (mutt_strwidth (prompt) > COLS - extra_space)
289  {
290    mutt_format_string (prompt, sizeof (prompt),
291                        0, COLS-extra_space, FMT_LEFT, 0,
292                        scratch, sizeof (scratch), 0);
293    safe_strcat (prompt, sizeof (prompt), "...?");
294  }
295  else
296    snprintf (prompt, sizeof (prompt), "%s?", scratch);
297
298  if (query_quadoption (OPT_BOUNCE, prompt) != M_YES)
299  {
300    rfc822_free_address (&adr);
301    CLEARLINE (LINES - 1);
302    mutt_message (h ? _("Message not bounced.") : _("Messages not bounced."));
303    return;
304  }
305
306  CLEARLINE (LINES - 1);
307 
308  rc = mutt_bounce_message (NULL, h, adr);
309  rfc822_free_address (&adr);
310  /* If no error, or background, display message. */
311  if ((rc == 0) || (rc == S_BKG))
312    mutt_message (h ? _("Message bounced.") : _("Messages bounced."));
313}
314
315static void pipe_set_flags (int decode, int print, int *cmflags, int *chflags)
316{
317  if (decode)
318  {
319    *cmflags |= M_CM_DECODE | M_CM_CHARCONV;
320    *chflags |= CH_DECODE | CH_REORDER;
321   
322    if (option (OPTWEED))
323    {
324      *chflags |= CH_WEED;
325      *cmflags |= M_CM_WEED;
326    }
327  }
328 
329  if (print)
330    *cmflags |= M_CM_PRINTING;
331 
332}
333
334static void pipe_msg (HEADER *h, FILE *fp, int decode, int print)
335{
336  int cmflags = 0;
337  int chflags = CH_FROM;
338 
339  pipe_set_flags (decode, print, &cmflags, &chflags);
340
341  if (WithCrypto && decode && h->security & ENCRYPT)
342  {
343    if(!crypt_valid_passphrase(h->security))
344      return;
345    endwin ();
346  }
347
348  if (decode)
349    mutt_parse_mime_message (Context, h);
350
351  mutt_copy_message (fp, Context, h, cmflags, chflags);
352}
353
354
355/* the following code is shared between printing and piping */
356
357static int _mutt_pipe_message (HEADER *h, char *cmd,
358                               int decode,
359                               int print,
360                               int split,
361                               char *sep)
362{
363 
364  int i, rc = 0;
365  pid_t thepid;
366  FILE *fpout;
367 
368/*   mutt_endwin (NULL);
369
370     is this really needed here ?
371     it makes the screen flicker on pgp and s/mime messages,
372     before asking for a passphrase...
373                                     Oliver Ehli */
374  if (h)
375  {
376
377    mutt_message_hook (Context, h, M_MESSAGEHOOK);
378
379    if (WithCrypto && decode)
380    {
381      mutt_parse_mime_message (Context, h);
382      if(h->security & ENCRYPT && !crypt_valid_passphrase(h->security))
383        return 1;
384    }
385    mutt_endwin (NULL);
386
387    if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0)
388    {
389      mutt_perror _("Can't create filter process");
390      return 1;
391    }
392     
393    pipe_msg (h, fpout, decode, print);
394    fclose (fpout);
395    rc = mutt_wait_filter (thepid);
396  }
397  else
398  { /* handle tagged messages */
399
400    if (WithCrypto && decode)
401    {
402      for (i = 0; i < Context->vcount; i++)
403        if(Context->hdrs[Context->v2r[i]]->tagged)
404        {
405          mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], M_MESSAGEHOOK);
406          mutt_parse_mime_message(Context, Context->hdrs[Context->v2r[i]]);
407          if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT &&
408              !crypt_valid_passphrase(Context->hdrs[Context->v2r[i]]->security))
409            return 1;
410        }
411    }
412   
413    if (split)
414    {
415      for (i = 0; i < Context->vcount; i++)
416      {
417        if (Context->hdrs[Context->v2r[i]]->tagged)
418        {
419          mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], M_MESSAGEHOOK);
420          mutt_endwin (NULL);
421          if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0)
422          {
423            mutt_perror _("Can't create filter process");
424            return 1;
425          }
426          pipe_msg (Context->hdrs[Context->v2r[i]], fpout, decode, print);
427          /* add the message separator */
428          if (sep)  fputs (sep, fpout);
429          safe_fclose (&fpout);
430          if (mutt_wait_filter (thepid) != 0)
431            rc = 1;
432        }
433      }
434    }
435    else
436    {
437      mutt_endwin (NULL);
438      if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0)
439      {
440        mutt_perror _("Can't create filter process");
441        return 1;
442      }
443      for (i = 0; i < Context->vcount; i++)
444      {
445        if (Context->hdrs[Context->v2r[i]]->tagged)
446        {
447          mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], M_MESSAGEHOOK);
448          pipe_msg (Context->hdrs[Context->v2r[i]], fpout, decode, print);
449          /* add the message separator */
450          if (sep) fputs (sep, fpout);
451        }
452      }
453      safe_fclose (&fpout);
454      if (mutt_wait_filter (thepid) != 0)
455        rc = 1;
456    }
457  }
458
459  if (rc || option (OPTWAITKEY))
460    mutt_any_key_to_continue (NULL);
461  return rc;
462}
463
464void mutt_pipe_message (HEADER *h)
465{
466  char buffer[LONG_STRING];
467
468  buffer[0] = 0;
469  if (mutt_get_field (_("Pipe to command: "), buffer, sizeof (buffer), M_CMD)
470      != 0 || !buffer[0])
471    return;
472
473  mutt_expand_path (buffer, sizeof (buffer));
474  _mutt_pipe_message (h, buffer,
475                      option (OPTPIPEDECODE),
476                      0, 
477                      option (OPTPIPESPLIT),
478                      PipeSep);
479}
480
481void mutt_print_message (HEADER *h)
482{
483
484  if (quadoption (OPT_PRINT) && (!PrintCmd || !*PrintCmd))
485  {
486    mutt_message (_("No printing command has been defined."));
487    return;
488  }
489 
490  if (query_quadoption (OPT_PRINT,
491                        h ? _("Print message?") : _("Print tagged messages?"))
492                        != M_YES)
493    return;
494
495  if (_mutt_pipe_message (h, PrintCmd,
496                          option (OPTPRINTDECODE),
497                          1,
498                          option (OPTPRINTSPLIT),
499                          "\f") == 0)
500    mutt_message (h ? _("Message printed") : _("Messages printed"));
501  else
502    mutt_message (h ? _("Message could not be printed") :
503                  _("Messages could not be printed"));
504}
505
506
507int mutt_select_sort (int reverse)
508{
509  int method = Sort; /* save the current method in case of abort */
510
511  switch (mutt_multi_choice (reverse ?
512                             _("Rev-Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: ") :
513                             _("Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: "),
514                             _("dfrsotuzcp")))
515  {
516  case -1: /* abort - don't resort */
517    return -1;
518
519  case 1: /* (d)ate */
520    Sort = SORT_DATE;
521    break;
522
523  case 2: /* (f)rm */
524    Sort = SORT_FROM;
525    break;
526 
527  case 3: /* (r)ecv */
528    Sort = SORT_RECEIVED;
529    break;
530 
531  case 4: /* (s)ubj */
532    Sort = SORT_SUBJECT;
533    break;
534 
535  case 5: /* t(o) */
536    Sort = SORT_TO;
537    break;
538 
539  case 6: /* (t)hread */
540    Sort = SORT_THREADS;
541    break;
542 
543  case 7: /* (u)nsort */
544    Sort = SORT_ORDER;
545    break;
546 
547  case 8: /* si(z)e */
548    Sort = SORT_SIZE;
549    break;
550 
551  case 9: /* s(c)ore */ 
552    Sort = SORT_SCORE;
553    break;
554
555  case 10: /* s(p)am */
556    Sort = SORT_SPAM;
557    break;
558  }
559  if (reverse)
560    Sort |= SORT_REVERSE;
561
562  return (Sort != method ? 0 : -1); /* no need to resort if it's the same */
563}
564
565/* invoke a command in a subshell */
566void mutt_shell_escape (void)
567{
568  char buf[LONG_STRING];
569
570  buf[0] = 0;
571  if (mutt_get_field (_("Shell command: "), buf, sizeof (buf), M_CMD) == 0)
572  {
573    if (!buf[0] && Shell)
574      strfcpy (buf, Shell, sizeof (buf));
575    if(buf[0])
576    {
577      CLEARLINE (LINES-1);
578      mutt_endwin (NULL);
579      fflush (stdout);
580      if (mutt_system (buf) != 0 || option (OPTWAITKEY))
581        mutt_any_key_to_continue (NULL);
582    }
583  }
584}
585
586/* enter a mutt command */
587void mutt_enter_command (void)
588{
589  BUFFER err, token;
590  char buffer[LONG_STRING], errbuf[LONG_STRING];
591  int r;
592
593  buffer[0] = 0;
594  if (mutt_get_field (":", buffer, sizeof (buffer), M_COMMAND) != 0 || !buffer[0])
595    return;
596  err.data = errbuf;
597  err.dsize = sizeof (errbuf);
598  memset (&token, 0, sizeof (token));
599  r = mutt_parse_rc_line (buffer, &token, &err);
600  FREE (&token.data);
601  if (errbuf[0])
602  {
603    /* since errbuf could potentially contain printf() sequences in it,
604       we must call mutt_error() in this fashion so that vsprintf()
605       doesn't expect more arguments that we passed */
606    if (r == 0)
607      mutt_message ("%s", errbuf);
608    else
609      mutt_error ("%s", errbuf);
610  }
611}
612
613void mutt_display_address (ENVELOPE *env)
614{
615  char *pfx = NULL;
616  char buf[SHORT_STRING];
617  ADDRESS *adr = NULL;
618
619  adr = mutt_get_address (env, &pfx);
620
621  if (!adr) return;
622 
623  /*
624   * Note: We don't convert IDNA to local representation this time.
625   * That is intentional, so the user has an opportunity to copy &
626   * paste the on-the-wire form of the address to other, IDN-unable
627   * software.
628   */
629 
630  buf[0] = 0;
631  rfc822_write_address (buf, sizeof (buf), adr, 0);
632  mutt_message ("%s: %s", pfx, buf);
633}
634
635static void set_copy_flags (HEADER *hdr, int decode, int decrypt, int *cmflags, int *chflags)
636{
637  *cmflags = 0;
638  *chflags = CH_UPDATE_LEN;
639 
640  if (WithCrypto && !decode && decrypt && (hdr->security & ENCRYPT))
641  {
642    if ((WithCrypto & APPLICATION_PGP)
643        && mutt_is_multipart_encrypted(hdr->content))
644    {
645      *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME;
646      *cmflags = M_CM_DECODE_PGP;
647    }
648    else if ((WithCrypto & APPLICATION_PGP)
649              && mutt_is_application_pgp (hdr->content) & ENCRYPT)
650      decode = 1;
651    else if ((WithCrypto & APPLICATION_SMIME)
652             && mutt_is_application_smime(hdr->content) & ENCRYPT)
653    {
654      *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME;
655      *cmflags = M_CM_DECODE_SMIME;
656    }
657  }
658
659  if (decode)
660  {
661    *chflags = CH_XMIT | CH_MIME | CH_TXTPLAIN;
662    *cmflags = M_CM_DECODE | M_CM_CHARCONV;
663
664    if (!decrypt)       /* If decode doesn't kick in for decrypt, */
665    {
666      *chflags |= CH_DECODE;    /* then decode RFC 2047 headers, */
667
668      if (option (OPTWEED))
669      {
670        *chflags |= CH_WEED;    /* and respect $weed. */
671        *cmflags |= M_CM_WEED;
672      }
673    }
674  }
675}
676
677int _mutt_save_message (HEADER *h, CONTEXT *ctx, int delete, int decode, int decrypt)
678{
679  int cmflags, chflags;
680  int rc;
681 
682  set_copy_flags (h, decode, decrypt, &cmflags, &chflags);
683
684  if (decode || decrypt)
685    mutt_parse_mime_message (Context, h);
686
687  if ((rc = mutt_append_message (ctx, Context, h, cmflags, chflags)) != 0)
688    return rc;
689
690  if (delete)
691  {
692    mutt_set_flag (Context, h, M_DELETE, 1);
693    if (option (OPTDELETEUNTAG))
694      mutt_set_flag (Context, h, M_TAG, 0);
695  }
696 
697  return 0;
698}
699
700/* returns 0 if the copy/save was successful, or -1 on error/abort */
701int mutt_save_message (HEADER *h, int delete, 
702                       int decode, int decrypt, int *redraw)
703{
704  int i, need_buffy_cleanup;
705  int need_passphrase = 0, app=0;
706  char prompt[SHORT_STRING], buf[_POSIX_PATH_MAX];
707  CONTEXT ctx;
708  struct stat st;
709  BUFFY *tmp = NULL;
710  struct utimbuf ut;
711
712  *redraw = 0;
713
714 
715  snprintf (prompt, sizeof (prompt),
716            decode  ? (delete ? _("Decode-save%s to mailbox") :
717                       _("Decode-copy%s to mailbox")) :
718            (decrypt ? (delete ? _("Decrypt-save%s to mailbox") :
719                        _("Decrypt-copy%s to mailbox")) :
720             (delete ? _("Save%s to mailbox") : _("Copy%s to mailbox"))),
721            h ? "" : _(" tagged"));
722 
723