Example #1
0
static int
check_attachments(ATTACHPTR **idx, short idxlen)
{
  int i, r;
  struct stat st;
  char pretty[_POSIX_PATH_MAX], msg[_POSIX_PATH_MAX + SHORT_STRING];

  for (i = 0; i < idxlen; i++)
  {
    strfcpy(pretty, idx[i]->content->filename, sizeof(pretty));
    if(stat(idx[i]->content->filename, &st) != 0)
    {
      mutt_pretty_mailbox(pretty, sizeof (pretty));
      mutt_error(_("%s [#%d] no longer exists!"),
		 pretty, i+1);
      return -1;
    }
    
    if(idx[i]->content->stamp < st.st_mtime)
    {
      mutt_pretty_mailbox(pretty, sizeof (pretty));
      snprintf(msg, sizeof(msg), _("%s [#%d] modified. Update encoding?"),
	       pretty, i+1);
      
      if((r = mutt_yesorno(msg, M_YES)) == M_YES)
	mutt_update_encoding(idx[i]->content);
      else if(r == -1)
	return -1;
    }
  }

  return 0;
}
Example #2
0
void mutt_select_fcc (char *path, size_t pathlen, HEADER *hdr)
{
  ADDRESS *adr;
  char buf[_POSIX_PATH_MAX];
  ENVELOPE *env = hdr->env;

  if (mutt_addr_hook (path, pathlen, M_FCCHOOK, NULL, hdr) != 0)
  {
    if ((option (OPTSAVENAME) || option (OPTFORCENAME)) &&
	(env->to || env->cc || env->bcc))
    {
      adr = env->to ? env->to : (env->cc ? env->cc : env->bcc);
      mutt_safe_path (buf, sizeof (buf), adr);
      mutt_concat_path (path, NONULL(Maildir), buf, pathlen);
      if (!option (OPTFORCENAME) && mx_access (path, W_OK) != 0)
	strfcpy (path, NONULL (Outbox), pathlen);
    }
    else
      strfcpy (path, NONULL (Outbox), pathlen);
  }
  mutt_pretty_mailbox (path, pathlen);
}
Example #3
0
/* %b = number of incoming folders with unread messages [option]
 * %d = number of deleted messages [option]
 * %f = full mailbox path
 * %F = number of flagged messages [option]
 * %h = hostname
 * %l = length of mailbox (in bytes) [option]
 * %m = total number of messages [option]
 * %M = number of messages shown (virtual message count when limiting) [option]
 * %n = number of new messages [option]
 * %o = number of old unread messages [option]
 * %p = number of postponed messages [option]
 * %P = percent of way through index
 * %r = readonly/wontwrite/changed flag
 * %s = current sorting method ($sort)
 * %S = current aux sorting method ($sort_aux)
 * %t = # of tagged messages [option]
 * %u = number of unread messages [option]
 * %v = Mutt version
 * %V = currently active limit pattern [option] */
static const char *
status_format_str(char          *buf,
                  size_t        buflen,
                  size_t        col,
                  char          op,
                  const char    *src,
                  const char    *prefix,
                  const char    *ifstring,
                  const char    *elsestring,
                  unsigned long data,
                  format_flag   flags)
{
    char fmt[SHORT_STRING], tmp[SHORT_STRING], *cp;
    int count, optional = (flags & M_FORMAT_OPTIONAL);
    MUTTMENU *menu = (MUTTMENU *)data;

    *buf = 0;

    switch (op) {
    case 'b':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen,      fmt,     mutt_buffy_check(0));
        } else if (!mutt_buffy_check(0))
            optional = 0;
        break;

    case 'd':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen, fmt, Context ? Context->deleted : 0);
        } else if (!Context || !Context->deleted)
            optional = 0;
        break;

    case 'f':
        snprintf(fmt, sizeof(fmt), "%%%ss", prefix);

        if (Context
            && Context->path) {
            strfcpy(tmp, Context->path, sizeof(tmp));
            mutt_pretty_mailbox(tmp, sizeof(tmp));
        } else
            strfcpy(tmp, _("(no mailbox)"), sizeof(tmp));
        snprintf(buf, buflen, fmt, tmp);
        break;

    case 'F':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen,      fmt,     Context ? Context->flagged : 0);
        } else if (!Context || !Context->flagged)
            optional = 0;
        break;

    case 'h':
        snprintf(fmt, sizeof(fmt), "%%%ss", prefix);
        snprintf(buf, buflen,      fmt,     NONULL(Hostname));
        break;

    case 'l':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%ss", prefix);
            mutt_pretty_size(tmp, sizeof(tmp), Context ? Context->size : 0);
            snprintf(buf, buflen, fmt, tmp);
        } else if (!Context || !Context->size)
            optional = 0;
        break;

    case 'L':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%ss", prefix);
            mutt_pretty_size(tmp, sizeof(tmp), Context ? Context->vsize : 0);
            snprintf(buf, buflen, fmt, tmp);
        } else if (!Context || !Context->pattern)
            optional = 0;
        break;

    case 'm':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen,      fmt,
                     Context ? Context->msgcount : 0);
        } else if (!Context || !Context->msgcount)
            optional = 0;
        break;

    case 'M':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen,      fmt,     Context ? Context->vcount : 0);
        } else if (!Context || !Context->pattern)
            optional = 0;
        break;

    case 'n':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen, fmt, Context ? Context->new_messages : 0);
        } else if (!Context || !Context->new_messages)
            optional = 0;
        break;

    case 'o':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf,
                     buflen,
                     fmt,
                     Context ? Context->unread - Context->new_messages : -1);
        } else if (!Context || !(Context->unread - Context->new_messages))
            optional = 0;
        break;

    case 'p':
        count = mutt_num_postponed(0);

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen,      fmt,     count);
        } else if (!count)
            optional = 0;
        break;

    case 'P':

        if (!menu)
            break;

        if (menu->top + menu->pagelen >= menu->max)
            cp = (char *) (menu->top ? (char *)"end" : (char *)"all");
        else {
            count = (100 * (menu->top + menu->pagelen)) / menu->max;
            snprintf(tmp, sizeof(tmp), "%d%%", count);
            cp = tmp;
        }
        snprintf(fmt, sizeof(fmt), "%%%ss", prefix);
        snprintf(buf, buflen,      fmt,     cp);
        break;

    case 'r':
    {
        size_t i = 0;

        if (Context) {
            i = globals.has_option(OPTATTACHMSG) ? 3 : ((Context->readonly
                                             || Context->dontwrite) ? 2 : (
                                                Context->changed
                                                ||/* deleted doesn't necessarily
                                                     mean changed in IMAP */
                                                (
                                                    Context->magic != M_IMAP
                                                    &&
                                                    Context->deleted)) ? 1 : 0);
        }

        if (!StChars)
            buf[0] = 0;
        else if (i >= mutt_strlen(StChars))
            buf[0] = StChars[0];
        else
            buf[0] = StChars[i];

        buf[1] = 0;
        break;
    }

    case 's':
        snprintf(fmt, sizeof(fmt), "%%%ss", prefix);
        snprintf(buf, buflen,      fmt,
                 get_sort_str(tmp, sizeof(tmp), Sort));
        break;

    case 'S':
        snprintf(fmt, sizeof(fmt), "%%%ss", prefix);
        snprintf(buf, buflen,      fmt,
                 get_sort_str(tmp, sizeof(tmp), SortAux));
        break;

    case 't':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen,      fmt,     Context ? Context->tagged : 0);
        } else if (!Context
                   || !Context->tagged)
            optional = 0;
        break;

    case 'u':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%sd", prefix);
            snprintf(buf, buflen, fmt, Context ? Context->unread : 0);
        } else if (!Context || !Context->unread)
            optional = 0;
        break;

    case 'v':
        snprintf(fmt, sizeof(fmt), "Mutt %%s");
        snprintf(buf, buflen,      fmt, MUTT_VERSION);
        break;

    case 'V':

        if (!optional) {
            snprintf(fmt, sizeof(fmt), "%%%ss", prefix);
            snprintf(buf,
                     buflen,
                     fmt,
                     (Context
                      && Context->pattern) ? Context->pattern : "");
        } else if (!Context
                   || !Context->pattern)
            optional = 0;
        break;

    case 0:
        *buf = 0;
        return src;

    default:
        snprintf(buf, buflen, "%%%s%c", prefix, op);
        break;
    }

    if (optional)
        _menu_status_line(buf, buflen, col, menu, ifstring);
    else if (flags & M_FORMAT_OPTIONAL)
        _menu_status_line(buf, buflen, col, menu, elsestring);

    return src;
}
Example #4
0
/* args:
 *      ctx	Context info, used when recalling a message to which
 *              we reply.
 *	hdr	envelope/attachment info for recalled message
 *	cur	if message was a reply, `cur' is set to the message which
 *		`hdr' is in reply to
 *	fcc	fcc for the recalled message
 *	fcclen	max length of fcc
 *
 * return vals:
 *	-1		error/no messages
 *	0		normal exit
 *	SENDREPLY	recalled message is a reply
 */
int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size_t fcclen)
{
  HEADER *h;
  int code = SENDPOSTPONED;
  LIST *tmp;
  LIST *last = NULL;
  LIST *next;
  const char *p;
  int opt_delete;

  if (!Postponed)
    return (-1);

  if ((PostContext = mx_open_mailbox (Postponed, M_NOSORT, NULL)) == NULL)
  {
    PostCount = 0;
    mutt_error _("No postponed messages.");
    return (-1);
  }

  if (! PostContext->msgcount)
  {
    PostCount = 0;
    mx_close_mailbox (PostContext, NULL);
    FREE (&PostContext);
    mutt_error _("No postponed messages.");
    return (-1);
  }

  if (PostContext->msgcount == 1)
  {
    /* only one message, so just use that one. */
    h = PostContext->hdrs[0];
  }
  else if ((h = select_msg ()) == NULL)
  {
    mx_close_mailbox (PostContext, NULL);
    FREE (&PostContext);
    return (-1);
  }

  if (mutt_prepare_template (NULL, PostContext, hdr, h, 0) < 0)
  {
    mx_fastclose_mailbox (PostContext);
    FREE (&PostContext);
    return (-1);
  }

  /* finished with this message, so delete it. */
  mutt_set_flag (PostContext, h, M_DELETE, 1);

  /* update the count for the status display */
  PostCount = PostContext->msgcount - PostContext->deleted;

  /* avoid the "purge deleted messages" prompt */
  opt_delete = quadoption (OPT_DELETE);
  set_quadoption (OPT_DELETE, M_YES);
  mx_close_mailbox (PostContext, NULL);
  set_quadoption (OPT_DELETE, opt_delete);

  FREE (&PostContext);

  for (tmp = hdr->env->userhdrs; tmp; )
  {
    if (ascii_strncasecmp ("X-Mutt-References:", tmp->data, 18) == 0)
    {
      if (ctx)
      {
	/* if a mailbox is currently open, look to see if the orignal message
	   the user attempted to reply to is in this mailbox */
	p = skip_email_wsp(tmp->data + 18);
	if (!ctx->id_hash)
	  ctx->id_hash = mutt_make_id_hash (ctx);
	*cur = hash_find (ctx->id_hash, p);
      }

      /* Remove the X-Mutt-References: header field. */
      next = tmp->next;
      if (last)
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
      if (*cur)
	code |= SENDREPLY;
    }
    else if (ascii_strncasecmp ("X-Mutt-Fcc:", tmp->data, 11) == 0)
    {
      p = skip_email_wsp(tmp->data + 11);
      strfcpy (fcc, p, fcclen);
      mutt_pretty_mailbox (fcc, fcclen);

      /* remove the X-Mutt-Fcc: header field */
      next = tmp->next;
      if (last)
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
     /* note that x-mutt-fcc was present.  we do this because we want to add a
      * default fcc if the header was missing, but preserve the request of the
      * user to not make a copy if the header field is present, but empty.
      * see http://dev.mutt.org/trac/ticket/3653
      */
      code |= SENDPOSTPONEDFCC;
    }
    else if ((WithCrypto & APPLICATION_PGP)
             && (mutt_strncmp ("Pgp:", tmp->data, 4) == 0 /* this is generated
						       * by old mutt versions
						       */
                 || mutt_strncmp ("X-Mutt-PGP:", tmp->data, 11) == 0))
    {
      hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1,
					    APPLICATION_PGP);
      hdr->security |= APPLICATION_PGP;

      /* remove the pgp field */
      next = tmp->next;
      if (last)
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }
    else if ((WithCrypto & APPLICATION_SMIME)
             && mutt_strncmp ("X-Mutt-SMIME:", tmp->data, 13) == 0)
    {
      hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1,
					    APPLICATION_SMIME);
      hdr->security |= APPLICATION_SMIME;

      /* remove the smime field */
      next = tmp->next;
      if (last)
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }

#ifdef MIXMASTER
    else if (mutt_strncmp ("X-Mutt-Mix:", tmp->data, 11) == 0)
    {
      char *t;
      mutt_free_list (&hdr->chain);

      t = strtok (tmp->data + 11, " \t\n");
      while (t)
      {
	hdr->chain = mutt_add_list (hdr->chain, t);
	t = strtok (NULL, " \t\n");
      }

      next = tmp->next;
      if (last)
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }
#endif

    else
    {
      last = tmp;
      tmp = tmp->next;
    }
  }

  if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
    crypt_opportunistic_encrypt (hdr);

  return (code);
}
Example #5
0
/* return values:
 *
 * 1	message should be postponed
 * 0	normal exit
 * -1	abort message
 */
int mutt_compose_menu (HEADER *msg,   /* structure for new message */
                       char *fcc,     /* where to save a copy of the message */
                       size_t fcclen,
                       HEADER *cur,   /* current message */
                       int flags)
{
  char helpstr[LONG_STRING];
  char buf[LONG_STRING];
  char fname[_POSIX_PATH_MAX];
  MUTTMENU *menu;
  ATTACHPTR **idx = NULL;
  short idxlen = 0;
  short idxmax = 0;
  int i, close = 0;
  int r = -1;		/* return value */
  int op = 0;
  int loop = 1;
  int fccSet = 0;	/* has the user edited the Fcc: field ? */
  CONTEXT *ctx = NULL, *this = NULL;
  /* Sort, SortAux could be changed in mutt_index_menu() */
  int oldSort, oldSortAux;
  struct stat st;

  mutt_attach_init (msg->content);
  idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);

  menu = mutt_new_menu (MENU_COMPOSE);
  menu->offset = HDR_ATTACH;
  menu->max = idxlen;
  menu->make_entry = snd_entry;
  menu->tag = mutt_tag_attach;
  menu->data = idx;
  menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
  
  while (loop)
  {
    switch (op = mutt_menuLoop (menu))
    {
      case OP_REDRAW:
	draw_envelope (msg, fcc);
	menu->offset = HDR_ATTACH;
	menu->pagelen = LINES - HDR_ATTACH - 2;
	break;
      case OP_COMPOSE_EDIT_FROM:
	menu->redraw = edit_address_list (HDR_FROM, &msg->env->from);
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
	break;
      case OP_COMPOSE_EDIT_TO:
	menu->redraw = edit_address_list (HDR_TO, &msg->env->to);
	if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
	{
	  crypt_opportunistic_encrypt (msg);
	  redraw_crypt_lines (msg);
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;
      case OP_COMPOSE_EDIT_BCC:
	menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc);
	if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
	{
	  crypt_opportunistic_encrypt (msg);
	  redraw_crypt_lines (msg);
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
	break;
      case OP_COMPOSE_EDIT_CC:
	menu->redraw = edit_address_list (HDR_CC, &msg->env->cc);
	if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
	{
	  crypt_opportunistic_encrypt (msg);
	  redraw_crypt_lines (msg);
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);	
        break;
      case OP_COMPOSE_EDIT_SUBJECT:
	if (msg->env->subject)
	  strfcpy (buf, msg->env->subject, sizeof (buf));
	else
	  buf[0] = 0;
	if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0)
	{
	  mutt_str_replace (&msg->env->subject, buf);
	  move (HDR_SUBJECT, HDR_XOFFSET);
	  if (msg->env->subject)
	    mutt_paddstr (W, msg->env->subject);
	  else
	    clrtoeol();
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;
      case OP_COMPOSE_EDIT_REPLY_TO:
	menu->redraw = edit_address_list (HDR_REPLYTO, &msg->env->reply_to);
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
	break;
      case OP_COMPOSE_EDIT_FCC:
	strfcpy (buf, fcc, sizeof (buf));
	if (mutt_get_field ("Fcc: ", buf, sizeof (buf), M_FILE | M_CLEAR) == 0)
	{
	  strfcpy (fcc, buf, fcclen);
	  mutt_pretty_mailbox (fcc, fcclen);
	  move (HDR_FCC, HDR_XOFFSET);
	  mutt_paddstr (W, fcc);
	  fccSet = 1;
	}
	MAYBE_REDRAW (menu->redraw);
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;
      case OP_COMPOSE_EDIT_MESSAGE:
	if (Editor && (mutt_strcmp ("builtin", Editor) != 0) && !option (OPTEDITHDRS))
	{
	  mutt_edit_file (Editor, msg->content->filename);
	  mutt_update_encoding (msg->content);
	  menu->redraw = REDRAW_FULL;
	  mutt_message_hook (NULL, msg, M_SEND2HOOK);
	  break;
	}
	/* fall through */
      case OP_COMPOSE_EDIT_HEADERS:
	if (mutt_strcmp ("builtin", Editor) != 0 &&
	    (op == OP_COMPOSE_EDIT_HEADERS ||
	    (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS))))
	{
	  char *tag = NULL, *err = NULL;
	  mutt_env_to_local (msg->env);
	  mutt_edit_headers (NONULL (Editor), msg->content->filename, msg,
			     fcc, fcclen);
	  if (mutt_env_to_intl (msg->env, &tag, &err))
	  {
	    mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
	    FREE (&err);
	  }
	  if (option (OPTCRYPTOPPORTUNISTICENCRYPT))
	    crypt_opportunistic_encrypt (msg);
	}
	else
	{
	  /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the
	     attachment list could change if the user invokes ~v to edit
	     the message with headers, in which we need to execute the
	     code below to regenerate the index array */
	  mutt_builtin_editor (msg->content->filename, msg, cur);
	}
	mutt_update_encoding (msg->content);

	/* attachments may have been added */
	if (idxlen && idx[idxlen - 1]->content->next)
	{
	  for (i = 0; i < idxlen; i++)
          {
            FREE (&idx[i]->tree);
	    FREE (&idx[i]);
          }
	  idxlen = 0;
	  idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);
	  menu->data = idx;
	  menu->max = idxlen;
	}

        menu->redraw = REDRAW_FULL;
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
	break;



      case OP_COMPOSE_ATTACH_KEY:
        if (!(WithCrypto & APPLICATION_PGP))
          break;       
	if (idxlen == idxmax)
        {
	  safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5));
	  menu->data = idx;
	}
	
	idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
	if ((idx[idxlen]->content = crypt_pgp_make_key_attachment(NULL)) != NULL)
	{
	  update_idx (menu, idx, idxlen++);
	  menu->redraw |= REDRAW_INDEX;
	}
	else
	  FREE (&idx[idxlen]);

	menu->redraw |= REDRAW_STATUS;

	if (option(OPTNEEDREDRAW))
	{
	  menu->redraw = REDRAW_FULL;
	  unset_option(OPTNEEDREDRAW);
	}
	
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;


      case OP_COMPOSE_ATTACH_FILE:
	{
	  char *prompt, **files;
	  int error, numfiles;

	  fname[0] = 0;
	  prompt = _("Attach file");
	  numfiles = 0;
	  files = NULL;

	  if (_mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 0, 1, &files, &numfiles) == -1 ||
	      *fname == '\0')
	    break;

	  if (idxlen + numfiles >= idxmax)
	  {
	    safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + numfiles));
	    menu->data = idx;
	  }

	  error = 0;
	  if (numfiles > 1)
	    mutt_message _("Attaching selected files...");
	  for (i = 0; i < numfiles; i++)
	  {
	    char *att = files[i];
	    idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
            idx[idxlen]->unowned = 1;
	    idx[idxlen]->content = mutt_make_file_attach (att);
	    if (idx[idxlen]->content != NULL)
	      update_idx (menu, idx, idxlen++);
	    else
	    {
	      error = 1;
	      mutt_error (_("Unable to attach %s!"), att);
	      FREE (&idx[idxlen]);
	    }
	  }
	  
	  FREE (&files);
	  if (!error) mutt_clear_error ();

	  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;

      case OP_COMPOSE_ATTACH_MESSAGE:
	{
	  char *prompt;
	  HEADER *h;

	  fname[0] = 0;
	  prompt = _("Open mailbox to attach message from");

	  if (Context)
	  {
	    strfcpy (fname, NONULL (Context->path), sizeof (fname));
	    mutt_pretty_mailbox (fname, sizeof (fname));
	  }

	  if (mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 1) == -1 || !fname[0])
	    break;

	  mutt_expand_path (fname, sizeof (fname));
#ifdef USE_IMAP
          if (!mx_is_imap (fname))
#endif
#ifdef USE_POP
          if (!mx_is_pop (fname))
#endif
	  /* check to make sure the file exists and is readable */
	  if (access (fname, R_OK) == -1)
	  {
	    mutt_perror (fname);
	    break;
	  }

	  menu->redraw = REDRAW_FULL;

	  ctx = mx_open_mailbox (fname, M_READONLY, NULL);
	  if (ctx == NULL)
	  {
	    mutt_error (_("Unable to open mailbox %s"), fname);
	    break;
	  }

	  if (!ctx->msgcount)
	  {
	    mx_close_mailbox (ctx, NULL);
	    FREE (&ctx);
	    mutt_error _("No messages in that folder.");
	    break;
	  }

	  this = Context; /* remember current folder and sort methods*/
	  oldSort = Sort; oldSortAux = SortAux;
	  
	  Context = ctx;
	  set_option(OPTATTACHMSG);
	  mutt_message _("Tag the messages you want to attach!");
	  close = mutt_index_menu ();
	  unset_option(OPTATTACHMSG);

	  if (!Context)
	  {
	    /* go back to the folder we started from */
	    Context = this;
	    /* Restore old $sort and $sort_aux */
	    Sort = oldSort;
	    SortAux = oldSortAux;
	    menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
	    break;
	  }

	  if (idxlen + Context->tagged >= idxmax)
	  {
	    safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + Context->tagged));
	    menu->data = idx;
	  }

	  for (i = 0; i < Context->msgcount; i++)
	  {
	    h = Context->hdrs[i];
	    if (h->tagged)
	    {
	      idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
	      idx[idxlen]->content = mutt_make_message_attach (Context, h, 1);
	      if (idx[idxlen]->content != NULL)
		update_idx (menu, idx, idxlen++);
	      else
	      {
		mutt_error _("Unable to attach!");
		FREE (&idx[idxlen]);
	      }
	    }
	  }
	  menu->redraw |= REDRAW_FULL;

	  if (close == OP_QUIT) 
	    mx_close_mailbox (Context, NULL);
	  else
	    mx_fastclose_mailbox (Context);
	  FREE (&Context);

	  /* go back to the folder we started from */
	  Context = this;
	  /* Restore old $sort and $sort_aux */
	  Sort = oldSort;
	  SortAux = oldSortAux;
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;

      case OP_DELETE:
	CHECK_COUNT;
        if (idx[menu->current]->unowned)
          idx[menu->current]->content->unlink = 0;
	if (delete_attachment (menu, &idxlen, menu->current) == -1)
	  break;
	mutt_update_tree (idx, idxlen);
	if (idxlen)
	{
	  if (menu->current > idxlen - 1)
	    menu->current = idxlen - 1;
	}
	else
	  menu->current = 0;

	if (menu->current == 0)
	  msg->content = idx[0]->content;

        menu->redraw |= REDRAW_STATUS;
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;

#define CURRENT idx[menu->current]->content
      
      case OP_COMPOSE_TOGGLE_RECODE:
      {      
        CHECK_COUNT;
        if (!mutt_is_text_part (CURRENT))
        {
	  mutt_error (_("Recoding only affects text attachments."));
	  break;
	}
        CURRENT->noconv = !CURRENT->noconv;
        if (CURRENT->noconv)
	  mutt_message (_("The current attachment won't be converted."));
        else
	  mutt_message (_("The current attachment will be converted."));
	menu->redraw = REDRAW_CURRENT;
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;
      }
#undef CURRENT

      case OP_COMPOSE_EDIT_DESCRIPTION:
	CHECK_COUNT;
	strfcpy (buf,
		 idx[menu->current]->content->description ?
		 idx[menu->current]->content->description : "",
		 sizeof (buf));
	/* header names should not be translated */
	if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0)
	{
	  mutt_str_replace (&idx[menu->current]->content->description, buf);
	  menu->redraw = REDRAW_CURRENT;
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;

      case OP_COMPOSE_UPDATE_ENCODING:
        CHECK_COUNT;
        if (menu->tagprefix)
        {
	  BODY *top;
	  for (top = msg->content; top; top = top->next)
	  {
	    if (top->tagged)
	      mutt_update_encoding (top);
	  }
	  menu->redraw = REDRAW_FULL;
	}
        else
        {
          mutt_update_encoding(idx[menu->current]->content);
	  menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;
      
      case OP_COMPOSE_TOGGLE_DISPOSITION:
	/* toggle the content-disposition between inline/attachment */
	idx[menu->current]->content->disposition = (idx[menu->current]->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE;
	menu->redraw = REDRAW_CURRENT;
	break;

      case OP_EDIT_TYPE:
	CHECK_COUNT;
        {
	  mutt_edit_content_type (NULL, idx[menu->current]->content, NULL);

	  /* this may have been a change to text/something */
	  mutt_update_encoding (idx[menu->current]->content);

	  menu->redraw = REDRAW_CURRENT;
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;

      case OP_COMPOSE_EDIT_ENCODING:
	CHECK_COUNT;
	strfcpy (buf, ENCODING (idx[menu->current]->content->encoding),
							      sizeof (buf));
	if (mutt_get_field ("Content-Transfer-Encoding: ", buf,
					    sizeof (buf), 0) == 0 && buf[0])
	{
	  if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED)
	  {
	    idx[menu->current]->content->encoding = i;
	    menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
	    mutt_clear_error();
	  }
	  else
	    mutt_error _("Invalid encoding.");
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;

      case OP_COMPOSE_SEND_MESSAGE:

        /* Note: We don't invoke send2-hook here, since we want to leave
	 * users an opportunity to change settings from the ":" prompt.
	 */
      
        if(check_attachments(idx, idxlen) != 0)
        {
	  menu->redraw = REDRAW_FULL;
	  break;
	}

      
#ifdef MIXMASTER
        if (msg->chain && mix_check_message (msg) != 0)
	  break;
#endif
      
	if (!fccSet && *fcc)
	{
	  if ((i = query_quadoption (OPT_COPY,
				_("Save a copy of this message?"))) == -1)
	    break;
	  else if (i == M_NO)
	    *fcc = 0;
	}

	loop = 0;
	r = 0;
	break;

      case OP_COMPOSE_EDIT_FILE:
	CHECK_COUNT;
	mutt_edit_file (NONULL(Editor), idx[menu->current]->content->filename);
	mutt_update_encoding (idx[menu->current]->content);
	menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
	break;

      case OP_COMPOSE_TOGGLE_UNLINK:
	CHECK_COUNT;
	idx[menu->current]->content->unlink = !idx[menu->current]->content->unlink;

#if 0
        /* OPTRESOLVE is otherwise ignored on this menu.
	 * Where's the bug?
	 */

        if (option (OPTRESOLVE) && menu->current + 1 < menu->max)
	  menu->current++;
# endif
	menu->redraw = REDRAW_INDEX;
        /* No send2hook since this doesn't change the message. */
	break;

      case OP_COMPOSE_GET_ATTACHMENT:
        CHECK_COUNT;
        if(menu->tagprefix)
        {
	  BODY *top;
	  for(top = msg->content; top; top = top->next)
	  {
	    if(top->tagged)
	      mutt_get_tmp_attachment(top);
	  }
	  menu->redraw = REDRAW_FULL;
	}
        else if (mutt_get_tmp_attachment(idx[menu->current]->content) == 0)
	  menu->redraw = REDRAW_CURRENT;

        /* No send2hook since this doesn't change the message. */
        break;
      
      case OP_COMPOSE_RENAME_FILE:
	CHECK_COUNT;
	strfcpy (fname, idx[menu->current]->content->filename, sizeof (fname));
	mutt_pretty_mailbox (fname, sizeof (fname));
	if (mutt_get_field (_("Rename to: "), fname, sizeof (fname), M_FILE)
							== 0 && fname[0])
	{
	  if (stat(idx[menu->current]->content->filename, &st) == -1)
	  {
            /* L10N:
               "stat" is a system call. Do "man 2 stat" for more information. */
	    mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
	    break;
	  }

	  mutt_expand_path (fname, sizeof (fname));
	  if(mutt_rename_file (idx[menu->current]->content->filename, fname))
	    break;
	  
	  mutt_str_replace (&idx[menu->current]->content->filename, fname);
	  menu->redraw = REDRAW_CURRENT;

	  if(idx[menu->current]->content->stamp >= st.st_mtime)
	    mutt_stamp_attachment(idx[menu->current]->content);
	  
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;

      case OP_COMPOSE_NEW_MIME:
	{
	  char type[STRING];
	  char *p;
	  int itype;
	  FILE *fp;

	  CLEARLINE (LINES-1);
	  fname[0] = 0;
	  if (mutt_get_field (_("New file: "), fname, sizeof (fname), M_FILE)
	      != 0 || !fname[0])
	    continue;
	  mutt_expand_path (fname, sizeof (fname));

	  /* Call to lookup_mime_type () ?  maybe later */
	  type[0] = 0;
	  if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0 
	      || !type[0])
	    continue;

	  if (!(p = strchr (type, '/')))
	  {
	    mutt_error _("Content-Type is of the form base/sub");
	    continue;
	  }
	  *p++ = 0;
	  if ((itype = mutt_check_mime_type (type)) == TYPEOTHER)
	  {
	    mutt_error (_("Unknown Content-Type %s"), type);
	    continue;
	  }
	  if (idxlen == idxmax)
	  {
	    safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5));
	    menu->data = idx;
	  }

	  idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
	  /* Touch the file */
	  if (!(fp = safe_fopen (fname, "w")))
	  {
	    mutt_error (_("Can't create file %s"), fname);
	    FREE (&idx[idxlen]);
	    continue;
	  }
	  safe_fclose (&fp);

	  if ((idx[idxlen]->content = mutt_make_file_attach (fname)) == NULL)
	  {
	    mutt_error _("What we have here is a failure to make an attachment");
	    continue;
	  }
	  update_idx (menu, idx, idxlen++);

	  idx[menu->current]->content->type = itype;
	  mutt_str_replace (&idx[menu->current]->content->subtype, p);
	  idx[menu->current]->content->unlink = 1;
	  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;

	  if (mutt_compose_attachment (idx[menu->current]->content))
	  {
	    mutt_update_encoding (idx[menu->current]->content);
	    menu->redraw = REDRAW_FULL;
	  }
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);    
        break;

      case OP_COMPOSE_EDIT_MIME:
	CHECK_COUNT;
	if (mutt_edit_attachment (idx[menu->current]->content))
	{
	  mutt_update_encoding (idx[menu->current]->content);
	  menu->redraw = REDRAW_FULL;
	}
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;

      case OP_VIEW_ATTACH:
      case OP_DISPLAY_HEADERS:
	CHECK_COUNT;
	mutt_attach_display_loop (menu, op, NULL, NULL, NULL, &idx, &idxlen, NULL, 0);
	menu->redraw = REDRAW_FULL;
        /* no send2hook, since this doesn't modify the message */
	break;

      case OP_SAVE:
	CHECK_COUNT;
	mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ?  msg->content : idx[menu->current]->content, NULL, menu);
	MAYBE_REDRAW (menu->redraw);
        /* no send2hook, since this doesn't modify the message */
	break;

      case OP_PRINT:
	CHECK_COUNT;
	mutt_print_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content);
        /* no send2hook, since this doesn't modify the message */
	break;

      case OP_PIPE:
      case OP_FILTER:
        CHECK_COUNT;
	mutt_pipe_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content, op == OP_FILTER);
	if (op == OP_FILTER) /* cte might have changed */
	  menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT;
        menu->redraw |= REDRAW_STATUS;
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
	break;

      case OP_EXIT:
	if ((i = query_quadoption (OPT_POSTPONE, _("Postpone this message?"))) == M_NO)
	{
          for (i = 0; i < idxlen; i++)
            if (idx[i]->unowned)
              idx[i]->content->unlink = 0;

          if (!(flags & M_COMPOSE_NOFREEHEADER))
          {
            while (idxlen-- > 0)
            {
              /* avoid freeing other attachments */
              idx[idxlen]->content->next = NULL;
              idx[idxlen]->content->parts = NULL;
              mutt_free_body (&idx[idxlen]->content);
              FREE (&idx[idxlen]->tree);
              FREE (&idx[idxlen]);
            }
            FREE (&idx);
            idxlen = 0;
            idxmax = 0;
          }
	  r = -1;
	  loop = 0;
	  break;
	}
	else if (i == -1)
	  break; /* abort */

	/* fall through to postpone! */

      case OP_COMPOSE_POSTPONE_MESSAGE:

        if(check_attachments(idx, idxlen) != 0)
        {
	  menu->redraw = REDRAW_FULL;
	  break;
	}
      
	loop = 0;
	r = 1;
	break;

      case OP_COMPOSE_ISPELL:
	endwin ();
	snprintf (buf, sizeof (buf), "%s -x %s", NONULL(Ispell), msg->content->filename);
	if (mutt_system (buf) == -1)
	  mutt_error (_("Error running \"%s\"!"), buf);
	else
        {
	  mutt_update_encoding (msg->content);
	  menu->redraw |= REDRAW_STATUS;
	}
	break;

      case OP_COMPOSE_WRITE_MESSAGE:

       fname[0] = '\0';
       if (Context)
       {
	 strfcpy (fname, NONULL (Context->path), sizeof (fname));
	 mutt_pretty_mailbox (fname, sizeof (fname));
       }
       if (idxlen)
         msg->content = idx[0]->content;
       if (mutt_enter_fname (_("Write message to mailbox"), fname, sizeof (fname),
                             &menu->redraw, 1) != -1 && fname[0])
       {
         mutt_message (_("Writing message to %s ..."), fname);
         mutt_expand_path (fname, sizeof (fname));

         if (msg->content->next)
           msg->content = mutt_make_multipart (msg->content);

         if (mutt_write_fcc (fname, msg, NULL, 0, NULL) < 0)
           msg->content = mutt_remove_multipart (msg->content);
         else
           mutt_message _("Message written.");
       }
       break;



      case OP_COMPOSE_PGP_MENU:
        if (!(WithCrypto & APPLICATION_PGP))
          break;
	if ((WithCrypto & APPLICATION_SMIME)
            && (msg->security & APPLICATION_SMIME))
	{
          if (msg->security & (ENCRYPT | SIGN))
          {
            if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "),
                              M_YES) != M_YES)
            {
              mutt_clear_error ();
              break;
            }
            msg->security &= ~(ENCRYPT | SIGN);
          }
	  msg->security &= ~APPLICATION_SMIME;
	  msg->security |= APPLICATION_PGP;
          crypt_opportunistic_encrypt (msg);
          redraw_crypt_lines (msg);
	}
	msg->security = crypt_pgp_send_menu (msg, &menu->redraw);
	redraw_crypt_lines (msg);
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;


      case OP_FORGET_PASSPHRASE:
	crypt_forget_passphrase ();
	break;


      case OP_COMPOSE_SMIME_MENU:
        if (!(WithCrypto & APPLICATION_SMIME))
          break;

	if ((WithCrypto & APPLICATION_PGP)
            && (msg->security & APPLICATION_PGP))
	{
          if (msg->security & (ENCRYPT | SIGN))
          {
            if (mutt_yesorno (_("PGP already selected. Clear & continue ? "),
                                M_YES) != M_YES)
            {
              mutt_clear_error ();
              break;
            }
            msg->security &= ~(ENCRYPT | SIGN);
          }
	  msg->security &= ~APPLICATION_PGP;
	  msg->security |= APPLICATION_SMIME;
          crypt_opportunistic_encrypt (msg);
          redraw_crypt_lines (msg);
	}
	msg->security = crypt_smime_send_menu(msg, &menu->redraw);
	redraw_crypt_lines (msg);
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;


#ifdef MIXMASTER
      case OP_COMPOSE_MIX:
      
      	mix_make_chain (&msg->chain, &menu->redraw);
        mutt_message_hook (NULL, msg, M_SEND2HOOK);
        break;
#endif

    }

    /* Draw formatted compose status line */
    if (menu->redraw & REDRAW_STATUS) 
    {
	compose_status_line (buf, sizeof (buf), 0, menu, NONULL(ComposeFormat));
	move(option (OPTSTATUSONTOP) ? 0 : LINES-2, 0);
	SETCOLOR (MT_COLOR_STATUS);
	mutt_paddstr (COLS, buf);
	NORMAL_COLOR;
	menu->redraw &= ~REDRAW_STATUS;
    }
  }

  mutt_menuDestroy (&menu);

  if (idxlen)
  {
    msg->content = idx[0]->content;
    for (i = 0; i < idxlen; i++)
    {
      idx[i]->content->aptr = NULL;
      FREE (&idx[i]->tree);
      FREE (&idx[i]);
    }
  }
  else
    msg->content = NULL;

  FREE (&idx);

  return (r);
}
Example #6
0
/* args:
 *      ctx	Context info, used when recalling a message to which
 *              we reply.
 *	hdr	envelope/attachment info for recalled message
 *	cur	if message was a reply, `cur' is set to the message which
 *		`hdr' is in reply to
 *	fcc	fcc for the recalled message
 *	fcclen	max length of fcc
 *
 * return vals:
 *	-1		error/no messages
 *	0		normal exit
 *	SENDREPLY	recalled message is a reply
 */
int mutt_get_postponed (CONTEXT * ctx, HEADER * hdr, HEADER ** cur, char *fcc,
                        size_t fcclen)
{
  HEADER *h;
  int code = SENDPOSTPONED;
  LIST *tmp;
  LIST *last = NULL;
  LIST *next;
  char *p;
  int opt_delete;

  if (!Postponed)
    return (-1);

  if ((PostContext = mx_open_mailbox (Postponed, M_NOSORT, NULL)) == NULL) {
    PostCount = 0;
    mutt_error _("No postponed messages.");

    return (-1);
  }

  if (!PostContext->msgcount) {
    PostCount = 0;
    mx_close_mailbox (PostContext, NULL);
    mem_free (&PostContext);
    mutt_error _("No postponed messages.");

    return (-1);
  }

  if (PostContext->msgcount == 1) {
    /* only one message, so just use that one. */
    h = PostContext->hdrs[0];
  }
  else if ((h = select_msg ()) == NULL) {
    mx_close_mailbox (PostContext, NULL);
    mem_free (&PostContext);
    return (-1);
  }

  if (mutt_prepare_template (NULL, PostContext, hdr, h, 0) < 0) {
    mx_fastclose_mailbox (PostContext);
    mem_free (&PostContext);
    return (-1);
  }

  /* finished with this message, so delete it. */
  mutt_set_flag (PostContext, h, M_DELETE, 1);

  /* and consider it saved, so that it won't be moved to the trash folder */
  mutt_set_flag (PostContext, h, M_APPENDED, 1);

  /* update the count for the status display */
  PostCount = PostContext->msgcount - PostContext->deleted;

  /* avoid the "purge deleted messages" prompt */
  opt_delete = quadoption (OPT_DELETE);
  set_quadoption (OPT_DELETE, M_YES);
  mx_close_mailbox (PostContext, NULL);
  set_quadoption (OPT_DELETE, opt_delete);

  mem_free (&PostContext);

  for (tmp = hdr->env->userhdrs; tmp;) {
    if (ascii_strncasecmp ("X-Mutt-References:", tmp->data, 18) == 0) {
      if (ctx) {
        /* if a mailbox is currently open, look to see if the orignal message
           the user attempted to reply to is in this mailbox */
        p = tmp->data + 18;
        SKIPWS (p);
        if (!ctx->id_hash)
          ctx->id_hash = mutt_make_id_hash (ctx);
        *cur = hash_find (ctx->id_hash, p);
      }

      /* Remove the X-Mutt-References: header field. */
      next = tmp->next;
      if (last)
        last->next = tmp->next;
      else
        hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
      if (*cur)
        code |= SENDREPLY;
    }
    else if (ascii_strncasecmp ("X-Mutt-Fcc:", tmp->data, 11) == 0) {
      p = tmp->data + 11;
      SKIPWS (p);
      strfcpy (fcc, p, fcclen);
      mutt_pretty_mailbox (fcc);

      /* remove the X-Mutt-Fcc: header field */
      next = tmp->next;
      if (last)
        last->next = tmp->next;
      else
        hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }
    else if ((WithCrypto & APPLICATION_PGP)
             && (str_ncmp ("Pgp:", tmp->data, 4) == 0       /* this is generated
                                                                 * by old mutt versions
                                                                 */
                 || str_ncmp ("X-Mutt-PGP:", tmp->data, 11) == 0)) {
      hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1);
      hdr->security |= APPLICATION_PGP;

      /* remove the pgp field */
      next = tmp->next;
      if (last)
        last->next = tmp->next;
      else
        hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }
    else if ((WithCrypto & APPLICATION_SMIME)
             && str_ncmp ("X-Mutt-SMIME:", tmp->data, 13) == 0) {
      hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1);
      hdr->security |= APPLICATION_SMIME;

      /* remove the smime field */
      next = tmp->next;
      if (last)
        last->next = tmp->next;
      else
        hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }

#ifdef MIXMASTER
    else if (str_ncmp ("X-Mutt-Mix:", tmp->data, 11) == 0) {
      char *t;

      mutt_free_list (&hdr->chain);

      t = strtok (tmp->data + 11, " \t\n");
      while (t) {
        hdr->chain = mutt_add_list (hdr->chain, t);
        t = strtok (NULL, " \t\n");
      }

      next = tmp->next;
      if (last)
        last->next = tmp->next;
      else
        hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }
#endif

    else {
      last = tmp;
      tmp = tmp->next;
    }
  }
  return (code);
}
void mutt_edit_headers (const char *editor,
			const char *body,
			HEADER *msg,
			char *fcc,
			size_t fcclen)
{
  char path[_POSIX_PATH_MAX];	/* tempfile used to edit headers + body */
  char buffer[LONG_STRING];
  char *p;
  FILE *ifp, *ofp;
  int i, keep;
  int in_reply_to = 0;	/* did we see the in-reply-to field ? */
  ENVELOPE *n;
  time_t mtime;
  struct stat st;
  LIST *cur, *last = NULL, *tmp;

  mutt_mktemp (path);
  if ((ofp = safe_fopen (path, "w")) == NULL)
  {
    mutt_perror (path);
    return;
  }

  mutt_write_rfc822_header (ofp, msg->env, NULL, 1, 0);
  fputc ('\n', ofp);	/* tie off the header. */

  /* now copy the body of the message. */
  if ((ifp = fopen (body, "r")) == NULL)
  {
    mutt_perror (body);
    return;
  }

  mutt_copy_stream (ifp, ofp);

  fclose (ifp);
  fclose (ofp);

  if (stat (path, &st) == -1)
  {
    mutt_perror (path);
    return;
  }

  mtime = st.st_mtime;
  mutt_edit_file (editor, path);
  stat (path, &st);
  if (mtime == st.st_mtime)
  {
    dprint (1, (debugfile, "ci_edit_headers(): temp file was not modified.\n"));
    /* the file has not changed! */
    mutt_unlink (path);
    return;
  }

  mutt_unlink (body);
  mutt_free_list (&msg->env->userhdrs);

  /* Read the temp file back in */
  if ((ifp = fopen (path, "r")) == NULL)
  {
    mutt_perror (path);
    return;
  }
  
  if ((ofp = safe_fopen (body, "w")) == NULL)
  {
    /* intentionally leak a possible temporary file here */
    fclose (ifp);
    mutt_perror (body);
    return;
  }
  
  n = mutt_read_rfc822_header (ifp, NULL, 1, 0);
  while ((i = fread (buffer, 1, sizeof (buffer), ifp)) > 0)
    fwrite (buffer, 1, i, ofp);
  fclose (ofp);
  fclose (ifp);
  mutt_unlink (path);

  /* restore old info. */
  n->references = msg->env->references;
  msg->env->references = NULL;
  mutt_free_envelope (&msg->env);
  msg->env = n;

  mutt_expand_aliases_env (msg->env);
  
  /* search through the user defined headers added to see if either a 
   * fcc: or attach-file: field was specified.  
   */

  cur = msg->env->userhdrs;
  while (cur)
  {
    keep = 1;

    /* keep track of whether or not we see the in-reply-to field.  if we did
     * not, remove the references: field later so that we can generate a new
     * message based upon this one.
     */
    if (mutt_strncasecmp ("in-reply-to. good thing you replied:", cur->data, 12) == 0)
      in_reply_to = 1;
    else if (fcc && mutt_strncasecmp ("fcc:", cur->data, 4) == 0)
    {
      p = cur->data + 4;
      SKIPWS (p);
      if (*p)
      {
	strfcpy (fcc, p, fcclen);
	mutt_pretty_mailbox (fcc);
      }
      keep = 0;
    }
    else if (mutt_strncasecmp ("attach file:", cur->data, 7) == 0)
    {
      BODY *body;
      BODY *parts;
      char *q;

      p = cur->data + 7;
      SKIPWS (p);
      if (*p)
      {
	if ((q = strpbrk (p, " \t")))
	{
	  mutt_substrcpy (path, p, q, sizeof (path));
	  SKIPWS (q);
	}
	else
	  strfcpy (path, p, sizeof (path));
	mutt_expand_path (path, sizeof (path));
	if ((body = mutt_make_file_attach (path)))
	{
	  body->description = safe_strdup (q);
	  for (parts = msg->content; parts->next; parts = parts->next) ;
	  parts->next = body;
	}
	else
	{
	  mutt_pretty_mailbox (path);
	  mutt_error (_("%s: unable to attach file. try again"), path);
	}
      }
      keep = 0;
    }



#ifdef HAVE_PGP
    else if (mutt_strncasecmp ("pgp:", cur->data, 4) == 0)
    {
      msg->pgp = mutt_parse_pgp_hdr (cur->data + 4, 0);
      keep = 0;
    }
#endif


    if (keep)
    {
      last = cur;
      cur = cur->next;
    }
    else
    {
      if (last)
	last->next = cur->next;
      else
	msg->env->userhdrs = cur->next;
      tmp = cur;
      cur = cur->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
    }
  }

  if (!in_reply_to)
    mutt_free_list (&msg->env->references);
}
Example #8
0
int
ci_send_message (int flags,		/* send mode */
		 HEADER *msg,		/* template to use for new message */
		 char *tempfile,	/* file specified by -i or -H */
		 CONTEXT *ctx,		/* current mailbox */
		 HEADER *cur)		/* current message */
{
  char buffer[LONG_STRING];
  char fcc[_POSIX_PATH_MAX] = ""; /* where to copy this message */
  FILE *tempfp = NULL;
  BODY *pbody;
  int i, killfrom = 0;
  int fcc_error = 0;
  int free_clear_content = 0;

  BODY *save_content = NULL;
  BODY *clear_content = NULL;
  char *pgpkeylist = NULL;
  /* save current value of "pgp_sign_as" */
  char *signas = NULL;
  char *tag = NULL, *err = NULL;
  char *ctype;

  int rv = -1;
  
  if (!flags && !msg && quadoption (OPT_RECALL) != M_NO &&
      mutt_num_postponed (1))
  {
    /* If the user is composing a new message, check to see if there
     * are any postponed messages first.
     */
    if ((i = query_quadoption (OPT_RECALL, _("Recall postponed message?"))) == -1)
      return rv;

    if(i == M_YES)
      flags |= SENDPOSTPONED;
  }
  
  
  if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED))
    signas = safe_strdup(PgpSignAs);

  /* Delay expansion of aliases until absolutely necessary--shouldn't
   * be necessary unless we are prompting the user or about to execute a
   * send-hook.
   */

  if (!msg)
  {
    msg = mutt_new_header ();

    if (flags == SENDPOSTPONED)
    {
      if ((flags = mutt_get_postponed (ctx, msg, &cur, fcc, sizeof (fcc))) < 0)
	goto cleanup;
    }

    if (flags & (SENDPOSTPONED|SENDRESEND))
    {
      if ((tempfp = safe_fopen (msg->content->filename, "a+")) == NULL)
      {
	mutt_perror (msg->content->filename);
	goto cleanup;
      }
    }

    if (!msg->env)
      msg->env = mutt_new_envelope ();
  }

  /* Parse and use an eventual list-post header */
  if ((flags & SENDLISTREPLY) 
      && cur && cur->env && cur->env->list_post) 
  {
    /* Use any list-post header as a template */
    url_parse_mailto (msg->env, NULL, cur->env->list_post);
    /* We don't let them set the sender's address. */
    rfc822_free_address (&msg->env->from);
  }
  
  if (! (flags & (SENDKEY | SENDPOSTPONED | SENDRESEND)))
  {
    pbody = mutt_new_body ();
    pbody->next = msg->content; /* don't kill command-line attachments */
    msg->content = pbody;

    if (!(ctype = safe_strdup (ContentType)))
      ctype = safe_strdup ("text/plain");
    mutt_parse_content_type (ctype, msg->content);
    FREE (&ctype);
    msg->content->unlink = 1;
    msg->content->use_disp = 0;
    msg->content->disposition = DISPINLINE;
    
    if (!tempfile)
    {
      mutt_mktemp (buffer, sizeof (buffer));
      tempfp = safe_fopen (buffer, "w+");
      msg->content->filename = safe_strdup (buffer);
    }
    else
    {
      tempfp = safe_fopen (tempfile, "a+");
      msg->content->filename = safe_strdup (tempfile);
    }

    if (!tempfp)
    {
      dprint(1,(debugfile, "newsend_message: can't create tempfile %s (errno=%d)\n", msg->content->filename, errno));
      mutt_perror (msg->content->filename);
      goto cleanup;
    }
  }

  /* this is handled here so that the user can match ~f in send-hook */
  if (cur && option (OPTREVNAME) && !(flags & (SENDPOSTPONED|SENDRESEND)))
  {
    /* we shouldn't have to worry about freeing `msg->env->from' before
     * setting it here since this code will only execute when doing some
     * sort of reply.  the pointer will only be set when using the -H command
     * line option.
     *
     * We shouldn't have to worry about alias expansion here since we are
     * either replying to a real or postponed message, therefore no aliases
     * should exist since the user has not had the opportunity to add
     * addresses to the list.  We just have to ensure the postponed messages
     * have their aliases expanded.
     */

    msg->env->from = set_reverse_name (cur->env);
  }

  if (! (flags & (SENDPOSTPONED|SENDRESEND)))
  {
    if ((flags & (SENDREPLY | SENDFORWARD)) && ctx &&
	envelope_defaults (msg->env, ctx, cur, flags) == -1)
      goto cleanup;

    if (option (OPTHDRS))
      process_user_recips (msg->env);

    /* Expand aliases and remove duplicates/crossrefs */
    mutt_expand_aliases_env (msg->env);
    
    if (flags & SENDREPLY)
      mutt_fix_reply_recipients (msg->env);

    if (! (flags & (SENDMAILX|SENDBATCH)) &&
	! (option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
	! ((flags & SENDREPLY) && option (OPTFASTREPLY)))
    {
      if (edit_envelope (msg->env) == -1)
	goto cleanup;
    }

    /* the from address must be set here regardless of whether or not
     * $use_from is set so that the `~P' (from you) operator in send-hook
     * patterns will work.  if $use_from is unset, the from address is killed
     * after send-hooks are evaulated */

    if (!msg->env->from)
    {
      msg->env->from = mutt_default_from ();
      killfrom = 1;
    }

    if ((flags & SENDREPLY) && cur)
    {
      /* change setting based upon message we are replying to */
      mutt_message_hook (ctx, cur, M_REPLYHOOK);

      /*
       * set the replied flag for the message we are generating so that the
       * user can use ~Q in a send-hook to know when reply-hook's are also
       * being used.
       */
      msg->replied = 1;
    }

    /* change settings based upon recipients */
    
    mutt_message_hook (NULL, msg, M_SENDHOOK);

    /*
     * Unset the replied flag from the message we are composing since it is
     * no longer required.  This is done here because the FCC'd copy of
     * this message was erroneously get the 'R'eplied flag when stored in
     * a maildir-style mailbox.
     */
    msg->replied = 0;

    if (! (flags & SENDKEY))
    {
      if (option (OPTTEXTFLOWED) && msg->content->type == TYPETEXT && !ascii_strcasecmp (msg->content->subtype, "plain"))
        mutt_set_parameter ("format", "flowed", &msg->content->parameter);
    }

    /* $use_from and/or $from might have changed in a send-hook */
    if (killfrom)
    {
      rfc822_free_address (&msg->env->from);
      if (option (OPTUSEFROM) && !(flags & (SENDPOSTPONED|SENDRESEND)))
	msg->env->from = mutt_default_from ();
      killfrom = 0;
    }

    if (option (OPTHDRS))
      process_user_header (msg->env);

    if (flags & SENDBATCH)
       mutt_copy_stream (stdin, tempfp);

    if (option (OPTSIGONTOP) && ! (flags & (SENDMAILX|SENDKEY|SENDBATCH))
	&& Editor && mutt_strcmp (Editor, "builtin") != 0)
      append_signature (tempfp);

    /* include replies/forwarded messages, unless we are given a template */
    if (!tempfile && (ctx || !(flags & (SENDREPLY|SENDFORWARD)))
	&& generate_body (tempfp, msg, flags, ctx, cur) == -1)
      goto cleanup;

    if (!option (OPTSIGONTOP) && ! (flags & (SENDMAILX|SENDKEY|SENDBATCH))
	&& Editor && mutt_strcmp (Editor, "builtin") != 0)
      append_signature (tempfp);
  }
  
  /* 
   * This hook is even called for postponed messages, and can, e.g., be
   * used for setting the editor, the sendmail path, or the
   * envelope sender.
   */
  mutt_message_hook (NULL, msg, M_SEND2HOOK);

  /* wait until now to set the real name portion of our return address so
     that $realname can be set in a send-hook */
  if (msg->env->from && !msg->env->from->personal
      && !(flags & (SENDRESEND|SENDPOSTPONED)))
    msg->env->from->personal = safe_strdup (Realname);

  if (!((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY)))
    safe_fclose (&tempfp);

  if (flags & SENDMAILX)
  {
    if (mutt_builtin_editor (msg->content->filename, msg, cur) == -1)
      goto cleanup;
  }
  else if (! (flags & SENDBATCH))
  {
    struct stat st;
    time_t mtime = mutt_decrease_mtime (msg->content->filename, NULL);

    mutt_update_encoding (msg->content);

    /*
     * Select whether or not the user's editor should be called now.  We
     * don't want to do this when:
     * 1) we are sending a key/cert
     * 2) we are forwarding a message and the user doesn't want to edit it.
     *    This is controlled by the quadoption $forward_edit.  However, if
     *    both $edit_headers and $autoedit are set, we want to ignore the
     *    setting of $forward_edit because the user probably needs to add the
     *    recipients.
     */
    if (! (flags & SENDKEY) &&
	((flags & SENDFORWARD) == 0 ||
	 (option (OPTEDITHDRS) && option (OPTAUTOEDIT)) ||
	 query_quadoption (OPT_FORWEDIT, _("Edit forwarded message?")) == M_YES))
    {
      /* If the this isn't a text message, look for a mailcap edit command */
      if (mutt_needs_mailcap (msg->content))
      {
	if (!mutt_edit_attachment (msg->content))
          goto cleanup;
      }
      else if (!Editor || mutt_strcmp ("builtin", Editor) == 0)
	mutt_builtin_editor (msg->content->filename, msg, cur);
      else if (option (OPTEDITHDRS))
      {
	mutt_env_to_local (msg->env);
	mutt_edit_headers (Editor, msg->content->filename, msg, fcc, sizeof (fcc));
	mutt_env_to_idna (msg->env, NULL, NULL);
      }
      else
      {
	mutt_edit_file (Editor, msg->content->filename);
	if (stat (msg->content->filename, &st) == 0)
	{
	  if (mtime != st.st_mtime)
	    fix_end_of_file (msg->content->filename);
	}
	else
	  mutt_perror (msg->content->filename);
      }
      
      /* If using format=flowed, perform space stuffing.  Avoid stuffing when
       * recalling a postponed message where the stuffing was already
       * performed.  If it has already been performed, the format=flowed
       * parameter will be present.
       */
      if (option (OPTTEXTFLOWED) && msg->content->type == TYPETEXT && !ascii_strcasecmp("plain", msg->content->subtype))
      {
	char *p = mutt_get_parameter("format", msg->content->parameter);
	if (ascii_strcasecmp("flowed", NONULL(p)))
	  rfc3676_space_stuff (msg);
      }

      mutt_message_hook (NULL, msg, M_SEND2HOOK);
    }

    if (! (flags & (SENDPOSTPONED | SENDFORWARD | SENDKEY | SENDRESEND)))
    {
      if (stat (msg->content->filename, &st) == 0)
      {
	/* if the file was not modified, bail out now */
	if (mtime == st.st_mtime && !msg->content->next &&
	    query_quadoption (OPT_ABORT, _("Abort unmodified message?")) == M_YES)
	{
	  mutt_message _("Aborted unmodified message.");
	  goto cleanup;
	}
      }
      else
	mutt_perror (msg->content->filename);
    }
  }

  /* 
   * Set the message security unless:
   * 1) crypto support is not enabled (WithCrypto==0)
   * 2) pgp: header field was present during message editing with $edit_headers (msg->security != 0)
   * 3) we are resending a message
   * 4) we are recalling a postponed message (don't override the user's saved settings)
   * 5) we are in mailx mode
   * 6) we are in batch mode
   *
   * This is done after allowing the user to edit the message so that security
   * settings can be configured with send2-hook and $edit_headers.
   */
  if (WithCrypto && (msg->security == 0) && !(flags & (SENDBATCH | SENDMAILX | SENDPOSTPONED | SENDRESEND)))
  {
    if (option (OPTCRYPTAUTOSIGN))
      msg->security |= SIGN;
    if (option (OPTCRYPTAUTOENCRYPT))
      msg->security |= ENCRYPT;
    if (option (OPTCRYPTREPLYENCRYPT) && cur && (cur->security & ENCRYPT))
      msg->security |= ENCRYPT;
    if (option (OPTCRYPTREPLYSIGN) && cur && (cur->security & SIGN))
      msg->security |= SIGN;
    if (option (OPTCRYPTREPLYSIGNENCRYPTED) && cur && (cur->security & ENCRYPT))
      msg->security |= SIGN;
    if (WithCrypto & APPLICATION_PGP && (msg->security & (ENCRYPT | SIGN)))
    {
      if (option (OPTPGPAUTOINLINE))
	msg->security |= INLINE;
      if (option (OPTPGPREPLYINLINE) && cur && (cur->security & INLINE))
	msg->security |= INLINE;
    }

    if (msg->security)
    {
      /* 
       * When replying / forwarding, use the original message's
       * crypto system.  According to the documentation,
       * smime_is_default should be disregarded here.
       * 
       * Problem: At least with forwarding, this doesn't really
       * make much sense. Should we have an option to completely
       * disable individual mechanisms at run-time?
       */
      if (cur)
      {
	if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP) 
	    && (cur->security & APPLICATION_PGP))
	  msg->security |= APPLICATION_PGP;
	else if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME)
	    && (cur->security & APPLICATION_SMIME))
	  msg->security |= APPLICATION_SMIME;
      }

      /*
       * No crypto mechanism selected? Use availability + smime_is_default
       * for the decision. 
       */
      if (!(msg->security & (APPLICATION_SMIME | APPLICATION_PGP)))
      {
	if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME) 
	    && option (OPTSMIMEISDEFAULT))
	  msg->security |= APPLICATION_SMIME;
	else if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP))
	  msg->security |= APPLICATION_PGP;
	else if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME))
	  msg->security |= APPLICATION_SMIME;
      }
    }

    /* No permissible mechanisms found.  Don't sign or encrypt. */
    if (!(msg->security & (APPLICATION_SMIME|APPLICATION_PGP)))
      msg->security = 0;
  }

  /* specify a default fcc.  if we are in batchmode, only save a copy of
   * the message if the value of $copy is yes or ask-yes */

  if (!fcc[0] && !(flags & (SENDPOSTPONED)) && (!(flags & SENDBATCH) || (quadoption (OPT_COPY) & 0x1)))
  {
    /* set the default FCC */
    if (!msg->env->from)
    {
      msg->env->from = mutt_default_from ();
      killfrom = 1; /* no need to check $use_from because if the user specified
		       a from address it would have already been set by now */
    }
    mutt_select_fcc (fcc, sizeof (fcc), msg);
    if (killfrom)
    {
      rfc822_free_address (&msg->env->from);
      killfrom = 0;
    }
  }

  
  mutt_update_encoding (msg->content);

  if (! (flags & (SENDMAILX | SENDBATCH)))
  {
main_loop:

    fcc_error = 0; /* reset value since we may have failed before */
    mutt_pretty_mailbox (fcc, sizeof (fcc));
    i = mutt_compose_menu (msg, fcc, sizeof (fcc), cur);
    if (i == -1)
    {
      /* abort */
      mutt_message _("Mail not sent.");
      goto cleanup;
    }
    else if (i == 1)
    {
      /* postpone the message until later. */
      if (msg->content->next)
	msg->content = mutt_make_multipart (msg->content);

      /*
       * make sure the message is written to the right part of a maildir 
       * postponed folder.
       */
      msg->read = 0; msg->old = 0;

      encode_descriptions (msg->content, 1);
      mutt_prepare_envelope (msg->env, 0);
      mutt_env_to_idna (msg->env, NULL, NULL);	/* Handle bad IDNAs the next time. */

      if (!Postponed || mutt_write_fcc (NONULL (Postponed), msg, (cur && (flags & SENDREPLY)) ? cur->env->message_id : NULL, 1, fcc) < 0)
      {
	msg->content = mutt_remove_multipart (msg->content);
	decode_descriptions (msg->content);
	mutt_unprepare_envelope (msg->env);
	goto main_loop;
      }
      mutt_update_num_postponed ();
      mutt_message _("Message postponed.");
      goto cleanup;
    }
  }

  if (!has_recips (msg->env->to) && !has_recips (msg->env->cc) &&
      !has_recips (msg->env->bcc))
  {
    if (! (flags & SENDBATCH))
    {
      mutt_error _("No recipients are specified!");
      goto main_loop;
    }
    else
    {
      puts _("No recipients were specified.");
      goto cleanup;
    }
  }

  if (mutt_env_to_idna (msg->env, &tag, &err))
  {
    mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
    FREE (&err);
    if (!(flags & SENDBATCH))
      goto main_loop;
    else 
      goto cleanup;
  }
  
  if (!msg->env->subject && ! (flags & SENDBATCH) &&
      (i = query_quadoption (OPT_SUBJECT, _("No subject, abort sending?"))) != M_NO)
  {
    /* if the abort is automatic, print an error message */
    if (quadoption (OPT_SUBJECT) == M_YES)
      mutt_error _("No subject specified.");
    goto main_loop;
  }

  if (msg->content->next)
    msg->content = mutt_make_multipart (msg->content);

  /* 
   * Ok, we need to do it this way instead of handling all fcc stuff in
   * one place in order to avoid going to main_loop with encoded "env"
   * in case of error.  Ugh.
   */

  encode_descriptions (msg->content, 1);
  
  /*
   * Make sure that clear_content and free_clear_content are
   * properly initialized -- we may visit this particular place in
   * the code multiple times, including after a failed call to
   * mutt_protect().
   */
  
  clear_content = NULL;
  free_clear_content = 0;
  
  if (WithCrypto)
  {
    if (msg->security)  
    {
      /* save the decrypted attachments */
      clear_content = msg->content;
  
      if ((crypt_get_keys (msg, &pgpkeylist) == -1) ||
          mutt_protect (msg, pgpkeylist) == -1)
      {
        msg->content = mutt_remove_multipart (msg->content);
        
	FREE (&pgpkeylist);
        
        decode_descriptions (msg->content);
        goto main_loop;
      }
      encode_descriptions (msg->content, 0);
    }
  
    /* 
     * at this point, msg->content is one of the following three things:
     * - multipart/signed.  In this case, clear_content is a child.
     * - multipart/encrypted.  In this case, clear_content exists
     *   independently
     * - application/pgp.  In this case, clear_content exists independently.
     * - something else.  In this case, it's the same as clear_content.
     */
  
    /* This is ugly -- lack of "reporting back" from mutt_protect(). */
    
    if (clear_content && (msg->content != clear_content)
        && (msg->content->parts != clear_content))
      free_clear_content = 1;
  }

  if (!option (OPTNOCURSES) && !(flags & SENDMAILX))
    mutt_message _("Sending message...");

  mutt_prepare_envelope (msg->env, 1);

  /* save a copy of the message, if necessary. */

  mutt_expand_path (fcc, sizeof (fcc));

  
  /* Don't save a copy when we are in batch-mode, and the FCC
   * folder is on an IMAP server: This would involve possibly lots
   * of user interaction, which is not available in batch mode. 
   * 
   * Note: A patch to fix the problems with the use of IMAP servers
   * from non-curses mode is available from Brendan Cully.  However, 
   * I'd like to think a bit more about this before including it.
   */

#ifdef USE_IMAP
  if ((flags & SENDBATCH) && fcc[0] && mx_is_imap (fcc))
    fcc[0] = '\0';
#endif

  if (*fcc && mutt_strcmp ("/dev/null", fcc) != 0)
  {
    BODY *tmpbody = msg->content;
    BODY *save_sig = NULL;
    BODY *save_parts = NULL;

    if (WithCrypto && msg->security && option (OPTFCCCLEAR))
      msg->content = clear_content;

    /* check to see if the user wants copies of all attachments */
    if (query_quadoption (OPT_FCCATTACH, _("Save attachments in Fcc?")) != M_YES &&
	msg->content->type == TYPEMULTIPART)
    {
      if (WithCrypto
          && (mutt_strcmp (msg->content->subtype, "encrypted") == 0 ||
              mutt_strcmp (msg->content->subtype, "signed") == 0))
      {
	if (clear_content->type == TYPEMULTIPART)
	{
	  if(!(msg->security & ENCRYPT) && (msg->security & SIGN))
	  {
	    /* save initial signature and attachments */
	    save_sig = msg->content->parts->next;
	    save_parts = clear_content->parts->next;
	  }

	  /* this means writing only the main part */
	  msg->content = clear_content->parts;

	  if (mutt_protect (msg, pgpkeylist) == -1)
	  {
	    /* we can't do much about it at this point, so
	     * fallback to saving the whole thing to fcc
	     */
	    msg->content = tmpbody;
	    save_sig = NULL;
	    goto full_fcc;
	  }

	  save_content = msg->content;
	}
      }
      else
	msg->content = msg->content->parts;
    }

full_fcc:
    if (msg->content)
    {
      /* update received time so that when storing to a mbox-style folder
       * the From_ line contains the current time instead of when the
       * message was first postponed.
       */
      msg->received = time (NULL);
      if (mutt_write_fcc (fcc, msg, NULL, 0, NULL) == -1)
      {
	/*
	 * Error writing FCC, we should abort sending.
	 */
	fcc_error = 1;
      }
    }

    msg->content = tmpbody;

    if (WithCrypto && save_sig)
    {
      /* cleanup the second signature structures */
      if (save_content->parts)
      {
	mutt_free_body (&save_content->parts->next);
	save_content->parts = NULL;
      }
      mutt_free_body (&save_content);

      /* restore old signature and attachments */
      msg->content->parts->next = save_sig;
      msg->content->parts->parts->next = save_parts;
    }
    else if (WithCrypto && save_content)
    {
      /* destroy the new encrypted body. */
      mutt_free_body (&save_content);
    }

  }


  /*
   * Don't attempt to send the message if the FCC failed.  Just pretend
   * the send failed as well so we give the user a chance to fix the
   * error.
   */
  if (fcc_error || (i = send_message (msg)) < 0)
  {
    if (!(flags & SENDBATCH))
    {
      if (!WithCrypto)
        ;
      else if ((msg->security & ENCRYPT) || 
               ((msg->security & SIGN)
                && msg->content->type == TYPEAPPLICATION))
      {
	mutt_free_body (&msg->content); /* destroy PGP data */
	msg->content = clear_content;	/* restore clear text. */
      }
      else if ((msg->security & SIGN) && msg->content->type == TYPEMULTIPART)
      {
	mutt_free_body (&msg->content->parts->next);	     /* destroy sig */
	msg->content = mutt_remove_multipart (msg->content); 
      }

      msg->content = mutt_remove_multipart (msg->content);
      decode_descriptions (msg->content);
      mutt_unprepare_envelope (msg->env);
      goto main_loop;
    }
    else
    {
      puts _("Could not send the message.");
      goto cleanup;
    }
  }
  else if (!option (OPTNOCURSES) && ! (flags & SENDMAILX))
    mutt_message (i == 0 ? _("Mail sent.") : _("Sending in background."));

  if (WithCrypto && (msg->security & ENCRYPT))
    FREE (&pgpkeylist);
  
  if (WithCrypto && free_clear_content)
    mutt_free_body (&clear_content);

  /* set 'replied' flag only if the user didn't change/remove
     In-Reply-To: and References: headers during edit */
  if (flags & SENDREPLY)
  {
    if (cur && ctx)
      mutt_set_flag (ctx, cur, M_REPLIED, is_reply (cur, msg));
    else if (!(flags & SENDPOSTPONED) && ctx && ctx->tagged)
    {
      for (i = 0; i < ctx->vcount; i++)
	if (ctx->hdrs[ctx->v2r[i]]->tagged)
	  mutt_set_flag (ctx, ctx->hdrs[ctx->v2r[i]], M_REPLIED,
			 is_reply (ctx->hdrs[ctx->v2r[i]], msg));
    }
  }


  rv = 0;
  
cleanup:

  if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED))
  {
    if(signas)
    {
      FREE (&PgpSignAs);
      PgpSignAs = signas;
    }
  }
   
  safe_fclose (&tempfp);
  mutt_free_header (&msg);
  
  return rv;
}
Example #9
0
File: enter.c Project: 0xAX/muttx
int _mutt_enter_string(char *buf, size_t buflen, int y, int x,
			int flags, int multiple, char ***files, int *numfiles,
			struct enter_state *state)
{
	int width = COLS - x - 1;
	int redraw;
	int pass =(flags & M_PASS);
	int first = 1;
	int ch, w, r;
	size_t i;
	wchar_t *tempbuf = 0;
	size_t templen = 0;
	history_class_t hclass;
	wchar_t wc;
	mbstate_t mbstate;

	int rv = 0;
	memset(&mbstate, 0, sizeof(mbstate));

	if (state->wbuf)
	{
		/* Coming back after return 1 */
		redraw = M_REDRAW_LINE;
		first = 0;
	}
	else
	{
		/* Initialise wbuf from buf */
		state->wbuflen = 0;
		state->lastchar = my_mbstowcs(&state->wbuf, &state->wbuflen, 0, buf);
		redraw = M_REDRAW_INIT;
	}

	if (flags & M_FILE)
		hclass = HC_FILE;
	else if (flags & M_EFILE)
		hclass = HC_MBOX;
	else if (flags & M_CMD)
		hclass = HC_CMD;
	else if (flags & M_ALIAS)
		hclass = HC_ALIAS;
	else if (flags & M_COMMAND)
		hclass = HC_COMMAND;
	else if (flags & M_PATTERN)
		hclass = HC_PATTERN;
	else
		hclass = HC_OTHER;

	for(;;)
	{
		if (redraw && !pass)
		{
			if (redraw == M_REDRAW_INIT)
			{
				/* Go to end of line */
				state->curpos = state->lastchar;
				state->begin = width_ceiling(state->wbuf, state->lastchar, my_wcswidth(state->wbuf, state->lastchar) - width + 1);
			}
			if (state->curpos < state->begin ||
			    my_wcswidth(state->wbuf + state->begin, state->curpos - state->begin) >= width)
				state->begin = width_ceiling(state->wbuf, state->lastchar, my_wcswidth(state->wbuf, state->curpos) - width / 2);
			move(y, x);
			w = 0;
			for(i = state->begin; i < state->lastchar; i++)
			{
				w += my_wcwidth(state->wbuf[i]);
				if (w > width)
					break;
				my_addwch(state->wbuf[i]);
			}
			clrtoeol();
			move(y, x + my_wcswidth(state->wbuf + state->begin, state->curpos - state->begin));
		}
		mutt_refresh();

		if ((ch = km_dokey(MENU_EDITOR)) == -1)
		{
			rv = -1;
			goto bye;
		}

		if (ch != OP_NULL)
		{
			first = 0;
			if (ch != OP_EDITOR_COMPLETE && ch != OP_EDITOR_COMPLETE_QUERY)
				state->tabs = 0;
			redraw = M_REDRAW_LINE;
			switch(ch)
			{
			case OP_EDITOR_HISTORY_UP:
				state->curpos = state->lastchar;
				if (mutt_history_at_scratch(hclass))
				{
					my_wcstombs(buf, buflen, state->wbuf, state->curpos);
					mutt_history_save_scratch(hclass, buf);
				}
				replace_part(state, 0, mutt_history_prev(hclass));
				redraw = M_REDRAW_INIT;
				break;

			case OP_EDITOR_HISTORY_DOWN:
				state->curpos = state->lastchar;
				if (mutt_history_at_scratch(hclass))
				{
					my_wcstombs(buf, buflen, state->wbuf, state->curpos);
					mutt_history_save_scratch(hclass, buf);
				}
				replace_part(state, 0, mutt_history_next(hclass));
				redraw = M_REDRAW_INIT;
				break;

			case OP_EDITOR_BACKSPACE:
				if (state->curpos == 0)
					BEEP();
				else
				{
					i = state->curpos;
					while(i && COMB_CHAR(state->wbuf[i - 1]))
						--i;
					if (i)
						--i;
					memmove(state->wbuf + i, state->wbuf + state->curpos,(state->lastchar - state->curpos) * sizeof(wchar_t));
					state->lastchar -= state->curpos - i;
					state->curpos = i;
				}
				break;

			case OP_EDITOR_BOL:
				state->curpos = 0;
				break;

			case OP_EDITOR_EOL:
				redraw= M_REDRAW_INIT;
				break;

			case OP_EDITOR_KILL_LINE:
				state->curpos = state->lastchar = 0;
				break;

			case OP_EDITOR_KILL_EOL:
				state->lastchar = state->curpos;
				break;

			case OP_EDITOR_BACKWARD_CHAR:
				if (state->curpos == 0)
					BEEP();
				else
				{
					while(state->curpos && COMB_CHAR(state->wbuf[state->curpos - 1]))
						state->curpos--;
					if (state->curpos)
						state->curpos--;
				}
				break;

			case OP_EDITOR_FORWARD_CHAR:
				if (state->curpos == state->lastchar)
					BEEP();
				else
				{
					++state->curpos;
					while(state->curpos < state->lastchar && COMB_CHAR(state->wbuf[state->curpos]))
						++state->curpos;
				}
				break;

			case OP_EDITOR_BACKWARD_WORD:
				if (state->curpos == 0)
					BEEP();
				else
				{
					while(state->curpos && iswspace(state->wbuf[state->curpos - 1]))
						--state->curpos;
					while(state->curpos && !iswspace(state->wbuf[state->curpos - 1]))
						--state->curpos;
				}
				break;

			case OP_EDITOR_FORWARD_WORD:
				if (state->curpos == state->lastchar)
					BEEP();
				else
				{
					while(state->curpos < state->lastchar && iswspace(state->wbuf[state->curpos]))
						++state->curpos;
					while(state->curpos < state->lastchar && !iswspace(state->wbuf[state->curpos]))
						++state->curpos;
				}
				break;

			case OP_EDITOR_CAPITALIZE_WORD:
			case OP_EDITOR_UPCASE_WORD:
			case OP_EDITOR_DOWNCASE_WORD:
				if (state->curpos == state->lastchar)
				{
					BEEP();
					break;
				}
				while(state->curpos && !iswspace(state->wbuf[state->curpos]))
					--state->curpos;
				while(state->curpos < state->lastchar && iswspace(state->wbuf[state->curpos]))
					++state->curpos;
				while(state->curpos < state->lastchar && !iswspace(state->wbuf[state->curpos]))
				{
					if (ch == OP_EDITOR_DOWNCASE_WORD)
						state->wbuf[state->curpos] = towlower(state->wbuf[state->curpos]);
					else
					{
						state->wbuf[state->curpos] = towupper(state->wbuf[state->curpos]);
						if (ch == OP_EDITOR_CAPITALIZE_WORD)
							ch = OP_EDITOR_DOWNCASE_WORD;
					}
					state->curpos++;
				}
				break;

			case OP_EDITOR_DELETE_CHAR:
				if (state->curpos == state->lastchar)
					BEEP();
				else
				{
					i = state->curpos;
					while(i < state->lastchar && COMB_CHAR(state->wbuf[i]))
						++i;
					if (i < state->lastchar)
						++i;
					while(i < state->lastchar && COMB_CHAR(state->wbuf[i]))
						++i;
					memmove(state->wbuf + state->curpos, state->wbuf + i,(state->lastchar - i) * sizeof(wchar_t));
					state->lastchar -= i - state->curpos;
				}
				break;

			case OP_EDITOR_KILL_WORD:
				/* delete to beginning of word */
				if (state->curpos != 0)
				{
					i = state->curpos;
					while(i && iswspace(state->wbuf[i - 1]))
						--i;
					if (i)
					{
						if (iswalnum(state->wbuf[i - 1]))
						{
							for(--i; i && iswalnum(state->wbuf[i - 1]); i--)
								;
						}
						else
							--i;
					}
					memmove(state->wbuf + i, state->wbuf + state->curpos,
						(state->lastchar - state->curpos) * sizeof(wchar_t));
					state->lastchar += i - state->curpos;
					state->curpos = i;
				}
				break;

			case OP_EDITOR_KILL_EOW:
				/* delete to end of word */

				/* first skip over whitespace */
				for(i = state->curpos;
				     i < state->lastchar && iswspace(state->wbuf[i]); i++)
					;

				/* if there are any characters left.. */
				if (i < state->lastchar)
				{
					/* if the current character is alphanumeric.. */
					if (iswalnum(state->wbuf[i]))
					{
						/* skip over the rest of the word consistent of only alphanumerics */
						for(; i < state->lastchar && iswalnum(state->wbuf[i]); i++)
							;
					}
					else
					{
						/* skip over one non-alphanumeric character */
						++i;
					}
				}

				memmove(state->wbuf + state->curpos, state->wbuf + i,
					(state->lastchar - i) * sizeof(wchar_t));
				state->lastchar += state->curpos - i;
				break;

			case OP_EDITOR_BUFFY_CYCLE:
				if (flags & M_EFILE)
				{
					first = 1; /* clear input if user types a real key later */
					my_wcstombs(buf, buflen, state->wbuf, state->curpos);
					mutt_buffy(buf, buflen);
					state->curpos = state->lastchar = my_mbstowcs(&state->wbuf, &state->wbuflen, 0, buf);
					break;
				}
				else if (!(flags & M_FILE))
					goto self_insert;
				/* fall through to completion routine(M_FILE) */

			case OP_EDITOR_COMPLETE:
			case OP_EDITOR_COMPLETE_QUERY:
				state->tabs++;
				if (flags & M_CMD)
				{
					for(i = state->curpos; i && !is_shell_char(state->wbuf[i-1]); i--)
						;
					my_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i);
					if (tempbuf && templen == state->lastchar - i &&
					    !memcmp(tempbuf, state->wbuf + i,(state->lastchar - i) * sizeof(wchar_t)))
					{
						mutt_select_file(buf, buflen,(flags & M_EFILE) ? M_SEL_FOLDER : 0);
						set_bit(options, OPTNEEDREDRAW);
						if (*buf)
							replace_part(state, i, buf);
						rv = 1;
						goto bye;
					}
					if (!mutt_complete(buf, buflen))
					{
						templen = state->lastchar - i;
						safe_realloc(&tempbuf, templen * sizeof(wchar_t));
					}
					else
						BEEP();

					replace_part(state, i, buf);
				}
				else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE)
				{
					/* invoke the alias-menu to get more addresses */
					for(i = state->curpos; i && state->wbuf[i-1] != ',' &&
						     state->wbuf[i-1] != ':'; i--)
						;
					for(; i < state->lastchar && state->wbuf[i] == ' '; i++)
						;
					my_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i);
					r = mutt_alias_complete(buf, buflen);
					replace_part(state, i, buf);
					if (!r)
					{
						rv = 1;
						goto bye;
					}
					break;
				}
				else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY)
				{
					/* invoke the query-menu to get more addresses */
					if ((i = state->curpos))
					{
						for(; i && state->wbuf[i - 1] != ','; i--)
							;
						for(; i < state->curpos && state->wbuf[i] == ' '; i++)
							;
					}

					my_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i);
					mutt_query_complete(buf, buflen);
					replace_part(state, i, buf);

					rv = 1;
					goto bye;
				}
				else if (flags & M_COMMAND)
				{
					my_wcstombs(buf, buflen, state->wbuf, state->curpos);
					i = strlen(buf);
					if (i && buf[i - 1] == '=' &&
					    mutt_var_value_complete(buf, buflen, i))
						state->tabs = 0;
					else if (!mutt_command_complete(buf, buflen, i, state->tabs))
						BEEP();
					replace_part(state, 0, buf);
				}
				else if (flags &(M_FILE | M_EFILE))
				{
					my_wcstombs(buf, buflen, state->wbuf, state->curpos);

					/* see if the path has changed from the last time */
					if ((!tempbuf && !state->lastchar) ||(tempbuf && templen == state->lastchar &&
									       !memcmp(tempbuf, state->wbuf, state->lastchar * sizeof(wchar_t))))
					{
						_mutt_select_file(buf, buflen,
								  ((flags & M_EFILE) ? M_SEL_FOLDER : 0) |(multiple ? M_SEL_MULTI : 0),
								   files, numfiles);
						set_bit(options, OPTNEEDREDRAW);
						if (*buf)
						{
							mutt_pretty_mailbox(buf, buflen);
							if (!pass)
								mutt_history_add(hclass, buf, 1);
							rv = 0;
							goto bye;
						}

						/* file selection cancelled */
						rv = 1;
						goto bye;
					}

					if (!mutt_complete(buf, buflen))
					{
						templen = state->lastchar;
						safe_realloc(&tempbuf, templen * sizeof(wchar_t));
						memcpy(tempbuf, state->wbuf, templen * sizeof(wchar_t));
					}
					else
						BEEP(); /* let the user know that nothing matched */
					replace_part(state, 0, buf);
				}
				else
					goto self_insert;
				break;

			case OP_EDITOR_QUOTE_CHAR:
			{
				struct event event;
				/*ADDCH(LastKey);*/
				event = mutt_getch();
				if (event.ch >= 0)
				{
					LastKey = event.ch;
					goto self_insert;
				}
			}

			case OP_EDITOR_TRANSPOSE_CHARS:
				if (state->lastchar < 2)
					BEEP();
				else
				{
					wchar_t t;

					if (state->curpos == 0)
						state->curpos = 2;
					else if (state->curpos < state->lastchar)
						++state->curpos;

					t = state->wbuf[state->curpos - 2];
					state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
					state->wbuf[state->curpos - 1] = t;
				}
				break;

			default:
				BEEP();
			}
		}
		else
		{

		self_insert:

			state->tabs = 0;
			/* use the raw keypress */
			ch = LastKey;

#if KEY_ENTER
			/* treat ENTER the same as RETURN */
			if (ch == KEY_ENTER)
				ch = '\r';
#endif

			/* quietly ignore all other function keys */
			if (ch & ~0xff)
				continue;

			/* gather the octets into a wide character */
			{
				char c;
				size_t k;

				c = ch;
				k = mbrtowc(&wc, &c, 1, &mbstate);
				if (k ==(size_t)(-2))
					continue;
				else if (k && k != 1)
				{
					memset(&mbstate, 0, sizeof(mbstate));
					continue;
				}
			}

			if (first &&(flags & M_CLEAR))
			{
				first = 0;
				if (IsWPrint(wc)) /* why? */
					state->curpos = state->lastchar = 0;
			}

			if (wc == '\r' || wc == '\n')
			{
				/* Convert from wide characters */
				my_wcstombs(buf, buflen, state->wbuf, state->lastchar);
				if (!pass)
					mutt_history_add(hclass, buf, 1);

				if (multiple)
				{
					char **tfiles;
					*numfiles = 1;
					tfiles = safe_calloc(*numfiles, sizeof(char *));
					mutt_expand_path(buf, buflen);
					tfiles[0] = safe_strdup(buf);
					*files = tfiles;
				}
				rv = 0;
				goto bye;
			}
			else if (wc &&(wc < ' ' || IsWPrint(wc))) /* why? */
			{
				if (state->lastchar >= state->wbuflen)
				{
					state->wbuflen = state->lastchar + 20;
					safe_realloc(&state->wbuf, state->wbuflen * sizeof(wchar_t));
				}
				memmove(state->wbuf + state->curpos + 1, state->wbuf + state->curpos,(state->lastchar - state->curpos) * sizeof(wchar_t));
				state->wbuf[state->curpos++] = wc;
				state->lastchar++;
			}
			else
			{
				mutt_flushinp();
				BEEP();
			}
		}
	}

bye:

	mutt_reset_history_state(hclass);
	safe_free(&tempbuf);
	return rv;
}
Example #10
0
File: mbox.c Project: kdave/neomutt
/**
 * mbox_mbox_sync - Implements MxOps::mbox_sync()
 */
static int mbox_mbox_sync(struct Mailbox *m, int *index_hint)
{
  if (!m)
    return -1;

  struct MboxAccountData *adata = mbox_adata_get(m);
  if (!adata)
    return -1;

  char tempfile[PATH_MAX];
  char buf[32];
  int i, j;
  enum SortType save_sort = SORT_ORDER;
  int rc = -1;
  int need_sort = 0; /* flag to resort mailbox if new mail arrives */
  int first = -1;    /* first message to be written */
  LOFF_T offset;     /* location in mailbox to write changed messages */
  struct stat statbuf;
  struct MUpdate *new_offset = NULL;
  struct MUpdate *old_offset = NULL;
  FILE *fp = NULL;
  struct Progress progress;
  char msgbuf[PATH_MAX + 64];

  /* sort message by their position in the mailbox on disk */
  if (C_Sort != SORT_ORDER)
  {
    save_sort = C_Sort;
    C_Sort = SORT_ORDER;
    mutt_mailbox_changed(m, MBN_RESORT);
    C_Sort = save_sort;
    need_sort = 1;
  }

  /* need to open the file for writing in such a way that it does not truncate
   * the file, so use read-write mode.  */
  adata->fp = freopen(m->path, "r+", adata->fp);
  if (!adata->fp)
  {
    mx_fastclose_mailbox(m);
    mutt_error(_("Fatal error!  Could not reopen mailbox!"));
    return -1;
  }

  mutt_sig_block();

  if (mbox_lock_mailbox(m, true, true) == -1)
  {
    mutt_sig_unblock();
    mutt_error(_("Unable to lock mailbox"));
    goto bail;
  }

  /* Check to make sure that the file hasn't changed on disk */
  i = mbox_mbox_check(m, index_hint);
  if ((i == MUTT_NEW_MAIL) || (i == MUTT_REOPENED))
  {
    /* new mail arrived, or mailbox reopened */
    rc = i;
    goto bail;
  }
  else if (i < 0)
  {
    /* fatal error */
    return -1;
  }

  /* Create a temporary file to write the new version of the mailbox in. */
  mutt_mktemp(tempfile, sizeof(tempfile));
  int fd = open(tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600);
  if ((fd == -1) || !(fp = fdopen(fd, "w")))
  {
    if (fd != -1)
    {
      close(fd);
      unlink(tempfile);
    }
    mutt_error(_("Could not create temporary file"));
    goto bail;
  }

  /* find the first deleted/changed message.  we save a lot of time by only
   * rewriting the mailbox from the point where it has actually changed.  */
  for (i = 0; (i < m->msg_count) && !m->emails[i]->deleted &&
              !m->emails[i]->changed && !m->emails[i]->attach_del;
       i++)
  {
  }
  if (i == m->msg_count)
  {
    /* this means ctx->changed or m->msg_deleted was set, but no
     * messages were found to be changed or deleted.  This should
     * never happen, is we presume it is a bug in neomutt.  */
    mutt_error(
        _("sync: mbox modified, but no modified messages (report this bug)"));
    mutt_debug(LL_DEBUG1, "no modified messages\n");
    unlink(tempfile);
    goto bail;
  }

  /* save the index of the first changed/deleted message */
  first = i;
  /* where to start overwriting */
  offset = m->emails[i]->offset;

  /* the offset stored in the header does not include the MMDF_SEP, so make
   * sure we seek to the correct location */
  if (m->magic == MUTT_MMDF)
    offset -= (sizeof(MMDF_SEP) - 1);

  /* allocate space for the new offsets */
  new_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
  old_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));

  if (!m->quiet)
  {
    snprintf(msgbuf, sizeof(msgbuf), _("Writing %s..."), m->path);
    mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, C_WriteInc, m->msg_count);
  }

  for (i = first, j = 0; i < m->msg_count; i++)
  {
    if (!m->quiet)
      mutt_progress_update(&progress, i, (int) (ftello(adata->fp) / (m->size / 100 + 1)));
    /* back up some information which is needed to restore offsets when
     * something fails.  */

    old_offset[i - first].valid = true;
    old_offset[i - first].hdr = m->emails[i]->offset;
    old_offset[i - first].body = m->emails[i]->content->offset;
    old_offset[i - first].lines = m->emails[i]->lines;
    old_offset[i - first].length = m->emails[i]->content->length;

    if (!m->emails[i]->deleted)
    {
      j++;

      if (m->magic == MUTT_MMDF)
      {
        if (fputs(MMDF_SEP, fp) == EOF)
        {
          mutt_perror(tempfile);
          unlink(tempfile);
          goto bail;
        }
      }

      /* save the new offset for this message.  we add 'offset' because the
       * temporary file only contains saved message which are located after
       * 'offset' in the real mailbox */
      new_offset[i - first].hdr = ftello(fp) + offset;

      if (mutt_copy_message_ctx(fp, m, m->emails[i], MUTT_CM_UPDATE,
                                CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0)
      {
        mutt_perror(tempfile);
        unlink(tempfile);
        goto bail;
      }

      /* Since messages could have been deleted, the offsets stored in memory
       * will be wrong, so update what we can, which is the offset of this
       * message, and the offset of the body.  If this is a multipart message,
       * we just flush the in memory cache so that the message will be reparsed
       * if the user accesses it later.  */
      new_offset[i - first].body = ftello(fp) - m->emails[i]->content->length + offset;
      mutt_body_free(&m->emails[i]->content->parts);

      switch (m->magic)
      {
        case MUTT_MMDF:
          if (fputs(MMDF_SEP, fp) == EOF)
          {
            mutt_perror(tempfile);
            unlink(tempfile);
            goto bail;
          }
          break;
        default:
          if (fputs("\n", fp) == EOF)
          {
            mutt_perror(tempfile);
            unlink(tempfile);
            goto bail;
          }
      }
    }
  }

  if (fclose(fp) != 0)
  {
    fp = NULL;
    mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n");
    unlink(tempfile);
    mutt_perror(tempfile);
    goto bail;
  }
  fp = NULL;

  /* Save the state of this folder. */
  if (stat(m->path, &statbuf) == -1)
  {
    mutt_perror(m->path);
    unlink(tempfile);
    goto bail;
  }

  fp = fopen(tempfile, "r");
  if (!fp)
  {
    mutt_sig_unblock();
    mx_fastclose_mailbox(m);
    mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n");
    mutt_perror(tempfile);
    FREE(&new_offset);
    FREE(&old_offset);
    return -1;
  }

  if ((fseeko(adata->fp, offset, SEEK_SET) != 0) || /* seek the append location */
      /* do a sanity check to make sure the mailbox looks ok */
      !fgets(buf, sizeof(buf), adata->fp) ||
      ((m->magic == MUTT_MBOX) && !mutt_str_startswith(buf, "From ", CASE_MATCH)) ||
      ((m->magic == MUTT_MMDF) && (mutt_str_strcmp(MMDF_SEP, buf) != 0)))
  {
    mutt_debug(LL_DEBUG1, "message not in expected position\n");
    mutt_debug(LL_DEBUG1, "\tLINE: %s\n", buf);
    i = -1;
  }
  else
  {
    if (fseeko(adata->fp, offset, SEEK_SET) != 0) /* return to proper offset */
    {
      i = -1;
      mutt_debug(LL_DEBUG1, "fseek() failed\n");
    }
    else
    {
      /* copy the temp mailbox back into place starting at the first
       * change/deleted message */
      if (!m->quiet)
        mutt_message(_("Committing changes..."));
      i = mutt_file_copy_stream(fp, adata->fp);

      if (ferror(adata->fp))
        i = -1;
    }
    if (i == 0)
    {
      m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */
      if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0))
      {
        i = -1;
        mutt_debug(LL_DEBUG1, "ftruncate() failed\n");
      }
    }
  }

  mutt_file_fclose(&fp);
  fp = NULL;
  mbox_unlock_mailbox(m);

  if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1))
  {
    /* error occurred while writing the mailbox back, so keep the temp copy around */

    char savefile[PATH_MAX];

    snprintf(savefile, sizeof(savefile), "%s/neomutt.%s-%s-%u", NONULL(C_Tmpdir),
             NONULL(Username), NONULL(ShortHostname), (unsigned int) getpid());
    rename(tempfile, savefile);
    mutt_sig_unblock();
    mx_fastclose_mailbox(m);
    mutt_pretty_mailbox(savefile, sizeof(savefile));
    mutt_error(_("Write failed!  Saved partial mailbox to %s"), savefile);
    FREE(&new_offset);
    FREE(&old_offset);
    return -1;
  }

  /* Restore the previous access/modification times */
  mbox_reset_atime(m, &statbuf);

  /* reopen the mailbox in read-only mode */
  adata->fp = fopen(m->path, "r");
  if (!adata->fp)
  {
    unlink(tempfile);
    mutt_sig_unblock();
    mx_fastclose_mailbox(m);
    mutt_error(_("Fatal error!  Could not reopen mailbox!"));
    FREE(&new_offset);
    FREE(&old_offset);
    return -1;
  }

  /* update the offsets of the rewritten messages */
  for (i = first, j = first; i < m->msg_count; i++)
  {
    if (!m->emails[i]->deleted)
    {
      m->emails[i]->offset = new_offset[i - first].hdr;
      m->emails[i]->content->hdr_offset = new_offset[i - first].hdr;
      m->emails[i]->content->offset = new_offset[i - first].body;
      m->emails[i]->index = j++;
    }
  }
  FREE(&new_offset);
  FREE(&old_offset);
  unlink(tempfile); /* remove partial copy of the mailbox */
  mutt_sig_unblock();

  if (C_CheckMboxSize)
  {
    struct Mailbox *tmp = mutt_find_mailbox(m->path);
    if (tmp && !tmp->has_new)
      mutt_update_mailbox(tmp);
  }

  return 0; /* signal success */

bail: /* Come here in case of disaster */

  mutt_file_fclose(&fp);

  /* restore offsets, as far as they are valid */
  if ((first >= 0) && old_offset)
  {
    for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++)
    {
      m->emails[i]->offset = old_offset[i - first].hdr;
      m->emails[i]->content->hdr_offset = old_offset[i - first].hdr;
      m->emails[i]->content->offset = old_offset[i - first].body;
      m->emails[i]->lines = old_offset[i - first].lines;
      m->emails[i]->content->length = old_offset[i - first].length;
    }
  }

  /* this is ok to call even if we haven't locked anything */
  mbox_unlock_mailbox(m);

  mutt_sig_unblock();
  FREE(&new_offset);
  FREE(&old_offset);

  adata->fp = freopen(m->path, "r", adata->fp);
  if (!adata->fp)
  {
    mutt_error(_("Could not reopen mailbox"));
    mx_fastclose_mailbox(m);
    return -1;
  }

  if (need_sort)
  {
    /* if the mailbox was reopened, the thread tree will be invalid so make
     * sure to start threading from scratch.  */
    mutt_mailbox_changed(m, MBN_RESORT);
  }

  return rc;
}
Example #11
0
File: dump.c Project: kdave/neomutt
/**
 * dump_config - Write all the config to a file
 * @param cs    ConfigSet to dump
 * @param style Output style, e.g. #CS_DUMP_STYLE_MUTT
 * @param flags Flags, see #ConfigDumpFlags
 * @param fp    File to write config to
 */
bool dump_config(struct ConfigSet *cs, enum CsDumpStyle style,
                 ConfigDumpFlags flags, FILE *fp)
{
  if (!cs)
    return false;

  struct HashElem *he = NULL;

  struct HashElem **list = get_elem_list(cs);
  if (!list)
    return false; /* LCOV_EXCL_LINE */

  bool result = true;

  struct Buffer *value = mutt_buffer_alloc(256);
  struct Buffer *initial = mutt_buffer_alloc(256);
  struct Buffer *tmp = mutt_buffer_alloc(256);

  for (size_t i = 0; list[i]; i++)
  {
    mutt_buffer_reset(value);
    mutt_buffer_reset(initial);
    he = list[i];
    const int type = DTYPE(he->type);

    if ((type == DT_SYNONYM) && !(flags & CS_DUMP_SHOW_SYNONYMS))
      continue;

    // if ((type == DT_DISABLED) && !(flags & CS_DUMP_SHOW_DISABLED))
    //   continue;

    if (type != DT_SYNONYM)
    {
      /* If necessary, get the current value */
      if ((flags & CS_DUMP_ONLY_CHANGED) || !(flags & CS_DUMP_HIDE_VALUE) ||
          (flags & CS_DUMP_SHOW_DEFAULTS))
      {
        int rc = cs_he_string_get(cs, he, value);
        if (CSR_RESULT(rc) != CSR_SUCCESS)
        {
          result = false; /* LCOV_EXCL_LINE */
          break;          /* LCOV_EXCL_LINE */
        }

        const struct ConfigDef *cdef = he->data;
        if (IS_SENSITIVE(*cdef) && (flags & CS_DUMP_HIDE_SENSITIVE) &&
            !mutt_buffer_is_empty(value))
        {
          mutt_buffer_reset(value);
          mutt_buffer_addstr(value, "***");
        }

        if ((type == DT_PATH) && (value->data[0] == '/'))
          mutt_pretty_mailbox(value->data, value->dsize);

        if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_LONG) &&
            (type != DT_QUAD) && !(flags & CS_DUMP_NO_ESCAPING))
        {
          mutt_buffer_reset(tmp);
          pretty_var(value->data, tmp);
          mutt_buffer_strcpy(value, tmp->data);
        }
      }

      /* If necessary, get the default value */
      if (flags & (CS_DUMP_ONLY_CHANGED | CS_DUMP_SHOW_DEFAULTS))
      {
        int rc = cs_he_initial_get(cs, he, initial);
        if (CSR_RESULT(rc) != CSR_SUCCESS)
        {
          result = false; /* LCOV_EXCL_LINE */
          break;          /* LCOV_EXCL_LINE */
        }

        if ((type == DT_PATH) && !(he->type & DT_MAILBOX))
          mutt_pretty_mailbox(initial->data, initial->dsize);

        if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_LONG) &&
            (type != DT_QUAD) && !(flags & CS_DUMP_NO_ESCAPING))
        {
          mutt_buffer_reset(tmp);
          pretty_var(initial->data, tmp);
          mutt_buffer_strcpy(initial, tmp->data);
        }
      }
    }

    if (style == CS_DUMP_STYLE_MUTT)
      dump_config_mutt(cs, he, value, initial, flags, fp);
    else
      dump_config_neo(cs, he, value, initial, flags, fp);
  }

  FREE(&list);
  mutt_buffer_free(&value);
  mutt_buffer_free(&initial);
  mutt_buffer_free(&tmp);

  return result;
}
Example #12
0
/* return values:
 *	0	success
 *	-1	failure
 */
int mbox_sync_mailbox(CONTEXT *ctx, int *index_hint)
{
    char tempfile[_POSIX_PATH_MAX];
    char buf[32];
    int i, j, save_sort = SORT_ORDER;
    int rc = -1;
    int need_sort = 0; /* flag to resort mailbox if new mail arrives */
    int first = -1;    /* first message to be written */
    LOFF_T offset;     /* location in mailbox to write changed messages */
    struct stat statbuf;
    struct m_update_t *newOffset = NULL;
    struct m_update_t *oldOffset = NULL;
    FILE *fp = NULL;
    progress_t progress;
    char msgbuf[STRING];

    /* sort message by their position in the mailbox on disk */
    if (Sort != SORT_ORDER) {
        save_sort = Sort;
        Sort = SORT_ORDER;
        mutt_sort_headers(ctx, 0);
        Sort = save_sort;
        need_sort = 1;
    }

    /* need to open the file for writing in such a way that it does not truncate
     * the file, so use read-write mode.
     */
    if ((ctx->fp = freopen(ctx->path, "r+", ctx->fp)) == NULL) {
        mx_fastclose_mailbox(ctx);
        mutt_error _("Fatal error!  Could not reopen mailbox!");
        return -1;
    }

    mutt_block_signals();

    if (mbox_lock_mailbox(ctx, 1, 1) == -1) {
        mutt_unblock_signals();
        mutt_error _("Unable to lock mailbox!");
        goto bail;
    }

    /* Check to make sure that the file hasn't changed on disk */
    if (((i = mbox_check_mailbox(ctx, index_hint)) == M_NEW_MAIL)
        || (i == M_REOPENED)) {
        /* new mail arrived, or mailbox reopened */
        need_sort = i;
        rc = i;
        goto bail;
    } else if (i < 0)
        /* fatal error */
        return -1;

    /* Create a temporary file to write the new version of the mailbox in. */
    mutt_mktemp(tempfile, sizeof(tempfile));

    if (((i = open(tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1)
        || ((fp = fdopen(i, "w")) == NULL)) {
        if (-1 != i) {
            close(i);
            unlink(tempfile);
        }
        mutt_error _("Could not create temporary file!");
        mutt_sleep(5);
        goto bail;
    }

    /* find the first deleted/changed message.  we save a lot of time by only
     * rewriting the mailbox from the point where it has actually changed.
     */
    for (i = 0; i < ctx->msgcount
         && !ctx->hdrs[i]->deleted
         && !ctx->hdrs[i]->changed
         && !ctx->hdrs[i]->attach_del; i++)
        ;

    if (i == ctx->msgcount) {
        /* this means ctx->changed or ctx->deleted was set, but no
         * messages were found to be changed or deleted.  This should
         * never happen, is we presume it is a bug in mutt.
         */
        mutt_error _(
            "sync: mbox modified, but no modified messages! (report this bug)");
        mutt_sleep(5); /* the mutt_error /will/ get cleared! */
        dprint(1, "mbox_sync_mailbox(): no modified messages.\n");
        unlink(tempfile);
        goto bail;
    }

    /* save the index of the first changed/deleted message */
    first = i;

    /* where to start overwriting */
    offset = ctx->hdrs[i]->offset;

    /* the offset stored in the header does not include the MMDF_SEP, so make
     * sure we seek to the correct location
     */
    if (ctx->magic == M_MMDF)
        offset -= (sizeof MMDF_SEP - 1);

    /* allocate space for the new offsets */
    newOffset = (__typeof__(newOffset)) safe_calloc(ctx->msgcount - first, sizeof(struct m_update_t));
    oldOffset = (__typeof__(oldOffset)) safe_calloc(ctx->msgcount - first, sizeof(struct m_update_t));

    if (!ctx->quiet) {
        snprintf(msgbuf, sizeof(msgbuf), _("Writing %s..."), ctx->path);
        mutt_progress_init(&progress,
                           msgbuf,
                           M_PROGRESS_MSG,
                           WriteInc,
                           ctx->msgcount);
    }

    for (i = first, j = 0; i < ctx->msgcount; i++) {
        if (!ctx->quiet)
            mutt_progress_update(&progress, i,
                                 (int)(ftello(ctx->fp) /
                                       (ctx->size / 100 + 1)));

        /*
         * back up some information which is needed to restore offsets when
         * something fails.
         */

        oldOffset[i - first].valid  = 1;
        oldOffset[i - first].hdr    = ctx->hdrs[i]->offset;
        oldOffset[i - first].body   = ctx->hdrs[i]->content->offset;
        oldOffset[i - first].lines  = ctx->hdrs[i]->lines;
        oldOffset[i - first].length = ctx->hdrs[i]->content->length;

        if (!ctx->hdrs[i]->deleted) {
            j++;

            if (ctx->magic == M_MMDF) {
                if (fputs(MMDF_SEP, fp) == EOF) {
                    mutt_perror(tempfile);
                    mutt_sleep(5);
                    unlink(tempfile);
                    goto bail;
                }
            }

            /* save the new offset for this message.  we add `offset' because
               the
             * temporary file only contains saved message which are located
             *after
             * `offset' in the real mailbox
             */
            newOffset[i - first].hdr = ftello(fp) + offset;

            if (mutt_copy_message(fp, ctx, ctx->hdrs[i], M_CM_UPDATE,
                                  CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0) {
                mutt_perror(tempfile);
                mutt_sleep(5);
                unlink(tempfile);
                goto bail;
            }

            /* Since messages could have been deleted, the offsets stored in
               memory
             * will be wrong, so update what we can, which is the offset of this
             * message, and the offset of the body.  If this is a multipart
             *message,
             * we just flush the in memory cache so that the message will be
             *reparsed
             * if the user accesses it later.
             */
            newOffset[i - first].body = ftello(fp) -
                                        ctx->hdrs[i]->content->length + offset;
            mutt_free_body(&ctx->hdrs[i]->content->parts);

            switch (ctx->magic) {
            case M_MMDF:

                if (fputs(MMDF_SEP, fp) == EOF) {
                    mutt_perror(tempfile);
                    mutt_sleep(5);
                    unlink(tempfile);
                    goto bail;
                }
                break;

            default:

                if (fputs("\n", fp) == EOF) {
                    mutt_perror(tempfile);
                    mutt_sleep(5);
                    unlink(tempfile);
                    goto bail;
                }
            }
        }
    }

    if (fclose(fp) != 0) {
        fp = NULL;
        dprint(1, "mbox_sync_mailbox: safe_fclose (&) returned non-zero.\n");
        unlink(tempfile);
        mutt_perror(tempfile);
        mutt_sleep(5);
        goto bail;
    }
    fp = NULL;

    /* Save the state of this folder. */
    if (stat(ctx->path, &statbuf) == -1) {
        mutt_perror(ctx->path);
        mutt_sleep(5);
        unlink(tempfile);
        goto bail;
    }

    if ((fp = fopen(tempfile, "r")) == NULL) {
        mutt_unblock_signals();
        mx_fastclose_mailbox(ctx);
        dprint(1, "mbox_sync_mailbox: unable to reopen temp copy of mailbox!\n");
        mutt_perror(tempfile);
        mutt_sleep(5);
        return -1;
    }

    if ((fseeko(ctx->fp, offset, SEEK_SET) != 0) /* seek the append location */
        ||/* do a sanity check to make sure the mailbox looks ok */
        (fgets(buf, sizeof(buf), ctx->fp) == NULL)
        || ((ctx->magic == M_MBOX)
            && (mutt_strncmp("From ", buf, 5) != 0))
        || ((ctx->magic == M_MMDF)
            && (mutt_strcmp(MMDF_SEP, buf) != 0))) {
        dprint(1, "mbox_sync_mailbox: message not in expected position.");
        dprint(1, "\tLINE: %s\n", buf);
        i = -1;
    } else {
        if (fseeko(ctx->fp, offset, SEEK_SET) != 0) { /* return to proper offset
                                                         */
            i = -1;
            dprint(1, "mbox_sync_mailbox: fseek() failed\n");
        } else {
            /* copy the temp mailbox back into place starting at the first
             * change/deleted message
             */
            if (!ctx->quiet)
                mutt_message _("Committing changes...");
            i = mutt_copy_stream(fp, ctx->fp);

            if (ferror(ctx->fp))
                i = -1;
        }

        if (i == 0) {
            ctx->size = ftello(ctx->fp); /* update the size of the mailbox */
            ftruncate(fileno(ctx->fp), ctx->size);
        }
    }

    safe_fclose(&fp);
    fp = NULL;
    mbox_unlock_mailbox(ctx);

    if ((fclose(ctx->fp) != 0)
        || (i == -1)) {
        /* error occurred while writing the mailbox back, so keep the temp copy
         * around
         */

        char savefile[_POSIX_PATH_MAX];

        snprintf(savefile, sizeof(savefile), "%s/mutt.%s-%s-%u",
                 NONULL(Tempdir), NONULL(Username), NONULL(Hostname),
                 (unsigned int)getpid());
        rename(tempfile, savefile);
        mutt_unblock_signals();
        mx_fastclose_mailbox(ctx);
        mutt_pretty_mailbox(savefile, sizeof(savefile));
        mutt_error(_("Write failed!  Saved partial mailbox to %s"), savefile);
        mutt_sleep(5);
        return -1;
    }

    /* Restore the previous access/modification times */
    mbox_reset_atime(ctx, &statbuf);

    /* reopen the mailbox in read-only mode */
    if ((ctx->fp = fopen(ctx->path, "r")) == NULL) {
        unlink(tempfile);
        mutt_unblock_signals();
        mx_fastclose_mailbox(ctx);
        mutt_error _("Fatal error!  Could not reopen mailbox!");
        return -1;
    }

    /* update the offsets of the rewritten messages */
    for (i = first, j = first; i < ctx->msgcount; i++) {
        if (!ctx->hdrs[i]->deleted) {
            ctx->hdrs[i]->offset = newOffset[i - first].hdr;
            ctx->hdrs[i]->content->hdr_offset = newOffset[i - first].hdr;
            ctx->hdrs[i]->content->offset = newOffset[i - first].body;
            ctx->hdrs[i]->index = j++;
        }
    }
    safe_free(&newOffset);
    safe_free(&oldOffset);
    unlink(tempfile); /* remove partial copy of the mailbox */
    mutt_unblock_signals();

    return 0;         /* signal success */

    bail:             /* Come here in case of disaster */

    safe_fclose(&fp);

    /* restore offsets, as far as they are valid */
    if ((first >= 0)
        && oldOffset) {
        for (i = first; i < ctx->msgcount
             && oldOffset[i - first].valid; i++) {
            ctx->hdrs[i]->offset = oldOffset[i - first].hdr;
            ctx->hdrs[i]->content->hdr_offset = oldOffset[i - first].hdr;
            ctx->hdrs[i]->content->offset = oldOffset[i - first].body;
            ctx->hdrs[i]->lines = oldOffset[i - first].lines;
            ctx->hdrs[i]->content->length = oldOffset[i - first].length;
        }
    }

    /* this is ok to call even if we haven't locked anything */
    mbox_unlock_mailbox(ctx);

    mutt_unblock_signals();
    safe_free(&newOffset);
    safe_free(&oldOffset);

    if ((ctx->fp = freopen(ctx->path, "r", ctx->fp)) == NULL) {
        mutt_error _("Could not reopen mailbox!");
        mx_fastclose_mailbox(ctx);
        return -1;
    }

    if (need_sort)
        /* if the mailbox was reopened, the thread tree will be invalid so make
         * sure to start threading from scratch.  */
        mutt_sort_headers(ctx, (need_sort == M_REOPENED));

    return rc;
}
/* args:
 *      ctx	Context info, used when recalling a message to which
 *              we reply.
 *	hdr	envelope/attachment info for recalled message
 *	cur	if message was a reply, `cur' is set to the message which
 *		`hdr' is in reply to
 *	fcc	fcc for the recalled message
 *	fcclen	max length of fcc
 *
 * return vals:
 *	-1		error/no messages
 *	0		normal exit
 *	SENDREPLY	recalled message is a reply
 */
int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size_t fcclen)
{
  HEADER *h;
  int code = SENDPOSTPONED;
  LIST *tmp;
  LIST *last = NULL;
  LIST *next;
  char *p;
  int opt_delete;
#ifdef USE_IMAP
  char curpath[LONG_STRING];
  int need_reopen = 0;
#endif

  if (!Postponed)
    return (-1);

#ifdef USE_IMAP
  /* if we're in an IMAP folder and the postponed folder is also IMAP, we may
   * need to take steps to avoid opening an additional connection to the same
   * server. */
  if ((ctx && ctx->magic == M_IMAP) && mx_is_imap (Postponed))
  { 
    strfcpy (curpath, ctx->path, sizeof (curpath));
    if (imap_select_mailbox (ctx, Postponed) < 0)
      return -1;
    need_reopen = 1;
    PostContext = ctx;
  }
  else
#endif
  if ((PostContext = mx_open_mailbox (Postponed, M_NOSORT, NULL)) == NULL)
  {
    PostCount = 0;
    mutt_error _("No postponed messages.");
    return (-1);
  }
  
  if (! PostContext->msgcount)
  {
    PostCount = 0;
    mx_close_mailbox (PostContext, NULL);
#ifdef USE_IMAP
  if (need_reopen)
    ctx = mx_open_mailbox (curpath, 0, PostContext);
  else
#endif
    safe_free ((void **) &PostContext);
    mutt_error _("No postponed messages.");
    return (-1);
  }

  if (PostContext->msgcount == 1)
  {
    /* only one message, so just use that one. */
    h = PostContext->hdrs[0];
  }
  else if ((h = select_msg ()) == NULL)
  {
    mx_close_mailbox (PostContext, NULL);
#ifdef USE_IMAP
  if (need_reopen)
    ctx = mx_open_mailbox (curpath, 0, PostContext);
  else
#endif
    safe_free ((void **) &PostContext);
    return (-1);
  }

  if (mutt_prepare_template (NULL, PostContext, hdr, h, 0) < 0)
  {
    mx_fastclose_mailbox (PostContext);
#ifdef USE_IMAP
    if (need_reopen)
      ctx = mx_open_mailbox (curpath, 0, NULL);
    else
#endif
    safe_free ((void **) &PostContext);
    return (-1);
  }

  /* finished with this message, so delete it. */
  mutt_set_flag (PostContext, h, M_DELETE, 1);

  /* update the count for the status display */
  PostCount = PostContext->msgcount - PostContext->deleted;

  /* avoid the "purge deleted messages" prompt */
  opt_delete = quadoption (OPT_DELETE);
  set_quadoption (OPT_DELETE, M_YES);
  mx_close_mailbox (PostContext, NULL);
  set_quadoption (OPT_DELETE, opt_delete);

#ifdef USE_IMAP
  if (need_reopen)
    ctx = mx_open_mailbox (curpath, 0, PostContext);
  else
#endif
  safe_free ((void **) &PostContext);

  for (tmp = hdr->env->userhdrs; tmp; )
  {
    if (mutt_strncasecmp ("X-Mutt-References:", tmp->data, 18) == 0)
    {
      if (ctx)
      {
	/* if a mailbox is currently open, look to see if the orignal message
	   the user attempted to reply to is in this mailbox */
	p = tmp->data + 18;
	SKIPWS (p);
	*cur = hash_find (ctx->id_hash, p);
      }

      /* Remove the X-Mutt-References: header field. */
      next = tmp->next;
      if (last)
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
      if (*cur)
	code |= SENDREPLY;
    }
    else if (mutt_strncasecmp ("X-Mutt-Fcc:", tmp->data, 11) == 0)
    {
      p = tmp->data + 11;
      SKIPWS (p);
      strfcpy (fcc, p, fcclen);
      mutt_pretty_mailbox (fcc);

      /* remove the X-Mutt-Fcc: header field */
      next = tmp->next;
      if (last)
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }



#ifdef HAVE_PGP
    else if (mutt_strncmp ("Pgp:", tmp->data, 4) == 0 /* this is generated
						       * by old mutt versions
						       */
	     || mutt_strncmp ("X-Mutt-PGP:", tmp->data, 11) == 0)
    {
      hdr->pgp = mutt_parse_pgp_hdr (strchr (tmp->data, ':') + 1, 1);
       
      /* remove the pgp field */
      next = tmp->next;
      if (last)
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }
#endif /* HAVE_PGP */

#ifdef MIXMASTER
    else if (mutt_strncmp ("X-Mutt-Mix:", tmp->data, 11) == 0)
    {
      char *t;
      mutt_free_list (&hdr->chain);
      
      t = strtok (tmp->data + 11, " \t\n");
      while (t)
      {
	hdr->chain = mutt_add_list (hdr->chain, t);
	t = strtok (NULL, " \t\n");
      }
      
      next = tmp->next;
      if (last) 
	last->next = tmp->next;
      else
	hdr->env->userhdrs = tmp->next;
      tmp->next = NULL;
      mutt_free_list (&tmp);
      tmp = next;
    }
#endif

    else
    {
      last = tmp;
      tmp = tmp->next;
    }
  }
  return (code);
}