/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (c) 1995-1999.  The Regents of the University of California.  All     */
/*   rights reserved.                                                      */
/*                                                                         */
/*   This work was produced at the University of California, Lawrence      */
/*   Livermore National Laboratory (UC LLNL) under contract no.            */
/*   W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy     */
/*   (DOE) and The Regents of the University of California (University)    */
/*   for the operation of UC LLNL.  Copyright is reserved to the           */
/*   University for purposes of controlled dissemination,                  */
/*   commercialization through formal licensing, or other disposition      */
/*   under terms of Contract 48; DOE policies, regulations and orders;     */
/*   and U.S. statutes.  The rights of the Federal Government are          */
/*   reserved under Contract 48 subject to the restrictions agreed upon    */
/*   by the DOE and University.                                            */
/*                                                                         */
/*                                                                         */
/*                              DISCLAIMER                                 */
/*                                                                         */
/*   This software was prepared as an account of work sponsored by an      */
/*   agency of the United States Government.  Neither the United States    */
/*   Government nor the University of California nor any of their          */
/*   employees, makes any warranty, express or implied, or assumes any     */
/*   liability or responsibility for the accuracy, completeness, or        */
/*   usefulness of any information, apparatus, product, or process         */
/*   disclosed, or represents that its specific commercial products,       */
/*   process, or service by trade name, trademark, manufacturer, or        */
/*   otherwise, does not necessarily constitute or imply its               */
/*   endorsement, recommendation, or favoring by the United States         */
/*   Government or the University of California. The views and opinions    */
/*   of the authors expressed herein do not necessarily state or reflect   */
/*   those of the United States Government or the University of            */
/*   California, and shall not be used for advertising or product          */
/*   endorsement purposes.                                                 */
/*                                                                         */
/*   Permission to use, copy, modify and distribute this software and its  */
/*   documentation for any non-commercial purpose, without fee, is         */
/*   hereby granted, provided that the above copyright notice and this     */
/*   permission notice appear in all copies of the software and            */
/*   supporting documentation, and that all UC LLNL identification in      */
/*   the user interface remain unchanged.  The title to copyright LLNL     */
/*   XDIR shall at all times remain with The Regents of the University     */
/*   of California and users agree to preserve same. Users seeking the     */
/*   right to make derivative works with LLNL XDIR for commercial          */
/*   purposes may obtain a license from the Lawrence Livermore National    */
/*   Laboratory's Technology Transfer Office, P.O. Box 808, L-795,         */
/*   Livermore, CA 94550.                                                  */
/*                                                                         */
/***************************************************************************/
/***************************************************************************/

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#if defined(SYSV) || defined(SVR4)
#include <netdb.h>
#endif
#include <ctype.h>
#include <X11/Intrinsic.h>
#include "xdir.h"
#include "str.h"

extern int max_ftp_retries;
extern int diagnostics;
extern struct st_host_info hinfo[];
extern int maxt;


/*
 * remote_cd - Change remote working directory to specified directory.
 *             "host" is the host id.  Set print_error_msg to True if
 *             FTP-generated error messages are to be written to the
 *             log when not in verbose mode.  Returns 0 if successful,
 *             -3 for broken connection, -6 for stop button pushed,
 *             and -1 for other errors.
 */
remote_cd(host, directory, print_error_msg)
int host;
char *directory;
int print_error_msg;
{
	char reply[MAXFTPREPLY];
	char cmd[MAXPATHLEN+5];
	int code;
	int retval;
	int retries = 0;
	int scode;

retry:

	/* Send CWD command to FTP server */
	sprintf(cmd, "CWD %s", directory);
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		return 0;
	case 4:
		if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
			if (diagnostics >= VERBOSE)
				report_retry("Change directory");
			goto retry;
		}
		if (diagnostics < VERBOSE && print_error_msg)
			report_ftp_reply(host, reply);
		return -1;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -3:
		return -3;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE && print_error_msg)
			report_ftp_reply(host, reply);
		return -1;
	}
}


/*
 * remote_mkdir - Create remote directory.  "host" is the host id.  Returns
 *                0 if successful, -3 for broken connection, -6 for stop
 *                button pushed, and -1 for other errors.
 */
remote_mkdir(host, path)
int host;
char *path;
{
	char reply[MAXFTPREPLY];
	char cmd[MAXPATHLEN+5];
	int code;
	int retval;
    char *dir;
    char *entry;
	int retries = 0;
	int scode;

retry:

    /* "cd" into directory */
    parse_path(hinfo[host].system, path, &dir, &entry);
    retval = remote_cd(host, dir, True);
    XtFree(dir);
    switch (retval) {
    case 0:
        break;
    case -3:
	case -6:
        XtFree(entry);
        return retval;
    default:
        XtFree(entry);
        return -1;
    }

	/* Send MKD command to FTP server */
	if (hinfo[host].system == SYS_VMS)
		sprintf(cmd, "MKD %s", path);
	else {
		sprintf(cmd, "MKD %s", entry);
		XtFree(entry);
	}
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		return 0;
	case 4:
		if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
			if (diagnostics >= VERBOSE)
				report_retry("Make directory");
			goto retry;
		}
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	}
}


/*
 * remote_delete - Delete file/directory from remote host.  "host" is
 *                 the host id.  Returns 0 if successful, -3 for broken
 *                 connection, -6 for stop button pushed, and -1 for
 *                 other errors.
 */
remote_delete(host, path)
int host;
char *path;
{
	char reply[MAXFTPREPLY];
	char cmd[MAXPATHLEN+6];
	int code;
	int retval;
	char *dir;
	char *entry;
	int retries = 0;
	int scode;

retry:

    /* "cd" into directory */
    parse_path(hinfo[host].system, path, &dir, &entry);
    retval = remote_cd(host, dir, True);
    XtFree(dir);
    switch (retval) {
    case 0:
        break;
    case -6:
        XtFree(entry);
        return retval;
    default:
        XtFree(entry);
        return -1;
    }

	/* Send DELE command to FTP server */
	sprintf(cmd, "DELE %s", entry);
	XtFree(entry);
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return -1;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		return 0;
	case 4:
		if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
			if (diagnostics >= VERBOSE)
				report_retry("Delete");
			goto retry;
		}
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	}
}


/*
 * remote_rmdir - Remove remote directory.  "host" is the host id.
 *                Returns 0 if successful, -3 for broken connection,
 *                -6 for stop button pushed, and -1 for other errors.
 */
remote_rmdir(host, path)
int host;
char *path;
{
	char reply[MAXFTPREPLY];
	char cmd[MAXPATHLEN+5];
	int code;
	int retval;
	char *dir;
	char *entry;
	int retries = 0;
	int scode;

retry:

    /* "cd" into directory */
    parse_path(hinfo[host].system, path, &dir, &entry);
    retval = remote_cd(host, dir, True);
    XtFree(dir);
    switch (retval) {
    case 0:
        break;
    case -3:
	case -6:
        XtFree(entry);
        return retval;
    default:
        XtFree(entry);
        return -1;
    }

	/* Send RMD command to FTP server */
	sprintf(cmd, "RMD %s", entry);
	XtFree(entry);
	if (hinfo[host].server == SERVER_VMS_3_0)
		strcat(cmd, ";1");
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return -1;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		return 0;
	case 4:
		if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
			if (diagnostics >= VERBOSE)
				report_retry("Remove directory");
			goto retry;
		}
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	}
}


/*
 * remote_pwd - Get remote working directory name.  If successful, "name"
 *              is set to point to a string allocated by remote_pwd, else
 *              "name" is undefined.   "host" is the host id.
 *              Use XtFree() to release memory pointed to by "name".
 *              Returns 0 if successful, -3 for broken connection, -6 for
 *              stop button pressed, and -1 for other errors.
 */
remote_pwd(host, name)
int host;
char **name;
{
	char reply[MAXFTPREPLY];
	int code;
	int nbytes;
	char *start;
	char *end;
	int retval;
	int retries = 0;
	int len;

retry:

	/* Send PWD command to FTP server */
	if ((retval = send_ftp_cmd(host, "PWD")) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
    retval = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
    switch (retval) {
    case 2:
        break;
	case 1:
    case 3:
	case 5:
        return -1;
	case 4:
		if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
			if (diagnostics >= VERBOSE)
				report_retry("Print working directory");
			goto retry;
		}
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
    case -2:
		report_client_timeout(host);
        return -1;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
    default:
		if (retval > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
        return retval;
    }

	/* Extract directory path */
	if ((start = strchr(reply, '\"'))) {
		start++;
		if ((end = strrchr(reply, '\"')) == NULL)
			return -1;
		if ((nbytes = end-start) <= 0)
			return -1;
		*name = XtMalloc(nbytes+1);
		bcopy(start, *name, nbytes);
		(*name)[nbytes] = '\0';
	} else {
		if (strtok(reply, " ") == NULL)
			return -1;
		if ((start = strtok(NULL, " ")) == NULL)
			return -1;
		*name = XtNewString(start);
	}

	/* Kludge for NCSA Telnet FTP server on Mac */
	if (hinfo[host].server == SERVER_MAC_NCSATELNET) {
		len = strlen(*name);
		if ((len>1) && (((*name)[len-1] == '/') || ((*name)[len-1] == '\001')))
			(*name)[len-1] = '\0';
	}

	/* If VMS, convert to lower case */
	if (hinfo[host].system == SYS_VMS)
		cvt_to_lower(*name);

	return 0;
}


/*
 * remote_rename - Rename/move a directory entry from "old_path" to
 *                 "new_path".  "host" is the host id.  Returns 0 if
 *                 successful, -3 for broken connection, -6 for stop
 *                 button pushed, and -1 for other errors.
 */
remote_rename(host, old_path, new_path)
int host;
char *old_path;
char *new_path;
{
	char reply[MAXFTPREPLY];
	char cmd[MAXPATHLEN+6];
	int code;
	int retval;
	int retries = 0;
	int scode;
	char *dir;
	char *entry;

retry:

	/* Send RNFR command to FTP server */
	if (hinfo[host].server == SERVER_MAC_VERSATERM ||
			hinfo[host].server == SERVER_WINDOWS_WFTPD) {
		parse_path(hinfo[host].system, old_path, &dir, &entry);
   		retval = remote_cd(host, dir, True);
    	XtFree(dir);
    	switch (retval) {
    	case 0:
       		break;
    	case -3:
		case -6:
        	XtFree(entry);
        	return retval;
    	default:
        	XtFree(entry);
        	return -1;
    	}
		sprintf(cmd, "RNFR %s", entry);
		XtFree(entry);
	} else
		sprintf(cmd, "RNFR %s", old_path);
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 3:
		break;
	case 4:
		if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
			if (diagnostics >= VERBOSE)
				report_retry("Rename");
			goto retry;
		}
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	}

	/* Send RNTO command to FTP server */
	if (hinfo[host].server == SERVER_MAC_VERSATERM ||
			hinfo[host].server == SERVER_WINDOWS_WFTPD) {
		parse_path(hinfo[host].system, new_path, &dir, &entry);
   		retval = remote_cd(host, dir, True);
    	XtFree(dir);
    	switch (retval) {
    	case 0:
       		break;
    	case -3:
        	XtFree(entry);
        	return -3;
		case -6:
			XtFree(entry);
			abort_ftp_cmd(host);
			return -6;
    	default:
        	XtFree(entry);
        	return -1;
    	}
		sprintf(cmd, "RNTO %s", entry);
		XtFree(entry);
	} else
		sprintf(cmd, "RNTO %s", new_path);
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval == -6) {
		abort_ftp_cmd(host);
		return -6;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		return 0;
	case 4:
		if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
			if (diagnostics >= VERBOSE)
				report_retry("Rename");
			goto retry;
		} else {
	        if (diagnostics < VERBOSE)
	            report_ftp_reply(host, reply);
			return -1;
		}
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	}
}


/*
 * quote_cmd - Send command to ftp server.  "host" is the host id.  Returns
 *             0 if successful, -3 for broken connection, -6 for stop button
 *             pushed, and -1 for other errors.
 */
quote_cmd(host, cmd)
int host;
char *cmd;
{
	char reply[MAXFTPREPLY];
	int code;
	int retval;

	/* Send command to FTP server */
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval == -6)
		return -6;
	else if (retval < 0)
		return -1;

	/* Get response from FTP server */
	while (1)
		switch (get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt)) {
		case 1:
		case 3:
			if (diagnostics < VERBOSE)
				report_ftp_reply(host, reply);
			continue;
		case -1:
			return -1;
		case -2:
			report_client_timeout(host);
			return -1;
		case -3:
			return -3;
		case -6:
			abort_ftp_cmd(host);
			return -6;
		default:
			if (diagnostics < VERBOSE)
				report_ftp_reply(host, reply);
			return 0;
		}
}


/*
 * remote_noop - Send no-op command to ftp server.  Returns 0 if
 *               successful, -3 for broken connection, -6 for stop
 *               button pushed, -2 for timeout, and -1 for other errors.
 */
remote_noop(host)
int host;
{
	char reply[MAXFTPREPLY];
	int code;
	int retval;
	int retries = 0;

retry:

	/* Send NOOP command to FTP server */
	if ((retval = send_ftp_cmd(host, "NOOP")) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
    retval = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
    switch (retval) {
	case 2:
        return 0;
	case 4:
		if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
			if (diagnostics >= VERBOSE)
				report_retry("No-op command");
			goto retry;
		}
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
        return -2;
    default:
		if (retval > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
        return -1;
    }
}


/*
 * stage_file - Tell Unitree or HPSS server to stage the file specified by
 *              "path".  "host" is the host id.  Returns 0 if file is already
 *              on disk, 1 if stage is successfully initiated, -3 for lost
 *              connection, -6 for stop button pushed, and -1 for other errors.
 */
stage_file(host, path)
int host;
char *path;
{
	/* Send STAGE command to FTP server */
	switch (hinfo[host].server) {
	case SERVER_UNIX_UNITREE:
	case SERVER_UNIX_NSL_UNITREE:
		return stage_unitree_file(host, path);
	case SERVER_UNIX_HPSS:
		return stage_hpss_file(host, path);
	default:   /* Sanity check */
		fatal_error("Bug in stage_file()");
		return 0;  /* keep compiler happy */
	}
}


/*
 * stage_unitree_file - Tell Unitree server to stage the file specified by
 *                      "path".  "host" is the host id.  Returns 0 if file
 *                      is already on disk, 1 if stage is successfully
 *                      initiated, -3 for lost connection, -6 for stop
 *                      button pushed, and -1 for other errors.
 */
stage_unitree_file(host, path)
int host;
char *path;
{
	char reply[MAXFTPREPLY];
	char cmd[MAXPATHLEN+9];
	int code;
	int retval;
	int scode;

	/* Send STAGE command to FTP server */
	sprintf(cmd, "STAGE 1 %s", path);
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		return 0;
	case 4:
	case 5:
		if (code == 405 || code == 505)
			return 1;
		if (diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	}
}


/*
 * stage_hpss_file - Tell HPSS server to stage the file specified by
 *                   "path".  "host" is the host id.  Returns 0 if file
 *                   is already on disk, 1 if stage is successfully
 *                   initiated, -3 for lost connection, -6 for stop
 *                   button pushed, and -1 for other errors.
 */
stage_hpss_file(host, path)
int host;
char *path;
{
	char reply[MAXFTPREPLY];
	char cmd[MAXPATHLEN+14];
	int code;
	int retval;
	int scode;

	/* Send STAGE command to FTP server */
	sprintf(cmd, "SITE STAGE %s", path);
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		if (strstr(reply, "ready for other processing"))
			return 0;
		else
			return 1;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	}
}


/*
 * set_hpss_wait - Set wait mode for HPSS server to "no wait".  Returns
 *                 0 for success, -3 for broken connection, -6 for stop
 *                 button pressed, and -1 for other errors.
 */
set_hpss_wait(host)
int host;
{
	char reply[MAXFTPREPLY];
	char cmd[15];
	int code;
	int retval;
	int scode;

	/* Sanity check */
	if (hinfo[host].server != SERVER_UNIX_HPSS) {
		fatal_error("Bug in set_hpss_wait()");
	}

	/* Send WAIT command to FTP server */
	sprintf(cmd, "SITE WAIT 0");
	if ((retval = send_ftp_cmd(host, cmd)) == -2) {
		report_client_timeout(host);
		return -1;
	} else if (retval < 0)
		return retval;

	/* Get response from FTP server */
	scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		return 0;
	case -3:
		return -3;
	case -6:
		abort_ftp_cmd(host);
		return -6;
	case -2:
		report_client_timeout(host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(host, reply);
		return -1;
	}
}



syntax highlighted by Code2HTML, v. 0.9.1