mirror of
https://bitbucket.org/jsuto/piler.git
synced 2024-12-24 04:30:12 +01:00
initial release
This commit is contained in:
commit
57f172c1b1
19
LICENSE
Normal file
19
LICENSE
Normal 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
106
Makefile.in
Normal 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
16
README
Normal 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
995
aclocal.m4
vendored
Normal 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
|
||||
|
324
configure.in
Normal file
324
configure.in
Normal 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
48
etc/Makefile
Normal 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
39
etc/Makefile.in
Normal 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
84
etc/example.conf
Normal 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
86
etc/sphinx.conf
Normal 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
251
install-sh
Executable 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
40
mkinstalldirs
Executable 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
15
piler-config.h.in
Normal 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
82
src/Makefile
Normal 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
82
src/Makefile.in
Normal 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
79
src/av.h
Normal 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
55
src/avir.c
Normal 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
98
src/boundary.c
Normal 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
14
src/boundary.h
Normal 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
282
src/cfg.c
Normal 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
66
src/cfg.h
Normal 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
135
src/clamd.c
Normal 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
107
src/config.h
Normal 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
121
src/counters.c
Normal 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
282
src/decoder.c
Normal 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
15
src/decoder.h
Normal 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
205
src/defs.h
Normal 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
60
src/digest.c
Normal 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
81
src/dirs.c
Normal 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
31
src/errmsg.h
Normal 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
38
src/html.h
Normal 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
288
src/htmlentities.h
Normal 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, "Æ" },
|
||||
{ 193, "Á" },
|
||||
{ 194, "Â" },
|
||||
{ 192, "À" },
|
||||
{ 913, "&Alpha" },
|
||||
{ 197, "Å" },
|
||||
{ 195, "Ã" },
|
||||
{ 196, "Ä" },
|
||||
{ 914, "&Beta" },
|
||||
{ 199, "Ç" },
|
||||
{ 935, "&Chi" },
|
||||
{ 8225, "&Dagger" },
|
||||
{ 916, "&Delta" },
|
||||
{ 208, "Ð" },
|
||||
{ 201, "É" },
|
||||
{ 202, "Ê" },
|
||||
{ 200, "È" },
|
||||
{ 917, "&Epsilon" },
|
||||
{ 919, "&Eta" },
|
||||
{ 203, "Ë" },
|
||||
{ 915, "&Gamma" },
|
||||
{ 205, "Í" },
|
||||
{ 206, "Î" },
|
||||
{ 204, "Ì" },
|
||||
{ 921, "&Iota" },
|
||||
{ 207, "Ï" },
|
||||
{ 922, "&Kappa" },
|
||||
{ 923, "&Lambda" },
|
||||
{ 924, "&Mu" },
|
||||
{ 209, "Ñ" },
|
||||
{ 925, "&Nu" },
|
||||
{ 338, "&OElig" },
|
||||
{ 211, "Ó" },
|
||||
{ 212, "Ô" },
|
||||
{ 210, "Ò" },
|
||||
{ 937, "&Omega" },
|
||||
{ 927, "&Omicron" },
|
||||
{ 216, "Ø" },
|
||||
{ 213, "Õ" },
|
||||
{ 214, "Ö" },
|
||||
{ 934, "&Phi" },
|
||||
{ 928, "&Pi" },
|
||||
{ 8243, "&Prime" },
|
||||
{ 936, "&Psi" },
|
||||
{ 929, "&Rho" },
|
||||
{ 352, "&Scaron" },
|
||||
{ 931, "&Sigma" },
|
||||
{ 222, "Þ" },
|
||||
{ 932, "&Tau" },
|
||||
{ 920, "&Theta" },
|
||||
{ 218, "Ú" },
|
||||
{ 219, "Û" },
|
||||
{ 217, "Ù" },
|
||||
{ 933, "&Upsilon" },
|
||||
{ 220, "Ü" },
|
||||
{ 926, "&Xi" },
|
||||
{ 221, "Ý" },
|
||||
{ 376, "&Yuml" },
|
||||
{ 918, "&Zeta" },
|
||||
{ 225, "á" },
|
||||
{ 226, "â" },
|
||||
{ 180, "´" },
|
||||
{ 230, "æ" },
|
||||
{ 224, "à" },
|
||||
{ 8501, "&alefsym" },
|
||||
{ 945, "&alpha" },
|
||||
{ 38, "&" },
|
||||
{ 38, "&" },
|
||||
{ 8743, "&and" },
|
||||
{ 8736, "&ang" },
|
||||
{ 39, "&apos" },
|
||||
{ 229, "å" },
|
||||
{ 8776, "&asymp" },
|
||||
{ 227, "ã" },
|
||||
{ 228, "ä" },
|
||||
{ 8222, "&bdquo" },
|
||||
{ 946, "&beta" },
|
||||
{ 166, "¦" },
|
||||
{ 8226, "&bull" },
|
||||
{ 8745, "&cap" },
|
||||
{ 231, "ç" },
|
||||
{ 184, "¸" },
|
||||
{ 162, "¢" },
|
||||
{ 967, "&chi" },
|
||||
{ 710, "&circ" },
|
||||
{ 9827, "&clubs" },
|
||||
{ 8773, "&cong" },
|
||||
{ 169, "©" },
|
||||
{ 8629, "&crarr" },
|
||||
{ 8746, "&cup" },
|
||||
{ 164, "¤" },
|
||||
{ 8659, "&dArr" },
|
||||
{ 8224, "&dagger" },
|
||||
{ 8595, "&darr" },
|
||||
{ 176, "°" },
|
||||
{ 948, "&delta" },
|
||||
{ 9830, "&diams" },
|
||||
{ 247, "÷" },
|
||||
{ 233, "é" },
|
||||
{ 234, "ê" },
|
||||
{ 232, "è" },
|
||||
{ 8709, "&empty" },
|
||||
{ 8195, "&emsp" },
|
||||
{ 8194, "&ensp" },
|
||||
{ 949, "&epsilon" },
|
||||
{ 8801, "&equiv" },
|
||||
{ 951, "&eta" },
|
||||
{ 240, "ð" },
|
||||
{ 235, "ë" },
|
||||
{ 8364, "&euro" },
|
||||
{ 8707, "&exist" },
|
||||
{ 402, "&fnof" },
|
||||
{ 8704, "&forall" },
|
||||
{ 189, "½" },
|
||||
{ 188, "¼" },
|
||||
{ 190, "¾" },
|
||||
{ 8260, "&frasl" },
|
||||
{ 947, "&gamma" },
|
||||
{ 8805, "&ge" },
|
||||
{ 62, ">" },
|
||||
{ 62, ">" },
|
||||
{ 8660, "&hArr" },
|
||||
{ 8596, "&harr" },
|
||||
{ 9829, "&hearts" },
|
||||
{ 8230, "&hellip" },
|
||||
{ 237, "í" },
|
||||
{ 238, "î" },
|
||||
{ 161, "¡" },
|
||||
{ 236, "ì" },
|
||||
{ 8465, "&image" },
|
||||
{ 8734, "&infin" },
|
||||
{ 8747, "&int" },
|
||||
{ 953, "&iota" },
|
||||
{ 191, "¿" },
|
||||
{ 8712, "&isin" },
|
||||
{ 239, "ï" },
|
||||
{ 954, "&kappa" },
|
||||
{ 8656, "&lArr" },
|
||||
{ 955, "&lambda" },
|
||||
{ 9001, "&lang" },
|
||||
{ 171, "«" },
|
||||
{ 8592, "&larr" },
|
||||
{ 8968, "&lceil" },
|
||||
{ 8220, "&ldquo" },
|
||||
{ 8804, "&le" },
|
||||
{ 8970, "&lfloor" },
|
||||
{ 8727, "&lowast" },
|
||||
{ 9674, "&loz" },
|
||||
{ 8206, "&lrm" },
|
||||
{ 8249, "&lsaquo" },
|
||||
{ 8216, "&lsquo" },
|
||||
{ 60, "<" },
|
||||
{ 60, "<" },
|
||||
{ 175, "¯" },
|
||||
{ 8212, "&mdash" },
|
||||
{ 181, "µ" },
|
||||
{ 183, "·" },
|
||||
{ 8722, "&minus" },
|
||||
{ 956, "&mu" },
|
||||
{ 8711, "&nabla" },
|
||||
{ 32, " " },
|
||||
{ 160, " " },
|
||||
{ 8211, "&ndash" },
|
||||
{ 8800, "&ne" },
|
||||
{ 8715, "&ni" },
|
||||
{ 172, "¬" },
|
||||
{ 8713, "¬in" },
|
||||
{ 8836, "&nsub" },
|
||||
{ 241, "ñ" },
|
||||
{ 957, "&nu" },
|
||||
{ 243, "ó" },
|
||||
{ 244, "ô" },
|
||||
{ 339, "&oelig" },
|
||||
{ 242, "ò" },
|
||||
{ 8254, "&oline" },
|
||||
{ 969, "&omega" },
|
||||
{ 959, "&omicron" },
|
||||
{ 8853, "&oplus" },
|
||||
{ 8744, "&or" },
|
||||
{ 170, "ª" },
|
||||
{ 186, "º" },
|
||||
{ 248, "ø" },
|
||||
{ 245, "õ" },
|
||||
{ 8855, "&otimes" },
|
||||
{ 246, "ö" },
|
||||
{ 182, "¶" },
|
||||
{ 8706, "&part" },
|
||||
{ 8240, "&permil" },
|
||||
{ 8869, "&perp" },
|
||||
{ 966, "&phi" },
|
||||
{ 960, "&pi" },
|
||||
{ 982, "&piv" },
|
||||
{ 177, "±" },
|
||||
{ 163, "£" },
|
||||
{ 8242, "&prime" },
|
||||
{ 8719, "&prod" },
|
||||
{ 8733, "&prop" },
|
||||
{ 968, "&psi" },
|
||||
{ 34, """ },
|
||||
{ 34, """ },
|
||||
{ 8658, "&rArr" },
|
||||
{ 8730, "&radic" },
|
||||
{ 9002, "&rang" },
|
||||
{ 187, "»" },
|
||||
{ 8594, "&rarr" },
|
||||
{ 8969, "&rceil" },
|
||||
{ 8221, "&rdquo" },
|
||||
{ 8476, "&real" },
|
||||
{ 174, "®" },
|
||||
{ 8971, "&rfloor" },
|
||||
{ 961, "&rho" },
|
||||
{ 8207, "&rlm" },
|
||||
{ 8250, "&rsaquo" },
|
||||
{ 8217, "&rsquo" },
|
||||
{ 8218, "&sbquo" },
|
||||
{ 353, "&scaron" },
|
||||
{ 8901, "&sdot" },
|
||||
{ 167, "§" },
|
||||
{ 173, "­" },
|
||||
{ 963, "&sigma" },
|
||||
{ 962, "&sigmaf" },
|
||||
{ 8764, "&sim" },
|
||||
{ 9824, "&spades" },
|
||||
{ 8834, "&sub" },
|
||||
{ 8838, "&sube" },
|
||||
{ 8721, "&sum" },
|
||||
{ 8835, "&sup" },
|
||||
{ 185, "¹" },
|
||||
{ 178, "²" },
|
||||
{ 179, "³" },
|
||||
{ 8839, "&supe" },
|
||||
{ 223, "ß" },
|
||||
{ 964, "&tau" },
|
||||
{ 8756, "&there4" },
|
||||
{ 952, "&theta" },
|
||||
{ 977, "&thetasym" },
|
||||
{ 8201, "&thinsp" },
|
||||
{ 254, "þ" },
|
||||
{ 732, "&tilde" },
|
||||
{ 215, "×" },
|
||||
{ 8482, "&trade" },
|
||||
{ 8657, "&uArr" },
|
||||
{ 250, "ú" },
|
||||
{ 8593, "&uarr" },
|
||||
{ 251, "û" },
|
||||
{ 249, "ù" },
|
||||
{ 168, "¨" },
|
||||
{ 978, "&upsih" },
|
||||
{ 965, "&upsilon" },
|
||||
{ 252, "ü" },
|
||||
{ 8472, "&weierp" },
|
||||
{ 958, "&xi" },
|
||||
{ 253, "ý" },
|
||||
{ 165, "¥" },
|
||||
{ 255, "ÿ" },
|
||||
{ 950, "&zeta" },
|
||||
{ 8205, "&zwj" },
|
||||
{ 8204, "&zwnj" }
|
||||
};
|
||||
|
||||
|
||||
#endif /* _HTMLENTITIES_H */
|
||||
|
27
src/ijc.h
Normal file
27
src/ijc.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* ijc.h, SJ
|
||||
*/
|
||||
|
||||
static char invalid_junk_characters[] = {
|
||||
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ','', ' ','',' ',' ',
|
||||
'x',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
|
||||
' ','¡','¢',' ', '¤','¥','¦',' ', '¨',' ','ª','«', '¬','','®','¯',
|
||||
'°','±','²','³', ' ','µ','¶',' ', '¸','¹','º','»', ' ','½','¾','¿',
|
||||
|
||||
' ',' ','Â','Ã', 'Ä','Å','Æ','Ç', 'È',' ',' ','Ë', 'Ì',' ',' ','Ï',
|
||||
'Ð','Ñ','Ò',' ', 'Ô',' ',' ','×', 'Ø',' ',' ',' ', ' ','Ý',' ',' ',
|
||||
'à',' ',' ','ã', ' ','å','æ','ç', 'è',' ','ê','ë', 'ì',' ','î',' ',
|
||||
'ð','ñ','ò',' ', 'ô',' ',' ',' ', ' ','ù',' ',' ', ' ','ý',' ','ÿ'
|
||||
};
|
||||
|
88
src/list.c
Normal file
88
src/list.c
Normal 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
16
src/list.h
Normal 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
400
src/memc.c
Normal 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
19
src/memc.h
Normal 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
204
src/memcached.c
Normal 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
257
src/message.c
Normal 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
432
src/misc.c
Normal 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
39
src/misc.h
Normal 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
403
src/parser.c
Normal 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
41
src/parser.h
Normal 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
836
src/parser_utils.c
Normal 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
249
src/piler.c
Normal 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
41
src/piler.h
Normal 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
60
src/pilerconf.c
Normal 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
499
src/session.c
Normal 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
10
src/session.h
Normal 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
45
src/sig.c
Normal 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
15
src/sig.h
Normal 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
61
src/smtpcodes.h
Normal 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
134
src/store.c
Normal 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
69
src/tai.c
Normal 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
25
src/tai.h
Normal 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
70
src/test.c
Normal 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
26
src/trans.h
Normal 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
53
util/db-mysql.sql
Normal 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);
|
||||
|
Loading…
Reference in New Issue
Block a user