/* ftp.c */ /* $RCSfile: ftp.c,v $ * $Revision: 14020.12 $ * $Date: 93/07/09 11:30:28 $ */ #include "sys.h" #include #include #include #ifndef AIX /* AIX-2.2.1 declares utimbuf in unistd.h */ #ifdef NO_UTIMEH struct utimbuf {time_t actime; time_t modtime;}; #else # include #endif #endif /*AIX*/ #ifdef SYSLOG # include #endif /* You may need this for declarations of fd_set, etc. */ #ifdef SYSSELECTH # include #else #ifdef STRICT_PROTOS #ifndef Select extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); #endif #endif #endif #include #include #include #include #include #include #ifdef NET_ERRNO_H # include #endif #include #include #include #include #include "util.h" #include "ftp.h" #include "cmds.h" #include "main.h" #include "ftprc.h" #include "getpass.h" #include "defaults.h" #include "copyright.h" /* ftp.c globals */ struct sockaddr_in hisctladdr; struct sockaddr_in data_addr; int data = -1; int abrtflag = 0; struct sockaddr_in myctladdr; FILE *cin = NULL, *cout = NULL; char *reply_string = NULL; jmp_buf sendabort, recvabort; int progress_meter = dPROGRESS; int cur_progress_meter; int sendport = -1; /* use PORT cmd for each data connection */ int code; /* return/reply code for ftp command */ string indataline; int cpend; /* flag: if != 0, then pending server reply */ char *xferbuf; /* buffer for local and remote I/O */ size_t xferbufsize; /* size in bytes, of the transfer buffer. */ long next_report; long bytes; long now_sec; long file_size; struct timeval start, stop; int buffer_only = 0; /* True if reading into redir line * buffer only (not echoing to * stdout). */ /* ftp.c externs */ extern FILE *logf; extern string anon_password; extern longstring cwd, lcwd; extern Hostname hostname; extern int verbose, debug, macnum, margc; extern int curtype, creating, toatty; extern int options, activemcmd, paging; extern int ansi_escapes, logged_in, macnum; extern char *line, *margv[]; extern char *tcap_normal, *tcap_boldface; extern char *tcap_underline, *tcap_reverse; extern struct userinfo uinfo; extern struct macel macros[]; extern struct lslist *lshead, *lstail; extern int is_ls; #ifdef GATEWAY extern string gateway; extern string gate_login; #endif #ifdef TERM_FTP #include extern int lcompression, rcompression; int compress_toggle = 0; extern int compress_toggle; #endif #if defined(TERM_FTP) && (defined(BSD) || defined(_POSIX_SOURCE)) FILE *safeopen(int s, char *lmode){ FILE *file; setreuid(geteuid(),getuid()); setregid(getegid(),getgid()); file=fdopen(s, lmode); setreuid(geteuid(),getuid()); setregid(getegid(),getgid()); return(file); } #else #define safeopen fdopen #endif #ifdef TERM_FTP int hookup(char *host, unsigned int port) { int s; int result = -1; lcompression = rcompression = compress_toggle; if ((s = connect_server(0)) < 0) { perror("ftp: connect to term server"); goto ret; } send_command(s, C_PORT, 0, "%s:%d", host, ntohs(port)); send_command(s, C_DUMB, 1, 0); cin = fdopen(s, "r"); cout = fdopen(dup(s), "w"); if (cin == NULL || cout == NULL) { fprintf(stderr, "ftp: safeopen failed.\n"); goto bad; } Strncpy(hostname, host); if (verbose) printf("Connected to %s.\n", hostname); if (getreply(0) > 2) { /* read startup message from server */ goto bad; } result = 0; goto ret; bad: (void) close(s); if (cin != NULL) (void) fclose(cin); if (cout != NULL) (void) fclose(cout); cin = cout = NULL; ret: return (result); } /* hookup_term */ #else /* Not TERM_FTP, i.e. most of us. */ int hookup(char *host, unsigned int port) { register struct hostent *hp = 0; int s, len, hErr = -1; string errstr; bzero((char *)&hisctladdr, sizeof (hisctladdr)); #ifdef BAD_INETADDR hisctladdr.sin_addr = inet_addr(host); #else hisctladdr.sin_addr.s_addr = inet_addr(host); #endif if (hisctladdr.sin_addr.s_addr != -1) { hisctladdr.sin_family = AF_INET; (void) Strncpy(hostname, host); } else { hp = gethostbyname(host); if (hp == NULL) { #ifdef HERROR extern int h_errno; if (h_errno == HOST_NOT_FOUND) (void) printf("%s: unknown host\n", host); else (void) fprintf(stderr, "%s: gethostbyname herror (%d): ", host, h_errno); herror(NULL); #else (void) printf("%s: unknown host\n", host); #endif goto done; } hisctladdr.sin_family = hp->h_addrtype; bcopy(hp->h_addr_list[0], (caddr_t)&hisctladdr.sin_addr, hp->h_length); (void) Strncpy(hostname, hp->h_name); } s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); if (s < 0) { PERROR("hookup", "socket"); goto done; } hisctladdr.sin_port = port; #ifdef SOCKS while (Rconnect(s, (struct sockaddr *) &hisctladdr, (int) sizeof (hisctladdr)) < 0) { #else while (Connect(s, &hisctladdr, sizeof (hisctladdr)) < 0) { #endif if (hp && hp->h_addr_list[1]) { (void) sprintf(errstr, "connect error to address %s", inet_ntoa(hisctladdr.sin_addr)); PERROR("hookup", errstr); hp->h_addr_list++; bcopy(hp->h_addr_list[0], (caddr_t)&hisctladdr.sin_addr, hp->h_length); (void) fprintf(stdout, "Trying %s...\n", inet_ntoa(hisctladdr.sin_addr)); (void) close(s); s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); if (s < 0) { PERROR("hookup", "socket"); goto done; } continue; } PERROR("hookup", "connect"); switch (errno) { case ENETDOWN: case ENETUNREACH: case ECONNABORTED: case ETIMEDOUT: case ECONNREFUSED: case EHOSTDOWN: hErr = -2; /* we can re-try later. */ } goto bad; } len = sizeof (myctladdr); if (Getsockname(s, (char *)&myctladdr, &len) < 0) { PERROR("hookup", "getsockname"); goto bad; } cin = safeopen(s, "r"); cout = safeopen(dup(s), "w"); if (cin == NULL || cout == NULL) { (void) fprintf(stderr, "ftp: safeopen failed.\n"); close_streams(0); goto bad; } if (IS_VVERBOSE) (void) printf("Connected to %s.\n", hostname); if (getreply(0) > 2) { /* read startup message from server */ close_streams(0); if (code == 421) hErr = -2; /* We can try again later. */ goto bad; } #ifdef SO_OOBINLINE { int on = 1; if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0 && debug) { PERROR("hookup", "setsockopt"); } } #endif /* SO_OOBINLINE */ hErr = 0; goto done; bad: (void) close(s); if (cin != NULL) (void) fclose(cin); if (cout != NULL) (void) fclose(cout); done: return (hErr); } /* hookup */ #endif /* TERM_FTP */ /* This registers the user's username, password, and account with the remote * host which validates it. If we get on, we also do some other things, like * enter a log entry and execute the startup macro. */ int Login(char *userNamePtr, char *passWordPtr, char *accountPtr, int doInit) { string userName; string str; int n; int sentAcct = 0; int userWasPrompted = 0; int result = CMDERR; time_t now; if (userNamePtr == NULL) { /* Prompt for a username. */ (void) sprintf(str, "Login Name (%s): ", uinfo.username); ++userWasPrompted; if (Gets(str, userName, sizeof(userName)) == NULL) goto done; else if (userName[0]) { /* User didn't just hit return. */ userNamePtr = userName; } else { /* * User can hit return if he wants to enter his username * automatically. */ if (*uinfo.username != '\0') userNamePtr = uinfo.username; else goto done; } } #ifdef GATEWAY if (*gateway) (void) sprintf(str, "USER %s@%s", (*gate_login ? gate_login : dGATEWAY_LOGIN), hostname); else #endif (void) sprintf(str, "USER %s", userNamePtr); /* Send the user name. */ n = command(str); if (n == CONTINUE) { if (passWordPtr == NULL) { if (((strcmp("anonymous", userName) == 0) || (strcmp("ftp", userName) == 0)) && (*anon_password != '\0')) passWordPtr = anon_password; else { /* Prompt for a password. */ ++userWasPrompted; passWordPtr = Getpass("Password:"); } } /* The remote site is requesting us to send the password now. */ (void) sprintf(str, "PASS %s", passWordPtr); n = command(str); if (n == CONTINUE) { /* The remote site is requesting us to send the account now. */ if (accountPtr == NULL) { /* Prompt for a username. */ (void) sprintf(str, "ACCT %s", Getpass("Account:")); ++userWasPrompted; } else { (void) sprintf(str, "ACCT %s", accountPtr); } ++sentAcct; /* Keep track that we've sent the account already. */ n = command(str); } } if (n != COMPLETE) { (void) printf("Login failed.\n"); goto done; } /* If you specified an account, and the remote-host didn't request it * (maybe it's optional), we will send the account information. */ if (!sentAcct && accountPtr != NULL) { (void) sprintf(str, "ACCT %s", accountPtr); (void) command(str); } /* See if remote host dropped connection. Some sites will let you log * in anonymously, only to tell you that they already have too many * anon users, then drop you. We do a no-op here to see if they've * ditched us. */ n = quiet_command("NOOP"); if (n == TRANSIENT) goto done; #ifdef SYSLOG syslog(LOG_INFO, "%s connected to %s as %s.", uinfo.username, hostname, userNamePtr); #endif /* Save which sites we opened to the user's logfile. */ if (logf != NULL) { (void) time(&now); (void) fprintf(logf, "%s opened at %s", hostname, ctime(&now)); } /* Let the user know we are logged in, unless he was prompted for some * information already. */ if (!userWasPrompted) if (NOT_VQUIET) (void) printf("Logged into %s.\n", hostname); if ((doInit) && (macnum > 0)) { /* Run the startup macro, if any. */ /* If macnum is non-zero, the init macro was defined from * ruserpass. It would be the only macro defined at this * point. */ (void) strcpy(line, "$init"); makeargv(); (void) domacro(margc, margv); } _cd(NULL); /* Init cwd variable. */ result = NOERR; logged_in = 1; done: return (result); } /* Login */ /*ARGSUSED*/ void cmdabort SIG_PARAMS { (void) printf("\n"); (void) fflush(stdout); abrtflag++; } /* cmdabort */ int CommandWithFlags(char *cmd, int flags) { int r; Sig_t oldintr; string str; if (cmd == NULL) { /* Should never happen; bug if it does. */ PERROR("command", "NULL command"); return (-1); } abrtflag = 0; if (debug) { if (strncmp(cmd, "PASS", (size_t)4) == 0) dbprintf("cmd: \"PASS ********\"\n"); else dbprintf("cmd: \"%s\" (length %d)\n", cmd, (int) strlen(cmd)); } if (cout == NULL) { (void) sprintf(str, "%s: No control connection for command", cmd); PERROR("command", str); return (0); } oldintr = Signal(SIGINT, /* cmdabort */ SIG_IGN); /* Used to have BROKEN_MEMCPY tested here. */ if (cout != NULL) (void) fprintf(cout, "%s\r\n", cmd); (void) fflush(cout); cpend = 1; r = (flags == WAIT_FOR_REPLY) ? (getreply(strcmp(cmd, "QUIT") == 0)) : PRELIM; if (abrtflag && oldintr != SIG_IGN && oldintr != NULL) (*oldintr)(0); (void) Signal(SIGINT, oldintr); return(r); } /* CommandWithFlags */ /* This stub runs 'CommandWithFlags' above, telling it to wait for * reply after the command is sent. */ int command(char *cmd) { return (CommandWithFlags(cmd, WAIT_FOR_REPLY)); } /* command */ /* This stub runs 'CommandWithFlags' above, telling it to NOT wait for * reply after the command is sent. */ int command_noreply(char *cmd) { return(CommandWithFlags(cmd, DONT_WAIT_FOR_REPLY)); } /* command */ int quiet_command(char *cmd) { register int oldverbose, result; oldverbose = verbose; verbose = debug ? V_VERBOSE : V_QUIET; result = command(cmd); verbose = oldverbose; return (result); } /* quiet_command */ int verbose_command(char *cmd) { register int oldverbose, result; oldverbose = verbose; verbose = V_VERBOSE; result = command(cmd); verbose = oldverbose; return (result); } /* quiet_command */ int getreply(int expecteof) { register int c, n = 0; int dig; char *cp, *end, *dp; int thiscode, originalcode = 0, continuation = 0; Sig_t oldintr; if (cin == NULL) return (-1); /* oldintr = Signal(SIGINT, SIG_IGN); */ oldintr = Signal(SIGINT, cmdabort); end = reply_string + RECEIVEDLINELEN - 2; for (;abrtflag==0;) { dig = n = thiscode = code = 0; cp = reply_string; for (;abrtflag==0;) { c = fgetc(cin); if (c == IAC) { /* handle telnet commands */ switch (c = fgetc(cin)) { case WILL: case WONT: c = fgetc(cin); (void) fprintf(cout, "%c%c%c",IAC,DONT,c); (void) fflush(cout); break; case DO: case DONT: c = fgetc(cin); (void) fprintf(cout, "%c%c%c",IAC,WONT,c); (void) fflush(cout); break; default: break; } continue; } dig++; if (c == EOF) { if (expecteof) { (void) Signal(SIGINT, oldintr); code = 221; return (0); } lostpeer(0); if (NOT_VQUIET) { (void) printf("421 Service not available, remote server has closed connection\n"); (void) fflush(stdout); } code = 421; return(4); } if (cp < end && c != '\r') *cp++ = c; if (c == '\n') break; if (dig < 4 && isdigit(c)) code = thiscode = code * 10 + (c - '0'); else if (dig == 4 && c == '-') { if (continuation) code = 0; continuation++; } if (n == 0) n = c; } /* end for(;;) #2 */ *cp = '\0'; switch (verbose) { case V_QUIET: /* Don't print anything. */ break; case V_ERRS: if (n == '5') { dp = reply_string; goto stripCode; } break; case V_IMPLICITCD: case V_TERSE: dp = NULL; if (n == '5' && verbose == V_TERSE) dp = reply_string; else { switch (thiscode) { case 230: case 214: case 331: case 332: case 421: /* For ftp.apple.com, etc. */ dp = reply_string; break; case 220: /* * Skip the foo FTP server ready line. */ if (strstr(reply_string, "ready.") == NULL) dp = reply_string; break; case 250: /* * Print 250 lines if they aren't * "250 CWD command successful." */ if (strncmp(reply_string + 4, "CWD ", (size_t) 4)) dp = reply_string; } } if (dp == NULL) break; stripCode: /* Try to strip out the code numbers, etc. */ if (isdigit(*dp++) && isdigit(*dp++) && isdigit(*dp++)) { if (*dp == ' ' || *dp == '-') { dp++; if (*dp == ' ') dp++; } else dp = reply_string; } else { int spaces; dp = reply_string; for (spaces = 0; spaces < 4; ++spaces) if (dp[spaces] != ' ') break; if (spaces == 4) dp += spaces; } goto printLine; case V_VERBOSE: dp = reply_string; printLine: (void) fputs(dp, stdout); } /* end switch */ if (continuation && code != originalcode) { if (originalcode == 0) originalcode = code; continue; } if (n != '1') cpend = 0; (void) Signal(SIGINT,oldintr); if (code == 421 || originalcode == 421) lostpeer(0); if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN && oldintr) (*oldintr)(0); break; } /* end for(;;) #1 */ return (n - '0'); } /* getreply */ static int empty(struct fd_set *mask, int sec) { struct timeval t; t.tv_sec = (long) sec; t.tv_usec = 0; return(Select(32, mask, NULL, NULL, &t)); } /* empty */ static void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0) { tdiff->tv_sec = t1->tv_sec - t0->tv_sec; tdiff->tv_usec = t1->tv_usec - t0->tv_usec; if (tdiff->tv_usec < 0) tdiff->tv_sec--, tdiff->tv_usec += 1000000; } /* tvsub */ /* Variables private to progress_report code. */ static int barlen; static long last_dot; static int dots; int start_progress(int sending, char *local) { long s; char spec[64]; cur_progress_meter = toatty ? progress_meter : 0; if ((cur_progress_meter > pr_last) || (cur_progress_meter < 0)) cur_progress_meter = dPROGRESS; if ((file_size <= 0) && ((cur_progress_meter == pr_percent) || (cur_progress_meter == pr_philbar) || (cur_progress_meter == pr_last))) cur_progress_meter = pr_kbytes; if (!ansi_escapes && (cur_progress_meter == pr_philbar)) cur_progress_meter = pr_dots; (void) Gettimeofday(&start); now_sec = start.tv_sec; switch (cur_progress_meter) { case pr_none: break; case pr_percent: (void) printf("%s: ", local); goto zz; case pr_kbytes: (void) printf("%s: ", local); goto zz; case pr_philbar: (void) printf("%s%s file: %s %s\n", tcap_boldface, sending ? "Sending" : "Receiving", local, tcap_normal ); barlen = 52; for (s = file_size; s > 0; s /= 10L) barlen--; (void) sprintf(spec, " 0 %%%ds %%ld bytes. ETA: --:--\r", barlen); (void) printf(spec, " ", file_size); goto zz; case pr_dots: last_dot = (file_size / 10) + 1; dots = 0; (void) printf("%s: ", local); zz: (void) fflush(stdout); Echo(stdin, 0); } /* end switch */ return (cur_progress_meter); } /* start_progress */ int progress_report(int finish_up) { int size; int perc; float frac; char spec[64]; float secsElap; int secsLeft, minLeft; struct timeval td; next_report += xferbufsize; (void) Gettimeofday(&stop); if ((stop.tv_sec > now_sec) || (finish_up && file_size)) { switch (cur_progress_meter) { case pr_none: break; case pr_percent: perc = (int) (100.0 * (float)bytes / (float)file_size); if (perc > 100) perc = 100; else if (perc < 0) perc = 0; (void) printf("\b\b\b\b%3d%%", perc); (void) fflush(stdout); break; case pr_philbar: frac = (float)bytes / (float)file_size; if (frac > 1.0) frac = 1.0; else if (frac < 0.0) frac = 0.0; size = (int) ((float)barlen * frac); (void) sprintf(spec, "%%3d%%%% 0 %%s%%%ds%%s%%%ds %%ld bytes. ETA:%%3d:%%02d\r", size, barlen - size); perc = (long) (100.0 * frac); tvsub(&td, &stop, &start); secsElap = td.tv_sec + (td.tv_usec / 1000000.0); secsLeft = (int) ((float)file_size / ((float)bytes/secsElap) - secsElap + 0.5); minLeft = secsLeft / 60; secsLeft = secsLeft - (minLeft * 60); (void) printf( spec, perc, tcap_reverse, "", tcap_normal, "", file_size, minLeft, secsLeft ); (void) fflush(stdout); break; case pr_kbytes: if ((bytes / 1024) > 0) { (void) printf("\b\b\b\b\b\b%5ldK", bytes / 1024); (void) fflush(stdout); } break; case pr_dots: if (bytes > last_dot) { (void) fputc('.', stdout); (void) fflush(stdout); last_dot += (file_size / 10) + 1; dots++; } } /* end switch */ now_sec = stop.tv_sec; } /* end if we updated */ return (UserLoggedIn()); } /* progress_report */ void end_progress(char *direction, char *local, char *remote) { struct timeval td; float s, bs = 0.0; str32 bsstr; int doLastReport; int receiving; longstring fullRemote, fullLocal; doLastReport = ((UserLoggedIn()) && (cur_progress_meter != pr_none) && (NOT_VQUIET) && (bytes > 0)); receiving = (direction[0] == 'r'); switch(FileType(local)) { case IS_FILE: (void) Strncpy(fullLocal, lcwd); (void) Strncat(fullLocal, "/"); (void) Strncat(fullLocal, local); break; case IS_PIPE: doLastReport = 0; local = Strncpy(fullLocal, local); break; case IS_STREAM: default: doLastReport = 0; local = Strncpy(fullLocal, receiving ? "stdout" : "stdin"); } if (doLastReport) (void) progress_report(1); /* tell progress proc to cleanup. */ tvsub(&td, &stop, &start); s = td.tv_sec + (td.tv_usec / 1000000.0); bsstr[0] = '\0'; if (s != 0.0) { bs = (float)bytes / s; if (bs > 1024.0) sprintf(bsstr, "%.2f K/s", bs / 1024.0); else sprintf(bsstr, "%.2f Bytes/sec", bs); } if (doLastReport) switch(cur_progress_meter) { case pr_none: zz: (void) printf("%s: %ld bytes %s in %.2f seconds, %s.\n", local, bytes, direction, s, bsstr); break; case pr_kbytes: case pr_percent: (void) printf("%s%ld bytes %s in %.2f seconds, %s.\n", cur_progress_meter == pr_kbytes ? "\b\b\b\b\b\b" : "\b\b\b\b", bytes, direction, s, bsstr); Echo(stdin, 1); break; case pr_philbar: (void) printf("\n"); Echo(stdin, 1); goto zz; case pr_dots: for (; dots < 10; dots++) (void) fputc('.', stdout); (void) fputc('\n', stdout); Echo(stdin, 1); goto zz; } /* Save transfers to the logfile. */ /* if a simple path is given, try to log the full path */ if (*remote != '/') { (void) Strncpy(fullRemote, cwd); (void) Strncat(fullRemote, "/"); (void) Strncat(fullRemote, remote); } else (void) Strncpy(fullRemote, remote); if (logf != NULL) { (void) fprintf(logf, "\t-> \"%s\" %s, %s\n", fullRemote, direction, bsstr); } #ifdef SYSLOG { longstring infoPart1; /* Some syslog()'s can't take an unlimited number of arguments, * so shorten our call to syslog to 5 arguments total. */ Strncpy(infoPart1, uinfo.username); if (receiving) { Strncat(infoPart1, " received "); Strncat(infoPart1, fullRemote); Strncat(infoPart1, " as "); Strncat(infoPart1, fullLocal); Strncat(infoPart1, " from "); } else { Strncat(infoPart1, " sent "); Strncat(infoPart1, fullLocal); Strncat(infoPart1, " as "); Strncat(infoPart1, fullRemote); Strncat(infoPart1, " to "); } Strncat(infoPart1, hostname); syslog (LOG_INFO, "%s (%ld bytes, %s).", infoPart1, bytes, bsstr); } #endif /* SYSLOG */ } /* end_progress */ void close_file(FILE **fin, int filetype) { if (*fin != NULL) { if (filetype == IS_FILE) { (void) fclose(*fin); *fin = NULL; } else if (filetype == IS_PIPE) { (void) pclose(*fin); *fin = NULL; } } } /* close_file */ /*ARGSUSED*/ void abortsend SIG_PARAMS { activemcmd = 0; abrtflag = 0; (void) fprintf(stderr, "\nSend aborted.\n"); Echo(stdin, 1); longjmp(sendabort, 1); } /* abortsend */ int sendrequest(char *cmd, char *local, char *remote) { FILE *fin, *dout = NULL; Sig_t oldintr, oldintp; string str; register int c, d; struct stat st; int filetype, result = NOERR; int do_reports = 0; char *mode; register char *bufp; dbprintf("cmd: %s; rmt: %s; loc: %s.\n", cmd, remote == NULL ? "(null)" : remote, local == NULL ? "(null)" : local ); oldintr = NULL; oldintp = NULL; mode = "w"; bytes = file_size = 0L; if (setjmp(sendabort)) { while (cpend) { (void) getreply(0); } if (data >= 0) { (void) close(data); data = -1; } if (oldintr) (void) Signal(SIGINT, oldintr); if (oldintp) (void) Signal(SIGPIPE, oldintp); result = -1; goto xx; } oldintr = Signal(SIGINT, abortsend); file_size = -1; if (strcmp(local, "-") == 0) { fin = stdin; filetype = IS_STREAM; } else if (*local == '|') { filetype = IS_PIPE; oldintp = Signal(SIGPIPE,SIG_IGN); fin = popen(local + 1, "r"); if (fin == NULL) { PERROR("sendrequest", local + 1); (void) Signal(SIGINT, oldintr); (void) Signal(SIGPIPE, oldintp); result = -1; goto xx; } } else { filetype = IS_FILE; fin = fopen(local, "r"); if (fin == NULL) { PERROR("sendrequest", local); (void) Signal(SIGINT, oldintr); result = -1; goto xx; } if (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG) { (void) fprintf(stdout, "%s: not a plain file.\n", local); (void) Signal(SIGINT, oldintr); (void) fclose(fin); result = -1; goto xx; } file_size = st.st_size; } if (initconn()) { (void) Signal(SIGINT, oldintr); if (oldintp) (void) Signal(SIGPIPE, oldintp); result = -1; close_file(&fin, filetype); goto xx; } if (setjmp(sendabort)) goto Abort; #ifdef TRY_NOREPLY if (remote) { (void) sprintf(str, "%s %s", cmd, remote); (void) command_noreply(str); } else { (void) command_noreply(cmd); } dout = dataconn(mode); if (dout == NULL) goto Abort; if(getreply(0) != PRELIM) { (void) Signal(SIGINT, oldintr); if (oldintp) (void) Signal(SIGPIPE, oldintp); close_file(&fin, filetype); return -1; } #else if (remote) { (void) sprintf(str, "%s %s", cmd, remote); if (command(str) != PRELIM) { (void) Signal(SIGINT, oldintr); if (oldintp) (void) Signal(SIGPIPE, oldintp); close_file(&fin, filetype); goto xx; } } else { if (command(cmd) != PRELIM) { (void) Signal(SIGINT, oldintr); if (oldintp) (void) Signal(SIGPIPE, oldintp); close_file(&fin, filetype); goto xx; } } dout = dataconn(mode); if (dout == NULL) goto Abort; #endif (void) Gettimeofday(&start); oldintp = Signal(SIGPIPE, SIG_IGN); if ((do_reports = (filetype == IS_FILE && NOT_VQUIET)) != 0) do_reports = start_progress(1, local); switch (curtype) { case TYPE_I: case TYPE_L: errno = d = 0; while ((c = read(fileno(fin), xferbuf, (int)xferbufsize)) > 0) { bytes += c; for (bufp = xferbuf; c > 0; c -= d, bufp += d) if ((d = write(fileno(dout), bufp, c)) <= 0) break; /* Print progress indicator. */ if (do_reports) do_reports = progress_report(0); } if (c < 0) PERROR("sendrequest", local); if (d <= 0) { if (d == 0 && !creating) (void) fprintf(stderr, "netout: write returned 0?\n"); else if (errno != EPIPE) PERROR("sendrequest", "netout"); bytes = -1; } break; case TYPE_A: next_report = xferbufsize; while ((c = getc(fin)) != EOF) { if (c == '\n') { if (ferror(dout)) break; (void) putc('\r', dout); bytes++; } (void) putc(c, dout); bytes++; /* Print progress indicator. */ if (do_reports && bytes > next_report) do_reports = progress_report(0); } if (ferror(fin)) PERROR("sendrequest", local); if (ferror(dout)) { if (errno != EPIPE) PERROR("sendrequest", "netout"); bytes = -1; } break; } Done: close_file(&fin, filetype); if (dout) (void) fclose(dout); (void) getreply(0); (void) Signal(SIGINT, oldintr); if (oldintp) (void) Signal(SIGPIPE, oldintp); end_progress("sent", local, remote); xx: return (result); Abort: result = -1; if (!cpend) goto xx; if (data >= 0) { (void) close(data); data = -1; } goto Done; } /* sendrequest */ /*ARGSUSED*/ void abortrecv SIG_PARAMS { activemcmd = 0; abrtflag = 0; (void) fprintf(stderr, #ifdef TRY_ABOR "(abort)\n"); #else "\nAborting, please wait..."); #endif (void) fflush(stderr); Echo(stdin, 1); longjmp(recvabort, 1); } /* abortrecv */ void GetLSRemoteDir(char *remote, char *remote_dir) { char *cp; /* * The ls() function can specify a directory to list along with ls flags, * if it sends the flags first followed by the directory name. * * So far, we don't care about the remote directory being listed. I put * it now so I won't forget in case I need to do something with it later. */ remote_dir[0] = 0; if (remote != NULL) { cp = index(remote, LS_FLAGS_AND_FILE); if (cp == NULL) (void) Strncpy(remote_dir, remote); else { *cp++ = ' '; (void) Strncpy(remote_dir, cp); } } } /* GetLSRemoteDir */ int AdjustLocalFileName(char *local) { char *dir; /* See if the file exists, and if we can overwrite it. */ if ((access(local, 0) == 0) && (access(local, 2) < 0)) goto noaccess; /* * Make sure we are writing to a valid local path. * First check the local directory, and see if we can write to it. */ if (access(local, 2) < 0) { dir = rindex(local, '/'); if (errno != ENOENT && errno != EACCES) { /* Report an error if it's one we can't handle. */ PERROR("AdjustLocalFileName", local); return -1; } /* See if we have write permission on this directory. */ if (dir != NULL) { /* Special case: /filename. */ if (dir != local) *dir = 0; if (access(dir == local ? "/" : local, 2) < 0) { /* * We have a big long pathname, like /a/b/c/d, * but see if we can write into the current * directory and call the file ./d. */ if (access(".", 2) < 0) { (void) strcpy(local, " and ."); goto noaccess; } (void) strcpy(local, dir + 1); /* use simple filename. */ } else *dir = '/'; } else { /* We have a simple path name (file name only). */ if (access(".", 2) < 0) { noaccess: PERROR("AdjustLocalFileName", local); return -1; } } } return (NOERR); } /* AdjustLocalFileName */ int SetToAsciiForLS(int is_retr, int currenttype) { int oldt = -1, oldv; if (!is_retr) { if (currenttype != TYPE_A) { oldt = currenttype; oldv = verbose; if (!debug) verbose = V_QUIET; (void) setascii(0, NULL); verbose = oldv; } } return oldt; } /* SetToAsciiForLS */ int IssueCommand(char *ftpcmd, char *remote) { string str; int result = NOERR; if (remote) (void) sprintf(str, "%s %s", ftpcmd, remote); else (void) Strncpy(str, ftpcmd); #ifdef TRY_NOREPLY if (command_noreply(str) != PRELIM) #else if (command(str) != PRELIM) #endif result = -1; return (result); } /* IssueCommand */ FILE *OpenOutputFile(int filetype, char *local, char *mode, Sig_t *oldintp) { FILE *fout; if (filetype == IS_STREAM) { fout = stdout; } else if (filetype == IS_PIPE) { /* If it is a pipe, the pipecmd will have a | as the first char. */ ++local; fout = popen(local, "w"); *oldintp = Signal(SIGPIPE, abortrecv); } else { fout = fopen(local, mode); } if (fout == NULL) PERROR("OpenOutputFile", local); return (fout); } /* OpenOutputFile */ void ReceiveBinary(FILE *din, FILE *fout, int *do_reports, char *localfn) { int c, d, do2; errno = 0; /* Clear any old error left around. */ do2 = *do_reports; /* A slight optimization :-) */ bytes = 0; /* Init the byte-transfer counter. */ for (;;) { /* Read a block from the input stream. */ c = read(fileno(din), xferbuf, (int)xferbufsize); /* If c is zero, then we've read the whole file. */ if (c == 0) break; /* Check for errors that may have occurred while reading. */ if (c < 0) { /* Error occurred while reading. */ if (errno != EPIPE) PERROR("ReceiveBinary", "netin"); bytes = -1; break; } /* Write out the same block we just read in. */ d = write(fileno(fout), xferbuf, c); /* Check for write errors. */ if ((d < 0) || (ferror(fout))) { /* Error occurred while writing. */ PERROR("ReceiveBinary", "outfile"); break; } if (d < c) { (void) fprintf(stderr, "%s: short write\n", localfn); break; } /* Update the byte counter. */ bytes += (long) c; /* Print progress indicator. */ if (do2 != 0) do2 = progress_report(0); } *do_reports = do2; /* Update the real do_reports variable. */ } /* ReceiveBinary */ void AddRedirLine(char *str2) { register struct lslist *new; (void) Strncpy(indataline, str2); new = (struct lslist *) malloc((size_t) sizeof(struct lslist)); if (new != NULL) { if ((new->string = NewString(str2)) != NULL) { new->next = NULL; if (lshead == NULL) lshead = lstail = new; else { lstail->next = new; lstail = new; } } } } /* AddRedirLine */ void ReceiveAscii(FILE *din, FILE *fout, int *do_reports, char *localfn, int lineMode) { string str2; int nchars = 0, c; char *linePtr; int do2 = *do_reports, stripped; next_report = xferbufsize; bytes = errno = 0; if (lineMode) { while ((linePtr = FGets(str2, din)) != NULL) { bytes += (long) RemoveTrailingNewline(linePtr, &stripped); if (is_ls || debug > 0) AddRedirLine(linePtr); /* Shutup while getting remote size and mod time. */ if (!buffer_only) { c = fputs(linePtr, fout); if (c != EOF) { if (stripped > 0) c = fputc('\n', fout); } if ((c == EOF) || (ferror(fout))) { PERROR("ReceiveAscii", "outfile"); break; } } /* Print progress indicator. */ if (do2 && bytes > next_report) do2 = progress_report(0); } } else while ((c = getc(din)) != EOF) { linePtr = str2; while (c == '\r') { bytes++; if ((c = getc(din)) != '\n') { if (ferror(fout)) goto break2; /* Shutup while getting remote size and mod time. */ if (!buffer_only) (void) putc('\r', fout); if (c == '\0') { bytes++; goto contin2; } if (c == EOF) goto contin2; } } /* Shutup while getting remote size and mod time. */ if (!buffer_only) (void) putc(c, fout); bytes++; /* Print progress indicator. */ if (do2 && bytes > next_report) do2 = progress_report(0); /* No seg violations, please */ if (nchars < sizeof(str2) - 1) { *linePtr++ = c; /* build redir string */ nchars++; } contin2: /* Save the input line in the buffer for recall later. */ if (c == '\n' && is_ls) { *--linePtr = 0; AddRedirLine(str2); nchars = 0; } } /* while ((c = getc(din)) != EOF) */ break2: if (ferror(din)) { if (errno != EPIPE) PERROR("ReceiveAscii", "netin"); bytes = -1; } if (ferror(fout)) { if (errno != EPIPE) PERROR("ReceiveAscii", localfn); } *do_reports = do2; } /* ReceiveAscii */ void CloseOutputFile(FILE *f, int filetype, char *name, time_t mt) { struct utimbuf ut; if (f != NULL) { (void) fflush(f); if (filetype == IS_FILE) { (void) fclose(f); if (mt != (time_t)0) { ut.actime = ut.modtime = mt; (void) utime(name, &ut); } } else if (filetype == IS_PIPE) { (void)pclose(f); } } } /* close_file */ void ResetOldType(int oldtype) { int oldv; if (oldtype >= 0) { oldv = verbose; if (!debug) verbose = V_QUIET; (void) SetTypeByNumber(oldtype); verbose = oldv; } } /* ResetOldType */ int FileType(char *fname) { int ft = IS_FILE; if (strcmp(fname, "-") == 0) ft = IS_STREAM; else if (*fname == '|') ft = IS_PIPE; return (ft); } /* FileType */ void CloseData(void) { if (data >= 0) { (void) close(data); data = -1; } } /* CloseData */ int recvrequest(char *cmd, char *local, char *remote, char *mode) { FILE *fout = NULL, *din = NULL; Sig_t oldintr = NULL, oldintp = NULL; int oldtype = -1, is_retr; int nfnd; char msg; struct fd_set mask; int filetype, do_reports = 0; string remote_dir; time_t remfTime = 0; int result = -1; dbprintf("---> cmd: %s; rmt: %s; loc: %s; mode: %s.\n", cmd, remote == NULL ? "(null)" : remote, local == NULL ? "(null)" : local, mode ); is_retr = strcmp(cmd, "RETR") == 0; GetLSRemoteDir(remote, remote_dir); if ((filetype = FileType(local)) == IS_FILE) { if (AdjustLocalFileName(local)) goto xx; } file_size = -1; if (filetype == IS_FILE) file_size = GetDateAndSize(remote, (unsigned long *) &remfTime); if (initconn()) goto xx; oldtype = SetToAsciiForLS(is_retr, curtype); /* Issue the NLST command but don't wait for the reply. Some FTP * servers make the data connection before issuing the * "150 Opening ASCII mode data connection for /bin/ls" reply. */ if (IssueCommand(cmd, remote)) goto xx; if ((fout = OpenOutputFile(filetype, local, mode, &oldintp)) == NULL) goto xx; if ((din = dataconn("r")) == NULL) goto Abort; #ifdef TRY_NOREPLY /* Now get the reply we skipped above. */ (void) getreply(0); #endif do_reports = NOT_VQUIET && is_retr && filetype == IS_FILE; if (do_reports) do_reports = start_progress(0, local); if (setjmp(recvabort)) { #ifdef TRY_ABOR goto Abort; #else /* Just read the rest of the stream without doing anything with * the results. */ (void) Signal(SIGINT, SIG_IGN); (void) Signal(SIGPIPE, SIG_IGN); /* Don't bug us while aborting. */ while (read(fileno(din), xferbuf, (int)xferbufsize) > 0) ; (void) fprintf(stderr, "\rAborted. \n"); #endif } else { oldintr = Signal(SIGINT, abortrecv); if (curtype == TYPE_A) ReceiveAscii(din, fout, &do_reports, local, 1); else ReceiveBinary(din, fout, &do_reports, local); result = NOERR; /* Don't interrupt us now, since we finished successfully. */ (void) Signal(SIGPIPE, SIG_IGN); (void) Signal(SIGINT, SIG_IGN); } CloseData(); (void) getreply(0); goto xx; Abort: /* Abort using RFC959 recommended IP,SYNC sequence */ (void) Signal(SIGPIPE, SIG_IGN); /* Don't bug us while aborting. */ (void) Signal(SIGINT, SIG_IGN); if (!cpend || !cout) goto xx; (void) fprintf(cout,"%c%c",IAC,IP); (void) fflush(cout); msg = IAC; /* send IAC in urgent mode instead of DM because UNIX places oob mark */ /* after urgent byte rather than before as now is protocol */ if (send(fileno(cout),&msg,1,MSG_OOB) != 1) PERROR("recvrequest", "abort"); (void) fprintf(cout,"%cABOR\r\n",DM); (void) fflush(cout); FD_ZERO(&mask); FD_SET(fileno(cin), &mask); if (din) FD_SET(fileno(din), &mask); if ((nfnd = empty(&mask,10)) <= 0) { if (nfnd < 0) PERROR("recvrequest", "abort"); lostpeer(0); } if (din && FD_ISSET(fileno(din), &mask)) { while ((read(fileno(din), xferbuf, xferbufsize)) > 0) ; } if ((getreply(0)) == ERROR && code == 552) { /* needed for nic style abort */ CloseData(); (void) getreply(0); } (void) getreply(0); result = -1; CloseData(); xx: CloseOutputFile(fout, filetype, local, remfTime); dbprintf("outfile closed.\n"); if (din) (void) fclose(din); if (is_retr) end_progress("received", local, remote); if (oldintr) (void) Signal(SIGINT, oldintr); if (oldintp) (void) Signal(SIGPIPE, oldintp); dbprintf("recvrequest result = %d.\n", result); if (oldtype >= 0) ResetOldType(oldtype); bytes = 0L; return (result); } /* recvrequest */ /* * Need to start a listen on the data channel * before we send the command, otherwise the * server's connect may fail. */ #ifdef TERM_FTP /* * Need to request that the server go into passive mode and then get the * address and port for the term server to connect to. */ int initconn(void) { int result; int n[6]; int s; if (data != -1) (void) close(data); result = command("PASV"); if (result == COMPLETE) { if (sscanf(reply_string, "%*[^(](%d,%d,%d,%d,%d,%d)", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5]) != 6) { printf("Cannot parse PASV response: %s\n", reply_string); return 1; } close(data); lcompression = rcompression = compress_toggle; if ((s = connect_server(0)) < 0) { perror("ftp: connect to term server"); data = -1; return 1; } data = s; send_command(s, C_PORT, 0, "%d.%d.%d.%d:%d", n[0], n[1], n[2], n[3], 256 * n[4] + n[5]); send_command(s, C_COMPRESS, 1, "n"); send_command(s, C_DUMB, 1, 0); return 0; } return 1; } /* initconn_term */ FILE *dataconn(char *lmode) { return (safeopen(data, lmode)); } /* dataconn_term */ #else /* not TERM_FTP */ int initconn(void) { register char *p, *a; int result, len, tmpno = 0; int on = 1, rval; string str; Sig_t oldintr; oldintr = Signal(SIGINT, SIG_IGN); noport: data_addr = myctladdr; if (sendport) data_addr.sin_port = 0; /* let system pick one */ if (data != -1) (void) close (data); data = socket(AF_INET, SOCK_STREAM, 0); if (data < 0) { PERROR("initconn", "socket"); if (tmpno) sendport = 1; rval = 1; goto Return; } if (!sendport) if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) { PERROR("initconn", "setsockopt (reuse address)"); goto bad; } #ifdef SOCKS if (Rbind(data, (struct sockaddr *)&data_addr, sizeof (data_addr), hisctladdr.sin_addr.s_addr) < 0) { #else if (Bind(data, &data_addr, sizeof (data_addr)) < 0) { #endif PERROR("initconn", "bind"); goto bad; } #ifdef LINGER /* If puts don't complete, you could try this. */ { struct linger li; li.l_onoff = 1; li.l_linger = 900; if (setsockopt(data, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(struct linger)) < 0) { PERROR("initconn", "setsockopt(SO_LINGER)"); } } #endif /* LINGER */ if (options & SO_DEBUG && setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0) PERROR("initconn", "setsockopt (ignored)"); len = sizeof (data_addr); if (Getsockname(data, (char *)&data_addr, &len) < 0) { PERROR("initconn", "getsockname"); goto bad; } #ifdef SOCKS if (Rlisten(data, 1) < 0) #else if (listen(data, 1) < 0) #endif PERROR("initconn", "listen"); if (sendport) { a = (char *)&data_addr.sin_addr; p = (char *)&data_addr.sin_port; #define UC(x) (int) (((int) x) & 0xff) (void) sprintf(str, "PORT %d,%d,%d,%d,%d,%d", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); result = command(str); if (result == ERROR && sendport == -1) { sendport = 0; tmpno = 1; goto noport; } rval = (result != COMPLETE); goto Return; } if (tmpno) sendport = 1; rval = 0; goto Return; bad: (void) close(data), data = -1; if (tmpno) sendport = 1; rval = 1; Return: (void) Signal(SIGINT, oldintr); return (rval); } /* initconn */ FILE * dataconn(char *mode) { struct sockaddr_in from; FILE *fp; int s, fromlen = sizeof (from); #ifdef SOCKS s = Raccept(data, (struct sockaddr *) &from, &fromlen); #else s = Accept(data, &from, &fromlen); #endif if (s < 0) { PERROR("dataconn", "accept"); (void) close(data), data = -1; fp = NULL; } else { (void) close(data); data = s; fp = safeopen(data, mode); } return (fp); } /* dataconn */ #endif /* eof ftp.c */