/*
* File: sendfile.c
*
* Author: Ulli Horlacher (framstag@rus.uni-stuttgart.de)
*
* Contrib.: Rainer Bawidamann (widi@sol.wohnheim.uni-ulm.de)
* Martin Buck (Martin-2.Buck@student.uni-ulm.de)
* Heiko Schlichting (heiko@fu-berlin.de)
* Christoph 'GNUish' Goern (goern@janus.beuel.rhein.de)
* Stefan Scholl (stesch@parsec.inka.de)
* Michael Neumayer (eumel@42.org)
*
* History:
*
* 1995-08-11 Framstag initial version
* 1995-08-12 Framstag elm alias support
* 1995-09-10 Framstag added delete and resend function
* 1996-02-06 Framstag added ATTR EXE
* 1996-02-07 Framstag check for already compressed files
* 1996-02-20 Framstag follow forward address if given
* 1996-02-21 widi better Solaris-2 support
* 1996-02-22 Framstag added bouncing (forwarding) of files
* 1995-02-23 mbuck bug fixes for getting $HOME
* 1995-02-27 Framstag added quiet options
* 1995-03-08 Framstag catch up signals in cleanup()
* 1995-03-17 Framstag set umask (for tmp-files)
* 1995-03-23 Framstag added percentage output
* 1995-03-24 Framstag $TMPDIR replaces $HOME for tmp-files
* 1996-03-28 Framstag extended search for support programs
* 1996-04-02 Framstag added forward address to COMMENT
* 1996-04-06 Framstag changed transaction message format
* added overwrite option
* 1996-04-10 Framstag better usage text
* 1996-04-12 Framstag added pgp support
* 1996-04-16 Framstag better pgp signature creation
* 1996-04-17 Framstag new error handling for open_connection
* 1996-04-18 Framstag verbose mode displays system() commands
* allowed multiple IDs for pgp encryption
* 1996-04-20 Framstag added pgp IDEA encryption option
* 1996-04-24 Framstag changed bouncing option syntax
* 1996-05-08 Framstag fixed bug when bouncing
* 1996-05-10 Framstag allowed multiple forwards
* 1996-05-14 Framstag moved send_data() to net.c
* 1996-05-21 Framstag fixed bug when sending archive
* 1996-05-23 Framstag added check for writeable tmp-files
* added -P option (read from stdin)
* 1996-08-13 Framstag fixed wrong "(compressed)" output
* 1996-09-04 Heiko some fixes for IRIX
* 1996-09-11 Heiko fixed redirection comment bug
* 1996-11-19 Framstag fix for broken AIX include-files
* 1997-01-04 Framstag moved check_forward() to address.c
* 1997-01-20 Framstag fixed bug with TEXT=charset attribute
* 1997-01-20 GNUish modified to move to gnu-style
* 1997-01-25 Framstag added -X option (extended headers)
* better usage text
* better pgp parsing
* 1997-02-01 Framstag set default quiet mode 1 on dump ttys
* 1997-02-14 GNUish added long options
* 1997-02-23 Framstag modified str_* function names
* extended with TYPE=MIME
* 1997-02-24 Framstag sprintf() -> snprintf()
* 1997-03-24 stesch fixed buffering bug with -X option
* 1997-05-15 Framstag added -c option usage text
* 1997-05-16 Framstag added file type guessing
* 1997-05-17 Framstag better file type guessing
* 1997-06-04 Framstag added SF_TMPDIR
* 1997-06-15 Framstag added -T option for file reading test
* added new line output in cleanexit()
* 1997-06-17 Framstag added packet_size config option
* 1997-06-30 Framstag better file type guessing
* 1997-07-04 Framstag auto dection of pgp ID for encryption
* 1997-08-20 Framstag new syntax with option -a=name-of-archive
* 1997-08-21 Framstag better handling of reply code 202
* 1997-12-05 Eumel fixed file type handling bug
* 1997-12-11 Framstag added bzip2 support
* 1997-12-14 Framstag fixed bug when sending to /dev/null
* 1997-12-15 Framstag added link speed test for auto-compression
* 1997-12-19 Framstag added -W option
* 1998-01-05 Framstag reactivated outgoing logging
* 1998-01-17 Framstag check SAFT-URL for alternative tcp port
* 1998-01-22 Framstag check compression method when bouncing
* 1998-02-27 Framstag fixed small bug in guess_ftype
* more verbose transfer statistics
* 1998-03-01 Framstag changed /dev/null testing recipient to :NULL:
* 1998-03-07 Framstag better detection of non-interactive mode
* added -i option for more transfer information
* 1998-03-08 Framstag fixed outlog deletion bug
* 1998-03-16 Framstag fixed error loop bug in cleanup()
* 1998-03-20 Framstag fixed archive+overwrite bug
* 1998-08-21 Framstag added maximum thruput option
* 1998-09-23 Framstag continue sending on tar errors
* 1998-11-05 Framstag do not compress files less than 1 KB
* 1998-11-12 Framstag print "spooled" information with size
* fixed spool+archive+overwrite bug
* 1999-08-03 Framstag gzip is default compression, back again
* 2001-01-10 Framstag fopen() ==> rfopen()
* 2001-02-04 Framstag added secure mktmpdir()
*
* The sendfile client of the sendfile package.
* Sends one or more files to the sendfiled of the destination system.
*
* Copyright © 1995-2001 Ulli Horlacher
* This file is covered by the GNU General Public License
*/
#include "config.h" /* autoconf header */
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <pwd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include "string.h" /* extended string functions */
#include "net.h" /* the network routines */
#include "io.h" /* socket and file IO extensions */
#include "message.h" /* information, warning and error messages */
#include "spool.h" /* operations on files in the sendfile spool */
#include "utf7.h" /* UTF-7 coding */
#include "address.h" /* address checking */
#if defined(HAVE_GETOPT_H)
#include <getopt.h>
#else
int getopt(int, char * const *, const char *);
extern int opterr;
extern int optind;
extern int optopt;
extern char *optarg;
#endif
#if defined(SOLARIS2)
int gethostname(char *, int);
#endif
#if defined(LINUX)
int gethostname(char *, size_t);
#endif
#ifndef AIX3
#ifndef CONVEXOS
FILE *popen(const char *, const char *);
#endif
int pclose(FILE *);
#endif
#ifdef NEXT
int shutdown(int, int);
#endif
#if defined(AIX3) || defined(ULTRIX)
# include "bsd.h"
#endif
/* print short help usage text */
int usage();
/* clean termination routine */
void cleanexit();
/* delete tmp-file and send LOG command to local server */
void cleanup();
/* encrypt a file with pgp */
void pgp_encrypt(int, char *, char *);
/* create detached pgp signature file and send SIGN header command */
void pgp_sign(const char *, const char *, int);
/* create and open outgoing spool header file */
FILE *outspool(const char *, const char *, char *);
/* start local spool daemon for outgoing files */
void start_spooldaemon(char *);
/* create temporary user outgoing log file */
void outlog(char *, char *, char *, char *);
/* forward a file with complete header from stdin */
void forward(char *, float);
/* read a header line from stdin */
void get_header(const char *, char *);
/* guess file type */
char guess_ftype(const char *, char *);
/* check if link to host is fast enough */
int linkspeed(const char *, int, char **);
/* note the link speed for later processing */
void notespeed(const char *, unsigned long, float);
/* list files in outgoing spool */
int list_spool();
/* print information about spooled file */
void spooled_info(const char *, const char *, int);
/* global variables */
int
pgppass=0, /* flag if the pgp password is set as an env variable */
verbose=0, /* flag for verbose mode */
client=1, /* flag to determine client or server */
quiet=0, /* quiet mode flag */
test=0, /* flag for file reading testing (send to /dev/null) */
outlogging=0, /* flag logging outgoing files */
packet_size=0; /* size of a packet in bytes */
char
*prg, /* name of the game */
*pgpvm, /* pgp verbose mode string */
*tmpdir, /* directory for temporary files */
*dontcompress[99], /* list of file extensions which are not compressible */
pw_name[FLEN], /* own user name */
localhost[FLEN], /* name of the local host */
outlogtmp[MAXLEN], /* user temporary outgoing log file */
userspool[MAXLEN], /* user spool directory */
tar_bin[MAXLEN], /* the tar binary */
gzip_bin[MAXLEN], /* the gzip binary */
bzip2_bin[MAXLEN], /* the bzip2 binary */
zprg[MAXLEN], /* the compress programm (either gzip or bzip2) */
pgp_bin[MAXLEN], /* the pgp binary */
stdintmp[MAXLEN], /* name of stdin temporary file */
tartmp[MAXLEN], /* name of tar temporary file */
ziptmp[MAXLEN], /* name of gzipped temporary file */
texttmp[MAXLEN], /* name of text temporary file in NVT telnet format */
pgptmp[MAXLEN]; /* name of pgp temporary file */
int main(int argc, char *argv[]) {
int
i,n, /* simple loop count */
pid, /* current proccess id */
status, /* return codes */
sockfd, /* socket file descriptor */
opt, /* option to test for */
fn, /* file number in argv */
tfn, /* total file number sent */
stdinf, /* read file from stdin */
ch, /* character to read in from file */
lanspeed, /* speed in kB/s which defines what is in the LAN */
pgpcrypt, /* pgp public key or IDEA encryption */
del, /* flag for deleting previous sent files */
tar, /* flag for sending files as tar archive */
zip, /* flag for sending files as zip archive */
exe, /* flag for sending executable */
pgp, /* flag for pgp encoding */
guess, /* flag for file type guessing */
text, /* flag for sending text file */
mime, /* flag for sending mime file */
source, /* flag for sending source code file */
spool, /* flag for do outgoing spooling */
list, /* flag for listing files in outgoing spool */
info, /* flag for information mode */
bounce, /* flag for bouncing files */
extended, /* flag for extended command parsing */
spooling, /* flag for spooling allowed */
overwrite, /* flag for overwriting already sent files */
do_compress; /* flag for really do compressing */
unsigned long
size, /* size of file to send */
orgsize; /* original file size uncompressed */
float
mtp, /* maximum thruput limit */
tsize, /* total size of all sent files */
attime, /* actual transfer time */
tttime, /* total transfer time */
thruput; /* total net throughput */
char
*cp, /* simple string pointer */
*argp, /* argument string pointer */
*pop, /* pgp option string pointer */
*fnp, /* file name pointer */
*type, /* type of file transfer */
*compress, /* the compression methode */
mode, /* guessed file mode */
to[2*FLEN], /* user@host from argv */
file[FLEN], /* name of file to send */
tinfo[FLEN], /* transfer information */
sdfn[FLEN], /* spool data file name */
shfn[FLEN], /* spool header file name */
rto[2*FLEN], /* user@host from spool header file */
ftype[FLEN], /* file type mode */
archive[FLEN], /* name of archive file */
where[FLEN], /* where is userspool or sendfile.cf */
aopt[FLEN], /* alias options */
recipient[FLEN], /* recipient at serverhost */
bouncearg[DLEN], /* bouncing files argument */
sizes[FLEN], /* original and compressed file size */
user[FLEN], /* local user name */
date[DLEN], /* date of file */
host[FLEN], /* name of serverhost */
pgpopt[FLEN], /* options for pgp */
pgprid[FLEN], /* pgp recipient id */
pgpsign[FLEN], /* pgp signature option */
redirect[MAXLEN], /* redirected comment */
cmd[MAXLEN], /* cmd string for system-call */
line[MAXLEN], /* one line of text */
reply[MAXLEN], /* reply from the server */
tmp[MAXLEN], /* temporary string */
comment[MAXLEN], /* file comment */
outgoing[MAXLEN], /* outgoing spool directory */
oshfn[MAXLEN], /* outgoing spool header file name */
osdfn[MAXLEN], /* outgoing spool data file name */
filelist[OVERSIZE], /* list of files for tar command */
force_compress[MAXLEN]; /* force compress with special programm */
const char
*cft[]= /* file types which are not compressible
(output from file(1) command */
{ "compress","zip","zoo","frozen","gif","jpg","jpeg","mpg","mpeg","" };
FILE
*shf, /* spool header file */
*oshf, /* outgoing spool header file */
*inf, /* input file */
*outf; /* output file */
struct passwd *pwe; /* password entry */
struct stat finfo; /* information about a file */
char
utf_name[LEN_UNI], /* name in UTF-7 format */
iso_name[LEN_ISO]; /* name in ISO Latin-1 format */
struct hostlist
*hls, /* host list start */
*hlp; /* host list pointer */
struct outfilelist
*oflp; /* outgoing file list pointer */
/* HAVE_GETOPTLONG_H is dead code for sendfile! */
#if defined(HAVE_GETOPTLONG_H)
static struct option long_options[] = {
{ "version", 0, 0, 'V' },
{ "help", 0, 0, 'h' },
{ "delete", 0, 0, 'd' },
{ "text", 0, 0, 't' },
{ "spool", 0, 0, 'S' },
{ "quiet", 0, 0, 'q' },
{ "real-quiet", 0, 0, 'Q' },
{ "source", 0, 0, 's' },
{ "mime", 0, 0, 'm' },
{ "verbose", 0, 0, 'v' },
{ "uncompressed", 0, 0, 'u' },
{ "overwrite", 0, 0, 'o' },
{ "comment", 1, 0, 'c' },
{ "extended", 1, 0, 'X' },
{ "archive", 1, 0, 'a' },
{ "bounce", 1, 0, 'b' },
{ "stdin", 0, 0, 'P' },
{ "pgp-sign", 0, 0, 'ps' },
{ "pgp-encrypt", 1, 0, 'p' }
};
int long_index = 0;
#endif
mtp=0;
tfn=0;
del=0;
tar=0;
zip=0;
exe=0;
pgp=0;
text=0;
spool=0;
quiet=0;
mime=0;
list=0;
info=0;
guess=0;
tsize=0;
source=0;
bounce=0;
sockfd=0;
stdinf=0;
tttime=0;
thruput=0;
verbose=0;
extended=0;
spooling=0;
pgpcrypt=0;
lanspeed=100;
overwrite=0;
do_compress=0;
*aopt=0;
*host=0;
*date=0;
*zprg=0;
*where=0;
*tinfo=0;
*comment=0;
*archive=0;
*redirect=0;
*filelist=0;
*pgprid=0;
*pgpopt=0;
*pgpsign=0;
*force_compress=0;
dontcompress[0]="";
type="BINARY";
compress=S_GZIP;
oshf=NULL;
pid=(int)getpid();
prg=argv[0];
if ((cp=strrchr(prg,'/'))) prg=cp+1;
/* switch off buffering for STDIN for -X option */
setvbuf(stdin, NULL, _IONBF, 0);
if (getenv("PGPPASS")) {
pgppass=1;
pgpvm="+verbose=0";
} else {
pgppass=0;
pgpvm="+verbose=1";
}
/* scann the command line on options */
#if defined(HAVE_GETOPTLONG_H)
while ((opt=getopt_long(argc, argv, "Vh?dtmSqQsvuoc:X:a:b:p:",
long_options, &long_index)) > 0)
#else
while ((opt=getopt(argc, argv, "ivVTgtsuqoMQPdSlh?a:c:p:b:m:X:C:W:")) > 0)
#endif
{
switch (opt) {
case ':':
case 'h':
case '?': exit(usage());
case 'd': del=1; break;
case 'g': guess=1; break;
case 't': text=1; break;
case 'T': test=1; break;
case 'S': spool=1; break;
case 'l': list=1; break;
case 'i': info++; break;
case 'q': quiet++; break;
case 'Q': quiet=2; break;
case 'P': stdinf=1; break;
case 's': source=1; break;
case 'M': mime=1; break;
case 'm': mtp=atof(optarg); if (mtp<0) mtp=0; break;
case 'v': verbose++; break;
case 'u': compress=""; break;
case 'C': if (*optarg == '=') strcpy(force_compress,optarg+1);
else strcpy(force_compress,optarg); break;
case 'o': overwrite=1; break;
case 'c': if (*optarg == '=') strcpy(comment,optarg+1);
else strcpy(comment,optarg); break;
case 'a': tar=1; strcpy(archive,optarg); break;
case 'A': zip=1; strcpy(archive,optarg); break;
case 'b': bounce=1; strcpy(bouncearg,optarg); break;
case 'X': extended=1; strcpy(host,optarg); break;
case 'W': if (*optarg == '=') strcpy(where,optarg+1);
else strcpy(where,optarg); break;
case 'p': pgp=1;
snprintf(tmp,FLEN-1,"%s\n%s",pgpopt,optarg);
strcpy(pgpopt,tmp);
break;
case 'V': message(prg,'I',"version "VERSION" revision "REVISION); exit(0);
}
}
/* too few arguments? */
if (argc-optind<2 && !extended && !list && !*where) {
if (argc-optind<1) exit(usage());
errno=0;
message(prg,'F',"too few arguments: "
"you must specify at least a file name and recipient");
}
/* non-interactive usage without a tty? */
if (!quiet) {
cp=getenv("TERM");
if (!cp || !*cp || strstr("tty|dumb",cp)) quiet=1;
}
/* incompatible options? */
if (test && spool) {
errno=0;
message(prg,'F',"you cannot specify :NULL: and -S option together");
}
if (extended) {
if (strchr(host,'@')) {
errno=0;
message(prg,'F',"you must specify only a host name with the -X option");
}
if (del||text||spool||stdinf||source||mime||overwrite||tar||bounce||pgp||
*comment) {
if (quiet<2) message(prg,'W',"you cannot use any other option with "
"the extended header option - ignored");
del=text=spool=stdinf=source=mime=overwrite=tar=bounce=pgp=0;
*comment=0;
}
}
if (bounce) {
if (!(str_eq(bouncearg,"k=y") || str_eq(bouncearg,"k=n"))) {
errno=0;
message(prg,'F',"wrong bouncing argument");
}
if (source||mime||text||tar||del|stdinf)
if (quiet<2) message(prg,'W',"you cannot use any other option "
"when bouncing a file - ignored");
text=source=mime=tar=del=stdinf=0;
compress="";
}
if (del) {
if (source||mime||text||tar||overwrite||stdinf||pgp||*comment)
if (quiet<2) message(prg,'W',"you cannot use any other option "
"when deleting a file - ignored");
text=source=mime=tar=stdinf=0;
compress="";
}
if (guess) {
if (source||mime||text||tar)
if (quiet<2) message(prg,'W',"you cannot use source, text, mime or "
"archive option when guessing the file "
"type - ignored");
text=source=mime=tar=0;
}
if (stdinf) {
if (tar)
if (quiet<2) message(prg,'W',"you cannot send stdin as an archive file; "
"-a option will be ignored");
tar=0;
}
if (tar&&zip) {
errno=0;
message(prg,'F',"you cannot use the -a and -A archive options together");
}
if (tar||zip) {
if (source)
if (quiet<2) message(prg,'W',"option SOURCE is not allowed when "
"sending in archive format - ignored");
if (mime)
if (quiet<2) message(prg,'W',"option MIME is not allowed when "
"sending in archive format - ignored");
if (text)
if (quiet<2) message(prg,'W',"option TEXT is not allowed when "
"sending in archive format - ignored");
text=source=mime=0;
}
/* correct archive option? */
if (*archive) {
if (*archive == '=') {
strcpy(tmp,archive+1);
strcpy(archive,tmp);
} else {
errno=0;
message(prg,'F',"you have not specified an archive "
"name with -a=name-of-archive");
}
}
/* correct comment? */
if (*comment && argc-optind>2 && !tar) {
errno=0;
message(prg,'F',"you can only comment a single file");
}
/* protect all tmp-files */
umask(~(S_IRUSR|S_IWUSR));
/* support programs defaults */
strcpy(tar_bin,TAR);
strcpy(pgp_bin,PGP);
strcpy(gzip_bin,GZIP);
strcpy(bzip2_bin,BZIP2);
/* look for environment variables */
if ((cp=getenv("SF_TAR"))) strcpy(tar_bin,cp);
if ((cp=getenv("SF_PGP"))) strcpy(pgp_bin,cp);
if ((cp=getenv("SF_GZIP"))) strcpy(gzip_bin,cp);
if ((cp=getenv("SF_BZIP2"))) strcpy(bzip2_bin,cp);
/* do the support programs really exist? */
if (access(tar_bin,X_OK)<0) strcpy(tar_bin,"tar");
if (access(pgp_bin,X_OK)<0) strcpy(pgp_bin,"pgp");
if (access(gzip_bin,X_OK)<0) strcpy(gzip_bin,"gzip");
if (access(bzip2_bin,X_OK)<0) strcpy(bzip2_bin,"bzip2");
/* determine which compress programm to use */
if (*force_compress) {
compress="";
if (strstr(force_compress,"gzip")) compress=S_GZIP;
if (strstr(force_compress,"bzip2")) compress=S_BZIP2;
if (!*compress) {
snprintf(MAXS(tmp),"unsupported compression program %s",force_compress);
errno=0;
message(prg,'F',tmp);
}
strcpy(zprg,force_compress);
} else if (*compress) {
#if 0
snprintf(MAXS(tmp),"%s --help 2>&1",bzip2_bin);
if ((pp=popen(tmp,"r"))) {
while (fgetl(line,pp)) {
if (strstr(line,"usage:")) {
strcpy(zprg,bzip2_bin);
break;
}
}
pclose(pp);
}
/* is bzip2 available? */
if (!spool && whereis(bzip2_bin)) {
strcpy(zprg,bzip2_bin);
compress=S_BZIP2;
}
if (!*zprg) *bzip2_bin=0;
snprintf(MAXS(tmp),"%s --help 2>&1",gzip_bin);
if ((pp=popen(tmp,"r"))) {
while (fgetl(line,pp)) if (strstr(line,"usage:")) break;
pclose(pp);
}
if (strstr(line,"usage:")) {
if (!*zprg) strcpy(zprg,gzip_bin);
} else
*gzip_bin=0;
#endif
/* is gzip available? */
if (whereis(gzip_bin) && !*zprg) {
strcpy(zprg,gzip_bin);
compress=S_GZIP;
}
if (!*zprg) {
compress="";
if (quiet<2) message(prg,'W',"no compression program found - sending uncompressed");
}
}
/* get the local host name */
if (gethostname(localhost,FLEN-1)<0) strcpy(localhost,"localhost");
/* extended header feature = read everything from stdin? */
if (extended) {
forward(host,mtp);
exit(0);
}
/* get own user name, recipient name and host and alias options */
destination(argc,argv,user,recipient,host,aopt);
/* printf("recipient: %s\nhost: %s\noptions: %s\n",recipient,host,aopt); */
/* name the local host */
if (str_eq(host,"127.0.0.1") || str_eq(host,"0")) strcpy(host,localhost);
if (*aopt) {
snprintf(MAXS(cmd),"%s %s ",argv[0],aopt);
for(i=1;i<argc-1;i++) {
strcat(cmd,"'");
strcat(cmd,argv[i]);
strcat(cmd,"' ");
}
strcat(cmd,"saft://");
strcat(cmd,host);
strcat(cmd,"/");
strcat(cmd,recipient);
if (verbose) {
snprintf(MAXS(tmp),"shell-call: %s\n",cmd);
message(prg,'I',tmp);
}
system(cmd);
exit(0);
}
/* recipient /dev/null or :NULL: means test mode: read file(s) without sending */
if (str_eq(recipient,"/dev/null")) strcpy(recipient,":NULL:");
if (str_eq(recipient,":NULL:") && str_eq(host,localhost)) test=1;
/* get the own user name and tmpdir */
if ((pwe=getpwuid(getuid())) == NULL)
message(prg,'F',"cannot determine own user name");
strcpy(pw_name,pwe->pw_name);
tmpdir=mktmpdir(verbose);
/* check pgp options */
if (pgp) {
pop=pgpopt;
/* conventional IDEA encryption? */
if (str_eq(pop,"\nc")) {
pgpcrypt='c';
compress="";
} else { /* check other pgp options */
while (*pop) {
pop++;
/* pgp encryption? */
if (*pop=='e') {
pgpcrypt='e';
compress="";
pop++;
snprintf(MAXS(pgprid),"%s@%s",recipient,host);
/* is there a recipient id? */
if (*pop>'\n') {
if (*pop=='=') strcpy(pgprid,++pop);
/* cut off any more options */
if ((cp=strchr(pgprid,'\n'))) {
*cp=0;
pop=strchr(pop,'\n');
} else
*pop=0;
}
/*
if (!*pgprid) {
errno=0;
message(prg,'F',"you have to specify a pgp recipient-ID "
"when encrypting");
}
*/
continue;
}
/* pgp signature? */
if (*pop=='s') {
strcpy(pgpsign," ");
pop++;
/* is there a signature id? */
if (*pop>'\n') {
if (*pop=='=') pop++;
snprintf(MAXS(pgpsign),"-u '%s",pop);
/* cut off any more options */
if ((cp=strchr(pgpsign,'\n'))) {
*cp=0;
pop=strchr(pop,'\n');
} else
*pop=0;
strcat(pgpsign,"'");
}
continue;
}
/* wrong pgp options */
errno=0;
snprintf(MAXS(tmp),"wrong pgp option, see 'man %s'",prg);
message(prg,'F',tmp);
}
}
}
/* set various file names */
snprintf(MAXS(userspool),SPOOL"/%s",pw_name);
snprintf(MAXS(outlogtmp),"%s/.sendfile_%d.log",userspool,pid);
snprintf(MAXS(tartmp),"%s/sendfile.tar",tmpdir);
snprintf(MAXS(ziptmp),"%s/sendfile.zip",tmpdir);
snprintf(MAXS(pgptmp),"%s/sendfile.pgp",tmpdir);
snprintf(MAXS(texttmp),"%s/sendfile.txt",tmpdir);
snprintf(MAXS(stdintmp),"%s/sendfile.tmp",tmpdir);
/* where are the files/directories ? */
if (*where) {
if (str_eq(where,"config") || str_eq(where,"sendfile.cf")) {
if (quiet)
printf(CONFIG"\n");
else
message(prg,'I',"the global configuration file is: "CONFIG);
} else if (str_eq(where,"spool")) {
if (quiet)
printf(SPOOL"\n");
else
message(prg,'I',"the spool directory is: "SPOOL);
} else if (str_eq(where,"userspool")) {
if (quiet)
printf("%s\n",userspool);
else {
snprintf(MAXS(tmp),"the user spool directory is: %s",userspool);
message(prg,'I',tmp);
}
} else {
snprintf(MAXS(tmp),"%s is an unknown -W argument",where);
errno=0;
message(prg,'E',tmp);
if (quiet<2) message(prg,'I',"you may specify -W=config, -W=spool, or "
"-W=userspool");
}
if (argc-optind<1) exit(0);
}
/* check tmp files */
unlink(tartmp);
unlink(ziptmp);
unlink(texttmp);
unlink(pgptmp);
unlink(stdintmp);
if (stat(tartmp,&finfo)==0) {
snprintf(MAXS(tmp),
"tmp-file %s does already exist and cannot be deleted",tartmp);
message(prg,'F',tmp);
}
if (stat(ziptmp,&finfo)==0) {
snprintf(MAXS(tmp),
"tmp-file %s does already exist and cannot be deleted",ziptmp);
message(prg,'F',tmp);
}
if (stat(texttmp,&finfo)==0) {
snprintf(MAXS(tmp),
"tmp-file %s does already exist and cannot be deleted",texttmp);
message(prg,'F',tmp);
}
if (stat(pgptmp,&finfo)==0) {
snprintf(MAXS(tmp),
"tmp-file %s does already exist and cannot be deleted",pgptmp);
message(prg,'F',tmp);
}
if (stat(stdintmp,&finfo)==0) {
snprintf(MAXS(tmp),
"tmp-file %s does already exist and cannot be deleted",stdintmp);
message(prg,'F',tmp);
}
/* parse the global config-file */
if ((inf=rfopen(CONFIG,"r"))) {
while (fgetl(line,inf)) {
/* prepare line to be parsed */
if ((cp=strchr(line,'#'))) *cp=0;
if ((cp=strchr(line,'='))) *cp=' ';
str_tolower(str_trim(line));
/* is there an option and an argument? */
if ((argp=strchr(line,' '))) {
*argp=0; argp++;
if (str_eq(line,"packet")) {
packet_size=atoi(argp);
continue;
}
if (str_eq(line,"lanspeed")) {
lanspeed=atoi(argp);
if (lanspeed<0) lanspeed=0;
continue;
}
if (str_eq(line,"log")) {
if (str_eq(argp,"out") || str_eq(argp,"both")) outlogging=1;
continue;
}
if (str_eq(line,"spooling")) {
if (str_eq(argp,"nostart")) spooling=1;
if (str_eq(argp,"on")) spooling=2;
continue;
}
if (str_eq(line,"dontcompress")) {
if (*argp) {
/* save the whole string in element #0 */
if (!(dontcompress[0]=strdup(argp)))
message(prg,'F',"out of memory");
/* parse and split the list (tricky, eh? :-) ) */
for (n=1,cp=strtok(dontcompress[0],", \t");
cp && n<99;
n++,cp=strtok(NULL,", \t")) {
/*printf("sfconf: >%s<\n",cp); */
dontcompress[n]=cp;
}
dontcompress[n]=""; /* last element must terminate the list! */
}
continue;
}
}
}
fclose(inf);
}
/* spooling allowed? */
if ((spool || list) && !spooling) {
errno=0;
message(prg,'F',"outgoing spooling of files is not allowed on this system");
}
/* list files in outgoing spool */
if (list) exit(list_spool());
/* set tcp packet length */
if (packet_size<1) packet_size=PACKET;
if (verbose && !spool && !del) {
snprintf(MAXS(tmp),"packet size = %d bytes",packet_size);
message(prg,'I',tmp);
}
/* enable simple interrupt handler */
signal(SIGTERM,cleanexit);
signal(SIGABRT,cleanexit);
signal(SIGQUIT,cleanexit);
signal(SIGHUP,cleanexit);
signal(SIGINT,cleanexit);
/* file as stdin data stream? */
if (stdinf) {
/* write stdin to tmp-file */
if (!(outf=rfopen(stdintmp,"w"))) {
snprintf(MAXS(tmp),"cannot open tmp-file %s",stdintmp);
message(prg,'F',tmp);
}
while ((ch=getchar())!=EOF) putc(ch,outf);
fclose(outf);
}
/* determine the file type */
type="BINARY";
if (text) type="TEXT="CHARSET;
if (mime) type="MIME";
if (source) type="SOURCE";
/* no testing mode */
if (!test) {
/* prepare sending or spooling */
if (!spool) {
/* look for correct SAFT server and open connection */
sockfd=saft_connect("file",recipient,user,host,redirect);
/* compression mode wanted? */
if (*compress) do_compress=1;
if (do_compress && !*force_compress)
do_compress=linkspeed(host,lanspeed,&compress);
/* test if server can handle bzip2 */
if (do_compress && *zprg && strstr(zprg,"bzip2")) {
sendcommand(sockfd,"TYPE BINARY COMPRESSED=BZIP2",reply);
if (!str_beq(reply,"200 ") && *gzip_bin && !*force_compress) {
compress=S_GZIP;
strcpy(zprg,gzip_bin);
} else
compress=S_BZIP2;
}
} else /* outgoing spooling */ {
/* does the outgoing spool exist? */
strcpy(outgoing,SPOOL"/OUTGOING");
if (stat(outgoing,&finfo)<0 || !S_ISDIR(finfo.st_mode)) {
snprintf(MAXS(tmp),"spool directory %s does not exist",outgoing);
message(prg,'F',tmp);
}
/* and does it have the correct protection? */
if (!((finfo.st_mode&S_ISVTX) && (finfo.st_mode&S_IRWXO))) {
snprintf(MAXS(tmp),
"spool directory %s has wrong protection (must have 1777)",
outgoing);
message(prg,'F',tmp);
}
/* name the local host */
if (str_eq(host,"127.0.0.1") || str_eq(host,"0")) strcpy(host,localhost);
}
}
/* bouncing files? */
if (bounce) {
/* does the spool directory exist? */
if (chdir(userspool)<0) {
snprintf(MAXS(tmp),"cannot access spool directory %s",userspool);
message(prg,'F',tmp);
}
/* main loop over the spool file names */
for (fn=optind; fn<argc-1; fn++) {
snprintf(MAXS(sdfn),"%s.d",argv[fn]);
snprintf(MAXS(shfn),"%s.h",argv[fn]);
if (info) snprintf(MAXS(tinfo),"#%d/%d: ",fn-optind+1,argc-optind-1);
/* try to open spool header file */
if (!(shf=rfopen(shfn,"r"))) {
snprintf(MAXS(tmp),"cannot open spool file #%s",argv[fn]);
message(prg,'E',tmp);
continue;
}
/* write to outgoing spool? */
if (spool) {
/* open outgoing spool header file */
if ((oshf=outspool(pw_name,outgoing,oshfn))==NULL)
message(prg,'F',"cannot create outgoing spool file");
/* TAB as whitespace is needed here, because scanspool insists on it */
fprintf(oshf,"FROM\t%s\n",user);
fprintf(oshf,"TO\t%s@%s\n",recipient,host);
}
*file=0;
*comment=0;
compress="";
/* scan spool header file and forward it */
while (fgetl(line,shf)) {
str_trim(line);
/* corrupt header line? */
if (!strchr(line,' ')) continue;
/* store original from as comment */
if (str_beq(line,"FROM")) {
strcpy(comment,strchr(line,' ')+1);
if ((cp=strchr(comment,' '))) {
*cp=0;
snprintf(MAXS(tmp),"%s+ACA-(%s)",comment,cp+1);
strcpy(comment,tmp);
}
continue;
}
/* store size and file name for later processing */
if (str_beq(line,"FILE")) utf2iso(0,NULL,file,NULL,line+5);
/* check if compression method is supported */
if (str_beq(line,"TYPE") && strstr(line," COMPRESSED")) {
for(;;) {
type="";
/* bzip2 ok? */
if (strstr(line," COMPRESSED=BZIP2")) {
type=S_BZIP2;
compress=S_BZIP2;
sendcommand(sockfd,line,reply);
if (str_beq(reply,"200 ")) break;
cp=strrchr(line,'=');
*cp=0;
}
/* gzip ok? */
if (!*type) type=S_GZIP;
compress=S_GZIP;
sendcommand(sockfd,line,reply);
if (str_beq(reply,"200 ")) break;
/* no compression ok? */
cp=strrchr(line,' ');
*cp=0;
compress="";
sendcommand(sockfd,line,reply);
if (str_beq(reply,"200 ")) break;
/* error! */
snprintf(MAXS(tmp),"cannot send %s : %s",file,reply+4);
errno=0;
message(prg,'E',tmp);
fclose(inf);
break;
}
/* compression methode accepted */
if (!str_eq(type,compress)) {
/* recompress spool file */
if (str_eq(type,S_BZIP2)) {
if (str_eq(compress,S_GZIP))
snprintf(MAXS(cmd),"%s -d <%s|%s>%s",BZIP2,sdfn,GZIP,ziptmp);
else
snprintf(MAXS(cmd),"%s -d < %s > %s",BZIP2,sdfn,ziptmp);
} else
snprintf(MAXS(cmd),"%s -dc %s > %s",GZIP,sdfn,ziptmp);
/* execute shell-command and close spool header file on error */
if (system(cmd)) {
snprintf(MAXS(tmp),"cannot recompress spool file #%s",argv[fn]);
message(prg,'E',tmp);
fclose(inf);
break;
}
}
continue; /* read next line from spool header file */
}
/* store size for later processing */
if (str_beq(line,"SIZE ")) {
sscanf(line,"SIZE %ld %ld",&size,&orgsize);
strcpy(sizes,line+5);
}
/* is there already a comment line? */
if (str_beq(line,"COMMENT")) {
if (*redirect)
snprintf(MAXS(line),"%s+AA0ACg-%s",comment,redirect);
else {
snprintf(MAXS(tmp),
"%s+AA0ACg-forward+ACA-from+ACA-%s",line,comment);
strcpy(line,tmp);
}
if (spool)
fprintf(oshf,"%s\n",line);
else
sendcommand(sockfd,line,NULL);
*comment=0;
continue;
}
/* all other header lines */
if (spool)
fprintf(oshf,"%s\n",line);
else
sendheader(sockfd,line);
}
/* if file has been already closed there is an error (see above) */
if (fclose(shf)) {
if (spool) fclose(oshf);
continue;
}
/* send comment if not already done */
if (*comment) {
iso2utf(tmp,"forward from ");
snprintf(MAXS(line),"COMMENT %s%s",tmp,comment);
if (*redirect) {
snprintf(MAXS(tmp),"\r\n%s",redirect);
iso2utf(comment,tmp);
strcat(line,comment);
}
if (spool)
fprintf(oshf,"%s\n",line);
else
sendcommand(sockfd,line,NULL);
}
/* check the file size */
if (stat(ziptmp,&finfo)==0) {
size=finfo.st_size;
snprintf(MAXS(sizes),"%ld %ld",size,orgsize);
snprintf(MAXS(line),"SIZE %s",sizes);
sendcommand(sockfd,line,NULL);
} else {
if (stat(sdfn,&finfo)<0 || size!=finfo.st_size) {
snprintf(MAXS(tmp),
"spool file #%s has wrong size count - ignored",argv[fn]);
errno=0;
message(prg,'E',tmp);
if (spool) fclose(oshf);
continue;
}
}
/* copy spool file to outgoing */
if (spool) {
fclose(oshf);
strcpy(osdfn,oshfn);
osdfn[strlen(osdfn)-1]='d';
if (fcopy(sdfn,osdfn,S_IRUSR|S_IWUSR)<0) {
unlink(oshfn);
unlink(osdfn);
} else {
if (str_eq(bouncearg,"k=n")) {
unlink(shfn);
unlink(sdfn);
}
strcpy(tmp,oshfn);
oshfn[strlen(oshfn)-1]='h';
rename(tmp,oshfn);
spooled_info(iso_name,osdfn,do_compress);
}
} else /* forward the spool data file */ {
if (stat(ziptmp,&finfo)==0) {
status=send_data(sockfd,size,ziptmp,tinfo,file,"",mtp,&attime);
unlink(ziptmp);
} else
status=send_data(sockfd,size,sdfn,tinfo,file,"",mtp,&attime);
/* summarize transfer statistics */
if (attime) {
tfn++;
tsize+=size;
tttime+=attime;
}
if (status==0) {
/* log data */
outlog(recipient,host,file,sizes);
notespeed(host,size,attime);
/* if not keep file delete the spool files */
if (str_eq(bouncearg,"k=n")) {
unlink(shfn);
unlink(sdfn);
}
}
}
}
} else if (tar||zip) { /* sending tar or zip archive */
/* translate the archive name to UTF-7 */
iso2utf7(utf_name,archive,0);
/* collect all files */
for (n=optind; n<argc-1; n++) {
strcat(filelist," ");
strcat(filelist,argv[n]);
}
/* do the tar, man! :-) */
if (verbose)
snprintf(MAXS(cmd),"trap \"rm -f %s 2>/dev/null\" 1 2 3;%s cvf %s %s",
tartmp,tar_bin,tartmp,filelist);
else {
if (!quiet) printf("making archive...\r");
fflush(stdout);
snprintf(MAXS(cmd),"trap \"rm -f %s 2>/dev/null\" 1 2 3;%s cf %s %s",
tartmp,tar_bin,tartmp,filelist);
}
if (verbose) printf("%s\n",strchr(cmd,';')+1);
if (system(cmd)) {
message(prg,'E',"no complete archive file");
if (!quiet) {
printf("Continue? ");
fgetl(tmp,stdin);
if (toupper(tmp[0])!='Y') exit(1);
}
}
/* get the file name and size */
strcpy(file,tartmp);
if (stat(file,&finfo)<0) message(prg,'F',"cannot access tmp file");
orgsize=finfo.st_size;
size=orgsize;
/* compression mode wanted? */
if (*compress) do_compress=1;
/* compressed mode? */
if (do_compress) {
/* compress tar-archive */
if (!quiet) printf("compressing... \r");
fflush(stdout);
snprintf(MAXS(cmd),"trap \"rm -f %s %s 2>/dev/null\" 1 2 3;%s < %s > %s",
tartmp,ziptmp,zprg,tartmp,ziptmp);
if (verbose) {
snprintf(MAXS(tmp),"shell-call: %s",strchr(cmd,';')+1);
message(prg,'I',tmp);
}
if (system(cmd)) message(prg,'F',"cannot compress archive file");
strcpy(file,ziptmp);
}
/* pgp encryption? */
if (pgpcrypt) pgp_encrypt(pgpcrypt,pgprid,file);
/* pgp signature to add? */
if (*pgpsign) pgp_sign(pgpsign,file,sockfd);
/* get the file size */
if (stat(file,&finfo)<0) message(prg,'F',"cannot access tmp file");
size=finfo.st_size;
snprintf(MAXS(sizes),"%ld %ld",size,orgsize);
/* write to outgoing spool? */
if (spool) {
/* overwrite archive file? */
if (overwrite && (hls=scanoutspool(pw_name))) {
/* create correct to-string */
if (strchr(argv[argc-1],'*'))
snprintf(MAXS(to),"%s",argv[argc-1]);
else
snprintf(MAXS(to),"%s@%s",recipient,host);
/* search for file in outgoing spool */
for (hlp=hls; hlp; hlp=hlp->next) {
for (oflp=hlp->flist; oflp; oflp=oflp->next) {
/* matching file name? */
if (simplematch(oflp->fname,utf_name,0)) {
/* matching recipient? */
snprintf(MAXS(rto),"%s@%s",oflp->to,hlp->host);
if (simplematch(rto,to,0)) {
unlink(oflp->oshfn);
oflp->oshfn[strlen(oflp->oshfn)-1]='d';
unlink(oflp->oshfn);
}
}
}
}
}
/* open outgoing spool header file */
if ((oshf=outspool(pw_name,outgoing,oshfn))==NULL)
message(prg,'F',"cannot create outgoing spool file");
/* TAB as whitespace is needed here, because scanspool insists on it */
fprintf(oshf,"FROM\t%s\n",user);
fprintf(oshf,"TO\t%s@%s\n",recipient,host);
fprintf(oshf,"FILE\t%s\n",utf_name);
if (*compress) {
if (str_eq(compress,S_GZIP))
fprintf(oshf,"TYPE\tBINARY COMPRESSED\n");
else
fprintf(oshf,"TYPE\tBINARY COMPRESSED=%s\n",compress);
} else if (pgpcrypt) {
fprintf(oshf,"TYPE\tBINARY CRYPTED\n");
} else
fprintf(oshf,"TYPE\tBINARY\n");
fprintf(oshf,"SIZE\t%s\n",sizes);
fprintf(oshf,"ATTR\tTAR\n");
} else { /* send header lines */
if (overwrite) {
snprintf(MAXS(tmp),"FILE %s",utf_name);
sendcommand(sockfd,tmp,reply);
if (str_beq(reply,"200 ")) sendcommand(sockfd,"DEL",reply);
}
snprintf(MAXS(tmp),"FILE %s",utf_name);
sendcommand(sockfd,tmp,reply);
if (!test && !str_beq(reply,"200 ") && quiet<2)
message(prg,'W',"remote site does not support file names");
if (*compress) {
if (str_eq(compress,S_GZIP))
snprintf(MAXS(tmp),"TYPE BINARY COMPRESSED");
else
snprintf(MAXS(tmp),"TYPE BINARY COMPRESSED=%s",compress);
sendcommand(sockfd,tmp,reply);
if (!test && !str_beq(reply,"200 ") && quiet<2) {
errno=0;
message(prg,'F',"remote site does not support compressed files");
}
} else if (pgpcrypt) {
sendcommand(sockfd,"TYPE BINARY CRYPTED",reply);
if (!test && !str_beq(reply,"200 ") && quiet<2) {
errno=0;
message(prg,'F',"remote site does not support encrypted files");
}
} else {
sendcommand(sockfd,"TYPE BINARY",reply);
if (!test && !str_beq(reply,"200 ") && quiet<2)
message(prg,'W',"remote site does not support binary files");
}
snprintf(MAXS(tmp),"SIZE %s",sizes);
sendheader(sockfd,tmp);
sendcommand(sockfd,"ATTR TAR",reply);
if (!test && !str_beq(reply,"200 ") && quiet<2) {
errno=0;
message(prg,'F',"remote site does not support archive file type");
}
}
/* comment to send? */
if (*comment || *redirect) {
*line=0;
if (*comment) strcat(line,comment);
if (*redirect) {
if (*line) {
snprintf(MAXS(tmp),"%s\r\n%s",line,redirect);
strcpy(line,tmp);
} else
strcpy(line,redirect);
}
iso2utf(tmp,line);
if (spool)
fprintf(oshf,"COMMENT\t%s\n",tmp);
else {
snprintf(MAXS(line),"COMMENT %s",tmp);
sendcommand(sockfd,line,NULL);
}
}
/* send the file data */
if (spool) {
fclose(oshf);
strcpy(osdfn,oshfn);
osdfn[strlen(osdfn)-1]='d';
if (fcopy(file,osdfn,S_IRUSR|S_IWUSR)<0) {
unlink(oshfn);
unlink(osdfn);
} else {
strcpy(tmp,oshfn);
oshfn[strlen(oshfn)-1]='h';
rename(tmp,oshfn);
spooled_info(archive,osdfn,do_compress);
}
} else {
strcpy(tmp,"archive");
if (*compress) strcat(tmp," compressed");
if (pgpcrypt) strcat(tmp," crypted");
if (send_data(sockfd,size,file,"",archive,tmp,mtp,&attime)==0) {
/* summarize transfer statistics */
if (attime) {
tfn++;
tsize+=size;
tttime+=attime;
}
/* do some logging */
outlog(recipient,host,utf_name,sizes);
notespeed(host,size,attime);
}
}
} else /* sending or deleting single files */ {
/* main loop over the file names */
for (fn=optind; fn<argc-1; fn++) {
if (info) snprintf(MAXS(tinfo),"#%d/%d: ",fn-optind+1,argc-optind-1);
/* file from stdin? */
if (stdinf) {
/* get real file name and transmission file name */
strcpy(file,stdintmp);
strcpy(iso_name,argv[fn]);
stdinf=0;
} else /* normal file */ {
/* get real file name */
strcpy(file,argv[fn]);
/* get the file name without path */
fnp=strrchr(file,'/');
if (!fnp) fnp=file; else fnp++;
/* get transmission file name */
strcpy(iso_name,fnp);
}
/* translate the file name into UTF-7 */
iso2utf7(utf_name,iso_name,0);
/* write to outgoing spool? */
if (spool) {
/* delete file? */
if ((del || overwrite) && (hls=scanoutspool(pw_name))) {
/* create correct to-string */
if (strchr(argv[argc-1],'*'))
snprintf(MAXS(to),"%s",argv[argc-1]);
else
snprintf(MAXS(to),"%s@%s",recipient,host);
/* search for file in outgoing spool */
for (hlp=hls; hlp; hlp=hlp->next) {
for (oflp=hlp->flist; oflp; oflp=oflp->next) {
/* matching file name? */
if (simplematch(oflp->fname,utf_name,0)) {
/* matching recipient? */
snprintf(MAXS(rto),"%s@%s",oflp->to,hlp->host);
if (simplematch(rto,to,0)) {
unlink(oflp->oshfn);
oflp->oshfn[strlen(oflp->oshfn)-1]='d';
unlink(oflp->oshfn);
if (del) {
del=2;
utf2iso(0,NULL,file,NULL,oflp->fname);
snprintf(MAXS(tmp),
"deleted from outgoing spool: '%s' for %s ",
file,rto);
if (quiet<2) message(prg,'I',tmp);
}
}
}
}
}
}
if (del) continue;
/* open outgoing spool header file */
if ((oshf=outspool(pw_name,outgoing,oshfn))==NULL)
message(prg,'F',"cannot create outgoing spool file");
fprintf(oshf,"FROM\t%s\n",user);
fprintf(oshf,"TO\t%s@%s\n",recipient,host);
fprintf(oshf,"FILE\t%s\n",utf_name);
/*if (del || overwrite) fprintf(oshf,"DEL\n");*/
} else /* send header lines */ {
/* send file name */
snprintf(MAXS(tmp),"FILE %s",utf_name);
sendcommand(sockfd,tmp,reply);
if (!test && !str_beq(reply,"200 ") && quiet<2)
message(prg,'W',"remote site does not support file names");
/* delete file? */
if (overwrite) sendcommand(sockfd,"DEL",NULL);
if (del) {
if (sendheader(sockfd,"DEL")) {
if (quiet<2) message(prg,'W',"remote site cannot delete files");
} else {
snprintf(MAXS(tmp),"'%s' deleted",iso_name);
if (quiet<2) message(prg,'I',tmp);
}
continue;
}
}
/* is the file readable? */
if (stat(file,&finfo)<0) {
snprintf(MAXS(tmp),"cannot access '%s'",file);
message(prg,'E',tmp);
if (spool) {
fclose(oshf);
unlink(oshfn);
}
continue;
}
/* is it a regular file? */
if (!S_ISREG(finfo.st_mode)) {
errno=0;
snprintf(MAXS(tmp),"%s is not a regular file, skipping",file);
message(prg,'E',tmp);
if (spool) {
fclose(oshf);
unlink(oshfn);
}
continue;
}
/* is it a executable? */
if (finfo.st_mode&S_IXUSR) exe=1; else exe=0;
/* no file mode set? guess the file type */
mode=guess_ftype(file,ftype);
if (!guess) mode=0;
/* get the original file size */
strftime(date,20,"%Y-%m-%d %H:%M:%S",gmtime(&finfo.st_mtime));
#ifdef HPUX
/* dirty hack around HPUX strftime bug */
date[17]=0;
strcat(date,"00");
#endif
/* text or source mode? */
if (text || source || mode=='t' || mode=='s') {
/* open and test file to send and open tmp-file */
inf=rfopen(file,"r");
outf=rfopen(texttmp,"w");
if (!inf) {
snprintf(MAXS(tmp),"cannot open '%s'",file);
message(prg,'E',tmp);
if (spool) {
fclose(oshf);
unlink(oshfn);
}
continue;
}
if (!outf) message(prg,'F',"cannot open tmp-file");
/* convert file to NVT format */
do {
ch=fgetc(inf);
if (ch!=EOF) {
if (ch=='\n')
fprintf(outf,"\r\n");
else
fputc(ch,outf);
}
} while (!feof(inf));
fclose(inf);
fclose(outf);
strcpy(file,texttmp);
}
/* get the original file size */
stat(file,&finfo);
orgsize=finfo.st_size;
/* compression mode wanted? */
if (*compress) do_compress=1;
/* check if file is compressible */
if (do_compress && !*force_compress) {
/* do not compress too small files */
if (finfo.st_size < 1024) do_compress=0;
/* determine file type */
/* loop over all non-compressible file type strings */
for (n=0;do_compress && *cft[n];n++) {
/* is this file a not compressible one? */
snprintf(MAXS(tmp),"*%s*",cft[n]);
if (simplematch(ftype,tmp,1)) do_compress=0;
}
}
/* compressed mode? */
if (do_compress) {
/* compress tmp-file */
if (!quiet) printf("compressing... \r");
fflush(stdout);
snprintf(MAXS(cmd),
"trap \"rm -f %s 2>/dev/null\" 1 2 3;%s < '%s' > %s",
ziptmp,zprg,file,ziptmp);
if (verbose) {
snprintf(MAXS(tmp),"shell-call: %s",strchr(cmd,';')+1);
message(prg,'I',tmp);
}
if (system(cmd)) {
snprintf(MAXS(tmp),"cannot compress %s",file);
message(prg,'E',tmp);
if (spool) {
fclose(oshf);
unlink(oshfn);
}
continue;
}
strcpy(file,ziptmp);
} else
size=orgsize;
/* pgp encryption? */
if (pgpcrypt) pgp_encrypt(pgpcrypt,pgprid,file);
/* pgp signature to add? */
if (*pgpsign) pgp_sign(pgpsign,file,sockfd);
/* get the file size */
if (stat(file,&finfo)<0) message(prg,'F',"cannot access tmp file");
size=finfo.st_size;
snprintf(MAXS(sizes),"%ld %ld",size,orgsize);
/* determine the file type */
type="BINARY";
if (guess) {
if (mode=='s') type="SOURCE";
if (mode=='t') type="TEXT="CHARSET;
}
else if (mime) type="MIME";
else if (source) type="SOURCE";
else if (text) type="TEXT="CHARSET;
/* write to outgoing spool? */
if (spool) {
if (do_compress) {
if (str_eq(compress,S_GZIP))
fprintf(oshf,"TYPE\tBINARY COMPRESSED\n");
else
fprintf(oshf,"TYPE\tBINARY COMPRESSED=%s\n",compress);
} else if (pgpcrypt)
fprintf(oshf,"TYPE\t%s CRYPTED\n",type);
else
fprintf(oshf,"TYPE\t%s\n",type);
fprintf(oshf,"SIZE\t%s\n",sizes);
fprintf(oshf,"DATE\t%s\n",date);
if (exe)
fprintf(oshf,"ATTR\tEXE\n");
else
fprintf(oshf,"ATTR\tNONE\n");
} else /* send the header information */ {
if (do_compress) {
if (str_eq(compress,S_GZIP))
snprintf(MAXS(line),"TYPE %s COMPRESSED",type);
else
snprintf(MAXS(line),"TYPE %s COMPRESSED=%s",type,compress);
} else if (pgpcrypt)
snprintf(MAXS(line),"TYPE %s CRYPTED",type);
else
snprintf(MAXS(line),"TYPE %s",type);
sendcommand(sockfd,line,reply);
if (!test && !str_beq(reply,"200 ") && quiet<2) {
errno=0;
snprintf(MAXS(tmp),"remote site does not support file of %s",line);
message(prg,'F',tmp);
}
snprintf(MAXS(tmp),"SIZE %s",sizes);
sendheader(sockfd,tmp);
snprintf(MAXS(tmp),"DATE %s",date);
if (sendheader(sockfd,tmp) && quiet<2)
message(prg,'W',"remote site does not support dates");
if (exe)
sendcommand(sockfd,"ATTR EXE",NULL);
else
sendcommand(sockfd,"ATTR NONE",NULL);
}
/* comment to send? */
if (*comment || *redirect) {
*line=0;
if (*comment) strcat(line,comment);
if (*redirect) {
if (*line) {
snprintf(MAXS(tmp),"%s\r\n%s",line,redirect);
strcpy(line,tmp);
} else
strcpy(line,redirect);
}
iso2utf(tmp,line);
if (spool)
fprintf(oshf,"COMMENT\t%s\n",tmp);
else {
snprintf(MAXS(line),"COMMENT %s",tmp);
sendcommand(sockfd,line,NULL);
}
}
/* send the file data */
if (spool) {
fclose(oshf);
strcpy(osdfn,oshfn);
osdfn[strlen(osdfn)-1]='d';
if (fcopy(file,osdfn,S_IRUSR|S_IWUSR)<0) {
unlink(oshfn);
unlink(osdfn);
} else {
strcpy(tmp,oshfn);
oshfn[strlen(oshfn)-1]='h';
rename(tmp,oshfn);
spooled_info(iso_name,osdfn,do_compress);
}
} else {
strcpy(tmp,type);
if ((cp=strchr(tmp,'='))) *cp=0;
str_tolower(tmp);
if (do_compress) strcat(tmp," compressed");
if (pgpcrypt) strcat(tmp," crypted");
if (send_data(sockfd,size,file,tinfo,iso_name,tmp,mtp,&attime)==0) {
/* summarize transfer statistics */
if (attime) {
tfn++;
tsize+=size;
tttime+=attime;
}
/* do some logging */
outlog(recipient,host,utf_name,sizes);
notespeed(host,size,attime);
}
}
}
}
/* start spool daemon or close the connection */
if (spool) {
if (del) {
if (del<2 && quiet<2)
message(prg,'W',"no matching files found in outgoing spool");
} else {
if (spooling==2) start_spooldaemon(localhost);
}
} else {
sendcommand(sockfd,"QUIT",NULL);
close(sockfd);
}
/* print total transfer statistics */
if (tsize && info && tfn>1 && quiet<2) {
thruput=tsize*1000/tttime;
if (tsize/1024>9999)
snprintf(MAXS(tmp),"%d files sent with %.1f MB",tfn,tsize/1024/1024);
else if (tsize>9999)
snprintf(MAXS(tmp),"%d files sent with %.1f kB",tfn,tsize/1024);
else
snprintf(MAXS(tmp),"%d files sent with %d byte",tfn,(int)tsize);
if (thruput>9999)
snprintf(MAXS(line),"%s at %.1f kB/s",tmp,thruput/1024);
else
snprintf(MAXS(line),"%s at %d byte/s",tmp,(int)thruput);
message("",'I',line);
}
/* delete tmp-files */
cleanup();
exit(0);
}
/*
* cleanexit - clean termination routine
*
* A very simple interrupt handler
*/
void cleanexit() {
printf("\r\n");
cleanup();
exit(0);
}
/*
* cleanup - delete tmp files and send LOG command to local server
*/
void cleanup() {
int
sockfd; /* socket file descriptor */
char
line[MAXLEN], /* one line of text */
reply[MAXLEN], /* reply string from server */
server[FLEN]; /* saft server */
/* ignore all relevant signals */
signal(SIGTERM,SIG_IGN);
signal(SIGABRT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
signal(SIGHUP,SIG_IGN);
signal(SIGINT,SIG_IGN);
if (verbose<2) verbose=0;
*reply=0;
#ifndef DEBUG
rmtmpdir(tmpdir);
if (outlogging) {
/* inform local saft server about outgoing log file */
if (access(outlogtmp,R_OK)==0 && !test &&
(sockfd=open_connection("127.0.0.1",SAFT))>=0) {
/* check local saft server */
sock_getline(sockfd,reply);
if (str_beq(reply,"220 ") && strstr(reply,"SAFT")) {
/* send LOG command */
snprintf(MAXS(line),"LOG %s %s",pw_name,outlogtmp);
sock_putline(sockfd,line);
sock_getline(sockfd,reply);
str_trim(reply);
/* emergency exit */
if (*reply=='4') {
unlink(outlogtmp);
exit(0);
}
/* forward address to real saft server? */
if (str_beq(reply,"510 ")) {
strcpy(server,strrchr(reply,' ')+1);
/* close current connection */
sendcommand(sockfd,"QUIT",NULL);
shutdown(sockfd,2);
/* connect real saft server */
if (!test && (sockfd=open_connection(server,SAFT))>=0) {
sock_getline(sockfd,reply);
if (str_beq(reply,"220 ") && strstr(reply,"SAFT")) {
/* send LOG command */
snprintf(MAXS(line),"LOG %s %s",pw_name,outlogtmp);
sock_putline(sockfd,line);
sock_getline(sockfd,reply);
str_trim(reply);
/* emergency exit */
if (*reply=='4') {
unlink(outlogtmp);
exit(0);
}
}
}
}
}
/* close the connection */
sendcommand(sockfd,"QUIT",NULL);
shutdown(sockfd,2);
}
}
unlink(outlogtmp);
#endif
}
/*
* pgp_encrypt - encrypt a file with pgp
*
* INPUT: pgpcrypt - public key or IDEA encryption
* pgprid - pgp recipient id
* file - input file name
*
* OUTPUT: file - output file name
*/
void pgp_encrypt(int pgpcrypt, char *pgprid, char *file) {
char
*cp, /* simple character pointer */
cmd[OVERSIZE], /* the command for system() */
tmp[MAXLEN], /* temporary string */
line[MAXLEN], /* one text line */
rmcmd[MAXLEN]; /* the rm command */
struct stat finfo; /* information about a file */
FILE *inf; /* input file */
inf=NULL;
/* determine which tmp-files to delete in system-trap */
if (str_eq(file,tartmp) || str_eq(file,texttmp))
snprintf(MAXS(rmcmd),
"trap \"rm -f %s %s 2>/dev/null\" 1 2 3 15",file,pgptmp);
else
snprintf(MAXS(rmcmd),"trap \"rm -f %s 2>/dev/null\" 1 2 3 15",pgptmp);
if (!quiet) message(prg,'I',"call to pgp...");
/* look for matching pgp-IDs */
if (strlen(pgprid)>1) {
snprintf(MAXS(cmd),"%s -kvf %s > %s 2>/dev/null",pgp_bin,pgprid,pgptmp);
if (verbose) {
snprintf(MAXS(tmp),"shell-call: %s",cmd);
message(prg,'I',tmp);
}
system(cmd);
if (stat(pgptmp,&finfo)<0 || finfo.st_size==0 || !(inf=rfopen(pgptmp,"r"))) {
errno=0;
message(prg,'F',"call to pgp failed");
}
while (fgetl(line,inf) && !strstr(line,"matching key"));
fclose(inf);
if ((cp=strchr(line,'.'))) *cp=0;
if (!str_eq(line,"1 matching key found")) {
if (!quiet) {
snprintf(MAXS(line),"ambigous pgp-ID '%s'",pgprid);
message(prg,'W',line);
inf=rfopen(pgptmp,"r");
while (fgetl(line,inf)) printf("%s",line);
fclose(inf);
}
*pgprid=0;
}
} else
*pgprid=0;
/* pgp needs user input? */
if (pgpcrypt=='c' || !*pgprid) {
snprintf(MAXS(cmd),"%s;%s +armor=off -f%c < %s > %s",
rmcmd,pgp_bin,pgpcrypt,file,pgptmp);
if (verbose) {
snprintf(MAXS(tmp),"shell-call: %s",strchr(cmd,';')+1);
message(prg,'I',tmp);
}
if (system(cmd) || stat(pgptmp,&finfo)<0 || finfo.st_size==0) {
errno=0;
message(prg,'F',"call to pgp failed");
}
printf("\n");
/* pgp needs no user input */
} else {
snprintf(MAXS(cmd),"%s;%s +armor=off -fe %s < %s > %s 2>/dev/null",
rmcmd,pgp_bin,pgprid,file,pgptmp);
if (verbose) {
snprintf(MAXS(tmp),"shell-call: %s",strchr(cmd,';')+1);
message(prg,'I',tmp);
}
if (system(cmd) || stat(pgptmp,&finfo)<0 || finfo.st_size==0) {
errno=0;
message(prg,'F',"call to pgp failed (wrong pgp user id?)");
}
}
strcpy(file,pgptmp);
}
/*
* pgp_sign - create detached pgp signature file and send SIGN header line
*
* INPUT: pgpsign - pgp user id
* infile - input file name
* sockfd - socket descriptor
*/
void pgp_sign(const char *pgpsign, const char *infile, int sockfd) {
int
check; /* check if sig is ok */
char
*cp, /* simple string pointer */
tmp[MAXLEN], /* temporary string */
sign[MAXLEN], /* signature */
line[MAXLEN], /* one line of text */
cmd[2*MAXLEN]; /* the command for popen() */
FILE *pipe; /* input file descriptor */
check=0;
*sign=0;
if (!quiet && !pgppass) message(prg,'I',"call to pgp...");
snprintf(MAXS(cmd),"%s %s -fsba %s < %s",pgp_bin,pgpvm,pgpsign,infile);
if (verbose) {
snprintf(MAXS(tmp),"shell-call: %s",cmd);
message(prg,'I',tmp);
}
if (!(pipe=popen(cmd,"r"))) message(prg,'F',"call to pgp (signature) failed");
/* read signature file in NVT format */
while (fgetl(line,pipe)) {
if ((cp=strchr(line,'\n'))) *cp=0;
if (str_eq(line,"-----BEGIN PGP MESSAGE-----")) check+=1;
if (str_eq(line,"-----END PGP MESSAGE-----")) check+=2;
strcat(sign,line);
strcat(sign,"\r\n");
}
pclose(pipe);
if (!pgppass) printf("\n");
if (check!=3) message(prg,'F',"call to pgp (signature) failed");
iso2utf(tmp,sign);
snprintf(MAXS(sign),"SIGN %s",tmp);
if (sockfd) sendcommand(sockfd,sign,NULL);
}
/*
* outspool - create and open outgoing spool header file
*
* INPUT: user - own user name
* outgoing - outgoing spool directory
*
* OUTPUT: oshf - outgoing spool header file name
*
* RETURN: FILE pointer if ok, NULL if failed
*
* This function does not use a locking or a atomar test&create function
* because this would not work with NFS. To avoid race conditions (remember:
* a spool daemon is normally running, too!) a tmp-header file is created.
*/
FILE *outspool(const char *user, const char *outgoing, char *oshf) {
struct stat finfo; /* information about a file */
struct timeval tv;
#if !defined(SOLARIS2) && !defined(IRIX)
struct timezone tz;
#endif
/* get time structure */
#if defined(SOLARIS2) || defined(IRIX)
# ifdef _SVID_GETTOD
gettimeofday(&tv);
# else
gettimeofday(&tv,NULL);
# endif
#else
gettimeofday(&tv,&tz);
#endif
/* build (tmp) header file name */
snprintf(oshf,MAXLEN-1,"%s/%s_%d.t",outgoing,user,(int)tv.tv_usec);
/* does the file already exist? */
if (stat(oshf,&finfo)==0) return(NULL);
return(rfopen(oshf,"w"));
}
/*
* reply - dummy function, only needed for linking
*/
void reply(int x) { }
/*
* start_spooldaemon - start local spool daemon for outgoing files
*
* INPUT: localhost - name of the local host
*/
void start_spooldaemon(char *localhost) {
int
sockfd; /* socket file descriptor */
char
*host, /* name of local SAFT server */
reply[MAXLEN], /* reply string from remote server */
tmp[MAXLEN], /* temporary string */
line[MAXLEN]; /* one line of text */
/* open connection to the own server */
sockfd=open_connection(localhost,SAFT);
if (sockfd==-1) snprintf(MAXS(tmp),"cannot create a network socket "
"- cannot start local spool daemon");
if (sockfd==-2) snprintf(MAXS(tmp),"cannot open connection to %s "
"- cannot start local spool daemon",localhost);
if (sockfd==-3) snprintf(MAXS(tmp),"%s is unknown (name server down?) "
"- cannot start local spool daemon",localhost);
if (sockfd<0) {
errno=0;
message(prg,'F',tmp);
}
/* no remote server or protocol error? */
sock_getline(sockfd,line);
if (!str_beq(line,"220 ") || !strstr(line,"SAFT")) {
errno=0;
snprintf(MAXS(tmp),"No SAFT server on port %d at %s "
"- cannot start local spool daemon",SAFT,localhost);
message(prg,'F',tmp);
}
sendcommand(sockfd,"START SPOOLDAEMON",reply);
str_trim(reply);
/* forward to local SAFT-server set? */
if (str_beq(reply,"510 ") && (host=strrchr(reply,' '))) {
host++;
/* close current connection */
sendcommand(sockfd,"QUIT",NULL);
shutdown(sockfd,2);
/* open connection to the own SAFT server */
sockfd=open_connection(host,SAFT);
if (sockfd==-1) snprintf(MAXS(tmp),"cannot create a network socket "
"- cannot start local spool daemon");
if (sockfd==-2) snprintf(MAXS(tmp),"cannot open connection to %s "
"- cannot start local spool daemon",host);
if (sockfd==-3) snprintf(MAXS(tmp),"%s is unknown (name server down?) "
"- cannot start local spool daemon",host);
if (sockfd<0) {
errno=0;
message(prg,'F',tmp);
}
/* no remote server or protocol error? */
sock_getline(sockfd,line);
if (!str_beq(line,"220 ") || !strstr(line,"SAFT")) {
errno=0;
snprintf(MAXS(tmp),"No SAFT server on port %d at %s "
"- cannot start local spool daemon",SAFT,host);
message(prg,'F',tmp);
}
sendheader(sockfd,"START SPOOLDAEMON");
sendheader(sockfd,"QUIT");
}
}
/*
* outlog - create temporary user outgoing log file
*
* INPUT: from - own user name
* to - recipient user name
* host - recipient host name
* file - file name
* sizes - original and compressed file size
*/
void outlog(char *to, char *host, char *file, char *sizes) {
char currentdate[FLEN]; /* current date */
FILE *outf; /* output file */
time_t timetick; /* unix time (in seconds) */
if (outlogging && strcmp(host,localhost) && strcmp(host,"localhost") &&
(outf=rfopen(outlogtmp,"a"))) {
/* get current date */
timetick=time(0);
strftime(currentdate,20,"%Y-%m-%d %H:%M:%S",localtime(&timetick));
/* write to log file */
fprintf(outf,"FROM\t%s\nTO\t%s@%s\nDATE\t%s\nFILE\t%s\nSIZES\t%s\n\n",
pw_name,to,host,currentdate,file,sizes);
fclose(outf);
}
return;
}
/*
* forward - forward a file with complete header from stdin
*
* INPUT: host - host to send to
* mtp - maximum thruput
*/
void forward(char *host, float mtp) {
int
sockfd; /* socket file descriptor */
unsigned long
size; /* size of the file */
float
ttime;
char
*arg, /* SAFT command argument */
from[FLEN], /* sender user name */
recipient[FLEN], /* recipient at serverhost */
line[MAXLEN], /* one line of text */
tmp[MAXLEN]; /* temporary string */
size=0;
*line=*recipient=*from=0;
get_header("FROM",from);
get_header("TO",recipient);
/* look for correct SAFT server and open connection */
sockfd=saft_connect("file",recipient,from,host,tmp);
/* loop over the other header lines */
while (fgetl(line,stdin)) {
str_trim(line);
if (str_neq_nocase("DATA",line,strlen(line))) break;
if (str_neq_nocase("RESEND",line,strlen(line))) {
errno=0;
message(prg,'F',"the RESEND command is not supported with the -X option");
}
/* look for SIZE command */
if ((arg=strchr(line,' '))) {
if (str_neq_nocase("SIZE",line,4)) sscanf(arg+1,"%ld",&size);
}
/* send this SAFT command */
sendheader(sockfd,line);
}
if (!size) {
errno=0;
message(prg,'F',"SIZE command is missing");
}
send_data(sockfd,size,"","","STDIN","",mtp,&ttime);
}
/*
* get_header - read a header line from stdin
*
* INPUT: cmd - command type
*
* OUTPUT: arg - the command argument
*/
void get_header(const char *cmd, char *arg){
char
*cp, /* a simple character pointer */
line[MAXLEN], /* one line of text */
tmp[MAXLEN]; /* temporary string */
/* read one header line */
fgetl(line,stdin);
str_trim(line);
/* split command and argument */
if (strlen(line)>3 && (cp=strchr(line,' '))) {
*cp=0;
strcpy(arg,cp+1);
str_toupper(line);
}
if (!str_eq(cmd,line)) {
errno=0;
line[MAXLEN-80]=0;
snprintf(MAXS(tmp),
"illegal SAFT command \"%s\", \"%s\" was expected",line,cmd);
message(prg,'F',tmp);
}
}
/*
* guess_ftype - guess file type
*
* INPUT: file - file name with path
*
* OUTPUT: type - file type as long string
*
* RETURN: b for binary, s for source, t for text
*
* Remark: you MUST call stat(file) and check the return value before
* calling guess_ftype(), because of symlink loop detection.
*/
char guess_ftype(const char *file, char *type) {
int i;
char
*cp,
**cpp,
tmp[MAXLEN], /* temporary string */
link[MAXLEN], /* symlink file name */
cmd[MAXLEN]; /* the command for popen() */
FILE *pipe; /* input file descriptor */
static const char *set[]= /* source extension type array */
{ "Makefile","Makefile.",
".c",".f",".f77",".for",".f90",".p",".pas",".java",".h",".ada",".pl",".sl",
".cc",".tcl",".tk",".ps","" };
static char *tet[]= /* text extension type array */
{ "README",".txt","" };
static char *sft[]= /* source file type array */
{ "source","shell","program","command","script",
"perl","pascal"," c ","c++","java","fortran","" };
static const char *tft[]= /* text file type array */
{ "text","ASCII","english","" };
static const char *bft[]= /* binary file type array */
{ "data","archive","zip","stripped","" };
static char *cet[]= /* compressed extension type array */
{ ".zip",".z",".zoo",".gz",".bz",".bz2",".tgz",".rpm",
".mp3",".gif",".jpg",".tif",".tiff",".png",".avi",".mpeg","" };
/* first look for known file names and extensions */
/* source types */
for (i=0;*set[i];i++) {
if (*set[i]=='.') {
if ((cp=strrchr(file,'.'))) if str_eq(cp,set[i]) return('s');
} else {
if str_eq(file,set[i]) return('s');
}
}
/* text types */
for (i=0;*tet[i];i++) {
if (*tet[i]=='.') {
if ((cp=strrchr(file,'.'))) if str_eq(cp,tet[i]) return('t');
} else {
if str_eq(file,tet[i]) return('t');
}
}
/* compressed binary types */
if (*dontcompress[0])
cpp=dontcompress; /* list from sendfile.cf */
else
cpp=cet;
for (i=0;*cpp[i];i++) {
/*printf(">%s<\n",cpp[i]);*/
if ((cp=strrchr(file,'.')) && str_eq(cp,cpp[i])) {
strcpy(type,"already compressed");
return('b');
}
}
/* next, try with file command */
/* read output from file command */
snprintf(MAXS(cmd),"file '%s'",file);
if ((pipe=popen(cmd,"r")) && fgetl(tmp,pipe)) {
pclose(pipe);
/* get type string */
if (str_beq(tmp,file))
strcpy(type,tmp+strlen(file));
else
strcpy(type,tmp);
if ((cp=strchr(type,'\n'))) *cp=0;
/* follow symbolic link */
if (strstr(type,"symbolic link")) {
strcpy(link,strrchr(type,' ')+1);
return guess_ftype(link,type);
}
if (verbose) {
snprintf(MAXS(tmp),"%s is of type %s",file,type);
message(prg,'I',tmp);
}
/* look for characteristic substrings */
str_tolower(type);
for (i=0;*bft[i];i++) if (strstr(type,bft[i])) return('b');
for (i=0;*sft[i];i++) if (strstr(type,sft[i])) return('s');
for (i=0;*tft[i];i++) if (strstr(type,tft[i])) return('t');
}
/* default type is binary */
return('b');
}
/*
* linkspeed - check if link to host is fast enough
*
* INPUT: host - remote host to send to
* lanspeed - min. speed for LAN in kB/s
* compress - the compression method
*
* OUTPUT: compress - empty compression method if fast link found
*
* RETURN: 1 for slow, 0 for faster than lanspeed
*/
int linkspeed(const char *host, int lanspeed, char **compress) {
int speed; /* found speed */
char
msg[MAXLEN], /* message output string */
speeddir[MAXLEN], /* directory with speed info files */
hostfile[MAXLEN]; /* file with speed for remote host */
FILE *inf; /* input file */
struct stat finfo; /* information about a file */
speed=0;
/* local host IS fast! :-) */
if (str_eq(host,localhost) || str_eq(host,"localhost")) {
if (verbose) message(prg,'I',"disabling compressing for localhost");
*compress="";
return(0);
}
/* on too low lanspeed value we suppose a slow link */
if (lanspeed<1) return(1);
/* create speeds dir if necessary */
snprintf(MAXS(speeddir),"%s/speeds",userspool);
if (stat(speeddir,&finfo)<0 || !S_ISDIR(finfo.st_mode)) {
unlink(speeddir);
if (mkdir(speeddir,S_IRUSR|S_IWUSR|S_IXUSR)<0) return(1);
chmod(speeddir,S_IRUSR|S_IWUSR|S_IXUSR);
}
snprintf(MAXS(hostfile),"%s/%s",speeddir,host);
/* if host file is missing return slow link */
if (!(inf=rfopen(hostfile,"r"))) return(1);
fscanf(inf,"%d",&speed);
fclose(inf);
if (speed<lanspeed) return(1);
if (verbose) {
snprintf(MAXS(msg),
"disabling compressing because last link speed to %s was %d kB/s",
host,speed);
message(prg,'I',msg);
snprintf(MAXS(msg),"LAN speed is defined as min %d kB/s",lanspeed);
message(prg,'I',msg);
}
if (compress) *compress="";
return(0);
}
/*
* notespeed - note the link speed in a log file for later connections
*
* INPUT: host - actual connected host
* size - file size in byte
* ttime - transfer time in ms
*/
void notespeed(const char *host, unsigned long size, float ttime) {
char
speeddir[MAXLEN], /* directory with speed info files */
hostfile[MAXLEN]; /* file with speed for remote host */
FILE *outf; /* output file */
struct stat finfo; /* information about a file */
/* don't log local host */
if (str_eq(host,localhost) || str_eq(host,"localhost")) return;
/* don't log too small files */
if (ttime<1 || size <10240) return;
/* create speeds dir if necessary */
snprintf(MAXS(speeddir),"%s/speeds",userspool);
if (stat(speeddir,&finfo)<0 || !S_ISDIR(finfo.st_mode)) {
unlink(speeddir);
if (mkdir(speeddir,S_IRUSR|S_IWUSR|S_IXUSR)<0) return;
chmod(speeddir,S_IRUSR|S_IWUSR|S_IXUSR);
}
snprintf(MAXS(hostfile),"%s/%s",speeddir,host);
if ((outf=rfopen(hostfile,"w"))) {
fprintf(outf,"%d\n",(int)(size/ttime/1.024)); /* kB/s */
fclose(outf);
}
}
/*
* list_spool - list files in outgoing spool
*
* RETURN: 0 if found, 1 if no files
*/
int list_spool() {
char
file[FLEN]; /* name of file to send */
struct hostlist
*hls, /* host list start */
*hlp; /* host list pointer */
struct outfilelist
*oflp; /* outgoing file list pointer */
hls=scanoutspool(pw_name);
if (!hls) {
if (quiet<2) message(prg,'W',"no files found in outgoing spool");
return(1);
}
printf("Files in outgoing spool:\n");
/* browse thru outgoing spool files lists */
for (hlp=hls; hlp; hlp=hlp->next) {
printf("\n");
for (oflp=hlp->flist; oflp; oflp=oflp->next) {
utf2iso(0,NULL,file,NULL,oflp->fname);
printf("%s@%s : %s (%ld KB)\n",
oflp->to,hlp->host,file,(oflp->size+512)/1024);
}
}
/*
printf("\n(To delete these files use: %s -Sd <filename> <recipient>)\n",prg);
*/
return(0);
}
/*
* spooled_info - print information about spooled file
*
* INPUT: file - original file name
* sdf - spool data file name
* compressed - compressed flag
*/
void spooled_info(const char *file, const char *sdf, int compressed) {
int size;
char tmp[MAXLEN]; /* temporary string */
struct stat finfo; /* information about a file */
if (quiet>1) return;
if (stat(sdf,&finfo)<0) {
snprintf(MAXS(tmp),"cannot access spool file %s",sdf);
message(prg,'E',tmp);
return;
}
size=(finfo.st_size+512)/1024;
if (compressed)
snprintf(MAXS(tmp),"'%s' spooled (%d KB [compressed])",file,size);
else
snprintf(MAXS(tmp),"'%s' spooled (%d KB)",file,size);
message(prg,'I',tmp);
}
/*
* usage - print short help usage text
*/
int usage() {
/* HAVE_GETOPTLONG_H is dead code for sendfile! */
#if defined(HAVE_GETOPTLONG_H)
fprintf(stderr,"usage: %s [OPTIONS] file [...] user[@host]\n",prg);
fprintf(stderr," or: %s [OPTIONS] -a archivename file-or-directory [...] user[@host]\n",prg);
fprintf(stderr,"options:\n");
fprintf(stderr," -a, --archive sends file-or-director(s) as\n archivename to user[@host]\n");
fprintf(stderr," -s, --source send file(s) in source mode\n");
fprintf(stderr," -t, --text send file(s) in text mode\n");
fprintf(stderr," -d, --delete delete previous sent file(s)\n");
fprintf(stderr," -c, --comment comment a single file\n");
fprintf(stderr," -o, --overwrite overwrite already sent file(s) with same name\n");
fprintf(stderr," -b, --bounce resend file(s) to user[@host]\n");
fprintf(stderr," -u, --uncompressed send file(s) uncompressed\n");
fprintf(stderr," -v, --verbose verbose mode\n");
fprintf(stderr," -q, --quiet quiet mode 1: no transfer messages or questions\n");
fprintf(stderr," -Q, --real-quiet quiet mode 2: no transfer\n information and warning messages\n");
fprintf(stderr," -P, --stdin read data stream from stdin\n normaly this is a pipe\n");
/* fprintf(stderr," -X, --extended this uses the extended\n header feature, this implies -P\n"); */
fprintf(stderr," -V, --version show version\n");
fprintf(stderr," -h, --help print this help\n");
fprintf(stderr," -ps, --pgp-sign pgp-sign the file\n");
fprintf(stderr," -pe=ID, --pgp-encrypt pgp-encrypt the file\n\n");
fprintf(stderr,"Default mode: send file(s) in binary mode and compressed.\n");
fprintf(stderr,"For a full description of all options, see 'man %s'.\n\n",prg);
fprintf(stderr,"Example: %s rabbit.gif beate@juhu.lake.de\n\n",prg);
fprintf(stderr,"Report bug to: Ulli Horlacher <framstag@belwue.de>\n");
#endif
fprintf(stderr,"\n");
fprintf(stderr,"usage: %s [OPTIONS] file [...] user[@host]\n",prg);
fprintf(stderr," or: %s [OPTIONS] -a=archive-name file-or-directory [...] user[@host]\n",prg);
fprintf(stderr,"options: -s send file(s) in source mode\n");
fprintf(stderr," -t send file(s) in text mode\n");
fprintf(stderr," -g send file(s) in guessed mode (does not work in every case!)\n");
fprintf(stderr," -d delete previously sent file(s)\n");
fprintf(stderr," -o overwrite file(s) with the same name\n");
fprintf(stderr," -u send file(s) uncompressed\n");
fprintf(stderr," -v verbose mode\n");
fprintf(stderr," -q quiet mode 1: no transfer messages or questions\n");
fprintf(stderr," -Q quiet mode 2: no transfer, information or warning messages\n");
fprintf(stderr," -P read file from stdin (this is usually a pipe)\n");
fprintf(stderr," -S spool file(s) for later processing\n");
fprintf(stderr," -l list file(s) in outgoing spool\n");
fprintf(stderr," -V show version\n");
fprintf(stderr," -m LIMIT maximum thruput at LIMIT KB/s\n");
fprintf(stderr," -a=name send all files in one archive\n");
fprintf(stderr," -c='comment' add a one line text comment to a single file\n");
fprintf(stderr," -ps pgp-sign\n");
fprintf(stderr," -pe[=ID] pgp-encrypt [for ID]\n");
fprintf(stderr,"example: %s rabbit.gif beate@juhu.lake.de\n",prg);
fprintf(stderr,"see also: sfconf\n");
/*
fprintf(stderr,"Default mode: send file(s) in binary mode and compressed.\n");
fprintf(stderr,"For a full description of all options, see 'man %s'.\n",prg);
*/
return(2);
}
syntax highlighted by Code2HTML, v. 0.9.1