This commit is contained in:
Graeme Walker 2001-10-21 12:00:00 +00:00
parent 245a01d527
commit 6b2298628a
98 changed files with 4818 additions and 2398 deletions

View File

@ -1,6 +1,14 @@
Change Log Change Log
========== ==========
0.9.2 -> 0.9.3
--------------
* Proxy mode (--immediate and --as-proxy)
* Message preprocessing (--filter)
* Message store classes better separated using abstract interfaces
* Improved notification script, with MIME encoding
* Builds with old 2.91 version of gcc
0.9.1 -> 0.9.2 0.9.1 -> 0.9.2
-------------- --------------
* Better autoconf detection. (Now builds on all SourceForge's * Better autoconf detection. (Now builds on all SourceForge's

View File

@ -5,7 +5,8 @@ Introduction
standard GNU "./configure; make; make install" installation from source. standard GNU "./configure; make; make install" installation from source.
The E-MailRelay userguide describes what you have to do after the "make The E-MailRelay userguide describes what you have to do after the "make
install" (under GNU/Linux) in order to get the emailrelay daemon to start install" (under GNU/Linux) in order to get the emailrelay daemon to start
up at boot-time and automatically forward e-mail to your ISP. up at boot-time, automatically forward e-mail to your ISP when connected,
and bounce failed mail.
Basic Installation Basic Installation
================== ==================

View File

@ -1,2 +1,2 @@
EXTRA_DIST = EXTRA_DIST = emailrelay.spec
SUBDIRS = src bin lib doc SUBDIRS = src bin lib doc

View File

@ -69,7 +69,7 @@ PACKAGE = @PACKAGE@
RANLIB = @RANLIB@ RANLIB = @RANLIB@
VERSION = @VERSION@ VERSION = @VERSION@
EXTRA_DIST = EXTRA_DIST = emailrelay.spec
SUBDIRS = src bin lib doc SUBDIRS = src bin lib doc
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
@ -276,6 +276,7 @@ distdir: $(DISTFILES)
|| exit 1; \ || exit 1; \
fi; \ fi; \
done done
info-am: info-am:
info: info-recursive info: info-recursive
dvi-am: dvi-am:

10
NEWS
View File

@ -1,10 +1,2 @@
no news
To do
=====
from 0.9.1
----------
Better use of autoconf
Port to BSD & solaris
test IPv6

67
README
View File

@ -1,27 +1,37 @@
E-MailRelay E-MailRelay Readme
=========== ==================
Abstract Abstract
-------- --------
E-MailRelay is a simple store-and-forward SMTP MTA, designed for standalone E-MailRelay is a simple SMTP store-and-forward message transfer agent (MTA).
machines with an intermittent (dial-up) connection to the wider Internet. It runs as an SMTP server, storing incoming e-mail in a local spool directory,
In most situations the only configuration required is to specify the mail and then forwarding the stored messages to a downstream SMTP server on request.
gateway address on the command line. It can also run as a proxy server, forwarding (and optionally pre-processing)
incoming e-mail as soon as it is received. It does not do any message routing,
other than to a local postmaster. Because of this functional simplicity it is
extremely easy to configure, typically only requiring the address of the
downstream SMTP server to be put on the command line.
C++ source code is available for Linux and Windows. Distribution is under C++ source code is available for Linux, FreeBSD and Windows. Distribution is
the GNU General Public License. under the GNU General Public License.
Quick start Quick start
----------- -----------
The "emailrelay" program can be run as an SMTP server daemon using the E-MailRelay can be built and installed from source using the ususal
command "emailrelay --as-server", and stored mail can be forwarded by "./configure ; make ; make install" incantation. The program runs as an SMTP
running the command "emailrelay --as-client <isp-mail-server>:smtp". server daemon using the "emailrelay --as-server" command, and stored mail is
forwarded to a downstream server by running "emailrelay --as-client <isp-mail-server>:smtp".
The "--as-server" command is typically run automatically at boot time, The "--as-server" command is typically run automatically at boot time, using
using the boot scripts under "/etc/init.d" or "/sbin/init.d", while the the boot scripts under "/etc/init.d" or "/sbin/init.d", while the "--as-client"
"--as-client" command is normally put into pppd's "ip-up" script ("/etc/ppp/ip-up") command is normally put into pppd's "ip-up" script ("/etc/ppp/ip-up")
in place of "sendmail -q". in place of "sendmail -q".
The program can also run as a proxy server ("--as-proxy") so that e-mail
messages are forwarded immediately to a downstream server -- usually the local
system's default MTA. This can be usefully combined with a mail pre-processor
program by using the "--filter" switch.
The program requires a writeable spool directory to store e-mail messages. The program requires a writeable spool directory to store e-mail messages.
The directory defaults to "/usr/local/var/spool/emailrelay", but it The directory defaults to "/usr/local/var/spool/emailrelay", but it
can be changed by using the "--spool-dir" switch. can be changed by using the "--spool-dir" switch.
@ -31,8 +41,8 @@ server is running then it will fail to start up, with a "cannot bind" error
message. The port number can be changed by using the "--port" switch. message. The port number can be changed by using the "--port" switch.
To test the program out without a full installation: To test the program out without a full installation:
(1) run the server as "emailrelay --no-daemon --no-syslog --log --spool-dir ${HOME} --port 10001 &" (1) run the server as "emailrelay --no-daemon --no-syslog --log --spool-dir ${HOME} --port 10025 &"
(2) reconfigure your e-mail client to use port 10001 rather than 25 ("smtp") (2) reconfigure your e-mail client to use port 10025 rather than 25 ("smtp")
(3) send some test messages to addressees on the Internet (3) send some test messages to addressees on the Internet
(4) connect to the Internet (4) connect to the Internet
(5) forward the stored messages using "emailrelay --spool-dir ${HOME} --as-client <your-isp-smtp-host>:smtp" (5) forward the stored messages using "emailrelay --spool-dir ${HOME} --as-client <your-isp-smtp-host>:smtp"
@ -41,17 +51,19 @@ To test the program out without a full installation:
Documentation Documentation
------------- -------------
The following documentation is provided: The following documentation is provided:
* README -- this document * README -- this document
* COPYING -- the GNU General Public License * COPYING -- the GNU General Public License
* INSTALL -- build & install instructions (based on generic GNU text) * INSTALL -- build & install instructions (based on generic GNU text)
* doc/userguide.txt -- user guide * doc/userguide.txt -- user guide
* doc/reference.txt -- reference document * doc/reference.txt -- reference document
* doc/developer.txt -- developer guide * doc/developer.txt -- developer guide
* ChangeLog -- change log for releases
And for completeness the following stub documents are also included: And for completeness the following stub documents are also included:
* NEWS * NEWS
* AUTHORS * AUTHORS
* ChangeLog
Source code documentation can be generated by using doxygen (www.doxygen.org).
Configurations Configurations
-------------- --------------
@ -65,3 +77,12 @@ The code was developed on SuSE Linux 7.1 using:
and ported to Windows 98 using and ported to Windows 98 using
* MSVC 6.0 * MSVC 6.0
The code has also been built successfully on:
* Linux on Alpha hardware (Debian 2.2)
* Linux on Sparc hardware
* Linux on RS6000 PPC hardware
* FreeBSD on Intel hardware
* Solaris, using gcc, on Sparc hardware
(courtesy of SourceForge's compile farm).

View File

@ -1,7 +1,7 @@
noinst_SCRIPTS = emailrelay-filter.sh emailrelay-test.sh noinst_SCRIPTS = emailrelay-doxygen-filter.sh emailrelay-test.sh
libexec_SCRIPTS = emailrelay.sh libexec_SCRIPTS = emailrelay.sh
pkgdata_DATA = emailrelay-notify.sh emailrelay-deliver.sh pkgdata_DATA = emailrelay-notify.sh emailrelay-deliver.sh emailrelay-process.sh
EXTRA_DIST = emailrelay-filter.sh_ emailrelay-test.sh_ emailrelay.sh_ txt2html.sh_ emailrelay-notify.sh_ emailrelay-deliver.sh_ EXTRA_DIST = emailrelay-doxygen-filter.sh_ emailrelay-test.sh_ emailrelay.sh_ txt2html.sh_ emailrelay-notify.sh_ emailrelay-deliver.sh_ emailrelay-process.sh_
CLEANFILES = $(noinst_SCRIPTS) $(libexec_SCRIPTS) $(pkgdata_DATA) CLEANFILES = $(noinst_SCRIPTS) $(libexec_SCRIPTS) $(pkgdata_DATA)
TESTS = emailrelay-test.sh TESTS = emailrelay-test.sh
SUFFIXES = .sh_ .sh SUFFIXES = .sh_ .sh

View File

@ -69,10 +69,10 @@ PACKAGE = @PACKAGE@
RANLIB = @RANLIB@ RANLIB = @RANLIB@
VERSION = @VERSION@ VERSION = @VERSION@
noinst_SCRIPTS = emailrelay-filter.sh emailrelay-test.sh noinst_SCRIPTS = emailrelay-doxygen-filter.sh emailrelay-test.sh
libexec_SCRIPTS = emailrelay.sh libexec_SCRIPTS = emailrelay.sh
pkgdata_DATA = emailrelay-notify.sh emailrelay-deliver.sh pkgdata_DATA = emailrelay-notify.sh emailrelay-deliver.sh emailrelay-process.sh
EXTRA_DIST = emailrelay-filter.sh_ emailrelay-test.sh_ emailrelay.sh_ txt2html.sh_ emailrelay-notify.sh_ emailrelay-deliver.sh_ EXTRA_DIST = emailrelay-doxygen-filter.sh_ emailrelay-test.sh_ emailrelay.sh_ txt2html.sh_ emailrelay-notify.sh_ emailrelay-deliver.sh_ emailrelay-process.sh_
CLEANFILES = $(noinst_SCRIPTS) $(libexec_SCRIPTS) $(pkgdata_DATA) CLEANFILES = $(noinst_SCRIPTS) $(libexec_SCRIPTS) $(pkgdata_DATA)
TESTS = emailrelay-test.sh TESTS = emailrelay-test.sh
SUFFIXES = .sh_ .sh SUFFIXES = .sh_ .sh

View File

@ -21,12 +21,22 @@
# #
# emailrelay-deliver.sh # emailrelay-deliver.sh
# #
# An example script which looks for local mail in the MailRelay # An example script which looks for local mail in the E-MailRelay
# spool directory, and delivers it using 'procmail'. # spool directory, and delivers it using 'procmail'.
# #
# usage: emailrelay-deliver.sh [<spool-dir>]
#
# Note that (1) E-MailRelay will only accept mail to a local recipient
# if that recipient is "postmaster", and the (2) by default E-MailRelay
# will only accept connections from local e-mail clients. This means that
# this script is only relevant if you are in the habit of sending
# yourself mail as "postmaster". It is provided for completeness,
# in response to certain requirements in the SMTP specification.
#
store="/var/spool/mailrelay" store="/usr/local/var/spool/emailrelay"
postmaster="root" postmaster="root"
procmail="procmail"
# parse the command line # parse the command line
# #
@ -45,7 +55,7 @@ fi
# for each e-mail to a local recipient... # for each e-mail to a local recipient...
# #
for file in ${store}/mail-relay.*.envelope.local "" for file in ${store}/emailrelay.*.envelope.local ""
do do
if test -f "${file}" if test -f "${file}"
then then
@ -62,11 +72,11 @@ do
if test -f "${content}" if test -f "${content}"
then then
echo `basename $0`: delivering `basename ${content}` to ${deliver_to} echo `basename $0`: delivering `basename ${content}` to ${deliver_to}
procmail -d ${deliver_to} < ${content} ${procmail} -d ${deliver_to} < ${content}
rc=$? rc=$?
if test "${rc}" -eq 0 if test "${rc}" -eq 0
then then
echo '' # rm -f "${file}" 2>/dev/null rm -f "${file}" 2>/dev/null
fi fi
fi fi
fi fi

View File

@ -21,16 +21,30 @@
# #
# emailrelay-notify.sh # emailrelay-notify.sh
# #
# An example script which looks for failed mail in the MailRelay spool # An example script which looks for failed mail in the E-MailRelay spool
# directory, and sends failure notification messages using 'procmail'. # directory, and sends failure notification messages using 'procmail'.
# #
# usage: emailrelay-notify.sh [<spool-dir>]
#
# Notification of failed e-mail by means of e-mail messages is a
# requirement imposed by the SMTP specification. However, a simpler
# approach might be more appropriate -- for example, a line like this in
# a ".profile" script:
#
# if test -f /usr/local/var/spool/emailrelay/*.envelope.bad ; then echo Failed mail >&2 ; fi
#
# or perhaps a cron entry like this (since output from a cron job gets sent as mail):
#
# 0 0 * * * /bin/cat /usr/local/var/spool/emailrelay/*.envelope.bad 2>/dev/null
#
tmp="/tmp/`basename $0`.$$.tmp" tmp="/tmp/`basename $0`.$$.tmp"
trap "rm -f ${tmp} 2>/dev/null ; exit 0" 0 1 2 3 13 15 trap "rm -f ${tmp} 2>/dev/null ; exit 0" 0 1 2 3 13 15
procmail="procmail"
# parse the command line # parse the command line
# #
store="/var/spool/mailrelay" store="/usr/local/var/spool/emailrelay"
if test $# -ge 1 if test $# -ge 1
then then
store="${1}" store="${1}"
@ -46,10 +60,12 @@ fi
# for each failed e-mail... # for each failed e-mail...
# #
for file in ${store}/mail-relay.*.envelope.bad "" for file in ${store}/emailrelay.*.envelope.bad ""
do do
if test -f "${file}" if test -f "${file}"
then then
# parse out the failure reason and the original sender
#
content="`echo ${file} | sed 's/envelope/content/' | sed 's/.bad//'`" content="`echo ${file} | sed 's/envelope/content/' | sed 's/.bad//'`"
reason="`fgrep MailRelay-Reason ${file} | sed 's/X-[^ ]*Reason: //' | tr -d '\015'`" reason="`fgrep MailRelay-Reason ${file} | sed 's/X-[^ ]*Reason: //' | tr -d '\015'`"
from="`fgrep MailRelay-From ${file} | sed 's/X-MailRelay-From: //' | tr -d '\015'`" from="`fgrep MailRelay-From ${file} | sed 's/X-MailRelay-From: //' | tr -d '\015'`"
@ -61,33 +77,53 @@ do
# create a notification message header # create a notification message header
# #
boundary="--------------`basename ${file}`.$$"
echo "To: ${deliver_to}" > ${tmp} echo "To: ${deliver_to}" > ${tmp}
echo "From: postmaster" >> ${tmp} echo "From: postmaster" >> ${tmp}
echo "Subject: Your e-mail could not be delivered" >> ${tmp} echo "Subject: Your e-mail could not be delivered" >> ${tmp}
echo " " >> ${tmp} echo "MIME-Version: 0.1" >> ${tmp}
echo "Content-Type: multipart/mixed; boundary=\"${boundary}\"" >> ${tmp}
echo "" >> ${tmp}
# add the message content # add the message text
# #
echo "" >> ${tmp}
echo "--${boundary}" >> ${tmp}
echo "Content-Type: text/plain; charset=us-ascii" >> ${tmp}
echo "" >> ${tmp}
if test -f "${content}"
then
egrep -i '^To:|^Subject:' ${content} >> ${tmp}
fi
if test "${reason}" != "" if test "${reason}" != ""
then then
echo "Reason: ${reason}" >> ${tmp} echo "Reason: ${reason}" >> ${tmp}
fi fi
# add the message attachment
#
if test -f "${content}" if test -f "${content}"
then then
egrep -i '^To:|^Subject:' ${content} >> ${tmp} echo "--${boundary}" >> ${tmp}
echo " " >> ${tmp} echo "Content-Type: message/rfc822" >> ${tmp}
echo "The original mail is saved as \"${content}\"." >> ${tmp} echo "Content-Transfer-Encoding: 8bit" >> ${tmp}
echo "You should make a copy of this file if necessary and then delete it." >> ${tmp} echo "Content-Description: `basename ${content}`" >> ${tmp}
echo "" >> ${tmp}
cat ${content} >> ${tmp}
fi fi
echo "--${boundary}--" >> ${tmp}
# deliver the notification using procmail # deliver the notification using procmail
# #
echo `basename $0`: delivering `basename ${content}` to ${deliver_to} echo `basename $0`: delivering `basename ${content}` to ${deliver_to}
procmail -d ${deliver_to} < ${tmp} ${procmail} -d ${deliver_to} < ${tmp}
rc=$? rc=$?
# clean up
#
if test "${rc}" -eq 0 if test "${rc}" -eq 0
then then
rm -f "${file}" 2>/dev/null rm -f "${file}" "${content}" 2>/dev/null
fi fi
fi fi
done done

146
bin/emailrelay-process.sh_ Normal file
View File

@ -0,0 +1,146 @@
#!/bin/sh
#
# Copyright (C) 2001 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.
#
# ===
#
# emailrelay-process.sh
#
# An example pre-processing script for the E-MailRelay
# system which does rot-13 masking.
#
awk="awk"
tmp="/tmp/`basename $0`.$$.tmp"
log="/tmp/`basename $0`.$$.out"
trap "rm -f ${tmp} >/dev/null 2>&1 ; exit" 0 1 2 3 13 15
###
# ProcessContent()
# Processes the content part of an RFC822 message. This
# implementation does rot13 masking.
#
ProcessContent()
{
${awk} '
BEGIN {
map_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
map_lower = tolower(map_upper)
in_header = 1
}
function rot( n , c , map )
{
return index(map,c) ? substr(map,((index(map,c)+n-1)%length(map))+1,1) : c
}
function rot_c( n , c )
{
return rot(n,rot(n,c,map_upper),map_lower)
}
function rot_s( n , string )
{
result = ""
for( i = 1 ; i <= length(string) ; i++ )
result = result rot_c(n,substr(string,i,1))
return result
}
{
is_blank = match($0,"^[[:space:]]*$")
if( in_header && is_blank )
in_header = 0
if( in_header )
print
else
print rot_s(13,$0)
}
'
}
###
# Wrap()
# Processes an RCF822 message so that the original content
# appears as an attachment.
#
Wrap()
{
${awk} -v boundary="-----`basename $0`.$$" -v message="$@" '
BEGIN {
in_header = 1
n = 1
}
{
is_blank = match($0,"^[[:space:]]*$")
if( in_header && is_blank )
{
printf( "Content-Type: multipart/mixed; boundary=\"%s\"\r\n" , boundary )
printf( "\r\n" )
printf( "\r\n" )
printf( "--%s\r\n" , boundary )
printf( "Content-Type: text/plain; charset=us-ascii\r\n" )
printf( "\r\n" )
printf( "%s\r\n" , message )
printf( "\r\n" )
printf( "--%s\r\n" , boundary )
printf( "Content-Type: message/rfc822\r\n" )
printf( "Content-Transfer-Encoding: 8bit\r\n" )
printf( "Content-Description: encrypted message\r\n" )
printf( "\r\n" )
for( i = 1 ; i < n ; i++ )
print header[i]
}
if( in_header && is_blank )
in_header = 0
if( in_header )
{
header[n++] = $0
is_mime_content = match($0,"^Content-")
is_continuation = match($0,"^[[:space:]][[:space:]]*[^[:space:]]")
block = is_mime_content || (was_mime_content && is_continuation)
was_mime_content = block
if( ! block )
print
}
else
{
print
}
}
END {
printf( "--%s--\r\n" , boundary )
printf( "\r\n" )
}
'
}
Main()
{
cat "${1}" | ProcessContent | Wrap "The original message has been encrypted..." > ${tmp}
cp ${tmp} "${1}"
}
# Main $@ > ${log} 2>&1
Main $@

View File

@ -23,11 +23,14 @@
# #
# Test the E-MailRelay system. # Test the E-MailRelay system.
# #
# Creates three temporary spool directories under /tmp and runs # Creates four temporary spool directories under /tmp and runs
# three emailrelay servers to bucket-brigade a test message from one to # four emailrelay servers to bucket-brigade a test message from one to
# the next. The test succeeds if the message gets into the third # the next. The test succeeds if the message gets into the final
# spool directory. # spool directory.
# #
# Once all the servers have been killed the separate log files are
# concatenated into the summary log file "/tmp/emailrelay-test.out".
#
# If this test takes more than a second or two then it has failed. # If this test takes more than a second or two then it has failed.
# #
@ -42,7 +45,8 @@ Cleanup()
kill `cat ${base_dir}/pid-* 2>/dev/null` 2>/dev/null kill `cat ${base_dir}/pid-* 2>/dev/null` 2>/dev/null
if test -d ${base_dir} if test -d ${base_dir}
then then
grep "." ${base_dir}/log-? > /tmp/`basename $0`.out 2>/dev/null grep "MailRelay-Reason" ${base_dir}/*/*envelope*bad > /tmp/`basename $0`.out 2>/dev/null
grep "." ${base_dir}/log-? >> /tmp/`basename $0`.out 2>/dev/null
fi fi
rm -rf ${base_dir} 2>/dev/null rm -rf ${base_dir} 2>/dev/null
} }
@ -113,7 +117,7 @@ CrLf()
CheckResults() CheckResults()
{ {
if test -f ${base_dir}/store-3/*.envelope -a -f ${base_dir}/store-3/*.content if test -f ${base_dir}/store-4/*.envelope -a -f ${base_dir}/store-4/*.content
then then
exit_code="0" exit_code="0"
echo `basename $0`: succeeded echo `basename $0`: succeeded
@ -139,10 +143,11 @@ trap "Trap 0 ; exit" 0
StartTimer StartTimer
RunServer ${pp}1 store-2 log-1 pid-1 RunServer ${pp}1 store-2 log-1 pid-1
RunServer ${pp}2 store-2 log-2 pid-2 "--admin ${pp}4 --forward-to localhost:${pp}3" RunServer ${pp}2 store-2 log-2 pid-2 "--admin ${pp}9 --forward-to localhost:${pp}3"
RunServer ${pp}3 store-3 log-3 pid-3 RunServer ${pp}3 store-3 log-3 pid-3 "--immediate --forward-to localhost:${pp}4 --filter /bin/touch"
RunServer ${pp}4 store-4 log-4 pid-4
CreateMessages CreateMessages
RunClient localhost:${pp}1 store-1 log-c pid-4 RunClient localhost:${pp}1 store-1 log-c pid-5
RunPoke ${pp}4 log-p RunPoke ${pp}9 log-p
CheckResults CheckResults

View File

@ -24,14 +24,19 @@
# A shell-script wrapper for E-MailRelay designed for # A shell-script wrapper for E-MailRelay designed for
# use in the SysV-init system (/etc/init.d). # use in the SysV-init system (/etc/init.d).
# #
# usage: emailrelay.sh { start | stop } # usage: emailrelay.sh { start [<emailrelay-switches>] | stop }
# #
# configuration # configuration
# #
var_run="/var/run" var_run="/var/run"
emailrelay="/usr/local/sbin/emailrelay"
switches=""
# configuration fallback
#
if test \! -d "${var_run}" ; then var_run="/tmp" ; fi if test \! -d "${var_run}" ; then var_run="/tmp" ; fi
params="" if test \! -x "${emailrelay}" ; then emailrelay="emailrelay" ; fi
# initialisation # initialisation
# #
@ -52,8 +57,9 @@ if test "${1}" = "start"
then then
# "start" # "start"
# #
shift
rm -f "${pid_file}" 2>/dev/null rm -f "${pid_file}" 2>/dev/null
emailrelay --as-server --pid-file "${pid_file}" ${params} $@ ${emailrelay} --as-server --pid-file "${pid_file}" ${switches} $@
elif test "${1}" = "stop" elif test "${1}" = "stop"
then then

View File

@ -21,10 +21,20 @@
# #
# txt2html.sh # txt2html.sh
# #
# usage: txt2html.sh [-a <awk-binary>] <input-file> # Converts plain-text to html. The plain-text has to use special
# formating conventions (see "function process()", Anchorise_1() etc).
#
# Embeds comments in the html output which can be used by "index.sh"
# to create a document index.
#
# Definition lists require a bullet graphic, "graphics/bullet.gif".
#
# usage: txt2html.sh [-a <awk-binary>] [-x] <input-file> [<title>]
#
# The -x switch excludes header and footer stuff.
# #
awk="awk" awk="gawk"
if test "${1}" = "-a" if test "${1}" = "-a"
then then
shift shift
@ -35,6 +45,13 @@ then
shift shift
fi fi
full="1"
if test "${1}" = "-x"
then
shift
full="0"
fi
file="${1}" file="${1}"
if test "${file}" = "" if test "${file}" = ""
then then
@ -48,23 +65,58 @@ then
exit 1 exit 1
fi fi
title="`basename ${file}`" title="`grep -v '^[[:space:]]*$' ${file} | head -1`"
if test "${2}" != "" if test "${2}" != ""
then then
title="${2}" title="${2}"
fi fi
# === # ===
# Include()
#
# Expands #include# directives. Included text is then processed
# as plain text, just like the top-level file. An include directive
# within a line (ie. not on the lhs) is treated as an inline
# sustitution, like shell backticks.
#
Include()
{
${awk} -v cat="${awk} '{print}'" '
{
line = $0
if( match(line,"^#include#[^#]*#") )
{
path = substr(line,10,RLENGTH-10)
system( cat " " path )
}
else
{
print line
}
}
'
}
# ===
# Main()
#
# Does the bulk of the conversion.
#
Main() Main()
{ {
${awk} -v title="${1}" ' ${awk} -v title="${1}" -v full="${2}" -v colour="${3}" '
BEGIN { BEGIN {
printf( "<html>\n" ) if( full )
printf( "<head>\n" ) {
printf( "<title>%s</title>\n" , title ) dtd = "-//W3C//DTD HTML 4.01 Transitional//EN"
printf( "</head>\n" ) printf( "<!DOCTYPE HTML PUBLIC \"%s\">\n" , dtd )
printf( "<body>\n" ) printf( "<html>\n" )
printf( "<head>\n" )
printf( "<title>%s</title>\n" , title )
printf( "</head>\n" )
printf( "<body bgcolor=\"%s\">\n" , colour )
printf( "<!-- index:0::::%s -->\n" , title )
}
} }
function escape( line ) function escape( line )
@ -103,33 +155,53 @@ function tagOutput( line , tag )
function process( line , next_ ) function process( line , next_ )
{ {
is_blank = match( line , "^[[:space:]]*$" ) tab = " "
is_sub_para = match( line , "^[[:space:]][[:space:]][^[:space:]]" ) is_blank = match( line , "^ *$" )
is_code = match( line , "^[[:space:]]" ) && !is_sub_para is_heading = match( next_ , "^==* *$" )
is_heading = match( next_ , "^==*[[:space:]]*$" ) is_sub_heading = match( next_ , "^--* *$" )
is_sub_heading = match( next_ , "^--*[[:space:]]*$" )
is_list_item = match( line , "^\\* " ) is_list_item = match( line , "^\\* " )
is_definition_term = match( line , "^\\# " )
is_definition_text = match( line , "^ [^- ]" )
is_outer_list_item = match( line , "^+ " )
is_inner_list_item = match( line , "^ - " )
is_sub_list_item = match( line , "^ + " )
is_numbered_item = match( line , "^\\([[:digit:]][[:digit:]]*\\)" ) is_numbered_item = match( line , "^\\([[:digit:]][[:digit:]]*\\)" )
is_heading_line = match( line , "^==*[[:space:]]*$" ) is_heading_line = match( line , "^==* *$" )
is_sub_heading_line = match( line , "^--*[[:space:]]*$" ) is_sub_heading_line = match( line , "^--* *$" )
is_code = match( line , "^" tab )
if( is_blank ) if( is_blank )
{ {
printf( "<p><br>\n" ) printf( "<br><br>\n" )
} }
else if( is_code ) else if( is_code )
{ {
tagOutput( line , "pre" ) tagOutput( line , "pre" )
} }
else if( is_sub_para ) else if( is_definition_term )
{ {
tagOutput( line , "sub" ) gsub( "^# " , "" , line )
tagOutput( line , "dt" )
}
else if( is_definition_text )
{
tagOutput( line , "dd" )
} }
else if( is_list_item ) else if( is_list_item )
{ {
gsub( "^\\* " , "" , line ) gsub( "^\\* " , "" , line )
tagOutput( line , "li" ) tagOutput( line , "li" )
} }
else if( is_outer_list_item )
{
gsub( "^+ " , "" , line )
tagOutput( line , "Li" )
}
else if( is_inner_list_item )
{
gsub( "^ - " , "" , line )
tagOutput( line , "lI" )
}
else if( is_numbered_item ) else if( is_numbered_item )
{ {
gsub( "^\\([[:digit:]][[:digit:]]*\\) " , "" , line ) gsub( "^\\([[:digit:]][[:digit:]]*\\) " , "" , line )
@ -137,11 +209,16 @@ function process( line , next_ )
} }
else if( is_heading ) else if( is_heading )
{ {
printf( "<h1>%s</h1>\n" , line ) major += 1
minor = 0
printf( "<h1><a name=\"H_%d\">%s</h1>" , major , line )
printf( "<!-- index:1:H:%d::%s -->\n" , major , line )
} }
else if( is_sub_heading ) else if( is_sub_heading )
{ {
printf( "<h2>%s</h2>\n" , line ) minor += 1
printf( "<h2><a name=\"SH_%d_%d\">%s</h2>" , major , minor , line )
printf( "<!-- index:2:SH:%d:%d:%s -->\n" , major , minor , line )
} }
else if( !is_heading_line && !is_sub_heading_line ) else if( !is_heading_line && !is_sub_heading_line )
{ {
@ -157,32 +234,57 @@ function process( line , next_ )
END { END {
process( previous , "" ) process( previous , "" )
printf( "</body>\n" ) if( full )
printf( "</html>\n" ) {
printf( "</body>\n" )
printf( "</html>\n" )
}
} ' } '
} }
# == # ===
# AugmentLists()
#
# Adds list begin/end tags around a set of list items.
#
AugmentLists() AugmentLists()
{ {
${awk} -v item_tag="${1}" -v list_tag="${2}" ' ${awk} -v item_tag="${1}" -v list_tag="${2}" -v ignore_1_re="${3}" -v ignore_2_re="${4}" '
{ {
line = $0 line = $0
is_list_item = match( line , "^<" item_tag ">.*</" item_tag ">$" ) ignore_1 = length(ignore_1_re) && match( line , ignore_1_re )
ignore_2 = length(ignore_2_re) && match( line , ignore_2_re )
ignore = ignore_1 || ignore_2
if( ignore )
{
print
}
else
{
is_list_item = match( line , "^<" item_tag ">.*</" item_tag ">$" )
if( is_list_item && !in_list ) if( is_list_item && !in_list )
printf( "<%s>\n" , list_tag ) printf( "<%s>\n" , list_tag )
else if( in_list && !is_list_item ) else if( in_list && !is_list_item )
printf( "</%s>\n" , list_tag ) printf( "</%s>\n" , list_tag )
print print
in_list = is_list_item in_list = is_list_item
}
} ' } '
} }
# == # ===
# Elide()
#
# Converts repeated lines of <foo>lineN</foo> into
# <foo>
# line1
# line2
# </foo>
#
# Useful for <pre> and <sub>.
#
Elide() Elide()
{ {
${awk} -v tag="${1}" ' ${awk} -v tag="${1}" '
@ -205,21 +307,47 @@ ${awk} -v tag="${1}" '
} ' } '
} }
# == # ===
# Decorate()
#
# Adds additional stuff after a given opening tag.
# The tag is expected to be at the start of the line.
#
Decorate()
{
${awk} -v tag="${1}" -v decoration="${2}" '
{
line = $0
sub( "^<" tag ">" , "<" tag ">" decoration , line )
print line
} '
}
# ===
# Compress()
#
# Removes blank lines near to headings (etc).
#
Compress() Compress()
{ {
${awk} ' ${awk} '
function process( previous , line , next_ ) function process( previous , line , next_ )
{ {
re_blank = "^<p><br>$" re_blank = "^<br><br>$"
re_heading = "^<[Hh][[:digit:]]>" re_heading = "^<[Hh][[:digit:]]>"
re_dd = "^<dd>"
re_pre_start = "^<pre>" re_pre_start = "^<pre>"
re_pre_end = "</pre>$" re_pre_end = "</pre>$"
if( match(line,re_blank) && ( match(next_,re_heading) || match(previous,re_heading) ) )
this_is_blank = match(line,re_blank)
next_is_special = match(next_,re_heading) || match(next_,re_dd)
previous_is_special = match(previous,re_heading) || match(previous,re_dd)
next_is_pre_start = match(next_,re_pre_start)
if( this_is_blank && ( next_is_special || previous_is_special ) )
{ {
} }
else if( match(line,re_blank) && match(next_,re_pre_start) ) else if( this_is_blank && next_is_pre_start )
{ {
} }
else else
@ -240,7 +368,84 @@ END {
' '
} }
# ===
# Anchorise_1()
#
# Converts [[-foo-bar-]] to <a href="foo">bar</a>.
#
Anchorise_1()
{
sed 's/\[\[-\([^-]*\)-\([^-]*\)-\]\]/<a href="\1">\2<\/a>/g'
}
# ===
# Anchorise_2()
#
# Converts [[foo]] to <a href="../foo">foo</a>.
#
Anchorise_2()
{
sed 's/\[\[\([^\]*\)\]\]/<a href=..\/"\1">\1<\/a>/g'
}
# ===
# Cat()
#
# An awk version of "cat" (cygwin cat is
# broken).
#
Cat()
{
${awk} '{print}' $@ | tr -d '\015'
}
# ===
# MoveIndex()
#
# Moves the index comments to a line before
# the header, rather than at the end of the
# header line.
#
MoveIndex()
{
${awk} '
{
line = $0
re = "<!-- index:"
pos = match(line,re)
if( pos )
{
head = substr(line,1,pos-1)
tail = substr(line,pos+length(re))
print head
print re tail
}
else
{
print line
}
}
'
}
# == # ==
cat "${file}" | Main "${title}" | Compress | AugmentLists li bl | AugmentLists LI ol | Elide "sub" | Elide "pre" colour="#FFFFFF"
Cat "${file}" | \
Include | \
Main "${title}" "${full}" "${colour}" | \
Compress | \
AugmentLists li ul | \
AugmentLists Li ul "^<lI>" "^<br><br>" | \
AugmentLists lI ul | \
AugmentLists LI ol | \
AugmentLists dt dl "^<dd>" "^<br><br>" | \
Elide "sub" | \
Elide "dd" | \
Elide "pre" | \
Decorate dt "<img src=\"graphics/bullet.gif\">\\\&nbsp;" | \
Decorate dd "<p>" | \
Anchorise_1 | \
Anchorise_2 | \
MoveIndex

View File

@ -33,6 +33,9 @@
/* Version number of package */ /* Version number of package */
#undef VERSION #undef VERSION
/* auto_ptr assignment has non-const rhs */
#undef HAVE_NONCONST_AUTOPTR
/* have reentrant localtime */ /* have reentrant localtime */
#undef HAVE_LOCALTIME_R #undef HAVE_LOCALTIME_R

77
configure vendored
View File

@ -691,7 +691,7 @@ fi
PACKAGE=emailrelay PACKAGE=emailrelay
VERSION=0.9.2 VERSION=0.9.3
if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then
{ echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; } { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; }
@ -2008,8 +2008,78 @@ else
fi fi
done done
ac_ext=C
# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
cross_compiling=$ac_cv_prog_cxx_cross
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 2013 "configure" #line 2020 "configure"
#include "confdefs.h"
#include <memory>
int main() {
std::auto_ptr<int> lhs ; const std::auto_ptr<int> & rhs = lhs ; lhs = rhs ;
; return 0; }
EOF
if { (eval echo configure:2027: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
:
else
echo "configure: failed program was:" >&5
cat conftest.$ac_ext >&5
rm -rf conftest*
cat >> confdefs.h <<\EOF
#define HAVE_NONCONST_AUTOPTR 1
EOF
fi
rm -f conftest*
echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6
echo "configure:2040: checking how to run the C++ preprocessor" >&5
if test -z "$CXXCPP"; then
if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
ac_ext=C
# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
cross_compiling=$ac_cv_prog_cxx_cross
CXXCPP="${CXX-g++} -E"
cat > conftest.$ac_ext <<EOF
#line 2053 "configure"
#include "confdefs.h"
#include <stdlib.h>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2058: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
:
else
echo "$ac_err" >&5
echo "configure: failed program was:" >&5
cat conftest.$ac_ext >&5
rm -rf conftest*
CXXCPP=/lib/cpp
fi
rm -f conftest*
ac_cv_prog_CXXCPP="$CXXCPP"
ac_ext=C
# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
cross_compiling=$ac_cv_prog_cxx_cross
fi
fi
CXXCPP="$ac_cv_prog_CXXCPP"
echo "$ac_t""$CXXCPP" 1>&6
cat > conftest.$ac_ext <<EOF
#line 2083 "configure"
#include "confdefs.h" #include "confdefs.h"
#include <time.h> #include <time.h>
EOF EOF
@ -2024,7 +2094,7 @@ fi
rm -f conftest* rm -f conftest*
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 2028 "configure" #line 2098 "configure"
#include "confdefs.h" #include "confdefs.h"
#include <time.h> #include <time.h>
EOF EOF
@ -2193,6 +2263,7 @@ s%@AR@%$AR%g
s%@HAVE_DOXYGEN@%$HAVE_DOXYGEN%g s%@HAVE_DOXYGEN@%$HAVE_DOXYGEN%g
s%@HAVE_MAN2HTML@%$HAVE_MAN2HTML%g s%@HAVE_MAN2HTML@%$HAVE_MAN2HTML%g
s%@CPP@%$CPP%g s%@CPP@%$CPP%g
s%@CXXCPP@%$CXXCPP%g
CEOF CEOF
EOF EOF

View File

@ -17,10 +17,29 @@ dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
dnl dnl
dnl === dnl ===
dnl
dnl Copyright (C) 2001 Graeme Walker <graeme_walker@users.sourceforge.net>
dnl
dnl This program is free software; you can redistribute it and/or
dnl modify it under the terms of the GNU General Public License
dnl as published by the Free Software Foundation; either
dnl version 2 of the License, or (at your option) any later
dnl version.
dnl
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
dnl
dnl ===
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
AC_INIT(src/main/gsmtp.h) AC_INIT(src/main/gsmtp.h)
AM_INIT_AUTOMAKE(emailrelay,0.9.2) AM_INIT_AUTOMAKE(emailrelay,0.9.3)
AM_CONFIG_HEADER(config.h) AM_CONFIG_HEADER(config.h)
dnl === dnl ===
@ -52,6 +71,8 @@ AC_HEADER_TIME
AC_CHECK_HEADERS(unistd.h) AC_CHECK_HEADERS(unistd.h)
AC_CHECK_HEADERS(sys/time.h) AC_CHECK_HEADERS(sys/time.h)
AC_CHECK_FUNCS(glob) AC_CHECK_FUNCS(glob)
AC_LANG_CPLUSPLUS
AC_TRY_COMPILE([#include <memory>],std::auto_ptr<int> lhs ; const std::auto_ptr<int> & rhs = lhs ; lhs = rhs ;,,AC_DEFINE(HAVE_NONCONST_AUTOPTR,1,auto_ptr assignment has non-const rhs))
dnl check for *_r() declarations -- using ac_check_funcs dnl check for *_r() declarations -- using ac_check_funcs
dnl is no good here since they may be in the library but dnl is no good here since they may be in the library but
dnl not the header (depending on preprocessor switches) dnl not the header (depending on preprocessor switches)

32
doc/Makefile.am Normal file → Executable file
View File

@ -1,32 +1,36 @@
EXTRA_DIST = developer.txt reference.txt userguide.txt index.html emailrelay.1 emailrelay-poke.1 index.html EXTRA_DIST = developer.txt reference.txt userguide.txt index.html emailrelay.1 emailrelay-poke.1 doxygen_header.html graphics/bullet.gif
noinst_SCRIPTS = .dox noinst_SCRIPTS = .dox
pkgdata_DATA = readme.html developer.html reference.html userguide.html man.html index.html pkgdata_DATA = readme.html developer.html reference.html userguide.html man.html index.html
CLEANFILES = $(pkgdata_DATA) $(noinst_SCRIPTS) html CLEANFILES = $(noinst_SCRIPTS) html *.ht readme.html developer.html reference.html userguide.html man.html
SUFFIXES = .txt .html SUFFIXES = .txt .html .ht
filter=$(top_builddir)/bin/emailrelay-filter.sh filter=$(top_builddir)/bin/emailrelay-doxygen-filter.sh
filter_src=$(top_srcdir)/bin/emailrelay-filter.sh_ filter_src=$(top_srcdir)/bin/emailrelay-doxygen-filter.sh_
converter=$(top_builddir)/bin/txt2html.sh converter=$(top_builddir)/bin/txt2html.sh
converter_src=$(top_srcdir)/bin/txt2html.sh_ converter_src=$(top_srcdir)/bin/txt2html.sh_
.txt.html: .txt.html:
$(converter) -a "$(AWK)" $< > $*.html $(converter) -a "$(AWK)" $< > $*.html
.txt.ht:
$(converter) -a "$(AWK)" -x $< > $*.ht
$(filter): $(filter_src)
cp $(filter_src) $(filter)
chmod ugo+x $(filter)
$(converter): $(converter_src)
cp $(converter_src) $(converter)
chmod ugo+x $(converter)
.dox: $(filter) .dox: $(filter)
if test "$(HAVE_DOXYGEN)" = "yes" ; then cat $(top_srcdir)/src/main/doxygen.cfg | sed "s:__TOP_SRC__:$(top_srcdir):g" | sed "s:__TOP_BUILD__:$(top_builddir):g" | doxygen - && touch .dox ; else echo no doxygen ; fi if test "$(HAVE_DOXYGEN)" = "yes" ; then cat $(top_srcdir)/src/main/doxygen.cfg | sed "s:__TOP_SRC__:$(top_srcdir):g" | sed "s:__TOP_BUILD__:$(top_builddir):g" | doxygen - && touch .dox ; else echo no doxygen ; fi
man.html: emailrelay.1 man.html: emailrelay.1
if test "$(HAVE_MAN2HTML)" = "yes" ; then man2html emailrelay.1 > man.html ; else echo no man2html ; fi if test "$(HAVE_MAN2HTML)" = "yes" ; then man2html emailrelay.1 > man.html ; else echo no man2html ; fi
$(filter): $(filter_src)
cp $< $(filter)
chmod ugo+x $(filter)
$(converter): $(converter_src)
cp $< $(converter)
chmod ugo+x $(converter)
developer.html reference.html userguide.html: $(converter) developer.html reference.html userguide.html: $(converter)
readme.html: $(top_srcdir)/README $(converter) readme.html: $(top_srcdir)/README $(converter)
@ -36,6 +40,8 @@ install-data-local:
$(mkinstalldirs) $(destdir)$(mandir)/man1 $(mkinstalldirs) $(destdir)$(mandir)/man1
$(INSTALL) $(top_srcdir)/doc/emailrelay.1 $(destdir)$(mandir)/man1/emailrelay.1 $(INSTALL) $(top_srcdir)/doc/emailrelay.1 $(destdir)$(mandir)/man1/emailrelay.1
$(INSTALL) $(top_srcdir)/doc/emailrelay-poke.1 $(destdir)$(mandir)/man1/emailrelay-poke.1 $(INSTALL) $(top_srcdir)/doc/emailrelay-poke.1 $(destdir)$(mandir)/man1/emailrelay-poke.1
$(mkinstalldirs) $(destdir)$(pkgdatadir)/graphics
$(INSTALL) $(top_srcdir)/doc/graphics/bullet.gif $(destdir)$(pkgdatadir)/graphics/bullet.gif
$(mkinstalldirs) $(destdir)$(pkgdatadir)/html $(mkinstalldirs) $(destdir)$(pkgdatadir)/html
if test "$(HAVE_DOXYGEN)" = "yes" ; then for file in html/* ; do $(INSTALL) $$file $(destdir)$(pkgdatadir)/$$file ; done ; fi if test "$(HAVE_DOXYGEN)" = "yes" ; then for file in html/* ; do $(INSTALL) $$file $(destdir)$(pkgdatadir)/$$file ; done ; fi

View File

@ -69,15 +69,16 @@ PACKAGE = @PACKAGE@
RANLIB = @RANLIB@ RANLIB = @RANLIB@
VERSION = @VERSION@ VERSION = @VERSION@
EXTRA_DIST = developer.txt reference.txt userguide.txt index.html emailrelay.1 emailrelay-poke.1 index.html EXTRA_DIST = developer.txt reference.txt userguide.txt index.html emailrelay.1 emailrelay-poke.1 doxygen_header.html graphics/bullet.gif
noinst_SCRIPTS = .dox noinst_SCRIPTS = .dox
pkgdata_DATA = readme.html developer.html reference.html userguide.html man.html index.html pkgdata_DATA = readme.html developer.html reference.html userguide.html man.html index.html
CLEANFILES = $(pkgdata_DATA) $(noinst_SCRIPTS) html CLEANFILES = $(noinst_SCRIPTS) html *.ht readme.html developer.html reference.html userguide.html man.html
SUFFIXES = .txt .html SUFFIXES = .txt .html .ht
filter = $(top_builddir)/bin/emailrelay-filter.sh filter = $(top_builddir)/bin/emailrelay-doxygen-filter.sh
filter_src = $(top_srcdir)/bin/emailrelay-filter.sh_ filter_src = $(top_srcdir)/bin/emailrelay-doxygen-filter.sh_
converter = $(top_builddir)/bin/txt2html.sh converter = $(top_builddir)/bin/txt2html.sh
converter_src = $(top_srcdir)/bin/txt2html.sh_ converter_src = $(top_srcdir)/bin/txt2html.sh_
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
@ -100,7 +101,7 @@ TAR = tar
GZIP_ENV = --best GZIP_ENV = --best
all: all-redirect all: all-redirect
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .html .txt .SUFFIXES: .ht .html .txt
$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) $(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps doc/Makefile cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps doc/Makefile
@ -136,6 +137,7 @@ distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
subdir = doc subdir = doc
distdir: $(DISTFILES) distdir: $(DISTFILES)
$(mkinstalldirs) $(distdir)/graphics
@for file in $(DISTFILES); do \ @for file in $(DISTFILES); do \
d=$(srcdir); \ d=$(srcdir); \
if test -d $$d/$$file; then \ if test -d $$d/$$file; then \
@ -213,20 +215,23 @@ maintainer-clean
.txt.html: .txt.html:
$(converter) -a "$(AWK)" $< > $*.html $(converter) -a "$(AWK)" $< > $*.html
.txt.ht:
$(converter) -a "$(AWK)" -x $< > $*.ht
$(filter): $(filter_src)
cp $(filter_src) $(filter)
chmod ugo+x $(filter)
$(converter): $(converter_src)
cp $(converter_src) $(converter)
chmod ugo+x $(converter)
.dox: $(filter) .dox: $(filter)
if test "$(HAVE_DOXYGEN)" = "yes" ; then cat $(top_srcdir)/src/main/doxygen.cfg | sed "s:__TOP_SRC__:$(top_srcdir):g" | sed "s:__TOP_BUILD__:$(top_builddir):g" | doxygen - && touch .dox ; else echo no doxygen ; fi if test "$(HAVE_DOXYGEN)" = "yes" ; then cat $(top_srcdir)/src/main/doxygen.cfg | sed "s:__TOP_SRC__:$(top_srcdir):g" | sed "s:__TOP_BUILD__:$(top_builddir):g" | doxygen - && touch .dox ; else echo no doxygen ; fi
man.html: emailrelay.1 man.html: emailrelay.1
if test "$(HAVE_MAN2HTML)" = "yes" ; then man2html emailrelay.1 > man.html ; else echo no man2html ; fi if test "$(HAVE_MAN2HTML)" = "yes" ; then man2html emailrelay.1 > man.html ; else echo no man2html ; fi
$(filter): $(filter_src)
cp $< $(filter)
chmod ugo+x $(filter)
$(converter): $(converter_src)
cp $< $(converter)
chmod ugo+x $(converter)
developer.html reference.html userguide.html: $(converter) developer.html reference.html userguide.html: $(converter)
readme.html: $(top_srcdir)/README $(converter) readme.html: $(top_srcdir)/README $(converter)
@ -236,6 +241,8 @@ install-data-local:
$(mkinstalldirs) $(destdir)$(mandir)/man1 $(mkinstalldirs) $(destdir)$(mandir)/man1
$(INSTALL) $(top_srcdir)/doc/emailrelay.1 $(destdir)$(mandir)/man1/emailrelay.1 $(INSTALL) $(top_srcdir)/doc/emailrelay.1 $(destdir)$(mandir)/man1/emailrelay.1
$(INSTALL) $(top_srcdir)/doc/emailrelay-poke.1 $(destdir)$(mandir)/man1/emailrelay-poke.1 $(INSTALL) $(top_srcdir)/doc/emailrelay-poke.1 $(destdir)$(mandir)/man1/emailrelay-poke.1
$(mkinstalldirs) $(destdir)$(pkgdatadir)/graphics
$(INSTALL) $(top_srcdir)/doc/graphics/bullet.gif $(destdir)$(pkgdatadir)/graphics/bullet.gif
$(mkinstalldirs) $(destdir)$(pkgdatadir)/html $(mkinstalldirs) $(destdir)$(pkgdatadir)/html
if test "$(HAVE_DOXYGEN)" = "yes" ; then for file in html/* ; do $(INSTALL) $$file $(destdir)$(pkgdatadir)/$$file ; done ; fi if test "$(HAVE_DOXYGEN)" = "yes" ; then for file in html/* ; do $(INSTALL) $$file $(destdir)$(pkgdatadir)/$$file ; done ; fi

View File

@ -1,5 +1,5 @@
E-MailRelay Developer reference E-MailRelay Developer guide
=============================== ===========================
Module structure Module structure
---------------- ----------------
@ -10,18 +10,73 @@ network classes using the Berkley socket and Winsock APIs. Both libraries
are portable between POSIX-like systems (eg. Linux) and Windows. are portable between POSIX-like systems (eg. Linux) and Windows.
The application-level classes are implemented within the "GSmtp" namespace. The application-level classes are implemented within the "GSmtp" namespace.
The key classes in this namespace are "ClientProtocol", "ServerProtocol" and The key interfaces in this namespace are "ClientProtocol", "ServerProtocol" and
"MessageStore". The protocol and message-store functionality is brought "MessageStore".
together by the high-level "GSmtp::Server" and "GSmtp::Client" classes.
Under Windows the "gnet" library needs to create hidden GUI windows in Under Windows there is an additional library for event handling. Windows has
order to receive network events. (Windows has historically built network historically built network event processing on top of the GUI event system,
event processing on top of the GUI event system, rather then implementing rather then implementing network events using a generic event notification
both GUI and network event handling on top of a generic event notification system as POSIX systems do. This means that the "gnet" library has to be able
system as POSIX systems do.) The Windows GUI and event classes are put into to create GUI windows in order to process network events. The extra GUI and
a separate "src/win32" directory. event classes are put into a separate library in the "src/win32" directory,
using the namespace "GGui".
For a quick tour of the code look at the following headers: Class structure overview
------------------------
The message-store functionality uses three abstract interfaces: "MessageStore",
"NewMessage" and "StoredMessage". The "NewMessage" interface is used to create
messages within the store, and the "StoredMessage" interface is used for
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
interface are supplied: one for normal spooling ("ProtocolMessageStore"), and
another for immediate forwarding ("ProtocolMessageForward").
The protocol and message-store functionality are brought together by the
high-level "GSmtp::Server" and "GSmtp::Client" classes.
Directory structure
-------------------
# src
Parent directory for source code.
# src/glib
A low-level class library, including classes for file-system abstraction,
date and time, string utility functions, logging, command line parsing etc.
# src/gnet
A network library using Berkley sockets or Winsock.
# src/win32
Additional classes for windows event processing.
# src/main
Application-level classes for E-MailRelay.
# lib
Parent directory for ANSI C++ fixes
# lib/gcc2.95
Standard headers which are missing in gcc2.95
# lib/msvc6.0
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/gpath.h
* src/glib/gstr.h * src/glib/gstr.h
* lib/gcc2.95/sstream * lib/gcc2.95/sstream
@ -33,43 +88,6 @@ For a quick tour of the code look at the following headers:
* src/main/gserverprotocol.h * src/main/gserverprotocol.h
* src/main/gsmtpserver.h * src/main/gsmtpserver.h
Directory structure
-------------------
* src
Parent directory for source code.
* src/glib
A low-level class library, including classes for file-system abstraction,
date and time, string utility functions, logging, command line parsing etc.
* src/gnet
A network library using Berkley sockets or Winsock.
* src/win32
Additional classes for windows event processing.
* src/main
Application-level classes for E-MailRelay.
* lib
Parent directory for ANSI C++ fixes
* lib/gcc2.95
Standard headers which are missing in gcc2.95
* lib/msvc6.0
Standard headers which are missing (or broken) in msvc6.0
Portability Portability
----------- -----------
The E-MailRelay code is written in ANSI C++, using the following The E-MailRelay code is written in ANSI C++, using the following
@ -89,39 +107,39 @@ but not:
* covariant return * covariant return
* "mutable" * "mutable"
The header files "gdef.h" in "src/glib", and "gnet.h" in "src/gnet" are The header files "gdef.h" in "src/glib", and "gnet.h" in "src/gnet" are intended
intended to be used to fix up portability issues such as missing standard to be used to fix up portability issues such as missing standard types,
types, non-standard system headers etc. Conditional compilation is not non-standard system headers etc. Conditional compilation is not used outside
used outside of these headers (with the odd exception). of these headers (with the odd exception).
Deficiencies in the ANSI C++ headers files provided by the compiler Deficiencies in the ANSI C++ headers files provided by the compiler are fixed up
are fixed up in the "lib" directory tree. For example, the msvc6.0 in the "lib" directory tree. For example, the msvc6.0 compiler sometimes does
compiler sometimes does not put its names into the "std" namespace, not put its names into the "std" namespace, even though the std-namespace
even though the std-namespace headers are used. This can be worked round headers are used. This can be worked round by additional "using" declarations
by additional "using" declarations in the "lib/msvc6.0" headers. in the "lib/msvc6.0" headers. These work-rounds are kept out of the "src" tree
These work-rounds are kept out of the "src" tree because they are likely because they are likely to be fixed in later compiler releases.
to be fixed in later compiler releases. Standards-compliant compilers Standards-compliant compilers should not need to include any headers from the
should not need to include any headers from the "lib" directory tree. "lib" directory tree.
Windows/unix portability is generally addressed by providing a common Windows/unix portability is generally addressed by providing a common class
class declaration with two implementations. Where necessary a pimple declaration with two implementations. Where necessary a "pimple" pattern is used
pattern is used to hide the system-specific parts of the declaration. to hide the system-specific parts of the declaration.
A good example is the "GDirectory" class used for iterating through files A good example is the "G::Directory" class used for iterating through files in
in a directory. The header file "src/glib/gdirectory.h" is common to both a directory. The header file "src/glib/gdirectory.h" is common to both systems,
systems, but two implementations are provided in "gdirectory_unix.cpp" but two implementations are provided in "gdirectory_unix.cpp" and
and "gdirectory_win32.cpp". The unix implementation uses opendir() and "gdirectory_win32.cpp". The unix implementation uses opendir() and glob(),
glob(), while the windows implementation uses FindFirstFile(). while the windows implementation uses FindFirstFile().
Sometimes only parts of the implementation are system-specific. In these Sometimes only small parts of the implementation are system-specific. In
cases there are three source files per header. For example, "gsocket.cpp", these cases there are three source files per header. For example, "gsocket.cpp",
"gsocket_win32.cpp" and "gsocket_unix.cpp" in the "src/gnet" directory. "gsocket_win32.cpp" and "gsocket_unix.cpp" in the "src/gnet" directory.
Porting Porting to other compilers
------- --------------------------
If trying a port using a good ANSI C++ compiler then start by removing If trying a port using a good ANSI C++ compiler then start by removing files
files from the "lib/gcc2.95" directory (or edit makefiles to remove the from the "lib/gcc2.95" directory (or edit makefiles to remove the include path),
include path), and then review the following header files: "src/glib/gdef.h", and then review the following header files: "src/glib/gdef.h",
"src/gnet/gnet.h", "src/glib/gmemory.h". "src/gnet/gnet.h", "src/glib/gmemory.h".
The unix (ie. POSIX-like) implementation of the directory iteration class The unix (ie. POSIX-like) implementation of the directory iteration class
@ -132,29 +150,89 @@ moved into the message store class.
IPv6 IPv6
---- ----
IPv6 is supported at compile-time by selecting source files in the IPv6 is supported at compile-time by selecting source files in the "src/gnet"
"src/gnet" directory ending "_ipv6.cpp" rather than "_ipv4.cpp". directory ending "_ipv6.cpp" rather than "_ipv4.cpp". The code has been tested
The code should be regarded as experimental. to a limited extent on Linux.
Doxygen
-------
The commenting style used in header files is compatible with Doxygen
if passed through the simple awk-based preprocessor "emailrelay-filter.sh".
A "make" in the "doc" directory will run Doxygen if it is found on
your path.
Windows build Windows build
------------- -------------
A simple project file "emailrelay.dsp" for msvc6.0 is provided in the A simple project file "emailrelay.dsp" for msvc6.0 is provided in the "src/main"
"src/main" directory. directory.
Style Style
----- -----
Tabs are used for indenting, not multiple spaces, but only at the left The commenting style used in header files is compatible with doxygen if passed
hand edge, and never within a line. This allows the reader to choose through the simple awk-based preprocessor "emailrelay-doxygen-filter.sh". A
how deep the indenting should be (appropriate to their window size) by "make" in the "doc" directory will run doxygen if it is found on your path.
setting the editor's tabstop. Using spaces does not allow the reader this
freedom. Patterns
--------
Gang-of-four Design Patterns (ISBN 0-201-63361-2):
+ Factory method
- GNet::EventSources::create()
- GNet::Server::newPeer()
- GSmtp::MessageStore::newMessage()
+ Iterator
- G::DirectoryIterator
- GNet::EventHandlerList::begin()/end()
- GSmtp::MessageStore::iterator()
+ Singleton
- G::LogOutput
- GGui::ApplicationInstance
- GNet::EventSources
- GSmtp::MessageStore
+ Facade
- G::File
- GNet::Address
+ Adapter/Mediator
- GSmtp::ProtocolMessage
Lakos' Large Scale C++ Software Design patterns (ISBN 0-201-63362-0):
+ Insulation; fully insulating concrete class (Meyer's Effective C++ Item 34, pimple pattern)
- G::DirectoryIterator
- GNet::Address
- GNet::Resolver
- GSmtp::ProtocolMessage
+ Insulation; protocol class
- GNet::EventHandler
- GSmtp::NewMessage
- GSmtp::StoredMessage
- GSmtp::ProtocolMessage
- GSmtp::ServerProtocol::Sender
- GSmtp::ClientProtocol::Sender
- GSmtp::ClientProtocol::Callback
- GSmtp::ProtocolMessage::Callback
- GSmtp::Client::ClientCallback
Meyer's More Effective C++ patterns (ISBN 0-201-63371-X):
+ Reference counting (Item 29)
- GSmtp::MessageStore::Iterator
+ Lazy evaluation (Item 17)
- GNet::EventHandlerList::list()
Other patterns:
+ Finite state machine
- GSmtp::ServerProtocol

6
doc/doxygen_header.html Normal file
View File

@ -0,0 +1,6 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
<title>E-MailRelay: $title</title>
<meta name="robots" content="noindex,nofollow">
<link href="doxygen.css" rel="stylesheet" type="text/css">
</head><body bgcolor="#ffffff">

View File

@ -34,7 +34,7 @@ emailrelay \- e-mail transfer agent
is an simple e-mail message transfer agent. It is intended to be used is an simple e-mail message transfer agent. It is intended to be used
on stand-alone machines which have a dial-up connection to an ISP. on stand-alone machines which have a dial-up connection to an ISP.
.LP .LP
It runs in two modes: a storage deamon It runs in two main modes: a storage deamon
.RI ( --as-server ) .RI ( --as-server )
and a forwarding and a forwarding
agent agent
@ -43,8 +43,16 @@ The storage daemon is an SMTP server which stores e-mail
messages in a local spool directory. The forwarding agent acts as an messages in a local spool directory. The forwarding agent acts as an
SMTP client, which passes the spooled e-mail messages on to an ISP's SMTP SMTP client, which passes the spooled e-mail messages on to an ISP's SMTP
server. server.
.LP
It can also run in a third mode, as a proxy server
.RI ( --as-proxy ).
In this mode all messages are forwarded immediately to the downstream
server.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-V,--version
Displays version information and exits.
.TP
.B \-a,--admin \fIadmin-port\fR .B \-a,--admin \fIadmin-port\fR
Enables the administration interface and specifies its listening port number. Enables the administration interface and specifies its listening port number.
.TP .TP
@ -66,6 +74,9 @@ Records the daemon process-id in the given file.
.B \-l,--log .B \-l,--log
Writes log information on standard error (if open) and syslog (if not disabled). Writes log information on standard error (if open) and syslog (if not disabled).
.TP .TP
.B \-m,--immediate
Forwards each message as soon as it is received (requires \fI--forward-to\fR).
.TP
.B \-n,--no-syslog .B \-n,--no-syslog
Disables syslog output. Disables syslog output.
.TP .TP
@ -93,8 +104,11 @@ Generates more verbose logging (if compiled-in and logging enabled and stderr op
.B \-x,--dont-serve .B \-x,--dont-serve
Stops the process acting as a server (usually used with \fI--forward\fR). Stops the process acting as a server (usually used with \fI--forward\fR).
.TP .TP
.B \-V,--version .B \-y,--as-proxy \fIhost:port\fR
Displays version information and exits. Equivalent to \fI--close-stderr\fR \fI--log\fR \fI--immediate\fR \fI--forward-to\fR.
.TP
.B \-z,--filter \fIprogram\fR
Defines a mail preprocessor (disallowed if running as root).
.SH "DIAGNOSTICS" .SH "DIAGNOSTICS"
If the If the
.IR --log , .IR --log ,
@ -119,7 +133,7 @@ Failed e-mail messages are kept in the spool directory and given
a a
.I .bad .I .bad
filename suffix. The failure reason is usually recorded within the filename suffix. The failure reason is usually recorded within the
envelope file iteself. envelope file itself.
.SH FILES .SH FILES
.IP \(bu 2 .IP \(bu 2
/usr/local/sbin/emailrelay /usr/local/sbin/emailrelay
@ -136,6 +150,8 @@ envelope file iteself.
.IP \(bu 2 .IP \(bu 2
/usr/local/share/emailrelay/emailrelay-deliver.sh /usr/local/share/emailrelay/emailrelay-deliver.sh
.IP \(bu 2 .IP \(bu 2
/usr/local/share/emailrelay/emailrelay-filter.sh
.IP \(bu 2
/usr/local/man/man1/emailrelay.1 /usr/local/man/man1/emailrelay.1
.IP \(bu 2 .IP \(bu 2
/usr/local/man/man1/emailrelay-poke.1 /usr/local/man/man1/emailrelay-poke.1
@ -151,6 +167,5 @@ E-MailRelay reference
.BR syslog (3), .BR syslog (3),
.BR pppd (8), .BR pppd (8),
.BR init.d (7) .BR init.d (7)
.BR emailrelay-poke (1),
.SH AUTHOR .SH AUTHOR
Graeme Walker, mailto:graeme_walker@users.sourceforge.net Graeme Walker, mailto:graeme_walker@users.sourceforge.net

BIN
doc/graphics/bullet.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

13
doc/index.html Normal file → Executable file
View File

@ -4,12 +4,13 @@
<body> <body>
<h1>E-MailRelay Documentation</h1> <h1>E-MailRelay Documentation</h1>
<bl> <bl>
<li><a href=readme.html>Readme</a></li> <li><a href="readme.html">Readme</a></li>
<li><a href=userguide.html>User guide</a></li> <li><a href="userguide.html">User guide</a></li>
<li><a href=reference.html>Reference manual</a></li> <li><a href="reference.html">Reference manual</a></li>
<li><a href=developer.html>Notes for developers</a></li> <li><a href="developer.html">Notes for developers</a></li>
<li><a href=html/index.html>Source code documentation</a> (generated by <a href=http://www.doxygen.org>doxygen</a>)</li> <li><a href="html/index.html">Source code documentation</a> (generated by <a href="http://www.doxygen.org">doxygen</a>, if available)</li>
<li><a href=man.html>Man page</a> (generated by man2html)</li> <li><a href="man.html">Man page</a> (generated by man2html, if available)</li>
<li><a href="http://emailrelay.sourceforge.net">Web site</a></li>
</bl> </bl>
</body> </body>
</html> </html>

View File

@ -1,70 +1,79 @@
E-MailRelay Reference Manual E-MailRelay Reference
============================ =====================
Document Introduction
-------- ------------
This is the E-MailRelay reference guide. It contains material which is supplementary This is the E-MailRelay reference guide. It contains material which is
to the user guide. supplementary to the user guide.
Usage Command line usage
----- ------------------
The "emailrelay" program supports the following command-line usage: The "emailrelay" program supports the following command-line usage:
emailrelay [<switch> [<switch> ...]] emailrelay [<switch> [<switch> ...]]
where <switch> is: where <switch> is:
* --version (-V) # --version (-V)
Displays version information and exits. Displays version information and exits.
* --admin (-a) # --admin (-a)
Enables the administration interface and specifies its listening port number. Enables the administration interface and specifies its listening port number.
* --as-server (-d) # --as-server (-d)
Equivalent to "--close-stderr --log". Equivalent to "--close-stderr --log".
* --close-stderr (-e) # --close-stderr (-e)
Closes the standard error stream when daemonising. Closes the standard error stream when daemonising.
* --forward (-f) # --forward (-f)
Forwards stored mail on startup (requires --forward-to). Forwards stored mail on startup (requires --forward-to).
* --help (-h) # --help (-h)
Displays help text and exits. Displays help text and exits.
* --pid-file (-i) # --pid-file (-i)
Records the daemon process-id in the given file. Records the daemon process-id in the given file.
* --log (-l) # --log (-l)
Writes log information on standard error (if open) and syslog (if not disabled). Writes log information on standard error (if open) and syslog (if not disabled).
* --no-syslog (-n) # --immediate (-m)
Forwards each message as soon as it is received (requires --forward-to).
# --no-syslog (-n)
Disables syslog output. Disables syslog output.
* --forward-to (-o) # --forward-to (-o)
Specifies the remote smtp server (required by --forward and --admin). Specifies the remote smtp server (required by --forward and --admin).
* --port (-p) # --port (-p)
Specifies the smtp listening port number. Specifies the smtp listening port number.
* --as-client (-q) # --as-client (-q)
Equivalent to "--no-syslog --no-daemon --log --dont-serve --forward --forward-to". Equivalent to "--no-syslog --no-daemon --log --dont-serve --forward --forward-to".
* --remote-clients (-r) # --remote-clients (-r)
Allows remote clients to connect. Allows remote clients to connect.
* --spool-dir (-s) # --spool-dir (-s)
Specifies the spool directory (default is "/usr/local/var/spool/emailrelay"). Specifies the spool directory (default is "/usr/local/var/spool/emailrelay").
* --no-daemon (-t) # --no-daemon (-t)
Does not detach from the terminal. Does not detach from the terminal.
* --verbose (-v) # --verbose (-v)
Generates more verbose logging (if compiled-in and logging enabled and stderr open). Generates more verbose logging (if compiled-in and logging enabled and stderr open).
* --dont-serve (-x) # --dont-serve (-x)
Stops the process acting as a server (usually used with --forward). Stops the process acting as a server (usually used with --forward).
# --as-proxy (-y)
Equivalent to "--close-stderr --log --immediate --forward-to".
# --filter (-z)
Defines a mail pre-processor (disallowed if running as root).
If no command-line switches are supplied at all then the default If no command-line switches are supplied at all then the default
behaviour is: behaviour is:
* to run as a daemon, detached from the terminal * to run as a daemon, detached from the terminal
@ -85,6 +94,11 @@ is provided to run the program...
The "--as-server" switch makes sure that logging is enabled and that The "--as-server" switch makes sure that logging is enabled and that
the standard error stream is closed. the standard error stream is closed.
Note that the test for allowing remote clients only takes account of the local
machine's canonical IP address: the connection is regarded as local if the
remote IP address matches the IP address of the local machine's canonical
hostname (ie. not any DNS aliases).
Message store Message store
------------- -------------
Mail messages are stored as text files in the configured spool directory. Each Mail messages are stored as text files in the configured spool directory. Each
@ -113,37 +127,37 @@ and the failure reason is written into the file.
SMTP issues SMTP issues
----------- -----------
Local delivery: # Local delivery:
E-MailRelay will reject all local recipients, with the exception of "postmaster". E-MailRelay will reject all local recipients, with the exception of
This is in line with its intended purpose as a simple mail relay, rather than "postmaster". This is in line with its intended purpose as a simple mail
a fully-fledged routing MTA. Any addressee (except "postmaster") without an "at" relay, rather than a fully-fledged routing MTA. Any addressee (except
sign (@) will be rejected at the time the message is submitted by the e-mail "postmaster") without an "at" sign (@) will be rejected at the time the
front-end. message is submitted by the e-mail front-end.
Delivery of mail to a local "postmaster" is a feature of E-MailRelay which is Delivery of mail to a local "postmaster" is a feature of E-MailRelay which is
provided for completeness and for comformance to the SMTP specification. It is provided for completeness and for comformance to the SMTP specification. It is
only relevant if you are in the habit of sending mail to yourself as "postmaster"; only relevant if you are in the habit of sending mail to yourself as
mail to "postmaster" from an external source is not processed by E-MailRelay "postmaster"; mail to "postmaster" from an external source is not processed
and should be delivered normally. by E-MailRelay and should be delivered normally.
Note that E-MailRelay daemon does not actually deliver mail to the postmaster Note that E-MailRelay daemon does not actually deliver mail to the postmaster
mailbox. All it does is create an envelope and content file in the spool directory mailbox. All it does is create an envelope and content file in the spool
with a ".local" suffix. Some external system, such as a shell script run directory with a ".local" suffix. Some external system, such as a shell
from cron calling "procmail", should be used to process the ".local" files. script run from cron calling "procmail", should be used to process the
An example script is provided. ".local" files. An example script is provided.
Timeouts: # Timeouts:
Client-side timeouts are not implemented. If the ISP server is very slow then Client-side timeouts are not implemented. If the ISP server is very slow then
in a typical setup the dial-up line will be dropped due to inactivity. This in a typical setup the dial-up line will be dropped due to inactivity. This
will have the desired effect of aborting the message submission. will have the desired effect of aborting the message submission.
Message loops: # Message loops:
Message loops are not detected. Message loops are not detected.
Eight bit messages: # Eight bit messages:
The 8BITMIME SMTP extension is supported, however no attempt is made to The 8BITMIME SMTP extension is supported, however no attempt is made to
re-encode 8-bit messages into 7-bit messages if the downstream server re-encode 8-bit messages into 7-bit messages if the downstream server
@ -151,39 +165,76 @@ Eight bit messages:
Administration interface Administration interface
------------------------ ------------------------
If enabled, the server will provide a network interface for performing administration If enabled, the server will provide a network interface for performing
tasks. This is a simple command-line interface which is compatible with telnet. administration tasks. This is a simple command-line interface which is
compatible with "telnet".
Currently the only supported command is "flush", which tries to forward spooled mail Currently the only supported command is "flush", which tries to forward spooled
to the configured dowstream SMTP server. The downstream server address must have been mail to the configured dowstream SMTP server. The downstream server address
defined on the "emailrelay" command line at start-up using the "--forward-to" switch; must have been defined on the "emailrelay" command line at start-up using the
it cannot be specified through the administration interface. "--forward-to" switch; it cannot be specified through the administration
interface.
Mail pre-processing
-------------------
The "--filter" command-line switch allows you to specify a pre-processor program
which operates on mail messages as they pass through the E-MailRelay system. The
pre-processor program is run as soon as the mail message has been stored in the
spool directory, with the full path of the content file specified on the command
line.
This can be combined with the "--immediate" (or "--as-proxy") switch to implement
personal mail pre-processing, without replacing or reconfiguring the system's
default MTA.
For example, the following command will start a proxy server on port 10025
which pre-processes mail using the specified filter program, and then
forwards the mail on to the system's default MTA (on port 25):
emailrelay --as-proxy localhost:smtp --port 10025 --no-syslog \
--filter ${HOME}/.emailrelay/filter \
--spool-dir ${HOME}/.emailrelay/spool
Security issues Security issues
--------------- ---------------
E-MailRelay runs with a umask of 177. It does not call exec() or system(). It does not A major security concern is the use of an external mail pre-processor (using the
change its effective userid. No configuration parameters can be changed through the --filter switch). In this release this feature is simply disabled if the process
administrative interface. By default connections to the SMTP and administrative ports is running as root (effective userid is zero). The pre-processor will run as the
will be rejected if they come from remote machines. same userid as the E-MailRelay program, but with an almost empty set of
environment variables, and no open file descriptors other than
"stdin"/"stdout"/"stderr" open onto "/dev/null". The pre-processor filename has
to be configured using a full path, so there is no dependence on the current
working directory or the PATH variable.
Some other points are:
* The program runs with a "umask" of 177 so files are created with "-rw-------" permissions.
* Strings are dynamically allocated, so buffer overflow/truncation issues are avoided.
* By default connections to the SMTP and administrative ports will be rejected if they come from remote machines.
* No configuration parameters can be changed through the administrative interface.
* No exec(), system() or popen() calls are used other than execve() to spawn the mail pre-processor.
Files Files
----- -----
By default "make install" installs the following files: By default "make install" installs files in the following locations:
* /usr/local/sbin/emailrelay * /usr/local/sbin/emailrelay
* /usr/local/libexec/emailrelay-poke * /usr/local/libexec/emailrelay-poke
* /usr/local/libexec/emailrelay.sh * /usr/local/libexec/emailrelay.sh
* /usr/local/var/spool/emailrelay/empty_file * /usr/local/var/spool/emailrelay/empty_file
* /usr/local/share/emailrelay/emailrelay-notify.sh * /usr/local/share/emailrelay/emailrelay-notify.sh
* /usr/local/share/emailrelay/emailrelay-deliver.sh * /usr/local/share/emailrelay/emailrelay-deliver.sh
* /usr/local/share/emailrelay/emailrelay-process.sh
* /usr/local/share/emailrelay/*.html
This directory structure is constrained by the autoconf and GNU standards. Preferred This directory structure is constrained by the "autoconf" and GNU standards.
locations would be something like this: Preferred locations for a GNU/Linux distribution would be something like this:
* /usr/sbin/emailrelay * /usr/sbin/emailrelay
* /opt/emailrelay/bin/emailrelay-poke * /opt/emailrelay/bin/emailrelay-poke
* /sbin/init.d/emailrelay.sh * /sbin/init.d/emailrelay.sh
* /var/spool/emailrelay/ * /var/spool/emailrelay/
* /opt/emailrelay/examples/emailrelay-notify.sh * /opt/emailrelay/examples/emailrelay-notify.sh
* /opt/emailrelay/examples/emailrelay-deliver.sh * /opt/emailrelay/examples/emailrelay-deliver.sh
* /opt/emailrelay/examples/emailrelay-process.sh
* /usr/share/doc/packages/emailrelay/*.html

View File

@ -1,47 +1,49 @@
E-MailRelay User Guide E-MailRelay User Guide
====================== ======================
Document
--------
This document is the user guide for E-MailRelay V0.9.1.
What is it? What is it?
----------- -----------
E-MailRelay is a simple e-mail store-and-forward transfer agent. It's a program E-MailRelay is a simple store-and-forward e-mail transfer agent. It's a program
which runs in the background and accepts e-mail front-ends (KMail, Mutt, Netscape etc.), which runs in the background and accepts e-mail front-ends (KMail, Outlook,
stores the messages on the hard disk, and when next connected to the Internet forwards Netscape etc.), stores the messages on the hard disk, and when next connected
them to a downstream SMTP server for onward delivery. to the Internet forwards them to a downstream SMTP server for onward delivery.
The E-MailRelay program ("emailrelay") can run in two modes: a storage daemon, or The E-MailRelay program ("emailrelay") can run in two main modes: a storage daemon,
a forwarding agent. As a storage daemon it waits for connections from your or a forwarding agent. As a storage daemon it waits for connections from your
e-mail front-end and stores the mail which it receives in a spool directory. e-mail front-end and stores the mail which it receives in a spool directory.
As a forwarding agent it pulls messages out of the spool directory and passes As a forwarding agent it pulls messages out of the spool directory and passes
them on to a remote server -- typically an ISP mail server. them on to a remote server -- typically an ISP mail server.
E-MailRelay uses the Simple Message Transfer Protocol (SMTP). When running as a E-MailRelay uses the Simple Message Transfer Protocol (SMTP). When running as a
storage daemon it acts as an SMTP server, and when running as a forwarding agent it storage daemon it acts as an SMTP server, and when running as a forwarding
acts as an SMTP client. agent it acts as an SMTP client.
E-MailRelay runs on GNU/Linux and Windows. The program can also run as a proxy server. In this mode e-mails submitted at the
server interface are passed on to the dowstream server immediately, without
spooling. This can be useful when combined with mail pre-processing to do things
like encryption, message archiving, addition of digital signatures, etc.
E-MailRelay runs on GNU/Linux, FreeBSD, Solaris and Windows.
What it's not What it's not
------------- -------------
E-MailRelay does not get involved in processing incoming e-mail messages; it only E-MailRelay does not get involved in processing incoming e-mail messages; it
operates on outgoing messages. Incoming e-mail messages will probably be retrieved only operates on outgoing messages. Incoming e-mail messages will probably be
from your ISP by your e-mail front-end program, using the POP3 or IMAP protocols. retrieved from your ISP by your e-mail front-end program, using the POP3 or
IMAP protocols.
E-MailRelay is not a routing MTA. It is designed to be used in situations where all E-MailRelay is not a routing MTA. It is designed to be used in situations where
outgoing e-mail message go out to the Internet, so it is not an appropriate choice all outgoing e-mail message go out to the Internet, so it is not an appropriate
if you send e-mail to other people who use the same machine or to people who are choice if you send e-mail to other people who use the same machine or to people
on the same local area network. who are on the same local area network.
Why use it? Why use it?
----------- -----------
The motivation for developing E-MailRelay is that e-mail store-and-forward using The motivation for developing E-MailRelay is that e-mail store-and-forward
SMTP is conceptually a very simple thing, but most popular MTAs are complex. using SMTP is conceptually a very simple thing, but most popular MTAs are
E-MailRelay just stores messages and then forwards them on to your ISP, whereas a complex. E-MailRelay just stores messages and then forwards them on to your
fully-featured MTA does clever things with address re-writing, message routing, ISP, whereas a fully-featured MTA does clever things with address re-writing,
local delivery, loop detection, fancy DNS lookups etc. message routing, local delivery, loop detection, fancy DNS lookups etc.
In particular the configuration of some popular MTAs is notoriously complex In particular the configuration of some popular MTAs is notoriously complex
and arcane, whereas the only thing the E-MailRelay system needs is the name of and arcane, whereas the only thing the E-MailRelay system needs is the name of
@ -51,10 +53,12 @@ With the move away from dial-up Internet connections a simple store-and-forward
MTA like E-MailRelay becomes more relevant to mobile computers and PDAs which MTA like E-MailRelay becomes more relevant to mobile computers and PDAs which
need to store mail while away from the network. need to store mail while away from the network.
The source code for E-MailRelay is well-structured, portable ANSI C++, without any The source code for E-MailRelay is well-structured, portable ANSI C++, without
thrid-party library dependencies. It could therefore be the basis for other SMTP any third-party library dependencies. It could therefore be the basis for other
projects such as, for example, an anonymising remailer or a protocol bridge between SMTP projects such as, for example, an anonymising remailer or an encryption
SMTP and proprietary mail transfer protocols used in private e-mail networks. gateway. E-MailRelay is well suited to gateway applications (sitting between
local e-mail clients and a local routing MTA) because of its functional
simplicity.
Running E-MailRelay Running E-MailRelay
------------------- -------------------
@ -62,8 +66,8 @@ To run E-MailRelay as a storage daemon use the command:
emailrelay --as-server emailrelay --as-server
To run E-MailRelay as a forwarding agent (once connected to the Internet), use a To run E-MailRelay as a forwarding agent (once connected to the Internet), use
command like this: a command like this:
emailrelay --as-client mail.myisp.net:smtp emailrelay --as-client mail.myisp.net:smtp
@ -76,37 +80,44 @@ interface. The administration interface is a command-line network service
compatible with telnet. The interface must be enabled using the "--admin" compatible with telnet. The interface must be enabled using the "--admin"
command line switch, specifying the listening port number.) command line switch, specifying the listening port number.)
To run E-MailRelay as a personal proxy server (on port 10025) to the system's
default server use a command like this:
emailrelay --as-proxy localhost:smtp --no-syslog --port 10025 --filter ${HOME}/.emailrelay/mailfilter --spool-dir ${HOME}/.emailrelay/spool
where 'mailfilter' is a program which does the appropriate mail processing.
For more information on the command-line options refer to the reference guide For more information on the command-line options refer to the reference guide
or run: or run:
emailrelay --help emailrelay --help
Starting the daemon at boot-time Starting the daemon at boot-time
-------------------------------- --------------------------------
The standard installation of E-MailRelay (using "make install") puts most of the The standard installation of E-MailRelay (using "make install") puts most of
files into the right places, but it does not set things up so that the daemon the files into the right places, but it does not set things up so that the
starts at boot time, or that e-mail gets forwarded automatically when you daemon starts at boot time, or that e-mail gets forwarded automatically when
connect to the Internet. You have to do those bits yourself because of the you connect to the Internet. You have to do those bits yourself because of the
differences between the various GNU/Linux distributions. differences between the various GNU/Linux distributions.
Many systems, including the most popular GNU/Linux distributions, use the Many systems, including the most popular GNU/Linux distributions, use the
System-V mechanism for starting daemons at boot time. The directory "/etc/init.d" System-V mechanism for starting daemons at boot time. The directory
(or "/sbin/init.d") contains a start/stop script for each daemon process, and "/etc/init.d" (or "/sbin/init.d") contains a start/stop script for each daemon
then symbolic links in the "rc<N>.d" subdirectories control which scripts are process, and then symbolic links in the "rc<N>.d" subdirectories control which
run when entering or leaving a particular run-level (<N>). The links point back scripts are run when entering or leaving a particular run-level (<N>). The
into the start/stop script in the parent directory, using a "S" prefix for the links point back into the start/stop script in the parent directory, using a
starting link, and a "K" prefix for the stopping link. The numeric part of "S" prefix for the starting link, and a "K" prefix for the stopping link. The
the link name determines the order in which the links are called. numeric part of the link name determines the order in which the links are
called.
Before you start you will need to know where your "init.d" directory can be found Before you start you will need to know where your "init.d" directory can be
and what your default run level is: found and what your default run level is:
$ ls -d /*/init.d $ ls -d /*/init.d
$ runlevel | awk '{print $2}' $ runlevel | awk '{print $2}'
Assuming these are "/etc/init.d" and "5" you should (as root) copy the E-MailRelay Assuming these are "/etc/init.d" and "5" you should (as root) copy the
start/stop script into "/etc/init.d": E-MailRelay start/stop script into "/etc/init.d":
$ cp /usr/local/libexec/emailrelay.sh /etc/init.d $ cp /usr/local/libexec/emailrelay.sh /etc/init.d
@ -131,27 +142,28 @@ daemons compete for the standard SMTP listening port):
(There are also KDE and GNOME GUIs which you can use to do the latter steps.) (There are also KDE and GNOME GUIs which you can use to do the latter steps.)
Automatic triggering of onward delivery Triggering onward delivery
--------------------------------------- --------------------------
This section assumes that you are using "pppd" to establish your dial-up Internet This section assumes that you are using "pppd" to establish your dial-up
connection. (Note that KDE's "kppp" and Red Hat's "rp3" are graphical front-ends Internet connection. (Note that KDE's "kppp" and Red Hat's "rp3" are graphical
to the underlying "pppd" daemon.) front-ends to the underlying "pppd" daemon.)
The ppp daemon calls the script "/etc/ppp/ip-up" when it has successfully established The ppp daemon calls the script "/etc/ppp/ip-up" when it has successfully
a dial-up link to your ISP. This script will probably set up IP routes, update the established a dial-up link to your ISP. This script will probably set up IP
DNS configuration, initialise a firewall, run "fetchmail" and "sendmail", etc. It may routes, update the DNS configuration, initialise a firewall, run "fetchmail"
also call out to another script, "ip-up.local" which is available for you to put and "sendmail", etc. It may also call out to another script, "ip-up.local"
stuff into without having to grub around inside "ip-up" itself. which is available for you to put stuff into without having to grub around
inside "ip-up" itself.
The simplest approach for editing "ip-up" is to look for a "sendmail -q" line. If The simplest approach for editing "ip-up" is to look for a "sendmail -q" line.
you find "sendmail -q" then it should be sufficient to replace it with this: If you find "sendmail -q" then it should be sufficient to replace it with this:
emailrelay --as-client <myisp>:smtp emailrelay --as-client <myisp>:smtp
where you substitute your ISP's SMTP server address for <myisp>. where you substitute your ISP's SMTP server address for <myisp>.
Or if your "ip-up" calls out to "ip-up.local" then create a two-line "ip-up.local" Or if your "ip-up" calls out to "ip-up.local" then create a two-line
script: "ip-up.local" script:
$ cd /etc/ppp $ cd /etc/ppp
$ cat << EOF > ip-up.local $ cat << EOF > ip-up.local
@ -160,28 +172,53 @@ script:
EOF EOF
$ chmod +x ip-up.local $ chmod +x ip-up.local
Notification of failed e-mails
------------------------------
If e-mail messages become corrupted or inaccessible within the spool directory
then they will get failed within the E-MailRelay system. In order to get failed
e-mails to 'bounce' back into your in-tray you will need to run the
"emailrelay-notify.sh" script periodically. Note that this script requires
that you have "procmail" installed on your system to act as a "delivery agent".
There are not many ways in which an e-mail can fail within the E-MailRelay
system. If everything is set up correctly then perhaps the most likely case is
that you have run out of disk space. If you are not too worried about getting
failed mail to bounce, or if you do not have a suitable delivery agent, then
a simple check in your ".profile" script for "*.bad" files in the spool
directory may be sufficient:
$ cat <<EOF >> ~/.profile
if test -f /usr/local/var/spool/emailrelay/*.envelope.bad ; then echo Failed mail >&2 ; fi
EOF
Glossary Glossary
-------- --------
ISP = Internet Service Provider. The company your modem calls to connect to the Internet. # ISP
Internet Service Provider.
MTA = Message Transfer Agent. Something which accepts incoming e-mail messages and # MTA
passes them on either to a local user, or to another MTA. A sophisticated MTA program, Message Transfer Agent. Something which accepts incoming e-mail messages
which is widely used on the Internet, is "sendmail". and passes them on either to a local user, or to another MTA. A sophisticated
MTA program, which is widely used on the Internet, is "sendmail".
SMTP = Simple Message Transfer Protocol. A set of rules which dictate how e-mail messages # SMTP
are passed from one part of the e-mail system to the next. The protocol rules are set Simple Message Transfer Protocol. A set of rules which dictate how
out in the document called RFC2821. e-mail messages are passed from one part of the e-mail system to the next.
The protocol rules are set out in the document "RFC2821".
POP3 = Post Office Protocol 3. A protocol for fetching incoming e-mail messages from your # POP3
ISP's mail server. Many e-mail front-ends (Mutt, KMail, Netscape, etc) will fetch messages Post Office Protocol 3. A protocol for fetching incoming e-mail messages.
directly from your ISP using the POP protocol, or you may have a program like "fetchmail" Many e-mail front-ends will fetch messages directly from an ISP using the POP
doing it on their behalf. protocol, or a program like "fetchmail" may do it on their behalf.
IMAP = Internet Message Access Protocol. A newer alternative to POP3. # IMAP
Internet Message Access Protocol. A newer alternative to POP3.
PPP = Point to Point Protocol. A low-level protocol used in dial-up connections to an ISP. # PPP
Usually implemented by the "pppd" program on GNU/Linux. Point to Point Protocol. A low-level protocol used in dial-up connections
to an ISP. Usually implemented by the "pppd" program on GNU/Linux.

60
emailrelay.spec Normal file
View File

@ -0,0 +1,60 @@
Summary: Simple e-mail message transfer agent using SMTP
Name: emailrelay
Version: 0.9.3
Release: 1
Copyright: GPL
Group: System Environment/Daemons
Source: http://emailrelay.sourceforge.net/.../emailrelay-src-0.9.3.tar.gz
BuildRoot: /tmp/emailrelay-install
%description
E-MailRelay is a simple SMTP store-and-forward message transfer agent (MTA).
It runs as an SMTP server, storing incoming e-mail in a local spool directory,
and then forwarding the stored messages to a downstream SMTP server on request.
It can also run as a proxy server, forwarding (and optionally pre-processing)
incoming e-mail as soon as it is received. It does not do any message routing,
other than to a local postmaster. Because of this functional simplicity it is
extremely easy to configure, typically only requiring the address of the
downstream SMTP server to be put on the command line.
C++ source code is available for Linux, FreeBSD and Windows. Distribution is
under the GNU General Public License.
%prep
%setup
%build
./configure
make
%install
make install destdir=$RPM_BUILD_ROOT DESTDIR=$RPM_BUILD_ROOT
%clean
rm -rf $RPM_BUILD_ROOT
%files
/usr/local/libexec/emailrelay-poke
/usr/local/libexec/emailrelay.sh
/usr/local/sbin/emailrelay
/usr/local/var/spool/emailrelay/empty_file
/usr/local/share/emailrelay/emailrelay-notify.sh
/usr/local/share/emailrelay/emailrelay-deliver.sh
/usr/local/share/emailrelay/emailrelay-process.sh
/usr/local/share/emailrelay/readme.html
/usr/local/share/emailrelay/developer.html
/usr/local/share/emailrelay/reference.html
/usr/local/share/emailrelay/userguide.html
/usr/local/share/emailrelay/man.html
/usr/local/share/emailrelay/index.html
/usr/local/share/emailrelay/graphics/bullet.gif
/usr/local/share/emailrelay/html/
/usr/local/man/man1/emailrelay.1
/usr/local/man/man1/emailrelay-poke.1
%changelog
* Mon Sep 24 2001 Graeme Walker <graeme_walker@users.sourceforge.net>
- Initial version.

View File

@ -46,7 +46,8 @@ std::stringstream::stringstream()
} }
inline inline
std::stringstream::stringstream( char * , size_t ) std::stringstream::stringstream( char * p , size_t n ) :
ostrstream( p , n )
{ {
} }

View File

@ -23,51 +23,51 @@ EXTRA_DIST=garg_win32.cpp \
gdirectory_win32.cpp \ gdirectory_win32.cpp \
gfs_win32.cpp \ gfs_win32.cpp \
glogoutput_win32.cpp \ glogoutput_win32.cpp \
gpid_win32.cpp \ gprocess_win32.cpp \
gfile_win32.cpp \ gfile_win32.cpp \
gnumber.cpp gnumber.cpp
INCLUDES = -I$(top_srcdir)/lib/gcc2.95 INCLUDES = -I$(top_srcdir)/lib/gcc2.95
noinst_LIBRARIES = libglib.a noinst_LIBRARIES = libglib.a
libglib_a_SOURCES = garg.cpp \ libglib_a_SOURCES = \
garg_unix.cpp \ garg.cpp \
gdaemon_unix.cpp \
gdate.cpp \
gdatetime.cpp \
gdatetime_unix.cpp \
gdirectory.cpp \
gdirectory_unix.cpp \
gexception.cpp \
gfile.cpp \
gfile_unix.cpp \
gfs_unix.cpp \
ggetopt.cpp \
glog.cpp \
glogoutput.cpp \
glogoutput_unix.cpp \
gpath.cpp \
gpid_unix.cpp \
gstr.cpp \
gtime.cpp \
gdef.h \
garg.h \ garg.h \
gdaemon.h \ garg_unix.cpp \
gdate.h \
gdatetime.h \
gdirectory.h \
gexception.h \
gfile.h \
gfs.h \
ggetopt.h \
glog.h \
glogoutput.h \
gnumber.h \
gpath.h \
gpid.h \
gstr.h \
gtime.h \
gstrings.h \
gdebug.h \
gassert.h \ gassert.h \
gconvert.h \ gconvert.h \
gmemory.h gdaemon.h \
gdaemon_unix.cpp \
gdate.cpp \
gdate.h \
gdatetime.cpp \
gdatetime.h \
gdatetime_unix.cpp \
gdebug.h \
gdef.h \
gdirectory.cpp \
gdirectory.h \
gdirectory_unix.cpp \
gexception.cpp \
gexception.h \
gfile.cpp \
gfile.h \
gfile_unix.cpp \
gfs.h \
gfs_unix.cpp \
ggetopt.cpp \
ggetopt.h \
glog.cpp \
glog.h \
glogoutput.cpp \
glogoutput.h \
glogoutput_unix.cpp \
gmemory.h \
gnumber.h \
gpath.cpp \
gpath.h \
gprocess.h \
gprocess_unix.cpp \
gstr.cpp \
gstr.h \
gstrings.h \
gtime.cpp \
gtime.h

View File

@ -89,11 +89,11 @@ PACKAGE = @PACKAGE@
RANLIB = @RANLIB@ RANLIB = @RANLIB@
VERSION = @VERSION@ VERSION = @VERSION@
EXTRA_DIST = garg_win32.cpp gdaemon_win32.cpp gdatetime_win32.cpp gdirectory_win32.cpp gfs_win32.cpp glogoutput_win32.cpp gpid_win32.cpp gfile_win32.cpp gnumber.cpp EXTRA_DIST = garg_win32.cpp gdaemon_win32.cpp gdatetime_win32.cpp gdirectory_win32.cpp gfs_win32.cpp glogoutput_win32.cpp gprocess_win32.cpp gfile_win32.cpp gnumber.cpp
INCLUDES = -I$(top_srcdir)/lib/gcc2.95 INCLUDES = -I$(top_srcdir)/lib/gcc2.95
noinst_LIBRARIES = libglib.a noinst_LIBRARIES = libglib.a
libglib_a_SOURCES = garg.cpp garg_unix.cpp gdaemon_unix.cpp gdate.cpp gdatetime.cpp gdatetime_unix.cpp gdirectory.cpp gdirectory_unix.cpp gexception.cpp gfile.cpp gfile_unix.cpp gfs_unix.cpp ggetopt.cpp glog.cpp glogoutput.cpp glogoutput_unix.cpp gpath.cpp gpid_unix.cpp gstr.cpp gtime.cpp gdef.h garg.h gdaemon.h gdate.h gdatetime.h gdirectory.h gexception.h gfile.h gfs.h ggetopt.h glog.h glogoutput.h gnumber.h gpath.h gpid.h gstr.h gtime.h gstrings.h gdebug.h gassert.h gconvert.h gmemory.h libglib_a_SOURCES = garg.cpp garg.h garg_unix.cpp gassert.h gconvert.h gdaemon.h gdaemon_unix.cpp gdate.cpp gdate.h gdatetime.cpp gdatetime.h gdatetime_unix.cpp gdebug.h gdef.h gdirectory.cpp gdirectory.h gdirectory_unix.cpp gexception.cpp gexception.h gfile.cpp gfile.h gfile_unix.cpp gfs.h gfs_unix.cpp ggetopt.cpp ggetopt.h glog.cpp glog.h glogoutput.cpp glogoutput.h glogoutput_unix.cpp gmemory.h gnumber.h gpath.cpp gpath.h gprocess.h gprocess_unix.cpp gstr.cpp gstr.h gstrings.h gtime.cpp gtime.h
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ../../config.h CONFIG_HEADER = ../../config.h
@ -109,7 +109,7 @@ libglib_a_LIBADD =
libglib_a_OBJECTS = garg.o garg_unix.o gdaemon_unix.o gdate.o \ libglib_a_OBJECTS = garg.o garg_unix.o gdaemon_unix.o gdate.o \
gdatetime.o gdatetime_unix.o gdirectory.o gdirectory_unix.o \ gdatetime.o gdatetime_unix.o gdirectory.o gdirectory_unix.o \
gexception.o gfile.o gfile_unix.o gfs_unix.o ggetopt.o glog.o \ gexception.o gfile.o gfile_unix.o gfs_unix.o ggetopt.o glog.o \
glogoutput.o glogoutput_unix.o gpath.o gpid_unix.o gstr.o gtime.o glogoutput.o glogoutput_unix.o gpath.o gprocess_unix.o gstr.o gtime.o
CXXFLAGS = @CXXFLAGS@ CXXFLAGS = @CXXFLAGS@
CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX) CXXLD = $(CXX)
@ -229,7 +229,7 @@ garg_unix.o: garg_unix.cpp gdef.h ../../config.h \
gdaemon_unix.o: gdaemon_unix.cpp gdef.h ../../config.h \ gdaemon_unix.o: gdaemon_unix.cpp gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \ ../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gdaemon.h \ ../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gdaemon.h \
gexception.h gpath.h gstrings.h gpid.h gexception.h gpath.h gstrings.h gprocess.h
gdate.o: gdate.cpp gdef.h ../../config.h ../../lib/gcc2.95/iostream \ gdate.o: gdate.cpp gdef.h ../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \ ../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gdate.h gdatetime.h gexception.h \ ../../lib/gcc2.95/limits gdate.h gdatetime.h gexception.h \
@ -283,9 +283,10 @@ gpath.o: gpath.cpp gdef.h ../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \ ../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gpath.h gstrings.h gfs.h gstr.h \ ../../lib/gcc2.95/limits gpath.h gstrings.h gfs.h gstr.h \
gexception.h gdebug.h glogoutput.h glog.h gassert.h gexception.h gdebug.h glogoutput.h glog.h gassert.h
gpid_unix.o: gpid_unix.cpp gdef.h ../../config.h \ gprocess_unix.o: gprocess_unix.cpp gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \ ../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gpid.h ../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gprocess.h \
gexception.h gpath.h gstrings.h gfs.h glog.h
gstr.o: gstr.cpp gdef.h ../../config.h ../../lib/gcc2.95/iostream \ gstr.o: gstr.cpp gdef.h ../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \ ../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gstr.h gexception.h gstrings.h \ ../../lib/gcc2.95/limits gstr.h gexception.h gstrings.h \

View File

@ -34,11 +34,13 @@ namespace G
} ; } ;
// Class: G::Arg // Class: G::Arg
// Description: A command line parser class. // Description: A class which holds a represention of the
// argc/argv command line array. Also does simple command line
// parsing, and, under Windows, command line splitting (the
// single command line string is split into an argv[] array,
// including argv[0]).
// //
// Also does command line splitting for Windows: // See also: G::GetOpt
// the single command line string is split into
// an argv[] array, including argv[0].
// //
class G::Arg class G::Arg
{ {

View File

@ -31,7 +31,6 @@
#define G_ASSERT( test ) G::LogOutput::assertion( __FILE__ , __LINE__ , test , #test ) #define G_ASSERT( test ) G::LogOutput::assertion( __FILE__ , __LINE__ , test , #test )
#else #else
#define G_ASSERT( test ) #define G_ASSERT( test )
//#define G_ASSERT( test ) G::LogOutput::assertion( 0 , 0 , test , NULL )
#endif #endif
#endif #endif

View File

@ -40,13 +40,22 @@ namespace G
// Deamonisation includes fork()ing, detaching from the // Deamonisation includes fork()ing, detaching from the
// controlling terminal, setting the process umask, etc. // controlling terminal, setting the process umask, etc.
// The windows implementation does nothing. // The windows implementation does nothing.
// See also: G::Process
// //
class G::Daemon class G::Daemon
{ {
public: public:
G_EXCEPTION( CannotFork , "cannot fork" ) ; G_EXCEPTION( CannotFork , "cannot fork" ) ;
G_EXCEPTION( BadPidFile , "invalid pid file" ) ; G_EXCEPTION( BadPidFile , "invalid pid file" ) ;
enum Who { Parent , Child } ; class PidFile // Used by G::Daemon::detach().
{
public: explicit PidFile( const Path & pid_file ) ;
public: PidFile() ;
public: void commit() ;
private: Path m_path ;
private: bool m_valid ;
friend class Daemon ;
} ;
static void detach() ; static void detach() ;
// Detaches from the parent environment. // Detaches from the parent environment.
@ -59,20 +68,23 @@ public:
// to a file. The path must be absolute. // to a file. The path must be absolute.
// Throws BadPidFile on error. // Throws BadPidFile on error.
static void closeFiles( bool keep_stderr = false ) ; static void detach( PidFile & pid_file ) ;
// Closes all open file descriptors. // An overload which allows for a delayed write
// of the new process-id to a file. The path
static void closeStderr() ; // must be absolute.
// Closes stderr. //
// A delayed write is useful for network daemons
static void setUmask() ; // which open a listening port. You do not want
// Sets a tight umask. // a second instance, which will fail on startup,
// to overwrite the pid file of the running
// server. In this situation call PidFile::commit()
// just before entering the event loop.
//
// Throws BadPidFile on error.
private: private:
Daemon() ; Daemon() ;
static Who fork() ;
static void setsid() ; static void setsid() ;
static void cd( const std::string & ) ;
} ; } ;
#endif #endif

View File

@ -23,21 +23,54 @@
#include "gdef.h" #include "gdef.h"
#include "gdaemon.h" #include "gdaemon.h"
#include "gpid.h" #include "gprocess.h"
namespace
{
void PidFile__testSyntax( const G::Path & pid_file )
{
if( pid_file != G::Path() && !pid_file.isAbsolute() )
throw G::Daemon::BadPidFile(std::string("must be an absolute path: ")+pid_file.str()) ;
}
void PidFile__testCreation( const G::Path & pid_file )
{
if( pid_file != G::Path() )
{
std::ofstream tester( pid_file.str().c_str() ) ;
if( !tester.good() )
throw G::Daemon::BadPidFile(std::string("cannot create file: ")+pid_file.str()) ;
}
}
void PidFile__create( const G::Path & pid_file )
{
if( pid_file != G::Path() )
{
std::ofstream file( pid_file.str().c_str() ) ;
file << G::Process::Id() << std::endl ;
if( !file.good() )
throw G::Daemon::BadPidFile(std::string("cannot create file: ")+pid_file.str()) ;
}
}
} ;
//static //static
void G::Daemon::detach( const Path & pid_file ) void G::Daemon::detach( const Path & pid_file )
{ {
if( !pid_file.isAbsolute() ) PidFile__testSyntax( pid_file ) ;
throw BadPidFile(std::string("must be an absolute path: ")+pid_file.str()) ; PidFile__testCreation( pid_file ) ;
if( !std::ofstream(pid_file.str().c_str()).good() )
throw BadPidFile(std::string("cannot create file: ")+pid_file.str()) ;
detach() ; detach() ;
std::ofstream file( pid_file.str().c_str() ) ; PidFile__create( pid_file ) ;
file << Pid() << std::endl ; }
//static
void G::Daemon::detach( PidFile & pid_file )
{
PidFile__testSyntax( pid_file.m_path ) ;
detach() ;
} }
//static //static
@ -45,13 +78,13 @@ void G::Daemon::detach()
{ {
// see Stevens, ISBN 0-201-563137-7, ch 13. // see Stevens, ISBN 0-201-563137-7, ch 13.
if( fork() == Parent ) if( Process::fork() == Process::Parent )
::_exit( 0 ) ; ::_exit( 0 ) ;
setsid() ; setsid() ;
cd( "/" ) ; (void) Process::cd( "/" , Process::NoThrow() ) ;
if( fork() == Parent ) if( Process::fork() == Process::Parent )
::_exit( 0 ) ; ::_exit( 0 ) ;
} }
@ -62,46 +95,23 @@ void G::Daemon::setsid()
; // no-op ; // no-op
} }
void G::Daemon::cd( const std::string & dir ) // ===
G::Daemon::PidFile::PidFile() :
m_valid(false)
{ {
if( 0 != ::chdir( dir.c_str() ) )
; // ignore it
} }
void G::Daemon::setUmask() G::Daemon::PidFile::PidFile( const G::Path & path ) :
m_path(path) ,
m_valid(true)
{ {
// (note that ansi std::ofstream does not support file permissions,
// so rely on the umask to keep things secure)
mode_t new_mode = 0177 ; // create as -rw-------
mode_t old_mode = ::umask( new_mode ) ;
} }
G::Daemon::Who G::Daemon::fork() void G::Daemon::PidFile::commit()
{ {
pid_t pid = ::fork() ; if( m_valid )
if( pid < 0 ) PidFile__create( m_path ) ;
{
throw CannotFork() ;
}
return pid == 0 ? Child : Parent ;
} }
void G::Daemon::closeStderr()
{
::close( STDERR_FILENO ) ;
}
void G::Daemon::closeFiles( bool keep_stderr )
{
int n = 256U ;
long rc = ::sysconf( _SC_OPEN_MAX ) ;
if( rc > 0L )
n = static_cast<int>( rc ) ;
for( int fd = 0 ; fd < n ; fd++ )
{
if( !keep_stderr || fd != STDERR_FILENO )
::close( fd ) ;
}
}

View File

@ -23,26 +23,39 @@
#include "gdef.h" #include "gdef.h"
#include "gdaemon.h" #include "gdaemon.h"
#include "gprocess.h"
//static //static
void G::Daemon::detach( const Path & ) void G::Daemon::detach( const Path & )
{ {
detach() ;
}
//static
void G::Daemon::detach( PidFile & )
{
detach() ;
} }
//static //static
void G::Daemon::detach() void G::Daemon::detach()
{ {
(void) ::FreeConsole() ;
} }
void G::Daemon::closeFiles( bool keep_stderr ) // ===
G::Daemon::PidFile::PidFile() :
m_valid(false)
{ {
} }
void G::Daemon::setUmask() G::Daemon::PidFile::PidFile( const Path & ) :
m_valid(false)
{ {
} }
void G::Daemon::closeStderr() void G::Daemon::PidFile::commit()
{ {
} }

View File

@ -26,6 +26,7 @@
#include "gdebug.h" #include "gdebug.h"
#include <sys/types.h> #include <sys/types.h>
#include <time.h> #include <time.h>
#include <iomanip>
//static //static
int G::Date::yearUpperLimit() int G::Date::yearUpperLimit()
@ -83,10 +84,17 @@ void G::Date::init( const G::DateTime::BrokenDownTime & tm )
m_weekday = sunday ; m_weekday = sunday ;
} }
std::string G::Date::string( Format ) const std::string G::Date::string( Format format ) const
{ {
const char * sep = format == yyyy_mm_dd_slash ? "/" : "" ;
std::stringstream ss ; std::stringstream ss ;
ss << m_year << "/" << m_month << "/" << m_day ; if( format == yyyy_mm_dd_slash )
ss << m_year << "/" << m_month << "/" << m_day ;
else
ss
<< m_year
<< std::setw(2) << std::setfill('0') << m_month
<< std::setw(2) << std::setfill('0') << m_day ;
return ss.str() ; return ss.str() ;
} }

View File

@ -38,6 +38,7 @@ namespace G
// Class: G::Date // Class: G::Date
// Description: A date (dd/mm/yyyy) class. // Description: A date (dd/mm/yyyy) class.
// See also: Time, DateTime
// //
class G::Date class G::Date
{ {
@ -53,7 +54,7 @@ public:
august , september , october , november , december } ; august , september , october , november , december } ;
enum Format enum Format
{ yyyy_mm_dd_slash } ; { yyyy_mm_dd_slash , yyyy_mm_dd } ;
static int yearUpperLimit() ; static int yearUpperLimit() ;
// Returns the smallest supported year value. // Returns the smallest supported year value.
@ -69,16 +70,16 @@ public:
// Constructor for the current date // Constructor for the current date
// in the local timezone. // in the local timezone.
explicit Date( const G::DateTime::BrokenDownTime & tm ) ; Date( const G::DateTime::BrokenDownTime & tm ) ;
// Constructor for the specified date. // Constructor for the specified date.
explicit Date( G::DateTime::EpochTime t , const LocalTime & ) ; Date( G::DateTime::EpochTime t , const LocalTime & ) ;
// Constructor for the date in the local // Constructor for the date in the local
// timezone as at the given epoch time. // timezone as at the given epoch time.
Date( int year , Month month , int day_of_month ) ; Date( int year , Month month , int day_of_month ) ;
// Constructor for the specified date. // Constructor for the specified date.
std::string string( Format format = yyyy_mm_dd_slash ) const ; std::string string( Format format = yyyy_mm_dd_slash ) const ;
// Returns a string representation of the date. // Returns a string representation of the date.
@ -86,7 +87,8 @@ public:
// Returns the day of the week. // Returns the day of the week.
std::string weekdayString( bool brief = false ) const ; std::string weekdayString( bool brief = false ) const ;
// Returns a string representation of the day of the week. // Returns an english string representation of
// the day of the week.
int monthday() const ; int monthday() const ;
// Returns the day of the month. // Returns the day of the month.
@ -98,7 +100,7 @@ public:
// Returns the month. // Returns the month.
std::string monthString( bool brief = false ) const ; std::string monthString( bool brief = false ) const ;
// Returns the month as a string. // Returns the month as a string (in english).
int year() const ; int year() const ;
// Returns the year. // Returns the year.
@ -106,16 +108,16 @@ public:
std::string yearString() const ; std::string yearString() const ;
// Returns the year as a string. // Returns the year as a string.
Date &operator++() ; Date & operator++() ;
// Increments the date by one day. // Increments the date by one day.
Date &operator--() ; Date & operator--() ;
// Decrements the date by one day. // Decrements the date by one day.
bool operator==( const Date &rhs ) const ; bool operator==( const Date & rhs ) const ;
// Comparison operator. // Comparison operator.
bool operator!=( const Date &rhs ) const ; bool operator!=( const Date & rhs ) const ;
// Comparison operator. // Comparison operator.
private: private:

View File

@ -172,7 +172,7 @@ std::string G::GetOpt::usageSummaryPartOne() const
bool first = true ; bool first = true ;
for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p ) for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p )
{ {
if( ! (*p).second.valued ) if( !(*p).second.valued && !(*p).second.hidden )
{ {
if( first ) if( first )
ss << "[-" ; ss << "[-" ;
@ -192,23 +192,26 @@ std::string G::GetOpt::usageSummaryPartTwo() const
const char * sep = "" ; const char * sep = "" ;
for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p ) for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p )
{ {
ss << sep << "[" ; if( !(*p).second.hidden )
if( (*p).second.name.length() )
{ {
ss << "--" << (*p).second.name ; ss << sep << "[" ;
if( (*p).second.name.length() )
{
ss << "--" << (*p).second.name ;
}
else
{
ss << "-" << (*p).first ;
}
if( (*p).second.valued )
{
std::string vd = (*p).second.value_description ;
if( vd.empty() ) vd = "value" ;
ss << " <" << vd << ">" ;
}
ss << "]" ;
sep = " " ;
} }
else
{
ss << "-" << (*p).first ;
}
if( (*p).second.valued )
{
std::string vd = (*p).second.value_description ;
if( vd.empty() ) vd = "value" ;
ss << " <" << vd << ">" ;
}
ss << "]" ;
sep = " " ;
} }
return ss.str() ; return ss.str() ;
} }
@ -223,38 +226,45 @@ std::string G::GetOpt::usageHelpCore( const std::string & prefix , size_t tab_st
std::string result ; std::string result ;
for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p ) for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p )
{ {
std::string line( prefix ) ; if( !(*p).second.hidden )
line.append( "-" ) ;
line.append( 1U , (*p).first ) ;
if( (*p).second.name.length() )
{ {
line.append( ",--" ) ; std::string line( prefix ) ;
line.append( (*p).second.name ) ; line.append( "-" ) ;
line.append( 1U , (*p).first ) ;
if( (*p).second.name.length() )
{
line.append( ",--" ) ;
line.append( (*p).second.name ) ;
}
if( (*p).second.valued )
{
std::string vd = (*p).second.value_description ;
if( vd.empty() ) vd = "value" ;
line.append( " <" ) ;
line.append( vd ) ;
line.append( ">" ) ;
}
line.append( 1U , ' ' ) ;
if( line.length() < tab_stop )
line.append( tab_stop-line.length() , ' ' ) ;
line.append( (*p).second.description ) ;
if( width )
{
std::string indent( tab_stop , ' ' ) ;
line = G::Str::wrap( line , "" , indent , width ) ;
}
else
{
line.append( 1U , '\n' ) ;
}
result.append( line ) ;
} }
if( (*p).second.valued )
{
std::string vd = (*p).second.value_description ;
if( vd.empty() ) vd = "value" ;
line.append( " <" ) ;
line.append( vd ) ;
line.append( ">" ) ;
}
line.append( 1U , ' ' ) ;
if( line.length() < tab_stop )
line.append( tab_stop-line.length() , ' ' ) ;
line.append( (*p).second.description ) ;
if( width )
{
std::string indent( tab_stop , ' ' ) ;
line = G::Str::wrap( line , "" , indent , width ) ;
}
result.append( line ) ;
} }
return result ; return result ;
} }

View File

@ -39,6 +39,7 @@ namespace G
// Class: G::GetOpt // Class: G::GetOpt
// Description: A command line switch parser. // Description: A command line switch parser.
// See also: G::Arg
// //
class G::GetOpt class G::GetOpt
{ {
@ -60,6 +61,9 @@ public:
// <switch-description> // <switch-description>
// <value-type> -- 0 is none, and 1 is a string // <value-type> -- 0 is none, and 1 is a string
// <value-description> // <value-description>
//
// If the switch-description field is empty
// then the switch is hidden.
Arg args() const ; Arg args() const ;
// Returns all the non-switch command-line arguments. // Returns all the non-switch command-line arguments.
@ -125,10 +129,12 @@ private:
std::string name ; std::string name ;
std::string description ; std::string description ;
bool valued ; bool valued ;
bool hidden ;
std::string value_description ; std::string value_description ;
SwitchSpec(char c_,const std::string &name_,const std::string &description_, SwitchSpec(char c_,const std::string &name_,const std::string &description_,
bool v_,const std::string &vd_) : bool v_,const std::string &vd_) :
c(c_) , name(name_) , description(description_) , c(c_) , name(name_) , description(description_) ,
hidden(description_.empty()) ,
valued(v_) , value_description(vd_) {} valued(v_) , value_description(vd_) {}
} ; } ;
typedef std::map<char,SwitchSpec GLessAllocator(char,SwitchSpec) > SwitchSpecMap ; typedef std::map<char,SwitchSpec GLessAllocator(char,SwitchSpec) > SwitchSpecMap ;

View File

@ -113,7 +113,7 @@ namespace G
// then warning/error messages should also get raised by some another // then warning/error messages should also get raised by some another
// independent means. // independent means.
// //
#define G_LOG_OUTPUT( expr , severity ) { G::Log::stream() << G::Log::Line(__FILE__,__LINE__) << expr << G::Log::end(severity) ; } #define G_LOG_OUTPUT( expr , severity ) { try { G::Log::stream() << G::Log::Line(__FILE__,__LINE__) << expr << G::Log::end(severity) ; } catch(...) {} }
#if defined(_DEBUG) && ! defined(G_NO_DEBUG) #if defined(_DEBUG) && ! defined(G_NO_DEBUG)
#define G_DEBUG( expr ) G_LOG_OUTPUT( expr , G::Log::s_Debug ) #define G_DEBUG( expr ) G_LOG_OUTPUT( expr , G::Log::s_Debug )
#else #else

View File

@ -26,8 +26,6 @@
#include <cstdlib> // getenv #include <cstdlib> // getenv
#include <cstring> // strlen #include <cstring> // strlen
namespace std { using ::getenv ; using ::strlen ; } ;
void G::LogOutput::rawOutput( G::Log::Severity severity , const char *message ) void G::LogOutput::rawOutput( G::Log::Severity severity , const char *message )
{ {
std::cerr << message ; std::cerr << message ;

View File

@ -27,22 +27,45 @@
#include "gdef.h" #include "gdef.h"
#include <memory> #include <memory>
#if HAVE_CONFIG_H
#include <config.h>
#else
#ifdef G_WINDOWS
#define HAVE_NONCONST_AUTOPTR 0
#else
#define HAVE_NONCONST_AUTOPTR 1
#endif
#endif
// Template function: operator<<= // Template function: operator<<=
// Description: A fix for the problem of resetting // Description: A fix for the problem of resetting
// an auto_ptr portably. MSVC6.0 does not have a reset // an auto_ptr portably. MSVC6.0 does not have a reset
// method, and GCC has a non-const assignment // method, and GCC 2.95 has a non-const assignment
// operators. This means that the MSVC code and // operators. This means that the MSVC code and
// the gcc code for resetting an auto_ptr are // the GCC code for resetting auto_ptr<>s has to
// radically different. This operator hides // be quite different. This operator hides
// those differences. // those differences.
// //
// Usage:
/// #include <memory>
/// #include "gmemory.h"
/// {
/// std::auto_ptr<Foo> ptr ;
/// for( int i = 0 ; i < 10 ; i++ )
/// {
/// ptr <<= new Foo ;
/// if( ptr->fn() )
/// eatFoo( ptr->release() ) ;
/// }
/// }
//
template <class T> template <class T>
void operator<<=( std::auto_ptr<T> & ap , T * p ) void operator<<=( std::auto_ptr<T> & ap , T * p )
{ {
#ifdef G_WINDOWS #if HAVE_NONCONST_AUTOPTR
ap = std::auto_ptr<T>( p ) ;
#else
ap.reset( p ) ; ap.reset( p ) ;
#else
ap = std::auto_ptr<T>( p ) ;
#endif #endif
} }
@ -52,7 +75,6 @@ void operator<<=( std::auto_ptr<T> & ap , T * p )
template <class T> template <class T>
void operator<<=( std::auto_ptr<T> & ap , int null_pointer ) void operator<<=( std::auto_ptr<T> & ap , int null_pointer )
{ {
//operator<<=<T>( ap , (T*)(0) ) ;
T * p = 0 ; T * p = 0 ;
ap <<= p ; ap <<= p ;
} }

129
src/glib/gprocess.h Normal file
View File

@ -0,0 +1,129 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gprocess.h
//
#ifndef G_PROCESS_H
#define G_PROCESS_H
#include "gdef.h"
#include "gexception.h"
#include "gpath.h"
#include <iostream>
#include <sys/types.h>
#include <string>
namespace G
{
class Process ;
} ;
// Class: G::Process
// Description: A static interface for doing things with processes.
// See also: G::Daemon
//
class G::Process
{
public:
G_EXCEPTION( CannotFork , "cannot fork()" ) ;
G_EXCEPTION( CannotChangeDirectory , "cannot cd()" ) ;
G_EXCEPTION( WaitError , "cannot wait()" ) ;
G_EXCEPTION( ChildError , "child process terminated abnormally or stopped" ) ;
G_EXCEPTION( InvalidPath , "invalid executable path -- must be absolute" ) ;
enum Who { Parent , Child } ;
class IdImp ;
class Id // Process-id class.
{
public: Id() ;
public: ~Id() ;
public: Id( const Id & other ) ;
public: Id & operator=( const Id & rhs ) ;
public: bool operator==( const Id & other ) const ;
public: std::string str() const ;
private: IdImp * m_imp ;
friend class Process ;
} ;
class NoThrow // An overload discriminator for Process.
{} ;
static void closeFiles( bool keep_stderr = false ) ;
// Closes all open file descriptors.
static void closeStderr() ;
// Closes stderr.
static void setUmask() ;
// Sets a tight umask.
static void cd( const Path & dir ) ;
// Changes directory.
static bool cd( const Path & dir , NoThrow ) ;
// Changes directory. Returns false on
// error.
static Who fork() ;
// Forks a new process.
static Who fork( Id & child ) ;
// Forks a new process. In the parent process
// the child process-id is returned by reference.
static void exec( const Path & exe , const std::string & arg = std::string() ) ;
// Executes a program taking reasonable security
// precautions.
static int wait( const Id & child ) ;
// Waits for a child process to terminate.
// Returns the exit code. Throws exceptions
// on error.
static int wait( const Id & child , int error_return ) ;
// Waits for a child process to terminate.
// Returns the exit code, or returns 'error_return'
// on error.
static int spawn( const Path & exe , const std::string & arg , int error_return = 127 ) ;
// Runs a command in a child process. Returns the
// child process's exit code, or 'error_return' on error.
static bool privileged() ;
// Returns true if this process has enhanced security
// privileges.
private:
Process() ;
static int errno_() ;
static void execCore( const Path & , const std::string & ) ;
} ;
namespace G
{
inline
std::ostream & operator<<( std::ostream & stream , const G::Process::Id & id )
{
return stream << id.str() ;
}
} ;
#endif

249
src/glib/gprocess_unix.cpp Normal file
View File

@ -0,0 +1,249 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gprocess_unix.cpp
//
#include "gdef.h"
#include "gprocess.h"
#include "gfs.h"
#include "glog.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h> // open()
class G::Process::IdImp
{
public:
pid_t m_pid ;
} ;
// ===
//static
void G::Process::cd( const Path & dir )
{
if( ! cd(dir,NoThrow()) )
throw CannotChangeDirectory( dir.str() ) ;
}
//static
bool G::Process::cd( const Path & dir , NoThrow )
{
return 0 == ::chdir( dir.str().c_str() ) ;
}
//static
void G::Process::setUmask()
{
mode_t new_mode = 0177 ; // create as -rw-------
mode_t old_mode = ::umask( new_mode ) ;
}
//static
void G::Process::closeStderr()
{
::close( STDERR_FILENO ) ;
}
//static
void G::Process::closeFiles( bool keep_stderr )
{
int n = 256U ;
long rc = ::sysconf( _SC_OPEN_MAX ) ;
if( rc > 0L )
n = static_cast<int>( rc ) ;
for( int fd = 0 ; fd < n ; fd++ )
{
if( !keep_stderr || fd != STDERR_FILENO )
::close( fd ) ;
}
}
G::Process::Who G::Process::fork()
{
Id id ;
return fork( id ) ;
}
G::Process::Who G::Process::fork( Id & child_pid )
{
pid_t rc = ::fork() ;
const bool ok = rc != -1 ;
if( ok )
{
if( rc != 0 )
child_pid.m_imp->m_pid = rc ;
}
else
{
throw CannotFork() ;
}
return rc == 0 ? Child : Parent ;
}
int G::Process::wait( const Id & child_pid )
{
int status ;
for(;;)
{
G_DEBUG( "G::Process::wait: waiting" ) ;
int rc = ::waitpid( child_pid.m_imp->m_pid , &status , 0 ) ;
if( rc == -1 && errno_() == EINTR )
{
; // signal in parent -- keep waiting
}
else if( rc == -1 )
{
int error = errno_() ;
throw WaitError( std::stringstream() << "errno=" << error ) ;
}
else
{
break ;
}
}
G_DEBUG( "G::Process::wait: done" ) ;
if( ! WIFEXITED(status) )
{
// uncaught signal or stopped
throw ChildError( std::stringstream() << "status=" << status ) ;
}
const int exit_status = WEXITSTATUS(status) ;
return exit_status ;
}
int G::Process::wait( const Id & child_pid , int error_return )
{
try
{
return wait( child_pid ) ;
}
catch(...)
{
}
return error_return ;
}
//static
int G::Process::errno_()
{
return errno ; // not ::errno or std::errno for gcc2.95
}
void G::Process::exec( const G::Path & exe , const std::string & arg )
{
if( exe.isRelative() )
throw InvalidPath( exe.str() ) ;
closeFiles() ;
(void) ::open( G::FileSystem::nullDevice() , O_RDONLY ) ; // stdin
(void) ::open( G::FileSystem::nullDevice() , O_WRONLY ) ; // stdout
(void) ::open( G::FileSystem::nullDevice() , O_WRONLY ) ; // stderr
// TODO: more security stuff required here -- setuid() etc.
execCore( exe , arg ) ;
}
void G::Process::execCore( const G::Path & exe , const std::string & arg )
{
char * argv[3U] ;
argv[0U] = const_cast<char*>( exe.pathCstr() ) ;
argv[1U] = arg.empty() ? static_cast<char*>(NULL) : const_cast<char*>(arg.c_str()) ;
argv[2U] = NULL ;
// TODO: review the set of environment variables
char * env[3U] ;
std::string path( "PATH=/usr/bin:/bin" ) ; // no "."
std::string ifr( "IFR= \t\n" ) ;
env[0U] = const_cast<char*>( path.c_str() ) ;
env[1U] = const_cast<char*>( ifr.c_str() ) ;
env[2U] = NULL ;
::execve( exe.str().c_str() , argv , env ) ;
const int error = errno_() ;
G_WARNING( "G::Process::exec: execve() returned: errno=" << error << ": " << exe ) ;
}
int G::Process::spawn( const G::Path & exe , const std::string & arg , int error_return )
{
Id child_pid ;
if( fork(child_pid) == Child )
{
exec( exe , arg ) ;
::_exit( error_return ) ;
}
else
{
return wait( child_pid , error_return ) ;
}
}
bool G::Process::privileged()
{
return getuid() == 0 || geteuid() == 0 ;
}
// ===
G::Process::Id::Id() : m_imp(NULL)
{
m_imp = new IdImp ;
m_imp->m_pid = ::getpid() ;
}
G::Process::Id::~Id()
{
delete m_imp ;
}
G::Process::Id::Id( const Id & other ) :
m_imp(NULL)
{
m_imp = new IdImp ;
m_imp->m_pid = other.m_imp->m_pid ;
}
G::Process::Id & G::Process::Id::operator=( const Id & rhs )
{
m_imp->m_pid = rhs.m_imp->m_pid ;
return *this ;
}
std::string G::Process::Id::str() const
{
std::stringstream ss ;
ss << m_imp->m_pid ;
return ss.str() ;
}
bool G::Process::Id::operator==( const Id & rhs ) const
{
return m_imp->m_pid == rhs.m_imp->m_pid ;
}

149
src/glib/gprocess_win32.cpp Normal file
View File

@ -0,0 +1,149 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gprocess_win32.cpp
//
#include "gdef.h"
#include "gprocess.h"
#include "glog.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <process.h>
#include <direct.h>
#include <io.h>
namespace G
{
const int STDERR_FILENO = 2 ;
const int SC_OPEN_MAX = 256 ; // 32 in limits.h !?
} ;
// ===
class G::Process::IdImp
{
public:
int m_pid ;
} ;
// ===
G::Process::Id::Id() : m_imp(NULL)
{
m_imp = new IdImp ;
m_imp->m_pid = ::_getpid() ;
}
G::Process::Id::~Id()
{
delete m_imp ;
}
G::Process::Id::Id( const Id & other ) :
m_imp(NULL)
{
m_imp = new IdImp ;
m_imp->m_pid = other.m_imp->m_pid ;
}
G::Process::Id & G::Process::Id::operator=( const Id & rhs )
{
m_imp->m_pid = rhs.m_imp->m_pid ;
return *this ;
}
std::string G::Process::Id::str() const
{
std::stringstream ss ;
ss << m_imp->m_pid ;
return ss.str() ;
}
bool G::Process::Id::operator==( const Id & rhs ) const
{
return m_imp->m_pid == rhs.m_imp->m_pid ;
}
void G::Process::closeFiles( bool keep_stderr )
{
const int n = SC_OPEN_MAX ;
for( int fd = 0 ; fd < n ; fd++ )
{
if( !keep_stderr || fd != STDERR_FILENO )
::_close( fd ) ;
}
}
void G::Process::setUmask()
{
// _umask() is available but not very useful
; // no-op
}
void G::Process::closeStderr()
{
int fd = STDERR_FILENO ;
::_close( fd ) ;
}
void G::Process::cd( const Path & dir )
{
if( !cd(dir,NoThrow()) )
throw CannotChangeDirectory( dir.str() ) ;
}
bool G::Process::cd( const Path & dir , NoThrow )
{
return 0 == ::_chdir( dir.str().c_str() ) ;
}
int G::Process::spawn( const Path & exe , const std::string & arg ,
int error_return )
{
// open file descriptors are inherited across ::_spawn() --
// no fcntl() is available to set close-on-exec -- but see
// also ::CreateProcess()
const char * argv [3U] ;
argv[0U] = exe.pathCstr() ;
argv[1U] = arg.c_str() ;
argv[2U] = NULL ;
const int mode = _P_WAIT ;
::_flushall() ;
G_LOG( "G::Process::spawn: " << exe << " " << arg ) ;
int rc = ::_spawnv( mode , exe.str().c_str() , argv ) ;
G_LOG( "G::Process::spawn: done (" << rc << ")" ) ;
return rc < 0 ? error_return : rc ;
}
bool G::Process::privileged()
{
return false ;
}
// not implemented...
// Who G::Process::fork() {}
// Who G::Process::fork( Id & child ) {}
// void G::Process::exec( const Path & exe , const std::string & arg ) {}
// int G::Process::wait( const Id & child ) {}
// int G::Process::wait( const Id & child , int error_return ) {}

View File

@ -38,9 +38,9 @@ namespace GNet
// Class: GNet::Address // Class: GNet::Address
// //
// Description: The Address class encapsulates an // Description: The Address class encapsulates an IP
// IP transport address. The address is stored // transport address. The address is stored internally
// internally as a 'sockaddr_in[6]' structure. // as a 'sockaddr_in/sockaddr_in6' structure.
// //
// See also: GNet::Resolver // See also: GNet::Resolver
// //
@ -87,7 +87,7 @@ public:
// given port number. Throws an exception if // given port number. Throws an exception if
// an invalid port number. See also: validPort() // an invalid port number. See also: validPort()
explicit Address( unsigned int port , Localhost ) ; Address( unsigned int port , Localhost ) ;
// Constructor for a local INADDR_LOOPBACK address with // Constructor for a local INADDR_LOOPBACK address with
// the given port number. Throws an exception if // the given port number. Throws an exception if
// an invalid port number. See also: validPort() // an invalid port number. See also: validPort()
@ -105,7 +105,7 @@ public:
static Address invalidAddress() ; static Address invalidAddress() ;
// Returns an invalid address. Should only be // Returns an invalid address. Should only be
// needed by socket classes. // needed by socket and resolver classes.
static Address localhost( unsigned int port = 0U ) ; static Address localhost( unsigned int port = 0U ) ;
// Returns a localhost ("loopback") address. // Returns a localhost ("loopback") address.

View File

@ -47,7 +47,7 @@ namespace GNet
// Class: GNet::ClientResolver // Class: GNet::ClientResolver
// Description: A resolver class which calls ClientImp::resolveCon() when done. // Description: A resolver class which calls ClientImp::resolveCon() when done.
// //
class GNet::ClientResolver : public Resolver class GNet::ClientResolver : public GNet:: Resolver
{ {
private: private:
ClientImp & m_client_imp ; ClientImp & m_client_imp ;
@ -73,7 +73,7 @@ GNet::ClientResolver::ClientResolver( ClientImp & imp ) :
// Class: GNet::ClientImp // Class: GNet::ClientImp
// Description: A pimple-pattern implementation class for GClient. // Description: A pimple-pattern implementation class for GClient.
// //
class GNet::ClientImp : public EventHandler class GNet::ClientImp : public GNet:: EventHandler
{ {
private: private:
ClientResolver m_resolver ; ClientResolver m_resolver ;

View File

@ -29,9 +29,13 @@
namespace GNet namespace GNet
{ {
typedef ::SOCKET Descriptor ; // SOCKET defined in gnet.h typedef ::SOCKET Descriptor ; // (SOCKET is defined in gnet.h)
bool Descriptor__valid( Descriptor fd ) ; bool Descriptor__valid( Descriptor fd ) ;
// Tests whether the given network descriptor is valid.
Descriptor Descriptor__invalid() ; Descriptor Descriptor__invalid() ;
// Returns an invalid network descriptor.
} ; } ;
#endif #endif

View File

@ -28,7 +28,7 @@
#include "gassert.h" #include "gassert.h"
#include "glog.h" #include "glog.h"
GNet::EventHandler::EventHandler() GNet::EventHandler::~EventHandler()
{ {
} }

View File

@ -54,13 +54,12 @@ namespace GNet
class GNet::EventHandler class GNet::EventHandler
{ {
public: public:
EventHandler() ; virtual ~EventHandler() ;
virtual void readEvent() /*=0*/ ; virtual void readEvent() /*=0*/ ;
virtual void writeEvent() /*=0*/ ; virtual void writeEvent() /*=0*/ ;
virtual void exceptionEvent() /*=0*/ ; virtual void exceptionEvent() /*=0*/ ;
private: private:
EventHandler( const EventHandler & ) ; void operator=( const EventHandler & ) ; // not implemented
void operator=( const EventHandler & ) ;
} ; } ;
// Class: GNet::EventSources // Class: GNet::EventSources

View File

@ -40,7 +40,7 @@ namespace GNet
// may be created. // may be created.
// See also: GNet::EventSources // See also: GNet::EventSources
// //
class GNet::EventServer : public Server class GNet::EventServer : public GNet:: Server
{ {
public: public:
explicit EventServer( unsigned int listening_port ) ; explicit EventServer( unsigned int listening_port ) ;

View File

@ -32,19 +32,6 @@ GNet::LineBuffer::LineBuffer( const std::string & eol ) :
{ {
} }
//static
std::string GNet::LineBuffer::asString( char c )
{
std::stringstream ss ;
if( c == '\n' )
ss << "\\n" ;
else if( c >= ' ' && c < 0x7f )
ss << c ;
else
ss << "\\" << (unsigned int)c ;
return ss.str() ;
}
void GNet::LineBuffer::add( const std::string & segment ) void GNet::LineBuffer::add( const std::string & segment )
{ {
size_t n = segment.size() ; size_t n = segment.size() ;

View File

@ -70,7 +70,6 @@ private:
LineBuffer( const LineBuffer & ) ; LineBuffer( const LineBuffer & ) ;
void operator=( const LineBuffer & ) ; void operator=( const LineBuffer & ) ;
bool terminated() const ; bool terminated() const ;
static std::string asString( char c ) ;
private: private:
G::Strings m_lines ; G::Strings m_lines ;

View File

@ -86,7 +86,7 @@ private:
// Class: GNet::HostRequest // Class: GNet::HostRequest
// Description: A derivation of GNet::Request used for hostname lookup requests. // Description: A derivation of GNet::Request used for hostname lookup requests.
// //
class GNet::HostRequest : public Request class GNet::HostRequest : public GNet:: Request
{ {
public: public:
HostRequest( std::string host_name , HWND hwnd , unsigned msg ) ; HostRequest( std::string host_name , HWND hwnd , unsigned msg ) ;
@ -101,7 +101,7 @@ private:
// Class: GNet::ServiceRequest // Class: GNet::ServiceRequest
// Description: A derivation of GNet::Request used for service (port) lookup requests. // Description: A derivation of GNet::Request used for service (port) lookup requests.
// //
class GNet::ServiceRequest : public Request class GNet::ServiceRequest : public GNet:: Request
{ {
public: public:
ServiceRequest( std::string service_name , bool udp , HWND hwnd , unsigned msg ) ; ServiceRequest( std::string service_name , bool udp , HWND hwnd , unsigned msg ) ;

View File

@ -33,7 +33,7 @@
// Class: GNet::ResolverImp // Class: GNet::ResolverImp
// Description: A pimple-pattern implementation class for GNet::Resolver. // Description: A pimple-pattern implementation class for GNet::Resolver.
// //
class GNet::ResolverImp : public EventHandler class GNet::ResolverImp : public GNet:: EventHandler
{ {
public: public:
ResolverImp( Resolver & resolver , unsigned int port ) ; ResolverImp( Resolver & resolver , unsigned int port ) ;

View File

@ -37,7 +37,7 @@ namespace GNet
// Class: GNet::Select // Class: GNet::Select
// Description: A event-source class which uses ::select(). // Description: A event-source class which uses ::select().
// //
class GNet::Select : public EventSources class GNet::Select : public GNet:: EventSources
{ {
public: public:
G_EXCEPTION( Error , "select() error" ) ; G_EXCEPTION( Error , "select() error" ) ;

View File

@ -44,7 +44,7 @@ namespace GNet
// event-source object (such as GNet::Select) for event handling. // event-source object (such as GNet::Select) for event handling.
// See also: GNet::ServerPeer // See also: GNet::ServerPeer
// //
class GNet::Server : public EventHandler class GNet::Server : public GNet:: EventHandler
{ {
public: public:
G_EXCEPTION( CannotBind , "cannot bind the listening port" ) ; G_EXCEPTION( CannotBind , "cannot bind the listening port" ) ;
@ -99,7 +99,7 @@ private:
// delete themselves when the connection is lost. // delete themselves when the connection is lost.
// See also: GNet::Server, GNet::EventHandler // See also: GNet::Server, GNet::EventHandler
// //
class GNet::ServerPeer : public EventHandler class GNet::ServerPeer : public GNet:: EventHandler
{ {
public: public:
ServerPeer( StreamSocket * , Address ) ; ServerPeer( StreamSocket * , Address ) ;

View File

@ -236,18 +236,26 @@ private:
// (The standard pair<> template cannot be used because gcc's auto_ptr<> has // (The standard pair<> template cannot be used because gcc's auto_ptr<> has
// a non-const copy constructor and assignment operator -- the pair<> op=() // a non-const copy constructor and assignment operator -- the pair<> op=()
// fails to compile because the rhs of the 'first' assignment is const, // fails to compile because the rhs of the 'first' assignment is const,
// not matching any op=() in auto_ptr<>. Note the use of const_cast<>() below.) // not matching any op=() in auto_ptr<>. Note the use of const_cast<>()
// in the implementation.)
// //
class GNet::AcceptPair class GNet::AcceptPair
{ {
public: public:
typedef std::auto_ptr<StreamSocket> first_type ; typedef std::auto_ptr<StreamSocket> first_type ;
typedef Address second_type ; typedef Address second_type ;
first_type first ; first_type first ;
second_type second ; second_type second ;
AcceptPair( StreamSocket * new_p , Address a ) ; AcceptPair( StreamSocket * new_p , Address a ) ;
// Constructor.
AcceptPair( const AcceptPair & other ) ; AcceptPair( const AcceptPair & other ) ;
// Copy constructor.
AcceptPair & operator=( const AcceptPair & rhs ) ; AcceptPair & operator=( const AcceptPair & rhs ) ;
// Assignment operator.
} ; } ;
// === // ===
@ -255,7 +263,7 @@ public:
// Class: GNet::StreamSocket // Class: GNet::StreamSocket
// Description: A derivation of Socket for a stream socket. // Description: A derivation of Socket for a stream socket.
// //
class GNet::StreamSocket : public Socket class GNet::StreamSocket : public GNet:: Socket
{ {
public: public:
StreamSocket() ; StreamSocket() ;
@ -293,7 +301,7 @@ private:
// Description: A derivation of Socket for a connectionless // Description: A derivation of Socket for a connectionless
// datagram socket. // datagram socket.
// //
class GNet::DatagramSocket : public Socket class GNet::DatagramSocket : public GNet:: Socket
{ {
public: public:
DatagramSocket(); DatagramSocket();

View File

@ -61,8 +61,8 @@ public:
// Returns false on error. // Returns false on error.
bool attach( HWND hwnd , unsigned int msg ) ; bool attach( HWND hwnd , unsigned int msg ) ;
// Initialises the WinSock library, passin // Initialises the WinSock library, passing
// it the specified window handle an // it the specified window handle and
// message number. WinSock events are sent // message number. WinSock events are sent
// to that window. Returns false on error. // to that window. Returns false on error.
// //
@ -88,7 +88,7 @@ public:
// 'msg' parameter. // 'msg' parameter.
virtual void run() ; virtual void run() ;
// Override from EventSources. Calls GGui::Pump::run() // Override from EventSources. Calls GGui::Pump::run().
virtual void quit() ; virtual void quit() ;
// Override from EventSources. Calls GGui::Pump::quit(). // Override from EventSources. Calls GGui::Pump::quit().

View File

@ -24,28 +24,45 @@ AM_INSTALL_PROGRAM_FLAGS=-s
# change the local-state directory from .../var to .../var/spool/emailrelay # change the local-state directory from .../var to .../var/spool/emailrelay
localstatedir = ${prefix}/var/spool/emailrelay localstatedir = ${prefix}/var/spool/emailrelay
EXTRA_DIST=gmessagestore_win32.cpp emailrelay.dsp empty_file doxygen.cfg EXTRA_DIST=gmessagestore_win32.cpp emailrelay.dsp icon-32.ico empty_file doxygen.cfg emailrelay.rc
INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib -I$(top_srcdir)/src/gnet INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib -I$(top_srcdir)/src/gnet
sbin_PROGRAMS = emailrelay sbin_PROGRAMS = emailrelay
libexec_PROGRAMS = emailrelay-poke libexec_PROGRAMS = emailrelay-poke
localstate_DATA = empty_file localstate_DATA = empty_file
emailrelay_SOURCES = gadminserver.cpp \ emailrelay_SOURCES = \
gclientprotocol.cpp \ gadminserver.cpp \
gmessagestore.cpp \
gmessagestore_unix.cpp \
gprotocolmessage.cpp \
gserverprotocol.cpp \
gsmtpclient.cpp \
gsmtpserver.cpp \
mailrelay.cpp \
gadminserver.h \ gadminserver.h \
gclientprotocol.cpp \
gclientprotocol.h \ gclientprotocol.h \
gfilestore.cpp \
gfilestore.h \
gmessagestore.cpp \
gmessagestore.h \ gmessagestore.h \
gmessagestore_unix.cpp \
gnewfile.cpp \
gnewfile.h \
gnewmessage.cpp \
gnewmessage.h \
gprotocolmessage.cpp \
gprotocolmessage.h \ gprotocolmessage.h \
gprotocolmessageforward.cpp \
gprotocolmessageforward.h \
gprotocolmessagestore.cpp \
gprotocolmessagestore.h \
gserverprotocol.cpp \
gserverprotocol.h \ gserverprotocol.h \
gsmtp.h \ gsmtp.h \
gsmtpclient.cpp \
gsmtpclient.h \ gsmtpclient.h \
gsmtpserver.h gsmtpserver.cpp \
gsmtpserver.h \
gstoredfile.cpp \
gstoredfile.h \
gstoredmessage.cpp \
gstoredmessage.h \
gverifier.cpp \
gverifier.h \
mailrelay.cpp
emailrelay_poke_SOURCES=poke.c emailrelay_poke_SOURCES=poke.c
emailrelay_LDADD = $(top_builddir)/src/glib/libglib.a $(top_builddir)/src/gnet/libgnet.a emailrelay_LDADD = $(top_builddir)/src/glib/libglib.a $(top_builddir)/src/gnet/libgnet.a

View File

@ -95,12 +95,12 @@ AM_INSTALL_PROGRAM_FLAGS = -s
# change the local-state directory from .../var to .../var/spool/emailrelay # change the local-state directory from .../var to .../var/spool/emailrelay
localstatedir = ${prefix}/var/spool/emailrelay localstatedir = ${prefix}/var/spool/emailrelay
EXTRA_DIST = gmessagestore_win32.cpp emailrelay.dsp empty_file doxygen.cfg EXTRA_DIST = gmessagestore_win32.cpp emailrelay.dsp icon-32.ico empty_file doxygen.cfg emailrelay.rc
INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib -I$(top_srcdir)/src/gnet INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib -I$(top_srcdir)/src/gnet
sbin_PROGRAMS = emailrelay sbin_PROGRAMS = emailrelay
libexec_PROGRAMS = emailrelay-poke libexec_PROGRAMS = emailrelay-poke
localstate_DATA = empty_file localstate_DATA = empty_file
emailrelay_SOURCES = gadminserver.cpp gclientprotocol.cpp gmessagestore.cpp gmessagestore_unix.cpp gprotocolmessage.cpp gserverprotocol.cpp gsmtpclient.cpp gsmtpserver.cpp mailrelay.cpp gadminserver.h gclientprotocol.h gmessagestore.h gprotocolmessage.h gserverprotocol.h gsmtp.h gsmtpclient.h gsmtpserver.h emailrelay_SOURCES = gadminserver.cpp gadminserver.h gclientprotocol.cpp gclientprotocol.h gfilestore.cpp gfilestore.h gmessagestore.cpp gmessagestore.h gmessagestore_unix.cpp gnewfile.cpp gnewfile.h gnewmessage.cpp gnewmessage.h gprotocolmessage.cpp gprotocolmessage.h gprotocolmessageforward.cpp gprotocolmessageforward.h gprotocolmessagestore.cpp gprotocolmessagestore.h gserverprotocol.cpp gserverprotocol.h gsmtp.h gsmtpclient.cpp gsmtpclient.h gsmtpserver.cpp gsmtpserver.h gstoredfile.cpp gstoredfile.h gstoredmessage.cpp gstoredmessage.h gverifier.cpp gverifier.h mailrelay.cpp
emailrelay_poke_SOURCES = poke.c emailrelay_poke_SOURCES = poke.c
emailrelay_LDADD = $(top_builddir)/src/glib/libglib.a $(top_builddir)/src/gnet/libgnet.a emailrelay_LDADD = $(top_builddir)/src/glib/libglib.a $(top_builddir)/src/gnet/libgnet.a
@ -118,9 +118,11 @@ emailrelay_poke_OBJECTS = poke.o
emailrelay_poke_LDADD = $(LDADD) emailrelay_poke_LDADD = $(LDADD)
emailrelay_poke_DEPENDENCIES = emailrelay_poke_DEPENDENCIES =
emailrelay_poke_LDFLAGS = emailrelay_poke_LDFLAGS =
emailrelay_OBJECTS = gadminserver.o gclientprotocol.o gmessagestore.o \ emailrelay_OBJECTS = gadminserver.o gclientprotocol.o gfilestore.o \
gmessagestore_unix.o gprotocolmessage.o gserverprotocol.o gsmtpclient.o \ gmessagestore.o gmessagestore_unix.o gnewfile.o gnewmessage.o \
gsmtpserver.o mailrelay.o gprotocolmessage.o gprotocolmessageforward.o gprotocolmessagestore.o \
gserverprotocol.o gsmtpclient.o gsmtpserver.o gstoredfile.o \
gstoredmessage.o gverifier.o mailrelay.o
emailrelay_DEPENDENCIES = $(top_builddir)/src/glib/libglib.a \ emailrelay_DEPENDENCIES = $(top_builddir)/src/glib/libglib.a \
$(top_builddir)/src/gnet/libgnet.a $(top_builddir)/src/gnet/libgnet.a
emailrelay_LDFLAGS = emailrelay_LDFLAGS =
@ -306,10 +308,10 @@ gadminserver.o: gadminserver.cpp ../../src/glib/gdef.h ../../config.h \
../../src/glib/gexception.h ../../src/gnet/gevent.h \ ../../src/glib/gexception.h ../../src/gnet/gevent.h \
../../src/gnet/gdescriptor.h ../../src/gnet/gselect.h \ ../../src/gnet/gdescriptor.h ../../src/gnet/gselect.h \
../../src/gnet/glinebuffer.h ../../src/glib/gstrings.h \ ../../src/gnet/glinebuffer.h ../../src/glib/gstrings.h \
gserverprotocol.h gprotocolmessage.h gsmtpclient.h \ gserverprotocol.h gprotocolmessage.h gverifier.h gsmtpclient.h \
../../src/gnet/gclient.h gclientprotocol.h gmessagestore.h \ ../../src/gnet/gclient.h gclientprotocol.h gmessagestore.h \
../../src/glib/gpath.h ../../src/glib/gstr.h \ gnewmessage.h gstoredmessage.h ../../src/glib/gpath.h \
../../src/glib/gmemory.h ../../src/glib/gstr.h ../../src/glib/gmemory.h
gclientprotocol.o: gclientprotocol.cpp ../../src/glib/gdef.h \ gclientprotocol.o: gclientprotocol.cpp ../../src/glib/gdef.h \
../../config.h ../../lib/gcc2.95/iostream \ ../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \ ../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
@ -319,32 +321,76 @@ gclientprotocol.o: gclientprotocol.cpp ../../src/glib/gdef.h \
../../src/glib/gfile.h ../../src/glib/gpath.h \ ../../src/glib/gfile.h ../../src/glib/gpath.h \
../../src/glib/gstrings.h ../../src/glib/gstr.h \ ../../src/glib/gstrings.h ../../src/glib/gstr.h \
../../src/glib/gmemory.h gclientprotocol.h gmessagestore.h \ ../../src/glib/gmemory.h gclientprotocol.h gmessagestore.h \
../../src/gnet/gresolve.h ../../src/glib/gassert.h \ gnewmessage.h gstoredmessage.h ../../src/gnet/gresolve.h \
../../src/glib/gassert.h ../../src/glib/glogoutput.h
gfilestore.o: gfilestore.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \
../../src/gnet/gnet.h ../../src/glib/glog.h gfilestore.h \
gmessagestore.h gnewmessage.h gstoredmessage.h \
../../src/glib/gstrings.h ../../src/glib/gpath.h \
../../src/glib/gexception.h gnewfile.h gstoredfile.h \
../../src/glib/gprocess.h ../../src/glib/gdirectory.h \
../../src/glib/gmemory.h ../../src/glib/gfile.h \
../../src/glib/gstr.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h ../../src/glib/glogoutput.h
gmessagestore.o: gmessagestore.cpp ../../src/glib/gdef.h ../../config.h \ gmessagestore.o: gmessagestore.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \ ../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \ ../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \
../../src/gnet/gnet.h ../../src/glib/glog.h \ ../../src/gnet/gnet.h ../../src/glib/glog.h gmessagestore.h \
../../src/glib/gpid.h ../../src/glib/gdirectory.h \ gnewmessage.h gstoredmessage.h ../../src/glib/gstrings.h \
../../src/glib/gpath.h ../../src/glib/gstrings.h \ ../../src/glib/gpath.h ../../src/glib/gexception.h \
gmessagestore.h ../../src/glib/gexception.h \ ../../src/glib/gassert.h ../../src/glib/glogoutput.h
../../src/glib/gmemory.h ../../src/glib/gfile.h \
../../src/glib/gstr.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h
gmessagestore_unix.o: gmessagestore_unix.cpp ../../src/glib/gdef.h \ gmessagestore_unix.o: gmessagestore_unix.cpp ../../src/glib/gdef.h \
../../config.h ../../lib/gcc2.95/iostream \ ../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \ ../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \ ../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \
../../src/glib/glog.h gmessagestore.h \ ../../src/glib/glog.h gmessagestore.h gnewmessage.h \
../../src/glib/gexception.h ../../src/glib/gstrings.h \ gstoredmessage.h ../../src/glib/gstrings.h \
../../src/glib/gpath.h ../../src/glib/gpath.h ../../src/glib/gexception.h
gnewfile.o: gnewfile.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \
../../src/gnet/gnet.h ../../src/glib/glog.h gmessagestore.h \
gnewmessage.h gstoredmessage.h ../../src/glib/gstrings.h \
../../src/glib/gpath.h ../../src/glib/gexception.h gnewfile.h \
gfilestore.h ../../src/glib/gmemory.h ../../src/glib/gprocess.h \
../../src/glib/gfile.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h
gnewmessage.o: gnewmessage.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \
../../src/gnet/gnet.h ../../src/glib/glog.h gmessagestore.h \
gnewmessage.h gstoredmessage.h ../../src/glib/gstrings.h \
../../src/glib/gpath.h ../../src/glib/gexception.h
gprotocolmessage.o: gprotocolmessage.cpp ../../src/glib/gdef.h \ gprotocolmessage.o: gprotocolmessage.cpp ../../src/glib/gdef.h \
../../config.h ../../lib/gcc2.95/iostream \ ../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \ ../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \ ../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \
../../src/glib/glog.h gprotocolmessage.h \ ../../src/glib/glog.h gprotocolmessage.h \
../../src/glib/gstrings.h gmessagestore.h \ ../../src/glib/gstrings.h gverifier.h
../../src/glib/gexception.h ../../src/glib/gpath.h \ gprotocolmessageforward.o: gprotocolmessageforward.cpp \
../../src/glib/gdef.h ../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \
../../src/glib/glog.h gprotocolmessageforward.h \
gprotocolmessage.h ../../src/glib/gstrings.h gverifier.h \
gprotocolmessagestore.h gnewmessage.h gsmtpclient.h \
../../src/gnet/glinebuffer.h ../../src/gnet/gclient.h \
../../src/gnet/gaddress.h ../../src/glib/gexception.h \
../../src/gnet/gsocket.h ../../src/gnet/gevent.h \
../../src/gnet/gdescriptor.h gclientprotocol.h gmessagestore.h \
gstoredmessage.h ../../src/glib/gpath.h \
../../src/glib/gmemory.h ../../src/glib/gstr.h \
../../src/glib/gassert.h ../../src/glib/glogoutput.h
gprotocolmessagestore.o: gprotocolmessagestore.cpp ../../src/glib/gdef.h \
../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \
../../src/glib/glog.h gprotocolmessagestore.h \
gprotocolmessage.h ../../src/glib/gstrings.h gverifier.h \
gnewmessage.h gmessagestore.h gstoredmessage.h \
../../src/glib/gpath.h ../../src/glib/gexception.h \
../../src/glib/gmemory.h ../../src/glib/gstr.h \ ../../src/glib/gmemory.h ../../src/glib/gstr.h \
../../src/glib/gassert.h ../../src/glib/glogoutput.h ../../src/glib/gassert.h ../../src/glib/glogoutput.h
gserverprotocol.o: gserverprotocol.cpp ../../src/glib/gdef.h \ gserverprotocol.o: gserverprotocol.cpp ../../src/glib/gdef.h \
@ -352,7 +398,7 @@ gserverprotocol.o: gserverprotocol.cpp ../../src/glib/gdef.h \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \ ../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \ ../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \
../../src/glib/glog.h gserverprotocol.h gprotocolmessage.h \ ../../src/glib/glog.h gserverprotocol.h gprotocolmessage.h \
../../src/glib/gstrings.h ../../src/glib/gdate.h \ ../../src/glib/gstrings.h gverifier.h ../../src/glib/gdate.h \
../../src/glib/gdatetime.h ../../src/glib/gexception.h \ ../../src/glib/gdatetime.h ../../src/glib/gexception.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \ ../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/gassert.h ../../src/glib/gtime.h \ ../../src/glib/gassert.h ../../src/glib/gtime.h \
@ -368,7 +414,8 @@ gsmtpclient.o: gsmtpclient.cpp ../../src/glib/gdef.h ../../config.h \
../../src/gnet/glinebuffer.h ../../src/gnet/gclient.h \ ../../src/gnet/glinebuffer.h ../../src/gnet/gclient.h \
../../src/gnet/gsocket.h ../../src/gnet/gevent.h \ ../../src/gnet/gsocket.h ../../src/gnet/gevent.h \
../../src/gnet/gdescriptor.h gclientprotocol.h gmessagestore.h \ ../../src/gnet/gdescriptor.h gclientprotocol.h gmessagestore.h \
../../src/gnet/gresolve.h gnewmessage.h gstoredmessage.h ../../src/gnet/gresolve.h \
../../src/glib/gassert.h ../../src/glib/glogoutput.h
gsmtpserver.o: gsmtpserver.cpp ../../src/glib/gdef.h ../../config.h \ gsmtpserver.o: gsmtpserver.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \ ../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \ ../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \
@ -377,9 +424,37 @@ gsmtpserver.o: gsmtpserver.cpp ../../src/glib/gdef.h ../../config.h \
../../src/gnet/gaddress.h ../../src/glib/gexception.h \ ../../src/gnet/gaddress.h ../../src/glib/gexception.h \
../../src/gnet/gevent.h ../../src/gnet/gdescriptor.h \ ../../src/gnet/gevent.h ../../src/gnet/gdescriptor.h \
../../src/gnet/gselect.h ../../src/gnet/glinebuffer.h \ ../../src/gnet/gselect.h ../../src/gnet/glinebuffer.h \
../../src/glib/gstrings.h gserverprotocol.h gprotocolmessage.h \ ../../src/glib/gstrings.h gverifier.h gserverprotocol.h \
../../src/gnet/glocal.h ../../src/glib/gdebug.h \ gprotocolmessage.h gprotocolmessagestore.h gnewmessage.h \
../../src/glib/glogoutput.h ../../src/glib/gassert.h gprotocolmessageforward.h gsmtpclient.h \
../../src/gnet/gclient.h gclientprotocol.h gmessagestore.h \
gstoredmessage.h ../../src/glib/gpath.h \
../../src/glib/gmemory.h ../../src/gnet/glocal.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/gassert.h
gstoredfile.o: gstoredfile.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \
../../src/gnet/gnet.h ../../src/glib/glog.h gfilestore.h \
gmessagestore.h gnewmessage.h gstoredmessage.h \
../../src/glib/gstrings.h ../../src/glib/gpath.h \
../../src/glib/gexception.h gstoredfile.h \
../../src/glib/gmemory.h ../../src/glib/gfile.h \
../../src/glib/gstr.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h
gstoredmessage.o: gstoredmessage.cpp ../../src/glib/gdef.h \
../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gsmtp.h ../../src/gnet/gnet.h \
../../src/glib/glog.h gstoredmessage.h \
../../src/glib/gstrings.h ../../src/glib/gpath.h
gverifier.o: gverifier.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \
../../src/gnet/gnet.h ../../src/glib/glog.h gverifier.h \
../../src/glib/gstr.h ../../src/glib/gexception.h \
../../src/glib/gstrings.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h
mailrelay.o: mailrelay.cpp ../../src/glib/gdef.h ../../config.h \ mailrelay.o: mailrelay.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \ ../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \ ../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gsmtp.h \
@ -388,12 +463,15 @@ mailrelay.o: mailrelay.cpp ../../src/glib/gdef.h ../../config.h \
../../src/gnet/gaddress.h ../../src/glib/gexception.h \ ../../src/gnet/gaddress.h ../../src/glib/gexception.h \
../../src/gnet/gevent.h ../../src/gnet/gdescriptor.h \ ../../src/gnet/gevent.h ../../src/gnet/gdescriptor.h \
../../src/gnet/gselect.h ../../src/gnet/glinebuffer.h \ ../../src/gnet/gselect.h ../../src/gnet/glinebuffer.h \
../../src/glib/gstrings.h gserverprotocol.h gprotocolmessage.h \ ../../src/glib/gstrings.h gverifier.h gserverprotocol.h \
gsmtpclient.h ../../src/gnet/gclient.h gclientprotocol.h \ gprotocolmessage.h gsmtpclient.h ../../src/gnet/gclient.h \
gmessagestore.h ../../src/glib/gpath.h ../../src/glib/garg.h \ gclientprotocol.h gmessagestore.h gnewmessage.h \
../../src/glib/gdaemon.h ../../src/glib/gstr.h gadminserver.h \ gstoredmessage.h ../../src/glib/gpath.h ../../src/glib/garg.h \
../../src/glib/ggetopt.h ../../src/glib/gdebug.h \ ../../src/glib/gdaemon.h ../../src/glib/gstr.h gfilestore.h \
../../src/glib/glogoutput.h ../../src/glib/gassert.h gnewfile.h gadminserver.h ../../src/glib/gprocess.h \
../../src/glib/gmemory.h ../../src/glib/ggetopt.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/gassert.h
poke.o: poke.c poke.o: poke.c
info-am: info-am:

View File

@ -3,7 +3,7 @@
# General configuration options # General configuration options
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
PROJECT_NAME = E-MailRelay PROJECT_NAME = E-MailRelay
PROJECT_NUMBER = 0.9.2 PROJECT_NUMBER = 0.9.3
OUTPUT_DIRECTORY = OUTPUT_DIRECTORY =
OUTPUT_LANGUAGE = English OUTPUT_LANGUAGE = English
EXTRACT_ALL = YES EXTRACT_ALL = YES
@ -71,7 +71,7 @@ EXCLUDE_PATTERNS = */*_win32.* */*_ipv6.cpp */old/* */resolverd.cpp
EXAMPLE_PATH = EXAMPLE_PATH =
EXAMPLE_PATTERNS = EXAMPLE_PATTERNS =
IMAGE_PATH = IMAGE_PATH =
INPUT_FILTER = __TOP_BUILD__/bin/emailrelay-filter.sh INPUT_FILTER = __TOP_BUILD__/bin/emailrelay-doxygen-filter.sh
FILTER_SOURCE_FILES = NO FILTER_SOURCE_FILES = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# configuration options related to the alphabetical class index # configuration options related to the alphabetical class index
@ -84,7 +84,7 @@ IGNORE_PREFIX =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
GENERATE_HTML = GENERATE_HTML =
HTML_OUTPUT = html HTML_OUTPUT = html
HTML_HEADER = HTML_HEADER = __TOP_SRC__/doc/doxygen_header.html
HTML_FOOTER = HTML_FOOTER =
HTML_STYLESHEET = HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = HTML_ALIGN_MEMBERS =

File diff suppressed because it is too large Load Diff

2
src/main/emailrelay.rc Normal file
View File

@ -0,0 +1,2 @@
#define IDI_ICON1 101
IDI_ICON1 ICON DISCARDABLE "icon-32.ico"

View File

@ -44,7 +44,7 @@ GSmtp::AdminPeer::AdminPeer( GNet::StreamSocket * s , GNet::Address a , AdminSer
{ {
} }
void GSmtp::AdminPeer::onCompletion( std::string s ) void GSmtp::AdminPeer::clientDone( std::string s )
{ {
if( s.empty() ) if( s.empty() )
send( "OK" ) ; send( "OK" ) ;

View File

@ -43,7 +43,7 @@ namespace GSmtp
// Class: GSmtp::AdminClient // Class: GSmtp::AdminClient
// Description: A private implementation class. // Description: A private implementation class.
// //
class GSmtp::AdminClient : public GSmtp::Client class GSmtp::AdminClient : public GSmtp:: Client
{ {
public: public:
AdminClient( AdminPeer & admin_peer ) ; AdminClient( AdminPeer & admin_peer ) ;
@ -55,7 +55,7 @@ public:
// Description: A derivation of ServerPeer for the administration interface. // Description: A derivation of ServerPeer for the administration interface.
// See also: AdminServer // See also: AdminServer
// //
class GSmtp::AdminPeer : public GNet::ServerPeer , public GSmtp::Client::ClientCallback class GSmtp::AdminPeer : public GNet::ServerPeer , public GSmtp:: Client::ClientCallback
{ {
public: public:
AdminPeer( GNet::StreamSocket * , GNet::Address , AdminServer & server , const std::string & ) ; AdminPeer( GNet::StreamSocket * , GNet::Address , AdminServer & server , const std::string & ) ;
@ -66,7 +66,7 @@ private:
void operator=( const AdminPeer & ) ; void operator=( const AdminPeer & ) ;
virtual void onDelete() ; // from GNet::ServerPeer virtual void onDelete() ; // from GNet::ServerPeer
virtual void onData( const char * , size_t ) ; // from GNet::ServerPeer virtual void onData( const char * , size_t ) ; // from GNet::ServerPeer
virtual void onCompletion( std::string ) ; // from Client::ClientCallback virtual void clientDone( std::string ) ; // from Client::ClientCallback
bool processLine( const std::string & line ) ; bool processLine( const std::string & line ) ;
static bool is( const std::string & , const char * ) ; static bool is( const std::string & , const char * ) ;
void flush( const std::string & ) ; void flush( const std::string & ) ;

View File

@ -74,7 +74,7 @@ bool GSmtp::ClientProtocol::done() const
return m_state == sEnd ; return m_state == sEnd ;
} }
void GSmtp::ClientProtocol::sendComplete() void GSmtp::ClientProtocol::sendDone()
{ {
if( m_state == sData ) if( m_state == sData )
{ {
@ -199,6 +199,12 @@ void GSmtp::ClientProtocol::applyEvent( const Reply & reply )
m_state = sSentData ; m_state = sSentData ;
send( std::string("DATA") ) ; send( std::string("DATA") ) ;
} }
else if( m_state == sSentRcpt )
{
G_WARNING( "GSmtp::ClientProtocol: recipient rejected" ) ;
m_state = sEnd ;
doCallback( false , reply.text() ) ;
}
else if( m_state == sSentData && reply.is(Reply::OkForData_354) ) else if( m_state == sSentData && reply.is(Reply::OkForData_354) )
{ {
m_state = sData ; m_state = sData ;
@ -214,21 +220,28 @@ void GSmtp::ClientProtocol::applyEvent( const Reply & reply )
send( "." , true ) ; send( "." , true ) ;
} }
} }
else if( m_state == sDone && reply.is(Reply::Ok_250) ) else if( m_state == sDone )
{ {
const bool ok = reply.is(Reply::Ok_250) ;
m_state = sEnd ; m_state = sEnd ;
if( m_callback ) doCallback( ok , ok ? std::string() : reply.text() ) ;
{
Callback * cb = m_callback ;
m_callback = NULL ;
cb->callback( true ) ;
}
} }
else else
{ {
G_WARNING( "GSmtp::ClientProtocol: protocol error: " << static_cast<int>(m_state) ) ; G_WARNING( "GSmtp::ClientProtocol: failure in client protocol: " << static_cast<int>(m_state) ) ;
m_state = sReset ; m_state = sEnd ; // (was sReset)
send( "RSET" ) ; send( "RSET" ) ; // for good meausre
doCallback( false , std::string("unexpected response: ")+reply.text() ) ;
}
}
void GSmtp::ClientProtocol::doCallback( bool ok , const std::string & reason )
{
if( m_callback )
{
Callback * cb = m_callback ;
m_callback = NULL ;
cb->protocolDone( ok , reason ) ;
} }
} }
@ -359,3 +372,15 @@ bool GSmtp::ClientProtocolReply::textContains( std::string key ) const
return m_text.find(key) != std::string::npos ; return m_text.find(key) != std::string::npos ;
} }
// ===
GSmtp::ClientProtocol::Sender::~Sender()
{
}
// ===
GSmtp::ClientProtocol::Callback::~Callback()
{
}

View File

@ -111,22 +111,22 @@ public:
// //
// Returns false if not all of the string // Returns false if not all of the string
// was sent, either due to flow control // was sent, either due to flow control
// or disconnection. After false os returned // or disconnection. After false is returned
// the user should call sendComplete() once // the user should call sendDone() once
// the full string has been sent. // the full string has been sent.
private: void operator=( const Sender & ) ; private: void operator=( const Sender & ) ; // not implemented
public: virtual ~Sender() {} public: virtual ~Sender() ;
} ; } ;
class Callback // A callback interface used by ClientProtocol. class Callback // A callback interface used by ClientProtocol.
{ {
public: virtual void callback( bool ok ) = 0 ; public: virtual void protocolDone( bool ok , const std::string & reason ) = 0 ;
// Called once the protocol has finished with // Called once the protocol has finished with
// a given message. See ClientProtocol::start(). // a given message. See ClientProtocol::start().
private: void operator=( const Callback & ) ; private: void operator=( const Callback & ) ; // not implemented
public: virtual ~Callback() {} public: virtual ~Callback() ;
} ; } ;
ClientProtocol( Sender & sender , const std::string & thishost ) ; ClientProtocol( Sender & sender , const std::string & thishost ) ;
@ -142,7 +142,7 @@ public:
// signal that the message has been // signal that the message has been
// processed. // processed.
void sendComplete() ; void sendDone() ;
// Called when a blocked connection becomes unblocked. // Called when a blocked connection becomes unblocked.
// See ClientProtocol::Sender::protocolSend(). // See ClientProtocol::Sender::protocolSend().
@ -160,6 +160,7 @@ private:
static std::string crlf() ; static std::string crlf() ;
void applyEvent( const Reply & event ) ; void applyEvent( const Reply & event ) ;
static bool parseReply( Reply & , const std::string & , std::string & ) ; static bool parseReply( Reply & , const std::string & , std::string & ) ;
void doCallback( bool , const std::string & ) ;
private: private:
enum State { sStart , sSentEhlo , sSentHelo , sSentMail , enum State { sStart , sSentEhlo , sSentHelo , sSentMail ,

226
src/main/gfilestore.cpp Normal file
View File

@ -0,0 +1,226 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gfilestore.cpp
//
#include "gdef.h"
#include "gsmtp.h"
#include "gfilestore.h"
#include "gnewfile.h"
#include "gstoredfile.h"
#include "gprocess.h"
#include "gdirectory.h"
#include "gmemory.h"
#include "gpath.h"
#include "gfile.h"
#include "gstr.h"
#include "glog.h"
#include "gassert.h"
#include <iostream>
#include <fstream>
namespace GSmtp
{
class FileIterator ;
} ;
// Class: FileIterator
// Description: A 'body' class for the MessageStore::Iterator
// 'handle'. The handle/body pattern allows us to copy
// iterators by value, and therefore return them
// from MessageStore::iterator().
//
class GSmtp::FileIterator : public GSmtp:: MessageStore::IteratorImp
{
public:
explicit FileIterator( const G::Directory & dir ) ;
virtual std::auto_ptr<GSmtp::StoredMessage> next() ;
private:
G::DirectoryIterator m_iter ;
private:
FileIterator( const FileIterator & ) ;
void operator=( const FileIterator & ) ;
} ;
// ===
GSmtp::FileIterator::FileIterator( const G::Directory & dir ) :
m_iter( dir , "*.envelope" )
{
}
std::auto_ptr<GSmtp::StoredMessage> GSmtp::FileIterator::next()
{
while( !m_iter.error() && m_iter.more() )
{
std::auto_ptr<StoredFile> m( new StoredFile(m_iter.filePath()) ) ;
if( m->lock() )
{
std::string reason ;
const bool check = true ; // check for no-remote-recipients
if( m->readEnvelope(reason,check) && m->openContent(reason) )
return std::auto_ptr<StoredMessage>( m.release() ) ;
m->fail( reason ) ;
}
G_WARNING( "GSmtp::MessageStore: cannot process file: \"" << m_iter.filePath() << "\"" ) ;
}
return std::auto_ptr<StoredMessage>(NULL) ;
}
// ===
GSmtp::FileStore::FileStore( const G::Path & dir , bool optimise ) :
m_seq(1UL) ,
m_dir(dir) ,
m_optimise(optimise) ,
m_empty(false)
{
checkPath( dir ) ;
}
//static
std::string GSmtp::FileStore::x()
{
return "X-MailRelay-" ;
}
//static
std::string GSmtp::FileStore::format()
{
return "#2821.2" ;
}
//static
void GSmtp::FileStore::checkPath( const G::Path & directory_path )
{
// (void) G::File::mkdir( directory_path ) ;
G::Directory dir_test( directory_path ) ;
if( ! dir_test.valid() )
{
throw InvalidDirectory( directory_path.str() ) ;
}
if( ! dir_test.valid(true) )
{
G_WARNING( "GSmtp::MessageStore: "
<< "directory not writable: \""
<< directory_path << "\"" ) ;
}
}
std::auto_ptr<std::ostream> GSmtp::FileStore::stream( const G::Path & path )
{
std::auto_ptr<std::ostream> ptr(
new std::ofstream( path.pathCstr() ,
std::ios_base::binary | std::ios_base::out | std::ios_base::trunc ) ) ;
return ptr ;
}
G::Path GSmtp::FileStore::contentPath( unsigned long seq ) const
{
return fullPath( filePrefix(seq) + ".content" ) ;
}
G::Path GSmtp::FileStore::envelopePath( unsigned long seq ) const
{
return fullPath( filePrefix(seq) + ".envelope" ) ;
}
G::Path GSmtp::FileStore::envelopeWorkingPath( unsigned long seq ) const
{
return fullPath( filePrefix(seq) + ".envelope.new" ) ;
}
std::string GSmtp::FileStore::filePrefix( unsigned long seq ) const
{
std::stringstream ss ;
ss << "emailrelay." << G::Process::Id() << "." << seq ;
return ss.str() ;
}
G::Path GSmtp::FileStore::fullPath( const std::string & filename ) const
{
G::Path p( m_dir ) ;
p.pathAppend( filename ) ;
return p ;
}
unsigned long GSmtp::FileStore::newSeq()
{
return m_seq++ ;
}
bool GSmtp::FileStore::empty() const
{
if( m_optimise )
{
if( !m_empty )
const_cast<FileStore*>(this)->m_empty = emptyCore() ;
return m_empty ;
}
else
{
return emptyCore() ;
}
}
bool GSmtp::FileStore::emptyCore() const
{
G::Directory dir( m_dir ) ;
G::DirectoryIterator iter( dir , "*.envelope" ) ;
const bool no_more = iter.error() || !iter.more() ;
return no_more ;
}
GSmtp::MessageStore::Iterator GSmtp::FileStore::iterator()
{
return MessageStore::Iterator( new FileIterator(G::Directory(m_dir) ) ) ;
}
std::auto_ptr<GSmtp::StoredMessage> GSmtp::FileStore::get( unsigned long id )
{
G::Path path = envelopePath( id ) ;
std::auto_ptr<StoredFile> message( new StoredFile(path) ) ;
if( ! message->lock() )
throw GetError( path.str() + ": cannot lock the file" ) ;
std::string reason ;
const bool check = false ; // don't check for no-remote-recipients
if( ! message->readEnvelope(reason,check) )
throw GetError( path.str() + ": cannot read the envelope: " + reason ) ;
if( ! message->openContent(reason) )
throw GetError( path.str() + ": cannot read the content: " + reason ) ;
std::auto_ptr<StoredMessage> message_base_ptr( message.release() ) ;
return message_base_ptr ;
}
std::auto_ptr<GSmtp::NewMessage> GSmtp::FileStore::newMessage( const std::string & from )
{
m_empty = false ;
return std::auto_ptr<NewMessage>( new NewFile(from,*this) ) ;
}

116
src/main/gfilestore.h Normal file
View File

@ -0,0 +1,116 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gfilestore.h
//
#ifndef G_SMTP_FILE_STORE_H
#define G_SMTP_FILE_STORE_H
#include "gdef.h"
#include "gsmtp.h"
#include "gmessagestore.h"
#include "gexception.h"
#include "gpath.h"
#include <memory>
#include <string>
namespace GSmtp
{
class FileStore ;
} ;
// Class: GSmtp::FileStore
// Description: A concrete implementation of the MessageStore
// interface, dealing in flat files. Passes out unique sequence
// numbers, filesystem paths and i/o streams to NewMessageImp.
//
class GSmtp::FileStore : public GSmtp:: MessageStore
{
public:
G_EXCEPTION( InvalidDirectory , "invalid spool directory" ) ;
G_EXCEPTION( GetError , "error reading specific message" ) ;
explicit FileStore( const G::Path & dir , bool optimise = false ) ;
// Constructor. Throws an exception if the
// storage directory is invalid.
//
// If the optimise flag is set then the implementation
// of empty() will be efficient for an empty file
// store (ignoring failed and local-delivery messages).
// This might be useful for applications in which the
// main event loop is used to check for pending jobs.
// The disadvantage is that this process will not be
// sensititive to messages deposited into its spool
// directory by other processes.
unsigned long newSeq() ;
// Hands out a new sequence number.
std::auto_ptr<std::ostream> stream( const G::Path & path );
// Returns a stream to the given content.
G::Path contentPath( unsigned long seq ) const ;
// Returns the path for a content file.
G::Path envelopePath( unsigned long seq ) const ;
// Returns the path for an envelope file.
G::Path envelopeWorkingPath( unsigned long seq ) const ;
// Returns the path for an envelope file
// which is in the process of being written.
virtual bool empty() const ;
// Returns true if there are no stored messages.
virtual std::auto_ptr<StoredMessage> get( unsigned long id ) ;
// Extracts a stored message.
virtual MessageStore::Iterator iterator() ;
// Returns an iterator for stored messages.
virtual std::auto_ptr<NewMessage> newMessage( const std::string & from ) ;
// Creates a new message in the store.
static std::string x() ;
// Returns the prefix for envelope header lines.
static std::string format() ;
// Returns an identifier for the storage format
// implemented by this class.
private:
static void checkPath( const G::Path & dir ) ;
G::Path fullPath( const std::string & filename ) const ;
std::string filePrefix( unsigned long seq ) const ;
std::string getline( std::istream & ) const ;
std::string value( const std::string & ) const ;
std::string crlf() const ;
bool emptyCore() const ;
private:
unsigned long m_seq ;
G::Path m_dir ;
bool m_optimise ;
bool m_empty ; // mutable
} ;
#endif

View File

@ -23,178 +23,40 @@
#include "gdef.h" #include "gdef.h"
#include "gsmtp.h" #include "gsmtp.h"
#include "gpid.h"
#include "gdirectory.h"
#include "gmessagestore.h" #include "gmessagestore.h"
#include "gmemory.h"
#include "gpath.h"
#include "gfile.h"
#include "gstr.h"
#include "glog.h" #include "glog.h"
#include "gassert.h" #include "gassert.h"
#include <iostream>
#include <fstream>
// Class: MessageStoreImp GSmtp::MessageStore * GSmtp::MessageStore::m_this = NULL ;
// Description: Pimple-pattern implementation for MessageStore.
// Passes out unique sequence numbers, filesystem paths and GSmtp::MessageStore::MessageStore()
// i/o streams to NewMessageImp.
//
class GSmtp::MessageStoreImp
{ {
private: if( m_this == NULL )
G::Path m_dir ; m_this = this ;
unsigned long m_seq ; }
public:
MessageStoreImp( const G::Path & dir ) ;
// new message... GSmtp::MessageStore & GSmtp::MessageStore::instance()
static void checkPath( const G::Path & ) ; {
const G::Path & dir() const ; if( m_this == NULL )
unsigned long newSeq() ; throw NoInstance() ;
std::auto_ptr<std::ostream> stream( const G::Path & path ); return * m_this ;
G::Path contentPath( unsigned long seq ) const ; }
G::Path envelopePath( unsigned long seq ) const ;
G::Path envelopeWorkingPath( unsigned long seq ) const ;
// stored message... GSmtp::MessageStore::~MessageStore()
bool empty() const ; {
std::auto_ptr<StoredMessage> get() ; if( m_this == this )
MessageStore::Iterator iterator() ; m_this = NULL ;
}
// both...
static std::string x() ;
static std::string format() ;
private:
G::Path fullPath( const std::string & filename ) const ;
std::string filePrefix( unsigned long seq ) const ;
std::string getline( std::istream & ) const ;
std::string value( const std::string & ) const ;
std::string crlf() const ;
} ;
// === // ===
// Class: StoredMessageImp GSmtp::MessageStore::IteratorImp::IteratorImp() :
// Description: A concete derived class implementing the
// StoredMessage (parent) and Callback (grandparent) interfaces.
//
class GSmtp::StoredMessageImp : public GSmtp::StoredMessage
{
public:
G_EXCEPTION( InvalidFormat , "invalid format field in envelope" ) ;
G_EXCEPTION( NoEnd , "invalid envelope file: no end marker" ) ;
G_EXCEPTION( InvalidTo , "invalid 'to' line in envelope file" ) ;
G_EXCEPTION( NoRecipients , "no remote recipients" ) ;
G_EXCEPTION( OpenError , "cannot open the envelope" ) ;
G_EXCEPTION( StreamError , "envelope reading/parsing error" ) ;
explicit StoredMessageImp( const G::Path & envelope_path ) ;
virtual ~StoredMessageImp() ;
bool lock() ;
bool readEnvelope( std::string & reason ) ;
bool readEnvelopeCore( std::string & reason ) ;
bool openContent( std::string & reason ) ;
virtual bool eightBit() const ;
virtual const std::string & from() const ;
virtual const G::Strings & to() const ;
virtual void destroy() ;
virtual void fail( const std::string & reason ) ;
virtual std::auto_ptr<std::istream> extractContentStream() ;
private:
StoredMessageImp( const StoredMessageImp & ) ;
void operator=( const StoredMessageImp & ) ;
std::string crlf() const ;
std::string getline( std::istream & stream ) const ;
std::string value( const std::string & s ) const ;
G::Path contentPath() const ;
private:
G::Strings m_to_local ;
G::Strings m_to_remote ;
std::string m_from ;
G::Path m_envelope_path ;
std::auto_ptr<std::istream> m_content ;
bool m_eight_bit ;
} ;
// ===
// Class: NewMessageImp
// Description: A concrete derived class implementing the
// NewMessage interface. Writes itself to the i/o streams
// supplied by MessageStoreImp.
//
class GSmtp::NewMessageImp : public GSmtp::NewMessage
{
public:
NewMessageImp( const std::string & from , MessageStoreImp & store ) ;
virtual ~NewMessageImp() ;
virtual void addTo( const std::string & to , bool local ) ;
virtual void addText( const std::string & line ) ;
virtual void store() ;
private:
MessageStoreImp & m_store ;
unsigned long m_seq ;
std::string m_from ;
G::Strings m_to_local ;
G::Strings m_to_remote ;
std::auto_ptr<std::ostream> m_content ;
G::Path m_content_path ;
bool m_eight_bit ;
private:
bool saveEnvelope( std::ostream & stream , const std::string & where ) 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 & ) ;
} ;
// ===
// Class: MessageStoreIteratorImp
// Description: A 'body' class for the MessageStoreIterator
// 'handle'. The handle/body pattern allows us to copy
// iterators by value, and therefore return them
// from MessageStore::iterator().
//
class GSmtp::MessageStoreIteratorImp
{
public:
explicit MessageStoreIteratorImp( const G::Directory & dir ) ;
std::auto_ptr<GSmtp::StoredMessage> next() ;
public:
unsigned long m_ref_count ;
private:
G::DirectoryIterator m_iter ;
private:
MessageStoreIteratorImp( const MessageStoreIteratorImp & ) ;
void operator=( const MessageStoreIteratorImp & ) ;
} ;
// ===
GSmtp::MessageStoreIteratorImp::MessageStoreIteratorImp( const G::Directory & dir ) :
m_iter( dir , "*.envelope" ) ,
m_ref_count(1UL) m_ref_count(1UL)
{ {
} }
std::auto_ptr<GSmtp::StoredMessage> GSmtp::MessageStoreIteratorImp::next() GSmtp::MessageStore::IteratorImp::~IteratorImp()
{ {
while( !m_iter.error() && m_iter.more() )
{
std::auto_ptr<StoredMessageImp> m( new StoredMessageImp(m_iter.filePath()) ) ;
if( m->lock() )
{
std::string reason ;
if( m->readEnvelope(reason) && m->openContent(reason) )
return std::auto_ptr<StoredMessage>( m.release() ) ;
m->fail( reason ) ;
}
G_WARNING( "GSmtp::MessageStore: cannot process file: \"" << m_iter.filePath() << "\"" ) ;
}
return std::auto_ptr<StoredMessage>(NULL) ;
} }
// === // ===
@ -204,7 +66,7 @@ GSmtp::MessageStore::Iterator::Iterator() :
{ {
} }
GSmtp::MessageStore::Iterator::Iterator( MessageStoreIteratorImp * imp ) : GSmtp::MessageStore::Iterator::Iterator( IteratorImp * imp ) :
m_imp(imp) m_imp(imp)
{ {
G_ASSERT( m_imp->m_ref_count == 1UL ) ; G_ASSERT( m_imp->m_ref_count == 1UL ) ;
@ -251,505 +113,3 @@ GSmtp::MessageStore::Iterator & GSmtp::MessageStore::Iterator::operator=( const
return * this ; return * this ;
} }
// ===
GSmtp::StoredMessage::~StoredMessage()
{
// empty
}
// ===
GSmtp::NewMessage::~NewMessage()
{
// empty
}
// ===
GSmtp::MessageStore * GSmtp::MessageStore::m_this = NULL ;
GSmtp::MessageStore::MessageStore( const G::Path & directory_path ) :
m_imp(NULL)
{
if( m_this == NULL )
m_this = this ;
MessageStoreImp::checkPath( directory_path ) ;
m_imp = new MessageStoreImp( directory_path ) ;
}
GSmtp::MessageStore & GSmtp::MessageStore::instance()
{
if( m_this == NULL )
throw NoInstance() ;
return * m_this ;
}
GSmtp::MessageStore::~MessageStore()
{
if( m_this == this )
m_this = NULL ;
delete m_imp ;
}
G::Path GSmtp::MessageStore::directory() const
{
return m_imp->dir() ;
}
std::auto_ptr<GSmtp::NewMessage> GSmtp::MessageStore::newMessage( const std::string & from )
{
std::auto_ptr<NewMessage> new_message( new NewMessageImp(from,*m_imp) ) ;
return new_message ;
}
bool GSmtp::MessageStore::empty() const
{
return m_imp->empty() ;
}
std::auto_ptr<GSmtp::StoredMessage> GSmtp::MessageStore::get()
{
return m_imp->get() ;
}
GSmtp::MessageStore::Iterator GSmtp::MessageStore::iterator()
{
return m_imp->iterator() ;
}
// ===
GSmtp::NewMessageImp::NewMessageImp( const std::string & from , MessageStoreImp & store ) :
m_from(from) ,
m_store(store),
m_eight_bit(false)
{
m_seq = store.newSeq() ;
m_content_path = m_store.contentPath( m_seq ) ;
G_LOG( "GSmtp::NewMessage: content file: " << m_content_path ) ;
std::auto_ptr<std::ostream> content_stream = m_store.stream( m_content_path ) ;
m_content = content_stream ;
}
GSmtp::NewMessageImp::~NewMessageImp()
{
}
void GSmtp::NewMessageImp::addTo( const std::string & to , bool local )
{
if( local )
m_to_local.push_back( to ) ;
else
m_to_remote.push_back( to ) ;
}
void GSmtp::NewMessageImp::addText( const std::string & line )
{
if( ! m_eight_bit )
m_eight_bit = isEightBit(line) ;
*(m_content.get()) << line << crlf() ;
}
bool GSmtp::NewMessageImp::isEightBit( const std::string & line )
{
const size_t n = line.length() ;
for( size_t i = 0U ; i < n ; --i )
{
const unsigned char c = static_cast<unsigned char>(line.at(i)) ;
if( c > 0x7fU )
return true ;
}
return false ;
}
void GSmtp::NewMessageImp::store()
{
// flush the content file
m_content->flush() ;
if( ! m_content->good() )
throw GSmtp::MessageStore::WriteError( m_content_path.str() ) ;
m_content <<= 0 ;
// write the envelope
G::Path p0 = m_store.envelopeWorkingPath( m_seq ) ;
G::Path p1 = m_store.envelopePath( m_seq ) ;
bool ok = false ;
{
std::auto_ptr<std::ostream> envelope_stream = m_store.stream( p0 ) ;
ok = saveEnvelope( *(envelope_stream.get()) , p0.str() ) ;
}
// deliver to local mailboxes (in theory)
if( ok && m_to_local.size() != 0U )
{
deliver( m_to_local , m_content_path , p0 , p1 ) ;
}
// commit the envelope, or rollback the content
if( ! ok || ! G::File::rename(p0,p1,G::File::NoThrow() ) )
{
G_ASSERT( m_content_path.str().length() != 0U ) ;
G::File::remove( m_content_path , G::File::NoThrow() ) ;
throw GSmtp::MessageStore::WriteError( p0.str() ) ;
}
}
void GSmtp::NewMessageImp::deliver( const G::Strings & to ,
const G::Path & content_path , const G::Path & envelope_path_now ,
const G::Path & envelope_path_later )
{
// could shell out to "procmail" or "deliver" here, but keep it
// simple and within the scope of a "message-store" class
G_LOG( "GSmtp::NewMessage: copying message for local recipient(s): "
<< content_path.basename() << ".local" ) ;
G::File::copy( content_path.str() , content_path.str()+".local" ) ;
G::File::copy( envelope_path_now.str() , envelope_path_later.str()+".local" ) ;
}
bool GSmtp::NewMessageImp::saveEnvelope( std::ostream & stream , const std::string & where ) const
{
G_LOG( "GSmtp::NewMessage: envelope file: " << where ) ;
const std::string x( MessageStoreImp::x() ) ;
stream << x << "Format: " << MessageStoreImp::format() << crlf() ;
stream << x << "Content: " << (m_eight_bit?"8":"7") << "bit" << crlf() ;
stream << x << "From: " << m_from << crlf() ;
stream << x << "ToCount: " << (m_to_local.size()+m_to_remote.size()) << crlf() ;
{
G::Strings::const_iterator to_p = m_to_local.begin() ;
for( ; to_p != m_to_local.end() ; ++to_p )
stream << x << "To-Local: " << *to_p << crlf() ;
}
{
G::Strings::const_iterator to_p = m_to_remote.begin() ;
for( ; to_p != m_to_remote.end() ; ++to_p )
stream << x << "To-Remote: " << *to_p << crlf() ;
}
stream << x << "End: 1" << crlf() ;
stream.flush() ;
return stream.good() ;
}
std::string GSmtp::NewMessageImp::crlf() const
{
return std::string( "\015\012" ) ;
}
// ===
GSmtp::MessageStoreImp::MessageStoreImp( const G::Path & dir ) :
m_dir(dir),
m_seq(1UL)
{
}
//static
std::string GSmtp::MessageStoreImp::x()
{
return "X-MailRelay-" ;
}
//static
std::string GSmtp::MessageStoreImp::format()
{
return "#2821.2" ;
}
const G::Path & GSmtp::MessageStoreImp::dir() const
{
return m_dir ;
}
//static
void GSmtp::MessageStoreImp::checkPath( const G::Path & directory_path )
{
// (void) G::File::mkdir( directory_path ) ;
G::Directory dir_test( directory_path ) ;
if( ! dir_test.valid() )
{
throw MessageStore::InvalidDirectory( directory_path.str() ) ;
}
if( ! dir_test.valid(true) )
{
G_WARNING( "GSmtp::MessageStore: "
<< "directory not writable: \""
<< directory_path << "\"" ) ;
}
}
std::auto_ptr<std::ostream> GSmtp::MessageStoreImp::stream( const G::Path & path )
{
std::auto_ptr<std::ostream> ptr(
new std::ofstream( path.pathCstr() ,
std::ios_base::binary | std::ios_base::out | std::ios_base::trunc ) ) ;
return ptr ;
}
G::Path GSmtp::MessageStoreImp::contentPath( unsigned long seq ) const
{
return fullPath( filePrefix(seq) + ".content" ) ;
}
G::Path GSmtp::MessageStoreImp::envelopePath( unsigned long seq ) const
{
return fullPath( filePrefix(seq) + ".envelope" ) ;
}
G::Path GSmtp::MessageStoreImp::envelopeWorkingPath( unsigned long seq ) const
{
return fullPath( filePrefix(seq) + ".envelope.new" ) ;
}
std::string GSmtp::MessageStoreImp::filePrefix( unsigned long seq ) const
{
std::stringstream ss ;
ss << "emailrelay." << G::Pid() << "." << seq ;
return ss.str() ;
}
G::Path GSmtp::MessageStoreImp::fullPath( const std::string & filename ) const
{
G::Path p( m_dir ) ;
p.pathAppend( filename ) ;
return p ;
}
unsigned long GSmtp::MessageStoreImp::newSeq()
{
return m_seq++ ;
}
bool GSmtp::MessageStoreImp::empty() const
{
G::Directory dir(m_dir) ;
G::DirectoryIterator iter( dir , "*.envelope" ) ;
bool no_more = iter.error() || !iter.more() ;
if( no_more )
G_DEBUG( "GSmtp::MessageStoreImp: no files to process" ) ;
return no_more ;
}
GSmtp::MessageStore::Iterator GSmtp::MessageStoreImp::iterator()
{
return MessageStore::Iterator( new MessageStoreIteratorImp(G::Directory(m_dir)) ) ;
}
std::auto_ptr<GSmtp::StoredMessage> GSmtp::MessageStoreImp::get()
{
MessageStore::Iterator iter = iterator() ;
return iter.next() ;
}
// ===
GSmtp::StoredMessageImp::StoredMessageImp( const G::Path & path ) :
m_envelope_path(path)
{
G_DEBUG( "StoredMessageImp: \"" << path << "\"" ) ;
}
GSmtp::StoredMessageImp::~StoredMessageImp()
{
}
bool GSmtp::StoredMessageImp::eightBit() const
{
return m_eight_bit ;
}
bool GSmtp::StoredMessageImp::readEnvelope( std::string & reason )
{
try
{
return readEnvelopeCore( reason ) ;
}
catch( std::exception & e )
{
reason = e.what() ;
return false ;
}
}
bool GSmtp::StoredMessageImp::readEnvelopeCore( std::string & reason )
{
std::ifstream stream( m_envelope_path.str().c_str() , std::ios_base::binary | std::ios_base::in ) ;
if( ! stream.good() )
throw OpenError() ;
std::string format_line = getline(stream) ;
if( value(format_line) != MessageStoreImp::format() )
throw InvalidFormat( value(format_line) + "!=" + MessageStoreImp::format() ) ;
std::string content_line = getline(stream) ;
m_eight_bit = value(content_line) == "8bit" ;
m_from = value(getline(stream)) ;
G_DEBUG( "GSmtp::StoredMessageImp::readEnvelope: from \"" << m_from << "\"" ) ;
std::string to_count_line = getline(stream) ;
unsigned int to_count = G::Str::toUInt( value(to_count_line) ) ;
for( unsigned int i = 0U ; i < to_count ; i++ )
{
std::string to_line = getline(stream) ;
bool is_local = to_line.find(MessageStoreImp::x()+"To-Local") == 0U ;
bool is_remote = to_line.find(MessageStoreImp::x()+"To-Remote") == 0U ;
if( ! is_local && ! is_remote )
throw InvalidTo(to_line) ;
G_DEBUG( "GSmtp::StoredMessageImp::readEnvelope: to "
"[" << (i+1U) << "/" << to_count << "] "
"(" << (is_local?"local":"remote") << ") "
<< "\"" << value(to_line) << "\"" ) ;
if( is_local )
m_to_local.push_back( value(to_line) ) ;
else
m_to_remote.push_back( value(to_line) ) ;
}
std::string end = getline(stream) ;
if( end.find(MessageStoreImp::x()+"End") != 0U )
throw NoEnd() ;
if( m_to_remote.size() == 0U )
throw NoRecipients() ;
if( ! stream.good() )
throw StreamError() ;
return stream.good() ;
}
bool GSmtp::StoredMessageImp::openContent( std::string & reason )
{
try
{
G::Path content_path = contentPath() ;
G_DEBUG( "GSmtp::MessageStoreImp::openContent: \"" << content_path << "\"" ) ;
std::auto_ptr<std::istream> stream( new std::ifstream(
content_path.str().c_str() , std::ios_base::in | std::ios_base::binary ) ) ;
if( !stream->good() )
{
reason = "cannot open content file" ;
return false ;
}
G_LOG( "GSmtp::MessageStore: processing envelope \"" << m_envelope_path.basename() << "\"" ) ;
G_LOG( "GSmtp::MessageStore: processing content \"" << content_path.basename() << "\"" ) ;
m_content = stream ;
return true ;
}
catch( std::exception & e )
{
G_WARNING( "GSmtp::MessageStoreImp: exception: " << e.what() ) ;
reason = e.what() ;
return false ;
}
}
std::string GSmtp::StoredMessageImp::getline( std::istream & stream ) const
{
return G::Str::readLineFrom( stream , crlf() ) ;
}
std::string GSmtp::StoredMessageImp::value( const std::string & s ) const
{
size_t pos = s.find(' ') ;
if( pos == std::string::npos )
throw MessageStore::FormatError() ;
return s.substr(pos+1U) ;
}
std::string GSmtp::StoredMessageImp::crlf() const
{
return std::string( "\015\012" ) ;
}
bool GSmtp::StoredMessageImp::lock()
{
G::Path & src = m_envelope_path ;
G::Path dst( src.str() + ".busy" ) ;
bool ok = G::File::rename( src , dst , G::File::NoThrow() ) ;
if( ok )
{
G_LOG( "GSmtp::StoredMessage: locking file \"" << src.basename() << "\"" ) ;
m_envelope_path = dst ;
}
return ok ;
}
void GSmtp::StoredMessageImp::fail( const std::string & reason )
{
try
{
{
std::ofstream file( m_envelope_path.str().c_str() ,
std::ios_base::binary | std::ios_base::ate ) ;
file << MessageStoreImp::x() << "Reason: " << reason ;
}
G::Path env_temp( m_envelope_path ) ; // "foo.envelope.busy"
env_temp.removeExtension() ; // "foo.envelope"
G::Path bad( env_temp.str() + ".bad" ) ; // "foo.envelope.bad"
G_LOG( "GSmtp::StoredMessage: failing file: "
<< "\"" << m_envelope_path.basename() << "\" -> "
<< "\"" << bad.basename() << "\"" ) ;
G::File::rename( m_envelope_path , bad , G::File::NoThrow() ) ;
}
catch(...)
{
}
}
void GSmtp::StoredMessageImp::destroy()
{
try
{
G_LOG( "GSmtp::StoredMessage: deleting file: \"" << m_envelope_path.basename() << "\"" ) ;
G::File::remove( m_envelope_path , G::File::NoThrow() ) ;
G::Path content_path = contentPath() ;
G_LOG( "GSmtp::StoredMessage: deleting file: \"" << content_path.basename() << "\"" ) ;
m_content <<= 0 ; // close it first
G::File::remove( content_path , G::File::NoThrow() ) ;
}
catch(...)
{
}
}
const std::string & GSmtp::StoredMessageImp::from() const
{
return m_from ;
}
const G::Strings & GSmtp::StoredMessageImp::to() const
{
return m_to_remote ;
}
std::auto_ptr<std::istream> GSmtp::StoredMessageImp::extractContentStream()
{
G_ASSERT( m_content.get() != NULL ) ;
return m_content ;
}
G::Path GSmtp::StoredMessageImp::contentPath() const
{
G::Path path( m_envelope_path ) ; // "foo.envelope.busy"
path.removeExtension() ; // "foo.envelope"
path.setExtension( "content" ) ; // "foo.content"
return path ;
}

View File

@ -26,6 +26,8 @@
#include "gdef.h" #include "gdef.h"
#include "gsmtp.h" #include "gsmtp.h"
#include "gnewmessage.h"
#include "gstoredmessage.h"
#include "gexception.h" #include "gexception.h"
#include "gstrings.h" #include "gstrings.h"
#include "gpath.h" #include "gpath.h"
@ -33,73 +35,6 @@
namespace GSmtp namespace GSmtp
{ {
class MessageStore ; class MessageStore ;
class StoredMessage ;
class NewMessage ;
class MessageStoreImp ;
class MessageStoreIteratorImp ;
class StoredMessageImp ;
class NewMessageImp ;
} ;
// Class: GSmtp::NewMessage
// Description: An abstract class to allow the creation
// of a new message in the message store.
// See also: MessageStore, MessageStore::newMessage()
//
class GSmtp::NewMessage
{
public:
virtual void addTo( const std::string & to , bool local ) = 0 ;
// Adds a 'to' address.
virtual void addText( const std::string & line ) = 0 ;
// Adds a line of content.
virtual void store() = 0 ;
// Stores the message in the message store.
virtual ~NewMessage() ;
// Destructor.
private:
void operator=( const NewMessage & ) ;
} ;
// Class: GSmtp::StoredMessage
// Description: An abstract class for messages which have
// come from the store.
// See also: MessageStore, MessageStore::get()
//
class GSmtp::StoredMessage
{
public:
virtual const std::string & from() const = 0 ;
// Returns the envelope 'from' field.
virtual const G::Strings & to() const = 0 ;
// Returns the envelope 'to' fields.
virtual std::auto_ptr<std::istream> extractContentStream() = 0 ;
// Extracts the content stream.
// Can only be called once.
virtual void destroy() = 0 ;
// Deletes the message within the store.
virtual void fail( const std::string & reason ) = 0 ;
// Marks the message as failed within the store.
virtual bool eightBit() const = 0 ;
// Returns true if the message content (header+body)
// contains a character with the most significant
// bit set.
virtual ~StoredMessage() ;
// Destructor.
private:
void operator=( const StoredMessage & ) ;
} ; } ;
// Class: GSmtp::MessageStore // Class: GSmtp::MessageStore
@ -118,16 +53,24 @@ private:
class GSmtp::MessageStore class GSmtp::MessageStore
{ {
public: public:
G_EXCEPTION( InvalidDirectory , "invalid spool directory" ) ;
G_EXCEPTION( WriteError , "error writing file" ) ; G_EXCEPTION( WriteError , "error writing file" ) ;
G_EXCEPTION( NoInstance , "no message store instance" ) ; G_EXCEPTION( NoInstance , "no message store instance" ) ;
G_EXCEPTION( FormatError , "format error" ) ; G_EXCEPTION( FormatError , "format error" ) ;
class IteratorImp // A base class for MessageStore::Iterator implementations.
{
public: unsigned long m_ref_count ;
public: virtual std::auto_ptr<GSmtp::StoredMessage> next() = 0 ;
public: IteratorImp() ;
public: virtual ~IteratorImp() ;
private: IteratorImp( const IteratorImp & ) ;
private: void operator=( const IteratorImp & ) ;
} ;
class Iterator // An iterator class for GSmtp::MessageStore. class Iterator // An iterator class for GSmtp::MessageStore.
{ {
public: std::auto_ptr<StoredMessage> next() ; public: std::auto_ptr<StoredMessage> next() ;
private: MessageStoreIteratorImp * m_imp ; private: IteratorImp * m_imp ;
public: Iterator() ; public: Iterator() ;
public: explicit Iterator( MessageStoreIteratorImp * ) ; public: explicit Iterator( IteratorImp * ) ;
public: ~Iterator() ; public: ~Iterator() ;
public: Iterator( const Iterator & ) ; public: Iterator( const Iterator & ) ;
public: Iterator & operator=( const Iterator & ) ; public: Iterator & operator=( const Iterator & ) ;
@ -141,29 +84,29 @@ public:
// "/usr/local/var/spool/emailrelay". (Typically // "/usr/local/var/spool/emailrelay". (Typically
// has an os-specific implementation.) // has an os-specific implementation.)
explicit MessageStore( const G::Path & directory ) ; MessageStore() ;
// Constructor. Throws exceptions if // Default constructor.
// not a valid storage directory.
~MessageStore() ; virtual ~MessageStore() ;
// Destructor. // Destructor.
std::auto_ptr<NewMessage> newMessage( const std::string & from ) ; virtual std::auto_ptr<NewMessage> newMessage( const std::string & from ) = 0 ;
// Creates a new message. // Creates a new message.
bool empty() const ; virtual bool empty() const = 0 ;
// Returns true if the message store is empty. // Returns true if the message store is empty.
std::auto_ptr<StoredMessage> get() ; virtual std::auto_ptr<StoredMessage> get( unsigned long id ) = 0 ;
// Pulls a message out of the store (selected // Pulls a message out of the store.
// at random). Returns a NULL smart pointer // Throws execptions on error.
// if there are no messages to extract. //
// See also NewMessage::id().
// //
// As a side effect some stored messages may be // As a side effect some stored messages may be
// marked as bad, or deleted (if they // marked as bad, or deleted (if they
// have no recipients). // have no recipients).
Iterator iterator() ; virtual Iterator iterator() = 0 ;
// Returns a read iterator. (Note that copies of // Returns a read iterator. (Note that copies of
// iterators share state. For independent iterators // iterators share state. For independent iterators
// call iterator() for each.) // call iterator() for each.)
@ -172,17 +115,12 @@ public:
// messages may be marked as bad, or deleted (if // messages may be marked as bad, or deleted (if
// they have no recipients). // they have no recipients).
G::Path directory() const ;
// Returns the storage directory (as passed
// to the constructor).
private: private:
MessageStore( const MessageStore & ) ; MessageStore( const MessageStore & ) ;
void operator=( const MessageStore & ) ; void operator=( const MessageStore & ) ;
private: private:
static MessageStore * m_this ; static MessageStore * m_this ;
MessageStoreImp * m_imp ;
} ; } ;
#endif #endif

207
src/main/gnewfile.cpp Normal file
View File

@ -0,0 +1,207 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gnewfile.cpp
//
#include "gdef.h"
#include "gsmtp.h"
#include "gmessagestore.h"
#include "gnewfile.h"
#include "gmemory.h"
#include "gprocess.h"
#include "gfile.h"
#include "gassert.h"
#include "glog.h"
#include <iostream>
#include <fstream>
bool GSmtp::NewFile::m_preprocess = false ;
G::Path GSmtp::NewFile::m_preprocessor ;
GSmtp::NewFile::NewFile( const std::string & from , FileStore & store ) :
m_from(from) ,
m_store(store),
m_eight_bit(false)
{
m_seq = store.newSeq() ;
m_content_path = m_store.contentPath( m_seq ) ;
G_LOG( "GSmtp::NewMessage: content file: " << m_content_path ) ;
std::auto_ptr<std::ostream> content_stream = m_store.stream( m_content_path ) ;
m_content = content_stream ;
}
GSmtp::NewFile::~NewFile()
{
}
void GSmtp::NewFile::addTo( const std::string & to , bool local )
{
if( local )
m_to_local.push_back( to ) ;
else
m_to_remote.push_back( to ) ;
}
void GSmtp::NewFile::addText( const std::string & line )
{
if( ! m_eight_bit )
m_eight_bit = isEightBit(line) ;
*(m_content.get()) << line << crlf() ;
}
bool GSmtp::NewFile::isEightBit( const std::string & line )
{
const size_t n = line.length() ;
for( size_t i = 0U ; i < n ; --i )
{
const unsigned char c = static_cast<unsigned char>(line.at(i)) ;
if( c > 0x7fU )
return true ;
}
return false ;
}
void GSmtp::NewFile::store()
{
// flush the content file
//
m_content->flush() ;
if( ! m_content->good() )
throw GSmtp::MessageStore::WriteError( m_content_path.str() ) ;
m_content <<= 0 ;
// write the envelope
//
G::Path p0 = m_store.envelopeWorkingPath( m_seq ) ;
G::Path p1 = m_store.envelopePath( m_seq ) ;
bool ok = false ;
std::string reason = p0.str() ;
{
std::auto_ptr<std::ostream> envelope_stream = m_store.stream( p0 ) ;
ok = saveEnvelope( *(envelope_stream.get()) , p0.str() ) ;
}
// shell out to a message pre-processor
//
if( ok )
{
ok = preprocess( m_content_path ) ;
if( !ok )
reason = "pre-processing failed" ;
}
// deliver to local mailboxes
//
if( ok && m_to_local.size() != 0U )
{
deliver( m_to_local , m_content_path , p0 , p1 ) ;
}
// commit the envelope, or rollback the content
//
if( ! ok || ! G::File::rename(p0,p1,G::File::NoThrow() ) )
{
G_ASSERT( m_content_path.str().length() != 0U ) ;
G::File::remove( m_content_path , G::File::NoThrow() ) ;
throw GSmtp::MessageStore::WriteError( reason ) ;
}
}
bool GSmtp::NewFile::preprocess( const G::Path & path )
{
if( m_preprocess )
{
G_LOG( "GSmtp::NewFile::preprocess: " << m_preprocessor << " " << path ) ;
int exit_code = G::Process::spawn( m_preprocessor , path.str() ) ;
G_LOG( "GSmtp::NewFile::preprocess: exit status " << exit_code ) ;
if( exit_code != 0 )
{
G_WARNING( "GSmtp::NewFile::preprocess: pre-processing failed: exit code " << exit_code ) ;
return false ;
}
}
return true ;
}
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 )
{
// could shell out to "procmail" or "deliver" here, but keep it
// simple and within the scope of a "message-store" class
G_LOG( "GSmtp::NewMessage: copying message for local recipient(s): "
<< content_path.basename() << ".local" ) ;
G::File::copy( content_path.str() , content_path.str()+".local" ) ;
G::File::copy( envelope_path_now.str() , envelope_path_later.str()+".local" ) ;
}
bool GSmtp::NewFile::saveEnvelope( std::ostream & stream , const std::string & where ) const
{
G_LOG( "GSmtp::NewMessage: envelope file: " << where ) ;
const std::string x( m_store.x() ) ;
stream << x << "Format: " << m_store.format() << crlf() ;
stream << x << "Content: " << (m_eight_bit?"8":"7") << "bit" << crlf() ;
stream << x << "From: " << m_from << crlf() ;
stream << x << "ToCount: " << (m_to_local.size()+m_to_remote.size()) << crlf() ;
{
G::Strings::const_iterator to_p = m_to_local.begin() ;
for( ; to_p != m_to_local.end() ; ++to_p )
stream << x << "To-Local: " << *to_p << crlf() ;
}
{
G::Strings::const_iterator to_p = m_to_remote.begin() ;
for( ; to_p != m_to_remote.end() ; ++to_p )
stream << x << "To-Remote: " << *to_p << crlf() ;
}
stream << x << "End: 1" << crlf() ;
stream.flush() ;
return stream.good() ;
}
std::string GSmtp::NewFile::crlf() const
{
return std::string( "\015\012" ) ;
}
unsigned long GSmtp::NewFile::id() const
{
return m_seq ;
}
void GSmtp::NewFile::setPreprocessor( const G::Path & exe )
{
if( exe.isRelative() )
throw InvalidPath( exe.str() ) ;
if( G::Process::privileged() )
throw Dangerous() ;
m_preprocess = true ;
m_preprocessor = exe ;
}

81
src/main/gnewfile.h Normal file
View File

@ -0,0 +1,81 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gnewfile.h
//
#ifndef G_SMTP_NEW_FILE_H
#define G_SMTP_NEW_FILE_H
#include "gdef.h"
#include "gsmtp.h"
#include "gfilestore.h"
#include "gnewmessage.h"
#include "gexception.h"
#include <iostream>
namespace GSmtp
{
class NewFile ;
} ;
// Class: GSmtp::NewFile
// Description: A concrete derived class implementing the
// NewMessage interface. Writes itself to the i/o streams
// supplied by MessageStoreImp.
//
class GSmtp::NewFile : public GSmtp:: NewMessage
{
public:
G_EXCEPTION( InvalidPath , "invalid path -- must be absolute" ) ;
G_EXCEPTION( Dangerous , "message filtering not allowed if running as a privileged process" ) ;
NewFile( const std::string & from , FileStore & store ) ;
virtual ~NewFile() ;
virtual void addTo( const std::string & to , bool local ) ;
virtual void addText( const std::string & line ) ;
virtual void store() ;
virtual unsigned long id() const ;
static void setPreprocessor( const G::Path & exe ) ;
// Defines a program which is used for pre-processing
// messages before they are stored.
private:
FileStore & m_store ;
unsigned long m_seq ;
std::string m_from ;
G::Strings m_to_local ;
G::Strings m_to_remote ;
std::auto_ptr<std::ostream> m_content ;
G::Path m_content_path ;
bool m_eight_bit ;
static bool m_preprocess ;
static G::Path m_preprocessor ;
private:
bool saveEnvelope( std::ostream & stream , const std::string & where ) 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 & ) ;
} ;
#endif

View File

@ -18,62 +18,17 @@
// //
// === // ===
// //
// gpid_win32.cpp // gnewmessage.cpp
// //
#include "gdef.h" #include "gdef.h"
#include "gpid.h" #include "gsmtp.h"
#include <process.h> #include "gmessagestore.h"
#include "gnewmessage.h"
#include <iostream>
namespace G GSmtp::NewMessage::~NewMessage()
{ {
class PidImp ; // empty
} ;
// Class: G::PidImp
// Description: A pimple implementation class for GPid.
//
class G::PidImp
{
public:
int m_pid ;
} ;
// ===
G::Pid::Pid() : m_imp(NULL)
{
m_imp = new PidImp ;
m_imp->m_pid = ::_getpid() ;
} }
G::Pid::~Pid()
{
delete m_imp ;
}
G::Pid::Pid( const Pid & other ) :
m_imp(NULL)
{
m_imp = new PidImp ;
m_imp->m_pid = other.m_imp->m_pid ;
}
G::Pid & G::Pid::operator=( const Pid & rhs )
{
m_imp->m_pid = rhs.m_imp->m_pid ;
return *this ;
}
std::string G::Pid::str() const
{
std::stringstream ss ;
ss << m_imp->m_pid ;
return ss.str() ;
}
bool G::Pid::operator==( const Pid & rhs ) const
{
return m_imp->m_pid == rhs.m_imp->m_pid ;
} ;

View File

@ -18,59 +18,46 @@
// //
// === // ===
// //
// gpid.h // gnewmessage.h
// //
#ifndef G_PID_H #ifndef G_SMTP_NEW_MESSAGE_H
#define G_PID_H #define G_SMTP_NEW_MESSAGE_H
#include "gdef.h" #include "gdef.h"
#include <iostream> #include "gsmtp.h"
namespace G namespace GSmtp
{ {
class PidImp ; class NewMessage ;
class Pid ; class MessageStoreImp ;
} ; } ;
// Class: G::Pid // Class: GSmtp::NewMessage
// Description: A process-id class. Uses a pimple // Description: An abstract class to allow the creation
// pattern to hide windows/unix type differences. // of a new message in the message store.
// See also: MessageStore, MessageStore::newMessage()
// //
class G::Pid class GSmtp::NewMessage
{ {
public: public:
Pid() ; virtual void addTo( const std::string & to , bool local ) = 0 ;
// Default constructor for this // Adds a 'to' address.
// process's id.
~Pid() ; virtual void addText( const std::string & line ) = 0 ;
// Adds a line of content.
virtual void store() = 0 ;
// Stores the message in the message store.
virtual unsigned long id() const = 0 ;
// Returns the message's unique identifier.
virtual ~NewMessage() ;
// Destructor. // Destructor.
Pid( const Pid & ) ;
// Copy constructor.
Pid & operator=( const Pid & ) ;
// Assignment operator.
std::string str() const ;
// Returns a string representation.
bool operator==( const Pid & ) const ;
// Comparison operator.
private: private:
PidImp * m_imp ; void operator=( const NewMessage & ) ; // not implemented
} ;
namespace G
{
inline
std::ostream & operator<<( std::ostream & stream , const Pid & pid )
{
stream << pid.str() ;
return stream ;
}
} ; } ;
#endif #endif

View File

@ -24,147 +24,14 @@
#include "gdef.h" #include "gdef.h"
#include "gsmtp.h" #include "gsmtp.h"
#include "gprotocolmessage.h" #include "gprotocolmessage.h"
#include "gmessagestore.h"
#include "gmemory.h"
#include "gstr.h"
#include "gassert.h"
#include "glog.h"
// Class: GSmtp::ProtocolMessageImp
// Description: A private pimple-pattern implementation class for GSmtp::ProtocolMessage.
//
class GSmtp::ProtocolMessageImp
{
public:
std::auto_ptr<NewMessage> m_msg ;
} ;
// ===
GSmtp::ProtocolMessage::ProtocolMessage() :
m_imp(NULL)
{
m_imp = new ProtocolMessageImp ;
}
GSmtp::ProtocolMessage::~ProtocolMessage() GSmtp::ProtocolMessage::~ProtocolMessage()
{ {
delete m_imp ;
} }
void GSmtp::ProtocolMessage::clear() // ===
GSmtp::ProtocolMessage::Callback::~Callback()
{ {
m_imp->m_msg <<= 0 ;
}
bool GSmtp::ProtocolMessage::setFrom( const std::string & from )
{
try
{
if( from.length() == 0U )
return false ;
G_ASSERT( m_imp->m_msg.get() == NULL ) ;
clear() ; // just in case
std::auto_ptr<NewMessage> new_msg = MessageStore::instance().newMessage( from ) ;
m_imp->m_msg = new_msg ;
return true ;
}
catch( std::exception & e )
{
G_ERROR( "GSmtp::ProtocolMessage::setFrom: error: " << e.what() ) ;
return false ;
}
}
bool GSmtp::ProtocolMessage::addTo( const std::string & to )
{
G_ASSERT( m_imp->m_msg.get() != NULL ) ;
if( to.length() > 0U && m_imp->m_msg.get() != NULL )
{
bool is_local = isLocal(to) ;
if( is_local && !isValid(to) )
{
G_WARNING( "GSmtp::ProtocolMessage: rejecting local recipent (not postmaster): " << to ) ;
return false ;
}
else
{
m_imp->m_msg->addTo( to , is_local ) ;
return true ;
}
}
else
{
return false ;
}
}
void GSmtp::ProtocolMessage::addReceived( const std::string & line )
{
addText( line ) ;
}
void GSmtp::ProtocolMessage::addText( const std::string & line )
{
G_ASSERT( m_imp->m_msg.get() != NULL ) ;
if( m_imp->m_msg.get() != NULL )
m_imp->m_msg->addText( line ) ;
}
std::string GSmtp::ProtocolMessage::process()
{
try
{
G_ASSERT( m_imp->m_msg.get() != NULL ) ;
if( m_imp->m_msg.get() != NULL )
{
m_imp->m_msg->store() ;
}
clear() ;
return std::string() ;
}
catch( std::exception & e )
{
G_ERROR( "GSmtp::ProtocolMessage::process: error: " << e.what() ) ;
clear() ;
return std::string( e.what() ) ;
}
}
std::pair<bool,std::string> GSmtp::ProtocolMessage::verify( const std::string & user )
{
G_DEBUG( "GSmtp::ProtocolMessage::verify: \"" << user << "\"" ) ;
std::pair<bool,std::string> rc( isLocal(user) , std::string() ) ;
if( isLocal(user) && isValid(user) )
rc.second = fullName(user) ;
return rc ;
}
//static
bool GSmtp::ProtocolMessage::isLocal( const std::string & user )
{
return user.find('@') == std::string::npos ;
}
//static
bool GSmtp::ProtocolMessage::isValid( const std::string & user )
{
// only recognise one local mailbox
return isPostmaster(user) ;
}
//static
bool GSmtp::ProtocolMessage::isPostmaster( std::string user )
{
G::Str::toUpper( user ) ;
G::Str::trim( user , " \t" ) ;
return user == "POSTMASTER" ;
}
//static
std::string GSmtp::ProtocolMessage::fullName( const std::string & user )
{
return "Local postmaster <postmaster@localhost>" ;
} }

View File

@ -27,83 +27,74 @@
#include "gdef.h" #include "gdef.h"
#include "gsmtp.h" #include "gsmtp.h"
#include "gstrings.h" #include "gstrings.h"
#include "gverifier.h"
#include <string> #include <string>
namespace GSmtp namespace GSmtp
{ {
class ProtocolMessage ; class ProtocolMessage ;
class ProtocolMessageImp ;
} ; } ;
// Class: GSmtp::ProtocolMessage // Class: GSmtp::ProtocolMessage
// Description: An interface used by the ServerProtocol // Description: An interface used by the ServerProtocol
// class to assemble and process an incoming message. // class to assemble and process an incoming message.
// It implements the three 'buffers' mentioned in // It implements the three 'buffers' mentioned in
// RFC2821 (esp. section 4.1.1). Also does mail-address // RFC2821 (esp. section 4.1.1).
// validation.
// //
// This class serves to decouple the ServerProtocol class from // This interface serves to decouple the ServerProtocol class
// the MessageStore (or whatever else is downstream). // from the MessageStore (or whatever else is downstream).
// //
class GSmtp::ProtocolMessage class GSmtp::ProtocolMessage
{ {
public: public:
ProtocolMessage() ; class Callback // A callback interface used by ProtocolMessage::process().
// Default constructor. {
public: virtual ~Callback() ;
public: virtual void processDone( bool success , unsigned long id , const std::string & reason ) = 0 ;
private: void operator=( const Callback & ) ; // not implemented
} ;
~ProtocolMessage() ; virtual ~ProtocolMessage() ;
// Destructor. // Destructor.
static std::pair<bool,std::string> verify( const std::string & ) ; virtual void clear() = 0 ;
// Checks an address returning // Clears the message state and terminates
// <is-local>|<local-full-name>. // any asynchronous message processing.
//
// (If syntactically local then 'first' is
// returned true. If local and valid then
// 'second' is set to the full description.
// If syntactically remote, then 'first'
// is returned false and 'second' is empty.)
void clear() ; virtual bool setFrom( const std::string & from_user ) = 0 ;
// Clears the message state.
bool setFrom( const std::string & from_user ) ;
// Sets the message envelope 'from'. // Sets the message envelope 'from'.
// Returns false if an invalid user. // Returns false if an invalid user.
bool addTo( const std::string & to_user ) ; virtual bool addTo( const std::string & to_user , Verifier::Status to_status ) = 0 ;
// Adds an envelope 'to'. // Adds an envelope 'to'.
//
// The 'to_status' parameter comes from
// GSmtp::Verifier.verify().
//
// Returns false if an invalid user. // Returns false if an invalid user.
// Precondition: setFrom() called // Precondition: setFrom() called
// since clear() or process(). // since clear() or process().
void addReceived( const std::string & ) ; virtual void addReceived( const std::string & ) = 0 ;
// Adds a 'received' line to the // Adds a 'received' line to the
// start of the content. // start of the content.
// Precondition: at least one // Precondition: at least one
// successful addTo() call // successful addTo() call
void addText( const std::string & ) ; virtual void addText( const std::string & ) = 0 ;
// Adds text. // Adds text.
// Precondition: at least one // Precondition: at least one
// successful addTo() call // successful addTo() call
std::string process() ; virtual void process( Callback & callback ) = 0 ;
// Processes and clears the message. // Starts asynchronous processing of the
// Returns a non-zero-length reason // message. Once processing is complete the
// string on error. // message state is cleared and the callback
// is triggered. The callback may be called
// before process() returns.
private: private:
static bool isLocal( const std::string & ) ; void operator=( const ProtocolMessage & ) ; // not implemented
static bool isValid( const std::string & ) ;
static bool isPostmaster( std::string ) ;
static std::string fullName( const std::string & ) ;
ProtocolMessage( const ProtocolMessage & ) ;
void operator=( const ProtocolMessage & ) ;
private:
G::Strings m_to_list ;
ProtocolMessageImp * m_imp ;
} ; } ;
#endif #endif

View File

@ -0,0 +1,143 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gprotocolmessageforward.cpp
//
#include "gdef.h"
#include "gsmtp.h"
#include "gprotocolmessageforward.h"
#include "gprotocolmessagestore.h"
#include "gmessagestore.h"
#include "gmemory.h"
#include "gstr.h"
#include "gassert.h"
#include "glog.h"
GSmtp::ProtocolMessageForward::ProtocolMessageForward( const std::string & server ) :
m_server(server) ,
m_callback(NULL)
{
}
GSmtp::ProtocolMessageForward::~ProtocolMessageForward()
{
}
void GSmtp::ProtocolMessageForward::clear()
{
m_pm.clear() ;
m_client <<= 0 ;
}
bool GSmtp::ProtocolMessageForward::setFrom( const std::string & from )
{
return m_pm.setFrom( from ) ;
}
bool GSmtp::ProtocolMessageForward::addTo( const std::string & to , Verifier::Status to_status )
{
return m_pm.addTo( to , to_status ) ;
}
void GSmtp::ProtocolMessageForward::addReceived( const std::string & line )
{
m_pm.addReceived( line ) ;
}
void GSmtp::ProtocolMessageForward::addText( const std::string & line )
{
m_pm.addText( line ) ;
}
void GSmtp::ProtocolMessageForward::process( ProtocolMessage::Callback & callback )
{
m_callback = & callback ;
m_pm.process( *this ) ;
}
void GSmtp::ProtocolMessageForward::processDone( bool success , unsigned long id , const std::string & reason_in )
{
std::string reason( reason_in ) ;
bool nothing_to_do = false ;
if( success )
{
m_id = id ;
success = forward( id , nothing_to_do , &reason ) ;
}
if( nothing_to_do || !success )
{
G_ASSERT( m_callback != NULL ) ;
if( m_callback )
m_callback->processDone( success , id , reason ) ;
m_callback = NULL ;
}
}
bool GSmtp::ProtocolMessageForward::forward( unsigned long id , bool & nothing_to_do , std::string * reason_p )
{
try
{
nothing_to_do = false ;
*reason_p = std::string() ;
G_DEBUG( "GSmtp::ProtocolMessageForward::forward: forwarding message " << id ) ;
std::auto_ptr<StoredMessage> message = GSmtp::MessageStore::instance().get( id ) ;
bool ok = true ;
if( message->remoteRecipientCount() == 0U )
{
// use our local delivery mechanism, not the downstream server's
nothing_to_do = true ;
message->destroy() ; // (already copied to "*.local")
}
else
{
m_client <<= new GSmtp::Client( message , *this ) ;
std::string reason = m_client->init( m_server ) ;
ok = reason.empty() ;
if( !ok && reason_p != NULL )
*reason_p = reason ;
}
return ok ;
}
catch( std::exception & e )
{
if( reason_p != NULL )
*reason_p = e.what() ;
return false ;
}
}
void GSmtp::ProtocolMessageForward::clientDone( std::string reason )
{
G_DEBUG( "GSmtp::ProtocolMessageForward::clientDone: \"" << reason << "\"" ) ;
const bool ok = reason.empty() ;
G_ASSERT( m_callback != NULL ) ;
if( m_callback )
{
m_callback->processDone( ok , m_id , reason ) ;
m_callback = NULL ;
}
}

View File

@ -0,0 +1,91 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gprotocolmessageforward.h
//
#ifndef G_SMTP_PROTOCOL_MESSAGE_FORWARD_H
#define G_SMTP_PROTOCOL_MESSAGE_FORWARD_H
#include "gdef.h"
#include "gsmtp.h"
#include "gprotocolmessage.h"
#include "gprotocolmessagestore.h"
#include "gsmtpclient.h"
#include "gnewmessage.h"
#include <string>
#include <memory>
namespace GSmtp
{
class ProtocolMessageForward ;
} ;
// Class: GSmtp::ProtocolMessageForward
// Description: A concrete implementation of the
// ProtocolMessage interface which stores incoming
// messages in the message store and then immediately
// forwards them on to the downstream server.
// See also: ProtocolMessageStore
//
class GSmtp::ProtocolMessageForward : public GSmtp:: ProtocolMessage ,
private GSmtp:: ProtocolMessage::Callback ,
private GSmtp:: Client::ClientCallback
{
public:
explicit ProtocolMessageForward( const std::string & server_address ) ;
// Constructor.
virtual ~ProtocolMessageForward() ;
// Destructor.
virtual void clear() ;
// See ProtocolMessage.
virtual bool setFrom( const std::string & from_user ) ;
// See ProtocolMessage.
virtual bool addTo( const std::string & to_user , Verifier::Status to_status ) ;
// See ProtocolMessage.
virtual void addReceived( const std::string & ) ;
// See ProtocolMessage.
virtual void addText( const std::string & ) ;
// See ProtocolMessage.
virtual void process( ProtocolMessage::Callback & callback ) ;
// See ProtocolMessage.
private:
void operator=( const ProtocolMessageForward & ) ; // not implemented
virtual void processDone( bool , unsigned long , const std::string & ) ; // from ProtocolMessage::Callback
virtual void clientDone( std::string ) ; // from Client::ClientCallback
bool forward( unsigned long , bool & , std::string * ) ;
private:
ProtocolMessageStore m_pm ;
std::string m_server ;
ProtocolMessage::Callback * m_callback ;
std::auto_ptr<Client> m_client ;
unsigned long m_id ;
} ;
#endif

View File

@ -0,0 +1,126 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gprotocolmessagestore.cpp
//
#include "gdef.h"
#include "gsmtp.h"
#include "gprotocolmessagestore.h"
#include "gmessagestore.h"
#include "gmemory.h"
#include "gstr.h"
#include "gassert.h"
#include "glog.h"
GSmtp::ProtocolMessageStore::ProtocolMessageStore()
{
}
GSmtp::ProtocolMessageStore::~ProtocolMessageStore()
{
}
void GSmtp::ProtocolMessageStore::clear()
{
m_msg <<= 0 ;
}
bool GSmtp::ProtocolMessageStore::setFrom( const std::string & from )
{
try
{
if( from.length() == 0U )
return false ;
G_ASSERT( m_msg.get() == NULL ) ;
clear() ; // just in case
std::auto_ptr<NewMessage> new_message( MessageStore::instance().newMessage(from) ) ;
m_msg <<= new_message.release() ;
return true ;
}
catch( std::exception & e )
{
G_ERROR( "GSmtp::ProtocolMessage::setFrom: error: " << e.what() ) ;
return false ;
}
}
bool GSmtp::ProtocolMessageStore::addTo( const std::string & to , Verifier::Status to_status )
{
G_ASSERT( m_msg.get() != NULL ) ;
if( to.length() > 0U && m_msg.get() != NULL )
{
const bool is_local = to_status.first ;
const bool is_valid = is_local && to_status.second.length() != 0U ;
if( is_local && !is_valid )
{
G_WARNING( "GSmtp::ProtocolMessage: rejecting local recipent (not postmaster): " << to ) ;
return false ;
}
else
{
m_msg->addTo( to , is_local ) ;
return true ;
}
}
else
{
return false ;
}
}
void GSmtp::ProtocolMessageStore::addReceived( const std::string & line )
{
addText( line ) ;
}
void GSmtp::ProtocolMessageStore::addText( const std::string & line )
{
G_ASSERT( m_msg.get() != NULL ) ;
if( m_msg.get() != NULL )
m_msg->addText( line ) ;
}
void GSmtp::ProtocolMessageStore::process( Callback & callback )
{
try
{
G_ASSERT( m_msg.get() != NULL ) ;
unsigned long id = 0UL ;
if( m_msg.get() != NULL )
{
m_msg->store() ;
id = m_msg->id() ;
}
clear() ;
callback.processDone( true , id , std::string() ) ;
}
catch( std::exception & e )
{
G_ERROR( "GSmtp::ProtocolMessage::process: error: " << e.what() ) ;
clear() ;
callback.processDone( false , 0UL , e.what() ) ;
}
}

View File

@ -0,0 +1,80 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gprotocolmessagestore.h
//
#ifndef G_SMTP_PROTOCOL_MESSAGE_STORE_H
#define G_SMTP_PROTOCOL_MESSAGE_STORE_H
#include "gdef.h"
#include "gsmtp.h"
#include "gprotocolmessage.h"
#include "gnewmessage.h"
#include <string>
#include <memory>
namespace GSmtp
{
class ProtocolMessageStore ;
} ;
// Class: GSmtp::ProtocolMessageStore
// Description: A concrete implementation of the
// ProtocolMessage interface which stores incoming
// messages in the message store.
// See also: ProtocolMessageForward
//
class GSmtp::ProtocolMessageStore : public GSmtp:: ProtocolMessage
{
public:
ProtocolMessageStore() ;
// Constructor.
virtual ~ProtocolMessageStore() ;
// Destructor.
virtual void clear() ;
// See ProtocolMessage.
virtual bool setFrom( const std::string & from_user ) ;
// See ProtocolMessage.
virtual bool addTo( const std::string & to_user , Verifier::Status to_status ) ;
// See ProtocolMessage.
virtual void addReceived( const std::string & ) ;
// See ProtocolMessage.
virtual void addText( const std::string & ) ;
// See ProtocolMessage.
virtual void process( ProtocolMessage::Callback & callback ) ;
// See ProtocolMessage.
private:
void operator=( const ProtocolMessageStore & ) ; // not implemented
private:
std::auto_ptr<NewMessage> m_msg ;
} ;
#endif

View File

@ -32,12 +32,13 @@
#include "gassert.h" #include "gassert.h"
#include <string> #include <string>
GSmtp::ServerProtocol::ServerProtocol( Sender & sender , const std::string & thishost , GSmtp::ServerProtocol::ServerProtocol( Sender & sender , Verifier & verifier , ProtocolMessage & pmessage ,
const std::string & peer_address ) : const std::string & thishost , const std::string & peer_address ) :
m_thishost(thishost) , m_thishost(thishost) ,
m_sender(sender) , m_sender(sender) ,
m_verifier(verifier) ,
m_pmessage(pmessage) ,
m_state(sStart) , m_state(sStart) ,
m_ss(NULL) ,
m_peer_address(peer_address) m_peer_address(peer_address)
{ {
// (dont send anything to the peer from this ctor -- the Sender // (dont send anything to the peer from this ctor -- the Sender
@ -71,7 +72,9 @@ void GSmtp::ServerProtocol::addTransition( Event e , State state_from , State st
void GSmtp::ServerProtocol::sendGreeting( const std::string & thishost , const std::string & ident ) void GSmtp::ServerProtocol::sendGreeting( const std::string & thishost , const std::string & ident )
{ {
ss() << "220 " << thishost << " -- " << ident << " -- Service ready" << end() ; std::stringstream ss ;
ss << "220 " << thishost << " -- " << ident << " -- Service ready" ;
send( ss.str() ) ;
} }
bool GSmtp::ServerProtocol::apply( const std::string & line ) bool GSmtp::ServerProtocol::apply( const std::string & line )
@ -82,26 +85,34 @@ bool GSmtp::ServerProtocol::apply( const std::string & line )
{ {
G_LOG( "GSmtp::ServerProtocol: rx<<: [message content not logged]" ) ; G_LOG( "GSmtp::ServerProtocol: rx<<: [message content not logged]" ) ;
G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::toPrintableAscii(line) << "\"" ) ; G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::toPrintableAscii(line) << "\"" ) ;
std::string reason = m_message.process() ; m_state = sProcessing ;
const bool success = reason.empty() ; m_pmessage.process( *this ) ; // processDone() callback
m_state = sIdle ;
sendCompletionReply( success , reason ) ;
} }
else else
{ {
m_message.addText( isEscaped(line) ? line.substr(1U) : line ) ; m_pmessage.addText( isEscaped(line) ? line.substr(1U) : line ) ;
} }
return false ; return false ;
} }
else else
{ {
G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::toPrintableAscii(line) << "\"" ) ; G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::toPrintableAscii(line) << "\"" ) ;
Event event = commandEvent( commandString(line) ) ; Event event = commandEvent( commandWord(line) ) ;
State new_state = applyEvent( event , line ) ; State new_state = applyEvent( event , commandLine(line) ) ;
return new_state == sEnd ; return new_state == sEnd ;
} }
} }
void GSmtp::ServerProtocol::processDone( bool success , unsigned long , const std::string & reason )
{
G_ASSERT( m_state == sProcessing ) ; // (a RSET will call m_pmessage.clear() to cancel the callback)
if( m_state == sProcessing ) // just in case
{
m_state = sIdle ;
sendCompletionReply( success , reason ) ;
}
}
GSmtp::ServerProtocol::State GSmtp::ServerProtocol::applyEvent( Event event , const std::string & line ) GSmtp::ServerProtocol::State GSmtp::ServerProtocol::applyEvent( Event event , const std::string & line )
{ {
// look up in the multimap keyed on current-state + event // look up in the multimap keyed on current-state + event
@ -154,17 +165,26 @@ void GSmtp::ServerProtocol::doNoop( const std::string & , bool & )
void GSmtp::ServerProtocol::doVrfy( const std::string & line , bool & ) void GSmtp::ServerProtocol::doVrfy( const std::string & line , bool & )
{ {
size_t pos = line.find_first_of( " \t" ) ; std::string mbox = parseMailbox( line ) ;
std::string user = line.substr(pos) ; Verifier::Status rc = m_verifier.verify( mbox ) ;
G::Str::trimLeft( user , " \t" ) ;
std::pair<bool,std::string> rc = ProtocolMessage::verify( user ) ;
bool local = rc.first ; bool local = rc.first ;
if( local && rc.second.length() ) if( local && rc.second.length() )
sendVerified( rc.second ) ; sendVerified( rc.second ) ;
else if( local ) else if( local )
sendNotVerified( rc.second ) ; sendNotVerified( mbox ) ;
else else
sendWillAccept( rc.second ) ; sendWillAccept( mbox ) ;
}
std::string GSmtp::ServerProtocol::parseMailbox( const std::string & line ) const
{
std::string user ;
size_t pos = line.find_first_of( " \t" ) ;
if( pos != std::string::npos )
user = line.substr(pos) ;
G::Str::trim( user , " \t" ) ;
return user ;
} }
void GSmtp::ServerProtocol::doEhlo( const std::string & line , bool & predicate ) void GSmtp::ServerProtocol::doEhlo( const std::string & line , bool & predicate )
@ -178,7 +198,7 @@ void GSmtp::ServerProtocol::doEhlo( const std::string & line , bool & predicate
else else
{ {
m_peer_name = peer_name ; m_peer_name = peer_name ;
m_message.clear() ; m_pmessage.clear() ;
sendEhloReply( m_thishost ) ; sendEhloReply( m_thishost ) ;
} }
} }
@ -194,16 +214,16 @@ void GSmtp::ServerProtocol::doHelo( const std::string & line , bool & predicate
else else
{ {
m_peer_name = peer_name ; m_peer_name = peer_name ;
m_message.clear() ; m_pmessage.clear() ;
sendHeloReply( m_thishost ) ; sendHeloReply( m_thishost ) ;
} }
} }
void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate ) void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate )
{ {
m_message.clear() ; m_pmessage.clear() ;
std::string from = parseFrom( line ) ; std::string from = parseFrom( line ) ;
bool ok = m_message.setFrom( from ) ; bool ok = m_pmessage.setFrom( from ) ;
predicate = ok ; predicate = ok ;
if( ok ) if( ok )
sendMailReply() ; sendMailReply() ;
@ -214,7 +234,7 @@ void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate
void GSmtp::ServerProtocol::doRcpt( const std::string & line , bool & predicate ) void GSmtp::ServerProtocol::doRcpt( const std::string & line , bool & predicate )
{ {
std::string to = parseTo( line ) ; std::string to = parseTo( line ) ;
bool ok = m_message.addTo( to ) ; bool ok = m_pmessage.addTo( to , m_verifier.verify(to) ) ;
predicate = ok ; predicate = ok ;
if( ok ) if( ok )
sendRcptReply() ; sendRcptReply() ;
@ -229,7 +249,7 @@ void GSmtp::ServerProtocol::doUnknown( const std::string & line , bool & )
void GSmtp::ServerProtocol::doRset( const std::string & line , bool & ) void GSmtp::ServerProtocol::doRset( const std::string & line , bool & )
{ {
m_message.clear() ; m_pmessage.clear() ;
sendRsetReply() ; sendRsetReply() ;
} }
@ -240,7 +260,7 @@ void GSmtp::ServerProtocol::doNoRecipients( const std::string & line , bool & )
void GSmtp::ServerProtocol::doData( const std::string & line , bool & ) void GSmtp::ServerProtocol::doData( const std::string & line , bool & )
{ {
m_message.addReceived( receivedLine() ) ; m_pmessage.addReceived( receivedLine() ) ;
sendDataReply() ; sendDataReply() ;
} }
@ -264,14 +284,25 @@ bool GSmtp::ServerProtocol::isEscaped( const std::string & line ) const
return line.length() > 1U && line.at(0U) == '.' ; return line.length() > 1U && line.at(0U) == '.' ;
} }
std::string GSmtp::ServerProtocol::commandString( const std::string & line ) const std::string GSmtp::ServerProtocol::commandWord( const std::string & line_in ) const
{ {
size_t ws_pos = line.find_first_of( " \t" ) ; std::string line( line_in ) ;
std::string command = line.substr( 0U , ws_pos ) ; G::Str::trimLeft( line , " \t" ) ;
size_t pos = line.find_first_of( " \t" ) ;
std::string command = line.substr( 0U , pos ) ;
G::Str::toUpper( command ) ; G::Str::toUpper( command ) ;
return command ; return command ;
} }
std::string GSmtp::ServerProtocol::commandLine( const std::string & line_in ) const
{
std::string line( line_in ) ;
G::Str::trimLeft( line , " \t" ) ;
return line ;
}
//static //static
GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::commandEvent( const std::string & command ) GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::commandEvent( const std::string & command )
{ {
@ -358,11 +389,12 @@ void GSmtp::ServerProtocol::sendBadTo( const std::string & to )
void GSmtp::ServerProtocol::sendEhloReply( const std::string & domain ) void GSmtp::ServerProtocol::sendEhloReply( const std::string & domain )
{ {
ss() std::stringstream ss ;
ss
<< "250-" << domain << " says hello" << crlf() << "250-" << domain << " says hello" << crlf()
//<<"250-XYZEXTENSION" << crlf() //<<"250-XYZEXTENSION" << crlf()
<< "250 8BITMIME" << "250 8BITMIME" ;
<< end() ; send( ss.str() ) ;
} }
void GSmtp::ServerProtocol::sendHeloReply( const std::string & domain ) void GSmtp::ServerProtocol::sendHeloReply( const std::string & domain )
@ -375,25 +407,6 @@ void GSmtp::ServerProtocol::sendOk()
send( "250 OK" ) ; send( "250 OK" ) ;
} }
std::ostream & GSmtp::ServerProtocol::ss()
{
delete m_ss ;
m_ss = NULL ;
m_ss = new std::stringstream ;
return *m_ss ;
}
GSmtp::ServerProtocol::End GSmtp::ServerProtocol::end()
{
return End(*this) ;
}
void GSmtp::ServerProtocol::onEnd()
{
G_ASSERT( m_ss != NULL ) ;
send( m_ss->str() ) ;
}
//static //static
std::string GSmtp::ServerProtocol::crlf() std::string GSmtp::ServerProtocol::crlf()
{ {
@ -409,7 +422,6 @@ void GSmtp::ServerProtocol::send( std::string line )
GSmtp::ServerProtocol::~ServerProtocol() GSmtp::ServerProtocol::~ServerProtocol()
{ {
delete m_ss ;
} }
std::string GSmtp::ServerProtocol::parseFrom( const std::string & line ) const std::string GSmtp::ServerProtocol::parseFrom( const std::string & line ) const
@ -454,7 +466,7 @@ std::string GSmtp::ServerProtocol::parsePeerName( const std::string & line ) con
return std::string() ; return std::string() ;
std::string peer_name = line.substr( pos + 1U ) ; std::string peer_name = line.substr( pos + 1U ) ;
G::Str::trimLeft( peer_name , " \t" ) ; G::Str::trim( peer_name , " \t" ) ;
return peer_name ; return peer_name ;
} }
@ -491,13 +503,6 @@ GSmtp::ServerProtocol::Sender::~Sender()
// === // ===
GSmtp::ServerProtocol::End::End( ServerProtocol & p ) :
m_p(p)
{
}
// ===
GSmtp::ServerProtocol::Transition::Transition( State s1 , State s2 , Action a , State s3 ) : GSmtp::ServerProtocol::Transition::Transition( State s1 , State s2 , Action a , State s3 ) :
from(s1) , from(s1) ,
to(s2) , to(s2) ,

View File

@ -27,6 +27,7 @@
#include "gdef.h" #include "gdef.h"
#include "gsmtp.h" #include "gsmtp.h"
#include "gprotocolmessage.h" #include "gprotocolmessage.h"
#include "gverifier.h"
#include <map> #include <map>
namespace GSmtp namespace GSmtp
@ -56,36 +57,40 @@ namespace GSmtp
// //
// See also: ProtocolMessage, RFC2821 // See also: ProtocolMessage, RFC2821
// //
class GSmtp::ServerProtocol class GSmtp::ServerProtocol : private GSmtp:: ProtocolMessage::Callback
{ {
public: public:
class Sender // Used to send protocol replies. class Sender // An interface used by ServerProtocol to send protocol replies.
{ {
public: virtual void protocolSend( const std::string & s ) = 0 ; public: virtual void protocolSend( const std::string & s ) = 0 ;
public: virtual void protocolDone() = 0 ; public: virtual void protocolDone() = 0 ;
public: virtual ~Sender() ; public: virtual ~Sender() ;
private: void operator=( const Sender & ) ; private: void operator=( const Sender & ) ; // not implemented
} ; } ;
class End // A private implementation class. ServerProtocol( Sender & sender , Verifier & verifier , ProtocolMessage & pmessage ,
{ const std::string & thishost , const std::string & peer_address ) ;
public: ServerProtocol & m_p ; // Constructor.
public: End( ServerProtocol & p ) ; //
} ; // The Verifier interface is used to verify recipient
// addresses. See GSmtp::Verifier.
ServerProtocol( Sender & sender , const std::string & thishost , //
const std::string & peer_address ) ; // The ProtocolMessage interface is used to assemble and
// Constructor. The Sender interface is used to // process an incoming message.
// send protocol replies back to the client. //
// The 'thishost' string is used in HELO // The Sender interface is used to send protocol
// replies (etc.) The peer address string is // replies back to the client.
// put into the "Received:" trace lines. //
// The 'thishost' string is used in HELO replies (etc).
//
// The peer address string is put into the "Received:"
// trace lines.
void init( const std::string & ident ) ; void init( const std::string & ident ) ;
// Starts the protocol. The 'ident' string is issued // Starts the protocol. The 'ident' string is issued
// to the client. // to the client.
~ServerProtocol() ; virtual ~ServerProtocol() ;
// Destructor. // Destructor.
bool apply( const std::string & line ) ; bool apply( const std::string & line ) ;
@ -94,9 +99,6 @@ public:
// Returns true if the protocol has completed // Returns true if the protocol has completed
// and Sender::protocolDone() has been called. // and Sender::protocolDone() has been called.
void onEnd() ;
// A pseudo-private method used by the End class.
private: private:
enum Event enum Event
{ {
@ -120,6 +122,7 @@ private:
sGotMail , sGotMail ,
sGotRcpt , sGotRcpt ,
sData , sData ,
sProcessing ,
s_Any , s_Any ,
s_Same s_Same
} ; } ;
@ -135,15 +138,15 @@ private:
typedef std::multimap<Event,Transition GLessAllocator(Event,Transition) > Map ; typedef std::multimap<Event,Transition GLessAllocator(Event,Transition) > Map ;
private: private:
ServerProtocol( const ServerProtocol & ) ; ServerProtocol( const ServerProtocol & ) ; // not implemented
void operator=( const ServerProtocol & ) ; void operator=( const ServerProtocol & ) ; // not implemented
State applyEvent( Event , const std::string & ) ; State applyEvent( Event , const std::string & ) ;
void send( std::string ) ; void send( std::string ) ;
static Event commandEvent( const std::string & ) ; static Event commandEvent( const std::string & ) ;
std::string commandString( const std::string & line ) const ; std::string commandWord( const std::string & line ) const ;
std::ostream & ss() ; std::string commandLine( const std::string & line ) const ;
End end() ;
static std::string crlf() ; static std::string crlf() ;
virtual void processDone( bool , unsigned long , const std::string & ) ; // from ProtocolMessage
bool isEndOfText( const std::string & ) const ; bool isEndOfText( const std::string & ) const ;
bool isEscaped( const std::string & ) const ; bool isEscaped( const std::string & ) const ;
void addTransition( Event , State old , State new_ , Action , State alt = s_Same ) ; void addTransition( Event , State old , State new_ , Action , State alt = s_Same ) ;
@ -179,29 +182,20 @@ private:
void sendOk() ; void sendOk() ;
std::string parseFrom( const std::string & ) const ; std::string parseFrom( const std::string & ) const ;
std::string parseTo( const std::string & ) const ; std::string parseTo( const std::string & ) const ;
std::string parseMailbox( const std::string & ) const ;
std::string parsePeerName( const std::string & ) const ; std::string parsePeerName( const std::string & ) const ;
std::string parse( const std::string & ) const ; std::string parse( const std::string & ) const ;
std::string receivedLine() const ; std::string receivedLine() const ;
private: private:
Sender & m_sender ; Sender & m_sender ;
ProtocolMessage & m_pmessage ;
Verifier & m_verifier ;
State m_state ; State m_state ;
Map m_map ; Map m_map ;
ProtocolMessage m_message ;
std::stringstream * m_ss ;
std::string m_thishost ; std::string m_thishost ;
std::string m_peer_name ; std::string m_peer_name ;
std::string m_peer_address ; std::string m_peer_address ;
} ; } ;
namespace GSmtp
{
inline
std::ostream & operator<<( std::ostream & stream , const ServerProtocol::End & e )
{
e.m_p.onEnd() ;
return stream ;
}
} ;
#endif #endif

View File

@ -30,6 +30,7 @@
#include "gmemory.h" #include "gmemory.h"
#include "gsmtpclient.h" #include "gsmtpclient.h"
#include "gresolve.h" #include "gresolve.h"
#include "gassert.h"
#include "glog.h" #include "glog.h"
//static //static
@ -38,20 +39,31 @@ std::string GSmtp::Client::crlf()
return std::string("\015\012") ; return std::string("\015\012") ;
} }
GSmtp::Client::Client( GSmtp::MessageStore & store , bool quit_on_disconnect ) : GSmtp::Client::Client( MessageStore & store , bool quit_on_disconnect ) :
GNet::Client(false,quit_on_disconnect) , GNet::Client(false,quit_on_disconnect) ,
m_callback(NULL) , m_callback(NULL) ,
m_store(store) , m_store(&store) ,
m_buffer(crlf()) , m_buffer(crlf()) ,
m_protocol(*this,GNet::Local::fqdn()) , m_protocol(*this,GNet::Local::fqdn()) ,
m_socket(NULL) m_socket(NULL)
{ {
} }
GSmtp::Client::Client( GSmtp::MessageStore & store , ClientCallback & callback , bool quit_on_disconnect ) : GSmtp::Client::Client( MessageStore & store , ClientCallback & callback , bool quit_on_disconnect ) :
GNet::Client(false,quit_on_disconnect) , GNet::Client(false,quit_on_disconnect) ,
m_callback(&callback) , m_callback(&callback) ,
m_store(store) , m_store(&store) ,
m_buffer(crlf()) ,
m_protocol(*this,GNet::Local::fqdn()) ,
m_socket(NULL)
{
}
GSmtp::Client::Client( std::auto_ptr<StoredMessage> message , ClientCallback & callback ) :
GNet::Client(false,false) ,
m_callback(&callback) ,
m_store(NULL) ,
m_message(message) ,
m_buffer(crlf()) , m_buffer(crlf()) ,
m_protocol(*this,GNet::Local::fqdn()) , m_protocol(*this,GNet::Local::fqdn()) ,
m_socket(NULL) m_socket(NULL)
@ -69,7 +81,7 @@ std::string GSmtp::Client::init( const std::string & s )
std::string GSmtp::Client::init( const std::string & host , const std::string & service ) std::string GSmtp::Client::init( const std::string & host , const std::string & service )
{ {
if( m_store.empty() ) if( m_store != NULL && m_store->empty() )
return "no messages to send" ; return "no messages to send" ;
std::string error ; std::string error ;
@ -107,20 +119,17 @@ bool GSmtp::Client::protocolSend( const std::string & line )
void GSmtp::Client::onConnect( GNet::Socket & socket ) void GSmtp::Client::onConnect( GNet::Socket & socket )
{ {
m_socket = &socket ; m_socket = &socket ;
m_iter = m_store.iterator() ; if( m_store != NULL )
if( !sendNext() )
finish() ;
}
void GSmtp::Client::finish()
{
if( m_callback != NULL )
{ {
m_callback->onCompletion(std::string()) ; m_iter = m_store->iterator() ;
m_callback = NULL ; if( !sendNext() )
finish() ;
}
else
{
G_ASSERT( m_message.get() != NULL ) ;
start( *m_message.get() ) ;
} }
disconnect() ; // GNet::Client::disconnect()
} }
bool GSmtp::Client::sendNext() bool GSmtp::Client::sendNext()
@ -140,29 +149,41 @@ bool GSmtp::Client::sendNext()
m_message = message ; m_message = message ;
} }
m_protocol.start( m_message->from() , m_message->to() , m_message->eightBit() , start( *m_message.get() ) ;
m_message->extractContentStream() , *this ) ;
return true ; return true ;
} }
void GSmtp::Client::callback( bool ok ) void GSmtp::Client::start( StoredMessage & message )
{ {
G_DEBUG( "GSmtp::Client::callback: " << ok ) ; m_protocol.start( message.from() , message.to() , message.eightBit() ,
message.extractContentStream() , *this ) ;
}
void GSmtp::Client::protocolDone( bool ok , const std::string & reason )
{
G_DEBUG( "GSmtp::Client::protocolDone: " << ok << ": " << reason ) ;
std::string error_message ;
if( !ok )
error_message = std::string("smtp client protocol failure: ") + reason ;
if( m_message.get() != NULL ) if( m_message.get() != NULL )
{ {
if( ok ) if( ok )
m_message->destroy() ; m_message->destroy() ;
else else
m_message->fail("smtp protocol failure") ; m_message->fail( error_message ) ;
}
if( m_store == NULL || !sendNext() )
{
finish( error_message ) ;
} }
if( !sendNext() )
finish() ;
} }
void GSmtp::Client::onDisconnect() void GSmtp::Client::onDisconnect()
{ {
if( m_callback != NULL ) doCallback( "connection to server lost" ) ;
m_callback->onCompletion( "connection to server lost" ) ;
m_socket = NULL ; m_socket = NULL ;
} }
@ -184,25 +205,38 @@ void GSmtp::Client::onData( const char * data , size_t size )
} }
} }
void GSmtp::Client::onError( const std::string &error ) void GSmtp::Client::onError( const std::string & error )
{
doCallback( std::string("error on connection to server: ") + error ) ;
G_WARNING( "GSmtp::Client: error: \"" << error << "\"" ) ;
}
void GSmtp::Client::finish( const std::string & reason )
{
doCallback( reason ) ;
disconnect() ; // GNet::Client::disconnect()
}
void GSmtp::Client::doCallback( const std::string & reason )
{ {
if( m_callback != NULL ) if( m_callback != NULL )
m_callback->onCompletion( std::string("error on connection to server: ") + error ) ; {
G_WARNING( "GSmtp::Client: error: \"" << error << "\"" ) ; m_callback->clientDone( reason ) ;
m_callback = NULL ;
}
} }
void GSmtp::Client::onWriteable() void GSmtp::Client::onWriteable()
{ {
if( protocolSend(m_pending) ) if( protocolSend(m_pending) )
{ {
m_protocol.sendComplete() ; m_protocol.sendDone() ;
} }
} }
// === // ===
void GSmtp::Client::ClientCallback::onCompletion( std::string ) GSmtp::Client::ClientCallback::~ClientCallback()
{ {
// empty
} }

View File

@ -31,6 +31,7 @@
#include "gclient.h" #include "gclient.h"
#include "gclientprotocol.h" #include "gclientprotocol.h"
#include "gmessagestore.h" #include "gmessagestore.h"
#include "gstoredmessage.h"
#include "gsocket.h" #include "gsocket.h"
#include "gstrings.h" #include "gstrings.h"
#include "gexception.h" #include "gexception.h"
@ -49,17 +50,22 @@ namespace GSmtp
// a remote SMTP server. // a remote SMTP server.
// //
class GSmtp::Client : private GNet::Client , class GSmtp::Client : private GNet::Client ,
private GSmtp::ClientProtocol::Sender , private GSmtp::ClientProtocol::Callback private GSmtp:: ClientProtocol::Sender , private GSmtp:: ClientProtocol::Callback
{ {
public: public:
G_EXCEPTION( NotConnected , "not connected" ) ; G_EXCEPTION( NotConnected , "not connected" ) ;
class ClientCallback // A callback interface used by GSmtp::Client. class ClientCallback // A callback interface used by GSmtp::Client.
{ {
public: virtual void onCompletion( std::string ) ; public: virtual void clientDone( std::string ) = 0 ;
public: virtual ~ClientCallback() ;
private: void operator=( const ClientCallback & ) ; // not implemented
} ; } ;
Client( MessageStore & store , bool quit_on_disconnect ) ; Client( MessageStore & store , bool quit_on_disconnect ) ;
// Constructor. The reference is kept. // Constructor. The message-store reference is kept.
//
// The 'quit_on_disconnect' parameter refers to
// GNet::EventSources::quit().
Client( MessageStore & store , ClientCallback & callback , bool quit_on_disconnect ) ; Client( MessageStore & store , ClientCallback & callback , bool quit_on_disconnect ) ;
// Constructor. The references are kept. // Constructor. The references are kept.
@ -69,20 +75,27 @@ public:
// or that the server connection has // or that the server connection has
// been lost. // been lost.
std::string init( const std::string & host , const std::string & service ) ; Client( std::auto_ptr<StoredMessage> message , ClientCallback & callback ) ;
// Constructor for sending a single message.
//
// The callback is used to signal that
// all message processing has finished
// or that the server connection has
// been lost.
std::string init( const std::string & server_address_string ) ;
// Starts the sending process. Messages // Starts the sending process. Messages
// are extracted from the message store // are extracted from the message store
// (as passed in the ctor) and forwarded // (as passed in the ctor) and forwarded
// on to the specified server. // on to the specified server.
// //
// To be called once (only) after construction.
//
// Returns an error string if there are no messages // Returns an error string if there are no messages
// to be sent, or if the network connection // to be sent, or if the network connection
// cannot be initiated. Returns the empty // cannot be initiated. Returns the empty
// string on success. // string on success.
std::string init( const std::string & host_service ) ;
// An overload.
bool busy() const ; bool busy() const ;
// Returns true if the client is still // Returns true if the client is still
// busy processing messages. // busy processing messages.
@ -93,15 +106,18 @@ private:
virtual void onData( const char * data , size_t size ) ; // GNet::Client virtual void onData( const char * data , size_t size ) ; // GNet::Client
virtual void onWriteable() ; // GNet::Client virtual void onWriteable() ; // GNet::Client
virtual void onError( const std::string & error ) ; // GNet::Client virtual void onError( const std::string & error ) ; // GNet::Client
virtual bool protocolSend( const std::string & ) ; // Sender virtual bool protocolSend( const std::string & ) ; // ClientProtocol::Sender
virtual void callback( bool ) ; // ClientCallback virtual void protocolDone( bool , const std::string & ) ; // ClientProtocol::Callback
std::string init( const std::string & , const std::string & ) ;
GNet::Socket & socket() ; GNet::Socket & socket() ;
static std::string crlf() ; static std::string crlf() ;
bool sendNext() ; bool sendNext() ;
void finish() ; void start( StoredMessage & ) ;
void doCallback( const std::string & ) ;
void finish( const std::string & reason = std::string() ) ;
private: private:
MessageStore & m_store ; MessageStore * m_store ;
std::auto_ptr<StoredMessage> m_message ; std::auto_ptr<StoredMessage> m_message ;
MessageStore::Iterator m_iter ; MessageStore::Iterator m_iter ;
GNet::LineBuffer m_buffer ; GNet::LineBuffer m_buffer ;

View File

@ -24,6 +24,9 @@
#include "gdef.h" #include "gdef.h"
#include "gsmtp.h" #include "gsmtp.h"
#include "gsmtpserver.h" #include "gsmtpserver.h"
#include "gprotocolmessagestore.h"
#include "gprotocolmessageforward.h"
#include "gmemory.h"
#include "glocal.h" #include "glocal.h"
#include "glog.h" #include "glog.h"
#include "gdebug.h" #include "gdebug.h"
@ -31,9 +34,10 @@
#include <string> #include <string>
GSmtp::ServerPeer::ServerPeer( GNet::StreamSocket * socket , GNet::Address peer_address , GSmtp::ServerPeer::ServerPeer( GNet::StreamSocket * socket , GNet::Address peer_address ,
Server & server , const std::string & ident ) : Server & server , std::auto_ptr<ProtocolMessage> pmessage , const std::string & ident ) :
GNet::ServerPeer( socket , peer_address ) , GNet::ServerPeer( socket , peer_address ) ,
m_protocol( *this , thishost() , peer_address.displayString(false) ) , m_pmessage( pmessage ) ,
m_protocol( *this , m_verifier , *m_pmessage.get() , thishost() , peer_address.displayString(false) ) ,
m_buffer( crlf() ) , m_buffer( crlf() ) ,
m_server( server ) m_server( server )
{ {
@ -105,17 +109,19 @@ void GSmtp::ServerPeer::protocolDone()
// === // ===
GSmtp::Server::Server( unsigned int port , bool allow_remote , const std::string & ident ) : GSmtp::Server::Server( unsigned int port , bool allow_remote , const std::string & ident ,
GNet::Server( port ) , const std::string & downstream_server ) :
m_ident( ident ) , GNet::Server( port ) ,
m_allow_remote( allow_remote ) m_ident( ident ) ,
m_allow_remote( allow_remote ) ,
m_downstream_server(downstream_server)
{ {
G_LOG( "GSmtp::Server: listening on port " << port ) ; //G_LOG( "GSmtp::Server: listening on port " << port ) ;
} }
GNet::ServerPeer * GSmtp::Server::newPeer( GNet::StreamSocket * socket , GNet::Address peer_address ) GNet::ServerPeer * GSmtp::Server::newPeer( GNet::StreamSocket * socket , GNet::Address peer_address )
{ {
std::auto_ptr<GNet::StreamSocket> ptr(socket) ; std::auto_ptr<GNet::StreamSocket> socket_ptr(socket) ;
if( ! m_allow_remote && if( ! m_allow_remote &&
!peer_address.sameHost(GNet::Local::canonicalAddress()) && !peer_address.sameHost(GNet::Local::canonicalAddress()) &&
@ -128,6 +134,13 @@ GNet::ServerPeer * GSmtp::Server::newPeer( GNet::StreamSocket * socket , GNet::A
return NULL ; return NULL ;
} }
return new ServerPeer( ptr.release() , peer_address , *this , m_ident ) ; const bool immediate = ! m_downstream_server.empty() ;
std::auto_ptr<ProtocolMessage> pmessage(
immediate ?
static_cast<ProtocolMessage*>(new ProtocolMessageForward(m_downstream_server)) :
static_cast<ProtocolMessage*>(new ProtocolMessageStore) ) ;
return new ServerPeer( socket_ptr.release() , peer_address , *this , pmessage , m_ident ) ;
} }

View File

@ -28,9 +28,12 @@
#include "gsmtp.h" #include "gsmtp.h"
#include "gserver.h" #include "gserver.h"
#include "glinebuffer.h" #include "glinebuffer.h"
#include "gverifier.h"
#include "gserverprotocol.h" #include "gserverprotocol.h"
#include "gprotocolmessage.h"
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <memory>
namespace GSmtp namespace GSmtp
{ {
@ -43,11 +46,12 @@ namespace GSmtp
// Instances are created on the heap by Server (only). // Instances are created on the heap by Server (only).
// See also: Server // See also: Server
// //
class GSmtp::ServerPeer : public GNet::ServerPeer , private GSmtp::ServerProtocol::Sender class GSmtp::ServerPeer : public GNet::ServerPeer , private GSmtp:: ServerProtocol::Sender
{ {
public: public:
ServerPeer( GNet::StreamSocket * , GNet::Address , Server & server , const std::string & ident ) ; ServerPeer( GNet::StreamSocket * socket , GNet::Address address ,
// Constructor. Server & server , std::auto_ptr<ProtocolMessage> pmessage , const std::string & ident ) ;
// Constructor.
private: private:
ServerPeer( const ServerPeer & ) ; ServerPeer( const ServerPeer & ) ;
@ -60,10 +64,12 @@ private:
std::string thishost() const ; std::string thishost() const ;
private: private:
Server & m_server ;
GNet::LineBuffer m_buffer ; GNet::LineBuffer m_buffer ;
static std::string crlf() ; static std::string crlf() ;
ServerProtocol m_protocol ; Verifier m_verifier ; // order dependency -- first
Server & m_server ; std::auto_ptr<ProtocolMessage> m_pmessage ; // order dependency -- second
ServerProtocol m_protocol ; // order dependency -- third
} ; } ;
// Class: GSmtp::Server // Class: GSmtp::Server
@ -72,8 +78,12 @@ private:
class GSmtp::Server : private GNet::Server class GSmtp::Server : private GNet::Server
{ {
public: public:
Server( unsigned int port , bool allow_remote , const std::string & ident ) ; Server( unsigned int port , bool allow_remote , const std::string & ident ,
// Constructor. const std::string & downstream_server_address ) ;
// Constructor.
//
// If the 'downstream-server-address' parameter is
// given then all messages are forwarded immediately.
private: private:
virtual GNet::ServerPeer * newPeer( GNet::StreamSocket * , GNet::Address ) ; virtual GNet::ServerPeer * newPeer( GNet::StreamSocket * , GNet::Address ) ;
@ -83,6 +93,7 @@ private:
private: private:
std::string m_ident ; std::string m_ident ;
bool m_allow_remote ; bool m_allow_remote ;
std::string m_downstream_server ;
} ; } ;
#endif #endif

262
src/main/gstoredfile.cpp Normal file
View File

@ -0,0 +1,262 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gstoredfile.cpp
//
#include "gdef.h"
#include "gsmtp.h"
#include "gfilestore.h"
#include "gstoredfile.h"
#include "gmemory.h"
#include "gfile.h"
#include "gstr.h"
#include "glog.h"
#include "gassert.h"
#include <fstream>
GSmtp::StoredFile::StoredFile( const G::Path & path ) :
m_envelope_path(path)
{
G_DEBUG( "StoredFile: \"" << path << "\"" ) ;
}
GSmtp::StoredFile::~StoredFile()
{
}
bool GSmtp::StoredFile::eightBit() const
{
return m_eight_bit ;
}
bool GSmtp::StoredFile::readEnvelope( std::string & reason , bool check )
{
try
{
readEnvelopeCore( check ) ;
return true ;
}
catch( std::exception & e )
{
reason = e.what() ;
return false ;
}
}
void GSmtp::StoredFile::readEnvelopeCore( bool check )
{
std::ifstream stream( m_envelope_path.str().c_str() , std::ios_base::binary | std::ios_base::in ) ;
if( ! stream.good() )
throw OpenError() ;
readFormat( stream ) ;
readFlag( stream ) ;
readFrom( stream ) ;
readToList( stream ) ;
if( check && m_to_remote.size() == 0U )
throw NoRecipients() ;
if( ! stream.good() )
throw StreamError() ;
}
void GSmtp::StoredFile::readFormat( std::istream & stream )
{
std::string format_line = getline(stream) ;
if( value(format_line) != FileStore::format() )
throw InvalidFormat( value(format_line) + "!=" + FileStore::format() ) ;
}
void GSmtp::StoredFile::readFlag( std::istream & stream )
{
std::string content_line = getline(stream) ;
m_eight_bit = value(content_line) == "8bit" ;
}
void GSmtp::StoredFile::readFrom( std::istream & stream )
{
m_from = value(getline(stream)) ;
G_DEBUG( "GSmtp::StoredFile::readFrom: from \"" << m_from << "\"" ) ;
}
void GSmtp::StoredFile::readToList( std::istream & stream )
{
std::string to_count_line = getline(stream) ;
unsigned int to_count = G::Str::toUInt( value(to_count_line) ) ;
for( unsigned int i = 0U ; i < to_count ; i++ )
{
std::string to_line = getline(stream) ;
bool is_local = to_line.find(FileStore::x()+"To-Local") == 0U ;
bool is_remote = to_line.find(FileStore::x()+"To-Remote") == 0U ;
if( ! is_local && ! is_remote )
throw InvalidTo(to_line) ;
G_DEBUG( "GSmtp::StoredFile::readToList: to "
"[" << (i+1U) << "/" << to_count << "] "
"(" << (is_local?"local":"remote") << ") "
<< "\"" << value(to_line) << "\"" ) ;
if( is_local )
m_to_local.push_back( value(to_line) ) ;
else
m_to_remote.push_back( value(to_line) ) ;
}
}
void GSmtp::StoredFile::readEnd( std::istream & stream )
{
std::string end = getline(stream) ;
if( end.find(FileStore::x()+"End") != 0U )
throw NoEnd() ;
}
bool GSmtp::StoredFile::openContent( std::string & reason )
{
try
{
G::Path content_path = contentPath() ;
G_DEBUG( "GSmtp::FileStore::openContent: \"" << content_path << "\"" ) ;
std::auto_ptr<std::istream> stream( new std::ifstream(
content_path.str().c_str() , std::ios_base::in | std::ios_base::binary ) ) ;
if( !stream->good() )
{
reason = "cannot open content file" ;
return false ;
}
G_LOG( "GSmtp::MessageStore: processing envelope \"" << m_envelope_path.basename() << "\"" ) ;
G_LOG( "GSmtp::MessageStore: processing content \"" << content_path.basename() << "\"" ) ;
m_content = stream ;
return true ;
}
catch( std::exception & e )
{
G_WARNING( "GSmtp::FileStore: exception: " << e.what() ) ;
reason = e.what() ;
return false ;
}
}
std::string GSmtp::StoredFile::getline( std::istream & stream ) const
{
return G::Str::readLineFrom( stream , crlf() ) ;
}
std::string GSmtp::StoredFile::value( const std::string & s ) const
{
size_t pos = s.find(' ') ;
if( pos == std::string::npos )
throw MessageStore::FormatError() ;
return s.substr(pos+1U) ;
}
std::string GSmtp::StoredFile::crlf() const
{
return std::string( "\015\012" ) ;
}
bool GSmtp::StoredFile::lock()
{
G::Path & src = m_envelope_path ;
G::Path dst( src.str() + ".busy" ) ;
bool ok = G::File::rename( src , dst , G::File::NoThrow() ) ;
if( ok )
{
G_LOG( "GSmtp::StoredMessage: locking file \"" << src.basename() << "\"" ) ;
m_envelope_path = dst ;
}
return ok ;
}
void GSmtp::StoredFile::fail( const std::string & reason )
{
try
{
// write the reason into the file
{
std::ofstream file( m_envelope_path.str().c_str() ,
std::ios_base::binary | std::ios_base::ate ) ;
file << FileStore::x() << "Reason: " << reason ;
}
G::Path env_temp( m_envelope_path ) ; // "foo.envelope.busy"
env_temp.removeExtension() ; // "foo.envelope"
G::Path bad( env_temp.str() + ".bad" ) ; // "foo.envelope.bad"
G_LOG( "GSmtp::StoredMessage: failing file: "
<< "\"" << m_envelope_path.basename() << "\" -> "
<< "\"" << bad.basename() << "\"" ) ;
G::File::rename( m_envelope_path , bad , G::File::NoThrow() ) ;
}
catch(...)
{
}
}
void GSmtp::StoredFile::destroy()
{
try
{
G_LOG( "GSmtp::StoredMessage: deleting file: \"" << m_envelope_path.basename() << "\"" ) ;
G::File::remove( m_envelope_path , G::File::NoThrow() ) ;
G::Path content_path = contentPath() ;
G_LOG( "GSmtp::StoredMessage: deleting file: \"" << content_path.basename() << "\"" ) ;
m_content <<= 0 ; // close it first
G::File::remove( content_path , G::File::NoThrow() ) ;
}
catch(...)
{
}
}
const std::string & GSmtp::StoredFile::from() const
{
return m_from ;
}
const G::Strings & GSmtp::StoredFile::to() const
{
return m_to_remote ;
}
std::auto_ptr<std::istream> GSmtp::StoredFile::extractContentStream()
{
G_ASSERT( m_content.get() != NULL ) ;
return m_content ;
}
G::Path GSmtp::StoredFile::contentPath() const
{
G::Path path( m_envelope_path ) ; // "foo.envelope.busy"
path.removeExtension() ; // "foo.envelope"
path.setExtension( "content" ) ; // "foo.content"
return path ;
}
size_t GSmtp::StoredFile::remoteRecipientCount() const
{
return m_to_remote.size() ;
}

119
src/main/gstoredfile.h Normal file
View File

@ -0,0 +1,119 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gstoredfile.h
//
#ifndef G_SMTP_STORED_FILE_H
#define G_SMTP_STORED_FILE_H
#include "gdef.h"
#include "gsmtp.h"
#include "gmessagestore.h"
#include "gstoredmessage.h"
#include "gexception.h"
#include "gpath.h"
#include "gstrings.h"
#include <iostream>
#include <memory>
namespace GSmtp
{
class StoredFile ;
} ;
// Class: GSmtp::StoredFile
// Description: A concete derived class implementing the
// StoredMessage interface.
//
class GSmtp::StoredFile : public GSmtp:: StoredMessage
{
public:
G_EXCEPTION( InvalidFormat , "invalid format field in envelope" ) ;
G_EXCEPTION( NoEnd , "invalid envelope file: no end marker" ) ;
G_EXCEPTION( InvalidTo , "invalid 'to' line in envelope file" ) ;
G_EXCEPTION( NoRecipients , "no remote recipients" ) ;
G_EXCEPTION( OpenError , "cannot open the envelope" ) ;
G_EXCEPTION( StreamError , "envelope reading/parsing error" ) ;
explicit StoredFile( const G::Path & envelope_path ) ;
// Constructor.
virtual ~StoredFile() ;
// Destructor.
bool lock() ;
// Locks the file by renaming the envelope file.
// Used by FileStore and FileIterator.
bool readEnvelope( std::string & reason , bool check_for_no_remote_recipients ) ;
// Reads the envelope. Returns false on error.
// Used by FileStore and FileIterator.
bool openContent( std::string & reason ) ;
// Opens the content file. Returns false on error.
// Used by FileStore and FileIterator.
virtual bool eightBit() const ;
// From StoredMessage.
virtual const std::string & from() const ;
// From StoredMessage.
virtual const G::Strings & to() const ;
// From StoredMessage.
virtual void destroy() ;
// From StoredMessage.
virtual void fail( const std::string & reason ) ;
// From StoredMessage.
virtual std::auto_ptr<std::istream> extractContentStream() ;
// From StoredMessage.
virtual size_t remoteRecipientCount() const ;
// From StoredMessage.
private:
StoredFile( const StoredFile & ) ;
void operator=( const StoredFile & ) ;
std::string crlf() const ;
std::string getline( std::istream & stream ) const ;
std::string value( const std::string & s ) const ;
G::Path contentPath() const ;
void readFormat( std::istream & stream ) ;
void readFlag( std::istream & stream ) ;
void readFrom( std::istream & stream ) ;
void readToList( std::istream & stream ) ;
void readEnd( std::istream & stream ) ;
void readEnvelopeCore( bool ) ;
private:
G::Strings m_to_local ;
G::Strings m_to_remote ;
std::string m_from ;
G::Path m_envelope_path ;
std::auto_ptr<std::istream> m_content ;
bool m_eight_bit ;
} ;
#endif

View File

@ -18,64 +18,15 @@
// //
// === // ===
// //
// gpid_unix.cpp // gstoredmessage.cpp
// //
#include "gdef.h" #include "gdef.h"
#include "gpid.h" #include "gsmtp.h"
#include <unistd.h> #include "gstoredmessage.h"
#include <sys/types.h>
#include <sstream>
namespace G GSmtp::StoredMessage::~StoredMessage()
{ {
class PidImp ; // empty
} ;
// Class: G::PidImp
// Description: A pimple implementation class for GPid.
//
class G::PidImp
{
public:
pid_t m_pid ;
} ;
// ===
G::Pid::Pid() : m_imp(NULL)
{
m_imp = new PidImp ;
m_imp->m_pid = ::getpid() ;
}
G::Pid::~Pid()
{
delete m_imp ;
}
G::Pid::Pid( const Pid & other ) :
m_imp(NULL)
{
m_imp = new PidImp ;
m_imp->m_pid = other.m_imp->m_pid ;
}
G::Pid & G::Pid::operator=( const Pid & rhs )
{
m_imp->m_pid = rhs.m_imp->m_pid ;
return *this ;
}
std::string G::Pid::str() const
{
std::stringstream ss ;
ss << m_imp->m_pid ;
return ss.str() ;
}
bool G::Pid::operator==( const Pid & rhs ) const
{
return m_imp->m_pid == rhs.m_imp->m_pid ;
} }

77
src/main/gstoredmessage.h Normal file
View File

@ -0,0 +1,77 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gstoredmessage.h
//
#ifndef G_SMTP_STORED_MESSAGE_H
#define G_SMTP_STORED_MESSAGE_H
#include "gdef.h"
#include "gsmtp.h"
#include "gstrings.h"
#include "gpath.h"
namespace GSmtp
{
class StoredMessage ;
} ;
// Class: GSmtp::StoredMessage
// Description: An abstract class for messages which have
// come from the store.
// See also: MessageStore, MessageStore::get()
//
class GSmtp::StoredMessage
{
public:
virtual const std::string & from() const = 0 ;
// Returns the envelope 'from' field.
virtual const G::Strings & to() const = 0 ;
// Returns the envelope 'to' fields.
virtual std::auto_ptr<std::istream> extractContentStream() = 0 ;
// Extracts the content stream.
// Can only be called once.
virtual void destroy() = 0 ;
// Deletes the message within the store.
virtual void fail( const std::string & reason ) = 0 ;
// Marks the message as failed within the store.
virtual bool eightBit() const = 0 ;
// Returns true if the message content (header+body)
// contains a character with the most significant
// bit set.
virtual size_t remoteRecipientCount() const = 0 ;
// Returns the number of non-local recipients.
virtual ~StoredMessage() ;
// Destructor.
private:
void operator=( const StoredMessage & ) ; // not implemented
} ;
#endif

66
src/main/gverifier.cpp Normal file
View File

@ -0,0 +1,66 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gverifier.cpp
//
#include "gdef.h"
#include "gsmtp.h"
#include "gverifier.h"
#include "gstr.h"
#include "gassert.h"
#include "glog.h"
GSmtp::Verifier::Status GSmtp::Verifier::verify( const std::string & user ) const
{
G_DEBUG( "GSmtp::ProtocolMessage::verify: \"" << user << "\"" ) ;
Status rc( isLocal(user) , std::string() ) ;
if( isLocal(user) && isValid(user) )
rc.second = fullName(user) ;
return rc ;
}
//static
bool GSmtp::Verifier::isLocal( const std::string & user )
{
return user.find('@') == std::string::npos ;
}
//static
bool GSmtp::Verifier::isValid( const std::string & user )
{
// only recognise one local mailbox
return isPostmaster(user) ;
}
//static
bool GSmtp::Verifier::isPostmaster( std::string user )
{
G::Str::toUpper( user ) ;
G::Str::trim( user , " \t" ) ;
return user == "POSTMASTER" ;
}
//static
std::string GSmtp::Verifier::fullName( const std::string & user )
{
return "Local postmaster <postmaster@localhost>" ;
}

64
src/main/gverifier.h Normal file
View File

@ -0,0 +1,64 @@
//
// Copyright (C) 2001 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.
//
// ===
//
// gverifier.h
//
#ifndef G_SMTP_VERIFIER_H
#define G_SMTP_VERIFIER_H
#include "gdef.h"
#include "gsmtp.h"
#include <string>
namespace GSmtp
{
class Verifier ;
} ;
// Class: GSmtp::Verifier
// Description: A class which verifies recipient addresses.
// This functionality is used in the VRFY and RCPT commands
// in the SMTP server-side protocol.
// See also: ServerProtocol
//
class GSmtp::Verifier
{
public:
typedef std::pair<bool,std::string> Status ;
std::pair<bool,std::string> verify( const std::string & recipient_address ) const ;
// Checks a recipient address returning
// <is-local>|<local-full-name>.
//
// If syntactically local then 'first' is
// returned true. If local and valid then
// 'second' is set to the full description.
// If syntactically remote, then 'first'
// is returned false and 'second' is empty.
private:
static bool isLocal( const std::string & user ) ;
static bool isValid( const std::string & user ) ;
static bool isPostmaster( std::string user ) ;
static std::string fullName( const std::string & user ) ;
} ;
#endif

BIN
src/main/icon-32.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View File

@ -30,9 +30,12 @@
#include "gdaemon.h" #include "gdaemon.h"
#include "gstr.h" #include "gstr.h"
#include "gpath.h" #include "gpath.h"
#include "gmessagestore.h" #include "gfilestore.h"
#include "gnewfile.h"
#include "gadminserver.h" #include "gadminserver.h"
#include "gexception.h" #include "gexception.h"
#include "gprocess.h"
#include "gmemory.h"
#include "ggetopt.h" #include "ggetopt.h"
#include "gdebug.h" #include "gdebug.h"
#include <iostream> #include <iostream>
@ -62,6 +65,7 @@ namespace
unsigned int optPort() const ; unsigned int optPort() const ;
unsigned int optAdminPort() const ; unsigned int optAdminPort() const ;
bool optCloseStderr() const ; bool optCloseStderr() const ;
bool optImmediate() const ;
bool optLog() const ; bool optLog() const ;
bool optSyslog() const ; bool optSyslog() const ;
bool optDaemon() const ; bool optDaemon() const ;
@ -102,8 +106,8 @@ void Main::warranty() const
{ {
std::cout std::cout
<< "This software is provided without warranty of any kind." << std::endl << "This software is provided without warranty of any kind." << std::endl
<< "You may redistribure copies of this program under the terms of the GNU " << "You may redistribure copies of this program under " << std::endl
<< "General Public License." << std::endl << "the terms of the GNU General Public License." << std::endl
<< "For more information refer to the file named COPYING." << std::endl ; << "For more information refer to the file named COPYING." << std::endl ;
} }
@ -116,7 +120,7 @@ void Main::version() const
std::string Main::versionNumber() const std::string Main::versionNumber() const
{ {
return "0.9.2" ; return "0.9.3" ;
} }
std::string Main::smtpIdent() const std::string Main::smtpIdent() const
@ -130,7 +134,10 @@ unsigned int Main::ttyColumns() const
try try
{ {
const char * p = std::getenv( "COLUMNS" ) ; const char * p = std::getenv( "COLUMNS" ) ;
return p ? G::Str::toUInt(p) : default_ ; if( p == NULL )
return default_ ;
else
return G::Str::toUInt(p) ;
} }
catch( std::exception & ) catch( std::exception & )
{ {
@ -158,9 +165,8 @@ void Main::help( const std::string & exe ) const
<< std::endl ; << std::endl ;
std::cout std::cout
<< "To start a 'store & forward' daemon..." << std::endl << "To run as a proxy (on port 10025) to a local server (on port 25)..." << std::endl
<< " " << exe << " --as-server --admin 10025 --forward-to mail.myisp.co.uk:smtp" << std::endl << " " << exe << " --port 10025 --as-proxy localhost:25" << std::endl
<< " (and then \"" << exe << "poke 10025\" to trigger forwarding)" << std::endl
<< std::endl ; << std::endl ;
} }
@ -186,6 +192,7 @@ std::string Main::switchSpec() const
<< "q!as-client!equivalent to \"--no-syslog --no-daemon --log --dont-serve --forward --forward-to\"!" << "q!as-client!equivalent to \"--no-syslog --no-daemon --log --dont-serve --forward --forward-to\"!"
<< "1!host:port|" << "1!host:port|"
<< "d!as-server!equivalent to \"--close-stderr --log\"!0!|" << "d!as-server!equivalent to \"--close-stderr --log\"!0!|"
<< "m!immediate!forwards each message as soon as it is received (requires --forward-to)!0!|"
<< "n!no-syslog!disables syslog output!0!|" << "n!no-syslog!disables syslog output!0!|"
<< "t!no-daemon!does not detach from the terminal!0!|" << "t!no-daemon!does not detach from the terminal!0!|"
<< "x!dont-serve!stops the process acting as a server (usually used with --forward)!0!|" << "x!dont-serve!stops the process acting as a server (usually used with --forward)!0!|"
@ -195,6 +202,8 @@ std::string Main::switchSpec() const
<< "i!pid-file!records the daemon process-id in the given file!1!pid-file|" << "i!pid-file!records the daemon process-id in the given file!1!pid-file|"
<< "p!port!specifies the smtp listening port number!1!port|" << "p!port!specifies the smtp listening port number!1!port|"
<< "a!admin!enables the administration interface and specifies its listening port number!1!admin-port|" << "a!admin!enables the administration interface and specifies its listening port number!1!admin-port|"
<< "y!as-proxy!equivalent to \"--close-stderr --log --immediate --forward-to\"!1!host:port|"
<< "z!filter!defines a mail pre-processor (disallowed if running as root)!1!program|"
<< "V!version!displays version information and exits!0!" << "V!version!displays version information and exits!0!"
; ;
return ss.str() ; return ss.str() ;
@ -244,7 +253,11 @@ void Main::run()
bool Main::optLog() const bool Main::optLog() const
{ {
return opt().contains("log") || opt().contains("as-client") || opt().contains("as-server") ; return
opt().contains("log") ||
opt().contains("as-client") ||
opt().contains("as-proxy") ||
opt().contains("as-server") ;
} }
bool Main::optSyslog() const bool Main::optSyslog() const
@ -266,7 +279,17 @@ unsigned int Main::optAdminPort() const
bool Main::optCloseStderr() const bool Main::optCloseStderr() const
{ {
return opt().contains("close-stderr") || opt().contains("as-server") ; return
opt().contains("close-stderr") ||
opt().contains("as-proxy") ||
opt().contains("as-server") ;
}
bool Main::optImmediate() const
{
return
opt().contains("immediate") ||
opt().contains("as-proxy") ;
} }
bool Main::optDaemon() const bool Main::optDaemon() const
@ -283,7 +306,11 @@ G::Path Main::optSpoolDir() const
std::string Main::optServerAddress() const std::string Main::optServerAddress() const
{ {
const char * key = opt().contains("forward-to") ? "forward-to" : "as-client" ; const char * key = "forward-to" ;
if( opt().contains("as-client") )
key = "as-client" ;
else if( opt().contains("as-proxy") )
key = "as-proxy" ;
return opt().contains(key) ? opt().value(key) : std::string() ; return opt().contains(key) ? opt().value(key) : std::string() ;
} }
@ -301,10 +328,12 @@ std::string Main::checkOptions() const
"be an absolute path (starting with /)" ; "be an absolute path (starting with /)" ;
} }
if( !opt().contains("forward-to") && if( !opt().contains("forward-to") && (
(opt().contains("admin") || opt().contains("forward")) ) opt().contains("forward") ||
opt().contains("immediate") ||
opt().contains("admin") ) )
{ {
return "usage error: the --admin and --forward " return "usage error: the --forward, --immediate and --admin "
"switches require --forward-to" ; "switches require --forward-to" ;
} }
@ -332,14 +361,14 @@ void Main::closeFiles()
if( optDaemon() ) if( optDaemon() )
{ {
const bool keep_stderr = true ; const bool keep_stderr = true ;
G::Daemon::closeFiles( keep_stderr ) ; G::Process::closeFiles( keep_stderr ) ;
} }
} }
void Main::closeMoreFiles() void Main::closeMoreFiles()
{ {
if( optDaemon() && optCloseStderr() ) if( optDaemon() && optCloseStderr() )
G::Daemon::closeStderr() ; G::Process::closeStderr() ;
} }
bool Main::optDoServing() const bool Main::optDoServing() const
@ -363,17 +392,19 @@ void Main::runCore()
if( !error.empty() ) if( !error.empty() )
throw G::Exception( error ) ; throw G::Exception( error ) ;
G::Daemon::setUmask() ; G::Daemon::PidFile pid_file ;
G::Process::setUmask() ;
if( optDaemon() ) if( optDaemon() )
{ {
closeFiles() ; // before opening any sockets or message-store streams closeFiles() ; // before opening any sockets or message-store streams
if( opt().contains("pid-file") ) if( opt().contains("pid-file") )
G::Daemon::detach( G::Path(opt().value("pid-file")) ) ; pid_file = G::Daemon::PidFile( G::Path(opt().value("pid-file")) ) ;
else G::Daemon::detach( pid_file ) ;
G::Daemon::detach() ;
} }
GSmtp::MessageStore store( optSpoolDir() ) ; GSmtp::FileStore store( optSpoolDir() ) ;
if( opt().contains("filter") )
GSmtp::NewFile::setPreprocessor( G::Path(opt().value("filter")) ) ;
std::auto_ptr<GNet::EventSources> event_loop( GNet::EventSources::create() ) ; std::auto_ptr<GNet::EventSources> event_loop( GNet::EventSources::create() ) ;
if( ! event_loop->init() ) if( ! event_loop->init() )
@ -389,20 +420,19 @@ void Main::runCore()
if( optDoServing() ) if( optDoServing() )
{ {
GSmtp::Server server( optPort() , optAllowRemoteClients() , smtpIdent() ) ; GSmtp::Server server( optPort() , optAllowRemoteClients() , smtpIdent() ,
optImmediate() ? optServerAddress() : std::string() ) ;
std::auto_ptr<GSmtp::AdminServer> admin_server ;
if( optDoAdmin() ) if( optDoAdmin() )
{ {
GSmtp::AdminServer admin_server( optAdminPort() , admin_server <<= new GSmtp::AdminServer( optAdminPort() ,
optAllowRemoteClients() , optServerAddress() ) ; optAllowRemoteClients() , optServerAddress() ) ;
closeMoreFiles() ;
event_loop->run() ;
}
else
{
closeMoreFiles() ;
event_loop->run() ;
} }
pid_file.commit() ;
closeMoreFiles() ;
event_loop->run() ;
} }
} }

View File

@ -50,9 +50,13 @@ int main( int argc , char * argv [] )
struct sockaddr_in address ; struct sockaddr_in address ;
int fd , rc ; int fd , rc ;
/* parse the command line -- port number */
if( argc > 1 ) if( argc > 1 )
{
port = atoi(argv[1]) ; port = atoi(argv[1]) ;
}
/* parse the command line -- send string */
if( argc > 2 ) if( argc > 2 )
{ {
buffer[0] = '\0' ; buffer[0] = '\0' ;
@ -60,22 +64,38 @@ int main( int argc , char * argv [] )
} }
strcat( buffer , "\015\012" ) ; strcat( buffer , "\015\012" ) ;
/* open the socket */
fd = socket( AF_INET , SOCK_STREAM , 0 ) ; fd = socket( AF_INET , SOCK_STREAM , 0 ) ;
if( fd < 0 ) return EXIT_FAILURE ; if( fd < 0 )
return EXIT_FAILURE ;
/* prepare the address */
memset( &address , 0 , sizeof(address) ) ; memset( &address , 0 , sizeof(address) ) ;
address.sin_family = AF_INET ; address.sin_family = AF_INET ;
address.sin_port = htons( port ) ; address.sin_port = htons( port ) ;
address.sin_addr.s_addr = inet_addr( host ) ; address.sin_addr.s_addr = inet_addr( host ) ;
/* connect */
rc = connect( fd , (const struct sockaddr*)&address , sizeof(address) ) ; rc = connect( fd , (const struct sockaddr*)&address , sizeof(address) ) ;
if( rc < 0 ) return EXIT_FAILURE ; if( rc < 0 )
return EXIT_FAILURE ;
/* send the string */
rc = write( fd , buffer , strlen(buffer) ) ; rc = write( fd , buffer , strlen(buffer) ) ;
if( rc != strlen(buffer) ) return EXIT_FAILURE ; if( rc != strlen(buffer) )
return EXIT_FAILURE ;
/* read the reply */
rc = read( fd , buffer , sizeof(buffer)-1U) ; rc = read( fd , buffer , sizeof(buffer)-1U) ;
if( rc <= 0 ) return EXIT_FAILURE ; if( rc <= 0 )
return EXIT_FAILURE ;
/* print the reply */
write( STDOUT_FILENO , buffer , rc ) ; write( STDOUT_FILENO , buffer , rc ) ;
buffer[0U] = '\n' ; buffer[0U] = '\n' ;
buffer[1U] = '\0' ; buffer[1U] = '\0' ;
write( STDOUT_FILENO , buffer , strlen(buffer) ) ; write( STDOUT_FILENO , buffer , strlen(buffer) ) ;
return EXIT_SUCCESS ; return EXIT_SUCCESS ;
} }