/* Batchftp.c Version 1.02 */

/* Batchftp is C program which automates the ftp process on BSD UNIX
  systems.  With it a user is able to log on to remote computers, make file
  requests, and execute all other ftp commands.  A user has the option to run
  the program in either the background or the foreground. */

#define         _DIR_PATH       200                     /* directory path name length -- can be set to MAXPATHNAMELEN */
#define         _NULL           0
#define         _HOSTLEN        MAXHOSTNAMELEN + 80     /* local hostname + domain + network */
#define         _PATHLEN        _DIR_PATH + MAXNAMLEN   /* file path length -- directory + filename */

#define         _TRUE            1
#define         _FALSE           0
#define         _EQUAL           0                      /* return values and error codes for string functions */
#define         _GREATER         1
#define         _LESS            (-1)
#define         _INVARG          (-2)

#define         _DEF_LHOST       "guest"                /* default local hostname if no other information can be obtained */
#define         _ERR             (-1)
#define         _TENEX           "type tenex"           /* 36 to 8 bit transfer mode -- check ftp docs for the proper command */
#define         _DEF_TRANSFER    "type ascii"           /* initial default transfer mode if none specified */
#define         _LBUF            501                    /* maximum input file line length (function fgets) */

#define		_FTP		 "ftp -in"		/* ftp command line */
#define         _ALARM_TIME      900                    /* transfer time (seconds) before ftp output is analyzed */

#define         _T_COMPLETE     "transfer complete"     /* phrases searched for in ftp output file */
#define         _NO_CONNECT     "not connected"
#define         _NO_HOST        "unknown host"
#define         _NO_LOGIN       "login failed"
#define         _UNREACHABLE    "unreachable"
#define         _NO_MAP         'd'                     /* command line switch to disable Internet address mapping */
#define         _NO_INIT        'i'                     /* command line switch to disable ftp script file initialization */

#define         _MOD_NUMBER     31                      /* mod number to generate random sleep -- seed is system time */
#define         _DEF_SLEEP      15                      /* minimum delay between ftp connection attempts */
#define         _MAX_ATTEMPT    25                      /* connection attempts before extended sleep */
#define         _EXTENDED_DELAY 3600                    /* time to sleep before next connection attempt --_MAX_ATTEMPT failures */

#define         _PASS_LENGTH    MAXHOSTNAMELEN + 80     /* default password is user@host */
#define         _LOGIN_LENGTH    80                     /* login name -- usually only 8 characters */
#define         _CBUF_LENGTH    160                     /* command buffer length for "system" calls to the shell */

#include <sys/param.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>

#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/wait.h>
#include <signal.h>
#include <setjmp.h>

#include <pwd.h>
#include <stdio.h>


#include <unistd.h>

typedef char    boolean;

/* All string functions used in Batchftp are user-defined */

char *strcpy ();
int  strlen ();
int   strncmp ();
char  *substr ();
char  *strcat ();
int   strpos ();

int     process_entry ();
int     assign_args ();
char    *remove_white ();
char    *findword ();
char   *lower_case ();
int     monitor_ftp ();
int     check_ftp ();
unsigned connect_delay ();
void     alarm_handler ();

char    *get_lhost ();
char    *get_rhost ();

/* Variables used in signal handler */
boolean   alarm_call = _FALSE;                          /* flag to indicate that alarm has been triggered */
jmp_buf   env_buf;                                      /* save process environmemt for setjmp call */

int 	 h_errno;                                       /* global data flag used by network database routines */

/* On some systems, "h_errno" is not defined in <netdb.h>.  If you receive
   an error such as "multiply defined variable during compilation or linking,
   delete the above declaration. */

main (argc, argv)
int     argc;
char    *argv[];
{
	char    msgfile[_PATHLEN];                      /* file containing output for all ftp transfer sessions */
	char    ftp_script[MAXNAMLEN + 1];              /* file containing ftp commands */
	char    user_name[_LOGIN_LENGTH + 1];           /* user's login name */
	char    pass_word[_PASS_LENGTH + 1];            /* password used for ftp login */
	char    local_dir[_DIR_PATH];                   /* local directory for ftp session */
	char    home_dir[_DIR_PATH];                    /* directory from which Batchftp is started */
	char    remote_dir[_DIR_PATH];                  /* remote directory for ftp session */
	char    input_file[_PATHLEN];                   /* user supplied file containing ftp commands and/or shorthand notation */
	char    remote_host[_HOSTLEN];                  /* remote hostname */
	char    f_stdout[MAXNAMLEN  + 1];               /* file containing output for a single transfer session */
	char    sh_name[MAXNAMLEN + 1];                 /* name of exec'd sh script -- 1 line ftp command */
	char     linebuf[_LBUF];                        /* one line of user's input file */

	boolean  start_flag = _FALSE;                   /* indicates whether ftp attempt should begin */
	boolean  free_flag =  _FALSE;                   /* indicates ftp script input mode -- free-form or regular */
	int     init_flag;                              /* indicates if command line contains -i switch */
	int     address_flag;                           /* indicates if command line contains -d switch */

	int     numchars;                               /* number of characters read by fgets (_LBUF - 1) */
	int     entry_flag;                             /* return value for function process_entry */
	int     file_count = 0;                         /* number of transfer sessions */
	boolean new_file = _TRUE;                       /* indicates if a new transfer session should be initiated */
	char    *bufptr;                                /* pointer to a line of user's input file */
	FILE    *mptr;                                  /* file pointer for msgfile (all ftp output) */
	FILE    *iptr;                                  /* file pointer to user's input file */
	FILE	*fptr;					/* file pointto ftp script */
	int    parent_pid;                              /* process id of parent --used to name files generated by Batchftp */


	parent_pid = (int) getpid();                    /* sprintf does not always support type "long" */
	getwd(home_dir);
	sprintf(msgfile, "%s/msg%d",home_dir, parent_pid);      /*construct full pathname for ftp output file */

	/* Parse command line switches and assign other arguments */

	if (assign_args(argc, argv, user_name, pass_word, input_file, remote_host,&init_flag,&address_flag) == -1) {
		mptr = fopen(msgfile,"w");
		fprintf(mptr,"Usage:  %s -[%c%c] input_file [remote_host username password]\n", argv[0], _NO_MAP, _NO_INIT);
		fprintf(mptr, "Program terminated\n");
		fclose(mptr);
		exit(-1);
		}

	strcpy(local_dir, home_dir);                    /* home directory is current local transfer directory */
	remote_dir[0] = '\0';
	mptr = fopen(msgfile, "w");

	sprintf(ftp_script, "fscript%d", parent_pid);   /* construct the names for the rest of the ftp files */
	sprintf(f_stdout, "f_stdout%d", parent_pid);
	sprintf(sh_name, "sh_script%d", parent_pid);

	if ((init_flag == _ERR) && (address_flag == _ERR)) {    /* get remote host information if user desires */
		if (get_rhost(remote_host) == _NULL) {
			fprintf(mptr, "Program terminated");
			fclose(mptr);
			if (access(input_file, F_OK) != -1)
				unlink(input_file);
			exit(-1);
			}
		}

	if ((iptr = fopen(input_file, "r")) == _NULL) {
		fprintf(mptr,"Program terminated\n");
		fclose (mptr);
		exit (-1);
		}

	init_sh_script(f_stdout, ftp_script, sh_name);          /* build sh shell script to be exec'd later */
	numchars = _LBUF - 1;

	while ((fgets(linebuf, numchars, iptr)) != _NULL) {     /* read in user's input file */
		bufptr = remove_white(linebuf);
		if ((new_file == _TRUE) && (*bufptr != '\0')) {  /* if end of last session and not a blank line */
			new_file = _FALSE;
			fptr = fopen(ftp_script, "w");
			if (init_flag == _ERR)
				init_fscript(remote_host, user_name, pass_word, &fptr);  /* initialize ftp script if user desires */
			file_count = file_count + 1;
			fprintf(mptr, "\n\n----------########## Beginning transfer attempt #%d ##########----------\n\n", file_count);
			}
		/* process input file a line at a time */
		entry_flag = process_entry(bufptr, local_dir, remote_dir, &fptr, &mptr, &free_flag, &start_flag);

		if (entry_flag == _ERR) {
			fclose(fptr);
			unlink(ftp_script);
			continue;
			}

		if (start_flag == _TRUE) {
			fclose(fptr);
			start_session(&mptr, f_stdout, sh_name, ftp_script, msgfile);
			unlink(ftp_script);
			}

		/* Reset flags at the end of a transfer or if a fatal error occurs
		   during input processing */

		if  ((start_flag == _TRUE) || (entry_flag == _ERR)) {
			start_flag = _FALSE;
			free_flag = _FALSE;
			new_file = _TRUE;
		}
	}
	fclose(iptr);

	/* If an end of free-form transfer marker, '}' has not been encountered,
	   append a quit command to the file and execute */

	if (free_flag == _TRUE) {
		fprintf(fptr, "\nquit");
		fclose(fptr);
		start_session(&mptr, f_stdout, sh_name, ftp_script, msgfile);
		unlink(ftp_script);
		}

	fclose(mptr);
	unlink(input_file);
	unlink(sh_name);
	exit(0);                                /* generate proper exit status for background jobs */
}

/* ------------------------------------------------------------------- */

/* Get_rhost attempts to obain information about the remote host that the user
   specified.  If a valid Internet address has been given, the function simply
   returns.  On the other hand if the user has specified the name of a remote
   host, get_rhost attempts to substitute the appropriate Internet address.  Of
   course, if this information cannot be obtained, the original argument is
   returned.  In the event that the user has provided an invalid name or address,
   get_rhost returns -1, and Batchftp terminates.  Sometimes, the network database
   may not contain a particular alias; address mapping can be disabled with the -d
   switch. */

char    *get_rhost (remote_host)
char    *remote_host;
{
	struct in_addr  in;             /* single field structure containing host's Internet address (binary) */
	struct hostent  *hp;            /* structure returned by gethostbyname */
	char  temp_host[_HOSTLEN];      /* original command line argument */
	/* extern int h_errno is global flag defined in <netdb.h> */

	strcpy(temp_host, remote_host);
	if (inet_addr(remote_host) != -1)               /* check for valid Internet address */
		return(remote_host);

	do {
		hp = gethostbyname(remote_host);
	} while ((h_errno == TRY_AGAIN) && (hp == _NULL));

	if ((h_errno == HOST_NOT_FOUND) && (hp == _NULL))       /* invalid remote host information given by user */
		return (_NULL);

	if (hp == _NULL)                                /* unable to obtain information from database */
		return(remote_host);

	bzero((char *)&in, sizeof(in));
	bcopy(hp->h_addr, (char *)&in, hp -> h_length);         /* copy Internet address into structure for ASCII conversion */
	*remote_host = '\0';
	strcpy(remote_host, inet_ntoa(in));
	if (*remote_host == '\0')
		return (strcpy(remote_host, temp_host));

	return(remote_host);
}
/*--------------------------------------------------------------- */

/* Get_lhost attempts to obtain the official name of the local host
   from the network database.  First, the function gets the local name
   returned by gethostname ().  This string is then used by gethostbyname()
   to search the network database.  If successful the function returns the
   h_name field of the hostent structure -- the official host name.  It seems
   that if a particular machine is actually on the Internet, the official host
   name contains contains the domain and network (host_name.domain.network).
   However, if a machine is hidden from the network, its official hostname
   does not contain the domain and network extensions. */

char    *get_lhost (local_host)
char    *local_host;
{
	char  hostname[MAXHOSTNAMELEN + 1];             /* local host name returned by gethostname () */
	int  namelen;                                   /* length of host name array */
	struct hostent *hp;                             /* structure returned by gethostbyname */

	namelen = MAXHOSTNAMELEN + 1;
	if (gethostname(hostname, namelen) == -1)       /* if no localhost info can be obtained, return _DEF_LHOST */
		return(strcpy(local_host, _DEF_LHOST));

	do {
		hp = gethostbyname(hostname);
	} while ((h_errno == TRY_AGAIN) && (hp == _NULL));

	if (hp == _NULL)
		return(strcpy(local_host, hostname));   /* if call to gethostbyname() fails, return hostname */
	else
		return(strcpy(local_host, hp->h_name));
}

 /*---------------------------------------------------------- */

 /* Init_fscript adds the login information to an ftp script file
    and sets the output mode to verbose.  It verbose mode is not
    activated, Batchftp will return incorrect results from its
    output analysis  -- indefinite silent connections.  Login
    initialization can be disabled with the -i switch; it is then
    the responsibility of the user to enter the proper login
    information in the input file (free-form mode). */

init_fscript (remote_host, user_name, pass_word, fptr)
char    *remote_host;
char    *user_name;
char    *pass_word;
FILE 	**fptr;
{
	fprintf(*fptr, "open %s\n", remote_host);
	fprintf(*fptr, "user %s %s\n", user_name, pass_word);
	fprintf(*fptr, "verbose\n");
}
/*--------------------------------------------------------------*/

/* Remove_white skips over whitespace (space, tab, null) in an
   input line. */

char    *remove_white (s)
char    *s;
{
	while ((*s == '\n') || (*s == '\t') || (*s == ' '))
		++s;
	return(s);
}
/*----------------------------------------------------------- */

/* Find_word replaces the whitespace character at the end of a word
   with a NULL and returns a pointer to the follwing character.  Then,
   pointers can reference individual words within an input line. */

char *findword (s)
char    *s;
{
	while (*s) {
		if ((*s == '\t') || (*s == ' ') || (*s == '\n')) {
			*s = '\0';
			return (++s);
			}
		++s;
		}
	return (s);
}

/*----------------------------------------------------------------- */

/* Init_sh_script creates the one line (not including interpreter) Bourne
   shell script to be exec'd during the ftp session. */

init_sh_script(f_stdout, ftp_script, sh_name)
char    *f_stdout;
char    *ftp_script;
char    *sh_name;
{
	FILE    *fptr;

	fptr = fopen(sh_name, "w");
	fprintf(fptr, "#! /bin/sh\n");
	fprintf(fptr, "%s < %s > %s 2>&1", _FTP, ftp_script, f_stdout);   /* redirect stdout and stderr to a single file */
	fclose(fptr);
}
/*---------------------------------------------------------------------- */

/* Process_entry translates a user's input file into one or more ftp command
   files.  In regular mode each file request (in shorthand notation) is a
   separate transfer session.  However, in free-form mode a transfer
   session is defined by braces -- '{' and '}'. All of the shorthand switches
   a, b, c, d, and l are also valid in free-form mode; but default transfer
   types are not supported.  If a input line does not contain any of the
   shorthand switches, it is copied directly into the script file (That is
   why default transfer types are not supported in free_form mode).  */

int  process_entry (linebuf,local_dir, remote_dir, fptr, mptr, free_flag, start_flag)
char    *linebuf;
char    *local_dir;
char    *remote_dir;
FILE    **fptr;
FILE    **mptr;
boolean *free_flag;
boolean *start_flag;
{

	static boolean	dir_flag = _FALSE;	/* indicates if dir info has been written to ftp script in free-form mode */
	static char   transfer_type[20] = _DEF_TRANSFER;	/* default transfer type for regular mode */
	char    *str;                           /* additional pointer to input line */
	char    *s1;                            /* first file argument (required) */
	char    *s2;                            /* second file argument (sometimes optional) */
	char    option;                         /* shorthand option */

	if (*linebuf == '\0')                   /* blank line -- no error */
		return (0);

	str = linebuf;
	while (*linebuf) {                      /* replace newline character at the end of a line with NULL */
		if (*linebuf == '\n')
			*linebuf = '\0';
		++linebuf;
		}
	option = *str;                          /* get option character */

	switch (option) {
		case '{' :
			*free_flag = _TRUE;     /* beginning free-form mode */
			return (0);
		case '}' :                      /* ending ftp script transfer session */
			if (*free_flag == _FALSE) {
				fprintf(*mptr, "\nOption { read without matching {\n");
				return (_ERR);
				}
			else {
				*free_flag = _FALSE;            /* free-form mode is complete */
				*start_flag = _TRUE;            /* upon return, start ftp transfer session */
				dir_flag = _FALSE;
				fprintf(*fptr, "quit");          /* append quit command */
				return (0);
				}
		case '-' :                                      /* indicates shorthand notation */
			str = str + 1;
			str = remove_white(str);                /* skip any whitespace before option character */
			option = *str;                          /* get option character if it exists */
			if (*str != '\0')
				str = str + 1;

			if ((*free_flag == _TRUE) && (dir_flag == _FALSE)) {
				fprintf(*fptr, "lcd %s\n", local_dir);
				if (*remote_dir != '\0')
					fprintf(*fptr, "cd %s\n", remote_dir);
				dir_flag = _TRUE;
				}
			break;
		default :
			if (*free_flag == _TRUE) {              /* copy line directly into command file */
				fprintf(*fptr,"%s\n",str);
				return (0);
				}
			else {                                  /* parse file arguments; include previous transfer type */
				s1 = str;
				str = findword(str);
				str = remove_white(str);
				s2 = str;
				str = findword(str);
				fprintf(*fptr,"%s\n", transfer_type);
				fprintf(*fptr,"lcd %s\n", local_dir);
				if (*remote_dir != '\0')
				       fprintf(*fptr, "cd %s\n", remote_dir);
				if (*s2 == '\0')
				       fprintf(*fptr,"get %s\n", s1);
				else
				       fprintf(*fptr, "get %s %s\n", s1, s2);

				fprintf(*fptr, "quit");
				*start_flag = _TRUE;
				return (0);
				}
			}

	str = remove_white(str);
	s1 = str;
	if (*s1 == '\0') {
		if (*free_flag == _FALSE)
			fprintf(*mptr, "\nIndicatior '-' with no accompanying option");
		else
			return (0);
		return (_ERR);
		}

	str = findword(str);                    /* parse file arguments for regular mode transfers */
	str = remove_white(str);
	s2 = str;
	str = findword(str);

	if ((option >= 'A') && (option <= 'Z'))
		option = option + 32;

	switch(option) {
		case 'a' : /* ascii transfer */
			strcpy(transfer_type, "type ascii");    /* save transfer type */
			fprintf(*fptr, "type ascii\n");

			if (*free_flag == _FALSE) {
				fprintf(*fptr, "lcd %s\n", local_dir);   /* include current local directory */
				if (*remote_dir != '\0')
					fprintf(*fptr,"cd %s\n",remote_dir);    /* also specify current remote directory if not NULL */
				}

			if (*s2 == '\0')
				fprintf(*fptr,"get %s\n",s1);
			else
				fprintf(*fptr, "get %s %s\n", s1,s2);

			if (*free_flag == _FALSE) {             /* Only end transfer session if in regular mode */
				fprintf(*fptr, "quit");
				*start_flag = _TRUE;
				}
			break;
		case 'b' :
			strcpy(transfer_type, "type binary");
			fprintf(*fptr, "type binary\n");

			if (*free_flag == _FALSE) {
				fprintf(*fptr, "lcd %s\n", local_dir);
				if (*remote_dir != '\0')
					fprintf(*fptr, "cd %s\n", remote_dir);
				}

			if (*s2 == '\0')
				fprintf(*fptr,"get %s\n",s1);
			else
				fprintf(*fptr, "get %s %s\n", s1,s2);

			if (*free_flag == _FALSE) {
				fprintf(*fptr, "quit");
				*start_flag = _TRUE;
				}
			break;

		case 't' :
			strcpy(transfer_type, _TENEX);
			fprintf(*fptr, _TENEX);
			fprintf(*fptr, "\n");

			if (*free_flag == _FALSE) {
				fprintf(*fptr, "lcd %s\n", local_dir);
				if (*remote_dir != '\0')
					fprintf(*fptr,"cd %s\n",remote_dir);
				}

			if (*s2 == '\0')
				fprintf(*fptr,"get %s\n",s1);
			else
				fprintf(*fptr, "get %s %s\n", s1,s2);

			if (*free_flag == _FALSE) {
				fprintf(*fptr, "quit");
				*start_flag = _TRUE;
				}
			break;

		case 'l' :      /* get directory listing */
			if (*s2 == '\0') {
				fprintf(*mptr,"\nDirectory listing requested for %s WITHOUT output file",s1);
				if (*free_flag == _TRUE)
					return(0);
				return (_ERR);
				}
			else {
				if (*free_flag == _FALSE)
					fprintf(*fptr, "lcd %s\n", local_dir);
				fprintf(*fptr, "dir %s %s\n", s1, s2);

				if (*free_flag == _FALSE) {
					fprintf(*fptr,"quit");
					*start_flag = _TRUE;
					}
				}
			break;

		case 'r' :      /* get remote directory listing */
			if (*s2 == '\0') {
				fprintf(*mptr,"\nRecursive directory listing requested for %s WITHOUT output file",s1);
				if (*free_flag == _TRUE)
					return(0);
				return (_ERR);
				}
			else {
				strcpy(remote_dir, s1);
				fprintf(*fptr, "cd %s\n", remote_dir);
				fprintf(*fptr, "ls -alR %s\n", s2);

				if (*free_flag == _FALSE) {
					fprintf(*fptr,"quit");
					*start_flag = _TRUE;
					}
				}
			break;

		case 'c' :
			strcpy(local_dir,s1);           /* save local directory --may be superceeded by a later request */
			if (*free_flag == _TRUE)
				fprintf(*fptr, "lcd %s\n", s1);
			break;
		case 'd' :
			strcpy(remote_dir, s1);         /* save remote directory --may be superceeded by a later request */
			if (*free_flag == _TRUE)
				fprintf(*fptr, "cd %s\n", s1);
			break;
		default :
			fprintf(*mptr, "\nInvalid option -%c", option);
			if (*free_flag == _TRUE)
				return(0);
			return (_ERR);
		}

	return (0);
}
/*------------------------------------------------------------------ */

/* Assign_args parses and interprets the command line switches and other
   arguments the user enters.  First, the concatenates any switches into
   a single option string.  Then, if a user has supplied an appropriate
   number of arguments for the transfer mode, assign_args will assign the
   arguments to the proper variables */

int     assign_args(argc, argv, user_name, pass_word, input_file, remote_host, init_flag, address_flag)
int     argc;
char    *argv[];
char    *user_name;
char    *pass_word;
char    *input_file;
char    *remote_host;
int     *init_flag;
int     *address_flag;
{
	char local_host[_HOSTLEN];
	char    login_name[_LOGIN_LENGTH];
	struct  passwd  *pf_ptr;                        /* pointer to user's password entry */
	int     arg_count = 0;                          /* argument count minus any switches */
	int     shift = 0;                              /* number of switches subtracted from orig arg count */
	int     current_arg = 1;                        /* process arguments starting with argv[1] */
	char    *s;
	char    cmd_buf[_PATHLEN];                      /* used to make copy of user's input file */
	char    option_string[20];                      /* all command line switches */

	option_string[0] = '\0';
	if (argc < 2)
		return (-1);

	while (current_arg < argc) {
		if (strncmp(argv[current_arg], "-", 1) == _EQUAL) {     /* concat command switches (look for '-') */
			shift = shift + 1;
			s = argv[current_arg];
			s = s + 1;
			strcat(option_string, s);
			}
		current_arg = current_arg + 1;
		}

	arg_count = argc - shift;
	*init_flag = strpos(option_string , _NO_INIT);          /*set command switch flags */
	*address_flag = strpos(option_string, _NO_MAP);

	if ((arg_count < 2) && (*init_flag != _ERR))            /* not enough args for _NO_INIT mode */
		return(-1);

	if ((arg_count < 3) && (*init_flag == _ERR))            /* not enough args for initialization mode */
		return (-1);

	if (access(argv[1 + shift], F_OK) == -1)                /* cannot open input file -- terminate */
		exit(-1);

	sprintf(input_file, "%s%d", argv[1 + shift], (int)getpid());
	sprintf(cmd_buf, "cp %s %s", argv[1 + shift], input_file);
	system(cmd_buf);                                        /*make copy of inputfile; append pid to filename */

	if (*init_flag != _ERR)                                 /* no further processing required for _NO_INIT mode */
		return (0);

	strcpy(remote_host,argv[2 + shift]);
	lower_case(remote_host);
	get_lhost(local_host);                                  /* get local host information */


/* A user can specify a password in one of three ways.
   1.  In input file (free-form mode)
   2.  On the command line (last argument -- must be preceeded by username)
   3.  At compile time (define DEF_PASS)

   Option 1 has the highest precedence; if passwords are provided in any other
   way they are overwritten or ignored. */

#ifdef  DEF_PASS
	strcpy(pass_word, DEF_PASS);
#else
	if ((pf_ptr = getpwuid(getuid())) != _NULL) {   /* get user's login name from password file */
		strcpy(login_name, pf_ptr -> pw_name);
		sprintf(pass_word, "%s@%s",login_name, local_host);
		}
	else
		strcpy(pass_word,local_host);           /* use "user@host" as password */
#endif

	switch (arg_count) {
		case 3 :
			strcpy(user_name, "anonymous");
			return (0);
		case 4 :
			strcpy(user_name, argv[3 + shift]);
			return (0);
		case 5 :
			strcpy(user_name, argv[3 + shift]);
			strcpy(pass_word, argv[4 + shift]);
			return (0);
		default :
			strcpy(user_name, argv[3 + shift]);
			strcpy(pass_word, argv[4 + shift]);
			}
	return(0);
}
/*-------------------------------------------------------------------- */

/* Start_ftp is the most important function of the entire program.
   First, it establishes a handler to catch the SIGALRM signal (process
   timer.  Next, start_ftp forks a child process which execv the one-line
   Bourne shell script (ftp command). While the child process is running,
   parent simply loops and "waits" for it to terminate.  If the alarm
   "sounds" and the ftp session has not already terminated, the parent
   will determine if the child (ftp) has produced any output.  If it
   has not genearated any output, the child is made the new group
   leader, and the child and the process created to execute the
   ftp command are killed simultaneously.  Otherwise, the child is not
   signalled, and it will terminate naturally. */

start_ftp (mptr, f_stdout, sh_name, alarm_flag, ftp_script)
FILE	**mptr;
char    *f_stdout;
char    *sh_name;
boolean *alarm_flag;
char	*ftp_script;
{
	int             pid;                    /* process id of child */
	int		pg_flag;		/* return value of setpgrp () */
	int		wpid;			/* return value of wait () */
	FILE            *fs_ptr;                /* pointer to f_stdout */
	FILE            *msg_ptr;               /* pointer to ftp output file */
	int		monitor_flag;		/* return value of monitor_ftp () */
	union wait      status;                 /* wait for child to terminate */
	char            *sh_args[3];            /* arguments for execv */

	*alarm_flag = _FALSE;                   /* alarm has not been called */
	sigsetmask(0);                          /* do not block any signals */

	if (access(sh_name, F_OK) == -1) {
		fs_ptr = fopen(f_stdout, "a");
		fprintf(fs_ptr,"\nUnable to open Bourne shell script: %s\n",sh_name);
		fprintf(fs_ptr,"Single transfer session aborted");
		fclose(fs_ptr);
		return;
		}

	if (access(ftp_script, F_OK) == -1) {
		fs_ptr = fopen(f_stdout, "a");
		fprintf(fs_ptr,"\nUnable to open ftp script: %s\n",ftp_script);
		fprintf(fs_ptr,"Single transfer session aborted");
		fclose(fs_ptr);
		return;
		}

	sh_args[0] = "/bin/sh";                 /* initialize arguments for execv call */
	sh_args[1] = sh_name;
	sh_args[2] = 0;

	if ((int) signal(SIGALRM, alarm_handler) == -1) {
		fprintf(*mptr, "\nSYSTEM ERROR -- Unable to establish timer");
		return;
		}

	alarm(_ALARM_TIME);                     /* start alarm */

	if ((pid = fork()) == 0) {              /* fork child and exec script */
		execv("/bin/sh", sh_args);
		exit(0);
		}

	if (pid == -1) {
		fprintf(*mptr, "\nSYSTEM ERROR -- Unable to start ftp transfer");
		return;
		}

	setjmp(env_buf);                        /* set return for longjmp() */
	if (alarm_call == _TRUE) {
		alarm_call = _FALSE;            /* alarm_call is global flag used in handler */
		*alarm_flag = _TRUE;            /* same flag returned to main -- not global */
		msg_ptr = *mptr;
		monitor_flag = monitor_ftp(&msg_ptr, f_stdout);
		*mptr = msg_ptr;
		if (monitor_flag == -1) {     /*ftp producing not output -- kill subprocesses */
			unlink(f_stdout);
			pg_flag = setpgid(pid, pid);                      /* make child group leader so that parent won't be killed */
			if ((killpg(pid, SIGKILL) != -1) && (pg_flag != -1)) /* kill child and the ftp process initiated by sh script */
				return;
			}
		}

	do {
		wpid = wait(&status);
	} while ((wpid != pid) && (wpid != -1));

	alarm(0);                               /* ftp process terminated normally; disable alarm */
	return;
}
/*-------------------------------------------------------------------- */

/* Alarm_handler catches the SIGALRM signal and sets a global flag to
   indicate that it has been called. */

void   alarm_handler ()
{
	alarm_call = _TRUE;
	longjmp(env_buf, 1);
}
/*-----------------------------------------------------------------------*/

/* Monitor_ftp determines if the transfer session is producing output by
  checking the size of the file f_stdout.  If the file does not exist or is
  empty, the function reports the system error or silent connection and
  returns -1 */

int   monitor_ftp(mptr, f_stdout)
FILE    **mptr;
char    *f_stdout;
{
	struct stat     statbuf;                /* obtain file statistics on f_stdout */

	if (access(f_stdout, F_OK) == -1) {
		fprintf(*mptr, "\nUnsuccessful ftp attempt -- Will try again");
		return (-1);
		}

	if (stat(f_stdout, &statbuf) == -1) {
		fprintf(*mptr, "\nSYSTEM ERROR -- Unable to obtain statistics on file transfer");
		return(0);
		}

	if (statbuf.st_size > 0)
		return (0);
	else {
	       fprintf(*mptr, "\nSilent connection -- Will try again");
	       return (-1);
	       }
}

/*--------------------------------------------------------------------- */
char  *lower_case(s)
char    *s;
{
	char    *ptr;

	ptr = s;
	while (*s) {
		if ((*s >= 'A') && (*s <= 'Z'))
			*s += 32;
		++s;
		}
	return(ptr);
}

/*---------------------------------------------------------------- */

/* Check_ftp reads the output of the transfer one line at a time and
   searches for keywords which determine the sessions success or
   failure.  If a retry is unnecessary, the contents of f_stdout is
   copied to msgfile. */

int   check_ftp(mptr, f_stdout, msgfile)
FILE	**mptr;
char    *f_stdout;
char 	*msgfile;
{
	FILE	*fs_ptr;				/* file pointer to f_stdout */
	int     transfers = 0;                  /* number of successful transfers per session */
	char    linebuf[_LBUF];                 /* buffer to read in output file */
	int     n;

	if ((fs_ptr = fopen(f_stdout,"r")) == _NULL)
		return (-1);

	n = _LBUF - 1;

	while (fgets(linebuf, n, fs_ptr) != _NULL) {
		lower_case(linebuf);                    /* convert entire line to lowercase to simplify searching */

		if (substr(linebuf, _T_COMPLETE) != _NULL) {
			++transfers;
			continue;
			}

		if (substr(linebuf, _UNREACHABLE) != _NULL)   {
			fprintf(*mptr, "\nHost not accessible -- Will try again");
			unlink(f_stdout);
			fclose(fs_ptr);
			return(-1);
			}

		if (substr(linebuf, _NO_HOST) != _NULL) {
			fprintf(*mptr,"\nUnknown host -- Program terminated");
			fclose(fs_ptr);
			unlink(f_stdout);
			return(0);
			}

		if (((substr(linebuf, _NO_LOGIN) != _NULL) && (transfers == 0)) || ((substr(linebuf, _NO_CONNECT) != _NULL) && (transfers == 0)))  {
			fprintf(*mptr, "\nLogin failed -- Will try again");
			fclose(fs_ptr);
			return (-1);
			}
		}
	fclose(fs_ptr);

	fprintf(*mptr,"\n----------\n\n");       /* copy output file and report successful transfers */
	fclose(*mptr);
	sprintf(linebuf, "cat %s >> %s",f_stdout, msgfile);
	system(linebuf);
	*mptr = fopen(msgfile, "a");
	fprintf(*mptr,"\n----------\n");
	fprintf(*mptr, "SUCCESSFUL TRANSFER(S) :  %d", transfers);
	unlink(f_stdout);
	return(transfers);
}
/*------------------------------------------------------------------- */
/* Connect_delay generates a random delay before each connection attempt.
   As the number of retries increases, the total delay time accumulates.
    */

unsigned      connect_delay (sleep_time)
unsigned        sleep_time;
{
	unsigned        rand_num;               /* random sleep time */
	int             seed;                   /* seed for random number generator */

	seed = (int) time(0L);
	srand (seed);
	rand_num = (unsigned) rand ();
	rand_num = (rand_num % (_MOD_NUMBER)) + (_DEF_SLEEP);   /*delay is at least _DEF_SLEEP */
	sleep_time += rand_num;
	sleep(sleep_time);
	return(sleep_time);
	}

/*------------------------------------------------------- */
/* Start_session is a driver which starts the ftp process
   and directs the error checking and output analysis. As
   stated earlier, Batchftp does not give up after an
   unsuccessful connection attempt.

   After (_MAX_ATTEMPTS - 1), the function sleeps for an extended delay.
   At this time, the sleep_time and the number of attempts are reset to
   zero. Of course, after a successful attempt sleep_time and attempt are
   reset. Start_session will not terminate until check_ftp() returns a value
   other than -1 */

start_session (mptr, f_stdout, sh_name, ftp_script, msgfile)
FILE    **mptr;
char    *f_stdout;
char    *sh_name;
char	*ftp_script;
char	*msgfile;
{
	boolean         alarm_flag;                     /* same as alarm_call -- flag for handler */
	unsigned        sleep_time = 0;                 /* accumulated time to sleep before connection */
	int             attempt = 0;                    /* connection attempts for a single session */
	FILE            *msg_ptr;			/* file pointer to msgfile */
	int		monitor_flag;			/* return value of monitor_ftp () */
	int		check_flag;			/* return value of check_ftp () */

	do {
		msg_ptr = *mptr;
		sleep_time = connect_delay(sleep_time);
		attempt = attempt + 1;
		if (attempt == _MAX_ATTEMPT) {
			sleep_time = 0;
			attempt = 0;
			fprintf(*mptr, "\nAttempt %d -- sleeping for %d seconds before connection attempt\n",_MAX_ATTEMPT, _EXTENDED_DELAY);
			sleep(_EXTENDED_DELAY);
			}
		start_ftp(&msg_ptr, f_stdout, sh_name, &alarm_flag, ftp_script);
		if (alarm_flag == _FALSE) {             /* call monitor_ftp if it has not already been called */
			monitor_flag = monitor_ftp(&msg_ptr, f_stdout);
			if (monitor_flag == -1) {   /* if monitor_ftp fails, check_ftp will also fail */
				unlink(f_stdout);
				continue;
			}
		}
		check_flag = check_ftp(&msg_ptr, f_stdout, msgfile);
		*mptr = msg_ptr;
		fclose(*mptr);
		*mptr = fopen(msgfile, "a");
	} while (check_flag == -1);


}
/*------------------------------------------------------- */

int  strlen (s)

register char  *s;
{
     register int   n = 0;

     while (*s++)
	  ++n;

     return (n);
}
/* -------------------------------------------------- */

int  strncmp (s1, s2, n)

register char  *s1, *s2;
register int   n;
{
	  if (n < 1)
	       return (_INVARG);

     {
	  register int   char_count = -1;

	  while ((++char_count < n) && (*s1) && (*s2)) {
	       if (*s1 > *s2)
		    return (_GREATER);

	       if (*s1++ < *s2++)
		    return (_LESS);
	}

	if (char_count == n)
	    return (_EQUAL);


    if (*s1 > *s2)
	return (_GREATER);

    if (*s1 < *s2)
	return (_LESS);

     }

     return (_EQUAL);
}

/*--------------------------------------------------- */

char  *strcpy (s1, s2)

register char  *s1, *s2;
{

     register char  *ptr;

     ptr = s1;
     while (*ptr++ = *s2++)
	  ;

     return (s1);
}
/*---------------------------------------------------------- */
char   *substr (s1, s2)

register char  *s1, *s2;
{
     register int   length2;

     length2 = strlen (s2);
     if (*s2 == '\0')
	++length2;

	do {
	  if (( strncmp(s1, s2,length2)) == _EQUAL)
	       return (s1);

     }  while (*s1++ != 0);
     return (_NULL);
}
/* -------------------------------------------------- */

char  *strcat (s1, s2)

register char   *s1, *s2;
{
     register char  *p;

     p = s1;
     while (*p++)
	  ;

     --p;
     strcpy (p, s2);
     return (s1);
}
/*--------------------------------------------------------- */

int   strpos (s, c)

register char  *s, c;
{
     register int   n = 0;

     do  {
	  if (*s == c)
	       return (n);

	  ++n;
     } while (*s++ != 0);

    return (_ERR);

}
