initial release

This commit is contained in:
SJ 2011-11-14 15:57:52 +01:00
commit 57f172c1b1
58 changed files with 13880 additions and 0 deletions

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (C) 2011, SJ <sj@acts.hu>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

106
Makefile.in Normal file
View File

@ -0,0 +1,106 @@
SHELL = @SHELL@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
includedir = @includedir@
libdir = @libdir@
libexecdir = @libexecdir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
mandir = @mandir@
datarootdir = @datarootdir@
localstatedir = @localstatedir@
CC = @CC@
CFLAGS = @CFLAGS@ @CPPFLAGS@
DEFS = @defs@
INCDIR = -I. @INCDIR@ @mysql_includes@
LIBDIR = -L. @LIBDIR@ @LDFLAGS@
LIBS = @LIBS@ @mysql_libs@
RUNNING_USER = @RUNNING_USER@
RUNNING_GROUP = `@id_bin@ -gn $(RUNNING_USER)`
SUBDIRS = @SUBDIRS@
RECURSIVE_TARGETS = all-recursive install-recursive clean-recursive distclean-recursive
MAKE = `which make`
INSTALL = @INSTALL@
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
piler-config.h stamp.h stamp-h1 params.h
all: all-recursive
install: installdirs install-recursive
$(RECURSIVE_TARGETS):
@failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
*k*) failcom='fail=yes';; \
esac; \
done; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
list='$(SUBDIRS)'; for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
installdirs: mkinstalldirs
$(srcdir)/mkinstalldirs \
$(DESTDIR)$(bindir) $(DESTDIR)$(sbindir) $(DESTDIR)$(libdir) $(DESTDIR)$(libexecdir)/piler $(DESTDIR)$(sysconfdir) \
$(DESTDIR)$(datarootdir)/piler $(DESTDIR)$(includedir)/piler $(DESTDIR)$(localstatedir)/lib/piler/data \
$(DESTDIR)$(localstatedir)/spool
$(INSTALL) -d -m 0755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) $(DESTDIR)$(localstatedir)/run/piler
$(INSTALL) -d -m 0755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) $(DESTDIR)$(localstatedir)/lib/piler/stat
install-am:
uninstall:
rm -f \
$(DESTDIR)$(sbindir)/piler $(DESTDIR)$(sbindir)/pilerconf $(DESTDIR)$(libdir)/libpiler.* \
$(DESTDIR)$(sysconfdir)/piler.conf
rm -rf $(DESTDIR)$(libexecdir)/piler $(DESTDIR)$(includedir)/piler $(DESTDIR)$(datarootdir)/piler
@echo "I left the $(DESTDIR)$(localstatedir)/lib/piler directory, remove it for yourself"
all-am: Makefile
clean: clean-recursive
rm -f piler.key
clean-am: clean-generic
clean-generic:
distclean: distclean-recursive
rm -f $(am__CONFIG_DISTCLEAN_FILES)
rm -f Makefile piler.key
distclean-am: clean-am
key:
dd if=/dev/urandom bs=56 count=1 of=piler.key

16
README Normal file
View File

@ -0,0 +1,16 @@
piler is an open source email archival application.
Features:
- ...
locales debian alatt: dpkg-reconfigure locales
indexer delta1 --rotate
indexer --merge main1 delta1 --rotate

995
aclocal.m4 vendored Normal file
View File

@ -0,0 +1,995 @@
# generated automatically by aclocal 1.11.1 -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.65],,
[m4_warning([this file was generated for autoconf 2.65.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically `autoreconf'.])])
# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.11'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.11.1], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# _AM_AUTOCONF_VERSION(VERSION)
# -----------------------------
# aclocal traces this macro to find the Autoconf version.
# This is a private macro too. Using m4_define simplifies
# the logic in aclocal, which can simply ignore this definition.
m4_define([_AM_AUTOCONF_VERSION], [])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.11.1])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
# therefore $ac_aux_dir as well) can be either absolute or relative,
# depending on how configure is run. This is pretty annoying, since
# it makes $ac_aux_dir quite unusable in subdirectories: in the top
# source directory, any form will work fine, but in subdirectories a
# relative path needs to be adjusted first.
#
# $ac_aux_dir/missing
# fails when called from a subdirectory if $ac_aux_dir is relative
# $top_srcdir/$ac_aux_dir/missing
# fails if $ac_aux_dir is absolute,
# fails when called from a subdirectory in a VPATH build with
# a relative $ac_aux_dir
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
# harmless because $srcdir is `.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
# and then we would define $MISSING as
# MISSING="\${SHELL} $am_aux_dir/missing"
# This will work as long as MISSING is not called from configure, because
# unfortunately $(top_srcdir) has no meaning in configure.
# However there are other variables, like CC, which are often used in
# configure, and could therefore not use this "fixed" $ac_aux_dir.
#
# Another solution, used here, is to always expand $ac_aux_dir to an
# absolute PATH. The drawback is that using absolute paths prevent a
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
[dnl Rely on autoconf to set up CDPATH properly.
AC_PREREQ([2.50])dnl
# expand $ac_aux_dir to an absolute path
am_aux_dir=`cd $ac_aux_dir && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 9
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ(2.52)dnl
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
m4_define([_AM_COND_VALUE_$1], [$2])dnl
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi
AC_CONFIG_COMMANDS_PRE(
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
AC_MSG_ERROR([[conditional "$1" was never defined.
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 10
# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
# written in clear, in which case automake, when reading aclocal.m4,
# will think it sees a *use*, and therefore will trigger all it's
# C support machinery. Also note that it means that autoscan, seeing
# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
# _AM_DEPENDENCIES(NAME)
# ----------------------
# See how the compiler implements dependency checking.
# NAME is "CC", "CXX", "GCJ", or "OBJC".
# We try a few techniques and use that to set a single cache variable.
#
# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
# dependency, and given that the user is not expected to run this macro,
# just rely on AC_PROG_CC.
AC_DEFUN([_AM_DEPENDENCIES],
[AC_REQUIRE([AM_SET_DEPDIR])dnl
AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
AC_REQUIRE([AM_MAKE_INCLUDE])dnl
AC_REQUIRE([AM_DEP_TRACK])dnl
ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
[$1], CXX, [depcc="$CXX" am_compiler_list=],
[$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
[$1], UPC, [depcc="$UPC" am_compiler_list=],
[$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
[depcc="$$1" am_compiler_list=])
AC_CACHE_CHECK([dependency style of $depcc],
[am_cv_$1_dependencies_compiler_type],
[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
# We make a subdir and do the tests there. Otherwise we can end up
# making bogus files that we don't know about and never remove. For
# instance it was reported that on HP-UX the gcc test will end up
# making a dummy file named `D' -- because `-MD' means `put the output
# in D'.
mkdir conftest.dir
# Copy depcomp to subdir because otherwise we won't find it if we're
# using a relative directory.
cp "$am_depcomp" conftest.dir
cd conftest.dir
# We will build objects and dependencies in a subdirectory because
# it helps to detect inapplicable dependency modes. For instance
# both Tru64's cc and ICC support -MD to output dependencies as a
# side effect of compilation, but ICC will put the dependencies in
# the current directory while Tru64 will put them in the object
# directory.
mkdir sub
am_cv_$1_dependencies_compiler_type=none
if test "$am_compiler_list" = ""; then
am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
fi
am__universal=false
m4_case([$1], [CC],
[case " $depcc " in #(
*\ -arch\ *\ -arch\ *) am__universal=true ;;
esac],
[CXX],
[case " $depcc " in #(
*\ -arch\ *\ -arch\ *) am__universal=true ;;
esac])
for depmode in $am_compiler_list; do
# Setup a source with many dependencies, because some compilers
# like to wrap large dependency lists on column 80 (with \), and
# we should not choose a depcomp mode which is confused by this.
#
# We need to recreate these files for each test, as the compiler may
# overwrite some of them when testing with obscure command lines.
# This happens at least with the AIX C compiler.
: > sub/conftest.c
for i in 1 2 3 4 5 6; do
echo '#include "conftst'$i'.h"' >> sub/conftest.c
# Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
# Solaris 8's {/usr,}/bin/sh.
touch sub/conftst$i.h
done
echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
# We check with `-c' and `-o' for the sake of the "dashmstdout"
# mode. It turns out that the SunPro C++ compiler does not properly
# handle `-M -o', and we need to detect this. Also, some Intel
# versions had trouble with output in subdirs
am__obj=sub/conftest.${OBJEXT-o}
am__minus_obj="-o $am__obj"
case $depmode in
gcc)
# This depmode causes a compiler race in universal mode.
test "$am__universal" = false || continue
;;
nosideeffect)
# after this tag, mechanisms are not by side-effect, so they'll
# only be used when explicitly requested
if test "x$enable_dependency_tracking" = xyes; then
continue
else
break
fi
;;
msvisualcpp | msvcmsys)
# This compiler won't grok `-c -o', but also, the minuso test has
# not run yet. These depmodes are late enough in the game, and
# so weak that their functioning should not be impacted.
am__obj=conftest.${OBJEXT-o}
am__minus_obj=
;;
none) break ;;
esac
if depmode=$depmode \
source=sub/conftest.c object=$am__obj \
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
$SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
>/dev/null 2>conftest.err &&
grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
# icc doesn't choke on unknown options, it will just issue warnings
# or remarks (even with -Werror). So we grep stderr for any message
# that says an option was ignored or not supported.
# When given -MP, icc 7.0 and 7.1 complain thusly:
# icc: Command line warning: ignoring option '-M'; no argument required
# The diagnosis changed in icc 8.0:
# icc: Command line remark: option '-MP' not supported
if (grep 'ignoring option' conftest.err ||
grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
am_cv_$1_dependencies_compiler_type=$depmode
break
fi
fi
done
cd ..
rm -rf conftest.dir
else
am_cv_$1_dependencies_compiler_type=none
fi
])
AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
AM_CONDITIONAL([am__fastdep$1], [
test "x$enable_dependency_tracking" != xno \
&& test "$am_cv_$1_dependencies_compiler_type" = gcc3])
])
# AM_SET_DEPDIR
# -------------
# Choose a directory name for dependency files.
# This macro is AC_REQUIREd in _AM_DEPENDENCIES
AC_DEFUN([AM_SET_DEPDIR],
[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
])
# AM_DEP_TRACK
# ------------
AC_DEFUN([AM_DEP_TRACK],
[AC_ARG_ENABLE(dependency-tracking,
[ --disable-dependency-tracking speeds up one-time build
--enable-dependency-tracking do not reject slow dependency extractors])
if test "x$enable_dependency_tracking" != xno; then
am_depcomp="$ac_aux_dir/depcomp"
AMDEPBACKSLASH='\'
fi
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
AC_SUBST([AMDEPBACKSLASH])dnl
_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
])
# Generate code to set up dependency tracking. -*- Autoconf -*-
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
#serial 5
# _AM_OUTPUT_DEPENDENCY_COMMANDS
# ------------------------------
AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
[{
# Autoconf 2.62 quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
case $CONFIG_FILES in
*\'*) eval set x "$CONFIG_FILES" ;;
*) set x $CONFIG_FILES ;;
esac
shift
for mf
do
# Strip MF so we end up with the name of the file.
mf=`echo "$mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile or not.
# We used to match only the files named `Makefile.in', but
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
# Grep'ing the whole file is not good either: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
dirpart=`AS_DIRNAME("$mf")`
else
continue
fi
# Extract the definition of DEPDIR, am__include, and am__quote
# from the Makefile without running `make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
test -z "am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
# When using ansi2knr, U may be empty or an underscore; expand it
U=`sed -n 's/^U = //p' < "$mf"`
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue
fdir=`AS_DIRNAME(["$file"])`
AS_MKDIR_P([$dirpart/$fdir])
# echo "creating $dirpart/$file"
echo '# dummy' > "$dirpart/$file"
done
done
}
])# _AM_OUTPUT_DEPENDENCY_COMMANDS
# AM_OUTPUT_DEPENDENCY_COMMANDS
# -----------------------------
# This macro should only be invoked once -- use via AC_REQUIRE.
#
# This code is only required when automatic dependency tracking
# is enabled. FIXME. This creates each `.P' file that we will
# need in order to bootstrap the dependency handling code.
AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
[AC_CONFIG_COMMANDS([depfiles],
[test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
[AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
])
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 16
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
# The call with PACKAGE and VERSION arguments is the old style
# call (pre autoconf-2.50), which is being phased out. PACKAGE
# and VERSION should now be passed to AC_INIT and removed from
# the call to AM_INIT_AUTOMAKE.
# We support both call styles for the transition. After
# the next Automake release, Autoconf can make the AC_INIT
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.62])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
fi
# test whether we have cygpath
if test -z "$CYGPATH_W"; then
if (cygpath --version) >/dev/null 2>/dev/null; then
CYGPATH_W='cygpath -w'
else
CYGPATH_W=echo
fi
fi
AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
AM_MISSING_PROG(AUTOCONF, autoconf)
AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
AM_MISSING_PROG(AUTOHEADER, autoheader)
AM_MISSING_PROG(MAKEINFO, makeinfo)
AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
AC_REQUIRE([AM_PROG_MKDIR_P])dnl
# We need awk for the "check" target. The system "awk" is bad on
# some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
[_AM_PROG_TAR([v7])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
[_AM_DEPENDENCIES(CC)],
[define([AC_PROG_CC],
defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES(CXX)],
[define([AC_PROG_CXX],
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
[_AM_DEPENDENCIES(OBJC)],
[define([AC_PROG_OBJC],
defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
])
_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
AC_CONFIG_COMMANDS_PRE(dnl
[m4_provide_if([_AM_COMPILER_EXEEXT],
[AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
])
dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
dnl mangled by Autoconf and run in a shell conditional statement.
m4_define([_AC_COMPILER_EXEEXT],
m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
# loop where config.status creates the headers, so we can generate
# our stamp files there.
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
[# Compute $1's index in $config_headers.
_am_arg=$1
_am_stamp_count=1
for _am_header in $config_headers :; do
case $_am_header in
$_am_arg | $_am_arg:* )
break ;;
* )
_am_stamp_count=`expr $_am_stamp_count + 1` ;;
esac
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_SH
# ------------------
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
if test x"${install_sh}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
*)
install_sh="\${SHELL} $am_aux_dir/install-sh"
esac
fi
AC_SUBST(install_sh)])
# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Check to see how 'make' treats includes. -*- Autoconf -*-
# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 4
# AM_MAKE_INCLUDE()
# -----------------
# Check to see how make treats includes.
AC_DEFUN([AM_MAKE_INCLUDE],
[am_make=${MAKE-make}
cat > confinc << 'END'
am__doit:
@echo this is the am__doit target
.PHONY: am__doit
END
# If we don't find an include directive, just comment out the code.
AC_MSG_CHECKING([for style of include used by $am_make])
am__include="#"
am__quote=
_am_result=none
# First try GNU make style include.
echo "include confinc" > confmf
# Ignore all kinds of additional output from `make'.
case `$am_make -s -f confmf 2> /dev/null` in #(
*the\ am__doit\ target*)
am__include=include
am__quote=
_am_result=GNU
;;
esac
# Now try BSD make style include.
if test "$am__include" = "#"; then
echo '.include "confinc"' > confmf
case `$am_make -s -f confmf 2> /dev/null` in #(
*the\ am__doit\ target*)
am__include=.include
am__quote="\""
_am_result=BSD
;;
esac
fi
AC_SUBST([am__include])
AC_SUBST([am__quote])
AC_MSG_RESULT([$_am_result])
rm -f confinc confmf
])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 6
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
[AC_REQUIRE([AM_MISSING_HAS_RUN])
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
# AM_MISSING_HAS_RUN
# ------------------
# Define MISSING if not defined so far and test if it supports --run.
# If it does, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
if test x"${MISSING+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
*)
MISSING="\${SHELL} $am_aux_dir/missing" ;;
esac
fi
# Use eval to expand $SHELL
if eval "$MISSING --run true"; then
am_missing_run="$MISSING --run "
else
am_missing_run=
AC_MSG_WARN([`missing' script is too old or missing])
fi
])
# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_MKDIR_P
# ---------------
# Check for `mkdir -p'.
AC_DEFUN([AM_PROG_MKDIR_P],
[AC_PREREQ([2.60])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
dnl while keeping a definition of mkdir_p for backward compatibility.
dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
dnl Makefile.ins that do not define MKDIR_P, so we do our own
dnl adjustment using top_builddir (which is defined more often than
dnl MKDIR_P).
AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
case $mkdir_p in
[[\\/$]]* | ?:[[\\/]]*) ;;
*/*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
esac
])
# Helper functions for option handling. -*- Autoconf -*-
# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 4
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
# ------------------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
# _AM_SET_OPTIONS(OPTIONS)
# ----------------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
# -------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_RUN_LOG(COMMAND)
# -------------------
# Run COMMAND, save the exit status in ac_status, and log it.
# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
AC_DEFUN([AM_RUN_LOG],
[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
(exit $ac_status); }])
# Check to make sure that the build environment is sane. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 5
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
# Just in case
sleep 1
echo timestamp > conftest.file
# Reject unsafe characters in $srcdir or the absolute working directory
# name. Accept space and tab only in the latter.
am_lf='
'
case `pwd` in
*[[\\\"\#\$\&\'\`$am_lf]]*)
AC_MSG_ERROR([unsafe absolute working directory name]);;
esac
case $srcdir in
*[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
esac
# Do `set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
if test "$[*]" = "X"; then
# -L didn't work.
set X `ls -t "$srcdir/configure" conftest.file`
fi
rm -f conftest.file
if test "$[*]" != "X $srcdir/configure conftest.file" \
&& test "$[*]" != "X conftest.file $srcdir/configure"; then
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
alias in your environment])
fi
test "$[2]" = conftest.file
)
then
# Ok.
:
else
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
AC_MSG_RESULT(yes)])
# Copyright (C) 2009 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 1
# AM_SILENT_RULES([DEFAULT])
# --------------------------
# Enable less verbose build rules; with the default set to DEFAULT
# (`yes' being less verbose, `no' or empty being verbose).
AC_DEFUN([AM_SILENT_RULES],
[AC_ARG_ENABLE([silent-rules],
[ --enable-silent-rules less verbose build output (undo: `make V=1')
--disable-silent-rules verbose build output (undo: `make V=0')])
case $enable_silent_rules in
yes) AM_DEFAULT_VERBOSITY=0;;
no) AM_DEFAULT_VERBOSITY=1;;
*) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
esac
AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
AM_BACKSLASH='\'
AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_STRIP
# ---------------------
# One issue with vendor `install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
# always use install-sh in `make install-strip', and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
# Installed binaries are usually stripped using `strip' when the user
# run `make install-strip'. However `strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
# will honor the `STRIP' environment variable to overrule this program.
dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Copyright (C) 2006, 2008 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Public sister of _AM_SUBST_NOTMAKE.
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
# FORMAT should be one of `v7', `ustar', or `pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
# $tardir.
# tardir=directory && $(am__tar) > result.tar
#
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
AC_DEFUN([_AM_PROG_TAR],
[# Always define AMTAR for backward compatibility.
AM_MISSING_PROG([AMTAR], [tar])
m4_if([$1], [v7],
[am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
[m4_case([$1], [ustar],, [pax],,
[m4_fatal([Unknown tar format])])
AC_MSG_CHECKING([how to create a $1 tar archive])
# Loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
_am_tools=${am_cv_prog_tar_$1-$_am_tools}
# Do not fold the above two line into one, because Tru64 sh and
# Solaris sh will not grok spaces in the rhs of `-'.
for _am_tool in $_am_tools
do
case $_am_tool in
gnutar)
for _am_tar in tar gnutar gtar;
do
AM_RUN_LOG([$_am_tar --version]) && break
done
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
am__untar="$_am_tar -xf -"
;;
plaintar)
# Must skip GNU tar: if it does not support --format= it doesn't create
# ustar tarball either.
(tar --version) >/dev/null 2>&1 && continue
am__tar='tar chf - "$$tardir"'
am__tar_='tar chf - "$tardir"'
am__untar='tar xf -'
;;
pax)
am__tar='pax -L -x $1 -w "$$tardir"'
am__tar_='pax -L -x $1 -w "$tardir"'
am__untar='pax -r'
;;
cpio)
am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
am__untar='cpio -i -H $1 -d'
;;
none)
am__tar=false
am__tar_=false
am__untar=false
;;
esac
# If the value was cached, stop now. We just wanted to have am__tar
# and am__untar set.
test -n "${am_cv_prog_tar_$1}" && break
# tar/untar a dummy directory, and stop if the command works
rm -rf conftest.dir
mkdir conftest.dir
echo GrepMe > conftest.dir/file
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
rm -rf conftest.dir
if test -s conftest.tar; then
AM_RUN_LOG([$am__untar <conftest.tar])
grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
fi
done
rm -rf conftest.dir
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
AC_MSG_RESULT([$am_cv_prog_tar_$1])])
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR

5617
configure vendored Executable file

File diff suppressed because it is too large Load Diff

324
configure.in Normal file
View File

@ -0,0 +1,324 @@
AC_INIT
AC_CONFIG_SRCDIR([src/piler.c])
AC_CONFIG_HEADER(piler-config.h)
CONFIGURE_PARAMS_FILE=params.h
rm -f $CONFIGURE_PARAMS_FILE; echo "#define CONFIGURE_PARAMS \"Build Date: "`date`"\nConfigure command: ./configure $@\"" > $CONFIGURE_PARAMS_FILE
dnl Checks for programs.
AC_PROG_CC
dnl AM_PROG_CC_STDC
AC_PROG_MAKE_SET
AC_PROG_INSTALL
dnl Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(sys/socket.h sys/time.h syslog.h signal.h ctype.h unistd.h)
AC_SUBST(defs)
AC_SUBST(INCDIR)
AC_SUBST(LIBDIR)
AC_SUBST(OBJS)
AC_SUBST(RUNNING_USER)
AC_SUBST(SUBDIRS)
AC_SUBST(mysql_includes)
AC_SUBST(mysql_libs)
AC_SUBST(mysql_obj)
AC_SUBST(libclamav_extra_libs)
AC_SUBST(sqlite3_batch)
AC_SUBST(id_bin)
have_libclamav="no"
have_libtool="no"
have_clamd="no"
have_antivirus="no"
have_mysql="no"
have_sqlite3="no"
have_zlib="no"
have_whitelist="no"
have_blacklist="no"
have_static_build="no"
antispam_libs="-lz -lm -ldl -lcrypto"
defs=""
objs=""
user_obj=""
mysql_obj=""
sqlite3_batch=""
os=`uname -s`
id_bin="id"
dnl SUBDIRS="src etc util perl init.d templates history contrib/stat"
SUBDIRS="src"
dnl static build
AC_ARG_ENABLE(static-build,
[ --enable-static-build build statically linked executables (default: dynamically linked)], have_static_build=$enableval, have_static_build="no")
dnl clamd
AC_ARG_ENABLE(clamd,
[ --enable-clamd build clamd antivirus support], want_clamd=$enableval, want_clamd="no")
if test "$want_clamd" = "yes"; then
have_clamd="yes"
have_antivirus="yes"
fi
dnl use whitelist
AC_ARG_ENABLE(whitelist,
[ --enable-whitelist use whitelist], want_whitelist=$enableval, want_whitelist="no")
if test "$want_whitelist" = "yes"; then
have_whitelist="yes"
fi
dnl use blacklist
AC_ARG_ENABLE(blacklist,
[ --enable-blacklist use blacklist (this is _NOT_ RBL)], want_blacklist=$enableval, want_blacklist="no")
if test "$want_blacklist" = "yes"; then
have_blacklist="yes"
fi
dnl math library
AC_CHECK_HEADERS(math.h, have_math=yes, have_math=no)
AC_CHECK_LIB([m],[fabs],[AC_CHECK_LIB(m, fabs)],[have_math=yes],[""; exit])ac_cv_lib_m=ac_cv_lib_m_main
dnl openssl library
AC_CHECK_HEADERS(openssl/sha.h, have_crypto=yes, have_crypto=no)
AC_CHECK_LIB([crypto],[SHA256_Init],[AC_CHECK_LIB(crypto, SHA256_Init, have_crypto=yes, echo "libcrypto is not found"; have_crypto=no)],[],[])ac_cv_lib_crypto=ac_cv_lib_crypto_main
if test "$have_crypto" = "no"; then
echo "please install openssl developer package"
exit 1
fi
dnl TRE library
AC_CHECK_HEADERS(tre/tre.h, have_tre=yes, echo "tre.h is not found")
AC_CHECK_LIB([tre],[main],[AC_CHECK_LIB(tre, regcomp, have_tre=yes, echo "libtre.so is not found"; have_tre=no)],[],[])ac_cv_lib_tre=ac_cv_lib_tre_main
dnl zlib
AC_CHECK_HEADERS(zlib.h, have_zlib=yes, echo "zlib.h is not found")
AC_CHECK_LIB([z],[main],[AC_CHECK_LIB(z, compress, have_zlib=yes, echo "libz.so is not found"; have_zlib=no)],[],[])ac_cv_lib_z=ac_cv_lib_z_main
if test "$have_zlib" = "no"; then
echo "please install the zlib development packages"
exit 1;
fi
AC_CHECK_PROG(MYSQL_CONFIG, mysql_config, yes)
if test x$MYSQL_CONFIG = xyes; then
have_mysql="yes"
fi
if test "$have_mysql" = "yes"; then
mysql_includes=`mysql_config --cflags`
mysql_libs=`mysql_config --libs_r`
AC_CHECK_LIB([guide],[main],[AC_CHECK_LIB(guide, _intel_fast_memset, have_icc_guide=yes, have_icc_guide=no)],[],[])ac_cv_lib_guide=ac_cv_lib_guide_main
else
echo "MySQL support is not found"
exit 1;
fi
AC_CHECK_HEADERS(sqlite3.h, have_sqlite3=yes, echo "sqlite3.h is not found")
AC_CHECK_LIB([sqlite3],[main],[AC_CHECK_LIB(sqlite3, sqlite3_open, have_sqlite3=yes, have_sqlite3=no)],[],[])ac_cv_lib_sqlite3=ac_cv_lib_sqlite3_main
if test "$have_sqlite3" = "no"; then
echo "sqlite3 not found";
dnl exit 1;
fi
if test `sqlite3 -help 2>&1| grep -c -- -batch` -gt 0; then sqlite3_batch="-batch"; fi
dnl user running piler
AC_ARG_WITH(piler-user,
[ --with-piler-user=username what user the piler daemon shall be run as],
[ RUNNING_USER=$withval ],
[ RUNNING_USER=piler ]
)
dnl configure config directory
cfg_dir=`echo $sysconfdir | grep prefix`
if test -n "$cfg_dir"; then
if test "$prefix" = "NONE"
then
cfg_dir="$ac_default_prefix/etc"
my_prefix=$ac_default_prefix
else
cfg_dir="$prefix/etc"
my_prefix=$prefix
fi
else
cfg_dir="$sysconfdir"
fi
CFGDIR=$cfg_dir
AC_SUBST(CFGDIR)
AC_DEFINE_UNQUOTED(CONFDIR,"$cfg_dir",[where to look for the config file])
dnl configure data directory
data_dir=`echo $localstatedir | grep prefix`
if test -n "$data_dir"; then
if test "$prefix" = "NONE"
then
data_dir="$ac_default_prefix/var"
else
data_dir="$prefix/var"
fi
else
data_dir="$localstatedir"
fi
DATADIR=$data_dir
AC_SUBST(DATADIR)
AC_DEFINE_UNQUOTED(DATADIR,"$data_dir",[where to look for the data files])
AC_DEFINE_UNQUOTED(VIRUS_TEMPLATE, "$my_prefix/share/clapf/template.virus", [where the virus template is])
AC_DEFINE_UNQUOTED(ZOMBIE_NET_REGEX, "$my_prefix/share/clapf/zombienets.regex", [where the virus template is])
dnl let us know if we are building on FreeBSD
if test "$os" = "FreeBSD"; then
defs="$defs -DFREEBSD"
fi
if test "$os" = "Linux"; then
defs="$defs -D_GNU_SOURCE"
fi
if test "$os" = "SunOS"; then
AC_DEFINE(HAVE_DAEMON, 0, [daemon() support])
sunos_libs="-lnsl -lsocket"
if test -x /usr/xpg4/bin/id; then id_bin="/usr/xpg4/bin/id"; fi
fi
if test `echo $os | grep -c CYGWIN` -eq 1; then
defs="$defs -DCYGWIN"
AC_DEFINE_UNQUOTED(ZOMBIE_NET_REGEX, "zombienets.regex", [where the virus template is])
fi
dnl whether we have antivirus support
if test "$have_clamd" = "yes" ; then
defs="$defs -DHAVE_ANTIVIRUS"
objs="$objs avir.o"
fi
echo; echo
if test "$have_clamd" = "yes"; then
echo "clamd support: yes"
objs="$objs clamd.o"
defs="$defs -DHAVE_CLAMD"
fi
if test "$have_whitelist" = "yes"; then
echo "whitelist: yes"
defs="$defs -DHAVE_WHITELIST"
else
echo "whitelist: no"
fi
if test "$have_blacklist" = "yes"; then
echo "blacklist: yes"
defs="$defs -DHAVE_BLACKLIST"
else
echo "blacklist: no"
fi
if test "$want_memcached" = "yes"; then
echo "memcached support: yes"
objs="$objs memc.o memcached.o"
defs="$defs -DHAVE_MEMCACHED"
fi
echo "run as user: $RUNNING_USER"
if test "$have_static_build" = "yes"; then
echo "link mode: static"
static="-static"
else
echo "link mode: dynamic"
fi
if test "$have_tre" = "yes"; then
echo "tre library: yes"
defs="$defs -DHAVE_TRE"
antispam_libs="$antispam_libs -ltre"
fi
if test "$have_mysql" = "yes"; then
defs="$defs -DNEED_MYSQL"
fi
if test "$have_sqlite3" = "yes"; then
defs="$defs -DNEED_SQLITE3"
sqlite3_libs="-lsqlite3 -lpthread"
fi
if test "$have_icc_guide" = "yes" && test "$have_mysql" = "yes"; then
mysql_libs="$mysql_libs -lguide"
fi
id -u $RUNNING_USER 2>/dev/null 1>/dev/null
if test $? -eq 1; then echo "the user \"$RUNNING_USER\" does not exists, please create it, first with adduser..."; exit 1; fi
if test "$have_clamd" = "no" ; then
echo; echo
echo "You have not selected any antivirus support."
echo "piler will not protect you from hostile code coming in e-mail"
fi
echo; echo
CFLAGS="$static -O2 -Wall -g"
LIBS="$antispam_libs $sunos_libs $sqlite3_libs"
OBJS="dirs.o misc.o counters.o cfg.o sig.o decoder.o list.o boundary.o parser.o parser_utils.o session.o message.o digest.o store.o tai.o $objs"
AC_CONFIG_FILES([Makefile src/Makefile])
AC_OUTPUT

48
etc/Makefile Normal file
View File

@ -0,0 +1,48 @@
SHELL = /bin/bash
prefix = /usr/local
exec_prefix = ${prefix}
bindir = ${exec_prefix}/bin
sbindir = ${exec_prefix}/sbin
includedir = ${prefix}/include
libdir = ${exec_prefix}/lib
libexecdir = ${exec_prefix}/libexec
srcdir = .
sysconfdir = ${prefix}/etc
mandir = ${datarootdir}/man
datarootdir = ${prefix}/share
localstatedir = /var
CC = gcc
CFLAGS = -O2 -Wall -g
DEFS = -DHAVE_MYSQL -D_GNU_SOURCE -DHAVE_ANTISPAM -DUSERS_IN_MYSQL -DHAVE_USERS -DNEED_MYSQL
INCDIR = -I. -I../.. -I../../src -I/usr/include/mysql -DBIG_JOINS=1 -fno-strict-aliasing -DUNIV_LINUX -DUNIV_LINUX
LIBDIR = -L. -L../../src
LIBS = -lm -ldl -rdynamic -L/usr/lib/mysql -lmysqlclient_r
LDAP_LIBS =
CLAPF_USER = clapf
CLAPF_GROUP = `id -gn $(CLAPF_USER)`
INSTALL = /usr/bin/install -c
all:
sed -e 's%pidfile=.*%pidfile=$(localstatedir)/run/clapf/clapf.pid%g' \
-e 's%sqlite3=.*%sqlite3=$(localstatedir)/lib/clapf/data/tokens.sdb%g' \
-e 's%workdir=.*%workdir=$(localstatedir)/spool/clapf/tmp%g' \
-e 's%queuedir=.*%queuedir=$(localstatedir)/lib/clapf/queue%g' \
-e 's%mydbfile=.*%mydbfile=$(localstatedir)/lib/clapf/tokens.mydb%g' < $(srcdir)/example.conf > $(srcdir)/clapf.conf
sed -e 's%LOCALSTATEDIR%$(localstatedir)%g' \
-e 's%LIBEXECDIR%$(libexecdir)%g' \
-e 's%SYSCONFDIR%$(sysconfdir)%g' \
-e 's%SBINDIR%$(sbindir)%g' \
-e 's%DATAROOTDIR%$(datarootdir)%g' < $(srcdir)/cron.jobs.in > $(srcdir)/cron.jobs
install:
if [ ! -f "$(DESTDIR)$(sysconfdir)/clapf.conf" ]; then $(INSTALL) -m 0640 $(srcdir)/clapf.conf $(DESTDIR)$(sysconfdir)/clapf.conf; chgrp $(CLAPF_GROUP) $(DESTDIR)$(sysconfdir)/clapf.conf; fi
clean:
rm -f clapf.conf cron.jobs
distclean: clean
rm -f Makefile

39
etc/Makefile.in Normal file
View File

@ -0,0 +1,39 @@
SHELL = @SHELL@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
includedir = @includedir@
libdir = @libdir@
libexecdir = @libexecdir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
mandir = @mandir@
datarootdir = @datarootdir@
localstatedir = @localstatedir@
CC = @CC@
CFLAGS = @CFLAGS@ @CPPFLAGS@
DEFS = @defs@
INCDIR = -I. -I../.. -I../../src @INCDIR@ @mysql_includes@
LIBDIR = -L. @LIBDIR@ @LDFLAGS@ -L../../src
LIBS = @LIBS@ @mysql_libs@
RUNNING_USER = @RUNNING_USER@
RUNNING_GROUP = `@id_bin@ -gn $(RUNNING_USER)`
INSTALL = @INSTALL@
all:
sed -e 's%pidfile=.*%pidfile=$(localstatedir)/run/piler/piler.pid%g' \
-e 's%workdir=.*%workdir=$(localstatedir)/spool/piler/tmp%g' \
-e 's%queuedir=.*%queuedir=$(localstatedir)/piler/queue%g' < $(srcdir)/example.conf > $(srcdir)/piler.conf
install:
if [ ! -f "$(DESTDIR)$(sysconfdir)/piler.conf" ]; then $(INSTALL) -m 0640 $(srcdir)/piler.conf $(DESTDIR)$(sysconfdir)/piler.conf; chgrp $(CLAPF_GROUP) $(DESTDIR)$(sysconfdir)/piler.conf; fi
clean:
rm -f piler.conf cron.jobs
distclean: clean
rm -f Makefile

84
etc/example.conf Normal file
View File

@ -0,0 +1,84 @@
; This is an example config with default values
; Attention: do _not_ use whitespace between keys and values
; verbosity level. The more logging the greater load
; 1: normal
; 3: info
; 5: debug
verbosity=1
; piler daemon will use this user (and its group)
; it it was started by root
username=piler
; number of worker processes, ie. the number of simultaneous smtp connections to piler.
number_of_worker_processes=5
; number of processed emails per each piler process
max_requests_per_child=50
; SMTP HELO identification string
hostid=av-engine.localhost
; write pid file
pidfile=/var/run/piler/piler.pid
; piler will listen here
listen_addr=0.0.0.0
listen_port=25
clamd_socket=/tmp/clamd
session_timeout=420
; the 2nd parameter of the listen() system call. Please note that this is set
; when clapf starts up and you should restart clapf if you change this variable.
; Please also note that the meaning of this variable depends on your Unix implementation
backlog=20
workdir=/var/spool/piler/tmp
;
; memcached stuff
;
; memcached server to use. Currently clapf support only 1 memcached server
memcached_servers=127.0.0.1
; ttl (in secs) of a stored object
; 0 means records don't expire
memcached_ttl=86400
piler_header_field=X-piler: ahahahahaha
;
; mysql stuff
;
;mysqlhost=127.0.0.1
;mysqlport=3306
mysqlsocket=/tmp/mysql.sock
mysqluser=piler
mysqlpwd=changeme
mysqldb=piler
mysql_connect_timeout=2
;
; sqlite3 stuff
;
; If you are using spamdrop (not the clapf daemon), you have two options:
; 1. You may specify this variable, then every user will share this token
; database (=shared database) OR
; 2. comment this variable out, and spamdrop will figure out where the users'
; individual token databases are.
sqlite3=/var/lib/piler/data/tokens.sdb
; set sqlite3 pragma, see http://www.sqlite.org/pragma.html for more details
; possible values are:
; PRAGMA synchronous = FULL
; PRAGMA synchronous = NORMAL
; PRAGMA synchronous = OFF
sqlite3_pragma=PRAGMA synchronous = OFF

86
etc/sphinx.conf Normal file
View File

@ -0,0 +1,86 @@
#
# Minimal Sphinx configuration sample (clean, simple, functional)
#
source main
{
type = mysql
sql_host = localhost
sql_db = sphinx
sql_user = sphinx
sql_pass = sphinx
sql_query_pre = SET NAMES utf8
sql_query = SELECT id, `from`, `to`, `subject`, `date`, `body`, `piler_id`, `header_id`, `body_id`, `size` FROM sph_index \
WHERE id<=( SELECT max_doc_id FROM sph_counter WHERE counter_id=1 )
sql_attr_uint = size
sql_attr_uint = date
sql_attr_string = piler_id
sql_attr_string = header_id
sql_attr_string = body_id
}
source delta
{
type = mysql
sql_host = localhost
sql_db = sphinx
sql_user = sphinx
sql_pass = sphinx
sql_query_pre = SET NAMES utf8
sql_query_pre = REPLACE INTO sph_counter SELECT 1, MAX(id) FROM sph_index
sql_query_post_index = DELETE FROM sph_index WHERE id<=(SELECT max_doc_id FROM sph_counter WHERE counter_id=1)
sql_query = SELECT id, `from`, `to`, `subject`, `date`, `body`, `piler_id`, `header_id`, `body_id`, `size` FROM sph_index \
WHERE id <= (SELECT max_doc_id FROM sph_counter WHERE counter_id=1)
sql_attr_uint = size
sql_attr_uint = date
sql_attr_string = piler_id
sql_attr_string = header_id
sql_attr_string = body_id
}
index main1
{
source = main
path = /var/data/main1
docinfo = extern
charset_type = utf-8
}
index delta1
{
source = delta
path = /var/data/delta1
docinfo = extern
charset_type = utf-8
}
indexer
{
mem_limit = 32M
}
searchd
{
listen = 9312
listen = 9306:mysql41
log = /var/data/log/searchd.log
binlog_path =
query_log = /var/data/log/query.log
read_timeout = 5
max_children = 30
pid_file = /var/data/log/searchd.pid
max_matches = 1000
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
workers = threads # for RT to work
}

251
install-sh Executable file
View File

@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

40
mkinstalldirs Executable file
View File

@ -0,0 +1,40 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Public domain
# $Id: mkinstalldirs,v 1.2 2000/01/10 23:36:14 bagder Exp $
errstatus=0
for file
do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d
do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp" 1>&2
mkdir "$pathcomp" || lasterr=$?
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus
# mkinstalldirs ends here

15
piler-config.h.in Normal file
View File

@ -0,0 +1,15 @@
/*
* piler-config.h.in, SJ
*/
#define CONFDIR "/usr/local/etc"
#define DATADIR "/usr/local/var"
#define VIRUS_TEMPLATE "/usr/local/share/clapf/template.virus"
#define ZOMBIE_NET_REGEX "/usr/local/share/clapf/zombienets.regex"
#define KEYFILE CONFDIR "/piler.key"
#define HAVE_DAEMON 1

82
src/Makefile Normal file
View File

@ -0,0 +1,82 @@
SHELL = /bin/sh
prefix = /usr/local
exec_prefix = ${prefix}
bindir = ${exec_prefix}/bin
sbindir = ${exec_prefix}/sbin
includedir = ${prefix}/include
libdir = ${exec_prefix}/lib
libexecdir = ${exec_prefix}/libexec
srcdir = .
sysconfdir = ${prefix}/etc
mandir = ${datarootdir}/man
datarootdir = ${prefix}/share
localstatedir = /var
CC = gcc
CFLAGS = -O2 -Wall -g
DEFS = -D_GNU_SOURCE -DHAVE_ANTIVIRUS -DHAVE_CLAMD -DHAVE_TRE -DNEED_MYSQL -DNEED_SQLITE3
INCDIR = -I. -I.. -I/usr/local/mysql/include -fPIC -g -static-libgcc -fno-omit-frame-pointer -m32 -fPIC -g -static-libgcc -fno-omit-frame-pointer -fno-strict-aliasing -DMY_PTHREAD_FASTMUTEX=1
LIBDIR = -L.
LIBS = -lz -lm -ldl -lcrypto -ltre -lsqlite3 -lpthread -L/usr/local/mysql/lib -lmysqlclient_r -lpthread -lm -lrt -ldl -lguide
OBJS = dirs.o misc.o counters.o cfg.o sig.o decoder.o list.o boundary.o parser.o parser_utils.o session.o message.o digest.o store.o tai.o avir.o clamd.o
MYSQL_OBJS =
RUNNING_USER = piler
RUNNING_GROUP = `id -gn $(RUNNING_USER)`
PILER_VERSION=0
PILER_REVISION=1
PILER_RELEASE=1
LIBPILER_VERSION=$(PILER_VERSION).$(PILER_REVISION).$(PILER_RELEASE)
MAKE = `which make`
INSTALL = /bin/ginstall -c
all: libpiler.a piler pilerconf test
install: install-piler
piler: piler.c libpiler.a
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ piler.c -lpiler $(LIBS) $(LDAP_LIBS) $(LIBDIR)
libpiler.a: $(OBJS) $(MYSQL_OBJS)
ar cr libpiler.a $(OBJS) $(MYSQL_OBJS)
ranlib libpiler.a
$(CC) -shared -Wl -o libpiler.so.$(LIBPILER_VERSION) $(OBJS) $(MYSQL_OBJS) $(LIBS) $(LDAP_LIBS)
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION)
pilerconf: pilerconf.c cfg.o misc.o tai.o
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $^ $(LIBS) $(LIBDIR)
test:
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o piletest $(srcdir)/test.c -lpiler $(LIBS) $(LDAP_LIBS) $(LIBDIR)
%.o: $(srcdir)/%.c
$(CC) $(CFLAGS) -fPIC $(INCDIR) $(DEFS) -c $< -o $@
install-piler:
$(INSTALL) -d $(DESTDIR)$(bindir)
$(INSTALL) -d $(DESTDIR)$(sbindir)
$(INSTALL) -d $(DESTDIR)$(libdir)
$(INSTALL) -m 0644 libpiler.a $(DESTDIR)$(libdir)
$(INSTALL) -m 0755 libpiler.so.$(LIBPILER_VERSION) $(DESTDIR)$(libdir)
(cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so)
(cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION))
$(INSTALL) -d $(DESTDIR)$(libexecdir)/piler
$(INSTALL) -d $(DESTDIR)$(datarootdir)/piler
$(INSTALL) -m 0755 piler $(DESTDIR)$(sbindir)
$(INSTALL) -m 0755 pilerconf $(DESTDIR)$(sbindir)
clean:
rm -f *.o *.a libpiler.so* piler pilerconf piletest
distclean: clean
rm -f Makefile

82
src/Makefile.in Normal file
View File

@ -0,0 +1,82 @@
SHELL = @SHELL@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
includedir = @includedir@
libdir = @libdir@
libexecdir = @libexecdir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
mandir = @mandir@
datarootdir = @datarootdir@
localstatedir = @localstatedir@
CC = @CC@
CFLAGS = @CFLAGS@ @CPPFLAGS@
DEFS = @defs@
INCDIR = -I. -I.. @INCDIR@ @mysql_includes@
LIBDIR = -L. @LIBDIR@ @LDFLAGS@
LIBS = @LIBS@ @mysql_libs@
OBJS = @OBJS@
MYSQL_OBJS = @mysql_obj@
RUNNING_USER = @RUNNING_USER@
RUNNING_GROUP = `@id_bin@ -gn $(RUNNING_USER)`
PILER_VERSION=0
PILER_REVISION=1
PILER_RELEASE=1
LIBPILER_VERSION=$(PILER_VERSION).$(PILER_REVISION).$(PILER_RELEASE)
MAKE = `which make`
INSTALL = @INSTALL@
all: libpiler.a piler pilerconf test
install: install-piler
piler: piler.c libpiler.a
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ piler.c -lpiler $(LIBS) $(LDAP_LIBS) $(LIBDIR) @LDFLAGS@ @libclamav_extra_libs@
libpiler.a: $(OBJS) $(MYSQL_OBJS)
ar cr libpiler.a $(OBJS) $(MYSQL_OBJS)
ranlib libpiler.a
$(CC) -shared -Wl -o libpiler.so.$(LIBPILER_VERSION) $(OBJS) $(MYSQL_OBJS) $(LIBS) $(LDAP_LIBS) @LDFLAGS@
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION)
pilerconf: pilerconf.c cfg.o misc.o tai.o
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $^ $(LIBS) $(LIBDIR)
test:
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o piletest $(srcdir)/test.c -lpiler $(LIBS) $(LDAP_LIBS) $(LIBDIR) @LDFLAGS@
%.o: $(srcdir)/%.c
$(CC) $(CFLAGS) -fPIC $(INCDIR) $(DEFS) -c $< -o $@
install-piler:
$(INSTALL) -d $(DESTDIR)$(bindir)
$(INSTALL) -d $(DESTDIR)$(sbindir)
$(INSTALL) -d $(DESTDIR)$(libdir)
$(INSTALL) -m 0644 libpiler.a $(DESTDIR)$(libdir)
$(INSTALL) -m 0755 libpiler.so.$(LIBPILER_VERSION) $(DESTDIR)$(libdir)
(cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so)
(cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION))
$(INSTALL) -d $(DESTDIR)$(libexecdir)/piler
$(INSTALL) -d $(DESTDIR)$(datarootdir)/piler
$(INSTALL) -m 0755 piler $(DESTDIR)$(sbindir)
$(INSTALL) -m 0755 pilerconf $(DESTDIR)$(sbindir)
clean:
rm -f *.o *.a libpiler.so* piler pilerconf piletest
distclean: clean
rm -f Makefile

79
src/av.h Normal file
View File

@ -0,0 +1,79 @@
/*
* av.h, SJ
*/
#ifndef _AV_H
#define _AV_H
#include "defs.h"
#include "cfg.h"
#define AV_OK 200
#define AV_VIRUS 403
#define AV_ERROR 501
// clamd stuff
#define CLAMD_RESP_CLEAN "OK"
#define CLAMD_RESP_INFECTED "FOUND"
#define CLAMD_RESP_ERROR "ERROR"
int clamd_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
int clamd_net_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
// Dr.Web stuff
#define DRWEB_RESP_VIRUS 0x20
#define DRWEB_VIRUS_HAS_FOUND_MESSAGE "Virus has been found in message. See drwebd.log for details"
int drweb_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
// avast! stuff
#define AVAST_READY "220"
#define AVAST_CMD_QUIT "QUIT\r\n"
#define AVAST_RESP_OK "200"
#define AVAST_RESP_ENGINE_ERROR "451"
#define AVAST_RESP_SYNTAX_ERROR "501"
#define AVAST_RESP_CLEAN "[+]"
#define AVAST_RESP_INFECTED "[L]"
int avast_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
int avast_cmd_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
// Kaspersky stuff
#define KAV_CMD_QUIT "QUIT\r\n"
#define KAV_READY "201 "
#define KAV_RESP_CLEAN "220 File is clean"
#define KAV_RESP_INFECTED "230 File is infected"
#define KAV_RESP_INFECTED_NAME "322-"
#define KAV_RESP_NOT_FOUND "525 File not found"
int kav_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
// avg stuff
#define AVG_READY "220"
#define AVG_CMD_QUIT "QUIT\r\n"
#define AVG_RESP_OK "200"
#define AVG_RESP_VIRUS "403"
#define AVG_RESP_NOT_FOUND "404"
#define AVG_RESP_ERROR "501"
#define AVG_NOT_FOUND 404
int avg_scan(char *tmpdir, char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
int moveMessageToQuarantine(struct session_data *sdata, struct __config *cfg);
void sendNotificationToPostmaster(struct session_data *sdata, char *rcpttoemail, char *fromemail, char *virusinfo, char *avengine, struct __config *cfg);
#endif /* _AV_H */

55
src/avir.c Normal file
View File

@ -0,0 +1,55 @@
/*
* avir.c, SJ
*/
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <piler.h>
int do_av_check(struct session_data *sdata, char *rcpttoemail, char *fromemail, char *virusinfo, struct __data *data, struct __config *cfg){
int rav = AVIR_OK;
char avengine[SMALLBUFSIZE];
if(sdata->need_scan == 0) return rav;
memset(avengine, 0, SMALLBUFSIZE);
#ifdef HAVE_LIBCLAMAV
const char *virname;
unsigned int options=0;
options = CL_SCAN_STDOPT | CL_SCAN_ARCHIVE | CL_SCAN_MAIL | CL_SCAN_OLE2;
if(cfg->use_libclamav_block_max_feature == 1) options |= CL_SCAN_BLOCKMAX;
if(cfg->clamav_block_encrypted_archives == 1) options |= CL_SCAN_BLOCKENCRYPTED;
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: trying to pass to libclamav", sdata->ttmpfile);
if(cl_scanfile(sdata->ttmpfile, &virname, NULL, data->engine, options) == CL_VIRUS){
memset(virusinfo, 0, SMALLBUFSIZE);
strncpy(virusinfo, virname, SMALLBUFSIZE-1);
rav = AVIR_VIRUS;
snprintf(avengine, SMALLBUFSIZE-1, "libClamAV");
}
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: virus info: '%s'", sdata->ttmpfile, virname);
#endif
#ifdef HAVE_CLAMD
if(strlen(cfg->clamd_addr) > 3 && cfg->clamd_port > 0){
if(clamd_net_scan(sdata->ttmpfile, avengine, virusinfo, cfg) == AV_VIRUS) rav = AVIR_VIRUS;
} else {
if(clamd_scan(sdata->ttmpfile, avengine, virusinfo, cfg) == AV_VIRUS) rav = AVIR_VIRUS;
}
#endif
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: done virus scanning", sdata->ttmpfile);
return rav;
}

98
src/boundary.c Normal file
View File

@ -0,0 +1,98 @@
/*
* boundary.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <piler.h>
/*
* append something to the boundary list if we have to
*/
int append_boundary(struct boundary **boundaries, char *p){
struct boundary *q, *t, *u=NULL;
q = *boundaries;
while(q){
if(strcmp(q->boundary_str, p) == 0)
return 0;
u = q;
q = q->r;
}
t = new_boundary(p);
if(t){
if(*boundaries == NULL)
*boundaries = t;
else if(u)
u->r = t;
return 1;
}
return -1;
}
/*
* create a new boundary structure
*/
struct boundary *new_boundary(char *s){
struct boundary *h=NULL;
if((h = malloc(sizeof(struct boundary))) == NULL)
return NULL;
strncpy(h->boundary_str, s, BOUNDARY_LEN-1);
h->r = NULL;
return h;
}
/*
* is this a boundary string?
*/
int is_boundary(struct boundary *boundaries, char *s){
struct boundary *p;
p = boundaries;
while(p != NULL){
if(strstr(s, p->boundary_str)) return 1;
p = p->r;
}
return 0;
}
/*
* free boundary list
*/
void free_boundary(struct boundary *b){
struct boundary *p;
while(b){
p = b->r;
//printf("free boundary: %s\n", b->boundary_str);
if(b)
free(b);
b = p;
}
}

14
src/boundary.h Normal file
View File

@ -0,0 +1,14 @@
/*
* boundary.h, SJ
*/
#ifndef _BOUNDARY_H
#define _BOUNDARY_H
int append_boundary(struct boundary **boundaries, char *p);
struct boundary *new_boundary(char *s);
int is_boundary(struct boundary *boundaries, char *s);
void free_boundary(struct boundary *b);
#endif /* _LIST_H */

282
src/cfg.c Normal file
View File

@ -0,0 +1,282 @@
/*
* cfg.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "cfg.h"
#include "misc.h"
#include "config.h"
int string_parser(char *src, char *target, int limit){
snprintf(target, limit, "%s", src);
return 0;
};
int multi_line_string_parser(char *src, char *target, int limit){
if(strlen(src) > 0 && strlen(target) + strlen(src) + 3 < limit){
strncat(target, src, limit);
strncat(target, "\r\n", limit);
return 0;
}
return 1;
};
int int_parser(char *src, int *target, int limit){
*target = strtol(src, (char **) NULL, 10);
return 0;
};
int float_parser(char *src, float *target, int limit){
*target = strtof(src, (char **) NULL);
return 0;
};
struct _parse_rule {
char *name;
char *type;
int(*parser)(char*,void*,int);
size_t offset;
char *def_val;
int limit;
};
/*
* all known configuration items in order
*/
struct _parse_rule config_parse_rules[] =
{
{ "backlog", "integer", (void*) int_parser, offsetof(struct __config, backlog), "20", sizeof(int)},
{ "clamd_addr", "string", (void*) string_parser, offsetof(struct __config, clamd_addr), "", MAXVAL-1},
{ "clamd_port", "integer", (void*) int_parser, offsetof(struct __config, clamd_port), "0", sizeof(int)},
{ "clamd_socket", "string", (void*) string_parser, offsetof(struct __config, clamd_socket), CLAMD_SOCKET, MAXVAL-1},
{ "deferdir", "string", (void*) string_parser, offsetof(struct __config, deferdir), DEFER_DIR, MAXVAL-1},
{ "hostid", "string", (void*) string_parser, offsetof(struct __config, hostid), HOSTID, MAXVAL-1},
{ "iv", "string", (void*) string_parser, offsetof(struct __config, iv), "", MAXVAL-1},
{ "listen_addr", "string", (void*) string_parser, offsetof(struct __config, listen_addr), "127.0.0.1", MAXVAL-1},
{ "listen_port", "integer", (void*) int_parser, offsetof(struct __config, listen_port), "10025", sizeof(int)},
{ "locale", "string", (void*) string_parser, offsetof(struct __config, locale), "", MAXVAL-1},
{ "max_connections", "integer", (void*) int_parser, offsetof(struct __config, max_connections), "30", sizeof(int)},
{ "max_requests_per_child", "integer", (void*) int_parser, offsetof(struct __config, max_requests_per_child), "200", sizeof(int)},
{ "memcached_servers", "string", (void*) string_parser, offsetof(struct __config, memcached_servers), "127.0.0.1", MAXVAL-1},
{ "memcached_ttl", "integer", (void*) int_parser, offsetof(struct __config, memcached_ttl), "86400", sizeof(int)},
{ "mysqlhost", "string", (void*) string_parser, offsetof(struct __config, mysqlhost), "", MAXVAL-1},
{ "mysqlport", "integer", (void*) int_parser, offsetof(struct __config, mysqlport), "", sizeof(int)},
{ "mysqlsocket", "string", (void*) string_parser, offsetof(struct __config, mysqlsocket), "/tmp/mysql.sock", MAXVAL-1},
{ "mysqluser", "string", (void*) string_parser, offsetof(struct __config, mysqluser), "clapf", MAXVAL-1},
{ "mysqlpwd", "string", (void*) string_parser, offsetof(struct __config, mysqlpwd), "", MAXVAL-1},
{ "mysqldb", "string", (void*) string_parser, offsetof(struct __config, mysqldb), "clapf", MAXVAL-1},
{ "mysql_connect_timeout", "integer", (void*) int_parser, offsetof(struct __config, mysql_connect_timeout), "2", sizeof(int)},
{ "number_of_worker_processes", "integer", (void*) int_parser, offsetof(struct __config, number_of_worker_processes), "10", sizeof(int)},
{ "pidfile", "string", (void*) string_parser, offsetof(struct __config, pidfile), PIDFILE, MAXVAL-1},
{ "piler_header_field", "string", (void*) string_parser, offsetof(struct __config, piler_header_field), "", MAXVAL-1},
{ "queuedir", "string", (void*) string_parser, offsetof(struct __config, queuedir), QUEUE_DIR, MAXVAL-1},
{ "session_timeout", "integer", (void*) int_parser, offsetof(struct __config, session_timeout), "420", sizeof(int)},
{ "sqlite3_pragma", "string", (void*) string_parser, offsetof(struct __config, sqlite3_pragma), "", MAXVAL-1},
{ "username", "string", (void*) string_parser, offsetof(struct __config, username), "piler", MAXVAL-1},
{ "use_antivirus", "integer", (void*) int_parser, offsetof(struct __config, use_antivirus), "1", sizeof(int)},
{ "verbosity", "integer", (void*) int_parser, offsetof(struct __config, verbosity), "1", sizeof(int)},
{ "workdir", "string", (void*) string_parser, offsetof(struct __config, workdir), WORK_DIR, MAXVAL-1},
{NULL, NULL, NULL, 0, 0}
};
/*
* parse configfile
*/
int parse_config_file(char *configfile, struct __config *target_cfg, struct _parse_rule *rules){
char line[MAXVAL], *chpos;
FILE *f;
if(!configfile) return -1;
f = fopen(configfile, "r");
if(!f) return -1;
while(fgets(&line[0], MAXVAL-1, f)){
if(line[0] == ';' || line[0] == '#') continue;
chpos = strchr(line, '=');
if(chpos){
trimBuffer(chpos+1);
*chpos = '\0';
int i = 0;
while(rules[i].name){
if(!strcmp(line, rules[i].name)) {
if(rules[i].parser(chpos+1, (char*)target_cfg + rules[i].offset, rules[i].limit)){
printf("failed to parse %s: \"%s\"\n", line, chpos+1);
}
break;
}
i++;
}
if(!rules[i].name) syslog(LOG_PRIORITY, "unknown key: \"%s\"", line);
}
}
fclose(f);
return 0;
}
int load_default_config(struct __config *cfg, struct _parse_rule *rules){
int i=0;
while(rules[i].name){
rules[i].parser(rules[i].def_val, (char*)cfg + rules[i].offset, rules[i].limit);
i++;
}
return 0;
}
/*
* read configuration file variables
*/
struct __config read_config(char *configfile){
struct __config cfg;
/* reset config structure and fill it with defaults */
memset((char *)&cfg, 0, sizeof(struct __config));
load_default_config(&cfg, config_parse_rules);
/* parse the config file */
if(parse_config_file(configfile, &cfg, config_parse_rules) == -1) printf("error parsing the configfile: %s\n", configfile);
return cfg;
}
/*
* print a single configuration item as key=value
*/
void print_config_item(struct __config *cfg, struct _parse_rule *rules, int i){
int j;
float f;
char *p, buf[MAXVAL];
p = (char*)cfg + rules[i].offset;
if(strcmp(rules[i].type, "integer") == 0){
memcpy((char*)&j, p, sizeof(int));
printf("%s=%d\n", rules[i].name, j);
}
else if(strcmp(rules[i].type, "float") == 0){
memcpy((char*)&f, p, sizeof(float));
printf("%s=%.4f\n", rules[i].name, f);
}
else if(strcmp(rules[i].type, "multi_line_string") == 0){
j = 0;
do {
p = split_str(p, "\r\n", buf, MAXVAL-1);
if(p || !j) printf("%s=%s\n", rules[i].name, buf);
j++;
} while(p);
}
else {
trimBuffer(p);
printf("%s=%s\n", rules[i].name, p);
}
}
/*
* print all known configuration items
*/
void print_config_all(struct __config *cfg, char *key){
int i=0;
struct _parse_rule *rules;
rules = &config_parse_rules[0];
while(rules[i].name){
if(key == NULL){
print_config_item(cfg, rules, i);
}
else {
if(strcmp(key, rules[i].name) == 0)
print_config_item(cfg, rules, i);
}
i++;
}
}
/*
* print all configuration items found in configfile
*/
void print_config(char *configfile, struct __config *cfg){
FILE *f;
char line[MAXVAL], *chpos, previtem[MAXVAL];
struct _parse_rule *rules;
if(!configfile) return;
f = fopen(configfile, "r");
if(!f) return;
rules = &config_parse_rules[0];
memset(previtem, 0, MAXVAL);
while(fgets(&line[0], MAXVAL-1, f)){
if(line[0] == ';' || line[0] == '#') continue;
chpos = strchr(line, '=');
if(chpos){
trimBuffer(chpos+1);
*chpos = '\0';
int i = 0;
while(rules[i].name){
if(strcmp(line, rules[i].name) == 0) {
if(strcmp(line, previtem)) print_config_item(cfg, rules, i);
snprintf(previtem, MAXVAL-1, "%s", line);
break;
}
i++;
}
if(!rules[i].name) printf("unknown key: \"%s\" \n", line);
}
}
fclose(f);
}

66
src/cfg.h Normal file
View File

@ -0,0 +1,66 @@
/*
* cfg.h, SJ
*/
#ifndef _CFG_H
#define _CFG_H
#include "config.h"
struct __config {
char username[MAXVAL];
char hostid[MAXVAL];
char pidfile[MAXVAL];
char listen_addr[MAXVAL];
int listen_port;
char clamd_addr[MAXVAL];
int clamd_port;
char clamd_socket[MAXVAL];
int use_antivirus;
char memcached_servers[MAXVAL];
int memcached_ttl;
int number_of_worker_processes;
int max_requests_per_child;
int max_connections;
int backlog;
char workdir[MAXVAL];
char queuedir[MAXVAL];
char deferdir[MAXVAL];
int verbosity;
char locale[MAXVAL];
int session_timeout;
char piler_header_field[MAXVAL];
unsigned char key[KEYLEN];
unsigned char iv[MAXVAL];
// mysql stuff
char mysqlhost[MAXVAL];
int mysqlport;
char mysqlsocket[MAXVAL];
char mysqluser[MAXVAL];
char mysqlpwd[MAXVAL];
char mysqldb[MAXVAL];
int mysql_connect_timeout;
// sqlite3 stuff
char sqlite3[MAXVAL];
char sqlite3_pragma[MAXVAL];
};
#endif /* _CFG_H */

135
src/clamd.c Normal file
View File

@ -0,0 +1,135 @@
/*
* clamd.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <piler.h>
int clamd_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg){
int s, n;
char *p, *q, buf[MAXBUFSIZE], scan_cmd[SMALLBUFSIZE];
struct sockaddr_un server;
memset(avinfo, 0, SMALLBUFSIZE);
chmod(tmpfile, 0644);
strcpy(server.sun_path, cfg->clamd_socket);
server.sun_family = AF_UNIX;
if((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1){
syslog(LOG_PRIORITY, "ERR: create socket");
return AV_ERROR;
}
if(connect(s, (struct sockaddr *)&server, strlen(server.sun_path) + sizeof (server.sun_family)) == -1){
syslog(LOG_PRIORITY, "CLAMD ERR: connect to %s", cfg->clamd_socket);
return AV_ERROR;
}
/* issue the SCAN command with full path to the temporary directory */
memset(scan_cmd, 0, SMALLBUFSIZE);
snprintf(scan_cmd, SMALLBUFSIZE-1, "SCAN %s/%s\r\n", cfg->workdir, tmpfile);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: CLAMD CMD: %s", tmpfile, scan_cmd);
send(s, scan_cmd, strlen(scan_cmd), 0);
/* read CLAMD's answers */
n = recvtimeout(s, buf, MAXBUFSIZE, TIMEOUT);
close(s);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: CLAMD DEBUG: %d %s", tmpfile, n, buf);
if(strcasestr(buf, CLAMD_RESP_INFECTED)){
p = strchr(buf, ' ');
if(p){
q = strrchr(p, ' ');
if(q){
*q = '\0';
p++;
strncpy(avinfo, p, SMALLBUFSIZE-1);
}
}
return AV_VIRUS;
}
return AV_OK;
}
int clamd_net_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg){
int n, psd;
char *p, *q, buf[MAXBUFSIZE], scan_cmd[SMALLBUFSIZE];
struct in_addr addr;
struct sockaddr_in clamd_addr;
memset(avinfo, 0, SMALLBUFSIZE);
chmod(tmpfile, 0644);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: trying to pass to clamd", tmpfile);
if((psd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
syslog(LOG_PRIORITY, "%s: ERR: create socket", tmpfile);
return AV_ERROR;
}
clamd_addr.sin_family = AF_INET;
clamd_addr.sin_port = htons(cfg->clamd_port);
inet_aton(cfg->clamd_addr, &addr);
clamd_addr.sin_addr.s_addr = addr.s_addr;
bzero(&(clamd_addr.sin_zero), 8);
if(connect(psd, (struct sockaddr *)&clamd_addr, sizeof(struct sockaddr)) == -1){
syslog(LOG_PRIORITY, "%s: CLAMD ERR: connect to %s %d", tmpfile, cfg->clamd_addr, cfg->clamd_port);
return AV_ERROR;
}
memset(scan_cmd, 0, SMALLBUFSIZE);
snprintf(scan_cmd, SMALLBUFSIZE-1, "SCAN %s/%s\r\n", cfg->workdir, tmpfile);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: CLAMD CMD: %s", tmpfile, scan_cmd);
send(psd, scan_cmd, strlen(scan_cmd), 0);
n = recvtimeout(psd, buf, MAXBUFSIZE, TIMEOUT);
close(psd);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: CLAMD DEBUG: %d %s", tmpfile, n, buf);
if(strcasestr(buf, CLAMD_RESP_INFECTED)){
p = strchr(buf, ' ');
if(p){
q = strrchr(p, ' ');
if(q){
*q = '\0';
p++;
strncpy(avinfo, p, SMALLBUFSIZE-1);
}
}
return AV_VIRUS;
}
return AV_OK;
}

107
src/config.h Normal file
View File

@ -0,0 +1,107 @@
/*
* config.h, SJ
*/
#ifndef _CONFIG_H
#define _CONFIG_H
#include <syslog.h>
#include "piler-config.h"
#include "params.h"
#define PROGNAME "piler"
#define VERSION "0.1.2"
#define PROGINFO VERSION ", Janos SUTO <sj@acts.hu>\n\n" CONFIGURE_PARAMS "\n\nSend bugs/issues to https://jira.acts.hu:8443/\n"
#define HOSTID "mailarchiver"
#define CONFIG_FILE CONFDIR "/piler.conf"
#define WORK_DIR DATADIR "/spool/piler/tmp"
#define QUEUE_DIR DATADIR "/spool/piler/new"
#define DEFER_DIR DATADIR "/spool/piler/deferred"
#define CLAMD_SOCKET "/tmp/clamd"
#define PIDFILE "/var/run/piler/piler.pid"
#define QUARANTINELEN 255
#define TIMEOUT 60
#define TIMEOUT_USEC 500000
#define SESSION_TIMEOUT 420
#define MAXBUFSIZE 8192
#define SMALLBUFSIZE 512
#define BIGBUFSIZE 65535
#define MAXVAL 256
#define RANDOM_POOL "/dev/urandom"
#define RND_STR_LEN 36
#define BUFLEN 32
#define IPLEN 16+1
#define KEYLEN 56
#define CRLF "\n"
#define MEMCACHED_CLAPF_PREFIX "_piler"
#define MAX_MEMCACHED_KEY_LEN 250
#define MEMCACHED_SUCCESS 0
#define MEMCACHED_FAILURE 1
#define MEMCACHED_COUNTERS_LAST_UPDATE MEMCACHED_CLAPF_PREFIX ":counters_last_update"
#define MEMCACHED_MSGS_RCVD MEMCACHED_CLAPF_PREFIX ":rcvd"
#define MEMCACHED_MSGS_VIRUS MEMCACHED_CLAPF_PREFIX ":virus"
#define MEMCACHED_MSGS_DUPLICATE MEMCACHED_CLAPF_PREFIX ":duplicate"
#define LOG_PRIORITY LOG_INFO
#define _LOG_INFO 3
#define _LOG_DEBUG 5
#define MAX_RCPT_TO 128
#ifdef HAVE_SQLITE3
#define MAX_KEY_VAL 9223372036854775807ULL
#else
#define MAX_KEY_VAL 18446744073709551615ULL
#endif
#define MIN_WORD_LEN 3
#define MAX_WORD_LEN 25
#define MAX_TOKEN_LEN 4*MAX_WORD_LEN
#define URL_LEN 48
#define DELIMITER ' '
#define SPAMINESS_HEADER_FIELD "X-Clapf-spamicity: "
#define BOUNDARY_LEN 255
#define JUNK_REPLACEMENT_CHAR 'j'
#define MAX_ATTACHMENTS 8
/* SQL stuff */
#define SQL_SPHINX_TABLE "sph_index"
#define SQL_METADATA_TABLE "metadata"
#define SQL_HEADER_TABLE "header"
#define SQL_BODY_TABLE "body"
#define SQL_COUNTER_TABLE "counter"
/* TRE stuff */
#define NUM_OF_REGEXES 20
/* Error codes */
#define OK 0
#define ERR 1
#define ERR_EXISTS 2
#define AVIR_OK 0
#define AVIR_VIRUS 1
#endif /* _CONFIG_H */

121
src/counters.c Normal file
View File

@ -0,0 +1,121 @@
/*
* counters.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <syslog.h>
#include <piler.h>
struct __counters loadCounters(struct session_data *sdata, struct __config *cfg){
char buf[SMALLBUFSIZE];
struct __counters counters;
bzero(&counters, sizeof(counters));
snprintf(buf, SMALLBUFSIZE-1, "SELECT rcvd, virus, duplicate FROM %s", SQL_COUNTER_TABLE);
#ifdef NEED_MYSQL
MYSQL_RES *res;
MYSQL_ROW row;
if(mysql_real_query(&(sdata->mysql), buf, strlen(buf)) == 0){
res = mysql_store_result(&(sdata->mysql));
if(res != NULL){
row = mysql_fetch_row(res);
if(row){
counters.c_rcvd = strtoull(row[0], NULL, 10);
counters.c_virus = strtoull(row[1], NULL, 10);
counters.c_duplicate = strtoull(row[2], NULL, 10);
}
mysql_free_result(res);
}
}
#endif
return counters;
}
void updateCounters(struct session_data *sdata, struct __data *data, struct __counters *counters, struct __config *cfg){
char buf[MAXBUFSIZE];
#ifdef HAVE_MEMCACHED
unsigned long long mc, rcvd;
struct __counters c;
char key[MAX_MEMCACHED_KEY_LEN];
unsigned int flags=0;
if(cfg->update_counters_to_memcached == 1){
/* increment counters to memcached */
if(memcached_increment(&(data->memc), MEMCACHED_MSGS_RCVD, strlen(MEMCACHED_MSGS_RCVD), counters->c_rcvd, &mc) == MEMCACHED_SUCCESS){
rcvd = mc;
if(counters->c_ham > 0) memcached_increment(&(data->memc), MEMCACHED_MSGS_HAM, strlen(MEMCACHED_MSGS_HAM), counters->c_ham, &mc);
if(counters->c_virus > 0) memcached_increment(&(data->memc), MEMCACHED_MSGS_VIRUS, strlen(MEMCACHED_MSGS_VIRUS), counters->c_virus, &mc);
if(counters->c_duplicate > 0) memcached_increment(&(data->memc), MEMCACHED_MSGS_DUPLICATE, strlen(MEMCACHED_MSGS_DUPLICATE), counters->c_duplicate, &mc);
bzero(&c, sizeof(c));
snprintf(buf, MAXBUFSIZE-1, "%s %s %s %s", MEMCACHED_MSGS_RCVD, MEMCACHED_MSGS_VIRUS, MEMCACHED_MSGS_DUPLICATE, MEMCACHED_COUNTERS_LAST_UPDATE);
if(memcached_mget(&(data->memc), buf) == MEMCACHED_SUCCESS){
while((memcached_fetch_result(&(data->memc), &key[0], &buf[0], &flags))){
if(!strcmp(key, MEMCACHED_MSGS_RCVD)) c.c_rcvd = strtoull(buf, NULL, 10);
else if(!strcmp(key, MEMCACHED_MSGS_VIRUS)) c.c_virus = strtoull(buf, NULL, 10);
else if(!strcmp(key, MEMCACHED_MSGS_DUPLICATE)) c.c_duplicate = strtoull(buf, NULL, 10);
else if(!strcmp(key, MEMCACHED_COUNTERS_LAST_UPDATE)) mc = strtoull(buf, NULL, 10);
}
if(sdata->now - mc > cfg->memcached_to_db_interval && c.c_rcvd > 0 && c.c_rcvd >= rcvd){
snprintf(buf, SMALLBUFSIZE-1, "%ld", sdata->now); memcached_set(&(data->memc), MEMCACHED_COUNTERS_LAST_UPDATE, strlen(MEMCACHED_COUNTERS_LAST_UPDATE), buf, strlen(buf), 0, 0);
snprintf(buf, SMALLBUFSIZE-1, "UPDATE `%s` SET rcvd=%llu, virus=%llu, duplicate=%llu", c.c_rcvd, c.c_virus, c.c_duplicate);
//if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: update counters: %s", sdata->ttmpfile, buf);
goto EXEC_SQL;
}
}
}
else {
c = loadCounters(sdata, cfg);
snprintf(buf, SMALLBUFSIZE-1, "%ld", sdata->now); memcached_add(&(data->memc), MEMCACHED_COUNTERS_LAST_UPDATE, strlen(MEMCACHED_COUNTERS_LAST_UPDATE), buf, strlen(buf), 0, 0);
snprintf(buf, SMALLBUFSIZE-1, "%llu", c.c_virus + counters->c_virus); memcached_add(&(data->memc), MEMCACHED_MSGS_VIRUS, strlen(MEMCACHED_MSGS_VIRUS), buf, strlen(buf), 0, 0);
snprintf(buf, SMALLBUFSIZE-1, "%llu", c.c_rcvd + counters->c_rcvd); memcached_add(&(data->memc), MEMCACHED_MSGS_RCVD, strlen(MEMCACHED_MSGS_RCVD), buf, strlen(buf), 0, 0);
snprintf(buf, SMALLBUFSIZE-1, "%llu", c.c_duplicate + counters->c_duplicate); memcached_add(&(data->memc), MEMCACHED_MSGS_DUPLICATE, strlen(MEMCACHED_MSGS_DUPLICATE), buf, strlen(buf), 0, 0);
}
}
else {
#endif
snprintf(buf, SMALLBUFSIZE-1, "UPDATE `%s` SET rcvd=rcvd+%llu, virus=virus+%llu, duplicate=duplicate+%llu", SQL_COUNTER_TABLE, counters->c_rcvd, counters->c_virus, counters->c_duplicate);
//if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: update counters: %s", sdata->ttmpfile, buf);
#ifdef HAVE_MEMCACHED
EXEC_SQL:
#endif
#ifdef NEED_MYSQL
mysql_real_query(&(sdata->mysql), buf, strlen(buf));
#endif
#ifdef HAVE_MEMCACHED
}
#endif
}

282
src/decoder.c Normal file
View File

@ -0,0 +1,282 @@
/*
* decoder.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "decoder.h"
#include "htmlentities.h"
#include "config.h"
static int b64[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};
static char hex_table[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static int compmi(const void *m1, const void *m2){
struct mi *mi1 = (struct mi *) m1;
struct mi *mi2 = (struct mi *) m2;
return strcmp(mi1->entity, mi2->entity);
}
void sanitiseBase64(char *s){
char *p1;
if(s == NULL) return;
for(; *s; s++){
if(b64[(unsigned int)(*s & 0xFF)] == 255){
for(p1 = s; p1[0] != '\0'; p1++)
p1[0] = p1[1];
}
}
}
int decodeBase64(char *p){
int i, j, n[4], k1, k2, len=0;
char s[5], s2[3], puf[MAXBUFSIZE];
if(strlen(p) < 4 || strlen(p) > MAXBUFSIZE/2)
return 0;
for(i=0; i<strlen(p); i++){
memcpy(s, p+i, 4);
s[4] = '\0';
i += 3;
if(strlen(s) == 4){
memset(s2, 0, 3);
for(j=0; j<4; j++){
k1 = s[j];
n[j] = b64[k1];
}
k1 = n[0]; k1 = k1 << 2;
k2 = n[1]; k2 = k2 >> 4;
s2[0] = k1 | k2;
k1 = (n[1] & 0x0F) << 4;
k2 = n[2]; k2 = k2 >> 2;
s2[1] = k1 | k2;
k1 = n[2] << 6;
k2 = n[3] >> 0;
s2[2] = k1 | k2;
// this is binary safe
memcpy(puf+len, s2, 3);
len += 3;
}
}
*(puf+len) = '\0';
snprintf(p, MAXBUFSIZE-1, "%s", puf);
return len;
}
void decodeUTF8(char *p){
int i, k=0, a, b;
unsigned char c, c1, c2;
if(p == NULL) return;
for(i=0; i<strlen(p); i++){
c = p[i];
if(p[i] == '=' && isxdigit(p[i+1]) && isxdigit(p[i+2]) &&
p[i+3] == '=' && isxdigit(p[i+4]) && isxdigit(p[i+5])){
a = p[i+1];
b = p[i+2];
c1 = 16 * hex_table[a] + hex_table[b];
a = p[i+4];
b = p[i+5];
c2 = 16 * hex_table[a] + hex_table[b];
if(c1 >= 192 && c1 <= 223){
c = 64 * (c1 - 192) + c2 - 128;
i += 5;
}
}
if(c >= 192 && c <= 223){
c = 64 * (c - 192) + p[i+1] - 128;
i++;
}
p[k] = c;
k++;
}
p[k] = '\0';
}
void decodeQP(char *p){
int i, k=0, a, b;
char c;
if(p == NULL) return;
for(i=0; i<strlen((char*)p); i++){
c = p[i];
if(p[i] == '=' && isxdigit(p[i+1]) && isxdigit(p[i+2])){
a = p[i+1];
b = p[i+2];
c = 16 * hex_table[a] + hex_table[b];
i += 2;
}
p[k] = c;
k++;
}
p[k] = '\0';
}
void decodeHTML(char *s){
char *p;
int i, c, k=0, unknown='q';
struct mi key, *res;
if(s == NULL) return;
for(i=0; i<strlen(s); i++){
c = s[i];
if(*(s+i) == '&'){
p = strchr(s+i, ';');
if(p){
*p = '\0';
if(*(s+i+1) == '#'){
c = atoi(s+i+2);
if(c == 0) c = unknown;
}
else {
key.entity = s+i;
res = bsearch(&key, htmlentities, NUM_OF_HTML_ENTITIES, sizeof(struct mi), compmi);
if(res && res->val <= 255) c = res->val;
else c = unknown;
}
i += strlen(s+i);
*p = ';';
}
}
s[k] = c;
k++;
}
s[k] = '\0';
}
void decodeURL(char *p){
int i, c, k=0, a, b;
if(p == NULL) return;
for(i=0; i<strlen(p); i++){
switch(p[i]){
case '+':
c = ' ';
break;
case '%':
if(isxdigit(p[i+1]) && isxdigit(p[i+2])){
a = p[i+1];
b = p[i+2];
c = 16 * hex_table[a] + hex_table[b];
i += 2;
}
else
c = p[i];
break;
default:
c = p[i];
break;
}
p[k] = c;
k++;
}
p[k] = '\0';
}

15
src/decoder.h Normal file
View File

@ -0,0 +1,15 @@
/*
* decoder.h, SJ
*/
#ifndef _DECODER_H
#define _DECODER_H
void sanitiseBase64(char *s);
int decodeBase64(char *p);
void decodeUTF8(char *p);
void decodeQP(char *p);
void decodeHTML(char *p);
void decodeURL(char *p);
#endif /* _DECODER_H */

205
src/defs.h Normal file
View File

@ -0,0 +1,205 @@
/*
* defs.h, SJ
*/
#ifndef _DEFS_H
#define _DEFS_H
#ifdef NEED_MYSQL
#include <mysql.h>
#endif
#ifdef NEED_SQLITE3
#include <sqlite3.h>
/* for older versions of sqlite3 do not have the sqlite3_prepare_v2() function, 2009.12.30, SJ */
#if SQLITE_VERSION_NUMBER < 3006000
#define sqlite3_prepare_v2 sqlite3_prepare
#endif
#endif
#ifdef HAVE_TRE
#include <tre/tre.h>
#include <tre/regex.h>
#endif
#include <openssl/sha.h>
#include "config.h"
#define MSG_UNDEF -1
#define MSG_BODY 0
#define MSG_RECEIVED 1
#define MSG_FROM 2
#define MSG_TO 3
#define MSG_CC 4
#define MSG_SUBJECT 5
#define MSG_CONTENT_TYPE 6
#define MSG_CONTENT_TRANSFER_ENCODING 7
#define MSG_CONTENT_DISPOSITION 8
#define MSG_MESSAGE_ID 9
#define MAXHASH 8171
#define BASE64_RATIO 1.33333333
#define DIGEST_LENGTH SHA256_DIGEST_LENGTH
#define UNDEF 0
#define READY 1
#define BUSY 2
#define PROCESSED 3
struct attachment {
int size;
char type[SMALLBUFSIZE];
char filename[SMALLBUFSIZE];
};
struct url {
char url_str[URL_LEN];
struct url *r;
};
struct boundary {
char boundary_str[BOUNDARY_LEN];
struct boundary *r;
};
struct _state {
int message_state;
int is_header;
int textplain;
int texthtml;
int octetstream;
int message_rfc822;
int base64;
int has_base64;
int utf8;
int qp;
int htmltag;
int style;
int skip_html;
int ipcnt;
int has_to_dump;
int fd;
int num_of_msword;
int num_of_images;
int realbinary;
int content_type_is_set;
int train_mode;
unsigned long c_shit;
unsigned long l_shit;
unsigned long line_num;
char ip[SMALLBUFSIZE];
char hostname[SMALLBUFSIZE];
char miscbuf[MAX_TOKEN_LEN];
char qpbuf[MAX_TOKEN_LEN];
char attachedfile[RND_STR_LEN+SMALLBUFSIZE];
char from[SMALLBUFSIZE];
char message_id[SMALLBUFSIZE];
unsigned long n_token;
unsigned long n_subject_token;
unsigned long n_body_token;
unsigned long n_chain_token;
struct url *urls;
int found_our_signo;
struct boundary *boundaries;
int n_attachments;
struct attachment attachments[MAX_ATTACHMENTS];
char b_from[SMALLBUFSIZE], b_to[SMALLBUFSIZE], b_subject[MAXBUFSIZE], b_body[BIGBUFSIZE];
};
struct session_data {
char ttmpfile[SMALLBUFSIZE], tre;
char mailfrom[SMALLBUFSIZE], rcptto[MAX_RCPT_TO][SMALLBUFSIZE], client_addr[SMALLBUFSIZE];
char acceptbuf[SMALLBUFSIZE];
char whitelist[MAXBUFSIZE], blacklist[MAXBUFSIZE];
int fd, hdr_len, tot_len, num_of_rcpt_to, rav;
int need_scan;
float __acquire, __parsed, __av, __store, __compress, __encrypt;
SHA256_CTX context;
unsigned char md[DIGEST_LENGTH];
char bodydigest[2*DIGEST_LENGTH+1];
time_t now, sent;
#ifdef NEED_MYSQL
MYSQL mysql;
#endif
#ifdef NEED_SQLITE3
sqlite3 *db;
#endif
};
#ifdef HAVE_MEMCACHED
#include <stdbool.h>
#include <netinet/in.h>
struct flags {
bool no_block:1;
bool no_reply:1;
bool tcp_nodelay:1;
bool tcp_keepalive:1;
};
struct memcached_server {
struct flags flags;
int fd;
unsigned int snd_timeout;
unsigned int rcv_timeout;
int send_size;
int recv_size;
unsigned int tcp_keepidle;
int last_read_bytes;
char *result;
char buf[MAXBUFSIZE];
struct sockaddr_in addr;
char server_ip[16];
int server_port;
char initialised;
};
#endif
struct __data {
struct url *blackhole;
#ifdef HAVE_LIBCLAMAV
struct cl_engine *engine;
#endif
#ifdef HAVE_TRE
regex_t pregs[NUM_OF_REGEXES];
int n_regex;
#endif
#ifdef HAVE_MEMCACHED
struct memcached_server memc;
#endif
};
struct __counters {
unsigned long long c_rcvd;
unsigned long long c_virus;
unsigned long long c_duplicate;
};
#endif /* _DEFS_H */

60
src/digest.c Normal file
View File

@ -0,0 +1,60 @@
/*
* digest.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <piler.h>
#include <openssl/evp.h>
int make_body_digest(struct session_data *sdata){
int i=0, n, fd;
char *p, *body=NULL;
unsigned char buf[MAXBUFSIZE];
memset(sdata->bodydigest, 0, 2*DIGEST_LENGTH+1);
SHA256_Init(&(sdata->context));
fd = open(sdata->ttmpfile, O_RDONLY);
if(fd == -1) return -1;
while((n = read(fd, buf, MAXBUFSIZE)) > 0){
body = (char *)&buf[0];
i++;
if(i == 1){
p = strstr((char*)buf, "\n\n");
if(p){
body = p+2;
n = strlen(body);
} else {
p = strstr((char*)buf, "\n\r\n");
if(p){
body = p+3;
n = strlen(body);
}
}
}
SHA256_Update(&(sdata->context), body, n);
}
close(fd);
SHA256_Final(sdata->md, &(sdata->context));
for(i=0;i<DIGEST_LENGTH;i++)
snprintf(sdata->bodydigest + i*2, 2*DIGEST_LENGTH, "%02x", sdata->md[i]);
return 0;
}

81
src/dirs.c Normal file
View File

@ -0,0 +1,81 @@
/*
* dirs.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include <piler.h>
void createdir(char *path, uid_t uid, gid_t gid, mode_t mode);
void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid){
char *p;
p = strrchr(cfg->workdir, '/');
if(p){
*p = '\0';
createdir(cfg->workdir, uid, gid, 0755);
*p = '/';
}
createdir(cfg->workdir, uid, gid, 0711);
p = strrchr(cfg->queuedir, '/');
if(p){
*p = '\0';
createdir(cfg->queuedir, uid, gid, 0755);
*p = '/';
}
createdir(cfg->queuedir, uid, gid, 0700);
p = strrchr(cfg->deferdir, '/');
if(p){
*p = '\0';
createdir(cfg->deferdir, uid, gid, 0755);
*p = '/';
}
createdir(cfg->deferdir, uid, gid, 0700);
p = strrchr(cfg->sqlite3, '/');
if(p){
*p = '\0';
createdir(cfg->sqlite3, uid, gid, 0755);
*p = '/';
}
p = strrchr(cfg->pidfile, '/');
if(p){
*p = '\0';
createdir(cfg->pidfile, uid, gid, 0755);
*p = '/';
}
}
void createdir(char *path, uid_t uid, gid_t gid, mode_t mode){
struct stat st;
int rc;
if(strlen(path) > 2){
if(path[strlen(path)-1] == '/') path[strlen(path)-1] = '\0';
if(stat(path, &st)){
if(mkdir(path, mode) == 0){
rc = chown(path, uid, gid);
syslog(LOG_PRIORITY, "created directory: *%s*", path);
}
else syslog(LOG_PRIORITY, "could not create directory: *%s*", path);
}
}
}

31
src/errmsg.h Normal file
View File

@ -0,0 +1,31 @@
/*
* errmsg.h, SJ
*/
#ifndef _ERRMSG_H
#define _ERRMSG_H
#define ERR_CANNOT_READ_FROM_POOL "ERR: cannot read from pool"
#define ERR_SIGACTION "sigaction failed"
#define ERR_OPEN_SOCKET "ERR: cannot open socket"
#define ERR_SET_SOCK_OPT "ERR: cannot set socket option"
#define ERR_BIND_TO_PORT "ERR: cannot bind to port"
#define ERR_LISTEN "ERR: cannot listen"
#define ERR_SETUID "ERR: setuid()"
#define ERR_SETGID "ERR: setgid()"
#define ERR_SELECT "ERR: select()"
#define ERR_CHDIR "ERR: chdir() to working directory failed"
#define ERR_OPEN_TMP_FILE "ERR: opening a tempfile"
#define ERR_TIMED_OUT "ERR: timed out"
#define ERR_FORK_FAILED "ERR: cannot fork()"
#define ERR_MYSQL_CONNECT "Cannot connect to mysql server"
#define ERR_PSQL_CONNECT "Cannot connect to PSql server"
#define ERR_SQLITE3_OPEN "Cannot open sqlite3 database"
#define ERR_SQL_DATA "No valid data from sql table"
#define ERR_NON_EXISTENT_USER "ERR: non existent user in config file, see the 'username' variable"
#define ERR_READING_KEY "ERR: reading cipher key"
#endif /* _ERRMSG_H */

38
src/html.h Normal file
View File

@ -0,0 +1,38 @@
struct html_tag {
unsigned char length;
char *entity;
};
#define NUM_OF_SKIP_TAGS2 10
struct html_tag skip_html_tags2[] = {
{ 4, "html" },
{ 5, "/html" },
{ 5, "/body" },
{ 4, "meta" },
{ 4, "head" },
{ 5, "/head" },
{ 5, "style" },
{ 6, "/style" },
{ 3, "div" },
{ 4, "/div" }
};
#define NUM_OF_SKIP_TAGS 11
struct html_tag skip_html_tags[] = {
{ 5, "style" },
{ 4, "dir=" },
{ 8, "content=" },
{ 5, "name=" },
{ 3, "id=" },
{ 2, "v:" },
{ 6, "class=" },
{ 5, "xmlns" },
{ 10, "http-equiv" },
{ 7, "spidmax" },
{ 5, "data=" }
};

288
src/htmlentities.h Normal file
View File

@ -0,0 +1,288 @@
/*
* htmlentities.h, SJ
*
* taken from dspam's src/decoder.c, then qsort()'ed:
qsort(htmlentities, NUM_OF_HTML_ENTITIES, sizeof(struct mi), compmi);
for(i=0; i<NUM_OF_HTML_ENTITIES; i++){
printf(" { %d, \"%s\" },\n", htmlentities[i].val, htmlentities[i].entity);
}
*/
#ifndef _HTMLENTITIES_H
#define _HTMLENTITIES_H
#define NUM_OF_HTML_ENTITIES 258
struct mi {
unsigned int val;
char *entity;
};
struct mi htmlentities[] = {
{ 198, "&AElig" },
{ 193, "&Aacute" },
{ 194, "&Acirc" },
{ 192, "&Agrave" },
{ 913, "&Alpha" },
{ 197, "&Aring" },
{ 195, "&Atilde" },
{ 196, "&Auml" },
{ 914, "&Beta" },
{ 199, "&Ccedil" },
{ 935, "&Chi" },
{ 8225, "&Dagger" },
{ 916, "&Delta" },
{ 208, "&ETH" },
{ 201, "&Eacute" },
{ 202, "&Ecirc" },
{ 200, "&Egrave" },
{ 917, "&Epsilon" },
{ 919, "&Eta" },
{ 203, "&Euml" },
{ 915, "&Gamma" },
{ 205, "&Iacute" },
{ 206, "&Icirc" },
{ 204, "&Igrave" },
{ 921, "&Iota" },
{ 207, "&Iuml" },
{ 922, "&Kappa" },
{ 923, "&Lambda" },
{ 924, "&Mu" },
{ 209, "&Ntilde" },
{ 925, "&Nu" },
{ 338, "&OElig" },
{ 211, "&Oacute" },
{ 212, "&Ocirc" },
{ 210, "&Ograve" },
{ 937, "&Omega" },
{ 927, "&Omicron" },
{ 216, "&Oslash" },
{ 213, "&Otilde" },
{ 214, "&Ouml" },
{ 934, "&Phi" },
{ 928, "&Pi" },
{ 8243, "&Prime" },
{ 936, "&Psi" },
{ 929, "&Rho" },
{ 352, "&Scaron" },
{ 931, "&Sigma" },
{ 222, "&THORN" },
{ 932, "&Tau" },
{ 920, "&Theta" },
{ 218, "&Uacute" },
{ 219, "&Ucirc" },
{ 217, "&Ugrave" },
{ 933, "&Upsilon" },
{ 220, "&Uuml" },
{ 926, "&Xi" },
{ 221, "&Yacute" },
{ 376, "&Yuml" },
{ 918, "&Zeta" },
{ 225, "&aacute" },
{ 226, "&acirc" },
{ 180, "&acute" },
{ 230, "&aelig" },
{ 224, "&agrave" },
{ 8501, "&alefsym" },
{ 945, "&alpha" },
{ 38, "&amp" },
{ 38, "&amp" },
{ 8743, "&and" },
{ 8736, "&ang" },
{ 39, "&apos" },
{ 229, "&aring" },
{ 8776, "&asymp" },
{ 227, "&atilde" },
{ 228, "&auml" },
{ 8222, "&bdquo" },
{ 946, "&beta" },
{ 166, "&brvbar" },
{ 8226, "&bull" },
{ 8745, "&cap" },
{ 231, "&ccedil" },
{ 184, "&cedil" },
{ 162, "&cent" },
{ 967, "&chi" },
{ 710, "&circ" },
{ 9827, "&clubs" },
{ 8773, "&cong" },
{ 169, "&copy" },
{ 8629, "&crarr" },
{ 8746, "&cup" },
{ 164, "&curren" },
{ 8659, "&dArr" },
{ 8224, "&dagger" },
{ 8595, "&darr" },
{ 176, "&deg" },
{ 948, "&delta" },
{ 9830, "&diams" },
{ 247, "&divide" },
{ 233, "&eacute" },
{ 234, "&ecirc" },
{ 232, "&egrave" },
{ 8709, "&empty" },
{ 8195, "&emsp" },
{ 8194, "&ensp" },
{ 949, "&epsilon" },
{ 8801, "&equiv" },
{ 951, "&eta" },
{ 240, "&eth" },
{ 235, "&euml" },
{ 8364, "&euro" },
{ 8707, "&exist" },
{ 402, "&fnof" },
{ 8704, "&forall" },
{ 189, "&frac12" },
{ 188, "&frac14" },
{ 190, "&frac34" },
{ 8260, "&frasl" },
{ 947, "&gamma" },
{ 8805, "&ge" },
{ 62, "&gt" },
{ 62, "&gt" },
{ 8660, "&hArr" },
{ 8596, "&harr" },
{ 9829, "&hearts" },
{ 8230, "&hellip" },
{ 237, "&iacute" },
{ 238, "&icirc" },
{ 161, "&iexcl" },
{ 236, "&igrave" },
{ 8465, "&image" },
{ 8734, "&infin" },
{ 8747, "&int" },
{ 953, "&iota" },
{ 191, "&iquest" },
{ 8712, "&isin" },
{ 239, "&iuml" },
{ 954, "&kappa" },
{ 8656, "&lArr" },
{ 955, "&lambda" },
{ 9001, "&lang" },
{ 171, "&laquo" },
{ 8592, "&larr" },
{ 8968, "&lceil" },
{ 8220, "&ldquo" },
{ 8804, "&le" },
{ 8970, "&lfloor" },
{ 8727, "&lowast" },
{ 9674, "&loz" },
{ 8206, "&lrm" },
{ 8249, "&lsaquo" },
{ 8216, "&lsquo" },
{ 60, "&lt" },
{ 60, "&lt" },
{ 175, "&macr" },
{ 8212, "&mdash" },
{ 181, "&micro" },
{ 183, "&middot" },
{ 8722, "&minus" },
{ 956, "&mu" },
{ 8711, "&nabla" },
{ 32, "&nbsp" },
{ 160, "&nbsp" },
{ 8211, "&ndash" },
{ 8800, "&ne" },
{ 8715, "&ni" },
{ 172, "&not" },
{ 8713, "&notin" },
{ 8836, "&nsub" },
{ 241, "&ntilde" },
{ 957, "&nu" },
{ 243, "&oacute" },
{ 244, "&ocirc" },
{ 339, "&oelig" },
{ 242, "&ograve" },
{ 8254, "&oline" },
{ 969, "&omega" },
{ 959, "&omicron" },
{ 8853, "&oplus" },
{ 8744, "&or" },
{ 170, "&ordf" },
{ 186, "&ordm" },
{ 248, "&oslash" },
{ 245, "&otilde" },
{ 8855, "&otimes" },
{ 246, "&ouml" },
{ 182, "&para" },
{ 8706, "&part" },
{ 8240, "&permil" },
{ 8869, "&perp" },
{ 966, "&phi" },
{ 960, "&pi" },
{ 982, "&piv" },
{ 177, "&plusmn" },
{ 163, "&pound" },
{ 8242, "&prime" },
{ 8719, "&prod" },
{ 8733, "&prop" },
{ 968, "&psi" },
{ 34, "&quot" },
{ 34, "&quot" },
{ 8658, "&rArr" },
{ 8730, "&radic" },
{ 9002, "&rang" },
{ 187, "&raquo" },
{ 8594, "&rarr" },
{ 8969, "&rceil" },
{ 8221, "&rdquo" },
{ 8476, "&real" },
{ 174, "&reg" },
{ 8971, "&rfloor" },
{ 961, "&rho" },
{ 8207, "&rlm" },
{ 8250, "&rsaquo" },
{ 8217, "&rsquo" },
{ 8218, "&sbquo" },
{ 353, "&scaron" },
{ 8901, "&sdot" },
{ 167, "&sect" },
{ 173, "&shy" },
{ 963, "&sigma" },
{ 962, "&sigmaf" },
{ 8764, "&sim" },
{ 9824, "&spades" },
{ 8834, "&sub" },
{ 8838, "&sube" },
{ 8721, "&sum" },
{ 8835, "&sup" },
{ 185, "&sup1" },
{ 178, "&sup2" },
{ 179, "&sup3" },
{ 8839, "&supe" },
{ 223, "&szlig" },
{ 964, "&tau" },
{ 8756, "&there4" },
{ 952, "&theta" },
{ 977, "&thetasym" },
{ 8201, "&thinsp" },
{ 254, "&thorn" },
{ 732, "&tilde" },
{ 215, "&times" },
{ 8482, "&trade" },
{ 8657, "&uArr" },
{ 250, "&uacute" },
{ 8593, "&uarr" },
{ 251, "&ucirc" },
{ 249, "&ugrave" },
{ 168, "&uml" },
{ 978, "&upsih" },
{ 965, "&upsilon" },
{ 252, "&uuml" },
{ 8472, "&weierp" },
{ 958, "&xi" },
{ 253, "&yacute" },
{ 165, "&yen" },
{ 255, "&yuml" },
{ 950, "&zeta" },
{ 8205, "&zwj" },
{ 8204, "&zwnj" }
};
#endif /* _HTMLENTITIES_H */

27
src/ijc.h Normal file
View File

@ -0,0 +1,27 @@
/*
* ijc.h, SJ
*/
static char invalid_junk_characters[] = {
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ','', ' ','',' ',' ',
'x',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ','¡','¢',' ', '¤','¥','¦',' ', '¨',' ','ª','«', '¬','­','®','¯',
'°','±','²','³', ' ','µ','',' ', '¸','¹','º','»', ' ','½','¾','¿',
' ',' ','Â','Ã', 'Ä','Å','Æ','Ç', 'È',' ',' ','Ë', 'Ì',' ',' ','Ï',
'Ð','Ñ','Ò',' ', 'Ô',' ',' ','×', 'Ø',' ',' ',' ', ' ','Ý',' ',' ',
'à',' ',' ','ã', ' ','å','æ','ç', 'è',' ','ê','ë', 'ì',' ','î',' ',
'ð','ñ','ò',' ', 'ô',' ',' ',' ', ' ','ù',' ',' ', ' ','ý',' ','ÿ'
};

88
src/list.c Normal file
View File

@ -0,0 +1,88 @@
/*
* list.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "list.h"
#include "config.h"
int append_list(struct url **urls, char *p){
struct url *q, *t, *u=NULL;
q = *urls;
while(q){
if(strcmp(q->url_str, p) == 0)
return 0;
u = q;
q = q->r;
}
t = createListItem(p);
if(t){
if(*urls == NULL)
*urls = t;
else if(u)
u->r = t;
return 1;
}
return -1;
}
struct url *createListItem(char *s){
struct url *h=NULL;
if((h = malloc(sizeof(struct url))) == NULL)
return NULL;
strncpy(h->url_str, s, URL_LEN-1);
h->r = NULL;
return h;
}
int isOnList(struct url *u, char *item){
struct url *p, *q;
p = u;
while(p != NULL){
q = p->r;
if(p){
if(strcmp(p->url_str, item) == 0) return 1;
}
p = q;
}
return 0;
}
void freeList(struct url *u){
struct url *p, *q;
p = u;
while(p != NULL){
q = p->r;
if(p)
free(p);
p = q;
}
}

16
src/list.h Normal file
View File

@ -0,0 +1,16 @@
/*
* list.h, SJ
*/
#ifndef _LIST_H
#define _LIST_H
#include "defs.h"
int append_list(struct url **urls, char *p);
struct url *createListItem(char *s);
int isOnList(struct url *u, char *item);
void freeList(struct url *u);
#endif /* _LIST_H */

400
src/memc.c Normal file
View File

@ -0,0 +1,400 @@
/*
* memc.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <errno.h>
#include <piler.h>
int __recvtimeout(int s, char *buf, int len, int timeout){
fd_set fds;
int n;
struct timeval tv;
memset(buf, 0, MAXBUFSIZE);
FD_ZERO(&fds);
FD_SET(s, &fds);
tv.tv_sec = 0;
tv.tv_usec = timeout;
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0) return -2; // timeout!
if (n == -1) return -1; // error
return recv(s, buf, len, 0);
}
void memcached_init(struct memcached_server *ptr, char *server_ip, int server_port){
ptr->snd_timeout= 0;
ptr->rcv_timeout= 100000;
ptr->send_size= -1;
ptr->recv_size= -1;
ptr->fd = -1;
ptr->last_read_bytes = 0;
snprintf(ptr->server_ip, IPLEN, "%s", server_ip);
ptr->server_port = server_port;
ptr->initialised = 0;
}
int set_socket_options(struct memcached_server *ptr){
int error, flag=1, flags, rval;
struct timeval waittime;
struct linger linger;
if(ptr->snd_timeout){
waittime.tv_sec = 0;
waittime.tv_usec = ptr->snd_timeout;
error = setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, &waittime, (socklen_t)sizeof(struct timeval));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->rcv_timeout){
waittime.tv_sec = 0;
waittime.tv_usec = ptr->rcv_timeout;
error = setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, &waittime, (socklen_t)sizeof(struct timeval));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->flags.no_block){
linger.l_onoff = 1;
linger.l_linger = 0; /* By default on close() just drop the socket */
error = setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, &linger, (socklen_t)sizeof(struct linger));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->flags.tcp_nodelay){
error = setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, &flag, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->flags.tcp_keepalive){
error= setsockopt(ptr->fd, SOL_SOCKET, SO_KEEPALIVE, &flag, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}
/*if(ptr->tcp_keepidle > 0){
error = setsockopt(ptr->fd, IPPROTO_TCP, TCP_KEEPIDLE, &ptr->tcp_keepidle, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}*/
if(ptr->send_size > 0){
error = setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, &ptr->send_size, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->recv_size > 0){
error = setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF, &ptr->recv_size, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}
/* always use nonblocking IO to avoid write deadlocks */
flags = fcntl(ptr->fd, F_GETFL, 0);
if(flags == -1) return MEMCACHED_FAILURE;
if((flags & O_NONBLOCK) == 0){
rval = fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
if(rval == -1) return MEMCACHED_FAILURE;
}
return MEMCACHED_SUCCESS;
}
int memcached_connect(struct memcached_server *ptr){
struct in_addr addr;
if(ptr->last_read_bytes > 0) return MEMCACHED_SUCCESS;
if(ptr->initialised == 0){
ptr->addr.sin_family = AF_INET;
ptr->addr.sin_port = htons(ptr->server_port);
if(inet_aton(ptr->server_ip, &addr) == 0) return MEMCACHED_FAILURE;
ptr->addr.sin_addr.s_addr = addr.s_addr;
bzero(&(ptr->addr.sin_zero), 8);
ptr->initialised = 1;
}
if((ptr->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ return MEMCACHED_FAILURE; }
if(set_socket_options(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
if(connect(ptr->fd, (struct sockaddr *)&ptr->addr, sizeof(struct sockaddr)) == -1){
if(errno == EINPROGRESS || /* nonblocking mode - first return, */
errno == EALREADY) /* nonblocking mode - subsequent returns */
{
struct pollfd fds[1];
fds[0].fd = ptr->fd;
fds[0].events = POLLOUT;
int error = poll(fds, 1, 1000);
if (error != 1 || fds[0].revents & POLLERR)
{
if (fds[0].revents & POLLERR)
{
int err;
socklen_t len = sizeof (err);
(void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len);
//ptr->cached_errno= (err == 0) ? errno : err;
}
(void)close(ptr->fd);
ptr->fd= -1;
return MEMCACHED_FAILURE;
}
return MEMCACHED_SUCCESS;
}
else if (errno == EISCONN) /* we are connected :-) */
{
return MEMCACHED_SUCCESS;
}
else if (errno != EINTR)
{
(void)close(ptr->fd);
ptr->fd= -1;
}
return MEMCACHED_FAILURE;
}
return MEMCACHED_SUCCESS;
}
int memcached_shutdown(struct memcached_server *ptr){
if(ptr->fd != -1){
close(ptr->fd);
ptr->fd = -1;
}
return MEMCACHED_SUCCESS;
}
int memcached_add(struct memcached_server *ptr, char *key, unsigned int keylen, char *value, unsigned int valuelen, unsigned int flags, unsigned long expiry){
int len=0;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
snprintf(ptr->buf, MAXBUFSIZE-1, "add %s %d %ld %d \r\n", key, flags, expiry, valuelen);
len = strlen(ptr->buf);
strncat(ptr->buf, value, MAXBUFSIZE-1);
strncat(ptr->buf, "\r\n", MAXBUFSIZE-1);
len += valuelen + 2;
send(ptr->fd, ptr->buf, len, 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(strcmp("STORED\r\n", ptr->buf)) return MEMCACHED_FAILURE;
return MEMCACHED_SUCCESS;
}
int memcached_set(struct memcached_server *ptr, char *key, unsigned int keylen, char *value, unsigned int valuelen, unsigned int flags, unsigned long expiry){
int len=0;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
snprintf(ptr->buf, MAXBUFSIZE-1, "set %s %d %ld %d \r\n", key, flags, expiry, valuelen);
len = strlen(ptr->buf);
strncat(ptr->buf, value, MAXBUFSIZE-1);
strncat(ptr->buf, "\r\n", MAXBUFSIZE-1);
len += valuelen + 2;
send(ptr->fd, ptr->buf, len, 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(strcmp("STORED\r\n", ptr->buf)) return MEMCACHED_FAILURE;
return MEMCACHED_SUCCESS;
}
int memcached_increment(struct memcached_server *ptr, char *key, unsigned int keylen, unsigned long long value, unsigned long long *result){
char *p;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
snprintf(ptr->buf, MAXBUFSIZE, "incr %s %llu\r\n", key, value);
send(ptr->fd, ptr->buf, strlen(ptr->buf), 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(!strcmp("NOT_FOUND\r\n", ptr->buf)) return MEMCACHED_FAILURE;
p = strchr(ptr->buf, '\r');
if(p){
*p = '\0';
*result = strtoul(ptr->buf, NULL, 10);
}
return MEMCACHED_SUCCESS;
}
char *memcached_get(struct memcached_server *ptr, char *key, unsigned int *len, unsigned int *flags){
int rc;
char *p;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return NULL;
snprintf(ptr->buf, MAXBUFSIZE, "get %s \r\n", key);
rc = send(ptr->fd, ptr->buf, strlen(ptr->buf), 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(ptr->last_read_bytes <= 0){
memcached_shutdown(ptr);
return NULL;
}
if(ptr->last_read_bytes < 10) return NULL;
if(strncmp("VALUE ", ptr->buf, 6)) return NULL;
p = strchr(ptr->buf, '\r');
if(!p) return NULL;
*p = '\0';
ptr->result = p + 2;
p = strrchr(ptr->buf + 6, ' ');
if(!p) return NULL;
*len = atoi(p+1);
*p = '\0';
p = strrchr(ptr->buf + 6, ' ');
if(!p) return NULL;
*flags = atoi(p+1);
*p = '\0';
p = strchr(ptr->result, '\r');
if(!p) return NULL;
*p = '\0';
return ptr->result;
}
int memcached_mget(struct memcached_server *ptr, char *key){
int rc;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
snprintf(ptr->buf, MAXBUFSIZE, "get %s \r\n", key);
rc = send(ptr->fd, ptr->buf, strlen(ptr->buf), 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(ptr->last_read_bytes <= 0){
memcached_shutdown(ptr);
return MEMCACHED_FAILURE;
}
ptr->result = ptr->buf;
return MEMCACHED_SUCCESS;
}
char *memcached_fetch_result(struct memcached_server *ptr, char *key, char *value, unsigned int *flags){
char *p, *q;
int len=0;
if(ptr->last_read_bytes < 10) return NULL;
if(strncmp("VALUE ", ptr->result, 6)) return NULL;
/* first read, eg. 'VALUE aaaa 0 4' */
p = strchr(ptr->result, '\r');
if(!p) return NULL;
*p = '\0';
p = ptr->result + strlen(ptr->result) + 2;
q = strrchr(ptr->result + 6, ' ');
if(!q) return NULL;
len = atoi(q+1);
*q = '\0';
q = strchr(ptr->result + 6, ' ');
if(!q) return NULL;
*q = '\0';
*flags = atoi(q+1);
snprintf(key, MAX_MEMCACHED_KEY_LEN-1, "%s", ptr->result + 6);
/* now read 'len' bytes */
q = p + len;
*q = '\0';
snprintf(value, MAXBUFSIZE-1, "%s", p);
p = q + 2;
ptr->result = p;
return p;
}

19
src/memc.h Normal file
View File

@ -0,0 +1,19 @@
/*
* memc.h, SJ
*/
#ifndef _MEMC_H
#define _MEMC_H
void memcached_init(struct memcached_server *ptr, char *server_ip, int server_port);
int set_socket_options(struct memcached_server *ptr);
int memcached_connect(struct memcached_server *ptr);
int memcached_shutdown(struct memcached_server *ptr);
int memcached_add(struct memcached_server *ptr, char *key, unsigned int keylen, char *value, unsigned int valuelen, unsigned int flags, unsigned long expiry);
int memcached_set(struct memcached_server *ptr, char *key, unsigned int keylen, char *value, unsigned int valuelen, unsigned int flags, unsigned long expiry);
int memcached_increment(struct memcached_server *ptr, char *key, unsigned int keylen, unsigned long long value, unsigned long long *result);
char *memcached_get(struct memcached_server *ptr, char *key, unsigned int *len, unsigned int *flags);
int memcached_mget(struct memcached_server *ptr, char *key);
char *memcached_fetch_result(struct memcached_server *ptr, char *key, char *value, unsigned int *flags);
#endif /* _MEMC_H */

204
src/memcached.c Normal file
View File

@ -0,0 +1,204 @@
/*
* memcached.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <syslog.h>
#include <piler.h>
int getUserdataFromMemcached(struct session_data *sdata, struct __data *data, char *email, struct __config *cfg){
unsigned int len=0;
uint32_t flags = 0;
char key[SMALLBUFSIZE], *s=NULL, *p;
//if(data->memc.initialised == 0) return 0;
snprintf(key, SMALLBUFSIZE-1, "%s:%s", MEMCACHED_CLAPF_PREFIX, email);
s = memcached_get(&(data->memc), key, &len, &flags);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached user query=%s, data=%s (%d)", sdata->ttmpfile, key, s, len);
if(len > 0){
/* 1000:8:sj:acts.hu:1 */
if(len == 1 && s[0] == 'U'){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: %s is unknown", sdata->ttmpfile, email);
return 1;
}
p = strchr(s, ':');
if(p){ *p = '\0'; sdata->uid = atol(s); s = p+1; }
p = strchr(s, ':');
if(p){ *p = '\0'; sdata->gid = atol(s); s = p+1; }
p = strchr(s, ':');
if(p){ *p = '\0'; snprintf(sdata->name, SMALLBUFSIZE-1, "%s", s); s = p+1; }
p = strchr(s, ':');
if(p){ *p = '\0'; snprintf(sdata->domain, SMALLBUFSIZE-1, "%s", s); s = p+1; }
sdata->policy_group = atoi(s);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached parsed user data: uid: %ld, gid: %ld, name: %s, domain: %s, policy group: %d", sdata->ttmpfile, sdata->uid, sdata->gid, sdata->name, sdata->domain, sdata->policy_group);
return 1;
}
return 0;
}
int putUserdataToMemcached(struct session_data *sdata, struct __data *data, char *email, struct __config *cfg){
uint32_t flags = 0;
char key[SMALLBUFSIZE], value[SMALLBUFSIZE];
snprintf(key, SMALLBUFSIZE-1, "%s:%s", MEMCACHED_CLAPF_PREFIX, email);
if(sdata->uid == 0)
strcpy(value, "U");
else
snprintf(value, SMALLBUFSIZE-1, "%ld:%ld:%s:%s:%d", sdata->uid, sdata->gid, sdata->name, sdata->domain, sdata->policy_group);
if(memcached_add(&(data->memc), key, strlen(key), value, strlen(value), cfg->memcached_ttl, flags) == MEMCACHED_SUCCESS) return 1;
return 0;
}
int getPolicyFromMemcached(struct session_data *sdata, struct __data *data, struct __config *cfg, struct __config *my_cfg){
unsigned int len=0;
uint32_t flags = 0;
char key[SMALLBUFSIZE], *s=NULL, *p;
if(sdata->policy_group <= 0) return 0;
snprintf(key, SMALLBUFSIZE-1, "%s:%d", MEMCACHED_CLAPF_PREFIX, sdata->policy_group);
s = memcached_get(&(data->memc), key, &len, &flags);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached policy query=%s, data=%s (%d)", sdata->ttmpfile, key, s, len);
if(len > 0){
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->deliver_infected_email = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->silently_discard_infected_email = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->use_antispam = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; snprintf(my_cfg->spam_subject_prefix, MAXVAL-1, "%s", s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->enable_auto_white_list = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->max_message_size_to_filter = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; snprintf(my_cfg->rbl_domain, MAXVAL-1, "%s", s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; snprintf(my_cfg->surbl_domain, MAXVAL-1, "%s", s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->spam_overall_limit = atof(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->spaminess_oblivion_limit = atof(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->replace_junk_characters = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->invalid_junk_limit = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->invalid_junk_line = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->penalize_images = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->penalize_embed_images = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->penalize_octet_stream = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->training_mode = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->initial_1000_learning = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->store_metadata = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->store_only_spam = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->message_from_a_zombie = atoi(s); }
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached parsed policy data: spam limit: %.4f, oblivion: %.4f, subject prefix: *%s*, rbl: *%s*, training mode: %d, meta: %d",
sdata->ttmpfile, my_cfg->spam_overall_limit, my_cfg->spaminess_oblivion_limit, my_cfg->spam_subject_prefix, my_cfg->rbl_domain, my_cfg->training_mode, my_cfg->store_metadata);
return 1;
}
return 0;
}
int putPolicyToMemcached(struct session_data *sdata, struct __data *data, struct __config *my_cfg){
uint32_t flags = 0;
char key[SMALLBUFSIZE], value[SMALLBUFSIZE];
if(sdata->policy_group <= 0) return 0;
snprintf(key, SMALLBUFSIZE-1, "%s:%d", MEMCACHED_CLAPF_PREFIX, sdata->policy_group);
snprintf(value, SMALLBUFSIZE-1, "%d:%d:%d:%s:%d:%ld:%s:%s:%.4f:%.4f:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
my_cfg->deliver_infected_email,
my_cfg->silently_discard_infected_email,
my_cfg->use_antispam,
my_cfg->spam_subject_prefix,
my_cfg->enable_auto_white_list,
my_cfg->max_message_size_to_filter,
my_cfg->rbl_domain,
my_cfg->surbl_domain,
my_cfg->spam_overall_limit,
my_cfg->spaminess_oblivion_limit,
my_cfg->replace_junk_characters,
my_cfg->invalid_junk_limit,
my_cfg->invalid_junk_line,
my_cfg->penalize_images,
my_cfg->penalize_embed_images,
my_cfg->penalize_octet_stream,
my_cfg->training_mode,
my_cfg->initial_1000_learning,
my_cfg->store_metadata,
my_cfg->store_only_spam,
my_cfg->message_from_a_zombie
);
if(memcached_add(&(data->memc), key, strlen(key), value, strlen(value), my_cfg->memcached_ttl, flags) == MEMCACHED_SUCCESS) return 1;
return 0;
}
int getWBLFromMemcached(struct session_data *sdata, struct __data *data, struct __config *cfg){
unsigned int len=0;
uint32_t flags = 0;
char key[SMALLBUFSIZE], *s=NULL, *p;
snprintf(key, SMALLBUFSIZE-1, "%s:wbl%ld", MEMCACHED_CLAPF_PREFIX, sdata->uid);
s = memcached_get(&(data->memc), key, &len, &flags);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached wbl query=%s, data=%s (%d)", sdata->ttmpfile, key, s, len);
if(len > 0){
/* whiteemail1,whiteemail2:blackemail1,blackemail2 */
p = strchr(s, ':');
if(p){
*p = '\0';
snprintf(sdata->whitelist, MAXBUFSIZE-1, "%s", s);
snprintf(sdata->blacklist, MAXBUFSIZE-1, "%s", p+1);
}
return 1;
}
return 0;
}
int putWBLToMemcached(struct session_data *sdata, struct __data *data, struct __config *cfg){
uint32_t flags = 0;
char key[SMALLBUFSIZE], value[2*MAXBUFSIZE];
if(sdata->uid <= 0) return 0;
snprintf(key, SMALLBUFSIZE-1, "%s:wbl%ld", MEMCACHED_CLAPF_PREFIX, sdata->uid);
snprintf(value, 2*MAXBUFSIZE-1, "%s:%s", sdata->whitelist, sdata->blacklist);
if(memcached_add(&(data->memc), key, strlen(key), value, strlen(value), cfg->memcached_ttl, flags) == MEMCACHED_SUCCESS) return 1;
return 0;
}

257
src/message.c Normal file
View File

@ -0,0 +1,257 @@
/*
* message.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <piler.h>
#include <zlib.h>
int is_existing_message_id(struct session_data *sdata, struct _state *state, struct __config *cfg){
int rc=0;
char s[SMALLBUFSIZE];
MYSQL_STMT *stmt;
MYSQL_BIND bind[1];
my_bool is_null[1];
unsigned long len=0;
stmt = mysql_stmt_init(&(sdata->mysql));
if(!stmt){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_init() error", sdata->ttmpfile, SQL_METADATA_TABLE);
goto ENDE;
}
snprintf(s, SMALLBUFSIZE-1, "SELECT message_id FROM %s WHERE message_id=?", SQL_METADATA_TABLE);
if(mysql_stmt_prepare(stmt, s, strlen(s))){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_prepare() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = state->message_id;
bind[0].is_null = 0;
len = strlen(state->message_id); bind[0].length = &len;
if(mysql_stmt_bind_param(stmt, bind)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_bind_param() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
if(mysql_stmt_execute(stmt)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_execute() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = &s[0];
bind[0].buffer_length = sizeof(s)-1;
bind[0].is_null = &is_null[0];
bind[0].length = &len;
if(mysql_stmt_bind_result(stmt, bind)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_bind_result() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
if(mysql_stmt_store_result(stmt)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_store_result() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
if(!mysql_stmt_fetch(stmt)){
syslog(LOG_PRIORITY, "%s: found message_id:*%s*(%ld) null=%d", sdata->ttmpfile, s, len, is_null[0]);
if(is_null[0] == 0) rc = 1;
}
mysql_stmt_close(stmt);
ENDE:
return rc;
}
int is_body_digest_already_stored(struct session_data *sdata, struct _state *state, struct __config *cfg){
int rc=0;
char s[SMALLBUFSIZE];
MYSQL_RES *res;
MYSQL_ROW row;
snprintf(s, SMALLBUFSIZE-1, "SELECT `bodydigest` FROM `%s` WHERE `bodydigest`='%s'", SQL_METADATA_TABLE, sdata->bodydigest);
//if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: check for body digest sql: *%s*", sdata->ttmpfile, s);
if(mysql_real_query(&(sdata->mysql), s, strlen(s)) == 0){
res = mysql_store_result(&(sdata->mysql));
if(res != NULL){
row = mysql_fetch_row(res);
if(row) rc = 1;
mysql_free_result(res);
}
}
return rc;
}
int hand_to_sphinx(struct session_data *sdata, struct _state *state, struct __config *cfg){
int rc;
char s[BIGBUFSIZE+2*MAXBUFSIZE];
snprintf(s, sizeof(s)-1, "INSERT INTO %s (`from`, `to`, `subject`, `body`, `arrived`, `sent`, `size`, `piler_id`) values('%s','%s','%s','%s',%ld,%ld,%d,'%s')", SQL_SPHINX_TABLE, state->b_from, state->b_to, state->b_subject, state->b_body, sdata->now, sdata->sent, sdata->tot_len, sdata->ttmpfile);
rc = mysql_real_query(&(sdata->mysql), s, strlen(s));
if(rc == 0) return OK;
syslog(LOG_PRIORITY, "%s: sphinx sql error: *%s*", sdata->ttmpfile, mysql_error(&(sdata->mysql)));
return ERR;
}
int store_meta_data(struct session_data *sdata, struct _state *state, struct __config *cfg){
int i=0, rc, ret=ERR;
char *p, s[MAXBUFSIZE], s2[SMALLBUFSIZE];
struct url *list = NULL;
MYSQL_STMT *stmt;
MYSQL_BIND bind[4];
unsigned long len[4];
stmt = mysql_stmt_init(&(sdata->mysql));
if(!stmt){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_init() error", sdata->ttmpfile, SQL_METADATA_TABLE);
goto ENDE_META;
}
snprintf(s, MAXBUFSIZE-1, "INSERT INTO %s (`from`,`to`,`subject`,`arrived`,`sent`,`size`,`hlen`,`piler_id`,`message_id`,`bodydigest`) VALUES(?,?,?,%ld,%ld,%d,%d,'%s',?,'%s')", SQL_METADATA_TABLE, sdata->now, sdata->sent, sdata->tot_len, sdata->hdr_len, sdata->ttmpfile, sdata->bodydigest);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: meta sql: *%s*", sdata->ttmpfile, s);
if(mysql_stmt_prepare(stmt, s, strlen(s))){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_prepare() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE_META;
}
if(strlen(state->b_to) < 5){
snprintf(s2, sizeof(s2)-1, "undisclosed-recipients");
p = NULL;
goto LABEL1;
}
else p = state->b_to;
do {
p = split_str(p, " ", s2, sizeof(s2)-1);
if(strlen(s2) > 5){
LABEL1:
if(isOnList(list, s2) == 1) continue;
append_list(&list, s2);
i++;
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = state->b_from;
bind[0].is_null = 0;
len[0] = strlen(state->b_from); bind[0].length = &len[0];
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = s2;
bind[1].is_null = 0;
len[1] = strlen(s2); bind[1].length = &len[1];
bind[2].buffer_type = MYSQL_TYPE_STRING;
bind[2].buffer = state->b_subject;
bind[2].is_null = 0;
len[2] = strlen(state->b_subject); bind[2].length = &len[2];
bind[3].buffer_type = MYSQL_TYPE_STRING;
bind[3].buffer = state->message_id;
bind[3].is_null = 0;
len[3] = strlen(state->message_id); bind[3].length = &len[3];
if(mysql_stmt_bind_param(stmt, bind)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_bind_param() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE_META;
}
rc = mysql_stmt_execute(stmt);
//rc = mysql_real_query(&(sdata->mysql), s, strlen(s));
if(rc){
syslog(LOG_PRIORITY, "%s: meta sql error: *%s*", sdata->ttmpfile, mysql_error(&(sdata->mysql)));
ret = ERR_EXISTS;
goto ENDE_META;
}
}
} while(p);
if(i == 0) ret = ERR_EXISTS;
else ret = OK;
ENDE_META:
freeList(list);
return ret;
}
int processMessage(struct session_data *sdata, struct _state *state, struct __config *cfg){
int rc;
/* discard if existing message_id */
if(is_existing_message_id(sdata, state, cfg) == 1){
return ERR_EXISTS;
}
/* check for existing body digest */
rc = is_body_digest_already_stored(sdata, state, cfg);
rc = store_message(sdata, state, rc, cfg);
rc = store_meta_data(sdata, state, cfg);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: stored metadata, rc=%d", sdata->ttmpfile, rc);
if(rc == ERR_EXISTS) return ERR_EXISTS;
rc = hand_to_sphinx(sdata, state, cfg);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: stored indexdata, rc=%d", sdata->ttmpfile, rc);
return OK;
}

432
src/misc.c Normal file
View File

@ -0,0 +1,432 @@
/*
* misc.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
#include <ctype.h>
#include "misc.h"
#include "smtpcodes.h"
#include "errmsg.h"
#include "config.h"
#include "tai.h"
/*
* fatal function for quitting
*/
void __fatal(char *s){
fprintf(stderr, "%s\n", s);
exit(1);
}
/*
* calculate the difference betwwen two timevals in [usec]
*/
long tvdiff(struct timeval a, struct timeval b){
double res;
res = (a.tv_sec * 1000000 + a.tv_usec) - (b.tv_sec * 1000000 + b.tv_usec);
return (long) res;
}
/*
* search something in a buffer
*/
int searchStringInBuffer(char *s, int len1, char *what, int len2){
int i, k, r;
for(i=0; i<len1; i++){
r = 0;
for(k=0; k<len2; k++){
if(*(s+i+k) == *(what+k))
r++;
}
if(r == len2)
return i;
}
return 0;
}
/*
* count a character in buffer
*/
int countCharacterInBuffer(char *p, char c){
int i=0;
for(; *p; p++){
if(*p == c)
i++;
}
return i;
}
void replaceCharacterInBuffer(char *p, char from, char to){
int i, k=0;
for(i=0; i<strlen(p); i++){
if(p[i] == from){
if(to > 0){
p[k] = to;
k++;
}
}
else {
p[k] = p[i];
k++;
}
}
p[k] = '\0';
}
/*
* split a string by a character as delimiter
*/
char *split(char *row, int ch, char *s, int size){
char *r;
int len;
if(row == NULL)
return NULL;
r = strchr(row, ch);
if(r == NULL){
len = strlen(row);
if(len > size)
len = size;
}
else {
len = strlen(row) - strlen(r);
if(len > size)
len = size;
r++;
}
if(s != NULL){
strncpy(s, row, len);
s[len] = '\0';
}
return r;
}
/*
* split a string by a string as delimiter
*/
char *split_str(char *row, char *what, char *s, int size){
char *r;
int len;
memset(s, 0, size);
if(row == NULL)
return NULL;
r = strstr(row, what);
if(r == NULL){
len = strlen(row);
if(len > size)
len = size;
}
else {
len = strlen(row) - strlen(r);
if(len > size)
len = size;
r += strlen(what);
}
if(s != NULL){
strncpy(s, row, len);
s[len] = '\0';
}
return r;
}
/*
* trim trailing CR-LF
*/
void trimBuffer(char *s){
char *p;
p = strrchr(s, '\n');
if(p) *p = '\0';
p = strrchr(s, '\r');
if(p) *p = '\0';
}
/*
* extract email
*/
int extractEmail(char *rawmail, char *email){
char *p;
p = strchr(rawmail, '<');
if(p){
snprintf(email, SMALLBUFSIZE-1, "%s", p+1);
p = strchr(email, '>');
if(p){
*p = '\0';
return 1;
}
}
return 0;
}
/*
* create an ID
*/
void create_id(char *id){
int i;
unsigned char buf[RND_STR_LEN/2];
memset(id, 0, SMALLBUFSIZE);
get_random_bytes(buf, RND_STR_LEN/2);
for(i=0; i < RND_STR_LEN/2; i++){
sprintf(id, "%02x", buf[i]);
id += 2;
}
}
/*
* reading from pool
*/
int get_random_bytes(unsigned char *buf, int len){
int fd, ret=0;
struct taia now;
char nowpack[TAIA_PACK];
/* the first 12 bytes are the taia timestamp */
taia_now(&now);
taia_pack(nowpack, &now);
memcpy(buf, nowpack, 12);
fd = open(RANDOM_POOL, O_RDONLY);
if(fd == -1) return ret;
if(readFromEntropyPool(fd, buf+12, len-12) != len-12){
syslog(LOG_PRIORITY, "%s: %s", ERR_CANNOT_READ_FROM_POOL, RANDOM_POOL);
}
close(fd);
return ret;
}
/*
* read random data from entropy pool
*/
int readFromEntropyPool(int fd, void *_s, size_t n){
char *s = _s;
ssize_t res, pos = 0;
while(n > pos){
res = read(fd, s + pos, n - pos);
switch(res){
case -1: continue;
case 0: return res;
default : pos += res;
}
}
return pos;
}
/*
* recv() with timeout
*/
int recvtimeout(int s, char *buf, int len, int timeout){
fd_set fds;
int n;
struct timeval tv;
memset(buf, 0, MAXBUFSIZE);
FD_ZERO(&fds);
FD_SET(s, &fds);
tv.tv_sec = timeout;
tv.tv_usec = TIMEOUT_USEC;
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0) return -2; // timeout!
if (n == -1) return -1; // error
return recv(s, buf, len, 0);
}
/*
* check if it's a valid ID
*/
int isValidClapfID(char *p){
if(strlen(p) != 30 && strlen(p) != 31)
return 0;
for(; *p; p++){
/* 0-9: 0x30-0x39, a-f: 0x61-0x66 */
if(! ((*p >= 0x30 && *p <= 0x39) || (*p >= 0x61 && *p <= 0x66) || *p == 0x0d) ){
//printf("%c*\n", *p);
return 0;
}
}
return 1;
}
/*
* is it a valid dotted IPv4 address
*/
int isDottedIPv4Address(char *s){
struct in_addr addr;
if(inet_aton(s, &addr) == 0) return 0;
return 1;
}
/*
* whitelist check
*/
int isEmailAddressOnList(char *list, char *tmpfile, char *email, struct __config *cfg){
char *p, *q, w[SMALLBUFSIZE];
if(email == NULL) return 0;
p = list;
if(cfg->verbosity >= _LOG_INFO) syslog(LOG_PRIORITY, "%s: list: %s", tmpfile, list);
do {
p = split(p, '\n', w, SMALLBUFSIZE-1);
trimBuffer(w);
if(strlen(w) > 2){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: matching '%s' on '%s'", tmpfile, w, email);
if(w[strlen(w)-1] == '$'){
q = email + strlen(email) - strlen(w) + 1;
if(strncasecmp(q, w, strlen(w)-1) == 0)
return 1;
}
else if(strcasestr(email, w))
return 1;
}
} while(p);
return 0;
}
void write_pid_file(char *pidfile){
FILE *f;
f = fopen(pidfile, "w");
if(f){
fprintf(f, "%d", (int)getpid());
fclose(f);
}
else syslog(LOG_PRIORITY, "cannot write pidfile: %s", pidfile);
}
int drop_privileges(struct passwd *pwd){
if(pwd->pw_uid > 0 && pwd->pw_gid > 0){
if(getgid() != pwd->pw_gid){
if(setgid(pwd->pw_gid)) return -1;
}
if(getuid() != pwd->pw_uid){
if(setuid(pwd->pw_uid)) return -1;
}
}
return 0;
}
#ifndef _GNU_SOURCE
char *strcasestr(const char *s, const char *find){
char c, sc;
size_t len;
if((c = *find++) != 0){
c = tolower((unsigned char)c);
len = strlen(find);
do {
do {
if((sc = *s++) == 0)
return (NULL);
} while((char)tolower((unsigned char)sc) != c);
} while (strncasecmp(s, find, len) != 0);
s--;
}
return((char*)s);
}
#endif

39
src/misc.h Normal file
View File

@ -0,0 +1,39 @@
/*
* misc.h, SJ
*/
#ifndef _MISC_H
#define _MISC_H
#include <sys/time.h>
#include <pwd.h>
#include <cfg.h>
#include "defs.h"
void __fatal(char *s);
long tvdiff(struct timeval a, struct timeval b);
int searchStringInBuffer(char *s, int len1, char *what, int len2);
int countCharacterInBuffer(char *p, char c);
void replaceCharacterInBuffer(char *p, char from, char to);
char *split(char *row, int ch, char *s, int size);
char *split_str(char *row, char *what, char *s, int size);
void trimBuffer(char *s);
int extractEmail(char *rawmail, char *email);
void create_id(char *id);
int get_random_bytes(unsigned char *buf, int len);
int readFromEntropyPool(int fd, void *_s, size_t n);
int recvtimeout(int s, char *buf, int len, int timeout);
int isValidClapfID(char *p);
int isDottedIPv4Address(char *s);
int isEmailAddressOnList(char *list, char *tmpfile, char *email, struct __config *cfg);
void write_pid_file(char *pidfile);
int drop_privileges(struct passwd *pwd);
#ifndef _GNU_SOURCE
char *strcasestr(const char *s, const char *find);
#endif
#endif /* _MISC_H */

403
src/parser.c Normal file
View File

@ -0,0 +1,403 @@
/*
* parser.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <piler.h>
struct _state parseMessage(struct session_data *sdata, struct __config *cfg){
FILE *f;
char buf[MAXBUFSIZE];
struct _state state;
initState(&state);
f = fopen(sdata->ttmpfile, "r");
if(!f){
syslog(LOG_PRIORITY, "%s: cannot open", sdata->ttmpfile);
return state;
}
while(fgets(buf, MAXBUFSIZE-1, f)){
parseLine(buf, &state, sdata, cfg);
}
fclose(f);
free_boundary(state.boundaries);
if(state.message_id[0] == 0) snprintf(state.message_id, SMALLBUFSIZE-1, "null");
if(state.b_from[strlen(state.b_from)-1] == ' ') state.b_from[strlen(state.b_from)-1] = '\0';
if(state.b_to[strlen(state.b_to)-1] == ' ') state.b_to[strlen(state.b_to)-1] = '\0';
if(state.b_subject[strlen(state.b_subject)-1] == ' ') state.b_subject[strlen(state.b_subject)-1] = '\0';
make_body_digest(sdata);
syslog(LOG_PRIORITY, "%s: from=%s, to=%s, subj=%s, message-id=%s", sdata->ttmpfile, state.b_from, state.b_to, state.b_subject, state.message_id);
return state;
}
int parseLine(char *buf, struct _state *state, struct session_data *sdata, struct __config *cfg){
char *p, *q, puf[MAXBUFSIZE], muf[MAXBUFSIZE], u[SMALLBUFSIZE], token[MAX_TOKEN_LEN];
int x, b64_len, boundary_line=0;
memset(token, 0, MAX_TOKEN_LEN);
state->line_num++;
if(*buf == '.' && *(buf+1) == '.') buf++;
/* undefined message state */
if(state->is_header == 1 && buf[0] != ' ' && buf[0] != '\t' && strchr(buf, ':')) state->message_state = MSG_UNDEF;
/* skip empty lines */
if(state->message_rfc822 == 0 && (buf[0] == '\r' || buf[0] == '\n') ){
state->message_state = MSG_BODY;
if(state->is_header == 1) state->is_header = 0;
return 0;
}
trimBuffer(buf);
/* skip the first line, if it's a "From <email address> date" format */
if(state->line_num == 1 && strncmp(buf, "From ", 5) == 0) return 0;
if(state->is_header == 0 && buf[0] != ' ' && buf[0] != '\t') state->message_state = MSG_BODY;
if((state->content_type_is_set == 0 || state->is_header == 1) && strncasecmp(buf, "Content-Type:", strlen("Content-Type:")) == 0) state->message_state = MSG_CONTENT_TYPE;
else if(strncasecmp(buf, "Content-Transfer-Encoding:", strlen("Content-Transfer-Encoding:")) == 0) state->message_state = MSG_CONTENT_TRANSFER_ENCODING;
else if(strncasecmp(buf, "Content-Disposition:", strlen("Content-Disposition:")) == 0) state->message_state = MSG_CONTENT_DISPOSITION;
if(state->message_state == MSG_CONTENT_TYPE || state->message_state == MSG_CONTENT_TRANSFER_ENCODING) state->is_header = 1;
/* header checks */
if(state->is_header == 1){
if(strncasecmp(buf, "Received: from ", strlen("Received: from ")) == 0) state->message_state = MSG_RECEIVED;
else if(strncasecmp(buf, "From:", strlen("From:")) == 0) state->message_state = MSG_FROM;
else if(strncasecmp(buf, "To:", 3) == 0) state->message_state = MSG_TO;
else if(strncasecmp(buf, "Cc:", 3) == 0) state->message_state = MSG_CC;
else if(strncasecmp(buf, "Message-Id:", 11) == 0) state->message_state = MSG_MESSAGE_ID;
else if(strncasecmp(buf, "Subject:", strlen("Subject:")) == 0) state->message_state = MSG_SUBJECT;
else if(strncasecmp(buf, "Date:", strlen("Date:")) == 0 && sdata->sent == 0) sdata->sent = parse_date_header(buf);
if(state->message_state == MSG_SUBJECT){
p = &buf[0];
if(strncmp(buf, "Subject:", strlen("Subject:")) == 0) p = &buf[8];
if(*p == ' ') p++;
}
if(state->message_state == MSG_MESSAGE_ID && state->message_id[0] == 0){
p = strchr(buf+11, ' ');
if(p) p = buf + 12;
else p = buf + 11;
snprintf(state->message_id, SMALLBUFSIZE-1, "%s", p);
}
if(state->message_state == MSG_FROM){
p = strchr(buf+5, ' ');
if(p) p = buf + 6;
else p = buf + 5;
snprintf(state->from, SMALLBUFSIZE-1, "FROM*%s", p);
}
/* we are interested in only From:, To:, Subject:, Received:, Content-*: header lines */
if(state->message_state <= 0) return 0;
}
if((p = strcasestr(buf, "boundary"))){
x = extract_boundary(p, state);
}
/* Content-type: checking */
if(state->message_state == MSG_CONTENT_TYPE){
state->message_rfc822 = 0;
/* extract Content type */
p = strchr(buf, ':');
if(p){
p++;
if(*p == ' ' || *p == '\t') p++;
snprintf(state->attachments[state->n_attachments].type, SMALLBUFSIZE-1, "%s", p);
state->content_type_is_set = 1;
p = strchr(state->attachments[state->n_attachments].type, ';');
if(p) *p = '\0';
}
p = strstr(buf, "name=");
if(p){
snprintf(state->attachments[state->n_attachments].filename, SMALLBUFSIZE-1, "%s", p);
}
if(strcasestr(buf, "text/plain") ||
strcasestr(buf, "multipart/mixed") ||
strcasestr(buf, "multipart/alternative") ||
strcasestr(buf, "multipart/report") ||
strcasestr(buf, "message/delivery-status") ||
strcasestr(buf, "text/rfc822-headers") ||
strcasestr(buf, "message/rfc822") ||
strcasestr(buf, "application/ms-tnef")
){
state->textplain = 1;
}
else if(strcasestr(buf, "text/html")){
state->texthtml = 1;
}
/* switch (back) to header mode if we encounterd an attachment with
"message/rfc822" content-type, 2010.05.16, SJ */
if(strcasestr(buf, "message/rfc822")){
state->message_rfc822 = 1;
state->is_header = 1;
}
if(strcasestr(buf, "application/octet-stream")) state->octetstream = 1;
if(strcasestr(buf, "charset") && strcasestr(buf, "UTF-8")) state->utf8 = 1;
extractNameFromHeaderLine(buf, "name", state->attachments[state->n_attachments].filename);
/*if(strlen(state->attachments[state->n_attachments].filename) > 5){
state->has_to_dump = 1;
snprintf(u, sizeof(u)-1, "%s.%d", sdata->ttmpfile, state->n_attachments);
state->fd = open(u, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
}*/
}
if(state->message_state == MSG_CONTENT_DISPOSITION && state->attachments[state->n_attachments].filename[0] == 0)
extractNameFromHeaderLine(buf, "name", state->attachments[state->n_attachments].filename);
if(state->message_state > 0 && state->message_state <= MSG_SUBJECT && state->message_rfc822 == 1) state->message_rfc822 = 0;
/* check for textual base64 encoded part, 2005.03.25, SJ */
if(state->message_state == MSG_CONTENT_TRANSFER_ENCODING){
if(strcasestr(buf, "base64")){
state->base64 = 1;
state->has_base64 = 1;
}
if(strcasestr(buf, "quoted-printable")) state->qp = 1;
if(strcasestr(buf, "image"))
state->num_of_images++;
if(strcasestr(buf, "msword"))
state->num_of_msword++;
}
/* skip the boundary itself */
boundary_line = is_boundary(state->boundaries, buf);
if(!strstr(buf, "boundary=") && !strstr(buf, "boundary =") && boundary_line == 1){
state->content_type_is_set = 0;
/*if(state->has_to_dump == 1){
if(state->fd != -1) close(state->fd);
state->fd = -1;
}*/
if(state->n_attachments < MAX_ATTACHMENTS-1) state->n_attachments++;
state->has_to_dump = 0;
state->base64 = 0; state->textplain = 0; state->texthtml = state->octetstream = 0;
state->skip_html = 0;
state->utf8 = 0;
state->qp = 0;
state->realbinary = 0;
return 0;
}
if(boundary_line == 1){ return 0; }
/* end of boundary check */
/* skip non textual stuff */
if(state->message_state == MSG_BODY){
/*if(state->has_to_dump == 1 && state->fd != -1){
write(state->fd, buf, strlen(buf));
}*/
if(state->base64 == 1) state->attachments[state->n_attachments].size += strlen(buf) / BASE64_RATIO;
else state->attachments[state->n_attachments].size += strlen(buf);
}
if(state->message_state == MSG_BODY && strlen(buf) < 2) return 0;
/*
* sometimes spammers screw up their junk messages, and
* use "application/octet-stream" type for textual parts.
* Now clapf checks whether the attachment is really
* binary. If it has no non-printable characters in a
* base64 encoded line, then let's tokenize it.
*
* Note: in this case we cannot expect fully compliant
* message part. However this case should be very rare
* since legitim messages use proper mime types.
*
* 2010.10.23, SJ
*/
if(state->message_state == MSG_BODY && state->realbinary == 0 && state->octetstream == 1){
snprintf(puf, MAXBUFSIZE-1, "%s", buf);
if(state->base64 == 1) decodeBase64(puf);
if(state->qp == 1) decodeQP(puf);
state->realbinary += countNonPrintableCharacters(puf);
}
if(state->is_header == 0 && state->textplain == 0 && state->texthtml == 0 && (state->message_state == MSG_BODY || state->message_state == MSG_CONTENT_DISPOSITION) && (state->octetstream == 0 || state->realbinary > 0) ) return 0;
/* base64 decode buffer */
if(state->base64 == 1 && state->message_state == MSG_BODY) b64_len = decodeBase64(buf);
/* fix encoded From:, To: and Subject: lines, 2008.11.24, SJ */
if(state->message_state == MSG_FROM || state->message_state == MSG_TO || state->message_state == MSG_CC || state->message_state == MSG_SUBJECT) fixupEncodedHeaderLine(buf);
/* fix soft breaks with quoted-printable decoded stuff, 2006.03.01, SJ */
if(state->qp == 1) fixupSoftBreakInQuotedPritableLine(buf, state);
/* fix base64 stuff if the line does not end with a line break, 2006.03.01, SJ */
if(state->base64 == 1 && state->message_state == MSG_BODY) fixupBase64EncodedLine(buf, state);
if(state->texthtml == 1 && state->message_state == MSG_BODY) markHTML(buf, state);
if(state->message_state == MSG_BODY){
if(state->qp == 1) decodeQP(buf);
if(state->utf8 == 1) decodeUTF8(buf);
}
decodeURL(buf);
if(state->texthtml == 1) decodeHTML(buf);
translateLine((unsigned char*)buf, state);
reassembleToken(buf);
if(state->is_header == 1) p = strchr(buf, ' ');
else p = buf;
//if(strlen(buf) > 3) printf("%d original: %s\n", state->message_state, buf);
do {
p = split(p, DELIMITER, puf, MAXBUFSIZE-1);
if(strcasestr(puf, "http://") || strcasestr(puf, "https://")){
q = puf;
do {
q = split_str(q, "http://", u, SMALLBUFSIZE-1);
if(u[strlen(u)-1] == '.') u[strlen(u)-1] = '\0';
if(strlen(u) > 2 && strncasecmp(u, "www.w3.org", 10) && strchr(u, '.') ){
snprintf(muf, MAXBUFSIZE-1, "http://%s", u);
fixURL(muf);
strncat(muf, " ", MAXBUFSIZE-1);
strncat(state->b_body, muf, BIGBUFSIZE-1);
}
} while(q);
continue;
}
if(state->message_state != MSG_SUBJECT && (strlen(puf) < MIN_WORD_LEN || (strlen(puf) > MAX_WORD_LEN && state->message_state != MSG_FROM && state->message_state != MSG_TO && state->message_state != MSG_CC) || isHexNumber(puf)))
continue;
if(strlen(puf) < 2 || strncmp(puf, "HTML*", 5) == 0) continue;
if(state->message_state == MSG_CONTENT_TYPE && strncmp(puf, "content-type", 12) == 0) continue;
if(state->message_state == MSG_CONTENT_DISPOSITION && strncmp(puf, "content-disposition", 19) == 0) continue;
if(state->message_state == MSG_CONTENT_TRANSFER_ENCODING && strncmp(puf, "content-transfer-encoding", 25) == 0) continue;
degenerateToken((unsigned char*)puf);
strncat(puf, " ", MAXBUFSIZE-1);
if(state->message_state == MSG_SUBJECT)
strncat(state->b_subject, puf, MAXBUFSIZE-1);
else if(state->message_state == MSG_FROM && strchr(puf, '@'))
strncat(state->b_from, puf, SMALLBUFSIZE-1);
else if(state->message_state == MSG_TO && strchr(puf, '@'))
strncat(state->b_to, puf, SMALLBUFSIZE-1);
else if(state->message_state == MSG_CC && strchr(puf, '@'))
strncat(state->b_to, puf, SMALLBUFSIZE-1);
else if(state->is_header == 0)
strncat(state->b_body, puf, BIGBUFSIZE-1);
} while(p);
return 0;
}

41
src/parser.h Normal file
View File

@ -0,0 +1,41 @@
/*
* parser.h, SJ
*/
#ifndef _PARSER_H
#define _PARSER_H
#include "cfg.h"
#include "config.h"
#include "defs.h"
struct _state parseMessage(struct session_data *sdata, struct __config *cfg);
struct _state parseBuffer(struct session_data *sdata, struct __config *cfg);
int parseLine(char *buf, struct _state *state, struct session_data *sdata, struct __config *cfg);
void initState(struct _state *state);
void freeState(struct _state *state);
int extract_boundary(char *p, struct _state *state);
int extractNameFromHeaderLine(char *s, char *name, char *resultbuf);
int attachment_by_type(struct _state *state, char *type);
void fixupEncodedHeaderLine(char *buf);
void fixupSoftBreakInQuotedPritableLine(char *buf, struct _state *state);
void fixupBase64EncodedLine(char *buf, struct _state *state);
void markHTML(char *buf, struct _state *state);
int appendHTMLTag(char *buf, char *htmlbuf, int pos, struct _state *state);
void fixupHTML(char *buf, struct _state *state, struct __config *cfg);
int isSkipHTMLTag(char *s);
void translateLine(unsigned char *p, struct _state *state);
void reassembleToken(char *p);
void degenerateToken(unsigned char *p);
int countInvalidJunkLines(char *p);
int countInvalidJunkCharacters(char *p, int replace_junk);
int countNonPrintableCharacters(char *p);
int isHexNumber(char *p);
void fixURL(char *url);
void fixFQDN(char *fqdn);
void getTLDFromName(char *name);
int isItemOnList(char *item, char *list, char *extralist);
unsigned long parse_date_header(char *s);
#endif /* _PARSER_H */

836
src/parser_utils.c Normal file
View File

@ -0,0 +1,836 @@
/*
* parser_utils.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <piler.h>
#include "trans.h"
#include "ijc.h"
#include "html.h"
void initState(struct _state *state){
int i;
state->message_state = MSG_UNDEF;
state->is_header = 1;
/* by default we are a text/plain message */
state->textplain = 1;
state->texthtml = 0;
state->message_rfc822 = 0;
state->octetstream = 0;
state->base64 = 0;
state->utf8 = 0;
state->qp = 0;
state->htmltag = 0;
state->style = 0;
state->skip_html = 0;
state->n_token = 0;
state->n_body_token = 0;
state->n_chain_token = 0;
state->n_subject_token = 0;
state->content_type_is_set = 0;
state->c_shit = 0;
state->l_shit = 0;
state->line_num = 0;
state->ipcnt = 0;
memset(state->ip, 0, SMALLBUFSIZE);
memset(state->hostname, 0, SMALLBUFSIZE);
memset(state->miscbuf, 0, MAX_TOKEN_LEN);
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
memset(state->from, 0, SMALLBUFSIZE);
memset(state->message_id, 0, SMALLBUFSIZE);
state->urls = NULL;
state->found_our_signo = 0;
state->has_to_dump = 0;
state->fd = -1;
state->num_of_images = 0;
state->num_of_msword = 0;
state->realbinary = 0;
state->boundaries = NULL;
state->n_attachments = 0;
state->has_base64 = 0;
for(i=0; i<MAX_ATTACHMENTS; i++){
state->attachments[i].size = 0;
memset(state->attachments[i].type, 0, SMALLBUFSIZE);
memset(state->attachments[i].filename, 0, SMALLBUFSIZE);
}
memset(state->b_from, 0, SMALLBUFSIZE);
memset(state->b_to, 0, SMALLBUFSIZE);
memset(state->b_subject, 0, MAXBUFSIZE);
memset(state->b_body, 0, BIGBUFSIZE);
}
void freeState(struct _state *state){
freeList(state->urls);
}
int attachment_by_type(struct _state *state, char *type){
int i;
for(i=0; i<MAX_ATTACHMENTS; i++){
if(strstr(state->attachments[i].type, type))
return 1;
}
return 0;
}
/*
* extract bondary
*/
int extract_boundary(char *p, struct _state *state){
char *q;
p += strlen("boundary");
q = strchr(p, '"');
if(q) *q = ' ';
/*
* I've seen an idiot spammer using the following boundary definition in the header:
*
* Content-Type: multipart/alternative;
* boundary=3D"b1_52b92b01a943615aff28b7f4d2f2d69d"
*/
if(strncmp(p, "=3D", 3) == 0){
*(p+3) = '=';
p += 3;
}
p = strchr(p, '=');
if(p){
p++;
for(; *p; p++){
if(isspace(*p) == 0)
break;
}
q = strrchr(p, '"');
if(q) *q = '\0';
q = strrchr(p, '\r');
if(q) *q = '\0';
q = strrchr(p, '\n');
if(q) *q = '\0';
append_boundary(&(state->boundaries), p);
return 1;
}
return 0;
}
int extractNameFromHeaderLine(char *s, char *name, char *resultbuf){
int rc=0;
char buf[SMALLBUFSIZE], *p, *q;
snprintf(buf, SMALLBUFSIZE-1, "%s", s);
p = strstr(buf, name);
if(p){
p += strlen(name);
p = strchr(p, '=');
if(p){
p++;
q = strrchr(p, ';');
if(q) *q = '\0';
q = strrchr(p, '"');
if(q){
*q = '\0';
p = strchr(p, '"');
if(p){
p++;
}
}
snprintf(resultbuf, SMALLBUFSIZE-1, "%s", p);
rc = 1;
}
}
return rc;
}
void fixupEncodedHeaderLine(char *buf){
char *p, *q, *r, *s, u[SMALLBUFSIZE], puf[MAXBUFSIZE];
char *start, *end;
memset(puf, 0, MAXBUFSIZE);
q = buf;
do {
q = split_str(q, " ", u, SMALLBUFSIZE-1);
p = u;
do {
start = strstr(p, "=?");
if(start){
if(start != p){
*start = '\0';
strncat(puf, p, MAXBUFSIZE-1);
*start = '=';
}
/* find the trailing '?=' sequence */
end = strrchr(p, '?'); r = strrchr(p, '=');
if(end && r && r == end+1){
*end = '\0';
p = end + 2;
s = NULL;
if((s = strcasestr(start+2, "?B?"))){
*s = '\0';
decodeBase64(s+3);
}
else if((s = strcasestr(start+2, "?Q?"))){
*s = '\0';
r = s + 3;
for(; *r; r++){
if(*r == '_') *r = ' ';
}
decodeQP(s+3);
}
if(s && strncasecmp(start, "=?utf-8", 5) == 0){
decodeUTF8(s+3);
}
if(s) strncat(puf, s+3, MAXBUFSIZE-1);
}
else {
start = NULL;
}
}
if(!start){
strncat(puf, p, MAXBUFSIZE-1);
}
} while(start);
strncat(puf, " ", MAXBUFSIZE-1);
} while(q);
snprintf(buf, MAXBUFSIZE-1, "%s", puf);
}
void fixupSoftBreakInQuotedPritableLine(char *buf, struct _state *state){
int i=0;
char *p, puf[MAXBUFSIZE];
if(strlen(state->qpbuf) > 0){
memset(puf, 0, MAXBUFSIZE);
strncpy(puf, state->qpbuf, MAXBUFSIZE-1);
strncat(puf, buf, MAXBUFSIZE-1);
memset(buf, 0, MAXBUFSIZE);
memcpy(buf, puf, MAXBUFSIZE);
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
}
if(buf[strlen(buf)-1] == '='){
buf[strlen(buf)-1] = '\0';
i = 1;
}
if(i == 1){
p = strrchr(buf, ' ');
if(p){
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
if(strlen(p) < MAX_TOKEN_LEN-1){
snprintf(state->qpbuf, MAXBUFSIZE-1, "%s", p);
*p = '\0';
}
}
}
}
void fixupBase64EncodedLine(char *buf, struct _state *state){
char *p, puf[MAXBUFSIZE];
if(strlen(state->miscbuf) > 0){
memset(puf, 0, MAXBUFSIZE);
strncpy(puf, state->miscbuf, MAXBUFSIZE-1);
strncat(puf, buf, MAXBUFSIZE-1);
memset(buf, 0, MAXBUFSIZE);
memcpy(buf, puf, MAXBUFSIZE);
memset(state->miscbuf, 0, MAX_TOKEN_LEN);
}
if(buf[strlen(buf)-1] != '\n'){
p = strrchr(buf, ' ');
if(p){
strncpy(state->miscbuf, p+1, MAX_TOKEN_LEN-1);
*p = '\0';
}
}
}
void markHTML(char *buf, struct _state *state){
char *s, puf[MAXBUFSIZE], html[SMALLBUFSIZE];
int k=0, j=0, pos=0;
memset(puf, 0, MAXBUFSIZE);
memset(html, 0, SMALLBUFSIZE);
s = buf;
for(; *s; s++){
if(*s == '<'){
state->htmltag = 1;
puf[k] = ' ';
k++;
memset(html, 0, SMALLBUFSIZE); j=0;
pos = 0;
//printf("start html:%c\n", *s);
}
if(state->htmltag == 1){
if(j == 0 && *s == '!'){
state->skip_html = 1;
//printf("skiphtml=1\n");
}
if(state->skip_html == 0){
if(*s != '>' && *s != '<' && *s != '"'){
//printf("j=%d/%c", j, *s);
html[j] = tolower(*s);
if(j < SMALLBUFSIZE-10) j++;
}
if(isspace(*s)){
if(j > 0){
k += appendHTMLTag(puf, html, pos, state);
memset(html, 0, SMALLBUFSIZE); j=0;
}
pos++;
}
}
}
else {
if(state->style == 0){
puf[k] = *s;
k++;
}
}
if(*s == '>'){
state->htmltag = 0;
state->skip_html = 0;
//printf("skiphtml=0\n");
//printf("end html:%c\n", *s);
//strncat(html, " ", SMALLBUFSIZE-1);
if(j > 0){
strncat(html, " ", SMALLBUFSIZE-1);
k += appendHTMLTag(puf, html, pos, state);
memset(html, 0, SMALLBUFSIZE); j=0;
}
}
}
//printf("append last in line:*%s*, html=+%s+, j=%d\n", puf, html, j);
if(j > 0){ k += appendHTMLTag(puf, html, pos, state); }
strcpy(buf, puf);
}
int appendHTMLTag(char *buf, char *htmlbuf, int pos, struct _state *state){
char *p, html[SMALLBUFSIZE];
int len;
if(pos == 0 && strncmp(htmlbuf, "style ", 6) == 0) state->style = 1;
if(pos == 0 && strncmp(htmlbuf, "/style ", 7) == 0) state->style = 0;
//printf("appendHTML: pos:%d, +%s+\n", pos, htmlbuf);
if(state->style == 1) return 0;
if(strlen(htmlbuf) == 0) return 0;
snprintf(html, SMALLBUFSIZE-1, "HTML*%s", htmlbuf);
len = strlen(html);
if(isSkipHTMLTag(html) == 1) return 0;
if(len > 8 && strchr(html, '=')){
p = strstr(html, "cid:");
if(p){
*(p+3) = '\0';
strncat(html, " ", SMALLBUFSIZE-1);
}
strncat(buf, html, MAXBUFSIZE-1);
return len;
}
if(strstr(html, "http") ){
strncat(buf, html+5, MAXBUFSIZE-1);
return len-5;
}
return 0;
}
int isSkipHTMLTag(char *s){
int i=0;
for(i=0; i<NUM_OF_SKIP_TAGS; i++){
//printf("matching *%s* on *%s*\n", s+5, skip_html_tags[i].entity);
if(strncmp(s+5, skip_html_tags[i].entity, skip_html_tags[i].length) == 0) return 1;
}
return 0;
}
void translateLine(unsigned char *p, struct _state *state){
int url=0;
unsigned char *q=NULL, *P=p;
for(; *p; p++){
/* save position of '=', 2006.01.05, SJ */
if(state->qp == 1 && *p == '='){
q = p;
}
if( (state->message_state == MSG_RECEIVED || state->message_state == MSG_FROM || state->message_state == MSG_TO || state->message_state == MSG_CC) && *p == '@'){ continue; }
if(state->message_state == MSG_SUBJECT && *p == '%'){ continue; }
if(state->message_state == MSG_CONTENT_TYPE && *p == '_' ){ continue; }
if(state->message_state != MSG_BODY && (*p == '.' || *p == '-') ){ continue; }
if(strncasecmp((char *)p, "http://", 7) == 0){ p += 7; url = 1; continue; }
if(strncasecmp((char *)p, "https://", 8) == 0){ p += 8; url = 1; continue; }
if(url == 1 && (*p == '.' || *p == '-' || *p == '_' || *p == '/' || isalnum(*p)) ) continue;
if(url == 1) url = 0;
if(state->texthtml == 1 && state->message_state == MSG_BODY && strncmp((char *)p, "HTML*", 5) == 0){
p += 5;
while(isspace(*p) == 0){
p++;
}
}
if(delimiter_characters[(unsigned int)*p] != ' ' || isalnum(*p) == 0)
*p = ' ';
else {
*p = tolower(*p);
}
}
/* restore the soft break in quoted-printable parts, 2006.01.05, SJ */
if(state->qp == 1 && q && (q > P + strlen((char*)P) - 3))
*q = '=';
}
/*
* reassemble 'V i a g r a' to 'Viagra'
*/
void reassembleToken(char *p){
int i, k=0;
for(i=0; i<strlen(p); i++){
if(isspace(*(p+i-1)) && !isspace(*(p+i)) && isspace(*(p+i+1)) && !isspace(*(p+i+2)) && isspace(*(p+i+3)) && !isspace(*(p+i+4)) && isspace(*(p+i+5)) ){
p[k] = *(p+i); k++;
p[k] = *(p+i+2); k++;
p[k] = *(p+i+4); k++;
i += 5;
}
else {
p[k] = *(p+i);
k++;
}
}
p[k] = '\0';
}
/*
* degenerate a token
*/
void degenerateToken(unsigned char *p){
int i=1, d=0, dp=0;
unsigned char *s;
/* quit if this the string does not end with a punctuation character */
if(!ispunct(*(p+strlen((char *)p)-1)))
return;
s = p;
for(; *p; p++){
if(ispunct(*p)){
d = i;
if(!ispunct(*(p-1)))
dp = d;
}
else
d = dp = i;
i++;
}
*(s+dp) = '\0';
if(*(s+dp-1) == '.' || *(s+dp-1) == '!' || *(s+dp-1) == '?') *(s+dp-1) = '\0';
}
/*
* count the invalid characters (ie. garbage on your display) in the buffer
*/
int countInvalidJunkCharacters(char *p, int replace_junk){
int i=0;
for(; *p; p++){
if(invalid_junk_characters[(unsigned char)*p] == *p){
i++;
if(replace_junk == 1) *p = JUNK_REPLACEMENT_CHAR;
}
}
return i;
}
/*
* detect Chinese, Japan, Korean, ... lines
*/
int countInvalidJunkLines(char *p){
int i=0;
if(*p == '' && *(p+1) == '$' && *(p+2) == 'B'){
for(; *p; p++){
if(*p == '' && *(p+1) == '(' && *(p+2) == 'B')
i++;
}
}
return i;
}
int countNonPrintableCharacters(char *p){
int n = 0;
for(; *p; p++){
if(!isprint(*p) && !isspace(*p)) n++;
}
return n;
}
/*
* is this a hexadecimal numeric string?
*/
int isHexNumber(char *p){
for(; *p; p++){
if(!(*p == '-' || (*p >= 0x30 && *p <= 0x39) || (*p >= 0x41 && *p <= 0x46) || (*p >= 0x61 && *p <= 0x66)) )
return 0;
}
return 1;
}
void fixURL(char *url){
char *p, *q, m[MAX_TOKEN_LEN], fixed_url[MAXBUFSIZE];
int i, dots=0;
struct in_addr addr;
/* chop trailing dot */
if(url[strlen(url)-1] == '.')
url[strlen(url)-1] = '\0';
memset(fixed_url, 0, MAXBUFSIZE);
if((strncasecmp(url, "http://", 7) == 0 || strncasecmp(url, "https://", 8) == 0) ){
p = url;
if(strncasecmp(p, "http://", 7) == 0) p += 7;
if(strncasecmp(p, "https://", 8) == 0) p += 8;
/* skip anything after the host part, 2006.12.11, SJ */
q = strchr(p, '/');
if(q)
*q = '\0';
/*
http://www.ajandekkaracsonyra.hu/email.php?page=email&cmd=unsubscribe&email=yy@xxxx.kom is
chopped to www.ajandekkaracsonyra.hu at this point, 2006.12.15, SJ
*/
dots = countCharacterInBuffer(p, '.');
if(dots < 1)
return;
strncpy(fixed_url, "URL*", MAXBUFSIZE-1);
/* is it a numeric IP-address? */
if(inet_aton(p, &addr)){
addr.s_addr = ntohl(addr.s_addr);
strncat(fixed_url, inet_ntoa(addr), MAXBUFSIZE-1);
strcpy(url, fixed_url);
}
else {
for(i=0; i<=dots; i++){
q = split(p, '.', m, MAX_TOKEN_LEN-1);
if(i>dots-2){
strncat(fixed_url, m, MAXBUFSIZE-1);
if(i < dots)
strncat(fixed_url, ".", MAXBUFSIZE-1);
}
p = q;
}
/* if it does not contain a single dot, the rest of the URL may be
in the next line or it is a fake one, anyway skip, 2006.04.06, SJ
*/
if(countCharacterInBuffer(fixed_url, '.') != 1)
memset(url, 0, MAXBUFSIZE);
else {
for(i=4; i<strlen(fixed_url); i++)
fixed_url[i] = tolower(fixed_url[i]);
strcpy(url, fixed_url);
}
}
}
}
/*
* fix a long FQDN
*/
void fixFQDN(char *fqdn){
char *p, *q, m[MAX_TOKEN_LEN], fixed_fqdn[MAXBUFSIZE];
int i, dots=0;
/* chop trailing dot */
if(fqdn[strlen(fqdn)-1] == '.')
fqdn[strlen(fqdn)-1] = '\0';
memset(fixed_fqdn, 0, MAXBUFSIZE);
p = fqdn;
dots = countCharacterInBuffer(p, '.');
if(dots < 1)
return;
for(i=0; i<=dots; i++){
q = split(p, '.', m, MAX_TOKEN_LEN-1);
if(i>dots-2){
strncat(fixed_fqdn, m, MAXBUFSIZE-1);
if(i < dots)
strncat(fixed_fqdn, ".", MAXBUFSIZE-1);
}
p = q;
}
strcpy(fqdn, fixed_fqdn);
}
/*
* extract the .tld from a name (URL, FQDN, ...)
*/
void getTLDFromName(char *name){
char *p, fixed_name[SMALLBUFSIZE];;
p = strrchr(name, '.');
if(p){
snprintf(fixed_name, SMALLBUFSIZE-1, "URL*%s", p+1);
strcpy(name, fixed_name);
}
}
int isItemOnList(char *item, char *list, char *extralist){
char *p, *q, w[SMALLBUFSIZE], my_list[SMALLBUFSIZE];
if(!item) return 0;
snprintf(my_list, SMALLBUFSIZE-1, "%s,%s", extralist, list);
p = my_list;
do {
p = split(p, ',', w, SMALLBUFSIZE-1);
trimBuffer(w);
if(strlen(w) > 2){
if(w[strlen(w)-1] == '$'){
q = item + strlen(item) - strlen(w) + 1;
if(strncasecmp(q, w, strlen(w)-1) == 0)
return 1;
}
else if(strcasestr(item, w))
return 1;
}
} while(p);
return 0;
}
unsigned long parse_date_header(char *s){
char *p;
unsigned long ts=0;
struct tm tm;
s += 5;
p = s;
if(*p == ' '){ p++; s++; }
p = strchr(s, ',');
if(!p) goto ENDE;
*p = '\0';
if(strcmp(s, "Mon") == 0) tm.tm_wday = 1;
else if(strcmp(s, "Tue") == 0) tm.tm_wday = 2;
else if(strcmp(s, "Wed") == 0) tm.tm_wday = 3;
else if(strcmp(s, "Thu") == 0) tm.tm_wday = 4;
else if(strcmp(s, "Fri") == 0) tm.tm_wday = 5;
else if(strcmp(s, "Sat") == 0) tm.tm_wday = 6;
else if(strcmp(s, "Sun") == 0) tm.tm_wday = 0;
s += 5;
p = strchr(s, ' '); if(!p) goto ENDE;
*p = '\0'; tm.tm_mday = atoi(s); s += 3;
p = strchr(s, ' '); if(!p) goto ENDE;
*p = '\0';
if(strcmp(s, "Jan") == 0) tm.tm_mon = 0;
else if(strcmp(s, "Feb") == 0) tm.tm_mon = 1;
else if(strcmp(s, "Mar") == 0) tm.tm_mon = 2;
else if(strcmp(s, "Apr") == 0) tm.tm_mon = 3;
else if(strcmp(s, "May") == 0) tm.tm_mon = 4;
else if(strcmp(s, "Jun") == 0) tm.tm_mon = 5;
else if(strcmp(s, "Jul") == 0) tm.tm_mon = 6;
else if(strcmp(s, "Aug") == 0) tm.tm_mon = 7;
else if(strcmp(s, "Sep") == 0) tm.tm_mon = 8;
else if(strcmp(s, "Oct") == 0) tm.tm_mon = 9;
else if(strcmp(s, "Nov") == 0) tm.tm_mon = 10;
else if(strcmp(s, "Dec") == 0) tm.tm_mon = 11;
s = p+1;
p = strchr(s, ' '); if(!p) goto ENDE;
tm.tm_year = atoi(s) - 1900; s = p+1;
p = strchr(s, ':'); if(!p) goto ENDE;
*p = '\0'; tm.tm_hour = atoi(s); s = p+1;
p = strchr(s, ':'); if(!p) goto ENDE;
*p = '\0'; tm.tm_min = atoi(s); s = p+1;
p = strchr(s, ' '); if(!p) goto ENDE;
*p = '\0'; tm.tm_sec = atoi(s); s = p+1;
tm.tm_isdst = -1;
ts = mktime(&tm);
ENDE:
return ts;
}

249
src/piler.c Normal file
View File

@ -0,0 +1,249 @@
/*
* piler.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <locale.h>
#include <errno.h>
#include <piler.h>
extern char *optarg;
extern int optind;
int sd;
int nconn = 0;
char *configfile = CONFIG_FILE;
struct __config cfg;
struct __data data;
struct passwd *pwd;
void clean_exit(){
if(sd != -1) close(sd);
freeList(data.blackhole);
syslog(LOG_PRIORITY, "%s has been terminated", PROGNAME);
unlink(cfg.pidfile);
exit(1);
}
void fatal(char *s){
syslog(LOG_PRIORITY, "%s\n", s);
clean_exit();
}
void sigchld(){
int pid, wstat;
while((pid = wait_nohang(&wstat)) > 0){
if(nconn > 0) nconn--;
}
}
void initialiseConfiguration(){
cfg = read_config(configfile);
if(strlen(cfg.username) > 1){
pwd = getpwnam(cfg.username);
if(!pwd) fatal(ERR_NON_EXISTENT_USER);
}
if(getuid() == 0 && pwd){
check_and_create_directories(&cfg, pwd->pw_uid, pwd->pw_gid);
}
if(chdir(cfg.workdir)){
syslog(LOG_PRIORITY, "workdir: *%s*", cfg.workdir);
fatal(ERR_CHDIR);
}
setlocale(LC_MESSAGES, cfg.locale);
setlocale(LC_CTYPE, cfg.locale);
freeList(data.blackhole);
data.blackhole = NULL;
syslog(LOG_PRIORITY, "reloaded config: %s", configfile);
#ifdef HAVE_MEMCACHED
memcached_init(&(data.memc), cfg.memcached_servers, 11211);
#endif
}
int read_key(struct __config *cfg){
int fd, n;
fd = open(KEYFILE, O_RDONLY);
if(fd == -1){
syslog(LOG_PRIORITY, "cannot read keyfile: %s", KEYFILE);
return -1;
}
n = read(fd, cfg->key, KEYLEN);
close(fd);
if(n > 5) return 0;
return 1;
}
int main(int argc, char **argv){
int i, new_sd, yes=1, pid, daemonise=0;
unsigned int clen;
struct sockaddr_in client_addr, serv_addr;
struct in_addr addr;
while((i = getopt(argc, argv, "c:dvVh")) > 0){
switch(i){
case 'c' :
configfile = optarg;
break;
case 'd' :
daemonise = 1;
break;
case 'v' :
case 'V' :
__fatal(PROGNAME " " PROGINFO);
break;
case 'h' :
default :
__fatal("usage: ...");
}
}
(void) openlog(PROGNAME, LOG_PID, LOG_MAIL);
sig_catch(SIGINT, clean_exit);
sig_catch(SIGQUIT, clean_exit);
sig_catch(SIGKILL, clean_exit);
sig_catch(SIGTERM, clean_exit);
sig_catch(SIGHUP, initialiseConfiguration);
data.blackhole = NULL;
sig_block(SIGCHLD);
sig_catch(SIGCHLD, sigchld);
initialiseConfiguration();
if(read_key(&cfg)) fatal(ERR_READING_KEY);
if((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
fatal(ERR_OPEN_SOCKET);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(cfg.listen_port);
inet_aton(cfg.listen_addr, &addr);
serv_addr.sin_addr.s_addr = addr.s_addr;
bzero(&(serv_addr.sin_zero), 8);
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
fatal(ERR_SET_SOCK_OPT);
if(bind(sd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
fatal(ERR_BIND_TO_PORT);
if(listen(sd, cfg.backlog) == -1)
fatal(ERR_LISTEN);
if(drop_privileges(pwd)) fatal(ERR_SETUID);
syslog(LOG_PRIORITY, "%s %s starting", PROGNAME, VERSION);
#if HAVE_DAEMON == 1
if(daemonise == 1) i = daemon(1, 0);
#endif
write_pid_file(cfg.pidfile);
/* main accept loop */
for(;;){
/* let new connections wait if we are too busy now */
if(nconn >= cfg.max_connections) sig_pause();
clen = sizeof(client_addr);
sig_unblock(SIGCHLD);
new_sd = accept(sd, (struct sockaddr *)&client_addr, &clen);
sig_block(SIGCHLD);
if(new_sd == -1) continue;
pid = fork();
if(pid == 0){
sig_uncatch(SIGCHLD);
sig_unblock(SIGCHLD);
sig_uncatch(SIGINT);
sig_uncatch(SIGQUIT);
sig_uncatch(SIGKILL);
sig_uncatch(SIGTERM);
sig_block(SIGHUP);
syslog(LOG_PRIORITY, "connection from client: %s", inet_ntoa(client_addr.sin_addr));
handle_smtp_session(new_sd, &data, &cfg);
_exit(0);
}
else if(pid > 0){
nconn++;
}
else {
syslog(LOG_PRIORITY, "%s", ERR_FORK_FAILED);
}
close(new_sd);
}
return 0;
}

41
src/piler.h Normal file
View File

@ -0,0 +1,41 @@
/*
* piler.h, SJ
*/
#ifndef _PILER_H
#define _PILER_H
#include <misc.h>
#include <list.h>
#include <parser.h>
#include <errmsg.h>
#include <smtpcodes.h>
#include <session.h>
#include <decoder.h>
#include <boundary.h>
#include <defs.h>
#include <sig.h>
#include <av.h>
#include <config.h>
#include <unistd.h>
#ifdef HAVE_MEMCACHED
#include "memc.h"
#endif
int do_av_check(struct session_data *sdata, char *rcpttoemail, char *fromemail, char *virusinfo, struct __data *data, struct __config *cfg);
int make_header_digest(struct session_data *sdata, struct _state *state);
int make_body_digest(struct session_data *sdata);
int processMessage(struct session_data *sdata, struct _state *sstate, struct __config *cfg);
int store_message(struct session_data *sdata, struct _state *state, int stored, struct __config *cfg);
struct __config read_config(char *configfile);
void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid);
void updateCounters(struct session_data *sdata, struct __data *data, struct __counters *counters, struct __config *cfg);
#endif /* _PILER_H */

60
src/pilerconf.c Normal file
View File

@ -0,0 +1,60 @@
/*
* pilerconf.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <piler.h>
extern char *optarg;
extern int optind;
void print_config_all(struct __config *cfg, char *key);
void print_config(char *configfile, struct __config *cfg);
int main(int argc, char **argv){
int i, print_from_file=0;
char *configfile=CONFIG_FILE, *query=NULL;
struct __config cfg;
while((i = getopt(argc, argv, "c:q:nh?")) > 0){
switch(i){
case 'c' :
configfile = optarg;
break;
case 'n' :
print_from_file = 1;
break;
case 'q' :
query = optarg;
break;
case 'h' :
case '?' :
printf("pilerconf [-n -c <configfile>] [-q <key>]\n");
break;
default :
break;
}
}
cfg = read_config(configfile);
if(print_from_file == 1){
print_config(configfile, &cfg);
}
else {
print_config_all(&cfg, query);
}
return 0;
}

499
src/session.c Normal file
View File

@ -0,0 +1,499 @@
/*
* session.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <syslog.h>
#include <time.h>
#include <piler.h>
void handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){
int i, ret, pos, n, inj=ERR, state, prevlen=0;
char *p, buf[MAXBUFSIZE], puf[MAXBUFSIZE], resp[MAXBUFSIZE], prevbuf[MAXBUFSIZE], last2buf[2*MAXBUFSIZE+1];
char rctptoemail[SMALLBUFSIZE], fromemail[SMALLBUFSIZE], virusinfo[SMALLBUFSIZE], reason[SMALLBUFSIZE];
struct session_data sdata;
struct _state sstate;
int db_conn=0;
int rc;
struct __counters counters;
struct timezone tz;
struct timeval tv1, tv2;
alarm(cfg->session_timeout);
sig_catch(SIGALRM, killChild);
state = SMTP_STATE_INIT;
initSessionData(&sdata);
bzero(&counters, sizeof(counters));
/* open database connection */
db_conn = 0;
#ifdef NEED_MYSQL
rc = 1;
mysql_init(&(sdata.mysql));
mysql_options(&(sdata.mysql), MYSQL_OPT_CONNECT_TIMEOUT, (const char*)&cfg->mysql_connect_timeout);
mysql_options(&(sdata.mysql), MYSQL_OPT_RECONNECT, (const char*)&rc);
if(mysql_real_connect(&(sdata.mysql), cfg->mysqlhost, cfg->mysqluser, cfg->mysqlpwd, cfg->mysqldb, cfg->mysqlport, cfg->mysqlsocket, 0))
db_conn = 1;
else
syslog(LOG_PRIORITY, "%s", ERR_MYSQL_CONNECT);
#endif
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: fork()", sdata.ttmpfile);
gettimeofday(&tv1, &tz);
#ifdef HAVE_LMTP
snprintf(buf, MAXBUFSIZE-1, LMTP_RESP_220_BANNER, cfg->hostid);
#else
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_220_BANNER, cfg->hostid);
#endif
send(new_sd, buf, strlen(buf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf);
while((n = recvtimeout(new_sd, puf, MAXBUFSIZE, TIMEOUT)) > 0){
pos = 0;
/* accept mail data */
if(state == SMTP_STATE_DATA){
/* join the last 2 buffer */
memset(last2buf, 0, 2*MAXBUFSIZE+1);
memcpy(last2buf, prevbuf, MAXBUFSIZE);
memcpy(last2buf+prevlen, puf, MAXBUFSIZE);
if(sdata.hdr_len == 0){
sdata.hdr_len = searchStringInBuffer(last2buf, 2*MAXBUFSIZE+1, "\n\r\n", 3);
if(sdata.hdr_len == 0) searchStringInBuffer(last2buf, 2*MAXBUFSIZE+1, "\n\n", 2);
}
pos = searchStringInBuffer(last2buf, 2*MAXBUFSIZE+1, SMTP_CMD_PERIOD, 5);
if(pos > 0){
/* fix position */
pos = pos - prevlen;
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: period found", sdata.ttmpfile);
/* write data only to (and including) the trailing period (.) */
ret = write(sdata.fd, puf, pos);
sdata.tot_len += ret;
/* fix posistion! */
pos += strlen(SMTP_CMD_PERIOD);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: got: (.), header length=%d", sdata.ttmpfile, sdata.hdr_len);
state = SMTP_STATE_PERIOD;
/* make sure we had a successful read */
rc = fsync(sdata.fd);
close(sdata.fd);
gettimeofday(&tv2, &tz);
sdata.__acquire = tvdiff(tv2, tv1);
if(rc){
syslog(LOG_PRIORITY, "failed writing data: %s", sdata.ttmpfile);
#ifdef HAVE_LMTP
for(i=0; i<sdata.num_of_rcpt_to; i++){
#endif
send(new_sd, SMTP_RESP_421_ERR_WRITE_FAILED, strlen(SMTP_RESP_421_ERR_WRITE_FAILED), 0);
#ifdef HAVE_LMTP
}
#endif
memset(puf, 0, MAXBUFSIZE);
goto AFTER_PERIOD;
}
gettimeofday(&tv1, &tz);
sstate = parseMessage(&sdata, cfg);
gettimeofday(&tv2, &tz);
sdata.__parsed = tvdiff(tv2, tv1);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: parsed message", sdata.ttmpfile);
sdata.need_scan = 1;
#ifdef HAVE_ANTIVIRUS
if(cfg->use_antivirus == 1){
gettimeofday(&tv1, &tz);
sdata.rav = do_av_check(&sdata, rctptoemail, fromemail, &virusinfo[0], data, cfg);
gettimeofday(&tv2, &tz);
sdata.__av = tvdiff(tv2, tv1);
}
#endif
#ifdef HAVE_LMTP
for(i=0; i<sdata.num_of_rcpt_to; i++){
#else
i = 0;
#endif
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: round %d in injection", sdata.ttmpfile, i);
extractEmail(sdata.rcptto[i], rctptoemail);
/* copy default config to enable policy support */
//memcpy(&my_cfg, cfg, sizeof(struct __config));
inj = ERR;
if(db_conn == 1){
if(AVIR_VIRUS == sdata.rav){
syslog(LOG_PRIORITY, "%s: found virus: %s", sdata.ttmpfile, virusinfo);
counters.c_virus++;
inj = OK;
} else if(strlen(sdata.bodydigest) < 10) {
syslog(LOG_PRIORITY, "%s: invalid digest", sdata.ttmpfile);
inj = ERR;
} else {
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: processing message", sdata.ttmpfile);
inj = processMessage(&sdata, &sstate, cfg);
}
}
/* set the accept buffer */
snprintf(sdata.acceptbuf, SMALLBUFSIZE-1, "250 Ok %s <%s>\r\n", sdata.ttmpfile, rctptoemail);
if(inj == ERR) snprintf(sdata.acceptbuf, SMALLBUFSIZE-1, "451 %s <%s>\r\n", sdata.ttmpfile, rctptoemail);
send(new_sd, sdata.acceptbuf, strlen(sdata.acceptbuf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, sdata.acceptbuf);
counters.c_rcvd++;
if(inj == ERR_EXISTS){
syslog(LOG_PRIORITY, "%s: discarding duplicate message", sdata.ttmpfile);
counters.c_duplicate++;
}
snprintf(reason, SMALLBUFSIZE-1, "delay=%.2f, delays=%.2f/%.2f/%.2f/%.2f/%.2f/%.2f",
(sdata.__acquire+sdata.__parsed+sdata.__av+sdata.__compress+sdata.__encrypt+sdata.__store)/1000000.0,
sdata.__acquire/1000000.0, sdata.__parsed/1000000.0, sdata.__av/1000000.0, sdata.__compress/1000000.0, sdata.__encrypt/1000000.0, sdata.__store/1000000.0);
syslog(LOG_PRIORITY, "%s: %s got mail size=%d, body digest=%s, %s", sdata.ttmpfile, rctptoemail, sdata.tot_len, sdata.bodydigest, reason);
#ifdef HAVE_LMTP
} /* for */
#endif
unlink(sdata.ttmpfile);
freeState(&sstate);
alarm(cfg->session_timeout);
/* if we have nothing after the trailing (.), we can read
the next command from the network */
if(puf[n-3] == '.' && puf[n-2] == '\r' && puf[n-1] == '\n') continue;
/* if we left something in the puffer, we are ready to proceed
to handle the additional commands, such as QUIT */
/* if we miss the trailing \r\n, ie. we need another read */
if(puf[n-2] != '\r' && puf[n-1] != '\n'){
memmove(puf, puf+pos, n-pos);
memset(puf+n-pos, 0, MAXBUFSIZE-n+pos);
i = recvtimeout(new_sd, buf, MAXBUFSIZE, TIMEOUT);
strncat(puf, buf, MAXBUFSIZE-1-n+pos);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: partial read: %s", sdata.ttmpfile, puf);
pos = 0;
}
} /* PERIOD found */
else {
ret = write(sdata.fd, puf, n);
sdata.tot_len += ret;
memcpy(prevbuf, puf, n);
prevlen = n;
continue;
}
} /* SMTP DATA */
AFTER_PERIOD:
/* handle smtp commands */
memset(resp, 0, MAXBUFSIZE);
p = &puf[pos];
while((p = split_str(p, "\r\n", buf, MAXBUFSIZE-1))){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: got: %s", sdata.ttmpfile, buf);
if(strncasecmp(buf, SMTP_CMD_EHLO, strlen(SMTP_CMD_EHLO)) == 0 || strncasecmp(buf, LMTP_CMD_LHLO, strlen(LMTP_CMD_LHLO)) == 0){
if(state == SMTP_STATE_INIT) state = SMTP_STATE_HELO;
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid);
strncat(resp, buf, MAXBUFSIZE-1);
continue;
/* FIXME: implement the ENHANCEDSTATUSCODE extensions */
}
if(strncasecmp(buf, SMTP_CMD_HELO, strlen(SMTP_CMD_HELO)) == 0){
if(state == SMTP_STATE_INIT) state = SMTP_STATE_HELO;
strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1);
continue;
}
if(strncasecmp(buf, SMTP_CMD_MAIL_FROM, strlen(SMTP_CMD_MAIL_FROM)) == 0){
if(state != SMTP_STATE_HELO){
strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1);
}
else {
state = SMTP_STATE_MAIL_FROM;
snprintf(sdata.mailfrom, SMALLBUFSIZE-1, "%s\r\n", buf);
memset(fromemail, 0, SMALLBUFSIZE);
extractEmail(sdata.mailfrom, fromemail);
strncat(resp, SMTP_RESP_250_OK, strlen(SMTP_RESP_250_OK));
}
continue;
}
if(strncasecmp(buf, SMTP_CMD_RCPT_TO, strlen(SMTP_CMD_RCPT_TO)) == 0){
if(state == SMTP_STATE_MAIL_FROM || state == SMTP_STATE_RCPT_TO){
if(strlen(buf) > SMALLBUFSIZE/2){
strncat(resp, SMTP_RESP_550_ERR_TOO_LONG_RCPT_TO, MAXBUFSIZE-1);
continue;
}
if(sdata.num_of_rcpt_to < MAX_RCPT_TO-1){
snprintf(sdata.rcptto[sdata.num_of_rcpt_to], SMALLBUFSIZE-1, "%s\r\n", buf);
}
state = SMTP_STATE_RCPT_TO;
/* check against blackhole addresses */
extractEmail(buf, rctptoemail);
if(sdata.num_of_rcpt_to < MAX_RCPT_TO-1) sdata.num_of_rcpt_to++;
strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1);
}
else {
strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1);
}
continue;
}
if(strncasecmp(buf, SMTP_CMD_DATA, strlen(SMTP_CMD_DATA)) == 0){
memset(last2buf, 0, 2*MAXBUFSIZE+1);
memset(prevbuf, 0, MAXBUFSIZE);
inj = ERR;
prevlen = 0;
if(state != SMTP_STATE_RCPT_TO){
strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1);
}
else {
sdata.fd = open(sdata.ttmpfile, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
if(sdata.fd == -1){
syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sdata.ttmpfile);
strncat(resp, SMTP_RESP_451_ERR, MAXBUFSIZE-1);
}
else {
state = SMTP_STATE_DATA;
strncat(resp, SMTP_RESP_354_DATA_OK, MAXBUFSIZE-1);
}
}
continue;
}
if(strncasecmp(buf, SMTP_CMD_QUIT, strlen(SMTP_CMD_QUIT)) == 0){
state = SMTP_STATE_FINISHED;
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_221_GOODBYE, cfg->hostid);
send(new_sd, buf, strlen(buf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf);
unlink(sdata.ttmpfile);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sdata.ttmpfile);
goto QUITTING;
}
if(strncasecmp(buf, SMTP_CMD_NOOP, strlen(SMTP_CMD_NOOP)) == 0){
strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1);
continue;
}
if(strncasecmp(buf, SMTP_CMD_RESET, strlen(SMTP_CMD_RESET)) == 0){
strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sdata.ttmpfile);
unlink(sdata.ttmpfile);
initSessionData(&sdata);
state = SMTP_STATE_HELO;
continue;
}
/* by default send 502 command not implemented message */
syslog(LOG_PRIORITY, "%s: invalid command: *%s*", sdata.ttmpfile, buf);
strncat(resp, SMTP_RESP_502_ERR, MAXBUFSIZE-1);
}
/* now we can send our buffered response */
if(strlen(resp) > 0){
send(new_sd, resp, strlen(resp), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, resp);
memset(resp, 0, MAXBUFSIZE);
}
} /* while */
/*
* if we are not in SMTP_STATE_QUIT and the message was not injected,
* ie. we have timed out than send back 421 error message
*/
if(state < SMTP_STATE_QUIT && inj != OK){
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_421_ERR, cfg->hostid);
send(new_sd, buf, strlen(buf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf);
goto QUITTING;
}
QUITTING:
updateCounters(&sdata, data, &counters, cfg);
#ifdef NEED_MYSQL
mysql_close(&(sdata.mysql));
#endif
#ifdef HAVE_MEMCACHED
memcached_shutdown(&(data->memc));
#endif
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child has finished");
if(cfg->verbosity >= _LOG_INFO) syslog(LOG_PRIORITY, "processed %llu messages", counters.c_rcvd);
}
void killChild(){
syslog(LOG_PRIORITY, "child is killed by force");
exit(0);
}
void initSessionData(struct session_data *sdata){
int i;
sdata->fd = -1;
create_id(&(sdata->ttmpfile[0]));
unlink(sdata->ttmpfile);
memset(sdata->mailfrom, 0, SMALLBUFSIZE);
snprintf(sdata->client_addr, SMALLBUFSIZE-1, "null");
memset(sdata->whitelist, 0, MAXBUFSIZE);
memset(sdata->blacklist, 0, MAXBUFSIZE);
sdata->hdr_len = 0;
sdata->tot_len = 0;
sdata->num_of_rcpt_to = 0;
sdata->tre = '-';
sdata->rav = AVIR_OK;
sdata->__acquire = sdata->__parsed = sdata->__av = sdata->__store = sdata->__compress = sdata->__encrypt = 0;
for(i=0; i<MAX_RCPT_TO; i++) memset(sdata->rcptto[i], 0, SMALLBUFSIZE);
time(&(sdata->now));
sdata->sent = sdata->now;
}

10
src/session.h Normal file
View File

@ -0,0 +1,10 @@
/*
* session.h, SJ
*/
#include "defs.h"
void handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg);
void initSessionData(struct session_data *sdata);
void killChild();

45
src/sig.c Normal file
View File

@ -0,0 +1,45 @@
/*
* sig.c
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void sig_block(int sig){
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss,sig);
sigprocmask(SIG_BLOCK, &ss, (sigset_t *) 0);
}
void sig_unblock(int sig){
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss,sig);
sigprocmask(SIG_UNBLOCK, &ss, (sigset_t *) 0);
}
void sig_catch(int sig, void (*f)()){
struct sigaction sa;
sa.sa_handler = f;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(sig, &sa, (struct sigaction *) 0);
}
void sig_uncatch(int sig){
void (*sig_defaulthandler)() = SIG_DFL;
sig_catch(sig, sig_defaulthandler);
}
void sig_pause(){
sigset_t ss;
sigemptyset(&ss);
sigsuspend(&ss);
}
int wait_nohang(int *wstat){
return waitpid(-1, wstat, WNOHANG);
}

15
src/sig.h Normal file
View File

@ -0,0 +1,15 @@
/*
* sig.h, SJ
*/
#ifndef _SIG_H
#define _SIG_H
void sig_block(int sig);
void sig_unblock(int sig);
void sig_catch(int sig, void (*f)());
void sig_uncatch(int sig);
void sig_pause();
int wait_nohang(int *wstat);
#endif

61
src/smtpcodes.h Normal file
View File

@ -0,0 +1,61 @@
/*
* smtpcodes.h, SJ
*/
// SMTP states
#define SMTP_STATE_INIT 0
#define SMTP_STATE_HELO 1
#define SMTP_STATE_MAIL_FROM 2
#define SMTP_STATE_RCPT_TO 3
#define SMTP_STATE_DATA 4
#define SMTP_STATE_PERIOD 5
#define SMTP_STATE_QUIT 6
#define SMTP_STATE_FINISHED 7
// SMTP commands
#define SMTP_CMD_HELO "HELO"
#define SMTP_CMD_EHLO "EHLO"
#define SMTP_CMD_MAIL_FROM "MAIL FROM:"
#define SMTP_CMD_RCPT_TO "RCPT TO:"
#define SMTP_CMD_DATA "DATA"
#define SMTP_CMD_PERIOD "\x0d\x0a\x2e\x0d\x0a"
#define SMTP_CMD_QUIT "QUIT"
#define SMTP_CMD_RESET "RSET"
#define SMTP_CMD_NOOP "NOOP"
#define SMTP_CMD_XFORWARD "XFORWARD"
#define SMTP_CMD_XCLIENT "XCLIENT"
// SMTP responses
#define SMTP_RESP_220_BANNER "220 %s ESMTP\r\n"
#define SMTP_RESP_221_GOODBYE "221 %s Goodbye\r\n"
#define SMTP_RESP_250_OK "250 Ok\r\n"
#define SMTP_RESP_250_EXTENSIONS "250-%s\r\n250-PIPELINING\r\n250-SIZE\r\n250 8BITMIME\r\n"
#define SMTP_RESP_354_DATA_OK "354 Send mail data; end it with <CRLF>.<CRLF>\r\n"
#define SMTP_RESP_421_ERR "421 %s Error: timed out\r\n"
#define SMTP_RESP_421_ERR_TMP "421 %s service not available\r\n"
#define SMTP_RESP_421_ERR_WRITE_FAILED "421 writing queue file failed\r\n"
#define SMTP_RESP_450_ERR_CMD_NOT_IMPLEMENTED "450 command not implemented\r\n"
#define SMTP_RESP_451_ERR "451 Error in processing, try again later\r\n"
#define SMTP_RESP_502_ERR "502 Command not implemented\r\n"
#define SMTP_RESP_503_ERR "503 Bad command sequence\r\n"
#define SMTP_RESP_550_ERR "550 Access denied.\r\n"
#define SMTP_RESP_550_ERR_PREF "550 Access denied."
#define SMTP_RESP_550_INVALID_RECIPIENT "550 Unknown recipient\r\n"
#define SMTP_RESP_550_ERR_TOO_LONG_RCPT_TO "550 too long recipient\r\n"
#define SMTP_RESP_552_ERR "552 Too many recipients\r\n"
// LMTP commands
#define LMTP_CMD_LHLO "LHLO"
// LMTP responses
#define LMTP_RESP_220_BANNER "220 %s LMTP\r\n"

134
src/store.c Normal file
View File

@ -0,0 +1,134 @@
/*
* store.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <piler.h>
#include <zlib.h>
#include <openssl/blowfish.h>
#include <openssl/evp.h>
int store_message(struct session_data *sdata, struct _state *state, int stored, struct __config *cfg){
int ret=0, rc, fd, n, len=sdata->tot_len;
char *addr, *p0, *p1, *p2, s[SMALLBUFSIZE];
struct stat st;
Bytef *z=NULL;
uLongf dstlen;
EVP_CIPHER_CTX ctx;
unsigned char *outbuf=NULL;
int outlen, tmplen;
struct timezone tz;
struct timeval tv1, tv2;
/* fix data length to store */
/*if(stored == 1 && sdata->hdr_len > 100){
len = sdata->hdr_len;
}*/
fd = open(sdata->ttmpfile, O_RDONLY);
if(fd == -1) return ret;
gettimeofday(&tv1, &tz);
addr = mmap(NULL, sdata->tot_len, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
if(addr == MAP_FAILED) return ret;
dstlen = compressBound(len);
z = malloc(dstlen);
if(z == NULL){
munmap(addr, sdata->tot_len);
return ret;
}
rc = compress(z, &dstlen, (const Bytef *)addr, len);
gettimeofday(&tv2, &tz);
sdata->__compress += tvdiff(tv2, tv1);
munmap(addr, sdata->tot_len);
if(rc != Z_OK) goto ENDE;
gettimeofday(&tv1, &tz);
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, cfg->key, cfg->iv);
outbuf = malloc(dstlen + EVP_MAX_BLOCK_LENGTH);
if(outbuf == NULL) goto ENDE;
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, z, dstlen)) goto ENDE;
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen)) goto ENDE;
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
gettimeofday(&tv2, &tz);
sdata->__encrypt += tvdiff(tv2, tv1);
/* create a filename according to piler_id */
snprintf(s, sizeof(s)-1, "%s/%c%c/%c%c/%c%c/%s", cfg->queuedir, sdata->ttmpfile[RND_STR_LEN-6], sdata->ttmpfile[RND_STR_LEN-5], sdata->ttmpfile[RND_STR_LEN-4], sdata->ttmpfile[RND_STR_LEN-3], sdata->ttmpfile[RND_STR_LEN-2], sdata->ttmpfile[RND_STR_LEN-1], sdata->ttmpfile);
p0 = strrchr(s, '/'); if(!p0) goto ENDE;
*p0 = '\0';
if(stat(s, &st)){
p1 = strrchr(s, '/'); if(!p1) goto ENDE;
*p1 = '\0';
p2 = strrchr(s, '/'); if(!p2) goto ENDE;
*p2 = '\0';
mkdir(s, 0755);
*p2 = '/';
mkdir(s, 0755);
*p1 = '/';
mkdir(s, 0755);
}
*p0 = '/';
fd = open(s, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if(fd == -1) goto ENDE;
n = write(fd, outbuf, outlen);
if(n == outlen){
ret = 1;
}
fsync(fd);
close(fd);
if(ret == 0){
unlink(s);
}
ENDE:
if(outbuf) free(outbuf);
if(z) free(z);
return ret;
}

69
src/tai.c Normal file
View File

@ -0,0 +1,69 @@
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "tai.h"
static char hex[16] = "0123456789abcdef";
void tai_pack(char *s, struct tai *t){
unsigned long long x;
x = t->x;
s[7] = x & 255; x >>= 8;
s[6] = x & 255; x >>= 8;
s[5] = x & 255; x >>= 8;
s[4] = x & 255; x >>= 8;
s[3] = x & 255; x >>= 8;
s[2] = x & 255; x >>= 8;
s[1] = x & 255; x >>= 8;
s[0] = x;
}
void taia_pack(char *s, struct taia *t){
unsigned long x;
tai_pack(s, &t->sec);
s += 8;
x = t->atto;
s[7] = x & 255; x >>= 8;
s[6] = x & 255; x >>= 8;
s[5] = x & 255; x >>= 8;
s[4] = x;
x = t->nano;
s[3] = x & 255; x >>= 8;
s[2] = x & 255; x >>= 8;
s[1] = x & 255; x >>= 8;
s[0] = x;
}
void taia_now(struct taia *t){
struct timeval now;
gettimeofday(&now,(struct timezone *) 0);
t->sec.x = 4611686018427387914ULL + (uint64) now.tv_sec;
t->nano = 1000 * now.tv_usec + 500;
t->atto = 0;
}
void tai_timestamp(char *s){
struct tai now;
char nowpack[TAI_PACK];
int i;
now.x = 4611686018427387914ULL + (unsigned long long) time((long *) 0);
tai_pack(nowpack, &now);
for (i = 0;i < 8;++i) {
*(s+i*2) = hex[(nowpack[i] >> 4) & 15];
*(s+i*2+1) = hex[nowpack[i] & 15];
}
*(s+2*TAI_PACK) = '\0';
}

25
src/tai.h Normal file
View File

@ -0,0 +1,25 @@
/*
* tai.h, SJ
*/
#define TAI_PACK 8
#define TAIA_PACK 16
#define TIMESTAMP 25
typedef unsigned long long uint64;
struct tai {
uint64 x;
};
struct taia {
struct tai sec;
unsigned long nano; /* 0...999999999 */
unsigned long atto; /* 0...999999999 */
};
void taia_now(struct taia *t);
void taia_pack(char *s, struct taia *t);

70
src/test.c Normal file
View File

@ -0,0 +1,70 @@
/*
* test.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <locale.h>
#include <piler.h>
int main(int argc, char **argv){
int i, rc;
struct timezone tz;
struct timeval tv_spam_start, tv_spam_stop;
struct session_data sdata;
struct _state state;
struct __config cfg;
struct stat st;
if(argc < 2){
fprintf(stderr, "usage: %s <message>\n", argv[0]);
exit(1);
}
if(stat(argv[1], &st) != 0){
fprintf(stderr, "%s is not found\n", argv[1]);
return 0;
}
cfg = read_config(CONFIG_FILE);
printf("locale: %s\n", setlocale(LC_MESSAGES, cfg.locale));
setlocale(LC_CTYPE, cfg.locale);
rc = 0;
sdata.num_of_rcpt_to = -1;
time(&(sdata.now));
sdata.sent = 0;
memset(sdata.rcptto[0], 0, SMALLBUFSIZE);
snprintf(sdata.ttmpfile, SMALLBUFSIZE-1, "%s", argv[1]);
state = parseMessage(&sdata, &cfg);
printf("from: %s\n", state.b_from);
printf("to: %s\n", state.b_to);
printf("subject: %s\n", state.b_subject);
printf("message-id: %s\n", state.message_id);
printf("body: %s\n", state.b_body);
printf("body digest: %s\n", sdata.bodydigest);
for(i=0; i<state.n_attachments; i++){
printf("i:%d, name=*%s*, type: *%s*, size: %d\n", i, state.attachments[i].filename, state.attachments[i].type, state.attachments[i].size);
}
gettimeofday(&tv_spam_start, &tz);
freeState(&state);
gettimeofday(&tv_spam_stop, &tz);
return 0;
}

26
src/trans.h Normal file
View File

@ -0,0 +1,26 @@
/*
* trans.h, SJ
*/
static char delimiter_characters[] = {
'x','x','x','x', 'x','x','x','x', 'x','x','x','x', 'x','x','x','x',
'x','x','x','x', 'x','x','x','x', 'x','x','x','x', 'x','x','x','x',
'x',' ','"','#', ' ','%','&','x', '(',')','*','+', ',',' ',' ','/',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',':',';', '<','=','>','?',
'@',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ','[', '\\',']','^','_',
'`',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ','{', '|','}','~','x',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' '
};

53
util/db-mysql.sql Normal file
View File

@ -0,0 +1,53 @@
drop table if exists `sph_counter`;
create table `sph_counter` (
`counter_id` int not null,
`max_doc_id` int not null,
primary key (`counter_id`)
);
drop table if exists `sph_index`;
create table `sph_index` (
`id` int not null auto_increment,
`from` char(255) default null,
`to` text(512) default null,
`subject` text(512) default null,
`arrived` int not null,
`sent` int not null,
`body` text,
`size` int default '0',
`piler_id` char(36) not null,
`header_id` char(16) default null,
`body_id` char(16) default null,
primary key (`id`)
) Engine=InnoDB;
drop table if exists `metadata`;
create table `metadata` (
`id` bigint unsigned not null auto_increment,
`from` char(255) not null,
`to` char(255) default null,
`subject` text(512) default null,
`arrived` int not null,
`sent` int not null,
`deleted` int default 0,
`size` int default 0,
`hlen` int default 0,
`piler_id` char(36) not null,
`message_id` char(128) character set 'latin1' not null,
`bodydigest` char(64) not null,
primary key (`id`), unique(`to`,`message_id`)
) Engine=InnoDB;
create index metadata_idx on metadata(`piler_id`);
create index metadata_idx2 on metadata(`message_id`);
create index metadata_idx3 on metadata(`bodydigest`);
drop table if exists `counter`;
create table if not exists `counter` (
`rcvd` bigint unsigned default 0,
`virus` bigint unsigned default 0,
`duplicate` bigint unsigned default 0
) Engine=InnoDB;
insert into `counter` values(0, 0, 0);