Skip to content
GitLab
Explore
Sign in
Register
Commits on Source (2)
New upstream release 0.5.
· d9f36efd
Hilmar Preuße
authored
Apr 14, 2018
d9f36efd
Policy bumped
· 0afd7993
Francesco Paolo Lovergine
authored
May 01, 2018
0afd7993
Show whitespace changes
Inline
Side-by-side
.gitattributes
0 → 100644
View file @
0afd7993
*.pl linguist-language=C
*.pm linguist-language=C
.travis.yml
0 → 100644
View file @
0afd7993
language
:
c
compiler
:
-
gcc
-
clang
install
:
-
sudo apt-get update -qq
# for libarchive
-
sudo apt-get install -y libarchive-dev
# for libbz2
-
sudo apt-get install -y libbz2-dev
# for unit tests
-
sudo apt-get install -y check
# for static code analysis
-
sudo apt-get install -y cppcheck rats
# for test code coverage
-
sudo apt-get install -y lcov
-
gem install coveralls-lcov
before_script
:
-
cd ${TRAVIS_BUILD_DIR}
-
lcov --directory . --zerocounters
script
:
# - find . -type f -name "*.c" -print | grep -v t\/ | xargs cppcheck 2>&1
# - find . -type f -name "*.c" -print | grep -v t\/ | xargs rats --language=c
-
git clone --depth 10 https://github.com/proftpd/proftpd.git
-
cp mod_msg.c proftpd/contrib/
-
cd proftpd
-
./configure LIBS='-lm -lrt -pthread' --enable-ctrls --enable-devel=coverage --enable-dso --enable-tests --with-shared=mod_msg
-
make
-
make clean
-
./configure LIBS='-lm -lrt -pthread' --enable-ctrls --enable-devel=coverage --enable-tests --with-modules=mod_msg
-
make
# Run `tidy -e -q mod_msg.html` for doc validation
README.md
0 → 100644
View file @
0afd7993
proftpd-mod_msg
===============
Status
------
[

](https://travis-ci.org/Castaglia/proftpd-mod_msg)
[

](https://img.shields.io/badge/license-GPL-brightgreen.svg)
Synopsis
--------
The
`mod_msg`
module for ProFTPD supports sending messages to connected FTP
clients.
For further module documentation, see
[
mod_msg.html
](
https://htmlpreview.github.io/?https://github.com/Castaglia/proftpd-mod_msg/blob/master/mod_msg.html
)
.
debian/changelog
View file @
0afd7993
proftpd-mod-msg (0.5-1) unstable; urgency=medium
[ Hilmar Preuße ]
* New upstream release
* 01c862d404df2e6c9c06c6229efc113a6623d215.patch: address compiler warnings
* Do not install *.la and *.a files.
[ Francesco Paolo Lovergine ]
* Updated Vcs-* fields.
* Policy bumped to 4.1.4, no changes.
-- Francesco Paolo Lovergine <frankie@debian.org> Tue, 01 May 2018 12:16:18 +0200
proftpd-mod-msg (0.4.1-2) unstable; urgency=low
[ Fabrizio Regalli ]
...
...
debian/control
View file @
0afd7993
...
...
@@ -3,12 +3,11 @@ Section: net
Priority: optional
Maintainer: ProFTPD Maintainance Team <pkg-proftpd-maintainers@lists.alioth.debian.org>
Uploaders: Francesco Paolo Lovergine <frankie@debian.org>
Build-Depends: debhelper (>= 9
~
), proftpd-dev (>= 1.3.6)
Standards-Version: 4.1.
3
Build-Depends: debhelper (>= 9
.20160114
), proftpd-dev (>= 1.3.6)
Standards-Version: 4.1.
4
Homepage: http://www.castaglia.org/proftpd/modules/mod_msg.html
Vcs-Git: git://git.debian.org/pkg-proftpd/proftpd-mod-msg.git
Vcs-Browser: http://git.debian.org/?p=pkg-proftpd/proftpd-mod-msg.git;a=summary
Vcs-Browser: https://salsa.debian.org/debian-proftpd-team/proftpd-mod-msg
Vcs-Git: https://salsa.debian.org/debian-proftpd-team/proftpd-mod-msg.git
Package: proftpd-mod-msg
Architecture: any
...
...
debian/patches/01c862d404df2e6c9c06c6229efc113a6623d215.patch
0 → 100644
View file @
0afd7993
From 01c862d404df2e6c9c06c6229efc113a6623d215 Mon Sep 17 00:00:00 2001
From: TJ Saunders <tj@castaglia.org>
Date: Wed, 11 Apr 2018 07:06:41 -0700
Subject: [PATCH] Address compiler warnings about signedness mismatches.
---
mod_msg.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/mod_msg.c b/mod_msg.c
index 7567253..757a53f 100644
--- a/mod_msg.c
+++ b/mod_msg.c
@@ -509,7 +509,7 @@
static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
/* Handle 'msg user' requests. */
if (strcmp(reqargv[0], "user") == 0) {
- register unsigned int i = 0;
+ register int i = 0;
pr_scoreboard_entry_t *score = NULL;
const char *user, *msgstr = "";
size_t msglen;
@@ -530,9 +530,10 @@
static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
* be a maximum length on this strength, depending on the maximum msg
* size allowed for SysV message queues.
*/
- for (i = 2; i < reqargc; i++)
+ for (i = 2; i < reqargc; i++) {
msgstr = pstrcat(ctrl->ctrls_tmp_pool, msgstr, *msgstr ? " " : "",
reqargv[i], NULL);
+ }
msglen = strlen(msgstr) + 1;
@@ -577,7 +578,7 @@
static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
/* Handle 'msg host' requests. */
} else if (strcmp(reqargv[0], "host") == 0) {
- register unsigned int i = 0;
+ register int i = 0;
pr_scoreboard_entry_t *score = NULL;
const char *addr, *msgstr = "";
const pr_netaddr_t *na;
@@ -596,9 +597,10 @@
static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
* be a maximum length on this strength, depending on the maximum msg
* size allowed for SysV message queues.
*/
- for (i = 2; i < reqargc; i++)
+ for (i = 2; i < reqargc; i++) {
msgstr = pstrcat(ctrl->ctrls_tmp_pool, msgstr, *msgstr ? " " : "",
reqargv[i], NULL);
+ }
if (strlen(msgstr) >= MSGMAX) {
pr_ctrls_add_response(ctrl, "message exceeds maximum length (%u). "
@@ -645,7 +647,7 @@
static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
/* Handle 'msg class' requests. */
} else if (strcmp(reqargv[0], "class") == 0) {
- register unsigned int i = 0;
+ register int i = 0;
pr_scoreboard_entry_t *score;
const char *class = reqargv[1], *msgstr = "";
@@ -663,9 +665,10 @@
static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
* be a maximum length on this strength, depending on the maximum msg
* size allowed for SysV message queues.
*/
- for (i = 2; i < reqargc; i++)
+ for (i = 2; i < reqargc; i++) {
msgstr = pstrcat(ctrl->ctrls_tmp_pool, msgstr, *msgstr ? " " : "",
reqargv[i], NULL);
+ }
if (strlen(msgstr) >= MSGMAX) {
pr_ctrls_add_response(ctrl, "message exceeds maximum length (%u). "
@@ -700,7 +703,7 @@
static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
/* Handle 'msg all' requests. */
} else if (strcmp(reqargv[0], "all") == 0) {
- register unsigned int i = 0;
+ register int i = 0;
pr_scoreboard_entry_t *score;
const char *msgstr = "";
@@ -713,9 +716,10 @@
static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
* be a maximum length on this strength, depending on the maximum msg
* size allowed for SysV message queues.
*/
- for (i = 1; i < reqargc; i++)
+ for (i = 1; i < reqargc; i++) {
msgstr = pstrcat(ctrl->ctrls_tmp_pool, msgstr, *msgstr ? " " : "",
reqargv[i], NULL);
+ }
if (strlen(msgstr) >= MSGMAX) {
pr_ctrls_add_response(ctrl, "message exceeds maximum length (%u). "
debian/patches/series
View file @
0afd7993
error-format-security.patch
proftpd-1.3.6_rc4-msg-refresh-api.patch
#error-format-security.patch
#proftpd-1.3.6_rc4-msg-refresh-api.patch
01c862d404df2e6c9c06c6229efc113a6623d215.patch
debian/proftpd-mod-msg.docs
View file @
0afd7993
mod_msg.html
README.md
debian/rules
View file @
0afd7993
...
...
@@ -14,6 +14,8 @@ override_dh_auto_build:
override_dh_auto_install
:
DESTDIR
=
$(
CURDIR
)
/debian/
$(
DEBNAME
)
prxs
-i
-c
$(
MODULE_NAME
)
.c
rm
-f
$(
CURDIR
)
/debian/
$(
DEBNAME
)
/usr/lib/proftpd/
*
.la
\
$(
CURDIR
)
/debian/
$(
DEBNAME
)
/usr/lib/proftpd/
*
.a
override_dh_gencontrol
:
cat
/usr/share/proftpd/proftpd-substvars
>>
$(
CURDIR
)
/debian/
$(
DEBNAME
)
.substvars
...
...
mod_msg.c
View file @
0afd7993
/*
* ProFTPD: mod_msg -- a module for sending messages to connected clients
*
* Copyright (c) 2004 TJ Saunders
* Copyright (c) 2004-2018 TJ Saunders
*
* 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
...
...
@@ -15,7 +14,7 @@
*
* 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., 5
9 Temple Place
, Suite
33
0, Boston, MA
0211
1
-13
07
, USA.
* Foundation, Inc., 5
1 Franklin Street
, Suite
50
0, Boston, MA 0211
0
-13
35
, USA.
*
* As a special exemption, TJ Saunders and other respective copyright holders
* give permission to link this program with OpenSSL, and distribute the
...
...
@@ -24,8 +23,6 @@
*
* This is mod_msg, contrib software for proftpd 1.2 and above.
* For more information contact TJ Saunders <tj@castaglia.org>.
*
* $Id: mod_msg.c,v 1.5 2004/05/26 19:43:18 tj Exp tj $
*/
#include
"conf.h"
...
...
@@ -39,10 +36,11 @@
# define MSGMAX 8192
#endif
/* MSGMAX */
#define MOD_MSG_VERSION "mod_msg/0.
4.1
"
#define MOD_MSG_VERSION "mod_msg/0.
5
"
#if PROFTPD_VERSION_NUMBER < 0x0001021001
# error "ProFTPD 1.2.10rc1 or later required"
/* Make sure the version of proftpd is as necessary. */
#if PROFTPD_VERSION_NUMBER < 0x0001030604
# error "ProFTPD 1.3.6 or later required"
#endif
#define MSG_PROJ_ID 246
...
...
@@ -52,9 +50,9 @@ extern pid_t mpid;
module
msg_module
;
#ifndef USE_CTRLS
#ifndef
PR_
USE_CTRLS
# error "mod_msg requires Controls support (--enable-ctrls)"
#endif
/* USE_CTRLS */
#endif
/*
PR_
USE_CTRLS */
static
ctrls_acttab_t
msg_acttab
[];
...
...
@@ -240,26 +238,27 @@ MODRET set_msgctrlsacls(cmd_rec *cmd) {
CONF_ERROR
(
cmd
,
pstrcat
(
cmd
->
tmp_pool
,
": unknown action: '"
,
bad_action
,
"'"
,
NULL
));
return
HANDLED
(
cmd
);
return
PR_
HANDLED
(
cmd
);
}
/* usage: MessageEngine on|off */
MODRET
set_msgengine
(
cmd_rec
*
cmd
)
{
int
bool
;
int
engine
=
-
1
;
config_rec
*
c
;
CHECK_ARGS
(
cmd
,
1
);
CHECK_CONF
(
cmd
,
CONF_ROOT
|
CONF_VIRTUAL
|
CONF_GLOBAL
);
bool
=
get_boolean
(
cmd
,
1
);
if
(
bool
==
-
1
)
engine
=
get_boolean
(
cmd
,
1
);
if
(
engine
==
-
1
)
{
CONF_ERROR
(
cmd
,
"expected Boolean parameter"
);
}
c
=
add_config_param
(
cmd
->
argv
[
0
],
1
,
NULL
);
c
->
argv
[
0
]
=
pcalloc
(
c
->
pool
,
sizeof
(
unsigned
char
));
*
((
unsigned
char
*
)
c
->
argv
[
0
])
=
bool
;
c
->
argv
[
0
]
=
pcalloc
(
c
->
pool
,
sizeof
(
int
));
*
((
int
*
)
c
->
argv
[
0
])
=
engine
;
return
HANDLED
(
cmd
);
return
PR_
HANDLED
(
cmd
);
}
/* usage: MessageLog path */
...
...
@@ -267,11 +266,12 @@ MODRET set_msglog(cmd_rec *cmd) {
CHECK_ARGS
(
cmd
,
1
);
CHECK_CONF
(
cmd
,
CONF_ROOT
);
if
(
pr_fs_valid_path
(
cmd
->
argv
[
1
])
<
0
)
if
(
pr_fs_valid_path
(
cmd
->
argv
[
1
])
<
0
)
{
CONF_ERROR
(
cmd
,
"must be an absolute path"
);
}
add_config_param_str
(
cmd
->
argv
[
0
],
1
,
cmd
->
argv
[
1
]);
return
HANDLED
(
cmd
);
return
PR_
HANDLED
(
cmd
);
}
/* usage: MessageQueue path */
...
...
@@ -279,11 +279,12 @@ MODRET set_msgqueue(cmd_rec *cmd) {
CHECK_ARGS
(
cmd
,
1
);
CHECK_CONF
(
cmd
,
CONF_ROOT
);
if
(
pr_fs_valid_path
(
cmd
->
argv
[
1
])
<
0
)
if
(
pr_fs_valid_path
(
cmd
->
argv
[
1
])
<
0
)
{
CONF_ERROR
(
cmd
,
"must be an absolute path"
);
}
msg_queue_path
=
pstrdup
(
msg_pool
,
cmd
->
argv
[
1
]);
return
HANDLED
(
cmd
);
return
PR_
HANDLED
(
cmd
);
}
/* Command handlers
...
...
@@ -293,64 +294,74 @@ MODRET msg_post_any(cmd_rec *cmd) {
register
unsigned
int
i
=
0
;
char
**
msgs
;
if
(
!
msg_engine
)
return
DECLINED
(
cmd
);
if
(
msg_engine
==
FALSE
)
{
return
PR_DECLINED
(
cmd
);
}
/* If there are no messages pending for this process, be done now. */
if
(
!
msg_pending_list
||
msg_pending_list
->
nelts
==
0
)
return
DECLINED
(
cmd
);
if
(
!
msg_pending_list
||
msg_pending_list
->
nelts
==
0
)
{
return
PR_DECLINED
(
cmd
);
}
/* Skip commands whose reply format is strictly proscribed. */
/* XXX there are probably more commands to be skipped here */
if
(
strcmp
(
cmd
->
argv
[
0
],
C_EPSV
)
==
0
||
strcmp
(
cmd
->
argv
[
0
],
C_PASV
)
==
0
||
strcmp
(
cmd
->
argv
[
0
],
C_STOU
)
==
0
)
return
DECLINED
(
cmd
);
strcmp
(
cmd
->
argv
[
0
],
C_STOU
)
==
0
)
{
return
PR_DECLINED
(
cmd
);
}
/* Tack on any messages to this command. */
msgs
=
(
char
**
)
msg_pending_list
->
elts
;
for
(
i
=
0
;
i
<
msg_pending_list
->
nelts
;
i
++
)
pr_response_add
(
R_DUP
,
msgs
[
i
]);
for
(
i
=
0
;
i
<
msg_pending_list
->
nelts
;
i
++
)
{
pr_response_add
(
R_DUP
,
"%s"
,
msgs
[
i
]);
}
/* Clear the pending pool. */
destroy_pool
(
msg_pending_pool
);
msg_pending_pool
=
NULL
;
msg_pending_list
=
NULL
;
return
DECLINED
(
cmd
);
return
PR_
DECLINED
(
cmd
);
}
MODRET
msg_post_err_any
(
cmd_rec
*
cmd
)
{
register
unsigned
int
i
=
0
;
char
**
msgs
;
if
(
!
msg_engine
)
return
DECLINED
(
cmd
);
if
(
msg_engine
==
FALSE
)
{
return
PR_DECLINED
(
cmd
);
}
/* If there are no messages pending for this process, be done now. */
if
(
!
msg_pending_list
||
msg_pending_list
->
nelts
==
0
)
return
DECLINED
(
cmd
);
if
(
!
msg_pending_list
||
msg_pending_list
->
nelts
==
0
)
{
return
PR_DECLINED
(
cmd
);
}
/* Skip commands whose reply format is strictly proscribed. */
/* XXX there are probably more commands to be skipped here */
if
(
strcmp
(
cmd
->
argv
[
0
],
C_EPSV
)
==
0
||
strcmp
(
cmd
->
argv
[
0
],
C_PASV
)
==
0
||
strcmp
(
cmd
->
argv
[
0
],
C_STOU
)
==
0
)
return
DECLINED
(
cmd
);
strcmp
(
cmd
->
argv
[
0
],
C_STOU
)
==
0
)
{
return
PR_DECLINED
(
cmd
);
}
/* Tack on any messages to this command. */
msgs
=
(
char
**
)
msg_pending_list
->
elts
;
for
(
i
=
0
;
i
<
msg_pending_list
->
nelts
;
i
++
)
pr_response_add_err
(
R_DUP
,
msgs
[
i
]);
for
(
i
=
0
;
i
<
msg_pending_list
->
nelts
;
i
++
)
{
pr_response_add_err
(
R_DUP
,
"%s"
,
msgs
[
i
]);
}
/* Clear the pending pool. */
destroy_pool
(
msg_pending_pool
);
msg_pending_pool
=
NULL
;
msg_pending_list
=
NULL
;
return
DECLINED
(
cmd
);
return
PR_
DECLINED
(
cmd
);
}
/* Event handlers
...
...
@@ -539,11 +550,14 @@ static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
/* Iterate through the scoreboard, looking for any sessions for the
* given user.
*/
if
(
pr_rewind_scoreboard
()
<
0
)
if
(
pr_rewind_scoreboard
()
<
0
)
{
(
void
)
pr_log_writefile
(
msg_logfd
,
MOD_MSG_VERSION
,
"error rewinding scoreboard: %s"
,
strerror
(
errno
));
}
while
((
score
=
pr_scoreboard_entry_read
())
!=
NULL
)
{
pr_signals_handle
();
while
((
score
=
pr_scoreboard_read_entry
())
!=
NULL
)
{
if
(
strcmp
(
user
,
score
->
sce_user
)
==
0
)
{
msg_know_dst
=
TRUE
;
...
...
@@ -553,10 +567,11 @@ static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
"error sending message to user '%s' (pid %u): %s"
,
user
,
score
->
sce_pid
,
strerror
(
errno
));
}
else
}
else
{
msg_sent
=
TRUE
;
}
}
}
pr_restore_scoreboard
();
...
...
@@ -565,7 +580,7 @@ static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
register
unsigned
int
i
=
0
;
pr_scoreboard_entry_t
*
score
=
NULL
;
const
char
*
addr
,
*
msgstr
=
""
;
pr_netaddr_t
*
na
;
const
pr_netaddr_t
*
na
;
if
(
reqargc
==
1
)
{
pr_ctrls_add_response
(
ctrl
,
"msg host: missing required host name"
);
...
...
@@ -603,11 +618,14 @@ static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
/* Iterate through the scoreboard, looking for any sessions for the
* given address.
*/
if
(
pr_rewind_scoreboard
()
<
0
)
if
(
pr_rewind_scoreboard
()
<
0
)
{
(
void
)
pr_log_writefile
(
msg_logfd
,
MOD_MSG_VERSION
,
"error rewinding scoreboard: %s"
,
strerror
(
errno
));
}
while
((
score
=
pr_scoreboard_entry_read
())
!=
NULL
)
{
pr_signals_handle
();
while
((
score
=
pr_scoreboard_read_entry
())
!=
NULL
)
{
if
(
strcmp
(
addr
,
score
->
sce_client_addr
)
==
0
)
{
msg_know_dst
=
TRUE
;
...
...
@@ -617,10 +635,11 @@ static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
"error sending message to host '%s' (pid %u): %s"
,
reqargv
[
1
],
score
->
sce_pid
,
strerror
(
errno
));
}
else
}
else
{
msg_sent
=
TRUE
;
}
}
}
pr_restore_scoreboard
();
...
...
@@ -654,11 +673,14 @@ static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
return
-
1
;
}
if
(
pr_rewind_scoreboard
()
<
0
)
if
(
pr_rewind_scoreboard
()
<
0
)
{
(
void
)
pr_log_writefile
(
msg_logfd
,
MOD_MSG_VERSION
,
"error rewinding scoreboard: %s"
,
strerror
(
errno
));
}
while
((
score
=
pr_scoreboard_entry_read
())
!=
NULL
)
{
pr_signals_handle
();
while
((
score
=
pr_scoreboard_read_entry
())
!=
NULL
)
{
if
(
strcmp
(
score
->
sce_class
,
class
)
==
0
)
{
msg_know_dst
=
TRUE
;
...
...
@@ -668,10 +690,11 @@ static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
"error sending message to class '%s' (pid %u): %s"
,
reqargv
[
1
],
score
->
sce_pid
,
strerror
(
errno
));
}
else
}
else
{
msg_sent
=
TRUE
;
}
}
}
pr_restore_scoreboard
();
...
...
@@ -700,21 +723,25 @@ static int msg_handle_msg(pr_ctrls_t *ctrl, int reqargc, char **reqargv) {
return
-
1
;
}
if
(
pr_rewind_scoreboard
()
<
0
)
if
(
pr_rewind_scoreboard
()
<
0
)
{
(
void
)
pr_log_writefile
(
msg_logfd
,
MOD_MSG_VERSION
,
"error rewinding scoreboard: %s"
,
strerror
(
errno
));
}
msg_know_dst
=
TRUE
;
while
((
score
=
pr_scoreboard_read_entry
())
!=
NULL
)
{
while
((
score
=
pr_scoreboard_entry_read
())
!=
NULL
)
{
pr_signals_handle
();
if
(
msg_send_msg
(
score
->
sce_pid
,
msgstr
)
<
0
)
{
msg_errno
=
errno
;
(
void
)
pr_log_writefile
(
msg_logfd
,
MOD_MSG_VERSION
,
"error sending message to all (pid %u): %s"
,
reqargv
[
1
],
score
->
sce_pid
,
strerror
(
errno
));
"error sending message to all (pid %
l
u): %s"
,
(
unsigned
long
)
score
->
sce_pid
,
strerror
(
errno
));
}
else
}
else
{
msg_sent
=
TRUE
;
}
}
pr_restore_scoreboard
();
...
...
mod_msg.html
View file @
0afd7993
<!-- $Id: mod_msg.html,v 1.1 2004/04/10 01:51:39 tj Exp tj $ -->
<!-- $Source: /home/tj/proftpd/modules/doc/RCS/mod_msg.html,v $ -->
<!DOCTYPE html>
<html>
<head>
<title>
ProFTPD module mod_msg
</title>
...
...
@@ -177,20 +175,12 @@ Example configuration:
</pre>
<p>
<hr><br>
Author:
<i>
$Author: tj $
</i><br>
Last Updated:
<i>
$Date: 2004/04/10 01:51:39 $
</i><br>
<br><hr>
<hr>
<font
size=
2
><b><i>
©
Copyright 20
04
TJ Saunders
<br>
©
Copyright 20
17
TJ Saunders
<br>
All Rights Reserved
<br>
</i></b></font>
<hr><br>
<hr>
</body>
</html>