home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume39 / ncftp / part02 < prev    next >
Encoding:
Text File  |  1993-08-25  |  60.4 KB  |  2,494 lines

  1. Newsgroups: comp.sources.misc
  2. From: mgleason@cse.unl.edu (Mike Gleason)
  3. Subject: v39i054:  ncftp - Alternative User Interface for FTP, v1.5.0, Part02/05
  4. Message-ID: <1993Aug26.000429.24270@sparky.sterling.com>
  5. X-Md4-Signature: a4a578101b3e21f79c7e83d2c104b615
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: NCEMRSoft
  8. Date: Thu, 26 Aug 1993 00:04:29 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: mgleason@cse.unl.edu (Mike Gleason)
  12. Posting-number: Volume 39, Issue 54
  13. Archive-name: ncftp/part02
  14. Environment: UNIX, ANSI-C, !SVR4
  15. Supersedes: ncftp: Volume 35, Issue 4-7
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then feed it
  19. # into a shell via "sh file" or similar.  To overwrite existing files,
  20. # type "sh file -c".
  21. # Contents:  Blurb ftp.c getpass.c open.c
  22. # Wrapped by kent@sparky on Wed Aug 25 18:59:16 1993
  23. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  24. echo If this archive is complete, you will see the following message:
  25. echo '          "shar: End of archive 2 (of 5)."'
  26. if test -f 'Blurb' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'Blurb'\"
  28. else
  29.   echo shar: Extracting \"'Blurb'\" \(1753 characters\)
  30.   sed "s/^X//" >'Blurb' <<'END_OF_FILE'
  31. XSubject:  NcFTP 1.5.0 - Alternative User Interface for FTP
  32. X
  33. XArchive-name: ncftp/part01
  34. XEnvironment: UNIX, ANSI-C, !SVR4
  35. X
  36. XNcFTP - Alternative user interface for FTP
  37. XVersion 1.5.0 by Mike Gleason, NCEMRSoft.
  38. X
  39. XI used to list the features of ncftp in this blurb, but there are just
  40. Xtoo many to list.  Even if you only ftp occasionally, it is worth your
  41. Xtime to install ncftp (or atleast bug your sysadmin to).  If you won't take
  42. Xmy word for it, just ask around, or extract this archive and read the
  43. Xman page.
  44. X
  45. XNote:  May not work correctly with System V R 4 (and Solaris 2.2).
  46. X
  47. XMajor changes since 1.0.2:
  48. X
  49. X* Supports the Getline (input-edit) and GNU Readline command-line
  50. X  editing and history libraries.
  51. X
  52. X* Supports the Socks firewall library, and another firewall gateway
  53. X  implementation.
  54. X
  55. X* Terrific new "recent-sites" file that automatically saves the
  56. X  sites you call;  when you open a site in the recent-sites file
  57. X  (of course you can abbreviate the names), you start in the
  58. X  same directory you were in last time.
  59. X
  60. X* Improved on-line help, and tips on how to use the program better
  61. X  are printed each time you run the program.
  62. X
  63. X* Rewritten man page.
  64. X
  65. X* Faster ascii transfers.
  66. X
  67. X* Typing 'open' by itself lists all the sites the program knows
  68. X  about (the ones in your .netrc and the recent-sites list) so
  69. X  you can just pick one.
  70. X
  71. X* Enhanced colon-mode, that can dump a file to stdout (ftpcat mode)
  72. X  or to a pager.  (i.e. ncftp -c wu:/README >/dev/null).
  73. X
  74. X* You can choose whether an open is anonymous by default (like it
  75. X  had always been) or a user login by default by setting a new
  76. X  program variable.
  77. X
  78. X* Bugs fixed.
  79. X
  80. XRead the enclosed file, v2_Notes, which explains why I won't be
  81. Xable to work on the nearly finished, and much improved v2.0.
  82. X
  83. END_OF_FILE
  84.   if test 1753 -ne `wc -c <'Blurb'`; then
  85.     echo shar: \"'Blurb'\" unpacked with wrong size!
  86.   fi
  87.   # end of 'Blurb'
  88. fi
  89. if test -f 'ftp.c' -a "${1}" != "-c" ; then 
  90.   echo shar: Will not clobber existing file \"'ftp.c'\"
  91. else
  92.   echo shar: Extracting \"'ftp.c'\" \(35792 characters\)
  93.   sed "s/^X//" >'ftp.c' <<'END_OF_FILE'
  94. X/* ftp.c */
  95. X
  96. X/*  $RCSfile: ftp.c,v $
  97. X *  $Revision: 14020.12 $
  98. X *  $Date: 93/07/09 11:30:28 $
  99. X */
  100. X
  101. X#include "sys.h"
  102. X#include <sys/types.h>
  103. X#include <sys/param.h>
  104. X#include <setjmp.h>
  105. X#include <sys/stat.h>
  106. X#include <sys/socket.h>
  107. X#include <sys/time.h>
  108. X#include <sys/file.h>
  109. X#include <string.h>
  110. X#include <time.h>
  111. X
  112. X#ifdef NO_UTIMEH
  113. Xstruct    utimbuf {time_t actime; time_t modtime;};
  114. X#else
  115. X#    include <utime.h>
  116. X#endif
  117. X
  118. X#ifdef SYSLOG
  119. X#    include <syslog.h>
  120. X#endif
  121. X
  122. X/* You may need this for declarations of fd_set, etc. */
  123. X#ifdef SYSSELECTH
  124. X#   include <sys/select.h>
  125. X#else
  126. X#ifdef STRICT_PROTOS
  127. Xextern int select (int, void *, void *, void *, struct timeval *);
  128. X#endif
  129. X#endif
  130. X
  131. X#ifndef NO_UNISTDH        /* for prototypes only. */
  132. X#    include <unistd.h>
  133. X#endif
  134. X
  135. X#include <netinet/in.h>
  136. X#include <arpa/ftp.h>
  137. X#include <arpa/inet.h>
  138. X#include <arpa/telnet.h>
  139. X#include <string.h>
  140. X#include <signal.h>
  141. X#include <errno.h>
  142. X#include <netdb.h>
  143. X#include <fcntl.h>
  144. X#include <pwd.h>
  145. X#include <ctype.h>
  146. X#include "util.h"
  147. X#include "ftp.h"
  148. X#include "cmds.h"
  149. X#include "main.h"
  150. X#include "ftprc.h"
  151. X#include "getpass.h"
  152. X#include "defaults.h"
  153. X#include "copyright.h"
  154. X
  155. X/* ftp.c globals */
  156. Xstruct                sockaddr_in hisctladdr;
  157. Xstruct                sockaddr_in data_addr;
  158. Xint                    data = -1;
  159. Xint                    abrtflag = 0;
  160. Xstruct sockaddr_in    myctladdr;
  161. XFILE                *cin = NULL, *cout = NULL;
  162. Xchar                *reply_string = NULL;
  163. Xjmp_buf                sendabort, recvabort;
  164. Xint                    progress_meter = dPROGRESS;
  165. Xint                    cur_progress_meter;
  166. Xint                    sendport = -1;        /* use PORT cmd for each data connection */
  167. Xint                    code;                /* return/reply code for ftp command */
  168. Xstring                indataline;            
  169. Xint                 cpend;                /* flag: if != 0, then pending server reply */
  170. Xchar                *xferbuf;            /* buffer for local and remote I/O */
  171. Xsize_t                xferbufsize;        /* size in bytes, of the transfer buffer. */
  172. Xlong                next_report;
  173. Xlong                bytes;
  174. Xlong                now_sec;
  175. Xlong                file_size;
  176. Xstruct timeval        start, stop;
  177. Xint                    buffer_only = 0;    /* True if reading into redir line
  178. X                                         * buffer only (not echoing to
  179. X                                         * stdout).
  180. X                                         */
  181. X
  182. X/* ftp.c externs */
  183. Xextern FILE                    *logf;
  184. Xextern string                cwd, anon_password;
  185. Xextern Hostname                hostname;
  186. Xextern int                    verbose, debug, macnum, margc;
  187. Xextern int                    curtype, creating;
  188. Xextern int                    options, activemcmd, paging;
  189. Xextern int                    ansi_escapes, logged_in, macnum;
  190. Xextern char                    *line, *margv[];
  191. Xextern char                    *tcap_normal, *tcap_boldface;
  192. Xextern char                    *tcap_underline, *tcap_reverse;
  193. Xextern struct userinfo        uinfo;
  194. Xextern struct macel            macros[];
  195. Xextern struct lslist        *lshead, *lstail;
  196. Xextern int                    is_ls;
  197. X
  198. X#ifdef GATEWAY
  199. Xextern string                gateway;
  200. Xextern string                gate_login;
  201. X#endif
  202. X
  203. X
  204. Xint hookup(char *host, unsigned int port)
  205. X{
  206. X    register struct hostent *hp = 0;
  207. X    int s, len, hErr = -1;
  208. X    string errstr;
  209. X
  210. X    bzero((char *)&hisctladdr, sizeof (hisctladdr));
  211. X#ifdef BAD_INETADDR
  212. X    hisctladdr.sin_addr = inet_addr(host);
  213. X#else
  214. X     hisctladdr.sin_addr.s_addr = inet_addr(host);
  215. X#endif
  216. X    if (hisctladdr.sin_addr.s_addr != -1) {
  217. X        hisctladdr.sin_family = AF_INET;
  218. X        (void) Strncpy(hostname, host);
  219. X    } else {
  220. X        hp = gethostbyname(host);
  221. X        if (hp == NULL) {
  222. X#ifdef HERROR
  223. X            extern int h_errno;
  224. X            if (h_errno == HOST_NOT_FOUND)
  225. X                (void) printf("%s: unknown host\n", host);
  226. X            else (void) fprintf(stderr, "%s: gethostbyname herror (%d):  ",
  227. X                host, h_errno);
  228. X            herror(NULL);
  229. X#else
  230. X            (void) printf("%s: unknown host\n", host);
  231. X#endif
  232. X            goto done;
  233. X        }
  234. X        hisctladdr.sin_family = hp->h_addrtype;
  235. X        bcopy(hp->h_addr_list[0],
  236. X            (caddr_t)&hisctladdr.sin_addr, hp->h_length);
  237. X        (void) Strncpy(hostname, hp->h_name);
  238. X    }
  239. X    s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
  240. X    if (s < 0) {
  241. X        PERROR("hookup", "socket");
  242. X        goto done;
  243. X    }
  244. X    hisctladdr.sin_port = port;
  245. X#ifdef SOCKS
  246. X    while (Rconnect(s, (struct sockaddr *) &hisctladdr, (int) sizeof (hisctladdr)) < 0) {
  247. X#else
  248. X    while (connect(s, (struct sockaddr *) &hisctladdr, (int) sizeof (hisctladdr)) < 0) {
  249. X#endif
  250. X        if (hp && hp->h_addr_list[1]) {
  251. X            (void) sprintf(errstr, "connect error to address %s",
  252. X                inet_ntoa(hisctladdr.sin_addr));
  253. X            PERROR("hookup", errstr);
  254. X            hp->h_addr_list++;
  255. X            bcopy(hp->h_addr_list[0],
  256. X                 (caddr_t)&hisctladdr.sin_addr, hp->h_length);
  257. X            (void) fprintf(stdout, "Trying %s...\n",
  258. X                inet_ntoa(hisctladdr.sin_addr));
  259. X            (void) close(s);
  260. X            s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
  261. X            if (s < 0) {
  262. X                PERROR("hookup", "socket");
  263. X                goto done;
  264. X            }
  265. X            continue;
  266. X        }
  267. X        PERROR("hookup", "connect");
  268. X        switch (errno) {
  269. X            case ENETDOWN:
  270. X            case ENETUNREACH:
  271. X            case ECONNABORTED:
  272. X            case ETIMEDOUT:
  273. X            case ECONNREFUSED:
  274. X            case EHOSTDOWN:
  275. X                hErr = -2;    /* we can re-try later. */
  276. X        }
  277. X        goto bad;
  278. X    }
  279. X    len = sizeof (myctladdr);
  280. X    if (Getsockname(s, (char *)&myctladdr, &len) < 0) {
  281. X        PERROR("hookup", "getsockname");
  282. X        goto bad;
  283. X    }
  284. X    cin = fdopen(s, "r");
  285. X    cout = fdopen(s, "w");
  286. X    if (cin == NULL || cout == NULL) {
  287. X        (void) fprintf(stderr, "ftp: fdopen failed.\n");
  288. X        close_streams(0);
  289. X        goto bad;
  290. X    }
  291. X    if (IS_VVERBOSE)
  292. X        (void) printf("Connected to %s.\n", hostname);
  293. X    if (getreply(0) > 2) {     /* read startup message from server */
  294. X        close_streams(0);
  295. X        if (code == 421)
  296. X            hErr = -2;    /* We can try again later. */
  297. X        goto bad;
  298. X    }
  299. X#ifdef SO_OOBINLINE
  300. X    {
  301. X    int on = 1;
  302. X
  303. X    if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on))
  304. X        < 0 && debug) {
  305. X            PERROR("hookup", "setsockopt");
  306. X        }
  307. X    }
  308. X#endif /* SO_OOBINLINE */
  309. X
  310. X    hErr = 0;
  311. X    goto done;
  312. X
  313. Xbad:
  314. X    (void) close(s);
  315. Xdone:
  316. X    return (hErr);
  317. X}    /* hookup */
  318. X
  319. X
  320. X
  321. X/* This registers the user's username, password, and account with the remote
  322. X * host which validates it.  If we get on, we also do some other things, like
  323. X * enter a log entry and execute the startup macro.
  324. X */
  325. Xint Login(char *userNamePtr, char *passWordPtr, char *accountPtr, int doInit)
  326. X{
  327. X    string userName;
  328. X    string str;
  329. X    int n;
  330. X    int sentAcct = 0;
  331. X    int userWasPrompted = 0;
  332. X    int result = CMDERR;
  333. X    time_t now;
  334. X
  335. X    if (userNamePtr == NULL) {
  336. X        /* Prompt for a username. */
  337. X        (void) sprintf(str, "Login Name (%s): ", uinfo.username);
  338. X        ++userWasPrompted;
  339. X        if (Gets(str, userName, sizeof(userName)) == NULL)
  340. X            goto done;
  341. X        else if (userName[0]) {
  342. X            /* User didn't just hit return. */
  343. X            userNamePtr = userName;
  344. X        } else {
  345. X            /*
  346. X             * User can hit return if he wants to enter his username
  347. X             * automatically.
  348. X             */
  349. X            if (*uinfo.username != '\0')
  350. X                userNamePtr = uinfo.username;
  351. X            else
  352. X                goto done;
  353. X        }
  354. X    }
  355. X
  356. X    if (passWordPtr == NULL) {
  357. X        if ((strcmp("anonymous", userName) == 0) && (*anon_password))
  358. X            passWordPtr = anon_password;
  359. X        else {
  360. X            /* Prompt for a password. */
  361. X            ++userWasPrompted;
  362. X            passWordPtr = Getpass("Password:");
  363. X        }
  364. X    }
  365. X
  366. X#ifdef GATEWAY
  367. X    if (*gateway)
  368. X        (void) sprintf(str, "USER %s@%s",
  369. X                (*gate_login ? gate_login : dGATEWAY_LOGIN),
  370. X                hostname);
  371. X    else
  372. X#endif
  373. X        (void) sprintf(str, "USER %s", userNamePtr);
  374. X
  375. X    /* Send the user name. */
  376. X    n = command(str);
  377. X    if (n == CONTINUE) {
  378. X        /* The remote site is requesting us to send the password now. */
  379. X        (void) sprintf(str, "PASS %s", passWordPtr);
  380. X        n = command(str);
  381. X        if (n == CONTINUE) {
  382. X            /* The remote site is requesting us to send the account now. */
  383. X            (void) sprintf(str, "ACCT %s", Getpass("Account:"));
  384. X            ++sentAcct;    /* Keep track that we've sent the account already. */
  385. X            ++userWasPrompted;
  386. X            n = command(str);
  387. X        }
  388. X    }
  389. X
  390. X    if (n != COMPLETE) {
  391. X        (void) printf("Login failed.\n");
  392. X        goto done;
  393. X    }
  394. X    
  395. X    /* If you specified an account, and the remote-host didn't request it
  396. X     * (maybe it's optional), we will send the account information.
  397. X     */
  398. X    if (!sentAcct && accountPtr != NULL) {
  399. X        (void) sprintf(str, "ACCT %s", accountPtr);
  400. X        (void) command(str);
  401. X    }
  402. X
  403. X    /* See if remote host dropped connection.  Some sites will let you log
  404. X     * in anonymously, only to tell you that they already have too many
  405. X     * anon users, then drop you.  We do a no-op here to see if they've
  406. X     * ditched us.
  407. X     */
  408. X    n = quiet_command("NOOP");
  409. X    if (n == 4)
  410. X        goto done;
  411. X
  412. X#ifdef SYSLOG
  413. X    syslog(LOG_INFO, "%s connected to %s as %s.",
  414. X           uinfo.username, hostname, userNamePtr);
  415. X#endif
  416. X
  417. X    /* Save which sites we opened to the user's logfile. */
  418. X    if (logf != NULL) {
  419. X        (void) time(&now);
  420. X        (void) fprintf(logf, "%s opened at %s",
  421. X                       hostname,
  422. X                       ctime(&now));
  423. X    }
  424. X
  425. X    /* Let the user know we are logged in, unless he was prompted for some
  426. X     * information already.
  427. X     */
  428. X    if (!userWasPrompted)
  429. X        if (NOT_VQUIET)
  430. X            (void) printf("Logged into %s.\n", hostname);
  431. X
  432. X    if ((doInit) && (macnum > 0)) {
  433. X        /* Run the startup macro, if any. */
  434. X        /* If macnum is non-zero, the init macro was defined from
  435. X         * ruserpass.  It would be the only macro defined at this
  436. X         * point.
  437. X         */
  438. X        (void) strcpy(line, "$init");
  439. X        makeargv();
  440. X        (void) domacro(margc, margv);
  441. X    }
  442. X
  443. X    _cd(NULL);    /* Init cwd variable. */
  444. X
  445. X    result = NOERR;
  446. X    logged_in = 1;
  447. X
  448. Xdone:
  449. X    return (result);
  450. X}                                       /* Login */
  451. X
  452. X
  453. X
  454. X/*ARGSUSED*/
  455. Xvoid cmdabort SIG_PARAMS
  456. X{
  457. X    (void) printf("\n");
  458. X    (void) fflush(stdout);
  459. X    abrtflag++;
  460. X}    /* cmdabort */
  461. X
  462. X
  463. X
  464. X
  465. Xint command(char *cmd)
  466. X{
  467. X    int r;
  468. X    sig_t oldintr;
  469. X    string str;
  470. X
  471. X    abrtflag = 0;
  472. X    dbprintf("cmd: \"%s\" (length %d)\n", cmd, (int) strlen(cmd));
  473. X    if (cout == NULL) {
  474. X        (void) sprintf(str, "%s: No control connection for command", cmd);
  475. X        PERROR("command", str);
  476. X        return (0);
  477. X    }
  478. X    oldintr = Signal(SIGINT, /* cmdabort */ SIG_IGN);
  479. X#ifndef BROKEN_MEMCPY
  480. X    if (cout != NULL)
  481. X        (void) fprintf(cout, "%s\r\n", cmd);
  482. X#else
  483. X    {
  484. X        /*
  485. X         * The fprintf() above gives me a core-dump in memcpy()...
  486. X         * This does the trick though...
  487. X         */
  488. X
  489. X        char *p = cmd;
  490. X        while (*p)
  491. X            fputc(*p++, cout);
  492. X        fputc('\r', cout);
  493. X        fputc('\n', cout);
  494. X    }
  495. X#endif /* !SCO324 */
  496. X    (void) fflush(cout);
  497. X    cpend = 1;
  498. X    r = getreply(strcmp(cmd, "QUIT") == 0);
  499. X    if (abrtflag && oldintr != SIG_IGN && oldintr != NULL)
  500. X        (*oldintr)(0);
  501. X    (void) Signal(SIGINT, oldintr);
  502. X    return(r);
  503. X}    /* command */
  504. X
  505. X
  506. X
  507. X
  508. Xint quiet_command(char *cmd)
  509. X{
  510. X    register int oldverbose, result;
  511. X    
  512. X    oldverbose = verbose;
  513. X    verbose = V_QUIET;
  514. X    result = command(cmd);
  515. X    verbose = oldverbose;
  516. X    return (result);
  517. X}    /* quiet_command */
  518. X
  519. X
  520. X
  521. X
  522. Xint verbose_command(char *cmd)
  523. X{
  524. X    register int oldverbose, result;
  525. X    
  526. X    oldverbose = verbose;
  527. X    verbose = V_VERBOSE;
  528. X    result = command(cmd);
  529. X    verbose = oldverbose;
  530. X    return (result);
  531. X}    /* quiet_command */
  532. X
  533. X
  534. X
  535. X
  536. Xint getreply(int expecteof)
  537. X{
  538. X    register int c, n;
  539. X    int dig;
  540. X    char *cp, *end, *dp;
  541. X    int thiscode, originalcode = 0, continuation = 0;
  542. X    sig_t oldintr;
  543. X
  544. X    if (cin == NULL)
  545. X        return (-1);
  546. X    /* oldintr = Signal(SIGINT, SIG_IGN); */
  547. X    oldintr = Signal(SIGINT, cmdabort);
  548. X    end = reply_string + RECEIVEDLINELEN - 2;
  549. X    for (;abrtflag==0;) {
  550. X        dig = n = thiscode = code = 0;
  551. X        cp = reply_string;
  552. X        for (;abrtflag==0;) {
  553. X            c = fgetc(cin);
  554. X            if (c == IAC) {     /* handle telnet commands */
  555. X                switch (c = fgetc(cin)) {
  556. X                case WILL:
  557. X                case WONT:
  558. X                    c = fgetc(cin);
  559. X                    (void) fprintf(cout, "%c%c%c",IAC,DONT,c);
  560. X                    (void) fflush(cout);
  561. X                    break;
  562. X                case DO:
  563. X                case DONT:
  564. X                    c = fgetc(cin);
  565. X                    (void) fprintf(cout, "%c%c%c",IAC,WONT,c);
  566. X                    (void) fflush(cout);
  567. X                    break;
  568. X                default:
  569. X                    break;
  570. X                }
  571. X                continue;
  572. X            }
  573. X            dig++;
  574. X            if (c == EOF) {
  575. X                if (expecteof) {
  576. X                    (void) Signal(SIGINT, oldintr);
  577. X                    code = 221;
  578. X                    return (0);
  579. X                }
  580. X                lostpeer(0);
  581. X                if (NOT_VQUIET) {
  582. X                    (void) printf("421 Service not available, remote server has closed connection\n");
  583. X                    (void) fflush(stdout);
  584. X                }
  585. X                code = 421;
  586. X                return(4);
  587. X            }
  588. X            if (cp < end && c != '\r')
  589. X                *cp++ = c;
  590. X
  591. X            if (c == '\n')
  592. X                break;
  593. X            if (dig < 4 && isdigit(c))
  594. X                code = thiscode = code * 10 + (c - '0');
  595. X            else if (dig == 4 && c == '-') {
  596. X                if (continuation)
  597. X                    code = 0;
  598. X                continuation++;
  599. X            }
  600. X            if (n == 0)
  601. X                n = c;
  602. X        }    /* end for(;;) #2 */
  603. X        
  604. X        *cp = '\0';
  605. X        switch (verbose) {
  606. X            case V_QUIET:
  607. X                /* Don't print anything. */
  608. X                break;
  609. X            case V_ERRS:
  610. X                if (n == '5') {
  611. X                    dp = reply_string;
  612. X                    goto stripCode;
  613. X                }
  614. X                break;    
  615. X            case V_IMPLICITCD:
  616. X            case V_TERSE:
  617. X                dp = NULL;
  618. X                if (n == '5' && verbose == V_TERSE)
  619. X                    dp = reply_string;
  620. X                else {
  621. X                    switch (thiscode) {
  622. X                        case 230:
  623. X                        case 214:
  624. X                        case 332:
  625. X                        case 421:    /* For ftp.apple.com, etc. */
  626. X                            dp = reply_string;
  627. X                            break;
  628. X                        case 220:
  629. X                            /*
  630. X                             * Skip the foo FTP server ready line.
  631. X                             */
  632. X                            if (strstr(reply_string, "ready.") == NULL)
  633. X                                dp = reply_string;
  634. X                            break;
  635. X                        case 250:
  636. X                            /*
  637. X                             * Print 250 lines if they aren't
  638. X                             * "250 CWD command successful."
  639. X                             */
  640. X                            if (strncmp(reply_string + 4, "CWD ", (size_t) 4))
  641. X                                dp = reply_string;
  642. X                    }
  643. X                }
  644. X                if (dp == NULL) break;            
  645. XstripCode:
  646. X                /* Try to strip out the code numbers, etc. */
  647. X                if (isdigit(*dp++) && isdigit(*dp++) && isdigit(*dp++)) {
  648. X                    if (*dp == ' ' || *dp == '-') {
  649. X                        dp++;
  650. X                        if (*dp == ' ') dp++;
  651. X                    } else dp = reply_string;            
  652. X                } else {
  653. X                    int spaces;
  654. X                    dp = reply_string;
  655. X                    for (spaces = 0; spaces < 4; ++spaces)
  656. X                        if (dp[spaces] != ' ')
  657. X                            break;
  658. X                    if (spaces == 4)
  659. X                        dp += spaces;
  660. X                }                    
  661. X                goto printLine;
  662. X            case V_VERBOSE:
  663. X                dp = reply_string;
  664. XprintLine:        (void) fputs(dp, stdout);
  665. X        }    /* end switch */
  666. X
  667. X        if (continuation && code != originalcode) {
  668. X            if (originalcode == 0)
  669. X                originalcode = code;
  670. X            continue;
  671. X        }
  672. X        if (n != '1')
  673. X            cpend = 0;
  674. X        (void) Signal(SIGINT,oldintr);
  675. X        if (code == 421 || originalcode == 421)
  676. X            lostpeer(0);
  677. X        if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN && oldintr)
  678. X            (*oldintr)(0);
  679. X        return (n - '0');
  680. X    }    /* end for(;;) #1 */
  681. X}    /* getreply */
  682. X
  683. X
  684. X
  685. X
  686. Xstatic int empty(struct fd_set *mask, int sec)
  687. X{
  688. X    struct timeval t;
  689. X
  690. X    t.tv_sec = (long) sec;
  691. X    t.tv_usec = 0;
  692. X
  693. X    return(Select(32, mask, NULL, NULL, &t));
  694. X}    /* empty */
  695. X
  696. X
  697. X
  698. X
  699. Xstatic void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0)
  700. X{
  701. X    tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
  702. X    tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
  703. X    if (tdiff->tv_usec < 0)
  704. X        tdiff->tv_sec--, tdiff->tv_usec += 1000000;
  705. X}    /* tvsub */
  706. X
  707. X
  708. X/* Variables private to progress_report code. */
  709. Xstatic int barlen;
  710. Xstatic long last_dot;
  711. Xstatic int dots;
  712. X
  713. Xint start_progress(int sending, char *local)
  714. X{
  715. X    long s;
  716. X    str32 spec;
  717. X
  718. X    cur_progress_meter = progress_meter;
  719. X    if ((cur_progress_meter > pr_last) || (cur_progress_meter < 0))
  720. X        cur_progress_meter = dPROGRESS;
  721. X    if ((file_size <= 0) && ((cur_progress_meter == pr_percent) || (cur_progress_meter == pr_philbar) || (cur_progress_meter == pr_last)))
  722. X        cur_progress_meter = pr_kbytes;
  723. X    if (!ansi_escapes && (cur_progress_meter == pr_philbar))
  724. X        cur_progress_meter = pr_dots;
  725. X
  726. X    (void) gettimeofday(&start, (struct timezone *)0);
  727. X    now_sec = start.tv_sec;
  728. X
  729. X    switch (cur_progress_meter) {
  730. X        case pr_none:
  731. X            break;
  732. X        case pr_percent:
  733. X            (void) printf("%s:     ", local);
  734. X            goto zz;
  735. X        case pr_kbytes:
  736. X            (void) printf("%s:       ", local);
  737. X            goto zz;
  738. X        case pr_philbar:
  739. X            (void) printf("%s%s file: %s %s\n", 
  740. X                tcap_boldface,
  741. X                sending ? "Sending" : "Receiving",
  742. X                local,
  743. X                tcap_normal
  744. X            );
  745. X            barlen = 64;
  746. X            for (s = file_size; s > 0; s /= 10L) barlen--;
  747. X            (void) sprintf(spec, "      0 %%%ds %%ld bytes.\r", barlen);
  748. X            (void) printf(spec, " ", file_size);
  749. X            goto zz;
  750. X        case pr_dots:
  751. X            last_dot = (file_size / 10) + 1;
  752. X            dots = 0;
  753. X            (void) printf("%s: ", local);
  754. X        zz:
  755. X            (void) fflush(stdout);
  756. X            echo(stdin, 0);
  757. X    }    /* end switch */
  758. X    return (cur_progress_meter);
  759. X}    /* start_progress */
  760. X
  761. X
  762. X
  763. X
  764. Xint progress_report(int finish_up)
  765. X{
  766. X    int size;
  767. X    long perc;
  768. X    str32 spec;
  769. X
  770. X    next_report += xferbufsize;
  771. X    (void) gettimeofday(&stop, (struct timezone *)0);
  772. X    if ((stop.tv_sec > now_sec) || finish_up && file_size) {
  773. X        switch (cur_progress_meter) {
  774. X            case pr_none:
  775. X                break;
  776. X            case pr_percent:
  777. X                perc = 100L * bytes / file_size;
  778. X                if (perc > 100L) perc = 100L;
  779. X                (void) printf("\b\b\b\b%3ld%%", perc);
  780. X                (void) fflush(stdout);
  781. X                break;
  782. X            case pr_philbar:
  783. X                size = (int) ((float)barlen * ((float) (bytes > file_size ?
  784. X                    file_size : bytes)/file_size));
  785. X                (void) sprintf(spec, "%%3ld%%%%  0 %%s%%%ds%%s\r", size);
  786. X                (void) printf(
  787. X                    spec,
  788. X                    100L * (bytes > file_size ? file_size : bytes) / file_size,
  789. X                    tcap_reverse,
  790. X                    " ",
  791. X                    tcap_normal
  792. X                );
  793. X                (void) fflush(stdout);
  794. X                break;
  795. X            case pr_kbytes:
  796. X                if ((bytes / 1024) > 0) {
  797. X                    (void) printf("\b\b\b\b\b\b%5ldK", bytes / 1024);
  798. X                    (void) fflush(stdout);
  799. X                }
  800. X                break;
  801. X            case pr_dots:
  802. X                if (bytes > last_dot) {
  803. X                    (void) fputc('.', stdout);
  804. X                    (void) fflush(stdout);
  805. X                    last_dot += (file_size / 10) + 1;
  806. X                    dots++;
  807. X                }    
  808. X        }    /* end switch */
  809. X        now_sec = stop.tv_sec;
  810. X    }    /* end if we updated */
  811. X    return (UserLoggedIn());
  812. X}    /* progress_report */
  813. X
  814. X
  815. X
  816. X
  817. Xvoid end_progress(char *direction, char *local, char *remote)
  818. X{
  819. X    struct timeval            td;
  820. X    float                    s, bs = 0.0;
  821. X    char                    *cp, *bsstr;
  822. X    string                    str;
  823. X
  824. X    if (bytes <= 0)
  825. X        return;
  826. X    (void) progress_report(1);        /* tell progress proc to cleanup. */
  827. X
  828. X    tvsub(&td, &stop, &start);
  829. X    s = td.tv_sec + (td.tv_usec / 1000000.0);
  830. X    if (s != 0.0)
  831. X        bs = bytes / s;
  832. X    if (bs > 1024.0) {
  833. X        bs /= 1024.0;
  834. X        bsstr = "K/s.\n";
  835. X    } else
  836. X        bsstr = "Bytes/s.\n";
  837. X
  838. X    if (NOT_VQUIET) switch(cur_progress_meter) {
  839. X        case pr_none:
  840. X        zz:
  841. X            (void) printf("%s: %ld bytes %s in %.2f seconds, %.2f %s", local, bytes, direction, s, bs, bsstr);
  842. X            break;
  843. X        case pr_kbytes:
  844. X        case pr_percent:
  845. X            (void) printf("%s%ld bytes %s in %.2f seconds, %.2f %s",
  846. X            cur_progress_meter == pr_kbytes ? "\b\b\b\b\b\b" : "\b\b\b\b",
  847. X            bytes, direction, s, bs, bsstr);
  848. X            echo(stdin, 1);
  849. X            break;
  850. X        case pr_philbar:
  851. X            (void) printf("\n");
  852. X            echo(stdin, 1);
  853. X            break;
  854. X        case pr_dots:
  855. X            for (; dots < 10; dots++)
  856. X                (void) fputc('.', stdout);
  857. X            (void) fputc('\n', stdout);
  858. X            echo(stdin, 1);
  859. X            goto zz;
  860. X    }
  861. X    
  862. X    /* Save transfers to the logfile. */
  863. X    if (logf != NULL) {
  864. X        /* if a simple path is given, try to log the full path */
  865. X        if (rindex(remote, '/') == NULL && cwd != NULL) {
  866. X            (void) sprintf(str, "%s/%s", cwd, remote);
  867. X             cp = str;
  868. X        } else
  869. X            cp = remote;
  870. X        (void) fprintf(logf, "\t-> \"%s\" %s, %.2f %s", cp, direction, bs, bsstr);
  871. X    } 
  872. X#ifdef SYSLOG
  873. X    if (direction[0] == 'r')
  874. X        syslog (LOG_INFO, "%s %s %s as %s from %s (%ld bytes).",
  875. X            uinfo.username, direction, remote, local, hostname, bytes);
  876. X    else
  877. X        syslog (LOG_INFO, "%s %s %s as %s to %s (%ld bytes).",
  878. X            uinfo.username, direction, local, remote, hostname, bytes);
  879. X#endif
  880. X}   /* end_progress */
  881. X
  882. X
  883. X
  884. Xvoid close_file(FILE **fin, int filetype)
  885. X{
  886. X    if (*fin != NULL) {
  887. X        if (filetype == IS_FILE) {
  888. X            (void) fclose(*fin);
  889. X            *fin = NULL;
  890. X        } else if (filetype == IS_PIPE) {
  891. X            (void) pclose(*fin);
  892. X            *fin = NULL;
  893. X        }
  894. X    }
  895. X}    /* close_file */
  896. X
  897. X
  898. X
  899. X
  900. X/*ARGSUSED*/
  901. Xvoid abortsend SIG_PARAMS
  902. X{
  903. X    activemcmd = 0;
  904. X    abrtflag = 0;
  905. X    (void) fprintf(stderr, "\nSend aborted.\n");
  906. X    echo(stdin, 1);
  907. X    longjmp(sendabort, 1);
  908. X}    /* abortsend */
  909. X
  910. X
  911. X
  912. Xint sendrequest(char *cmd, char *local, char *remote)
  913. X{
  914. X    FILE                    *fin, *dout = NULL;
  915. X    sig_t                    oldintr, oldintp;
  916. X    string                    str;
  917. X    register int            c, d;
  918. X    struct stat                st;
  919. X    int                        filetype, result = NOERR;
  920. X    int                        do_reports = 0;
  921. X    char                    *mode;
  922. X    register char            *bufp;
  923. X
  924. X    dbprintf("cmd: %s;  rmt: %s;  loc: %s.\n", cmd, remote, local);
  925. X    oldintr = NULL;
  926. X    oldintp = NULL;
  927. X    mode = "w";
  928. X    bytes = file_size = 0L;
  929. X    if (setjmp(sendabort)) {
  930. X        while (cpend) {
  931. X            (void) getreply(0);
  932. X        }
  933. X        if (data >= 0) {
  934. X            (void) close(data);
  935. X            data = -1;
  936. X        }
  937. X        if (oldintr)
  938. X            (void) Signal(SIGINT, oldintr);
  939. X        if (oldintp)
  940. X            (void) Signal(SIGPIPE, oldintp);
  941. X        result = -1;
  942. X        goto xx;
  943. X    }
  944. X    oldintr = Signal(SIGINT, abortsend);
  945. X    file_size = -1;
  946. X    if (strcmp(local, "-") == 0)  {
  947. X        fin = stdin;
  948. X        filetype = IS_STREAM;
  949. X    } else if (*local == '|') {
  950. X        filetype = IS_PIPE;
  951. X        oldintp = Signal(SIGPIPE,SIG_IGN);
  952. X        fin = popen(local + 1, "r");
  953. X        if (fin == NULL) {
  954. X            PERROR("sendrequest", local + 1);
  955. X            (void) Signal(SIGINT, oldintr);
  956. X            (void) Signal(SIGPIPE, oldintp);
  957. X            result = -1;
  958. X            goto xx;
  959. X        }
  960. X    } else {
  961. X        filetype = IS_FILE;
  962. X        fin = fopen(local, "r");
  963. X        if (fin == NULL) {
  964. X            PERROR("sendrequest", local);
  965. X            (void) Signal(SIGINT, oldintr);
  966. X            result = -1;
  967. X            goto xx;
  968. X        }
  969. X        if (fstat(fileno(fin), &st) < 0 ||
  970. X            (st.st_mode&S_IFMT) != S_IFREG) {
  971. X            (void) fprintf(stdout, "%s: not a plain file.\n", local);
  972. X            (void) Signal(SIGINT, oldintr);
  973. X            (void) fclose(fin);
  974. X            result = -1;
  975. X            goto xx;
  976. X        }
  977. X        file_size = st.st_size;
  978. X    }
  979. X    if (initconn()) {
  980. X        (void) Signal(SIGINT, oldintr);
  981. X        if (oldintp)
  982. X            (void) Signal(SIGPIPE, oldintp);
  983. X        result = -1;
  984. X        close_file(&fin, filetype);
  985. X        goto xx;
  986. X    }
  987. X    if (setjmp(sendabort))
  988. X        goto Abort;
  989. X
  990. X    if (remote) {
  991. X        (void) sprintf(str, "%s %s", cmd, remote);
  992. X        if (command(str) != PRELIM) {
  993. X            (void) Signal(SIGINT, oldintr);
  994. X            if (oldintp)
  995. X                (void) Signal(SIGPIPE, oldintp);
  996. X            close_file(&fin, filetype);
  997. X            goto xx;
  998. X        }
  999. X    } else
  1000. X        if (command(cmd) != PRELIM) {
  1001. X            (void) Signal(SIGINT, oldintr);
  1002. X            if (oldintp)
  1003. X                (void) Signal(SIGPIPE, oldintp);
  1004. X            close_file(&fin, filetype);
  1005. X            goto xx;
  1006. X        }
  1007. X    dout = dataconn(mode);
  1008. X    if (dout == NULL)
  1009. X        goto Abort;
  1010. X    (void) gettimeofday(&start, (struct timezone *)0);
  1011. X    oldintp = Signal(SIGPIPE, SIG_IGN);
  1012. X    if (do_reports = (filetype == IS_FILE && NOT_VQUIET))
  1013. X        do_reports = start_progress(1, local);
  1014. X
  1015. X    switch (curtype) {
  1016. X
  1017. X    case TYPE_I:
  1018. X    case TYPE_L:
  1019. X        errno = d = 0;
  1020. X        while ((c = read(fileno(fin), xferbuf, (int)xferbufsize)) > 0) {
  1021. X            bytes += c;
  1022. X            for (bufp = xferbuf; c > 0; c -= d, bufp += d)
  1023. X                if ((d = write(fileno(dout), bufp, c)) <= 0)
  1024. X                    break;
  1025. X            /* Print progress indicator. */
  1026. X            if (do_reports)
  1027. X                do_reports = progress_report(0);
  1028. X        }
  1029. X        if (c < 0)
  1030. X            PERROR("sendrequest", local);
  1031. X        if (d <= 0) {
  1032. X            if (d == 0 && !creating)
  1033. X                (void) fprintf(stderr, "netout: write returned 0?\n");
  1034. X            else if (errno != EPIPE) 
  1035. X                PERROR("sendrequest", "netout");
  1036. X            bytes = -1;
  1037. X        }
  1038. X        break;
  1039. X
  1040. X    case TYPE_A:
  1041. X        next_report = xferbufsize;
  1042. X        while ((c = getc(fin)) != EOF) {
  1043. X            if (c == '\n') {
  1044. X                if (ferror(dout))
  1045. X                    break;
  1046. X                (void) putc('\r', dout);
  1047. X                bytes++;
  1048. X            }
  1049. X            (void) putc(c, dout);
  1050. X            bytes++;
  1051. X
  1052. X            /* Print progress indicator. */
  1053. X            if (do_reports && bytes > next_report)
  1054. X                do_reports = progress_report(0);
  1055. X        }
  1056. X        if (ferror(fin))
  1057. X            PERROR("sendrequest", local);
  1058. X        if (ferror(dout)) {
  1059. X            if (errno != EPIPE)
  1060. X                PERROR("sendrequest", "netout");
  1061. X            bytes = -1;
  1062. X        }
  1063. X        break;
  1064. X    }
  1065. XDone:
  1066. X    close_file(&fin, filetype);
  1067. X    if (dout)
  1068. X        (void) fclose(dout);
  1069. X    (void) getreply(0);
  1070. X    (void) Signal(SIGINT, oldintr);
  1071. X    if (oldintp)
  1072. X        (void) Signal(SIGPIPE, oldintp);
  1073. X    if (do_reports)
  1074. X        end_progress("sent", local, remote);
  1075. Xxx:
  1076. X    return (result);
  1077. XAbort:
  1078. X    result = -1;
  1079. X    if (!cpend)
  1080. X        goto xx;
  1081. X    if (data >= 0) {
  1082. X        (void) close(data);
  1083. X        data = -1;
  1084. X    }
  1085. X    goto Done;
  1086. X}    /* sendrequest */
  1087. X
  1088. X
  1089. X
  1090. X
  1091. X/*ARGSUSED*/
  1092. Xvoid abortrecv SIG_PARAMS
  1093. X{
  1094. X    activemcmd = 0;
  1095. X    abrtflag = 0;
  1096. X    (void) fprintf(stderr, 
  1097. X#ifdef TryAbort
  1098. X    "(abort)\n");
  1099. X#else
  1100. X    "\nAborting, please wait...");
  1101. X#endif
  1102. X    (void) fflush(stderr);
  1103. X    echo(stdin, 1);
  1104. X    longjmp(recvabort, 1);
  1105. X}    /* abortrecv */
  1106. X
  1107. X
  1108. X
  1109. X
  1110. Xvoid GetLSRemoteDir(char *remote, char *remote_dir)
  1111. X{
  1112. X    char *cp;
  1113. X
  1114. X    /*
  1115. X     * The ls() function can specify a directory to list along with ls flags,
  1116. X     * if it sends the flags first followed by the directory name.
  1117. X     *
  1118. X     * So far, we don't care about the remote directory being listed.  I put
  1119. X     * it now so I won't forget in case I need to do something with it later.
  1120. X     */
  1121. X    remote_dir[0] = 0;
  1122. X    if (remote != NULL) {
  1123. X        cp = index(remote, LS_FLAGS_AND_FILE);
  1124. X        if (cp == NULL)
  1125. X            (void) Strncpy(remote_dir, remote);
  1126. X        else {
  1127. X            *cp++ = ' ';
  1128. X            (void) Strncpy(remote_dir, cp);
  1129. X        }
  1130. X    }
  1131. X}    /* GetLSRemoteDir */
  1132. X
  1133. X
  1134. X
  1135. X
  1136. Xint AdjustLocalFileName(char *local)
  1137. X{
  1138. X    char *dir;
  1139. X
  1140. X    /*
  1141. X     * Make sure we are writing to a valid local path.
  1142. X     * First check the local directory, and see if we can write to it.
  1143. X     */
  1144. X    if (access(local, 2) < 0) {
  1145. X        dir = rindex(local, '/');
  1146. X
  1147. X        if (errno != ENOENT && errno != EACCES) {
  1148. X            /* Report an error if it's one we can't handle. */
  1149. X            PERROR("AdjustLocalFileName", local);
  1150. X            return -1;
  1151. X        }
  1152. X        /* See if we have write permission on this directory. */
  1153. X        if (dir != NULL) {
  1154. X            /* Special case: /filename. */
  1155. X            if (dir != local)
  1156. X                *dir = 0;
  1157. X            if (access(dir == local ? "/" : local, 2) < 0) {
  1158. X                /*
  1159. X                 *    We have a big long pathname, like /a/b/c/d,
  1160. X                 *    but see if we can write into the current
  1161. X                 *    directory and call the file ./d.
  1162. X                 */
  1163. X                if (access(".", 2) < 0) {
  1164. X                    (void) strcpy(local, " and .");
  1165. X                    goto noaccess;
  1166. X                }
  1167. X                (void) strcpy(local, dir + 1);    /* use simple filename. */
  1168. X            } else
  1169. X                *dir = '/';
  1170. X        } else {
  1171. X            /* We have a simple path name (file name only). */
  1172. X            if (access(".", 2) < 0) {
  1173. Xnoaccess:        PERROR("AdjustLocalFileName", local);
  1174. X                return -1;
  1175. X            }
  1176. X        }
  1177. X    }
  1178. X    return (NOERR);
  1179. X}    /* AdjustLocalFileName */
  1180. X    
  1181. X
  1182. X
  1183. Xint SetToAsciiForLS(int is_retr, int currenttype)
  1184. X{
  1185. X    int oldt = 0, oldv;
  1186. X
  1187. X    if (!is_retr) {
  1188. X        if (currenttype != TYPE_A) {
  1189. X            oldt = currenttype;
  1190. X            oldv = verbose;
  1191. X            if (!debug)
  1192. X                verbose = V_QUIET;
  1193. X            (void) setascii(0, NULL);
  1194. X            verbose = oldv;
  1195. X        }
  1196. X    }
  1197. X    return oldt;
  1198. X}    /* SetToAsciiForLS */
  1199. X
  1200. X
  1201. X
  1202. Xint IssueCommand(char *ftpcmd, char *remote)
  1203. X{
  1204. X    string str;
  1205. X    int result = NOERR;
  1206. X
  1207. X    if (remote)
  1208. X        (void) sprintf(str, "%s %s", ftpcmd, remote);
  1209. X    else
  1210. X        (void) Strncpy(str, ftpcmd);
  1211. X        
  1212. X    if (command(str) != PRELIM)
  1213. X        result = -1;
  1214. X    return (result);
  1215. X}    /* IssueCommand */
  1216. X
  1217. X
  1218. X
  1219. XFILE *OpenOutputFile(int filetype, char *local, char *mode, sig_t *oldintp)
  1220. X{
  1221. X    FILE *fout;
  1222. X
  1223. X    if (filetype == IS_STREAM) {
  1224. X        fout = stdout;
  1225. X    } else if (filetype == IS_PIPE) {
  1226. X        /* If it is a pipe, the pipecmd will have a | as the first char. */
  1227. X        ++local;
  1228. X        fout = popen(local, "w");
  1229. X        *oldintp = Signal(SIGPIPE, abortrecv);
  1230. X    } else {
  1231. X        fout = fopen(local, mode);
  1232. X    }
  1233. X    if (fout == NULL)
  1234. X        PERROR("OpenOutputFile", local);
  1235. X    return (fout);
  1236. X}    /* OpenOutputFile */
  1237. X
  1238. X
  1239. X
  1240. Xvoid ReceiveBinary(FILE *din, FILE *fout, int *do_reports, char *localfn)
  1241. X{
  1242. X    int                            c, d, do2;
  1243. X
  1244. X    errno = 0;            /* Clear any old error left around. */
  1245. X    do2 = *do_reports;    /* A slight optimization :-) */
  1246. X    bytes = 0;            /* Init the byte-transfer counter. */
  1247. X
  1248. X    for (;;) {
  1249. X        /* Read a block from the input stream. */
  1250. X        c = read(fileno(din), xferbuf, (int)xferbufsize);
  1251. X
  1252. X        /* If c is zero, then we've read the whole file. */
  1253. X        if (c == 0)
  1254. X            break;
  1255. X
  1256. X        /* Check for errors that may have occurred while reading. */
  1257. X        if (c < 0) {
  1258. X            /* Error occurred while reading. */
  1259. X            if (errno != EPIPE)
  1260. X                PERROR("ReceiveBinary", "netin");
  1261. X            bytes = -1;
  1262. X            break;
  1263. X        }
  1264. X
  1265. X        /* Write out the same block we just read in. */
  1266. X        d = write(fileno(fout), xferbuf, c);
  1267. X
  1268. X        /* Check for write errors. */
  1269. X        if ((d < 0) || (ferror(fout))) {
  1270. X            /* Error occurred while writing. */
  1271. X            PERROR("ReceiveBinary", "outfile");
  1272. X            break;
  1273. X        }
  1274. X        if (d < c) {
  1275. X            (void) fprintf(stderr, "%s: short write\n", localfn);
  1276. X            break;
  1277. X        }
  1278. X
  1279. X        /* Update the byte counter. */
  1280. X        bytes += (long) c;
  1281. X
  1282. X        /* Print progress indicator. */
  1283. X        if (do2 != 0)
  1284. X            do2 = progress_report(0);
  1285. X    }
  1286. X
  1287. X    *do_reports = do2;    /* Update the real do_reports variable. */
  1288. X}    /* ReceiveBinary */
  1289. X
  1290. X
  1291. X
  1292. Xvoid AddRedirLine(char *str2)
  1293. X{
  1294. X    register struct lslist *new;
  1295. X
  1296. X    (void) Strncpy(indataline, str2);
  1297. X    new = (struct lslist *) malloc((size_t) sizeof(struct lslist));
  1298. X    if (new != NULL) {
  1299. X        if ((new->string = NewString(str2)) != NULL) {
  1300. X               new->next = NULL;
  1301. X            if (lshead == NULL)
  1302. X                lshead = lstail = new;
  1303. X            else {
  1304. X                lstail->next = new;
  1305. X                lstail = new;
  1306. X            }
  1307. X        }
  1308. X    }
  1309. X}    /* AddRedirLine */
  1310. X
  1311. X
  1312. X
  1313. Xvoid ReceiveAscii(FILE *din, FILE *fout, int *do_reports, char *localfn, int
  1314. XlineMode)
  1315. X{
  1316. X    string str2;
  1317. X    int nchars = 0, c;
  1318. X    char *linePtr;
  1319. X    int do2 = *do_reports, stripped;
  1320. X
  1321. X    next_report = xferbufsize;
  1322. X    bytes = errno = 0;
  1323. X    if (lineMode) {
  1324. X        while ((linePtr = FGets(str2, din)) != NULL) {
  1325. X            bytes += (long) RemoveTrailingNewline(linePtr, &stripped);
  1326. X            if (is_ls || debug > 0)
  1327. X                AddRedirLine(linePtr);
  1328. X
  1329. X            /* Shutup while getting remote size and mod time. */
  1330. X            if (!buffer_only) {
  1331. X                c = fputs(linePtr, fout);
  1332. X
  1333. X                if (c != EOF) {
  1334. X                    if (stripped > 0)
  1335. X                        c = fputc('\n', fout);
  1336. X                }
  1337. X                if ((c == EOF) || (ferror(fout))) {
  1338. X                    PERROR("ReceiveAscii", "outfile");
  1339. X                    break;
  1340. X                }
  1341. X            }
  1342. X
  1343. X            /* Print progress indicator. */
  1344. X            if (do2 && bytes > next_report)
  1345. X                do2 = progress_report(0);
  1346. X        }
  1347. X    } else while ((c = getc(din)) != EOF) {
  1348. X        linePtr = str2;
  1349. X        while (c == '\r') {
  1350. X            bytes++;
  1351. X            if ((c = getc(din)) != '\n') {
  1352. X                if (ferror(fout))
  1353. X                    goto break2;
  1354. X                /* Shutup while getting remote size and mod time. */
  1355. X                if (!buffer_only)
  1356. X                    (void) putc('\r', fout);
  1357. X                if (c == '\0') {
  1358. X                    bytes++;
  1359. X                    goto contin2;
  1360. X                }
  1361. X                if (c == EOF)
  1362. X                    goto contin2;
  1363. X            }
  1364. X        }
  1365. X        /* Shutup while getting remote size and mod time. */
  1366. X        if (!buffer_only)
  1367. X            (void) putc(c, fout);
  1368. X        bytes++;
  1369. X        
  1370. X        /* Print progress indicator. */
  1371. X        if (do2 && bytes > next_report)
  1372. X            do2 = progress_report(0);
  1373. X
  1374. X        /* No seg violations, please */
  1375. X        if (nchars < sizeof(str2) - 1) {
  1376. X             *linePtr++ = c;  /* build redir string */
  1377. X            nchars++;
  1378. X        }
  1379. X
  1380. X   contin2:
  1381. X        /* Save the input line in the buffer for recall later. */
  1382. X        if (c == '\n' && is_ls) {
  1383. X            *--linePtr = 0;
  1384. X            AddRedirLine(str2);
  1385. X            nchars = 0;
  1386. X        }
  1387. X       
  1388. X    }    /* while ((c = getc(din)) != EOF) */
  1389. Xbreak2:
  1390. X    if (ferror(din)) {
  1391. X        if (errno != EPIPE)
  1392. X            PERROR("ReceiveAscii", "netin");
  1393. X        bytes = -1;
  1394. X    }
  1395. X    if (ferror(fout)) {
  1396. X        if (errno != EPIPE)
  1397. X            PERROR("ReceiveAscii", localfn);
  1398. X    }
  1399. X    *do_reports = do2;
  1400. X}    /* ReceiveAscii */
  1401. X
  1402. X
  1403. X
  1404. Xvoid CloseOutputFile(FILE *f, int filetype, char *name, time_t mt)
  1405. X{
  1406. X    struct utimbuf                ut;
  1407. X
  1408. X    if (f != NULL) {
  1409. X        (void) fflush(f);
  1410. X        if (filetype == IS_FILE) {
  1411. X            (void) fclose(f);
  1412. X            if (mt != (time_t)0) {
  1413. X                ut.actime = ut.modtime = mt;
  1414. X                (void) utime(name, &ut);
  1415. X            }
  1416. X        } else if (filetype == IS_PIPE) {
  1417. X            (void)pclose(f);
  1418. X        }
  1419. X    }
  1420. X}    /* close_file */
  1421. X
  1422. X
  1423. X
  1424. Xvoid ResetOldType(int oldtype)
  1425. X{
  1426. X    int oldv;
  1427. X
  1428. X    if (oldtype) {
  1429. X        oldv = verbose;
  1430. X        if (!debug)
  1431. X            verbose = V_QUIET;
  1432. X        if (oldtype == TYPE_I)
  1433. X            (void) setbinary(0, NULL);
  1434. X        verbose = oldv;
  1435. X    }
  1436. X}    /* ResetOldType */
  1437. X
  1438. X
  1439. X
  1440. Xint FileType(char *fname)
  1441. X{
  1442. X    int ft = IS_FILE;
  1443. X
  1444. X    if (strcmp(fname, "-") == 0)
  1445. X        ft = IS_STREAM;
  1446. X    else if (*fname == '|')
  1447. X        ft = IS_PIPE;
  1448. X    return (ft);
  1449. X}    /* FileType */
  1450. X
  1451. X
  1452. X
  1453. X
  1454. Xvoid CloseData(void) {
  1455. X    if (data >= 0) {
  1456. X        (void) close(data);
  1457. X        data = -1;
  1458. X    }
  1459. X}    /* CloseData */
  1460. X
  1461. X
  1462. X
  1463. X
  1464. Xint recvrequest(char *cmd, char *local, char *remote, char *mode)
  1465. X{
  1466. X    FILE                        *fout = NULL, *din = NULL;
  1467. X    sig_t                        oldintr = NULL, oldintp = NULL;
  1468. X    int                            oldtype = 0, is_retr;
  1469. X    int                            nfnd;
  1470. X    char                        msg;
  1471. X    struct fd_set                mask;
  1472. X    int                            filetype, do_reports = 0;
  1473. X    string                        remote_dir;
  1474. X    time_t                        remfTime = 0;
  1475. X    int                            result = -1;
  1476. X
  1477. X    dbprintf("---> cmd: %s;  rmt: %s;  loc: %s;  mode: %s.\n",
  1478. X        cmd, remote, local, mode);
  1479. X    is_retr = strcmp(cmd, "RETR") == 0;
  1480. X
  1481. X    GetLSRemoteDir(remote, remote_dir);
  1482. X    if ((filetype = FileType(local)) == IS_FILE) {
  1483. X        if (AdjustLocalFileName(local))
  1484. X            goto xx;
  1485. X    }
  1486. X
  1487. X    file_size = -1;
  1488. X    if (filetype == IS_FILE)
  1489. X        file_size = GetDateAndSize(remote, (unsigned long *) &remfTime);
  1490. X
  1491. X    if (initconn())
  1492. X        goto xx;
  1493. X
  1494. X    oldtype = SetToAsciiForLS(is_retr, curtype);
  1495. X
  1496. X    if (IssueCommand(cmd, remote))
  1497. X        goto xx;
  1498. X    
  1499. X    if ((fout = OpenOutputFile(filetype, local, mode, &oldintp)) == NULL)
  1500. X        goto xx;
  1501. X
  1502. X    if ((din = dataconn("r")) == NULL)
  1503. X        goto Abort;
  1504. X
  1505. X    do_reports = NOT_VQUIET && is_retr && filetype == IS_FILE;
  1506. X    if (do_reports)
  1507. X        do_reports = start_progress(0, local);
  1508. X
  1509. X    if (setjmp(recvabort)) {
  1510. X#ifdef TryAbort
  1511. X        goto Abort;
  1512. X#else
  1513. X        /* Just read the rest of the stream without doing anything with
  1514. X         * the results.
  1515. X         */
  1516. X        (void) Signal(SIGINT, SIG_IGN);
  1517. X        (void) Signal(SIGPIPE, SIG_IGN);    /* Don't bug us while aborting. */
  1518. X        while (read(fileno(din), xferbuf, (int)xferbufsize) > 0)
  1519. X            ;
  1520. X        (void) fprintf(stderr, "\rAborted.                   \n");
  1521. X#endif
  1522. X    } else {
  1523. X        oldintr = Signal(SIGINT, abortrecv);
  1524. X
  1525. X        if (curtype == TYPE_A)
  1526. X            ReceiveAscii(din, fout, &do_reports, local, 1);
  1527. X        else
  1528. X            ReceiveBinary(din, fout, &do_reports, local);
  1529. X        result = NOERR;
  1530. X        /* Don't interrupt us now, since we finished successfully. */
  1531. X        (void) Signal(SIGPIPE, SIG_IGN);
  1532. X        (void) Signal(SIGINT, SIG_IGN);
  1533. X    }    
  1534. X    (void) getreply(0);
  1535. X    ResetOldType(oldtype);
  1536. X
  1537. X    goto xx;
  1538. X
  1539. XAbort:
  1540. X
  1541. X/* Abort using RFC959 recommended IP,SYNC sequence  */
  1542. X
  1543. X    (void) Signal(SIGPIPE, SIG_IGN);    /* Don't bug us while aborting. */
  1544. X    (void) Signal(SIGINT, SIG_IGN);
  1545. X    ResetOldType(oldtype);
  1546. X    if (!cpend || !cout) goto xx;
  1547. X    (void) fprintf(cout,"%c%c",IAC,IP);
  1548. X    (void) fflush(cout); 
  1549. X    msg = IAC;
  1550. X/* send IAC in urgent mode instead of DM because UNIX places oob mark */
  1551. X/* after urgent byte rather than before as now is protocol            */
  1552. X    if (send(fileno(cout),&msg,1,MSG_OOB) != 1)
  1553. X        PERROR("recvrequest", "abort");
  1554. X    (void) fprintf(cout,"%cABOR\r\n",DM);
  1555. X    (void) fflush(cout);
  1556. X    FD_ZERO(&mask);
  1557. X    FD_SET(fileno(cin), &mask);
  1558. X    if (din)
  1559. X        FD_SET(fileno(din), &mask);
  1560. X    if ((nfnd = empty(&mask,10)) <= 0) {
  1561. X        if (nfnd < 0)
  1562. X            PERROR("recvrequest", "abort");
  1563. X        lostpeer(0);
  1564. X    }
  1565. X    if (din && FD_ISSET(fileno(din), &mask)) {
  1566. X        while ((read(fileno(din), xferbuf, xferbufsize)) > 0)
  1567. X            ;
  1568. X    }
  1569. X    if ((getreply(0)) == ERROR && code == 552) { /* needed for nic style abort */
  1570. X        CloseData();
  1571. X        (void) getreply(0);
  1572. X    }
  1573. X    (void) getreply(0);
  1574. X    result = -1;
  1575. X    CloseData();
  1576. X
  1577. Xxx:
  1578. X    CloseOutputFile(fout, filetype, local, remfTime);
  1579. X    dbprintf("outfile closed.\n");
  1580. X    if (din)
  1581. X        (void) fclose(din);
  1582. X    if (do_reports)
  1583. X        end_progress("received", local, remote);
  1584. X    if (oldintr)
  1585. X        (void) Signal(SIGINT, oldintr);
  1586. X    if (oldintp)
  1587. X        (void) Signal(SIGPIPE, oldintp);
  1588. X    dbprintf("recvrequest result = %d.\n", result);
  1589. X    return (result);
  1590. X}    /* recvrequest */
  1591. X
  1592. X
  1593. X
  1594. X
  1595. X/*
  1596. X * Need to start a listen on the data channel
  1597. X * before we send the command, otherwise the
  1598. X * server's connect may fail.
  1599. X */
  1600. X
  1601. Xint initconn(void)
  1602. X{
  1603. X    register char        *p, *a;
  1604. X    int                    result, len, tmpno = 0;
  1605. X    int                    on = 1, rval;
  1606. X    string                str;
  1607. X    sig_t                oldintr;
  1608. X
  1609. X    oldintr = Signal(SIGINT, SIG_IGN);
  1610. Xnoport:
  1611. X    data_addr = myctladdr;
  1612. X    if (sendport)
  1613. X        data_addr.sin_port = 0;    /* let system pick one */ 
  1614. X    if (data != -1)
  1615. X        (void) close (data);
  1616. X    data = socket(AF_INET, SOCK_STREAM, 0);
  1617. X    if (data < 0) {
  1618. X        PERROR("initconn", "socket");
  1619. X        if (tmpno)
  1620. X            sendport = 1;
  1621. X        rval = 1;  goto Return;
  1622. X    }
  1623. X    if (!sendport)
  1624. X        if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) {
  1625. X            PERROR("initconn", "setsockopt (reuse address)");
  1626. X            goto bad;
  1627. X        }
  1628. X#ifdef SOCKS
  1629. X    if (Rbind(data, (struct sockaddr *)&data_addr, sizeof (data_addr), hisctladdr.sin_addr.s_addr) < 0) {
  1630. X#else
  1631. X    if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
  1632. X#endif
  1633. X        PERROR("initconn", "bind");
  1634. X        goto bad;
  1635. X    }
  1636. X    if (options & SO_DEBUG &&
  1637. X        setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
  1638. X        PERROR("initconn", "setsockopt (ignored)");
  1639. X    len = sizeof (data_addr);
  1640. X    if (Getsockname(data, (char *)&data_addr, &len) < 0) {
  1641. X        PERROR("initconn", "getsockname");
  1642. X        goto bad;
  1643. X    }
  1644. X#ifdef SOCKS 
  1645. X    if (Rlisten(data, 1) < 0)
  1646. X#else
  1647. X    if (listen(data, 1) < 0)
  1648. X#endif
  1649. X        PERROR("initconn", "listen");
  1650. X    if (sendport) {
  1651. X        a = (char *)&data_addr.sin_addr;
  1652. X        p = (char *)&data_addr.sin_port;
  1653. X#define UC(x) (int) (((int) x) & 0xff)
  1654. X        (void) sprintf(str, "PORT %d,%d,%d,%d,%d,%d",
  1655. X            UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
  1656. X        result = command(str);
  1657. X        if (result == ERROR && sendport == -1) {
  1658. X            sendport = 0;
  1659. X            tmpno = 1;
  1660. X            goto noport;
  1661. X        }
  1662. X        rval = (result != COMPLETE);  goto Return;
  1663. X    }
  1664. X    if (tmpno)
  1665. X        sendport = 1;
  1666. X    rval = 0;  goto Return;
  1667. Xbad:
  1668. X    (void) close(data), data = -1;
  1669. X    if (tmpno)
  1670. X        sendport = 1;
  1671. X    rval = 1;
  1672. XReturn:
  1673. X    (void) Signal(SIGINT, oldintr);
  1674. X    return (rval);
  1675. X}    /* initconn */
  1676. X
  1677. X
  1678. X
  1679. X
  1680. XFILE *
  1681. Xdataconn(char *mode)
  1682. X{
  1683. X    struct sockaddr_in from;
  1684. X    FILE *fp;
  1685. X    int s, fromlen = sizeof (from);
  1686. X
  1687. X#ifdef SOCKS
  1688. X    s = Raccept(data, (struct sockaddr *) &from, &fromlen);
  1689. X#else
  1690. X    s = accept(data, (struct sockaddr *) &from, &fromlen);
  1691. X#endif
  1692. X    if (s < 0) {
  1693. X        PERROR("dataconn", "accept");
  1694. X        (void) close(data), data = -1;
  1695. X        fp = NULL;
  1696. X    } else {
  1697. X        (void) close(data);
  1698. X        data = s;
  1699. X        fp = fdopen(data, mode);
  1700. X    }
  1701. X    return (fp);
  1702. X}    /* dataconn */
  1703. X
  1704. X/* eof ftp.c */
  1705. END_OF_FILE
  1706.   if test 35792 -ne `wc -c <'ftp.c'`; then
  1707.     echo shar: \"'ftp.c'\" unpacked with wrong size!
  1708.   fi
  1709.   # end of 'ftp.c'
  1710. fi
  1711. if test -f 'getpass.c' -a "${1}" != "-c" ; then 
  1712.   echo shar: Will not clobber existing file \"'getpass.c'\"
  1713. else
  1714.   echo shar: Extracting \"'getpass.c'\" \(2638 characters\)
  1715.   sed "s/^X//" >'getpass.c' <<'END_OF_FILE'
  1716. X/* Getpass.c */
  1717. X
  1718. X/*  $RCSfile: getpass.c,v $
  1719. X *  $Revision: 14020.11 $
  1720. X *  $Date: 93/05/21 05:44:36 $
  1721. X */
  1722. X
  1723. X#include "sys.h"
  1724. X#include <stdio.h>
  1725. X#include <signal.h>
  1726. X
  1727. X#include "util.h"
  1728. X#include "cmds.h"
  1729. X#include "getpass.h"
  1730. X#include "copyright.h"
  1731. X
  1732. X#ifndef GETPASS
  1733. X
  1734. X#ifndef NO_UNISTDH
  1735. X#    include <unistd.h>
  1736. X#endif
  1737. X
  1738. X#include <sys/ioctl.h>
  1739. X
  1740. X#ifdef TERMIOS
  1741. X#        include <termios.h>
  1742. X#else
  1743. X#    ifdef SGTTYB
  1744. X#        include <sgtty.h>
  1745. X#    else
  1746. X#        include <termio.h>
  1747. X#    endif
  1748. X#endif /* !TERMIOS */
  1749. X
  1750. X#ifdef STRICT_PROTOS
  1751. Xint ioctl(int, int, ...);
  1752. X#endif
  1753. X
  1754. X#endif    /* GETPASS */
  1755. X
  1756. X
  1757. X
  1758. X
  1759. Xvoid echo(FILE *fp, int on)
  1760. X{
  1761. X#ifndef GETPASS        /* Otherwise just do nothing which is ok. */
  1762. X
  1763. X#ifdef TERMIOS
  1764. X    static struct termios orig, noecho, *tp;
  1765. X#else
  1766. X#    ifdef SGTTYB
  1767. X    static struct sgttyb orig, noecho, *tp;
  1768. X#    else
  1769. X    static struct termio orig, noecho, *tp;
  1770. X#    endif
  1771. X#endif
  1772. X    static int state = 0;
  1773. X    int fd = fileno(fp);
  1774. X    
  1775. X    if (!isatty(fd))
  1776. X        return;
  1777. X
  1778. X    if (state == 0) {
  1779. X#ifdef TERMIOS
  1780. X        if (tcgetattr(fd, &orig) < 0)
  1781. X            PERROR("echo", "tcgetattr");
  1782. X        noecho = orig;
  1783. X        noecho.c_lflag &= ~ECHO;
  1784. X#else
  1785. X#    ifdef SGTTYB
  1786. X        if (ioctl(fd, TIOCGETP, &orig) < 0)
  1787. X            PERROR("echo", "ioctl");
  1788. X        noecho = orig;
  1789. X        noecho.sg_flags &= ~ECHO;
  1790. X#    else
  1791. X        if (ioctl(fd, TCGETA, &orig) < 0)
  1792. X            PERROR("echo", "ioctl");
  1793. X        noecho = orig;
  1794. X        noecho.c_lflag &= ~ECHO;
  1795. X#    endif
  1796. X#endif
  1797. X        state = 1;
  1798. X    }
  1799. X    tp = NULL;
  1800. X    if (on && state == 2) {
  1801. X        /* Turn echo back on. */
  1802. X        tp = &orig;
  1803. X        state = 1;
  1804. X    } else if (!on && state == 1) {
  1805. X        /* Turn echo off. */
  1806. X        tp = &noecho;
  1807. X        state = 2;
  1808. X    }
  1809. X    if (tp != NULL) {
  1810. X#ifdef TERMIOS
  1811. X        if (tcsetattr(fd, TCSAFLUSH, tp) < 0)
  1812. X            PERROR("echo", "tcsetattr");
  1813. X#else
  1814. X#    ifdef SGTTYB
  1815. X        if (ioctl(fd, TIOCSETP, tp) < 0)
  1816. X            PERROR("echo", "ioctl");
  1817. X#    else
  1818. X        if (ioctl(fd, TCSETA, tp) < 0)
  1819. X            PERROR("echo", "ioctl");
  1820. X#    endif
  1821. X#endif    /* !TERMIOS */
  1822. X    }
  1823. X
  1824. X#endif    /* GETPASS */
  1825. X}    /* echo */
  1826. X
  1827. X
  1828. X
  1829. X#ifndef GETPASS
  1830. X
  1831. Xchar *Getpass(char *promptstr)
  1832. X{
  1833. X    register int ch;
  1834. X    register char *p;
  1835. X    FILE *fp, *outfp;
  1836. X    sig_t oldintr;
  1837. X    static char buf[kMaxPassLen + 1];
  1838. X
  1839. X    /*
  1840. X     * read and write to /dev/tty if possible; else read from
  1841. X     * stdin and write to stderr.
  1842. X     */
  1843. X    if ((outfp = fp = fopen("/dev/tty", "w+")) == NULL) {
  1844. X        outfp = stderr;
  1845. X        fp = stdin;
  1846. X    }
  1847. X    oldintr = Signal(SIGINT, SIG_IGN);
  1848. X    echo(fp, 0);        /* Turn echoing off. */
  1849. X    (void) fputs(promptstr, outfp);
  1850. X    (void) rewind(outfp);            /* implied flush */
  1851. X    for (p = buf; (ch = getc(fp)) != EOF && ch != '\n';)
  1852. X        if (p < buf + kMaxPassLen)
  1853. X            *p++ = ch;
  1854. X    *p = '\0';
  1855. X    (void)write(fileno(outfp), "\n", 1);
  1856. X    echo(fp, 1);
  1857. X    (void) Signal(SIGINT, oldintr);
  1858. X    if (fp != stdin)
  1859. X        (void)fclose(fp);
  1860. X    return(buf);
  1861. X}    /* Getpass */
  1862. X
  1863. X#endif /* GETPASS */
  1864. X
  1865. X/* eof Getpass.c */
  1866. END_OF_FILE
  1867.   if test 2638 -ne `wc -c <'getpass.c'`; then
  1868.     echo shar: \"'getpass.c'\" unpacked with wrong size!
  1869.   fi
  1870.   # end of 'getpass.c'
  1871. fi
  1872. if test -f 'open.c' -a "${1}" != "-c" ; then 
  1873.   echo shar: Will not clobber existing file \"'open.c'\"
  1874. else
  1875.   echo shar: Extracting \"'open.c'\" \(16396 characters\)
  1876.   sed "s/^X//" >'open.c' <<'END_OF_FILE'
  1877. X/* open.c */
  1878. X
  1879. X/*  $RCSfile: open.c,v $
  1880. X *  $Revision: 1.1 $
  1881. X *  $Date: 93/07/09 11:27:07 $
  1882. X */
  1883. X
  1884. X#include "sys.h"
  1885. X
  1886. X#include <sys/types.h>
  1887. X#include <sys/param.h>
  1888. X#include <sys/socket.h>
  1889. X#include <netdb.h>
  1890. X#include <netinet/in.h>
  1891. X#include <arpa/ftp.h>
  1892. X
  1893. X#include <string.h>
  1894. X#include <errno.h>
  1895. X
  1896. X#ifndef NO_UNISTDH
  1897. X#    include <unistd.h>
  1898. X#endif
  1899. X
  1900. X#include "util.h"
  1901. X#include "open.h"
  1902. X#include "cmds.h"
  1903. X#include "ftp.h"
  1904. X#include "ftprc.h"
  1905. X#include "main.h"
  1906. X#include "defaults.h"
  1907. X#include "copyright.h"
  1908. X
  1909. X/* open.c globals */
  1910. Xint                    remote_is_unix;        /* TRUE if remote host is unix. */
  1911. Xint                    auto_binary = dAUTOBINARY;
  1912. Xint                    anon_open = dANONOPEN;
  1913. X                                        /* Anonymous logins by default? */
  1914. Xint                    connected = 0;        /* TRUE if connected to server */
  1915. X                                        /* If TRUE, set binary each connection. */
  1916. XHostname            hostname;            /* Name of current host */
  1917. X#ifdef GATEWAY
  1918. Xstring                gateway;            /* node name of firewall gateway */
  1919. Xstring                gate_login;            /* login at firewall gateway */
  1920. X#endif
  1921. X
  1922. X/* open.c externs */
  1923. Xextern char                    *reply_string, *line, *Optarg, *margv[];
  1924. Xextern int                    Optind, margc, verbose;
  1925. Xextern long                    eventnumber;
  1926. Xextern struct servent        serv;
  1927. Xextern FILE                    *cout;
  1928. Xextern string                anon_password;
  1929. X
  1930. X/* Given a pointer to an OpenOptions (structure containing all variables
  1931. X * that can be set from the command line), this routine makes sure all
  1932. X * the variables have valid values by setting them to their defaults.
  1933. X */
  1934. Xint InitOpenOptions(OpenOptions *openopt)
  1935. X{
  1936. X    /* How do you want to open a site if neither -a or -u are given?
  1937. X     * anon_open is true (default to anonymous login), unless
  1938. X     * defaults.h was edited to set dANONOPEN to 0 instead.
  1939. X     */
  1940. X    openopt->openmode = anon_open ? openImplicitAnon : openImplicitUser;
  1941. X
  1942. X    /* Normally you don't want to ignore the entry in your netrc. */
  1943. X    openopt->ignore_rc = 0;
  1944. X
  1945. X    /* Set the default delay if the user specifies redial mode without
  1946. X     * specifying the redial delay.
  1947. X     */
  1948. X    openopt->redial_delay = dREDIALDELAY;
  1949. X
  1950. X    /* Normally, you only want to try once. If you specify redial mode,
  1951. X     * this is changed.
  1952. X     */
  1953. X    openopt->max_dials = 1;
  1954. X    
  1955. X    /* You don't want to cat the file to stdout by default. */
  1956. X    openopt->ftpcat = NO_FTPCAT;
  1957. X
  1958. X    /* Setup the port number to try. */
  1959. X#ifdef dFTP_PORT
  1960. X    /* If dFTP_PORT is defined, we use a different port number by default
  1961. X     * than the one supplied in the servent structure.
  1962. X     */
  1963. X    openopt->port = dFTP_PORT;
  1964. X    /* Make sure the correct byte order is supplied! */
  1965. X    openopt->port = htons(openopt->port);
  1966. X#else
  1967. X    /* Use the port number supplied by the operating system's servent
  1968. X     * structure.
  1969. X     */
  1970. X    openopt->port = serv.s_port;
  1971. X#endif
  1972. X
  1973. X    /* We are not in colon-mode (yet). */
  1974. X    openopt->colonmodepath[0] = 0;
  1975. X
  1976. X    /* Set the hostname to a null string, since there is no default host. */
  1977. X    openopt->hostname[0] = 0;
  1978. X    
  1979. X    /* Set the opening directory path to a null string. */
  1980. X    openopt->cdpath[0] = 0;
  1981. X}    /* InitOpenOptions */
  1982. X
  1983. X
  1984. X
  1985. X
  1986. X/* This is responsible for parsing the command line and setting variables
  1987. X * in the OpenOptions structure according to the user's flags.
  1988. X */
  1989. X
  1990. Xint GetOpenOptions(int argc, char **argv, OpenOptions *openopt)
  1991. X{
  1992. X    int                    opt;
  1993. X
  1994. X    /* First setup the openopt variables. */
  1995. X    InitOpenOptions(openopt);
  1996. X
  1997. X    /* Tell Getopt() that we want to start over with a new command. */
  1998. X    Getopt_Reset();
  1999. X    while ((opt = Getopt(argc, argv, "aiup:rd:g:cm")) >= 0) {
  2000. X        switch (opt) {        
  2001. X            case 'a':
  2002. X                /* User wants to open anonymously. */
  2003. X                openopt->openmode = openExplicitAnon;
  2004. X                break;
  2005. X                
  2006. X            case 'u':
  2007. X                /* User wants to open with a login and password. */
  2008. X                openopt->openmode = openExplicitUser;
  2009. X                break;
  2010. X                
  2011. X            case 'i':
  2012. X                /* User wants to ignore the entry in the netrc. */
  2013. X                openopt->ignore_rc = 1;
  2014. X                break;
  2015. X                
  2016. X            case 'p':
  2017. X                /* User supplied a port number different from the default
  2018. X                 * ftp port.
  2019. X                 */
  2020. X                openopt->port = atoi(Optarg);
  2021. X                if (openopt->port <= 0) {
  2022. X                    /* Probably never happen, but just in case. */
  2023. X                    (void) printf("%s: bad port number (%s).\n", argv[0], Optarg);
  2024. X                    goto usage;
  2025. X                }
  2026. X                /* Must ensure that the port is in the correct byte order! */
  2027. X                openopt->port = htons(openopt->port);
  2028. X                break;
  2029. X                
  2030. X            case 'd':
  2031. X                /* User supplied a delay (in seconds) that differs from
  2032. X                 * the default.
  2033. X                 */
  2034. X                openopt->redial_delay = atoi(Optarg);
  2035. X                break;
  2036. X                
  2037. X            case 'g':
  2038. X                /* User supplied an upper-bound on the number of redials
  2039. X                 * to try.
  2040. X                 */
  2041. X                openopt->max_dials = atoi(Optarg);
  2042. X                break;
  2043. X
  2044. X            case 'r':
  2045. X                openopt->max_dials = -1;
  2046. X                break;
  2047. X
  2048. X            case 'm':
  2049. X                /* ftpcat mode is only available from your shell command-line,
  2050. X                 * not from the ncftp shell.  Do that yourself with 'more zz'.
  2051. X                 */
  2052. X                if (eventnumber == 0L) {
  2053. X                    /* If eventnumber is zero, then we were called directly
  2054. X                     * from main(), and before the ftp shell has started.
  2055. X                     */
  2056. X                    openopt->ftpcat = FTPMORE;
  2057. X                    /* ftpcat mode is really ftpmore mode. */
  2058. X                    break;
  2059. X                } else {
  2060. X                    fprintf(stderr,
  2061. X"You can only use this form of colon-mode (-m) from your shell command line.\n\
  2062. XTry 'ncftp -m wuarchive.wustl.edu:/README'\n");
  2063. X                    goto usage;
  2064. X                }
  2065. X                break;
  2066. X
  2067. X            case 'c':
  2068. X                /* ftpcat mode is only available from your shell command-line,
  2069. X                 * not from the ncftp shell.  Do that yourself with 'get zz -'.
  2070. X                 */
  2071. X                if (eventnumber == 0L) {
  2072. X                    /* If eventnumber is zero, then we were called directly
  2073. X                     * from main(), and before the ftp shell has started.
  2074. X                     */
  2075. X                    openopt->ftpcat = FTPCAT;
  2076. X                    break;
  2077. X                } else {
  2078. X                    fprintf(stderr,
  2079. X"You can only use ftpcat/colon-mode from your shell command line.\n\
  2080. XTry 'ncftp -c wuarchive.wustl.edu:/README > file.'\n");
  2081. X                    goto usage;
  2082. X                }
  2083. X                break;
  2084. X                
  2085. X            default:
  2086. X            usage:
  2087. X                return USAGE;
  2088. X        }
  2089. X    }
  2090. X
  2091. X    if (argv[Optind] == NULL) {
  2092. X        /* No host was supplied.  Print out the list of sites we know
  2093. X         * about and ask the user for one.
  2094. X         */
  2095. X        PrintSiteList();
  2096. X        (void) Gets("(site to open) ", openopt->hostname, sizeof(openopt->hostname));
  2097. X        /* Make sure the user just didn't hit return, in which case we
  2098. X         * just give up and go home.
  2099. X         */
  2100. X        if (openopt->hostname[0] == 0)
  2101. X            goto usage;
  2102. X    } else {
  2103. X        /* The user gave us a host to open.  */
  2104. X        (void) Strncpy(openopt->hostname, argv[Optind]);
  2105. X    }
  2106. X    return NOERR;
  2107. X}    /* GetOpenOptions */
  2108. X
  2109. X
  2110. X
  2111. X
  2112. X/* This examines the format of the string stored in the hostname
  2113. X * field of the OpenOptions, and sees if has to strip out a colon-mode
  2114. X * pathname (to store in the colonmodepath field).  Since colon-mode
  2115. X * is run quietly (without any output being generated), we init the
  2116. X * login_verbosity variable here to quiet if we are running colon-mode.
  2117. X */
  2118. Xint CheckForColonMode(OpenOptions *openopt, int *login_verbosity)
  2119. X{
  2120. X    char *path;
  2121. X
  2122. X    /* Usually the user doesn't supply hostname in colon-mode format,
  2123. X     * and wants to interactively browse the remote host, so set the
  2124. X     * login_verbosity to whatever it is set to now.
  2125. X     */
  2126. X    *login_verbosity = verbose;
  2127. X
  2128. X    if ((path = index(openopt->hostname, ':')) != NULL) {
  2129. X        *path++ = 0;
  2130. X        (void) Strncpy(openopt->colonmodepath, path);
  2131. X        
  2132. X        /* But if the user does use colon-mode, we want to do our business
  2133. X         * and leave, without all the login messages, etc., so set
  2134. X         * login_verbosity to quiet so we won't print anything until
  2135. X         * we finish.  Colon-mode can be specified from the shell command
  2136. X         * line, so we would like to be able to execute ncftp as a one
  2137. X         * line command from the shell without spewing gobs of output.
  2138. X         */
  2139. X        *login_verbosity = V_QUIET;
  2140. X    } else if (openopt->ftpcat != 0) {
  2141. X        /* User specified ftpcat mode, but didn't supply the host:file. */
  2142. X        (void) fprintf(stderr, "You didn't use colon mode correctly.\n\
  2143. XIf you use -c or -m, you need to do something like this:\n\
  2144. X    ncftp -c wuarchive.wustl.edu:/pub/README (to cat this file to stdout).\n");
  2145. X        return USAGE;
  2146. X    }
  2147. X    return NOERR;
  2148. X}    /* CheckForColonMode */
  2149. X
  2150. X
  2151. X
  2152. X
  2153. X/* All this short routine does is to hookup a socket to either the
  2154. X * remote host or the firewall gateway host.
  2155. X */
  2156. Xint HookupToRemote(OpenOptions *openopt)
  2157. X{
  2158. X    int hErr;
  2159. X
  2160. X#ifdef GATEWAY
  2161. X    /* Try connecting to the gateway host. */
  2162. X    if (*gateway)
  2163. X        hErr = hookup(gateway, openopt->port);
  2164. X    else
  2165. X#endif
  2166. X        hErr = hookup(openopt->hostname, openopt->port);
  2167. X    
  2168. X    return hErr;
  2169. X}    /* HookupToRemote */
  2170. X
  2171. X
  2172. X
  2173. X
  2174. Xvoid CheckRemoteSystemType(OpenOptions *openopt)
  2175. X{
  2176. X    int tmpverbose;
  2177. X    char *cp, c;
  2178. X
  2179. X    /* As of this writing, UNIX is pretty much standard. */
  2180. X    remote_is_unix = 1;
  2181. X
  2182. X    /* Do a SYSTem command quietly. */
  2183. X    tmpverbose = verbose;
  2184. X    verbose = V_QUIET;
  2185. X    if (command("SYST") == COMPLETE) {
  2186. X        if (tmpverbose == V_VERBOSE) {        
  2187. X            /* Find the system type embedded in the reply_string,
  2188. X             * and separate it from the rest of the junk.
  2189. X             */
  2190. X            cp = index(reply_string+4, ' ');
  2191. X            if (cp == NULL)
  2192. X                cp = index(reply_string+4, '\r');
  2193. X            if (cp) {
  2194. X                if (cp[-1] == '.')
  2195. X                    cp--;
  2196. X                c = *cp;
  2197. X                *cp = '\0';
  2198. X            }
  2199. X
  2200. X            (void) printf("Remote system type is %s.\n",
  2201. X                reply_string+4);
  2202. X            if (cp)
  2203. X                *cp = c;
  2204. X        }
  2205. X        remote_is_unix = !strncmp(reply_string + 4, "UNIX", (size_t) 4);
  2206. X    }
  2207. X
  2208. X    /* Set to binary mode if any of the following are true:
  2209. X     * (a) The user has auto-binary set;
  2210. X     * (b) The user is using colon-mode;
  2211. X     * (c) The reply-string from SYST said it was UNIX with 8-bit chars.
  2212. X     */
  2213. X    if (auto_binary || openopt->colonmodepath[0]
  2214. X        || !strncmp(reply_string, "215 UNIX Type: L8", (size_t) 17)) {
  2215. X        (void) _settype("binary");
  2216. X        if (tmpverbose > V_TERSE)
  2217. X            (void) printf("Using binary mode to transfer files.\n");
  2218. X    }
  2219. X
  2220. X    /* Print a warning for that (extremely) rare Tenex machine. */
  2221. X    if (tmpverbose >= V_ERRS && 
  2222. X        !strncmp(reply_string, "215 TOPS20", (size_t) 10)) {
  2223. X        (void) printf(
  2224. X"Remember to set tenex mode when transfering _binary_ files from this machine.\n");
  2225. X    }
  2226. X    verbose = tmpverbose;
  2227. X}    /* CheckRemoteSystemType */
  2228. X
  2229. X
  2230. X
  2231. X/* This is called if the user opened the host with a file appended to
  2232. X * the host's name, like "wuarchive.wustl.edu:/pub/readme," or
  2233. X * "wuarchive.wustl.edu:/pub."  In the former case, we open wuarchive,
  2234. X * and fetch "readme."  In the latter case, we open wuarchive, then set
  2235. X * the current remote directory to "/pub."  If we are fetching a file,
  2236. X * we can do some other tricks if "ftpcat mode" is enabled.  This mode
  2237. X * must be selected from your shell's command line, and this allows you
  2238. X * to use the program as a one-liner to pipe a remote file into something,
  2239. X * like "ncftp -c wu:/pub/README | wc."  If the user uses ftpcat mode,
  2240. X * the program immediately quits instead of going into it's own command
  2241. X * shell.
  2242. X */
  2243. Xvoid ColonMode(OpenOptions *openopt)
  2244. X{
  2245. X    int tmpverbose;
  2246. X
  2247. X    /* How do we tell if colonmodepath is a file or a directory?
  2248. X     * We first try cd'ing to the path first.  If we can, then it
  2249. X     * was a directory.  If we could not, we'll assume it was a file.
  2250. X     */
  2251. X
  2252. X    /* Shut up, so cd won't print 'foobar: Not a directory.' */
  2253. X    tmpverbose = verbose;
  2254. X    verbose = V_QUIET;
  2255. X
  2256. X    /* If we are using ftpcat|more mode, or we couldn't cd to the
  2257. X     * colon-mode path (then it must be a file to fetch), then
  2258. X     * we need to fetch a file.
  2259. X     */
  2260. X    if (openopt->ftpcat || ! _cd(openopt->colonmodepath)) {
  2261. X        /* We call the appropriate fetching routine, so we have to
  2262. X         * have the argc and argv set up correctly.  To do this,
  2263. X         * we just make an entire command line, then let makeargv()
  2264. X         * convert it to argv/argc.
  2265. X         */
  2266. X        if (openopt->ftpcat == FTPCAT)
  2267. X            (void) sprintf(line, "get %s -", openopt->colonmodepath);
  2268. X        else if (openopt->ftpcat == FTPMORE)
  2269. X            (void) sprintf(line, "more %s", openopt->colonmodepath);
  2270. X        else {
  2271. X            /* Regular colon-mode, where we fetch the file, putting the
  2272. X             * copy in the current local directory.
  2273. X             */
  2274. X            (void) sprintf(line, "mget %s", openopt->colonmodepath);
  2275. X        }
  2276. X        makeargv();
  2277. X
  2278. X        /* Turn on messaging if we aren't catting. */
  2279. X        if (openopt->ftpcat == 0)
  2280. X            verbose = tmpverbose;
  2281. X        
  2282. X        /* get() also handles 'more'. */
  2283. X        if (openopt->ftpcat)
  2284. X            (void) get(margc, margv);
  2285. X        else
  2286. X            (void) mget(margc, margv);
  2287. X
  2288. X        /* If we were invoked from the command line, quit
  2289. X         * after we got this file.
  2290. X         */
  2291. X        if (eventnumber == 0L) {
  2292. X            (void) quit(0, NULL);
  2293. X        }
  2294. X    }
  2295. X    verbose = tmpverbose;
  2296. X}    /* ColonMode */
  2297. X
  2298. X
  2299. X
  2300. X
  2301. X/* Given a properly set up OpenOptions, we try connecting to the site,
  2302. X * redialing if necessary, and do some initialization steps so the user
  2303. X * can send commands.
  2304. X */
  2305. Xint Open(OpenOptions *openopt)
  2306. X{
  2307. X    int                    tmpverbose, hErr;
  2308. X    int                    dials;
  2309. X    char *ruser, *rpass, *racct;
  2310. X    int siteInRC;
  2311. X    char *user, *pass, *acct;    
  2312. X    int                    login_verbosity;
  2313. X
  2314. X    /* If there is already a site open, close that one so we can
  2315. X     * open a new one.
  2316. X     */
  2317. X    if (connected && NOT_VQUIET && hostname[0]) {
  2318. X        (void) printf("Closing %s...\n", hostname);
  2319. X        (void) disconnect(0, NULL);
  2320. X    }
  2321. X
  2322. X    ruser = rpass = racct = NULL;
  2323. X    /* This also loads the init macro. */
  2324. X    siteInRC = ruserpass2(openopt->hostname, &ruser, &rpass, &racct);
  2325. X    if (ISANONOPEN(openopt->openmode)) {
  2326. X        user = "anonymous";
  2327. X        pass = anon_password;
  2328. X    } else {
  2329. X        user = NULL;
  2330. X        pass = NULL;
  2331. X    }
  2332. X    acct = NULL;
  2333. X    
  2334. X    if (siteInRC && !openopt->ignore_rc) {
  2335. X        acct = racct;
  2336. X        if (ruser != NULL) {
  2337. X            /* We were given a username.  If we were given explicit
  2338. X             * instructions from the command line, follow those and
  2339. X             * ignore what the RC had.  Otherwise if no -a or -u
  2340. X             * was specified, we use whatever was in the RC.
  2341. X             */
  2342. X            if (ISIMPLICITOPEN(openopt->openmode)) {
  2343. X                user = ruser;
  2344. X                pass = rpass;
  2345. X            }
  2346. X        }        
  2347. X    }
  2348. X
  2349. X
  2350. X    /* If the hostname supplied is in the form host.name.str:/path/file,
  2351. X     * then colon mode was used, and we need to fix the hostname to be
  2352. X     * just the hostname, copy the /path/file to colonmode path, and init
  2353. X     * the login_verbosity variable.
  2354. X     */
  2355. X    if (CheckForColonMode(openopt, &login_verbosity) == USAGE)
  2356. X        return USAGE;
  2357. X
  2358. X#ifdef GATEWAY
  2359. X    /* Make sure the gateway host name is a full name and not an
  2360. X     * abbreviation.
  2361. X     */
  2362. X    if (*gateway)
  2363. X        GetFullSiteName(gateway, NULL);
  2364. X#endif
  2365. X
  2366. X    /* If the hostname supplied was an abbreviation, such as just
  2367. X     * "wu" (wuarchive.wustl.edu), look through the list of sites
  2368. X     * we know about and get the whole name.  We also would like
  2369. X     * the path we want to start out in, if it is available.
  2370. X     */
  2371. X    GetFullSiteName(openopt->hostname, openopt->cdpath);
  2372. X
  2373. X    for (
  2374. X            dials = 0;
  2375. X            openopt->max_dials < 0 || dials < openopt->max_dials;
  2376. X            dials++)
  2377. X    {
  2378. X        if (dials > 0) {
  2379. X            /* If this is the second dial or higher, sleep a bit. */
  2380. X            (void) sleep(openopt->redial_delay);
  2381. X            (void) fprintf(stderr, "Retry Number: %d\n", dials + 1);
  2382. X        }
  2383. X
  2384. X        if ((hErr = HookupToRemote(openopt)) == -2)    
  2385. X            /* Recoverable, so we can try re-dialing. */
  2386. X            continue;
  2387. X        else if (hErr == NOERR) {
  2388. X            /* We were hookup'd successfully. */
  2389. X            connected = 1;
  2390. X
  2391. X#ifdef GATEWAY
  2392. X            if (*gateway) {
  2393. X                if ((Login(
  2394. X                    user,
  2395. X                    pass,
  2396. X                    acct,
  2397. X                    (!openopt->ignore_rc && !openopt->colonmodepath[0])
  2398. X                ) != NOERR) || cout == NULL)
  2399. X                    goto nextdial;        /* error! */
  2400. X            }
  2401. X#endif
  2402. X
  2403. X            /* We need to check for unix and see if we should set binary
  2404. X             * mode automatically.
  2405. X             */
  2406. X            CheckRemoteSystemType(openopt);
  2407. X
  2408. X#ifdef GATEWAY
  2409. X            if (!*gateway) {
  2410. X#endif
  2411. X                /* We don't want to run the init macro for colon-mode. */
  2412. X                if ((Login(
  2413. X                        user,
  2414. X                        pass,
  2415. X                        acct,
  2416. X                        (!openopt->ignore_rc && !openopt->colonmodepath[0])
  2417. X                    ) != NOERR) || cout == NULL)
  2418. X                {
  2419. X                    goto nextdial;        /* error! */
  2420. X                }
  2421. X#ifdef GATEWAY
  2422. X            }
  2423. X#endif
  2424. X
  2425. X            if (openopt->colonmodepath[0]) {
  2426. X                ColonMode(openopt);
  2427. X            } else if (openopt->cdpath[0]) {
  2428. X                /* If we didn't have a colon-mode path, we try setting
  2429. X                 * the current remote directory to cdpath.  cdpath is
  2430. X                 * usually the last directory we were in the previous
  2431. X                 * time we called this site.
  2432. X                 */
  2433. X                (void) _cd(openopt->cdpath);
  2434. X            } else {
  2435. X                /* Freshen 'cwd' variable for the prompt. 
  2436. X                 * We have to do atleast one 'cd' so our variable
  2437. X                 * cwd (which is saved by _cd()) is set to something
  2438. X                 * valid.
  2439. X                 */
  2440. X                (void) _cd(NULL);
  2441. X            }
  2442. X            break;    /* we are connected, so break the redial loop. */
  2443. X            /* end if we are connected */
  2444. X        } else {
  2445. X            /* Irrecoverable error, so don't bother redialing. */
  2446. X            /* The error message should have already been printed
  2447. X             * from Hookup().
  2448. X             */
  2449. X            break;
  2450. X        }
  2451. Xnextdial: continue;    /* Try re-dialing. */
  2452. X    }
  2453. X    return (NOERR);
  2454. X}    /* Open */
  2455. X
  2456. X
  2457. X
  2458. X/* This stub is called by our command parser. */
  2459. Xint cmdOpen(int argc, char **argv)
  2460. X{
  2461. X    OpenOptions            openopt;
  2462. X
  2463. X    if ((GetOpenOptions(argc, argv, &openopt) == USAGE) ||
  2464. X        (Open(&openopt) == USAGE))
  2465. X        return USAGE;
  2466. X    return NOERR;
  2467. X}    /* cmdOpen */
  2468. X
  2469. X/* eof open.c */
  2470. END_OF_FILE
  2471.   if test 16396 -ne `wc -c <'open.c'`; then
  2472.     echo shar: \"'open.c'\" unpacked with wrong size!
  2473.   fi
  2474.   # end of 'open.c'
  2475. fi
  2476. echo shar: End of archive 2 \(of 5\).
  2477. cp /dev/null ark2isdone
  2478. MISSING=""
  2479. for I in 1 2 3 4 5 ; do
  2480.     if test ! -f ark${I}isdone ; then
  2481.     MISSING="${MISSING} ${I}"
  2482.     fi
  2483. done
  2484. if test "${MISSING}" = "" ; then
  2485.     echo You have unpacked all 5 archives.
  2486.     rm -f ark[1-9]isdone
  2487. else
  2488.     echo You still must unpack the following archives:
  2489.     echo "        " ${MISSING}
  2490. fi
  2491. exit 0
  2492. exit 0 # Just in case...
  2493.