[libparse-win32registry-perl] 01/02: Imported Upstream version 0.18
Hilko Bengen
bengen at moszumanska.debian.org
Sun Sep 27 18:30:15 UTC 2015
This is an automated email from the git hooks/post-receive script.
bengen pushed a commit to annotated tag debian/0.18-7
in repository libparse-win32registry-perl.
commit 9b4a22a07d1f747495d8a96ef3bd4f305f5694e6
Author: Hilko Bengen <bengen at debian.org>
Date: Sun Sep 27 19:31:27 2015 +0200
Imported Upstream version 0.18
---
Changes | 29 +++
LICENSE | 79 ++++++
MANIFEST | 15 ++
Makefile.PL | 87 +++++++
Milter.pm | 837 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Milter.xs | 468 +++++++++++++++++++++++++++++++++
README | 105 ++++++++
TODO | 10 +
callbacks.c | 768 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
callbacks.h | 15 ++
intpools.c | 527 ++++++++++++++++++++++++++++++++++++++
intpools.h | 57 +++++
sample.pl | 258 +++++++++++++++++++
test.pl | 81 ++++++
typemap | 18 ++
15 files changed, 3354 insertions(+)
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..75f5384
--- /dev/null
+++ b/Changes
@@ -0,0 +1,29 @@
+Revision history for Perl extension Sendmail::Milter.
+
+0.18 Tue Oct 9 21:38:09 2001
+ - Patches to properly link with sendmail 8.12.1. Fixed
+ auto_setconn to support abbreviated T= syntax. Thanks to
+ Derek J. Balling of Yahoo, Inc.
+ - Updates to documentation to reflect sendmail 8.12.1.
+0.17 Sat Jul 29 09:55:02 2000
+ - Fixed build to properly link on Solaris. Thanks to
+ Claus Assmann of Sendmail, Inc.
+0.16 Mon Jul 24 05:37:59 2000
+ - Fixed bug in detecting no F= flags in auto_getconn().
+0.15 Wed Jul 19 19:15:49 2000
+ - Tested against sendmail 8.11.0 release.
+ - Updated README against released sendmail 8.11.0.
+0.14 Tue Jul 18 08:28:00 2000
+ - Now store code refs in globals to avoid sv_dup.
+ - Update README with SourceForge information.
+0.12 Thu Jul 13 11:16:17 2000
+ - Include sendmail's LICENSE file.
+0.11 Thu Jul 6 22:46:26 2000
+ - Now block for locking interpreters with condition variables.
+ - Successfully support code references and function names.
+ - Now support sendmail-8.11.0
+ - Fixed idiotic bug where all callbacks were going through
+ one interpreter.
+
+0.10 Tue Jul 4 23:22:51 2000
+ - Never released, only for internal testing.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..89b12f5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,79 @@
+ SENDMAIL LICENSE
+
+The following license terms and conditions apply, unless a different
+license is obtained from Sendmail, Inc., 6425 Christie Ave, Fourth Floor,
+Emeryville, CA 94608, or by electronic mail at license at sendmail.com.
+
+License Terms:
+
+Use, Modification and Redistribution (including distribution of any
+modified or derived work) in source and binary forms is permitted only if
+each of the following conditions is met:
+
+1. Redistributions qualify as "freeware" or "Open Source Software" under
+ one of the following terms:
+
+ (a) Redistributions are made at no charge beyond the reasonable cost of
+ materials and delivery.
+
+ (b) Redistributions are accompanied by a copy of the Source Code or by an
+ irrevocable offer to provide a copy of the Source Code for up to three
+ years at the cost of materials and delivery. Such redistributions
+ must allow further use, modification, and redistribution of the Source
+ Code under substantially the same terms as this license. For the
+ purposes of redistribution "Source Code" means the complete compilable
+ and linkable source code of sendmail including all modifications.
+
+2. Redistributions of source code must retain the copyright notices as they
+ appear in each source code file, these license terms, and the
+ disclaimer/limitation of liability set forth as paragraph 6 below.
+
+3. Redistributions in binary form must reproduce the Copyright Notice,
+ these license terms, and the disclaimer/limitation of liability set
+ forth as paragraph 6 below, in the documentation and/or other materials
+ provided with the distribution. For the purposes of binary distribution
+ the "Copyright Notice" refers to the following language:
+ "Copyright (c) 1998-2000 Sendmail, Inc. All rights reserved."
+
+4. Neither the name of Sendmail, Inc. nor the University of California nor
+ the names of their contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission. The name "sendmail" is a trademark of Sendmail, Inc.
+
+5. All redistributions must comply with the conditions imposed by the
+ University of California on certain embedded code, whose copyright
+ notice and conditions for redistribution are as follows:
+
+ (a) Copyright (c) 1988, 1993 The Regents of the University of
+ California. All rights reserved.
+
+ (b) Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ (i) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (ii) Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ (iii) Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY
+ SENDMAIL, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF
+ CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+$Revision: 1.1.1.1 $, Last updated $Date: 2000/07/14 05:46:15 $
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..363d9e5
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,15 @@
+Changes
+LICENSE
+MANIFEST
+README
+TODO
+Makefile.PL
+Milter.pm
+Milter.xs
+intpools.c
+intpools.h
+callbacks.c
+callbacks.h
+typemap
+sample.pl
+test.pl
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..bd827fb
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,87 @@
+use 5.006;
+
+use strict;
+
+use ExtUtils::MakeMaker;
+use Config;
+
+if ((not $ARGV[0]) or (not $ARGV[1]))
+{
+ print "Usage: perl Makefile.PL <path-to-sendmail-source> <path-to-sendmail-obj.dir>\n";
+ print "(e.g. 'perl Makefile.PL ../sendmail ../sendmail/obj.FreeBSD.4.0-RELEASE.i386')\n";
+ print "\n";
+ exit;
+}
+
+if (not $Config{usethreads})
+{
+ print "To use this module, your perl interpreter must have been compiled with\n";
+ print "\t-Dusethreads.\n";
+ print "\n";
+ exit;
+}
+
+my $SENDMAIL_PATH = MM->canonpath($ARGV[0]);
+my $SENDMAIL_OBJ_PATH = MM->canonpath($ARGV[1]);
+
+my $MILTER_LIB = MM->catdir($SENDMAIL_OBJ_PATH, "libmilter");
+my $SMUTIL_LIB = MM->catdir($SENDMAIL_OBJ_PATH, "libsmutil");
+my $SM_LIB = MM->catdir($SENDMAIL_OBJ_PATH, "libsm");
+my $MILTER_INCLUDE = MM->catdir($SENDMAIL_PATH, "include");
+my $SENDMAIL_INCLUDE = MM->catdir($SENDMAIL_PATH, "sendmail");
+
+sub milter_configure
+{
+ my $hash_ref = {};
+ my $libs;
+ my $ccflags;
+
+ # Standard milter libraries
+ $libs = "-L$MILTER_LIB -L$SMUTIL_LIB -L$SM_LIB -lmilter -lsmutil -lsm";
+
+ # POSIX threads support.
+ if ($Config{libs} =~ /-lpthread/)
+ {
+ $libs .= " -lpthread";
+ }
+ else
+ {
+ $ccflags = '-pthread';
+ }
+
+ # Solaris 2.6 -lsocket -lnsl support.
+ if ($Config{libs} =~ /-lsocket/)
+ {
+ $libs .= " -lsocket";
+ }
+ if ($Config{libs} =~ /-lnsl/)
+ {
+ $libs .= " -lnsl";
+ }
+
+ # Solaris and inet_aton / inet_pton functions.
+ if (($^O eq 'solaris') && (not $Config{d_inetaton}))
+ {
+ $libs .= " -lresolv";
+ }
+
+ # Only set the CCFLAGS variable if there's something.
+ if ($ccflags)
+ {
+ $hash_ref->{'CCFLAGS'} = $ccflags;
+ }
+
+ $hash_ref->{'LIBS'} = [ "$libs" ];
+
+ return $hash_ref;
+}
+
+WriteMakefile(
+ 'NAME' => 'Sendmail::Milter',
+ 'VERSION_FROM' => 'Milter.pm',
+ 'CONFIGURE' => \&milter_configure,
+ 'OBJECT' => '$(BASEEXT)$(OBJ_EXT) intpools$(OBJ_EXT) callbacks$(OBJ_EXT)',
+ 'DEFINE' => '',
+ 'INC' => "-I$SENDMAIL_INCLUDE -I$MILTER_INCLUDE",
+);
+
diff --git a/Milter.pm b/Milter.pm
new file mode 100644
index 0000000..81cf8b8
--- /dev/null
+++ b/Milter.pm
@@ -0,0 +1,837 @@
+#
+# Copyright (c) 2000-2001 Charles Ying. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the same terms as sendmail itself.
+#
+
+package Sendmail::Milter;
+
+use 5.006;
+
+use strict;
+use warnings;
+use Carp;
+
+require Exporter;
+require DynaLoader;
+use AutoLoader;
+
+our @ISA = qw(Exporter DynaLoader);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+# This allows declaration use Sendmail::Milter ':all';
+# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
+# will save memory.
+our %EXPORT_TAGS = ( 'all' => [ qw(
+ SMFIF_ADDHDRS
+ SMFIF_ADDRCPT
+ SMFIF_CHGBODY
+ SMFIF_CHGHDRS
+ SMFIF_DELRCPT
+ SMFIF_MODBODY
+ SMFIS_ACCEPT
+ SMFIS_CONTINUE
+ SMFIS_DISCARD
+ SMFIS_REJECT
+ SMFIS_TEMPFAIL
+ SMFI_CURR_ACTS
+ SMFI_V1_ACTS
+ SMFI_V2_ACTS
+) ] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+
+our @EXPORT = qw(
+ SMFIF_ADDHDRS
+ SMFIF_ADDRCPT
+ SMFIF_CHGBODY
+ SMFIF_CHGHDRS
+ SMFIF_DELRCPT
+ SMFIF_MODBODY
+ SMFIS_ACCEPT
+ SMFIS_CONTINUE
+ SMFIS_DISCARD
+ SMFIS_REJECT
+ SMFIS_TEMPFAIL
+ SMFI_CURR_ACTS
+ SMFI_V1_ACTS
+ SMFI_V2_ACTS
+);
+
+our $VERSION = '0.18';
+
+sub AUTOLOAD {
+ # This AUTOLOAD is used to 'autoload' constants from the constant()
+ # XS function. If a constant is not found then control is passed
+ # to the AUTOLOAD in AutoLoader.
+
+ my $constname;
+ our $AUTOLOAD;
+ ($constname = $AUTOLOAD) =~ s/.*:://;
+ croak "& not defined" if $constname eq 'constant';
+ my $val = constant($constname, @_ ? $_[0] : 0);
+ if ($! != 0) {
+ if ($! =~ /Invalid/ || $!{EINVAL}) {
+ $AutoLoader::AUTOLOAD = $AUTOLOAD;
+ goto &AutoLoader::AUTOLOAD;
+ }
+ else {
+ croak "Your vendor has not defined Sendmail::Milter macro $constname";
+ }
+ }
+ {
+ no strict 'refs';
+
+ *$AUTOLOAD = sub { $val };
+ }
+ goto &$AUTOLOAD;
+}
+
+bootstrap Sendmail::Milter $VERSION;
+
+# Preloaded methods go here.
+
+our %DEFAULT_CALLBACKS =
+(
+ 'connect' => 'connect_callback',
+ 'helo' => 'helo_callback',
+ 'envfrom' => 'envfrom_callback',
+ 'envrcpt' => 'envrcpt_callback',
+ 'header' => 'header_callback',
+ 'eoh' => 'eoh_callback',
+ 'body' => 'body_callback',
+ 'eom' => 'eom_callback',
+ 'abort' => 'abort_callback',
+ 'close' => 'close_callback',
+);
+
+
+sub auto_setconn
+{
+ my $name = shift;
+ my $cf_filename = shift || undef;
+
+ my $conn_info = Sendmail::Milter::auto_getconn($name, $cf_filename);
+
+ if ($conn_info)
+ {
+ Sendmail::Milter::setconn($conn_info);
+ return 1;
+ }
+
+ return 0;
+}
+
+sub auto_getconn
+{
+ my $name = shift;
+ my $cf_filename = shift || '/etc/mail/sendmail.cf';
+ my $raw_file;
+
+ my $current_name;
+ my $conn_info;
+
+ open(CF_FILE, $cf_filename) || die "Can't open '$cf_filename' for reading: $!";
+
+ $raw_file = join('', <CF_FILE>);
+ $raw_file =~ s/\n[ \t]/ /g;
+
+ close(CF_FILE);
+
+ foreach my $line (split(/\n/, $raw_file))
+ {
+ chomp $line;
+
+ # Just ignore rest of line in case it's F=T, T=blah...
+ # Or just T=blah...
+
+ if ($line =~ /^X(.+),\s*S\=(.+),\s*[FT]\=(.)/)
+ {
+ $current_name = $1;
+ $conn_info = $2;
+
+ if ($current_name eq $name)
+ {
+ return $conn_info;
+ }
+ }
+ elsif ($line =~ /^X(.+),\s*S\=(.+)/)
+ {
+ $current_name = $1;
+ $conn_info = $2;
+
+ if ($current_name eq $name)
+ {
+ return $conn_info;
+ }
+ }
+ }
+
+ return undef;
+}
+
+# Autoload methods go after =cut, and are processed by the autosplit program.
+
+1;
+__END__
+
+=head1 NAME
+
+Sendmail::Milter - Interface to sendmail's Mail Filter API
+
+=head1 SYNOPSIS
+
+ use Sendmail::Milter;
+
+ my %my_milter_callbacks =
+ (
+ 'connect' => \&my_connect_callback,
+ 'helo' => \&my_helo_callback,
+ 'envfrom' => \&my_envfrom_callback,
+ 'envrcpt' => \&my_envrcpt_callback,
+ 'header' => \&my_header_callback,
+ 'eoh' => \&my_eoh_callback,
+ 'body' => \&my_body_callback,
+ 'eom' => \&my_eom_callback,
+ 'abort' => \&my_abort_callback,
+ 'close' => \&my_close_callback,
+ );
+
+ sub my_connect_callback;
+ sub my_helo_callback;
+ sub my_envfrom_callback;
+ sub my_envrcpt_callback;
+ sub my_header_callback;
+ sub my_eoh_callback;
+ sub my_body_callback;
+ sub my_eom_callback;
+ sub my_abort_callback;
+ sub my_close_callback;
+
+
+ BEGIN:
+ {
+ # Get myfilter's connection information
+ # from /etc/mail/sendmail.cf
+
+ Sendmail::Milter::auto_setconn("myfilter");
+ Sendmail::Milter::register("myfilter",
+ \%my_milter_callbacks, SMFI_CURR_ACTS);
+
+ Sendmail::Milter::main();
+
+ # Never reaches here, callbacks are called from Milter.
+ }
+
+=head1 DESCRIPTION
+
+B<Sendmail::Milter> is a Perl extension to sendmail's Mail Filter API (Milter).
+
+B<Note:> You need to have a Perl 5.6 or later interpreter built with
+B<-Dusethreads>.
+
+=head1 FUNCTIONS
+
+Portions of this document come from comments in the B<libmilter/mfapi.h> header
+file.
+
+=head2 Main Functions
+
+B<Note:> No functions are exported. You must call these functions explicitly
+from the B<Sendmail::Milter> package.
+
+=over 4
+
+=item register NAME, CALLBACKS [, FLAGS]
+
+Registers a mail filter NAME with hash reference CALLBACKS callbacks, and
+optional capability flags FLAGS. NAME is the same filter name that you would
+pass to B<auto_setconn>. CALLBACKS is a hash reference that can contain any of
+the following keys:
+
+ connect
+ helo
+ envfrom
+ envrcpt
+ header
+ eoh
+ body
+ eom
+ abort
+ close
+
+The values for these keys indicate the callback routine that is associated with
+each Milter callback. The values must be either function names, code references
+or closures.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+B<%Sendmail::Milter::DEFAULT_CALLBACKS> is a hash with default function names
+for all of the Milter callbacks. The default callback function names are:
+
+B<connect_callback>, B<helo_callback>, B<envfrom_callback>,
+B<envrcpt_callback>, B<header_callback>, B<eoh_callback>, B<body_callback>,
+B<eom_callback>, B<abort_callback>, B<close_callback>.
+
+See the section B<Writing Milter Callbacks> for more information on writing
+the callbacks themselves.
+
+For more information on capability flags, see the section B<Capability Flags>
+in the B<@EXPORT> section.
+
+=item main [MAX_INTERPRETERS] [, MAX_REQUESTS]
+
+Starts the mail filter. If successful, this function never returns. Instead, it
+launches the Milter engine which will call each of the callback routines as
+appropriate.
+
+MAX_INTERPRETERS sets the limit on the maximum number of interpreters that
+B<Sendmail::Milter> is allowed to create. These interpreters will only be
+created as the need arises and are not all created at startup. The default
+value is 0. (No maximum limit)
+
+MAX_REQUESTS sets the limit on the maximum number of requests an interpreter
+will process before being recycled. The default value is 0. (Don't recycle
+interpreters)
+
+This function returns nonzero on success (if a kill was signaled or something),
+the undefined value otherwise.
+
+B<Note:> You should have at least registered a callback and set the connection
+information string before calling this function.
+
+
+=item setconn CONNECTION_INFO
+
+Sets the connection information string for the filter. The format of this
+string is identical to that found in the Milter documentation. Some examples
+are C<local:/var/run/f1.sock>, C<inet6:999 at localhost>, C<inet:3333 at localhost>.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=item auto_setconn NAME [, SENDMAIL_CF_FILENAME]
+
+This function automatically sets the connection information by parsing the
+sendmail .cf file for the appropriate X line containing the connection
+information for the NAME mail filter and calling B<setconn> if it was
+successful. It is provided as a helper function and does not exist in the
+current Milter library.
+
+B<Note:> This connection information isn't useful for implementing a Milter
+that resides on a machine that is remote to the machine running sendmail. In
+those cases, you will want to set the connection information manually with
+B<setconn>.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+SENDMAIL_CF_FILENAME defaults to C</etc/mail/sendmail.cf> if not specified.
+
+
+=item auto_getconn NAME [, SENDMAIL_CF_FILENAME]
+
+Similar to B<auto_setconn>, this function parses the sendmail .cf file for the
+appropriate X line containing the connection information for NAME. It does not,
+however, call B<setconn>. It only retrieves the connection information.
+
+This function returns the connection information string for NAME, or undef on
+failure.
+
+SENDMAIL_CF_FILENAME defaults to C</etc/mail/sendmail.cf> if not specified.
+
+
+=item settimeout TIMEOUT
+
+Sets the timeout for reads/writes in the Milter engine.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=item setdbg LEVEL
+
+Sets the debug level for the Milter engine.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=back
+
+
+
+=head2 Writing Milter Callbacks
+
+Writing Milter callbacks is pretty easy when you're doing simple text
+processing.
+
+But remember one thing: Each Milter callback could quite possibly run in a
+different instance of the Perl interpreter.
+
+B<Sendmail::Milter> launches multiple persistent Perl interpreters to increase
+performance (so it doesn't have to startup and shutdown the interpreters
+constantly). Thus, you can't rely on setting external package variables, global
+variables, or even running other modules which rely on such things. This will
+continue to be true while interpreter thread support in Perl is experimental.
+For more information, see L<perlfork>. Most of that information applies here.
+
+Remember to return one of the B<SMFIS_*> result codes from the callback
+routine. Remember there can be multiple message body chunks. And remember that
+only B<eom_callback> is allowed to manipulate the headers, recipients, message
+body, etc.
+
+See the B<@EXPORT> section for information on the B<SMFIS_*> result codes.
+
+Here is an example of a B<connect_callback> routine:
+
+ # External modules are OK, but note the caveats above.
+ use Socket;
+
+ sub connect_callback
+ {
+ my $ctx = shift; # The Milter context object.
+ my $hostname = shift; # The connection's host name.
+ my $sockaddr_in = shift;
+ my ($port, $iaddr) = sockaddr_in($sockaddr_in);
+
+ print "Hostname is: " . $hostname . "\n";
+
+ # Cool, a printable IP address.
+ print "IP Address is: " . inet_ntoa($iaddr) . "\n";
+
+ return SMFIS_CONTINUE; # Returning a value is important!
+ }
+
+B<Note:> The $ctx Milter context object is not a true Perl object. It's really
+a blessed reference to an opaque C structure. Only use the Milter context
+functions (described in a later section) with this object. (Don't touch it,
+it's evil.)
+
+=head2 Milter Callback Interfaces
+
+These interfaces closely mirror their Milter callback counterparts, however
+there are some differences that take advantage of Perl's syntactic sugar.
+
+B<Note:> Each callback receives a Milter context object as the first
+argument. This context object is used in making Milter Context function
+calls. See B<Milter Context Functions> for more details.
+
+=over 4
+
+=item B<connect_callback> CTX, HOSTNAME, SOCKADDR_IN
+
+Invoked on each connection. HOSTNAME is the host domain name, as determined by
+a reverse lookup on the host address. SOCKADDR_IN is the AF_INET portion of the
+host address, as determined by a B<getpeername(2)> syscall on the SMTP
+socket. You can use B<Socket::unpack_sockaddr_in()> to unpack it into a port
+and IP address.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<helo_callback> CTX, HELOHOST
+
+Invoked on SMTP HELO/EHLO command. HELOHOST is the value passed to HELO/EHLO
+command, which should be the domain name of the sending host (but is, in
+practice, anything the sending host wants to send).
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<envfrom_callback> CTX, ARG1, ARG2, ..., ARGn
+
+Invoked on envelope from. ARG1, ARG2, ... ARGn are SMTP command arguments. ARG1
+is guaranteed to be the sender address. Later arguments are the ESMTP
+arguments.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<envrcpt_callback> CTX, ARG1, ARG2, ..., ARGn
+
+Invoked on each envelope recipient. ARG1, ARG2, ... ARGn are SMTP command
+arguments. ARG1 is guaranteed to be the recipient address. Later arguments are
+the ESMTP arguments.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<header_callback> CTX, FIELD, VALUE
+
+Invoked on each message header. The content of the header may have folded white
+space (that is, multiple lines with following white space) included. FIELD is
+the header field name, VALUE is the header field value.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<eoh_callback> CTX
+
+Invoked at end of header.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<body_callback> CTX, BODY, LEN
+
+Invoked for each body chunk. There may be multiple body chunks passed to the
+filter. End-of-lines are represented as received from SMTP (normally
+Carriage-Return/Line-Feed). BODY contains the body data, LEN contains the
+length of the body data.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<eom_callback> CTX
+
+Invoked at end of message. This routine can perform special operations such as
+modifying the message header, body, or envelope. See the section on
+B<eom_callback> in B<Milter Context Functions>.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<abort_callback> CTX
+
+Invoked if message is aborted outside of the control of the filter, for
+example, if the SMTP sender issues an RSET command. If B<abort_callback> is
+called, B<eom_callback> will not be called and vice versa.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=item B<close_callback> CTX
+
+Invoked at end of the connection. This is called on close even if the previous
+mail transaction was aborted.
+
+This callback should return one of the B<SMFIS_*> result codes.
+
+
+=back
+
+
+
+=head2 Milter Context Functions
+
+These routines are object methods that are part of the
+B<Sendmail::Milter::Context> pseudo-package for use by B<Sendmail::Milter>
+callback functions. Any attempts to use them without a properly blessed Milter
+context object will fail miserably. Please see restrictions on when these
+routines may be called.
+
+B<Context routines available to all Milter callback functions:>
+
+These functions are available to all types of Milter callback functions. It is
+worth noting that passing connection-private data by reference is probably more
+efficient than passing by value.
+
+=over 4
+
+=item B<$ctx>-E<gt>setpriv DATA
+
+Each B<$ctx> can contain connection-private data (specific to an SMTP
+connection). This routine can be used to allocate this private data. Calling
+this function with DATA set to the undefined value will clear Milter's pointer
+to this private data. You should always do this to decrement the private data's
+reference count.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=item B<$ctx>-E<gt>getpriv
+
+Each B<$ctx> can contain connection-private data (specific to an SMTP
+connection). This routine can be used to retrieve this private data.
+
+This function returns a scalar containing B<$ctx>'s private data.
+
+
+=item B<$ctx>-E<gt>getsymval SYMNAME
+
+Additional information is passed in to the vendor filter routines using
+symbols. Symbols correspond closely to sendmail macros. The symbols defined
+depend on the context. SYMNAME is the name of the symbol to access.
+
+This function returns the value of the symbol name SYMNAME.
+
+
+=item B<$ctx>-E<gt>setreply RCODE, XCODE, MESSAGE
+
+Set the specific reply code to be used in response to the active command. If
+not specified, a generic reply code is used.
+RCODE is the three-digit (B<RFC 821>) SMTP reply code to be returned, e.g. C<551>.
+XCODE is the extended (B<RFC 2034>) reply code, e.g., C<5.7.6>.
+MESSAGE is the text part of the SMTP reply.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+=back
+
+
+B<Context routines available only to the eom_callback function:>
+
+The B<eom_callback> Milter callback is called at the end of a message
+(essentially, after the final DATA dot). This routine can call some special
+routines to modify the envelope, header, or body of the message before the
+message is enqueued. These routines must not be called from any vendor routine
+other than B<eom_callback>.
+
+=over 4
+
+=item B<$ctx>-E<gt>addheader FIELD, VALUE
+
+Add a header to the message. FIELD is the header field name. VALUE is the
+header field value. This header is not passed to other filters. It is not
+checked for standards compliance; the mail filter must ensure that no protocols
+are violated as a result of adding this header.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=item B<$ctx>-E<gt>chgheader FIELD, INDEX, VALUE
+
+Change/delete a header in the message. FIELD is the header field name. INDEX is
+the Nth occurence of the header field name. VALUE is the new header field value
+(empty for delete header). It is not checked for standards compliance; the mail
+filter must ensure that no protocols are violated as a result of adding this
+header.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=item B<$ctx>-E<gt>addrcpt RCPT
+
+Add a recipient to the envelope. RCPT is the recipient to be added.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=item B<$ctx>-E<gt>delrcpt RCPT
+
+Delete a recipient from the envelope. RCPT is the envelope recipient to be
+deleted. This should be in exactly the same form passed to B<envrcpt_callback>
+or the address may not be deleted.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=item B<$ctx>-E<gt>replacebody DATA
+
+Replace the body of the message. DATA is the scalar containing the block of
+message body information to insert. This routine may be called multiple times
+if the body is longer than convenient to send in one call. End of line should
+be represented as Carriage-Return/Line Feed.
+
+This function returns nonzero upon success, the undefined value otherwise.
+
+
+=back
+
+
+
+=head1 @EXPORT
+
+B<Sendmail::Milter> exports the following constants:
+
+=head2 Callback Result Codes
+
+These are the possible result codes that may be returned by the Milter callback
+functions. If you do not specify a return value, B<Sendmail::Milter> will send
+a default result code of B<SMFIS_CONTINUE> back to Milter.
+
+=over 4
+
+=item SMFIS_CONTINUE
+
+Continue processing message/connection
+
+=item SMFIS_REJECT
+
+Reject the message/connection. No further routines will be called for this
+message (or connection, if returned from a connection-oriented routine).
+
+=item SMFIS_DISCARD
+
+Accept the message, but silently discard the message. No further routines will
+be called for this message. This is only meaningful from message-oriented
+routines.
+
+=item SMFIS_ACCEPT
+
+Accept the message/connection. No further routines will be called for this
+message (or connection, if returned from a connection-oriented routine; in this
+case, it causes all messages on this connection to be accepted without
+filtering).
+
+=item SMFIS_TEMPFAIL
+
+Return a temporary failure, i.e., the corresponding SMTP command will return a
+4xx status code. In some cases this may prevent further routines from being
+called on this message or connection, although in other cases (e.g., when
+processing an envelope recipient) processing of the message will continue.
+
+=back
+
+=head2 Capability Flags
+
+These are possible capability flags for what a mail filter can do.
+Normally, you should specify each capability explicitly as needed.
+
+=over 4
+
+=item SMFIF_ADDHDRS
+
+Allows a mail filter to add headers.
+
+=item SMFIF_CHGBODY
+
+Allows a mail filter to change the message body.
+
+=item SMFIF_ADDRCPT
+
+Allows a mail filter to add recipients.
+
+=item SMFIF_DELRCPT
+
+Allows a mail filter to delete recipients.
+
+=item SMFIF_CHGHDRS
+
+Allows a mail filter to change headers.
+
+=item SMFIF_MODBODY
+
+Allows a mail filter to change the message body. (Provided only for backwards
+compatibility)
+
+=back
+
+
+=head2 Capability Flag Sets
+
+These provide sets of capability flags that indicate all of the capabilities in
+a particular version of Milter. B<SMFI_CURR_ACTS> is set to the capabilities in
+the current version of Milter.
+
+=over 4
+
+=item SMFI_CURR_ACTS
+
+Enables the set of capabilities available to mail filters in the current
+version of Milter.
+
+=item SMFI_V1_ACTS
+
+Enables the set of capabilities available to mail filters in V1 of Milter.
+
+=item SMFI_V2_ACTS
+
+Enables the set of capabilities available to mail filters in V2 of Milter.
+
+=back
+
+
+=head1 EXAMPLES
+
+=head2 Appending a line to the message body
+
+ use Sendmail::Milter;
+
+ my %my_milter_callbacks =
+ (
+ 'eoh' => \&my_eoh_callback,
+ 'body' => \&my_body_callback,
+ 'eom' => \&my_eom_callback,
+ 'abort' => \&my_abort_callback,
+ );
+
+ sub my_eoh_callback
+ {
+ my $ctx = shift;
+ my $body = "";
+
+ $ctx->setpriv(\$body);
+
+ return SMFIS_CONTINUE;
+ }
+
+ sub my_body_callback
+ {
+ my $ctx = shift;
+ my $body_chunk = shift;
+ my $body_ref = $ctx->getpriv();
+
+ ${$body_ref} .= $body_chunk;
+
+ # This is crucial, the reference to the body may have
+ # changed.
+
+ $ctx->setpriv($body_ref);
+
+ return SMFIS_CONTINUE;
+ }
+
+ sub my_eom_callback
+ {
+ my $ctx = shift;
+ my $body_ref = $ctx->getpriv();
+
+ # Note: This doesn't support messages with MIME data.
+
+ ${$body_ref} .= "---> Append me to this message body!\n";
+
+ $ctx->replacebody(${$body_ref});
+
+ $ctx->setpriv(undef);
+
+ return SMFIS_ACCEPT;
+ }
+
+ sub my_abort_callback
+ {
+ my $ctx = shift;
+
+ $ctx->setpriv(undef);
+
+ return SMFIS_CONTINUE;
+ }
+
+
+ # The following code does not necessarily need to be in a
+ # BEGIN block. It just looks funny without it. :)
+
+ BEGIN:
+ {
+ Sendmail::Milter::auto_setconn("myfilter");
+ Sendmail::Milter::register("myfilter",
+ \%my_milter_callbacks, SMFI_CURR_ACTS);
+
+ Sendmail::Milter::main();
+
+ # Never reaches here, callbacks are called from Milter.
+ }
+
+
+See the B<test.pl> sample test case for more callback examples.
+
+=head1 AUTHOR
+
+Charles Ying, cying at cpan.org.
+
+=head1 COPYRIGHT
+
+Copyright (c) 2000-2001 Charles Ying. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same terms
+as sendmail itself.
+
+The interpreter pools portion (found in the intpools.c, intpools.h, and test.pl
+files) of this code is also available under the same terms as perl itself.
+
+=head1 SEE ALSO
+
+perl(1), sendmail(8).
+
+=cut
diff --git a/Milter.xs b/Milter.xs
new file mode 100644
index 0000000..60d4de6
--- /dev/null
+++ b/Milter.xs
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2000 Charles Ying. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the same terms as sendmail itself.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#include "intpools.h"
+
+#include "libmilter/mfapi.h"
+#include "callbacks.h"
+
+
+/* Conversion for an easier interface to the milter API. */
+#define MI_BOOL_CVT(mi_bool) (((mi_bool) == MI_SUCCESS) ? TRUE : FALSE)
+
+typedef SMFICTX *Sendmail_Milter_Context;
+
+
+/* Wrapper functions to do some real work. */
+
+int milter_register(pTHX_ char *name, SV *milter_desc_ref, int flags)
+{
+ HV *milter_desc = (HV *)NULL;
+ struct smfiDesc filter_desc;
+
+ if (!SvROK(milter_desc_ref) &&
+ (SvTYPE(SvRV(milter_desc_ref)) != SVt_PVHV))
+ croak("expected reference to hash for milter descriptor.");
+
+ milter_desc = (HV *)SvRV(milter_desc_ref);
+
+ register_callbacks(&filter_desc, name, milter_desc, flags);
+
+ return smfi_register(filter_desc);
+}
+
+int milter_main(int max_interpreters, int max_requests)
+{
+ init_callbacks(max_interpreters, max_requests);
+
+ return smfi_main();
+}
+
+
+/* Constants from libmilter/mfapi.h */
+
+static int
+not_here(char *s)
+{
+ croak("%s not implemented on this architecture", s);
+ return -1;
+}
+
+static double
+constant_SMFIF_A(char *name, int len, int arg)
+{
+ if (7 + 2 >= len ) {
+ errno = EINVAL;
+ return 0;
+ }
+ switch (name[7 + 2]) {
+ case 'H':
+ if (strEQ(name + 7, "DDHDRS")) { /* SMFIF_A removed */
+#ifdef SMFIF_ADDHDRS
+ return SMFIF_ADDHDRS;
+#else
+ goto not_there;
+#endif
+ }
+ case 'R':
+ if (strEQ(name + 7, "DDRCPT")) { /* SMFIF_A removed */
+#ifdef SMFIF_ADDRCPT
+ return SMFIF_ADDRCPT;
+#else
+ goto not_there;
+#endif
+ }
+ }
+ errno = EINVAL;
+ return 0;
+
+not_there:
+ errno = ENOENT;
+ return 0;
+}
+
+static double
+constant_SMFIF_C(char *name, int len, int arg)
+{
+ if (7 + 2 >= len ) {
+ errno = EINVAL;
+ return 0;
+ }
+ switch (name[7 + 2]) {
+ case 'B':
+ if (strEQ(name + 7, "HGBODY")) { /* SMFIF_C removed */
+#ifdef SMFIF_CHGBODY
+ return SMFIF_CHGBODY;
+#else
+ goto not_there;
+#endif
+ }
+ case 'H':
+ if (strEQ(name + 7, "HGHDRS")) { /* SMFIF_C removed */
+#ifdef SMFIF_CHGHDRS
+ return SMFIF_CHGHDRS;
+#else
+ goto not_there;
+#endif
+ }
+ }
+ errno = EINVAL;
+ return 0;
+
+not_there:
+ errno = ENOENT;
+ return 0;
+}
+
+static double
+constant_SMFIF(char *name, int len, int arg)
+{
+ if (5 + 1 >= len ) {
+ errno = EINVAL;
+ return 0;
+ }
+ switch (name[5 + 1]) {
+ case 'A':
+ if (!strnEQ(name + 5,"_", 1))
+ break;
+ return constant_SMFIF_A(name, len, arg);
+ case 'C':
+ if (!strnEQ(name + 5,"_", 1))
+ break;
+ return constant_SMFIF_C(name, len, arg);
+ case 'D':
+ if (strEQ(name + 5, "_DELRCPT")) { /* SMFIF removed */
+#ifdef SMFIF_DELRCPT
+ return SMFIF_DELRCPT;
+#else
+ goto not_there;
+#endif
+ }
+ case 'M':
+ if (strEQ(name + 5, "_MODBODY")) { /* SMFIF removed */
+#ifdef SMFIF_MODBODY
+ return SMFIF_MODBODY;
+#else
+ goto not_there;
+#endif
+ }
+ }
+ errno = EINVAL;
+ return 0;
+
+not_there:
+ errno = ENOENT;
+ return 0;
+}
+
+static double
+constant_SMFI_V(char *name, int len, int arg)
+{
+ switch (name[6 + 0]) {
+ case '1':
+ if (strEQ(name + 6, "1_ACTS")) { /* SMFI_V removed */
+#ifdef SMFI_V1_ACTS
+ return SMFI_V1_ACTS;
+#else
+ goto not_there;
+#endif
+ }
+ case '2':
+ if (strEQ(name + 6, "2_ACTS")) { /* SMFI_V removed */
+#ifdef SMFI_V2_ACTS
+ return SMFI_V2_ACTS;
+#else
+ goto not_there;
+#endif
+ }
+ }
+ errno = EINVAL;
+ return 0;
+
+not_there:
+ errno = ENOENT;
+ return 0;
+}
+
+static double
+constant_SMFI_(char *name, int len, int arg)
+{
+ switch (name[5 + 0]) {
+ case 'C':
+ if (strEQ(name + 5, "CURR_ACTS")) { /* SMFI_ removed */
+#ifdef SMFI_CURR_ACTS
+ return SMFI_CURR_ACTS;
+#else
+ goto not_there;
+#endif
+ }
+ case 'V':
+ return constant_SMFI_V(name, len, arg);
+ }
+ errno = EINVAL;
+ return 0;
+
+not_there:
+ errno = ENOENT;
+ return 0;
+}
+
+static double
+constant_SMFIS(char *name, int len, int arg)
+{
+ if (5 + 1 >= len ) {
+ errno = EINVAL;
+ return 0;
+ }
+ switch (name[5 + 1]) {
+ case 'A':
+ if (strEQ(name + 5, "_ACCEPT")) { /* SMFIS removed */
+#ifdef SMFIS_ACCEPT
+ return SMFIS_ACCEPT;
+#else
+ goto not_there;
+#endif
+ }
+ case 'C':
+ if (strEQ(name + 5, "_CONTINUE")) { /* SMFIS removed */
+#ifdef SMFIS_CONTINUE
+ return SMFIS_CONTINUE;
+#else
+ goto not_there;
+#endif
+ }
+ case 'D':
+ if (strEQ(name + 5, "_DISCARD")) { /* SMFIS removed */
+#ifdef SMFIS_DISCARD
+ return SMFIS_DISCARD;
+#else
+ goto not_there;
+#endif
+ }
+ case 'R':
+ if (strEQ(name + 5, "_REJECT")) { /* SMFIS removed */
+#ifdef SMFIS_REJECT
+ return SMFIS_REJECT;
+#else
+ goto not_there;
+#endif
+ }
+ case 'T':
+ if (strEQ(name + 5, "_TEMPFAIL")) { /* SMFIS removed */
+#ifdef SMFIS_TEMPFAIL
+ return SMFIS_TEMPFAIL;
+#else
+ goto not_there;
+#endif
+ }
+ }
+ errno = EINVAL;
+ return 0;
+
+not_there:
+ errno = ENOENT;
+ return 0;
+}
+
+static double
+constant(char *name, int len, int arg)
+{
+ errno = 0;
+ if (0 + 4 >= len ) {
+ errno = EINVAL;
+ return 0;
+ }
+ switch (name[0 + 4]) {
+ case 'F':
+ if (!strnEQ(name + 0,"SMFI", 4))
+ break;
+ return constant_SMFIF(name, len, arg);
+ case 'S':
+ if (!strnEQ(name + 0,"SMFI", 4))
+ break;
+ return constant_SMFIS(name, len, arg);
+ case '_':
+ if (!strnEQ(name + 0,"SMFI", 4))
+ break;
+ return constant_SMFI_(name, len, arg);
+ }
+ errno = EINVAL;
+ return 0;
+
+not_there:
+ errno = ENOENT;
+ return 0;
+}
+
+
+MODULE = Sendmail::Milter PACKAGE = Sendmail::Milter PREFIX = smfi_
+
+PROTOTYPES: DISABLE
+
+double
+constant(sv,arg)
+ PREINIT:
+ STRLEN len;
+ INPUT:
+ SV * sv
+ char * s = SvPV(sv, len);
+ int arg
+ CODE:
+ RETVAL = constant(s,len,arg);
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_register(name, milter_desc_ref, flags=0)
+ char* name;
+ SV* milter_desc_ref;
+ int flags;
+ CODE:
+ RETVAL = MI_BOOL_CVT(milter_register(aTHX_ name, milter_desc_ref,
+ flags));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_main(max_interpreters=0, max_requests=0)
+ int max_interpreters;
+ int max_requests;
+ CODE:
+ RETVAL = MI_BOOL_CVT(milter_main(max_interpreters, max_requests));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_setdbg(dbg)
+ int dbg;
+ CODE:
+ RETVAL = MI_BOOL_CVT(smfi_setdbg(dbg));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_setconn(conn)
+ char* conn;
+ CODE:
+ RETVAL = MI_BOOL_CVT(smfi_setconn(conn));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_settimeout(timeout)
+ int timeout;
+ CODE:
+ RETVAL = MI_BOOL_CVT(smfi_settimeout(timeout));
+ OUTPUT:
+ RETVAL
+
+int
+test_intpools(max_interp, max_requests, i_max, j_max, callback)
+ int max_interp;
+ int max_requests;
+ int i_max;
+ int j_max;
+ SV* callback;
+ CODE:
+ RETVAL = test_intpools(aTHX_ max_interp, max_requests, i_max, j_max,
+ callback);
+ OUTPUT:
+ RETVAL
+
+
+MODULE = Sendmail::Milter PACKAGE = Sendmail::Milter::Context PREFIX = smfi_
+
+char *
+smfi_getsymval(Sendmail_Milter_Context ctx, char* symname)
+
+bool
+smfi_setreply(ctx, rcode, xcode, message)
+ Sendmail_Milter_Context ctx;
+ char* rcode;
+ char* xcode;
+ char* message;
+ CODE:
+ RETVAL = MI_BOOL_CVT(smfi_setreply(ctx, rcode, xcode, message));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_addheader(ctx, headerf, headerv)
+ Sendmail_Milter_Context ctx;
+ char* headerf;
+ char* headerv;
+ CODE:
+ RETVAL = MI_BOOL_CVT(smfi_addheader(ctx, headerf, headerv));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_chgheader(ctx, headerf, index, headerv)
+ Sendmail_Milter_Context ctx;
+ char* headerf;
+ int index;
+ char* headerv;
+ CODE:
+ RETVAL = MI_BOOL_CVT(smfi_chgheader(ctx, headerf, index, headerv));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_addrcpt(ctx, rcpt)
+ Sendmail_Milter_Context ctx;
+ char* rcpt;
+ CODE:
+ RETVAL = MI_BOOL_CVT(smfi_addrcpt(ctx, rcpt));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_delrcpt(ctx, rcpt)
+ Sendmail_Milter_Context ctx;
+ char* rcpt;
+ CODE:
+ RETVAL = MI_BOOL_CVT(smfi_delrcpt(ctx, rcpt));
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_replacebody(ctx, body_data)
+ Sendmail_Milter_Context ctx;
+ SV* body_data;
+ PREINIT:
+ u_char *bodyp;
+ int len;
+ CODE:
+ bodyp = SvPV(body_data, len);
+ RETVAL = MI_BOOL_CVT(smfi_replacebody(ctx, bodyp, len));;
+ OUTPUT:
+ RETVAL
+
+bool
+smfi_setpriv(ctx, data)
+ Sendmail_Milter_Context ctx;
+ SV* data;
+ CODE:
+ if (SvTRUE(data))
+ RETVAL = MI_BOOL_CVT(smfi_setpriv(ctx, (void *)newSVsv(data)));
+ else
+ RETVAL = MI_BOOL_CVT(smfi_setpriv(ctx, NULL));
+ OUTPUT:
+ RETVAL
+
+SV *
+smfi_getpriv(ctx)
+ Sendmail_Milter_Context ctx;
+ CODE:
+ RETVAL = (SV *) smfi_getpriv(ctx);
+ OUTPUT:
+ RETVAL
diff --git a/README b/README
new file mode 100644
index 0000000..24d04ba
--- /dev/null
+++ b/README
@@ -0,0 +1,105 @@
+Sendmail::Milter - Perl interface to sendmail's Mail Filter API
+===============================================================
+
+Copyright Notice
+----------------
+
+Copyright (c) 2000-2001 Charles Ying. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same terms
+as sendmail itself.
+
+The interpreter pools portion (found in the intpools.c, intpools.h, and test.pl
+files) of this code is also available under the same terms as perl itself.
+
+
+About Sendmail::Milter
+----------------------
+
+Sendmail::Milter provides users with the ability to write mail filters in Perl
+that tightly integrate with sendmail's mail filter API.
+
+With this module, you can define and register Perl callbacks with the Milter
+engine. This module calls your perl callbacks using interpreters from a
+threaded persistent interpreter pool. Milter contexts are presented using an
+object-oriented style interface for performing operations on a Milter context.
+
+The main project web page for this module is:
+
+ http://sourceforge.net/projects/sendmail-milter/
+
+
+Prerequisites
+-------------
+
+Sendmail::Milter has been tested with the following:
+
+ sendmail 8.12.1 built with -DMILTER
+ perl 5.6.1 built with -Dusethreads
+
+You can find the latest version of sendmail from:
+
+ ftp://ftp.sendmail.org/pub/sendmail/
+
+You can try this module out with newer versions of Perl, hopefully interpreter
+threads support will come out of its experimental state in the future.
+
+You'll also need to have an operating system with a viable POSIX threads
+implementation.
+
+This module has only been tested on FreeBSD 4.0-RELEASE. Your mileage may vary.
+
+Sendmail::Milter uses the new perl_clone() call in 5.6.0 to make copies of the
+Perl interpreter for its interpreter pools (see intpools.c and intpools.h). See
+the perldelta manpage for more information on this feature.
+
+
+Before You Begin
+----------------
+
+Read the libmilter/README file that comes with the sendmail source
+distribution to find out how to build sendmail with the Mail Filter API.
+
+
+Building Sendmail::Milter
+-------------------------
+
+Begin by building sendmail, libmilter, and perl with -Dusethreads. Next,
+perform the following commands:
+
+% perl Makefile.PL ../sendmail ../sendmail/obj.FreeBSD.4.0-RELEASE.i386
+% make
+% make install
+
+The paths ../sendmail and ../sendmail/obj.FreeBSD.4.0-RELEASE.i386 should point
+to the sendmail source tree and the sendmail build directory, respectively.
+
+
+Using Sendmail::Milter
+----------------------
+
+See the pod documentation for complete information on writing your own mail
+filters with this module.
+
+
+Testing the sample sample.pl mail filter
+----------------------------------------
+
+sample.pl, a sample test case has been provided. You can run it by using the
+following command:
+
+% perl sample.pl myfilter /etc/mail/sendmail.cf
+
+But before you do that, add a line similar to:
+
+INPUT_MAIL_FILTER(`myfilter', `S=local:/var/run/perl.sock')dnl
+
+to your .mc file. sample.pl isn't terribly interesting, but should give you a
+good feel for how mail filters are written with Sendmail::Milter.
+
+
+Mailing List
+------------
+
+You can subscribe to the sendmail-milter-users at lists.sourceforge.net mailing
+list. Instructions on how to do so can be found off the Sendmail::Milter
+project page.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..0241c77
--- /dev/null
+++ b/TODO
@@ -0,0 +1,10 @@
+TODO
+----
+o Init several interpreters at startup.
+
+o Interpreter pool manager that cleans up the number of interpreters back down
+ to the minimum if the system is idle.
+
+o Forking interpreters with IPC instead of threaded. (Since perlthreads are
+ becoming more stable, this should become less relevant down the road)
+
diff --git a/callbacks.c b/callbacks.c
new file mode 100644
index 0000000..5980829
--- /dev/null
+++ b/callbacks.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 2000 Charles Ying. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the same terms as sendmail itself.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include <pthread.h>
+
+#include "intpools.h"
+
+#include "libmilter/mfapi.h"
+
+/* Keys for each callback for the register callback hash */
+
+#define KEY_CONNECT newSVpv("connect", 0)
+#define KEY_HELO newSVpv("helo", 0)
+#define KEY_ENVFROM newSVpv("envfrom", 0)
+#define KEY_ENVRCPT newSVpv("envrcpt", 0)
+#define KEY_HEADER newSVpv("header", 0)
+#define KEY_EOH newSVpv("eoh", 0)
+#define KEY_BODY newSVpv("body", 0)
+#define KEY_EOM newSVpv("eom", 0)
+#define KEY_ABORT newSVpv("abort", 0)
+#define KEY_CLOSE newSVpv("close", 0)
+
+/* Macro for pushing the SMFICTX * argument */
+
+#define XPUSHs_Sendmail_Milter_Context \
+ (XPUSHs(sv_2mortal(sv_setref_iv(NEWSV(25, 0), \
+ "Sendmail::Milter::Context", (IV) ctx))))
+
+/* Global callback variable names */
+
+#define GLOBAL_CONNECT "Sendmail::Milter::Callbacks::_xxfi_connect"
+#define GLOBAL_HELO "Sendmail::Milter::Callbacks::_xxfi_helo"
+#define GLOBAL_ENVFROM "Sendmail::Milter::Callbacks::_xxfi_envfrom"
+#define GLOBAL_ENVRCPT "Sendmail::Milter::Callbacks::_xxfi_envrcpt"
+#define GLOBAL_HEADER "Sendmail::Milter::Callbacks::_xxfi_header"
+#define GLOBAL_EOH "Sendmail::Milter::Callbacks::_xxfi_eoh"
+#define GLOBAL_BODY "Sendmail::Milter::Callbacks::_xxfi_body"
+#define GLOBAL_EOM "Sendmail::Milter::Callbacks::_xxfi_eom"
+#define GLOBAL_ABORT "Sendmail::Milter::Callbacks::_xxfi_abort"
+#define GLOBAL_CLOSE "Sendmail::Milter::Callbacks::_xxfi_close"
+
+
+/* Callback prototypes for first-level callback wrappers. */
+
+sfsistat hook_connect(SMFICTX *, char *, _SOCK_ADDR *);
+sfsistat hook_helo(SMFICTX *, char *);
+sfsistat hook_envfrom(SMFICTX *, char **);
+sfsistat hook_envrcpt(SMFICTX *, char **);
+sfsistat hook_header(SMFICTX *, char *, char *);
+sfsistat hook_eoh(SMFICTX *);
+sfsistat hook_body(SMFICTX *, u_char *, size_t);
+sfsistat hook_eom(SMFICTX *);
+sfsistat hook_abort(SMFICTX *);
+sfsistat hook_close(SMFICTX *);
+
+
+/* A structure for housing callbacks and their mutexes. */
+
+struct callback_cache_t
+{
+ SV *xxfi_connect;
+ SV *xxfi_helo;
+ SV *xxfi_envfrom;
+ SV *xxfi_envrcpt;
+ SV *xxfi_header;
+ SV *xxfi_eoh;
+ SV *xxfi_body;
+ SV *xxfi_eom;
+ SV *xxfi_abort;
+ SV *xxfi_close;
+};
+
+typedef struct callback_cache_t callback_cache_t;
+
+
+/* The Milter perl interpreter pool */
+
+static intpool_t I_pool;
+
+
+/* Routines for managing callback caches */
+
+void
+init_callback_cache(pTHX_ interp_t *interp)
+{
+ callback_cache_t *cache_ptr;
+
+ if (interp->cache != NULL)
+ return;
+
+ alloc_interpreter_cache(interp, sizeof(callback_cache_t));
+
+ cache_ptr = (callback_cache_t *)interp->cache;
+
+ cache_ptr->xxfi_connect = get_sv(GLOBAL_CONNECT, FALSE);
+ cache_ptr->xxfi_helo = get_sv(GLOBAL_HELO, FALSE);
+ cache_ptr->xxfi_envfrom = get_sv(GLOBAL_ENVFROM, FALSE);
+ cache_ptr->xxfi_envrcpt = get_sv(GLOBAL_ENVRCPT, FALSE);
+ cache_ptr->xxfi_header = get_sv(GLOBAL_HEADER, FALSE);
+ cache_ptr->xxfi_eoh = get_sv(GLOBAL_EOH, FALSE);
+ cache_ptr->xxfi_body = get_sv(GLOBAL_BODY, FALSE);
+ cache_ptr->xxfi_eom = get_sv(GLOBAL_EOM, FALSE);
+ cache_ptr->xxfi_abort = get_sv(GLOBAL_ABORT, FALSE);
+ cache_ptr->xxfi_close = get_sv(GLOBAL_CLOSE, FALSE);
+}
+
+
+/* Set global variables in the parent interpreter. */
+
+void
+init_callback(char *var_name, SV *parent_callback)
+{
+ SV *new_sv;
+
+ new_sv = get_sv(var_name, TRUE);
+ sv_setsv(new_sv, parent_callback);
+}
+
+
+/* Main interfaces. */
+
+void
+init_callbacks(max_interpreters, max_requests)
+ int max_interpreters;
+ int max_requests;
+{
+ init_interpreters(&I_pool, max_interpreters, max_requests);
+}
+
+
+SV *
+get_callback(perl_desc, key)
+ HV *perl_desc;
+ SV *key;
+{
+ HE *entry;
+
+ entry = hv_fetch_ent(perl_desc, key, 0, 0);
+
+ if (entry == NULL)
+ croak("couldn't fetch callback symbol from descriptor.");
+
+ return newSVsv(HeVAL(entry));
+}
+
+
+void
+register_callbacks(desc, name, my_callback_table, flags)
+ struct smfiDesc *desc;
+ char *name;
+ HV *my_callback_table;
+ int flags;
+{
+ memset(desc, '\0', sizeof(struct smfiDesc));
+
+ desc->xxfi_name = strdup(name);
+ desc->xxfi_version = SMFI_VERSION;
+ desc->xxfi_flags = flags;
+
+ if (hv_exists_ent(my_callback_table, KEY_CONNECT, 0))
+ {
+ init_callback(GLOBAL_CONNECT,
+ get_callback(my_callback_table, KEY_CONNECT));
+
+ desc->xxfi_connect = hook_connect;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_HELO, 0))
+ {
+ init_callback(GLOBAL_HELO,
+ get_callback(my_callback_table, KEY_HELO));
+
+ desc->xxfi_helo = hook_helo;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_ENVFROM, 0))
+ {
+ init_callback(GLOBAL_ENVFROM,
+ get_callback(my_callback_table, KEY_ENVFROM));
+
+ desc->xxfi_envfrom = hook_envfrom;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_ENVRCPT, 0))
+ {
+ init_callback(GLOBAL_ENVRCPT,
+ get_callback(my_callback_table, KEY_ENVRCPT));
+
+ desc->xxfi_envrcpt = hook_envrcpt;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_HEADER, 0))
+ {
+ init_callback(GLOBAL_HEADER,
+ get_callback(my_callback_table, KEY_HEADER));
+
+ desc->xxfi_header = hook_header;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_EOH, 0))
+ {
+ init_callback(GLOBAL_EOH,
+ get_callback(my_callback_table, KEY_EOH));
+
+ desc->xxfi_eoh = hook_eoh;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_BODY, 0))
+ {
+ init_callback(GLOBAL_BODY,
+ get_callback(my_callback_table, KEY_BODY));
+
+ desc->xxfi_body = hook_body;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_EOM, 0))
+ {
+ init_callback(GLOBAL_EOM,
+ get_callback(my_callback_table, KEY_EOM));
+
+ desc->xxfi_eom = hook_eom;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_ABORT, 0))
+ {
+ init_callback(GLOBAL_ABORT,
+ get_callback(my_callback_table, KEY_ABORT));
+
+ desc->xxfi_abort = hook_abort;
+ }
+
+ if (hv_exists_ent(my_callback_table, KEY_CLOSE, 0))
+ {
+ init_callback(GLOBAL_CLOSE,
+ get_callback(my_callback_table, KEY_CLOSE));
+
+ desc->xxfi_close = hook_close;
+ }
+}
+
+
+/* Second-layer callbacks. These do the actual work. */
+
+sfsistat
+callback_noargs(pTHX_ SV *callback, SMFICTX *ctx)
+{
+ int n;
+ sfsistat retval;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs_Sendmail_Milter_Context;
+
+ PUTBACK;
+
+ n = call_sv(callback, G_EVAL | G_SCALAR);
+
+ SPAGAIN;
+
+ /* Check the eval first. */
+ if (SvTRUE(ERRSV))
+ {
+ POPs;
+ retval = SMFIS_TEMPFAIL;
+ }
+ else if (n == 1)
+ {
+ retval = (sfsistat) POPi;
+ }
+ else
+ {
+ retval = SMFIS_CONTINUE;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return retval;
+}
+
+sfsistat
+callback_s(pTHX_ SV *callback, SMFICTX *ctx, char *arg1)
+{
+ int n;
+ sfsistat retval;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs_Sendmail_Milter_Context;
+ XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
+
+ PUTBACK;
+
+ n = call_sv(callback, G_EVAL | G_SCALAR);
+
+ SPAGAIN;
+
+ /* Check the eval first. */
+ if (SvTRUE(ERRSV))
+ {
+ POPs;
+ retval = SMFIS_TEMPFAIL;
+ }
+ else if (n == 1)
+ {
+ retval = (sfsistat) POPi;
+ }
+ else
+ {
+ retval = SMFIS_CONTINUE;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return retval;
+}
+
+sfsistat
+callback_body(pTHX_ SV *callback, SMFICTX *ctx,
+ u_char *arg1, size_t arg2)
+{
+ int n;
+ sfsistat retval;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs_Sendmail_Milter_Context;
+ XPUSHs(sv_2mortal(newSVpvn(arg1, arg2)));
+ XPUSHs(sv_2mortal(newSViv((IV) arg2)));
+
+ PUTBACK;
+
+ n = call_sv(callback, G_EVAL | G_SCALAR);
+
+ SPAGAIN;
+
+ /* Check the eval first. */
+ if (SvTRUE(ERRSV))
+ {
+ POPs;
+ retval = SMFIS_TEMPFAIL;
+ }
+ else if (n == 1)
+ {
+ retval = (sfsistat) POPi;
+ }
+ else
+ {
+ retval = SMFIS_CONTINUE;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return retval;
+}
+
+sfsistat
+callback_argv(pTHX_ SV *callback, SMFICTX *ctx, char **arg1)
+{
+ int n;
+ sfsistat retval;
+ char **iter = arg1;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs_Sendmail_Milter_Context;
+
+ while(iter != NULL)
+ {
+ if (*iter == NULL)
+ break;
+
+ XPUSHs(sv_2mortal(newSVpv(*iter, 0)));
+ iter++;
+ }
+
+ PUTBACK;
+
+ n = call_sv(callback, G_EVAL | G_SCALAR);
+
+ SPAGAIN;
+
+ /* Check the eval first. */
+ if (SvTRUE(ERRSV))
+ {
+ POPs;
+ retval = SMFIS_TEMPFAIL;
+ }
+ else if (n == 1)
+ {
+ retval = (sfsistat) POPi;
+ }
+ else
+ {
+ retval = SMFIS_CONTINUE;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return retval;
+}
+
+sfsistat
+callback_ss(pTHX_ SV *callback, SMFICTX *ctx, char *arg1, char *arg2)
+{
+ int n;
+ sfsistat retval;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs_Sendmail_Milter_Context;
+ XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
+ XPUSHs(sv_2mortal(newSVpv(arg2, 0)));
+
+ PUTBACK;
+
+ n = call_sv(callback, G_EVAL | G_SCALAR);
+
+ SPAGAIN;
+
+ /* Check the eval first. */
+ if (SvTRUE(ERRSV))
+ {
+ POPs;
+ retval = SMFIS_TEMPFAIL;
+ }
+ else if (n == 1)
+ {
+ retval = (sfsistat) POPi;
+ }
+ else
+ {
+ retval = SMFIS_CONTINUE;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return retval;
+}
+
+sfsistat
+callback_ssockaddr(pTHX_ SV *callback, SMFICTX *ctx, char *arg1,
+ _SOCK_ADDR *arg_sa)
+{
+ int n;
+ sfsistat retval;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs_Sendmail_Milter_Context;
+
+ XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
+
+ /* A Perl sockaddr_in is all we handle right now. */
+ if (arg_sa == NULL)
+ {
+ XPUSHs(sv_2mortal(newSVsv(&PL_sv_undef)));
+ }
+ else if (arg_sa->sa_family == AF_INET)
+ {
+ XPUSHs(sv_2mortal(newSVpvn((char *)arg_sa,
+ sizeof(_SOCK_ADDR))));
+ }
+ else
+ {
+ XPUSHs(sv_2mortal(newSVsv(&PL_sv_undef)));
+ }
+
+ PUTBACK;
+
+ n = call_sv(callback, G_EVAL | G_SCALAR);
+
+ SPAGAIN;
+
+ /* Check the eval first. */
+ if (SvTRUE(ERRSV))
+ {
+ POPs;
+ retval = SMFIS_TEMPFAIL;
+ }
+ else if (n == 1)
+ {
+ retval = (sfsistat) POPi;
+ }
+ else
+ {
+ retval = SMFIS_CONTINUE;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return retval;
+}
+
+
+/* First-layer callbacks */
+
+sfsistat
+hook_connect(ctx, hostname, hostaddr)
+ SMFICTX *ctx;
+ char *hostname;
+ _SOCK_ADDR *hostaddr;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_connect;
+
+ retval = callback_ssockaddr(aTHX_ callback, ctx,
+ hostname, hostaddr);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_helo(ctx, helohost)
+ SMFICTX *ctx;
+ char *helohost;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_helo;
+
+ retval = callback_s(aTHX_ callback, ctx, helohost);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_envfrom(ctx, argv)
+ SMFICTX *ctx;
+ char **argv;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_envfrom;
+
+ retval = callback_argv(aTHX_ callback, ctx, argv);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_envrcpt(ctx, argv)
+ SMFICTX *ctx;
+ char **argv;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_envrcpt;
+
+ retval = callback_argv(aTHX_ callback, ctx, argv);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_header(ctx, headerf, headerv)
+ SMFICTX *ctx;
+ char *headerf;
+ char *headerv;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_header;
+
+ retval = callback_ss(aTHX_ callback, ctx, headerf, headerv);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_eoh(ctx)
+ SMFICTX *ctx;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_eoh;
+
+ retval = callback_noargs(aTHX_ callback, ctx);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_body(ctx, bodyp, bodylen)
+ SMFICTX *ctx;
+ u_char *bodyp;
+ size_t bodylen;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_body;
+
+ retval = callback_body(aTHX_ callback, ctx, bodyp, bodylen);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_eom(ctx)
+ SMFICTX *ctx;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_eom;
+
+ retval = callback_noargs(aTHX_ callback, ctx);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_abort(ctx)
+ SMFICTX *ctx;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_abort;
+
+ retval = callback_noargs(aTHX_ callback, ctx);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
+sfsistat
+hook_close(ctx)
+ SMFICTX *ctx;
+{
+ interp_t *interp;
+ sfsistat retval;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&I_pool)) == NULL)
+ croak("could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ init_callback_cache(aTHX_ interp);
+ callback = ((callback_cache_t *)(interp->cache))->xxfi_close;
+
+ retval = callback_noargs(aTHX_ callback, ctx);
+
+ unlock_interpreter(&I_pool, interp);
+
+ return retval;
+}
+
diff --git a/callbacks.h b/callbacks.h
new file mode 100644
index 0000000..1caf521
--- /dev/null
+++ b/callbacks.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2000 Charles Ying. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the same terms as sendmail itself.
+ *
+ */
+
+#ifndef __CALLBACKS_H_
+#define __CALLBACKS_H_
+
+extern void init_callbacks(int, int);
+extern void register_callbacks(struct smfiDesc *, char *, HV *, int);
+
+#endif /* __CALLBACKS_H_ */
diff --git a/intpools.c b/intpools.c
new file mode 100644
index 0000000..72677dc
--- /dev/null
+++ b/intpools.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2000 Charles Ying. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the same terms as perl itself.
+ *
+ * Please note that this code falls under a different license than the
+ * other code found in Sendmail::Milter.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include <pthread.h>
+
+#include "intpools.h"
+
+/*
+** INIT_INTERPRETERS -- initialize the interpreter pool
+**
+** Parameters:
+** ipool -- interpreter pool
+** max_interp -- the maximum limit on interpreters allowed.
+** max_requests -- the maximum limit on requests perinterpreter.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets up the global variables for the interpreter pool.
+*/
+
+void
+init_interpreters(ipool, max_interp, max_requests)
+ intpool_t *ipool;
+ int max_interp;
+ int max_requests;
+{
+ int error;
+
+ memset(ipool, 0, sizeof(intpool_t));
+
+ /* Initialize the mutex */
+ if ((error = pthread_mutex_init(&(ipool->ip_mutex), NULL)) != 0)
+ croak("intpool pthread_mutex_init failed: %d", error);
+
+ /* Initialize the condition variable */
+ if ((error = pthread_cond_init(&(ipool->ip_cond), NULL)) != 0)
+ croak("intpool pthread_cond_init() failed: %d", error);
+
+ /* Lock interpreter table */
+ if ((error = pthread_mutex_lock(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_lock() failed: %d", error);
+
+ /* Critical section */
+
+ /* Initialize the max number of interpreters */
+ ipool->ip_max = max_interp;
+ ipool->ip_retire = max_requests;
+
+ /* Initialize the free table */
+ ipool->ip_freequeue = (AV*) newAV();
+
+ /* Set the number of busy interpreters to zero. */
+ ipool->ip_busycount = 0;
+
+ /* This is the global interpreter that thread wrappers will clone .*/
+ ipool->ip_parent = PERL_GET_CONTEXT;
+
+ /* End critical section */
+
+ /* Unlock interpreter table */
+ if ((error = pthread_mutex_unlock(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_unlock() failed: %d", error);
+}
+
+
+/*
+** ALLOC_INTERPRETER_CACHE -- Allocate memory for interpreter cache.
+**
+** Parameters:
+** interp -- Interpreter to allocate cache for.
+** size -- Size of cache to allocate.
+**
+** Returns:
+** none.
+**
+** Warning:
+** This routine is not thread-safe.
+*/
+
+void
+alloc_interpreter_cache(interp_t *interp, size_t size)
+{
+ if ((interp->cache = malloc(size)) == NULL)
+ croak("failed to allocate memory for interpreter cache.");
+}
+
+/*
+** FREE_INTERPRETER_CACHE -- Free memory used by interpreter cache.
+**
+** Parameters:
+** interp -- Interpreter to free cache for.
+**
+** Returns:
+** none.
+**
+** Warning:
+** This routine is not thread-safe.
+*/
+
+void
+free_interpreter_cache(interp_t *interp)
+{
+ free(interp->cache);
+ interp->cache = NULL;
+}
+
+
+/*
+** CREATE_INTERPRETER -- create an interpreter from the parent.
+**
+** Parameters:
+** ipool -- interpreter pool
+**
+** Returns:
+** An interpreter context cloned off the parent.
+**
+** Warning:
+** This routine is not thread-safe.
+*/
+
+interp_t *
+create_interpreter(ipool)
+ intpool_t *ipool;
+{
+ interp_t *new_interp;
+
+ /* Clone the reference interpreter and use that. */
+ new_interp = (interp_t *) malloc(sizeof(interp_t));
+
+ new_interp->perl = perl_clone(ipool->ip_parent, FALSE);
+ new_interp->requests = 1;
+ new_interp->cache = NULL;
+
+ {
+ /* Hack from modperl until Perl 5.6.1 */
+ dTHXa(new_interp->perl);
+ if (PL_scopestack_ix == 0)
+ {
+ /* ENTER could expand. A lot. */
+ ENTER;
+ }
+ }
+
+ /* Restore the parent interpreter after a perl_clone() */
+ PERL_SET_CONTEXT(ipool->ip_parent);
+
+ return new_interp;
+}
+
+
+/*
+** CLEANUP_INTERPRETER -- destroy an interpreter
+**
+** Parameters:
+** ipool -- interpreter pool
+** del_interp - the interp_t to destroy.
+**
+** Returns:
+** none.
+**
+** Warning:
+** This routine is not thread-safe.
+*/
+
+void
+cleanup_interpreter(ipool, del_interp)
+ intpool_t *ipool;
+ interp_t *del_interp;
+{
+ perl_destruct(del_interp->perl);
+ perl_free(del_interp->perl);
+
+ free_interpreter_cache(del_interp);
+
+ free(del_interp);
+}
+
+
+/*
+** LOCK_INTERPRETER -- lock and retrieve a perl interpreter
+**
+** Parameters:
+** ipool -- interpreter pool
+**
+** Returns:
+** An interpreter context out of the interpreter pool.
+**
+** Side Effects:
+** The caller has exclusive rights to the interpreter
+** until the caller unlocks the interpreter.
+**
+** Warning:
+** This routine will block until a free interpreter
+** is available.
+**
+** (A timeout might be implemented in the future)
+*/
+
+interp_t *
+lock_interpreter(ipool)
+ intpool_t *ipool;
+{
+ int error;
+ SV *sv_value;
+ interp_t *new_interp;
+
+ /* Lock interpreter table */
+ if ((error = pthread_mutex_lock(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_lock() failed: %d", error);
+
+ /* Critical section */
+
+ /*
+ ** Predicate: Any available interpreters? (Free or createable)
+ **
+ ** ASSERT: ipool->ip_busycount always contains the number of
+ ** interpreters that are locked in the system.
+ */
+
+ while ( !((ipool->ip_max == 0) ||
+ (ipool->ip_busycount < ipool->ip_max)) )
+ {
+ /* No. */
+
+ /* P(): Lock on the condition variable. */
+ if ((error = pthread_cond_wait( &(ipool->ip_cond),
+ &(ipool->ip_mutex) )) != 0)
+ {
+ croak("cond_wait failed waiting for interpreter: %d",
+ error);
+ }
+
+ /* When we wake up again, we might get a new interpreter. */
+ }
+
+ /* Restore the parent interpreter context */
+ PERL_SET_CONTEXT(ipool->ip_parent);
+
+ /* Any free interpreters on the queue? */
+ if (av_len(ipool->ip_freequeue) != -1)
+ {
+ /* Reuse an old interpreter */
+ sv_value = av_shift(ipool->ip_freequeue);
+
+ new_interp = (interp_t *) SvIV(sv_value);
+
+ /* Decrement the reference count. */
+ (void) SvREFCNT_dec(sv_value);
+
+ /* Increase the number of requests. */
+ new_interp->requests++;
+
+ /* Increment the number of busy interpreters */
+ ipool->ip_busycount++;
+ }
+ else /* No, there aren't, but we can still create one. */
+ {
+ new_interp = create_interpreter(ipool);
+
+ /* Increment the number of busy interpreters */
+ ipool->ip_busycount++;
+ }
+
+ /* End critical section */
+
+ /* Restore the parent interpreter context. */
+ PERL_SET_CONTEXT(ipool->ip_parent);
+
+ /* Unlock interpreter table */
+ if ((error = pthread_mutex_unlock(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_unlock() failed: %d", error);
+
+ return new_interp;
+}
+
+
+/*
+** UNLOCK_INTERPRETER -- unlock a perl interpreter
+**
+** Parameters:
+** ipool -- interpreter pool
+** busy_interp -- the interpreter context to unlock.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** The interpreter is placed back in the interpreter pool
+** and the caller should immediately discard its pointer
+** to the interpreter.
+*/
+
+void
+unlock_interpreter(ipool, busy_interp)
+ intpool_t *ipool;
+ interp_t *busy_interp;
+{
+ int error;
+
+ /* Lock interpreter table */
+ if ((error = pthread_mutex_lock(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_lock() failed: %d", error);
+
+ /* Critical section */
+
+ /* Restore the parent interpreter context. */
+ PERL_SET_CONTEXT(ipool->ip_parent);
+
+ /* ASSERT(ipool->ip_busycount > 0)
+ if (ipool->ip_busycount <= 0)
+ croak("internal error: busy_count reached zero unexpectedly.");
+
+ /* Decrement the number of busy interpreters */
+ ipool->ip_busycount--;
+
+ if ((ipool->ip_retire != 0) &&
+ (busy_interp->requests > ipool->ip_retire))
+ {
+ /* Interpreter is too old, recycle it. */
+ cleanup_interpreter(ipool, busy_interp);
+
+ busy_interp = create_interpreter(ipool);
+ }
+
+ /* Stick busy_interp in the free table */
+ (void) av_push(ipool->ip_freequeue, newSViv((IV) busy_interp));
+
+ /* V(): Signal a thread that a new interpreter is available. */
+ if ((error = pthread_cond_signal(&(ipool->ip_cond))) != 0)
+ {
+ croak("cond_signal failed to signal a free interpreter: %d",
+ error);
+ }
+
+ /* Restore the parent interpreter context. */
+ PERL_SET_CONTEXT(ipool->ip_parent);
+
+ /* End critical section */
+
+ /* Unlock interpreter table */
+ if ((error = pthread_mutex_unlock(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_unlock() failed: %d", error);
+}
+
+
+/*
+** CLEANUP_INTERPRETERS -- clean up the interpreter pool
+**
+** Parameters:
+** ipool -- interpreter pool
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Shuts down and cleans up the interpreter pool.
+**
+** Warning:
+** All interpreters should be unlocked before
+** calling this routine.
+*/
+
+void
+cleanup_interpreters(ipool)
+ intpool_t *ipool;
+{
+ int error;
+ SV *sv_value;
+ interp_t *del_interp;
+
+ /* Lock interpreter table */
+ if ((error = pthread_mutex_lock(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_lock() failed: %d", error);
+
+ /* Critical section */
+
+ /* Restore the original interpreter context. */
+ PERL_SET_CONTEXT(ipool->ip_parent);
+
+ /* At some point, we really should V() all of the waiting threads. */
+ while (av_len(ipool->ip_freequeue) != -1)
+ {
+ /* Reuse an old interpreter */
+ sv_value = av_shift(ipool->ip_freequeue);
+
+ del_interp = (interp_t *) SvIV(sv_value);
+
+ /* Decrement the reference count. */
+ (void) SvREFCNT_dec(sv_value);
+
+ cleanup_interpreter(ipool, del_interp);
+ }
+
+ av_undef(ipool->ip_freequeue);
+ ipool->ip_freequeue = NULL;
+
+ /* Restore the original interpreter context. */
+ PERL_SET_CONTEXT(ipool->ip_parent);
+
+ /* End critical section */
+
+ /* Unlock interpreter table */
+ if ((error = pthread_mutex_unlock(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_unlock() failed: %d", error);
+
+ /* Destroy the condition variable */
+ if ((error = pthread_cond_destroy(&(ipool->ip_cond))) != 0)
+ croak("intpool pthread_cond_destroy() failed: %d", error);
+
+ /* Destroy the intpool mutex */
+ if ((error = pthread_mutex_destroy(&(ipool->ip_mutex))) != 0)
+ croak("intpool pthread_mutex_destroy() failed: %d", error);
+}
+
+
+/* ---+ Interpreter pools test code. -------------------------------------- */
+
+typedef void *(*test_callback_ptr)(void *);
+
+static intpool_t T_pool;
+
+#define GLOBAL_TEST "Sendmail::Milter::Callbacks::_test_callback"
+
+void
+test_run_callback(pTHX_ SV *callback)
+{
+ int error;
+
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+
+ XPUSHs(sv_2mortal(newSViv((IV) aTHX)));
+
+ PUTBACK;
+
+ printf("test_wrapper: Analysing callback...\n");
+
+ if (SvROK(callback) && (SvTYPE(SvRV(callback)) == SVt_PVCV))
+ {
+ printf("test_wrapper: It's a code reference to: 0x%08x\n",
+ SvRV(callback));
+ }
+
+ if (SvPOK(callback))
+ {
+ int len;
+ printf("test_wrapper: pointer to string... string is '%s'\n",
+ SvPV(callback, len));
+ }
+
+ printf("test_wrapper: Calling callback 0x%08x from aTHX 0x%08x.\n",
+ callback, aTHX);
+
+ call_sv(callback, G_DISCARD);
+
+ SPAGAIN;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+}
+
+void *
+test_callback_wrapper(void *arg)
+{
+ interp_t *interp;
+ SV *callback;
+
+ if ((interp = lock_interpreter(&T_pool)) == NULL)
+ croak("test_wrapper: could not lock a new perl interpreter.");
+
+ PERL_SET_CONTEXT(interp->perl);
+
+ callback = get_sv(GLOBAL_TEST, FALSE);
+
+ test_run_callback(aTHX_ callback);
+
+ unlock_interpreter(&T_pool, interp);
+
+ return NULL;
+}
+
+int
+test_intpools(pTHX_ int max_interp, int max_requests, int i_max, int j_max,
+ SV* callback)
+{
+ int i;
+ int j;
+ pthread_t thread_id;
+ SV *global_callback;
+
+ printf("test_wrapper: Original interpreter cloned: 0x%08x\n", aTHX);
+
+ init_interpreters(&T_pool, max_interp, max_requests);
+
+ global_callback = get_sv(GLOBAL_TEST, TRUE);
+
+ sv_setsv(global_callback, callback);
+
+ for (i = 0; i < i_max; i++)
+ {
+ for (j = 0; j < j_max; j++)
+ pthread_create(&thread_id, NULL,
+ (test_callback_ptr) test_callback_wrapper,
+ (void *)NULL);
+
+ pthread_join(thread_id, NULL);
+ }
+
+ cleanup_interpreters(&T_pool);
+
+ return 1;
+}
diff --git a/intpools.h b/intpools.h
new file mode 100644
index 0000000..79015e8
--- /dev/null
+++ b/intpools.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2000 Charles Ying. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the same terms as perl itself.
+ *
+ * Please note that this code falls under a different license than the
+ * other code found in Sendmail::Milter.
+ *
+ */
+
+#ifndef __INTPOOLS_H_
+#define __INTPOOLS_H_
+
+struct interp_t
+{
+ PerlInterpreter *perl;
+ void *cache;
+ int requests;
+};
+
+typedef struct interp_t interp_t;
+
+struct intpool_t
+{
+ pthread_mutex_t ip_mutex;
+ pthread_cond_t ip_cond;
+
+ PerlInterpreter *ip_parent;
+
+ int ip_max;
+ int ip_retire;
+
+ int ip_busycount;
+
+ AV* ip_freequeue;
+};
+
+typedef struct intpool_t intpool_t;
+
+
+extern void init_interpreters(intpool_t *, int, int);
+extern void cleanup_interpreters(intpool_t *);
+
+extern interp_t *lock_interpreter(intpool_t *);
+extern void unlock_interpreter(intpool_t *, interp_t *);
+
+extern interp_t *create_interpreter(intpool_t *);
+extern void cleanup_interpreter(intpool_t *, interp_t *);
+
+extern void alloc_interpreter_cache(interp_t *interp, size_t size);
+extern void free_interpreter_cache(interp_t *interp);
+
+extern int test_intpools(pTHX_ int, int, int, int, SV*);
+
+#endif /* __INTPOOLS_H_ */
+
diff --git a/sample.pl b/sample.pl
new file mode 100644
index 0000000..7385d3d
--- /dev/null
+++ b/sample.pl
@@ -0,0 +1,258 @@
+use ExtUtils::testlib;
+
+use Sendmail::Milter;
+use Socket;
+
+#
+# Each of these callbacks is actually called with a first argument
+# that is blessed into the pseudo-package Sendmail::Milter::Context. You can
+# use them like object methods of package Sendmail::Milter::Context.
+#
+# $ctx is a blessed reference of package Sendmail::Milter::Context to something
+# yucky, but the Mail Filter API routines are available as object methods
+# (sans the smfi_ prefix) from this
+#
+
+sub connect_callback
+{
+ my $ctx = shift; # Some people think of this as $self
+ my $hostname = shift;
+ my $sockaddr_in = shift;
+ my ($port, $iaddr);
+
+ print "my_connect:\n";
+ print " + hostname: '$hostname'\n";
+
+ if (defined $sockaddr_in)
+ {
+ ($port, $iaddr) = sockaddr_in($sockaddr_in);
+ print " + port: '$port'\n";
+ print " + iaddr: '" . inet_ntoa($iaddr) . "'\n";
+ }
+
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub helo_callback
+{
+ my $ctx = shift;
+ my $helohost = shift;
+
+ print "my_helo:\n";
+ print " + helohost: '$helohost'\n";
+
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub envfrom_callback
+{
+ my $ctx = shift;
+ my @args = @_;
+ my $message = "";
+
+ print "my_envfrom:\n";
+ print " + args: '" . join(', ', @args) . "'\n";
+
+ $ctx->setpriv(\$message);
+ print " + private data allocated.\n";
+
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub envrcpt_callback
+{
+ my $ctx = shift;
+ my @args = @_;
+
+ print "my_envrcpt:\n";
+ print " + args: '" . join(', ', @args) . "'\n";
+
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub header_callback
+{
+ my $ctx = shift;
+ my $headerf = shift;
+ my $headerv = shift;
+
+ print "my_header:\n";
+ print " + field: '$headerf'\n";
+ print " + value: '$headerv'\n";
+
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub eoh_callback
+{
+ my $ctx = shift;
+
+ print "my_eoh:\n";
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub body_callback
+{
+ my $ctx = shift;
+ my $body_chunk = shift;
+ my $len = shift;
+ my $message_ref = $ctx->getpriv();
+
+ # Note: You don't need $len to have a good time.
+ # But it's there if you like.
+
+ print "my_body:\n";
+ print " + chunk len: $len\n";
+
+ ${$message_ref} .= $body_chunk;
+
+ $ctx->setpriv($message_ref);
+
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub eom_callback
+{
+ my $ctx = shift;
+ my $message_ref = $ctx->getpriv();
+ my $chunk;
+
+ print "my_eom:\n";
+ print " + adding line to message body...\n";
+
+ # Let's have some fun...
+ # Note: This doesn't support messages with MIME data.
+
+ # Pig-Latin, Babelfish, Double dutch, soo many possibilities!
+ # But we're boring...
+
+ ${$message_ref} .= "---> Append me to this message body!\r\n";
+
+ if (not $ctx->replacebody(${$message_ref}))
+ {
+ print " - write error!\n";
+ last;
+ }
+
+ $ctx->setpriv(undef);
+ print " + private data cleared.\n";
+
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub abort_callback
+{
+ my $ctx = shift;
+
+ print "my_abort:\n";
+
+ $ctx->setpriv(undef);
+ print " + private data cleared.\n";
+
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+sub close_callback
+{
+ my $ctx = shift;
+
+ print "my_close:\n";
+ print " + callback completed.\n";
+
+ return SMFIS_CONTINUE;
+}
+
+my %my_callbacks =
+(
+ 'connect' => \&connect_callback,
+ 'helo' => \&helo_callback,
+ 'envfrom' => \&envfrom_callback,
+ 'envrcpt' => \&envrcpt_callback,
+ 'header' => \&header_callback,
+ 'eoh' => \&eoh_callback,
+ 'body' => \&body_callback,
+ 'eom' => \&eom_callback,
+ 'abort' => \&abort_callback,
+ 'close' => \&close_callback,
+);
+
+BEGIN:
+{
+ if (scalar(@ARGV) < 2)
+ {
+ print "Usage: perl $0 <name_of_filter> <path_to_sendmail.cf>\n";
+ exit;
+ }
+
+ my $conn = Sendmail::Milter::auto_getconn($ARGV[0], $ARGV[1]);
+
+ print "Found connection info for '$ARGV[0]': $conn\n";
+
+ if ($conn =~ /^local:(.+)$/)
+ {
+ my $unix_socket = $1;
+
+ if (-e $unix_socket)
+ {
+ print "Attempting to unlink UNIX socket '$conn' ... ";
+
+ if (unlink($unix_socket) == 0)
+ {
+ print "failed.\n";
+ exit;
+ }
+ print "successful.\n";
+ }
+ }
+
+ if (not Sendmail::Milter::auto_setconn($ARGV[0], $ARGV[1]))
+ {
+ print "Failed to detect connection information.\n";
+ exit;
+ }
+
+ #
+ # The flags parameter is optional. SMFI_CURR_ACTS sets all of the
+ # current version's filtering capabilities.
+ #
+ # %Sendmail::Milter::DEFAULT_CALLBACKS is provided for you in getting
+ # up to speed quickly. I highly recommend creating a callback table
+ # of your own with only the callbacks that you need.
+ #
+
+ if (not Sendmail::Milter::register($ARGV[0], \%my_callbacks,
+ SMFI_CURR_ACTS))
+ {
+ print "Failed to register callbacks for $ARGV[0].\n";
+ exit;
+ }
+
+ print "Starting Sendmail::Milter $Sendmail::Milter::VERSION engine.\n";
+
+ if (Sendmail::Milter::main())
+ {
+ print "Successful exit from the Sendmail::Milter engine.\n";
+ }
+ else
+ {
+ print "Unsuccessful exit from the Sendmail::Milter engine.\n";
+ }
+}
diff --git a/test.pl b/test.pl
new file mode 100644
index 0000000..b062709
--- /dev/null
+++ b/test.pl
@@ -0,0 +1,81 @@
+#
+# Copyright (c) 2000 Charles Ying. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the same terms as perl itself.
+#
+# Please note that this code falls under a different license than the
+# other code found in Sendmail::Milter.
+#
+
+use ExtUtils::testlib;
+
+use Sendmail::Milter;
+
+sub dottedline { '-' x 72 . "\n"; }
+
+sub perl_callback
+{
+ my $interp = shift;
+
+ printf "---> Starting callback from interpreter: [0x%08x].\n", $interp;
+ sleep 1;
+ printf "---> Finished callback from interpreter: [0x%08x].\n", $interp;
+}
+
+print dottedline;
+print "Interpreter pool tests. See sample.pl for a sample Milter.\n";
+print dottedline;
+print "Running starvation test... (Core dump indicates failure ;-)\n";
+print dottedline;
+
+Sendmail::Milter::test_intpools(1, 0, 2, 2, \&perl_callback);
+
+# If we didn't core-dump, we're good. :)
+
+print dottedline;
+print "Starvation test successful.\n";
+print dottedline;
+print "Running multiplicity test... (Core dump indicates failure ;-)\n";
+print dottedline;
+
+Sendmail::Milter::test_intpools(0, 0, 2, 4, \&perl_callback);
+
+# If we didn't core-dump, we're good. :)
+
+print dottedline;
+print "Multiplicity test successful.\n";
+print dottedline;
+print "Running scalar function name test... (Core dump indicates failure ;-)\n";
+print dottedline;
+
+Sendmail::Milter::test_intpools(0, 0, 2, 2, 'perl_callback');
+
+print dottedline;
+print "Scalar function name test successful.\n";
+print dottedline;
+print "Running closure test... (Core dump indicates failure ;-)\n";
+print dottedline;
+
+Sendmail::Milter::test_intpools(0, 0, 2, 2, sub
+{
+ my $interp = shift;
+
+ printf "---> Starting callback from interpreter: [0x%08x].\n", $interp;
+ sleep 1;
+ printf "---> Finished callback from interpreter: [0x%08x].\n", $interp;
+});
+
+print dottedline;
+print "Closure test successful.\n";
+print dottedline;
+print "Running recycle test... (Core dump indicates failure ;-)\n";
+print dottedline;
+
+Sendmail::Milter::test_intpools(0, 1, 2, 4, \&perl_callback);
+
+print dottedline;
+print "Recycle test successful.\n";
+print dottedline;
+print "All tests finished successfully.\n";
+print dottedline;
diff --git a/typemap b/typemap
new file mode 100644
index 0000000..c7d3518
--- /dev/null
+++ b/typemap
@@ -0,0 +1,18 @@
+TYPEMAP
+Sendmail_Milter_Context T_PTROBJ_SPECIAL
+u_char * T_PV
+
+INPUT
+T_PTROBJ_SPECIAL
+ if (sv_derived_from($arg, \"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\"))
+ {
+ IV tmp = SvIV((SV*)SvRV($arg));
+ $var = ($type) tmp;
+ }
+ else
+ croak(\"$var is not of type ${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\")
+
+OUTPUT
+T_PTROBJ_SPECIAL
+ sv_setref_pv($arg, \"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\", (void*)$var);
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libparse-win32registry-perl.git
More information about the Pkg-perl-cvs-commits
mailing list