summaryrefslogtreecommitdiff
path: root/libmailutils/mailer/progmailer.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/mailer/progmailer.c')
-rw-r--r--libmailutils/mailer/progmailer.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/libmailutils/mailer/progmailer.c b/libmailutils/mailer/progmailer.c
new file mode 100644
index 000000000..f39440fcd
--- /dev/null
+++ b/libmailutils/mailer/progmailer.c
@@ -0,0 +1,314 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sysexits.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <mailutils/debug.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/stream.h>
+#include <mailutils/header.h>
+#include <mailutils/body.h>
+#include <mailutils/message.h>
+#include <mailutils/progmailer.h>
+#include <mailutils/mutil.h>
+#include <mailutils/cstr.h>
+
+struct _mu_progmailer
+{
+ int fd;
+ pid_t pid;
+ RETSIGTYPE (*sighandler)();
+ mu_debug_t debug;
+ char *command;
+};
+
+/* Close FD unless it is part of pipe P */
+#define SCLOSE(fd,p) if (p[0]!=fd&&p[1]!=fd) close(fd)
+
+int
+mu_progmailer_create (struct _mu_progmailer **ppm)
+{
+ struct _mu_progmailer *pm = malloc (sizeof (*pm));
+ if (!pm)
+ return ENOMEM;
+ pm->fd = -1;
+ pm->pid = -1;
+ pm->sighandler = SIG_ERR;
+ pm->debug = NULL;
+ pm->command = NULL;
+ *ppm = pm;
+ return 0;
+}
+
+int
+mu_progmailer_set_command (mu_progmailer_t pm, const char *command)
+{
+ if (!pm)
+ return EINVAL;
+ free (pm->command);
+ if (command)
+ {
+ pm->command = strdup (command);
+ if (!pm->command)
+ return EINVAL;
+ }
+ else
+ pm->command = NULL;
+ return 0;
+}
+
+int
+mu_progmailer_sget_command (mu_progmailer_t pm, const char **command)
+{
+ if (!pm)
+ return EINVAL;
+ *command = pm->command;
+ return 0;
+}
+
+int
+mu_progmailer_set_debug (mu_progmailer_t pm, mu_debug_t debug)
+{
+ if (!pm)
+ return EINVAL;
+ pm->debug = debug;
+ return 0;
+}
+
+void
+mu_progmailer_destroy (struct _mu_progmailer **ppm)
+{
+ if (*ppm)
+ {
+ free ((*ppm)->command);
+ free (*ppm);
+ *ppm = NULL;
+ }
+}
+
+int
+mu_progmailer_open (struct _mu_progmailer *pm, char **argv)
+{
+ int tunnel[2];
+ int status = 0;
+ int i;
+
+ if (!pm || !argv)
+ return EINVAL;
+
+ if ((pm->sighandler = signal (SIGCHLD, SIG_DFL)) == SIG_ERR)
+ {
+ status = errno;
+ MU_DEBUG1 (pm->debug, MU_DEBUG_ERROR,
+ "setting SIGCHLD failed: %s\n", mu_strerror (status));
+ return status;
+ }
+
+ if (pipe (tunnel) == 0)
+ {
+ pm->fd = tunnel[1];
+ pm->pid = fork ();
+ if (pm->pid == 0) /* Child. */
+ {
+ SCLOSE (STDIN_FILENO, tunnel);
+ SCLOSE (STDOUT_FILENO, tunnel);
+ SCLOSE (STDERR_FILENO, tunnel);
+ close (tunnel[1]);
+ dup2 (tunnel[0], STDIN_FILENO);
+ execv (pm->command ? pm->command : argv[0], argv);
+ exit (errno ? EX_CANTCREAT : 0);
+ }
+ else if (pm->pid == -1)
+ {
+ status = errno;
+ MU_DEBUG1 (pm->debug, MU_DEBUG_ERROR,
+ "fork failed: %s\n", mu_strerror (status));
+ }
+ }
+ else
+ {
+ status = errno;
+ MU_DEBUG1 (pm->debug, MU_DEBUG_ERROR,
+ "pipe() failed: %s\n", mu_strerror (status));
+ }
+ MU_DEBUG1 (pm->debug, MU_DEBUG_TRACE, "exec %s argv:", pm->command);
+ for (i = 0; argv[i]; i++)
+ MU_DEBUG1 (pm->debug, MU_DEBUG_TRACE, " %s", argv[i]);
+ MU_DEBUG (pm->debug, MU_DEBUG_TRACE, "\n");
+ close (tunnel[0]);
+
+ if (status != 0)
+ close (pm->fd);
+ return status;
+}
+
+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_DEBUG1 (pm->debug, MU_DEBUG_ERROR,
+ "cannot get header stream: %s\n", mu_strerror (status));
+ return status;
+ }
+ MU_DEBUG (pm->debug, MU_DEBUG_TRACE, "Sending headers...\n");
+ 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_DEBUG1 (pm->debug, MU_DEBUG_PROT, "Header: %s", buffer);
+ if (write (pm->fd, buffer, len) == -1)
+ {
+ status = errno;
+
+ MU_DEBUG1 (pm->debug, MU_DEBUG_TRACE,
+ "write failed: %s\n", strerror (status));
+ break;
+ }
+ }
+ found_nl = (len == 1 && buffer[0] == '\n');
+ }
+
+ if (!found_nl)
+ {
+ if (write (pm->fd, "\n", 1) == -1)
+ {
+ status = errno;
+
+ MU_DEBUG1 (pm->debug, MU_DEBUG_TRACE,
+ "write failed: %s\n", strerror (status));
+ }
+ }
+ mu_stream_destroy (&stream);
+
+ MU_DEBUG (pm->debug, MU_DEBUG_TRACE, "Sending body...\n");
+ mu_message_get_body (msg, &body);
+ status = mu_body_get_streamref (body, &stream);
+ if (status)
+ {
+ MU_DEBUG1 (pm->debug, 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_DEBUG1 (pm->debug, MU_DEBUG_TRACE,
+ "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_DEBUG2 (pm->debug, MU_DEBUG_TRACE,
+ "waitpid(%lu) failed: %s\n",
+ (unsigned long) pm->pid, strerror (status));
+ }
+ }
+ else if (WIFEXITED (exit_status))
+ {
+ exit_status = WEXITSTATUS (exit_status);
+ MU_DEBUG2 (pm->debug, 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;
+}
+
+int
+mu_progmailer_close (struct _mu_progmailer *pm)
+{
+ int status = 0;
+
+ if (!pm)
+ return EINVAL;
+
+ if (pm->pid > 0)
+ {
+ kill (SIGTERM, pm->pid);
+ pm->pid = -1;
+ }
+
+ if (pm->sighandler != SIG_ERR
+ && signal (SIGCHLD, pm->sighandler) == SIG_ERR)
+ {
+ status = errno;
+ MU_DEBUG1 (pm->debug, MU_DEBUG_ERROR,
+ "resetting SIGCHLD failed: %s\n", mu_strerror (status));
+ }
+ pm->sighandler = SIG_ERR;
+ return status;
+}

Return to:

Send suggestions and report system problems to the System administrator.