diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-08-24 16:56:30 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-08-24 17:05:53 +0300 |
commit | 19f41d6c5bcebc79cfa80bf39e242e4b5e8e7a6c (patch) | |
tree | adf4dfd289968afaf8c2e43dd38d7a5632b56d9a | |
parent | 1b07ddac6d91db6b051b7272262069850b4cf136 (diff) | |
download | mailfromd-19f41d6c5bcebc79cfa80bf39e242e4b5e8e7a6c.tar.gz mailfromd-19f41d6c5bcebc79cfa80bf39e242e4b5e8e7a6c.tar.bz2 |
Improve third-party interfaces to process arbitrary messages (not only current one).
* NEWS: Updated.
* doc/functions.texi: Reflect changes in sieve, clamav
and sa (spamc) functions.
* mflib/Makefile.am: Build sa.h
* mflib/sa.mf (SA_SYMBOLS, SA_REPORT)
(SA_LEARN_SPAM,SA_LEARN_HAM,SA_FORGET): New constants.
(sa): Wrapper over spamc for backward compatibility.
* src/builtin/Makefile.am (builtin.h): Depends on builtin.def
* src/builtin/body.bi (current_message): Use MF_STREAM_TO_MESSAGE.
* src/builtin/builtin.c: Add more includes.
(_builtin_stream_cleanup): New function.
(_builtin_mu_stream_to_message): New function.
* src/builtin/builtin.def (_builtin_stream_cleanup)
(_builtin_mu_stream_to_message): New protos.
* src/builtin/dspam.bi: Use _builtin_stream_cleanup for
stream cleanups.
* src/builtin/msg.bi (message_from_stream): Use MF_STREAM_TO_MESSAGE.
* src/builtin/sa.bi (open_connection): Register returned stream
for cleanup.
(sa): Rewrite as spamc function.
(clamav): Take message descriptor as the first argument.
* src/builtin/sieve.bi (sieve): Take message descriptor as the first argument.
Use cleanups.
* src/builtin/snarf.m4 (MF_STREAM_TO_MESSAGE): New macro.
* src/main.c (options): Remove "debug" option, handled by srvcfg.c
* tests/bctx00.at: Use current_message function.
* tests/bctx01.at: Likewise.
* tests/fctx00.at: Likewise.
* tests/fctx01.at: Likewise.
-rw-r--r-- | NEWS | 50 | ||||
-rw-r--r-- | doc/functions.texi | 106 | ||||
-rw-r--r-- | mflib/.gitignore | 1 | ||||
-rw-r--r-- | mflib/Makefile.am | 4 | ||||
-rw-r--r-- | mflib/sa.mf | 18 | ||||
-rw-r--r-- | src/builtin/Makefile.am | 2 | ||||
-rw-r--r-- | src/builtin/body.bi | 7 | ||||
-rw-r--r-- | src/builtin/builtin.c | 33 | ||||
-rw-r--r-- | src/builtin/builtin.def | 20 | ||||
-rw-r--r-- | src/builtin/dspam.bi | 11 | ||||
-rw-r--r-- | src/builtin/msg.bi | 7 | ||||
-rw-r--r-- | src/builtin/sa.bi | 244 | ||||
-rw-r--r-- | src/builtin/sieve.bi | 70 | ||||
-rw-r--r-- | src/builtin/snarf.m4 | 4 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | tests/bctx00.at | 6 | ||||
-rw-r--r-- | tests/bctx01.at | 4 | ||||
-rw-r--r-- | tests/fctx00.at | 11 | ||||
-rw-r--r-- | tests/fctx01.at | 9 |
19 files changed, 396 insertions, 213 deletions
@@ -1,4 +1,4 @@ -Mailfromd NEWS -- history of user-visible changes. 2011-08-18 +Mailfromd NEWS -- history of user-visible changes. 2011-08-24 Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Sergey Poznyakoff See the end of file for copying conditions. @@ -94,6 +94,54 @@ The % is now used as modulo operator (see below). tempfile vercmp +** clamav and sieve + +These two functions take a descriptor of the message as their first +argument. The new prototypes are: + + number sieve(number msg, string script ; + number flags, string file, number line) + number clamav(number msg, string url) + +This change is incompatible with previous versions. + +To use these functions in the eom handler, pass current_message +as their first argument, e.g.: + +prog eom +do + if clamav(current_message(), "tcp://127.0.0.1:3344") + ... + +** sa and spamc + +New function `spamc' is an improved version of the old `sa' function: + + number spamc(number nmsg, string url, number prec, number command) + +Arguments are: + + nmsg - descriptor of the message to be processed, + url - URL of the spamd server, + prec - precision, + command - command to send to spamd. + +Allowed values for command argument are: + + SA_SYMBOLS Process the message and return 1 or 0 depending on + whether it is diagnosed as spam or not. Store + SpamAssassin keywords in the global variable + sa_keywords. + SA_REPORT Process the message and return 1 or 0 depending on + whether it is diagnosed as spam or not. Store entire + SpamAssassin report in the global variable + sa_keywords. + SA_LEARN_SPAM Learn the supplied message as spam. + SA_LEARN_HAM Learn the supplied message as ham. + SA_FORGET Forget any prior classification of the message. + + The function `sa' is rewritten as a wrapper over `spamc' + ** New M4 macros New macro `string_list_iterate' is provided to compensate for the lack diff --git a/doc/functions.texi b/doc/functions.texi index cfc2a481..19e274aa 100644 --- a/doc/functions.texi +++ b/doc/functions.texi @@ -2659,13 +2659,13 @@ of this language. For a description of the language and available extensions, see @ref{Sieve Language, Sieve Language, Sieve Language, mailutils, GNU Mailutils Manual}. -@deftypefn {Built-in Function} boolean sieve (string @var{script} @ +@deftypefn {Built-in Function} boolean sieve (number @var{msg}, @ + string @var{script} @ [, number @var{flags}, string @var{file}, number @var{line}]) Compile the Sieve program @var{script} and execute it over the -collected message. This function can be used only in @code{eom} -handler. +message identified by the descriptor @var{nmsg}. -@flindex sieve.mfh +@flindex sieve.mf Optional @var{flags} modify the behavior of the function. It is a bit-mask field, consisting of a bitwise @code{or} of one or more of the following flags, defined in @file{sieve.mf}: @@ -2716,7 +2716,7 @@ require 'sieve' group eom do - if not sieve("/etc/mail/filter.siv", MF_SIEVE_LOG) + if not sieve(current_message(), "/etc/mail/filter.siv", MF_SIEVE_LOG) discard fi done @@ -2730,7 +2730,8 @@ require 'sieve' prog eom do - if not sieve("require \"fileinto\";\n" + if not sieve(current_message(), + "require \"fileinto\";\n" "fileinto \"/tmp/sieved.mbox\";", MF_SIEVE_TEXT | MF_SIEVE_LOG) discard @@ -2744,7 +2745,7 @@ to @code{sieve} appears. For example, the above program produces the following in the log: @smallexample -prog.mf:6: FILEINTO; delivering into /tmp/sieved.mbox +prog.mf:7: FILEINTO; delivering into /tmp/sieved.mbox @end smallexample Notice, that the line number correctly refers to the line where the @@ -2768,7 +2769,8 @@ EOT prog eom do - if not sieve(sieve_prog, MF_SIEVE_TEXT | MF_SIEVE_LOG, + if not sieve(current_message(), + sieve_prog, MF_SIEVE_TEXT | MF_SIEVE_LOG, __file__, sieve_prog_line) discard fi @@ -2787,15 +2789,13 @@ initialization. @command{SpamAssassin} @command{spamd} daemon and with @command{ClamAV} anti-virus. - These functions can be used only in @code{eom} handler. - Both interfaces work much the same way: the remote filter is connected and the message is passed to it. If the remote filter confirms that the message matches its requirements, the function returns @code{true}. Notice that in practice that means that such a message @emph{should be rejected or deferred}. - The address of the remote filter is supplied as the first argument + The address of the remote filter is supplied as the second argument in the form of a standard @acronym{URL}: @smallexample @@ -2830,14 +2830,43 @@ socket:///var/run/filter.sock @anchor{sa} @cindex SpamAssassin @cindex spamd -@deftypefn {Built-in Function} boolean sa (string @var{url}, number @var{prec}) -@deftypefnx {Built-in Function} boolean sa (string @var{url}, @ - number @var{prec}, number @var{report}) - Pass the message to the SpamAssassin daemon (@code{spamd}) at -@var{url}. Return @code{true} if SpamAssassin considers it a spam, -@code{false} otherwise. The second arguments, @var{prec}, gives the -precision, in decimal digits, to be used when converting SpamAssassin -diagnostic data and storing them into @command{mailfromd} variables. +@deftypefn {Built-in Function} boolean spamc (number @var{msg}, @ + string @var{url}, number @var{prec}, number @var{command}) + Send the message @var{msg}t to the SpamAssassin daemon (@code{spamd}) +listening on the given @var{url}. The @var{command} argument +identifies what kind of processing is needed for the message. Allowed +values are: + +@table @asis +@kwindex SA_SYMBOLS +@item SA_SYMBOLS +Process the message and return 1 or 0 depending on whether it is +diagnosed as spam or not. Store SpamAssassin keywords in the global +variable @code{sa_keywords} (see below). + +@kwindex SA_REPORT +@item SA_REPORT +Process the message and return 1 or 0 depending on whether it is +diagnosed as spam or not. Store entire SpamAssassin report in the +global variable @code{sa_keywords}. + +@kwindex SA_LEARN_SPAM +@item SA_LEARN_SPAM +Learn the supplied message as spam. + +@kwindex SA_LEARN_HAM +@item SA_LEARN_HAM +Learn the supplied message as ham. + +@kwindex SA_FORGET +@item SA_FORGET +Forget any prior classification of the message. +@end table + +The second argument, @var{prec}, gives the precision, in decimal +digits, to be used when converting SpamAssassin diagnostic data and +storing them into @command{mailfromd} variables. + The floating point SpamAssassin data are converted to the integer @command{mailfromd} variables using the following relation: @@ -2850,10 +2879,6 @@ where @var{sa-var} stands for the SpamAssassin value and @var{var} stands for the corresponding @command{mailfromd} one. @code{int()} means taking the integer part. -Optional third argument, @var{report}, controls what kind of -data is returned in the @code{sa_keywords} variable. See below for -its description. - The function returns additional information via the following variables: @@ -2871,7 +2896,7 @@ The threshold, converted to integer form. @cindex sa_keywords, global variable @item sa_keywords -If @var{report} is not supplied or is null, this variable contains a +If @var{command} is @samp{SA_SYMBOLS}, this variable contains a string of comma-separated SpamAssassin keywords identifying this message, e.g.: @@ -2879,7 +2904,7 @@ message, e.g.: ADVANCE_FEE_1,AWL,BAYES_99 @end smallexample -Otherwise, if @var{report} is not null, the value of this variable is +If @var{command} is @samp{SA_REPORT}, the value of this variable is a @dfn{spam report} message. It is a multi-line textual message, containing detailed description of spam scores in a tabular form. It consists of the following parts: @@ -2922,20 +2947,24 @@ The score table can be extracted from @code{sa_keywords} using @code{sa_format_report_header} function (@pxref{String manipulation, sa_format_report_header}), as illustrated in the example below. @end enumerate + +The value of this variable is undefined if @var{command} is +@samp{SA_LEARN_SPAM}, @samp{SA_LEARN_HAM} or @samp{SA_FORGET}. @end table -The @code{sa} function can signal the following exceptions: +The @code{spamc} function can signal the following exceptions: @code{e_failure} if the connection fails, @code{e_url} if the supplied @acronym{URL} is invalid and @code{e_range} if the supplied port number is out of the range 1--65535. -The simplest way to use the function is: +An example of using this function: @smallexample @group prog eom do - if sa("tcp://192.168.10.1:3333", 3) + if spamc(current_message(), "tcp://192.168.10.1:3333", 3, + SA_SYMBOLS) reject 550 5.7.0 "Spam detected, score %sa_score with threshold %sa_threshold" fi @@ -2949,7 +2978,8 @@ done prog eom do set prec 3 - if sa("tcp://192.168.10.1:3333", prec, 1) + if spamc(current_message(), + "tcp://192.168.10.1:3333", prec, SA_REPORT) add "X-Spamd-Status" "SPAM" else add "X-Spamd-Status" "OK" @@ -2959,14 +2989,26 @@ do add "X-Spamd-Keywords" sa_format_report_header(sa_keywords) done @end smallexample +@end deftypefn + +@deftypefn {Built-in Function} boolean sa (string @var{url}, @ + number @var{prec}; number @var{command}) +Additional interface to the @code{spamc} function, provided for +backward compatibility. It is equivalent to + +@smallexample +spamc(current_message(), @var{url}, @var{prev}, @var{command}) +@end smallexample +If @var{command} is not supplied, @samp{SA_SYMBOLS} is used. @end deftypefn @anchor{ClamAV} @cindex ClamAV -@deftypefn {Built-in Function} boolean clamav (string @var{url}) +@deftypefn {Built-in Function} boolean clamav (number @var{msg}, @ + string @var{url}) @cindex clamav_virus_name, global variable - Pass the message to the ClamAV daemon at @var{url}. Return + Pass the message @var{msg} to the ClamAV daemon at @var{url}. Return @code{true} if it detects a virus in it. Return virus name in @code{clamav_virus_name} global variable. @@ -2981,7 +3023,7 @@ is out of the range 1--65535. @group prog eom do - if clamav("tcp://192.168.10.1:6300") + if clamav(current_message(), "tcp://192.168.10.1:6300") reject 550 5.7.0 "Infected with %clamav_virus_name" fi done diff --git a/mflib/.gitignore b/mflib/.gitignore index 9088ef1e..fe5da99f 100644 --- a/mflib/.gitignore +++ b/mflib/.gitignore @@ -7,6 +7,7 @@ email.h portprobe.mf rateok.mf safedb.mf +sa.h sieve.h status.ex syslog.h diff --git a/mflib/Makefile.am b/mflib/Makefile.am index de47ac13..822041d8 100644 --- a/mflib/Makefile.am +++ b/mflib/Makefile.am @@ -55,8 +55,8 @@ MF_FILES =\ $(DSPAM_MODULE)\ $(MF4_FILES:.mf4=.mf) -noinst_HEADERS = dspam.h email.h sieve.h syslog.h _register.h status.ex -BUILT_SOURCES = dspam.h email.h sieve.h syslog.h _register.h status.ex +noinst_HEADERS = dspam.h email.h sa.h sieve.h syslog.h _register.h status.ex +BUILT_SOURCES = dspam.h email.h sa.h sieve.h syslog.h _register.h status.ex EXTRA_DIST=$(inc_DATA) pp-setup $(MF4_FILES) mfex.awk mfh.awk diff --git a/mflib/sa.mf b/mflib/sa.mf index c2b12009..d3af4768 100644 --- a/mflib/sa.mf +++ b/mflib/sa.mf @@ -16,6 +16,12 @@ module 'sa'. +const SA_SYMBOLS 0 +const SA_REPORT 1 +const SA_LEARN_SPAM 2 +const SA_LEARN_HAM 3 +const SA_FORGET 4 + #pragma regex push +extended static func __sa_format_score0(string code, number prec) @@ -90,4 +96,16 @@ do return ret done +func sa(string urlstr, number prec; number flag) + returns number +do + number command + if defined(flag) + set command flag + else + set command SA_SYMBOLS + fi + return spamc(current_message(), urlstr, prec, command) +done + #pragma regex pop diff --git a/src/builtin/Makefile.am b/src/builtin/Makefile.am index 904cd02b..8a93e340 100644 --- a/src/builtin/Makefile.am +++ b/src/builtin/Makefile.am @@ -80,7 +80,7 @@ INCLUDES = \ -I$(top_srcdir)\ $(DSPAM_CFLAGS) -builtin.h: Makefile.am +builtin.h: Makefile.am builtin.def $(BI_FILES:.bi=.c): snarf.m4 init.m4 diff --git a/src/builtin/body.bi b/src/builtin/body.bi index 26695cb5..f684801b 100644 --- a/src/builtin/body.bi +++ b/src/builtin/body.bi @@ -44,12 +44,7 @@ MF_DEFUN(current_message, NUMBER) rc = bi_get_current_message(env); if (rc < 0) { - rc = mu_stream_to_message(mstr, &msg); - MF_ASSERT(rc == 0, - mfe_failure, - "mu_stream_to_message: %s", - mu_strerror(rc)); - mu_stream_unref(mstr); + msg = MF_STREAM_TO_MESSAGE(mstr); rc = bi_message_register(env, NULL, msg, 1); MF_ASSERT(rc >= 0, mfe_failure, diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index cf5f9989..6b858ca4 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -20,9 +20,12 @@ #include <stdlib.h> #include <string.h> #include <mailutils/error.h> +#include <mailutils/stream.h> #include <gettext.h> #define DEFINE_BUILTIN_MODULE +#include "mailfromd.h" +#include "prog.h" #include "builtin.h" void @@ -63,3 +66,33 @@ builtin_module_trace(unsigned idx) { return idx < BUILTIN_IDX_MAX && builtin_module[idx].trace; } + +void +_builtin_stream_cleanup(void *ptr) +{ + mu_stream_t str = ptr; + mu_stream_unref(str); +} + + +mu_message_t +_builtin_mu_stream_to_message(mu_stream_t mstr, eval_environ_t env, + const char *func_name) +{ + int rc; + mu_header_t hdr; + mu_message_t msg; + + rc = mu_stream_to_message(mstr, &msg); + if (rc) + env_throw_bi(env, mfe_failure, + func_name, + "cannot obtain stream reference: %s", + mu_strerror(rc)); + mu_stream_unref(mstr); + /* FIXME: This works over a bug in mailutils 2.99.92 + <= release-2.2-378-g6060ab1 */ + mu_message_get_header(msg, &hdr); + return msg; +} + diff --git a/src/builtin/builtin.def b/src/builtin/builtin.def index 366d2648..9ad3a53c 100644 --- a/src/builtin/builtin.def +++ b/src/builtin/builtin.def @@ -1,3 +1,19 @@ +/* This file is part of Mailfromd. + Copyright (C) 2010, 2011 Sergey Poznyakoff + + This program 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. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + MF_BEGIN_OUTPUT struct builtin_module { @@ -19,3 +35,7 @@ void builtin_setup(void); void builtin_set_module_trace(const char *name, size_t len, int val); void builtin_set_all_module_trace(int val); int builtin_module_trace(unsigned idx); +void _builtin_stream_cleanup(void *); +mu_message_t _builtin_mu_stream_to_message(mu_stream_t str, + eval_environ_t env, + const char *func_name); diff --git a/src/builtin/dspam.bi b/src/builtin/dspam.bi index 8afea8e2..72c41b79 100644 --- a/src/builtin/dspam.bi +++ b/src/builtin/dspam.bi @@ -120,13 +120,6 @@ translate_flags(struct transtab *tab, size_t count, int num) } static void -stream_cleanup(void *ptr) -{ - mu_stream_t str = ptr; - mu_stream_unref(str); -} - -static void ctx_cleanup(void *ptr) { DSPAM_CTX *ctx = ptr; @@ -457,14 +450,14 @@ MF_DEFUN(dspam, NUMBER, NUMBER nmsg, NUMBER mode_flags, OPTIONAL, NUMBER class_s mfe_failure, "mu_static_memory_stream_create: %s", mu_strerror(rc)); - MF_DCL_CLEANUP(msgstr, stream_cleanup); + MF_DCL_CLEANUP(msgstr, _builtin_stream_cleanup); rc = mu_message_get_streamref(msg, &instr); MF_ASSERT(rc == 0, mfe_failure, "mu_message_get_streamref: %s", mu_strerror(rc)); - MF_DCL_CLEANUP(instr, stream_cleanup); + MF_DCL_CLEANUP(instr, _builtin_stream_cleanup); rc = mu_stream_copy(msgstr, instr, msgsize, NULL); MF_ASSERT(rc == 0, diff --git a/src/builtin/msg.bi b/src/builtin/msg.bi index bd633e9e..aa25b1f4 100644 --- a/src/builtin/msg.bi +++ b/src/builtin/msg.bi @@ -591,12 +591,7 @@ MF_DEFUN(message_from_stream, NUMBER, NUMBER fd, OPTIONAL, src = flt; } - rc = mu_stream_to_message(src, &msg); - mu_stream_unref(src); - MF_ASSERT(rc == 0, - mfe_failure, - "mu_stream_to_message: %s", - mu_strerror(rc)); + msg = MF_STREAM_TO_MESSAGE(src); rc = bi_message_register(env, NULL, msg, 0); if (rc < 0) { diff --git a/src/builtin/sa.bi b/src/builtin/sa.bi index 231ad00e..d4b823e8 100644 --- a/src/builtin/sa.bi +++ b/src/builtin/sa.bi @@ -22,6 +22,11 @@ #include <string.h> #include <signal.h> +#include <mailutils/stream.h> + +#include "msg.h" +#include "mflib/sa.h" + MF_VAR(sa_score, NUMBER); MF_VAR(sa_threshold, NUMBER); MF_VAR(sa_keywords, STRING); @@ -300,7 +305,7 @@ open_connection(eval_environ_t env, char *urlstr, char **phost) } rc = spamd_connect(env, &str, path, port); - + MF_DCL_CLEANUP(str, _builtin_stream_cleanup); if (rc == 0 && phost) { if (port) *phost = path; @@ -311,11 +316,12 @@ open_connection(eval_environ_t env, char *urlstr, char **phost) } MF_DSEXP -MF_STATE(eom) -MF_CAPTURE(mstr) -MF_DEFUN(sa, NUMBER, STRING urlstr, NUMBER prec, OPTIONAL, NUMBER report) +MF_DEFUN(spamc, NUMBER, NUMBER nmsg, STRING urlstr, NUMBER prec, NUMBER command) { + mu_message_t msg; + mu_stream_t mstr; mu_off_t msize; + size_t lines; mu_stream_t ostr; signal_handler_fn handler; char *buffer = NULL; @@ -326,9 +332,17 @@ MF_DEFUN(sa, NUMBER, STRING urlstr, NUMBER prec, OPTIONAL, NUMBER report) long version; int result; long score, threshold; - int report_mode = MF_OPTVAL(report); + char *cmdstr; int rc; + msg = bi_message_from_descr(env, nmsg); + rc = mu_message_get_streamref(msg, &mstr); + MF_ASSERT(rc == 0, + mfe_failure, + "mu_stream_get_streamref: %s", + mu_strerror (rc)); + MF_DCL_CLEANUP(mstr, _builtin_stream_cleanup); + rc = mu_stream_size(mstr, &msize); MF_ASSERT(rc == 0, mfe_failure, @@ -336,94 +350,136 @@ MF_DEFUN(sa, NUMBER, STRING urlstr, NUMBER prec, OPTIONAL, NUMBER report) mu_strerror (rc)); ostr = open_connection(env, urlstr, NULL); - - msize += env_get_line_count(env); - mu_stream_printf(ostr, - report_mode ? "REPORT SPAMC/1.2\n" - : "SYMBOLS SPAMC/1.2\n"); + + /* And that, finally, gets the number of lines */ + rc = mu_message_lines(msg, &lines); + MF_ASSERT(rc == 0, + mfe_failure, + "mu_message_lines: %s", + mu_strerror (rc)); + msize += lines; + + switch (command) { + case SA_REPORT: + cmdstr = "REPORT"; + break; + case SA_SYMBOLS: + cmdstr = "SYMBOLS"; + break; + case SA_LEARN_SPAM: + case SA_LEARN_HAM: + case SA_FORGET: + cmdstr = "TELL"; + break; + default: + MF_THROW(mfe_failure, + "unknown flag: %ld", command); + } + mu_stream_printf(ostr, "%s SPAMC/1.2\n", cmdstr); + + switch (command) { + case SA_LEARN_SPAM: + mu_stream_printf(ostr, + "Message-class: spam\n" + "Set: local\n"); + break; + case SA_LEARN_HAM: + mu_stream_printf(ostr, + "Message-class: ham\n" + "Set: local\n"); + break; + case SA_FORGET: + mu_stream_printf(ostr, + "Remove: local\n"); + } + mu_stream_printf(ostr, "Content-length: %lu\n", (unsigned long) msize); /*FIXME: spamd_send_command(ostr, "User: %s", ??) */ got_sigpipe = 0; handler = set_signal_handler(SIGPIPE, sigpipe_handler); mu_stream_write(ostr, "\n", 1, NULL); - if (rc = spamd_send_stream(ostr, mstr)) { - mu_stream_destroy(&ostr); - MF_THROW(mfe_failure, - _("send stream failed: %s"), - mu_strerror (rc)); - } + rc = spamd_send_stream(ostr, mstr); + MF_ASSERT(rc == 0, + mfe_failure, + _("send stream failed: %s"), + mu_strerror(rc)); + mu_stream_shutdown(ostr, MU_STREAM_WRITE); set_signal_handler(SIGPIPE, handler); - + + MF_DCL_CLEANUP(buffer); spamd_get_line(ostr, &buffer, &bufsize); - if (got_sigpipe) { - mu_stream_destroy(&ostr); - free(buffer); + if (got_sigpipe) MF_THROW(mfe_failure, _("remote side has closed connection")); - } - if (sscanf(buffer, "SPAMD/%18s %d %*s", version_str, &result) != 2) { - mu_stream_destroy(&ostr); - free(buffer); - MF_THROW(mfe_failure, - _("spamd responded with bad string '%s'"), - buffer); - } + MF_ASSERT(sscanf(buffer, "SPAMD/%18s %d %*s", version_str, + &result) == 2, + mfe_failure, + _("spamd responded with bad string '%s'"), + buffer); decode_float(&version, version_str, 1); - if (version < 10) { - mu_stream_destroy(&ostr); - free(buffer); - MF_THROW(mfe_failure, - _("unsupported SPAMD version: %s"), - version_str); - } + MF_ASSERT(version >= 10, + mfe_failure, + _("unsupported SPAMD version: %s"), + version_str); - if (result) { - mu_stream_destroy(&ostr); - free(buffer); - MF_THROW(mfe_failure, "%s", buffer); - } + MF_ASSERT(result == 0, mfe_failure, "%s", buffer); spamd_get_line(ostr, &buffer, &bufsize); - if (sscanf (buffer, "Spam: %5s ; %20s / %20s", - spam_str, score_str, threshold_str) != 3) { - mu_stream_destroy(&ostr); - free(buffer); - MF_THROW(mfe_failure, - _("spamd responded with bad Spam header '%s'"), - buffer); - } - result = decode_boolean(spam_str); - decode_float(&score, score_str, prec); - decode_float(&threshold, threshold_str, prec); - - MF_VAR_REF(sa_score, score); - MF_VAR_REF(sa_threshold, threshold); + switch (command) { + case SA_REPORT: + case SA_SYMBOLS: + MF_ASSERT(sscanf(buffer, "Spam: %5s ; %20s / %20s", + spam_str, score_str, threshold_str) == 3, + mfe_failure, + _("spamd responded with bad Spam header '%s'"), + buffer); + + result = decode_boolean(spam_str); + decode_float(&score, score_str, prec); + decode_float(&threshold, threshold_str, prec); + + MF_VAR_REF(sa_score, score); + MF_VAR_REF(sa_threshold, threshold); - /* Skip newline */ - spamd_get_line(ostr, &buffer, &bufsize); - if (report_mode) { + /* Skip newline */ + spamd_get_line(ostr, &buffer, &bufsize); + break; + + case SA_LEARN_SPAM: + case SA_LEARN_HAM: + result = !!strcmp(buffer, "DidSet: local"); + break; + + case SA_FORGET: + result = !!strcmp(buffer, "DidRemove: local"); + break; + } + + switch (command) { + case SA_REPORT: MF_OBSTACK_BEGIN(); while (mu_stream_getline(ostr, &buffer, &bufsize, &n) == 0 && n > 0) MF_OBSTACK_GROW(buffer, n); MF_OBSTACK_1GROW(0); MF_VAR_REF(sa_keywords, MF_OBSTACK_FINISH); - } else { + break; + + case SA_SYMBOLS: /* Read symbol list */ spamd_get_line(ostr, &buffer, &bufsize); - MF_VAR_SET_STRING(sa_keywords, buffer); - - while (mu_stream_getline(ostr, &buffer, &bufsize, &n) == 0 - && n > 0) - /* Drain input */; } - mu_stream_destroy(&ostr); - free(buffer); + + /* Just in case */ + while (mu_stream_getline(ostr, &buffer, &bufsize, &n) == 0 + && n > 0) + /* Drain input */; + MF_RETURN(result); } END @@ -446,10 +502,10 @@ clamav_open_data_stream(mu_stream_t *retstr, const char *host, unsigned port) } MF_DSEXP -MF_STATE(eom) -MF_CAPTURE(mstr) -MF_DEFUN(clamav, NUMBER, STRING urlstr) +MF_DEFUN(clamav, NUMBER, NUMBER nmsg, STRING urlstr) { + mu_message_t msg; + mu_stream_t mstr; mu_stream_t cstr, dstr; char *buffer = NULL; size_t bufsize = 0; @@ -459,44 +515,45 @@ MF_DEFUN(clamav, NUMBER, STRING urlstr) signal_handler_fn handler; char *p; - cstr = open_connection(env, urlstr, &host); + msg = bi_message_from_descr(env, nmsg); + rc = mu_message_get_streamref(msg, &mstr); + MF_ASSERT(rc == 0, + mfe_failure, + "mu_stream_get_streamref: %s", + mu_strerror (rc)); + MF_DCL_CLEANUP(mstr, _builtin_stream_cleanup); + MF_DCL_CLEANUP(buffer); + cstr = open_connection(env, urlstr, &host); + mu_stream_printf(cstr, "STREAM\n"); spamd_get_line(cstr, &buffer, &bufsize); - if (sscanf(buffer, "PORT %hu", &port) != 1) { - mu_stream_destroy(&cstr); - free(buffer); - MF_THROW(mfe_failure, - _("bad response from clamav: expected `PORT' but found `%s'"), - buffer); - } + MF_ASSERT(sscanf(buffer, "PORT %hu", &port) == 1, + mfe_failure, + _("bad response from clamav: expected `PORT' but found `%s'"), + buffer); if (!host) host = strdup("127.0.0.1"); /* FIXME */ rc = clamav_open_data_stream(&dstr, host, port); free(host); - if (rc) { - mu_stream_destroy(&cstr); - MF_THROW(mfe_failure, - "mu_tcp_stream_create: %s", - mu_strerror(rc)); - } + MF_ASSERT(rc == 0, + mfe_failure, + "mu_tcp_stream_create: %s", + mu_strerror(rc)); handler = set_signal_handler(SIGPIPE, sigpipe_handler); rc = spamd_send_stream(dstr, mstr); mu_stream_shutdown(dstr, MU_STREAM_WRITE); mu_stream_destroy(&dstr); set_signal_handler(SIGPIPE, handler); - if (rc) { - mu_stream_destroy(&cstr); - free(buffer); - MF_THROW(mfe_failure, - _("sending to stream failed: %s"), - mu_strerror (rc)); - } + MF_ASSERT(rc == 0, + mfe_failure, + _("sending to stream failed: %s"), + mu_strerror(rc)); rc = spamd_get_line(cstr, &buffer, &bufsize); - mu_stream_destroy(&cstr); + MF_CLEANUP(cstr); MF_ASSERT(rc == 0, mfe_failure, _("error reading clamav response: %s"), mu_strerror(rc)); @@ -523,17 +580,12 @@ MF_DEFUN(clamav, NUMBER, STRING urlstr) rc = 1; } else if (strncmp(p, "ERROR", 5) == 0) { /* FIXME: mf code */ - free(buffer); - MF_THROW(mfe_failure, - _("clamav error: %s"), - buffer); + MF_THROW(mfe_failure, _("clamav error: %s"), buffer); } else { - free(buffer); MF_THROW(mfe_failure, _("unknown clamav response: %s"), buffer); } - free(buffer); MF_RETURN(rc); } END diff --git a/src/builtin/sieve.bi b/src/builtin/sieve.bi index eff0693b..3b4f5879 100644 --- a/src/builtin/sieve.bi +++ b/src/builtin/sieve.bi @@ -16,6 +16,7 @@ #include <mailutils/mailutils.h> #include <mflib/sieve.h> +#include "msg.h" static void _sieve_text_action_log(void *env_ptr, @@ -58,9 +59,14 @@ _sieve_file_action_log(void *env_ptr, _("sieve called from here")); } -MF_STATE(eom) -MF_CAPTURE(mstr) -MF_DEFUN(sieve, NUMBER, STRING script, OPTIONAL, NUMBER flags, +static void +mach_cleanup(void *ptr) +{ + mu_sieve_machine_t mach = ptr; + mu_sieve_machine_destroy(&mach); +} + +MF_DEFUN(sieve, NUMBER, NUMBER nmsg, STRING script, OPTIONAL, NUMBER flags, STRING file, NUMBER line) { mu_sieve_machine_t mach; @@ -68,12 +74,15 @@ MF_DEFUN(sieve, NUMBER, STRING script, OPTIONAL, NUMBER flags, int rc; int retval = 0; int f = MF_OPTVAL(flags); + mu_attribute_t attr; + mu_message_t msg; rc = mu_sieve_machine_init(&mach); MF_ASSERT |