/*
* File: fetchfile.c
*
* Author: Ulli Horlacher (framstag@rus.uni-stuttgart.de)
*
* Contrib.: Michael Neumayer (eumel@42.org)
*
* History:
*
* 1997-09-28 Framstag initial version
* 1997-10-03 Framstag added -r autoreceive option
* 1997-10-05 Framstag better pgp error handling
* 1997-10-24 Framstag added check for pgp key files
* 1997-12-11 Eumel fixed pgp my_ID problem
* 1997-12-19 Framstag default key length is now 1024 bit
* 1998-01-03 Framstag config options id and server merged to o-saft
* 1998-03-11 Framstag new default user configuration directory
* 2001-02-04 Framstag added secure mktmpdir()
*
* The fetchfile client of the sendfile package.
* Lists or gets files from a SAFT server to put them into the local spool.
*
* Copyright © 1997,1998 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 <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) read/write */
#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 */
#include "getline.h" /* get a line of text from stdin */
#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);
int symlink(const char *, const char *);
#endif
#ifndef AIX
#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();
/* get file list from server */
int get_list(int, const char *, const char *, FILE *);
/* get file from server */
int get_file(int, int, int);
/* delete a file from the server */
int delete_file(int, int);
/* clean termination routine */
void cleanexit();
/* delete tmp-file */
void cleanup();
/* delete tmp-file */
void init();
/* test local sendfiled */
int sendfiled_test(const char *);
/* exit wit receive evaluation */
void rexit(int);
/* global variables */
int verbose=0, /* flag for verbose mode */
client=1, /* flag to determine client or server */
quiet=0, /* quiet mode */
ignore=0, /* ignore testing flag */
packet_size=0; /* packet size */
char *prg, /* name of the game */
*tmpdir, /* directory for temporary files */
swd[MAXLEN], /* start working directory */
rfilen[MAXLEN], /* receive files for -R option */
userspool[MAXLEN], /* user spool directory */
userconfig[MAXLEN],/* user configuration directory */
listfile[MAXLEN], /* file with list command output */
pgp_bin[MAXLEN], /* the pgp binary */
pgptmp[MAXLEN]; /* name of pgp temporary file */
struct passwd *pwe; /* password entry */
struct senderlist *sls; /* sender list start */
int main(int argc, char *argv[]) {
int
i, /* simple loop counter */
n, /* number of files on the server */
pid, /* current proccess id */
sockfd, /* socket file descriptor */
opt, /* option to test for */
number, /* file number */
wconf, /* flag for writing config file (0=reading) */
del, /* flag for deleting previous sent files */
all, /* flag for specifying all files */
numbers, /* flag for specifying file numbers */
ptso, /* flag for "pipe to standard output" */
keep, /* flag for keeping files on server */
list; /* flag for listing files in outgoing spool */
char
*cp, /* simple string pointer */
*argp, /* argument string pointer */
*user, /* sending user pattern */
id[FLEN], /* user identication for SAFT server */
server[FLEN], /* SAFT server */
alternate[MAXLEN], /* alternate user and/or SAFT server */
cmd[MAXLEN], /* cmd string for system-call */
line[MAXLEN], /* one line of text */
from[MAXLEN], /* sender address */
fname[MAXLEN], /* file name */
conffile[MAXLEN], /* config file name */
response[MAXLEN], /* signed response to authorization challenge */
tmp[3*MAXLEN]; /* temporary string */
FILE
*pp, /* pipe */
*listf, /* list command output file */
*inf, /* input file */
*outf; /* output file */
struct stat finfo; /* information about a file */
n=0;
del=0;
all=0;
ptso=0;
list=0;
keep=0;
sockfd=0;
verbose=0;
numbers=0;
*id=0;
*server=0;
*rfilen=0;
*conffile=0;
*response=0;
*alternate=0;
user="*";
listf=NULL;
pid=(int)getpid();
getcwd(MAXS(swd));
prg=argv[0];
if ((cp=strrchr(prg,'/'))) prg=cp+1;
/* protect all tmp-files */
umask(~(S_IRUSR|S_IWUSR));
/* support programs defaults */
strcpy(pgp_bin,PGP);
/* look for environment variables */
if ((cp=getenv("SF_PGP"))) strcpy(pgp_bin,cp);
/* do the support programs really exist? */
if (access(pgp_bin,X_OK)<0) strcpy(pgp_bin,"pgp");
/* get the own user name and tmpdir */
if ((pwe=getpwuid(getuid())) == NULL)
message(prg,'F',"cannot access user system informations");
tmpdir=mktmpdir(verbose);
/* set various file names and check user spool and configuration directory */
snprintf(MAXS(pgptmp),"%s/fetchfile.pgp",tmpdir);
snprintf(MAXS(userspool),SPOOL"/%s",pwe->pw_name);
if (stat(userspool,&finfo)<0) sendfiled_test(pwe->pw_name);
snprintf(MAXS(userconfig),"%s/.sendfile",pwe->pw_dir);
snprintf(MAXS(tmp),"%s/config",userspool);
if (stat(userconfig,&finfo)<0 && stat(userspool,&finfo)==0)
symlink(tmp,userconfig);
snprintf(MAXS(tmp),"%s/.sfspool",pwe->pw_dir);
if (stat(tmp,&finfo)==0 && finfo.st_mode&S_IFDIR) strcpy(userspool,tmp);
/* scan the command line on options */
while ((opt=getopt(argc, argv, "vVqQ?hrPIliadkns:f:C:")) > 0) {
switch (opt) {
case ':':
case 'h':
case '?': exit(usage());
case 'r': strcpy(rfilen,"receive -n "); break;
case 's': strcpy(alternate,optarg); break;
case 'C': strcpy(conffile,optarg); break;
case 'f': user=optarg; break;
case 'd': del=1; break;
case 'k': keep=1; break;
case 'P': ptso=1; break;
case 'i': ignore=1; break;
case 'n': numbers=1; break;
case 'a': all=1; break;
case 'l': list=1; break;
case 'q': quiet=1; break;
case 'Q': quiet=2; break;
case 'v': verbose=1; break;
case 'V': message(prg,'I',"version "VERSION" revision "REVISION); exit(0);
case 'I': init(); exit(0);
}
}
/* too few arguments? */
if (argc-optind<1 && !all && !list && !*conffile) exit(usage());
/* incompatible options? */
if (*conffile && (del || keep || all || list || numbers)) {
errno=0;
message(prg,'F',"you cannot mix -C with any other option");
}
if (del && keep) {
errno=0;
message(prg,'F',"you cannot delete and keep a file at the same time");
}
if (ptso && *rfilen) {
errno=0;
message(prg,'F',"you cannot auto-receive and pipe to stdout (-R and -n options)");
}
if (list && (del || keep || all || *rfilen || ptso)) {
errno=0;
message(prg,'F',"you cannot not specify any other option with -l");
}
if (all && argc>optind) {
errno=0;
message(prg,'F',"you cannot specify file names or numbers "
"together with -a option");
}
/* alternate user or server specified? */
if (*alternate) {
if (str_beq_nocase(alternate,"saft://")) saft2rfc822(alternate);
if ((cp=strchr(alternate,'@'))) {
*cp=0;
strcpy(id,alternate);
strcpy(server,cp+1);
} else {
strcpy(server,alternate);
}
}
/* check tmp files */
unlink(pgptmp);
if (stat(pgptmp,&finfo)==0) {
snprintf(MAXS(tmp),
"tmp-file %s does already exist and cannot be deleted",pgptmp);
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;
}
}
}
fclose(inf);
}
/* check pgp key files */
snprintf(MAXS(tmp),"%s/private.pgp",userconfig);
if (stat(tmp,&finfo)<0) {
snprintf(MAXS(line),"no access to %s (try 'fetchfile -I' first)",tmp);
message(prg,'F',line);
}
snprintf(MAXS(tmp),"%s/public.pgp",userconfig);
if (stat(tmp,&finfo)<0) {
snprintf(MAXS(line),"no access to %s (try 'fetchfile -I' first)",tmp);
message(prg,'F',line);
}
/* parse the user config-file */
snprintf(MAXS(tmp),"%s/config",userconfig);
if ((inf=rfopen(tmp,"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,"id") && !*id) { /* depreciated */
strcpy(id,argp);
continue;
}
if (str_eq(line,"server") && !*server) { /* depreciated */
strcpy(server,argp);
continue;
}
if (str_eq(line,"o-saft") && !*server) {
if (str_beq_nocase(argp,"saft://")) saft2rfc822(argp);
if ((cp=strchr(argp,'@'))) {
*cp=0;
strcpy(id,argp);
strcpy(server,cp+1);
} else {
strcpy(id,pwe->pw_name);
strcpy(server,argp);
}
}
}
}
fclose(inf);
}
if (!*id) strcpy(id,pwe->pw_name);
if (!*server) strcpy(server,"localhost");
snprintf(MAXS(listfile),"%s/%s@%s:fetch.lis",userspool,id,server);
/* initiate the connection to the server */
if (!*server) {
errno=0;
message(prg,'F',"no SAFT server is defined");
}
snprintf(MAXS(tmp),"connecting to SAFT server %s",server);
if (quiet<2) message(prg,'I',tmp);
sockfd=open_connection(server,SAFT);
if (sockfd==-1) snprintf(MAXS(tmp),"cannot create a network socket");
if (sockfd==-2) snprintf(MAXS(tmp),"cannot open connection to %s",server);
if (sockfd==-3) snprintf(MAXS(tmp),"%s is unknown",server);
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",SAFT,server);
message(prg,'F',tmp);
}
/* send ID */
snprintf(MAXS(tmp),"ID %s",id);
sock_putline(sockfd,tmp);
sock_getline(sockfd,line);
if (str_beq(line,"520")) {
errno=0;
snprintf(MAXS(tmp),"user %s is unknown on SAFT-server %s",id,server);
message(prg,'F',tmp);
}
if (!str_beq(line,"331")) {
errno=0;
snprintf(MAXS(tmp),"server error: %s",line+4);
message(prg,'F',tmp);
}
str_trim(line);
cp=strrchr(line,' ');
if (!cp) {
errno=0;
message(prg,'F',"bad answer from server");
}
outf=rfopen(pgptmp,"w");
if (!outf) {
errno=0;
snprintf(MAXS(tmp),"cannot open/write to %s",pgptmp);
message(prg,'F',tmp);
}
fprintf(outf,"%s",cp+1);
fclose(outf);
/* goto user spool directory */
if (chdir(userspool)<0) {
snprintf(MAXS(tmp),"cannot change to %s",userspool);
message(prg,'F',tmp);
}
/* call pgp */
/* DONT REMOVE 2>/dev/null IN THE FOLLOWING LINE! */
snprintf(MAXS(cmd),"cd %s; PGPPATH='.' %s -sbaf "
"+secring=private.pgp +pubring=public.pgp <%s 2>/dev/null",
userconfig,pgp_bin,pgptmp);
if (verbose) printf("call: %s\n",cmd);
if (!(pp=popen(cmd,"r"))) message(prg,'F',"call to pgp failed");
while (fgetl(line,pp) &&
strlen(response)+strlen(line)+3 < sizeof(response)) {
if ((cp=strchr(line,'\n'))) *cp=0;
strcat(response,line);
strcat(response,"\r\n");
}
pclose(pp);
/* wrong pgp output? */
if (!strstr(response,"BEGIN PGP MESSAGE")) {
errno=0;
message(prg,'E',"corrupt pgp reply:");
cp=strrchr(cmd,'2');
*cp=0;
system(cmd);
message(prg,'F',"corrupt pgp reply");
}
iso2utf(tmp,response);
snprintf(MAXS(response),"AUTH %s",tmp);
sendheader(sockfd,response);
/* config file transfer? */
if (*conffile) {
if (*conffile=='w') wconf=1; else wconf=0;
/* extract real file name */
if (!(cp=strchr(conffile+1,'='))) cp=conffile;
strcpy(tmp,cp+1);
if (*tmp == '/')
strcpy(conffile,tmp);
else
snprintf(MAXS(conffile),"%s/%s",swd,tmp);
/* write config file */
if (wconf) {
if ((cp=strrchr(conffile,'/')))
cp++;
else
cp=conffile;
snprintf(MAXS(tmp),"CONF WRITE %s",cp);
sock_putline(sockfd,tmp);
sock_getline(sockfd,line);
if (!str_beq(line,"302 ") && !str_beq(line,"200 ")) {
errno=0;
snprintf(MAXS(tmp),"server error: %s",line+4);
message(prg,'F',tmp);
}
inf=rfopen(conffile,"r");
if (!inf) {
snprintf(MAXS(tmp),"cannot open %s",conffile);
message(prg,'F',tmp);
}
if (quiet<2) {
snprintf(MAXS(tmp),"transfering %s",conffile);
message(prg,'I',tmp);
}
/* send the file */
while (fgetl(line,inf)) {
if ((cp=strchr(line,'\n'))) *cp=0;
sock_putline(sockfd,line);
}
sock_putline(sockfd,"\004"); /* End Of Transmission */
fclose(inf);
sock_getline(sockfd,line);
if (!str_beq(line,"201 ")) {
errno=0;
snprintf(MAXS(tmp),"server error: %s",line+4);
message(prg,'F',tmp);
}
} else { /* read config file */
if ((cp=strrchr(conffile,'/')))
cp++;
else
cp=conffile;
snprintf(MAXS(tmp),"CONF READ %s",cp);
sock_putline(sockfd,tmp);
while (sock_getline(sockfd,line)) {
if (!str_beq(line,"250")) {
errno=0;
snprintf(MAXS(tmp),"server error: %s",line+4);
message(prg,'F',tmp);
}
if (str_beq("250 ",line)) break;
utf2iso(0,NULL,tmp,NULL,line+4);
printf("%s\n",tmp);
}
}
sock_putline(sockfd,"QUIT");
getreply(sockfd);
close(sockfd);
cleanup();
exit(0);
}
/* enable simple interrupt handler */
signal(SIGTERM,cleanexit);
signal(SIGABRT,cleanexit);
signal(SIGQUIT,cleanexit);
signal(SIGHUP,cleanexit);
signal(SIGINT,cleanexit);
/* list files in outgoing spool */
if (list) {
n=get_list(sockfd,server,id,listf);
sock_putline(sockfd,"QUIT");
getreply(sockfd);
close(sockfd);
cleanup();
exit(n);
}
/* set tcp packet length */
if (packet_size<1) packet_size=PACKET;
/* scan local spool */
sls=scanspool("");
if (numbers) {
for (i=optind; i<argc; i++) {
number=atoi(argv[i]);
if (del) {
if (delete_file(sockfd,number)<0) {
snprintf(MAXS(tmp),"cannot delete file #%d from server",number);
errno=0;
message(prg,'E',prg);
} else {
snprintf(MAXS(tmp),"file #%d deleted from server",number);
if (quiet<2) message(prg,'I',tmp);
n++;
}
} else {
if (get_file(sockfd,number,ptso)<0) break;
if (!keep && delete_file(sockfd,number)<0)
message(prg,'W',"cannot delete this file from server");
n++;
}
}
rexit(n);
}
/* get actual list of files from server */
listf=rfopen(listfile,"w");
if (listf) {
get_list(sockfd,server,id,listf);
fclose(listf);
} else {
snprintf(MAXS(tmp),"cannot open %s for writing",listfile);
message(prg,'F',tmp);
}
/* fetch (or delete) all files */
if (all) {
listf=rfopen(listfile,"r");
if (!listf) {
snprintf(MAXS(tmp),"cannot open %s for reading",listfile);
message(prg,'F',tmp);
}
while (fgetl(line,listf)) {
if ((cp=strchr(line,' '))) {
*cp=0;
strcpy(tmp,cp+1);
if ((cp=strchr(tmp,' '))) *cp=0;
utf2iso(1,NULL,fname,NULL,tmp);
strcpy(tmp,cp+1);
if ((cp=strchr(tmp,' '))) *cp=0;
utf2iso(1,NULL,from,NULL,tmp);
if ((cp=strchr(from,' '))) *cp=0;
}
if (simplematch(from,user,0)==1) {
number=atoi(line);
if (del) {
if (delete_file(sockfd,number)<0) {
snprintf(MAXS(tmp),"cannot delete file #%d (%s) from server",number,fname);
errno=0;
message(prg,'E',prg);
} else {
snprintf(MAXS(tmp),"file #%d (%s) deleted from server",number,fname);
if (quiet<2) message(prg,'I',tmp);
n++;
}
} else {
if (get_file(sockfd,number,ptso)<0) break;
if (!keep && delete_file(sockfd,number)<0)
message(prg,'W',"cannot delete this file from server");
n++;
}
}
}
fclose(listf);
rexit(n);
}
/* fetch specified files */
for (i=optind;i<argc;i++) {
listf=rfopen(listfile,"r");
if (!listf) {
snprintf(MAXS(tmp),"cannot open %s for reading",listfile);
message(prg,'F',tmp);
}
while (fgetl(line,listf)) {
number=atoi(line);
if (!(cp=strchr(line,' '))) continue;
strcpy(tmp,cp+1);
if ((cp=strchr(tmp,' '))) *cp=0;
utf2iso(1,NULL,fname,NULL,tmp);
strcpy(tmp,cp+1);
if ((cp=strchr(tmp,' '))) *cp=0;
utf2iso(1,NULL,from,NULL,tmp);
if ((cp=strchr(from,' '))) *cp=0;
if (simplematch(fname,argv[i],0)==1 && simplematch(from,user,0)==1) {
number=atoi(line);
if (del) {
if (delete_file(sockfd,number)<0) {
snprintf(MAXS(tmp),"cannot delete file #%d (%s) from server",number,fname);
errno=0;
message(prg,'E',prg);
} else {
snprintf(MAXS(tmp),"file #%d (%s) deleted from server",number,fname);
if (quiet<2) message(prg,'I',tmp);
n++;
}
} else {
if (get_file(sockfd,number,ptso)<0) break;
if (!keep && delete_file(sockfd,number)<0)
message(prg,'W',"cannot delete this file from server");
n++;
}
}
}
fclose(listf);
}
rexit(n);
exit(0);
}
/*
* cleanexit - clean termination routine
*
* A very simple interrupt handler
*/
void cleanexit() {
/* ignore all relevant signals */
signal(SIGTERM,SIG_IGN);
signal(SIGABRT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
signal(SIGHUP,SIG_IGN);
signal(SIGINT,SIG_IGN);
printf("\r\n");
cleanup();
exit(0);
}
/*
* cleanup - delete tmp files
*/
void cleanup() {
#ifndef DEBUG
rmtmpdir(tmpdir);
#endif
}
/*
* rexit - exit with receive evaluation
*/
void rexit(int n) {
char tmp[MAXLEN]; /* temporary string */
cleanup();
/* are there any files to be received automaticly? */
if (strcmp(rfilen,"receive -n ")>0) {
/* change back to starting directory */
if (chdir(swd)<0) {
snprintf(MAXS(tmp),"cannot change back to %s",swd);
message(prg,'E',tmp);
} else
if (verbose) printf("shell-call: %s\n",rfilen);
/* call receive */
system(rfilen);
}
exit(n);
}
/*
* delete_file - delete a file from the server
*
* INPUT: sockfd - socket file descriptor
* number - spool file number
*
* RETURN: 0 on success, -1 on error
*/
int delete_file(int sockfd, int number) {
char line[MAXLEN]; /* one line of text */
/* send LIST command */
snprintf(MAXS(line),"DEL %d",number);
sock_putline(sockfd,line);
sock_getline(sockfd,line);
if (str_beq(line,"200 "))
return(0);
else
return(-1);
}
/*
* get_list - get list of files from server
*
* INPUT: sockfd - socket file descriptor
* server - server name
* id - user name
* listf - list command output file
*
* RETURN: number of files, -1 on error
*/
int get_list(int sockfd, const char *server, const char *id, FILE *listf) {
int
i, /* simple loop counter */
n, /* number of files */
number; /* file number */
unsigned long
size; /* file size */
char
*cp, /* simple string pointer */
tmp[MAXLEN], /* temporary string */
line[MAXLEN], /* one line of text */
from[MAXLEN]; /* address and name of sender */
char
*flist[6]; /* file list elements */
n=0;
*from=0;
/* send LIST command */
sock_putline(sockfd,"LIST");
for (;;) {
/* get one line of answer from server */
sock_getline(sockfd,line);
str_trim(line);
/* invalid answer? */
if (!str_beq(line,"250")) {
errno=0;
snprintf(MAXS(tmp),"invalid answer from server: %s",line+4);
message(prg,'E',tmp);
return(n);
}
/* last answer line? */
if (str_beq(line,"250 ")) {
if (!listf) printf("\n");
return(n);
}
/* write only to list file? */
if (listf) {
fprintf(listf,"%s\n",line+4);
continue;
}
/* print server ID on top */
if (!*from) {
sprintf(tmp,"Files on SAFT-server %s for user %s:",server,id);
printf("\n%s\n",tmp);
for (i=1; i<80 && i<=strlen(tmp); i++) printf("=");
printf("\n");
}
/* split file list */
cp=line+4;
str_trim(cp);
for(i=1;i<6;i++) {
flist[i]=cp;
if ((cp=strchr(cp,' '))) {
*cp=0;
cp++;
}
}
/* new from? */
if (!str_eq(from,flist[3])) {
strcpy(from,flist[3]);
utf2iso(0,NULL,tmp,NULL,from);
printf("\nFrom %s\n",tmp);
for (i=1; i<80 && i<=strlen(tmp)+5; i++) printf("-");
printf("\n");
}
/* build and print list line */
if ((cp=strrchr(flist[5],':')) > strchr(flist[5],':')) *cp=0;
number=atoi(flist[1]);
size=atol(flist[4]);
size=(size+1023)/1024;
sprintf(tmp,"%3d) %s %8lu kB %s",number,flist[5],size,flist[2]);
utf2iso(1,NULL,line,NULL,tmp);
printf("%s\n",line);
n++;
}
}
/*
* get_file - get a file from the server
*
* INPUT: sockfd - socket file descriptor
* number - file number
* ptso - flag for "pipe to standard output"
*
* RETURN: 0 if ok, -1 on error
*/
int get_file(int sockfd, int number, int ptso) {
int
percent, /* what percentage of file has been transmitted */
chunk, /* number of bytes in one read chunk */
shfd, /* spool header file descriptor */
sdfd, /* spool data file descriptor */
id; /* local spool id */
unsigned long
msec, /* milliseconds */
size, /* total file size */
rsize, /* rest file size */
offset, /* bytes that have been already transfered */
bytes; /* bytes to receive */
char
*cp, /* simple string pointer */
date[DLEN], /* date string */
shfile[FLEN], /* spool header file */
sdfile[FLEN], /* spool data file */
packet[OVERSIZE], /* one line of text */
line[MAXLEN], /* one line of text */
from[MAXLEN], /* sender name (address) */
fname[MAXLEN], /* file name */
tmp[MAXLEN]; /* temporary string */
time_t sec1,sec2; /* unix time */
float
thruput; /* net throughput */
struct filelist *
flp; /* file list pointer */
struct senderlist
*slp; /* sender list pointer */
struct timeval tv1,tv2;
#if !defined(SOLARIS2) && !defined(IRIX)
struct timezone tz;
#endif
id=0;
shfd=0;
sdfd=0;
size=0;
offset=0;
*date=0;
flp=NULL;
/* get time normal */
#if defined(SOLARIS2) || defined(IRIX)
#ifdef _SVID_GETTOD
gettimeofday(&tv1);
#else
gettimeofday(&tv1,NULL);
#endif
#else
gettimeofday(&tv1,&tz);
#endif
sec1=0;
/* get next spool id */
if (!ptso) {
id=spoolid(999999);
if (!id) message(prg,'F',"cannot create local spool file");
/* open spool header and data files */
snprintf(MAXS(shfile),"%d.h",id);
snprintf(MAXS(sdfile),"%d.d",id);
sdfd=open(sdfile,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
shfd=open(shfile,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
if (shfd<0 || sdfd<0) message(prg,'F',"cannot create local spool file");
}
snprintf(MAXS(tmp),"GET HEADER %d",number);
sock_putline(sockfd,tmp);
for (;;) {
/* get one line of answer from server */
sock_getline(sockfd,line);
str_trim(line);
/* invalid answer? */
if (!str_beq(line,"250")) {
if (!ptso) {
close(sdfd);
close(shfd);
unlink(sdfile);
unlink(shfile);
}
errno=0;
snprintf(MAXS(tmp),"server-error: %s",line+4);
message(prg,'E',tmp);
return(-1);
}
/* last answer line? */
if (str_beq(line,"250 ")) break;
/* make SAFT commands uppercase */
if ((cp=strchr(line+4,' '))) {
*cp=0;
str_toupper(line+4);
*cp='\t';
}
/* store attributes */
if (str_beq(line+4,"FILE")) strcpy(fname,line+9);
if (str_beq(line+4,"SIZE")) size=atol(line+9);
if (str_beq(line+4,"DATE")) utf2iso(1,date,NULL,NULL,line+9);
if (str_beq(line+4,"FROM")) {
if ((cp=strchr(line+10,' '))) {
*cp=0;
snprintf(MAXS(tmp),"%s (%s)",line+9,cp+1);
*cp=' ';
} else
strcpy(tmp,line+9);
utf2iso(1,from,NULL,NULL,tmp);
}
strcat(line,"\n");
if (ptso)
printf("%s",line+4);
else
write(shfd,line+4,strlen(line+4));
}
close(shfd);
/* check if file has been already fetched and how much of it */
if (!ignore) {
/* loop over sender list */
for (slp=sls, offset=0; slp!=NULL; slp=slp->next) {
/* same sender? */
if (str_eq(slp->from,from)) {
/* loop over files list */
for (flp=slp->flist; flp!=NULL; flp=flp->next) {
/* same file? */
if (str_eq(flp->fname,fname) && flp->csize==size &&
(*date==0 || str_eq(flp->date,date))) {
offset=flp->tsize;
break;
}
}
}
/* leave loop if found */
if (offset) break;
}
}
utf2iso(1,NULL,tmp,NULL,fname);
strcpy(fname,tmp);
if (quiet<2) {
if (flp && flp->csize==offset) {
snprintf(MAXS(tmp),"file %d (%s) has been already fetched",number,fname);
message(prg,'I',tmp);
} else {
if (quiet==1) {
if (offset)
snprintf(MAXS(tmp),"resuming fetching file %d (%s) with %ld kB",
number,fname,(size+1023)/1024);
else
snprintf(MAXS(tmp),"fetching file %d (%s) with %ld kB",
number,fname,(size+1023)/1024);
message(prg,'I',tmp);
}
}
}
if (flp && flp->csize==offset) {
close(sdfd);
unlink(sdfile);
unlink(shfile);
return(0);
}
snprintf(MAXS(tmp),"GET FILE %d %ld",number,offset);
sock_putline(sockfd,tmp);
sock_getline(sockfd,line);
/* invalid answer? */
if (!str_beq(line,"231 ")) {
if (!ptso) {
close(sdfd);
unlink(sdfile);
unlink(shfile);
}
errno=0;
snprintf(MAXS(tmp),"server-error: %s",line+4);
message(prg,'E',tmp);
return(-1);
}
if (ptso) printf("DATA\n");
fflush(stdout);
rsize=atol(line+4);
/* resend active? */
if (offset && quiet<2) {
snprintf(MAXS(tmp),"resuming at byte %ld",offset);
message("",'I',tmp);
}
bytes=0;
while (bytes<rsize) {
chunk=read(sockfd,packet,packet_size);
if (chunk<=0) {
if (!ptso) close(sdfd);
errno=0;
message(prg,'E',"received too few data from server");
return(-1);
}
bytes+=chunk;
if (ptso) {
write(fileno(stdout),packet,chunk);
} else {
percent=(bytes+offset)*100.0/(size);
if (!quiet) {
sec2=time(0);
if (sec2>sec1) {
fprintf(stderr,"%s: %3d%% (%ld of %ld kB)\r",
fname,percent,(bytes+offset-1)/1024+1,(size-1)/1024+1);
fflush(stderr);
sec1=sec2;
}
}
write(sdfd,packet,chunk);
}
}
if (!ptso) close(sdfd);
/* note spool number for auto-receive */
if (*rfilen) {
sprintf(tmp," %d",id);
strcat(rfilen,tmp);
}
if (quiet<2) {
/* get time difference */
#if defined(SOLARIS2) || defined(IRIX)
#ifdef _SVID_GETTOD
gettimeofday(&tv2);
#else
gettimeofday(&tv2,NULL);
#endif
#else
gettimeofday(&tv2,&tz);
#endif
msec=(tv2.tv_usec-tv1.tv_usec)/1000+(tv2.tv_sec-tv1.tv_sec)*1000;
if (msec<1) msec=1;
thruput=bytes*1000.0/msec;
/* print transfer statistics */
if (quiet==1) {
if (thruput>9999)
snprintf(MAXS(tmp),
"transfer of %s completed: %.1f kB/s",fname,thruput/1024);
else
snprintf(MAXS(tmp),
"transfer of %s completed: %d byte/s",fname,(int)thruput);
message("",'I',tmp);
} else {
if (bytes>9999)
fprintf(stderr,"%s: 100%% (%ld kB, ",fname,bytes/1024);
else
fprintf(stderr,"%s: 100%% (%ld byte, ",fname,bytes);
if (thruput>9999)
fprintf(stderr,"%.1f kB/s) \n",thruput/1024);
else
fprintf(stderr,"%d byte/s) \n",(int)thruput);
fflush(stderr);
}
}
return(0);
}
/*
* reply - dummy function, only needed for linking
*/
void reply(int x) { }
/*
* init - initialize spool and pgp for fetchfile
*/
void init() {
char
answer[FLEN], /* answer string */
server[FLEN], /* SAFT server */
configf[MAXLEN], /* config file */
tmp[MAXLEN]; /* temporary string */
struct stat finfo; /* information about a file */
FILE *outf; /* file to create */
*server=0;
sprintf(configf,"%s/config",userconfig);
umask(~S_IRWXU);
printf("\nThis is the init routine for %s.\n",prg);
printf("It will create the necessary pgp files and the spool directory.\n");
printf("You can press Ctrl-C at any time to stop this procedure.\n\n");
snprintf(MAXS(userspool),SPOOL"/%s",pwe->pw_name);
if (stat(userspool,&finfo)<0 || !(finfo.st_mode&S_IFDIR)) {
printf("User spool %s does not exist.\n",userspool);
snprintf(MAXS(userspool),"%s/.sfspool",pwe->pw_dir);
printf("May I create local spool %s? ",userspool);
fgetl(answer,stdin);
if (*answer!='y' && *answer!='Y') {
errno=0;
message(prg,'F',"cannot continue with answer 'no'.");
}
unlink(userspool);
if (mkdir(userspool,S_IRWXU)<0) {
sprintf(tmp,"cannot create directory %s",userspool);
message(prg,'F',tmp);
}
}
if (stat(userconfig,&finfo)<0) {
if (mkdir(userconfig,S_IRWXU)<0) {
sprintf(tmp,"cannot create config directory %s",userconfig);
message(prg,'F',tmp);
}
}
if (chdir(userconfig)<0) {
sprintf(tmp,"cannot change to directory %s",userconfig);
message(prg,'F',tmp);
}
if (!(outf=rfopen(configf,"a"))) {
snprintf(MAXS(tmp),"cannot open %s",configf);
message(prg,'F',tmp);
}
printf("What is the address of your SAFT server where you want to "
"poll your files?\n");
while (getpromptline(MAXS(server))==NULL || *server=='\n' || *server==0)
printf("Illegal address! Please repeat: ");
fprintf(outf,"\n# The address of your default SAFT-server for fetchfile\n");
fprintf(outf,"server = %s\n",server);
fclose(outf);
if (stat("public.pgp",&finfo)==0 || stat("private.pgp",&finfo)==0) {
printf("There are already pgp files in your config directory %s\n",userconfig);
printf("Overwrite them? ");
fgetl(answer,stdin);
if (*answer!='y' && *answer!='Y') {
errno=0;
message(prg,'F',"cannot continue with answer 'no'.");
}
}
printf("\npgp will now be started to create a new sendfile-only key pair.\n");
printf("When asked, you can enter your normal e-mail address.\n\n");
printf("YOU MUST ENTER AN EMPTY PASS PHRASE, ");
printf("OR FETCHFILE WILL NOT WORK!\n\n");
if (system("PGPPATH='.' "
" pgp -kg +pubring=public.pgp +secring=private.pgp 1024"))
message(prg,'F',"pgp call failed");
printf("\nYou should now send %s/public.pgp to\n",userconfig);
printf("root@%s, because this file has to be stored in your\n",server);
printf("sendfile spool on %s\n",server);
printf("For example you can type:\n");
printf("sendfile -c 'this is my SAFT/fetchfile public key' \\\n");
printf(" %s/public.pgp root@%s\n\n",userconfig,server);
printf("If you want to change your default SAFT server, then edit\n%s\n\n",
configf);
exit(0);
}
/*
* sendfiled_test - test local sendfiled
*
* INPUT: user - user name
*
* RETURN: 0 if ok, -1 if failed
*/
int sendfiled_test(const char *user) {
int sockfd; /* socket file descriptor */
char line[MAXLEN]; /* one line of text */
/* test the local sendfiled */
if (verbose) printf("testing local SAFT server:\n");
sockfd=open_connection("127.0.0.1",SAFT);
sock_getline(sockfd,line);
/* no local server? */
if (!str_beq(line,"220 ") || !strstr(line,"SAFT")) return(-1);
/* test if you can receive messages */
snprintf(MAXS(line),"FROM %s",user);
sock_putline(sockfd,line);
sock_getline(sockfd,line);
if (!str_beq(line,"200 ")) return(-1);
snprintf(MAXS(line),"TO %s",user);
sock_putline(sockfd,line);
sock_getline(sockfd,line);
if (!str_beq(line,"200 ")) return(-1);
sock_putline(sockfd,"FILE test");
sock_getline(sockfd,line);
if (!str_beq(line,"200 ")) return(-1);
sock_putline(sockfd,"QUIT");
sock_getline(sockfd,line);
close(sockfd);
return(0);
}
/*
* usage - print short help usage text
*/
int usage() {
fprintf(stderr,"\n");
fprintf(stderr,"usage: %s [OPTIONS]\n",prg);
fprintf(stderr," or: %s [OPTIONS] file [...]\n",prg);
fprintf(stderr," or: %s [OPTIONS] -n file-number [...]\n",prg);
fprintf(stderr,"options: -l only list files\n");
fprintf(stderr," -a fetch all files\n");
fprintf(stderr," -k keep file on SAFT-server after receiving\n");
fprintf(stderr," -d delete file on SAFT-server without receiving\n");
fprintf(stderr," -n fetch file number instead of file name\n");
fprintf(stderr," -P pipe file directly to stdout\n");
fprintf(stderr," -r call receive automatically\n");
fprintf(stderr," -i ignore testing if file has already been fetched\n");
fprintf(stderr," -q quiet mode 1: no information messages\n");
fprintf(stderr," -Q quiet mode 2: no information or transfer messages\n");
fprintf(stderr," -v verbose output\n");
fprintf(stderr," -V show version information\n");
fprintf(stderr," -I initalize fetchfile (pgp and spool)\n");
fprintf(stderr," -f user fetch only files from this user\n");
fprintf(stderr," -s [user@]server specify alternate SAFT-server and user name\n");
fprintf(stderr," -Cw=conffile send config file to server\n");
fprintf(stderr," -Cr=conffile read config file from server\n");
fprintf(stderr," (conffile is \"config\" or \"restrictions\", see: man sendfile)\n");
fprintf(stderr,"examples: %s -l\n",prg);
fprintf(stderr," %s -a\n",prg);
fprintf(stderr," %s hoppel.gif\n\n",prg);
return(2);
}
syntax highlighted by Code2HTML, v. 0.9.1