Exemple #1
0
int
mu_vgetans (const char *variants, const char *fmt, va_list ap)
{
  char repl[64];

  while (1)
    {
      size_t n;
      char *p;
      
      mu_stream_vprintf (mu_strout, fmt, ap);
      mu_stream_write (mu_strout, "? ", 2, NULL);
      mu_stream_flush (mu_strout);
      if (mu_stream_read (mu_strin, repl, sizeof repl, &n) || n == 0)
	return 0;
      mu_rtrim_class (repl, MU_CTYPE_ENDLN);

      p = strchr (variants, *repl);
      if (p)
	return *p;
      
      mu_stream_printf (mu_strout, _("Please answer one of [%s]: "), variants);
    }
  return 0; /* to pacify gcc */
}
Exemple #2
0
int
mu_vgetyn (const char *fmt, va_list ap)
{
  char repl[64];

  while (1)
    {
      int rc;
      size_t n;
      
      mu_stream_vprintf (mu_strout, fmt, ap);
      mu_stream_write (mu_strout, "? ", 2, NULL);
      mu_stream_flush (mu_strout);
      if (mu_stream_read (mu_strin, repl, sizeof repl, &n) || n == 0)
	return 0;
      mu_rtrim_class (repl, MU_CTYPE_ENDLN);

      rc = mu_true_answer_p (repl);
      
      if (rc >= 0)
	return rc;
      
      /* TRANSLATORS: See msgids "nN" and "yY". */
      mu_stream_printf (mu_strout, "%s", _("Please answer yes or no: "));
    }
  return 0; /* to pacify gcc */
}
Exemple #3
0
static int
_streamref_read (struct _mu_stream *str, char *buf, size_t bufsize,
		 size_t *pnread)
{
  struct _mu_streamref *sp = (struct _mu_streamref *)str;
  int rc;
  size_t nread;
  mu_off_t off;
  
  rc = mu_stream_seek (sp->transport, sp->offset, MU_SEEK_SET, &off);
  if (rc == 0)
    {
      if (sp->end)
	{
	  size_t size = sp->end - off + 1;
	  if (size < bufsize)
	    bufsize = size;
	}
      rc = mu_stream_read (sp->transport, buf, bufsize, &nread);
      if (rc == 0)
	{
	  sp->offset += nread;
	  *pnread = nread;
	}
    }
  else if (rc == ESPIPE)
    {
      *pnread = 0;
      mu_stream_clearerr (sp->transport);
      return 0;
    }
  return streamref_return (sp, rc);
}
Exemple #4
0
char *
dispatch_docstring (const char *text)
{
  mu_stream_t str;
  struct mutool_action_tab *p;
  mu_off_t size;
  size_t n;
  char *ret;
  
  mu_memory_stream_create (&str, MU_STREAM_RDWR);
  mu_stream_printf (str, "%s\n%s\n\n", text, _("Commands are:"));
  for (p = mutool_action_tab; p->name; p++)
    mu_stream_printf (str, "  %s %-16s - %s\n",
		      mu_program_name,
		      p->name, gettext (p->docstring));
  mu_stream_printf (str,
		      _("\nTry `%s COMMAND --help' to get help on a particular "
		      "COMMAND.\n\n"),
		      mu_program_name);
  mu_stream_printf (str, "%s\n", _("Options are:"));
  mu_stream_flush (str);
  mu_stream_size (str, &size);
  ret = mu_alloc (size + 1);
  mu_stream_seek (str, 0, MU_SEEK_SET, NULL);
  mu_stream_read (str, ret, size, &n);
  ret[n] = 0;
  mu_stream_destroy (&str);
  return ret;
}
Exemple #5
0
static void
pop_stream_drain (mu_stream_t str)
{
  char buf[2048];
  size_t size;

  while (mu_stream_read (str, buf, sizeof buf, &size) == 0 && size)
    ;
}
Exemple #6
0
static int
_mh_prop_read_stream (mu_header_t *phdr, mu_stream_t stream)
{
  int rc;
  mu_stream_t flt;
  const char *argv[4];
  mu_off_t size;
  size_t total;
  char *blurb;
  
  rc = mu_stream_size (stream, &size);
  if (rc)
    return rc;
  
  argv[0] = "INLINE-COMMENT";
  argv[1] = "#";
  argv[2] = "-r";
  argv[3] = NULL;
  rc = mu_filter_create_args (&flt, stream, argv[0], 3, argv,
			      MU_FILTER_DECODE, MU_STREAM_READ);
  if (rc)
    {
      mu_error (_("cannot open filter stream: %s"), mu_strerror (rc));
      return rc;
    }

  blurb = malloc (size + 1);
  if (!blurb)
    {
      mu_stream_destroy (&flt);
      return ENOMEM;
    }

  total = 0;
  while (1)
    {
      size_t n;
      
      rc = mu_stream_read (flt, blurb + total, size - total, &n);
      if (rc)
	break;
      if (n == 0)
	break;
      total += n;
    }

  mu_stream_destroy (&flt);
  if (rc)
    {
      free (blurb);
      return rc;
    }

  rc = mu_header_create (phdr, blurb, total);
  free (blurb);
  return rc;
}
Exemple #7
0
static int
_iostream_read (struct _mu_stream *str, char *buf, size_t bufsize,
		size_t *pnread)
{
  struct _mu_iostream *sp = (struct _mu_iostream *)str;
  int rc = mu_stream_read (sp->transport[_MU_STREAM_INPUT], buf, bufsize,
			   pnread);
  if (rc)
    sp->last_err_str = _MU_STREAM_INPUT;
  return rc;
}
Exemple #8
0
void
imap4d_readline (struct imap4d_tokbuf *tok)
{
  tok->argc = 0;
  tok->level = 0;
  for (;;)
    {
      char *last_arg;
      size_t off = imap4d_tokbuf_getline (tok);
      imap4d_tokbuf_tokenize (tok, off);
      if (tok->argc == 0)
        break;  
      last_arg = tok->buffer + tok->argp[tok->argc - 1];
      if (last_arg[0] == '{' && last_arg[strlen(last_arg)-1] == '}')
	{
	  int rc;
	  unsigned long number;
	  char *sp = NULL;
	  char *buf;
	  size_t len;
	  int xlev = set_xscript_level (MU_XSCRIPT_PAYLOAD);
	  
	  number = strtoul (last_arg + 1, &sp, 10);
	  /* Client can ask for non-synchronised literal,
	     if a '+' is appended to the octet count. */
	  if (*sp == '}')
	    io_sendf ("+ GO AHEAD\n");
	  else if (*sp != '+')
	    break;
	  imap4d_tokbuf_expand (tok, number + 1);
	  off = tok->level;
	  buf = tok->buffer + off;
          len = 0;
          while (len < number)
            {
               size_t sz;
	       rc = mu_stream_read (iostream, buf + len, number - len, &sz);
               if (rc || sz == 0)
                 break;
               len += sz;
            }
	  check_input_err (rc, len);
	  imap4d_tokbuf_unquote (tok, &off, &len);
	  imap4d_tokbuf_decrlf (tok, off, &len);
	  tok->level += len;
	  tok->buffer[tok->level++] = 0;
	  tok->argp[tok->argc - 1] = off;
	  set_xscript_level (xlev);
	}
      else
	break;
    }
}  
Exemple #9
0
static int
mbox_readstream (mbox_message_t mum, char *buffer, size_t buflen,
		 mu_off_t off, size_t *pnread, int isreadline,
		 mu_off_t start, mu_off_t end)
{
  size_t nread = 0;
  int status = 0;

  if (buffer == NULL || buflen == 0)
    {
      if (pnread)
	*pnread = nread;
      return 0;
    }

  mu_monitor_rdlock (mum->mud->mailbox->monitor);
#ifdef WITH_PTHREAD
  /* read() is cancellation point since we're doing a potentially
     long operation.  Lets make sure we clean the state.  */
  pthread_cleanup_push (mbox_cleanup, (void *)mum->mud->mailbox);
#endif
  {
    mu_off_t ln = end - (start + off);
    if (ln > 0)
      {
	/* Position the file pointer and the buffer.  */
	nread = ((size_t)ln < buflen) ? (size_t)ln : buflen;
	if (isreadline)
	  status = mu_stream_readline (mum->mud->mailbox->stream,
				       buffer, buflen,
				       start + off, &nread);
	else
	  status = mu_stream_read (mum->mud->mailbox->stream, buffer, nread,
				   start + off, &nread);
      }
  }
  mu_monitor_unlock (mum->mud->mailbox->monitor);
#ifdef WITH_PTHREAD
  pthread_cleanup_pop (0);
#endif

  if (pnread)
    *pnread = nread;
  return status;
}
Exemple #10
0
int
mta_send (mu_message_t msg)
{
  size_t n;
  char buffer[512];    
  mu_stream_t stream = NULL;
  size_t off = 0, line;
  char *value;

  value = from_address ();
  if (value)
    {
      fprintf (diag, "ENVELOPE FROM: %s\n", value);
      free (value);
    }
  
  value = address_email_string (recipients);
  fprintf (diag, "ENVELOPE TO: %s\n", value);
  free (value);

  mu_message_get_stream (msg, &stream);
  line = 0;
  fprintf (diag, "%4lu: ", (unsigned long) line);
  while (mu_stream_read (stream, buffer, sizeof buffer - 1, off, &n) == 0
	 && n != 0)
    {
      size_t i;

      for (i = 0; i < n; i++)
	{
	  fputc (buffer[i], diag);
	  if (buffer[i] == '\n')
	    {
	      line++;
	      fprintf (diag, "%4lu: ", (unsigned long) line);
	    }
	}
      off += n;
    }
  fprintf (diag, "\nEND OF MESSAGE\n");
  fflush (diag);
  return 0;
}
Exemple #11
0
static int
chk_ssha (const char *db_pass, const char *pass)
{
  int rc;
  unsigned char sha1digest[20];
  unsigned char *d1;
  struct mu_sha1_ctx sha1context;
  mu_stream_t str = NULL, flt = NULL;
  size_t size;
  
  size = strlen (db_pass);
  mu_static_memory_stream_create (&str, db_pass, size);
  mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
  mu_stream_unref (str);

  d1 = malloc (size);
  if (!d1)
    {
      mu_stream_destroy (&flt);
      return ENOMEM;
    }
  
  mu_stream_read (flt, (char*) d1, size, &size);
  mu_stream_destroy (&flt);

  if (size <= 16)
    {
      mu_error ("malformed SSHA1 password: %s", db_pass);
      return MU_ERR_FAILURE;
    }
  
  mu_sha1_init_ctx (&sha1context);
  mu_sha1_process_bytes (pass, strlen (pass), &sha1context);
  mu_sha1_process_bytes (d1 + 20, size - 20, &sha1context);
  mu_sha1_finish_ctx (&sha1context, sha1digest);

  rc = memcmp (sha1digest, d1, sizeof sha1digest) == 0 ?
                  0 : MU_ERR_AUTH_FAILURE;
  free (d1);
  return rc;
}
Exemple #12
0
static int
chk_sha (const char *db_pass, const char *pass)
{
  unsigned char sha1digest[20];
  unsigned char d1[20];
  mu_stream_t str = NULL, flt = NULL;
  struct mu_sha1_ctx sha1context;
   
  mu_sha1_init_ctx (&sha1context);
  mu_sha1_process_bytes (pass, strlen (pass), &sha1context);
  mu_sha1_finish_ctx (&sha1context, sha1digest);

  mu_static_memory_stream_create (&str, db_pass, strlen (db_pass));
  mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
  mu_stream_unref (str);

  mu_stream_read (flt, (char*) d1, sizeof d1, NULL);
  mu_stream_destroy (&flt);
  
  return memcmp (sha1digest, d1, sizeof sha1digest) == 0 ?
                  0 : MU_ERR_AUTH_FAILURE;
}
Exemple #13
0
static int
mu_port_fill_input (SCM port)
{
  struct mu_port *mp = MU_PORT (port);
  scm_port *pt = SCM_PTAB_ENTRY (port);
  size_t nread = 0;
  int status;
  
  status = mu_stream_read (mp->stream, (char*) pt->read_buf, pt->read_buf_size,
			   mp->offset, &nread);
  if (status)
    mu_scm_error ("mu_port_fill_input", status,
		  "Error reading from stream", SCM_BOOL_F);

  if (nread == 0)
    return EOF;

  mp->offset += nread;
  pt->read_pos = pt->read_buf;
  pt->read_end = pt->read_buf + nread;
  return *pt->read_buf;
}
Exemple #14
0
static PyObject *
api_stream_read (PyObject *self, PyObject *args)
{
  int status;
  size_t read_count;
  char rbuf[1024];
  PyObject *py_ret;
  PyStream *py_stm;

  memset (rbuf, 0, sizeof (rbuf));

  if (!PyArg_ParseTuple (args, "O!", &PyStreamType, &py_stm))
    return NULL;

  status = mu_stream_read (py_stm->stm, rbuf, sizeof (rbuf), &read_count);

  py_ret = PyTuple_New (3);
  PyTuple_SetItem (py_ret, 0, PyInt_FromLong (status));
  PyTuple_SetItem (py_ret, 1, PyString_FromString (rbuf));
  PyTuple_SetItem (py_ret, 2, PyInt_FromLong (read_count));
  return _ro (py_ret);
}
Exemple #15
0
/* Display the contents of the given file on the terminal */
static void
display_file (const char *name)
{
  const char *pager = mh_global_profile_get ("moreproc", getenv ("PAGER"));

  if (pager)
    mh_spawnp (pager, name);
  else
    {
      mu_stream_t stream;
      int rc;
      size_t off = 0;
      size_t n;
      char buffer[512];
      
      rc = mu_file_stream_create (&stream, name, MU_STREAM_READ);
      if (rc)
	{
	  mu_error ("mu_file_stream_create: %s", mu_strerror (rc));
	  return;
	}
      rc = mu_stream_open (stream);
      if (rc)
	{
	  mu_error ("mu_stream_open: %s", mu_strerror (rc));
	  return;
	} 
      
      while (mu_stream_read (stream, buffer, sizeof buffer - 1, off, &n) == 0
	     && n != 0)
	{
	  buffer[n] = '\0';
	  printf ("%s", buffer);
	  off += n;
	}
      mu_stream_destroy (&stream, NULL);
    }
}      
Exemple #16
0
/* Message read overload  */
static int
nntp_body_read (mu_stream_t stream, char *buffer, size_t buflen, mu_off_t offset, size_t *plen)
{
  mu_body_t body = mu_stream_get_owner (stream);
  mu_message_t msg = mu_body_get_owner (body);
  msg_nntp_t msg_nntp = mu_message_get_owner (msg);
  m_nntp_t m_nntp = msg_nntp->m_nntp;
  f_nntp_t f_nntp = m_nntp->f_nntp;
  int status;
  size_t len = 0;

  /* Start over.  */
  if (plen == NULL)
    plen = &len;

  /* Select first.  */
  status = nntp_mailbox_messages_count (m_nntp->mailbox, NULL);
  if (status != 0)
    return status;

  if (msg_nntp->bstream == NULL)
    {
      status = mu_nntp_body (f_nntp->nntp, msg_nntp->msgno, NULL, &msg_nntp->mid,  &msg_nntp->bstream);
      if (status != 0)
	return status;
    }
  status = mu_stream_read (msg_nntp->bstream, buffer, buflen, offset, plen);
  if (status == 0)
    {
      /* Destroy the stream.  */
      if (*plen == 0)
	{
	  mu_stream_destroy (&msg_nntp->bstream, NULL);
	}
    }
  return status;
}
Exemple #17
0
static void
c_copy (mu_stream_t out, mu_stream_t in)
{
  if (verbose)
    {
      mu_stream_set_stat (in,
			  MU_STREAM_STAT_MASK_ALL,
			  instat);
      mu_stream_set_stat (out,
			  MU_STREAM_STAT_MASK_ALL,
			  outstat);
    }
  
  if (printable)
    {
      char c;
      size_t size;
      
      while (mu_stream_read (in, &c, 1, &size) == 0 && size > 0)
	{
	  int rc;
	  
	  if (printable && !ISPRINT (c))
	    {
	      char outbuf[24];
	      sprintf (outbuf, "\\%03o", (unsigned char) c);
	      rc = mu_stream_write (out, outbuf, strlen (outbuf), NULL);
	    }
	  else
	    rc = mu_stream_write (out, &c, 1, NULL);
	}
    }
  else
    MU_ASSERT (mu_stream_copy (out, in, 0, NULL));

}
Exemple #18
0
int
mu_progmailer_send (struct _mu_progmailer *pm, mu_message_t msg)
{
  int status;
  mu_stream_t stream = NULL;
  char buffer[512];
  size_t len = 0;
  int rc;
  mu_header_t hdr;
  mu_body_t body;
  int found_nl = 0;
  int exit_status;
	
  if (!pm || !msg)
    return EINVAL;
  mu_message_get_header (msg, &hdr);
  status = mu_header_get_streamref (hdr, &stream);
  if (status)
    {
      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		("cannot get header stream: %s", mu_strerror (status)));
      return status;
    }
  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE, ("Sending headers..."));
  mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
  while ((status = mu_stream_readline (stream, buffer, sizeof (buffer),
				       &len)) == 0
	 && len != 0)
    {
      if (mu_c_strncasecmp (buffer, MU_HEADER_FCC, sizeof (MU_HEADER_FCC) - 1))
	{
	  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_PROT, ("Header: %s", buffer));
	  if (write (pm->fd, buffer, len) == -1)
	    {
	      status = errno;
	      
	      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
			("write failed: %s", strerror (status)));
	      break;
	    }
	}
      found_nl = (len == 1 && buffer[0] == '\n');
    }

  if (!found_nl)
    {
      if (write (pm->fd, "\n", 1) == -1)
	{
	  status = errno;
		
	  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		    ("write failed: %s", strerror (status)));
	}
    }
  mu_stream_destroy (&stream);
  
  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE, ("Sending body..."));
  mu_message_get_body (msg, &body);
  status = mu_body_get_streamref (body, &stream);
  if (status)
    {
      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		("cannot get body stream: %s\n", mu_strerror (status)));
      return status;
    }

  mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
  while ((status = mu_stream_read (stream, buffer, sizeof (buffer),
				   &len)) == 0
	 && len != 0)
    {
      if (write (pm->fd, buffer, len) == -1)
	{
	  status = errno;
	  
	  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		    ("write failed: %s\n", strerror (status)));
	  break;
	}
    }
  mu_body_get_streamref (body, &stream);

  close (pm->fd);

  rc = waitpid (pm->pid, &exit_status, 0);
  if (status == 0)
    {
      if (rc < 0)
	{
	  if (errno == ECHILD)
	    status = 0;
	  else
	    { 
	      status = errno;
	      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
			("waitpid(%lu) failed: %s\n",
			 (unsigned long) pm->pid, strerror (status)));
	    }
	}
      else if (WIFEXITED (exit_status))
	{
	  exit_status = WEXITSTATUS (exit_status);
	  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE,
		    ("%s exited with: %d\n",
		     pm->command, exit_status));
	  status = (exit_status == 0) ? 0 : MU_ERR_PROCESS_EXITED;
	}
      else if (WIFSIGNALED (exit_status))
	status = MU_ERR_PROCESS_SIGNALED;
      else
	status = MU_ERR_PROCESS_UNKNOWN_FAILURE;
    }
  pm->pid = -1;
  return status;
}
Exemple #19
0
/* Copy SIZE bytes from SRC to DST.  If SIZE is 0, copy everything up to
   EOF. */
int
mu_stream_copy (mu_stream_t dst, mu_stream_t src, mu_off_t size,
		mu_off_t *pcsz)
{
  int status;
  size_t bufsize, n;
  char *buf;
  mu_off_t total = 0;
  
  if (pcsz)
    *pcsz = 0;
  if (size == 0)
    {
      status = mu_stream_size (src, &size);
      switch (status)
	{
	case 0:
	  break;

	case ENOSYS:
	  size = 0;
	  break;

	default:
	  return status;
	}
      
      if (size)
	{
	  mu_off_t pos;
	  status = mu_stream_seek (src, 0, MU_SEEK_CUR, &pos);
	  switch (status)
	    {
	    case 0:
	      if (pos > size)
		return ESPIPE;
	      size -= pos;
	      break;
      
	    case EACCES:
	      mu_stream_clearerr (src);
	    case ENOSYS:
	      size = 0;
	      break;

	    default:
	      return status;
	    }
	}
    }

  bufsize = size;
  if (!bufsize)
    bufsize = STREAMCPY_MAX_BUF_SIZE;

  for (; (buf = malloc (bufsize)) == NULL; bufsize >>= 1)
    if (bufsize < STREAMCPY_MIN_BUF_SIZE)
      return ENOMEM;

  if (size)
    while (size)
      {
	size_t rdsize = bufsize < size ? bufsize : size;

	status = mu_stream_read (src, buf, rdsize, &n);
	if (status)
	  break;
	if (n == 0)
	  break;
	status = mu_stream_write (dst, buf, n, NULL);
	if (status)
	  break;
	size -= n;
	total += n;
      }
  else
    while ((status = mu_stream_read (src, buf, bufsize, &n)) == 0
	   && n > 0)
      {
	status = mu_stream_write (dst, buf, n, NULL);
	if (status)
	  break;
	total += n;
      }

  if (pcsz)
    *pcsz = total;
  /* FIXME: When EOF error code is implemented:
  else if (total == 0)
    status = EOF;
  */
  free (buf);
  return status;
}
Exemple #20
0
/* FIXME: Do we need to escape body line that begins with "From "? This
   will require reading the body line by line instead of by chunks,
   considerably hurting perfomance when expunging.  But should not this
   be the responsibility of the client ?  */
static int
mbox_append_message0 (mu_mailbox_t mailbox, mu_message_t msg, mu_off_t *psize,
		      int is_expunging, int first)
{
  mbox_data_t mud = mailbox->data;
  int status = 0;
  size_t n = 0;
  char nl = '\n';
  size_t orig_size = *psize;
  char *s;
  
  switch (mud->state)
    {
    case MBOX_NO_STATE:
      mud->off = 0;
      mud->state = MBOX_STATE_APPEND_SENDER;

    case MBOX_STATE_APPEND_SENDER:
      /* Generate the sender for the "From " separator.  */
      {
	char *s;
	mu_envelope_t envelope = NULL;
	mu_message_get_envelope (msg, &envelope);
	status = mu_envelope_aget_sender (envelope, &mud->sender);
	switch (status) {
	case 0:
	  break;
	  
	case EAGAIN:
	  return status;

	case MU_ERR_NOENT: /* Envelope headers not found: try to guess */
	  free (mud->sender);
          mud->sender = NULL;
	  status = restore_sender (msg, mud);
	  if (status == 0)
            break;
	  
	default:
	  free (mud->sender);
	  free (mud->date);
	  mud->date = mud->sender = NULL;
	  mud->state = MBOX_NO_STATE;
	  return status;
	}

	/* Nuke trailing newline.  */
	s = strchr (mud->sender, nl);
	if (s)
	  *s = '\0';
	mud->state = MBOX_STATE_APPEND_DATE;
      }

    case MBOX_STATE_APPEND_DATE:
      /* Generate a date for the "From "  separator.  */
      {
	mu_envelope_t envelope = NULL;
	const char *envarr[5];
	
	mu_message_get_envelope (msg, &envelope);
	status = mu_envelope_aget_date (envelope, &mud->date);
	switch (status) {
	case 0:
	  break;
	  
	case EAGAIN:
	  return status;

	case MU_ERR_NOENT: /* Envelope headers not found: try to guess */
	  free (mud->date);
          mud->date = NULL;
	  status = restore_date (msg, mud);
	  if (status == 0)
	    break;
	  
	default:
	  free (mud->sender);
	  free (mud->date);
	  mud->date = mud->sender = NULL;
	  mud->state = MBOX_NO_STATE;
	  return status;
	}

	/* Nuke trailing newline.  */
	s = strchr (mud->date, nl);
	if (s)
	  *s = '\0';

	/* Write the separator to the mailbox.  */
	envarr[0] = "From ";
	envarr[1] = mud->sender;
	envarr[2] = " ";
	envarr[3] = mud->date;
	envarr[4] = "\n";
	
	status = write_array (mailbox->stream, psize, 5, envarr);
	if (status)
	  break;

	free (mud->sender);
	free (mud->date);
	mud->sender = mud->date = NULL;
	/* If we are not expunging get the message in one block via the stream
	   message instead of the header/body.  This is good for POP where
	   there is no separation between header and body(RETR).  */
	if (! is_expunging)
	  {
	    mud->state = MBOX_STATE_APPEND_MESSAGE;
	    break;
	  }
	mud->state = MBOX_STATE_APPEND_HEADER;
      }

    case MBOX_STATE_APPEND_HEADER:
      /* Append the Header.  */
      {
	char buffer[1024];
	size_t nread = 0;
	mu_stream_t is = NULL;
	mu_header_t header = NULL;
	mu_message_get_header (msg, &header);
	mu_header_get_stream (header, &is);
	do
	  {
	    status = mu_stream_readline (is, buffer, sizeof (buffer), mud->off,
					 &nread);
	    if (status != 0)
	      {
		if (status != EAGAIN)
		  {
		    mud->state = MBOX_NO_STATE;
		    mud->off = 0;
		  }
		mu_stream_truncate (mailbox->stream, orig_size);
		return status;
	      }
	    mud->off += nread;
	    if (*buffer == '\n')
	      break;

	    /* We do not copy the Status since it is rewritten by the
	       attribute code below. Ditto for X-UID and X-IMAPBase.
	       FIXME:
	       - We have a problem here the header may not fit the buffer.
	       - Should  we skip the IMAP "X-Status"? */
	    if ((mu_c_strncasecmp (buffer, "Status", 6) == 0)
		|| (mu_c_strncasecmp (buffer, "X-IMAPbase", 10) == 0)
		/* FIXME: isn't the length of "X-UID" 5, not 4? And
		 this will match X-UID and X-UIDL, is this intended? */
		|| (mu_c_strncasecmp (buffer, "X-UID", 4) == 0
		    && (buffer[5] == ':' || buffer[5] == ' '
			|| buffer[5] == '\t')))
	      continue;

	    status = mu_stream_write (mailbox->stream, buffer, nread,
				      *psize, &n);
	    if (status)
	      break;
	    *psize += n;
	  }
	while (nread > 0);
	mud->off = 0;

	/* Rewrite the X-IMAPbase marker. */
	if (first && is_expunging)
	  {
	    n = sprintf (buffer, "X-IMAPbase: %lu %u\n",
			 (unsigned long) mud->uidvalidity,
			 (unsigned) mud->uidnext);
	    status = mu_stream_write (mailbox->stream, buffer, n, *psize, &n);
	    if (status)
	      break;
	    *psize += n;
	  }
	mud->state = MBOX_STATE_APPEND_ATTRIBUTE;
      }

      case MBOX_STATE_APPEND_ATTRIBUTE:
      /* Put the new attributes.  */
      {
#define STATUS_PREFIX_LEN (sizeof(MU_HEADER_STATUS) - 1 + 2)
	char abuf[STATUS_PREFIX_LEN + MU_STATUS_BUF_SIZE + 1];
	size_t na = 0;
	mu_attribute_t attr = NULL;

	strcpy(abuf, MU_HEADER_STATUS);
	strcat(abuf, ": ");
	mu_message_get_attribute (msg, &attr);
	mu_attribute_to_string (attr, abuf + STATUS_PREFIX_LEN, 
	                        sizeof(abuf) - STATUS_PREFIX_LEN - 1, &na);
	strcat (abuf, "\n");
	na = strlen (abuf);
	mu_stream_write (mailbox->stream, abuf, na, *psize, &n);
	if (status != 0)
	  break;
	*psize += n;

	mud->state = MBOX_STATE_APPEND_UID;
      }

    case MBOX_STATE_APPEND_UID:
      /* The new X-UID. */
      {
	char suid[64];
	size_t uid = 0;
	if (is_expunging)
	  {
	    status = mu_message_get_uid (msg, &uid);
	    if (status == EAGAIN)
	      return status;
	  }
	else
	  uid = mud->uidnext++;

	if (status == 0 || uid != 0)
	  {
	    n = sprintf (suid, "X-UID: %u\n", (unsigned) uid);
	    /* Put the UID.  */
	    status = mu_stream_write (mailbox->stream, suid, n, *psize, &n);
	    if (status)
	      break;
	    *psize += n;
	  }

	/* New line separator of the Header.  */
	status = mu_stream_write (mailbox->stream, &nl , 1, *psize, &n);
	if (status)
	  break;
	*psize += n;
	mud->state = MBOX_STATE_APPEND_BODY;
      }

    case MBOX_STATE_APPEND_BODY:
      /* Append the Body.  */
      {
	char buffer[1024];
	size_t nread = 0;
	mu_stream_t is = NULL;
	mu_body_t body = NULL;
	mu_message_get_body (msg, &body);
	mu_body_get_stream (body, &is);
	do
	  {
	    status = mu_stream_read (is, buffer, sizeof (buffer), mud->off,
				     &nread);
	    if (status != 0)
	      {
		if (status != EAGAIN)
		  {
		    mud->state = MBOX_NO_STATE;
		    mud->off = 0;
		  }
		return status;
	      }
	    mud->off += nread;
	    status = mu_stream_write (mailbox->stream, buffer, nread,
				      *psize, &n);
	    if (status)
	      break;
	    *psize += n;
	  }
	while (nread > 0);
	mud->off = 0;
	n = 0;
	status = mu_stream_write (mailbox->stream, &nl, 1, *psize, &n);
	if (status)
	  break;
	*psize += n;
      }

    default:
      break;
    }

  /* If not expunging we are taking the stream message.  */
  if (!is_expunging)
    {
      switch (mud->state)
        {
	case MBOX_STATE_APPEND_MESSAGE:
	  {
	    /* Append the Message.  */
	    char buffer[1024];
	    size_t nread = 0;
	    mu_stream_t is = NULL;
	    mu_message_get_stream (msg, &is);
	    do
	      {
		status = mu_stream_read (is, buffer, sizeof (buffer), mud->off,
				      &nread);
		if (status != 0)
		  {
		    if (status != EAGAIN)
		      {
			mud->state = MBOX_NO_STATE;
			mud->off = 0;
		      }
		    mu_stream_truncate (mailbox->stream, orig_size);
		    return status;
		  }
		status = mu_stream_write (mailbox->stream, buffer, nread,
					  *psize, &n);
		if (status)
		  break;
		mud->off += nread;
		*psize += n;
	      }
	    while (nread > 0);
	    if (status)
	      break;
       	    status = mu_stream_write (mailbox->stream, &nl, 1, *psize, &n);
	    if (status == 0)
	      *psize += n;
	  }

	default:
	  break;
	}
    } /* is_expunging */
  mud->state = MBOX_NO_STATE;
  if (status)
    mu_stream_truncate (mailbox->stream, orig_size);
  else
    mu_stream_flush (mailbox->stream);
  return status;
}
Exemple #21
0
/* For the expunge bits  we took a very cautionnary approach, meaning
   we create a temporary mailbox in the tmpdir copy all the message not mark
   deleted(Actually we copy all the message that may have been modified
   i.e new header values set; UIDL or UID or etc ....
   and skip the deleted ones, truncate the real mailbox to the desired size
   and overwrite with the tmp mailbox.  The approach to do everyting
   in core is tempting but require
   - to much memory, it is not rare nowadays to have 30 Megs mailbox,
   - also there is danger for filesystems with quotas,
   - if we run out of disk space everything is lost.
   - or some program may not respect the advisory lock and decide to append
   a new message while your expunging etc ...
   The real downside to the approach is that when things go wrong
   the temporary file may be left in /tmp, which is not all that bad
   because at least, we have something to recuperate when failure.  */
static int
mbox_expunge0 (mu_mailbox_t mailbox, int remove_deleted)
{
  mbox_data_t mud = mailbox->data;
  mbox_message_t mum;
  int status = 0;
  sigset_t signalset;
  int tempfile;
  size_t i, j, dirty;  /* dirty will indicate the first modified message.  */
  mu_off_t marker = 0;    /* marker will be the position to truncate.  */
  mu_off_t total = 0;
  char *tmpmboxname = NULL;
  mu_mailbox_t tmpmailbox = NULL;
  size_t save_imapbase = 0;  /* uidvalidity is save in the first message.  */
#ifdef WITH_PTHREAD
  int state;
#endif

  if (mud == NULL)
    return EINVAL;

  MU_DEBUG1 (mailbox->debug, MU_DEBUG_TRACE1, "mbox_expunge (%s)\n", mud->name);

  /* Noop.  */
  if (mud->messages_count == 0)
    return 0;

  /* Find the first dirty(modified) message.  */
  for (dirty = 0; dirty < mud->messages_count; dirty++)
    {
      mum = mud->umessages[dirty];
      /* Message may have been tampered, break here.  */
      if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) ||
	  (mum->attr_flags & MU_ATTRIBUTE_DELETED) ||
	  (mum->message && mu_message_is_modified (mum->message)))
	break;
    }

  /* Did something change ?  */
  if (dirty == mud->messages_count)
    return 0; /* Nothing change, bail out.  */

  /* Create a temporary file.  */
  tempfile = mbox_tmpfile (mailbox, &tmpmboxname);
  if (tempfile == -1)
    {
      if (tmpmboxname)
	free (tmpmboxname);
      mu_error (_("failed to create temporary file when expunging"));
      return errno;
    }

  /* This is redundant, we go to the loop again.  But it's more secure here
     since we don't want to be disturb when expunging.  Destroy all the
     messages mark for deletion.  */
  if (remove_deleted)
    {
      for (j = 0; j < mud->messages_count; j++)
	{
	  mum = mud->umessages[j];
	  if (mum && mum->message && ATTRIBUTE_IS_DELETED (mum->attr_flags))
	    mu_message_destroy (&(mum->message), mum);
	}
    }

  /* Create temporary mu_mailbox_t.  */
  {
    mbox_data_t tmp_mud;
    char *m = malloc (5 + strlen (tmpmboxname) + 1);
    if (!m)
      return ENOMEM;
    /* Try via the mbox: protocol.  */
    sprintf (m, "mbox:%s", tmpmboxname);
    status = mu_mailbox_create (&tmpmailbox, m);
    if (status != 0)
      {
	/* Do not give up just yet, maybe they register the mu_path_record.  */
	sprintf (m, "%s", tmpmboxname);
	status = mu_mailbox_create (&tmpmailbox, m);
	if (status != 0)
	  {
	    /* Ok give up.  */
	    close (tempfile);
	    remove (tmpmboxname);
            free (m);
	    free (tmpmboxname);
	    return status;
	  }
      }
    free (m);
    /* Must be flag CREATE if not the mu_mailbox_open will try to mmap()
       the file.  */
    status = mu_mailbox_open (tmpmailbox, MU_STREAM_CREAT | MU_STREAM_RDWR);
    if (status != 0)
      {
	close (tempfile);
	remove (tmpmboxname);
	free (tmpmboxname);
	return status;
      }
    close (tempfile); /* This one is useless the mailbox have its own.  */
    tmp_mud = tmpmailbox->data;
    /* May need when appending.  */
    tmp_mud->uidvalidity = mud->uidvalidity;
    tmp_mud->uidnext = mud->uidnext;
  }

  /* Get the File lock.  */
  if ((status = mu_locker_lock (mailbox->locker)) != 0)
    {
      mu_mailbox_close (tmpmailbox);
      mu_mailbox_destroy (&tmpmailbox);
      remove (tmpmboxname);
      free (tmpmboxname);
      mu_error (_("failed to grab the lock: %s"), mu_strerror (status));
      return status;
    }

  /* Critical section, we can not allowed signal here.  */
#ifdef WITH_PTHREAD
  pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
#endif
  sigemptyset (&signalset);
  sigaddset (&signalset, SIGTERM);
  sigaddset (&signalset, SIGHUP);
  sigaddset (&signalset, SIGTSTP);
  sigaddset (&signalset, SIGINT);
  sigaddset (&signalset, SIGWINCH);
  sigprocmask (SIG_BLOCK, &signalset, 0);

  /* Set the marker position.  */
  marker = mud->umessages[dirty]->header_from;
  total = 0;

  /* Copy to the temporary mailbox emails not mark deleted.  */
  for (i = dirty; i < mud->messages_count; i++)
    {
      mum = mud->umessages[i];

      /* Skip it, if mark for deletion.  */
      if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags))
	{
	  /* We save the uidvalidity in the first message, if it is being
	     deleted we need to move the uidvalidity to the first available
	     (non-deleted) message.  */
	  if (i == save_imapbase)
	    {
	      save_imapbase = i + 1;
	      if (save_imapbase < mud->messages_count)
		(mud->umessages[save_imapbase])->attr_flags |= MU_ATTRIBUTE_MODIFIED;
	    }
	  continue;
	}

      /* Do the expensive mbox_append_message0() only if mark dirty.  */
      if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) ||
	  (mum->message && mu_message_is_modified (mum->message)))
	{
	  /* The message was not instantiated, probably the dirty flag was
	     set by mbox_scan(), create one here.  */
	  if (mum->message == 0)
	    {
	      mu_message_t msg;
	      status = mbox_get_message (mailbox, i + 1, &msg);
	      if (status != 0)
		{
		  mu_error (_("error expunging:%d: %s"), __LINE__,
			    mu_strerror (status));
		  goto bailout0;
		}
	    }
	  status = mbox_append_message0 (tmpmailbox, mum->message,
					 &total, 1, (i == save_imapbase));
	  if (status != 0)
	    {
	      mu_error (_("error expunging:%d: %s"), __LINE__,
		        mu_strerror (status));
	      goto bailout0;
	    }
	  /* Clear the dirty bits.  */
	  mum->attr_flags &= ~MU_ATTRIBUTE_MODIFIED;
	  mu_message_clear_modified (mum->message);
	}
      else
	{
	  /* Nothing changed copy the message straight.  */
	  char buffer [1024];
	  size_t n;
	  mu_off_t offset = mum->header_from;
	  size_t len = mum->body_end - mum->header_from;
	  while (len > 0)
	    {
	      n = (len < sizeof (buffer)) ? len : sizeof (buffer);
	      if ((status = mu_stream_read (mailbox->stream, buffer, n, offset,
					 &n) != 0)
		  || (status = mu_stream_write (tmpmailbox->stream, buffer, n,
					     total, &n) != 0))
		{
		  mu_error (_("error expunging:%d: %s"), __LINE__,
			    mu_strerror (status));
		  goto bailout0;
		}
	      len -= n;
	      total += n;
	      offset += n;
	    }
	  /* Add the newline separator.  */
	  status = mu_stream_write (tmpmailbox->stream, "\n", 1, total, &n);
	  if (status != 0)
	    {
	      mu_error (_("error expunging:%d: %s"), __LINE__,
		        mu_strerror (status));
	      goto bailout0;
	    }
	  total++;
	}
    } /* for (;;) */

  /* Caution: before ftruncate()ing the file see
     - if we've receive new mails.  Some programs may not respect the lock,
     - or the lock was held for too long.
     - The mailbox may not have been properly updated before expunging.  */
  {
    mu_off_t size = 0;
    if (mu_stream_size (mailbox->stream, &size) == 0)
      {
	mu_off_t len = size - mud->size;
	mu_off_t offset = mud->size;
	char buffer [1024];
	size_t n = 0;
	if (len > 0 )
	  {
	    while ((status = mu_stream_read (mailbox->stream, buffer,
					  sizeof (buffer), offset, &n)) == 0
		   && n > 0)
	      {
		status = mu_stream_write (tmpmailbox->stream, buffer, n,
				       total, &n);
		if (status != 0)
		  {
		    mu_error (_("error expunging:%d: %s"), __LINE__,
			      mu_strerror (status));
		    goto bailout0;
		  }
		total += n;
		offset += n;
	      }
	  }
	else if (len < 0)
	  {
	    /* Corrupted mailbox.  */
	    mu_error (_("error expunging:%d: %s"), __LINE__,
		      mu_strerror (status));
	    goto bailout0;
	  }
      }
  } /* End of precaution.  */

  /* Seek and rewrite it.  */
  if (total > 0)
    {
      char buffer [1024];
      size_t n = 0;
      mu_off_t off = 0;
      mu_off_t offset = marker;
      while ((status = mu_stream_read (tmpmailbox->stream, buffer,
				    sizeof (buffer), off, &n)) == 0
	     && n > 0)
	{
	  status = mu_stream_write (mailbox->stream, buffer, n, offset, &n);
	  if (status != 0)
	    {
	      mu_error (_("error expunging:%d: %s"), __LINE__,
		        mu_strerror (status));
	      goto bailout;
	    }
	  off += n;
	  offset += n;
	}
    }

  /* Flush/truncation. Need to flush before truncate.  */
  mu_stream_flush (mailbox->stream);
  status = mu_stream_truncate (mailbox->stream, total + marker);
  if (status != 0)
    {
      mu_error (_("error expunging:%d: %s"), __LINE__,
	        mu_strerror (status));
      goto bailout;
    }

  /* Don't remove the tmp mbox in case of errors, when writing back.  */
 bailout0:
  remove (tmpmboxname);

 bailout:

  free (tmpmboxname);
  /* Release the File lock.  */
  mu_locker_unlock (mailbox->locker);
  mu_mailbox_close (tmpmailbox);
  mu_mailbox_destroy (&tmpmailbox);

  /* Reenable signal.  */
#ifdef WITH_PTHREAD
  pthread_setcancelstate (state, &state);
#endif
  sigprocmask (SIG_UNBLOCK, &signalset, 0);

  /* We need to readjust the pointers.
     It is a little hairy because we need to keep the message pointers alive
     So we are going through the array and "defragmentize".  For example
     in (1 2 3 4) if 2 was deleted we need to move 3 4 up by one etc ..
  */
  if (status == 0)
    {
      size_t dlast;
      mu_monitor_wrlock (mailbox->monitor);
      for (j = dirty, dlast = mud->messages_count - 1;
	   j <= dlast; j++)
	{
	  /* Clear all the references, any attach messages been already
	     destroy above.  */
	  mum = mud->umessages[j];
	  if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags))
	    {
	      if ((j + 1) <= dlast)
		{
		  /* Move all the pointers up.  So the message pointer
		     part of mum will be at the right position.  */
		  memmove (mud->umessages + j, mud->umessages + j + 1,
			   (dlast - j) * sizeof (mum));
#if 0
		  mum->header_from = mum->header_from_end = 0;
		  mum->body = mum->body_end = 0;
		  mum->header_lines = mum->body_lines = 0;
#endif
		  memset (mum, 0, sizeof (*mum));
		  /* We are not free()ing the useless mum, but instead
		     we put it back in the pool, to be reuse.  */
		  mud->umessages[dlast] = mum;
		  dlast--;
		  /* Set mum to the new value after the memmove so it
		     gets cleared to.  */
		  mum = mud->umessages[j];
		}
	      else
		{
		  memset (mum, 0, sizeof (*mum));
		}
	    }
	  mum->header_from = mum->header_from_end = 0;
	  mum->body = mum->body_end = 0;
	  mum->header_lines = mum->body_lines = 0;
	}
      mu_monitor_unlock (mailbox->monitor);
      /* This is should reset the messages_count, the last argument 0 means
	 not to send event notification.  */
      mbox_scan0 (mailbox, dirty, NULL, 0);
    }
  return status;
}
Exemple #22
0
int
mu_imapio_getline (struct _mu_imapio *io)
{
  int rc;
  char *last_arg;
  int xlev = MU_XSCRIPT_NORMAL;
  
  if (io->_imap_reply_ready)
    {
      mu_wordsplit_free_words (&io->_imap_ws);
      io->_imap_reply_ready = 0;
    }
  
  for (;;)
    {
      rc = mu_stream_getline (io->_imap_stream,
			      &io->_imap_buf_base, &io->_imap_buf_size,
			      &io->_imap_buf_level);
      if (rc)
	break;
      if (io->_imap_buf_level == 0)
	break;
      io->_imap_buf_level = mu_rtrim_class (io->_imap_buf_base,
					    MU_CTYPE_ENDLN);
      
      rc = initial_parse (io);
      if (rc == IMAPIO_ERR)
	{
	  rc = MU_ERR_PARSE;
	  break;
	}
      else if (rc == IMAPIO_RESP)
	{
	  rc = 0;
	  break;
	}
	
      rc = mu_wordsplit_len (io->_imap_buf_base + io->_imap_ws.ws_endp,
			     io->_imap_buf_level - io->_imap_ws.ws_endp,
			     &io->_imap_ws, io->_imap_ws_flags);
      if (rc)
	{
	  rc = MU_ERR_PARSE;
	  break;
	}
      
      if (io->_imap_ws.ws_wordc == 0)
	break;
      
      last_arg = io->_imap_ws.ws_wordv[io->_imap_ws.ws_wordc - 1];
      if (last_arg[0] == '{' && last_arg[strlen (last_arg)-1] == '}')
	{
	  int rc;
	  unsigned long number;
	  char *sp = NULL;

	  if (!io->_imap_trace_payload)
	    xlev = mu_imapio_set_xscript_level (io, MU_XSCRIPT_PAYLOAD);
	  
	  number = strtoul (last_arg + 1, &sp, 10);
	  /* Client can ask for non-synchronised literal,
	     if a '+' is appended to the octet count. */
	  if (*sp == '}')
	    {
	      if (io->_imap_server)
		mu_stream_printf (io->_imap_stream, "+ GO AHEAD\n");
	    }
	  else if (*sp != '+')
	    break;

	  if (number + 1 > io->_imap_buf_size)
	    {
	      size_t newsize = number + 1;
	      void *newp = realloc (io->_imap_buf_base, newsize);
	      if (!newp)
		{
		  rc = ENOMEM;
		  break;
		}
	      io->_imap_buf_base = newp;
	      io->_imap_buf_size = newsize;
	    }
	      
          for (io->_imap_buf_level = 0; io->_imap_buf_level < number; )
            {
               size_t sz;
	       rc = mu_stream_read (io->_imap_stream,
				    io->_imap_buf_base + io->_imap_buf_level,
				    number - io->_imap_buf_level,
				    &sz);
               if (rc || sz == 0)
                 break;
               io->_imap_buf_level += sz;
            }
	  mu_imapio_set_xscript_level (io, xlev);
	  if (rc)
	    break;
	  io->_imap_buf_base[io->_imap_buf_level++] = 0;

	  free (last_arg);
	  io->_imap_ws.ws_wordv[--io->_imap_ws.ws_wordc] = NULL;
	  if (mu_wordsplit_len (io->_imap_buf_base, io->_imap_buf_level,
				&io->_imap_ws,
				io->_imap_ws_flags|MU_WRDSF_NOSPLIT))
	    {
	      rc = MU_ERR_PARSE;
	      break;
	    }
	}
      else
	break;
    }

  if (!io->_imap_trace_payload)
    mu_imapio_set_xscript_level (io, xlev);

  io->_imap_reply_ready = 1;
  return rc;
}