/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (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>
#ifdef HIPPI
#include <netinet/in.h>
#include <netinet/tcp.h>
#endif
#include <fcntl.h>
#include <sys/stat.h>
#include <Xm/Xm.h>
#include "xdir.h"
#include "xfer.h"

static char *tcpbuf = NULL;
static char *filebuf = NULL;

extern struct xfer_ctrl_block xc;
extern int max_ftp_retries;
extern int diagnostics;
extern int store_unique;
extern int maxt;
extern int passive_mode;
extern struct st_host_info hinfo[];


/*
 * init_put - Initialize transfer of file from local host to remote host.
 *            "xc" is used for input and output.  Returns 0 if successful,
 *            -4 for lost sink connection, -6 for stop button pushed, and
 *            -1 for other errors.
 */
init_put()
{
	char *src_path;
	char *snk_dir;
	char *snk_file;
	int file_fd;
	int data_fd;
	long file_len;
	long flen;
	struct stat status;
	int code;
	char reply[MAXFTPREPLY];
	int sock;
	char cmd[MAXPATHLEN+5];
	int retval;
	char msg[MAXPATHLEN+30];
	int retries = 0;
	int scode;
	int errval;
    int sys_tcpbuflen = XFER_SYS_TCPBUFLEN;
	char *snk_path;
#ifdef HIPPI
    int winshift = XFER_TCPWINSHIFT;
#endif

retry:

	/* Open source file and verify that it is a "real" file */
	src_paths_of_xfer_node(xc.current_node, NULL, &src_path);
	if ((file_fd = iopen2(src_path, O_RDONLY)) < 0) {
		report_perror(src_path);
		XtFree(src_path);
		return -1;
	}
	if (fstat(file_fd, &status) < 0 || !S_ISREG(status.st_mode)) {
		sprintf(msg, "%s is not a \"real\" file", src_path);
		XtFree(src_path);
		record_warning(msg);
		close(file_fd);
		return -1;
	}
	XtFree(src_path);

	/* Get length of file */
	file_len = status.st_size;

	/* "cd" into sink directory */
	snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
	parse_path(xc.snk_host_system, snk_path, &snk_dir,
		&snk_file);
	XtFree(snk_path);
	retval = remote_cd(xc.snk_dirwin->host, snk_dir, True);
	XtFree(snk_dir);
	switch (retval) {
	case 0:
		break;
	case -3:
		XtFree(snk_file);
		close(file_fd);
		return -4;
	case -6:
		XtFree(snk_file);
		close(file_fd);
		return -6;
	default:
		XtFree(snk_file);
		close(file_fd);
		return -1;
	}

	/* Initialize data connection */
	if (passive_mode) {
		if ((data_fd = passive_data_conn(xc.snk_dirwin->host)) < 0) {
			close(file_fd);
			return data_fd;
		}
	} else if ((sock = init_data_conn(xc.snk_dirwin->host)) < 0) {
		XtFree(snk_file);
		close(file_fd);
		if (sock == -3)
			return -4;
		else
			return sock;
	}

	/* Preallocate storage for UniTree and HPSS FTP servers */
	if (hinfo[xc.snk_dirwin->host].server == SERVER_UNIX_HPSS) {
		if (file_len < 10)  /* HPSS bug workaround */
			flen = 10;
		else
			flen = file_len;
		if ((retval = ftp_allocate(xc.snk_dirwin->host, flen)) < 0) {
			if (retval == -3)
				errval = -4;
			else
				errval = retval;
			goto error;
		}
	} else if (((hinfo[xc.snk_dirwin->host].server == SERVER_UNIX_UNITREE) ||
			(hinfo[xc.snk_dirwin->host].server == SERVER_UNIX_NSL_UNITREE)) &&
			(xc.mode == BINARY)) {
		if ((retval = ftp_allocate(xc.snk_dirwin->host, file_len)) < 0) {
			if (retval == -3)
				errval = -4;
			else
				errval = retval;
			goto error;
		}
	}

	/* Send STOR command to FTP server */
	if (store_unique)
		sprintf(cmd, "STOU %s", snk_file);
	else
		sprintf(cmd, "STOR %s", snk_file);
	XtFree(snk_file);
	if ((retval = send_ftp_cmd(xc.snk_dirwin->host, cmd)) < 0) {
		switch (retval) {
		case -6:
			errval = -6;
			goto error;
		case -3:
			errval = -4;
			goto error;
		case -2:
			report_client_timeout(xc.snk_dirwin->host);
		default:
			errval = -1;
			goto error;
		}
	}

	/* Get initial response from FTP server */
	scode = get_ftp_reply(xc.snk_dirwin->host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 1:
		break;
    case 4:
		close(file_fd);
		if (passive_mode)
			close(data_fd);
		else
			close(sock);
        if (code >= 450 && code < 460 && retries++ < max_ftp_retries) {
            if (diagnostics >= VERBOSE)
                write_log("*** Unable to initialize put.  Will retry.\n");
            goto retry;
        }
		if (diagnostics < VERBOSE)
			report_ftp_reply(xc.snk_dirwin->host, reply);
        return -1;
	case -3:
		errval = -4;
		goto error;
	case -6:
		abort_ftp_cmd(xc.snk_dirwin->host);
		errval = -6;
		goto error;
	case -2:
		report_client_timeout(xc.snk_dirwin->host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(xc.snk_dirwin->host, reply);
		errval = -1;
		goto error;
	}

	/* Establish data connection */
	if (!passive_mode) {
		data_fd = accept_data_conn(sock);
		close(sock);
		if (data_fd < 0) {
			close(file_fd);
			return -1;
		}
	}

	/* Attempt to increase TCP send buffer size to improve performance */
#if defined(SOL_SOCKET) && defined(SO_SNDBUF)
    setsockopt(data_fd, SOL_SOCKET, SO_SNDBUF, (char *)&sys_tcpbuflen,
        sizeof(sys_tcpbuflen));
#endif

    /* Optimize tranfer rate for HIPPI channel on UNICOS Cray */
#ifdef HIPPI
    setsockopt(data_fd, IPPROTO_TCP, TCP_WINSHIFT, (char *)&winshift,
        sizeof(winshift));
#endif

	/* Allocate user buffers (but first perform sanity check) */
	if (tcpbuf)
		fatal_error("Programming bug in init_put() involving tcpbuf");
	tcpbuf = XtMalloc(XFER_USER_TCPBUFLEN);
	if (filebuf)
		fatal_error("Programming bug in init_put() involving filebuf");
	filebuf = XtMalloc(XFER_FILEBUFLEN);

	xc.data_fd = data_fd;
	xc.src_file_fd = file_fd;
	xc.file_len = file_len;
	xc.file_index = 0;
	return 0;

error:

	close(file_fd);
	if (passive_mode)
		close(data_fd);
	else
		close(sock);
	return errval;
}


/*
 * do_put - Transfer next part of file from local host to remote host.
 *          "xc" is used for input and output.  Returns 0 if file
 *          transfer complete, 1 if file transfer not complete,
 *          -4 for lost sink connection, -7 for a "552" reply (for
 *          UniTree retry mechanism), -6 for stop button pressed,
 *          and -1 for other errors.
 */
do_put()
{
	int tcp_index = 0;
	int nbytes;
	int code;
	char reply[MAXFTPREPLY];
	int i;
	int retval;
	int scode;
	char *src_path;

	if (xc.mode == ASCII) {
		if (xc.file_index < xc.file_len) {
			nbytes = MIN(xc.file_len-xc.file_index, XFER_FILEBUFLEN);
			if (iread(xc.src_file_fd, filebuf, nbytes) != nbytes) {
				src_paths_of_xfer_node(xc.current_node, NULL, &src_path);
				report_perror(src_path);
				XtFree(src_path);
				return abort_put();
			}
			for (i=0; i<nbytes; i++)
				if (filebuf[i] == '\n') {
					if (tcp_index+2 >= XFER_USER_TCPBUFLEN) {
						retval = write_tcp(xc.snk_dirwin->host, xc.data_fd,
							tcpbuf, tcp_index, maxt);
						if (retval != tcp_index) {
							switch (retval) {
							case -3:
								close(xc.src_file_fd);
								close(xc.data_fd);
								XtFree(tcpbuf);
								tcpbuf = NULL;
								XtFree(filebuf);
								filebuf = NULL;
								return -4;
							case -6:
								abort_put();
								return -6;
							case -2:
								report_client_timeout(xc.snk_dirwin->host);
							default:
								return abort_put();
							}
						}
						tcp_index = 0;
					}
					tcpbuf[tcp_index++] = '\r';
					tcpbuf[tcp_index++] = '\n';
				} else {
					if (tcp_index+1 >= XFER_USER_TCPBUFLEN) {
						retval = write_tcp(xc.snk_dirwin->host, xc.data_fd,
							tcpbuf, tcp_index, maxt);
						if (retval != tcp_index) {
							switch (retval) {
							case -3:
								close(xc.src_file_fd);
								close(xc.data_fd);
								XtFree(tcpbuf);
								tcpbuf = NULL;
								XtFree(filebuf);
								filebuf = NULL;
								return -4;
							case -6:
								abort_put();
								return -6;
							case -2:
								report_client_timeout(xc.snk_dirwin->host);
							default:
								return abort_put();
							}
						}
						tcp_index = 0;
					}
					tcpbuf[tcp_index++] = filebuf[i];
				}
			xc.file_index += nbytes;
		}
		if (tcp_index) {
			retval = write_tcp(xc.snk_dirwin->host, xc.data_fd, tcpbuf,
				tcp_index, maxt);
			if (retval != tcp_index) {
				switch (retval) {
				case -3:
					close(xc.src_file_fd);
					close(xc.data_fd);
					XtFree(tcpbuf);
					tcpbuf = NULL;
					XtFree(filebuf);
					filebuf = NULL;
					return -4;
				case -6:
					abort_put();
					return -6;
				case -2:
					report_client_timeout(xc.snk_dirwin->host);
				default:
					return abort_put();
				}
			}
		}
	} else      /* Send binary file to FTP server */
		if (xc.file_index < xc.file_len) {
			nbytes = MIN(xc.file_len-xc.file_index, XFER_FILEBUFLEN);
			if (iread(xc.src_file_fd, filebuf, nbytes) != nbytes) {
				src_paths_of_xfer_node(xc.current_node, NULL, &src_path);
				report_perror(src_path);
				XtFree(src_path);
				return abort_put();
			}
			retval = write_tcp(xc.snk_dirwin->host, xc.data_fd, filebuf,
				nbytes, maxt);
			if (retval != nbytes) {
				switch (retval) {
				case -3:
					close(xc.src_file_fd);
					close(xc.data_fd);
					XtFree(tcpbuf);
					tcpbuf = NULL;
					XtFree(filebuf);
					filebuf = NULL;
					return -4;
				case -6:
					abort_put();
					return -6;
				case -2:
					report_client_timeout(xc.snk_dirwin->host);
				default:
					return abort_put();
				}
			}
			xc.file_index += nbytes;
		}

	/* Have all the bytes been transferred? */
	if (xc.file_index < xc.file_len)
		return 1;

	/* Close source file */
	close(xc.src_file_fd);

	/* Close data connection */
	close(xc.data_fd);

	/* Free up user buffers */
	XtFree(tcpbuf);
	tcpbuf = NULL;
	XtFree(filebuf);
	filebuf = NULL;

	/* Get final response from FTP server */
	scode = get_ftp_reply(xc.snk_dirwin->host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 2:
		return 0;
	case -3:
		return -4;
	case -6:
		abort_ftp_cmd(xc.snk_dirwin->host);
		return -6;
	case -2:
		report_client_timeout(xc.snk_dirwin->host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(xc.snk_dirwin->host, reply);
		if (code == 552)        /* UniTree retry logic */
			return -7;
		else
			return -1;
	}
}


/*
 * abort_put - Abort transfer of file from local host to remote host.
 *             "xc" is used for input and output.  Returns -4 for lost
 *             sink connection, -6 for stop button pushed, else -1.
 */
abort_put()
{
	int code;
	char reply[MAXFTPREPLY];
	int retval;

	/* Pop up abort dialog */
	show_abort_dialog();

	/* Close source file */
	close(xc.src_file_fd);

	/* Close data connection */
	close(xc.data_fd);

	/* Make sure that user buffers have been freed */
	XtFree(tcpbuf);
	tcpbuf = NULL;
	XtFree(filebuf);
	filebuf = NULL;

	/* Get final response from FTP server */
	retval = get_ftp_reply(xc.snk_dirwin->host, reply, MAXFTPREPLY, &code,
		maxt);

	/* Hide abort dialog */
	hide_abort_dialog();

	/* Finish up */
	switch (retval) {
	case -3:
		return -4;
	case -6:
		abort_ftp_cmd(xc.snk_dirwin->host);
		return -6;
	case -2:
		report_client_timeout(xc.snk_dirwin->host);
	default:
		return -1;
	}
}



syntax highlighted by Code2HTML, v. 0.9.1