aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS50
-rw-r--r--doc/functions.texi106
-rw-r--r--mflib/.gitignore1
-rw-r--r--mflib/Makefile.am4
-rw-r--r--mflib/sa.mf18
-rw-r--r--src/builtin/Makefile.am2
-rw-r--r--src/builtin/body.bi7
-rw-r--r--src/builtin/builtin.c33
-rw-r--r--src/builtin/builtin.def20
-rw-r--r--src/builtin/dspam.bi11
-rw-r--r--src/builtin/msg.bi7
-rw-r--r--src/builtin/sa.bi244
-rw-r--r--src/builtin/sieve.bi70
-rw-r--r--src/builtin/snarf.m44
-rw-r--r--src/main.c2
-rw-r--r--tests/bctx00.at6
-rw-r--r--tests/bctx01.at4
-rw-r--r--tests/fctx00.at11
-rw-r--r--tests/fctx01.at9
19 files changed, 396 insertions, 213 deletions
diff --git a/NEWS b/NEWS
index ed3f81c8..37183ff4 100644
--- a/NEWS
+++ b/NEWS
@@ -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(rc == 0, mfe_failure,
_("failed to initialize sieve machine: %s"),
mu_strerror(rc));
-
+ MF_DCL_CLEANUP(mach, mach_cleanup);
+
if (f & MF_SIEVE_DEBUG_TRACE)
sieve_debug_flags |= MU_SIEVE_DEBUG_TRACE;
if (f & MF_SIEVE_DEBUG_INSTR)
@@ -83,53 +92,34 @@ MF_DEFUN(sieve, NUMBER, STRING script, OPTIONAL, NUMBER flags,
mu_sieve_set_data(mach, env);
+ if (f & MF_SIEVE_LOG)
+ mu_sieve_set_logger(mach, _sieve_text_action_log);
if (f & MF_SIEVE_TEXT) {
struct locus locus;
env_get_locus(env, &locus);
- if (f & MF_SIEVE_LOG)
- mu_sieve_set_logger(mach, _sieve_text_action_log);
rc = mu_sieve_compile_buffer(mach, script, strlen(script),
MF_OPTVAL(file, locus.file),
MF_OPTVAL(line, locus.line));
} else {
- if (f & MF_SIEVE_LOG)
- mu_sieve_set_logger(mach, _sieve_file_action_log);
rc = mu_sieve_compile(mach, script);
}
- if (rc == 0) {
- mu_attribute_t attr;
- mu_message_t msg;
-
- rc = mu_stream_to_message(mstr, &msg);
- if (rc) {
- mu_sieve_machine_destroy(&mach);
- MF_THROW(mfe_failure,
- _("cannot translate stream to message: %s"),
- mu_strerror (rc));
- }
+
+ MF_ASSERT(rc == 0,
+ mfe_failure,
+ _("compilation of Sieve script %s failed"),
+ script);
+
+ msg = bi_message_from_descr(env, nmsg);
- mu_message_get_attribute(msg, &attr);
- mu_attribute_unset_deleted(attr);
- rc = mu_sieve_message(mach, msg);
- if (rc == 0)
- retval = !(mu_attribute_is_deleted(attr) == 0);
- /* Destroy the message. This also indirectly unrefs the mstr,
- bringing its refcount back to its initial value. */
- mu_message_destroy(&msg