Beispiel #1
0
/* imap_add_folder: add a folder name to the browser list, formatting it as
 *   necessary. */
static void imap_add_folder (char delim, char *folder, int noselect,
  int noinferiors, struct browser_state *state, short isparent)
{
  char tmp[LONG_STRING];
  char relpath[LONG_STRING];
  IMAP_MBOX mx;

  if (imap_parse_path (state->folder, &mx))
    return;

  imap_unmunge_mbox_name (folder);

  if (state->entrylen + 1 == state->entrymax)
  {
    safe_realloc ((void **) &state->entry,
      sizeof (struct folder_file) * (state->entrymax += 256));
    memset (state->entry + state->entrylen, 0,
      (sizeof (struct folder_file) * (state->entrymax - state->entrylen)));
  }

  /* render superiors as unix-standard ".." */
  if (isparent)
    strfcpy (relpath, "../", sizeof (relpath));
  /* strip current folder from target, to render a relative path */
  else if (!mutt_strncmp (mx.mbox, folder, mutt_strlen (mx.mbox)))
    strfcpy (relpath, folder + mutt_strlen (mx.mbox), sizeof (relpath));
  else
    strfcpy (relpath, folder, sizeof (relpath));

  /* apply filemask filter. This should really be done at menu setup rather
   * than at scan, since it's so expensive to scan. But that's big changes
   * to browser.c */
  if (!((regexec (Mask.rx, relpath, 0, NULL, 0) == 0) ^ Mask.not))
  {
    FREE (&mx.mbox);
    return;
  }

  imap_qualify_path (tmp, sizeof (tmp), &mx, folder);
  (state->entry)[state->entrylen].name = safe_strdup (tmp);

  /* mark desc with delim in browser if it can have subfolders */
  if (!isparent && !noinferiors && strlen (relpath) < sizeof (relpath) - 1)
  {
    relpath[strlen (relpath) + 1] = '\0';
    relpath[strlen (relpath)] = delim;
  }
  
  (state->entry)[state->entrylen].desc = safe_strdup (relpath);

  (state->entry)[state->entrylen].imap = 1;
  /* delimiter at the root is useless. */
  if (folder[0] == '\0')
    delim = '\0';
  (state->entry)[state->entrylen].delim = delim;
  (state->entry)[state->entrylen].selectable = !noselect;
  (state->entry)[state->entrylen].inferiors = !noinferiors;
  (state->entrylen)++;

  FREE (&mx.mbox);
}
Beispiel #2
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, MUTT_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, MUTT_DELETE, 1);
  mutt_set_flag (PostContext, h, MUTT_PURGE, 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, MUTT_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);
}
void mutt_fetchPopMail (void)
{
  struct sockaddr_in sin;
#if SIZEOF_LONG == 4
  long n;
#else
  int n;
#endif
  struct hostent *he;
  char buffer[2048];
  char msgbuf[SHORT_STRING];
  int s, i, last = 0, msgs, bytes, err = 0;
  CONTEXT ctx;
  MESSAGE *msg = NULL;

  if (!PopHost)
  {
    mutt_error _("POP host is not defined.");
    return;
  }

  if (!PopUser)
  {
    mutt_error _("No POP username is defined.");
    return;
  }
    
  if (!getPass ()) return;

  s = socket (AF_INET, SOCK_STREAM, IPPROTO_IP);

  memset ((char *) &sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons (PopPort);

  if ((n = inet_addr (NONULL(PopHost))) == -1)
  {
    /* Must be a DNS name */
    if ((he = gethostbyname (NONULL(PopHost))) == NULL)
    {
      mutt_error (_("Could not find address for host %s."), PopHost);
      return;
    }
    memcpy ((void *)&sin.sin_addr, *(he->h_addr_list), he->h_length);
  }
  else
    memcpy ((void *)&sin.sin_addr, (void *)&n, sizeof(n));
  
  mutt_message (_("Connecting to %s"), inet_ntoa (sin.sin_addr));

  if (connect (s, (struct sockaddr *) &sin, sizeof (struct sockaddr_in)) == -1)
  {
    mutt_perror ("connect");
    return;
  }
  
  if (getLine (s, buffer, sizeof (buffer)) == -1)
    goto fail;

  if (mutt_strncmp (buffer, "+OK", 3) != 0)
  {
    mutt_remove_trailing_ws (buffer);
    mutt_error ("%s", buffer);
    goto finish;
  }

  snprintf (buffer, sizeof(buffer), "user %s\r\n", PopUser);
  write (s, buffer, mutt_strlen (buffer));

  if (getLine (s, buffer, sizeof (buffer)) == -1)
    goto fail;

  if (mutt_strncmp (buffer, "+OK", 3) != 0)
  {
    mutt_remove_trailing_ws (buffer);
    mutt_error ("%s", buffer);
    goto finish;
  }
  
  snprintf (buffer, sizeof(buffer), "pass %s\r\n", NONULL(PopPass));
  write (s, buffer, mutt_strlen (buffer));
  
  if (getLine (s, buffer, sizeof (buffer)) == -1)
    goto fail;

  if (mutt_strncmp (buffer, "+OK", 3) != 0)
  {
    if(PopPass)
      memset(PopPass, 0, mutt_strlen(PopPass));
    
    safe_free((void **) &PopPass); /* void the given password */
    mutt_remove_trailing_ws (buffer);
    mutt_error ("%s", buffer[0] ? buffer : _("Server closed connection!"));
    goto finish;
  }
  
  /* find out how many messages are in the mailbox. */
  write (s, "stat\r\n", 6);
  
  if (getLine (s, buffer, sizeof (buffer)) == -1)
    goto fail;

  if (mutt_strncmp (buffer, "+OK", 3) != 0)
  {
    mutt_remove_trailing_ws (buffer);
    mutt_error ("%s", buffer);
    goto finish;
  }
  
  sscanf (buffer, "+OK %d %d", &msgs, &bytes);

  if (msgs == 0)
  {
    mutt_message _("No new mail in POP mailbox.");
    goto finish;
  }

  if (mx_open_mailbox (NONULL(Spoolfile), M_APPEND, &ctx) == NULL)
    goto finish;

  /* only get unread messages */
  if(option(OPTPOPLAST))
  {
    write (s, "last\r\n", 6);
    if (getLine (s, buffer, sizeof (buffer)) == -1)
      goto fail;
    
    if (mutt_strncmp (buffer, "+OK", 3) == 0)
      sscanf (buffer, "+OK %d", &last);
    else
      /* ignore an error here and assume all messages are new */
      last = 0;
  }
  
  snprintf (msgbuf, sizeof (msgbuf),
	    msgs > 1 ? _("Reading new messages (%d bytes)...") :
		    _("Reading new message (%d bytes)..."), bytes);
  mutt_message (msgbuf);

  for (i = last + 1 ; i <= msgs ; i++)
  {
    snprintf (buffer, sizeof(buffer), "retr %d\r\n", i);
    write (s, buffer, mutt_strlen (buffer));

    if (getLine (s, buffer, sizeof (buffer)) == -1)
    {
      mx_fastclose_mailbox (&ctx);
      goto fail;
    }

    if (mutt_strncmp (buffer, "+OK", 3) != 0)
    {
      mutt_remove_trailing_ws (buffer);
      mutt_error ("%s", buffer);
      break;
    }

    if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL)
    {
      err = 1;
      break;
    }

    /* Now read the actual message. */
    FOREVER
    {
      char *p;
      int chunk;

      if ((chunk = getLine (s, buffer, sizeof (buffer))) == -1)
      {
	mutt_error _("Error reading message!");
	err = 1;
	break;
      }

      /* check to see if we got a full line */
      if (buffer[chunk-2] == '\r' && buffer[chunk-1] == '\n')
      {
	if (mutt_strcmp(".\r\n", buffer) == 0)
	{
	  /* end of message */
	  break;
	}

	/* change CRLF to just LF */
	buffer[chunk-2] = '\n';
	buffer[chunk-1] = 0;
	chunk--;

	/* see if the line was byte-stuffed */
	if (buffer[0] == '.')
	{
	  p = buffer + 1;
	  chunk--;
	}
	else
	  p = buffer;
      }
      else
	p = buffer;
      
      fwrite (p, 1, chunk, msg->fp);
    }

    if (mx_commit_message (msg, &ctx) != 0)
    {
      mutt_error _("Error while writing mailbox!");
      err = 1;
    }

    mx_close_message (&msg);

    if (err)
      break;

    if (option (OPTPOPDELETE))
    {
      /* delete the message on the server */
      snprintf (buffer, sizeof(buffer), "dele %d\r\n", i);
      write (s, buffer, mutt_strlen (buffer));

      /* eat the server response */
      getLine (s, buffer, sizeof (buffer));
      if (mutt_strncmp (buffer, "+OK", 3) != 0)
      {
	err = 1;
        mutt_remove_trailing_ws (buffer);
	mutt_error ("%s", buffer);
	break;
      }
    }
    
    if ( msgs > 1)
      mutt_message (_("%s [%d of %d messages read]"), msgbuf, i, msgs);
    else
      mutt_message (_("%s [%d message read]"), msgbuf, msgs);

  }

  if (msg)
  {
    if (mx_commit_message (msg, &ctx) != 0)
      err = 1;
    mx_close_message (&msg);
  }
  mx_close_mailbox (&ctx, NULL);

  if (err)
  {
    /* make sure no messages get deleted */
    write (s, "rset\r\n", 6);
    getLine (s, buffer, sizeof (buffer)); /* snarf the response */
  }

finish:

  /* exit gracefully */
  write (s, "quit\r\n", 6);
  getLine (s, buffer, sizeof (buffer)); /* snarf the response */
  close (s);
  return;

  /* not reached */

fail:

  mutt_error _("Server closed connection!");
  close (s);
}
Beispiel #4
0
Datei: copy.c Projekt: 0xAX/mutt
/* Ok, the only reason for not merging this with mutt_copy_header()
 * below is to avoid creating a HEADER structure in message_handler().
 * Also, this one will wrap headers much more aggressively than the other one.
 */
int
mutt_copy_hdr (FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags,
	       const char *prefix)
{
  int from = 0;
  int this_is_from;
  int ignore = 0;
  char buf[LONG_STRING]; /* should be long enough to get most fields in one pass */
  char *nl;
  LIST *t;
  char **headers;
  int hdr_count;
  int x;
  char *this_one = NULL;
  size_t this_one_len = 0;
  int error;

  if (ftello (in) != off_start)
    fseeko (in, off_start, 0);

  buf[0] = '\n';
  buf[1] = 0;

  if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | CH_WEED_DELIVERED)) == 0)
  {
    /* Without these flags to complicate things
     * we can do a more efficient line to line copying
     */
    while (ftello (in) < off_end)
    {
      nl = strchr (buf, '\n');

      if ((fgets (buf, sizeof (buf), in)) == NULL)
	break;

      /* Is it the beginning of a header? */
      if (nl && buf[0] != ' ' && buf[0] != '\t')
      {
	ignore = 1;
	if (!from && mutt_strncmp ("From ", buf, 5) == 0)
	{
	  if ((flags & CH_FROM) == 0)
	    continue;
	  from = 1;
	}
	else if (flags & (CH_NOQFROM) &&
			ascii_strncasecmp (">From ", buf, 6) == 0)
		continue;

	else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
	  break; /* end of header */

	if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
	    (ascii_strncasecmp ("Status:", buf, 7) == 0 ||
	     ascii_strncasecmp ("X-Status:", buf, 9) == 0))
	  continue;
	if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
	    (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 ||
	     ascii_strncasecmp ("Lines:", buf, 6) == 0))
	  continue;
	if ((flags & CH_UPDATE_REFS) &&
	    ascii_strncasecmp ("References:", buf, 11) == 0)
	  continue;
	if ((flags & CH_UPDATE_IRT) &&
	    ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0)
	  continue;
	ignore = 0;
      }

      if (!ignore && fputs (buf, out) == EOF)
	return (-1);
    }
    return 0;
  }

  hdr_count = 1;
  x = 0;
  error = FALSE;

  /* We are going to read and collect the headers in an array
   * so we are able to do re-ordering.
   * First count the number of entries in the array
   */
  if (flags & CH_REORDER)
  {
    for (t = HeaderOrderList; t; t = t->next)
    {
      dprint(3, (debugfile, "Reorder list: %s\n", t->data));
      hdr_count++;
    }
  }

  dprint (1, (debugfile, "WEED is %s\n", (flags & CH_WEED) ? "Set" : "Not"));

  headers = safe_calloc (hdr_count, sizeof (char *));

  /* Read all the headers into the array */
  while (ftello (in) < off_end)
  {
    nl = strchr (buf, '\n');

    /* Read a line */
    if ((fgets (buf, sizeof (buf), in)) == NULL)
      break;

    /* Is it the beginning of a header? */
    if (nl && buf[0] != ' ' && buf[0] != '\t')
    {
      /* Do we have anything pending? */
      if (this_one)
      {
	if (flags & CH_DECODE) 
	{
	  if (!address_header_decode (&this_one))
	    rfc2047_decode (&this_one);
	  this_one_len = mutt_strlen (this_one);
	}

	if (!headers[x])
	  headers[x] = this_one;
	else 
	{
	  int hlen = mutt_strlen (headers[x]);

	  safe_realloc (&headers[x], hlen + this_one_len + sizeof (char));
	  strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */
	  FREE (&this_one);
	}

	this_one = NULL;
      }

      ignore = 1;
      this_is_from = 0;
      if (!from && mutt_strncmp ("From ", buf, 5) == 0)
      {
	if ((flags & CH_FROM) == 0)
	  continue;
	this_is_from = from = 1;
      }
      else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
	break; /* end of header */

      /* note: CH_FROM takes precedence over header weeding. */
      if (!((flags & CH_FROM) && (flags & CH_FORCE_FROM) && this_is_from) &&
	  (flags & CH_WEED) &&
	  mutt_matches_ignore (buf, Ignore) &&
	  !mutt_matches_ignore (buf, UnIgnore))
	continue;
      if ((flags & CH_WEED_DELIVERED) &&
	  ascii_strncasecmp ("Delivered-To:", buf, 13) == 0)
	continue;
      if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
	  (ascii_strncasecmp ("Status:", buf, 7) == 0 ||
	   ascii_strncasecmp ("X-Status:", buf, 9) == 0))
	continue;
      if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
	  (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 ||
	   ascii_strncasecmp ("Lines:", buf, 6) == 0))
	continue;
      if ((flags & CH_MIME) &&
	  ((ascii_strncasecmp ("content-", buf, 8) == 0 &&
	    (ascii_strncasecmp ("transfer-encoding:", buf + 8, 18) == 0 ||
	     ascii_strncasecmp ("type:", buf + 8, 5) == 0)) ||
	   ascii_strncasecmp ("mime-version:", buf, 13) == 0))
	continue;
      if ((flags & CH_UPDATE_REFS) &&
	  ascii_strncasecmp ("References:", buf, 11) == 0)
	continue;
      if ((flags & CH_UPDATE_IRT) &&
	  ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0)
	continue;

      /* Find x -- the array entry where this header is to be saved */
      if (flags & CH_REORDER)
      {
	for (t = HeaderOrderList, x = 0 ; (t) ; t = t->next, x++)
	{
	  if (!ascii_strncasecmp (buf, t->data, mutt_strlen (t->data)))
	  {
	    dprint(2, (debugfile, "Reorder: %s matches %s\n", t->data, buf));
	    break;
	  }
	}
      }

      ignore = 0;
    } /* If beginning of header */

    if (!ignore)
    {
      dprint (2, (debugfile, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count));
      if (!this_one) {
	this_one = safe_strdup (buf);
	this_one_len = mutt_strlen (this_one);
      } else {
	int blen = mutt_strlen (buf);

	safe_realloc (&this_one, this_one_len + blen + sizeof (char));
	strcat (this_one + this_one_len, buf); /* __STRCAT_CHECKED__ */
	this_one_len += blen;
      }
    }
  } /* while (ftello (in) < off_end) */

  /* Do we have anything pending?  -- XXX, same code as in above in the loop. */
  if (this_one)
  {
    if (flags & CH_DECODE) 
    {
      if (!address_header_decode (&this_one))
	rfc2047_decode (&this_one);
      this_one_len = mutt_strlen (this_one);
    }
    
    if (!headers[x])
      headers[x] = this_one;
    else 
    {
      int hlen = mutt_strlen (headers[x]);

      safe_realloc (&headers[x], hlen + this_one_len + sizeof (char));
      strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */
      FREE (&this_one);
    }

    this_one = NULL;
  }

  /* Now output the headers in order */
  for (x = 0; x < hdr_count; x++)
  {
    if (headers[x])
    {
#if 0
      if (flags & CH_DECODE)
	rfc2047_decode (&headers[x]);
#endif

      /* We couldn't do the prefixing when reading because RFC 2047
       * decoding may have concatenated lines.
       */
      
      if (flags & (CH_DECODE|CH_PREFIX))
      {
	if (mutt_write_one_header (out, 0, headers[x], 
				   flags & CH_PREFIX ? prefix : 0, mutt_term_width (Wrap)-SidebarWidth, flags) == -1)
	{
	  error = TRUE;
	  break;
	}
      }
      else
      {      
	if (fputs (headers[x], out) == EOF)
	{
	  error = TRUE;
	  break;
	}
      }
    }
  }

  /* Free in a separate loop to be sure that all headers are freed
   * in case of error. */
  for (x = 0; x < hdr_count; x++)
    FREE (&headers[x]);
  FREE (&headers);

  if (error)
    return (-1);
  return (0);
}
time_t is_from (const char *s, char *path, size_t pathlen)
{
  struct tm tm;
  int yr;

  if (path)
    *path = 0;

  if (mutt_strncmp ("From ", s, 5) != 0)
    return 0;

  s = next_word (s); /* skip over the From part. */
  if (!*s)
    return 0;

  dprint (3, (debugfile, "\nis_from(): parsing: %s", s));

  if (!is_day_name (s))
  {
    const char *p;
    size_t len;
    short q = 0;

    for (p = s; *p && (q || !ISSPACE (*p)); p++)
    {
      if (*p == '\\')
      {
	if (*++p == '\0') 
	  return 0;
      }
      else if (*p == '"')
      {
	q = !q;
      }
    }
    
    if (q || !*p) return 0;
    
    if (path)
    {
      len = (size_t) (p - s);
      if (len + 1 > pathlen)
	len = pathlen - 1;
      memcpy (path, s, len);
      path[len] = 0;
      dprint (3, (debugfile, "is_from(): got return path: %s\n", path));
    }
    
    s = p + 1;
    SKIPWS (s);
    if (!*s)
      return 0;

    if (!is_day_name (s))
    {
      dprint(1, (debugfile, "is_from():  expected weekday, got: %s\n", s));
      return 0;
    }
  }

  s = next_word (s);
  if (!*s) return 0;

  /* do a quick check to make sure that this isn't really the day of the week.
   * this could happen when receiving mail from a local user whose login name
   * is the same as a three-letter abbreviation of the day of the week.
   */
  if (is_day_name (s))
  {
    s = next_word (s);
    if (!*s) return 0;
  }

  /* now we should be on the month. */
  if ((tm.tm_mon = mutt_check_month (s)) < 0) return 0;

  /* day */
  s = next_word (s);
  if (!*s) return 0;
  if (sscanf (s, "%d", &tm.tm_mday) != 1) return 0;

  /* time */
  s = next_word (s);
  if (!*s) return 0;

  /* Accept either HH:MM or HH:MM:SS */
  if (sscanf (s, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 3);
  else if (sscanf (s, "%d:%d", &tm.tm_hour, &tm.tm_min) == 2)
    tm.tm_sec = 0;
  else
    return 0;

  s = next_word (s);
  if (!*s) return 0;

  /* timezone? */
  if (isalpha ((unsigned char) *s) || *s == '+' || *s == '-')
  {
    s = next_word (s);
    if (!*s) return 0;

    /*
     * some places have two timezone fields after the time, e.g.
     *      From [email protected] Wed Aug  2 00:39:12 MET DST 1995
     */
    if (isalpha ((unsigned char) *s))
    {
      s = next_word (s);
      if (!*s) return 0;
    }
  }

  /* year */
  if (sscanf (s, "%d", &yr) != 1) return 0;
  tm.tm_year = yr > 1900 ? yr - 1900 : (yr < 70 ? yr + 100 : yr);
  
  dprint (3,(debugfile, "is_from(): month=%d, day=%d, hr=%d, min=%d, sec=%d, yr=%d.\n",
	     tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year));

  tm.tm_isdst = -1;

  return (mutt_mktime (&tm, 0));
}
Beispiel #6
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;
}
Beispiel #7
0
/* check to see if the mailbox has changed on disk.
 *
 * return values:
 *	M_REOPENED	mailbox has been reopened
 *	M_NEW_MAIL	new mail has arrived!
 *	M_LOCKED	couldn't lock the file
 *	0		no change
 *	-1		error
 */
int mbox_check_mailbox(CONTEXT *ctx, int *index_hint)
{
    struct stat st;
    char buffer[LONG_STRING];
    int unlock = 0;
    int modified = 0;

    if (stat(ctx->path, &st) == 0) {
        if ((st.st_mtime == ctx->mtime)
            && (st.st_size == ctx->size))
            return 0;

        if (st.st_size == ctx->size) {
            /* the file was touched, but it is still the same length, so just
               exit */
            ctx->mtime = st.st_mtime;
            return 0;
        }

        if (st.st_size > ctx->size) {
            /* lock the file if it isn't already */
            if (!ctx->locked) {
                mutt_block_signals();

                if (mbox_lock_mailbox(ctx, 0, 0) == -1) {
                    mutt_unblock_signals();

                    /* we couldn't lock the mailbox, but nothing serious
                       happened:
                     * probably the new mail arrived: no reason to wait till we
                     *can
                     * parse it: we'll get it on the next pass
                     */
                    return M_LOCKED;
                }
                unlock = 1;
            }

            /*
             * Check to make sure that the only change to the mailbox is that
             * message(s) were appended to this file.  My heuristic is that we
             *should
             * see the message separator at *exactly* what used to be the end of
             *the
             * folder.
             */
            if (fseeko(ctx->fp, ctx->size, SEEK_SET) != 0)
                dprint(1, "mbox_check_mailbox: fseek() failed\n");

            if (fgets(buffer, sizeof(buffer), ctx->fp) != NULL) {
                if (((ctx->magic == M_MBOX)
                     && (mutt_strncmp("From ", buffer, 5) == 0))
                    || ((ctx->magic == M_MMDF)
                        && (mutt_strcmp(MMDF_SEP, buffer) == 0))) {
                    if (fseeko(ctx->fp, ctx->size, SEEK_SET) != 0)
                        dprint(1, "mbox_check_mailbox: fseek() failed\n");

                    if (ctx->magic == M_MBOX)
                        mbox_parse_mailbox(ctx);
                    else
                        mmdf_parse_mailbox(ctx);

                    /* Only unlock the folder if it was locked inside of this
                       routine.
                     * It may have been locked elsewhere, like in
                     * mutt_checkpoint_mailbox().
                     */

                    if (unlock) {
                        mbox_unlock_mailbox(ctx);
                        mutt_unblock_signals();
                    }

                    return M_NEW_MAIL; /* signal that new mail arrived */
                } else
                    modified = 1;
            } else {
                dprint(1, "mbox_check_mailbox: fgets returned NULL.\n");
                modified = 1;
            }
        } else
            modified = 1;
    }

    if (modified) {
        if (mutt_reopen_mailbox(ctx, index_hint) != -1) {
            if (unlock) {
                mbox_unlock_mailbox(ctx);
                mutt_unblock_signals();
            }
            return M_REOPENED;
        }
    }

    /* fatal error */

    mbox_unlock_mailbox(ctx);
    mx_fastclose_mailbox(ctx);
    mutt_unblock_signals();
    mutt_error _("Mailbox was corrupted!");
    return -1;
}
Beispiel #8
0
/* Note that this function is also called when new mail is appended to the
 * currently open folder, and NOT just when the mailbox is initially read.
 *
 * NOTE: it is assumed that the mailbox being read has been locked before
 * this routine gets called.  Strange things could happen if it's not!
 */
int mbox_parse_mailbox(CONTEXT *ctx)
{
    struct stat sb;
    char buf[HUGE_STRING], return_path[STRING];
    HEADER *curhdr;
    time_t t;
    int count = 0, lines = 0;
    LOFF_T loc;

#ifdef NFS_ATTRIBUTE_HACK
    struct utimbuf newtime;
#endif /* ifdef NFS_ATTRIBUTE_HACK */
    progress_t progress;
    char msgbuf[STRING];

    /* Save information about the folder at the time we opened it. */
    if (stat(ctx->path, &sb) == -1) {
        mutt_perror(ctx->path);
        return -1;
    }

    ctx->size = sb.st_size;
    ctx->mtime = sb.st_mtime;
    ctx->atime = sb.st_atime;

#ifdef NFS_ATTRIBUTE_HACK

    if (sb.st_mtime > sb.st_atime) {
        newtime.modtime = sb.st_mtime;
        newtime.actime = time(NULL);
        utime(ctx->path, &newtime);
    }
#endif /* ifdef NFS_ATTRIBUTE_HACK */

    if (!ctx->readonly)
        ctx->readonly = access(ctx->path, W_OK) ? 1 : 0;

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

    loc = ftello(ctx->fp);

    while (fgets(buf, sizeof(buf), ctx->fp) != NULL) {
        if (is_from(buf, return_path, sizeof(return_path), &t)) {
            /* Save the Content-Length of the previous message */
            if (count > 0) {
#define PREV ctx->hdrs[ctx->msgcount - 1]

                if (PREV->content->length < 0) {
                    PREV->content->length = loc - PREV->content->offset - 1;

                    if (PREV->content->length < 0)
                        PREV->content->length = 0;
                }

                if (!PREV->lines)
                    PREV->lines = lines ? lines - 1 : 0;
            }

            count++;

            if (!ctx->quiet)
                mutt_progress_update(&progress, count,
                                     (int)(ftello(ctx->fp) /
                                           (ctx->size / 100 + 1)));

            if (ctx->msgcount == ctx->hdrmax)
                mx_alloc_memory(ctx);

            curhdr = ctx->hdrs[ctx->msgcount] = mutt_new_header();
            curhdr->received = t - mutt_local_tz(t);
            curhdr->offset = loc;
            curhdr->index = ctx->msgcount;

            curhdr->env = mutt_read_rfc822_header(ctx->fp, curhdr, 0, 0);

            /* if we know how long this message is, either just skip over the
               body,
             * or if we don't know how many lines there are, count them now
             *(this will
             * save time by not having to search for the next message marker).
             */
            if (curhdr->content->length > 0) {
                LOFF_T tmploc;

                loc = ftello(ctx->fp);
                tmploc = loc + curhdr->content->length + 1;

                if ((0 < tmploc)
                    && (tmploc < ctx->size)) {
                    /*
                     * check to see if the content-length looks valid.  we
                     *expect to
                     * to see a valid message separator at this point in the
                     *stream
                     */
                    if ((fseeko(ctx->fp, tmploc, SEEK_SET) != 0)
                        || (fgets(buf, sizeof(buf), ctx->fp) == NULL)
                        || (mutt_strncmp("From ", buf, 5) != 0)) {
                        dprint(1, "mbox_parse_mailbox: bad content-length in message %d (cl="
                                OFF_T_FMT ")\n", curhdr->index, curhdr->content->length);
                        dprint(1, "\tLINE: %s", buf);

                        if (fseeko(ctx->fp, loc, SEEK_SET) != 0) { /* nope,
                                                                      return the
                                                                      previous
                                                                      position
                                                                      */
                            dprint(1, "mbox_parse_mailbox: fseek() failed\n");
                        }
                        curhdr->content->length = -1;
                    }
                } else if (tmploc != ctx->size) {
                    /* content-length would put us past the end of the file, so
                       it
                     * must be wrong
                     */
                    curhdr->content->length = -1;
                }

                if (curhdr->content->length != -1) {
                    /* good content-length.  check to see if we know how many
                       lines
                     * are in this message.
                     */
                    if (curhdr->lines == 0) {
                        int cl = curhdr->content->length;

                        /* count the number of lines in this message */
                        if (fseeko(ctx->fp, loc, SEEK_SET) != 0)
                            dprint(1, "mbox_parse_mailbox: fseek() failed\n");

                        while (cl-- > 0) {
                            if (fgetc(ctx->fp) == '\n')
                                curhdr->lines++;
                        }
                    }

                    /* return to the offset of the next message separator */
                    if (fseeko(ctx->fp, tmploc, SEEK_SET) != 0)
                        dprint(1, "mbox_parse_mailbox: fseek() failed\n");
                }
            }

            ctx->msgcount++;

            if (!curhdr->env->return_path
                && return_path[0])
                curhdr->env->return_path = rfc822_parse_adrlist(
                    curhdr->env->return_path,
                    return_path);

            if (!curhdr->env->from)
                curhdr->env->from = rfc822_cpy_adr(curhdr->env->return_path, 0);


            lines = 0;
        } else
            lines++;

        loc = ftello(ctx->fp);
    }

    /*
     * Only set the content-length of the previous message if we have read more
     * than one message during _this_ invocation.  If this routine is called
     * when new mail is received, we need to make sure not to clobber what
     * previously was the last message since the headers may be sorted.
     */
    if (count > 0) {
        if (PREV->content->length < 0) {
            PREV->content->length = ftello(ctx->fp) - PREV->content->offset - 1;

            if (PREV->content->length < 0)
                PREV->content->length = 0;
        }

        if (!PREV->lines)
            PREV->lines = lines ? lines - 1 : 0;

        mx_update_context(ctx, count);
    }

    return 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;
#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);
}