/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (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 <unistd.h>
#include <Xm/Xm.h>
#include "xdir.h"
#include "xfer.h"
#include "str.h"

static char *tcpbuf = NULL;

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

long extract_file_len();
char *create_unique_name();


/*
 * init_get - Initialize transfer of file from remote host to local host.
 *            "xc" is used for input and output.  Returns 0 if successful,
 *            -3 for lost source connection, -6 for stop button pressed,
 *            and -1 for other errors.
 */
init_get()
{
	char *src_dir;
	char *src_file;
	char snk_path[MAXPATHLEN+1];
	char *tmp_snk_path;
	char *q;
	int data_fd;
	int code;
	char reply[MAXFTPREPLY];
	int sock;
	FILE *fp;
	char cmd[MAXPATHLEN+5];
	int retval;
	long file_len;
	int scode;
	int errval;
	int sys_tcpbuflen = XFER_SYS_TCPBUFLEN;
	char *src_path;
#ifdef HIPPI
	int winshift = XFER_TCPWINSHIFT;
#endif

	/* Get path of sink file */
	snk_paths_of_xfer_node(xc.current_node, NULL, &tmp_snk_path);
	strcpy(snk_path, tmp_snk_path);
	XtFree(tmp_snk_path);
	if ((xc.src_host_system == SYS_VMS) && 
			(!preserve_vms_ver_nums || (xc.operation == VIEW))) {
		if ((q = strrchr(snk_path, ';')))
			*q = '\0';
	}

	/* Get unique file name if requested */
	if (store_unique) {
		if ((tmp_snk_path = create_unique_name(snk_path)) == NULL)
			return -1;
		strcpy(snk_path, tmp_snk_path);
	}

	/* Verify that a writable sink file can be created */
	if (!can_create_sinkfile(snk_path))
		return -1;

    /* "cd" into source directory */
	src_paths_of_xfer_node(xc.current_node, NULL, &src_path);
    parse_path(xc.src_host_system, src_path, &src_dir, &src_file);
	XtFree(src_path);
    retval = remote_cd(xc.src_dirwin->host, src_dir, True);
    XtFree(src_dir);
    switch (retval) {
    case 0:
        break;
    case -3:
	case -6:
        XtFree(src_file);
        return retval;
    default:
        XtFree(src_file);
        return -1;
    }

	/* Initialize data connection */
	if (passive_mode) {
		if ((data_fd = passive_data_conn(xc.src_dirwin->host)) < 0)
			return data_fd;
	} else if ((sock = init_data_conn(xc.src_dirwin->host)) < 0) {
		XtFree(src_file);
		return sock;
	}

	/* Send RETR command to FTP server */
	sprintf(cmd, "RETR %s", src_file);
	XtFree(src_file);
	if ((retval = send_ftp_cmd(xc.src_dirwin->host, cmd)) == -2) {
		report_client_timeout(xc.src_dirwin->host);
		errval = -1;
		goto error;
	} else if (retval < 0) {
		errval = retval;
		goto error;
	}

	/* Get initial response from FTP server */
	scode = get_ftp_reply(xc.src_dirwin->host, reply, MAXFTPREPLY, &code, maxt);
	switch (scode) {
	case 1:
		file_len = extract_file_len(reply);
		break;
    case 4:
		if (diagnostics < VERBOSE)
			report_ftp_reply(xc.src_dirwin->host, reply);
		errval = -1;
		goto error;
	case -6:
		abort_ftp_cmd(xc.src_dirwin->host);
	case -3:
		errval = scode;
		goto error;
	case -2:
		report_client_timeout(xc.src_dirwin->host);
	default:
		if (scode > 0 && diagnostics < VERBOSE)
			report_ftp_reply(xc.src_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)
			return -1;
	}

	/* Attempt to increase TCP receive buffer size to improve performance */
#if defined(SOL_SOCKET) && defined(SO_RCVBUF)
	setsockopt(data_fd, SOL_SOCKET, SO_RCVBUF, (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

	/* Create sink file */
	if ((fp = fopen(snk_path, "w")) == NULL) {
		report_perror(snk_path);
		return abort_get();
	}

    /* Allocate user TCP buffer (but first perform sanity check) */
    if (tcpbuf)
        fatal_error("Programming bug in init_get() involving tcpbuf");
    tcpbuf = XtMalloc(XFER_USER_TCPBUFLEN);

    xc.data_fd = data_fd;
	xc.snk_fp = fp;
	xc.file_index = 0;
    xc.file_len = file_len;
	return 0;

error:

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


/*
 * do_get - Transfer next part of file from remote host to local host.
 *          "xc" is used for input and output.  Returns 0 if file
 *          transfer complete, 1 if file transfer not complete,
 *          -3 for lost source connection, -6 for stop button pressed,
 *          and -1 for other errors.
 */
do_get()
{
	char ch;
	int nbytes;
	int code;
	char reply[MAXFTPREPLY];
	int len_tcpbuf = 0;
	int index_tcpbuf = 0;
	int scode;
	char *snk_path;

	if (xc.mode == ASCII) {    /* Receive ASCII file from FTP server */
		switch (nbytes = read_tcp(xc.src_dirwin->host, xc.data_fd, tcpbuf,
			XFER_USER_TCPBUFLEN, DELTA)) {
		case -2:
			return 1;
		case -1:
		case -3:
			return abort_get();
		case 0:
			goto done;
		case -6:
			abort_get();
			return -6;
		}
		len_tcpbuf = nbytes;
		while (index_tcpbuf < len_tcpbuf) {
			ch = tcpbuf[index_tcpbuf++];
			if (ch == '\r') {
				if (index_tcpbuf >= len_tcpbuf) {
					do {
						nbytes = read_tcp(xc.src_dirwin->host, xc.data_fd,
							tcpbuf, 1, DELTA);
					} while (nbytes == -2);
					switch (nbytes) {
					case 1:
						index_tcpbuf = 0;
						len_tcpbuf = 1;
						break;
					case 0:
						putc('\r', xc.snk_fp);
						xc.file_index++;
						goto done;
					case -6:
						abort_get();
						return -6;
					default:
						fclose(xc.snk_fp);
						close(xc.data_fd);
						return abort_get();
					}
				}
				ch = tcpbuf[index_tcpbuf];
				if (ch != '\n') {
					putc('\r', xc.snk_fp);
					xc.file_index++;
				}
			} else {
				putc(ch, xc.snk_fp);
				xc.file_index++;
			}
		}
	} else               /* Receive binary file from FTP server */
		switch (nbytes = read_tcp(xc.src_dirwin->host, xc.data_fd, tcpbuf,
			XFER_USER_TCPBUFLEN, DELTA)) {
		case -2:
			return 1;
		case -1:
		case -3:
			return abort_get();
		case 0:
			goto done;
		case -6:
			abort_get();
			return -6;
		default:
			if (iwrite(fileno(xc.snk_fp), tcpbuf, nbytes) != nbytes) {
				snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
				report_perror(snk_path);
				XtFree(snk_path);
				return abort_get();
			}
			xc.file_index += nbytes;
		}

	return 1;

done:

	/* Close sink file */
	fclose(xc.snk_fp);

    /* Free up user TCP buffer */
    XtFree(tcpbuf);
    tcpbuf = NULL;

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


/*
 * abort_get - Abort transfer of file from remote host to local host.
 *             "xc" is used for input and output.  Returns -3 for lost
 *             source connection, -6 for stop button pressed,  else -1.
 *             (If connection is lost and stop button is pressed, -6 is
 *             returned.)
 */
abort_get()
{
	int code;
	char reply[MAXFTPREPLY];
	int retval;
	int stop_was_pressed = False;
	int nsecs;

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

	/* Close sink file */
	fclose(xc.snk_fp);

	/* Send urgent abort to server */
	if (urgent_abort(xc.src_dirwin->host) == -6)
		stop_was_pressed = True;

	/* Make sure user TCP buffer is allocated */
	if (!tcpbuf)
		tcpbuf = XtMalloc(XFER_USER_TCPBUFLEN);

	/* Clear out all incoming data */
	if ((xc.src_host_server != SERVER_UNIX_UNITREE) &&
			(xc.src_host_server != SERVER_UNIX_NSL_UNITREE)) {
		while ((retval = read_tcp(xc.src_dirwin->host, xc.data_fd, tcpbuf,
			XFER_USER_TCPBUFLEN, 10)) > 0);
		if (retval == -6)
			stop_was_pressed = True;
	}
	close(xc.data_fd);

	/* Get responses from server */
	nsecs = 15;
	retval = 1;
	while (retval > 0) {
		retval = get_ftp_reply(xc.src_dirwin->host, reply, MAXFTPREPLY, &code,
			nsecs);
		nsecs = 5;
	}
	if (retval == -6)
		stop_was_pressed = True;

    /* Free up user TCP buffer */
    XtFree(tcpbuf);
    tcpbuf = NULL;

	/* Want to check for lost host before issuing next command */
	hinfo[xc.src_dirwin->host].last_reply_time = 0;

	/* Hide abort dialog */
	hide_abort_dialog();

	/* Determine return value */
	if (stop_was_pressed)
		return -6;
	else if (retval == -3)
		return -3;
	else if (retval == -2)
		report_client_timeout(xc.src_dirwin->host);
	return -1;
}



syntax highlighted by Code2HTML, v. 0.9.1