From 22f05f11fe991e8213e51ec561fb2aae03c8898d Mon Sep 17 00:00:00 2001
From: Aki Tuomi <aki.tuomi@dovecot.fi>
Date: Wed, 5 Oct 2016 22:28:03 +0300
Subject: [PATCH] lib-sieve: program-client was moved to dovecot core

---
 src/lib-sieve/util/Makefile.am              |   6 -
 src/lib-sieve/util/program-client-local.c   | 446 ----------------
 src/lib-sieve/util/program-client-private.h |  72 ---
 src/lib-sieve/util/program-client-remote.c  | 317 -----------
 src/lib-sieve/util/program-client.c         | 550 --------------------
 src/lib-sieve/util/program-client.h         |  58 ---
 6 files changed, 1449 deletions(-)
 delete mode 100644 src/lib-sieve/util/program-client-local.c
 delete mode 100644 src/lib-sieve/util/program-client-private.h
 delete mode 100644 src/lib-sieve/util/program-client-remote.c
 delete mode 100644 src/lib-sieve/util/program-client.c
 delete mode 100644 src/lib-sieve/util/program-client.h

diff --git a/src/lib-sieve/util/Makefile.am b/src/lib-sieve/util/Makefile.am
index 6e3bc959b..8b049f785 100644
--- a/src/lib-sieve/util/Makefile.am
+++ b/src/lib-sieve/util/Makefile.am
@@ -10,18 +10,12 @@ libsieve_util_la_DEPENDENCIES = $(LIBDOVECOT_STORAGE_DEPS) $(LIBDOVECOT_DEPS)
 libsieve_util_la_SOURCES = \
 	edit-mail.c \
 	rfc2822.c \
-	program-client-local.c \
-	program-client-remote.c \
-	program-client.c \
 	realpath.c \
 	strtrim.c
 
 headers = \
 	edit-mail.h \
 	rfc2822.h \
-	program-client-private.h \
-	program-client.h \
-	realpath.h \
 	strtrim.h
 
 pkginc_libdir=$(dovecot_pkgincludedir)/sieve
diff --git a/src/lib-sieve/util/program-client-local.c b/src/lib-sieve/util/program-client-local.c
deleted file mode 100644
index 0839a7a76..000000000
--- a/src/lib-sieve/util/program-client-local.c
+++ /dev/null
@@ -1,446 +0,0 @@
-/* Copyright (c) 2002-2016 Pigeonhole authors, see the included COPYING file
- */
-
-#include "lib.h"
-#include "lib-signals.h"
-#include "env-util.h"
-#include "execv-const.h"
-#include "array.h"
-#include "net.h"
-#include "istream.h"
-#include "ostream.h"
-
-#include "program-client-private.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <grp.h>
-
-struct program_client_local {
-	struct program_client client;
-
-	pid_t pid;
-};
-
-static void exec_child
-(const char *bin_path, const char *const *args, const char *const *envs,
-	int in_fd, int out_fd, int *extra_fds, bool drop_stderr)
-{
-	ARRAY_TYPE(const_string) exec_args;
-
-	/* Setup stdin/stdout */
-
-	if ( in_fd < 0 ) {
-		in_fd = open("/dev/null", O_RDONLY);
-
-		if ( in_fd == -1 )
-			i_fatal("open(/dev/null) failed: %m");
-	}
-	if ( out_fd < 0 ) {
-		out_fd = open("/dev/null", O_WRONLY);
-
-		if ( out_fd == -1 )
-			i_fatal("open(/dev/null) failed: %m");
-	}
-
-	if ( in_fd != STDIN_FILENO &&
-		dup2(in_fd, STDIN_FILENO) < 0 )
-		i_fatal("dup2(stdin) failed: %m");
-	if ( out_fd != STDOUT_FILENO &&
-		dup2(out_fd, STDOUT_FILENO) < 0 )
-		i_fatal("dup2(stdout) failed: %m");
-
-	if (  in_fd != STDIN_FILENO &&
-		close(in_fd) < 0 )
-		i_error("close(in_fd) failed: %m");
-	if ( out_fd != STDOUT_FILENO &&
-		(out_fd != in_fd) && close(out_fd) < 0 )
-		i_error("close(out_fd) failed: %m");
-
-	/* Drop stderr if requested */
-	if ( drop_stderr ) {
-		int err_fd = open("/dev/null", O_WRONLY);
-		if ( err_fd == -1 )
-			i_fatal("open(/dev/null) failed: %m");
-		if ( err_fd != STDERR_FILENO ) {
-			if ( dup2(err_fd, STDERR_FILENO) < 0 )
-				i_fatal("dup2(stderr) failed: %m");
-			if ( close(err_fd) < 0 )
-				i_error("close(err_fd) failed: %m");
-		}
-	}
-
-	/* Setup extra fds */
-	if ( extra_fds != NULL ) {
-		int *efd;
-		for (efd = extra_fds; *efd != -1; efd += 2 ) {
-			i_assert(efd[1] != STDIN_FILENO);
-			i_assert(efd[1] != STDOUT_FILENO);
-			i_assert(efd[1] != STDERR_FILENO);
-			if ( efd[0] != efd[1] ) {
-				if ( dup2(efd[0], efd[1]) < 0 )
-					i_fatal("dup2(extra_fd=%d) failed: %m", efd[1]);
-			}
-		}
-		for (efd = extra_fds; *efd != -1; efd += 2 ) {
-			if ( efd[0] != efd[1] && efd[0] != STDIN_FILENO &&
-				efd[0] != STDOUT_FILENO && efd[0] != STDERR_FILENO) {
-				if ( close(efd[0]) < 0 )
-					i_error("close(extra_fd=%d) failed: %m", efd[1]);
-			}
-		}
-	}
-
-	/* Compose argv */
-
-	t_array_init(&exec_args, 16);
-	array_append(&exec_args, &bin_path, 1);
-	if ( args != NULL ) {
-		for (; *args != NULL; args++)
-			array_append(&exec_args, args, 1);
-	}
-	(void)array_append_space(&exec_args);
-
-	/* Setup environment */
-
-	env_clean();
-	if ( envs != NULL ) {
-		for (; *envs != NULL; envs++)
-			env_put(*envs);
-	}
-
-	/* Execute */
-
-	args = array_idx(&exec_args, 0);
-	execvp_const(args[0], args);
-}
-
-static int program_client_local_connect
-(struct program_client *pclient)
-{
-	struct program_client_local *slclient = 
-		(struct program_client_local *) pclient;
-	int fd_in[2] = { -1, -1 }, fd_out[2] = { -1, -1 };
-	struct program_client_extra_fd *efds = NULL;
-	int *parent_extra_fds = NULL, *child_extra_fds = NULL;
-	unsigned int xfd_count = 0, i;
-
-	/* create normal I/O fds */
-	if ( pclient->input != NULL ) {
-		if ( pipe(fd_in) < 0 ) {
-			i_error("pipe(in) failed: %m");
-			return -1;
-		}
-	}
-	if ( pclient->output != NULL ||	pclient->output_seekable ) {
-		if ( pipe(fd_out) < 0 ) {
-			i_error("pipe(out) failed: %m");
-			return -1;
-		}
-	}
-
-	/* create pipes for additional output through side-channel fds */
-	if ( array_is_created(&pclient->extra_fds) ) {
-		int extra_fd[2];
-		
-		efds = array_get_modifiable(&pclient->extra_fds, &xfd_count);
-		if (	xfd_count > 0 ) {
-			parent_extra_fds = t_malloc0(sizeof(int) * xfd_count);
-			child_extra_fds = t_malloc0(sizeof(int) * xfd_count * 2 + 1);
-			for ( i = 0; i < xfd_count; i++ ) {
-				if ( pipe(extra_fd) < 0 ) {
-					i_error("pipe(extra=%d) failed: %m", extra_fd[1]);
-					return -1;
-				}
-				parent_extra_fds[i] = extra_fd[0];
-				child_extra_fds[i*2+0] = extra_fd[1];
-				child_extra_fds[i*2+1] = efds[i].child_fd;
-			}
-			child_extra_fds[xfd_count*2] = -1;
-		}
-	}
-
-	/* fork child */
-	if ( (slclient->pid = fork()) == (pid_t)-1 ) {
-		i_error("fork() failed: %m");
-
-		/* clean up */
-		if ( fd_in[0] >= 0 && close(fd_in[0]) < 0 ) {
-			i_error("close(pipe:in:rd) failed: %m");
-		}
-		if ( fd_in[1] >= 0 && close(fd_in[1]) < 0 ) {
-			i_error("close(pipe:in:wr) failed: %m");
-		}
-		if ( fd_out[0] >= 0 && close(fd_out[0]) < 0 ) {
-			i_error("close(pipe:out:rd) failed: %m");
-		}
-		if ( fd_out[1] >= 0 && close(fd_out[1]) < 0 ) {
-			i_error("close(pipe:out:wr) failed: %m");
-		}
-		for ( i = 0; i < xfd_count; i++ ) {
-			if ( close(child_extra_fds[i*2]) < 0 ) {
-				i_error("close(pipe:extra=%d:wr) failed: %m",
-					child_extra_fds[i*2+1]);
-			}
-			if ( close(parent_extra_fds[i]) < 0 ) {
-				i_error("close(pipe:extra=%d:rd) failed: %m",
-					child_extra_fds[i*2+1]);
-			}
-		}
-		return -1;
-	}
-
-	if ( slclient->pid == 0 ) {
-		unsigned int count;
-		const char *const *envs = NULL;
-
-		/* child */
-		if ( fd_in[1] >= 0 && close(fd_in[1]) < 0 )
-			i_error("close(pipe:in:wr) failed: %m");
-		if ( fd_out[0] >= 0 && close(fd_out[0]) < 0 )
-			i_error("close(pipe:out:rd) failed: %m");
-		for ( i = 0; i < xfd_count; i++ ) {
-			if ( close(parent_extra_fds[i]) < 0 ) {
-				i_error("close(pipe:extra=%d:rd) failed: %m",
-					child_extra_fds[i*2+1]);
-			}
-		}
-
-		/* drop privileges if we have any */
-		if ( getuid() == 0 ) {
-			uid_t uid;
-			gid_t gid;
-
-			/* switch back to root */
-			if (seteuid(0) < 0)
-				i_fatal("seteuid(0) failed: %m");
-
-			/* drop gids first */
-			gid = getgid();
-			if ( gid == 0 || gid != pclient->set.gid ) {
-				if ( pclient->set.gid != 0 )
-					gid = pclient->set.gid;
-				else
-					gid = getegid();
-			}
-	    if ( setgroups(1, &gid) < 0 )
-				i_fatal("setgroups(%d) failed: %m", gid);
-			if ( gid != 0 && setgid(gid) < 0 )
-				i_fatal("setgid(%d) failed: %m", gid);
-		
-			/* drop uid */
-			if ( pclient->set.uid != 0 )
-				uid = pclient->set.uid;
-			else
-				uid = geteuid();
-			if ( uid != 0 && setuid(uid) < 0 )
-				i_fatal("setuid(%d) failed: %m", uid);
-		}
-
-		i_assert(pclient->set.uid == 0 || getuid() != 0);
-		i_assert(pclient->set.gid == 0 || getgid() != 0);
-
-		if ( array_is_created(&pclient->envs) )
-			envs = array_get(&pclient->envs, &count);
-
-		exec_child(pclient->path, pclient->args, envs,
-			fd_in[0], fd_out[1], child_extra_fds,
-			pclient->set.drop_stderr);
-		i_unreached();
-	}
-
-	/* parent */
-	if ( fd_in[0] >= 0 && close(fd_in[0]) < 0 )
-		i_error("close(pipe:in:rd) failed: %m");
-	if ( fd_out[1] >= 0 && close(fd_out[1]) < 0 )
-		i_error("close(pipe:out:wr) failed: %m");
-	if ( fd_in[1] >= 0 ) {
-		net_set_nonblock(fd_in[1], TRUE);
-		pclient->fd_out = fd_in[1];
-	}
-	if ( fd_out[0] >= 0 ) {
-		net_set_nonblock(fd_out[0], TRUE);
-		pclient->fd_in = fd_out[0];
-	}
-	for ( i = 0; i < xfd_count; i++ ) {
-		if ( close(child_extra_fds[i*2]) < 0 ) {
-			i_error("close(pipe:extra=%d:wr) failed: %m",
-				child_extra_fds[i*2+1]);
-		}
-		net_set_nonblock(parent_extra_fds[i], TRUE);
-		efds[i].parent_fd = parent_extra_fds[i];
-	}
-
-	program_client_init_streams(pclient);
-	return program_client_connected(pclient);
-}
-
-static int program_client_local_close_output(struct program_client *pclient)
-{
-	int fd_out = pclient->fd_out;
-
-	pclient->fd_out = -1;
-
-	/* Shutdown output; program stdin will get EOF */
-	if ( fd_out >= 0 && close(fd_out) < 0 ) {
-		i_error("close(%s) failed: %m", pclient->path);
-		return -1;
-	}
-	return 1;
-}
-
-static int program_client_local_disconnect
-(struct program_client *pclient, bool force)
-{
-	struct program_client_local *slclient = 
-		(struct program_client_local *) pclient;
-	pid_t pid = slclient->pid, ret;
-	time_t runtime, timeout = 0;
-	int status;
-	
-	if ( pid < 0 ) {
-		/* program never started */
-		pclient->exit_code = 0;
-		return 0;
-	}
-
-	slclient->pid = -1;
-
-	/* Calculate timeout */
-	runtime = ioloop_time - pclient->start_time;
-	if ( !force && pclient->set.input_idle_timeout_secs > 0 &&
-		runtime < (time_t)pclient->set.input_idle_timeout_secs )
-		timeout = pclient->set.input_idle_timeout_secs - runtime;
-
-	if ( pclient->debug ) {
-		i_debug("waiting for program `%s' to finish after %llu seconds",
-			pclient->path, (unsigned long long int)runtime);
-	}
-
-	/* Wait for child to exit */
-	force = force ||
-		(timeout == 0 && pclient->set.input_idle_timeout_secs > 0);
-	if ( !force ) {
-		alarm(timeout);
-		ret = waitpid(pid, &status, 0);
-		alarm(0);
-	}
-	if ( force || ret < 0 ) {
-		if ( !force && errno != EINTR ) {
-			i_error("waitpid(%s) failed: %m", pclient->path);
-			(void)kill(pid, SIGKILL);
-			return -1;
-		}
-
-		/* Timed out */
-		force = TRUE;
-		if ( pclient->error == PROGRAM_CLIENT_ERROR_NONE )
-			pclient->error = PROGRAM_CLIENT_ERROR_RUN_TIMEOUT;
-		if ( pclient->debug ) {
-			i_debug("program `%s' execution timed out after %llu seconds: "
-				"sending TERM signal", pclient->path,
-				(unsigned long long int)pclient->set.input_idle_timeout_secs);
-		}
-
-		/* Kill child gently first */
-		if ( kill(pid, SIGTERM) < 0 ) {
-			i_error("failed to send SIGTERM signal to program `%s'", pclient->path);
-			(void)kill(pid, SIGKILL);
-			return -1;
-		} 
-			
-		/* Wait for it to die (give it some more time) */
-		alarm(5);
-		ret = waitpid(pid, &status, 0);
-		alarm(0);
-		if ( ret < 0 ) {
-			if ( errno != EINTR ) {
-				i_error("waitpid(%s) failed: %m", pclient->path);
-				(void)kill(pid, SIGKILL); 
-				return -1;
-			}
-
-			/* Timed out again */
-			if ( pclient->debug ) {
-				i_debug("program `%s' execution timed out: sending KILL signal",
-					pclient->path);
-			}
-
-			/* Kill it brutally now */
-			if ( kill(pid, SIGKILL) < 0 ) {
-				i_error("failed to send SIGKILL signal to program `%s'",
-					pclient->path);
-				return -1;
-			}
-
-			/* Now it will die immediately */
-			if ( waitpid(pid, &status, 0) < 0 ) {
-				i_error("waitpid(%s) failed: %m", pclient->path);
-				return -1;
-			}
-		}
-	}
-	
-	/* Evaluate child exit status */
-	pclient->exit_code = -1;
-	if ( WIFEXITED(status) ) {
-		/* Exited */
-		int exit_code = WEXITSTATUS(status);
-				
-		if ( exit_code != 0 ) {
-			i_info("program `%s' terminated with non-zero exit code %d", 
-				pclient->path, exit_code);
-			pclient->exit_code = 0;
-			return 0;
-		}
-
-		pclient->exit_code = 1;
-		return 1;	
-
-	} else if ( WIFSIGNALED(status) ) {
-		/* Killed with a signal */
-		
-		if ( force ) {
-			i_error("program `%s' was forcibly terminated with signal %d",
-				pclient->path, WTERMSIG(status));
-		} else {
-			i_error("program `%s' terminated abnormally, signal %d",
-				pclient->path, WTERMSIG(status));
-		}
-		return -1;
-
-	} else if ( WIFSTOPPED(status) ) {
-		/* Stopped */
-		i_error("program `%s' stopped, signal %d",
-			pclient->path, WSTOPSIG(status));
-		return -1;
-	} 
-
-	/* Something else */
-	i_error("program `%s' terminated abnormally, return status %d",
-		pclient->path, status);
-	return -1;
-}
-
-struct program_client *program_client_local_create
-(const char *bin_path, const char *const *args,
-	const struct program_client_settings *set)
-{
-	struct program_client_local *pclient;
-	pool_t pool;
-
-	pool = pool_alloconly_create("program client local", 1024);
-	pclient = p_new(pool, struct program_client_local, 1);
-	program_client_init(&pclient->client, pool, bin_path, args, set);
-	pclient->client.connect = program_client_local_connect;
-	pclient->client.close_output = program_client_local_close_output;
-	pclient->client.disconnect = program_client_local_disconnect;
-	pclient->pid = -1;
-
-	return &pclient->client;
-}
-
diff --git a/src/lib-sieve/util/program-client-private.h b/src/lib-sieve/util/program-client-private.h
deleted file mode 100644
index 06f91689d..000000000
--- a/src/lib-sieve/util/program-client-private.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* Copyright (c) 2002-2016 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __PROGRAM_CLIENT_PRIVATE_H
-#define __PROGRAM_CLIENT_PRIVATE_H
-
-#include "program-client.h"
-
-enum program_client_error {
-	PROGRAM_CLIENT_ERROR_NONE,
-	PROGRAM_CLIENT_ERROR_CONNECT_TIMEOUT,
-	PROGRAM_CLIENT_ERROR_RUN_TIMEOUT,
-	PROGRAM_CLIENT_ERROR_IO,
-	PROGRAM_CLIENT_ERROR_OTHER
-};
-
-struct program_client_extra_fd {
-	struct program_client *pclient;
-
-	int child_fd, parent_fd;
-	struct istream *input;
-	struct io *io;
-
-	program_client_fd_callback_t *callback;
-	void *context;
-};
-
-struct program_client {
-	pool_t pool;
-	struct program_client_settings set;
-
-	char *path;
-	const char **args;
-	ARRAY_TYPE(const_string) envs;
-
-	int fd_in, fd_out;
-	struct io *io;
-	struct ioloop *ioloop;
-	struct timeout *to;
-	time_t start_time;
-
-	struct istream *input, *program_input, *seekable_output;
-	struct ostream *output, *program_output;
-	char *temp_prefix;
-
-	ARRAY(struct program_client_extra_fd) extra_fds;
-
-	enum program_client_error error;
-	int exit_code;
-
-	int (*connect)(struct program_client *pclient);
-	int (*close_output)(struct program_client *pclient);
-	int (*disconnect)(struct program_client *pclient, bool force);
-	
-	bool debug:1;
-	bool disconnected:1;
-	bool output_seekable:1;
-};
-
-void program_client_init
-	(struct program_client *pclient, pool_t pool, const char *path,
-		const char *const *args, const struct program_client_settings *set);
-
-void program_client_init_streams(struct program_client *pclient);
-
-int program_client_connected(struct program_client *pclient);
-
-void program_client_fail
-	(struct program_client *pclient, enum program_client_error error);
-
-#endif
-
diff --git a/src/lib-sieve/util/program-client-remote.c b/src/lib-sieve/util/program-client-remote.c
deleted file mode 100644
index f1e87878e..000000000
--- a/src/lib-sieve/util/program-client-remote.c
+++ /dev/null
@@ -1,317 +0,0 @@
-/* Copyright (c) 2002-2016 Pigeonhole authors, see the included COPYING file
- */
-
-#include "lib.h"
-#include "ioloop.h"
-#include "str.h"
-#include "net.h"
-#include "write-full.h"
-#include "eacces-error.h"
-#include "istream-private.h"
-#include "ostream.h"
-
-#include "program-client-private.h"
-
-#include <unistd.h>
-#include <sys/wait.h>
-#include <sysexits.h>
-
-/*
- * Script client input stream
- */
-
-struct program_client_istream {
-	struct istream_private istream;
-
-	struct stat statbuf;
-
-	struct program_client *client;
-};
-
-static void program_client_istream_destroy(struct iostream_private *stream)
-{
-	struct program_client_istream *scstream =
-		(struct program_client_istream *)stream;
-
-	i_stream_unref(&scstream->istream.parent);
-}
-
-static ssize_t program_client_istream_read(struct istream_private *stream)
-{
-	struct program_client_istream *scstream =
-		(struct program_client_istream *)stream;
-	size_t pos, reserved;
-	ssize_t ret = 0;
-
-	i_stream_skip(stream->parent, stream->skip);
-	stream->skip = 0;
-
-	stream->buffer = i_stream_get_data(stream->parent, &pos);
-
-	reserved = 0;
-	if ( stream->buffer != NULL && pos >= 1 ) {
-		/* retain/hide potential return code at end of buffer */
-		reserved = ( stream->buffer[pos-1] == '\n' && pos > 1 ? 2 : 1 );
-		pos -= reserved;
-	}
-
-	if ( stream->parent->eof ) {
-		if (pos == 0)
-			i_stream_skip(stream->parent, reserved);
-		stream->istream.eof = TRUE;
-		ret = -1;
-	} else do {
-		if ((ret = i_stream_read(stream->parent)) == -2) {
-			return -2; /* input buffer full */
-		}
-
-		if ( ret == 0 || (ret < 0 && !stream->parent->eof) ) break;
-
-		stream->istream.stream_errno = stream->parent->stream_errno;
-		stream->buffer = i_stream_get_data(stream->parent, &pos);
-
-		if ( stream->parent->eof ) {
-			/* Check return code at EOF */
-			if ( stream->buffer != NULL && pos >= 2 &&
-				stream->buffer[pos-1] == '\n' ) {
-				switch ( stream->buffer[pos-2] ) {
-				case '+':
-					scstream->client->exit_code = 1;
-					break;
-				case '-':
-					scstream->client->exit_code = 0;
-					break;
-				default:
-					scstream->client->exit_code = -1;
-				}
-			} else {
-				scstream->client->exit_code = -1;
-			}
-		}
-	
-		if ( stream->buffer != NULL && pos >= 1 ) {
-			/* retain/hide potential return code at end of buffer */
-			size_t old_reserved = reserved;
-			ssize_t reserve_mod;
-
-			reserved = ( stream->buffer[pos-1] == '\n' && pos > 1 ? 2 : 1 );
-			reserve_mod = reserved - old_reserved;
-			pos -= reserved;
-
-			if (ret >= reserve_mod) {
-				ret -= reserve_mod;
-			}
-		}
-
-		if ( ret <= 0 && stream->parent->eof ) {
-			/* Parent EOF and not more data to return; EOF here as well */
-			if (pos == 0)
-				i_stream_skip(stream->parent, reserved);
-			stream->istream.eof = TRUE;
-			ret = -1;
-		}		
-	} while ( ret == 0 );
-
-	stream->pos = pos;
-
-	i_assert(ret != -1 || stream->istream.eof ||
-		stream->istream.stream_errno != 0);
-	return ret;
-}
-
-static void ATTR_NORETURN program_client_istream_sync
-(struct istream_private *stream ATTR_UNUSED)
-{
-	i_panic("program_client_istream sync() not implemented");
-}
-
-static int program_client_istream_stat
-(struct istream_private *stream, bool exact)
-{
-	struct program_client_istream *scstream =
-		(struct program_client_istream *)stream;
-	const struct stat *st;
-	int ret;
-
-	/* Stat the original stream */
-	ret = i_stream_stat(stream->parent, exact, &st);
-	if (ret < 0 || st->st_size == -1 || !exact)
-		return ret;
-
-	scstream->statbuf = *st;
-	scstream->statbuf.st_size = -1;
-
-	return ret;
-}
-
-static struct istream *program_client_istream_create
-(struct program_client *program_client, struct istream *input)
-{
-	struct program_client_istream *scstream;
-
-	scstream = i_new(struct program_client_istream, 1);
-	scstream->client = program_client;
-
-	scstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
-
-	scstream->istream.iostream.destroy = program_client_istream_destroy;
-	scstream->istream.read = program_client_istream_read;
-	scstream->istream.sync = program_client_istream_sync;
-	scstream->istream.stat = program_client_istream_stat;
-
-	scstream->istream.istream.readable_fd = FALSE;
-	scstream->istream.istream.blocking = input->blocking;
-	scstream->istream.istream.seekable = FALSE;
-
-	i_stream_seek(input, 0);
-
-	return i_stream_create(&scstream->istream, input, -1);
-}
-
-/*
- * Program client
- */
-
-struct program_client_remote {
-	struct program_client client;
-
-	bool noreply:1;
-};
-
-static void program_client_remote_connected(struct program_client *pclient)
-{
-	struct program_client_remote *slclient =
-		(struct program_client_remote *)pclient;
-	const char **args = pclient->args;
-	string_t *str;
-
-	io_remove(&pclient->io);
-	program_client_init_streams(pclient);
-
-	if ( !slclient->noreply ) {
-		pclient->program_input = program_client_istream_create
-			(pclient, pclient->program_input);
-	}
-
-	str = t_str_new(1024);
-	str_append(str, "VERSION\tscript\t3\t0\n");
-	if ( slclient->noreply )
-		str_append(str, "noreply\n");
-	else
-		str_append(str, "-\n");
-	if ( args != NULL ) {
-		for (; *args != NULL; args++) {
-			str_append(str, *args);
-			str_append_c(str, '\n');
-		}
-	}
-	str_append_c(str, '\n');
-
-	if ( o_stream_send
-		(pclient->program_output, str_data(str), str_len(str)) < 0 ) {
-		i_error("write(%s) failed: %s",
-			o_stream_get_name(pclient->program_output),
-			o_stream_get_error(pclient->program_output));
-		program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
-		return;
-	}
-
-	(void)program_client_connected(pclient);
-}
-
-static int program_client_remote_connect(struct program_client *pclient)
-{
-	struct program_client_remote *slclient =
-		(struct program_client_remote *)pclient;
-	int fd;
-
-	if ((fd = net_connect_unix_with_retries(pclient->path, 1000)) < 0) {
-		switch (errno) {
-		case EACCES:
-			i_error("%s", eacces_error_get("net_connect_unix", pclient->path));
-			return -1;
-		default:
-			i_error("net_connect_unix(%s) failed: %m", pclient->path);
-			return -1;
-		}
-	}
-
-	net_set_nonblock(fd, TRUE);
-	
-	pclient->fd_in = ( slclient->noreply && pclient->output == NULL &&
-		!pclient->output_seekable ? -1 : fd );
-	pclient->fd_out = fd;
-	pclient->io = io_add(fd, IO_WRITE, program_client_remote_connected, pclient);
-	return 0;
-}
-
-static int program_client_remote_close_output(struct program_client *pclient)
-{
-	int fd_out = pclient->fd_out, fd_in = pclient->fd_in;
-
-	pclient->fd_out = -1;
-
-	/* Shutdown output; program stdin will get EOF */
-	if ( fd_out >= 0 ) {
-		if ( fd_in >= 0 ) {
-			if ( shutdown(fd_out, SHUT_WR) < 0 && errno != ENOTCONN ) {
-				i_error("shutdown(%s, SHUT_WR) failed: %m", pclient->path);
-				return -1;
-			}
-		} else if ( close(fd_out) < 0 ) {
-			i_error("close(%s) failed: %m", pclient->path);
-			return -1;
-		}
-	}
-
-	return 1;
-}
-
-static int program_client_remote_disconnect
-(struct program_client *pclient, bool force)
-{
-	struct program_client_remote *slclient =
-		(struct program_client_remote *)pclient;
-	int ret = 0;
-	
-	if ( pclient->error == PROGRAM_CLIENT_ERROR_NONE && !slclient->noreply &&
-		pclient->program_input != NULL && !force) {
-		const unsigned char *data;
-		size_t size;
-
-		/* Skip any remaining program output and parse the exit code */
-		while ((ret = i_stream_read_more
-			(pclient->program_input, &data, &size)) > 0) {
-			i_stream_skip(pclient->program_input, size);
-		}
-
-		/* Get exit code */
-		if ( !pclient->program_input->eof )
-			ret = -1;
-		else
-			ret = pclient->exit_code;		
-	} else {
-		ret = 1;
-	}
-
-	return ret;
-}
-
-struct program_client *program_client_remote_create
-(const char *socket_path, const char *const *args, 
-	const struct program_client_settings *set, bool noreply)
-{
-	struct program_client_remote *pclient;
-	pool_t pool;
-
-	pool = pool_alloconly_create("program client remote", 1024);
-	pclient = p_new(pool, struct program_client_remote, 1);
-	program_client_init(&pclient->client, pool, socket_path, args, set);
-	pclient->client.connect = program_client_remote_connect;
-	pclient->client.close_output = program_client_remote_close_output;
-	pclient->client.disconnect = program_client_remote_disconnect;
-	pclient->noreply = noreply;
-
-	return &pclient->client;
-}
-
diff --git a/src/lib-sieve/util/program-client.c b/src/lib-sieve/util/program-client.c
deleted file mode 100644
index d2370ef25..000000000
--- a/src/lib-sieve/util/program-client.c
+++ /dev/null
@@ -1,550 +0,0 @@
-/* Copyright (c) 2002-2016 Pigeonhole authors, see the included COPYING file
- */
-
-#include "lib.h"
-#include "ioloop.h"
-#include "array.h"
-#include "str.h"
-#include "safe-mkstemp.h"
-#include "istream-private.h"
-#include "istream-seekable.h"
-#include "ostream.h"
-
-#include "program-client-private.h"
-
-#include <unistd.h>
-
-#define MAX_OUTPUT_BUFFER_SIZE 16384
-#define MAX_OUTPUT_MEMORY_BUFFER (1024*128)
-
-static int program_client_seekable_fd_callback
-(const char **path_r, void *context)
-{
-	struct program_client *pclient = (struct program_client *)context;
-	string_t *path;
-	int fd;
-
-	path = t_str_new(128);
-	str_append(path, pclient->temp_prefix);
-	fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
-	if (fd == -1) {
-		i_error("safe_mkstemp(%s) failed: %m", str_c(path));
-		return -1;
-	}
-
-	/* we just want the fd, unlink it */
-	if (i_unlink(str_c(path)) < 0) {
-		/* shouldn't happen.. */
-		i_close_fd(&fd);
-		return -1;
-	}
-
-	*path_r = str_c(path);
-	return fd;
-}
-
-static void program_client_timeout(struct program_client *pclient)
-{
-	i_error("program `%s' execution timed out (> %d secs)",
-		pclient->path, pclient->set.input_idle_timeout_secs);
-	program_client_fail(pclient, PROGRAM_CLIENT_ERROR_RUN_TIMEOUT);
-}
-
-static void program_client_connect_timeout(struct program_client *pclient)
-{
-	i_error("program `%s' socket connection timed out (> %d msecs)",
-		pclient->path, pclient->set.client_connect_timeout_msecs);
-	program_client_fail(pclient, PROGRAM_CLIENT_ERROR_CONNECT_TIMEOUT);
-}
-
-static int program_client_connect(struct program_client *pclient)
-{
-	int ret;
-
-	if (pclient->set.client_connect_timeout_msecs != 0) {
-		pclient->to = timeout_add
-			(pclient->set.client_connect_timeout_msecs,
-				program_client_connect_timeout, pclient);
-	}
-
-	if ( (ret=pclient->connect(pclient)) < 0 ) {
-		program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
-		return -1;
-	}
-	return ret;
-}
-
-static int program_client_close_output(struct program_client *pclient)
-{
-	int ret;
-
-	if ( pclient->program_output != NULL )
-		o_stream_destroy(&pclient->program_output);
-	if ( (ret=pclient->close_output(pclient)) < 0 )
-		return -1;
-	pclient->program_output = NULL;
-
-	return ret;
-}
-
-static void program_client_disconnect_extra_fds
-(struct program_client *pclient)
-{
-	struct program_client_extra_fd *efds;
-	unsigned int i, count;
-	
-	if (!array_is_created(&pclient->extra_fds))
-		return;
-
-	efds = array_get_modifiable(&pclient->extra_fds, &count);
-	for ( i = 0; i < count; i++ ) {
-		if ( efds[i].input != NULL )
-			i_stream_unref(&efds[i].input);
-		if ( efds[i].io != NULL )
-			io_remove(&efds[i].io);
-		if ( efds[i].parent_fd != -1 && close(efds[i].parent_fd) < 0 )
-			i_error("close(fd=%d) failed: %m", efds[i].parent_fd);
-	}
-}
-
-static void program_client_disconnect
-(struct program_client *pclient, bool force)
-{
-	bool error = FALSE;
-	int ret;
-
-	if ( pclient->ioloop != NULL )
-		io_loop_stop(pclient->ioloop);
-
-	if ( pclient->disconnected )
-		return;
-
-	if ( (ret=program_client_close_output(pclient)) < 0 )
-		error = TRUE;
-
-	program_client_disconnect_extra_fds(pclient);
-	if ( (ret=pclient->disconnect(pclient, force)) < 0 )
-		error = TRUE;
-
-	if ( pclient->program_input != NULL ) {
-		if (pclient->output_seekable)
-			i_stream_unref(&pclient->program_input);
-		else
-			i_stream_destroy(&pclient->program_input);
-	} 
-	if ( pclient->program_output != NULL )
-		o_stream_destroy(&pclient->program_output);
-
-	if ( pclient->to != NULL )
-		timeout_remove(&pclient->to);
-	if ( pclient->io != NULL )
-		io_remove(&pclient->io);
-
-	if (pclient->fd_in != -1 && close(pclient->fd_in) < 0)
-		i_error("close(%s) failed: %m", pclient->path);
-	if (pclient->fd_out != -1 && pclient->fd_out != pclient->fd_in
-		&& close(pclient->fd_out) < 0)
-		i_error("close(%s/out) failed: %m", pclient->path);
-	pclient->fd_in = pclient->fd_out = -1;
-	
-	pclient->disconnected = TRUE;
-	if (error && pclient->error == PROGRAM_CLIENT_ERROR_NONE ) {
-		pclient->error = PROGRAM_CLIENT_ERROR_OTHER;
-	}
-}
-
-void program_client_fail
-(struct program_client *pclient, enum program_client_error error)
-{
-	if ( pclient->error != PROGRAM_CLIENT_ERROR_NONE )
-		return;
-
-	pclient->error = error;
-	program_client_disconnect(pclient, TRUE);
-}
-
-static bool program_client_input_pending(struct program_client *pclient)
-{
-	struct program_client_extra_fd *efds = NULL;
-	unsigned int count, i;
-
-	if ( pclient->program_input != NULL &&
-		!pclient->program_input->closed &&
-		!i_stream_is_eof(pclient->program_input) ) {
-		return TRUE;
-	}
-
-	if ( array_is_created(&pclient->extra_fds) ) {
-		efds = array_get_modifiable(&pclient->extra_fds, &count);
-		for ( i = 0; i < count; i++ ) {
-			if ( efds[i].input != NULL &&
-				!efds[i].input->closed &&
-				!i_stream_is_eof(efds[i].input) ) {
-				return TRUE;
-			}
-		}
-	}
-
-	return FALSE;
-}
-
-static int program_client_program_output(struct program_client *pclient)
-{
-	struct istream *input = pclient->input;
-	struct ostream *output = pclient->program_output;
-	enum ostream_send_istream_result res;
-	int ret = 0;
-
-	if ((ret = o_stream_flush(output)) <= 0) {
-		if (ret < 0) {
-			i_error("write(%s) failed: %s",
-				o_stream_get_name(output),
-				o_stream_get_error(output));
-			program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
-		}
-		return ret;
-	}
-
-	if ( input != NULL && output != NULL ) {
-		res = o_stream_send_istream(output, input);
-
-		switch (res) {
-		case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
-			i_stream_unref(&pclient->input);
-			input = NULL;
-			break;
-		case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
-			return 1;
-		case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
-			return 0;
-		case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
-			i_error("read(%s) failed: %s",
-				i_stream_get_name(input),
-				i_stream_get_error(input));
-			program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
-			return -1;
-		case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
-			i_error("write(%s) failed: %s",
-				o_stream_get_name(output),
-				o_stream_get_error(output));
-			program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
-			return -1;
-		}
-	}
-
-	if ( input == NULL ) {
-		if ( !program_client_input_pending(pclient) ) {
-			program_client_disconnect(pclient, FALSE);
-		} else if (program_client_close_output(pclient) < 0) {
-			program_client_fail(pclient, PROGRAM_CLIENT_ERROR_OTHER);
-		}
-	}
-	return 1;
-}
-
-static void program_client_program_input(struct program_client *pclient)
-{
-	struct istream *input = pclient->program_input;
-	struct ostream *output = pclient->output;
-	enum ostream_send_istream_result res;
-	const unsigned char *data;
-	size_t size;
-	int ret = 0;
-
-	if (pclient->output_seekable && pclient->seekable_output == NULL) {
-		struct istream *input_list[2] = { input, NULL };
-
-		input = i_stream_create_seekable(input_list, MAX_OUTPUT_MEMORY_BUFFER,
-					 program_client_seekable_fd_callback, pclient);
-		i_stream_unref(&pclient->program_input);
-		pclient->program_input = input;
-
-		pclient->seekable_output = input;
-		i_stream_ref(pclient->seekable_output);
-	}
-
-	if ( input != NULL ) {
-		if ( output != NULL ) {
-			res = o_stream_send_istream(output, input);
-
-			switch (res) {
-			case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
-				break;
-			case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
-			case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
-				return;
-			case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
-				i_error("read(%s) failed: %s",
-					i_stream_get_name(input),
-					i_stream_get_error(input));
-				program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
-				return;
-			case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
-				i_error("write(%s) failed: %s",
-					o_stream_get_name(output),
-					o_stream_get_error(output));
-				program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
-				return;
-			}
-		} else {
-			while ( (ret=i_stream_read_more(input, &data, &size)) > 0 ||
-				ret == -2 ) {
-				i_stream_skip(input, size);
-			}
-
-			if ( ret == 0 )
-				return;
-			if ( ret < 0 ) {
-				if ( input->stream_errno != 0 ) {
-					i_error("read(%s) failed: %s",
-						i_stream_get_name(input),
-						i_stream_get_error(input));
-					program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
-					return;
-				}
-			}
-		}
-		if ( !program_client_input_pending(pclient) )
-			program_client_disconnect(pclient, FALSE);
-	}
-}
-
-static void program_client_extra_fd_input
-(struct program_client_extra_fd *efd)
-{
-	struct program_client *pclient = efd->pclient;
-
-	i_assert(efd->callback != NULL);
-	efd->callback(efd->context, efd->input);
-
-	if (efd->input->closed || i_stream_is_eof(efd->input)) {
-		if ( !program_client_input_pending(pclient) )
-			program_client_disconnect(pclient, FALSE);
-	}
-}
-
-int program_client_connected
-(struct program_client *pclient)
-{
-	int ret = 1;
-
-	pclient->start_time = ioloop_time;
-	if (pclient->to != NULL)
-		timeout_remove(&pclient->to);
-	if ( pclient->set.input_idle_timeout_secs != 0 ) {
-		pclient->to = timeout_add(pclient->set.input_idle_timeout_secs*1000,
-      program_client_timeout, pclient);
-	}
-
-	/* run output */
-	if ( pclient->program_output != NULL &&
-		(ret=program_client_program_output(pclient)) == 0 ) {
-		if ( pclient->program_output != NULL ) {
-			o_stream_set_flush_callback
-				(pclient->program_output, program_client_program_output, pclient);
-		}
-	}
-
-	return ret;
-}
-
-void program_client_init
-(struct program_client *pclient, pool_t pool, const char *path,
-	const char *const *args, const struct program_client_settings *set)
-{
-	pclient->pool = pool;
-	pclient->path = p_strdup(pool, path);
-	if ( args != NULL )
-		pclient->args = p_strarray_dup(pool, args);
-	pclient->set = *set;
-	pclient->debug = set->debug;
-	pclient->fd_in = -1;
-	pclient->fd_out = -1;
-}
-
-void program_client_set_input
-(struct program_client *pclient, struct istream *input)
-{
-	if ( pclient->input != NULL )
-		i_stream_unref(&pclient->input);
-	if ( input != NULL )
-		i_stream_ref(input);
-	pclient->input = input;
-}
-
-void program_client_set_output
-(struct program_client *pclient, struct ostream *output)
-{
-	if ( pclient->output != NULL )
-		o_stream_unref(&pclient->output);
-	if ( output != NULL )
-		o_stream_ref(output);
-	pclient->output = output;
-	pclient->output_seekable = FALSE;
-	i_free(pclient->temp_prefix);
-}
-
-void program_client_set_output_seekable
-(struct program_client *pclient, const char *temp_prefix)
-{
-	if ( pclient->output != NULL )
-		o_stream_unref(&pclient->output);
-	pclient->temp_prefix = i_strdup(temp_prefix);
-	pclient->output_seekable = TRUE;
-}
-
-struct istream *program_client_get_output_seekable
-(struct program_client *pclient)
-{
-	struct istream *input = pclient->seekable_output;
-	
-	pclient->seekable_output = NULL;
-
-	i_stream_seek(input, 0);
-	return input;
-}
-
-#undef program_client_set_extra_fd
-void program_client_set_extra_fd
-(struct program_client *pclient, int fd,
-	program_client_fd_callback_t *callback, void *context)
-{
-	struct program_client_extra_fd *efds;
-	struct program_client_extra_fd *efd = NULL;
-	unsigned int i, count;
-	i_assert(fd > 1);
-	
-	if ( !array_is_created(&pclient->extra_fds) )
-		p_array_init(&pclient->extra_fds, pclient->pool, 2);
-
-	efds = array_get_modifiable(&pclient->extra_fds, &count);
-	for ( i = 0; i < count; i++ ) {
-		if ( efds[i].child_fd == fd ) {
-			efd = &efds[i];
-			break;
-		}
-	}
-
-	if ( efd == NULL ) {
-		efd = array_append_space(&pclient->extra_fds);
-		efd->pclient = pclient;
-		efd->child_fd = fd;
-		efd->parent_fd = -1;
-	}
-	efd->callback = callback;
-	efd->context = context;
-}
-
-void program_client_set_env
-(struct program_client *pclient, const char *name, const char *value)
-{
-	const char *env;
-
-	if ( !array_is_created(&pclient->envs) )
-		p_array_init(&pclient->envs, pclient->pool, 16);
-
-	env = p_strdup_printf(pclient->pool, "%s=%s", name, value);
-	array_append(&pclient->envs, &env, 1);
-}
-
-void program_client_init_streams(struct program_client *pclient)
-{
-	/* Create streams for normal program I/O */
-	if ( pclient->fd_out >= 0 ) {
-		pclient->program_output =
-			o_stream_create_fd(pclient->fd_out, MAX_OUTPUT_BUFFER_SIZE);
-		o_stream_set_name(pclient->program_output, "program stdin");
-	}
-	if ( pclient->fd_in >= 0 ) {
-		struct istream *input;
-		
-		input = i_stream_create_fd(pclient->fd_in, (size_t)-1);
-
-		pclient->program_input = input;
-		i_stream_set_name(pclient->program_input, "program stdout");
-
-		pclient->io = io_add
-			(pclient->fd_in, IO_READ, program_client_program_input, pclient);
-	}
-
-	/* Create streams for additional output through side-channel fds */
-	if ( array_is_created(&pclient->extra_fds) ) {
-		struct program_client_extra_fd *efds = NULL;
-		unsigned int count, i;
-		
-		efds = array_get_modifiable(&pclient->extra_fds, &count);
-		for ( i = 0; i < count; i++ ) {
-			i_assert( efds[i].parent_fd >= 0 );
-			efds[i].input = i_stream_create_fd
-				(efds[i].parent_fd, (size_t)-1);
-			i_stream_set_name(efds[i].input,
-				t_strdup_printf("program output fd=%d", efds[i].child_fd));
-			efds[i].io = io_add
-				(efds[i].parent_fd, IO_READ, program_client_extra_fd_input, &efds[i]);
-		}
-	}
-}
-
-void program_client_destroy(struct program_client **_pclient)
-{
-	struct program_client *pclient = *_pclient;
-
-	program_client_disconnect(pclient, TRUE);
-
-	if ( pclient->input != NULL )
-		i_stream_unref(&pclient->input);
-	if ( pclient->output != NULL )
-		o_stream_unref(&pclient->output);
-	if ( pclient->seekable_output != NULL )
-		i_stream_unref(&pclient->seekable_output);
-	if ( pclient->io != NULL )
-		io_remove(&pclient->io);
-	if ( pclient->ioloop != NULL )
-		io_loop_destroy(&pclient->ioloop);
-	i_free(pclient->temp_prefix);
-	pool_unref(&pclient->pool);
-	*_pclient = NULL;
-}
-
-int program_client_run(struct program_client *pclient)
-{
-	int ret;
-
-	/* reset */
-	pclient->disconnected = FALSE;
-	pclient->exit_code = 1;
-	pclient->error = PROGRAM_CLIENT_ERROR_NONE;
-
-	pclient->ioloop = io_loop_create();
-
-	if ( (ret=program_client_connect(pclient)) >= 0 ) {
-		/* run output */
-		if ( ret > 0 && pclient->program_output != NULL &&
-			(ret=o_stream_flush(pclient->program_output)) == 0 ) {
-			o_stream_set_flush_callback
-				(pclient->program_output, program_client_program_output, pclient);
-		}
-
-		/* run i/o event loop */
-		if ( ret < 0 ) {
-			i_error("write(%s) failed: %s",
-				o_stream_get_name(pclient->program_output),
-				o_stream_get_error(pclient->program_output));
-			pclient->error = PROGRAM_CLIENT_ERROR_IO;
-		} else if ( !pclient->disconnected &&
-			(ret == 0 || program_client_input_pending(pclient)) ) {
-			io_loop_run(pclient->ioloop);
-		}
-
-		/* finished */
-		program_client_disconnect(pclient, FALSE);
-	}
-
-	io_loop_destroy(&pclient->ioloop);
-
-	if ( pclient->error != PROGRAM_CLIENT_ERROR_NONE )
-		return -1;
-
-	return pclient->exit_code;
-}
-
diff --git a/src/lib-sieve/util/program-client.h b/src/lib-sieve/util/program-client.h
deleted file mode 100644
index 9f7fdd91e..000000000
--- a/src/lib-sieve/util/program-client.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright (c) 2002-2016 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __PROGRAM_CLIENT_H
-#define __PROGRAM_CLIENT_H
-
-struct program_client;
-
-struct program_client_settings {
-	unsigned int client_connect_timeout_msecs;
-	unsigned int input_idle_timeout_secs;
-
-	uid_t uid;
-	gid_t gid;
-
-	bool debug:1;
-	bool drop_stderr:1;
-};
-
-typedef void program_client_fd_callback_t
-	(void *context, struct istream *input);
-
-struct program_client *program_client_local_create
-	(const char *bin_path, const char *const *args,
-		const struct program_client_settings *set);
-struct program_client *program_client_remote_create
-	(const char *socket_path, const char *const *args,
-		const struct program_client_settings *set, bool noreply);
-
-void program_client_destroy(struct program_client **_pclient);
-
-void program_client_set_input
-	(struct program_client *pclient, struct istream *input);
-void program_client_set_output
-	(struct program_client *pclient, struct ostream *output);
-
-void program_client_set_output_seekable
-	(struct program_client *pclient, const char *temp_prefix);
-struct istream *program_client_get_output_seekable
-	(struct program_client *pclient);
-
-/* Program provides side-channel output through an extra fd */
-void program_client_set_extra_fd
-	(struct program_client *pclient, int fd,
-		program_client_fd_callback_t *callback, void *context);
-#define program_client_set_extra_fd(pclient, fd, callback, context) \
-	program_client_set_extra_fd(pclient, fd + \
-		CALLBACK_TYPECHECK(callback, \
-			void (*)(typeof(context), struct istream *input)), \
-		(program_client_fd_callback_t *)callback, context)
-
-void program_client_set_env
-	(struct program_client *pclient, const char *name, const char *value);
-
-int program_client_run(struct program_client *pclient);
-
-#endif
-
-- 
GitLab