This commit is contained in:
Graeme Walker 2003-09-07 12:00:00 +00:00
parent c7cbeb435a
commit d37e3dc1da
73 changed files with 1621 additions and 305 deletions

View File

@ -1,6 +1,15 @@
E-MailRelay Change Log
======================
1.1.1 -> 1.1.2
--------------
* Earlier check for un-bindable ports on startup, and later fork()ing [bug-id 776972].
* Resolved the file-descriptor kludge for "--verifier" on Windows.
* Less strict about failing eight bit messages sent to servers with no "8BITMIME" extension.
* Supplementary group memberships revoked at startup if root or suid.
* Pre-processor ("--filter") program's standard output searched for a failure reason string.
* Undocumented "--scanner" switch added for asynchronous processing by a separate network server.
1.1.0 -> 1.1.1
--------------
* Restored the fix for building with gcc2.96.

View File

@ -23,18 +23,38 @@ dnl
AC_DEFUN(ACLOCAL_TYPE_SOCKLEN_T,
[AC_CACHE_CHECK([for socklen_t], aclocal_cv_type_socklen_t,
[
AC_TRY_COMPILE(
[#include <sys/types.h>
AC_TRY_COMPILE(
[#include <sys/types.h>
#include <sys/socket.h>],
[socklen_t len = 42; return len;],
aclocal_cv_type_socklen_t=yes,
aclocal_cv_type_socklen_t=no)
[socklen_t len = 42; return len;],
aclocal_cv_type_socklen_t=yes,
aclocal_cv_type_socklen_t=no )
])
if test $aclocal_cv_type_socklen_t = yes; then
AC_DEFINE(HAVE_SOCKLEN_T, 1,[Define if socklen_t type definition in sys/socket.h])
else
AC_DEFINE(HAVE_SOCKLEN_T, 0,[Define if socklen_t type definition in sys/socket.h])
fi
if test $aclocal_cv_type_socklen_t = yes; then
AC_DEFINE(HAVE_SOCKLEN_T, 1,[Define if socklen_t type definition in sys/socket.h])
else
AC_DEFINE(HAVE_SOCKLEN_T, 0,[Define if socklen_t type definition in sys/socket.h])
fi
])
dnl setgroups
dnl
AC_DEFUN([ACLOCAL_CHECK_SETGROUPS],
[AC_CACHE_CHECK([for setgroups], aclocal_cv_setgroups,
[
AC_TRY_COMPILE(
[#include <sys/types.h>
#include <unistd.h>
#include <grp.h>],
[setgroups(0,0) ;],
aclocal_cv_setgroups=yes ,
aclocal_cv_setgroups=no )
])
if test $aclocal_cv_setgroups = yes; then
AC_DEFINE(HAVE_SETGROUPS,1,[Define if setgroups is available])
else
AC_DEFINE(HAVE_SETGROUPS,0,[Define if setgroups is available])
fi
])
dnl gmtime_r

40
aclocal.m4 vendored
View File

@ -36,18 +36,38 @@ dnl
AC_DEFUN(ACLOCAL_TYPE_SOCKLEN_T,
[AC_CACHE_CHECK([for socklen_t], aclocal_cv_type_socklen_t,
[
AC_TRY_COMPILE(
[#include <sys/types.h>
AC_TRY_COMPILE(
[#include <sys/types.h>
#include <sys/socket.h>],
[socklen_t len = 42; return len;],
aclocal_cv_type_socklen_t=yes,
aclocal_cv_type_socklen_t=no)
[socklen_t len = 42; return len;],
aclocal_cv_type_socklen_t=yes,
aclocal_cv_type_socklen_t=no )
])
if test $aclocal_cv_type_socklen_t = yes; then
AC_DEFINE(HAVE_SOCKLEN_T, 1,[Define if socklen_t type definition in sys/socket.h])
else
AC_DEFINE(HAVE_SOCKLEN_T, 0,[Define if socklen_t type definition in sys/socket.h])
fi
if test $aclocal_cv_type_socklen_t = yes; then
AC_DEFINE(HAVE_SOCKLEN_T, 1,[Define if socklen_t type definition in sys/socket.h])
else
AC_DEFINE(HAVE_SOCKLEN_T, 0,[Define if socklen_t type definition in sys/socket.h])
fi
])
dnl setgroups
dnl
AC_DEFUN([ACLOCAL_CHECK_SETGROUPS],
[AC_CACHE_CHECK([for setgroups], aclocal_cv_setgroups,
[
AC_TRY_COMPILE(
[#include <sys/types.h>
#include <unistd.h>
#include <grp.h>],
[setgroups(0,0) ;],
aclocal_cv_setgroups=yes ,
aclocal_cv_setgroups=no )
])
if test $aclocal_cv_setgroups = yes; then
AC_DEFINE(HAVE_SETGROUPS,1,[Define if setgroups is available])
else
AC_DEFINE(HAVE_SETGROUPS,0,[Define if setgroups is available])
fi
])
dnl gmtime_r

View File

@ -60,7 +60,8 @@ base_dir="/tmp/`basename $0`.$$.tmp"
summary_log="/tmp/`basename $0`.out"
exit_code="1"
watchdog_pid=""
export MALLOC_CHECK_="2"
MALLOC_CHECK_="2"
export MALLOC_CHECK_
ulimit -c 1000000
if test "${use_valgrind}" -eq 1
then

View File

@ -176,6 +176,7 @@ redhat_cmd_start()
redhat_errno="$?"
#touch /var/lock/subsys/emailrelay
if test "${redhat_errno}" -eq 0 ; then success ; else failure ; fi
echo
}
redhat_cmd_stop()
@ -185,6 +186,7 @@ redhat_cmd_stop()
redhat_errno="$?"
#rm -f /var/lock/subsys/emailrelay
if test "${redhat_errno}" -eq 0 ; then success ; else failure ; fi
echo
}
redhat_cmd_restarted()

View File

@ -29,8 +29,9 @@
# The "-t" switch can be used to suppress expansion where the included
# file has an extension of ".html".
#
# In edit mode (--edit) the exit value is 0 (shell true) if the file is
# modified. This allows for recursive expansion using a shell while loop.
# In edit mode (--edit) the specified file is modified in-place. With this
# switch the exit value is 0 (shell true) if the file is modified in any
# way. This allows for recursive expansion using a shell while loop.
#
# usage: expand.sh [-a <awk>] [-t] { --edit <file> | [<file> ...] }
#

View File

@ -25,6 +25,9 @@
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
/* Define if setgroups is available */
#undef HAVE_SETGROUPS
/* Define if socklen_t type definition in sys/socket.h */
#undef HAVE_SOCKLEN_T

73
configure vendored
View File

@ -1459,7 +1459,7 @@ fi
# Define the identity of the package.
PACKAGE=emailrelay
VERSION=1.1.1
VERSION=1.1.2
cat >>confdefs.h <<_ACEOF
@ -4565,7 +4565,7 @@ if test "${aclocal_cv_type_socklen_t+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
cat >conftest.$ac_ext <<_ACEOF
cat >conftest.$ac_ext <<_ACEOF
#line $LINENO "configure"
#include "confdefs.h"
#include <sys/types.h>
@ -4607,19 +4607,19 @@ rm -f conftest.$ac_objext conftest.$ac_ext
fi
echo "$as_me:$LINENO: result: $aclocal_cv_type_socklen_t" >&5
echo "${ECHO_T}$aclocal_cv_type_socklen_t" >&6
if test $aclocal_cv_type_socklen_t = yes; then
if test $aclocal_cv_type_socklen_t = yes; then
cat >>confdefs.h <<\_ACEOF
#define HAVE_SOCKLEN_T 1
_ACEOF
else
else
cat >>confdefs.h <<\_ACEOF
#define HAVE_SOCKLEN_T 0
_ACEOF
fi
fi
echo "$as_me:$LINENO: checking for buggy ctime" >&5
echo $ECHO_N "checking for buggy ctime... $ECHO_C" >&6
@ -4805,6 +4805,69 @@ _ACEOF
fi
echo "$as_me:$LINENO: checking for setgroups" >&5
echo $ECHO_N "checking for setgroups... $ECHO_C" >&6
if test "${aclocal_cv_setgroups+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
cat >conftest.$ac_ext <<_ACEOF
#line $LINENO "configure"
#include "confdefs.h"
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#ifdef F77_DUMMY_MAIN
# ifdef __cplusplus
extern "C"
# endif
int F77_DUMMY_MAIN() { return 1; }
#endif
int
main ()
{
setgroups(0,0) ;
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -s conftest.$ac_objext'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; }; then
aclocal_cv_setgroups=yes
else
echo "$as_me: failed program was:" >&5
cat conftest.$ac_ext >&5
aclocal_cv_setgroups=no
fi
rm -f conftest.$ac_objext conftest.$ac_ext
fi
echo "$as_me:$LINENO: result: $aclocal_cv_setgroups" >&5
echo "${ECHO_T}$aclocal_cv_setgroups" >&6
if test $aclocal_cv_setgroups = yes; then
cat >>confdefs.h <<\_ACEOF
#define HAVE_SETGROUPS 1
_ACEOF
else
cat >>confdefs.h <<\_ACEOF
#define HAVE_SETGROUPS 0
_ACEOF
fi
# Check whether --enable-debug or --disable-debug was given.
if test "${enable_debug+set}" = set; then

3
configure.ac Executable file → Normal file
View File

@ -21,7 +21,7 @@ dnl Process this file with autoconf to produce a configure script.
dnl
AC_INIT(src/gsmtp/gsmtp.h)
AM_INIT_AUTOMAKE(emailrelay,1.1.1)
AM_INIT_AUTOMAKE(emailrelay,1.1.2)
AM_CONFIG_HEADER(config.h)
dnl ===
@ -60,6 +60,7 @@ ACLOCAL_TYPE_SOCKLEN_T
ACLOCAL_CHECK_BUGGY_CTIME
ACLOCAL_CHECK_GMTIME_R
ACLOCAL_CHECK_LOCALTIME_R
ACLOCAL_CHECK_SETGROUPS
dnl ===
dnl "--enable-debug"

View File

@ -25,12 +25,12 @@ man_files_out=emailrelay.1.gz emailrelay-passwd.1.gz emailrelay-poke.1.gz emailr
html_files_in=doxygen_header.html
html_files_thru=index.html emailrelay-man.html $(stylesheet)
html_files_out=readme.html developer.html reference.html userguide.html windows.html changelog.html
png_files=gsmtp-classes.png gnet-classes.png
png_files=gsmtp-classes.png gnet-classes.png sequence-1.png sequence-2.png sequence-3.png sequence-4.png gnet-client.png gsmtp-serverprotocol.png gsmtp-scannerclient.png
EXTRA_DIST = $(man_files_in) $(txt_files) $(html_files_in) $(html_files_thru) $(png_files)
noinst_SCRIPTS = .dox
e_man1_DATA = $(man_files_out)
e_doc_DATA = $(txt_files) $(html_files_out) $(html_files_thru)
e_doc_DATA = $(txt_files) $(html_files_out) $(html_files_thru) $(png_files)
CLEANFILES = $(noinst_SCRIPTS) $(man_files_out) $(html_files_out) doxygen/*
SUFFIXES = .txt .html

View File

@ -100,12 +100,12 @@ man_files_out = emailrelay.1.gz emailrelay-passwd.1.gz emailrelay-poke.1.gz emai
html_files_in = doxygen_header.html
html_files_thru = index.html emailrelay-man.html $(stylesheet)
html_files_out = readme.html developer.html reference.html userguide.html windows.html changelog.html
png_files = gsmtp-classes.png gnet-classes.png
png_files = gsmtp-classes.png gnet-classes.png sequence-1.png sequence-2.png sequence-3.png sequence-4.png gnet-client.png gsmtp-serverprotocol.png gsmtp-scannerclient.png
EXTRA_DIST = $(man_files_in) $(txt_files) $(html_files_in) $(html_files_thru) $(png_files)
noinst_SCRIPTS = .dox
e_man1_DATA = $(man_files_out)
e_doc_DATA = $(txt_files) $(html_files_out) $(html_files_thru)
e_doc_DATA = $(txt_files) $(html_files_out) $(html_files_thru) $(png_files)
CLEANFILES = $(noinst_SCRIPTS) $(man_files_out) $(html_files_out) doxygen/*
SUFFIXES = .txt .html

View File

@ -1,5 +1,5 @@
E-MailRelay Developer guide
===========================
E-MailRelay Design and implementation
=====================================
Module structure
----------------
@ -27,8 +27,8 @@ reading and extracting messages from the store. The concrete implementation
classes based on these interfaces are respectively "FileStore", "NewFile" and
"StoredFile".
The interaction betweeen the server protocol class and the message store is
mediated by the "ProtocolMessage" interface. Two implementations of this
The interaction between the server protocol class and the message store is
mediated by the "ProtocolMessage" interface. Two main implementations of this
interface are supplied: one for normal spooling ("ProtocolMessageStore"), and
another for immediate forwarding ("ProtocolMessageForward").
@ -78,19 +78,32 @@ Directory structure
Standard headers which are missing (or broken) in msvc6.0
Tour
----
For a quick bottom-up tour of the code take a look at the following headers:
* src/glib/gpath.h
* src/glib/gstr.h
* lib/gcc2.95/sstream
* src/gnet/gevent.h
* src/gnet/gaddress.h
* src/gnet/gsocket.h
* src/gnet/gserver.h
* src/smtp/gmessagestore.h
* src/smtp/gserverprotocol.h
* src/smtp/gsmtpserver.h
Event model
-----------
The E-MailRelay server uses non-blocking socket i/o, with a select() event loop.
This event model means that the server can handle multiple clients simultaneously
from a single thread. The only significant blocking occurs when external programs
are executed (see "--filter" and "--verifier").
At higher levels the slot/signal design pattern is used to propagate events
between objects.
Diagrams
--------
Class diagrams:
* *GNet namespace* [gnet-classes.png]
* *GSmtp namespace* [gsmtp-classes.png]
State transition diagrams:
* *GNet::Client* [gnet-client.png]
* *GSmtp::ServerProtocol* [gsmtp-serverprotocol.png]
* *GSmtp::ScannerClient* [gsmtp-scannerclient.png]
Sequence diagrams:
* *Proxy mode forwarding* [sequence-3.png]
* *Scanning* [sequence-4.png]
* *ProtocolMessage::prepare() returns false* [sequence-1.png]
* *ProtocolMessage::prepare() returns true* [sequence-2.png]
Portability
-----------
@ -139,7 +152,7 @@ these cases there are three source files per header. For example, "gsocket.cpp",
Compile-time features
---------------------
The following features are available to source-code hackers:
The following compile-time features can be enabled with some effort:
# IPv6
@ -162,8 +175,11 @@ The following features are available to source-code hackers:
Windows build
-------------
A simple project file "emailrelay.dsp" for msvc6.0 is provided in the "src/main"
directory.
Simple MSVC project files are provided in the "src/main" directory, bundled into
the "emailrelay.dws" workspace.
Makefiles for the *MinGW* [http://www.mingw.org/] system (gcc on Windows) are also
provided.
Style
-----

View File

@ -428,7 +428,7 @@ Graeme Walker, mailto:<A HREF="mailto:graeme_walker@users.sourceforge.net">graem
This document was created by
<A HREF="http://localhost/cgi-bin/man/man2html">man2html</A>,
using the manual pages.<BR>
Time: 12:51:06 GMT, July 05, 2003
Time: 14:14:01 GMT, September 07, 2003
</BODY>
</HTML>
<!-- Copyright (C) 2001-2003 Graeme Walker <graeme_walker@users.sourceforge.net>. All rights reserved. -->

View File

@ -102,6 +102,20 @@ a.a-toc:hover
background-color: #ccc ;
}
a.a-toc-expander
{
color: #09c ;
background-color: inherit ;
text-decoration: none ;
font-size: smaller ;
}
a.a-toc-expander:hover
{
color: #09c ;
background-color: #ccc ;
}
a.a-header
{
text-decoration:none ;

BIN
doc/gnet-client.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
doc/gsmtp-scannerclient.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -13,7 +13,7 @@
<li><a class="a-href" href="userguide.html">User guide</a></li>
<li><a class="a-href" href="reference.html">Reference</a></li>
<li><a class="a-href" href="windows.html">Windows installation guide</a></li>
<li><a class="a-href" href="developer.html">Notes for developers</a></li>
<li><a class="a-href" href="developer.html">Design and implementation</a></li>
<li><a class="a-href" href="doxygen/index.html">Source code documentation</a> (generated by <a class="a-href" href="http://www.doxygen.org">doxygen</a>, if available)</li>
<li><a class="a-href" href="emailrelay-man.html">Man page</a> (generated by man2html, if available)</li>
<li><a class="a-href" href="http://emailrelay.sourceforge.net">Web site</a></li>

View File

@ -251,11 +251,16 @@ success, or a value between 1 and 99 to indicate failure. Exit codes between
further processing of the message, and 103 has the effect of immediately expiring
any "--poll" timer.
If the pre-processor program terminates with a non-zero exit code then the first
few thousand characters of the standard output stream are searched for a line
starting with "<<" followed by ">>". The text inbetween is taken as a failure
reason, and passed back to the SMTP client.
The pre-processor program can edit any part of the message's envelope file or
content file: E-MailRelay remembers nothing about the message while the
pre-processor is running, except the filename. But if the message is deleted
by the pre-processor then E-MailRelay will be upset, unless the exit code
is 100.
by the pre-processor then E-MailRelay will be upset, so to avoid the
error message use an exit code of 100.
If the pre-processor program creates new messages in the spool directory then
they may not be processed immediately, or they may be completely ignored. To get
@ -381,8 +386,8 @@ following precautions are taken:
# execution environment
The mail pre-processor runs with an almost empty set of environment variables
("PATH" and "IFS"), and with no open file descriptors other than "stdin"/"stdout"/"stderr"
open onto "/dev/null".
("PATH" and "IFS"), and with no open file descriptors other than "stdin" and
"stderr" open onto "/dev/null", and "stdout" open onto a pipe.
# configuration

BIN
doc/sequence-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
doc/sequence-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
doc/sequence-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
doc/sequence-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -12,7 +12,7 @@ the outside world, using the SMTP protocol. As soon as an e-mail message is
received it is forwarded on to the next SMTP server for onward delivery. This
becomes more useful when you add in your own message processing: as each
message is received it can be passed one of your programs for editing,
filtering, encypting etc.
filtering, encrypting etc.
When used as a store-and-forward transfer agent E-MailRelay runs in two modes:
the storage daemon part, and the forwarding agent. The storage daemon

View File

@ -1,5 +1,5 @@
E-MailRelay for Windows
=======================
E-MailRelay Windows installation
================================
Introduction
------------
@ -92,14 +92,6 @@ Finally you will need to configure your e-mail client program to use the local
E-MailRelay server for outgoing mail. Where it asks for the name of the SMTP
server for outgoing mail you should tell it to use "localhost" or "127.0.0.1".
COM registration
----------------
The server can be optionally registered as a COM component by running the
executable with the "-regserver" command line switch. The COM interface may
allow stored messages to be forwarded to any remote server, so consider the
security implications before doing the COM registration. Refer to the reference
guide for more details.
Uninstall
---------
If you have ever registered the executable as a COM component, then deregister it

View File

@ -1,10 +1,10 @@
Summary: Simple e-mail message transfer agent using SMTP
Name: emailrelay
Version: 1.1.1
Version: 1.1.2
Release: 1
Copyright: GPL
Group: System Environment/Daemons
Source: http://emailrelay.sourceforge.net/.../emailrelay-src-1.1.1.tar.gz
Source: http://emailrelay.sourceforge.net/.../emailrelay-src-1.1.2.tar.gz
BuildRoot: /tmp/emailrelay-install
%define prefix /usr

View File

@ -109,7 +109,7 @@ then
echo "install: no input file specified"
exit 1
else
true
:
fi
if [ x"$dir_arg" != x ]; then
@ -120,7 +120,7 @@ if [ x"$dir_arg" != x ]; then
instcmd=:
chmodcmd=""
else
instcmd=mkdir
instcmd=$mkdirprog
fi
else
@ -128,9 +128,9 @@ else
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
if [ -f "$src" ] || [ -d "$src" ]
then
true
:
else
echo "install: $src does not exist"
exit 1
@ -141,7 +141,7 @@ else
echo "install: no destination specified"
exit 1
else
true
:
fi
# If destination is a directory, append the input filename; if your system
@ -151,7 +151,7 @@ else
then
dst="$dst"/`basename $src`
else
true
:
fi
fi
@ -163,8 +163,8 @@ dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
@ -183,7 +183,7 @@ while [ $# -ne 0 ] ; do
then
$mkdirprog "${pathcomp}"
else
true
:
fi
pathcomp="${pathcomp}/"
@ -194,10 +194,10 @@ 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
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi
else
# If we're going to rename the final executable, determine the name now.
@ -216,7 +216,7 @@ else
then
dstfile=`basename $dst`
else
true
:
fi
# Make a temp file name in the proper directory.
@ -235,10 +235,10 @@ else
# 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 &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi &&
# Now rename the file to the real destination.

69
missing
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Common stub for a few missing GNU programs while installing.
# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
# Copyright (C) 1996, 1997, 1999, 2000, 2002 Free Software Foundation, Inc.
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
@ -78,7 +78,7 @@ Supported PROGRAM values:
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing 0.3 - GNU automake"
echo "missing 0.4 - GNU automake"
;;
-*)
@ -87,7 +87,12 @@ Supported PROGRAM values:
exit 1
;;
aclocal)
aclocal*)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
@ -97,6 +102,11 @@ WARNING: \`$1' is missing on your system. You should only need it if
;;
autoconf)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`${configure_ac}'. You might want to install the
@ -106,6 +116,11 @@ WARNING: \`$1' is missing on your system. You should only need it if
;;
autoheader)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`acconfig.h' or \`${configure_ac}'. You might want
@ -124,7 +139,12 @@ WARNING: \`$1' is missing on your system. You should only need it if
touch $touch_files
;;
automake)
automake*)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
@ -135,6 +155,34 @@ WARNING: \`$1' is missing on your system. You should only need it if
while read f; do touch "$f"; done
;;
autom4te)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is needed, and you do not seem to have it handy on your
system. You might have modified some files without having the
proper tools for further handling them.
You can get \`$1Help2man' as part of \`Autoconf' from any GNU
archive site."
file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
if test -f "$file"; then
touch $file
else
test -z "$file" || exec >$file
echo "#! /bin/sh"
echo "# Created by GNU Automake missing as a replacement of"
echo "# $ $@"
echo "exit 0"
chmod +x $file
exit 1
fi
;;
bison|yacc)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
@ -189,6 +237,11 @@ WARNING: \`$1' is missing on your system. You should only need it if
;;
help2man)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified a dependency of a manual page. You may need the
@ -240,23 +293,23 @@ WARNING: \`$1' is missing on your system. You should only need it if
# Look for gnutar/gtar before invocation to avoid ugly error
# messages.
if (gnutar --version > /dev/null 2>&1); then
gnutar ${1+"$@"} && exit 0
gnutar "$@" && exit 0
fi
if (gtar --version > /dev/null 2>&1); then
gtar ${1+"$@"} && exit 0
gtar "$@" && exit 0
fi
firstarg="$1"
if shift; then
case "$firstarg" in
*o*)
firstarg=`echo "$firstarg" | sed s/o//`
tar "$firstarg" ${1+"$@"} && exit 0
tar "$firstarg" "$@" && exit 0
;;
esac
case "$firstarg" in
*h*)
firstarg=`echo "$firstarg" | sed s/h//`
tar "$firstarg" ${1+"$@"} && exit 0
tar "$firstarg" "$@" && exit 0
;;
esac
fi

View File

@ -4,9 +4,53 @@
# Created: 1993-05-16
# Public domain
# $Id: mkinstalldirs,v 1.13 1999/01/05 03:18:55 bje Exp $
errstatus=0
dirmode=""
usage="\
Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..."
# process command line arguments
while test $# -gt 0 ; do
case "${1}" in
-h | --help | --h* ) # -h for help
echo "${usage}" 1>&2; exit 0 ;;
-m ) # -m PERM arg
shift
test $# -eq 0 && { echo "${usage}" 1>&2; exit 1; }
dirmode="${1}"
shift ;;
-- ) shift; break ;; # stop option processing
-* ) echo "${usage}" 1>&2; exit 1 ;; # unknown option
* ) break ;; # first non-opt arg
esac
done
for file
do
if test -d "$file"; then
shift
else
break
fi
done
case $# in
0) exit 0 ;;
esac
case $dirmode in
'')
if mkdir -p -- . 2>/dev/null; then
echo "mkdir -p -- $*"
exec mkdir -p -- "$@"
fi ;;
*)
if mkdir -m "$dirmode" -p -- . 2>/dev/null; then
echo "mkdir -m $dirmode -p -- $*"
exec mkdir -m "$dirmode" -p -- "$@"
fi ;;
esac
for file
do
@ -22,13 +66,24 @@ do
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp"
echo "mkdir $pathcomp"
mkdir "$pathcomp" || lasterr=$?
mkdir "$pathcomp" || lasterr=$?
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
if test ! -d "$pathcomp"; then
errstatus=$lasterr
else
if test ! -z "$dirmode"; then
echo "chmod $dirmode $pathcomp"
lasterr=""
chmod "$dirmode" "$pathcomp" || lasterr=$?
if test ! -z "$lasterr"; then
errstatus=$lasterr
fi
fi
fi
fi
pathcomp="$pathcomp/"
@ -37,4 +92,8 @@ done
exit $errstatus
# Local Variables:
# mode: shell-script
# sh-indentation: 3
# End:
# mkinstalldirs ends here

View File

@ -73,15 +73,51 @@ void G::Arg::setPrefix()
void G::Arg::parse( HINSTANCE hinstance , const std::string & command_line )
{
m_array.push_back( moduleName(hinstance) ) ;
G::Str::splitIntoTokens( command_line , m_array , " \t" ) ;
parseCore( command_line ) ;
setPrefix() ;
}
void G::Arg::reparse( const std::string & command_line )
{
while( m_array.size() > 1U )
m_array.pop_back() ;
G::Str::splitIntoTokens( command_line , m_array , " \t" ) ;
while( m_array.size() > 1U ) m_array.pop_back() ;
parseCore( command_line ) ;
}
void G::Arg::parseCore( const std::string & command_line )
{
std::string s( command_line ) ;
protect( s ) ;
G::Str::splitIntoTokens( s , m_array , " " ) ;
unprotect( m_array ) ;
}
void G::Arg::protect( std::string & s )
{
// replace all quoted spaces with a replacement
// (could do better: escaped quotes, tabs, single quotes)
G_DEBUG( "protect: before: " << Str::toPrintableAscii(s) ) ;
bool in_quote = false ;
const char quote = '"' ;
const char space = ' ' ;
const char replacement = '\0' ;
for( size_t pos = 0U ; pos < s.length() ; pos++ )
{
if( s.at(pos) == quote ) in_quote = ! in_quote ;
if( in_quote && s.at(pos) == space ) s[pos] = replacement ;
}
G_DEBUG( "protect: after: " << Str::toPrintableAscii(s) ) ;
}
void G::Arg::unprotect( StringArray & array )
{
// restore replacements to spaces
const char space = ' ' ;
const char replacement = '\0' ;
for( StringArray::iterator p = array.begin() ; p != array.end() ; ++p )
{
std::string & s = *p ;
G::Str::replaceAll( s , std::string(1U,replacement) , std::string(1U,space) ) ;
}
}
bool G::Arg::contains( const std::string & sw , size_t sw_args , bool cs ) const

View File

@ -120,6 +120,9 @@ private:
bool find( bool , const std::string & , size_t , size_t * ) const ;
void setPrefix() ;
static bool match( bool , const std::string & , const std::string & ) ;
void parseCore( const std::string & ) ;
void protect( std::string & ) ;
void unprotect( StringArray & ) ;
private:
StringArray m_array ;

View File

@ -32,9 +32,13 @@
#ifndef G_DEF_H
#define G_DEF_H
// Autoconf stuff
// Autoconf, part 1
//
#if defined(HAVE_CONFIG_H)
#if defined( HAVE_CONFIG_H )
#if ! defined( G_UNIX )
#define G_UNIX
#endif
#include <config.h>
@ -47,29 +51,10 @@
#include <ctime>
#endif
#if ! HAVE_GMTIME_R
inline std::tm * gmtime_r( const std::time_t * tp , std::tm * tm_p )
{
* tm_p = * std::gmtime( tp ) ;
return tm_p ;
}
#endif
#if ! HAVE_LOCALTIME_R
inline std::tm * localtime_r( const std::time_t * tp , std::tm * tm_p )
{
* tm_p = * std::localtime( tp ) ;
return tm_p ;
}
#endif
#if ! HAVE_SOCKLEN_T
typedef int socklen_t ;
#endif
#if ! defined( G_UNIX )
#define G_UNIX
#endif
#endif
// Check operating-system switches
@ -111,6 +96,9 @@
#else
#include <unistd.h>
#include <sys/stat.h>
#if ! defined( HAVE_CONFIG_H )
#include <grp.h>
#endif
#endif
// Define Windows-style types (only used for
@ -178,5 +166,32 @@
#define G_IGNORE (void)
#endif
// Autoconf, part 2
//
#if defined( HAVE_CONFIG_H )
#if ! HAVE_GMTIME_R
inline std::tm * gmtime_r( const std::time_t * tp , std::tm * tm_p )
{
* tm_p = * std::gmtime( tp ) ;
return tm_p ;
}
#endif
#if ! HAVE_LOCALTIME_R
inline std::tm * localtime_r( const std::time_t * tp , std::tm * tm_p )
{
* tm_p = * std::localtime( tp ) ;
return tm_p ;
}
#endif
#if HAVE_SETGROUPS
#include <grp.h>
#else
inline int setgroups( size_t , const gid_t * )
{
return 0 ;
}
#endif
#endif
#endif

View File

@ -115,6 +115,10 @@ public:
static int errno_() ;
// Returns the process's current 'errno' value.
static void revokeExtraGroups() ;
// Revokes secondary group memberships if really root
// or if suid.
static Identity beOrdinary( Identity nobody , bool change_group = true ) ;
// Revokes special privileges (root or suid).
//

View File

@ -292,6 +292,15 @@ void G::Process::beSpecial( Identity identity , bool change_group )
if( change_group) setEffectiveGroupTo( identity , do_throw ) ;
}
void G::Process::revokeExtraGroups()
{
if( Identity::real().isRoot() || Identity::effective() != Identity::real() )
{
gid_t dummy ;
G_IGNORE ::setgroups( 0U , &dummy ) ; // (only works for root, so ignore the return code)
}
}
G::Identity G::Process::beOrdinary( Identity nobody , bool change_group )
{
Identity special_identity( Identity::effective() ) ;
@ -337,8 +346,11 @@ G::Process::Id::Id( const char * path ) :
{
// reentrant implementation suitable for a signal handler...
int fd = ::open( path ? path : "" , O_RDONLY ) ;
char buffer[10] ;
ssize_t rc = ::read( fd , buffer , sizeof(buffer) ) ;
const size_t buffer_size = 11U ;
char buffer[buffer_size] ;
buffer[0U] = '\0' ;
ssize_t rc = ::read( fd , buffer , buffer_size - 1U ) ;
::close( fd ) ;
for( const char * p = buffer ; rc > 0 && *p >= '0' && *p <= '9' ; p++ , rc-- )
{
m_pid *= 10 ;

View File

@ -36,6 +36,7 @@ namespace G
{
const int g_stderr_fileno = 2 ;
const int g_sc_open_max = 256 ; // 32 in limits.h !?
const HANDLE HNULL = INVALID_HANDLE_VALUE ;
class Pipe ;
} ;
@ -43,14 +44,17 @@ class G::Pipe
{
public:
G_EXCEPTION( Error , "pipe error" ) ;
explicit Pipe( bool active ) ;
explicit Pipe( bool active , bool do_throw = true ) ;
~Pipe() ;
int fd() const ;
std::string read() ;
HANDLE h() const ;
std::string read( bool do_throw = true ) ;
private:
static HANDLE create( HANDLE & h_write , bool do_throw ) ;
static HANDLE duplicate( HANDLE h , bool do_throw ) ;
private:
bool m_active ;
int m_fds[2] ;
int m_fd_writer ;
HANDLE m_read ;
HANDLE m_write ;
} ;
class G::Process::IdImp
@ -61,18 +65,20 @@ public:
// ===
G::Pipe::Pipe( bool active ) :
G::Pipe::Pipe( bool active , bool do_throw ) :
m_active(active) ,
m_fd_writer(-1)
m_read(HNULL) ,
m_write(HNULL)
{
m_fds[0] = m_fds[1] = -1 ;
if( m_active )
{
int rc = ::_pipe( m_fds , 256 , _O_BINARY | _O_NOINHERIT ) ;
if( rc < 0 ) throw Error() ;
m_fd_writer = ::_dup( m_fds[1] ) ; // inherited
::_close( m_fds[1] ) ;
m_fds[1] = -1 ;
const bool do_throw = true ;
HANDLE h = create( m_write , do_throw ) ;
if( h != HNULL )
{
// dont let child processes inherit the read end
m_read = duplicate( h , do_throw ) ;
}
}
}
@ -80,25 +86,76 @@ G::Pipe::~Pipe()
{
if( m_active )
{
::_close( m_fds[0] ) ;
::_close( m_fds[1] ) ;
::_close( m_fd_writer ) ;
if( m_read != HNULL ) ::CloseHandle( m_read ) ;
if( m_write != HNULL ) ::CloseHandle( m_write ) ;
}
}
int G::Pipe::fd() const
//static
HANDLE G::Pipe::create( HANDLE & h_write , bool do_throw )
{
return m_fd_writer ;
static SECURITY_ATTRIBUTES zero_attributes ;
SECURITY_ATTRIBUTES attributes( zero_attributes ) ;
attributes.nLength = sizeof(attributes) ;
attributes.lpSecurityDescriptor = NULL ;
attributes.bInheritHandle = TRUE ;
HANDLE h_read = HNULL ;
h_write = HNULL ;
BOOL rc = ::CreatePipe( &h_read , &h_write , &attributes , 0 ) ;
if( rc == 0 )
{
DWORD error = ::GetLastError() ;
G_ERROR( "G::Pipe::create: pipe error: create: " << error ) ;
if( do_throw ) throw Error( "create" ) ;
h_read = h_write = HNULL ;
}
return h_read ;
}
std::string G::Pipe::read()
//static
HANDLE G::Pipe::duplicate( HANDLE h , bool do_throw )
{
HANDLE result = HNULL ;
BOOL rc = ::DuplicateHandle( GetCurrentProcess() , h , GetCurrentProcess() ,
&result , 0 , FALSE , DUPLICATE_SAME_ACCESS ) ;
if( rc == 0 || result == HNULL )
{
DWORD error = ::GetLastError() ;
::CloseHandle( h ) ;
G_ERROR( "G::Pipe::duplicate: pipe error: dup: " << error ) ;
if( do_throw ) throw Error( "dup" ) ;
result = HNULL ;
}
::CloseHandle( h ) ;
return result ;
}
HANDLE G::Pipe::h() const
{
return m_write ;
}
std::string G::Pipe::read( bool do_throw )
{
if( ! m_active ) return std::string() ;
::_close( m_fd_writer ) ;
::CloseHandle( m_write ) ; m_write = HNULL ;
char buffer[4096] ;
int rc = m_fds[0] == -1 ? 0 : ::_read( m_fds[0] , buffer , sizeof(buffer) ) ;
if( rc < 0 ) throw Error() ;
return std::string( buffer , rc ) ;
DWORD n = sizeof(buffer) ;
DWORD m = 0UL ;
BOOL rc = ::ReadFile( m_read , buffer , n , &m , NULL ) ;
DWORD error = ::GetLastError() ;
::CloseHandle( m_read ) ; m_read = HNULL ;
if( !rc )
{
G_DEBUG( "G::Pipe::read: error: " << m << " byte(s): error=" << error ) ;
if( error != ERROR_BROKEN_PIPE )
{
G_ERROR( "G::Pipe::read: pipe read error: " << error ) ;
if( do_throw ) throw Error( "read" ) ;
}
}
return std::string( buffer , m ) ;
}
// ===
@ -173,38 +230,64 @@ int G::Process::errno_()
return errno ;
}
int G::Process::spawn( Identity , const Path & exe , const Strings & args_ ,
int G::Process::spawn( Identity , const Path & exe , const Strings & args ,
std::string * pipe_result_p , int error_return )
{
// open file descriptors are inherited across ::_spawn() --
// no fcntl() is available to set close-on-exec -- but see
// also ::CreateProcess()
Strings args( args_ ) ; // non-const copy
Pipe pipe( pipe_result_p != NULL ) ;
if( pipe_result_p != NULL )
args.push_front( Str::fromInt(pipe.fd()) ) ; // kludge -- child must write on fd passed as argv[1]
G_DEBUG( "G::Process::spawn: \"" << exe << "\": \"" << Str::join(args,"\",\"") << "\"" ) ;
char ** argv = new char* [ args.size() + 2U ] ;
argv[0U] = const_cast<char*>( exe.pathCstr() ) ;
unsigned int argc = 1U ;
for( Strings::const_iterator arg_p = args.begin() ; arg_p != args.end() ; ++arg_p , argc++ )
argv[argc] = const_cast<char*>(arg_p->c_str()) ;
argv[argc] = NULL ;
std::string command_line = std::string("\"") + exe.str() + "\"" ;
for( Strings::const_iterator arg_p = args.begin() ; arg_p != args.end() ; ++arg_p )
{
std::string arg = *arg_p ;
if( arg.find(" ") != std::string::npos && arg.find("\"") != 0U )
arg = std::string("\"") + arg + "\"" ;
command_line += ( std::string(" ") + arg ) ;
}
const int mode = _P_WAIT ;
::_flushall() ;
int rc = ::_spawnv( mode , exe.str().c_str() , argv ) ;
int error = errno_() ;
delete [] argv ;
G_DEBUG( "G::Process::spawn: _spawnv(): rc=" << rc << ": errno=" << error ) ;
Pipe pipe( pipe_result_p != NULL ) ;
if( pipe_result_p != NULL )
*pipe_result_p = pipe.read() ;
SECURITY_ATTRIBUTES * process_attributes = NULL ;
SECURITY_ATTRIBUTES * thread_attributes = NULL ;
BOOL inherit = TRUE ;
DWORD flags = CREATE_NO_WINDOW ;
LPVOID env = NULL ;
LPCTSTR cwd = NULL ;
static STARTUPINFO zero_start ;
STARTUPINFO start(zero_start) ;
start.cb = sizeof(start) ;
start.dwFlags = STARTF_USESTDHANDLES ;
start.hStdInput = HNULL ;
start.hStdOutput = pipe.h() ;
start.hStdError = HNULL ;
PROCESS_INFORMATION info ;
char * command_line_p = const_cast<char*>(command_line.c_str()) ;
BOOL rc = ::CreateProcess( exe.str().c_str() , command_line_p ,
process_attributes , thread_attributes , inherit ,
flags , env , cwd , &start , &info ) ;
return rc < 0 ? error_return : rc ;
bool ok = !!rc ;
DWORD exit_code = error_return ;
if( !ok )
{
DWORD e = ::GetLastError() ;
G_ERROR( "G::Process::spawn: create-process error " << e << ": " << command_line ) ;
}
else
{
DWORD timeout_ms = 30000UL ;
if( WAIT_TIMEOUT == ::WaitForSingleObject( info.hProcess , timeout_ms ) )
{
G_ERROR( "G::Process::spawn: child process has not terminated: still waiting" ) ;
::WaitForSingleObject( info.hProcess , INFINITE ) ;
}
::GetExitCodeProcess( info.hProcess , &exit_code ) ;
G_LOG( "G::Process::spawn: exit " << exit_code << " from \"" << exe << "\": " << exit_code ) ;
if( pipe_result_p != NULL )
*pipe_result_p = pipe.read(false) ;
}
return exit_code ;
}
G::Identity G::Process::beOrdinary( Identity identity , bool )
@ -218,6 +301,11 @@ void G::Process::beSpecial( Identity , bool )
// not implemented
}
void G::Process::revokeExtraGroups()
{
// not implemented
}
// not implemented...
// Who G::Process::fork() {}
// Who G::Process::fork( Id & child ) {}

View File

@ -63,6 +63,7 @@ G::Root::~Root()
//static
void G::Root::init( const std::string & nobody )
{
Process::revokeExtraGroups() ;
m_nobody = nobody.empty() ? Identity::invalid() : Identity(nobody) ;
m_special = Process::beOrdinary( m_nobody ) ;
}

View File

@ -1,22 +1,3 @@
//
// Copyright (C) 2001-2003 Graeme Walker <graeme_walker@users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ===
/* MD5.H - header file for MD5C.C
*/

View File

@ -1,23 +1,3 @@
/*
Copyright (C) 2001-2003 Graeme Walker <graeme_walker@users.sourceforge.net>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
*/

View File

@ -39,6 +39,7 @@ namespace
const int c_retries = 10 ; // number of retries when using a priviledged local port number
const int c_port_start = 512 ;
const int c_port_end = 1024 ;
const std::string c_cannot_connect_to( "cannot connect to " ) ;
}
namespace GNet
@ -73,7 +74,7 @@ GNet::ClientResolver::ClientResolver( ClientImp & imp ) :
// ===
// Class: GNet::ClientImp
// Description: A pimple-pattern implementation class for GClient.
// Description: A pimple-pattern implementation class for GNet::Client.
//
class GNet::ClientImp : public GNet::EventHandler
{
@ -174,6 +175,12 @@ std::string GNet::Client::peerName() const
return m_imp->peerName() ;
}
//static
bool GNet::Client::canRetry( const std::string & error )
{
return error.find( c_cannot_connect_to ) == 0U ;
}
// ===
bool GNet::ClientImp::m_first = true ;
@ -269,7 +276,7 @@ bool GNet::ClientImp::connect( std::string host , std::string service ,
}
if( immediate )
{
G_WARNING( "GNet::Client::connect: immediate connection" ) ; // delete soon
G_DEBUG( "GNet::Client::connect: immediate connection" ) ; // delete soon
s().addReadHandler( *this ) ;
s().addExceptionHandler( *this ) ;
setState( Connected ) ;
@ -392,7 +399,7 @@ GNet::ClientImp::Status GNet::ClientImp::connectCore( Address remote_address ,
if( !s().connect( remote_address , &immediate ) )
{
G_DEBUG( "GNet::ClientImp::connect: immediate failure" ) ;
error = "cannot connect to " ;
error = c_cannot_connect_to ;
error.append( remote_address.displayString().c_str() ) ;
// we should return Failure here, but Microsoft's stack
@ -433,7 +440,7 @@ void GNet::ClientImp::writeEvent()
}
else if( m_state == Connecting )
{
std::string message( "cannot connect to " ) ;
std::string message( c_cannot_connect_to ) ;
message.append( m_address.displayString().c_str() ) ;
setState( Failed ) ;
close() ;

View File

@ -100,24 +100,44 @@ public:
// Returns the peer's canonical name if available.
// Returns the empty string if not.
static bool canRetry( const std::string & error_string ) ;
// Parses the error string from onError(), retuning true
// if it might be temporary. In practice it returns
// true iff Socket::connect() failed.
protected:
friend class ClientImp ;
virtual void onConnect( Socket & socket ) = 0 ;
// Called once connected. May (unfortunately) be
// called from within connect().
//
// Precondition: !connected()
// Postcondition: connected()
virtual void onDisconnect() = 0 ;
// Called when disconnected by the peer.
//
// Precondition: connected()
// Postcondition: !connected()
virtual void onData( const char * data , size_t size ) = 0 ;
// Called on receipt of data.
//
// Precondition: connected()
// Postcondition: connected()
virtual void onError( const std::string &error ) = 0 ;
// Called when an asyncronous, and fatal, error occurs.
virtual void onError( const std::string & error ) = 0 ;
// Called when a connect request fails.
//
// Precondition: !connected()
// Postcondition: !connected()
virtual void onWriteable() = 0 ;
// Called when a blocked connection become writeable.
//
// Precondition: connected()
// Postcondition: connected()
private:
Client( const Client& ) ; // Copy constructor. Not implemented.

View File

@ -144,6 +144,17 @@ void GNet::Server::init( const Address & listening_address )
m_socket->addReadHandler( *this ) ;
}
//static
bool GNet::Server::canBind( const Address & address , bool do_throw )
{
G::Root claim_root ;
StreamSocket s ;
bool ok = s.canBindHint( address ) ;
if( !ok && do_throw )
throw CannotBind( address.displayString() ) ;
return ok ;
}
GNet::Server::~Server()
{
serverCleanup() ;

View File

@ -92,6 +92,11 @@ public:
PeerInfo() ;
} ;
static bool canBind( const Address & listening_address , bool do_throw ) ;
// Checks that the specified address can be
// bound. Throws CannotBind if the address cannot
// be bound and 'do_throw' is true.
explicit Server( unsigned int listening_port ) ;
// Constructor taking a port number. The server
// listens on all local interfaces.

View File

@ -94,11 +94,19 @@ public:
// address. This is used for listening
// sockets.
bool bind();
bool bind() ;
// Binds the socket with an INADDR_ANY network address
// and a zero port number. This is used to
// initialise the socket prior to connect().
bool canBindHint( const Address & address ) ;
// Returns true if the socket can probably be
// bound with the given address. Some
// implementations will always return
// true. This method should be used on a
// temporary socket of the correct dynamic
// type.
bool connect( const Address &addr , bool *done = NULL ) ;
// Initiates a connection to (or association
// with) the given address. Returns false on

View File

@ -18,7 +18,7 @@
//
// ===
//
// gsocket_unix.cc
// gsocket_unix.cpp
//
#include "gdef.h"
@ -59,7 +59,7 @@ int GNet::Socket::reason()
void GNet::Socket::doClose()
{
G_ASSERT( valid() ) ;
::close( m_socket );
::close( m_socket ) ;
m_socket = -1;
}
@ -93,3 +93,10 @@ void GNet::Socket::setFault()
m_reason = EFAULT ;
}
bool GNet::Socket::canBindHint( const Address & address )
{
bool can_bind = bind( address ) ;
close() ;
return can_bind ;
}

View File

@ -91,3 +91,9 @@ void GNet::Socket::setFault()
m_reason = WSAEFAULT ;
}
bool GNet::Socket::canBindHint( const Address & )
{
// rebinding the same port number fails, so a dummy implementation here
return true ;
}

View File

@ -47,10 +47,14 @@ libgsmtp_a_SOURCES = \
gprotocolmessage.h \
gprotocolmessageforward.cpp \
gprotocolmessageforward.h \
gprotocolmessagescanner.cpp \
gprotocolmessagescanner.h \
gprotocolmessagestore.cpp \
gprotocolmessagestore.h \
gsasl_native.cpp \
gsasl.h \
gscannerclient.cpp \
gscannerclient.h \
gsecrets.cpp \
gsecrets.h \
gserverprotocol.cpp \

View File

@ -123,10 +123,14 @@ libgsmtp_a_SOURCES = \
gprotocolmessage.h \
gprotocolmessageforward.cpp \
gprotocolmessageforward.h \
gprotocolmessagescanner.cpp \
gprotocolmessagescanner.h \
gprotocolmessagestore.cpp \
gprotocolmessagestore.h \
gsasl_native.cpp \
gsasl.h \
gscannerclient.cpp \
gscannerclient.h \
gsecrets.cpp \
gsecrets.h \
gserverprotocol.cpp \
@ -158,11 +162,12 @@ am_libgsmtp_a_OBJECTS = gadminserver.$(OBJEXT) gbase64.$(OBJEXT) \
gmessagestore.$(OBJEXT) gmessagestore_unix.$(OBJEXT) \
gnewfile.$(OBJEXT) gnewmessage.$(OBJEXT) \
gprotocolmessage.$(OBJEXT) gprotocolmessageforward.$(OBJEXT) \
gprotocolmessagescanner.$(OBJEXT) \
gprotocolmessagestore.$(OBJEXT) gsasl_native.$(OBJEXT) \
gsecrets.$(OBJEXT) gserverprotocol.$(OBJEXT) \
gsmtpclient.$(OBJEXT) gsmtpserver.$(OBJEXT) \
gstoredfile.$(OBJEXT) gstoredmessage.$(OBJEXT) \
gverifier.$(OBJEXT) gxtext.$(OBJEXT)
gscannerclient.$(OBJEXT) gsecrets.$(OBJEXT) \
gserverprotocol.$(OBJEXT) gsmtpclient.$(OBJEXT) \
gsmtpserver.$(OBJEXT) gstoredfile.$(OBJEXT) \
gstoredmessage.$(OBJEXT) gverifier.$(OBJEXT) gxtext.$(OBJEXT)
libgsmtp_a_OBJECTS = $(am_libgsmtp_a_OBJECTS)
DEFS = @DEFS@
@ -181,8 +186,11 @@ am__depfiles_maybe = depfiles
@AMDEP_TRUE@ ./$(DEPDIR)/gnewfile.Po ./$(DEPDIR)/gnewmessage.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gprotocolmessage.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gprotocolmessageforward.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gprotocolmessagescanner.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gprotocolmessagestore.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gsasl_native.Po ./$(DEPDIR)/gsecrets.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gsasl_native.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gscannerclient.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gsecrets.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gserverprotocol.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gsmtpclient.Po \
@AMDEP_TRUE@ ./$(DEPDIR)/gsmtpserver.Po \
@ -237,8 +245,10 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnewmessage.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gprotocolmessage.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gprotocolmessageforward.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gprotocolmessagescanner.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gprotocolmessagestore.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsasl_native.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gscannerclient.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsecrets.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gserverprotocol.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsmtpclient.Po@am__quote@

View File

@ -37,7 +37,7 @@
#include "gassert.h"
GSmtp::ClientProtocol::ClientProtocol( Sender & sender , const Secrets & secrets ,
const std::string & thishost_name , unsigned int timeout , bool must_authenticate ) :
const std::string & thishost_name , unsigned int timeout , bool must_authenticate , bool strict ) :
m_sender(sender) ,
m_secrets(secrets) ,
m_thishost(thishost_name) ,
@ -47,6 +47,8 @@ GSmtp::ClientProtocol::ClientProtocol( Sender & sender , const Secrets & secrets
m_message_is_8bit(false) ,
m_authenticated_with_server(false) ,
m_must_authenticate(must_authenticate) ,
m_strict(strict) ,
m_warned(false) ,
m_timeout(timeout) ,
m_signalled(false)
{
@ -147,7 +149,8 @@ void GSmtp::ClientProtocol::apply( const std::string & rx )
void GSmtp::ClientProtocol::sendMail()
{
if( !m_server_has_8bitmime && m_message_is_8bit )
const bool dodgy = m_message_is_8bit && !m_server_has_8bitmime ;
if( dodgy && m_strict )
{
std::string reason = "cannot send 8-bit message to 7-bit server" ;
G_WARNING( "GSmtp::ClientProtocol: " << reason ) ;
@ -156,6 +159,12 @@ void GSmtp::ClientProtocol::sendMail()
}
else
{
if( dodgy && !m_warned )
{
m_warned = true ;
G_WARNING( "GSmtp::ClientProtocol::sendMail: sending an eight-bit message "
"to a server which has not advertised the 8BITMIME extension" ) ;
}
sendMailCore() ;
}
}

View File

@ -159,7 +159,8 @@ public:
ClientProtocol( Sender & sender , const Secrets & secrets ,
const std::string & thishost_name ,
unsigned int timeout , bool must_authenticate ) ;
unsigned int timeout , bool must_authenticate ,
bool eight_bit_strict = false ) ;
// Constructor. The 'sender' and 'secrets' references
// are kept.
//
@ -168,6 +169,10 @@ public:
//
// The 'thishost_name' parameter is used in the
// SMTP EHLO request.
//
// If the 'eight-bit-strict' flag is true then
// an eight-bit message being sent to a
// seven-bit server will be failed.
G::Signal3<bool,bool,std::string> & doneSignal() ;
// Returns a signal which is raised once the protocol has
@ -237,6 +242,8 @@ private:
std::string m_auth_mechanism ;
std::auto_ptr<SaslClient> m_sasl ;
bool m_must_authenticate ;
bool m_strict ;
bool m_warned ;
unsigned int m_timeout ;
G::Signal3<bool,bool,std::string> m_signal ;
bool m_signalled ;

View File

@ -27,6 +27,7 @@
#include "gnewfile.h"
#include "gmemory.h"
#include "gprocess.h"
#include "gstr.h"
#include "groot.h"
#include "gfile.h"
#include "gxtext.h"
@ -119,9 +120,10 @@ bool GSmtp::NewFile::store( const std::string & auth_id , const std::string & cl
bool cancelled = false ;
if( ok )
{
ok = preprocess( m_content_path , cancelled ) ;
std::string output ;
ok = preprocess( m_content_path , cancelled , output ) ;
if( !ok )
reason = "pre-processing failed" ;
reason = output.empty() ? std::string("pre-processing failed") : output ;
}
G_ASSERT( !(ok&&cancelled) ) ;
@ -165,11 +167,11 @@ void GSmtp::NewFile::cleanup()
}
}
bool GSmtp::NewFile::preprocess( const G::Path & path , bool & cancelled )
bool GSmtp::NewFile::preprocess( const G::Path & path , bool & cancelled , std::string & output )
{
if( ! m_preprocess ) return true ;
int exit_code = preprocessCore( path ) ;
int exit_code = preprocessCore( path , output ) ;
bool is_ok = exit_code == 0 ;
bool is_special = exit_code >= 100 && exit_code <= 107 ;
@ -199,16 +201,43 @@ bool GSmtp::NewFile::preprocess( const G::Path & path , bool & cancelled )
}
}
int GSmtp::NewFile::preprocessCore( const G::Path & path )
int GSmtp::NewFile::preprocessCore( const G::Path & path , std::string & output )
{
G_LOG( "GSmtp::NewFile::preprocess: " << m_preprocessor << " " << path ) ;
G::Strings args ;
args.push_back( path.str() ) ;
int exit_code = G::Process::spawn( G::Root::nobody() , m_preprocessor , args ) ;
G_LOG( "GSmtp::NewFile::preprocess: exit status " << exit_code ) ;
std::string raw_output ;
int exit_code = G::Process::spawn( G::Root::nobody() , m_preprocessor , args , &raw_output ) ;
output = parseOutput( raw_output ) ;
G_LOG( "GSmtp::NewFile::preprocess: exit status " << exit_code << " (\"" << output << "\")" ) ;
return exit_code ;
}
std::string GSmtp::NewFile::parseOutput( std::string s ) const
{
G_DEBUG( "GSmtp::NewFile::parseOutput: in: \"" << G::Str::toPrintableAscii(s) << "\"" ) ;
const std::string start("<<") ;
const std::string end(">>") ;
std::string result ;
G::Str::replaceAll( s , "\r\n" , "\n" ) ;
G::Str::replaceAll( s , "\r" , "\n" ) ;
G::Strings lines ;
G::Str::splitIntoFields( s , lines , "\n" ) ;
for( G::Strings::iterator p = lines.begin() ; p != lines.end() ; ++p )
{
std::string line = *p ;
size_t pos_start = line.find(start) ;
size_t pos_end = line.find(end) ;
if( pos_start == 0U && pos_end != std::string::npos )
{
result = G::Str::toPrintableAscii(line.substr(start.length(),pos_end-start.length())) ;
}
}
G_DEBUG( "GSmtp::NewFile::parseOutput: in: \"" << G::Str::toPrintableAscii(result) << "\"" ) ;
return result ;
}
void GSmtp::NewFile::deliver( const G::Strings & /*to*/ ,
const G::Path & content_path , const G::Path & envelope_path_now ,
const G::Path & envelope_path_later )
@ -263,6 +292,11 @@ unsigned long GSmtp::NewFile::id() const
return m_seq ;
}
G::Path GSmtp::NewFile::contentPath() const
{
return m_content_path ;
}
void GSmtp::NewFile::setPreprocessor( const G::Path & exe )
{
if( exe.isRelative() )

View File

@ -70,6 +70,9 @@ public:
// Defines a program which is used for pre-processing
// messages before they are stored.
G::Path contentPath() const ;
// Returns the path of the content file.
private:
FileStore & m_store ;
unsigned long m_seq ;
@ -90,8 +93,9 @@ private:
const std::string & crlf() const ;
static bool isEightBit( const std::string & line ) ;
void deliver( const G::Strings & , const G::Path & , const G::Path & , const G::Path & ) ;
bool preprocess( const G::Path & , bool & ) ;
int preprocessCore( const G::Path & ) ;
bool preprocess( const G::Path & , bool & , std::string & ) ;
int preprocessCore( const G::Path & , std::string & ) ;
std::string parseOutput( std::string ) const ;
bool commit( const G::Path & , const G::Path & ) ;
void rollback() ;
void cleanup() ;

View File

@ -61,6 +61,13 @@ public:
// As a special case, if success is true and id is zero then
// the message processing was cancelled.
virtual G::Signal3<bool,bool,std::string> & preparedSignal() = 0 ;
// Returns a signal which is raised once prepare() has
// completed.
//
// The signal parameters are 'success', 'temporary' and
// 'reason'.
virtual void clear() = 0 ;
// Clears the message state and terminates
// any asynchronous message processing.
@ -69,6 +76,13 @@ public:
// Sets the message envelope 'from'.
// Returns false if an invalid user.
virtual bool prepare() = 0 ;
// Called to start any asynchronous preparation which is
// required after setFrom(). Returns true if there
// is something to do (in which case preparedSignal()
// must be fired later), or false if there is nothing
// to do.
virtual bool addTo( const std::string & to_user , Verifier::Status to_status ) = 0 ;
// Adds an envelope 'to'.
//

View File

@ -44,13 +44,23 @@ GSmtp::ProtocolMessageForward::ProtocolMessageForward( MessageStore & store ,
m_pm.doneSignal().connect( G::slot(*this,&ProtocolMessageForward::processDone) ) ;
}
G::Signal3<bool,unsigned long,std::string> & GSmtp::ProtocolMessageForward::storageDoneSignal()
{
return m_pm.doneSignal() ;
}
GSmtp::ProtocolMessageForward::~ProtocolMessageForward()
{
}
G::Signal3<bool,unsigned long,std::string> & GSmtp::ProtocolMessageForward::doneSignal()
{
return m_signal ;
return m_done_signal ;
}
G::Signal3<bool,bool,std::string> & GSmtp::ProtocolMessageForward::preparedSignal()
{
return m_prepared_signal ;
}
void GSmtp::ProtocolMessageForward::clear()
@ -64,6 +74,11 @@ bool GSmtp::ProtocolMessageForward::setFrom( const std::string & from )
return m_pm.setFrom( from ) ;
}
bool GSmtp::ProtocolMessageForward::prepare()
{
return false ; // no async preparation required
}
bool GSmtp::ProtocolMessageForward::addTo( const std::string & to , Verifier::Status to_status )
{
return m_pm.addTo( to , to_status ) ;
@ -90,19 +105,24 @@ void GSmtp::ProtocolMessageForward::process( const std::string & auth_id ,
m_pm.process( auth_id , client_ip ) ;
}
void GSmtp::ProtocolMessageForward::processDone( bool success , unsigned long id , std::string reason_in )
void GSmtp::ProtocolMessageForward::processDone( bool success , unsigned long id , std::string reason )
{
std::string reason( reason_in ) ;
bool nothing_to_do = success && id == 0UL ;
if( success && id != 0UL )
{
m_id = id ;
success = forward( id , nothing_to_do , &reason ) ;
}
if( nothing_to_do || !success )
bool nothing_to_do = false ;
success = forward( id , nothing_to_do , &reason ) ;
if( !success || nothing_to_do )
{
// failed or no recipients
m_done_signal.emit( success , id , reason ) ;
}
}
else
{
m_signal.emit( success , id , reason ) ;
// failed or cancelled
m_done_signal.emit( success , id , reason ) ;
}
}
@ -131,14 +151,20 @@ bool GSmtp::ProtocolMessageForward::forward( unsigned long id , bool & nothing_t
ok = reason.empty() ;
if( !ok && reason_p != NULL )
{
G_DEBUG( "GSmtp::ProtocolMessageForward::forward: client connect error" ) ;
*reason_p = reason ;
}
}
return ok ;
}
catch( std::exception & e )
{
if( reason_p != NULL )
{
G_DEBUG( "GSmtp::ProtocolMessageForward::forward: exception" ) ;
*reason_p = e.what() ;
}
return false ;
}
}
@ -147,6 +173,6 @@ void GSmtp::ProtocolMessageForward::clientDone( std::string reason )
{
G_DEBUG( "GSmtp::ProtocolMessageForward::clientDone: \"" << reason << "\"" ) ;
const bool ok = reason.empty() ;
m_signal.emit( ok , m_id , reason ) ;
m_done_signal.emit( ok , m_id , reason ) ;
}

View File

@ -66,12 +66,18 @@ public:
virtual G::Signal3<bool,unsigned long,std::string> & doneSignal() ;
// See ProtocolMessage.
virtual G::Signal3<bool,bool,std::string> & preparedSignal() ;
// See ProtocolMessage.
virtual void clear() ;
// See ProtocolMessage.
virtual bool setFrom( const std::string & from_user ) ;
// See ProtocolMessage.
virtual bool prepare() ;
// See ProtocolMessage.
virtual bool addTo( const std::string & to_user , Verifier::Status to_status ) ;
// See ProtocolMessage.
@ -87,9 +93,16 @@ public:
virtual void process( const std::string & auth_id , const std::string & client_ip ) ;
// See ProtocolMessage.
protected:
G::Signal3<bool,unsigned long,std::string> & storageDoneSignal() ;
// Returns the signal which is used to signal that the storage
// is complete.
void processDone( bool , unsigned long , std::string ) ;
// ...
private:
void operator=( const ProtocolMessageForward & ) ; // not implemented
void processDone( bool , unsigned long , std::string ) ; // ProtocolMessage::doneSignal()
void clientDone( std::string ) ; // Client::doneSignal()
bool forward( unsigned long , bool & , std::string * ) ;
@ -102,7 +115,8 @@ private:
unsigned long m_id ;
unsigned int m_response_timeout ;
unsigned int m_connection_timeout ;
G::Signal3<bool,unsigned long,std::string> m_signal ;
G::Signal3<bool,unsigned long,std::string> m_done_signal ;
G::Signal3<bool,bool,std::string> m_prepared_signal ;
} ;
#endif

View File

@ -0,0 +1,101 @@
//
// Copyright (C) 2001-2003 Graeme Walker <graeme_walker@users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ===
//
// gprotocolmessagescanner.cpp
//
#include "gdef.h"
#include "gsmtp.h"
#include "gprotocolmessagescanner.h"
#include "gmessagestore.h"
#include "gfilestore.h"
#include "gmemory.h"
#include "gstr.h"
#include "gassert.h"
#include "glog.h"
GSmtp::ProtocolMessageScanner::ProtocolMessageScanner( MessageStore & store ,
const Secrets & client_secrets ,
const std::string & smtp_server ,
unsigned int smtp_response_timeout , unsigned int smtp_connection_timeout ,
const std::string & scanner_server ,
unsigned int scanner_response_timeout , unsigned int scanner_connection_timeout ) :
ProtocolMessageForward(store,client_secrets,smtp_server,smtp_response_timeout,smtp_connection_timeout),
m_store(store) ,
m_scanner_server(scanner_server) ,
m_scanner_response_timeout(scanner_response_timeout) ,
m_scanner_connection_timeout(scanner_connection_timeout) ,
m_id(0UL)
{
G_DEBUG( "GSmtp::ProtocolMessageScanner::ctor" ) ;
scannerInit() ;
ProtocolMessageForward::storageDoneSignal().disconnect() ;
ProtocolMessageForward::storageDoneSignal().connect( G::slot(*this,&ProtocolMessageScanner::storageDone) ) ;
}
void GSmtp::ProtocolMessageScanner::scannerInit()
{
m_scanner_client <<=
new ScannerClient(m_scanner_server,m_scanner_connection_timeout,m_scanner_response_timeout) ;
m_scanner_client->connectedSignal().connect( G::slot(*this,&ProtocolMessageScanner::connectDone) ) ;
m_scanner_client->doneSignal().connect( G::slot(*this,&ProtocolMessageScanner::scannerDone) ) ;
}
GSmtp::ProtocolMessageScanner::~ProtocolMessageScanner()
{
}
G::Signal3<bool,bool,std::string> & GSmtp::ProtocolMessageScanner::preparedSignal()
{
return m_prepared_signal ;
}
bool GSmtp::ProtocolMessageScanner::prepare()
{
m_scanner_client->startConnecting() ;
return true ;
}
void GSmtp::ProtocolMessageScanner::connectDone( std::string reason , bool temporary_error )
{
G_DEBUG( "GSmtp::ProtocolMessageScanner::connectDone: \"" << reason << "\", " << temporary_error ) ;
m_prepared_signal.emit( reason.empty() , temporary_error , reason ) ;
}
void GSmtp::ProtocolMessageScanner::storageDone( bool , unsigned long id , std::string )
{
G_DEBUG( "GSmtp::ProtocolMessageScanner::storageDone" ) ;
m_id = id ;
FileStore & file_store = dynamic_cast<FileStore&>(m_store) ;
m_scanner_client->startScanning( file_store.contentPath(id) ) ;
}
void GSmtp::ProtocolMessageScanner::scannerDone( bool /* reason_is_from_scanner */ , std::string reason )
{
const bool ok = reason.empty() ;
ProtocolMessageForward::processDone( ok , m_id , reason ) ;
}
void GSmtp::ProtocolMessageScanner::clear()
{
scannerInit() ;
Base::clear() ;
}

View File

@ -0,0 +1,94 @@
//
// Copyright (C) 2001-2003 Graeme Walker <graeme_walker@users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ===
//
// gprotocolmessagescanner.h
//
#ifndef G_SMTP_PROTOCOL_MESSAGE_SCANNER_H
#define G_SMTP_PROTOCOL_MESSAGE_SCANNER_H
#include "gdef.h"
#include "gsmtp.h"
#include "gprotocolmessage.h"
#include "gprotocolmessagestore.h"
#include "gprotocolmessageforward.h"
#include "gscannerclient.h"
#include "gsecrets.h"
#include "gsmtpclient.h"
#include "gmessagestore.h"
#include "gnewmessage.h"
#include <string>
#include <memory>
namespace GSmtp
{
class ProtocolMessageScanner ;
}
// Class: GSmtp::ProtocolMessageScanner
// Description: A derivation of ProtocolMessageForward which adds in
// a scanning step.
//
// The scanning part deletages to a ScannerClient data member.
//
// See also: ProtocolMessageStore, ProtocolMessageForward
//
class GSmtp::ProtocolMessageScanner : public GSmtp::ProtocolMessageForward
{
public:
ProtocolMessageScanner( MessageStore & store , const Secrets & client_secrets ,
const std::string & smtp_server_address ,
unsigned int smtp_response_timeout , unsigned int smtp_connection_timeout ,
const std::string & scanner_server_address ,
unsigned int scanner_response_timeout , unsigned int scanner_connection_timeout ) ;
// Constructor. The 'store' and 'client-secrets' references
// are kept.
virtual ~ProtocolMessageScanner() ;
// Destructor.
virtual G::Signal3<bool,bool,std::string> & preparedSignal() ;
// See ProtocolMessage.
virtual bool prepare() ;
// See ProtocolMessage.
virtual void clear() ;
// See ProtocolMessage.
private:
void operator=( const ProtocolMessageScanner & ) ; // not implemented
void connectDone( std::string reason , bool ) ;
void storageDone( bool success , unsigned long id , std::string reason ) ;
void scannerDone( bool b , std::string reason ) ;
void scannerInit() ;
private:
typedef ProtocolMessageForward Base ;
MessageStore & m_store ;
std::string m_scanner_server ;
unsigned int m_scanner_response_timeout ;
unsigned int m_scanner_connection_timeout ;
std::auto_ptr<ScannerClient> m_scanner_client ;
G::Signal3<bool,bool,std::string> m_prepared_signal ;
unsigned long m_id ;
} ;
#endif

View File

@ -68,6 +68,11 @@ bool GSmtp::ProtocolMessageStore::setFrom( const std::string & from )
}
}
bool GSmtp::ProtocolMessageStore::prepare()
{
return false ; // no async preparation required
}
bool GSmtp::ProtocolMessageStore::addTo( const std::string & to , Verifier::Status to_status )
{
G_ASSERT( m_msg.get() != NULL ) ;
@ -121,19 +126,23 @@ void GSmtp::ProtocolMessageStore::process( const std::string & auth_id , const s
id = m_msg->id() ;
}
clear() ;
m_signal.emit( true , id , std::string() ) ;
m_done_signal.emit( true , id , std::string() ) ;
}
catch( std::exception & e )
{
G_ERROR( "GSmtp::ProtocolMessage::process: error: " << e.what() ) ;
clear() ;
m_signal.emit( false , 0UL , e.what() ) ;
m_done_signal.emit( false , 0UL , e.what() ) ;
}
}
G::Signal3<bool,unsigned long,std::string> & GSmtp::ProtocolMessageStore::doneSignal()
{
return m_signal ;
return m_done_signal ;
}
G::Signal3<bool,bool,std::string> & GSmtp::ProtocolMessageStore::preparedSignal()
{
return m_prepared_signal ;
}

View File

@ -56,12 +56,18 @@ public:
virtual G::Signal3<bool,unsigned long,std::string> & doneSignal() ;
// See ProtocolMessage.
virtual G::Signal3<bool,bool,std::string> & preparedSignal() ;
// See ProtocolMessage.
virtual void clear() ;
// See ProtocolMessage.
virtual bool setFrom( const std::string & from_user ) ;
// See ProtocolMessage.
virtual bool prepare() ;
// See ProtocolMessage.
virtual bool addTo( const std::string & to_user , Verifier::Status to_status ) ;
// See ProtocolMessage.
@ -84,7 +90,8 @@ private:
MessageStore & m_store ;
std::auto_ptr<NewMessage> m_msg ;
std::string m_from ;
G::Signal3<bool,unsigned long,std::string> m_signal ;
G::Signal3<bool,unsigned long,std::string> m_done_signal ;
G::Signal3<bool,bool,std::string> m_prepared_signal ;
} ;
#endif

View File

@ -0,0 +1,255 @@
//
// Copyright (C) 2001-2003 Graeme Walker <graeme_walker@users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ===
//
// gscannerclient.cpp
//
#include "gdef.h"
#include "gnet.h"
#include "gsmtp.h"
#include "gstr.h"
#include "gscannerclient.h"
#include "gassert.h"
GSmtp::ScannerClient::ScannerClient( const std::string & host_and_service ,
unsigned int connect_timeout , unsigned int response_timeout ) :
m_timer(*this) ,
m_connect_timeout(connect_timeout) ,
m_response_timeout(response_timeout) ,
m_state("idle") ,
m_socket(NULL) ,
m_host(hostPart(host_and_service)) ,
m_service(servicePart(host_and_service))
{
G_DEBUG( "GSmtp::ScannerClient::ctor: " << host_and_service ) ;
}
GSmtp::ScannerClient::ScannerClient( const std::string & host , const std::string & service ,
unsigned int connect_timeout , unsigned int response_timeout ) :
m_timer(*this) ,
m_connect_timeout(connect_timeout) ,
m_response_timeout(response_timeout) ,
m_state("idle") ,
m_socket(NULL) ,
m_host(host) ,
m_service(service)
{
G_DEBUG( "GSmtp::ScannerClient::ctor: " << host << ":" << service ) ;
}
G::Signal2<std::string,bool> & GSmtp::ScannerClient::connectedSignal()
{
return m_connected_signal ;
}
G::Signal2<bool,std::string> & GSmtp::ScannerClient::doneSignal()
{
return m_done_signal ;
}
void GSmtp::ScannerClient::startConnecting()
{
G_DEBUG( "GSmtp::ScannerClient::startConnecting" ) ;
G_ASSERT( m_state == "idle" ) ;
m_timer.startTimer( m_connect_timeout ) ;
setState( "connecting" ) ;
if( ! connect( m_host , m_service ) )
{
setState( "failing" ) ;
m_timer.cancelTimer() ;
m_timer.startTimer(0U) ;
}
}
void GSmtp::ScannerClient::onConnect( GNet::Socket & socket )
{
G_DEBUG( "GSmtp::ScannerClient::onConnect" ) ;
G_ASSERT( m_state == "connecting" ) ;
m_socket = &socket ;
setState( "temp" ) ;
m_timer.cancelTimer() ;
m_timer.startTimer(0U) ;
}
void GSmtp::ScannerClient::onError( const std::string & error )
{
G_WARNING( "GSmtp::ScannerClient::onError: connect error: " << error ) ;
G_ASSERT( m_state == "connecting" ) ;
m_timer.cancelTimer() ;
setState( "end" ) ;
m_connected_signal.emit( error , GNet::Client::canRetry(error) ) ;
}
std::string GSmtp::ScannerClient::startScanning( const G::Path & path )
{
G_DEBUG( "GSmtp::ScannerClient::startScanning: \"" << path << "\"" ) ;
G_ASSERT( m_state == "connected" || m_state == "disconnected" ) ;
if( m_state == "disconnected" )
{
setState( "end" ) ;
return "disconnected" ;
}
else
{
m_timer.startTimer( m_response_timeout ) ;
std::string data = request( path ) ;
ssize_t rc = m_socket->write( data.c_str() , data.length() ) ;
std::string result =
rc < static_cast<ssize_t>(data.length()) ?
( m_socket->eWouldBlock() ?
std::string("flow control asserted by peer") :
std::string("connection lost") ) :
std::string() ;
bool ok = result.empty() ;
if( ok )
{
setState( "scanning" ) ;
}
else
{
setState( "end" ) ;
m_timer.cancelTimer() ;
}
return result ;
}
}
void GSmtp::ScannerClient::onDisconnect()
{
G_DEBUG( "GSmtp::ScannerClient::onDisconnect" ) ;
G_ASSERT( m_state == "connected" || m_state == "scanning" ) ;
if( m_state == "connected" )
{
setState( "disconnected" ) ;
}
else
{
setState( "end" ) ;
m_done_signal.emit( false , "disconnected" ) ;
}
}
void GSmtp::ScannerClient::onData( const char * data , size_t size )
{
std::string s( data , size ) ;
G_DEBUG( "GSmtp::ScannerClient::onData: " << G::Str::toPrintableAscii(s) ) ;
G_ASSERT( m_state == "scanning" ) ;
m_line_buffer.add( s ) ;
if( isDone() )
{
G_DEBUG( "GSmtp::ScannerClient::onData: done" ) ;
m_timer.cancelTimer() ;
m_socket->close() ;
setState( "end" ) ;
bool from_scanner = true ;
m_done_signal.emit( from_scanner , result() ) ;
}
}
void GSmtp::ScannerClient::onWriteable()
{
// never gets here
G_DEBUG( "GSmtp::ScannerClient::onWriteable" ) ;
}
void GSmtp::ScannerClient::onTimeout( GNet::Timer & )
{
if( m_state == "failing" )
{
setState( "end" ) ;
m_connected_signal.emit( "cannot connect" , false ) ;
}
else if( m_state == "temp" )
{
setState( "connected" ) ;
m_connected_signal.emit( std::string() , false ) ;
}
else if( m_state == "connecting" )
{
setState( "end" ) ;
m_connected_signal.emit( "connect timeout" , true ) ;
}
else if( m_state == "scanning" )
{
setState( "end" ) ;
bool from_scanner = false ;
m_done_signal.emit( from_scanner , "response timeout" ) ;
}
}
void GSmtp::ScannerClient::setState( const std::string & new_state )
{
G_DEBUG( "GSmtp::ScannerClient::setState: \"" << m_state << "\" -> \"" << new_state << "\"" ) ;
m_state = new_state ;
}
//static
std::string GSmtp::ScannerClient::hostPart( const std::string & s )
{
size_t pos = s.find(":") ;
if( pos == std::string::npos )
{
throw FormatError(s) ;
}
return s.substr( 0U , pos ) ;
}
//static
std::string GSmtp::ScannerClient::servicePart( const std::string & s )
{
size_t pos = s.find(":") ;
if( pos == std::string::npos || (pos+1U) == s.length() )
{
throw FormatError(s) ;
}
return s.substr( pos+1U ) ;
}
// scanner customisation...
std::string GSmtp::ScannerClient::request( const G::Path & path ) const
{
std::string prefix = "AREA: " ;
return prefix + path.str() + "\n" ;
}
bool GSmtp::ScannerClient::isDone() const
{
return m_line_buffer.more() ;
}
std::string GSmtp::ScannerClient::result()
{
std::string s = m_line_buffer.line() ;
if( s.find("ok") != std::string::npos )
return std::string() ;
else
return s ;
}

124
src/gsmtp/gscannerclient.h Normal file
View File

@ -0,0 +1,124 @@
//
// Copyright (C) 2001-2003 Graeme Walker <graeme_walker@users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ===
//
// gscannerclient.h
//
#ifndef G_SCANNER_CLIENT_H
#define G_SCANNER_CLIENT_H
#include "gdef.h"
#include "gnet.h"
#include "gsmtp.h"
#include "gclient.h"
#include "gpath.h"
#include "gslot.h"
#include "gtimer.h"
#include "glinebuffer.h"
#include "gexception.h"
namespace GSmtp
{
class ScannerClient ;
}
// Class: GSmtp::ScannerClient
// Description: A class which interacts with a remote 'scanner' process. The
// interface is asynchronous, with separate 'connect' and 'scan' stages.
//
class GSmtp::ScannerClient : private GNet::Client , private GNet::TimeoutHandler
{
public:
G_EXCEPTION( FormatError , "scanner server format error" ) ;
ScannerClient( const std::string & host_and_service ,
unsigned int connect_timeout , unsigned int response_timeout ) ;
// Constructor.
ScannerClient( const std::string & host , const std::string & service ,
unsigned int connect_timeout , unsigned int response_timeout ) ;
// Constructor.
G::Signal2<std::string,bool> & connectedSignal() ;
// Returns a signal which indicates that connection
// is complete.
//
// The signal parameters are the empty string on success
// or a failure reason, and a boolean flag which is
// true if the failure reason implies a temporary
// error.
G::Signal2<bool,std::string> & doneSignal() ;
// Returns a signal which indicates that scanning
// is complete.
//
// The signal parameters are a boolean flag and
// a string. If the flag is true then the string is
// the response from the scanner, empty on success.
// If the flag is false then there has been a network
// error and the string is a reason string.
void startConnecting() ;
// Initiates a connection to the scanner.
//
// The connectedSignal() will get raised
// some time later.
std::string startScanning( const G::Path & path ) ;
// Starts the scanning process for the given
// content file.
//
// Returns an error string if an immediate error.
//
// The doneSignal() will get raised some time
// after startScanning() returns the empty
// string.
private:
virtual void onConnect( GNet::Socket & socket ) ; // GNet::Client
virtual void onDisconnect() ; // GNet::Client
virtual void onData( const char * data , size_t size ) ; // GNet::Client
virtual void onWriteable() ; // GNet::Client
virtual void onError( const std::string & error ) ; // GNet::Client
virtual void onTimeout( GNet::Timer & ) ; // GNet::TimeoutHandler
void raiseSignal( const std::string & ) ;
std::string request( const G::Path & ) const ;
bool isDone() const ;
std::string result() ;
void setState( const std::string & ) ;
static std::string hostPart( const std::string & ) ;
static std::string servicePart( const std::string & ) ;
ScannerClient( const ScannerClient & ) ; // not implemented
void operator=( const ScannerClient & ) ; // not implemented
private:
G::Signal2<bool,std::string> m_done_signal ;
G::Signal2<std::string,bool> m_connected_signal ;
GNet::Timer m_timer ;
unsigned int m_connect_timeout ;
unsigned int m_response_timeout ;
std::string m_state ;
GNet::Socket * m_socket ;
GNet::LineBuffer m_line_buffer ;
std::string m_host ;
std::string m_service ;
} ;
#endif

View File

@ -45,6 +45,7 @@ GSmtp::ServerProtocol::ServerProtocol( Sender & sender , Verifier & verifier , P
m_sasl(secrets)
{
m_pmessage.doneSignal().connect( G::slot(*this,&ServerProtocol::processDone) ) ;
m_pmessage.preparedSignal().connect( G::slot(*this,&ServerProtocol::prepareDone) ) ;
// (dont send anything to the peer from this ctor -- the Sender
// object is not fuly constructed)
@ -56,7 +57,8 @@ GSmtp::ServerProtocol::ServerProtocol( Sender & sender , Verifier & verifier , P
m_fsm.addTransition( eVrfy , s_Any , s_Same , &GSmtp::ServerProtocol::doVrfy ) ;
m_fsm.addTransition( eEhlo , s_Any , sIdle , &GSmtp::ServerProtocol::doEhlo , s_Same ) ;
m_fsm.addTransition( eHelo , s_Any , sIdle , &GSmtp::ServerProtocol::doHelo , s_Same ) ;
m_fsm.addTransition( eMail , sIdle , sGotMail , &GSmtp::ServerProtocol::doMail , sIdle ) ;
m_fsm.addTransition( eMail , sIdle , sPrepare , &GSmtp::ServerProtocol::doMailPrepare , sIdle ) ;
m_fsm.addTransition( ePrepared, sPrepare, sGotMail , &GSmtp::ServerProtocol::doMail , sIdle ) ;
m_fsm.addTransition( eRcpt , sGotMail, sGotRcpt , &GSmtp::ServerProtocol::doRcpt , sGotMail ) ;
m_fsm.addTransition( eRcpt , sGotRcpt, sGotRcpt , &GSmtp::ServerProtocol::doRcpt ) ;
m_fsm.addTransition( eData , sGotMail, sIdle , &GSmtp::ServerProtocol::doNoRecipients ) ;
@ -128,6 +130,7 @@ bool GSmtp::ServerProtocol::apply( const std::string & line )
void GSmtp::ServerProtocol::processDone( bool success , unsigned long , std::string reason )
{
G_DEBUG( "GSmtp::ServerProtocol::processDone: " << success << ", \"" << reason << "\"" ) ;
G_ASSERT( m_fsm.state() == sProcessing ) ; // (a RSET will call m_pmessage.clear() to cancel the callback)
if( m_fsm.state() == sProcessing ) // just in case
{
@ -136,12 +139,11 @@ void GSmtp::ServerProtocol::processDone( bool success , unsigned long , std::str
}
}
void GSmtp::ServerProtocol::doQuit( const std::string & , bool & )
{
sendClosing() ;
m_sender.protocolDone() ;
// do nothing more -- this object may have been deleted already
// do nothing more -- this object may have been deleted in protocolDone()
}
void GSmtp::ServerProtocol::doNoop( const std::string & , bool & )
@ -314,7 +316,7 @@ void GSmtp::ServerProtocol::sendChallenge( const std::string & s )
send( std::string("334 ") + Base64::encode(s,std::string()) ) ;
}
void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate )
void GSmtp::ServerProtocol::doMailPrepare( const std::string & line , bool & predicate )
{
if( m_sasl.active() && !m_sasl.trusted(m_peer_address) && !m_authenticated )
{
@ -328,21 +330,56 @@ void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate
bool ok = m_pmessage.setFrom( from ) ;
predicate = ok ;
if( ok )
sendMailReply() ;
{
bool async_prepare = m_pmessage.prepare() ;
if( ! async_prepare )
m_fsm.apply( *this , ePrepared , "" ) ; // re-entrancy ok
}
else
{
sendBadFrom( from ) ;
}
}
}
void GSmtp::ServerProtocol::prepareDone( bool success , bool temporary_fault , std::string reason )
{
G_DEBUG( "GSmtp::ServerProtocol::prepareDone: " << success << ", "
<< temporary_fault << ", \"" << reason << "\"" ) ;
// as a kludge mark temporary failures by prepending a space
if( !success && temporary_fault )
reason = std::string(" ")+reason ;
m_fsm.apply( *this , ePrepared , reason ) ;
}
void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate )
{
// here 'line' comes from prepareDone(), or empty if no preparation stage
G_DEBUG( "GSmtp::ServerProtocol::doMail: \"" << line << "\"" ) ;
if( line.empty() )
{
sendMailReply() ;
}
else
{
predicate = false ;
bool temporary = line.at(0U) == ' ' ;
sendMailError( line.substr(temporary?1U:0U) , temporary ) ;
}
}
void GSmtp::ServerProtocol::doRcpt( const std::string & line , bool & predicate )
{
std::string to = parseTo( line ) ;
bool ok = m_pmessage.addTo( to , verify(to,m_pmessage.from()) ) ;
Verifier::Status status = verify( to , m_pmessage.from() ) ;
bool ok = m_pmessage.addTo( to , status ) ;
predicate = ok ;
if( ok )
sendRcptReply() ;
else
sendBadTo( to ) ;
sendBadTo( G::Str::toPrintableAscii(status.reason) ) ;
}
void GSmtp::ServerProtocol::doUnknown( const std::string & line , bool & )
@ -473,6 +510,12 @@ void GSmtp::ServerProtocol::sendMailReply()
sendOk() ;
}
void GSmtp::ServerProtocol::sendMailError( const std::string & reason , bool temporary )
{
std::string number( temporary ? "452" : "550" ) ;
send( number + " " + reason ) ;
}
void GSmtp::ServerProtocol::sendCompletionReply( bool ok , const std::string & reason )
{
if( ok )
@ -491,9 +534,9 @@ void GSmtp::ServerProtocol::sendBadFrom( const std::string & /*from*/ )
send( "553 mailbox name not allowed" ) ;
}
void GSmtp::ServerProtocol::sendBadTo( const std::string & to )
void GSmtp::ServerProtocol::sendBadTo( const std::string & text )
{
send( std::string("550 mailbox unavailable: ") + to ) ;
send( std::string("550 mailbox unavailable: ") + text ) ;
}
void GSmtp::ServerProtocol::sendEhloReply( const std::string & domain )

View File

@ -113,6 +113,7 @@ private:
eData ,
eRcpt ,
eMail ,
ePrepared ,
eVrfy ,
eHelp ,
eAuth ,
@ -125,6 +126,7 @@ private:
sEnd ,
sIdle ,
sGotMail ,
sPrepare ,
sGotRcpt ,
sData ,
sProcessing ,
@ -143,6 +145,7 @@ private:
std::string commandLine( const std::string & line ) const ;
static std::string crlf() ;
void processDone( bool , unsigned long , std::string ) ; // ProtocolMessage::doneSignal()
void prepareDone( bool , bool , std::string ) ;
bool isEndOfText( const std::string & ) const ;
bool isEscaped( const std::string & ) const ;
void doNoop( const std::string & , bool & ) ;
@ -151,6 +154,7 @@ private:
void doHelo( const std::string & , bool & ) ;
void doAuth( const std::string & , bool & ) ;
void doAuthData( const std::string & , bool & ) ;
void doMailPrepare( const std::string & line , bool & ) ;
void doMail( const std::string & line , bool & ) ;
void doRcpt( const std::string & line , bool & ) ;
void doUnknown( const std::string & line , bool & ) ;
@ -169,6 +173,7 @@ private:
void sendEhloReply( const std::string & ) ;
void sendRsetReply() ;
void sendMailReply() ;
void sendMailError( const std::string & , bool ) ;
void sendRcptReply() ;
void sendDataReply() ;
void sendCompletionReply( bool ok , const std::string & ) ;

View File

@ -26,6 +26,7 @@
#include "gsmtpserver.h"
#include "gprotocolmessagestore.h"
#include "gprotocolmessageforward.h"
#include "gprotocolmessagescanner.h"
#include "gmemory.h"
#include "glocal.h"
#include "glog.h"
@ -116,17 +117,22 @@ GSmtp::Server::Server( MessageStore & store ,
const Secrets & server_secrets , const Verifier & verifier ,
const std::string & ident , bool allow_remote ,
unsigned int port , const AddressList & interfaces ,
const std::string & downstream_server ,
unsigned int response_timeout , unsigned int connection_timeout ,
const Secrets & client_secrets ) :
const std::string & smtp_server ,
unsigned int smtp_response_timeout , unsigned int smtp_connection_timeout ,
const Secrets & client_secrets ,
const std::string & scanner_server ,
unsigned int scanner_response_timeout , unsigned int scanner_connection_timeout ) :
m_store(store) ,
m_ident(ident) ,
m_allow_remote( allow_remote ) ,
m_server_secrets(server_secrets) ,
m_verifier(verifier) ,
m_downstream_server(downstream_server) ,
m_response_timeout(response_timeout) ,
m_connection_timeout(connection_timeout) ,
m_smtp_server(smtp_server) ,
m_smtp_response_timeout(smtp_response_timeout) ,
m_smtp_connection_timeout(smtp_connection_timeout) ,
m_scanner_server(scanner_server) ,
m_scanner_response_timeout(scanner_response_timeout) ,
m_scanner_connection_timeout(scanner_connection_timeout) ,
m_client_secrets(client_secrets) ,
m_gnet_server_1( *this ) ,
m_gnet_server_2( *this ) ,
@ -183,17 +189,34 @@ GNet::ServerPeer * GSmtp::Server::newPeer( GNet::Server::PeerInfo peer_info )
return NULL ;
}
const bool immediate = ! m_downstream_server.empty() ;
std::auto_ptr<ProtocolMessage> pmessage(
immediate ?
static_cast<ProtocolMessage*>(new ProtocolMessageForward(m_store,
m_client_secrets,m_downstream_server,m_response_timeout,m_connection_timeout)) :
static_cast<ProtocolMessage*>(new ProtocolMessageStore(m_store)) ) ;
std::auto_ptr<ProtocolMessage> pmessage( newProtocolMessage() ) ;
return new ServerPeer( peer_info , *this , pmessage , m_ident , m_server_secrets , m_verifier ) ;
}
GSmtp::ProtocolMessage * GSmtp::Server::newProtocolMessage()
{
const bool immediate = ! m_smtp_server.empty() ;
const bool scan = ! m_scanner_server.empty() ;
if( immediate && scan )
{
G_DEBUG( "GSmtp::Server::newProtocolMessage: new ProtocolMessageScanner" ) ;
return new ProtocolMessageScanner(m_store,m_client_secrets,
m_smtp_server,m_smtp_response_timeout,m_smtp_connection_timeout,
m_scanner_server,m_scanner_response_timeout,m_scanner_connection_timeout) ;
}
else if( immediate )
{
G_DEBUG( "GSmtp::Server::newProtocolMessage: new ProtocolMessageForward" ) ;
return new ProtocolMessageForward(m_store,m_client_secrets,
m_smtp_server,m_smtp_response_timeout,m_smtp_connection_timeout) ;
}
else
{
G_DEBUG( "GSmtp::Server::newProtocolMessage: new ProtocolMessageStore" ) ;
return new ProtocolMessageStore(m_store) ;
}
}
// ===
GSmtp::ServerImp::ServerImp( GSmtp::Server & server ) :

View File

@ -111,10 +111,13 @@ public:
const Secrets & server_secrets , const Verifier & verifier ,
const std::string & ident , bool allow_remote ,
unsigned int port , const AddressList & interfaces ,
const std::string & downstream_server_address ,
unsigned int response_timeout ,
unsigned int connection_timeout ,
const Secrets & client_secrets ) ;
const std::string & smtp_server_address ,
unsigned int smtp_response_timeout ,
unsigned int smtp_connection_timeout ,
const Secrets & client_secrets ,
const std::string & scanner_address ,
unsigned int scanner_response_timeout ,
unsigned int scanner_connection_timeout ) ;
// Constructor. Listens on the given port number
// using INET_ANY if 'interfaces' is empty, or
// on specific interfaces otherwise. Currently
@ -138,6 +141,7 @@ public:
private:
void bind( ServerImp & , GNet::Address , unsigned int ) ;
ProtocolMessage * newProtocolMessage() ;
ServerImp & imp( size_t n ) ;
private:
@ -146,9 +150,12 @@ private:
bool m_allow_remote ;
const Secrets & m_server_secrets ;
Verifier m_verifier ;
std::string m_downstream_server ;
unsigned int m_response_timeout ;
unsigned int m_connection_timeout ;
std::string m_smtp_server ;
unsigned int m_smtp_response_timeout ;
unsigned int m_smtp_connection_timeout ;
std::string m_scanner_server ;
unsigned int m_scanner_response_timeout ;
unsigned int m_scanner_connection_timeout ;
const Secrets & m_client_secrets ;
ServerImp m_gnet_server_1 ;
ServerImp m_gnet_server_2 ;

View File

@ -120,8 +120,10 @@ GSmtp::Verifier::Status GSmtp::Verifier::verifyExternal( const std::string & add
std::string response ;
int rc = G::Process::spawn( G::Root::nobody() , m_path , args , &response ) ;
G::Str::trim( response , "\r\n\t" ) ;
G_LOG( "GSmtp::Verifier: " << rc << ": \"" << G::Str::toPrintableAscii(response) << "\"" ) ;
G::Str::trimRight( response , " \n\t" ) ;
G::Str::replaceAll( response , "\r\n" , "\n" ) ;
G::Str::replaceAll( response , "\r" , "" ) ;
G::Strings response_parts ;
G::Str::splitIntoFields( response , response_parts , "\n" ) ;

View File

@ -76,6 +76,7 @@ std::string Main::CommandLine::switchSpec( bool is_windows )
<< "P!postmaster!deliver to postmaster and reject all other local mailbox addresses!0!!3|"
<< "Z!verifier!defines an external address verifier program!1!program!3|"
<< "Q!admin-terminate!!0!!0|"
<< "R!scanner!!1!host:port!0|"
;
return ss.str() ;
}
@ -193,6 +194,11 @@ std::string Main::CommandLine::semanticError() const
return "the --forward, --immediate and --poll switches require --forward-to" ;
}
if( m_getopt.contains("scanner") && ! ( m_getopt.contains("as-proxy") || m_getopt.contains("immediate") ) )
{
return "the --scanner switch requires --as-proxy or --immediate" ;
}
const bool log =
m_getopt.contains("log") ||
m_getopt.contains("as-server") ||

View File

@ -289,6 +289,10 @@ SOURCE=..\gsmtp\gprotocolmessageforward.cpp
# End Source File
# Begin Source File
SOURCE=..\gsmtp\gprotocolmessagescanner.cpp
# End Source File
# Begin Source File
SOURCE=..\gsmtp\gprotocolmessagestore.cpp
# End Source File
# Begin Source File
@ -329,6 +333,10 @@ SOURCE=..\gsmtp\gsasl_native.cpp
# End Source File
# Begin Source File
SOURCE=..\gsmtp\gscannerclient.cpp
# End Source File
# Begin Source File
SOURCE=..\win32\gscmap.cpp
# End Source File
# Begin Source File

View File

@ -67,7 +67,7 @@ std::string Main::Configuration::str( const std::string & p , const std::string
<< p << "spool directory: " << spoolDir() << eol
<< p << "immediate forwarding? " << yn(immediate()) << eol
<< p << "mail processor: " << (useFilter()?filter():na()) << eol
//<< p << "address verifier: " << na(verifier().str()) << eol
<< p << "address verifier: " << na(verifier().str()) << eol
<< p << "admin port: " << (doAdmin()?G::Str::fromUInt(adminPort()):na()) << eol
<< p << "run as daemon? " << yn(daemon()) << eol
<< p << "verbose logging? " << yn(verbose()) << eol
@ -299,4 +299,18 @@ bool Main::Configuration::withTerminate() const
return m_cl.contains("admin-terminate") ;
}
std::string Main::Configuration::scannerAddress() const
{
return m_cl.contains("scanner") ? m_cl.value("scanner") : std::string() ;
}
unsigned int Main::Configuration::scannerConnectionTimeout() const
{
return 10U ; // for now
}
unsigned int Main::Configuration::scannerResponseTimeout() const
{
return 90U ; // for now
}

View File

@ -167,6 +167,15 @@ public:
// Returns true if the admin interface should support the
// terminate command.
std::string scannerAddress() const ;
// Returns the address of a scanner process.
unsigned int scannerConnectionTimeout() const ;
// Returns a timeout for connecting to the scanner process.
unsigned int scannerResponseTimeout() const ;
// Returns a timeout for talking to the scanner process.
private:
const CommandLine & m_cl ;

2
src/main/doxygen.cfg Executable file → Normal file
View File

@ -3,7 +3,7 @@
# General configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = E-MailRelay
PROJECT_NUMBER = 1.1.1
PROJECT_NUMBER = 1.1.2
OUTPUT_DIRECTORY =
OUTPUT_LANGUAGE = English
EXTRACT_ALL = YES

View File

@ -27,10 +27,19 @@ source code. The <a href="namespaces.html">Namespace List</a> is a good starting
for browsing -- the detailed description section towards the end of each namespace
page gives a list of the namespace's key classes.
The E-MailRelay <a href="../developer.html">developer's guide</a> also gives an overview
of the code structure, including simple class diagrams for the
<a href="../gnet-classes.png">GNet</a> and
<a href="../gsmtp-classes.png">GSmtp</a> namespaces.
The E-MailRelay <a href="../developer.html">design and implementation guide</a> gives an overview
of the code structure, and there are a number of supporting diagrams:
<ul>
<li><a href="../gnet-classes.png">GNet namespace class diagram</a></li>
<li><a href="../gsmtp-classes.png">GSmtp namespace class diagram</a></li>
<li><a href="../sequence-1.png">ProtocolMessage sequence diagram 1</a></li>
<li><a href="../sequence-2.png">ProtocolMessage sequence diagram 2</a></li>
<li><a href="../sequence-3.png">Proxy-mode forwarding sequence diagram</a></li>
<li><a href="../sequence-4.png">Scanning sequence diagram</a></li>
<li><a href="../gnet-client.png">GNet::Client state transition diagram</a></li>
<li><a href="../gsmtp-scannerclient.png">GNet::ScannerClient state transition diagram</a></li>
<li><a href="../gsmtp-serverprotocol.png">GSmtp::ServerProtocol state transition diagram</a></li>
</ul>
*/

49
src/main/run.cpp Executable file → Normal file
View File

@ -50,7 +50,7 @@
//static
std::string Main::Run::versionNumber()
{
return "1.1.1" ;
return "1.1.2" ;
}
Main::Run::Run( Main::Output & output , const G::Arg & arg , const std::string & switch_spec ) :
@ -147,21 +147,41 @@ void Main::Run::run()
}
}
void Main::Run::checkPort( const std::string & interface_ , unsigned int port )
{
GNet::Address address =
interface_.length() ?
GNet::Address(interface_,port) :
GNet::Address(port) ;
GNet::Server::canBind( address , true ) ;
}
void Main::Run::checkPorts() const
{
if( cfg().doServing() && cfg().doSmtp() )
checkPort( cfg().interface_() , cfg().port() ) ;
if( cfg().doServing() && cfg().doAdmin() )
checkPort( cfg().interface_() , cfg().adminPort() ) ;
}
void Main::Run::runCore()
{
// fqdn override option
//
GNet::Local::fqdn( cfg().fqdn() ) ;
// daemonising
// tighten the umask
//
G::PidFile pid_file ;
G::Process::Umask::set( G::Process::Umask::Tightest ) ;
if( cfg().daemon() ) closeFiles() ; // before opening any sockets or message-store streams
if( cfg().usePidFile() ) pid_file.init( G::Path(cfg().pidFile()) ) ;
if( cfg().daemon() ) G::Daemon::detach( pid_file ) ;
// release root privileges
// close inherited file descriptors to avoid locking file
// systems when running as a daemon -- this has to be done
// early, before opening any sockets or message-store streams
//
if( cfg().daemon() ) closeFiles() ;
// release root privileges and extra group memberships
//
G::Root::init( cfg().nobody() ) ;
@ -172,6 +192,10 @@ void Main::Run::runCore()
if( ! event_loop->init() )
throw G::Exception( "cannot initialise network layer" ) ;
// early check on socket bindability
//
checkPorts() ;
// network monitor singleton
//
GNet::Monitor monitor ;
@ -189,6 +213,12 @@ void Main::Run::runCore()
m_client_secrets <<= new GSmtp::Secrets( cfg().clientSecretsFile() , "client" ) ;
GSmtp::Secrets server_secrets( cfg().serverSecretsFile() , "server" ) ;
// daemonise
//
G::PidFile pid_file ;
if( cfg().usePidFile() ) pid_file.init( G::Path(cfg().pidFile()) ) ;
if( cfg().daemon() ) G::Daemon::detach( pid_file ) ;
// run as forwarding agent
//
if( cfg().doForwarding() )
@ -228,7 +258,10 @@ void Main::Run::doServing( GSmtp::MessageStore & store , const GSmtp::Secrets &
cfg().immediate() ? cfg().serverAddress() : std::string() ,
cfg().responseTimeout() ,
cfg().connectionTimeout() ,
client_secrets ) ;
client_secrets ,
cfg().scannerAddress() ,
cfg().scannerResponseTimeout() ,
cfg().scannerConnectionTimeout() ) ;
}
if( cfg().doAdmin() )

View File

@ -108,6 +108,8 @@ private:
void raiseNetworkEvent( std::string , std::string ) ;
void emit( const std::string & , const std::string & , const std::string & ) ;
std::string doPoll() ;
void checkPorts() const ;
static void checkPort( const std::string & , unsigned int ) ;
private:
Output & m_output ;