home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume43
/
ftpmail
/
part01
next >
Wrap
Internet Message Format
|
1994-07-07
|
58KB
From: lmjm@doc.ic.ac.uk (Lee M J McLoughlin)
Newsgroups: comp.sources.misc
Subject: v43i072: ftpmail - Automatic Email to FTP Gateway, v1.23, Part01/03
Date: 7 Jul 1994 18:06:08 -0500
Organization: Sterling Software
Sender: kent@sparky.sterling.com
Approved: kent@sparky.sterling.com
Message-ID: <csm-v43i072=ftpmail.180556@sparky.sterling.com>
X-Md4-Signature: d257edd0be5c73c3053eba4c2500ba7a
Submitted-by: lmjm@doc.ic.ac.uk (Lee M J McLoughlin)
Posting-number: Volume 43, Issue 72
Archive-name: ftpmail/part01
Environment: UNIX, Perl, Sun, Dec, INET
Supersedes: ftpmail: Volume 37, Issue 51-52
Ftpmail is an email->ftp gateway. You mail requests to a user (eg:
ftpmail). This causes q.pl to be called which checks the request and
sticks it in a queue. dq.pl then parses the queue and does the ftp
transfers that the job specifies mailing back the files that were
transfers. As various things happen notes are writen in the
ftpmail log file.
It is all writen in perl and sends responses using either mail or by
directly calling sendmail. When using sendmail MIME support is
available.
If a transfer fails for a fatal reason then it is dequed and the user
is emailed. If it fails for a non-fatal reason (such as timeout on
connect) then it will be requeued to try later (the next time dq.pl is
called). Once a transfer (get|dir|ls) has succeeded it is marked as
DONE and will be skipped. All other commands will still be obeyed. A
job will only be tried for a fix number of times, then rejected.
For user level details read the help file.
If the file motd is present then its contents are inserted at
the start of any responses.
WORKERS
-------
If you want to help develop ftpmail then there is now a mailing list:
ftpmail-workers@doc.ic.ac.uk
To subscribe email to: ftpmail-workers-request@doc.ic.ac.uk
a message like:
Subject: add me
subscribe ftpmail-workers Your Full Name Here
UPGRADING
---------
If you are upgrading to 1.23 from an earlier release you may want to make
use of some new features.
* You can now have multiple dq processes running in parallel. This is all
controlled by the $max_dqs variable in config.pl. The interface is
designed to not need any changes to the cron or other startup
scripts. The main dq will automatically spawn off the slaves. All
dq's will check the status of all dq's every 5 mins, spot any dead
dq's and restart them. So you shouldn't need to change and cron or
other startup scripts. This *requires* fcntl based locking code.
Unless you have a lot of traffic you should probably not bother setting
max_dq's to anything other than 1.
* Jobs can be given a priority base on the site the user is trying to reach.
See @site_priorities. This is based on ideas by Kurt Jaeger
<pi@rus.uni-stuttgart.de>. The first digit of queue items is a priority
character. The max length of a filename is still the same, for old USG
filename restrictions.
To rename all the jobs to the new scheme use the command req in the
home directory of ftpmail. After that incoming deletes won't work for a
while as jobs will have been renamed. You could just let the old entries
alone and let them be processed out of the queue.
* The sites that can be accessed via ftpmail can be resticted with the
$ftp_permitted pattern.
* You can now vrfy addresses before trying to reply to them by using the
external vrfy command, courtesy of Christophe.Wolfhugel@hsc-sec.fr
* There is now an rfc parser, courtesy of Alan Barrett
<barrett@daisy.ee.und.ac.za>.
* q.pl is now very much better at handling weird input. It can now cope
with bizzare mailers that insert a second copy of the mail headers and
whitespace in front of all the mail body!
* The max_tries field is now obeyed correctly.
* The delay between tries is now 2 hours.
* killfm is a simple program to shutdown all the dq proc's. It is not meant
to be used casually.
THANKS
------
Thanks to all those who suggested improvements. Also special thanks
to Christophe.Wolfhugel@hsc-sec.fr for all his work on mime
and the new queing system which formed the basis for some of the new code.
---------
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# Contents: README README.upgrade ftp.pl pp_mailfilter q.pl
# Wrapped by kent@sparky on Thu Jul 7 17:54:04 1994
PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 1 (of 3)."'
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(4550 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XFtpmail is an email->ftp gateway. You mail requests to a user (eg:
Xftpmail). This causes q.pl to be called which checks the request and
Xsticks it in a queue. dq.pl then parses the queue and does the ftp
Xtransfers that the job specifies mailing back the files that were
Xtransfers. As various things happen notes are writen in the
Xftpmail log file.
X
XIt is all writen in perl and sends responses using either mail or by
Xdirectly calling sendmail. When using sendmail MIME support is
Xavailable.
X
XIf a transfer fails for a fatal reason then it is dequed and the user
Xis emailed. If it fails for a non-fatal reason (such as timeout on
Xconnect) then it will be requeued to try later (the next time dq.pl is
Xcalled). Once a transfer (get|dir|ls) has succeeded it is marked as
XDONE and will be skipped. All other commands will still be obeyed. A
Xjob will only be tried for a fix number of times, then rejected.
X
XFor user level details read the help file.
X
XIf the file motd is present then its contents are inserted at
Xthe start of any responses.
X
XARCHIVES
X--------
XThis packages is available from:
X file://src.doc.ic.ac.uk/packages/ftpmail/
X http://src.doc.ic.ac.uk/packages/ftpmail/
X gopher://src.doc.ic.ac.uk/1/packages/ftpmail/
X
X file://grasp.univ-lyon1.fr/pub/unix/mail/tools/ftpmail/
X
X file://ftp.sterling.com/mail/ftpmail/
X
X
XUPGRADING
X---------
X
XSee README.upgrade.
X
X
XWHERE SERVERS ARE
X-----------------
X
XSee the WWW file: http://src.doc.ic.ac.uk/ftpmail-servers.html
Xor FTP file://src.doc.ic.ac.uk/packages/ftpmail/ftpmail-servers.txt
Xfor details about where ftpmail servers are running.
X
X
XTO INSTALL
X----------
X
XCreate an account called 'ftpmail', the home directory of ftpmail is
Xwhere all the scripts will be installed and subdirectories of it form
Xthe queues.
X
XEdit config.pl to reflect your local details. (If you
Xchange the default site also edit help.) The auth file is
Xjust a series of regexps, so a line of just dot would allow all email
Xaddresses to use ftpmail.
X
XOnce you have edited the above files run inst.pl. inst.pl
Xwill create the ftpmail directories based on values in
Xconfig.pl and copy in various files. Its a bit of a
Xhack.
X
XAt src.doc.ic.ac.uk I only allow requests to be submitted via email.
XThe ftpmail account is not present on any general machine, just on the
Xmain mail gateway . On that I use the PP .mailfilter script mechanism
Xto cause any mail delivered to that ftpmail to invoke q.pl. But
Xanything that causes q.pl to be run on the input request will do.
XUnder sendmail create ~ftpmail/.forward containing:
X|"/public/ic.doc/ftpmail/q.pl || exit 75"
X(Or similar.)
X
XThe file crontab contains a suggested cron entry that should be run as
Xthe user ftpmail. This calls dq.pl that dequeues the entries and
Xruns them. dq.pl should run forever once started. But as I am a
Xparanoid person I call it every half hour just to be safe.
X
XNote that mail sent is sent by ftpmail not ftpmail-request. ftpmail
Xdoes other tricks to prevent mail loops forming. I tried running with
Xmail being sent by ftpmail-request and ftpmail-request aliases to me.
XI found that most of the traffic to ftpmail-request is from people who
Xsubmit jobs by replying to ftpmail responses in order to submit new
Xjobs.
X
XSOCKET.PH && SOLARIS 2.x
X------------------------
XAlthough there is a socket.ph in this distribution you should really
Xuse the one generated by h2ph when installing perl. Socket.ph is
Xarchitecture specific so the socket.ph I use is unlikely to work unless
Xyou are on a Sparc running Sunos 4.1.x.
X
XUnfortunetly the sys/socket.ph generated on Solaris 2.x by perl's h2ph is
Xincorrect so you will have to install socket.ph-solaris from this distribution
Xas /usr/local/lib/perl/sys/socket.ph (or whereever appropriate on your
Xsystem).
X
XTHANKS
X------
XThanks to all those who suggested improvements. Also special thanks
Xto Christophe.Wolfhugel@hsc-sec.fr for all his work on mime
Xand the new queing system which formed the basis for some of the new code.
X
XWORKERS
X-------
XIf you want to help develope ftpmail then there is now a mailing list:
X ftpmail-workers@doc.ic.ac.uk
XTo subscribe email to: ftpmail-workers-request@doc.ic.ac.uk
Xa message like:
X Subject: add me
X
X subscribe ftpmail-workers Your Full Name Here
X
XCOPYRIGHT
X---------
XWriten by Lee McLoughlin <lmjm@doc.ic.ac.uk>
X
XYou can do what you like with this except claim that you wrote it or
Xgive copies with changes not approved by Lee. Neither Lee nor any other
Xorganisation can be held liable for any problems caused by the use or
Xstorage of this package.
END_OF_FILE
if test 4550 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'README.upgrade' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README.upgrade'\"
else
echo shar: Extracting \"'README.upgrade'\" \(1996 characters\)
sed "s/^X//" >'README.upgrade' <<'END_OF_FILE'
XIf you are upgrading to 1.23 from an earlier release you may want to make
Xuse of some new features.
X
X* You can now have multiple dq processes running in parallel. This is all
X controlled by the $max_dqs variable in config.pl. The interface is
X designed to not need any changes to the cron or other startup
X scripts. The main dq will automatically spawn off the slaves. All
X dq's will check the status of all dq's every 5 mins, spot any dead
X dq's and restart them. So you shouldn't need to change and cron or
X other startup scripts. This *requires* fcntl based locking code.
X
X Unless you have a lot of traffic you should probably not bother setting
X max_dq's to anything other than 1.
X
X* Jobs can be given a priority base on the site the user is trying to reach. See
X @site_priorities. This is based on ideas by Kurt Jaeger <pi@rus.uni-stuttgart.de>.
X The first digit of queue items is a priority character. The max length
X of a filename is still the same, for old USG filename restrictions.
X
X To rename all the jobs to the new scheme use the command req in the
X home directory of ftpmail. After that incoming deletes won't work for a while
X as jobs will have been renamed. You could just let the old entries alone
X and let them be processed out of the queue.
X
X* The sites that can be accessed via ftpmail can be resticted with the $ftp_permitted
X pattern.
X
X* You can now vrfy addresses before trying to reply to them by using the external
X vrfy command, courtesy of Christophe.Wolfhugel@hsc-sec.fr
X
X* There is now an rfc parser, courtesy of Alan Barrett <barrett@daisy.ee.und.ac.za>.
X
X* q.pl is now very much better at handling weird input. It can now cope with
X bizzare mailers that insert a second copy of the mail headers and whitespace in
X front of all the mail body!
X
X* The max_tries field is now obeyed correctly.
X
X* The delay between tries is now 2 hours.
X
X* killfm is a simple program to shutdown all the dq proc's. It is not meant
X to be used casually.
END_OF_FILE
if test 1996 -ne `wc -c <'README.upgrade'`; then
echo shar: \"'README.upgrade'\" unpacked with wrong size!
fi
# end of 'README.upgrade'
fi
if test -f 'ftp.pl' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ftp.pl'\"
else
echo shar: Extracting \"'ftp.pl'\" \(24614 characters\)
sed "s/^X//" >'ftp.pl' <<'END_OF_FILE'
X#-*-perl-*-
X# This is a wrapper to the lchat.pl routines that make life easier
X# to do ftp type work.
X# Mostly by Lee McLoughlin <lmjm@doc.ic.ac.uk>
X# based on original version by Alan R. Martello <al@ee.pitt.edu>
X# And by A.Macpherson@bnr.co.uk for multi-homed hosts
X#
X# Basic usage:
X# $ftp_port = 21;
X# $retry_call = 1;
X# $attempts = 2;
X# if( &ftp'open( $site, $ftp_port, $retry_call, $attempts ) != 1 ){
X# die "failed to open ftp connection";
X# }
X# if( ! &ftp'login( $user, $pass ) ){
X# die "failed to login";
X# }
X# &ftp'type( $text_mode ? 'A' : 'I' );
X# if( ! &ftp'get( $remote_filename, $local_filename, 0 ) ){
X# die "failed to get file;
X# }
X# &ftp'quit();
X#
X#
X# $Id: ftp.pl,v 2.6 1994/06/06 18:37:37 lmjm Exp lmjm $
X# $Log: ftp.pl,v $
X# Revision 2.6 1994/06/06 18:37:37 lmjm
X# Switched to lchat - a subset of chat.
X# Allow for 'remote help's need to parse the help strings in the continuations
X# Use real_site for proxy connections.
X# Allow for cr stripping and corrected use of buffer (from Andrew).
X#
X# Revision 2.5 1994/04/29 20:11:04 lmjm
X# Converted to use rfc1123.
X#
X# Revision 2.4 1994/01/26 14:59:07 lmjm
X# Added DG result code.
X#
X# Revision 2.3 1994/01/18 21:58:18 lmjm
X# Reduce calls to sigset.
X# Reset to old signal after use.
X#
X# Revision 2.2 1993/12/14 11:09:06 lmjm
X# Use installed socket.ph.
X# Allow for more returns.
X#
X# Revision 2.1 1993/06/28 15:02:00 lmjm
X# Full 2.1 release
X#
X#
X
Xrequire 'sys/socket.ph';
X# lchat.pl is a special subset of chat2.pl that avoids some memory leaks.
Xrequire 'lchat.pl';
X
X
Xpackage ftp;
X
X$retry_pause = 60; # Pause before retrying a login.
X
Xif( defined( &main'PF_INET ) ){
X $pf_inet = &main'PF_INET;
X $sock_stream = &main'SOCK_STREAM;
X local($name, $aliases, $proto) = getprotobyname( 'tcp' );
X $tcp_proto = $proto;
X}
Xelse {
X # XXX hardwired $PF_INET, $SOCK_STREAM, 'tcp'
X # but who the heck would change these anyway? (:-)
X $pf_inet = 2;
X $sock_stream = 1;
X $tcp_proto = 6;
X}
X
X# If the remote ftp daemon doesn't respond within this time presume its dead
X# or something.
X$timeout = 120;
X
X# Timeout a read if I don't get data back within this many seconds
X$timeout_read = 3 * $timeout;
X
X# Timeout an open
X$timeout_open = $timeout;
X
X$ftp'version = '$Revision: 2.6 $';
X
X# This is a "global" it contains the last response from the remote ftp server
X# for use in error messages
X$ftp'response = "";
X# Also ftp'NS is the socket containing the data coming in from the remote ls
X# command.
X
X# The size of block to be read or written when talking to the remote
X# ftp server
X$ftp'ftpbufsize = 4096;
X
X# How often to print a hash out, when debugging
X$ftp'hashevery = 1024;
X# Output a newline after this many hashes to prevent outputing very long lines
X$ftp'hashnl = 70;
X
X# Is there a connection open?
X$ftp'service_open = 0;
X
X# If a proxy connection then who am I really talking to?
X$real_site = "";
X
X# Where error/log reports are sent to
X$ftp'showfd = 'STDERR';
X
X# Should a 421 be treated as a connection close and return 99 from
X# ftp'expect. This is against rfc1123 recommendations but I've found
X# it to be a wise default.
X$drop_on_421 = 1;
X
X# Name of a function to call on a pathname to map it into a remote
X# pathname.
X$ftp'mapunixout = '';
X$ftp'manunixin = '';
X
X# This is just a tracing aid.
X$ftp_show = 0;
X
X# Wether to keep the continuation messages so the user can look at them
X$ftp'keep_continuations = 0;
X
X# Uncomment to turn on lots of debugging.
X# &ftp'debug( 10 );
X
Xsub ftp'debug
X{
X $ftp_show = @_[0];
X if( $ftp_show > 9 ){
X $chat'debug = 1;
X }
X}
X
Xsub ftp'set_timeout
X{
X local( $to ) = @_;
X return if $to == $timeout;
X $timeout = $to;
X $timeout_open = $timeout;
X $timeout_read = 3 * $timeout;
X if( $ftp_show ){
X print $ftp'showfd "ftp timeout set to $timeout\n";
X }
X}
X
X
Xsub ftp'open_alarm
X{
X die "timeout: open";
X}
X
Xsub ftp'timed_open
X{
X local( $site, $ftp_port, $retry_call, $attempts ) = @_;
X local( $connect_site, $connect_port );
X local( $ret );
X
X alarm( $timeout_open );
X
X while( $attempts-- ){
X if( $ftp_show ){
X print $ftp'showfd "proxy connecting via $proxy_gateway [$proxy_ftp_port]\n" if $proxy;
X print $ftp'showfd "Connecting to $site";
X if( $ftp_port != 21 ){
X print $ftp'showfd " [port $ftp_port]";
X }
X print $ftp'showfd "\n";
X }
X
X if( $proxy ) {
X if( ! $proxy_gateway ) {
X # if not otherwise set
X $proxy_gateway = "internet-gateway";
X }
X if( $debug ) {
X print $ftp'showfd "using proxy services of $proxy_gateway, ";
X print $ftp'showfd "at $proxy_ftp_port\n";
X }
X $connect_site = $proxy_gateway;
X $connect_port = $proxy_ftp_port;
X $real_site = $site;
X }
X else {
X $connect_site = $site;
X $connect_port = $ftp_port;
X }
X if( ! &chat'open_port( $connect_site, $connect_port ) ){
X if( $retry_call ){
X print $ftp'showfd "Failed to connect\n" if $ftp_show;
X next;
X }
X else {
X print $ftp'showfd "proxy connection failed " if $proxy;
X print $ftp'showfd "Cannot open ftp to $connect_site\n" if $ftp_show;
X return 0;
X }
X }
X $ret = &ftp'expect( $timeout,
X 220, 1 ); # ready for login to $site
X if( $ret != 1 ){
X &chat'close();
X next;
X }
X return 1;
X }
X continue {
X print $ftp'showfd "Pausing between retries\n";
X sleep( $retry_pause );
X }
X return 0;
X}
X
Xsub main'ftp__sighandler
X{
X local( $sig ) = @_;
X local( $msg ) = "Caught a SIG$sig flagging connection down";
X $ftp'service_open = 0;
X if( $ftp_logger ){
X eval "&$ftp_logger( \$msg )";
X }
X}
X
Xsub ftp'set_signals
X{
X $ftp_logger = @_;
X $SIG{ 'PIPE' } = "ftp__sighandler";
X}
X
X# Set the mapunixout and mapunixin functions
Xsub ftp'set_namemap
X{
X ($ftp'mapunixout, $ftp'mapunixin) = @_;
X if( $debug ) {
X print $ftp'showfd "mapunixout = $ftp'mapunixout, $mapunixin = $ftp'mapunixin\n";
X }
X}
X
X
Xsub ftp'open
X{
X local( $site, $ftp_port, $retry_call, $attempts ) = @_;
X
X local( $old_sig ) = $SIG{ 'ALRM' };
X $SIG{ 'ALRM' } = "ftp\'open_alarm";
X
X local( $ret ) = eval "&timed_open( '$site', $ftp_port, $retry_call, $attempts )";
X alarm( 0 );
X $SIG{ 'ALRM' } = $old_sig;
X
X if( $@ =~ /^timeout/ ){
X return -1;
X }
X
X if( $ret ){
X $ftp'service_open = 1;
X }
X
X return $ret;
X}
X
Xsub ftp'login
X{
X local( $remote_user, $remote_password ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( $proxy ){
X # Should site or real_site be used here?
X &ftp'send( "USER $remote_user@$real_site" );
X }
X else {
X &ftp'send( "USER $remote_user" );
X }
X $ret = &ftp'expect( $timeout,
X 230, 1, # $remote_user logged in
X 331, 2, # send password for $remote_user
X 332, 0 ); # account for login - not yet supported
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X if( $ret == 1 ){
X # Logged in no password needed
X return 1;
X }
X elsif( $ret == 2 ){
X # A password is needed
X &ftp'send( "PASS $remote_password" );
X
X $ret = &ftp'expect( $timeout,
X 230, 1 ); # $remote_user logged in
X if( $ret == 99 ){
X &service_closed();
X }
X elsif( $ret == 1 ){
X # Logged in
X return 1;
X }
X }
X # If I got here I failed to login
X return 0;
X}
X
Xsub service_closed
X{
X $ftp'service_open = 0;
X &chat'close();
X}
X
Xsub ftp'close
X{
X &ftp'quit();
X $ftp'service_open = 0;
X &chat'close();
X}
X
X# Change directory
X# return 1 if successful
X# 0 on a failure
Xsub ftp'cwd
X{
X local( $dir ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( $ftp'mapunixout ){
X $dir = eval "&$ftp'mapunixout( \$dir, 'd' )";
X }
X
X &ftp'send( "CWD $dir" );
X
X $ret = &ftp'expect( $timeout,
X 2, 1 ); # working directory = $dir
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X
X return $ret;
X}
X
X# Get a full directory listing:
X# &ftp'dir( remote LIST options )
X# Start a list going with the given options.
X# Presuming that the remote deamon uses the ls command to generate the
X# data to send back then then you can send it some extra options (eg: -lRa)
X# return 1 if sucessful and 0 on a failure
Xsub ftp'dir_open
X{
X local( $options ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( ! &ftp'open_data_socket() ){
X return 0;
X }
X
X if( $options ){
X &ftp'send( "LIST $options" );
X }
X else {
X &ftp'send( "LIST" );
X }
X
X $ret = &ftp'expect( $timeout,
X 150, 1 ); # reading directory
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X
X if( ! $ret ){
X &ftp'close_data_socket;
X return 0;
X }
X
X accept( NS, S ) || die "accept failed $!";
X
X #
X # the data should be coming at us now
X #
X
X return 1;
X}
X
X
X# Close down reading the result of a remote ls command
X# return 1 if successful and 0 on failure
Xsub ftp'dir_close
X{
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X # read the close
X #
X $ret = &ftp'expect($timeout,
X 2, 1 ); # transfer complete, closing connection
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X
X # shut down our end of the socket
X &ftp'close_data_socket;
X
X if( ! $ret ){
X return 0;
X }
X
X return 1;
X}
X
X# Quit from the remote ftp server
X# return 1 if successful and 0 on failure
Xsub ftp'quit
X{
X local( $ret );
X
X $site_command_check = 0;
X @site_command_list = ();
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X &ftp'send( "QUIT" );
X
X $ret = &ftp'expect( $timeout,
X 221, 1 ); # transfer complete, closing connection
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X return $ret;
X}
X
X# Support for ftp'read
Xsub ftp'read_alarm
X{
X die "timeout: read";
X}
X
X# Support for ftp'read
Xsub ftp'timed_read
X{
X alarm( $timeout_read );
X
X return sysread( NS, $ftpbuf, $ftpbufsize );
X}
X
X# Do not use this routing use ftp'get
Xsub ftp'read
X{
X if( ! $ftp'service_open ){
X return -1;
X }
X
X local( $ret ) = eval '&timed_read()';
X alarm( 0 );
X
X if( $@ =~ /^timeout/ ){
X return -1;
X }
X return $ret;
X}
X
Xsub ftp'dostrip
X{
X ($strip_cr ) = @_;
X}
X
X# Get a remote file back into a local file.
X# If no loc_fname passed then uses rem_fname.
X# returns 1 on success and 0 on failure
Xsub ftp'get
X{
X local($rem_fname, $loc_fname, $restart ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( $loc_fname eq "" ){
X $loc_fname = $rem_fname;
X }
X
X if( ! &ftp'open_data_socket() ){
X print $ftp'showfd "Cannot open data socket\n";
X return 0;
X }
X
X if( $loc_fname ne '-' ){
X # Find the size of the target file
X local( $restart_at ) = &ftp'filesize( $loc_fname );
X if( $restart && $restart_at > 0 && &ftp'restart( $restart_at ) ){
X $restart = 1;
X # Make sure the file can be updated
X chmod( 0644, $loc_fname );
X }
X else {
X $restart = 0;
X unlink( $loc_fname );
X }
X }
X
X if( $ftp'mapunixout ){
X $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
X }
X
X &ftp'send( "RETR $rem_fname" );
X
X $ret = &ftp'expect( $timeout,
X 150, 1 ); # receiving $rem_fname
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X if( $ret != 1 ){
X print $ftp'showfd "Failure on 'RETR $rem_fname' command\n";
X
X # shut down our end of the socket
X &ftp'close_data_socket;
X
X return 0;
X }
X
X accept( NS, S ) || die "accept failed $!";
X
X #
X # the data should be coming at us now
X #
X
X #
X # open the local fname
X # concatenate on the end if restarting, else just overwrite
X if( !open( FH, ($restart ? '>>' : '>') . $loc_fname ) ){
X print $ftp'showfd "Cannot create local file $loc_fname\n";
X
X # shut down our end of the socket
X &ftp'close_data_socket;
X
X return 0;
X }
X
X local( $start_time ) = time;
X local( $bytes, $lasthash, $hashes ) = (0, 0, 0);
X local( $old_sig ) = $SIG{ 'ALRM' };
X $SIG{ 'ALRM' } = "ftp\'read_alarm";
X while( ($len = &ftp'read()) > 0 ){
X $bytes += $len;
X if( $strip_cr ){
X $ftp'ftpbuf =~ s/\r//g;
X }
X if( $ftp_show ){
X while( $bytes > ($lasthash + $ftp'hashevery) ){
X print $ftp'showfd '#';
X $lasthash += $ftp'hashevery;
X $hashes++;
X if( ($hashes % $ftp'hashnl) == 0 ){
X print $ftp'showfd "\n";
X }
X }
X }
X if( ! print FH $ftp'ftpbuf ){
X print $ftp'showfd "\nfailed to write data";
X $bytes = -1;
X last;
X }
X }
X $SIG{ 'ALRM' } = $old_sig;
X close( FH );
X
X # shut down our end of the socket
X &ftp'close_data_socket;
X
X if( $len < 0 ){
X print $ftp'showfd "\ntimed out reading data!\n";
X
X return 0;
X }
X
X if( $ftp_show && $bytes > 0 ){
X if( $hashes && ($hashes % $ftp'hashnl) != 0 ){
X print $ftp'showfd "\n";
X }
X local( $secs ) = (time - $start_time);
X if( $secs <= 0 ){
X $secs = 1; # To avoid a divide by zero;
X }
X
X local( $rate ) = int( $bytes / $secs );
X print $ftp'showfd "Got $bytes bytes ($rate bytes/sec)\n";
X }
X
X #
X # read the close
X #
X
X $ret = &ftp'expect( $timeout,
X 2, 1 ); # transfer complete, closing connection
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X
X if( $ret && $bytes < 0 ){
X $ret = 0;
X }
X
X return $ret;
X}
X
Xsub ftp'delete
X{
X local( $rem_fname ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( $ftp'mapunixout ){
X $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
X }
X
X &ftp'send( "DELE $rem_fname" );
X
X $ret = &ftp'expect( $timeout,
X 250, 1 ); # Deleted $rem_fname
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X
X return $ret == 1;
X}
X
Xsub ftp'deldir
X{
X local( $fname ) = @_;
X
X # not yet implemented
X # RMD
X}
X
X# UPDATE ME!!!!!!
X# Add in the hash printing and newline conversion
Xsub ftp'put
X{
X local( $loc_fname, $rem_fname ) = @_;
X local( $strip_cr );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( $loc_fname eq "" ){
X $loc_fname = $rem_fname;
X }
X
X if( ! &ftp'open_data_socket() ){
X return 0;
X }
X
X if( $ftp'mapunixout ){
X $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
X }
X
X &ftp'send( "STOR $rem_fname" );
X
X #
X # the data should be coming at us now
X #
X
X local( $ret ) =
X &ftp'expect( $timeout,
X 150, 1 ); # sending $loc_fname
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X
X if( $ret != 1 ){
X # shut down our end of the socket
X &ftp'close_data_socket;
X
X return 0;
X }
X
X
X accept( NS, S ) || die "accept failed $!";
X
X #
X # the data should be coming at us now
X #
X
X #
X # open the local fname
X #
X if( !open( FH, "<$loc_fname" ) ){
X print $ftp'showfd "Cannot open local file $loc_fname\n";
X
X # shut down our end of the socket
X &ftp'close_data_socket;
X
X return 0;
X }
X
X while( <FH> ){
X if( ! $ftp'service_open ){
X last;
X }
X print NS ;
X }
X close( FH );
X
X # shut down our end of the socket to signal EOF
X &ftp'close_data_socket;
X
X #
X # read the close
X #
X
X $ret = &ftp'expect( $timeout,
X 2, 1 ); # transfer complete, closing connection
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X if( ! $ret ){
X print $ftp'showfd "Failure on 'STOR $loc_fname' command\n";
X }
X return $ret;
X}
X
Xsub ftp'restart
X{
X local( $restart_point, $ret ) = @_;
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X &ftp'send( "REST $restart_point" );
X
X #
X # see what they say
X
X $ret = &ftp'expect( $timeout,
X 350, 1 ); # restarting at $restart_point
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X return $ret;
X}
X
X# Set the file transfer type
Xsub ftp'type
X{
X local( $type ) = @_;
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X &ftp'send( "TYPE $type" );
X
X #
X # see what they say
X
X $ret = &ftp'expect( $timeout,
X 2, 1 ); # file type set to $type
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X return $ret;
X}
X
X$site_command_check = 0;
X@site_command_list = ();
X
X# routine to query the remote server for 'SITE' commands supported
Xsub ftp'site_commands
X{
X local( $ret );
X
X @site_command_list = ();
X $site_command_check = 0;
X
X if( ! $ftp'service_open ){
X return @site_command_list;
X }
X
X # if we havent sent a 'HELP SITE', send it now
X if( !$site_command_check ){
X
X $site_command_check = 1;
X
X &ftp'send( "HELP SITE" );
X
X # assume the line in the HELP SITE response with the 'HELP'
X # command is the one for us
X $ftp'keep_continuations = 1;
X $ret = &ftp'expect( $timeout,
X ".*HELP.*", 1 );
X $ftp'keep_continuations = 0;
X if( $ret == 99 ){
X &service_closed();
X return @site_command_list;
X }
X
X if( $ret != 0 ){
X print $ftp'showfd "No response from HELP SITE ($ret)\n" if( $ftp_show );
X }
X
X @site_command_list = split(/\s+/, $ftp'response);
X }
X
X return @site_command_list;
X}
X
X# return the pwd, or null if we can't get the pwd
Xsub ftp'pwd
X{
X local( $ret, $cwd );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X &ftp'send( "PWD" );
X
X #
X # see what they say
X
X $ret = &ftp'expect( $timeout,
X 2, 1 ); # working dir is
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X if( $ret ){
X if( $ftp'response =~ /^2\d\d\s*"(.*)"\s.*$/ ){
X $cwd = $1;
X }
X }
X return $cwd;
X}
X
X# return 1 for success, 0 for failure
Xsub ftp'mkdir
X{
X local( $path ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( $ftp'mapunixout ){
X $path = eval "&$ftp'mapunixout( \$path, 'f' )";
X }
X
X &ftp'send( "MKD $path" );
X
X #
X # see what they say
X
X $ret = &ftp'expect( $timeout,
X 257, 1 ); # made directory $path
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X return $ret;
X}
X
X# return 1 for success, 0 for failure
Xsub ftp'chmod
X{
X local( $path, $mode ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( $ftp'mapunixout ){
X $path = eval "&$ftp'mapunixout( \$path, 'f' )";
X }
X
X &ftp'send( sprintf( "SITE CHMOD %o $path", $mode ) );
X
X #
X # see what they say
X
X $ret = &ftp'expect( $timeout,
X 2, 1 ); # chmod $mode $path succeeded
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X return $ret;
X}
X
X# rename a file
Xsub ftp'rename
X{
X local( $old_name, $new_name ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X if( $ftp'mapunixout ){
X $old_name = eval "&$ftp'mapunixout( \$old_name, 'f' )";
X }
X
X &ftp'send( "RNFR $old_name" );
X
X #
X # see what they say
X
X $ret = &ftp'expect( $timeout,
X 350, 1 ); # OK
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X
X # check if the "rename from" occurred ok
X if( $ret ){
X if( $ftp'mapunixout ){
X $new_name = eval "&$ftp'mapunixout( \$new_name, 'f' )";
X }
X
X &ftp'send( "RNTO $new_name" );
X
X #
X # see what they say
X
X $ret = &ftp'expect( $timeout,
X 250, 1 ); # rename $old_name to $new_name
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X }
X
X return $ret;
X}
X
X
Xsub ftp'quote
X{
X local( $cmd ) = @_;
X local( $ret );
X
X if( ! $ftp'service_open ){
X return 0;
X }
X
X &ftp'send( $cmd );
X
X $ret = &ftp'expect( $timeout,
X 2, 1 ); # Remote '$cmd' OK
X if( $ret == 99 ){
X &service_closed();
X $ret = 0;
X }
X return $ret;
X}
X
X# ------------------------------------------------------------------------------
X# These are the lower level support routines
X
Xsub ftp'expectgot
X{
X ($ftp'resp, $ftp'fatalerror) = @_;
X if( $ftp_show ){
X print $ftp'showfd "$ftp'resp\n";
X }
X if( $ftp'keep_continuations ){
X $ftp'response .= $ftp'resp;
X }
X else {
X $ftp'response = $ftp'resp;
X }
X}
X
X#
X# create the list of parameters for chat'expect
X#
X# ftp'expect( time_out, {value, return value} );
X# the last response is stored in $ftp'response
X#
Xsub ftp'expect
X{
X local( $ret );
X local( $time_out );
X local( @expect_args );
X local( $code, $pre );
X
X $ftp'response = '';
X $ftp'fatalerror = 0;
X
X $time_out = shift( @_ );
X
X if( $drop_on_421 ){
X # Handle 421 specially - has to go first in case a pattern
X # matches on a generic 4.. response
X push( @expect_args, "[.|\n]*^(421 .*)\\015\\n" );
X push( @expect_args, "&expectgot( \$1, 0 ); 99" );
X }
X
X # Match any obvious continuations.
X push( @expect_args, "[.|\n]*^(\\d\\d\\d-.*|[^\\d].*)\\015\\n" );
X push( @expect_args, "&expectgot( \$1, 0 ); 100" );
X
X while( @_ ){
X $code = shift( @_ );
X $pre = '^';
X $post = ' ';
X if( $code =~ /^\d\d+$/ ){
X $pre = "[.|\n]*^";
X }
X elsif( $code =~ /^\d$/ ){
X $pre = "[.|\n]*^";
X $post = '\d\d ';
X }
X push( @expect_args, "$pre(" . $code . $post . ".*)\\015\\n" );
X push( @expect_args,
X "&expectgot( \$1, 0 ); " . shift( @_ ) );
X }
X # Match any numeric response codes not explicitly looked for.
X push( @expect_args, "[.|\n]*^(\\d\\d\\d .*)\\015\\n" );
X push( @expect_args, "&expectgot( \$1, 0 ); 0" );
X
X # Treat all unrecognised lines as continuations
X push( @expect_args, "^(.*)\\015\\n" );
X push( @expect_args, "&expectgot( \$1, 0 ); 100" );
X
X # add patterns TIMEOUT and EOF
X push( @expect_args, 'TIMEOUT' );
X push( @expect_args, "&expectgot( 'timed out', 0 ); 0" );
X push( @expect_args, 'EOF' );
X push( @expect_args, "&expectgot( 'remote server gone away', 1 ); 99" );
X
X # if we see a continuation line, wait for the real info
X $ret = 100;
X while( $ret == 100 ){
X if( $ftp_show > 9 ){
X &printargs( $time_out, @expect_args );
X }
X $ret = &chat'expect( $time_out, @expect_args );
X }
X
X return $ret;
X}
X
X
X#
X# opens NS for io
X#
Xsub ftp'open_data_socket
X{
X local( $sockaddr, $port );
X local( $type, $myaddr, $a, $b, $c, $d );
X local( $mysockaddr, $family, $hi, $lo );
X
X $sockaddr = 'S n a4 x8';
X
X ($a,$b,$c,$d) = unpack( 'C4', $chat'thisaddr );
X $this = $chat'thisproc;
X
X socket( S, $pf_inet, $sock_stream, $tcp_proto ) || die "socket: $!";
X bind( S, $this ) || die "bind: $!";
X
X # get the port number
X $mysockaddr = getsockname( S );
X ($family, $port, $myaddr) = unpack( $sockaddr, $mysockaddr );
X
X $hi = ($port >> 8) & 0x00ff;
X $lo = $port & 0x00ff;
X
X #
X # we MUST do a listen before sending the port otherwise
X # the PORT may fail
X #
X listen( S, 5 ) || die "listen";
X
X &ftp'send( "PORT $a,$b,$c,$d,$hi,$lo" );
X
X return &ftp'expect( $timeout,
X 2, 1 ); # PORT command successful
X}
X
Xsub ftp'close_data_socket
X{
X close( NS );
X}
X
Xsub ftp'send
X{
X local( $send_cmd ) = @_;
X
X if( $send_cmd =~ /\n/ ){
X print $ftp'showfd "ERROR, \\n in send string for $send_cmd\n";
X }
X
X if( $ftp_show ){
X local( $sc ) = $send_cmd;
X
X if( $send_cmd =~ /^PASS/){
X $sc = "PASS <somestring>";
X }
X print $ftp'showfd "---> $sc\n";
X }
X
X &chat'print( "$send_cmd\r\n" );
X}
X
Xsub ftp'printargs
X{
X while( @_ ){
X print $ftp'showfd shift( @_ ) . "\n";
X }
X}
X
Xsub ftp'filesize
X{
X local( $fname ) = @_;
X
X if( ! -f $fname ){
X return -1;
X }
X
X return (stat( _ ))[ 7 ];
X
X}
X
X# Reply codes, see RFC959:
X# 1yz Positive Preliminary. Expect another reply before proceeding
X# 2yz Positive Completion.
X# 3yz Positive Intermediate. More information required.
X# 4yz Transient Negative Completion. The user should try again.
X# 5yz Permanent Negative Completion.
X# x0z Syntax error
X# x1z Information
X# x2z Connection - control info.
X# x3z Authentication and accounting.
X# x4z Unspecified
X# x5z File system.
X
X# 110 Restart marker reply.
X# In this case, the text is exact and not left to the
X# particular implementation; it must read:
X# MARK yyyy = mmmm
X# Where yyyy is User-process data stream marker, and mmmm
X# server's equivalent marker (note the spaces between markers
X# and "=").
X# 120 Service ready in nnn minutes.
X# 125 Data connection already open; transfer starting.
X# 150 File status okay; about to open data connection.
X
X# 200 Command okay.
X# 202 Command not implemented, superfluous at this site.
X# 211 System status, or system help reply.
X# 212 Directory status.
X# 213 File status.
X# 214 Help message.
X# On how to use the server or the meaning of a particular
X# non-standard command. This reply is useful only to the
X# human user.
X# 215 NAME system type.
X# Where NAME is an official system name from the list in the
X# Assigned Numbers document.
X# 220 Service ready for new user.
X# 221 Service closing control connection.
X# Logged out if appropriate.
X# 225 Data connection open; no transfer in progress.
X# 226 Closing data connection.
X# Requested file action successful (for example, file
X# transfer or file abort).
X# 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
X# 230 User logged in, proceed.
X# 250 Requested file action okay, completed.
X# 257 "PATHNAME" created.
X
X# 331 User name okay, need password.
X# 332 Need account for login.
X# 350 Requested file action pending further information.
X
X# 421 Service not available, closing control connection.
X# This may be a reply to any command if the service knows it
X# must shut down.
X# 425 Can't open data connection.
X# 426 Connection closed; transfer aborted.
X# 450 Requested file action not taken.
X# File unavailable (e.g., file busy).
X# 451 Requested action aborted: local error in processing.
X# 452 Requested action not taken.
X# Insufficient storage space in system.
X
X# 500 Syntax error, command unrecognized.
X# This may include errors such as command line too long.
X# 501 Syntax error in parameters or arguments.
X# 502 Command not implemented.
X# 503 Bad sequence of commands.
X# 504 Command not implemented for that parameter.
X# 530 Not logged in.
X# 532 Need account for storing files.
X# 550 Requested action not taken.
X# File unavailable (e.g., file not found, no access).
X# 551 Requested action aborted: page type unknown.
X# 552 Requested file action aborted.
X# Exceeded storage allocation (for current directory or
X# dataset).
X# 553 Requested action not taken.
X# File name not allowed.
X
X
X# make this package return true
X1;
END_OF_FILE
if test 24614 -ne `wc -c <'ftp.pl'`; then
echo shar: \"'ftp.pl'\" unpacked with wrong size!
fi
# end of 'ftp.pl'
fi
if test -f 'pp_mailfilter' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'pp_mailfilter'\"
else
echo shar: Extracting \"'pp_mailfilter'\" \(246 characters\)
sed "s/^X//" >'pp_mailfilter' <<'END_OF_FILE'
X# Default path is only /vol/pp/bin
X# Under MMDF path contained $HOME, hence /homes/info-server
XPATH="/vol/pp/bin:/homes/info-server:/usr/local/bin:/usr/ucb/bin:/usr/bin";
X
Xif( !delivered ){
X pipe "/src.doc.ic.ac.uk/public/ic.doc/ftpmail/q.pl";
X}
END_OF_FILE
if test 246 -ne `wc -c <'pp_mailfilter'`; then
echo shar: \"'pp_mailfilter'\" unpacked with wrong size!
fi
# end of 'pp_mailfilter'
fi
if test -f 'q.pl' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'q.pl'\"
else
echo shar: Extracting \"'q.pl'\" \(17435 characters\)
sed "s/^X//" >'q.pl' <<'END_OF_FILE'
X#!/usr/bin/perl -s
X# Very simple ftpmail system
X# Queue a transfer to be done
X# By Lee McLoughlin <lmjm@doc.ic.ac.uk>
X# You can do what you like with this except claim that you wrote it or
X# give copies with changes not approved by Lee. Neither Lee nor any other
X# organisation can be held liable for any problems caused by the use or
X# storage of this package.
X#
X# $Header: /a/swan/home/swan/staff/csg/lmjm/src/perl/mirror/src/ftpmail/RCS/q.pl,v 1.23 1994/07/06 14:56:38 lmjm Exp lmjm $
X# $Log: q.pl,v $
X# Revision 1.23 1994/07/06 14:56:38 lmjm
X# Allow for use of vrfy.
X# Skip leading rubbish and whitespace at the start of commands.
X# Give each job a priority.
X# Report queue size back to users.
X#
X# Revision 1.22 1994/05/02 18:10:49 lmjm
X# Switched to rfc822 parsing.
X#
X# Revision 1.21 1994/02/16 22:33:49 lmjm
X# Ignore message body till start of job found.
X# Check errors in correct order (patch was email to me but lost name).
X#
X# Revision 1.20 1993/12/14 10:40:14 lmjm
X# Try to ignore extra block of mail headers at start of job.
X# Allow for more ways to write reply-to.
X# Check for size argument.
X# Make sure patterns containing an @ have a \ in front.
X#
X# Revision 1.19 1993/06/17 18:18:10 lmjm
X# Version upgrade
X#
X# Revision 1.18 1993/06/17 09:58:33 lmjm
X# Version upgrade.
X#
X# Revision 1.17 1993/06/16 20:45:22 lmjm
X# Dont allow ' if not using sendmail.
X#
X# Revision 1.16 1993/05/13 21:23:35 lmjm
X# Corrected btoa_it use in dq.pl
X#
X# Revision 1.15 1993/05/12 11:14:50 lmjm
X# Upgraded support.pl
X#
X# Revision 1.14 1993/05/11 20:07:57 lmjm
X# Optionally ban connection to a.b.c.d type addresses
X#
X# Revision 1.13 1993/05/07 19:05:52 lmjm
X# Added Chris's fixed not_ok code.
X#
X# Revision 1.12 1993/04/28 18:19:20 lmjm
X# Handle size suffix correctly.
X#
X# Revision 1.11 1993/04/25 20:27:55 lmjm
X# Cut new release
X#
X# Revision 1.10 1993/04/25 14:15:11 lmjm
X# Allow for multiple help files (one per language).
X#
X# Revision 1.9 1993/04/23 23:27:07 lmjm
X# Massive renaming for sys5.
X# Also shrink qfile names.
X# Correct handling of <> on input.
X#
X# Revision 1.8 1993/04/23 20:03:17 lmjm
X# Use own version of library routines before others.
X#
X# Revision 1.7 1993/04/23 17:23:40 lmjm
X# Renamed ftpmail-local-config.pl to ftpmail-config.pl
X# Made pathnames relative to $ftpmail_dir.
X# Allow for some leeway in the max_cmds thing.
X# Look out for $ftpmail_response in the headers.
X# Fail if no reply_to in the headers.
X# Keep copies of input if they have peculiar errors.
X# If prematute end of input - check if the user is just after help.
X# Allow for 'reply to email'
X# Log change of reply_to in the job.
X# Added corrections from Wolf + his not-auth code..
X# Correct problem with 'no option' handling.
X#
X# Revision 1.6 1993/04/21 10:58:40 lmjm
X# Smarter mail header parsing by andy.linton@comp.vuw.ac.nz
X#
X# Revision 1.5 1993/04/20 20:15:40 lmjm
X# Added delete option.
X#
X# Revision 1.4 1993/04/13 10:34:38 lmjm
X# Tailored help variables.
X# Cleanup where necessary.
X# Allowed for a help command.
X# Corrected size option.
X#
X# Revision 1.3 1993/03/30 20:32:22 lmjm
X# Must have an ftpmail account whose home directory everything is in.
X# New -test option that uses /tmp/ftpmail-test
X# Added better error handling.
X#
X# Revision 1.2 1993/03/23 21:40:14 lmjm
X# Now use ftpmail home directory.
X# Cleanup tmp files when there are problem
X#
X
X$ftpmail = 'ftpmail';
X
X$Revision = '$Revision: 1.23 $';
X
Xif( $test ){
X $ftpmail_dir = '/usr/tmp/ftpmail-test';
X}
Xelse {
X # The ftpmail_dir is the home directory of ftpmail.
X $ftpmail_dir = (getpwnam( $ftpmail ))[ 7 ];
X}
X
Xif( ! $ftpmail_dir ){
X die "No home directory for ftpmail\n";
X}
X
Xif( ! -d $ftpmail_dir ){
X die "no such directory as $ftpmail_dir\n";
X}
X
Xchdir( $ftpmail_dir ) || die "cannot chdir to $ftpmail_dir\n";
X
X# All the auxillary scripts come from ftpmail's home dir.
Xunshift( @INC, '.' );
X
Xrequire 'config.pl';
Xrequire 'support.pl';
Xrequire 'rfc822.pl';
X
X# It is more meaningful for the user to say 1 but in here zero is
X# more useful.
X$max_dqs--;
X
X# Don't leave files around writable
Xumask( 077 );
X
X$quenum = time();
X# Lop off the first bit to try and keep the namelen
X# small enough for system 5.
X$quenum =~ s/^....//;
X# If you change this keep the format as: \d+\.\d+
X# The T, for temporary, will be replaced by the priority.
X$qfile = "$quedir/T$quenum.$$";
X
X# No route: a pattern that doesn't match routes
X$nr = '[^@!%]+';
X
X# Match any ftpmail command. (Don't match too much of the line
X# or error handling is much harder.
X$command = "^\\s*((reply(-|_)to|reply)(\\s+to)?\\s|delete\\s|help|open|connect|(cd|chdir)\\s|ls|dir|get\\s|binary|ascii|mode\\s|compress|gzip|uuencode|btoa|mime|vvencode|no|force|size\\s|quit|close)";
X
X# Copy of incoming data - keep all the header and the first lump of cmds
X$input_copy = "$incopydir/in$$";
Xopen( INCOPY, ">$input_copy" ) || &fatal( "Cannot create $input_copy" );
X$cmd_lines = 0;
X$in_body = 0;
X$toomany = 0;
Xwhile( <> ){
X print INCOPY;
X if( /^$/ ){
X $in_body = 1;
X next;
X }
X if( ! $in_body ){
X next;
X }
X if( /^\s/ || /^\S+\s*:/ ){
X next;
X }
X # Some mailers send an extra block of mail headers at the start
X # of the job.
X if( ! /$command/io ){
X next;
X }
X # allow for some leeway in the max_cmds thing.
X if( $cmd_lines++ > ($max_cmds +5) ){
X $toomany = 1;
X }
X}
Xclose( INCOPY );
X
Xopen( INCOPY, $input_copy ) || &fatal( "Cannot reopen $input_copy" );
X
X&read_auth();
X
X# Parse the email header to see who sent this message
X# (This clever bit of code by andy.linton@comp.vuw.ac.nz based on a posting
X# by Larry Wall.)
X$/ = ""; # paragraph mode
X$* = 1; # multi-line pattern matching
X$_ = <INCOPY>; # read one paragraph
Xchop( $_ ); # Chop newline ending the paragraph
X
X# Should I ignore this?
Xif( /$ftpmail_response/ ){
X &log( "Input contains '$ftpmail_response', ignoring file" );
X &cleanexit();
X}
X
Xs/\n[ \t]+/ /g; # join multi-line entries
Xs/^reply-to/Reply-To/ig; # Fix up case on header keys
Xs/^from/From/ig;
Xs/^sender/Sender-to/ig;
X%head = ('PRESTUFF', split( /^(\S+):\s*/ )); # split on entry names
X$reply_to = $head{ 'Reply-To' } || $head{ 'From' } || $head{ 'Sender' };
Xchop( $reply_to ); # strip newline
X
X$/ = "\n"; # line mode
X$* = 0; # single line pattern matching
X
Xif( ! $reply_to ){
X &log( "No reply_to found in message $input_copy" );
X # Force a copy to be kept
X $cleanup = 0;
X &cleanexit();
X}
X
Xif( $dumb_mailer ){
X &dumb_fix_reply_to( $reply_to );
X}
X
Xif( $reply_to =~ /'/ && $mail_cmd !~ /sendmail/ ){
X &log( "reply_to: $reply_to contains a quote - cannot cope with this without sendmail" );
X &cleanexit();
X}
X
X$reply_to = &rfc822'first_addr_spec( $reply_to );
X$reply_to = &rfc822'uncomment( $reply_to );
Xif( $dont_reply_to && $reply_to =~ /$dont_reply_to/i ){
X &log( "reply_to: $reply_to in dont_reply_to pattern: $dont_reply_to, ignoring" );
X &cleanexit();
X}
X
Xif( $vrfy ){
X &log( "vrfy $reply_to" );
X if( system( "$vrfy -s -c 30 -t 120 $reply_to" ) != 0 ){
X &log( "vrfy $reply_to failed, ignoring" );
X &cleanexit();
X }
X}
X
X
Xif( eof ){
X # Maybe this is an attempt to get help?
X local( $subject ) = $head{ 'Subject' };
X if( $subject =~ /help(\s+french)?/ ){
X &mail_back( "help$1" );
X }
X # No point in going any further
X &log( "Premature end of input $input_copy" );
X # Force a copy to be kept
X $cleanup = 0;
X &cleanexit();
X}
X
Xif( $toomany ){
X &mail_back( "there are too many commands in your job, the limit is $max_cmds" );
X}
X
X# Anything to actually transfer?
X$work = 0;
X
X# Process lines
Xwhile( <INCOPY> ){
X if( /$ftpmail_response/ ){
X &log( "Input contains '$ftpmail_response', ignoring file" );
X &cleanexit();
X }
X # Some mailers send an extra block of mail headers at the start
X # of the job so ignore everything not an ftpmail command.
X if( ! /$command/io ){
X next;
X }
X if( /^\s*(reply(-|_)to|reply)(\s+to)?(\s+(.+))?/i ){
X local( $full, $addr ) = ($4, $5);
X if( $full =~ /^\s*$/ ){
X &mail_back( "reply-to needs an argument of who to send replies to" );
X }
X $new_reply_to = $addr;
X if( ! $new_reply_to ){
X &log( "tried to reset reply_to to nothing, ignored" );
X }
X else {
X $reply_to = $new_reply_to;
X &log( "reply_to reset to $reply_to" );
X }
X next;
X }
X
X if( /^\s*delete\s+(.*)/ ){
X $delete = $1;
X last;
X }
X
X if( /^\s*help(\s+\S+)?/ ){
X $help = "help$1";
X last;
X }
X
X if( /^\s*(open|connect)(\s+(\S+))?(\s+(\S+))?(\s+(\S+))?/i ){
X if( $site ){
X &mail_back( "Cannot have multiple open's" );
X }
X ($site, $user, $pass ) = ($3, $5, $7);
X if( $site eq ''){
X $site = $default_site;
X }
X if( $fqdn_only && $site =~ /^\d+\.\d+\.\d+\.\d+$/ ){
X &mail_back( "Cannot ftp you may only use the NAME for the host to connect to" );
X }
X if( $ftp_permitted && $site !~ /$ftp_permitted/ ){
X &mail_back( "Cannot ftp to that site only sites matching $ftp_permitted are allowed" );
X }
X push( @comms, "open $site" );
X if( $user eq '' ){
X $user = 'anonymous';
X }
X if( $pass eq '' ){
X $pass = $reply_to;
X $pass .= "@".$hostname unless $pass =~ /\@/;
X
X }
X if( ! $restricted ){
X push( @comms, "user $user" );
X push( @comms, "pass $pass" );
X }
X else {
X push( @comms, "user anonymous" );
X $pass = "ftpmail/$pass" unless $pass =~ /^ftpmail\//;
X $pass =~ s,^ftpmail/-,-ftpmail/,;
X push( @comms, "pass $pass" );
X }
X }
X elsif( /^\s*(cd|chdir)(\s+(.+))?/i ){
X if( $2 =~ /^\s*$/ ){
X &mail_back( "chdir needs an argument of which directory to move to" );
X }
X push( @comms, "cd $3" );
X }
X elsif( /^\s*ls\s*(.*)/i ){
X push( @comms, "ls $1" );
X $work = 1;
X }
X elsif( /^\s*dir\s*(.*)/i ){
X push( @comms, "dir $1" );
X $work = 1;
X }
X elsif( /^\s*get(\s+(.+))?/i ){
X if( $1 =~ /^\s*$/ ){
X &mail_back( "get needs an argument of which file to get" );
X }
X push( @comms, "get $2" );
X $work = 1;
X }
X elsif( /^\s*binary/i ){
X push( @comms, "mode binary" );
X }
X elsif( /^\s*ascii/i ){
X push( @comms, "mode ascii" );
X }
X elsif( /^\s*mode(\s+(binary|ascii))?\s*/i ){
X if( $1 =~ /^\s*$/ ){
X &mail_back( "mode needs an argument of either binary or ascii" );
X }
X push( @comms, "mode $2" );
X }
X elsif( /^\s*(compress|gzip|uuencode|btoa|mime|vvencode)(\s+no)?\s*$/i ){
X local( $what, $yea_nay ) = ($1, $2);
X local( $no ) = '';
X if( $yea_nay =~ /no/i ){
X $no = ' no';
X }
X push( @comms, "$what$no" );
X }
X elsif( /^\s*(no)\s*(compress|gzip|uuencode|btoa|mime|vvencode)\s*$/i ){
X local( $yea_nay, $what ) = ($1, $2);
X local( $no ) = '';
X if( $yea_nay =~ /no/i ){
X $no = ' no';
X }
X push( @comms, "$what$no" );
X }
X elsif( /^\s*force(\s+(compress|gzip|uuencode|btoa|mime|vvencode)\s*)?$/i ){
X local( $full, $what ) = ($1, $2);
X if( $full =~ /^\s*$/ ){
X &mail_back( "force needs an argument of one of: compress gzip uuencode btoa mime" );
X }
X push( @comms, "force $what" );
X }
X elsif( /^\s*size\s+(\d+)?\s*(k|b|m)?\s*$/i ){
X local( $size, $kind ) = ($1, $2);
X if( $size eq '' ){
X &mail_back( "size needs a number argument optionally followed by k, b or m" );
X }
X if( $kind =~ /[mM]/ ){
X $size *= (1024*1024);
X }
X elsif( $kind =~ /[bB]/ ){
X $size *= 512;
X }
X elsif( $kind =~ /[kK]/ ){
X $size *= 1024;
X }
X if( $size < $min_size || $size > $max_size ){
X $size = $def_max_size;
X }
X push( @comms, "size $size" );
X }
X elsif( /^\s*(quit|close|==)/i ){
X last;
X }
X else {
X $error = "Unrecognised input (maybe quit needed?): $_";
X last;
X }
X}
X
Xif( !$reply_to ){
X &fatal( "Must have a 'reply-to emailaddress'" );
X}
X
X&fix_reply_to();
X
Xif( ! &auth( $reply_to ) ){
X &mail_back( "reply-to $reply_to not allowed to use this service" );
X}
X
Xif( $delete ){
X # If any problems call &mail_back( "delete fail <err>\n<long err>" )
X # and mail_back will generate sensible error messages
X if( $delete =~ /^\s*(\d+.\d+)\s*$/ ){
X $delete = $1;
X }
X else {
X &mail_back( "delete fail bad argument\nShould be delete <jobid> not: delete $delete" );
X }
X local( $job ) = "$quedir/$delete";
X # Make sure the reply_to's are the same
X if( ! open( job, $job ) ){
X &mail_back( "delete fail no such job\nCannot delete $delete failed because I couldn't find the job in the queue" );
X }
X while( <job> ){
X if( /^reply-to (.+)$/ ){
X $job_reply_to = $1;
X last;
X }
X }
X close( job );
X if( $job_reply_to ne $reply_to ){
X &mail_back( "delete fail not queuer\nYou cannot delete this job $delete as, according to the reply-to, you are not\nThe person who queued it.\n" );
X }
X # Zap a job and tell them its gone
X unlink( $job );
X &mail_back( "deleted $delete by user" );
X}
Xelsif( $help ){
X &mail_back( $help );
X}
X
X
Xif( !$site ){
X &mail_back( "Must have an 'open [site [user [pass]]]'" );
X}
X
Xif( $error ){
X &mail_back( $error );
X}
X
Xif( ! $work ){
X &mail_back( "Your job contains no get, ls or dir commands so I am ignoring it" );
X}
X
X# Work out the priority
X$prio = 0;
X$matched = 0;
Xforeach $site_prio ( @site_priorities ){
X if( $site =~ /$site_prio/ ){
X $matched = 1;
X last;
X }
X $prio++;
X last if $prio >= 9;
X}
Xif( ! $matched ){
X $prio = 9;
X}
X
X$qfile = "$quedir/T$quenum.$$";
X$realqfile = $qfile;
X# replace the T by the prio.
X$realqfile =~ s/($quedir\/)T($quenum.$$)/$1$prio$2/;
X&log( "queueing entry for $reply_to in $realqfile" );
X$tries = 0;
X$whenretry = 0;
X&write_entry();
Xrename( $qfile, $realqfile );
X&mail_back( "ack" );
X
Xsub mail_back
X{
X local( $error ) = @_;
X local( $show_help ) = 1;
X local( $help, $ack, $del, $del_fail );
X
X chop( $error ) if $error =~ /\n$/;
X
X if( $error =~ /^help(\s+\S+)?/ ){
X &log( "mail_back: $reply_to $error" );
X $help = $error;
X $error = 0;
X }
X elsif( $error eq 'ack' ){
X &log( "mail_back: $reply_to $error" );
X $ack = 1;
X $error = 0;
X }
X elsif( $error =~ /^deleted / ){
X &log( "mail_back: $reply_to $error" );
X $del = $error;
X $error = 0;
X }
X elsif( $error =~ /^(delete fail .*)\n/ ){
X &log( "mail_back: $reply_to $1" );
X $del_fail = $error;
X $error = 0;
X }
X else {
X &log( "mail_back: $reply_to failed to queue because: $error" );
X }
X
X if( $mail_cmd =~ /sendmail/ ){
X open( MAIL, "| $mail_cmd " ) ||
X &fatal( "Cannot send email" );
X print MAIL "To: $reply_to\n";
X print MAIL "Subject: $ftpmail_response\n\n";
X }
X else {
X open( MAIL, "| $mail_cmd -s '$ftpmail_response' '$reply_to' >/dev/null 2>&1" ) ||
X &fatal( "Cannot send email" );
X }
X
X print MAIL "$ftpmail_response\n";
X
X &mail_motd();
X
X if( $error ){
X print MAIL "ftpmail has failed to queue your request with an";
X print MAIL " error of:\n\t$error\n";
X &mail_incopy();
X }
X elsif( $ack ){
X local( $qf ) = $realqfile;
X $qf =~ s,.*/([^/]+),$1,;
X print MAIL "ftpmail has received the following job from you:\n";
X &mail_comms();
X print MAIL "\nftpmail has queued your job as: $qf\n";
X print MAIL "Your priority is $prio (0 = highest, 9 = lowest)\n";
X print MAIL "$priorities_msg\n";
X &scan_q( $prio );
X local( $ahead ) = $#qfiles; # don't count my new one
X $ahead = "no" if $ahead <= 0;
X print MAIL "There are $ahead jobs ahead of this one in the queue.\n";
X if( $max_dqs > 0 ){
X local( $dqs ) = $max_dqs + 1;
X print MAIL "$dqs ftpmail handlers available.\n";
X }
X print MAIL "\nTo remove send a message to $ftpmail_email containing just:\ndelete $qf\n\n";
X &mail_incopy();
X $show_help = 0;
X }
X elsif( $del ){
X print MAIL "ftpmail has $del\n";
X $show_help = 0;
X }
X elsif( $del_fail ){
X print MAIL "ftpmail $del_fail\n";
X $show_help = 0;
X }
X
X if( $show_help ){
X if( $help =~ /^help(\s+(\S+))/ ){
X $hf = "$helpdir/$2";
X }
X else {
X $hf = "$helpdir/help";
X }
X if( open( HELP, $hf ) ){
X while( <HELP> ){
X s/\$default_site/$default_site/g;
X s/\$ftpmail_email/$ftpmail_email/g;
X s/\$help_email/$help_email/g;
X s/\$managers_email/$managers_email/g;
X s/\$hostname/$hostname/g;
X s/\$max_cmds/$max_cmds/g;
X s/\$max_size/$max_size/g;
X # I use the []'s to prevent RCS from expanding it
X s/\$[R]evision/$Revision/g;
X print MAIL;
X }
X close( HELP );
X }
X else {
X print MAIL "Cannot find $help";
X }
X }
X
X close( MAIL );
X
X &cleanexit();
X}
X
Xsub mail_incopy
X{
X close( INCOPY );
X if( ! open( INCOPY, $input_copy ) ){
X print MAIL "internal error, cannot reopen input file!";
X }
X else {
X print MAIL "\nYour original input " . ($toomany ? "began" : "was") . ">>\n";
X while( <INCOPY> ){
X s/^/>/;
X print MAIL;
X }
X close( INCOPY );
X print MAIL "<<End of your input\n";
X }
X}
X
X# Read a file of patterns for authorised users
Xsub read_auth
X{
X if( ! open( auth, $authfile ) ){
X &log( "Cannot open $authfile" );
X return;
X }
X while( <auth> ){
X next if /^#/;
X chop;
X if( /^not\s+(.+)$/ ){
X $bad_add = $1;
X if( /\@/ ){
X $b = $auth_not_ok;
X $auth_not_ok = $b ? "$b|$bad_add" : $bad_add;
X }
X else {
X $auth_host{ $bad_add } = 0;
X }
X }
X elsif( /\@/ ){
X # user@host pattern
X $a = $auth_ok;
X $auth_ok = $a ? "$a|$_" : $_;
X }
X else {
X # hostname
X $auth_host{ $_ } = 1;
X }
X }
X close auth;
X}
X
Xsub auth
X{
X local( $addr ) = @_;
X
X if( $addr =~ /^$auth_not_ok$/i ){
X return 0;
X }
X
X if( $addr =~ /^$auth_ok$/){
X return 1;
X }
X
X if( $addr =~ /^($nr)@($nr)$/ ){
X local( $user, $host ) = ($1, $2);
X return $auth_host{ $host };
X }
X
X if( $addr !~ /[@!%]/ ){
X return $auth_host{ 'localhost' };
X }
X
X return 0;
X}
X
Xsub fix_reply_to
X{
X # Make sure that reply_to doesn't contain any shell escapes
X # Since I use it as '$reply_to' then all I have to worry about is
X # backprime itself
X
X # For now just zap them!
X $reply_to =~ s/'//g;
X}
X
X# Try to strip away all comments.
Xsub dumb_fix_reply_to
X{
X $reply_to = &rfc822'first_addr_spec( $reply_to );
X}
X
Xsub cleanexit
X{
X if( $cleanup ){
X unlink( $input_copy );
X }
X exit( 0 );
X}
END_OF_FILE
if test 17435 -ne `wc -c <'q.pl'`; then
echo shar: \"'q.pl'\" unpacked with wrong size!
fi
chmod +x 'q.pl'
# end of 'q.pl'
fi
echo shar: End of archive 1 \(of 3\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 3 archives.
rm -f ark[1-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...