This commit is contained in:
Graeme Walker 2001-12-18 12:00:00 +00:00
parent 1f92260cdf
commit 2911440f23
131 changed files with 7924 additions and 2321 deletions

View File

@ -1,13 +1,23 @@
Change Log
==========
E-MailRelay Change Log
======================
0.9.5 -> 0.9.6
--------------
* SMTP AUTHentication extension -- LOGIN mechanism only.
* Client-side protocol timeout.
* Client-side connection timeout.
* Preprocessor can cancel further message processing.
* Client's IP address recorded in envelope files.
* Multiple hard-coded listening addresses supported at compile-time.
* Fix for automatic reopening of stderr stream.
0.9.4 -> 0.9.5
--------------
* Windows fixes and improvements:
- system-tray + dialog-box user interface
- fix for dropped connections
- fix for content file deletion
- fix for directory iterator
Windows fixes and improvements...
* system-tray + dialog-box user interface
* fix for dropped connections
* fix for content file deletion
* fix for directory iterator
0.9.3 -> 0.9.4
--------------
@ -26,8 +36,7 @@ Change Log
0.9.1 -> 0.9.2
--------------
* Better autoconf detection. (Now builds on all SourceForge's
compile-farm environments, including FreeBSD and Solaris+gcc.)
* Better autoconf detection.
* Workround for FreeBSD uname() feature.
* Added missing .sh_ files to the distribution.
* Fixed a benign directory iterator bug.

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -165,7 +165,7 @@ maintainer-clean-recursive:
dot_seen=no; \
rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \
rev="$$subdir $$rev"; \
test "$$subdir" = "." && dot_seen=yes; \
test "$$subdir" != "." || dot_seen=yes; \
done; \
test "$$dot_seen" = "no" && rev=". $$rev"; \
target=`echo $@ | sed s/-recursive//`; \
@ -259,7 +259,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
@ -276,7 +276,6 @@ distdir: $(DISTFILES)
|| exit 1; \
fi; \
done
info-am:
info: info-recursive
dvi-am:

6
NEWS
View File

@ -1,6 +1,2 @@
to do
-----
* more-native windows port (system tray, registry etc)
* Mac OSX build
* setuid() security
no news

12
README
View File

@ -59,20 +59,16 @@ The following documentation is provided:
* doc/developer.txt -- developer guide
* ChangeLog -- change log for releases
And for completeness the following stub documents are also included:
* NEWS
* AUTHORS
Source code documentation can be generated by using doxygen (www.doxygen.org).
Configurations
--------------
The code was developed on SuSE Linux 7.1 using:
* linux 2.2.18,
* gcc 2.95.2,
* glibc 2.2.7 (libc.so.6),
* linux 2.4.10,
* gcc 2.95.3,
* glibc 2.2.4 (libc.so.6),
* gnu make 3.79.1,
* autoconf 2.13
* autoconf 2.52
and ported to Windows 98 using
* MSVC 6.0

12
aclocal.m4 vendored
View File

@ -1,6 +1,6 @@
dnl aclocal.m4 generated automatically by aclocal 1.4
dnl aclocal.m4 generated automatically by aclocal 1.4-p5
dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@ -19,7 +19,7 @@ dnl PARTICULAR PURPOSE.
dnl Usage:
dnl AM_INIT_AUTOMAKE(package,version, [no-define])
AC_DEFUN(AM_INIT_AUTOMAKE,
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_REQUIRE([AC_PROG_INSTALL])
PACKAGE=[$1]
AC_SUBST(PACKAGE)
@ -47,7 +47,7 @@ AC_REQUIRE([AC_PROG_MAKE_SET])])
# Check to make sure that the build environment is sane.
#
AC_DEFUN(AM_SANITY_CHECK,
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
# Just in case
sleep 1
@ -88,7 +88,7 @@ AC_MSG_RESULT(yes)])
dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY)
dnl The program must properly implement --version.
AC_DEFUN(AM_MISSING_PROG,
AC_DEFUN([AM_MISSING_PROG],
[AC_MSG_CHECKING(for working $2)
# Run test in a subshell; some versions of sh will print an error if
# an executable is not found, even if stderr is redirected.
@ -104,7 +104,7 @@ AC_SUBST($1)])
# Like AC_CONFIG_HEADER, but automatically create stamp file.
AC_DEFUN(AM_CONFIG_HEADER,
AC_DEFUN([AM_CONFIG_HEADER],
[AC_PREREQ([2.12])
AC_CONFIG_HEADER([$1])
dnl When config.status generates a header, we must update the stamp-h file.

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -150,7 +150,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \

View File

@ -27,7 +27,7 @@
awk="awk"
tmp="/tmp/`basename $0`.$$.tmp"
log="/tmp/`basename $0`.$$.out"
log="/tmp/`basename $0`.out"
trap "rm -f ${tmp} >/dev/null 2>&1 ; exit" 0 1 2 3 13 15
###
@ -56,10 +56,10 @@ ProcessContent()
function rot_s( n , string )
{
result = ""
rot_s_result = ""
for( i = 1 ; i <= length(string) ; i++ )
result = result rot_c(n,substr(string,i,1))
return result
rot_s_result = rot_s_result rot_c(n,substr(string,i,1))
return rot_s_result
}
{
@ -117,10 +117,10 @@ Wrap()
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
suppress = is_mime_content || (was_mime_content && is_continuation)
was_mime_content = suppress
if( ! block )
if( ! suppress )
print
}
else
@ -141,6 +141,11 @@ Main()
cp ${tmp} "${1}"
}
# Main $@ > ${log} 2>&1
debug="0"
if test "${debug}" -eq 1
then
Main $@ > ${log} 2>&1
else
Main $@
fi

View File

@ -23,33 +23,39 @@
#
# Soak tests the E-MailRelay system.
#
# (As a side-effect it does "killall emailrelay".)
#
# configuration
#
exe="../src/main/emailrelay"
exe="`dirname $0`/../src/main/emailrelay"
content="/etc/services"
pp="1001" # port prefix
null_filter="/bin/touch"
as_client="--no-syslog --no-daemon --dont-serve --forward --forward-to" # no --log
as_server="--log" # no --close-stderr
as_proxy="--log --immediate --forward-to" # no --close-stderr
content="/etc/passwd"
# configuration fallback
#
if test \! -f "${exe}"
bin_dir="`dirname $0`"
if test \! -f "${exe}" -a -x "${bin_dir}/../emailrelay"
then
exe="../emailrelay"
fi
if test \! -f "${null_filter}"
then
null_filter="/usr/bin/touch"
exe="${bin_dir}/../emailrelay"
echo `basename $0`: using executable \"${exe}\" >&2
fi
# initialisation
#
as_client="--no-syslog --no-daemon --dont-serve --forward --forward-to" # no --log
as_server="--log" # no --close-stderr
as_proxy="--log --immediate --forward-to" # no --close-stderr
base_dir="/tmp/`basename $0`.$$.tmp"
auth_file="${base_dir}/`basename $0 .sh`.auth"
mkdir "${base_dir}"
trap "rm -rf ${base_dir} 2>/dev/null ; killall emailrelay ; exit" 0 1 2 3 13 15
trap "rm -rf ${base_dir} 2>/dev/null ; killall emailrelay 2>/dev/null ; exit" 0 1 2 3 13 15
Auth()
{
echo "login server joe joe+00s+3Dpassword"
echo "login client joe joe+00s+3Dpassword"
}
Content()
{
@ -57,7 +63,7 @@ Content()
echo "Subject: test message from process" $$
echo "From: tester"
echo ""
for i in 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
for i in 1 1 1 1 1 1 1 1 1 1
do
cat "${content}"
done
@ -65,11 +71,13 @@ Content()
Envelope()
{
echo "X-MailRelay-Format: #2821.2"
echo "X-MailRelay-Format: #2821.3"
echo "X-MailRelay-Content: 8bit"
echo "X-MailRelay-From: me"
echo "X-MailRelay-ToCount: 1"
echo "X-MailRelay-To-Remote:" ${USER}@localhost
echo "X-MailRelay-Authentication: anon"
echo "X-MailRelay-Client: 127.0.0.1"
echo "X-MailRelay-End: 1"
}
@ -91,7 +99,7 @@ RunClient()
to_address="${1}"
spool_dir="${2}"
${exe} ${as_client} ${to_address} --spool-dir ${spool_dir}
${exe} ${as_client} ${to_address} --spool-dir ${spool_dir} --client-auth ${auth_file}
}
Send()
@ -120,6 +128,7 @@ RunServer()
mkdir -p "${spool_dir}"
${exe} ${as_server} --port ${port} --spool-dir ${spool_dir} \
--pid-file ${pid_file} \
--server-auth ${auth_file} \
--admin `expr ${port} + 100` --forward-to localhost:smtp --no-syslog 2> "${log}"
}
@ -139,23 +148,55 @@ RunProxy()
mkdir -p "${spool_dir}"
${exe} ${as_proxy} ${to_address} --port ${port} --spool-dir ${spool_dir} \
--pid-file ${pid_file} \
--server-auth ${auth_file} \
--client-auth ${auth_file} \
--admin `expr ${port} + 100` --no-syslog 2> "${log}"
}
killall emailrelay
Init()
{
killall emailrelay 2>/dev/null
Auth > ${auth_file}
}
RunServers()
{
RunServer ${pp}3 ${base_dir}/spool-3 server-2.out server-2.pid
RunProxy ${pp}1 localhost:${pp}3 ${base_dir}/spool-1 proxy.out proxy.pid
RunServer ${pp}2 ${base_dir}/spool-2 server-1.out server-1.pid
sleep 1 # to allow pid files time to be written
echo ps -l -p `cat server-2.pid` -p `cat server-1.pid` -p `cat proxy.pid`
ps -l -p `cat server-2.pid` -p `cat server-1.pid` -p `cat proxy.pid`
}
CheckServers()
{
if test \! -f server-1.pid -o \! -f server-2.pid -o \! -f proxy.pid
then
echo `basename $0`: error starting 'server(s)' >&2
cat server-?.out proxy.out 2>/dev/null | sed 's/^/ /' >&2
exit 1
fi
}
Ps()
{
echo `basename $0`: output from \"ps -l -p `cat server-2.pid` -p `cat server-1.pid` -p `cat proxy.pid`\"...
ps -l -p `cat server-2.pid` -p `cat server-1.pid` -p `cat proxy.pid` | sed 's/^/ /'
}
Main()
{
while true
do
Send localhost:${pp}1
Send localhost:${pp}2
echo -n .
rm -f ${base_dir}/spool-?/*content
done
}
Init
RunServers
CheckServers
Ps
Main

View File

@ -36,9 +36,12 @@
# configuration
#
exe="../src/main/emailrelay"
poke="../src/main/emailrelay-poke"
exe_dir="../src/main"
if test "${1}" != "" ; then exe_dir="${1}" ; fi
exe="${exe_dir}/emailrelay"
poke="${exe_dir}/emailrelay-poke"
null_filter="/bin/touch"
content_file="/etc/services"
pp="1001" # port-prefix
# configuration fallback
@ -51,6 +54,7 @@ fi
# initialisation
#
base_dir="/tmp/`basename $0`.$$.tmp"
summary_log="/tmp/`basename $0`.out"
exit_code="1"
Cleanup()
@ -58,10 +62,11 @@ Cleanup()
kill `cat ${base_dir}/pid-* 2>/dev/null` 2>/dev/null
if test -d ${base_dir}
then
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
grep "MailRelay-Reason" ${base_dir}/*/*envelope*bad > "${summary_log}" 2>/dev/null
grep "." ${base_dir}/log-? >> "${summary_log}" 2>/dev/null
ls -lR ${base_dir} >> "${summary_log}" 2>/dev/null
diff -w ${base_dir}/store-4/*content "${content_file}" >> "${summary_log}" 2>/dev/null
fi
rm -rf ${base_dir} 2>/dev/null
}
Trap()
@ -109,17 +114,19 @@ Content()
echo "Subject: test message 1"
echo "From: sender"
echo " "
echo "Content"
cat "${content_file}"
}
Envelope()
{
echo "X-MailRelay-Format: #2821.2"
echo "X-MailRelay-Format: #2821.3"
echo "X-MailRelay-Content: 8bit"
echo "X-MailRelay-From: sender"
echo "X-MailRelay-ToCount: 2"
echo "X-MailRelay-To-Remote: recipient-1@localhost"
echo "X-MailRelay-To-Remote: recipient-2@localhost"
echo "X-MailRelay-Authentication: "
echo "X-MailRelay-Client: 127.0.0.1"
echo "X-MailRelay-End: 1"
}
@ -135,13 +142,13 @@ CheckResults()
exit_code="0"
echo `basename $0`: succeeded
else
echo `basename $0`: failed: see /tmp/`basename $0`.out >&2
echo `basename $0`: failed: see ${summary_log} >&2
fi
}
StartTimer()
{
( sleep 5 ; Cleanup ) &
( sleep 30 ; Cleanup ) &
}
CreateMessages()
@ -151,14 +158,33 @@ CreateMessages()
Envelope | CrLf > ${base_dir}/store-1/emailrelay.0.1.envelope
}
CreateAuth()
{
mkdir -p "${base_dir}"
file="${base_dir}/server.auth"
echo "# server.auth" > ${file}
echo "login server fred freds_password" >> ${file}
echo "login server joe joe+00s_password" >> ${file}
echo "login client dummy pwd" >> ${file}
echo "cram-md5 client dummy digest" >> ${file}
file="${base_dir}/client.auth"
echo "# client.auth" > ${file}
echo "cram-md5 client foo bar" >> ${file}
echo "login client joe joe+00s_password" >> ${file}
echo "login server abc def" >> ${file}
}
trap "Trap ; exit" 1 2 3 13 15
trap "Trap 0 ; exit" 0
StartTimer
CreateAuth
RunServer ${pp}1 store-2 log-1 pid-1
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 "--immediate --forward-to localhost:${pp}4 --filter ${null_filter}"
RunServer ${pp}4 store-4 log-4 pid-4
RunServer ${pp}3 store-3 log-3 pid-3 "--immediate --forward-to localhost:${pp}4 --filter ${null_filter} --client-auth ${base_dir}/client.auth"
RunServer ${pp}4 store-4 log-4 pid-4 "--server-auth ${base_dir}/server.auth"
CreateMessages
RunClient localhost:${pp}1 store-1 log-c pid-5
RunPoke ${pp}9 log-p

View File

@ -131,9 +131,13 @@ function dequote( line )
{
quote = "\""
not_quote = "[^" quote "]"
gsub( quote not_quote "*" quote , "<b><em>&</em></b>" , line )
gsub( "<em>" quote , "<em>" , line )
gsub( quote "</em>" , "</em>" , line )
#start_tag="<kbd><b>"
#end_tag="</b></kbd>"
start_tag="<b>"
end_tag="</b>"
gsub( quote not_quote "*" quote , start_tag "&" end_tag , line )
gsub( start_tag quote , start_tag , line )
gsub( quote end_tag , end_tag , line )
return line
}
@ -153,6 +157,11 @@ function tagOutput( line , tag )
printf( "<%s>%s</%s>\n" , tag , fn(dequote(escape(line))) , tag )
}
function tagOutputRaw( line , tag )
{
printf( "<%s>%s</%s>\n" , tag , escape(line) , tag )
}
function process( line , next_ )
{
tab = " "
@ -176,7 +185,7 @@ function process( line , next_ )
}
else if( is_code )
{
tagOutput( line , "pre" )
tagOutputRaw( line , "pre" )
}
else if( is_definition_term )
{
@ -211,13 +220,13 @@ function process( line , next_ )
{
major += 1
minor = 0
printf( "<h1><a name=\"H_%d\">%s</h1>" , major , line )
printf( "<h1><a name=\"H_%d\">%s</a></h1>" , major , line )
printf( "<!-- index:1:H:%d::%s -->\n" , major , line )
}
else if( is_sub_heading )
{
minor += 1
printf( "<h2><a name=\"SH_%d_%d\">%s</h2>" , major , minor , line )
printf( "<h2><a name=\"SH_%d_%d\">%s</a></h2>" , major , minor , line )
printf( "<!-- index:2:SH:%d:%d:%s -->\n" , major , minor , line )
}
else if( !is_heading_line && !is_sub_heading_line )
@ -245,7 +254,13 @@ END {
# ===
# AugmentLists()
#
# Adds list begin/end tags around a set of list items.
# Adds list begin/end tags around a set of list items
# eg. <ul> and </ul> tags either side of a set of
# contiguous <li> lines.
#
# The 'ignore' parameters can be used to make sure that
# list-item lines separated with 'ignore' patterns are
# treated as being contiguous.
#
AugmentLists()
{
@ -310,15 +325,17 @@ ${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.
# Adds additional stuff after a given opening tag
# and optionally before a closing tag. The opening
# tag is expected to be at the start of the line.
#
Decorate()
{
${awk} -v tag="${1}" -v decoration="${2}" '
${awk} -v tag="${1}" -v first="${2}" -v second="${3}" '
{
line = $0
sub( "^<" tag ">" , "<" tag ">" decoration , line )
sub( "^<" tag ">" , "<" tag ">" first , line )
sub( "</" tag ">" , second "</" tag ">" , line )
print line
} '
}
@ -371,21 +388,33 @@ END {
# ===
# Anchorise_1()
#
# Converts [[-foo-bar-]] to <a href="foo">bar</a>.
# Converts "*foo* [bar]" to <a href="bar">foo</a>.
#
Anchorise_1()
{
sed 's/\[\[-\([^-]*\)-\([^-]*\)-\]\]/<a href="\1">\2<\/a>/g'
sed 's/\*\([^[:space:]]*\)\* \[\([^[:space:]]*\)\]/<a href="\2">\1<\/a>/g'
}
# ===
# Anchorise_2()
#
# Converts [[foo]] to <a href="../foo">foo</a>.
# Converts [[-foo-bar-]] to <a href="foo">bar</a>.
# Deprecated.
#
Anchorise_2()
{
sed 's/\[\[\([^\]*\)\]\]/<a href=..\/"\1">\1<\/a>/g'
sed 's/\[\[-\([^-]*\)-\([^-]*\)-\]\]/<a href="\1" type="deprecated">\2<\/a>/g'
}
# ===
# Anchorise_3()
#
# Converts [[foo]] to <a href="../foo">foo</a>.
# Deprecated.
#
Anchorise_3()
{
sed 's/\[\[\([^\]*\)\]\]/<a href=..\/"\1" type="deprecated">\1<\/a>/g'
}
# ===
@ -444,8 +473,7 @@ Cat "${file}" | \
Elide "dd" | \
Elide "pre" | \
Decorate dt "<img src=\"graphics/bullet.gif\">\\\&nbsp;" | \
Decorate dd "<p>" | \
Decorate dd "<p>" "<p>" | \
Anchorise_1 | \
Anchorise_2 | \
MoveIndex

View File

@ -1,24 +1,27 @@
/* config.h.in. Generated automatically from configure.in by autoheader. */
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define if you have the glob function. */
#undef HAVE_GLOB
/* Define if you have the <dirent.h> header file. */
/* Define if you have the <dirent.h> header file, and it defines `DIR'. */
#undef HAVE_DIRENT_H
/* Define if you have the <ndir.h> header file. */
/* Define if you have the `glob' function. */
#undef HAVE_GLOB
/* have reentrant gmtime */
#undef HAVE_GMTIME_R
/* have reentrant localtime */
#undef HAVE_LOCALTIME_R
/* Define if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
/* Define if you have the <sys/dir.h> header file. */
/* auto_ptr assignment has non-const rhs */
#undef HAVE_NONCONST_AUTOPTR
/* Define if you have the <sys/dir.h> header file, and it defines `DIR'. */
#undef HAVE_SYS_DIR_H
/* Define if you have the <sys/ndir.h> header file. */
/* Define if you have the <sys/ndir.h> header file, and it defines `DIR'. */
#undef HAVE_SYS_NDIR_H
/* Define if you have the <sys/time.h> header file. */
@ -30,15 +33,11 @@
/* Name of package */
#undef PACKAGE
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Version number of package */
#undef VERSION
/* auto_ptr assignment has non-const rhs */
#undef HAVE_NONCONST_AUTOPTR
/* have reentrant localtime */
#undef HAVE_LOCALTIME_R
/* have reentrant gmtime */
#undef HAVE_GMTIME_R

4632
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@ dnl ===
dnl Process this file with autoconf to produce a configure script.
AC_INIT(src/main/gsmtp.h)
AM_INIT_AUTOMAKE(emailrelay,0.9.5)
AM_INIT_AUTOMAKE(emailrelay,0.9.6)
AM_CONFIG_HEADER(config.h)
dnl ===

View File

@ -1,7 +1,7 @@
EXTRA_DIST = developer.txt reference.txt userguide.txt index.html emailrelay.1 emailrelay-poke.1 doxygen_header.html graphics/bullet.gif
EXTRA_DIST = developer.txt reference.txt userguide.txt windows.txt index.html emailrelay.1 emailrelay-poke.1 doxygen_header.html graphics/bullet.gif
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 windows.html changelog.html
CLEANFILES = $(noinst_SCRIPTS) html *.ht readme.html developer.html reference.html userguide.html man.html
SUFFIXES = .txt .html .ht
@ -36,6 +36,9 @@ developer.html reference.html userguide.html: $(converter)
readme.html: $(top_srcdir)/README $(converter)
$(converter) -a "$(AWK)" $(top_srcdir)/README > readme.html
changelog.html: $(top_srcdir)/ChangeLog $(converter)
$(converter) -a "$(AWK)" $(top_srcdir)/ChangeLog > changelog.html
install-data-local:
$(mkinstalldirs) $(destdir)$(mandir)/man1
$(INSTALL) $(top_srcdir)/doc/emailrelay.1 $(destdir)$(mandir)/man1/emailrelay.1

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -69,10 +69,10 @@ PACKAGE = @PACKAGE@
RANLIB = @RANLIB@
VERSION = @VERSION@
EXTRA_DIST = developer.txt reference.txt userguide.txt index.html emailrelay.1 emailrelay-poke.1 doxygen_header.html graphics/bullet.gif
EXTRA_DIST = developer.txt reference.txt userguide.txt windows.txt index.html emailrelay.1 emailrelay-poke.1 doxygen_header.html graphics/bullet.gif
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 windows.html changelog.html
CLEANFILES = $(noinst_SCRIPTS) html *.ht readme.html developer.html reference.html userguide.html man.html
SUFFIXES = .txt .html .ht
@ -141,7 +141,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
@ -237,6 +237,9 @@ developer.html reference.html userguide.html: $(converter)
readme.html: $(top_srcdir)/README $(converter)
$(converter) -a "$(AWK)" $(top_srcdir)/README > readme.html
changelog.html: $(top_srcdir)/ChangeLog $(converter)
$(converter) -a "$(AWK)" $(top_srcdir)/ChangeLog > changelog.html
install-data-local:
$(mkinstalldirs) $(destdir)$(mandir)/man1
$(INSTALL) $(top_srcdir)/doc/emailrelay.1 $(destdir)$(mandir)/man1/emailrelay.1

View File

@ -21,8 +21,8 @@ to create GUI windows in order to process network events. The extra GUI and
event classes are put into a separate library in the "src/win32" directory,
using the namespace "GGui".
Class structure overview
------------------------
Class structure
---------------
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
@ -38,6 +38,9 @@ another for immediate forwarding ("ProtocolMessageForward").
The protocol and message-store functionality are brought together by the
high-level "GSmtp::Server" and "GSmtp::Client" classes.
Simplified class diagrams for the *GNet* [graphics/gnet-classes.png] and
*GSmtp* [graphics/gsmtp-classes.png] namespaces are available.
Directory structure
-------------------
@ -142,12 +145,28 @@ If porting to a good ANSI C++ compiler then start by removing files from the
review the following header files: "src/glib/gdef.h", "src/gnet/gnet.h",
"src/glib/gmemory.h".
IPv6
----
Compile-time features
---------------------
The following features are available to source-code hackers:
# IPv6
IPv6 is supported at compile-time by selecting source files in the "src/gnet"
directory ending "_ipv6.cpp" rather than "_ipv4.cpp". The code has been tested
to a limited extent on Linux.
# Verbose logging
Verbose logging can be enabled by defining the pre-processor symbol "_DEBUG".
(See "src/glib/glog.h".)
# Multiple listening ports
Refer to the Server constructor in "src/main/gsmtpserver.cpp". Set "normal"
to false and edit the hard-coded address strings. If the addresses
need different port numbers then pass them to bind() in the third
parameter.
Windows build
-------------
A simple project file "emailrelay.dsp" for msvc6.0 is provided in the "src/main"
@ -228,6 +247,11 @@ Other patterns:
- GSmtp::ServerProtocol
Idioms
------
The "<<=" operator defined in "src/glib/gmemory.h" is used idiomatically
to reassign a std::auto_ptr<>.
Copyright (C) 2001 Graeme Walker <graeme_walker@users.sourceforge.net>. All rights reserved.

View File

@ -29,6 +29,10 @@ emailrelay \- e-mail transfer agent
.B emailrelay
--as-client
.I server-address
.LP
.B emailrelay
--as-proxy
.I server-address
.SH DESCRIPTION
.I emailrelay
is an simple e-mail message transfer agent. It is intended to be used
@ -50,90 +54,77 @@ In this mode all messages are forwarded immediately to the downstream
server.
.SH OPTIONS
.TP
.B \-V,--version
Displays version information and exits.
.TP
.B \-a,--admin \fIadmin-port\fR
Enables the administration interface and specifies its listening port number.
.TP
.B \-q,--as-client \fIhost:port\fR
Equivalent to \fI--log\fR \fI--no-syslog\fR \fI--no-daemon\fR \fI--dont-serve\fR \fI--forward\fR \fI--forward-to\fR.
.TP
.B \-y,--as-proxy \fIhost:port\fR
Equivalent to \fI--log\fR \fI--close-stderr\fR \fI--immediate\fR \fI--forward-to\fR.
.TP
.B \-d,--as-server
Equivalent to \fI--close-stderr\fR \fI--log\fR.
Equivalent to \fI--log\fR \fI--close-stderr\fR.
.TP
.B \-C,--client-auth \fIfile\fR
Enables authentication with remote server, using the given secrets file.
.TP
.B \-e,--close-stderr
Closes the standard error stream when daemonising.
Closes the standard error stream after start-up.
.TP
.B \-f,--forward
Forwards stored mail on startup (requires \fI--forward-to\fR).
.TP
.B \-h,--help
Displays help text and exits.
.TP
.B \-i,--pid-file \fIpid-file\fR
Records the daemon process-id in the given file.
.TP
.B \-l,--log
Writes log information on standard error (if open) and syslog (if not disabled).
.TP
.B \-m,--immediate
Forwards each message as soon as it is received (requires \fI--forward-to\fR).
.TP
.B \-n,--no-syslog
Disables syslog output.
.TP
.B \-o,--forward-to \fIhost:port\fR
Specifies the remote smtp server (required by \fI--forward\fR and \fI--admin\fR).
.TP
.B \-p,--port \fIport\fR
Specifies the smtp listening port number.
.TP
.B \-q,--as-client \fIhost:port\fR
Equivalent to \fI--no-syslog\fR \fI--no-daemon\fR \fI--log\fR \fI--dont-serve\fR \fI--forward\fR \fI--forward-to\fR.
.TP
.B \-r,--remote-clients
Allows remote clients to connect.
.TP
.B \-s,--spool-dir \fIdir\fR
Specifies the spool directory (default is \fI/usr/local/var/spool/emailrelay\fR).
.TP
.B \-t,--no-daemon
Does not detach from the terminal.
.TP
.B \-v,--verbose
Generates more verbose logging (if compiled-in and logging enabled and stderr open).
.B \-U,--connection-timeout \fItime\fR
Sets the client-side connection timeout in seconds (default is 40).
.TP
.B \-x,--dont-serve
Stops the process acting as a server (usually used with \fI--forward\fR).
.TP
.B \-y,--as-proxy \fIhost:port\fR
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"
If the
.IR --log ,
.I --as-client
or
.I --as-server
switches are in force then warning and error messages
are sent to
.I syslog
(using the
.BR LOG_MAIL
facility) and to
.IR stderr .
Output to
.I syslog
can be disabled with
.IR --no-syslog ,
and output to stderr can be disabled in daemon mode with
.IR --close-stderr .
.PP
Failed e-mail messages are kept in the spool directory and given
a
.I .bad
filename suffix. The failure reason is usually recorded within the
envelope file itself.
Defines a mail pre-processor (disallowed if running as root).
.TP
.B \-f,--forward
Forwards stored mail on startup (requires \fI--forward-to\fR).
.TP
.B \-o,--forward-to \fIhost:port\fR
Specifies the remote smtp server (required by \fI--forward\fR and \fI--admin\fR).
.TP
.B \-h,--help
Displays help text and exits.
.TP
.B \-m,--immediate
Forwards each message as soon as it is received (requires \fI--forward-to\fR).
.TP
.B \-l,--log
Writes log information on standard error (if open) and syslog (if not disabled).
.TP
.B \-t,--no-daemon
Does not detach from the terminal.
.TP
.B \-n,--no-syslog
Disables syslog output.
.TP
.B \-i,--pid-file \fIpid-file\fR
Records the daemon process-id in the given file.
.TP
.B \-p,--port \fIport\fR
Specifies the smtp listening port number.
.TP
.B \-r,--remote-clients
Allows remote clients to connect.
.TP
.B \-T,--response-timeout \fItime\fR
Sets the client-side response timeout in seconds (default is 1800).
.TP
.B \-S,--server-auth \fIfile\fR
Enables authentication of remote clients, using the given secrets file.
.TP
.B \-s,--spool-dir \fIdir\fR
Specifies the spool directory (default is \fI/usr/local/var/spool/emailrelay\fR).
.TP
.B \-v,--verbose
Generates more verbose logging (if compiled-in and logging enabled and stderr open).
.TP
.B \-V,--version
Displays version information and exits.
.SH FILES
.IP \(bu 2
/usr/local/sbin/emailrelay

Binary file not shown.

Before

Width:  |  Height:  |  Size: 573 B

After

Width:  |  Height:  |  Size: 487 B

View File

@ -1,16 +1,19 @@
<html>
<head>
<title>E-MailRelay index</title>
</head>
<body>
<h1>E-MailRelay Documentation</h1>
<bl>
<ul>
<li><a href="readme.html">Readme</a></li>
<li><a href="changelog.html">Change log</a></li>
<li><a href="userguide.html">User guide</a></li>
<li><a href="reference.html">Reference manual</a></li>
<li><a href="windows.html">Windows installation guide</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>, if available)</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>
</ul>
</body>
</html>

View File

@ -14,66 +14,78 @@ The "emailrelay" program supports the following command-line usage:
where <switch> is:
# --version (-V)
Displays version information and exits.
# --admin (-a)
Enables the administration interface and specifies its listening port number.
# --as-client (-q)
Equivalent to "--log --no-syslog --no-daemon --dont-serve --forward --forward-to".
# --as-proxy (-y)
Equivalent to "--log --close-stderr --immediate --forward-to".
# --as-server (-d)
Equivalent to "--close-stderr --log".
Equivalent to "--log --close-stderr".
# --client-auth (-C)
Enables authentication with remote server, using the given secrets file.
# --close-stderr (-e)
Closes the standard error stream when daemonising.
Closes the standard error stream after start-up.
# --forward (-f)
Forwards stored mail on startup (requires --forward-to).
# --help (-h)
Displays help text and exits.
# --pid-file (-i)
Records the daemon process-id in the given file.
# --log (-l)
Writes log information on standard error (if open) and syslog (if not disabled).
# --immediate (-m)
Forwards each message as soon as it is received (requires --forward-to).
# --no-syslog (-n)
Disables syslog output.
# --forward-to (-o)
Specifies the remote smtp server (required by --forward and --admin).
# --port (-p)
Specifies the smtp listening port number.
# --as-client (-q)
Equivalent to "--no-syslog --no-daemon --log --dont-serve --forward --forward-to".
# --remote-clients (-r)
Allows remote clients to connect.
# --spool-dir (-s)
Specifies the spool directory (default is "/usr/local/var/spool/emailrelay").
# --no-daemon (-t)
Does not detach from the terminal.
# --verbose (-v)
Generates more verbose logging (if compiled-in and logging enabled and stderr open).
# --connection-timeout (-U)
Sets the client-side connection timeout in seconds (default is 40).
# --dont-serve (-x)
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).
# --forward (-f)
Forwards stored mail on startup (requires --forward-to).
# --forward-to (-o)
Specifies the remote smtp server (required by --forward and --admin).
# --help (-h)
Displays help text and exits.
# --immediate (-m)
Forwards each message as soon as it is received (requires --forward-to).
# --log (-l)
Writes log information on standard error (if open) and syslog (if not disabled).
# --no-daemon (-t)
Does not detach from the terminal.
# --no-syslog (-n)
Disables syslog output.
# --pid-file (-i)
Records the daemon process-id in the given file.
# --port (-p)
Specifies the smtp listening port number.
# --remote-clients (-r)
Allows remote clients to connect.
# --response-timeout (-T)
Sets the client-side response timeout in seconds (default is 1800).
# --server-auth (-S)
Enables authentication of remote clients, using the given secrets file.
# --spool-dir (-s)
Specifies the spool directory (default is "/usr/local/var/spool/emailrelay").
# --verbose (-v)
Generates more verbose logging (if compiled-in and logging enabled and stderr open).
# --version (-V)
Displays version information and exits.
If no command-line switches are supplied at all then the default
behaviour is:
* to run as a daemon, detached from the terminal
@ -127,6 +139,11 @@ and the failure reason is written into the file.
SMTP issues
-----------
# Authentication:
The AUTH extension is supported in V0.9.6, but only with the unofficial LOGIN
mechanism.
# Local delivery:
E-MailRelay will reject all local recipients, with the exception of
@ -149,9 +166,9 @@ SMTP issues
# Timeouts:
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
will have the desired effect of aborting the message submission.
A simple client-side timeout is implemented which will abort the transaction
if the server fails to respond to any of the client's SMTP commands within the
given time period.
# Message loops:
@ -200,17 +217,52 @@ forwards the mail on to the system's default MTA (on port 25):
--filter ${HOME}/.emailrelay/filter \
--spool-dir ${HOME}/.emailrelay/spool
The pre-processor program should terminate with an exit code of zero to indicate
success. An exit code of 100 can be used to cancel all further processing of the
message, and any other non-zero exit code is used to indicate an error.
For example, the following pre-processor shell script examines the client's
IP address and conditionally dumps the message into "sendmail" (using the
sendmail command-line interface rather than SMTP):
#!/bin/sh
#
content="${1}"
envelope="`echo \"${content}\" | sed 's/content/envelope/'`"
ip="`awk '/MailRelay-Client:/ {print $2;exit}' \"${envelope}\"`"
if test "${ip}" = "192.168.0.2"
then
cat "${content}" | /usr/sbin/sendmail -t
rm -f "${envelope}" "${content}"
exit 100 # <= cancel further processing by emailrelay
fi
exit 0
An example pre-processor script which does simple rot-13 masking of messages is
also provided in the distribution ("share/emailrelay/emailrelay-process.sh").
This could be used as a template for a more sophisticated message encryption
system.
Security issues
---------------
A major security concern is the use of an external mail pre-processor (using the
--filter switch). In this release this feature is simply disabled if the process
is running as root (effective userid is zero). The pre-processor will run as the
same userid as the E-MailRelay program, but with an almost empty set of
environment variables, and no open file descriptors other than
"--filter" switch). In this release this feature is simply disabled if the
process is running as root (effective userid is zero). The pre-processor will
run as the 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.
Security issues which relate to the SMTP protocol itself are beyond the scope of
this document, but RFC2821 makes the following observation: "SMTP mail is
inherently insecure in that it is feasible for even [..] casual users to [..]
create messages that will trick a [..] recipient into believing that they came
from somewhere else. [..] Real [..] security lies [..] in end-to-end methods [..]
such as those which use digital signatures."
The "Authentication" section below also relates to security.
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.
@ -218,28 +270,85 @@ Some other points are:
* 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.
Authentication
--------------
E-MailRelay has some support for the ESMTP "AUTH" extension, as defined in
RFC2554, on both the server-side and client-side. The only authentication
mechanism currently provided is the non-standard (but widely used) "LOGIN"
mechanism, using plaintext passwords. A future release may add "CRAM-MD5"
(RFC2195).
Authentication is enabled with the "--auth-client" and "--auth-server"
command-line switches. The switch parameter is the name of a "secrets" file,
containing (in the current release) plaintext passwords.
The secrets file has a line-based format: blank lines are ignored and the hash
character (#) is used for comments. Lines have four white-space delimited
fields: "mechanism", "client-or-server", "userid", and "secret". The "mechanism"
field must be "login" (case-insensitive); the "client-or-server" field must be
"client" or "server"; the "userid" field is xtext-encoded user identifier; and
the "secret" field is the xtext-encoded plaintext password. (The "xtext"
encoding scheme is defined in RFC1891.) A client-side secrets file should
contain at least one "login client" entry, and a server-side secrets file should
contains zero or more "login server" entries. The same secrets file may be
specified for both "--auth-client" and "--auth-server" switches.
For example, the following secrets file defines "jsmith" as the username to be
used when E-MailRelay authenticates with a downstream server, and defines two
usernames ("user1" and "user2") which can be used by clients when they
authenticate with the E-MailRelay server:
#
# emailrelay secrets file
#
login client jsmith my+20password
login server user1 secret
login server user2 ignorance+3Ddeath
Clearly storing plaintext passwords in a file and then sending them unencypted
over a network is a bad thing. You should at least make sure that the secrets
file has tight permissions, and that the passwords in it are not also used for
anything important (such as root access).
On the server side authentication is advertised in the response to the SMTP
"EHLO" command, but authentication by the client is optional. If the client does
authenticate then the authenticated user-id is stored with the message and
then passed on to a downstream server using an "AUTH=userid" parameter on the
SMTP "MAIL FROM" command. If the client chooses not to authenticate then the
submitted messages will be forwarded using "AUTH=<>" on the "MAIL FROM"
command. Note that any "AUTH=userid" information on incoming submitted messages
is ignored and discarded: it is the authorised userid from the AUTH command
which is propogated, not the userid from the incoming "MAIL FROM" command's
"AUTH=" parameter.
On the client side authentication is performed when the client has connected to
a server which supports the AUTH extension with the LOGIN mechanism. If client
authentication is enabled (with the "--auth-client" switch) but the server does not
support the AUTH extension, or does not support the LOGIN mechanism, then the
client will fail the first message and terminate with an error message.
Note that some ISPs require separate POP/IMAP authentication before SMTP access
from a particular IP address is allowed. This type of POP-before-SMTP
authentication can be done outside the E-MailRelay system by POP/IMAP utilities
such as "fetchmail".
Files
-----
By default "make install" installs files in the following locations:
* /usr/local/sbin/emailrelay
* /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/*.html
* /usr/local/share/emailrelay/graphics/bullet.gif
* /usr/local/man/man1/emailrelay.1
* /usr/local/man/man1/emailrelay-poke.1
This directory structure is constrained by the "autoconf" and GNU standards.
Preferred locations for a GNU/Linux distribution would be something like this:
* /usr/sbin/emailrelay
* /opt/emailrelay/bin/emailrelay-poke
* /sbin/init.d/emailrelay.sh
* /var/spool/emailrelay/
* /opt/emailrelay/examples/emailrelay-notify.sh
* /opt/emailrelay/examples/emailrelay-deliver.sh
* /opt/emailrelay/examples/emailrelay-process.sh
* /usr/share/doc/packages/emailrelay/*.html
This directory structure is constrained by the GNU/"autoconf" conventions rather
than the Filesystem Hierarchy Standard.

View File

@ -184,6 +184,89 @@ directory may be sufficient:
EOF
Troubleshooting
---------------
A useful technique for troubleshooting SMTP problems is to telnet into the
remote server and drive the SMTP protocol manually. Telnet can be told to
connect to the remote SMTP port by putting the port number (25) on the command
line after the remote hostname, for example: "telnet smtp.myisp.net 25".
Once connected you should get a startup banner from the server, which will
probably tell you what server software you have connected to. From there you
should type something like "EHLO myhost.mydomain". The response to the EHLO
command should contain a list of SMTP extensions which the server software
supports. If this includes the AUTH extension then the set of supported
authentication mechanisms (such as LOGIN, CRAM-MD5 etc.) will be listed on the
same line.
After the EHLO response you should type "MAIL FROM:<myhost.mydomain>", retaining
the angle brackets but substituing your own address. If this is accepted then
enter a "RCPT TO:<me@myhost.mydomain>" command to say where the e-mail is going.
(Again, retain the angle brackets but substitute an appropriate address.)
After one or more "RCPT TO" commands you should enter the "DATA" command,
followed by the message content. The message content should include an RFC822
header, followed by a blank line, followed by the message text. For testing
purposes you might get away without having any header/body structure at all, but
to do things properly you should have at least a "To:" line, a "From:" line and
a "Subject:" line in the header.
At the end of the message text type a "." on a line of its own. At that point the
message should get dispatched, and end up in your in-box in the usual way
(assuming you put your own address in the "RCPT TO" command).
The following is an example SMTP dialogue, with ">>" and "<<" marks added to
show what was typed and what was received:
>> telnet smtp.myisp.net 25
<< Trying 12.34.56.78...
<< Connected to smtp.myisp.net.
<< Escape character is '^]'.
<< 220 mail12.myisp.net ESMTP Exim 3.13 #0 Sat, 17 Nov 2001 16:22:39 +0000
>> EHLO myhost.myisp.net
<< 250-mail12.myisp.net Hello modem-185.myisp.net [12.34.56.78]
<< 250-SIZE 104857600
<< 250-PIPELINING
<< 250 HELP
>> MAIL FROM:<me@myhost.myisp.net>
<< 250 <me@myhost.myisp.net> is syntactically correct
>> RCPT TO:<me@myhost.myisp.net>
<< 250 <me@myhost.myisp.net> verified
>> DATA
<< 354 Enter message, ending with "." on a line by itself
>> To: me@myhost.myisp.net
>> From: me@myhost.myisp.net
>> Subject: test
>>
>> Test message.
>> .
<< 250 OK id=1658Fp-0000Il-00
>> QUIT
<< 221 mail12.myisp.net closing connection
<< Connection closed by foreign host.
If you get some sort of "access denied" errors when talking to a server which
does not support the AUTH extension, then your ISP may be using POP-before-SMTP
authentication. In this scheme you are required to conduct an authenticated POP
or IMAP dialogue before you try to use SMTP. The POP/IMAP dialogue is done
separately from the SMTP connection, but bear in mind that there might be a time
limit so that your SMTP connection has to be made soon after the POP/IMAP
authentication. You should be able to use an e-mail front-end program, or
something like "fetchmail" to do the POP/IMAP authentication.
If you can send mail messages sucessfully using telnet, then you should look at
the E-MailRelay log output and compare what you do interactively with what
the program does. Usually when running as a server E-MailRelay logging goes to
the "syslog" system, and when running as a client it goes to the standard error
stream ("stderr"). To get the server to log onto stderr, replace the
"--as-server" command-line switch with "--log --no-syslog". Refer to the
reference guide for more information.
On Windows things are a bit more difficult because there is no syslog equivalent
on Win9x, and the standard error stream often gets lost. Starting E-MailRelay
from cygwin/bash on Win98 keeps stderr open (albeit with dreadful performance),
whereas the standard command prompt does not. If necessary the environment
variable "GLOGOUTPUT_FILE" can be defined as the name of a log file.
Glossary
--------

99
doc/windows.txt Normal file
View File

@ -0,0 +1,99 @@
E-MailRelay for Windows
=======================
Introduction
------------
E-MailRelay was originally developed on Linux and so most of the documentation
relates to Unix-like operating systems rather than Microsoft Windows. This
document provides some help on Windows installation and setup.
Quick start
------------
In summary, the Windows installation process is as follows:
* Unpack the zip file to "Program Files\emailrelay".
* Create a shortcut to the E-MailRelay executable in "Start->Programs->StartUp".
* Add "storage-daemon" configuration options (if any) to the "StartUp" shortcut's command line.
* Create a shortcut to the executable on the taskbar and/or desktop.
* Add "forwarding-client" configuration options to the taskbar/desktop shortcut's command line. These configuration options will normally include something like "--as-client smtp.myisp.com:smtp".
* Create a spool directory under "<windir>\spool", eg. "c:\win98\spool\emailrelay".
* Configure your e-mail client (eg. Outlook) to use SMTP on the local machine for outgoing e-mail.
* Run the forwarding client from the taskbar/desktop shortcut once connected to the Internet.
These steps are explained in more detail below.
Unpacking
---------
To start the installation process you will need to extract the files from
the "zip" archive. If you have "WinZip" installed then double-clicking on
the E-MailRelay "zip" file should start WinZip and open the archive.
From WinZip you can press the "Extract" button, or use the "Actions->Extract"
menu option. In the "Extract to" box you can type the full path of a new
sub-directory under "Program Files" which will hold the extracted files, eg.
"C:\Program Files\emailrelay". WinZip will create the sub-directory
("emailrelay") if necessary.
Server configuration
--------------------
Once the files are unpacked you need to create a shortcut to the E-MailRelay
executable in the Start menu's "StartUp" folder. This will make sure that the
E-MailRelay server is started automatically when you next boot the machine and
log in.
One way to do this is to open up the "Program Files\emailrelay" folder using
Windows Explorer or "My Computer", and open a second folder window for the
Start menu. To open thes second "Start menu" window: (1) right-click on a
blank part of the taskbar, (2) select "Properties" from the popup menu, (3)
select the "Start Menu Programs" tab, and (4) press the "Advanced..."
button. In the Start menu window go down into the "Programs" folder and then
into "StartUp". Then simply drag the "emailrelay.exe" file from one window
to the other using the right mouse button. When you drop the file, select
the "Create Shortcut Here" option on the pop-up menu.
You can then configure the E-MailRelay server by adding configuration options
to the shortcut's command line. The server should work okay without this step,
but there may be options you want to add. Refer to the userguide and reference
manual for more information on what configuration options are available.
To add configuration options right-click on the new shortcut which you have
created in the "Start menu" window. Select "Properties" from the pop-up menu,
and then select the "Shortcut" tab in the properties dialog box. The "Target"
box should contain the name of the E-MailRelay executable. Add any additional
configuration options after the executable name, separated by a space.
Client configuration
--------------------
Once the server is configured the next step is to create an icon on the desktop
(or taskbar) which you can use to start the E-MailRelay forwarding client once
you are connected to the Internet.
You can do this by simply dragging the E-MailRelay executable file from the
"Program Files\emailrelay" folder onto the desktop. Then right click on the
new icon and select "Properties" from the pop-up menu.
Again the "Target" box should contain the path of the E-MailRelay executable,
and you should add configuration options at the end, separated by a space.
As a minumum you will need to add "--as-client myisp:smtp", where "myisp"
is replaced with the hostname of your ISP's SMTP server.
Preparation
-----------
To complete the installation you should create a spool directory to hold
your e-mail messages while you are off-line. By default E-MailRelay will
look for a directory "<windir>\spool\emailrelay", where "<windir>" is the
path of your main windows directory, typically "c:\win98" for Windows 98.
Finally you will need to configure you e-mail client program to use
the local E-MailRelay server for outgoing mail. Where it asks for the
name of the SMTP server for outgoing mail you should tell it to use
"localhost" or "127.0.0.1".
Uninstall
---------
There are no DLLs or registry entries to clean up: just delete the files under
"Program Files\emailrelay" and "<windir>\spool\emailrelay", and remove any
taskbar, desktop or "Start->Programs->StartUp" shortcuts.
Copyright (C) 2001 Graeme Walker <graeme_walker@users.sourceforge.net>. All rights reserved.

View File

@ -1,10 +1,10 @@
Summary: Simple e-mail message transfer agent using SMTP
Name: emailrelay
Version: 0.9.5
Version: 0.9.6
Release: 1
Copyright: GPL
Group: System Environment/Daemons
Source: http://emailrelay.sourceforge.net/.../emailrelay-src-0.9.5.tar.gz
Source: http://emailrelay.sourceforge.net/.../emailrelay-src-0.9.6.tar.gz
BuildRoot: /tmp/emailrelay-install
%description
@ -48,6 +48,7 @@ rm -rf $RPM_BUILD_ROOT
/usr/local/share/emailrelay/userguide.html
/usr/local/share/emailrelay/man.html
/usr/local/share/emailrelay/index.html
/usr/local/share/emailrelay/windows.html
/usr/local/share/emailrelay/graphics/bullet.gif
/usr/local/share/emailrelay/html/
/usr/local/man/man1/emailrelay.1

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -126,7 +126,7 @@ maintainer-clean-recursive:
dot_seen=no; \
rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \
rev="$$subdir $$rev"; \
test "$$subdir" = "." && dot_seen=yes; \
test "$$subdir" != "." || dot_seen=yes; \
done; \
test "$$dot_seen" = "no" && rev=". $$rev"; \
target=`echo $@ | sed s/-recursive//`; \
@ -187,7 +187,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -121,7 +121,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -121,7 +121,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \

20
missing
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Common stub for a few missing GNU programs while installing.
# Copyright (C) 1996, 1997 Free Software Foundation, Inc.
# Copyright (C) 1996, 1997, 2001 Free Software Foundation, Inc.
# Franc,ois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
@ -23,6 +23,14 @@ if test $# -eq 0; then
exit 1
fi
# In the cases where this matters, `missing' is being run in the
# srcdir already.
if test -f configure.in; then
configure_ac=configure.ac
else
configure_ac=configure.in
fi
case "$1" in
-h|--h|--he|--hel|--help)
@ -61,7 +69,7 @@ Supported PROGRAM values:
aclocal)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`acinclude.m4' or \`configure.in'. You might want
you modified \`acinclude.m4' or \`$configure_ac'. You might want
to install the \`Automake' and \`Perl' packages. Grab them from
any GNU archive site."
touch aclocal.m4
@ -70,7 +78,7 @@ WARNING: \`$1' is missing on your system. You should only need it if
autoconf)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`configure.in'. You might want to install the
you modified \`$configure_ac'. You might want to install the
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
archive site."
touch configure
@ -79,10 +87,10 @@ WARNING: \`$1' is missing on your system. You should only need it if
autoheader)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`acconfig.h' or \`configure.in'. You might want
you modified \`acconfig.h' or \`$configure_ac'. You might want
to install the \`Autoconf' and \`GNU m4' packages. Grab them
from any GNU archive site."
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' configure.in`
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' $configure_ac`
test -z "$files" && files="config.h"
touch_files=
for f in $files; do
@ -98,7 +106,7 @@ WARNING: \`$1' is missing on your system. You should only need it if
automake)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`Makefile.am', \`acinclude.m4' or \`configure.in'.
you modified \`Makefile.am', \`acinclude.m4' or \`$configure_ac'.
You might want to install the \`Automake' and \`Perl' packages.
Grab them from any GNU archive site."
find . -type f -name Makefile.am -print |

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -126,7 +126,7 @@ maintainer-clean-recursive:
dot_seen=no; \
rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \
rev="$$subdir $$rev"; \
test "$$subdir" = "." && dot_seen=yes; \
test "$$subdir" != "." || dot_seen=yes; \
done; \
test "$$dot_seen" = "no" && rev=". $$rev"; \
target=`echo $@ | sed s/-recursive//`; \
@ -187,7 +187,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -211,7 +211,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
@ -249,22 +249,23 @@ gdatetime_unix.o: gdatetime_unix.cpp gdef.h ../../config.h \
gdirectory.o: gdirectory.cpp gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gdirectory.h \
gpath.h gstrings.h gfs.h glog.h
gpath.h gstrings.h gexception.h gfs.h glog.h
gdirectory_unix.o: gdirectory_unix.cpp gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gdirectory.h \
gpath.h gstrings.h gfs.h gdebug.h glogoutput.h glog.h gassert.h
gpath.h gstrings.h gexception.h gfs.h gfile.h gdebug.h \
glogoutput.h glog.h gassert.h
gexception.o: gexception.cpp gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gexception.h
gfile.o: gfile.cpp gdef.h ../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
../../lib/gcc2.95/limits gfile.h gpath.h gstrings.h \
gexception.h glog.h
gexception.h gprocess.h glog.h
gfile_unix.o: gfile_unix.cpp gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gfile.h \
gpath.h gstrings.h gexception.h
gpath.h gstrings.h gexception.h gprocess.h
gfs_unix.o: gfs_unix.cpp gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gfs.h

View File

@ -27,6 +27,7 @@
#include "gstr.h"
#include "gdebug.h"
#include "gassert.h"
#include <cstring>
G::Arg::Arg()
{
@ -138,3 +139,14 @@ std::string G::Arg::prefix() const
return m_prefix ;
}
//static
const char * G::Arg::prefix( char * argv [] ) // throw()
{
const char * exe = argv[0] ;
const char * p1 = std::strrchr( exe , '/' ) ;
const char * p2 = std::strrchr( exe , '\\' ) ;
p1 = p1 ? (p1+1U) : exe ;
p2 = p2 ? (p2+1U) : exe ;
return p1 > p2 ? p1 : p2 ;
}

View File

@ -76,6 +76,11 @@ public:
// Returns the basename of v(0) without
// any extension.
static const char * prefix( char * argv[] ) ; // throw()
// An exception-free version of prefix() which can
// be used in main() outside of the outermost try
// block.
bool contains( const std::string & sw , size_t sw_args = 0U ) const ;
// Returns true if the command line
// contains the given switch with enough

View File

@ -25,7 +25,7 @@
#include "garg.h"
#include "gdebug.h"
std::string G::Arg::moduleName( HINSTANCE hinstance )
std::string G::Arg::moduleName( HINSTANCE )
{
return std::string() ;
}

View File

@ -86,7 +86,6 @@ void G::Date::init( const G::DateTime::BrokenDownTime & tm )
std::string G::Date::string( Format format ) const
{
const char * sep = format == yyyy_mm_dd_slash ? "/" : "" ;
std::stringstream ss ;
if( format == yyyy_mm_dd_slash )
ss << m_year << "/" << m_month << "/" << m_day ;

View File

@ -27,9 +27,9 @@
#include "gassert.h"
#include <sstream>
const unsigned int minute = 60U ;
const unsigned int hour = 60U * minute ;
const unsigned int day = 24U * hour ;
const time_t minute = 60U ;
const time_t hour = 60U * minute ;
const time_t day = 24U * hour ;
G::DateTime::EpochTime G::DateTime::now()
{
@ -43,7 +43,7 @@ G::DateTime::EpochTime G::DateTime::epochTime( const BrokenDownTime & bdt_in )
EpochTime start = std::mktime( &bdt ) ; // localtime
// iterate over all timezones
const unsigned int delta = minute * 30U ;
const time_t delta = minute * 30U ;
for( EpochTime t = (start-day-delta) ; t <= (start+day+delta) ; t += delta )
{
if( equivalent( t , bdt_in ) )

View File

@ -67,7 +67,8 @@ public:
// operation.)
static std::string offsetString( Offset offset ) ;
// Uses the five-character format "+/-hhmm".
// Converts the given utc/localtime offset into a five-character
// "+/-hhmm" string.
// See also RFC2822.
private:

View File

@ -101,8 +101,8 @@
#include <sys/stat.h>
#endif
// Define Windows-style types (under unix these
// are only used for unimplemented declarations)
// Define Windows-style types (only used for
// unimplemented declarations under unix)
//
#if ! defined( G_WINDOWS )
typedef unsigned char BOOL ;
@ -111,7 +111,8 @@
typedef unsigned int HANDLE ;
#endif
// Include commonly-used system headers
// Include commonly-used system headers (good for
// pre-compilation)
//
#include <iostream>
#include <fstream>
@ -126,6 +127,8 @@
//
typedef unsigned long g_uint32_t ;
typedef unsigned short g_uint16_t ;
typedef long g_int32_t ;
typedef short g_int16_t ;
// Define short-name types
//
@ -155,6 +158,7 @@
#pragma warning( disable : 4511 ) // cannot create default copy ctor
#pragma warning( disable : 4512 ) // cannot create default op=()
#pragma warning( disable : 4786 ) // truncation in debug info
#pragma warning( disable : 4275 ) // dll-interface stuff in <complex>
#endif
#endif

View File

@ -26,6 +26,7 @@
#include "gdef.h"
#include "gpath.h"
#include "gexception.h"
#include <string>
#include <sys/types.h>

View File

@ -24,6 +24,7 @@
#include "gdef.h"
#include "gdirectory.h"
#include "gfs.h"
#include "gfile.h"
#include "gdebug.h"
#include "glog.h"
#include <unistd.h>
@ -236,6 +237,7 @@ std::string G::DirectoryIteratorImp::modificationTimeString() const
std::string G::DirectoryIteratorImp::sizeString() const
{
return std::string("0") ; // for now
std::string s = G::File::sizeString( filePath() ) ;
return s.empty() ? std::string("0") : s ;
}

View File

@ -24,6 +24,7 @@
#include "gdef.h"
#include "gdirectory.h"
#include "gfs.h"
#include "gfile.h"
#include "gdebug.h"
#include "glog.h"
#include <iomanip>
@ -295,8 +296,6 @@ std::string G::DirectoryIteratorImp::sizeString() const
const DWORD & hi = m_context.nFileSizeHigh ;
const DWORD & lo = m_context.nFileSizeLow ;
std::stringstream ss ;
ss << hi << std::setw(8) << std::setfill('0') << lo ;
return ss.str() ;
return G::File::sizeString( hi , lo ) ;
}

View File

@ -23,6 +23,7 @@
#include "gdef.h"
#include "gfile.h"
#include "gprocess.h"
#include "glog.h"
#include <iostream>
#include <cstdio>
@ -38,7 +39,7 @@ void G::File::remove( const Path & path )
{
if( 0 != std::remove( path.pathCstr() ) )
{
//int error = std::errno ;
//int error = G::Process::errno_() ;
throw CannotRemove( path.str() ) ;
}
G_DEBUG( "G::File::remove: \"" << path << "\"" ) ;
@ -55,7 +56,7 @@ void G::File::rename( const Path & from , const Path & to )
{
if( 0 != std::rename( from.pathCstr() , to.pathCstr() ) )
{
//int error = std::errno ;
//int error = G::Process::errno_() ;
throw CannotRename( from.str() ) ;
}
G_DEBUG( "G::File::rename: \"" << from << "\" -> \"" << to << "\"" ) ;
@ -83,3 +84,32 @@ void G::File::mkdir( const Path & dir )
throw CannotMkdir( dir.str() ) ;
}
bool G::File::exists( const Path & path )
{
return exists( path , false , true ) ;
}
bool G::File::exists( const Path & path , const NoThrow & )
{
return exists( path , false , false ) ;
}
bool G::File::exists( const Path & path , bool on_error , bool do_throw )
{
bool enoent = false ;
bool rc = exists( path.pathCstr() , enoent ) ; // o/s-specific
if( !rc && enoent )
{
return false ;
}
else if( !rc && do_throw )
{
throw StatError( path.str() ) ;
}
else if( !rc )
{
return on_error ;
}
return true ;
}

View File

@ -32,6 +32,7 @@
namespace G
{
class File ;
class DirectoryIteratorImp ;
} ;
// Class: G::File
@ -41,10 +42,12 @@ namespace G
class G::File
{
public:
G_EXCEPTION( StatError , "cannot stat() file" ) ;
G_EXCEPTION( CannotRemove , "cannot delete file" ) ;
G_EXCEPTION( CannotRename , "cannot rename file" ) ;
G_EXCEPTION( CannotCopy , "cannot copy file" ) ;
G_EXCEPTION( CannotMkdir , "cannot mkdir" ) ;
G_EXCEPTION( SizeOverflow , "file size overflow" ) ;
class NoThrow // An overload discriminator class for File methods.
{} ;
@ -71,6 +74,25 @@ public:
static void mkdir( const Path & dir ) ;
// Creates a directory.
static std::string sizeString( const Path & file ) ;
// Returns the file's size in string format.
// Returns the empty string on error.
static bool exists( const Path & file ) ;
// Returns true if the file (or link or device etc.)
// exists. Throws an exception if permission denied
// or too many symlinks etc.
static bool exists( const Path & file , const NoThrow & ) ;
// Returns true if the file (or link or device etc.)
// exists. Returns false on error.
private:
friend class G::DirectoryIteratorImp ;
static std::string sizeString( g_uint32_t hi , g_uint32_t lo ) ; // win32
static bool exists( const Path & , bool , bool ) ;
static bool exists( const char * , bool & ) ; // o/s-specific
} ;
#endif

View File

@ -23,10 +23,39 @@
#include "gdef.h"
#include "gfile.h"
#include "gprocess.h"
#include <errno.h>
#include <sys/stat.h>
#include <sstream>
bool G::File::mkdir( const Path & dir , const NoThrow & )
{
return 0 == ::mkdir( dir.str().c_str() , S_IRUSR | S_IWUSR | S_IXUSR ) ;
}
bool G::File::exists( const char * path , bool & enoent )
{
struct stat statbuf ;
if( 0 == ::stat( path , &statbuf ) )
{
return true ;
}
else
{
int error = G::Process::errno_() ;
enoent = error == ENOENT || error == ENOTDIR ;
return false ;
}
}
std::string G::File::sizeString( const Path & path )
{
struct stat statbuf ;
if( 0 != ::stat( path.pathCstr() , &statbuf ) )
return std::string() ;
std::stringstream ss ;
ss << statbuf.st_size ;
return ss.str() ;
}

View File

@ -25,9 +25,55 @@
#include "gfile.h"
#include <sys/stat.h>
#include <direct.h>
#include <iomanip>
#include <sstream>
bool G::File::mkdir( const Path & dir , const NoThrow & )
{
return 0 == ::_mkdir( dir.str().c_str() ) ;
}
std::string G::File::sizeString( const Path & path )
{
WIN32_FIND_DATA info ;
HANDLE h = ::FindFirstFile( path.str().c_str() , &info ) ;
if( h == INVALID_HANDLE_VALUE )
return std::string() ;
const DWORD & hi = info.nFileSizeHigh ;
const DWORD & lo = info.nFileSizeLow ;
::FindClose( h ) ;
return sizeString( hi , lo ) ;
}
std::string G::File::sizeString( g_uint32_t hi , g_uint32_t lo )
{
__int64 n = hi ;
n <<= 32U ;
n |= lo ;
if( n < 0 )
throw SizeOverflow() ;
if( n == 0 )
return std::string("0") ;
std::string s ;
while( n != 0 )
{
size_t i = n % 10U ;
s.insert( 0U , 1U , '0' + i ) ;
n /= 10U ;
}
return s ;
}
bool G::File::exists( const char * path , bool & enoent )
{
struct _stat statbuf ;
bool ok = 0 == ::_stat( path , &statbuf ) ;
enoent = !ok ;
return ok ;
}

View File

@ -148,6 +148,11 @@ size_t G::GetOpt::widthLimit( size_t w )
return (w != 0U && w < 50U) ? 50U : w ;
}
void G::GetOpt::showUsage( std::ostream & stream , const std::string & args ) const
{
showUsage( stream , m_args.prefix() , args ) ;
}
void G::GetOpt::showUsage( std::ostream & stream , const std::string & exe , const std::string & args ,
size_t tab_stop , size_t width ) const
{
@ -494,6 +499,11 @@ bool G::GetOpt::hasErrors() const
return m_errors.size() != 0U ;
}
void G::GetOpt::showErrors( std::ostream & stream ) const
{
showErrors( stream , m_args.prefix() ) ;
}
void G::GetOpt::showErrors( std::ostream & stream , std::string prefix_1 ,
std::string prefix_2 ) const
{

View File

@ -91,8 +91,11 @@ public:
const std::string & args , size_t tab_stop = 30U ,
size_t wrap_width = wrapDefault() ) const ;
// Streams out multi-line usage text using
// usageSummary() and usageHelp(). Does nothing
// about non-switch arguments.
// usageSummary() and usageHelp().
void showUsage( std::ostream & stream , const std::string & args ) const ;
// Streams out multi-line usage text using
// usageSummary() and usageHelp().
bool hasErrors() const ;
// Returns true if there are errors.
@ -103,6 +106,9 @@ public:
// item to the given stream, prefixed with the given
// prefix(es). The two prefixes are simply concatenated.
void showErrors( std::ostream & stream ) const ;
// An overload which uses prefix() as <prefix_1>.
void show( std::ostream & stream , std::string prefix ) const ;
// For debugging.
@ -134,8 +140,8 @@ private:
SwitchSpec(char c_,const std::string &name_,const std::string &description_,
bool v_,const std::string &vd_) :
c(c_) , name(name_) , description(description_) ,
hidden(description_.empty()) ,
valued(v_) , value_description(vd_) {}
valued(v_) , hidden(description_.empty()) ,
value_description(vd_) {}
} ;
typedef std::map<std::string,SwitchSpec GLessAllocator(char,SwitchSpec) > SwitchSpecMap ;
typedef std::pair<bool,std::string> Value ;

View File

@ -32,19 +32,14 @@ void G::LogOutput::rawOutput( G::Log::Severity severity , const char *message )
std::cerr << message ;
std::cerr.flush() ;
if( std::getenv("GLOGOUTPUT_DEBUGGER") != NULL )
static bool debugger = std::getenv("GLOGOUTPUT_DEBUGGER") != NULL ;
if( debugger )
{
::OutputDebugString( message ) ;
}
static bool first = true ;
static const char * filename = NULL ;
if( first )
{
first = false ;
const char * key = "GLOGOUTPUT_FILE" ;
filename = std::getenv(key) ;
}
static const char * key = "GLOGOUTPUT_FILE" ;
static const char * filename = std::getenv( key ) ;
if( filename != NULL && *filename != '\0' )
{
static std::ofstream file( filename ) ;

View File

@ -75,7 +75,7 @@ void operator<<=( std::auto_ptr<T> & ap , T * p )
// Description: A version for null-pointer constants.
//
template <class T>
void operator<<=( std::auto_ptr<T> & ap , int null_pointer )
void operator<<=( std::auto_ptr<T> & ap , int /* null_pointer */ )
{
T * p = 0 ;
ap <<= p ;

View File

@ -110,9 +110,11 @@ public:
// Returns true if this process has enhanced security
// privileges.
static int errno_() ;
// Returns the process's current 'errno' value.
private:
Process() ;
static int errno_() ;
static void execCore( const Path & , const std::string & ) ;
} ;

View File

@ -31,6 +31,9 @@
#include <sys/stat.h>
#include <fcntl.h> // open()
// Class: G::Process::IdImp
// Description: A private implementation class used by G::Process.
//
class G::Process::IdImp
{
public:
@ -56,13 +59,15 @@ bool G::Process::cd( const Path & dir , NoThrow )
void G::Process::setUmask()
{
mode_t new_mode = 0177 ; // create as -rw-------
mode_t old_mode = ::umask( new_mode ) ;
(void) ::umask( new_mode ) ;
}
//static
void G::Process::closeStderr()
{
::close( STDERR_FILENO ) ;
::open( G::FileSystem::nullDevice() , O_WRONLY ) ;
::fcntl( STDERR_FILENO , F_SETFD , 0 ) ; // close-on-exec false
}
//static
@ -76,10 +81,26 @@ void G::Process::closeFiles( bool keep_stderr )
for( int fd = 0 ; fd < n ; fd++ )
{
if( !keep_stderr || fd != STDERR_FILENO )
{
::close( fd ) ;
}
}
// reopen standard fds to prevent accidental use
// of arbitrary files or sockets as standard
// streams
//
::open( G::FileSystem::nullDevice() , O_RDONLY ) ;
::open( G::FileSystem::nullDevice() , O_WRONLY ) ;
if( !keep_stderr )
{
::open( G::FileSystem::nullDevice() , O_WRONLY ) ;
::fcntl( STDERR_FILENO , F_SETFD , 0 ) ; // close-on-exec false
}
::fcntl( STDIN_FILENO , F_SETFD , 0 ) ; // close-on-exec false
::fcntl( STDOUT_FILENO , F_SETFD , 0 ) ; // close-on-exec false
}
G::Process::Who G::Process::fork()
{
Id id ;
@ -160,9 +181,6 @@ void G::Process::exec( const G::Path & exe , const std::string & arg )
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.

View File

@ -141,6 +141,7 @@ bool G::Process::privileged()
}
// not implemented...
// int G::Process::errno_()
// Who G::Process::fork() {}
// Who G::Process::fork( Id & child ) {}
// void G::Process::exec( const Path & exe , const std::string & arg ) {}

View File

@ -182,7 +182,7 @@ unsigned int G::Str::toUInt( const std::string &s , bool limited )
unsigned long G::Str::toULong( const std::string &s , bool limited )
{
char * end = NULL ;
unsigned long result = ::strtoul( s.c_str(), &end, 0 ) ;
unsigned long result = ::strtoul( s.c_str(), &end, 10 ) ;
if( end == 0 || end[0] != '\0' )
throw InvalidFormat( s ) ;
@ -257,6 +257,10 @@ std::string G::Str::toPrintableAscii( char c , char escape )
{
result.append( 1U , 't' ) ;
}
else if( c == '\0' )
{
result.append( 1U , '0' ) ;
}
else
{
unsigned int n = c ;
@ -270,7 +274,7 @@ std::string G::Str::toPrintableAscii( char c , char escape )
std::string G::Str::toPrintableAscii( const std::string & in , char escape )
{
std::string result ;
for( const char * p = in.c_str() ; *p ; ++p )
for( std::string::const_iterator p = in.begin() ; p != in.end() ; ++p )
result.append( toPrintableAscii(*p,escape) ) ;
return result ;
}
@ -293,8 +297,16 @@ std::string G::Str::readLineFrom( std::istream & stream , char ignore )
std::string G::Str::readLineFrom( std::istream & stream , const std::string & eol )
{
std::string result ;
readLineFrom( stream , eol , result ) ;
return result ;
}
void G::Str::readLineFrom( std::istream & stream , const std::string & eol , std::string & line )
{
line.erase() ;
const size_t eol_length = eol.length() ;
std::string line ;
char c ;
for( size_t line_length = 1U ; stream.get(c) ; ++line_length )
{
@ -305,11 +317,10 @@ std::string G::Str::readLineFrom( std::istream & stream , const std::string & eo
if( line.find(eol,offset) == offset )
{
line.erase(offset) ;
return line ;
break ;
}
}
}
return line ;
}
std::string G::Str::wrap( std::string text , const std::string & prefix_1 ,

View File

@ -153,6 +153,9 @@ public:
// An overload which uses 'eol' as the terminator, and
// without the 'ignore' feature.
static void readLineFrom( std::istream & stream , const std::string & eol , std::string & result ) ;
// An overload which avoids string copying.
static std::string wrap( std::string text ,
const std::string & prefix_first_line , const std::string & prefix_subsequent_lines ,
size_t width = 70U ) ;

View File

@ -46,6 +46,7 @@ libgnet_a_SOURCES = gaddress_ipv4.cpp \
gserver.cpp \
gsocket.cpp \
gsocket_unix.cpp \
gtimer.cpp \
gaddress.h \
gclient.h \
gconnection.h \
@ -61,4 +62,5 @@ libgnet_a_SOURCES = gaddress_ipv4.cpp \
gselect.h \
gserver.h \
gsocket.h \
gtimer.h \
gwinsock.h

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -93,7 +93,7 @@ EXTRA_DIST = gclient_win32.cpp gdescriptor_win32.cpp gevent_win32.cpp glocal_
INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib
noinst_LIBRARIES = libgnet.a
libgnet_a_SOURCES = gaddress_ipv4.cpp gclient.cpp gclient_unix.cpp gconnection.cpp gdescriptor_unix.cpp gevent.cpp gevent_unix.cpp geventserver.cpp glinebuffer.cpp glocal_unix.cpp gmonitor.cpp gresolve.cpp gresolve_ipv4.cpp gresolve_unix.cpp gselect.cpp gserver.cpp gsocket.cpp gsocket_unix.cpp gaddress.h gclient.h gconnection.h gdescriptor.h gevent.h geventserver.h glinebuffer.h glocal.h gmonitor.h gnet.h grequest.h gresolve.h gselect.h gserver.h gsocket.h gwinsock.h
libgnet_a_SOURCES = gaddress_ipv4.cpp gclient.cpp gclient_unix.cpp gconnection.cpp gdescriptor_unix.cpp gevent.cpp gevent_unix.cpp geventserver.cpp glinebuffer.cpp glocal_unix.cpp gmonitor.cpp gresolve.cpp gresolve_ipv4.cpp gresolve_unix.cpp gselect.cpp gserver.cpp gsocket.cpp gsocket_unix.cpp gtimer.cpp gaddress.h gclient.h gconnection.h gdescriptor.h gevent.h geventserver.h glinebuffer.h glocal.h gmonitor.h gnet.h grequest.h gresolve.h gselect.h gserver.h gsocket.h gtimer.h gwinsock.h
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ../../config.h
@ -109,7 +109,7 @@ libgnet_a_LIBADD =
libgnet_a_OBJECTS = gaddress_ipv4.o gclient.o gclient_unix.o \
gconnection.o gdescriptor_unix.o gevent.o gevent_unix.o geventserver.o \
glinebuffer.o glocal_unix.o gmonitor.o gresolve.o gresolve_ipv4.o \
gresolve_unix.o gselect.o gserver.o gsocket.o gsocket_unix.o
gresolve_unix.o gselect.o gserver.o gsocket.o gsocket_unix.o gtimer.o
CXXFLAGS = @CXXFLAGS@
CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX)
@ -211,7 +211,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
@ -230,7 +230,7 @@ gclient.o: gclient.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
gaddress.h ../../src/glib/gexception.h gsocket.h gevent.h \
gdescriptor.h ../../src/glib/gdatetime.h gresolve.h gmonitor.h \
../../src/glib/gdatetime.h gdescriptor.h gresolve.h gmonitor.h \
gclient.h gconnection.h gserver.h gselect.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/glog.h ../../src/glib/gassert.h
@ -238,7 +238,7 @@ gclient_unix.o: gclient_unix.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
gclient.h gaddress.h ../../src/glib/gexception.h gconnection.h \
gsocket.h gevent.h gdescriptor.h
gsocket.h gevent.h ../../src/glib/gdatetime.h gdescriptor.h
gconnection.o: gconnection.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
@ -250,19 +250,21 @@ gdescriptor_unix.o: gdescriptor_unix.cpp ../../src/glib/gdef.h \
gevent.o: gevent.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
gevent.h gdescriptor.h ../../src/glib/gdebug.h \
gevent.h ../../src/glib/gdatetime.h ../../src/glib/gexception.h \
gdescriptor.h ../../src/glib/gdebug.h \
../../src/glib/glogoutput.h ../../src/glib/glog.h \
../../src/glib/gassert.h
gevent_unix.o: gevent_unix.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
gevent.h gdescriptor.h gselect.h ../../src/glib/gexception.h
gevent.h ../../src/glib/gdatetime.h ../../src/glib/gexception.h \
gdescriptor.h gselect.h
geventserver.o: geventserver.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
geventserver.h gserver.h gsocket.h gaddress.h \
../../src/glib/gexception.h gevent.h gdescriptor.h \
gconnection.h gselect.h ../../src/glib/glog.h
../../src/glib/gexception.h gevent.h ../../src/glib/gdatetime.h \
gdescriptor.h gconnection.h gselect.h ../../src/glib/glog.h
glinebuffer.o: glinebuffer.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
@ -278,17 +280,17 @@ gmonitor.o: gmonitor.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
gmonitor.h gclient.h gaddress.h ../../src/glib/gexception.h \
gconnection.h gsocket.h gevent.h gdescriptor.h gserver.h \
gselect.h ../../src/glib/gassert.h ../../src/glib/glogoutput.h \
../../src/glib/glog.h
gconnection.h gsocket.h gevent.h ../../src/glib/gdatetime.h \
gdescriptor.h gserver.h gselect.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h ../../src/glib/glog.h
gresolve.o: gresolve.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gresolve.h \
gnet.h gaddress.h ../../src/glib/gexception.h gsocket.h \
gevent.h gdescriptor.h ../../src/glib/gstr.h \
../../src/glib/gstrings.h ../../src/glib/gdebug.h \
../../src/glib/glogoutput.h ../../src/glib/glog.h \
../../src/glib/gassert.h
gevent.h ../../src/glib/gdatetime.h gdescriptor.h \
../../src/glib/gstr.h ../../src/glib/gstrings.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/glog.h ../../src/glib/gassert.h
gresolve_ipv4.o: gresolve_ipv4.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gresolve.h \
@ -297,35 +299,45 @@ gresolve_unix.o: gresolve_unix.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gresolve.h \
gnet.h gaddress.h ../../src/glib/gexception.h gsocket.h \
gevent.h gdescriptor.h ../../src/glib/gstr.h \
../../src/glib/gstrings.h ../../src/glib/gdebug.h \
../../src/glib/glogoutput.h ../../src/glib/glog.h \
../../src/glib/gassert.h
gevent.h ../../src/glib/gdatetime.h gdescriptor.h \
../../src/glib/gstr.h ../../src/glib/gstrings.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/glog.h ../../src/glib/gassert.h
gselect.o: gselect.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gselect.h \
gnet.h gevent.h gdescriptor.h ../../src/glib/gexception.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/glog.h ../../src/glib/gassert.h
gnet.h gevent.h ../../src/glib/gdatetime.h \
../../src/glib/gexception.h gdescriptor.h ../../src/glib/gstr.h \
../../src/glib/gstrings.h gtimer.h ../../src/glib/gdebug.h \
../../src/glib/glogoutput.h ../../src/glib/glog.h \
../../src/glib/gassert.h
gserver.o: gserver.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
gserver.h gsocket.h gaddress.h ../../src/glib/gexception.h \
gevent.h gdescriptor.h gconnection.h gselect.h gmonitor.h \
gclient.h ../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/glog.h ../../src/glib/gassert.h
gevent.h ../../src/glib/gdatetime.h gdescriptor.h gconnection.h \
gselect.h gmonitor.h gclient.h ../../src/glib/gdebug.h \
../../src/glib/glogoutput.h ../../src/glib/glog.h \
../../src/glib/gassert.h ../../src/glib/gmemory.h
gsocket.o: gsocket.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
../../src/glib/gassert.h ../../src/glib/glogoutput.h \
../../src/glib/glog.h gsocket.h gaddress.h \
../../src/glib/gexception.h gevent.h gdescriptor.h \
../../src/glib/gmemory.h ../../src/glib/gdebug.h
../../src/glib/gexception.h gevent.h ../../src/glib/gdatetime.h \
gdescriptor.h ../../src/glib/gmemory.h ../../src/glib/gdebug.h
gsocket_unix.o: gsocket_unix.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gnet.h \
gsocket.h gaddress.h ../../src/glib/gexception.h gevent.h \
gdescriptor.h ../../src/glib/gdebug.h \
../../src/glib/gdatetime.h gdescriptor.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/glog.h ../../src/glib/gassert.h
gtimer.o: gtimer.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
../../lib/gcc2.95/xlocale ../../lib/gcc2.95/limits gtimer.h \
gnet.h ../../src/glib/gdatetime.h ../../src/glib/gexception.h \
gevent.h gdescriptor.h ../../src/glib/gdebug.h \
../../src/glib/glogoutput.h ../../src/glib/glog.h \
../../src/glib/gassert.h

View File

@ -80,6 +80,7 @@ private:
ClientResolver m_resolver ;
StreamSocket * m_s ;
Address m_address ;
std::string m_peer_name ;
Client & m_interface ;
bool m_priviledged ;
enum Status { Success , Failure , Retry , ImmediateSuccess } ;
@ -96,7 +97,7 @@ public:
void writeEvent() ;
void exceptionEvent() ;
bool connect( std::string host , std::string service , std::string *error , bool sync_dns ) ;
std::string startConnecting( const Address & , bool & ) ;
std::string startConnecting( const Address & , const std::string & , bool & ) ;
Status connectCore( Address , std::string * , bool , unsigned int ) ;
void disconnect() ;
StreamSocket & s() ;
@ -108,6 +109,7 @@ public:
void setState( State ) ;
std::pair<bool,Address> localAddress() const ;
std::pair<bool,Address> peerAddress() const ;
std::string peerName() const ;
private:
ClientImp( const ClientImp & ) ;
@ -166,17 +168,22 @@ std::pair<bool,GNet::Address> GNet::Client::peerAddress() const
return m_imp->peerAddress() ;
}
std::string GNet::Client::peerName() const
{
return m_imp->peerName() ;
}
// ===
bool GNet::ClientImp::m_first = true ;
GNet::ClientImp::ClientImp( Client &intaface , bool priviledged , bool quit_on_disconnect ) :
m_interface(intaface) ,
m_state(Idle) ,
m_s(NULL) ,
m_resolver(*this) ,
m_priviledged(priviledged) ,
m_s(NULL) ,
m_address(Address::invalidAddress()) ,
m_interface(intaface) ,
m_priviledged(priviledged) ,
m_state(Idle) ,
m_quit_on_disconnect(quit_on_disconnect)
{
G_DEBUG( "ClientImp::ctor" ) ;
@ -252,7 +259,7 @@ bool GNet::ClientImp::connect( std::string host , std::string service ,
return false ;
}
bool immediate = false ;
std::string connect_reason = startConnecting( pair.first.address , immediate ) ;
std::string connect_reason = startConnecting( pair.first.address, pair.first.canonical_name, immediate);
if( connect_reason.length() != 0U )
{
error = connect_reason ;
@ -295,8 +302,9 @@ void GNet::ClientImp::resolveCon( bool success , const Address &address ,
if( success )
{
G_DEBUG( "GNet::ClientImp::resolveCon: " << address.displayString() ) ;
std::string peer_name = resolve_reason ;
bool immediate = false ;
std::string connect_reason = startConnecting( address , immediate ) ;
std::string connect_reason = startConnecting( address , peer_name , immediate ) ;
if( connect_reason.length() )
{
close() ;
@ -314,11 +322,12 @@ void GNet::ClientImp::resolveCon( bool success , const Address &address ,
}
}
std::string GNet::ClientImp::startConnecting( const Address & address , bool & immediate )
std::string GNet::ClientImp::startConnecting( const Address & address , const std::string & peer_name , bool & immediate )
{
// save the target address
G_DEBUG( "GNet::ClientImp::startConnecting: " << address.displayString() ) ;
m_address = address ;
m_peer_name = peer_name ;
// create and open a socket
//
@ -444,7 +453,7 @@ void GNet::ClientImp::readEvent()
else if( n != -1 )
{
G_ASSERT( n <= sizeof(buffer) ) ;
G_DEBUG( "GNet::ClientImp::readEvent: " << n << " byte(s)" ) ;
//G_DEBUG( "GNet::ClientImp::readEvent: " << n << " byte(s)" ) ;
m_interface.onData( buffer , n ) ;
}
else
@ -489,6 +498,11 @@ std::pair<bool,GNet::Address> GNet::ClientImp::peerAddress() const
return s().getPeerAddress() ;
}
std::string GNet::ClientImp::peerName() const
{
return m_peer_name ;
}
// ===
void GNet::ClientResolver::resolveCon( bool success , const Address &address ,

View File

@ -89,13 +89,17 @@ public:
// Destructor.
virtual std::pair<bool,Address> localAddress() const ;
// Returns the local address.
// Override from Connection. Returns the local address.
// Pair.first is false on error.
virtual std::pair<bool,Address> peerAddress() const ;
// Returns the peer address.
// Override from Connection. Returns the peer address.
// Pair.first is false on error.
std::string peerName() const ;
// Returns the peer's canonical name if available.
// Returns the empty string if not.
protected:
friend class ClientImp ;

View File

@ -83,7 +83,8 @@ GNet::EventHandlerList::EventHandlerList( std::string type ) :
//static
bool GNet::EventHandlerList::contains( const EventHandlerListImp & list , Descriptor fd )
{
for( List::const_iterator p = list.begin() ; p != list.end() ; ++p )
const List::const_iterator end = list.end() ;
for( List::const_iterator p = list.begin() ; p != end ; ++p )
{
if( (*p).m_fd == fd )
return true ;
@ -153,7 +154,8 @@ void GNet::EventHandlerList::remove( Descriptor fd )
GNet::EventHandler * GNet::EventHandlerList::find( Descriptor fd )
{
for( List::iterator p = m_list.begin() ; p != m_list.end() ; ++p )
const List::iterator end = m_list.end() ;
for( List::iterator p = m_list.begin() ; p != end ; ++p )
{
if( (*p).m_fd == fd )
return (*p).m_handler ;
@ -162,30 +164,6 @@ GNet::EventHandler * GNet::EventHandlerList::find( Descriptor fd )
return NULL ;
}
GNet::EventHandlerList::Iterator GNet::EventHandlerList::begin() const
{
return m_list.begin() ;
}
GNet::EventHandlerList::Iterator GNet::EventHandlerList::end() const
{
return m_list.end() ;
}
//static
GNet::Descriptor GNet::EventHandlerList::fd( Iterator i )
{
return (*i).m_fd ;
}
//static
GNet::EventHandler & GNet::EventHandlerList::handler( Iterator i )
{
EventHandler * p = (*i).m_handler ;
G_ASSERT( p != NULL ) ;
return *p ;
}
void GNet::EventHandlerList::lock()
{
m_lock++ ;

View File

@ -26,6 +26,7 @@
#include "gdef.h"
#include "gnet.h"
#include "gdatetime.h"
#include "gdescriptor.h"
#include <list>
#include <string>
@ -55,9 +56,20 @@ class GNet::EventHandler
{
public:
virtual ~EventHandler() ;
virtual void readEvent() /*=0*/ ;
virtual void writeEvent() /*=0*/ ;
virtual void exceptionEvent() /*=0*/ ;
// Destructor.
virtual void readEvent() ;
// Called for a read event. The default
// implementation does nothing.
virtual void writeEvent() ;
// Called for a write event. The default
// implementation does nothing.
virtual void exceptionEvent() ;
// Called for an exception event. The default
// implementation does nothing.
private:
void operator=( const EventHandler & ) ; // not implemented
} ;
@ -80,9 +92,6 @@ private:
//
class GNet::EventSources
{
private:
static EventSources *m_this ;
protected:
EventSources() ;
// Constructor.
@ -139,6 +148,17 @@ public:
// Removes the given event source descriptor
// from the list of exception sources.
// See also Socket::dropExceptionHandler().
virtual void setTimeout( G::DateTime::EpochTime t ) = 0 ;
// Used by GNet::TimerList. Sets the time at which
// TimerList::doTimeouts() is to be called.
// A parameter of zero is used to cancel the
// timer. Some concrete implementations of this
// interface may use TimerList::interval()
// rather than setTimeout()/doTimeouts().
private:
static EventSources * m_this ;
} ;
@ -152,10 +172,16 @@ public:
Descriptor m_fd ;
EventHandler * m_handler ;
EventHandlerListItem( Descriptor fd = Descriptor__invalid() ,
EventHandler * handler = NULL ) :
m_fd(fd) , m_handler(handler) {}
EventHandler * handler = NULL ) ;
} ;
inline
GNet::EventHandlerListItem::EventHandlerListItem( Descriptor fd , EventHandler * handler ) :
m_fd(fd) ,
m_handler(handler)
{
}
namespace GNet
{
typedef std::list< EventHandlerListItem GAllocator(EventHandlerListItem) >
@ -164,7 +190,7 @@ namespace GNet
// Class: GNet::EventHandlerList
// Description: A class which can be used in the implemention
// of classes derived from GEventSources.
// of classes derived from GNet::EventSources.
//
class GNet::EventHandlerList
{
@ -231,6 +257,31 @@ private:
bool m_copied ;
} ;
inline
GNet::EventHandlerList::Iterator GNet::EventHandlerList::begin() const
{
return m_list.begin() ;
}
inline
GNet::EventHandlerList::Iterator GNet::EventHandlerList::end() const
{
return m_list.end() ;
}
//static
inline
GNet::Descriptor GNet::EventHandlerList::fd( Iterator i )
{
return (*i).m_fd ;
}
//static
inline
GNet::EventHandler & GNet::EventHandlerList::handler( Iterator i )
{
return *((*i).m_handler) ;
}
#endif

View File

@ -51,8 +51,8 @@ std::string GNet::Local::domainname()
if( pos == std::string::npos )
throw Error( "invalid fqdn" ) ;
G_DEBUG( "GNet::Local::domainname: \"" << full.substr(0U,pos) << "\"" ) ;
return full.substr( 0U , pos ) ;
G_DEBUG( "GNet::Local::domainname: \"" << full.substr(pos+1U) << "\"" ) ;
return full.substr( pos+1U ) ;
}
GNet::Address GNet::Local::canonicalAddress()

View File

@ -43,6 +43,7 @@ std::string GNet::Local::domainname()
size_t pos = full.find( '.' ) ;
if( pos == std::string::npos )
throw Error( std::stringstream() << "invalid fqdn: no dots in \"" << full << "\"" ) ;
return full.substr(pos+1U) ;
}
@ -85,6 +86,13 @@ std::string GNet::Local::fqdn()
throw Error( std::stringstream() << "resolve: " << rc.second ) ;
result = rc.first.canonical_name ;
size_t pos = result.find( '.' ) ;
if( pos == std::string::npos )
{
G_WARNING( "GNet::Local: no valid domain in \"" << result << "\": defaulting to \".local\"" ) ;
result.append( ".local" ) ;
}
}
return result ;
}

View File

@ -27,6 +27,9 @@
#include "gassert.h"
#include <set>
// Class: GNet::MontiorImp
// Description: A pimple pattern implementation class for GNet::Monitor.
//
class GNet::MonitorImp
{
public:

View File

@ -160,6 +160,21 @@ GNet::Address GNet::HostRequest::result() const
}
}
std::string GNet::HostRequest::fqdn() const
{
G_ASSERT( m_done && m_handle == 0 ) ;
if( m_numeric )
{
return std::string() ;
}
else
{
const hostent *h = reinterpret_cast<const hostent*>(m_buffer) ;
G_ASSERT( h != NULL && h->h_name != NULL ) ;
return std::string( h->h_name ) ;
}
}
// SERVICE...
GNet::ServiceRequest::ServiceRequest( std::string service_name , bool udp , HWND hwnd , unsigned msg ) :

View File

@ -38,7 +38,8 @@ namespace GNet
// Class: GNet::Request
// Description: A base class for making
// asynchronous DNS requests.
// asynchronous DNS requests under Windows.
// See also: WSAAsyncGetHostByName()
//
class GNet::Request
{
@ -56,7 +57,9 @@ protected:
protected:
explicit Request( bool host ) ;
// Constructor. Derived class constructors
// should issue the appropriate request.
// should issue the appropriate WSAAsync..()
// request, with m_buffer[] given as the
// result buffer.
public:
virtual ~Request() ;
@ -90,7 +93,14 @@ class GNet::HostRequest : public GNet:: Request
{
public:
HostRequest( std::string host_name , HWND hwnd , unsigned msg ) ;
Address result() const ; // zero port
// Constructor.
Address result() const ;
// Returns the resolved address with a zero port number.
std::string fqdn() const ;
// Returns the fully-qualified canonical hostname, if
// available.
private:
bool numeric( std::string s , Address & address ) ;
@ -104,8 +114,12 @@ private:
class GNet::ServiceRequest : public GNet:: Request
{
public:
ServiceRequest( std::string service_name , bool udp , HWND hwnd , unsigned msg ) ;
Address result() const ; // zero host address
ServiceRequest( std::string service_name , bool udp ,
HWND hwnd , unsigned msg ) ;
// Constructor.
Address result() const ;
// Returns the address with a zeroed host part.
private:
static const char * protocol( bool udp ) ;

View File

@ -75,7 +75,7 @@ public:
// zero-length service name defaults to "0".
virtual void resolveCon( bool success, const Address & address ,
std::string failure_reason ) ;
std::string fqdn_or_failure_reason ) ;
// Called when the resolution process is complete.
// This function is never called from within
// resolveReq().

View File

@ -59,9 +59,9 @@ private:
// ===
GNet::ResolverImp::ResolverImp( Resolver & resolver , unsigned int port ) :
m_s(NULL) ,
m_address(Address::localhost(port)) ,
m_outer(resolver) ,
m_address(Address::localhost(port))
m_s(NULL)
{
}
@ -131,10 +131,14 @@ void GNet::ResolverImp::readEvent()
{
std::string result( buffer , rc ) ;
G_DEBUG( "GNet::ResolverImp::readEvent: \"" << result << "\"" ) ;
G::Str::trimRight( result , " \n" ) ;
if( Address::validString(result) )
G::Str::trim( result , " \n" ) ;
size_t pos = result.find( ' ' ) ;
std::string head = pos == std::string::npos ? result : result.substr(0U,pos) ;
std::string tail = pos == std::string::npos ? std::string() : result.substr(pos+1U) ;
if( Address::validString(head) )
{
m_outer.resolveCon( true , Address(result) , "" ) ;
G::Str::trim( tail , " \n" ) ;
m_outer.resolveCon( true , Address(result) , tail ) ;
}
else
{

View File

@ -45,6 +45,7 @@ private:
bool m_udp ;
Address m_result ;
std::string m_fqdn ;
public:
explicit ResolverImp( Resolver & resolver ) ;
@ -59,11 +60,9 @@ private:
ResolverImp( const ResolverImp & ) ;
const char *errorString( bool host_error , int error ) ;
void cleanup() ;
void saveHost( const Address &address ) ;
void saveHost( const Address &address , const std::string & fqdn ) ;
void saveService( const Address &address ) ;
protected:
LRESULT onUser( WPARAM wparam , LPARAM lparam ) ;
virtual LRESULT onUser( WPARAM wparam , LPARAM lparam ) ;
} ;
// ===
@ -97,7 +96,7 @@ bool GNet::ResolverImp::resolveReq( std::string host_part, std::string service_p
m_service = service_part ;
m_udp = udp ;
m_host_request = new HostRequest( host_part , handle() , WM_USER ) ;
m_host_request = new HostRequest( host_part , handle() , Cracker::wm_user() ) ;
if( !m_host_request->valid() )
{
std::string reason = m_host_request->reason() ; // not used
@ -129,9 +128,10 @@ bool GNet::ResolverImp::busy() const
m_host_request != NULL ;
}
void GNet::ResolverImp::saveHost( const Address & address )
void GNet::ResolverImp::saveHost( const Address & address , const std::string & fqdn )
{
m_result = address ;
m_fqdn = fqdn ;
}
void GNet::ResolverImp::saveService( const Address & address )
@ -148,10 +148,10 @@ LRESULT GNet::ResolverImp::onUser( WPARAM wparam , LPARAM lparam )
{
if( m_host_request->onMessage( wparam , lparam ) )
{
saveHost( m_host_request->result() ) ;
saveHost( m_host_request->result() , m_host_request->fqdn() ) ;
cleanup() ;
m_service_request = new ServiceRequest( m_service , m_udp , handle() , WM_USER ) ;
m_service_request = new ServiceRequest( m_service , m_udp , handle() , Cracker::wm_user() ) ;
if( !m_service_request->valid() )
{
std::string reason = m_service_request->reason() ;
@ -173,7 +173,7 @@ LRESULT GNet::ResolverImp::onUser( WPARAM wparam , LPARAM lparam )
saveService( m_service_request->result() ) ;
Address address( m_result ) ;
cleanup() ;
m_if.resolveCon( true , address , std::string() ) ; // success
m_if.resolveCon( true , address , m_fqdn ) ; // success
}
else
{

View File

@ -23,10 +23,14 @@
#include "gdef.h"
#include "gselect.h"
#include "gstr.h"
#include "gtimer.h"
#include "gdebug.h"
#include <sys/types.h>
#include <sys/time.h>
typedef struct timeval Timeval ; // std:: ??
namespace GNet
{
class Lock ;
@ -83,7 +87,8 @@ GNet::Lock::~Lock()
int GNet::FdSet::init( int n , fd_set * set , const EventHandlerList & list )
{
FD_ZERO( set ) ;
for( EventHandlerList::Iterator p = list.begin() ; p != list.end() ; ++p )
const EventHandlerList::Iterator end = list.end() ;
for( EventHandlerList::Iterator p = list.begin() ; p != end ; ++p )
{
Descriptor fd = EventHandlerList::fd( p ) ;
FD_SET( fd , set ) ;
@ -95,15 +100,16 @@ int GNet::FdSet::init( int n , fd_set * set , const EventHandlerList & list )
//static
void GNet::FdSet::raiseEvents( fd_set * set , EventHandlerList & list ,
void (EventHandler::*method)() , const char * type )
void (EventHandler::*method)() , const char * /*type*/ )
{
GNet::Lock lock( list ) ;
for( EventHandlerList::Iterator p = list.begin() ; p != list.end() ; ++p )
GNet::Lock lock( list ) ; // since event handlers may change the list while we iterate
const EventHandlerList::Iterator end = list.end() ;
for( EventHandlerList::Iterator p = list.begin() ; p != end ; ++p )
{
Descriptor fd = EventHandlerList::fd( p ) ;
if( FD_ISSET( fd , set ) )
{
G_DEBUG( "raiseEvents: " << type << " event on fd " << fd ) ;
//G_DEBUG( "raiseEvents: " << type << " event on fd " << fd ) ;
EventHandler & h = EventHandlerList::handler( p ) ;
(h.*method)() ;
}
@ -113,10 +119,10 @@ void GNet::FdSet::raiseEvents( fd_set * set , EventHandlerList & list ,
// ===
GNet::Select::Select() :
m_quit(false) ,
m_read_list(std::string("read")) ,
m_write_list(std::string("write")) ,
m_exception_list(std::string("exception")) ,
m_quit(false)
m_exception_list(std::string("exception"))
{
}
@ -150,22 +156,40 @@ void GNet::Select::runOnce()
fd_set w ; n = FdSet::init( n , &w , m_write_list ) ;
fd_set e ; n = FdSet::init( n , &e , m_exception_list ) ;
struct timeval * const infinite = NULL ;
Timeval timeout ;
Timeval * timeout_p = NULL ;
if( TimerList::instance(TimerList::NoThrow()) != NULL )
{
bool infinite = false ;
timeout.tv_sec = TimerList::instance().interval( infinite ) ;
timeout.tv_usec = 0 ; // micro seconds
timeout_p = infinite ? NULL : &timeout ;
}
const bool debug = false ;
if( debug )
{
G_DEBUG( "GNet::Select::runOnce: selecting: fd(max) = " << (n-1) << ": "
<< "read-list=\"" << m_read_list.asString() << "\": "
<< "write-list=\"" << m_write_list.asString() << "\": "
<< "exception-list=\"" << m_exception_list.asString() << "\"" ) ;
<< "exception-list=\"" << m_exception_list.asString() << "\": "
<< "timeout=" << (timeout_p?G::Str::fromUInt(timeout_p->tv_sec):std::string("infinite")) ) ;
}
int rc = ::select( n , &r , &w , &e , infinite ) ;
if( rc > 0 )
int rc = ::select( n , &r , &w , &e , timeout_p ) ;
if( rc == 0 )
{
G_DEBUG( "GNet::Select::runOnce: select() timeout" ) ;
TimerList::instance().doTimeouts() ;
}
else if( rc > 0 )
{
G_DEBUG( "GNet::Select::runOnce: detected event(s) on " << rc << " fd(s)" ) ;
FdSet::raiseEvents( &r , m_read_list , & EventHandler::readEvent , "read" ) ;
FdSet::raiseEvents( &w , m_write_list , & EventHandler::writeEvent , "write" ) ;
FdSet::raiseEvents( &e , m_exception_list , & EventHandler::exceptionEvent , "exception" ) ;
}
else if( rc < 0 )
else
{
throw Error() ;
}
@ -201,3 +225,8 @@ void GNet::Select::dropException( Descriptor fd )
m_exception_list.remove( fd ) ;
}
void GNet::Select::setTimeout( G::DateTime::EpochTime )
{
// not used -- interval() in runOnce() suffices
}

View File

@ -79,6 +79,7 @@ private:
Select( const Select & ) ;
void operator=( const Select & ) ;
void runOnce() ;
virtual void setTimeout( G::DateTime::EpochTime t ) ;
private:
bool m_quit ;

View File

@ -27,11 +27,12 @@
#include "gmonitor.h"
#include "gdebug.h"
#include "gassert.h"
#include "gmemory.h"
GNet::ServerPeer::ServerPeer( StreamSocket * s , Address a ) :
m_socket(s) ,
m_ref_count(1U) ,
m_address(a) ,
m_ref_count(1U)
m_socket(s)
{
G_ASSERT( m_socket != NULL ) ;
G_DEBUG( "GNet::ServerPeer::ctor: fd " << m_socket->asString() << ": " << m_address.displayString() ) ;
@ -121,32 +122,31 @@ std::pair<bool,GNet::Address> GNet::ServerPeer::peerAddress() const
// ===
GNet::Server::Server( unsigned int listening_port ) :
m_socket(NULL)
{
try
GNet::Server::Server( unsigned int listening_port )
{
init( listening_port ) ;
}
catch(...)
GNet::Server::Server( const Address & listening_address )
{
delete m_socket ;
throw ;
}
init( listening_address ) ;
}
GNet::Server::Server() :
m_socket(NULL)
GNet::Server::Server()
{
}
void GNet::Server::init( unsigned int listening_port )
{
m_socket = new StreamSocket ;
G_DEBUG( "GNet::Server::init: " << (void*)this << ": listening on port " << listening_port ) ;
Address local_address( listening_port ) ;
if( ! m_socket->bind( local_address ) )
throw CannotBind( local_address.displayString() ) ;
init( Address(listening_port) ) ;
}
void GNet::Server::init( const Address & listening_address )
{
m_socket <<= new StreamSocket ;
G_DEBUG( "GNet::Server::init: listening on " << listening_address.displayString() ) ;
if( ! m_socket->bind( listening_address ) )
throw CannotBind( listening_address.displayString() ) ;
if( ! m_socket->listen() )
throw CannotListen() ;
m_socket->addReadHandler( *this ) ;
@ -154,7 +154,6 @@ void GNet::Server::init( unsigned int listening_port )
GNet::Server::~Server()
{
delete m_socket ;
}
void GNet::Server::readEvent()
@ -162,7 +161,7 @@ void GNet::Server::readEvent()
// read-event-on-listening-port => new connection to accept
G_DEBUG( "GNet::Server::readEvent: " << (void*)this ) ;
G_ASSERT( m_socket != NULL ) ;
G_ASSERT( m_socket.get() != NULL ) ;
AcceptPair pair = m_socket->accept() ;
if( pair.first.get() == NULL )
{

View File

@ -31,6 +31,7 @@
#include "gselect.h"
#include "gevent.h"
#include <list>
#include <memory>
#include <string>
namespace GNet
@ -52,8 +53,12 @@ public:
G_EXCEPTION( CannotListen , "cannot listen" ) ;
explicit Server( unsigned int listening_port ) ;
// Constructor. Throws exceptions on
// error.
// Constructor taking a port number. The server
// listens on all local interfaces.
explicit Server( const Address & listening_address ) ;
// Constructor. The server listens only on the
// specific (local) interface.
Server() ;
// Default constructor. Initialise with init().
@ -61,6 +66,9 @@ public:
void init( unsigned int listening_port ) ;
// Iniailisation after default construction.
void init( const Address & listening_address ) ;
// Iniailisation after default construction.
virtual ~Server() ;
// Destructor.
@ -90,7 +98,7 @@ private:
virtual void exceptionEvent() ; // from EventHandler
private:
StreamSocket * m_socket ;
std::auto_ptr<StreamSocket> m_socket ;
} ;
// Class: GNet::ServerPeer

View File

@ -57,8 +57,8 @@ bool GNet::Socket::open( int domain, int type, int protocol )
}
GNet::Socket::Socket( Descriptor s ) :
m_socket(s) ,
m_reason( 0 )
m_reason(0) ,
m_socket(s)
{
;
}
@ -168,7 +168,7 @@ ssize_t GNet::Socket::write( const char *buf, size_t len )
G_DEBUG( "GNet::Socket::write: write error " << m_reason ) ;
return -1 ;
}
else if( nsent < len )
else if( nsent < 0 || static_cast<size_t>(nsent) < len )
{
m_reason = reason() ;
}
@ -183,7 +183,7 @@ void GNet::Socket::setNoLinger()
options.l_onoff = 0 ;
options.l_linger = 0 ;
socklen_t sizeof_options = sizeof(options) ;
(void)setsockopt( m_socket , SOL_SOCKET , SO_LINGER ,
(void)::setsockopt( m_socket , SOL_SOCKET , SO_LINGER ,
(char*)&options , sizeof_options ) ;
}

230
src/gnet/gtimer.cpp Normal file
View File

@ -0,0 +1,230 @@
//
// 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.
//
// ===
//
// gtimer.cpp
//
#include "gdef.h"
#include "gtimer.h"
#include "gevent.h"
#include "gdebug.h"
namespace GNet
{
class TimerUpdate ;
} ;
// Class: GNet::TimerUpdate
// Description: A private implementation class used by GNet::Timer.
//
class GNet::TimerUpdate
{
public:
TimerUpdate( Timer & , const std::string & ) ;
~TimerUpdate() ;
private:
TimerUpdate( const TimerUpdate & ) ; // not implemented
void operator=( const TimerUpdate & ) ; // not implemented
private:
Timer & m_timer ;
std::string m_type ;
G::DateTime::EpochTime m_soonest ;
} ;
// ===
GNet::TimeoutHandler::~TimeoutHandler()
{
}
// ===
GNet::Timer::Timer( TimeoutHandler & handler ) :
m_time(0UL) ,
m_handler(&handler)
{
TimerUpdate update( *this , "ctor" ) ;
TimerList::instance().add( *this ) ;
}
GNet::Timer::Timer() :
m_time(0UL) ,
m_handler(NULL)
{
TimerUpdate update( *this , "ctor" ) ;
TimerList::instance().add( *this ) ;
}
GNet::Timer::~Timer()
{
try
{
TimerUpdate update( *this , "dtor" ) ;
TimerList::instance().remove( *this ) ;
}
catch(...)
{
}
}
void GNet::Timer::startTimer( unsigned int time )
{
TimerUpdate update( *this , "start" ) ;
m_time = G::DateTime::now() + time ;
}
void GNet::Timer::cancelTimer()
{
TimerUpdate update( *this , "cancel" ) ;
m_time = 0U ;
}
void GNet::Timer::doTimeout()
{
if( m_time != 0U )
{
m_time = 0U ;
G_DEBUG( "GNet::Timer::doTimeout" ) ;
onTimeout() ;
if( m_handler != NULL )
m_handler->onTimeout(*this) ;
}
}
void GNet::Timer::onTimeout()
{
// no-op
}
G::DateTime::EpochTime GNet::Timer::t() const
{
return m_time ;
}
// ===
GNet::TimerList * GNet::TimerList::m_this = NULL ;
GNet::TimerList::TimerList()
{
if( m_this == NULL )
m_this = this ;
}
GNet::TimerList::~TimerList()
{
if( m_this == this )
m_this = NULL ;
}
void GNet::TimerList::add( Timer & t )
{
m_set.insert( &t ) ;
}
void GNet::TimerList::remove( Timer & t )
{
m_set.erase( &t ) ;
}
void GNet::TimerList::update( G::DateTime::EpochTime t_old ,
const std::string & op )
{
G::DateTime::EpochTime t_new = soonest() ;
G_DEBUG( "GNet::TimerList::update: " << op << ": " << t_old << " -> " << t_new ) ;
(void) op.length() ; // pacify the compiler
if( t_old != t_new )
{
EventSources::instance().setTimeout( t_new ) ;
}
}
G::DateTime::EpochTime GNet::TimerList::soonest() const
{
G::DateTime::EpochTime result = 0U ;
const Set::const_iterator end = m_set.end() ;
for( Set::const_iterator p = m_set.begin() ; p != end ; ++p )
{
if( (*p)->t() != 0UL && ( result == 0U || (*p)->t() < result ) )
result = (*p)->t() ;
}
return result ;
}
unsigned int GNet::TimerList::interval( bool & infinite ) const
{
G::DateTime::EpochTime then = soonest() ;
infinite = then == 0U ;
if( infinite )
{
return 0U ;
}
else
{
G::DateTime::EpochTime now = G::DateTime::now() ;
return now >= then ? 0U : (then-now) ;
}
}
GNet::TimerList * GNet::TimerList::instance( const NoThrow & )
{
return m_this ;
}
GNet::TimerList & GNet::TimerList::instance()
{
if( m_this == NULL )
throw NoInstance() ;
return * m_this ;
}
void GNet::TimerList::doTimeouts()
{
G_DEBUG( "GNet::TimerList::doTimeouts" ) ;
G::DateTime::EpochTime now = G::DateTime::now() ;
for( Set::iterator p = m_set.begin() ; p != m_set.end() ; ++p )
{
if( now >= (*p)->t() )
(*p)->doTimeout() ;
}
EventSources::instance().setTimeout( soonest() ) ;
}
// ===
GNet::TimerUpdate::TimerUpdate( Timer & timer , const std::string & type ) :
m_timer(timer) ,
m_type(type)
{
m_soonest = TimerList::instance().soonest() ;
}
GNet::TimerUpdate::~TimerUpdate()
{
try
{
TimerList::instance().update( m_soonest , m_type ) ;
}
catch(...)
{
}
}

156
src/gnet/gtimer.h Normal file
View File

@ -0,0 +1,156 @@
//
// 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.
//
// ===
//
// gtimer.h
//
#ifndef G_NET_TIMER_H
#define G_NET_TIMER_H
#include "gdef.h"
#include "gnet.h"
#include "gdatetime.h"
#include "gexception.h"
#include <set>
namespace GNet
{
class Timer ;
class TimeoutHandler ;
class TimerList ;
} ;
// Class: GNet::TimeoutHandler
// Description: An interface used by GNet::Timer.
//
class GNet::TimeoutHandler
{
public:
virtual ~TimeoutHandler() ;
// Destructor.
virtual void onTimeout( Timer & ) = 0 ;
// Called when the associated timer
// expires.
private:
void operator=( const TimeoutHandler & ) ; // not implemented
} ;
// Class: GNet::Timer
// Description: A timer class.
//
class GNet::Timer
{
public:
explicit Timer( TimeoutHandler & handler ) ;
// Constructor.
Timer() ;
// Default constructor.
virtual ~Timer() ;
// Destructor.
void startTimer( unsigned int time ) ;
// Starts the timer.
void cancelTimer() ;
// Cancels the timer.
protected:
virtual void onTimeout() ;
// Called when the timer expires (or soon
// after).
private:
Timer( const Timer & ) ; // not implemented
void operator=( const Timer & ) ; // not implemented
private:
friend class TimerList ;
void doTimeout() ; // called by friendly TimerList
G::DateTime::EpochTime t() const ; // called by friendly TimerList
private:
G::DateTime::EpochTime m_time ;
TimeoutHandler * m_handler ;
} ;
// Class: GNet::TimerList
// Description: A singleton which maintains a list of all Timer
// objects, and interfaces to the event loop on their behalf.
//
class GNet::TimerList
{
public:
G_EXCEPTION( NoInstance , "no TimerList instance" ) ;
class NoThrow // Overload discriminator class for TimerList.
{} ;
TimerList() ;
// Default constructor.
~TimerList() ;
// Destructor.
void add( Timer & ) ;
// Adds a timer. Used by Timer::Timer().
void remove( Timer & ) ;
// Removes a timer from the list.
// Used by Timer::~Timer().
void update( G::DateTime::EpochTime previous_soonest ,
const std::string & why ) ;
// Called when one of the list's timers
// has changed.
G::DateTime::EpochTime soonest() const ;
// Returns the time of the first timer to expire,
// or zero if none.
unsigned int interval( bool & infinite ) const ;
// Returns the interval to the next
// timer expiry. The 'infinite' value is
// set to true if there are no timers
// running.
void doTimeouts() ;
// Triggers the timeout callbacks of any expired
// timers. Called by the event loop (GNet::EventSources).
static TimerList * instance( const NoThrow & ) ;
// Singleton access. Returns NULL if none.
static TimerList & instance() ;
// Singleton access. Throws an exception if none.
private:
TimerList( const TimerList & ) ; // not implemented
void operator=( const TimerList & ) ; // not implemented
private:
static TimerList * m_this ;
typedef std::set<Timer* GLessAllocator(Timer*,Timer*) > Set ;
Set m_set ;
} ;
#endif

View File

@ -27,6 +27,8 @@
#include "gappinst.h"
#include "gwinhid.h"
#include "gwinsock.h"
#include "gexception.h"
#include "gtimer.h"
#include "gassert.h"
#include "gdebug.h"
#include "glog.h"
@ -45,7 +47,8 @@ class GNet::WinsockWindow : public GGui::WindowHidden
public:
WinsockWindow( Winsock & ws , HINSTANCE h ) ;
private:
virtual LRESULT onUser( WPARAM , LPARAM ) ;
virtual void onWinsock( WPARAM , LPARAM ) ;
virtual void onTimer( unsigned int ) ;
Winsock & m_ws ;
} ;
@ -55,11 +58,15 @@ GNet::WinsockWindow::WinsockWindow( Winsock & ws , HINSTANCE hinstance ) :
{
}
// (wm_winsock is WM_USER -- should be called onWinsock())
LRESULT GNet::WinsockWindow::onUser( WPARAM w , LPARAM l )
void GNet::WinsockWindow::onWinsock( WPARAM w , LPARAM l )
{
m_ws.onMessage( w , l ) ;
return 0 ;
}
void GNet::WinsockWindow::onTimer( unsigned int timer_id )
{
G_DEBUG( "GNet::WinsockWindow::onTimer: " << timer_id ) ;
m_ws.onTimer() ;
}
// ===
@ -71,7 +78,8 @@ GNet::Winsock::Winsock() :
m_exception_list("exception") ,
m_success(false) ,
m_hwnd(0) ,
m_msg(0)
m_msg(0) ,
m_timer_id(1U)
{
}
@ -95,13 +103,14 @@ bool GNet::Winsock::init()
G_WARNING( "GNet::Winsock::init: cannot create hidden window" ) ;
return false ;
}
return attach( m_window->handle() , GGui::Cracker::wm_winsock() ) ;
return attach( m_window->handle() , GGui::Cracker::wm_winsock() , 1U ) ;
}
bool GNet::Winsock::attach( HWND hwnd , unsigned msg )
bool GNet::Winsock::attach( HWND hwnd , unsigned msg , unsigned int timer_id )
{
m_hwnd = hwnd ;
m_msg = msg ;
m_timer_id = timer_id ;
WSADATA info ;
WORD version = MAKEWORD( 1 , 1 ) ;
@ -248,6 +257,36 @@ void GNet::Winsock::onMessage( WPARAM wparam , LPARAM lparam )
}
}
void GNet::Winsock::onTimer()
{
G_DEBUG( "GNet::Winsock::onTimer" ) ;
::KillTimer( m_hwnd , m_timer_id ) ; // since periodic
TimerList::instance().doTimeouts() ;
}
void GNet::Winsock::setTimeout( G::DateTime::EpochTime t )
{
G_DEBUG( "GNet::Winsock::setTimeout: " << t ) ;
if( t != 0U )
{
G::DateTime::EpochTime now = G::DateTime::now() ;
unsigned int interval = t > now ? (t - now) : 0U ;
unsigned long ms = interval ;
ms *= 1000UL ;
G_DEBUG( "GNet::Winsock::setTimeout: SetTimer(): " << ms << "ms" ) ;
::KillTimer( m_hwnd , m_timer_id ) ;
unsigned int rc = ::SetTimer( m_hwnd , m_timer_id , ms , NULL ) ;
if( rc == 0U )
throw G::Exception( "GNet::Winsock: SetTimer() failure" ) ;
G_ASSERT( rc == m_timer_id ) ;
}
else
{
G_DEBUG( "GNet::Winsock::setTimeout: KillTimer()" ) ;
::KillTimer( m_hwnd , m_timer_id ) ;
}
}
void GNet::Winsock::run()
{
GGui::Pump::run() ;

View File

@ -59,8 +59,11 @@ public:
// WinSock library, passing it the handle
// of an internally-created hidden window.
// Returns false on error.
//
// Use either init() for an internally-created
// window, or attach().
bool attach( HWND hwnd , unsigned int msg ) ;
bool attach( HWND hwnd , unsigned int msg , unsigned int timer_id = 0U ) ;
// Initialises the WinSock library, passing
// it the specified window handle and
// message number. WinSock events are sent
@ -68,6 +71,10 @@ public:
//
// For simple, synchronous programs
// the window handle may be zero.
//
// Use either init() or attach().
//
// See also onMessage() and onTimer().
std::string reason() const ;
// Returns the reason for initialisation
@ -83,9 +90,13 @@ public:
// if this is the last Winsock object.
void onMessage( WPARAM wparam , LPARAM lparam ) ;
// To be called on receipt of a window
// message corresponding to the constructor's
// 'msg' parameter.
// To be called when the attach()ed window
// receives a message with a message-id
// equal to attach() 'msg' parameter.
void onTimer() ;
// To be called when the attach()ed window
// receives a WM_TIMER message.
virtual void run() ;
// Override from EventSources. Calls GGui::Pump::run().
@ -100,6 +111,7 @@ protected:
virtual void dropRead( Descriptor fd ) ;
virtual void dropWrite( Descriptor fd ) ;
virtual void dropException( Descriptor fd ) ;
virtual void setTimeout( G::DateTime::EpochTime ) ;
private:
Winsock( const Winsock & other ) ;
@ -119,6 +131,7 @@ private:
EventHandlerList m_read_list ;
EventHandlerList m_write_list ;
EventHandlerList m_exception_list ;
unsigned int m_timer_id ;
} ;
#endif

View File

@ -24,7 +24,7 @@ AM_INSTALL_PROGRAM_FLAGS=-s
# change the local-state directory from .../var to .../var/spool/emailrelay
localstatedir = ${prefix}/var/spool/emailrelay
EXTRA_DIST=commandline_win32.cpp main_win32.cpp gmessagestore_win32.cpp emailrelay.dsp icon-32.ico icon2.ico icon3.ico empty_file doxygen.cfg emailrelay.rc resource.h
EXTRA_DIST=commandline_win32.cpp main_win32.cpp gmessagestore_win32.cpp emailrelay.dsp icon-32.ico icon2.ico icon3.ico icon4.ico empty_file doxygen.cfg emailrelay.rc resource.h gsasl_cyrus.cpp
INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib -I$(top_srcdir)/src/gnet
sbin_PROGRAMS = emailrelay
libexec_PROGRAMS = emailrelay-poke
@ -32,6 +32,8 @@ localstate_DATA = empty_file
emailrelay_SOURCES = \
gadminserver.cpp \
gadminserver.h \
gbase64.cpp \
gbase64.h \
gclientprotocol.cpp \
gclientprotocol.h \
gfilestore.cpp \
@ -49,6 +51,10 @@ emailrelay_SOURCES = \
gprotocolmessageforward.h \
gprotocolmessagestore.cpp \
gprotocolmessagestore.h \
gsasl_login.cpp \
gsasl.h \
gsecrets.cpp \
gsecrets.h \
gserverprotocol.cpp \
gserverprotocol.h \
gsmtp.h \
@ -62,6 +68,8 @@ emailrelay_SOURCES = \
gstoredmessage.h \
gverifier.cpp \
gverifier.h \
gxtext.cpp \
gxtext.h \
main_unix.cpp \
configuration.cpp \
configuration.h \

View File

@ -1,6 +1,6 @@
# Makefile.in generated automatically by automake 1.4 from Makefile.am
# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -95,12 +95,12 @@ AM_INSTALL_PROGRAM_FLAGS = -s
# change the local-state directory from .../var to .../var/spool/emailrelay
localstatedir = ${prefix}/var/spool/emailrelay
EXTRA_DIST = commandline_win32.cpp main_win32.cpp gmessagestore_win32.cpp emailrelay.dsp icon-32.ico icon2.ico icon3.ico empty_file doxygen.cfg emailrelay.rc resource.h
EXTRA_DIST = commandline_win32.cpp main_win32.cpp gmessagestore_win32.cpp emailrelay.dsp icon-32.ico icon2.ico icon3.ico icon4.ico empty_file doxygen.cfg emailrelay.rc resource.h gsasl_cyrus.cpp
INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib -I$(top_srcdir)/src/gnet
sbin_PROGRAMS = emailrelay
libexec_PROGRAMS = emailrelay-poke
localstate_DATA = empty_file
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 main_unix.cpp configuration.cpp configuration.h run.cpp run.h commandline.cpp commandline_unix.cpp commandline.h
emailrelay_SOURCES = gadminserver.cpp gadminserver.h gbase64.cpp gbase64.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 gsasl_login.cpp gsasl.h gsecrets.cpp gsecrets.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 gxtext.cpp gxtext.h main_unix.cpp configuration.cpp configuration.h run.cpp run.h commandline.cpp commandline_unix.cpp commandline.h
emailrelay_poke_SOURCES = poke.c
emailrelay_LDADD = $(top_builddir)/src/glib/libglib.a $(top_builddir)/src/gnet/libgnet.a
@ -118,12 +118,13 @@ emailrelay_poke_OBJECTS = poke.o
emailrelay_poke_LDADD = $(LDADD)
emailrelay_poke_DEPENDENCIES =
emailrelay_poke_LDFLAGS =
emailrelay_OBJECTS = gadminserver.o gclientprotocol.o gfilestore.o \
gmessagestore.o gmessagestore_unix.o gnewfile.o gnewmessage.o \
gprotocolmessage.o gprotocolmessageforward.o gprotocolmessagestore.o \
gserverprotocol.o gsmtpclient.o gsmtpserver.o gstoredfile.o \
gstoredmessage.o gverifier.o main_unix.o configuration.o run.o \
commandline.o commandline_unix.o
emailrelay_OBJECTS = gadminserver.o gbase64.o gclientprotocol.o \
gfilestore.o gmessagestore.o gmessagestore_unix.o gnewfile.o \
gnewmessage.o gprotocolmessage.o gprotocolmessageforward.o \
gprotocolmessagestore.o gsasl_login.o gsecrets.o gserverprotocol.o \
gsmtpclient.o gsmtpserver.o gstoredfile.o gstoredmessage.o gverifier.o \
gxtext.o main_unix.o configuration.o run.o commandline.o \
commandline_unix.o
emailrelay_DEPENDENCIES = $(top_builddir)/src/glib/libglib.a \
$(top_builddir)/src/gnet/libgnet.a
emailrelay_LDFLAGS =
@ -293,7 +294,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
@ -333,14 +334,22 @@ gadminserver.o: gadminserver.cpp ../../src/glib/gdef.h ../../config.h \
gadminserver.h ../../src/gnet/gserver.h \
../../src/gnet/gsocket.h ../../src/gnet/gaddress.h \
../../src/glib/gexception.h ../../src/gnet/gevent.h \
../../src/gnet/gdescriptor.h ../../src/gnet/gconnection.h \
../../src/gnet/gselect.h ../../src/gnet/glinebuffer.h \
../../src/glib/gstrings.h gserverprotocol.h gprotocolmessage.h \
gverifier.h gsmtpclient.h ../../src/gnet/gclient.h \
gclientprotocol.h gmessagestore.h gnewmessage.h \
gstoredmessage.h ../../src/glib/gpath.h \
../../src/glib/gdatetime.h ../../src/gnet/gdescriptor.h \
../../src/gnet/gconnection.h ../../src/gnet/gselect.h \
../../src/gnet/glinebuffer.h ../../src/glib/gstrings.h \
gserverprotocol.h gprotocolmessage.h gverifier.h gsasl.h \
gsecrets.h ../../src/glib/gpath.h gsmtpclient.h \
../../src/gnet/gclient.h gclientprotocol.h gmessagestore.h \
gnewmessage.h gstoredmessage.h ../../src/gnet/gtimer.h \
../../src/gnet/gmonitor.h ../../src/glib/gstr.h \
../../src/glib/gmemory.h
gbase64.o: gbase64.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 gbase64.h \
../../src/glib/gexception.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h ../../src/glib/gstr.h \
../../src/glib/gstrings.h
gclientprotocol.o: gclientprotocol.cpp ../../src/glib/gdef.h \
../../config.h ../../lib/gcc2.95/iostream \
../../lib/gcc2.95/sstream ../../lib/gcc2.95/xlocale \
@ -348,9 +357,11 @@ gclientprotocol.o: gclientprotocol.cpp ../../src/glib/gdef.h \
../../src/glib/glog.h ../../src/gnet/glocal.h \
../../src/gnet/gaddress.h ../../src/glib/gexception.h \
../../src/glib/gfile.h ../../src/glib/gpath.h \
../../src/glib/gstrings.h ../../src/glib/gstr.h \
../../src/glib/gmemory.h gclientprotocol.h gmessagestore.h \
gnewmessage.h gstoredmessage.h ../../src/gnet/gresolve.h \
../../src/glib/gstrings.h gsasl.h gsecrets.h gbase64.h \
../../src/glib/gstr.h ../../src/glib/gmemory.h gxtext.h \
gclientprotocol.h gmessagestore.h gnewmessage.h \
gstoredmessage.h ../../src/gnet/gtimer.h \
../../src/glib/gdatetime.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 \
@ -385,7 +396,7 @@ gnewfile.o: gnewfile.cpp ../../src/glib/gdef.h ../../config.h \
../../src/glib/gpath.h ../../src/glib/gexception.h gnewfile.h \
gfilestore.h ../../src/glib/gdatetime.h \
../../src/glib/gmemory.h ../../src/glib/gprocess.h \
../../src/glib/gfile.h ../../src/glib/gassert.h \
../../src/glib/gfile.h gxtext.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 \
@ -405,13 +416,14 @@ gprotocolmessageforward.o: gprotocolmessageforward.cpp \
../../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 \
gprotocolmessagestore.h gnewmessage.h gsmtpclient.h gsecrets.h \
../../src/glib/gpath.h ../../src/glib/gexception.h \
../../src/gnet/glinebuffer.h ../../src/gnet/gclient.h \
../../src/gnet/gaddress.h ../../src/glib/gexception.h \
../../src/gnet/gconnection.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/gnet/gaddress.h ../../src/gnet/gconnection.h \
../../src/gnet/gsocket.h ../../src/gnet/gevent.h \
../../src/glib/gdatetime.h ../../src/gnet/gdescriptor.h \
gclientprotocol.h gmessagestore.h gstoredmessage.h gsasl.h \
../../src/gnet/gtimer.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 \
@ -424,13 +436,28 @@ gprotocolmessagestore.o: gprotocolmessagestore.cpp ../../src/glib/gdef.h \
../../src/glib/gpath.h ../../src/glib/gexception.h \
../../src/glib/gmemory.h ../../src/glib/gstr.h \
../../src/glib/gassert.h ../../src/glib/glogoutput.h
gsasl_login.o: gsasl_login.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 gsasl.h gsecrets.h \
../../src/glib/gpath.h ../../src/glib/gstrings.h \
../../src/glib/gexception.h ../../src/glib/gstr.h \
../../src/glib/gmemory.h ../../src/glib/gdebug.h \
../../src/glib/glogoutput.h ../../src/glib/gassert.h
gsecrets.o: gsecrets.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 gsecrets.h \
../../src/glib/gpath.h ../../src/glib/gstrings.h \
../../src/glib/gexception.h gxtext.h ../../src/glib/gstr.h
gserverprotocol.o: gserverprotocol.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 gserverprotocol.h gprotocolmessage.h \
../../src/glib/gstrings.h gverifier.h ../../src/glib/gdate.h \
../../src/glib/gdatetime.h ../../src/glib/gexception.h \
../../src/glib/gstrings.h gverifier.h gsasl.h gsecrets.h \
../../src/glib/gpath.h ../../src/glib/gexception.h gbase64.h \
../../src/glib/gdate.h ../../src/glib/gdatetime.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/gassert.h ../../src/glib/gtime.h \
../../src/glib/gstr.h
@ -441,26 +468,29 @@ gsmtpclient.o: gsmtpclient.cpp ../../src/glib/gdef.h ../../config.h \
../../src/gnet/glocal.h ../../src/gnet/gaddress.h \
../../src/glib/gexception.h ../../src/glib/gfile.h \
../../src/glib/gpath.h ../../src/glib/gstrings.h \
../../src/glib/gstr.h ../../src/glib/gmemory.h gsmtpclient.h \
../../src/gnet/glinebuffer.h ../../src/gnet/gclient.h \
../../src/gnet/gconnection.h ../../src/gnet/gsocket.h \
../../src/gnet/gevent.h ../../src/gnet/gdescriptor.h \
gclientprotocol.h gmessagestore.h gnewmessage.h \
gstoredmessage.h ../../src/gnet/gresolve.h \
../../src/glib/gassert.h ../../src/glib/glogoutput.h
../../src/glib/gstr.h ../../src/glib/gmemory.h \
../../src/gnet/gtimer.h ../../src/glib/gdatetime.h \
gsmtpclient.h gsecrets.h ../../src/gnet/glinebuffer.h \
../../src/gnet/gclient.h ../../src/gnet/gconnection.h \
../../src/gnet/gsocket.h ../../src/gnet/gevent.h \
../../src/gnet/gdescriptor.h gclientprotocol.h gmessagestore.h \
gnewmessage.h gstoredmessage.h gsasl.h \
../../src/gnet/gresolve.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h
gsmtpserver.o: gsmtpserver.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 gsmtpserver.h \
../../src/gnet/gserver.h ../../src/gnet/gsocket.h \
../../src/gnet/gaddress.h ../../src/glib/gexception.h \
../../src/gnet/gevent.h ../../src/gnet/gdescriptor.h \
../../src/gnet/gconnection.h ../../src/gnet/gselect.h \
../../src/gnet/glinebuffer.h ../../src/glib/gstrings.h \
gverifier.h gserverprotocol.h gprotocolmessage.h \
../../src/gnet/gevent.h ../../src/glib/gdatetime.h \
../../src/gnet/gdescriptor.h ../../src/gnet/gconnection.h \
../../src/gnet/gselect.h ../../src/gnet/glinebuffer.h \
../../src/glib/gstrings.h gverifier.h gserverprotocol.h \
gprotocolmessage.h gsasl.h gsecrets.h ../../src/glib/gpath.h \
gprotocolmessagestore.h gnewmessage.h gprotocolmessageforward.h \
gsmtpclient.h ../../src/gnet/gclient.h gclientprotocol.h \
gmessagestore.h gstoredmessage.h ../../src/glib/gpath.h \
gmessagestore.h gstoredmessage.h ../../src/gnet/gtimer.h \
../../src/glib/gmemory.h ../../src/gnet/glocal.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/gassert.h
@ -471,9 +501,9 @@ gstoredfile.o: gstoredfile.cpp ../../src/glib/gdef.h ../../config.h \
gmessagestore.h gnewmessage.h gstoredmessage.h \
../../src/glib/gstrings.h ../../src/glib/gpath.h \
../../src/glib/gexception.h ../../src/glib/gdatetime.h \
gstoredfile.h ../../src/glib/gmemory.h ../../src/glib/gfile.h \
../../src/glib/gstr.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h
gstoredfile.h ../../src/glib/gmemory.h gxtext.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 \
@ -487,6 +517,11 @@ gverifier.o: gverifier.cpp ../../src/glib/gdef.h ../../config.h \
../../src/glib/gstr.h ../../src/glib/gexception.h \
../../src/glib/gstrings.h ../../src/glib/gassert.h \
../../src/glib/glogoutput.h
gxtext.o: gxtext.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 gxtext.h \
../../src/glib/gassert.h ../../src/glib/glogoutput.h
main_unix.o: main_unix.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 \
@ -494,12 +529,13 @@ main_unix.o: main_unix.cpp ../../src/glib/gdef.h ../../config.h \
../../src/glib/garg.h run.h configuration.h \
../../src/glib/gpath.h ../../src/glib/gstrings.h commandline.h \
../../src/glib/ggetopt.h ../../src/glib/gexception.h \
../../src/gnet/gevent.h ../../src/gnet/gdescriptor.h \
../../src/glib/gdaemon.h gmessagestore.h gnewmessage.h \
gstoredmessage.h gsmtpclient.h ../../src/gnet/glinebuffer.h \
../../src/gnet/gevent.h ../../src/glib/gdatetime.h \
../../src/gnet/gdescriptor.h ../../src/glib/gdaemon.h \
gmessagestore.h gnewmessage.h gstoredmessage.h gsmtpclient.h \
gsecrets.h ../../src/gnet/glinebuffer.h \
../../src/gnet/gclient.h ../../src/gnet/gaddress.h \
../../src/gnet/gconnection.h ../../src/gnet/gsocket.h \
gclientprotocol.h
gclientprotocol.h gsasl.h ../../src/gnet/gtimer.h
poke.o: poke.c
run.o: run.cpp ../../src/glib/gdef.h ../../config.h \
../../lib/gcc2.95/iostream ../../lib/gcc2.95/sstream \
@ -508,15 +544,16 @@ run.o: run.cpp ../../src/glib/gdef.h ../../config.h \
configuration.h ../../src/glib/gpath.h \
../../src/glib/gstrings.h commandline.h ../../src/glib/garg.h \
../../src/glib/ggetopt.h ../../src/glib/gexception.h \
../../src/gnet/gevent.h ../../src/gnet/gdescriptor.h \
../../src/glib/gdaemon.h gmessagestore.h gnewmessage.h \
gstoredmessage.h gsmtpclient.h ../../src/gnet/glinebuffer.h \
../../src/gnet/gevent.h ../../src/glib/gdatetime.h \
../../src/gnet/gdescriptor.h ../../src/glib/gdaemon.h \
gmessagestore.h gnewmessage.h gstoredmessage.h gsmtpclient.h \
gsecrets.h ../../src/gnet/glinebuffer.h \
../../src/gnet/gclient.h ../../src/gnet/gaddress.h \
../../src/gnet/gconnection.h ../../src/gnet/gsocket.h \
gclientprotocol.h gsmtpserver.h ../../src/gnet/gserver.h \
../../src/gnet/gselect.h gverifier.h gserverprotocol.h \
gprotocolmessage.h gfilestore.h ../../src/glib/gdatetime.h \
gnewfile.h gadminserver.h ../../src/gnet/gmonitor.h \
gclientprotocol.h gsasl.h ../../src/gnet/gtimer.h gsmtpserver.h \
../../src/gnet/gserver.h ../../src/gnet/gselect.h gverifier.h \
gserverprotocol.h gprotocolmessage.h gfilestore.h gnewfile.h \
gadminserver.h ../../src/gnet/gmonitor.h \
../../src/glib/gprocess.h ../../src/glib/gmemory.h \
../../src/glib/gdebug.h ../../src/glib/glogoutput.h \
../../src/glib/gassert.h

View File

@ -36,6 +36,8 @@ std::string Main::CommandLine::switchSpec()
std::stringstream ss ;
ss
<< osSwitchSpec() << "|"
<< "C!client-auth!enables authentication with remote server, using the given secrets file!1!file|"
<< "S!server-auth!enables authentication of remote clients, using the given secrets file!1!file|"
<< "y!as-proxy!equivalent to \"--log --close-stderr --immediate --forward-to\"!1!host:port|"
<< "e!close-stderr!closes the standard error stream after start-up!0!|"
<< "a!admin!enables the administration interface and specifies its listening port number!1!admin-port|"
@ -44,6 +46,8 @@ std::string Main::CommandLine::switchSpec()
<< "f!forward!forwards stored mail on startup (requires --forward-to)!0!|"
<< "o!forward-to!specifies the remote smtp server (required by --forward and --admin)!1!host:port|"
<< "h!help!displays help text and exits!0!|"
<< "T!response-timeout!sets the client-side response timeout in seconds (default is 1800)!1!time|"
<< "U!connection-timeout!sets the client-side connection timeout in seconds (default is 40)!1!time|"
<< "m!immediate!forwards each message as soon as it is received (requires --forward-to)!0!|"
<< "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|"
@ -81,7 +85,7 @@ void Main::CommandLine::showUsage( bool e ) const
{
Show show( e ) ;
unsigned int columns = ttyColumns() ;
m_getopt.showUsage( show.s() , m_arg.prefix() , "" , 30U , columns ) ;
m_getopt.showUsage( show.s() , m_arg.prefix() , "" , 33U , columns ) ;
}
bool Main::CommandLine::contains( const std::string & name ) const
@ -108,6 +112,14 @@ std::string Main::CommandLine::semanticError() const
"be an absolute path" ;
}
if( cfg().daemon() && (
( !cfg().clientSecretsFile().empty() && G::Path(cfg().clientSecretsFile()).isRelative() ) ||
( !cfg().serverSecretsFile().empty() && G::Path(cfg().serverSecretsFile()).isRelative() ) ) )
{
return "in daemon mode the authorisation secrets file(s) must "
"be absolute paths" ;
}
if( !m_getopt.contains("forward-to") && (
m_getopt.contains("forward") ||
m_getopt.contains("immediate") ||

View File

@ -30,8 +30,9 @@ Main::CommandLine::Show * Main::CommandLine::Show::m_this = NULL ;
//static
std::string Main::CommandLine::osSwitchSpec()
{
// (could use empty descriptions here so that G::GetOpt does
// not put them in the --help listing)
// (could use empty descriptions for some switches so that they
// do not appear in the "--help" listing, but that might be
// confusing)
std::stringstream ss ;
ss
@ -40,12 +41,19 @@ std::string Main::CommandLine::osSwitchSpec()
<< "n!no-syslog!has no effect on windows!0!|"
<< "q!as-client!equivalent to \"--log --no-daemon --dont-serve --forward --forward-to\"!" << "1!host:port|"
<< "d!as-server!equivalent to \"--log --close-stderr\" (has little effect on windows)!0!|"
<< "I!icon!chooses the application icon!1!icon index {0,1,2}"
<< "I!icon!selects the application icon!1!0^|1^|2^|3"
;
return ss.str() ;
}
unsigned int Main::CommandLine::ttyColumns() const
{
return 120U ;
}
// ===
Main::CommandLine::Show::Show( bool )
{
if( m_this == NULL )
@ -68,9 +76,3 @@ Main::CommandLine::Show::~Show()
}
}
unsigned int Main::CommandLine::ttyColumns() const
{
return 120U ;
}

View File

@ -173,6 +173,36 @@ std::string Main::Configuration::filter() const
unsigned int Main::Configuration::icon() const
{
return m_cl.contains("icon") ? G::Str::toUInt(m_cl.value("icon")) : 0U ;
unsigned int n = 0U ;
if( m_cl.contains("icon") )
{
n = G::Str::toUInt(m_cl.value("icon")) ;
n %= 4U ;
}
return n ;
}
std::string Main::Configuration::clientSecretsFile() const
{
return m_cl.contains("client-auth") ? m_cl.value("client-auth") : std::string() ;
}
std::string Main::Configuration::serverSecretsFile() const
{
return m_cl.contains("server-auth") ? m_cl.value("server-auth") : std::string() ;
}
unsigned int Main::Configuration::responseTimeout() const
{
const unsigned int default_timeout = 30U * 60U ;
return m_cl.contains("response-timeout") ?
G::Str::toUInt(m_cl.value("response-timeout")) : default_timeout ;
}
unsigned int Main::Configuration::connectionTimeout() const
{
const unsigned int default_timeout = 40U ;
return m_cl.contains("connection-timeout") ?
G::Str::toUInt(m_cl.value("connection-timeout")) : default_timeout ;
}

View File

@ -109,6 +109,20 @@ public:
unsigned int icon() const ;
// Returns the icon selector.
unsigned int responseTimeout() const ;
// Returns the client-side protocol timeout value.
unsigned int connectionTimeout() const ;
// Returns the client-side connection timeout value.
std::string clientSecretsFile() const ;
// Returns the client-side autentication secrets (password) file.
// Returns the empty string if none.
std::string serverSecretsFile() const ;
// Returns the server-side autentication secrets (password) file.
// Returns the empty string if none.
private:
const CommandLine & m_cl ;

View File

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

View File

@ -127,6 +127,10 @@ SOURCE=..\..\src\glib\garg_win32.cpp
# End Source File
# Begin Source File
SOURCE=.\gbase64.cpp
# End Source File
# Begin Source File
SOURCE=..\..\src\gnet\gclient.cpp
# End Source File
# Begin Source File
@ -307,10 +311,18 @@ SOURCE=..\..\src\gnet\gresolve_win32.cpp
# End Source File
# Begin Source File
SOURCE=.\gsasl_login.cpp
# End Source File
# Begin Source File
SOURCE=..\win32\gscmap.cpp
# End Source File
# Begin Source File
SOURCE=.\gsecrets.cpp
# End Source File
# Begin Source File
SOURCE=..\gnet\gserver.cpp
# End Source File
# Begin Source File
@ -351,6 +363,10 @@ SOURCE=..\..\src\glib\gtime.cpp
# End Source File
# Begin Source File
SOURCE=..\gnet\gtimer.cpp
# End Source File
# Begin Source File
SOURCE=..\win32\gtray.cpp
# End Source File
# Begin Source File
@ -375,6 +391,10 @@ SOURCE=..\..\src\gnet\gwinsock.cpp
# End Source File
# Begin Source File
SOURCE=.\gxtext.cpp
# End Source File
# Begin Source File
SOURCE=.\main_win32.cpp
# End Source File
# Begin Source File

View File

@ -30,6 +30,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
IDI_ICON1 ICON DISCARDABLE "icon-32.ico"
IDI_ICON2 ICON DISCARDABLE "icon2.ico"
IDI_ICON3 ICON DISCARDABLE "icon3.ico"
IDI_ICON4 ICON DISCARDABLE "icon4.ico"
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////

View File

@ -39,8 +39,8 @@ GSmtp::AdminClient::AdminClient( AdminPeer & admin_peer ) :
GSmtp::AdminPeer::AdminPeer( GNet::StreamSocket * s , GNet::Address a , AdminServer & server ,
const std::string & server_address ) :
GNet::ServerPeer( s , a ) ,
m_server(server) ,
m_buffer(crlf()) ,
m_server(server) ,
m_server_address(server_address)
{
// dont prompt() here -- it confuses the poke program
@ -146,7 +146,7 @@ void GSmtp::AdminPeer::prompt()
{
std::string p( "E-MailRelay> " ) ;
ssize_t rc = socket().write( p.data() , p.length() ) ;
if( rc < p.length() )
if( rc < 0 || static_cast<size_t>(rc) < p.length() )
doDelete() ; // onDelete() and "delete this"
}
@ -154,7 +154,7 @@ void GSmtp::AdminPeer::send( std::string line )
{
line.append( crlf() ) ;
ssize_t rc = socket().write( line.data() , line.length() ) ;
if( rc < line.length() )
if( rc < 0 || static_cast<size_t>(rc) < line.length() )
doDelete() ; // onDelete() and "delete this"
}

183
src/main/gbase64.cpp Normal file
View File

@ -0,0 +1,183 @@
//
// 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.
//
// ===
//
// gbase64.cpp
//
#include "gdef.h"
#include "gsmtp.h"
#include "gbase64.h"
#include "gassert.h"
#include "gstr.h"
namespace
{
const char * map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
char pad = '=' ;
} ;
g_uint32_t GSmtp::Base64::numeric( char c )
{
return static_cast<g_uint32_t>( static_cast<unsigned char>(c) ) ;
}
void GSmtp::Base64::accumulate_8( g_uint32_t & n , std::string::const_iterator & p ,
std::string::const_iterator end , int & i )
{
char c = p == end ? '\0' : *p ;
n <<= 8U ;
n |= numeric(c) ;
if( p != end )
{
++p ;
++i ;
}
}
size_t GSmtp::Base64::hi_6( g_uint32_t n )
{
return (n >> 18U) & 0x3F ;
}
void GSmtp::Base64::generate_6( g_uint32_t & n , int & i , std::string & result )
{
char c = i-- >= 0 ? map[hi_6(n)] : pad ;
result.append( 1U , c ) ;
n <<= 6U ;
}
std::string GSmtp::Base64::crlf()
{
return std::string( "\r\n" ) ;
}
std::string GSmtp::Base64::encode( const std::string & s_in , const std::string & eol )
{
std::string result ;
size_t blocks = 0U ;
for( std::string::const_iterator p = s_in.begin() ; p != s_in.end() ; blocks++ )
{
if( blocks && (blocks % 19U) == 0U )
result.append( eol ) ;
g_uint32_t n = 0UL ;
int i = 0 ;
accumulate_8( n , p , s_in.end() , i ) ;
accumulate_8( n , p , s_in.end() , i ) ;
accumulate_8( n , p , s_in.end() , i ) ;
generate_6( n , i , result ) ;
generate_6( n , i , result ) ;
generate_6( n , i , result ) ;
generate_6( n , i , result ) ;
}
// delete when tested...
if( decode(result) != s_in )
{
std::string decode_result = decode(result) ;
G_ERROR( "GSmtp::Base64::encode: mismatch: "
<< "in \"" << G::Str::toPrintableAscii(s_in) << "\", "
<< "encoded \"" << G::Str::toPrintableAscii(result) << "\", "
<< "decoded \"" << G::Str::toPrintableAscii(decode_result) << "\"" ) ;
G_ASSERT( !"encode/decode mismatch" ) ;
}
return result ;
}
// ---
char GSmtp::Base64::to_char( g_uint32_t n )
{
return static_cast<char>(static_cast<unsigned char>(n)) ;
}
size_t GSmtp::Base64::index( char c , bool & error )
{
const char * p = std::strchr( map , c ) ;
error = error || !c || !p ;
return p ? (p-map) : 0U ;
}
size_t GSmtp::Base64::accumulate_6( g_uint32_t & n , char c_in , int & n_out , bool & error )
{
n <<= 6U ;
if( c_in != pad )
{
n |= index(c_in,error) ;
n_out++ ;
}
return c_in != '\0' ;
}
g_uint32_t GSmtp::Base64::hi_8( g_uint32_t n )
{
return (n >> 16U) & 0xff ;
}
void GSmtp::Base64::generate_8( g_uint32_t & n , int & n_out , std::string & result )
{
if( n_out-- > 0 )
result.append( 1U , to_char(hi_8(n)) ) ;
n <<= 8U ;
}
std::string GSmtp::Base64::decode( const std::string & s )
{
bool error = false ;
std::string result = decode( s , error ) ;
if( error )
throw Error() ;
return result ;
}
std::string GSmtp::Base64::decode( const std::string & s , bool & error )
{
std::string result ;
for( const char * p = s.c_str() ; *p ; )
{
if( *p == '\r' || *p == '\n' )
{
p++ ;
continue ;
}
g_uint32_t n = 0UL ;
size_t i = 0U ;
int n_out = -1 ;
i += accumulate_6( n , p[i] , n_out , error ) ;
i += accumulate_6( n , p[i] , n_out , error ) ;
i += accumulate_6( n , p[i] , n_out , error ) ;
i += accumulate_6( n , p[i] , n_out , error ) ;
p += i ;
generate_8( n , n_out , result ) ;
generate_8( n , n_out , result ) ;
generate_8( n , n_out , result ) ;
}
return result ;
}
bool GSmtp::Base64::valid( const std::string & s )
{
bool error = false ;
(void) decode( s , error ) ;
return !error ;
}

75
src/main/gbase64.h Normal file
View File

@ -0,0 +1,75 @@
//
// 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.
//
// ===
//
// gbase64.h
//
#ifndef G_BASE64_H
#define G_BASE64_H
#include "gdef.h"
#include "gsmtp.h"
#include "gexception.h"
#include <string>
namespace GSmtp
{
class Base64 ;
} ;
// Class: GSmtp::Base64
// Description: A base64 codec class.
// See also: RFC 1341 section 5.2
//
class GSmtp::Base64
{
public:
G_EXCEPTION( Error , "base64 encoding error" ) ;
static std::string encode( const std::string & s , const std::string & eol = crlf() ) ;
// Encodes the given string.
static std::string decode( const std::string & ) ;
// Decodes the given string. Throws an exception
// if not a valid encoding.
static bool valid( const std::string & ) ;
// Returns true if the string can be decoded.
static std::string crlf() ;
// Returns carriage-return-line-feed.
private:
Base64() ;
static inline g_uint32_t numeric( char c ) ;
static inline void accumulate_8( g_uint32_t & n , std::string::const_iterator & ,
std::string::const_iterator , int & ) ;
static inline size_t hi_6( g_uint32_t n ) ;
static inline void generate_6( g_uint32_t & n , int & i , std::string & result ) ;
static inline char to_char( g_uint32_t n ) ;
static inline size_t index( char c , bool & error ) ;
static inline size_t accumulate_6( g_uint32_t & n , char c_in , int & , bool & error ) ;
static inline g_uint32_t hi_8( g_uint32_t n ) ;
static inline void generate_8( g_uint32_t & n , int & i , std::string & result ) ;
static std::string decode( const std::string & s , bool & error ) ;
} ;
#endif

View File

@ -26,24 +26,33 @@
#include "gsmtp.h"
#include "glocal.h"
#include "gfile.h"
#include "gsasl.h"
#include "gbase64.h"
#include "gstr.h"
#include "gmemory.h"
#include "gxtext.h"
#include "gclientprotocol.h"
#include "gresolve.h"
#include "glog.h"
#include "gassert.h"
GSmtp::ClientProtocol::ClientProtocol( Sender & sender , const std::string & thishost ) :
m_state(sStart) ,
m_thishost(thishost) ,
GSmtp::ClientProtocol::ClientProtocol( Sender & sender , const std::string & thishost_name ,
unsigned int timeout , bool must_authenticate ) :
m_sender(sender) ,
m_thishost(thishost_name) ,
m_state(sStart) ,
m_callback(NULL) ,
m_server_has_8bitmime(false) ,
m_said_hello(false)
m_said_hello(false) ,
m_message_is_8bit(false) ,
m_authenticated_with_server(false) ,
m_must_authenticate(must_authenticate) ,
m_timeout(timeout)
{
}
void GSmtp::ClientProtocol::start( const std::string & from , const G::Strings & to , bool eight_bit ,
std::string authentication , std::string server_name ,
std::auto_ptr<std::istream> content , Callback & callback )
{
G_DEBUG( "GSmtp::ClientProtocol::start" ) ;
@ -51,9 +60,10 @@ void GSmtp::ClientProtocol::start( const std::string & from , const G::Strings &
m_from = from ;
m_content = content ;
m_callback = &callback ;
m_server_has_8bitmime = false ;
m_message_is_8bit = eight_bit ;
m_message_authentication = authentication ;
m_reply = Reply() ;
m_sasl <<= new SaslClient( server_name ) ;
if( m_state != sStart && m_state != sEnd )
throw NotReady() ;
@ -78,9 +88,7 @@ void GSmtp::ClientProtocol::sendDone()
{
if( m_state == sData )
{
size_t n = 0U ;
while( sendLine() )
n++ ;
size_t n = sendLines() ;
G_LOG( "GSmtp::ClientProtocol: tx>>: [" << n << " line(s) of content]" ) ;
if( endOfContent() )
@ -141,15 +149,25 @@ void GSmtp::ClientProtocol::sendMail()
{
mail_from.append( " BODY=8BITMIME" ) ;
}
else if( m_message_is_8bit )
if( !m_server_has_8bitmime && m_message_is_8bit )
{
throw NarrowPipe() ; // (could do better)
}
if( m_authenticated_with_server && !m_message_authentication.empty() )
{
mail_from.append( std::string(" AUTH=") + Xtext::encode(m_message_authentication) ) ;
}
else if( m_authenticated_with_server )
{
mail_from.append( " AUTH=<>" ) ;
}
send( mail_from ) ;
}
void GSmtp::ClientProtocol::applyEvent( const Reply & reply )
{
cancelTimer() ;
if( reply.is(Reply::ServiceReady_220) )
{
; // no-op
@ -158,6 +176,7 @@ void GSmtp::ClientProtocol::applyEvent( const Reply & reply )
{
m_state = sStart ;
m_said_hello = false ;
m_authenticated_with_server = false ;
}
else if( m_state == sStart )
{
@ -170,12 +189,62 @@ void GSmtp::ClientProtocol::applyEvent( const Reply & reply )
}
else if( (m_state==sSentEhlo || m_state==sSentHelo) && reply.is(Reply::Ok_250) )
{
m_server_has_8bitmime = m_state == sSentEhlo && reply.textContains("8BITMIME") ;
G_ASSERT( m_sasl.get() != NULL ) ;
G_DEBUG( "GSmtp::ClientProtocol::applyEvent: ehlo reply \"" << G::Str::toPrintableAscii(reply.text()) << "\"" ) ;
m_auth_mechanism = m_sasl->preferred( serverAuthMechanisms(reply) ) ;
m_server_has_8bitmime = m_state == sSentEhlo && reply.textContains("\n8BITMIME") ;
m_said_hello = true ;
if( m_sasl->active() && !m_auth_mechanism.empty() )
{
m_state = sAuth1 ;
send( std::string("AUTH ") + m_auth_mechanism ) ;
}
else if( m_sasl->active() && m_must_authenticate )
{
std::string reason = "cannot do mandatory authentication" ; // eg. no suitable mechanism
G_WARNING( "GSmtp::ClientProtocol: " << reason ) ;
m_state = sEnd ;
doCallback( false , true , reason ) ;
}
else
{
m_state = sSentMail ;
sendMail() ;
}
}
else if( m_state == sAuth1 && reply.is(Reply::Challenge_334) && Base64::valid(reply.text()) )
{
bool done = true ;
bool error = false ;
std::string rsp = m_sasl->response( m_auth_mechanism , Base64::decode(reply.text()) , done , error ) ;
if( error )
{
m_state = sAuth2 ;
send( "*" ) ; // ie. cancel authentication
}
else
{
m_state = done ? sAuth2 : m_state ;
send( Base64::encode(rsp) ) ;
}
}
else if( m_state == sAuth2 )
{
m_authenticated_with_server = reply.is(Reply::Authenticated_235) ;
if( !m_authenticated_with_server && m_must_authenticate )
{
m_state = sEnd ;
doCallback( false , true , "mandatory authentication failed" ) ;
}
else
{
m_state = sSentMail ;
sendMail() ; // (with or without sucessful authentication)
}
}
else if( m_state == sSentMail && reply.is(Reply::Ok_250) )
{
if( m_to.size() == 0U )
@ -203,15 +272,13 @@ void GSmtp::ClientProtocol::applyEvent( const Reply & reply )
{
G_WARNING( "GSmtp::ClientProtocol: recipient rejected" ) ;
m_state = sEnd ;
doCallback( false , reply.text() ) ;
doCallback( false , false , reply.text() ) ;
}
else if( m_state == sSentData && reply.is(Reply::OkForData_354) )
{
m_state = sData ;
size_t n = 0U ;
while( sendLine() )
n++ ;
size_t n = sendLines() ;
G_LOG( "GSmtp::ClientProtocol: tx>>: [" << n << " line(s) of content]" ) ;
if( endOfContent() )
@ -224,38 +291,45 @@ void GSmtp::ClientProtocol::applyEvent( const Reply & reply )
{
const bool ok = reply.is(Reply::Ok_250) ;
m_state = sEnd ;
doCallback( ok , ok ? std::string() : reply.text() ) ;
doCallback( ok , false , ok ? std::string() : reply.text() ) ;
}
else
{
G_WARNING( "GSmtp::ClientProtocol: failure in client protocol: " << static_cast<int>(m_state) ) ;
m_state = sEnd ; // (was sReset)
send( "RSET" ) ; // for good meausre
doCallback( false , std::string("unexpected response: ")+reply.text() ) ;
if( 0 ) send( "RSET" ) ; // for good meausre
doCallback( false , true , std::string("unexpected response: ")+reply.text() ) ;
}
}
void GSmtp::ClientProtocol::doCallback( bool ok , const std::string & reason )
void GSmtp::ClientProtocol::onTimeout()
{
G_WARNING( "GSmtp::ClientProtocol: timeout" ) ;
m_state = sEnd ;
doCallback( false , false , "timeout" ) ;
}
G::Strings GSmtp::ClientProtocol::serverAuthMechanisms( const ClientProtocolReply & reply ) const
{
G::Strings result ;
std::string auth_line = reply.textLine("AUTH") ;
if( ! auth_line.empty() )
{
G::Str::splitIntoTokens( auth_line , result , " \t" ) ;
if( result.size() )
result.pop_front() ; // remove "AUTH" ;
}
return result ;
}
void GSmtp::ClientProtocol::doCallback( bool ok , bool abort , const std::string & reason )
{
m_content <<= 0 ;
if( m_callback )
{
Callback * cb = m_callback ;
m_callback = NULL ;
cb->protocolDone( ok , reason ) ;
}
}
bool GSmtp::ClientProtocol::sendLine()
{
std::string line = G::Str::readLineFrom( *(m_content.get()) , crlf() ) ;
if( m_content->good() )
{
return send( line , false , false ) ;
}
else
{
return false ;
cb->protocolDone( ok , abort , reason ) ;
}
}
@ -264,11 +338,37 @@ bool GSmtp::ClientProtocol::endOfContent() const
return !m_content->good() ;
}
size_t GSmtp::ClientProtocol::sendLines()
{
size_t n = 0U ;
std::string line ;
while( sendLine(line) )
n++ ;
return n ;
}
bool GSmtp::ClientProtocol::sendLine( std::string & line )
{
G::Str::readLineFrom( *(m_content.get()) , crlf() , line ) ;
if( m_content->good() )
{
line.append( crlf() ) ;
return m_sender.protocolSend( line ) ;
}
else
{
return false ;
}
}
bool GSmtp::ClientProtocol::send( const std::string & line , bool eot , bool log )
{
if( log )
G_LOG( "GSmtp::ClientProtocol: tx>>: \"" << G::Str::toPrintableAscii(line) << "\"" ) ;
if( m_timeout != 0U )
startTimer( m_timeout ) ;
if( !eot && line.length() && line.at(0U) == '.' )
return m_sender.protocolSend( std::string(".")+line+crlf() ) ;
else
@ -276,16 +376,17 @@ bool GSmtp::ClientProtocol::send( const std::string & line , bool eot , bool log
}
//static
std::string GSmtp::ClientProtocol::crlf()
const std::string & GSmtp::ClientProtocol::crlf()
{
return std::string("\015\012") ;
static std::string s("\015\012") ;
return s ;
}
// ===
GSmtp::ClientProtocolReply::ClientProtocolReply( const std::string & line ) :
m_valid(false) ,
m_complete(false)
m_complete(false) ,
m_valid(false)
{
if( line.length() >= 3U &&
is_digit(line.at(0U)) &&
@ -297,9 +398,9 @@ GSmtp::ClientProtocolReply::ClientProtocolReply( const std::string & line ) :
m_valid = true ;
m_complete = line.length() == 3U || line.at(3U) == ' ' ;
m_value = G::Str::toUInt( line.substr(0U,3U) ) ;
if( line.length() > 3U )
if( line.length() > 4U )
{
m_text = line.substr(3U) ;
m_text = line.substr(4U) ;
G::Str::trimLeft( m_text , " \t" ) ;
}
}
@ -335,6 +436,21 @@ std::string GSmtp::ClientProtocolReply::text() const
return m_text ;
}
std::string GSmtp::ClientProtocolReply::textLine( const std::string & prefix ) const
{
size_t start_pos = m_text.find( std::string("\n")+prefix ) ;
if( start_pos == std::string::npos )
{
return std::string() ;
}
else
{
start_pos++ ;
size_t end_pos = m_text.find( "\n" , start_pos + prefix.length() ) ;
return m_text.substr( start_pos , end_pos-start_pos ) ;
}
}
//static
bool GSmtp::ClientProtocolReply::is_digit( char c )
{

View File

@ -28,7 +28,10 @@
#include "gnet.h"
#include "gsmtp.h"
#include "gmessagestore.h"
#include "gsasl.h"
#include "gsecrets.h"
#include "gstrings.h"
#include "gtimer.h"
#include "gexception.h"
#include <memory>
#include <iostream>
@ -64,13 +67,15 @@ public:
} ;
enum Value
{
Invalid = 0 ,
ServiceReady_220 = 220 ,
SyntaxError_500 = 500 ,
BadSequence_503 = 503 ,
NotImplemented_502 = 502 ,
Ok_250 = 250 ,
Authenticated_235 = 235 ,
Challenge_334 = 334 ,
OkForData_354 = 354 ,
Ok_250 = 250
SyntaxError_500 = 500 ,
NotImplemented_502 = 502 ,
BadSequence_503 = 503 ,
Invalid = 0 ,
} ;
explicit ClientProtocolReply( const std::string & line = std::string() ) ;
bool incomplete() const ;
@ -80,6 +85,7 @@ public:
bool is( Value v ) const ;
unsigned int value() const ;
std::string text() const ;
std::string textLine( const std::string & prefix ) const ;
Type type() const ;
SubType subType() const ;
bool textContains( std::string s ) const ;
@ -95,7 +101,7 @@ private:
// Class: GSmtp::ClientProtocol
// Description: Implements the client-side SMTP protocol.
//
class GSmtp::ClientProtocol
class GSmtp::ClientProtocol : private GNet::Timer
{
public:
G_EXCEPTION( NotReady , "not ready" ) ;
@ -121,26 +127,44 @@ public:
class Callback // A callback interface used by ClientProtocol.
{
public: virtual void protocolDone( bool ok , const std::string & reason ) = 0 ;
public: virtual void protocolDone( bool ok , bool abort , const std::string & reason ) = 0 ;
// Called once the protocol has finished with
// a given message. See ClientProtocol::start().
//
// If 'ok' is false then 'abort' indicates
// whether there is any point in trying to
// send more messages to the same server.
// The 'abort' parameter will be true if,
// for example, authentication failed -- if
// it failed for one message then it will
// fail for all the others.
private: void operator=( const Callback & ) ; // not implemented
public: virtual ~Callback() ;
} ;
ClientProtocol( Sender & sender , const std::string & thishost ) ;
// Constructor. The sender reference is kept.
// The Sender interface is used to send
// protocol messages to the peer.
ClientProtocol( Sender & sender , const std::string & thishost_name ,
unsigned int timeout , bool must_authenticate ) ;
// Constructor. The 'sender' and 'secrets' references
// are kept.
//
// The Sender interface is used to send protocol
// messages to the peer.
//
// The 'thishost_name' parameter is used in the
// SMTP EHLO request.
void start( const std::string & from , const G::Strings & to , bool eight_bit ,
std::string authentication , std::string server_name ,
std::auto_ptr<std::istream> content , Callback & callback ) ;
// Starts transmission of the given message.
//
// The 'callback' parameter is used to
// signal that the message has been
// processed.
// The 'callback' parameter is used to signal that the
// message has been processed.
//
// The 'server_name' parameter is passed to the SASL
// authentication code. It should be a fully-qualified
// domain name where possible.
void sendDone() ;
// Called when a blocked connection becomes unblocked.
@ -154,16 +178,19 @@ public:
private:
bool send( const std::string & , bool eot = false , bool log = true ) ;
bool sendLine() ;
bool sendLine( std::string & ) ;
size_t sendLines() ;
void sendMail() ;
bool endOfContent() const ;
static std::string crlf() ;
static const std::string & crlf() ;
void applyEvent( const Reply & event ) ;
static bool parseReply( Reply & , const std::string & , std::string & ) ;
void doCallback( bool , const std::string & ) ;
void doCallback( bool , bool , const std::string & ) ;
G::Strings serverAuthMechanisms( const ClientProtocolReply & reply ) const ;
void onTimeout() ;
private:
enum State { sStart , sSentEhlo , sSentHelo , sSentMail ,
enum State { sStart , sSentEhlo , sSentHelo , sAuth1 , sAuth2 , sSentMail ,
sSentRcpt , sSentData , sData , sDone , sEnd , sReset } ;
Sender & m_sender ;
std::string m_thishost ;
@ -175,7 +202,13 @@ private:
bool m_server_has_8bitmime ;
bool m_said_hello ;
bool m_message_is_8bit ;
std::string m_message_authentication ;
Reply m_reply ;
bool m_authenticated_with_server ;
std::string m_auth_mechanism ;
std::auto_ptr<SaslClient> m_sasl ;
bool m_must_authenticate ;
unsigned int m_timeout ;
} ;
#endif

View File

@ -107,9 +107,12 @@ std::string GSmtp::FileStore::x()
}
//static
std::string GSmtp::FileStore::format()
std::string GSmtp::FileStore::format( int n )
{
return "#2821.2" ;
if( n == 0 )
return "#2821.3" ; // current -- includes message authentication and client ip
else
return "#2821.2" ; // old
}
//static
@ -168,7 +171,10 @@ G::Path GSmtp::FileStore::fullPath( const std::string & filename ) const
unsigned long GSmtp::FileStore::newSeq()
{
return m_seq++ ;
m_seq++ ;
if( m_seq == 0UL )
m_seq++ ;
return m_seq ;
}
bool GSmtp::FileStore::empty() const

View File

@ -63,7 +63,7 @@ public:
// directory by other processes.
unsigned long newSeq() ;
// Hands out a new sequence number.
// Hands out a new non-zero sequence number.
std::auto_ptr<std::ostream> stream( const G::Path & path );
// Returns a stream to the given content.
@ -93,9 +93,10 @@ public:
static std::string x() ;
// Returns the prefix for envelope header lines.
static std::string format() ;
static std::string format( int n = 0 ) ;
// Returns an identifier for the storage format
// implemented by this class.
// implemented by this class. If n is -1 then
// it returns the previous format (etc.).
private:
static void checkPath( const G::Path & dir ) ;

View File

@ -54,6 +54,7 @@ class GSmtp::MessageStore
{
public:
G_EXCEPTION( WriteError , "error writing file" ) ;
G_EXCEPTION( StorageError , "error storing message" ) ;
G_EXCEPTION( NoInstance , "no message store instance" ) ;
G_EXCEPTION( FormatError , "format error" ) ;
class IteratorImp // A base class for MessageStore::Iterator implementations.

View File

@ -28,6 +28,7 @@
#include "gmemory.h"
#include "gprocess.h"
#include "gfile.h"
#include "gxtext.h"
#include "gassert.h"
#include "glog.h"
#include <iostream>
@ -37,8 +38,8 @@ 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_from(from) ,
m_eight_bit(false)
{
m_seq = store.newSeq() ;
@ -72,17 +73,17 @@ void GSmtp::NewFile::addText( const std::string & line )
bool GSmtp::NewFile::isEightBit( const std::string & line )
{
const size_t n = line.length() ;
for( size_t i = 0U ; i < n ; --i )
std::string::const_iterator end = line.end() ;
for( std::string::const_iterator p = line.begin() ; p != end ; ++p )
{
const unsigned char c = static_cast<unsigned char>(line.at(i)) ;
const unsigned char c = static_cast<unsigned char>(*p) ;
if( c > 0x7fU )
return true ;
}
return false ;
}
void GSmtp::NewFile::store()
bool GSmtp::NewFile::store( const std::string & auth_id , const std::string & client_ip )
{
// flush the content file
//
@ -99,17 +100,19 @@ void GSmtp::NewFile::store()
std::string reason = p0.str() ;
{
std::auto_ptr<std::ostream> envelope_stream = m_store.stream( p0 ) ;
ok = saveEnvelope( *(envelope_stream.get()) , p0.str() ) ;
ok = saveEnvelope( *(envelope_stream.get()) , p0.str() , auth_id , client_ip ) ;
}
// shell out to a message pre-processor
//
bool cancelled = false ;
if( ok )
{
ok = preprocess( m_content_path ) ;
ok = preprocess( m_content_path , cancelled ) ;
if( !ok )
reason = "pre-processing failed" ;
}
G_ASSERT( !(ok&&cancelled) ) ;
// deliver to local mailboxes
//
@ -124,18 +127,32 @@ void GSmtp::NewFile::store()
{
G_ASSERT( m_content_path.str().length() != 0U ) ;
G::File::remove( m_content_path , G::File::NoThrow() ) ;
throw GSmtp::MessageStore::WriteError( reason ) ;
}
if( !cancelled )
throw GSmtp::MessageStore::StorageError( reason ) ;
}
bool GSmtp::NewFile::preprocess( const G::Path & path )
return cancelled ;
}
bool GSmtp::NewFile::preprocess( const G::Path & path , bool & cancelled )
{
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 )
if( exit_code == 100 )
{
// a special exit-code for pre-processors which
// do their own message handling -- the pre-processor
// should delete the files before returning this
// exit code
//
cancelled = true ;
G_LOG( "GSmtp::NewFile: message processing cancelled by preprocessor" ) ;
return false ;
}
else if( exit_code != 0 )
{
G_WARNING( "GSmtp::NewFile::preprocess: pre-processing failed: exit code " << exit_code ) ;
return false ;
@ -144,7 +161,7 @@ bool GSmtp::NewFile::preprocess( const G::Path & path )
return true ;
}
void GSmtp::NewFile::deliver( const G::Strings & to ,
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 )
{
@ -158,7 +175,8 @@ void GSmtp::NewFile::deliver( const G::Strings & to ,
G::File::copy( envelope_path_now.str() , envelope_path_later.str()+".local" ) ;
}
bool GSmtp::NewFile::saveEnvelope( std::ostream & stream , const std::string & where ) const
bool GSmtp::NewFile::saveEnvelope( std::ostream & stream , const std::string & where ,
const std::string & auth_id , const std::string & client_ip ) const
{
G_LOG( "GSmtp::NewMessage: envelope file: " << where ) ;
@ -178,14 +196,17 @@ bool GSmtp::NewFile::saveEnvelope( std::ostream & stream , const std::string & w
for( ; to_p != m_to_remote.end() ; ++to_p )
stream << x << "To-Remote: " << *to_p << crlf() ;
}
stream << x << "Authentication: " << Xtext::encode(auth_id) << crlf() ;
stream << x << "Client: " << client_ip << crlf() ;
stream << x << "End: 1" << crlf() ;
stream.flush() ;
return stream.good() ;
}
std::string GSmtp::NewFile::crlf() const
const std::string & GSmtp::NewFile::crlf() const
{
return std::string( "\015\012" ) ;
static std::string s( "\015\012" ) ;
return s ;
}
unsigned long GSmtp::NewFile::id() const

View File

@ -59,11 +59,13 @@ public:
virtual void addText( const std::string & line ) ;
// Adds a line of content.
virtual void store() ;
virtual bool store( const std::string & auth_id , const std::string & client_ip ) ;
// Stores the message in the message store.
// Returns true if storage was deliberately
// cancelled.
virtual unsigned long id() const ;
// Returns the message's unique identifier.
// Returns the message's unique non-zero identifier.
static void setPreprocessor( const G::Path & exe ) ;
// Defines a program which is used for pre-processing
@ -82,11 +84,12 @@ private:
static G::Path m_preprocessor ;
private:
bool saveEnvelope( std::ostream & stream , const std::string & where ) const ;
std::string crlf() const ;
bool saveEnvelope( std::ostream & , const std::string & where ,
const std::string & auth_id , const std::string & client_ip ) const ;
const std::string & crlf() const ;
static bool isEightBit( const std::string & line ) ;
void deliver( const G::Strings & , const G::Path & , const G::Path & , const G::Path & ) ;
bool preprocess( const G::Path & ) ;
bool preprocess( const G::Path & , bool & ) ;
} ;
#endif

View File

@ -47,11 +47,13 @@ public:
virtual void addText( const std::string & line ) = 0 ;
// Adds a line of content.
virtual void store() = 0 ;
virtual bool store( const std::string & auth_id , const std::string & client_ip ) = 0 ;
// Stores the message in the message store.
// Returns true if storage was deliberately
// cancelled.
virtual unsigned long id() const = 0 ;
// Returns the message's unique identifier.
// Returns the message's unique non-zero identifier.
virtual ~NewMessage() ;
// Destructor.

View File

@ -51,6 +51,10 @@ public:
{
public: virtual ~Callback() ;
public: virtual void processDone( bool success , unsigned long id , const std::string & reason ) = 0 ;
// Signals that ProtocolMessage::process() has completed.
// As a special case, if success is true and id is zero then
// the message processing was cancelled.
private: void operator=( const Callback & ) ; // not implemented
} ;
@ -86,12 +90,19 @@ public:
// Precondition: at least one
// successful addTo() call
virtual void process( Callback & callback ) = 0 ;
virtual void process( Callback & callback , const std::string & authenticated_client_id ,
const std::string & peer_ip_address ) = 0 ;
// Starts asynchronous processing of the
// message. Once processing is complete the
// message state is cleared and the callback
// is triggered. The callback may be called
// before process() returns.
//
// The client-id parameter is used to propogate
// authentication information from the SMTP
// AUTH command into individual messages.
// It is the empty string for unauthenticated
// clients. See also GSmtp::Sasl::id().
private:
void operator=( const ProtocolMessage & ) ; // not implemented

View File

@ -67,17 +67,18 @@ void GSmtp::ProtocolMessageForward::addText( const std::string & line )
m_pm.addText( line ) ;
}
void GSmtp::ProtocolMessageForward::process( ProtocolMessage::Callback & callback )
void GSmtp::ProtocolMessageForward::process( ProtocolMessage::Callback & callback , const std::string & auth_id ,
const std::string & client_ip )
{
m_callback = & callback ;
m_pm.process( *this ) ;
m_pm.process( *this , auth_id , client_ip ) ;
}
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 )
bool nothing_to_do = success && id == 0UL ;
if( success && id != 0UL )
{
m_id = id ;
success = forward( id , nothing_to_do , &reason ) ;

View File

@ -39,10 +39,14 @@ namespace GSmtp
} ;
// 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.
// 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.
//
// The implementation delegates to an instance of the ProtocolMessageStore
// class (ie. its sibling class) to do the storage, and to an instance
// of the Client class to do the forwarding.
//
// See also: ProtocolMessageStore
//
class GSmtp::ProtocolMessageForward : public GSmtp:: ProtocolMessage ,
@ -71,7 +75,8 @@ public:
virtual void addText( const std::string & ) ;
// See ProtocolMessage.
virtual void process( ProtocolMessage::Callback & callback ) ;
virtual void process( ProtocolMessage::Callback & callback , const std::string & auth_id ,
const std::string & client_ip ) ;
// See ProtocolMessage.
private:

View File

@ -101,15 +101,18 @@ void GSmtp::ProtocolMessageStore::addText( const std::string & line )
m_msg->addText( line ) ;
}
void GSmtp::ProtocolMessageStore::process( Callback & callback )
void GSmtp::ProtocolMessageStore::process( Callback & callback , const std::string & auth_id ,
const std::string & client_ip )
{
try
{
G_ASSERT( m_msg.get() != NULL ) ;
unsigned long id = 0UL ;
bool cancelled = false ;
if( m_msg.get() != NULL )
{
m_msg->store() ;
cancelled = m_msg->store( auth_id , client_ip ) ;
if( !cancelled )
id = m_msg->id() ;
}
clear() ;

View File

@ -66,7 +66,8 @@ public:
virtual void addText( const std::string & ) ;
// See ProtocolMessage.
virtual void process( ProtocolMessage::Callback & callback ) ;
virtual void process( ProtocolMessage::Callback & callback , const std::string & auth_id ,
const std::string & client_ip ) ;
// See ProtocolMessage.
private:

Some files were not shown because too many files have changed in this diff Show More