diff options
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | doc/functions.texi | 100 | ||||
-rw-r--r-- | gacopyz/gacopyz.c | 75 | ||||
-rw-r--r-- | gacopyz/gacopyz.h | 4 | ||||
-rw-r--r-- | src/builtin/body.bi | 6 | ||||
-rw-r--r-- | src/builtin/io.bi | 102 | ||||
-rw-r--r-- | src/builtin/system.bi | 9 | ||||
-rw-r--r-- | src/engine.c | 9 | ||||
-rw-r--r-- | src/gram.y | 3 | ||||
-rw-r--r-- | src/mailfromd.h | 1 | ||||
-rw-r--r-- | src/main.c | 1 |
11 files changed, 296 insertions, 19 deletions
@@ -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; @@ -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 */ }; @@ -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(); |