commit-mailutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Mailutils branch, master, updated. release-2.2-269-g04c2931


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. release-2.2-269-g04c2931
Date: Tue, 07 Dec 2010 16:17:23 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Mailutils".

http://git.savannah.gnu.org/cgit/mailutils.git/commit/?id=04c2931bb963fefd4654ef2de2cf0667cf8a333d

The branch, master has been updated
       via  04c2931bb963fefd4654ef2de2cf0667cf8a333d (commit)
       via  ac08a8575ae8ea3b85180bd05464a48768f398ee (commit)
       via  c2b4805128f2619235396726a116331606729e62 (commit)
      from  da9c204ef4ff9b89863a15bd3346331acc7b5679 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 04c2931bb963fefd4654ef2de2cf0667cf8a333d
Author: Sergey Poznyakoff <address@hidden>
Date:   Tue Dec 7 17:58:15 2010 +0200

    Rewrite prog stream support.
    
    Two interfaces are provided: mu_prog_stream_create offers full control
    over the program execution environment (i.e. running privileges, cwd,
    resource limits) via a set of hints.
    A simpler interface, mu_command_stream_create, runs the command in the
    current environment.
    
    mu_filter_prog_stream_create is removed, because its functionality can
    be achieved by a correspondingly crafted set of hints to
    mu_prog_stream_create.
    
    * include/mailutils/prog.h: New file.
    * include/mailutils/mailutils.h: Include mailutils/prog.h
    * include/mailutils/Makefile.am (pkginclude_HEADERS): Add prog.h
    * include/mailutils/stream.h (mu_prog_stream_create)
    (mu_filter_prog_stream_create): Remove prototypes.
    * include/mailutils/sys/prog_stream.h (_mu_prog_stream): Change structure.
    (_mu_prog_limit_codes, _mu_prog_limit_flags): New externs.
    * include/mailutils/util.h (mu_set_user_privileges)
    (mu_switch_to_privs): New prototypes.
    
    * lib/userprivs.c: Move to libmailutils/base.
    * lib/Makefile.am (libmuaux_a_SOURCES): Remove userprivs.c
    * libmailutils/base/Makefile.am (libbase_la_SOURCES): Add userprivs.c
    * libmailutils/base/userprivs.c (mu_set_user_privileges): New function.
    (mu_switch_to_privs): Rewrite as another entry point to 
mu_set_user_privileges.
    
    * libmailutils/stream/prog_stream.c (_mu_prog_limit_flags)
    (_mu_prog_limit_codes): New global variables.
    (start_program_filter): Use hints to control execution environment.
    (_prog_stream_create): Save hints.
    (mu_prog_stream_create): Change signature.
    (mu_command_stream_create): New function (corresponds to the prior
    mu_prog_stream_create).
    (mu_filter_prog_stream_create): Remove function.
    * comsat/action.c (action_exec): Use new mu_prog_stream_create calling
    convention.
    * examples/murun.c: Rewrite.
    
    * mh/mhn.c (show_internal): Use new mu_prog_stream_create calling
    convention.
    * mh/tests/mhn.at: Reflect changes to mhn.
    
    * imap4d/preauth.c: Use mu_command_stream_create.
    * libmu_sieve/extensions/pipe.c
    * mail/decode.c
    * mail/pipe.c
    * mail/send.c
    * mh/mhl.c
    * mu/shell.c
    
    * mail/mail.h: Include mailutils/prog.h
    * mh/mh.h
    
    * po/POTFILES.in: Add libmailutils/base/userprivs.c.

commit ac08a8575ae8ea3b85180bd05464a48768f398ee
Author: Sergey Poznyakoff <address@hidden>
Date:   Tue Dec 7 13:45:34 2010 +0200

    comsat: Use mu streams instead of stdio
    
    * comsat/action.c (action_beep, echo_string)
    (action_echo, action_exec): Take mu_stream_t as first argument.
    (open_rc): Take mu_stream_t as tty, return mu_stream_t as well.
    Install the linecon filter.
    (run_user_action): Take mu_stream_t as first arg.  Pass it to all
    invoked actions.
    * comsat/comsat.c (get_newline_str): Remove.
    (need_crlf): New function.
    (notify_user): Use mu_stream_t instead of FILE.
    Install a 7bit filter and CRLF filter (if the device requires it).
    * comsat/comsat.h: Include mailutils/filter.h.
    (run_user_action): Change signature.

commit c2b4805128f2619235396726a116331606729e62
Author: Sergey Poznyakoff <address@hidden>
Date:   Tue Dec 7 13:22:44 2010 +0200

    file_stream: Bugfixes
    
    * libmailutils/stream/file_stream.c (fd_open): Remove file state
    checks: it is the responsibility of the caller.
    Set autoclose on the *stream* flags (bugfix, previously the underlying
    fd was never closed).
    Clear MU_STREAM_SEEK flag if seek fails.
    (_mu_file_stream_create): Remove assignment to stream.error_string
    * include/mailutils/stream.h (MU_STREAM_ALLOW_LINKS): Remove flag.
    * libmailutils/base/amd.c (amd_message_stream_open): Initialize flags
    to 0.

-----------------------------------------------------------------------

Summary of changes:
 comsat/action.c                        |  114 ++++++------
 comsat/comsat.c                        |   60 +++++--
 comsat/comsat.h                        |    3 +-
 examples/murun.c                       |  171 ++++++++++++++---
 imap4d/preauth.c                       |    2 +-
 include/mailutils/Makefile.am          |    1 +
 include/mailutils/mailutils.h          |    1 +
 include/mailutils/prog.h               |   70 +++++++
 include/mailutils/stream.h             |    5 +-
 include/mailutils/sys/prog_stream.h    |   17 ++-
 include/mailutils/util.h               |    4 +-
 lib/Makefile.am                        |    1 -
 libmailutils/base/Makefile.am          |    1 +
 libmailutils/base/amd.c                |    2 +-
 {lib => libmailutils/base}/userprivs.c |  146 ++++++++++-----
 libmailutils/stream/file_stream.c      |   49 +-----
 libmailutils/stream/prog_stream.c      |  322 ++++++++++++++++++++++++++------
 libmu_sieve/extensions/pipe.c          |    3 +-
 mail/decode.c                          |    5 +-
 mail/mail.h                            |    1 +
 mail/pipe.c                            |    2 +-
 mail/send.c                            |    2 +-
 mh/mh.h                                |    1 +
 mh/mhl.c                               |    2 +-
 mh/mhn.c                               |   36 +++-
 mh/tests/mhn.at                        |    4 +-
 mu/shell.c                             |    2 +-
 po/POTFILES.in                         |    1 +
 28 files changed, 734 insertions(+), 294 deletions(-)
 create mode 100644 include/mailutils/prog.h
 rename {lib => libmailutils/base}/userprivs.c (63%)

diff --git a/comsat/action.c b/comsat/action.c
index 483b057..dcb2563 100644
--- a/comsat/action.c
+++ b/comsat/action.c
@@ -17,7 +17,8 @@
 
 #include "comsat.h"
 #include <mailutils/io.h>
-#include <mailutils/filter.h>
+#include <mailutils/argcv.h>
+#include <mailutils/prog.h>
 #define obstack_chunk_alloc malloc
 #define obstack_chunk_free free
 #include <obstack.h>
@@ -188,65 +189,45 @@ const char *default_action =
 "$B(,5)\n"
 "---\n";
 
-/* Take care to clear eighth bit, so we won't upset some stupid terminals */
-#define LB(c) ((c)&0x7f)
-
 static void
-action_beep (FILE *tty)
+action_beep (mu_stream_t tty)
 {
-  fprintf (tty, "\a\a");
+  mu_stream_write (tty, "\a\a", 2, NULL);
+  mu_stream_flush (tty);
 }
 
 static void
-echo_string (FILE *tty, const char *cr, char *str)
+echo_string (mu_stream_t tty, char *str)
 {
   if (!str)
     return;
-  for (; *str; str++)
-    {
-      if (*str == '\n')
-       fprintf (tty, "%s", cr);
-      else
-       {
-         char c = LB (*str);
-         putc (c, tty);
-       }
-    }
-  fflush (tty);
+  mu_stream_write (tty, str, strlen (str), NULL);
 }
 
 static void
-action_echo (FILE *tty, const char *cr, int omit_newline,
-            int argc, char **argv)
+action_echo (mu_stream_t tty, int omit_newline, int argc, char **argv)
 {
   int i;
 
-  if (omit_newline)
-    {
-      argc--;
-      argv++;
-    }
-  
   for (i = 0;;)
     {
-      echo_string (tty, cr, argv[i]);
+      echo_string (tty, argv[i]);
       if (++i < argc)
-       echo_string (tty, cr, " ");
+       echo_string (tty, " ");
       else
-       {
-         if (!omit_newline)
-           echo_string (tty, cr, "\n");
-         break;
-       }
+       break;
     }
+  if (!omit_newline)
+    echo_string (tty, "\n");
 }
 
 static void
-action_exec (FILE *tty, int argc, char **argv)
+action_exec (mu_stream_t tty, int argc, char **argv)
 {
-  pid_t pid;
+  mu_stream_t pstream;
   struct stat stb;
-
+  int status;
+  
   if (argc == 0)
     {
       mu_diag_output (MU_DIAG_ERROR, _("no arguments for exec"));
@@ -268,26 +249,28 @@ action_exec (FILE *tty, int argc, char **argv)
 
   if (stb.st_mode & (S_ISUID|S_ISGID))
     {
-      mu_diag_output (MU_DIAG_ERROR, _("will not execute set[ug]id programs"));
+      mu_diag_output (MU_DIAG_ERROR,
+                     _("will not execute set[ug]id programs"));
       return;
     }
 
-  pid = fork ();
-  if (pid == 0)
+  status = mu_prog_stream_create (&pstream,
+                                 argv[0], argc, argv,
+                                 MU_PROG_HINT_ERRTOOUT,
+                                 NULL,
+                                 MU_STREAM_READ);
+  if (status)
     {
-      close (1);
-      close (2);
-      dup2 (fileno (tty), 1);
-      dup2 (fileno (tty), 2);
-      fclose (tty);
-      execv (argv[0], argv);
-      mu_diag_output (MU_DIAG_ERROR, _("cannot execute %s: %s"), argv[0], 
strerror (errno));
-      exit (0);
+      mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", argv[0],
+                      status);
+      return;
     }
+  mu_stream_copy (tty, pstream, 0, NULL);
+  mu_stream_destroy (&pstream);
 }
 
 static mu_stream_t
-open_rc (const char *filename, FILE *tty)
+open_rc (const char *filename, mu_stream_t tty)
 {
   struct stat stb;
   struct passwd *pw = getpwnam (username);
@@ -307,8 +290,8 @@ open_rc (const char *filename, FILE *tty)
        }
       if ((stb.st_mode & 0777) != 0600)
        {
-         fprintf (tty, "%s\r\n",
-                  _("Warning: your .biffrc has wrong permissions"));
+         mu_stream_printf (tty, "%s\n",
+                           _("Warning: your .biffrc has wrong permissions"));
          mu_diag_output (MU_DIAG_NOTICE, _("%s's %s has wrong permissions"),
                  username, filename);
          return NULL;
@@ -319,8 +302,8 @@ open_rc (const char *filename, FILE *tty)
     {
       if (rc != ENOENT)
        {
-         fprintf (tty, _("Cannot open .biffrc file: %s\r\n"),
-                  mu_strerror (rc));
+         mu_stream_printf (tty, _("Cannot open .biffrc file: %s\n"),
+                           mu_strerror (rc));
          mu_diag_output (MU_DIAG_NOTICE, _("cannot open %s for %s: %s"),
                          filename, username, mu_strerror (rc));
        }
@@ -331,8 +314,9 @@ open_rc (const char *filename, FILE *tty)
   mu_stream_unref (input);
   if (rc)
     {
-      fprintf (tty, _("Cannot create filter for your .biffrc file: %s\r\n"),
-              mu_strerror (rc));
+      mu_stream_printf (tty,
+                       _("Cannot create filter for your .biffrc file: %s\n"),
+                       mu_strerror (rc));
       mu_diag_output (MU_DIAG_NOTICE,
                      _("cannot create filter for file %s of %s: %s"),
                      filename, username, mu_strerror (rc));
@@ -342,7 +326,7 @@ open_rc (const char *filename, FILE *tty)
 }
 
 void
-run_user_action (FILE *tty, const char *cr, mu_message_t msg)
+run_user_action (mu_stream_t tty, mu_message_t msg)
 {
   mu_stream_t input;
   int nact = 0;
@@ -403,8 +387,14 @@ run_user_action (FILE *tty, const char *cr, mu_message_t 
msg)
                  
                  if (strcmp (ws.ws_wordv[0], "echo") == 0)
                    {
-                     action_echo (tty, cr, n_option,
-                                  ws.ws_wordc - 1, ws.ws_wordv + 1);
+                     int argc = ws.ws_wordc - 1;
+                     char **argv = ws.ws_wordv + 1;
+                     if (n_option)
+                       {
+                         argc--;
+                         argv++;
+                       }
+                     action_echo (tty, n_option, argc, argv);
                      nact++;
                    }
                  else if (strcmp (ws.ws_wordv[0], "exec") == 0)
@@ -414,9 +404,9 @@ run_user_action (FILE *tty, const char *cr, mu_message_t 
msg)
                    }
                  else
                    {
-                     fprintf (tty, _(".biffrc:%d: unknown keyword"),
-                              locus.mu_line);
-                     fprintf (tty, "\r\n");
+                     mu_stream_printf (tty,
+                                       _(".biffrc:%d: unknown keyword\n"),
+                                       locus.mu_line);
                      mu_diag_output (MU_DIAG_ERROR, _("unknown keyword %s"),
                                      ws.ws_wordv[0]);
                      break;
@@ -424,6 +414,8 @@ run_user_action (FILE *tty, const char *cr, mu_message_t 
msg)
                } 
            }
          mu_wordsplit_free (&ws);
+         /* FIXME: line number is incorrect if .biffrc contains
+            escaped newlines */
          locus.mu_line++;
        }
       mu_stream_destroy (&input);
@@ -433,5 +425,5 @@ run_user_action (FILE *tty, const char *cr, mu_message_t 
msg)
     }
 
   if (nact == 0)
-    echo_string (tty, cr, expand_line (default_action, msg));
+    echo_string (tty, expand_line (default_action, msg));
 }
diff --git a/comsat/comsat.c b/comsat/comsat.c
index 8a22a3f..faf18be 100644
--- a/comsat/comsat.c
+++ b/comsat/comsat.c
@@ -358,19 +358,22 @@ comsat_connection (int fd, struct sockaddr *sa, int salen,
   return 0;
 }
 
-static const char *
-get_newline_str (FILE *fp)
+static int
+need_crlf (mu_stream_t str)
 {
 #if defined(OPOST) && defined(ONLCR)
+  mu_transport_t trans[2];
   struct termios tbuf;
 
-  tcgetattr (fileno (fp), &tbuf);
-  if ((tbuf.c_oflag & OPOST) && (tbuf.c_oflag & ONLCR))
-    return "\n";
+  if (mu_stream_ioctl (str, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, trans))
+    return 1; /* suppose we do need it */
+  if (tcgetattr ((int)trans[0], &tbuf) == 0 &&
+      (tbuf.c_oflag & OPOST) && (tbuf.c_oflag & ONLCR))
+    return 0;
   else
-    return "\r\n";
+    return 1;
 #else
-  return "\r\n"; /* Just in case */
+  return 1; /* Just in case */
 #endif
 }
 
@@ -380,21 +383,43 @@ static void
 notify_user (const char *user, const char *device, const char *path,
             mu_message_qid_t qid)
 {
-  FILE *fp;
-  const char *cr;
+  mu_stream_t out, dev;
   mu_mailbox_t mbox = NULL;
   mu_message_t msg;
   int status;
 
   if (change_user (user))
     return;
-  if ((fp = fopen (device, "w")) == NULL)
+  status = mu_file_stream_create (&dev, device, MU_STREAM_WRITE);
+  if (status)
     {
-      mu_error (_("cannot open device %s: %s"), device, mu_strerror (errno));
+      mu_error (_("cannot open device %s: %s"), device, mu_strerror (status));
       return;
     }
-
-  cr = get_newline_str (fp);
+  mu_stream_set_buffer (dev, mu_buffer_line, 0);
+  
+  status = mu_filter_create (&out, dev, "7bit", MU_FILTER_ENCODE,
+                            MU_STREAM_WRITE);
+  mu_stream_unref (dev);
+  if (status)
+    {
+      mu_error (_("cannot create 7bit filter: %s"), mu_strerror (status));
+      return;      
+    }
+  
+  if (need_crlf (out))
+    {
+      mu_stream_t str;
+      status = mu_filter_create (&str, out, "CRLF", MU_FILTER_ENCODE,
+                                MU_STREAM_WRITE);
+      mu_stream_unref (out);
+      if (status)
+       {
+         mu_error (_("cannot create crlf filter: %s"), mu_strerror (status));
+         return;
+       }
+      out = str;
+    }
 
   if (!path)
     {
@@ -403,8 +428,9 @@ notify_user (const char *user, const char *device, const 
char *path,
        return;
     }
 
-  if ((status = mu_mailbox_create (&mbox, path)) != 0
-      || (status = mu_mailbox_open (mbox, MU_STREAM_READ|MU_STREAM_QACCESS)) 
!= 0)
+  if ((status = mu_mailbox_create (&mbox, path)) != 0 ||
+      (status = mu_mailbox_open (mbox,
+                                MU_STREAM_READ|MU_STREAM_QACCESS)) != 0)
     {
       mu_error (_("cannot open mailbox %s: %s"),
              path, mu_strerror (status));
@@ -419,8 +445,8 @@ notify_user (const char *user, const char *device, const 
char *path,
       return; /* FIXME: Notify the user, anyway */
     }
 
-  run_user_action (fp, cr, msg);
-  fclose (fp);
+  run_user_action (out, msg);
+  mu_stream_destroy (&out);
 }
 
 /* Search utmp for the local user */
diff --git a/comsat/comsat.h b/comsat/comsat.h
index 85c09fd..e23aaba 100644
--- a/comsat/comsat.h
+++ b/comsat/comsat.h
@@ -60,6 +60,7 @@
 #include <mailutils/acl.h>
 #include <mailutils/server.h>
 #include <mailutils/cctype.h>
+#include <mailutils/filter.h>
 
 #ifndef INADDR_NONE
 # define INADDR_NONE -1
@@ -77,5 +78,5 @@ extern const char *username;
 extern char *hostname;
 extern struct daemon_param daemon_param;
 
-void run_user_action (FILE *tty, const char *cr, mu_message_t msg);
+void run_user_action (mu_stream_t str, mu_message_t msg);
 
diff --git a/examples/murun.c b/examples/murun.c
index 71e2a10..af50624 100644
--- a/examples/murun.c
+++ b/examples/murun.c
@@ -24,20 +24,7 @@
 #include <ctype.h>
 #include <string.h>
 #include <mailutils/mailutils.h>
-#include <mailutils/argcv.h>
-
-static void
-read_and_print (mu_stream_t in, mu_stream_t out)
-{
-  size_t size;
-  char buffer[128];
-  
-  while (mu_stream_readline (in, buffer, sizeof (buffer), &size) == 0
-        && size > 0)
-    {
-      mu_stream_write (out, buffer, size, NULL);
-    }
-}
+#include <mailutils/sys/prog_stream.h>
 
 int
 main (int argc, char *argv[])
@@ -45,34 +32,154 @@ main (int argc, char *argv[])
   int rc;
   mu_stream_t stream, out;
   int read_stdin = 0;
-  int i = 1;
-  char *cmdline;
+  int i;
   int flags = MU_STREAM_READ;
-
-  if (argc > 1 && strcmp (argv[i], "--stdin") == 0)
+  struct mu_prog_hints hints;
+  int hint_flags = 0;
+  char *progname = NULL;
+  gid_t gid[20];
+  size_t gn = 0;
+  
+  for (i = 1; i < argc; i++)
     {
-      read_stdin = 1;
-      flags |= MU_STREAM_WRITE;
-      i++;
+      if (strcmp (argv[i], "--stdin") == 0)
+       {
+         read_stdin = 1;
+         flags |= MU_STREAM_WRITE;
+       }
+      else if (strcmp (argv[i], "--chdir") == 0)
+       {
+         hints.mu_prog_workdir = argv[i+1];
+         hint_flags |= MU_PROG_HINT_WORKDIR;
+         i++;
+       }
+      else if (strncmp (argv[i], "--limit", 7) == 0
+              && mu_isdigit (argv[i][7]))
+       {
+         int n;
+
+         if (i + 1 == argc)
+           {
+             fprintf (stderr, "%s requires argument\n", argv[i]);
+             exit (1);
+           }
+         n = argv[i][7] - '0';
+         if (!(_mu_prog_limit_flags & MU_PROG_HINT_LIMIT(n)))
+           {
+             fprintf (stderr, "%s is not supported\n", argv[i]+2);
+             continue;
+           }
+         hint_flags |= MU_PROG_HINT_LIMIT(n);
+         hints.mu_prog_limit[n] = strtoul (argv[i+1], NULL, 10);
+         i++;
+       }
+      else if (strcmp (argv[i], "--prio") == 0)
+       {
+         if (i + 1 == argc)
+           {
+             fprintf (stderr, "%s requires argument\n", argv[i]);
+             exit (1);
+           }
+         hint_flags |= MU_PROG_HINT_PRIO;
+         hints.mu_prog_prio = strtoul (argv[i+1], NULL, 10);
+         i++;
+       }
+      else if (strcmp (argv[i], "--exec") == 0)
+       {
+         if (i + 1 == argc)
+           {
+             fprintf (stderr, "%s requires argument\n", argv[i]);
+             exit (1);
+           }
+         progname = argv[++i];
+       }
+      else if (strcmp (argv[i], "--errignore") == 0)
+       hint_flags |= MU_PROG_HINT_IGNOREFAIL;
+      else if (strcmp (argv[i], "--uid") == 0)
+       {
+         if (i + 1 == argc)
+           {
+             fprintf (stderr, "%s requires argument\n", argv[i]);
+             exit (1);
+           }
+         hint_flags |= MU_PROG_HINT_UID;
+         hints.mu_prog_uid = strtoul (argv[i+1], NULL, 10);
+         i++;
+       }
+      else if (strcmp (argv[i], "--gid") == 0)
+       {
+         mu_list_t list;
+         mu_iterator_t itr;
+         
+         if (i + 1 == argc)
+           {
+             fprintf (stderr, "%s requires argument\n", argv[i]);
+             exit (1);
+           }
+         mu_list_create (&list);
+         mu_list_set_destroy_item (list, mu_list_free_item);
+         rc = mu_string_split (argv[++i], ",", list);
+         if (mu_list_get_iterator (list, &itr) == 0)
+           {
+             char *p;
+             
+             for (mu_iterator_first (itr);
+                  !mu_iterator_is_done (itr); mu_iterator_next (itr))
+               {
+                 if (gn >= MU_ARRAY_SIZE (gid))
+                   {
+                     fprintf (stderr, "too many gids\n");
+                     exit (1);
+                   }
+                 gid[gn++] = strtoul (p, NULL, 10);
+               }
+             mu_iterator_destroy (&itr);
+           }
+         else
+           {
+             mu_diag_funcall (MU_DIAG_ERROR, "mu_list_get_iterator", NULL,
+                              rc);
+             exit (1);
+           }
+         mu_list_destroy (&list);
+         hint_flags |= MU_PROG_HINT_GID;
+       }
+      else if (strcmp (argv[i], "--") == 0)
+       {
+         i++;
+         break;
+       }
+      else
+       break;
     }
   
   if (i == argc)
     {
-      fprintf (stderr, "Usage: %s [--stdin] progname [args]\n", argv[0]);
+      fprintf (stderr,
+              "Usage: %s [--stdin] [--chdir dir] [--limit{0-9} lim] [--prio 
N]\n"
+              "          [--exec progname] progname [args]\n", argv[0]);
       exit (1);
     }
 
-  MU_ASSERT (mu_argcv_string (argc - i, &argv[i], &cmdline));
+  argc -= i;
+  argv += i;
+
+  if (!progname)
+    progname = argv[0];
+  
   if (read_stdin)
     {
-      mu_stream_t in;
-      MU_ASSERT (mu_stdio_stream_create (&in, MU_STDIN_FD, 0));
-      rc = mu_filter_prog_stream_create (&stream, cmdline, in);
-      /* Make sure closing/destroying stream will close/destroy in */
-      mu_stream_unref (in);
+      MU_ASSERT (mu_stdio_stream_create (&hints.mu_prog_input,
+                                        MU_STDIN_FD, 0));
+      hint_flags |= MU_PROG_HINT_INPUT;
     }
-  else
-    rc = mu_prog_stream_create (&stream, cmdline, flags);
+
+  rc = mu_prog_stream_create (&stream, progname, argc, argv,
+                             hint_flags, &hints, flags);
+  if (hint_flags & MU_PROG_HINT_INPUT)
+    /* Make sure closing/destroying stream will close/destroy input */
+    mu_stream_unref (hints.mu_prog_input);
+
   if (rc)
     {
       fprintf (stderr, "%s: cannot create program filter stream: %s\n",
@@ -81,8 +188,8 @@ main (int argc, char *argv[])
     }
   
   MU_ASSERT (mu_stdio_stream_create (&out, MU_STDOUT_FD, 0));
-  
-  read_and_print (stream, out);
+
+  mu_stream_copy (out, stream, 0, NULL);
   mu_stream_close (stream);
   mu_stream_destroy (&stream);
   mu_stream_close (out);
diff --git a/imap4d/preauth.c b/imap4d/preauth.c
index ef36e72..63cc5dc 100644
--- a/imap4d/preauth.c
+++ b/imap4d/preauth.c
@@ -448,7 +448,7 @@ do_preauth_program (struct sockaddr *pcs, struct sockaddr 
*sa)
       return NULL;
     }
 
-  rc = mu_prog_stream_create (&str, ws.ws_wordv[0], MU_STREAM_READ);
+  rc = mu_command_stream_create (&str, ws.ws_wordv[0], MU_STREAM_READ);
   mu_wordsplit_free (&ws);
   if (rc)
     {
diff --git a/include/mailutils/Makefile.am b/include/mailutils/Makefile.am
index 676c9cd..675672b 100644
--- a/include/mailutils/Makefile.am
+++ b/include/mailutils/Makefile.am
@@ -77,6 +77,7 @@ pkginclude_HEADERS = \
  pam.h\
  parse822.h\
  pop3.h\
+ prog.h\
  progmailer.h\
  property.h\
  python.h\
diff --git a/include/mailutils/mailutils.h b/include/mailutils/mailutils.h
index 19b8e77..0bb91b6 100644
--- a/include/mailutils/mailutils.h
+++ b/include/mailutils/mailutils.h
@@ -66,5 +66,6 @@
 #include <mailutils/wordsplit.h>
 #include <mailutils/log.h>
 #include <mailutils/stdstream.h>
+#include <mailutils/prog.h>
 
 /* EOF */
diff --git a/include/mailutils/prog.h b/include/mailutils/prog.h
new file mode 100644
index 0000000..e29170b
--- /dev/null
+++ b/include/mailutils/prog.h
@@ -0,0 +1,70 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   GNU Mailutils is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GNU Mailutils is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _MAILUTILS_PROG_H
+#define _MAILUTILS_PROG_H
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <mailutils/types.h>
+
+#define MU_PROG_LIMIT_AS      0
+#define MU_PROG_LIMIT_CPU     1
+#define MU_PROG_LIMIT_DATA    2
+#define MU_PROG_LIMIT_FSIZE   3
+#define MU_PROG_LIMIT_NPROC   4
+#define MU_PROG_LIMIT_CORE    5
+#define MU_PROG_LIMIT_MEMLOCK 6
+#define MU_PROG_LIMIT_NOFILE  7
+#define MU_PROG_LIMIT_RSS     8
+#define MU_PROG_LIMIT_STACK   9
+
+#define _MU_PROG_LIMIT_MAX   10
+
+#define MU_PROG_HINT_WORKDIR     0x0001  /* Workdir is set */
+#define MU_PROG_HINT_PRIO        0x0002  /* Prio is set */
+#define MU_PROG_HINT_INPUT       0x0004  /* Input stream is set */
+#define MU_PROG_HINT_UID         0x0008  /* Uid is set */
+#define MU_PROG_HINT_GID         0x0010  /* Supplementary gids are set */
+#define MU_PROG_HINT_ERRTOOUT    0x0020  /* Redirect stderr to stdout */
+#define MU_PROG_HINT_ERRTOSTREAM 0x0040  /* Redirect stderr to errstream */
+#define MU_PROG_HINT_IGNOREFAIL  0x0080  /* Ignore hint setup failures */
+#define _MU_PROG_HINT_MASK       0x00ff
+#define MU_PROG_HINT_LIMIT(n) (0x100 << (n)) /* MU_PROG_LIMIT_n is set */
+
+struct mu_prog_hints
+{
+  char *mu_prog_workdir;                    /* Working directory */
+  uid_t mu_prog_uid;                        /* Run as this user */
+  gid_t *mu_prog_gidv;                      /* Array of supplementary gids */
+  size_t mu_prog_gidc;                      /* Number of elements in gidv */
+  rlim_t mu_prog_limit[_MU_PROG_LIMIT_MAX]; /* Limits */
+  int mu_prog_prio;                         /* Scheduling priority */
+  mu_stream_t mu_prog_input;                /* Input stream */
+  mu_stream_t mu_prog_error;                /* Error stream */       
+};
+
+int mu_prog_stream_create (mu_stream_t *pstream,
+                          const char *progname,
+                          size_t argc, char **argv,
+                          int hflags,
+                          struct mu_prog_hints *hints,
+                          int flags);
+int mu_command_stream_create (mu_stream_t *pstream, const char *command,
+                             int flags);
+
+#endif
+
diff --git a/include/mailutils/stream.h b/include/mailutils/stream.h
index 300639e..407b45a 100644
--- a/include/mailutils/stream.h
+++ b/include/mailutils/stream.h
@@ -46,7 +46,7 @@ enum mu_buffer_type
 #define MU_STREAM_AUTOCLOSE   0x00000040
 /* Not used. Intended for mailboxes only. */
 #define MU_STREAM_NONLOCK     0x00000080
-#define MU_STREAM_ALLOW_LINKS 0x00000100
+/* Not used as well           0x00000100  */
 /* FIXME: This one affects only mailboxes */  
 #define MU_STREAM_QACCESS     0x00000200
 
@@ -250,9 +250,6 @@ int mu_fd_stream_create (mu_stream_t *pstream, char 
*filename, int fd,
 #define MU_STDERR_FD 2
 int mu_stdio_stream_create (mu_stream_t *pstream, int fd, int flags);
 
-int mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int 
flags);
-int mu_filter_prog_stream_create (mu_stream_t *pstream, const char *progname,
-                                 mu_stream_t input);
 int mu_memory_stream_create (mu_stream_t *pstream, int flags);
 int mu_static_memory_stream_create (mu_stream_t *pstream, const void *mem,
                                    size_t size);
diff --git a/include/mailutils/sys/prog_stream.h 
b/include/mailutils/sys/prog_stream.h
index fa042f2..ec17122 100644
--- a/include/mailutils/sys/prog_stream.h
+++ b/include/mailutils/sys/prog_stream.h
@@ -18,18 +18,25 @@
 #define _MAILUTILS_SYS_PROG_STREAM_H
 
 #include <mailutils/sys/stream.h>
+#include <mailutils/prog.h>
 
 struct _mu_prog_stream
 {
-  struct _mu_stream stream;
+  struct _mu_stream stream;   /* Base stream */
+  char *progname;             /* Program name */
+  size_t argc;                /* Number of arguments */
+  char **argv;                /* Program arguments */
+  int hint_flags;             /* Hint flags */
+  struct mu_prog_hints hints; /* Invocation hints */
+  
   pid_t pid;
   int status;
   pid_t writer_pid;
-  int argc;
-  char **argv;
+  
   mu_stream_t in, out;
-
-  mu_stream_t input;
 };
 
+extern int _mu_prog_limit_flags;
+extern int _mu_prog_limit_codes[_MU_PROG_LIMIT_MAX];
+
 #endif
diff --git a/include/mailutils/util.h b/include/mailutils/util.h
index fe0327a..e56b066 100644
--- a/include/mailutils/util.h
+++ b/include/mailutils/util.h
@@ -175,8 +175,10 @@ int mu_getpass (mu_stream_t in, mu_stream_t out, const 
char *prompt,
   /* ----------------------- */
 /* Get the host name, doing a gethostbyname() if possible. */
 int mu_get_host_name (char **host);
-int mu_spawnvp(const char *prog, char *av[], int *stat);
+int mu_spawnvp (const char *prog, char *av[], int *stat);
 int mu_scheme_autodetect_p (mu_url_t);
+int mu_set_user_privileges (uid_t uid, gid_t *gidv, size_t gidc);
+int mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups);
 
 struct timeval; 
 int mu_fd_wait (int fd, int *pflags, struct timeval *tvp);
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 504e463..bc821b6 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -27,7 +27,6 @@ libmuaux_a_SOURCES += \
  signal.c\
  strexit.c\
  tcpwrap.c\
- userprivs.c\
  mu_umaxtostr.c\
  mu_umaxtostr.h
 
diff --git a/libmailutils/base/Makefile.am b/libmailutils/base/Makefile.am
index b50781c..0fdc1a4 100644
--- a/libmailutils/base/Makefile.am
+++ b/libmailutils/base/Makefile.am
@@ -63,6 +63,7 @@ libbase_la_SOURCES = \
  tempfile.c\
  ticket.c\
  tilde.c\
+ userprivs.c\
  usremail.c\
  version.c\
  wicket.c
diff --git a/libmailutils/base/amd.c b/libmailutils/base/amd.c
index 99f340a..9f95016 100644
--- a/libmailutils/base/amd.c
+++ b/libmailutils/base/amd.c
@@ -1589,7 +1589,7 @@ amd_message_stream_open (struct _amd_message *mhm)
   struct _amd_data *amd = mhm->amd;
   char *filename;
   int status;
-  int flags = MU_STREAM_ALLOW_LINKS;
+  int flags = 0;
 
   status = amd->cur_msg_file_name (mhm, &filename);
   if (status)
diff --git a/lib/userprivs.c b/libmailutils/base/userprivs.c
similarity index 63%
rename from lib/userprivs.c
rename to libmailutils/base/userprivs.c
index 2185777..c3af949 100644
--- a/lib/userprivs.c
+++ b/libmailutils/base/userprivs.c
@@ -29,75 +29,90 @@
 #include <mailutils/nls.h>
 #include <mailutils/list.h>
 #include <mailutils/iterator.h>
-#include <xalloc.h>
 
 /* Switch to the given UID/GID */
 int
-mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
+mu_set_user_privileges (uid_t uid, gid_t *gidv, size_t gidc)
 {
   int rc = 0;
-  gid_t *emptygidset;
-  size_t size = 1, j = 1;
-  mu_iterator_t itr;
+  gid_t gid;
 
+  if (getuid ())
+    return EACCES;
+  
   if (uid == 0)
     return 0;
 
-  /* Create a list of supplementary groups */
-  mu_list_count (retain_groups, &size);
-  size++;
-  emptygidset = xmalloc (size * sizeof emptygidset[0]);
-  emptygidset[0] = gid ? gid : getegid ();
-
-  if (mu_list_get_iterator (retain_groups, &itr) == 0)
+  /* Reset group permissions */
+  if (gidv && gidc)
     {
-      for (mu_iterator_first (itr);
-          !mu_iterator_is_done (itr); mu_iterator_next (itr)) 
-       mu_iterator_current (itr,
-                            (void **)(emptygidset + j++));
-      mu_iterator_destroy (&itr);
+      if (geteuid () == 0 && setgroups (gidc, gidv))
+       {
+         mu_error(_("setgroups(1, %lu) failed: %s"),
+                  (unsigned long) gidv[0], mu_strerror (errno));
+         return errno;
+       }
+      gid = gidv[0];
     }
-
-  /* Reset group permissions */
-  if (geteuid () == 0 && setgroups (j, emptygidset))
+  else
     {
-      mu_error(_("setgroups(1, %lu) failed: %s"),
-              (unsigned long) emptygidset[0], mu_strerror (errno));
-      rc = 1;
+      struct passwd *pwd = getpwuid (uid);
+      if (pwd)
+       gid = pwd->pw_gid;
+      else
+       gid = getegid ();
     }
-  free (emptygidset);
-       
+
   /* Switch to the user's gid. On some OSes the effective gid must
      be reset first */
 
 #if defined(HAVE_SETEGID)
-  if ((rc = setegid (gid)) < 0)
-    mu_error (_("setegid(%lu) failed: %s"),
-             (unsigned long) gid, mu_strerror (errno));
+  if (setegid (gid) < 0)
+    {
+      rc = errno;
+      mu_error (_("setegid(%lu) failed: %s"),
+               (unsigned long) gid, mu_strerror (rc));
+    }
 #elif defined(HAVE_SETREGID)
-  if ((rc = setregid (gid, gid)) < 0)
-    mu_error (_("setregid(%lu,%lu) failed: %s"),
-             (unsigned long) gid, (unsigned long) gid,
-             mu_strerror (errno));
+  if (setregid (gid, gid) < 0)
+    {
+      rc = errno;
+      mu_error (_("setregid(%lu,%lu) failed: %s"),
+               (unsigned long) gid, (unsigned long) gid,
+               mu_strerror (rc));
+    }
 #elif defined(HAVE_SETRESGID)
-  if ((rc = setresgid (gid, gid, gid)) < 0)
-    mu_error (_("setresgid(%lu,%lu,%lu) failed: %s"),
-             (unsigned long) gid,
-             (unsigned long) gid,
-             (unsigned long) gid,
-             mu_strerror (errno));
+  if (setresgid (gid, gid, gid) < 0)
+    {
+      rc = errno;
+      mu_error (_("setresgid(%lu,%lu,%lu) failed: %s"),
+               (unsigned long) gid,
+               (unsigned long) gid,
+               (unsigned long) gid,
+               mu_strerror (rc));
+    }
 #endif
 
   if (rc == 0 && gid != 0)
     {
-      if ((rc = setgid (gid)) < 0 && getegid () != gid) 
-       mu_error (_("setgid(%lu) failed: %s"),
-                 (unsigned long) gid, mu_strerror (errno));
+      if (setgid (gid) < 0)
+       {
+         rc = errno;
+         mu_error (_("setgid(%lu) failed: %s"),
+                   (unsigned long) gid, mu_strerror (rc));
+       }
+      else if (getegid () != gid)
+       {
+         rc = MU_ERR_FAILURE;
+         mu_error (_("setgid(%lu) failed: %s"),
+                   (unsigned long) gid, mu_strerror (rc));
+       }
+      
       if (rc == 0 && getegid () != gid)
        {
          mu_error (_("Cannot set effective gid to %lu"),
                    (unsigned long) gid);
-         rc = 1;
+         rc = MU_ERR_FAILURE;
        }
     }
 
@@ -114,24 +129,24 @@ mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t 
retain_groups)
            {
              if (setreuid (uid, -1) < 0)
                {
+                 rc = errno;
                  mu_error (_("setreuid(%lu,-1) failed: %s"),
                            (unsigned long) uid,
-                           mu_strerror (errno));
-                 rc = 1;
+                           mu_strerror (rc));
                }
              if (setuid (uid) < 0)
                {
+                 rc = errno;
                  mu_error (_("second setuid(%lu) failed: %s"),
-                           (unsigned long) uid, mu_strerror (errno));
-                 rc = 1;
+                           (unsigned long) uid, mu_strerror (rc));
                }
            } else
 #endif
                {
+                 rc = errno;
                  mu_error (_("setuid(%lu) failed: %s"),
                            (unsigned long) uid,
-                           mu_strerror (errno));
-                 rc = 1;
+                           mu_strerror (rc));
                }
        }
        
@@ -139,15 +154,46 @@ mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t 
retain_groups)
       if (uid != 0 && setuid (0) == 0)
        {
          mu_error (_("seteuid(0) succeeded when it should not"));
-         rc = 1;
+         rc = MU_ERR_FAILURE;
        }
       else if (uid != euid && setuid (euid) == 0)
        {
          mu_error (_("Cannot drop non-root setuid privileges"));
-         rc = 1;
+         rc = MU_ERR_FAILURE;
        }
     }
   return rc;
 }
 
+int
+mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
+{
+  int rc = 0;
+  gid_t *emptygidset;
+  size_t size = 1, j = 1;
+  mu_iterator_t itr;
+
+  if (uid == 0)
+    return 0;
+
+  /* Create a list of supplementary groups */
+  mu_list_count (retain_groups, &size);
+  size++;
+  emptygidset = malloc (size * sizeof emptygidset[0]);
+  if (!emptygidset)
+    return ENOMEM;
+  emptygidset[0] = gid ? gid : getegid ();
+
+  if (mu_list_get_iterator (retain_groups, &itr) == 0)
+    {
+      for (mu_iterator_first (itr);
+          !mu_iterator_is_done (itr); mu_iterator_next (itr)) 
+       mu_iterator_current (itr,
+                            (void **)(emptygidset + j++));
+      mu_iterator_destroy (&itr);
+    }
+  rc = mu_set_user_privileges (uid, emptygidset, j);
+  free (emptygidset);
+  return rc;
+}
 
diff --git a/libmailutils/stream/file_stream.c 
b/libmailutils/stream/file_stream.c
index b978392..ffaf5e4 100644
--- a/libmailutils/stream/file_stream.c
+++ b/libmailutils/stream/file_stream.c
@@ -37,8 +37,6 @@
 #include <mailutils/sys/file_stream.h>
 #include <mailutils/util.h>
 
-#define MU_FSTR_ERROR_NOREG (MU_ERR_LAST+1)
-
 static int
 fd_read (struct _mu_stream *str, char *buf, size_t size, size_t *pret)
 {
@@ -107,7 +105,6 @@ fd_open (struct _mu_stream *str)
          /* Oops bail out.  */
          if (errno != ENOENT)
            return errno;
-         /* Race condition here when creating the file ??.  */
          fd = open (fstr->filename, oflg|O_CREAT|O_EXCL,
                     0600 | mu_stream_flags_to_mode (fstr->stream.flags, 0));
        }
@@ -117,41 +114,13 @@ fd_open (struct _mu_stream *str)
 
   if (fd == -1)
     return errno;
-  
-  if (!(fstr->stream.flags & MU_STREAM_ALLOW_LINKS)
-      && (fstr->stream.flags & (MU_STREAM_CREAT | MU_STREAM_RDWR |
-                               MU_STREAM_APPEND)))
-    {
-      struct stat fdbuf, filebuf;
 
-      /* The following two stats should never fail.  */
-      if (fstat (fd, &fdbuf) == -1
-         || lstat (fstr->filename, &filebuf) == -1)
-       {
-         close (fd);
-         return errno;
-       }
-
-      /* Now check that: file and fd reference the same file,
-        file only has one link, file is plain file.  */
-      if ((fdbuf.st_dev != filebuf.st_dev
-          || fdbuf.st_ino != filebuf.st_ino
-          || fdbuf.st_nlink != 1
-          || filebuf.st_nlink != 1
-          || (fdbuf.st_mode & S_IFMT) != S_IFREG))
-       {
-         /* FIXME: Be silent */
-         close (fd);
-         return MU_FSTR_ERROR_NOREG;
-       }
-    }
+  if (lseek (fd, 0, SEEK_SET) == -1)
+    str->flags &= ~MU_STREAM_SEEK;
   
-  if (fd < 0)
-    return errno;
-
   /* Make sure it will be closed */
-  fstr->flags |= MU_STREAM_AUTOCLOSE;
-
+  str->flags |= MU_STREAM_AUTOCLOSE;
+  
   fstr->fd = fd;
   return 0;
 }
@@ -190,15 +159,6 @@ fd_done (struct _mu_stream *str)
     free (fstr->echo_state);
 }
 
-const char *
-fd_error_string (struct _mu_stream *str, int rc)
-{
-  if (rc == MU_FSTR_ERROR_NOREG)
-    return _("must be a plain file with one link");
-  else
-    return mu_strerror (rc);
-}
-
 #ifndef TCSASOFT
 # define TCSASOFT 0
 #endif
@@ -353,7 +313,6 @@ _mu_file_stream_create (struct _mu_file_stream **pstream, 
size_t size,
   str->stream.ctl = fd_ioctl;
   str->stream.wait = fd_wait;
   str->stream.truncate = fd_truncate;
-  str->stream.error_string = fd_error_string;
 
   if (filename)
     str->filename = mu_strdup (filename);
diff --git a/libmailutils/stream/prog_stream.c 
b/libmailutils/stream/prog_stream.c
index ddf3944..03a860f 100644
--- a/libmailutils/stream/prog_stream.c
+++ b/libmailutils/stream/prog_stream.c
@@ -71,9 +71,105 @@ _prog_stream_unregister (struct _mu_prog_stream *stream)
 #define REDIRECT_STDIN_P(f) ((f) & MU_STREAM_WRITE)
 #define REDIRECT_STDOUT_P(f) ((f) & MU_STREAM_READ)
 
+#ifdef RLIMIT_AS
+# define _MU_RLIMIT_AS_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_AS)
+#else
+# define _MU_RLIMIT_AS_FLAG 0
+# define RLIMIT_AS 0
+#endif
+
+#ifdef RLIMIT_CPU
+# define _MU_RLIMIT_CPU_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_CPU)
+#else
+# define _MU_RLIMIT_CPU_FLAG 0
+# define RLIMIT_CPU 0
+#endif
+
+#ifdef RLIMIT_DATA
+# define _MU_RLIMIT_DATA_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_DATA)
+#else
+# define _MU_RLIMIT_DATA_FLAG 0
+# define RLIMIT_DATA 0
+#endif
+
+#ifdef RLIMIT_FSIZE
+# define _MU_RLIMIT_FSIZE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_FSIZE)
+#else
+# define _MU_RLIMIT_FSIZE_FLAG 0
+# define RLIMIT_FSIZE 0
+#endif
+
+#ifdef RLIMIT_NPROC
+# define _MU_RLIMIT_NPROC_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_NPROC)
+#else
+# define _MU_RLIMIT_NPROC_FLAG 0
+# define RLIMIT_NPROC 0
+#endif
+
+#ifdef RLIMIT_CORE
+# define _MU_RLIMIT_CORE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_CORE)
+#else
+# define _MU_RLIMIT_CORE_FLAG 0
+# define RLIMIT_CORE 0
+#endif
+
+#ifdef RLIMIT_MEMLOCK
+# define _MU_RLIMIT_MEMLOCK_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_MEMLOCK)
+#else
+# define _MU_RLIMIT_MEMLOCK_FLAG 0
+# define RLIMIT_MEMLOCK 0
+#endif
+
+#ifdef RLIMIT_NOFILE
+# define _MU_RLIMIT_NOFILE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_NOFILE)
+#else
+# define _MU_RLIMIT_NOFILE_FLAG 0
+# define RLIMIT_NOFILE 0
+#endif
+
+#ifdef RLIMIT_RSS
+# define _MU_RLIMIT_RSS_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_RSS)
+#else
+# define _MU_RLIMIT_RSS_FLAG 0
+# define RLIMIT_RSS 0
+#endif
+
+#ifdef RLIMIT_STACK
+# define _MU_RLIMIT_STACK MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_STACK)
+#else
+# define _MU_RLIMIT_STACK 0
+# define RLIMIT_STACK 0
+#endif
+
+#define _MU_PROG_AVAILABLE_LIMITS \
+  (_MU_RLIMIT_AS_FLAG |                  \
+   _MU_RLIMIT_CPU_FLAG |         \
+   _MU_RLIMIT_DATA_FLAG |        \
+   _MU_RLIMIT_FSIZE_FLAG |       \
+   _MU_RLIMIT_NPROC_FLAG |       \
+   _MU_RLIMIT_CORE_FLAG |        \
+   _MU_RLIMIT_MEMLOCK_FLAG |     \
+   _MU_RLIMIT_NOFILE_FLAG |      \
+   _MU_RLIMIT_RSS_FLAG |         \
+   _MU_RLIMIT_STACK)
+
+int _mu_prog_limit_flags = _MU_PROG_AVAILABLE_LIMITS;
+
+int _mu_prog_limit_codes[_MU_PROG_LIMIT_MAX] = {
+  RLIMIT_AS,
+  RLIMIT_CPU,
+  RLIMIT_DATA,
+  RLIMIT_FSIZE,
+  RLIMIT_NPROC,
+  RLIMIT_CORE,
+  RLIMIT_MEMLOCK,
+  RLIMIT_NOFILE,
+  RLIMIT_RSS,
+  RLIMIT_STACK
+};
+
 static int
-start_program_filter (pid_t *pid, int *p, int argc, char **argv,
-                     char *errfile, int flags)
+start_program_filter (int *p, struct _mu_prog_stream *fs, int flags)
 {
   int rightp[2], leftp[2];
   int i;
@@ -84,7 +180,7 @@ start_program_filter (pid_t *pid, int *p, int argc, char 
**argv,
   if (REDIRECT_STDOUT_P (flags))
     pipe (rightp);
   
-  switch (*pid = fork ())
+  switch (fs->pid = fork ())
     {
       /* The child branch.  */
     case 0:
@@ -111,28 +207,69 @@ start_program_filter (pid_t *pid, int *p, int argc, char 
**argv,
            }
          close (leftp[1]);
        }
+
+      if (fs->hint_flags & MU_PROG_HINT_ERRTOOUT)
+       dup2 (1, 2);
       
-      /* Error output */
-      if (errfile)
+      if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
        {
-         i = open (errfile, O_CREAT|O_WRONLY|O_APPEND, 0644);
-         if (i > 0 && i != 2)
+         if (chdir (fs->hints.mu_prog_workdir))
            {
-             dup2 (i, 2);
-             close (i);
+             mu_error (_("cannot change to %s: %s"),
+                       fs->hints.mu_prog_workdir, mu_strerror (errno));
+             if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
+               _exit (127);
            }
        }
+
+      if (fs->hint_flags & MU_PROG_HINT_UID)
+       {
+         if (mu_set_user_privileges (fs->hints.mu_prog_uid,
+                                     fs->hints.mu_prog_gidv,
+                                     fs->hints.mu_prog_gidc)
+             && !(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
+           _exit (127);
+       }
+      
+      for (i = 0; i < _MU_PROG_LIMIT_MAX; i++)
+       {
+         if (MU_PROG_HINT_LIMIT(i) & fs->hint_flags)
+           {
+             struct rlimit rlim;
+             
+             rlim.rlim_cur = rlim.rlim_max = fs->hints.mu_prog_limit[i];
+             if (setrlimit (_mu_prog_limit_codes[i], &rlim))
+               {
+                 mu_error (_("error setting limit %d to %lu: %s"),
+                           i, (unsigned long) rlim.rlim_cur,
+                           mu_strerror (errno));
+                 if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
+                   _exit (127);
+               }
+           }
+       }
+      if (MU_PROG_HINT_PRIO & fs->hint_flags)
+       {
+         if (setpriority (PRIO_PROCESS, 0, fs->hints.mu_prog_prio))
+           {
+             mu_error (_("error setting priority: %s"),
+                       mu_strerror (errno));
+             if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
+               _exit (127);
+           }
+       }
+       
       /* Close unneded descripitors */
       for (i = getmaxfd (); i > 2; i--)
        close (i);
 
       /*FIXME: Switch to other uid/gid if desired */
-      execvp (argv[0], argv);
+      execvp (fs->progname, fs->argv);
                
       /* Report error via syslog */
       syslog (LOG_ERR|LOG_USER, "can't run %s (ruid=%d, euid=%d): %m",
-             argv[0], getuid (), geteuid ());
-      exit (127);
+             fs->progname, getuid (), geteuid ());
+      _exit (127);
       /********************/
 
       /* Parent branches: */
@@ -187,10 +324,16 @@ _prog_wait (pid_t pid, int *pstatus)
 static void
 _prog_done (mu_stream_t stream)
 {
-  struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
   int status;
+  struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
     
   mu_argcv_free (fs->argc, fs->argv);
+  free (fs->progname);
+  if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
+    free (fs->hints.mu_prog_workdir);
+  if (fs->hint_flags & MU_PROG_HINT_INPUT)
+    mu_stream_unref (fs->hints.mu_prog_input);
+  
   if (fs->in)
     mu_stream_destroy (&fs->in);
   if (fs->out)
@@ -245,8 +388,6 @@ static int
 feed_input (struct _mu_prog_stream *fs)
 {
   pid_t pid;
-  size_t size;
-  char buffer[128];
   int rc = 0;
 
   pid = fork ();
@@ -261,10 +402,7 @@ feed_input (struct _mu_prog_stream *fs)
       
     case 0:
       /* Child */
-      while (mu_stream_read (fs->input, buffer, sizeof (buffer),
-                            &size) == 0
-            && size > 0)
-       mu_stream_write (fs->out, buffer, size, NULL);
+      mu_stream_copy (fs->out, fs->hints.mu_prog_input, 0, NULL);
       mu_stream_close (fs->out);
       exit (0);
       
@@ -295,7 +433,7 @@ _prog_open (mu_stream_t stream)
   mu_stream_get_flags (stream, &flags);
   seekable_flag = (flags & MU_STREAM_SEEK);
   
-  rc = start_program_filter (&fs->pid, pfd, fs->argc, fs->argv, NULL, flags);
+  rc = start_program_filter (pfd, fs, flags);
   if (rc)
     return rc;
 
@@ -322,7 +460,7 @@ _prog_open (mu_stream_t stream)
     }
 
   _prog_stream_register (fs);
-  if (fs->input)
+  if (fs->hint_flags & MU_PROG_HINT_INPUT)
     return feed_input (fs);
   return 0;
 }
@@ -408,30 +546,25 @@ _prog_ioctl (struct _mu_stream *str, int code, int 
opcode, void *ptr)
   return 0;
 }
 
-struct _mu_prog_stream *
-_prog_stream_create (const char *progname, int flags)
+/* NOTE: Steals argv */
+static struct _mu_prog_stream *
+_prog_stream_create (const char *progname, size_t argc, char **argv,
+                    int hint_flags, struct mu_prog_hints *hints, int flags)
 {
   struct _mu_prog_stream *fs;
-  struct mu_wordsplit ws;
-  
+
   fs = (struct _mu_prog_stream *) _mu_stream_create (sizeof (*fs), flags);
   if (!fs)
     return NULL;
 
-  ws.ws_comment = "#";
-  if (mu_wordsplit (progname, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
+  fs->progname = strdup (progname);
+  if (!fs->progname)
     {
-      mu_error (_("cannot split line `%s': %s"), progname,
-               mu_wordsplit_strerror (&ws));
       free (fs);
       return NULL;
     }
-  fs->argc = ws.ws_wordc;
-  fs->argv = ws.ws_wordv;
-  ws.ws_wordc = 0;
-  ws.ws_wordv = NULL;
-  mu_wordsplit_free (&ws);
-
+  fs->argc = argc;
+  fs->argv = argv;
   fs->stream.read = _prog_read;
   fs->stream.write = _prog_write;
   fs->stream.open = _prog_open;
@@ -440,24 +573,95 @@ _prog_stream_create (const char *progname, int flags)
   fs->stream.flush = _prog_flush;
   fs->stream.done = _prog_done;
 
+  if (!hints)
+    fs->hint_flags = 0;
+  else
+    {
+      fs->hint_flags = (hint_flags & _MU_PROG_HINT_MASK) |
+                       (hint_flags & _MU_PROG_AVAILABLE_LIMITS);
+      if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
+       {
+         fs->hints.mu_prog_workdir = strdup (hints->mu_prog_workdir);
+         if (!fs->hints.mu_prog_workdir)
+           {
+             free (fs);
+             return NULL;
+           }
+       }
+      memcpy (fs->hints.mu_prog_limit, hints->mu_prog_limit,
+             sizeof (fs->hints.mu_prog_limit));
+      fs->hints.mu_prog_prio = hints->mu_prog_prio;
+      if (fs->hint_flags & MU_PROG_HINT_INPUT)
+       {
+         fs->hints.mu_prog_input = hints->mu_prog_input;
+         mu_stream_ref (fs->hints.mu_prog_input);
+       }
+      if (fs->hint_flags & MU_PROG_HINT_UID)
+       {
+         fs->hints.mu_prog_uid = hints->mu_prog_uid;
+         if (fs->hint_flags & MU_PROG_HINT_GID)
+           {
+             fs->hints.mu_prog_gidv = calloc (hints->mu_prog_gidc,
+                                              sizeof 
(fs->hints.mu_prog_gidv[0]));
+             if (!fs->hints.mu_prog_gidv)
+               {
+                 mu_stream_unref ((mu_stream_t) fs);
+                 return NULL;
+               }
+             memcpy (fs->hints.mu_prog_gidv, hints->mu_prog_gidv,
+                     hints->mu_prog_gidc * fs->hints.mu_prog_gidv[0]);
+             fs->hints.mu_prog_gidc = hints->mu_prog_gidc;
+           }
+         else
+           {
+             fs->hints.mu_prog_gidc = 0;
+             fs->hints.mu_prog_gidv = NULL;
+           }
+       }
+    }
+  
   return fs;
 }
 
 int
-mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags)
+mu_prog_stream_create (mu_stream_t *pstream,
+                      const char *progname, size_t argc, char **argv,
+                      int hint_flags,
+                      struct mu_prog_hints *hints,
+                      int flags)
 {
   int rc;
   mu_stream_t stream;
-  
+  char **xargv;
+  size_t i;
+    
   if (pstream == NULL)
     return MU_ERR_OUT_PTR_NULL;
 
   if (progname == NULL)
     return EINVAL;
 
-  if ((stream = (mu_stream_t) _prog_stream_create (progname, flags)) == NULL)
+  xargv = calloc (argc + 1, sizeof (xargv[0]));
+  if (!xargv)
     return ENOMEM;
 
+  for (i = 0; i < argc; i++)
+    {
+      xargv[i] = strdup (argv[i]);
+      if (!xargv[i])
+       {
+         mu_argcv_free (i, argv);
+         return ENOMEM;
+       }
+    }
+  stream = (mu_stream_t) _prog_stream_create (progname, argc, xargv,
+                                             hint_flags, hints, flags);
+  if (!stream)
+    {
+      mu_argcv_free (argc, xargv);
+      return ENOMEM;
+    }
+  
   rc = mu_stream_open (stream);
   if (rc)
     mu_stream_destroy (&stream);
@@ -467,30 +671,38 @@ mu_prog_stream_create (mu_stream_t *pstream, const char 
*progname, int flags)
 }
 
 int
-mu_filter_prog_stream_create (mu_stream_t *pstream, const char *progname,
-                             mu_stream_t input)
+mu_command_stream_create (mu_stream_t *pstream, const char *command,
+                         int flags)
 {
   int rc;
   mu_stream_t stream;
-  struct _mu_prog_stream *fs;
-
+  struct mu_wordsplit ws;
+  
   if (pstream == NULL)
     return MU_ERR_OUT_PTR_NULL;
 
-  if (progname == NULL)
+  if (command == NULL)
     return EINVAL;
 
-  fs = _prog_stream_create (progname, MU_STREAM_RDWR);
-  if (!fs)
-    return ENOMEM;
-  mu_stream_ref (input);
-  fs->input = input;
-  stream = (mu_stream_t) fs;
-  rc = mu_stream_open (stream);
-  if (rc)
-    mu_stream_destroy (&stream);
-  else
-    *pstream = stream;
+  ws.ws_comment = "#";
+  if (mu_wordsplit (command, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
+    {
+      mu_error (_("cannot split line `%s': %s"), command,
+               mu_wordsplit_strerror (&ws));
+      return errno;
+    }
+
+  rc = mu_prog_stream_create (&stream,
+                             ws.ws_wordv[0],
+                             ws.ws_wordc, ws.ws_wordv,
+                             0, NULL, flags);
+  if (rc == 0)
+    { 
+      ws.ws_wordc = 0;
+      ws.ws_wordv = NULL;
+      *pstream = stream;
+    }
+  mu_wordsplit_free (&ws);
+
   return rc;
 }
-
diff --git a/libmu_sieve/extensions/pipe.c b/libmu_sieve/extensions/pipe.c
index 1219efe..fbb5099 100644
--- a/libmu_sieve/extensions/pipe.c
+++ b/libmu_sieve/extensions/pipe.c
@@ -39,6 +39,7 @@
 #include <signal.h>
 #include <regex.h>
 #include <mailutils/sieve.h>
+#include <mailutils/prog.h>
 
 int
 sieve_action_pipe (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags)
@@ -86,7 +87,7 @@ sieve_action_pipe (mu_sieve_machine_t mach, mu_list_t args, 
mu_list_t tags)
       rc = mu_message_get_streamref (msg, &mstr);
       ONERR (rc, _("cannot get message stream"), NULL);
 
-      rc = mu_prog_stream_create (&pstr, cmd, MU_STREAM_WRITE);
+      rc = mu_command_stream_create (&pstr, cmd, MU_STREAM_WRITE);
       ONERR (rc, _("cannot create command stream"), cmd);
 
       if (mu_sieve_tag_lookup (tags, "envelope", &val))
diff --git a/mail/decode.c b/mail/decode.c
index 09f22d1..f3497b1 100644
--- a/mail/decode.c
+++ b/mail/decode.c
@@ -409,10 +409,11 @@ run_metamail (const char *mailcap_cmd, mu_message_t mesg)
              break;
            }
 
-         status = mu_prog_stream_create (&pstr, mailcap_cmd, MU_STREAM_WRITE);
+         status = mu_command_stream_create (&pstr, mailcap_cmd,
+                                            MU_STREAM_WRITE);
          if (status)
            {
-             mu_error ("mu_prog_stream_create: %s", mu_strerror (status));
+             mu_error ("mu_command_stream_create: %s", mu_strerror (status));
              break;
            }
 
diff --git a/mail/mail.h b/mail/mail.h
index f484363..4857717 100644
--- a/mail/mail.h
+++ b/mail/mail.h
@@ -82,6 +82,7 @@
 #include <mailutils/cstr.h>
 #include <mailutils/io.h>
 #include <mailutils/stdstream.h>
+#include <mailutils/prog.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/mail/pipe.c b/mail/pipe.c
index 58fa64e..0954edd 100644
--- a/mail/pipe.c
+++ b/mail/pipe.c
@@ -38,7 +38,7 @@ mail_pipe (int argc, char **argv)
   if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &list))
     return 1;
 
-  rc = mu_prog_stream_create (&outstr, cmd, MU_STREAM_WRITE);
+  rc = mu_command_stream_create (&outstr, cmd, MU_STREAM_WRITE);
   if (rc)
     {
       mu_error (_("cannot open `%s': %s"), cmd, mu_strerror (rc));
diff --git a/mail/send.c b/mail/send.c
index 72c7f18..6d3e42a 100644
--- a/mail/send.c
+++ b/mail/send.c
@@ -702,7 +702,7 @@ msg_to_pipe (const char *cmd, mu_message_t msg)
   mu_stream_t progstream, msgstream;
   int status, rc;
   
-  status = mu_prog_stream_create (&progstream, cmd, MU_STREAM_WRITE);
+  status = mu_command_stream_create (&progstream, cmd, MU_STREAM_WRITE);
   if (status)
     {
       util_error (_("Cannot pipe to %s: %s"), cmd, mu_strerror (status));
diff --git a/mh/mh.h b/mh/mh.h
index 08a19fd..5af2c73 100644
--- a/mh/mh.h
+++ b/mh/mh.h
@@ -56,6 +56,7 @@
 #include <mailutils/mime.h>
 #include <mailutils/io.h>
 #include <mailutils/property.h>
+#include <mailutils/prog.h>
 #include <mailutils/mh.h>
 
 #include <mu_umaxtostr.h>
diff --git a/mh/mhl.c b/mh/mhl.c
index e81933d..c71cabf 100644
--- a/mh/mhl.c
+++ b/mh/mhl.c
@@ -149,7 +149,7 @@ open_output ()
     moreproc = NULL;
 
   if (moreproc)
-    rc = mu_prog_stream_create (&output, moreproc, MU_STREAM_WRITE);
+    rc = mu_command_stream_create (&output, moreproc, MU_STREAM_WRITE);
   else
     rc = mu_stdio_stream_create (&output, MU_STDOUT_FD, MU_STREAM_WRITE);
 
diff --git a/mh/mhn.c b/mh/mhn.c
index de5bf37..c3c433f 100644
--- a/mh/mhn.c
+++ b/mh/mhn.c
@@ -1403,7 +1403,7 @@ show_internal (mu_message_t msg, msg_part_t part, char 
*encoding,
 int
 mhn_exec (mu_stream_t *str, const char *cmd, int flags)
 {
-  int rc = mu_prog_stream_create (str, cmd, MU_STREAM_WRITE);
+  int rc = mu_command_stream_create (str, cmd, MU_STREAM_WRITE);
   if (rc)
     {
       mu_error (_("cannot create proc stream (command %s): %s"),
@@ -1773,16 +1773,30 @@ store_handler (mu_message_t msg, msg_part_t part, char 
*type, char *encoding,
       break;
 
     case store_to_command:
-      /* FIXME: Change to homedir, reflect this in the message below.
-        Chdir should better be implemented within mu_prog_stream_create
-        Example message:
-          storing msg 4 part 1 using command (cd /home/gray;  less)
-      */
-      printf (_("storing msg %s part %s using command %s\n"),
-             prefix, partstr, name);
-      rc = mu_prog_stream_create (&out, name, MU_STREAM_WRITE);
-      if (rc)
-       mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", NULL, rc);
+      {
+       struct mu_prog_hints hints;
+       struct mu_wordsplit ws;
+      
+       hints.mu_prog_workdir = mu_get_homedir ();
+       ws.ws_comment = "#";
+       if (mu_wordsplit (name, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
+         {
+           mu_error (_("cannot split line `%s': %s"), name,
+                     mu_wordsplit_strerror (&ws));
+           break;
+         }
+       
+       printf (_("storing msg %s part %s using command (cd %s; %s)\n"),
+               prefix, partstr, hints.mu_prog_workdir, name);
+       rc = mu_prog_stream_create (&out,
+                                   ws.ws_wordv[0],
+                                   ws.ws_wordc, ws.ws_wordv,
+                                   MU_PROG_HINT_WORKDIR,
+                                   &hints, MU_STREAM_WRITE);
+       mu_wordsplit_free (&ws);
+       if (rc)
+         mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", NULL, rc);
+      }
       break;
       
     case store_to_stdout:
diff --git a/mh/tests/mhn.at b/mh/tests/mhn.at
index fe64993..8e78ebe 100644
--- a/mh/tests/mhn.at
+++ b/mh/tests/mhn.at
@@ -247,7 +247,7 @@ mkdir Mail/inbox
 cp $abs_top_srcdir/testsuite/mh/mbox1/4 Mail/inbox
 echo "Current-Folder: inbox" > Mail/context
 echo "mhn-store-text: | $abs_top_srcdir/mh/tests/mhed -" >> $MH
-mhn +inbox -store -part 1 4 | sed "s|$abs_top_srcdir/mh/tests/||;s|  *$||" || 
exit $?
+mhn +inbox -store -part 1 4 | sed "s|$abs_top_srcdir/mh/tests/||;s|(cd 
\(.*\)\;|(cd home\;|;s|  *$||" || exit $?
 ],
 [0],
 [-- Editor invocation: -
@@ -263,7 +263,7 @@ But, now that I'm perfectly sure I have none,
 Why, I do it again and again.'
 
 -- Input file end
-storing msg 4 part 1 using command mhed -
+storing msg 4 part 1 using command (cd home; mhed -)
 ])
 
 dnl -------------------------------------------------------------------
diff --git a/mu/shell.c b/mu/shell.c
index 76bd2c6..45e3797 100644
--- a/mu/shell.c
+++ b/mu/shell.c
@@ -240,7 +240,7 @@ mutool_open_pager ()
   if (mutool_shell_interactive && (pager = getenv ("PAGER")) != NULL)
     {
       mu_stream_t stream;
-      int rc = mu_prog_stream_create (&stream, pager, MU_STREAM_WRITE);
+      int rc = mu_command_stream_create (&stream, pager, MU_STREAM_WRITE);
       if (rc == 0)
        return stream;
       mu_error (_("cannot start pager: %s"), mu_strerror (rc));
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d4a1aeb..3099cf3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -118,6 +118,7 @@ mail/unalias.c
 mail/util.c
 mail/z.c
 
+libmailutils/base/userprivs.c
 libmailutils/cfg/driver.c
 libmailutils/cfg/format.c
 libmailutils/cfg/lexer.l


hooks/post-receive
-- 
GNU Mailutils



reply via email to

[Prev in Thread] Current Thread [Next in Thread]