/***************************************************************************/ /***************************************************************************/ /* */ /* (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 #if defined(_AIX) || defined __QNX__ #include #endif #include #include #include #include #include #include #include "xdir.h" #include "str.h" #define DM 242 /* Telnet commands */ #define IP 244 #define WILL 251 #define WONT 252 #define DO 253 #define DONT 254 #define IAC 255 #define MICRODELTA 100000 int sig_pipe_invoked = False; extern struct st_host_info hinfo[]; extern int diagnostics; extern XtAppContext app; extern Display *display; extern int maxt; /* * write_tcp - Writes "n" bytes from "buf" to the TCP connection on * socket "fd" to "host". If unable to send data during * time interval, "maxtime", -2 is returned. If connection * is lost, -3 is returned. If stop button is pressed, -6 * is returned. Returns -1 for other errors. */ write_tcp(host, fd, buf, n, maxtime) int host; int fd; char *buf; int n; int maxtime; { int total_nbytes = 0; int nbytes; int saved_errno; while (total_nbytes < n) switch (select_on_write(fd, maxtime)) { case 1: if ((nbytes = iwrite(fd, &buf[total_nbytes], n-total_nbytes)) < 0) { saved_errno = errno; report_perror("Writing connection"); if (saved_errno == EPIPE) return -3; else return -1; } else total_nbytes += nbytes; break; case 0: if (total_nbytes) return total_nbytes; else return -1; /* Maybe should change to -2 */ case -1: return -1; case -6: return -6; } return total_nbytes; } /* * select_on_write - Waits for file descriptor fd to be writeable. maxtime * is the number of seconds to wait. Returns values are 1 * for fd is readable, 0 for timeout, -6 for stop button * pressed, and -1 for error. */ select_on_write(fd, maxtime) int fd; int maxtime; { fd_set writefds; fd_set exceptfds; fd_set readfds; struct timeval timeout; int nfds; int xfd; struct timeval finish_time; struct timeval current_time; struct timeval delta; /* Determine timeout time */ gettimeofday(¤t_time, NULL); delta.tv_sec = maxtime; delta.tv_usec = 0; add_timevals(¤t_time, &delta, &finish_time); /* Get file descriptor for display connection socket */ xfd = ConnectionNumber(display); while (1) { if (stop()) return -6; gettimeofday(¤t_time, NULL); if (cmp_timevals(¤t_time, &finish_time) > 0) return 0; /* Timeout */ FD_ZERO(&writefds); FD_SET(fd, &writefds); FD_ZERO(&readfds); FD_SET(xfd, &readfds); FD_ZERO(&exceptfds); FD_SET(fd, &exceptfds); FD_SET(xfd, &exceptfds); timeout.tv_sec = 0; timeout.tv_usec = MICRODELTA; nfds = iselect(max_fds(), &readfds, &writefds, &exceptfds, &timeout); if (nfds > 0) { if (FD_ISSET(xfd, &readfds) || FD_ISSET(xfd, &exceptfds)) XtAppProcessEvent(app, (XtInputMask)XtIMAll); else return 1; } else if (nfds < 0) return -1; } } /* * read_tcp - Reads "n" bytes to "buf" from the TCP connection on socket * "fd" to "host". Returns number of bytes read if no error, * socket "fd". If no data is received during time interval, * -2 is returned. If connection is lost, -3 is returned. If * stop button is pressed, -6 is returned. Returns -1 for other * errors. */ read_tcp(host, fd, buf, n, maxtime) int host; int fd; char *buf; int n; int maxtime; { int total_nbytes = 0; int nbytes; int saved_errno; while (total_nbytes < n) switch (select_on_read(fd, maxtime)) { case 1: if ((nbytes = iread(fd, &buf[total_nbytes], n-total_nbytes)) < 0) { saved_errno = errno; report_perror("Reading connection"); if (saved_errno == ECONNRESET) return -3; else return -1; } else if (nbytes == 0) return total_nbytes; else total_nbytes += nbytes; case 0: if (total_nbytes) return total_nbytes; else return -2; case -1: return -1; case -6: return -6; } return total_nbytes; } /* * select_on_read - Waits for file descriptor fd to be readable. maxtime * is the number of seconds to wait. Returns values are * 1 for fd is readable, 0 for timeout, -6 for stop button * pressed, and -1 for error. */ select_on_read(fd, maxtime) int fd; int maxtime; { fd_set readfds; fd_set exceptfds; struct timeval timeout; int nfds; int xfd; struct timeval finish_time; struct timeval current_time; struct timeval delta; /* Determine timeout time */ gettimeofday(¤t_time, NULL); delta.tv_sec = maxtime; delta.tv_usec = 0; add_timevals(¤t_time, &delta, &finish_time); /* Get file descriptor for display connection socket */ xfd = ConnectionNumber(display); while (1) { if (stop()) return -6; gettimeofday(¤t_time, NULL); if (cmp_timevals(¤t_time, &finish_time) > 0) return 0; /* Timeout */ FD_ZERO(&readfds); FD_SET(fd, &readfds); FD_SET(xfd, &readfds); FD_ZERO(&exceptfds); FD_SET(fd, &exceptfds); FD_SET(xfd, &exceptfds); timeout.tv_sec = 0; timeout.tv_usec = MICRODELTA; nfds = iselect(max_fds(), &readfds, NULL, &exceptfds, &timeout); if (nfds > 0) { if (FD_ISSET(xfd, &readfds) || FD_ISSET(xfd, &exceptfds)) XtAppProcessEvent(app, (XtInputMask)XtIMAll); else return 1; } else if (nfds < 0) return -1; } } /* * get_ftp_reply - Returns in "reply" a string containing the next reply * from the FTP daemon. The function return value is the * first digit of the reply code. "host" is the host id. * Input argument "maxreply" is the maximum length string * to be returned. "code" is the full reply code. * The input argument "maxtime" is the maximum allowable * interval in seconds in which no data is received. * Other return values are -2 for timeout, -3 for * broken connection, -6 for stop button pressed, and * -1 for other failures. */ get_ftp_reply(host, reply, maxreply, code, maxtime) int host; char *reply; int maxreply; int *code; int maxtime; { int fd = hinfo[host].ctrl_fd; int firstline = True; int origcode; int replycode; int nbytes; int lenline; int lenreply; char line[MAXFTPLINE]; static char *vms_timeout_pat = "500 FTP connection terminated"; int len_vms_timeout_pat = strlen(vms_timeout_pat); struct timeval current_time; reply[0] = '\0'; while (1) { if ((replycode = get_ftp_line(host, fd, line, MAXFTPLINE, maxtime)) < 0) return replycode; lenline = strlen(line); lenreply = strlen(reply); nbytes = MIN(maxreply-lenreply-1, lenline); strncat(reply, line, nbytes); if (firstline) { if (replycode == 0) return -1; if (line[3] == '-') { origcode = replycode; firstline = False; } else break; } else if (replycode == origcode && line[3] == ' ') break; } if (diagnostics >= VERBOSE) report_ftp_reply(host, reply); /* Check for server timeout */ if (hinfo[host].system == SYS_VMS) { if ((replycode == 421) || !strncmp(line, vms_timeout_pat, len_vms_timeout_pat)) { write_log("*** FTP server on remote host timed out.\n"); return -3; } } else if (replycode == 421) { write_log("*** FTP server on remote host timed out.\n"); return -3; } /* Remember when last reply from server was received */ gettimeofday(¤t_time, NULL); hinfo[host].last_reply_time = current_time.tv_sec; /* Return reply code */ *code = replycode; return replycode/100; } /* * get_ftp_line - Returns in "line" a string containing the next line * from the FTP daemon. The function return value is the * reply code. "fd" is the file descriptor of the socket * used for the control connection. The input argument * "maxline" is the maximum length string to be returned. * The input argument "maxtime" is the maximum allowable * interval in seconds in which no data is received. * "host" is the host to get the FTP response from. * The return value is for lines containing no reply code. * Other return values are -2 for timeout, -3 for broken * connection, -6 for stop button pressed, and -1 for other * failures. */ get_ftp_line(host, fd, line, maxline, maxtime) int host; int fd; char *line; int maxline; int maxtime; { int cnt; unsigned char ch; char buf[3]; int code = 0; int indx = 0; int retval; while (1) { if ((cnt = read_tcp(host, fd, (char *)&ch, 1, maxtime)) < 0) return cnt; else if (cnt == 0) return -3; if (ch == IAC) { if ((cnt = read_tcp(host, fd, (char *)&ch, 1, maxtime)) < 0) return cnt; if (cnt == 0) return -3; switch (ch) { case DO: case DONT: if ((cnt = read_tcp(host, fd, (char *)&ch, 1, maxtime)) < 0) return cnt; if (cnt == 0) return -3; buf[0] = IAC; buf[1] = DONT; buf[2] = ch; if ((retval = write_tcp(host, fd, buf, 3, maxtime)) != 3) return retval; break; case WILL: case WONT: if ((cnt = read_tcp(host, fd, (char *)&ch, 1, maxtime)) < 0) return cnt; if (cnt == 0) return -3; buf[0] = IAC; buf[1] = WONT; buf[2] = ch; if ((retval = write_tcp(host, fd, buf, 3, maxtime)) != 3) return retval; } continue; } if (ch != '\r' && indx < maxline-1) line[indx++] = ch; if (ch == '\n') { if (indx >= 4 && (line[3] == ' ' || line[3] == '-') && isdigit(line[0]) && isdigit(line[1]) && isdigit(line[2])) code = (line[0]-'0')*100+(line[1]-'0')*10+(line[2]-'0'); line[indx] = '\0'; return code; } } } /* * send_ftp_cmd - Sends command "cmd" to the FTP server. "host" is the * host id. "\r\n" is appended to cmd before it is sent. * "maxtime" is the maximum allowable interval in seconds * in which some data must be sent. Returns 0 for success, * -2 for timeout, -3 for broken connection, -6 for stop * button pressed, and -1 for other errors. */ send_ftp_cmd(host, cmd) int host; char *cmd; { int fd = hinfo[host].ctrl_fd; int len = strlen(cmd); int retval; char *buf; if (diagnostics == DEBUG) report_ftp_cmd(host,cmd); buf = XtMalloc(len+3); strcpy(buf, cmd); strcat(buf, "\r\n"); retval = write_tcp(host, fd, buf, len+2, maxt); bzero(buf, (int)strlen(buf)); XtFree(buf); if (retval != len+2) return retval; else return 0; } /* * sig_pipe - Will be invoked if trying to write on a broken connection. */ void sig_pipe() { sig_pipe_invoked = True; } /* * urgent_abort - Sends an urgent abort request to the FTP server * connected via the TCP connection for "host". * Returns 0 if successful, -3 for broken connection, * -6 for stop button pressed, and -1 for other errors. * (If connection is lost and stop button is pushed, * -6 is returned.) If stop button was pressed, this * routine does not return prematurely. This routine * does not try to receive a reply from the FTP server. */ urgent_abort(host) int host; { int fd = hinfo[host].ctrl_fd; char buf[2]; int retval; int stop_was_pressed = False; if (diagnostics == DEBUG) report_ftp_cmd(host, "Urgent Abort"); /* Send IAC, IP */ buf[0] = IAC; buf[1] = IP; retval = write_tcp(host, fd, buf, 2, 5); if (retval == -6) { stop_was_pressed = True; retval = write_tcp(host, fd, buf, 2, 5); } if (retval != 2) { if (stop_was_pressed) return -6; else return retval; } /* Send IAC, DM in urgent mode */ buf[0] = IAC; if (isend(fd, buf, 1, MSG_OOB) != 1) return -1; buf[0] = DM; /* Send ABOR command */ retval = write_tcp(host, fd, buf, 1, 5); if (retval == -6) { stop_was_pressed = True; retval = write_tcp(host, fd, buf, 1, 5); } if (retval != 1) { if (stop_was_pressed) return -6; else return retval; } retval = write_tcp(host, fd, "ABOR\r\n", 6, 5); if (retval == -6) { stop_was_pressed = True; retval = write_tcp(host, fd, "ABOR\r\n", 6, 5); } if (retval != 6) { if (stop_was_pressed) return -6; else return retval; } if (stop_was_pressed) return -6; else return 0; } /* * abort_ftp_cmd - Send urgent abort to specified remote host, and absorb * any responses. */ void abort_ftp_cmd(host) int host; { char reply[MAXFTPREPLY]; int code; int scode; /* Pop up abort dialog */ show_abort_dialog(); /* Send urgent abort */ urgent_abort(host); /* Absorb first reply */ scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, 15); /* If no response, send NOOP command to try to jostle server */ if (scode < 0) { send_ftp_cmd(host, "NOOP"); scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, 15); } /* Absorb remaining replies */ while (scode > 0) scode = get_ftp_reply(host, reply, MAXFTPREPLY, &code, 5); /* Want to check for lost host before issuing next command */ hinfo[host].last_reply_time = 0; /* Get rid of abort dialog */ hide_abort_dialog(); } /* * rest_stop - A replacement for sleep() that returns in "seconds" seconds. * Returns values are 0 for time has elapsed, -6 for stop * button pressed, and -1 for error. X events are processed. * Call rest_stop() instead of sleep() when the stop button * is enabled. */ rest_stop(seconds) int seconds; { fd_set readfds; fd_set exceptfds; struct timeval timeout; int nfds; int xfd; struct timeval finish_time; struct timeval current_time; struct timeval delta; /* Determine timeout time */ gettimeofday(¤t_time, NULL); delta.tv_sec = seconds; delta.tv_usec = 0; add_timevals(¤t_time, &delta, &finish_time); /* Get file descriptor for display connection socket */ xfd = ConnectionNumber(display); while (1) { if (stop()) return -6; gettimeofday(¤t_time, NULL); if (cmp_timevals(¤t_time, &finish_time) > 0) return 0; /* Time is up */ FD_ZERO(&readfds); FD_SET(xfd, &readfds); FD_ZERO(&exceptfds); FD_SET(xfd, &exceptfds); timeout.tv_sec = 0; timeout.tv_usec = MICRODELTA; nfds = iselect(max_fds(), &readfds, NULL, &exceptfds, &timeout); if (nfds > 0 && (FD_ISSET(xfd, &readfds) || FD_ISSET(xfd, &exceptfds))) XtAppProcessEvent(app, (XtInputMask)XtIMAll); else return -1; } }