aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--doc/functions.texi100
-rw-r--r--gacopyz/gacopyz.c75
-rw-r--r--gacopyz/gacopyz.h4
-rw-r--r--src/builtin/body.bi6
-rw-r--r--src/builtin/io.bi102
-rw-r--r--src/builtin/system.bi9
-rw-r--r--src/engine.c9
-rw-r--r--src/gram.y3
-rw-r--r--src/mailfromd.h1
-rw-r--r--src/main.c1
11 files changed, 296 insertions, 19 deletions
diff --git a/NEWS b/NEWS
index 6ecc2397..4a9ea0b1 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Mailfromd NEWS -- history of user-visible changes. 2012-11-03
+Mailfromd NEWS -- history of user-visible changes. 2013-07-29
Copyright (C) 2005-2012 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -88,11 +88,14 @@ The % is now used as modulo operator (see below).
message_to_stream
message_from_stream
message_body_to_stream
+ replbody_fd
rewind
shutdown
send_message
+ spawn
syslog
tempfile
+ unlink
vercmp
** message_burst
diff --git a/doc/functions.texi b/doc/functions.texi
index bcb939d3..055d3988 100644
--- a/doc/functions.texi
+++ b/doc/functions.texi
@@ -912,6 +912,45 @@ Example:
No restrictions are imposed on the format of @var{text}.
@end deftypefn
+@deftypefn {Built-in Function} void replbody_fd (number @var{fd})
+Replace the body of the message with the content of the stream
+@var{fd}. Use this function if the body is very big, or if it is
+returned by an external program.
+
+Notice that this function starts reading from the current position in
+@var{fd}. Use @code{rewind} if you wish to read from the beginning of
+the stream.
+
+The example below shows how to preprocess the body of the message
+using external program @file{/usr/bin/mailproc}, which is supposed to
+read the body from its standard input and write the processed text to
+its standard output:
+
+@example
+number fd # @r{Temporary file descriptor}
+
+prog data
+do
+ # Open the temporary file
+ set fd tempfile()
+done
+
+prog body
+do
+ # Write the body to it.
+ write_body(fd, $1, $2)
+done
+
+prog eom
+do
+ # Use the resulting stream as the stdin to the @code{mailproc}
+ # command and read the new body from its standard output.
+ rewind(fd)
+ replbody_fd(spawn("</usr/bin/mailproc", fd))
+done
+@end example
+@end deftypefn
+
@node Message modification queue
@section Message Modification Queue
@cindex message modification queue
@@ -2429,6 +2468,42 @@ reading.
is unable to open the resource or get the required access to it.
@end deftypefn
+@deftypefn {Built-in Function} number spawn (string @var{cmd} [, @
+ number @var{in}, number @var{out}, number @var{err}])
+Runs the supplied command @var{cmd}. The syntax of the @var{cmd} is
+the same as for the @var{name} argument to @code{open} (see above),
+which begins with @samp{|}, excepting that the @samp{|} sign is
+optional. That is:
+
+@example
+spawn("/bin/cat")
+@end example
+
+@noindent
+has exactly the same effect as
+
+@example
+open("|/bin/cat")
+@end example
+
+Optional arguments specify file stream descriptors to be used for the
+program standard input, output and error streams, correspondingly.
+If supplied, these should be the values returned by a previous call to
+@code{open} or @code{tempfile}. The value @samp{-1} means no
+redirection.
+
+The example below starts the @code{awk} program with a simple
+expression as its argument and redirects the content of the
+file @file{/etc/passwd} to its standard input. The returned
+stream descriptor is bound to the command's standard output
+(see the description of @samp{|<} prefix above). The standard
+error is closed:
+
+@example
+number fd spawn("<awk -F: '@{print $1@}'", open("/etc/passwd"))
+@end example
+@end deftypefn
+
@deftypefn {Built-in Function} void close (number @var{rd})
The argument @var{rd} is a resource descriptor returned by a
previous call to @code{open}. The function @code{close} closes the
@@ -2495,6 +2570,26 @@ outside of allowed range of resource descriptors, and @code{e_io}
exception if an @acronym{I/O} error occurs.
@end deftypefn
+@deftypefn {Built-in Function} void write_body (number @var{rd}, pointer @var{bp} @
+ , number @var{size})
+Write the body segment of length @var{size} from pointer @var{bp} to
+the stream @var{rd}. This function can be used only in @code{prog
+body} (@pxref{body handler}). Its second and third arguments
+correspond exactly to the parameters of the @code{body} handler, so
+the following construct writes the message body to the resource
+@code{fd}, which should have been open prior to invoking the
+@code{body} handler:
+
+@example
+@group
+prog body
+do
+ write_body(fd, $1, $2)
+done
+@end group
+@end example
+@end deftypefn
+
@deftypefn {Built-in Function} string read (number @var{rd}, number @var{n})
Read and return @var{n} bytes from the resource descriptor @var{rd}.
@@ -2734,6 +2829,11 @@ Notice the use of single quotes.
@end deftypefn
+@deftypefn {Built-in Function} void unlink (string @var{name})
+Unlinks (deletes) the file @var{name}. On error, throws the
+@code{e_failure} exception.
+@end deftypefn
+
@deftypefn {Built-in Function} number system (string @var{str})
The function @code{system} executes a command specified in @var{str}
by calling @command{/bin/sh -c string}, and returns -1 on error or
diff --git a/gacopyz/gacopyz.c b/gacopyz/gacopyz.c
index 34247643..9931d022 100644
--- a/gacopyz/gacopyz.c
+++ b/gacopyz/gacopyz.c
@@ -2161,34 +2161,83 @@ gacopyz_del_rcpt(SMFICTX *ctx, const char *rcpt)
#define GACOPYZ_CHUNK_SIZE 65535
int
-gacopyz_replace_body(SMFICTX *ctx, const unsigned char *bodyp, size_t bodylen)
+gacopyz_replace_body_fn(SMFICTX *ctx,
+ void *bodyp,
+ ssize_t (*cpf)(char*,void*,size_t))
{
union header *hptr;
char buf[sizeof hptr->buf + GACOPYZ_CHUNK_SIZE];
- if (bodylen < 0 || (!bodyp && bodylen > 0))
+ if (!bodyp || !cpf)
return MI_FAILURE;
if (!ok_to_send(ctx, SMFIF_CHGBODY))
return MI_FAILURE;
hptr = (union header *)buf;
hptr->hdr.cmd = SMFIR_REPLBODY;
- while (bodylen) {
- size_t size = (bodylen >= GACOPYZ_CHUNK_SIZE)
- ? GACOPYZ_CHUNK_SIZE : bodylen;
-
- TRACE(ctx, hptr->hdr.cmd, size, buf);
-
- hptr->hdr.size = htonl(size + 1);
- memcpy(buf + sizeof hptr->buf, bodyp, size);
- if (ctx_write(ctx, buf, sizeof hptr->buf + size))
+ while (1) {
+ ssize_t wrs = cpf(buf + sizeof hptr->buf, bodyp,
+ GACOPYZ_CHUNK_SIZE);
+ if (wrs == 0)
+ break;
+ if (wrs < 0)
+ return MI_FAILURE;
+ hptr->hdr.size = htonl(wrs + 1);
+ TRACE(ctx, hptr->hdr.cmd, wrs, buf);
+ if (ctx_write(ctx, buf, sizeof hptr->buf + wrs))
return MI_FAILURE;
- bodyp += size;
- bodylen -= size;
}
return MI_SUCCESS;
}
+struct bodyptr {
+ const unsigned char *bodyp;
+ size_t bodylen;
+};
+
+static ssize_t
+cpf_mem(char *dst, void *src, size_t size)
+{
+ struct bodyptr *bp = src;
+ if (size > bp->bodylen) {
+ size = bp->bodylen;
+ if (size == 0)
+ return size;
+ }
+ memcpy(dst, bp->bodyp, size);
+ bp->bodyp += size;
+ bp->bodylen -= size;
+ return size;
+}
+
+int
+gacopyz_replace_body(SMFICTX *ctx, const unsigned char *bodyp, size_t bodylen)
+{
+ struct bodyptr bp;
+ bp.bodyp = bodyp;
+ bp.bodylen = bodylen;
+ return gacopyz_replace_body_fn(ctx, &bp, cpf_mem);
+}
+
+struct bodyfd {
+ int fd;
+};
+
+static ssize_t
+cpf_fd(char *dst, void *src, size_t size)
+{
+ struct bodyfd *bp = src;
+ return read(bp->fd, dst, size);
+}
+
+int
+gacopyz_replace_body_fd(SMFICTX *ctx, int fd)
+{
+ struct bodyfd bp;
+ bp.fd = fd;
+ return gacopyz_replace_body_fn(ctx, &bp, cpf_fd);
+}
+
int
gacopyz_progress(SMFICTX *ctx)
{
diff --git a/gacopyz/gacopyz.h b/gacopyz/gacopyz.h
index 459bad9d..d04acdc1 100644
--- a/gacopyz/gacopyz.h
+++ b/gacopyz/gacopyz.h
@@ -376,6 +376,10 @@ int gacopyz_add_rcpt(SMFICTX *ctx, const char *rcpt);
int gacopyz_del_rcpt(SMFICTX *ctx, const char *rcpt);
int gacopyz_replace_body(SMFICTX *ctx, const unsigned char *bodyp,
size_t bodylen);
+int gacopyz_replace_body_fn(SMFICTX *ctx,
+ void *bodyp,
+ ssize_t (*cpf)(char*,void*,size_t));
+int gacopyz_replace_body_fd(SMFICTX *ctx, int fd);
int gacopyz_progress(SMFICTX *ctx);
int gacopyz_quarantine(SMFICTX *ctx, const char *reason);
int gacopyz_add_rcpt_par(SMFICTX *ctx, const char *rcpt, const char *args);
diff --git a/src/builtin/body.bi b/src/builtin/body.bi
index be39da6e..42c8fd62 100644
--- a/src/builtin/body.bi
+++ b/src/builtin/body.bi
@@ -60,4 +60,10 @@ MF_DEFUN(replbody, VOID, STRING text)
}
END
+MF_DEFUN(replbody_fd, VOID, NUMBER fd)
+{
+ env_msgmod(env, body_repl_fd, "BODYFD", NULL, _bi_io_fd(env, fd, 0));
+}
+END
+
MF_INIT
diff --git a/src/builtin/io.bi b/src/builtin/io.bi
index 2ced45f9..af59c420 100644
--- a/src/builtin/io.bi
+++ b/src/builtin/io.bi
@@ -294,9 +294,10 @@ parse_stderr_redirect(const char **pcmd, int *perr, char **parg)
static int
-open_program_stream(eval_environ_t env,
- struct io_stream *str, const char *cmdline,
- int flags)
+open_program_stream_ioe(eval_environ_t env,
+ struct io_stream *str, const char *cmdline,
+ int flags,
+ int ioe[2])
{
int rightp[2], leftp[2];
int rc = 0;
@@ -323,15 +324,22 @@ open_program_stream(eval_environ_t env,
if (REDIRECT_STDOUT_P(flags)) {
if (rightp[1] != 1)
dup2(rightp[1], 1);
+ } else if (ioe && ioe[1] != -1 && ioe[1] != 1) {
+ dup2(ioe[1], 1);
}
/* Left-end */
if (REDIRECT_STDIN_P(flags)) {
if (leftp[0] != 0)
dup2(leftp[0], 0);
+ } else if (ioe && ioe[0] != -1 && ioe[0] != 0) {
+ dup2(ioe[0], 0);
}
-
- stderr_handler(err, arg, cmdline);
+
+ if (ioe && ioe[2] != -1 && ioe[2] != 2)
+ dup2(ioe[2], 2);
+ else
+ stderr_handler(err, arg, cmdline);
/* Close unneded descripitors */
close_fds_above(2);
@@ -381,6 +389,13 @@ open_program_stream(eval_environ_t env,
return rc;
}
+static int
+open_program_stream(eval_environ_t env,
+ struct io_stream *str, const char *cmdline,
+ int flags)
+{
+ return open_program_stream_ioe(env, str, cmdline, flags, NULL);
+}
static int
open_file_stream(eval_environ_t env,
@@ -726,6 +741,65 @@ MF_DEFUN(open, NUMBER, STRING name)
}
END
+MF_DEFUN(spawn, NUMBER, STRING name, OPTIONAL,
+ NUMBER fin, NUMBER fout, NUMBER ferr)
+{
+ int i, rc;
+ struct io_stream *iotab = MF_GET_DATA;
+ int ioe[3];
+ int flags;
+
+ for (i = 0; i < nstreams; i++) {
+ if (iotab[i].fd[0] == -1)
+ break;
+ }
+ MF_ASSERT(i < nstreams,
+ mfe_failure,
+ _("no more files available"));
+
+ MF_DEBUG(10, ("spawning %s", name));
+ iotab[i].name = xstrdup(name);
+ iotab[i].delim = NULL;
+
+ flags = O_WRONLY;
+ if (*name == '|')
+ name++;
+ if (*name == '&') {
+ flags = O_RDWR;
+ name++;
+ } else if (*name == '<') {
+ flags = O_RDONLY;
+ name++;
+ }
+
+ for (;*name && c_isspace(*name); name++)
+ ;
+
+ if (MF_DEFINED(fin))
+ ioe[0] = _bi_io_fd(env, MF_OPTVAL(fin), 0);
+ else
+ ioe[0] = -1;
+ if (MF_DEFINED(fout))
+ ioe[1] = _bi_io_fd(env, MF_OPTVAL(fout), 1);
+ else
+ ioe[1] = -1;
+ if (MF_DEFINED(ferr))
+ ioe[2] = _bi_io_fd(env, MF_OPTVAL(fout), 1);
+ else
+ ioe[2] = -1;
+
+ rc = open_program_stream_ioe(env, &iotab[i], name, flags, ioe);
+
+ MF_ASSERT(rc == 0,
+ mfe_failure,
+ _("cannot open stream %s: %s"), name,
+ mu_strerror(rc));
+ MF_DEBUG(10, ("spawn(%s) = %d", name, i));
+ MF_RETURN(i);
+
+}
+END
+
MF_DSEXP
MF_DEFUN(tempfile, NUMBER, OPTIONAL, STRING tempdir)
{
@@ -844,6 +918,24 @@ MF_DEFUN(write, VOID, NUMBER fd, STRING str, OPTIONAL, NUMBER n)
}
END
+MF_STATE(body)
+MF_DEFUN(write_body, VOID, NUMBER fd, POINTER str, NUMBER n)
+{
+ struct io_stream *iotab = MF_GET_DATA;
+ int rc;
+
+ MF_DEBUG(10, ("writing %s to %lu", str, fd));
+ MF_ASSERT(fd >= 0 && fd < nstreams && OFD(iotab[fd]) != -1,
+ mfe_range,
+ _("invalid file descriptor"));
+ rc = write(OFD(iotab[fd]), str, n);
+ MF_ASSERT(n == rc,
+ mfe_io,
+ _("write error on %s: %s"),
+ iotab[fd].name, mu_strerror(errno));
+}
+END
+
MF_DEFUN(read, STRING, NUMBER fd, NUMBER size)
{
struct io_stream *iotab = MF_GET_DATA;
diff --git a/src/builtin/system.bi b/src/builtin/system.bi
index 387ec03a..e10e35f2 100644
--- a/src/builtin/system.bi
+++ b/src/builtin/system.bi
@@ -149,4 +149,13 @@ MF_DEFUN(getenv, STRING, STRING name)
}
END
+MF_DEFUN(unlink, VOID, STRING name)
+{
+ int rc = unlink(name);
+ MF_ASSERT(rc == 0,
+ mfe_failure,
+ "cannot unlink %s: %s", name, mu_strerror(errno));
+}
+END
+
MF_INIT
diff --git a/src/engine.c b/src/engine.c
index 18b0b6b4..977ee78f 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -729,6 +729,15 @@ run_msgmod(void *item, void *data)
xlate_and_replace_body(ctx, hdr->value, strlen(hdr->value));
break;
+ case body_repl_fd:
+ if (gacopyz_replace_body_fd(ctx, hdr->idx) != MI_SUCCESS) {
+ mu_error(_("%s failed: %s"),
+ "gacopyz_replace_body_fd",
+ mu_strerror(errno));
+ break;
+ }
+ break;
+
case header_replace_nth:
md = priv_get(ctx);
thdr = *hdr;
diff --git a/src/gram.y b/src/gram.y
index 7e40daec..f54b3283 100644
--- a/src/gram.y
+++ b/src/gram.y
@@ -3244,6 +3244,9 @@ msgmod_opcode_str(enum msgmod_opcode opcode)
case body_repl:
return "REPLACE BODY";
+ case body_repl_fd:
+ return "REPLACE BODY FROM FILE";
+
case header_replace_nth:
return "REPLACE NTH HEADER";
diff --git a/src/mailfromd.h b/src/mailfromd.h
index 02307056..46fb52c2 100644
--- a/src/mailfromd.h
+++ b/src/mailfromd.h
@@ -246,6 +246,7 @@ enum msgmod_opcode { /* Message modification operation */
rcpt_delete, /* Delete a recipient */
quarantine, /* Quarantine a message */
body_repl, /* Replace message body */
+ body_repl_fd, /* Replace message body from file */
header_delete_nth, /* Delete Nth header */
header_replace_nth /* Replace Nth header */
};
diff --git a/src/main.c b/src/main.c
index 4b92598c..b432627a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1249,6 +1249,7 @@ main(int argc, char **argv)
argpflag(argc, argv), &index, &args);
if (rc)
exit(EX_CONFIG);
+
flush_arguments(&args);
mf_srvcfg_flush();

Return to:

Send suggestions and report system problems to the System administrator.