#include "ftp.h" #include "usr.h" #include "ftptelnet.h" #include "ctype.h" #include "sys/types.h" #include "sys/stat.h" #include "sys/timeb.h" #include "sgtty.h" #include "setjmp.h" #include "ftp_lib.h" /* * User FTP * * Modified by Dan Franklin Sept 8 1978 (BBN) to take * commands from "getline" routine. This enables it * to have its input redirected to a file of commands. * Also moved declarations of strings for command prompting * to ahead of the command table so that our new C compiler * won't complain. * * Modified by Dan Franklin (BBN) August 11 19598 to put * blank line after "From:" line in send-mail command. * The blank line indicates the end of the header as * per RFC733. * * 3/79, added cwd and quote commands. delete inside rwait returns * to prompt instead of saying (incorrectly) "Host exited" ado/bbn * * changed to use long hosts jsq BBN 3-25-79 * use fmodes for creat mode, give error on NCP open fail jsq BBN 19July79 * init and subshl changed to handle signals differently: if parent * shell ignored signals, this process does too. jsq BBN 5Aug79 * add arguments to allow passing process id of other half so that one * can kill the other when it is ready to die; also do all host * name-number conversions in ftp.c so we don't have so much stuff * in here. jsq BBN 18Aug79 * * made f_retr check if destination already existed, and not delete on error * if it does. This should eliminate one cause of the /dev/tty disappearances. * Also fixed signal handling in fork exec'ing shell. Dan Franklin (BBN) 12/21/79 * * Added NLST command (like LIST but different at server end). Also changed * CMDENTS to use sizeof cmdtab. * * Added XSEN and XSEM commands, which are just like MAIL (to user FTP) but * send messages instead of/along with mail on servers which implement * them. Dan Franklin BBN Feb 8 '80 * * Added SEND and GET synonyms for STOR and RETR respectively. Added * LOGIN command with 1, 2 or 3 args, and new argument list types * ARG1AND2 and ARG1OR2OR3. Buz Owen, BBN, March 4 '80. * * Fixed bug whereby LIST and NLST were synonyms of RETR. Changed QUOTE * command to take two args. Changed ARGSIZ to 80, and LINSIZ to 250. * Changed quote and cwd to use putarg, putstr, putcmd more conventionally. * Buz Owen, BBN, March 11, 80. * * ca. Mar 25, 1980 (dm at BBN): * changed issep (c) to return TRUE if (c == '\0'); this is so movarg * can act on things its already acted on, so it could be used in the * multiple store stuff * added the multiple store stuff to server and user processes, working * on unix-unix tree transfers * in chekds(), which opens the data connection, turned off user-interrupts * while waiting for the connection to be openned. at least with tenex, * failure to open this connection after you've said you were going to can * get the server confused, and it stops listening to you (probably * waiting for the connection to open). * changed f_stor() to act as a front end for a separate subroutine, store() * which does the actual work; store() is called by the multiple store * stuff. * * jsq BBN 5April80 declared cmdtab as struct ftucmd cmdtab[] in initiali- * zation, fixed definition of ftucmd.ft_info, declared getcmd properly in * main before calling it, fixed getcmd to use ft_info correctly. * * dm (bbn) 15 apr 1980: * installed mretrieve() command; gets an NLST from the other site, and then * does a retrieve on all the names of files returned. * has subcommands to translate tenex-like file-names into unixable file- * names, and work other transformations * changed error() to accept fprintf-like arguments, so i could improve * error messages it returns * added the statistics package * note that statistics aren't gathered on recursive stores (done in * a lower fork. * moved all the MGET internals to the end of the file, so they would be * easier to find * dm (bbn) 31 May 1980: * put a 1-second sleep into the loop in sendabunch() tenex requires time * to become convinced the last data-socket was closed, so, cycling too * quickly through several files will result in a "cannot open data * connection" message, and Unix will be sitting there trying to open * the data socket, anyway... * changed rwait() to know about the proposed 257 reply code which tells * you what the directory was which was created. Also changed ftptty * to know about such things. * put the interrupt stuff back into checkds()--its abscence caused too * many problems, and i guess people can restart their tenex ftps if * tenex gets confused... * jsq BBN 27May80 make prstat give baud rate as well as bytes/sec. * dm (bbn) 5 Sept. 80: make sure indat() & outdat() close the dataconnection * when the file is through. * dm (bbn) 25 Nov. 80: make a version which works with both TCP & NCP * changed setspec() & calling routines to handle the length of the * tables automatically (i.e., recognize that a null entry implies * the end of a table) * changed f_mlfl() to use putcmd() & sndcmd() instead of writing * directly to the network * dsfds & dsfdr are structures now * * ado (bbn) feb - jun, 81. finished tcp version, v7ized everything and * stdio'd everything possible, * following agn's changes to c70 version, and further urging. * combined ftp (the command), ftpmain, and ftptty into one program. * stripped out data transfer routines in favor of ftp_lib routines which * are common to both user and server ftp, supplied by agn. * completely new version rwait to deal with systematic reply code scheme * of new protocol. * ado (bbn) jul 81. added cmdget and cmdsend commands, to retrieve into a * a filter, and send output from a command respectively. * */ #define MGET 1 /* turn on the multiple store junk */ struct net_stuff NetParams; extern char * progname; char * malloc(), * strcpy(); char * HOST_ID, * them, * us; char * errmsg(); char * getarg(); char * desyntactify(); #define ARGSIZ 80 #define TTBSIZ 160 #define MAXSTRING 10 /* Length of the longest command name */ struct spctab { char *typnm; char *typarg; int typnum; }; struct spctab tycode[] = { { "ASCII", "A", 0 }, { "TELNET", NULL, 1 }, /* non implimented values in */ { "PRINT", NULL, 2 }, /* anyway, so we can give */ { "EBCDIC", NULL, 3 }, /* slightly more intelligent */ { "IMAGE", "I", 6 }, { "LOCAL", "L8", 7 }, { NULL, NULL, -1 } }; struct spctab mdcode[] = { { "STREAM", "S", 0 }, { "BLOCK", NULL, -1 }, /* error messages. */ { "COMPRESSED", NULL, -1 }, /* not supported... */ { NULL, NULL, -1 }, }; struct spctab strucode[] = { { "FILE", "F", 0 }, { "RECORD", "R", 1 }, { NULL, NULL, -1 }, }; /* */ /* Initial [default] settings: */ int mode = MODES; int type = TYPEA; int stru = STRUF; struct net_stuff dsfds; struct net_stuff dsfdr; FILE *fdfil = NULL; FILE *fdpip = NULL; int pipnum = -1; /* Uninitialized data */ int abrtxfer; char ttbuf[TTBSIZ], *ttptr, ttcnt; /* For tty input */ char arg1[ARGSIZ], arg2[ARGSIZ], arg3[ARGSIZ]; /* For string arguments */ char linbuf[LINSIZ]; char *linptr; /* For Telnet output */ jmp_buf env; /* for long jumping */ struct stat sttbf; /* For file status info */ int uicount = 0; /* No. of user interrupts */ int xreply; /* Global reply location */ char mlbf[82], *mlptr; /* For mail headers */ struct ftucmd /* Structure of the command table */ { char *ft_cnm; int ft_nargs; char **ft_info; int (*ft_fn)(); char *ft_desc; }; /* Definitions for ft_nargs field */ #define ARG0 1 #define ARG1 2 #define ARG2 4 #define ARG0OR1 010 #define ARG1OR2 020 #define ARG1AND2 040 #define ARG1OR2OR3 100 /* */ /* Strings for command prompting */ char locfil[] = "localfile: "; char rmtfil[] = "remotefile: "; char persn[] = "person: "; char *afsnd[] = { "Input filter (command):", rmtfil }; char *afget[] = { locfil, "Output filter (command): " }; char badcmd[] = "bad filter/command: %s\n"; FILE *popen(); char aborstr[] = { TNIAC, TNIP, TNIAC, TNDM, 'A', 'B', 'O', 'R', 0 }; char *arsto[] = { locfil, rmtfil }; char *arcwd[] = { "remote pathname: " }; char *ardir[] = { "local pathname: "}; char *ardel[] = { rmtfil }; char *arlis[] = { "remote pathname: ", locfil }; char *armod[] = { "transfer mode: "}; char *arren[] = { "old remotefile: ", "new remotefile: " }; char *arret[] = { rmtfil, locfil }; char *arate[] = { "[reset] " }; char *arper[] = { persn }; char *arquo[] = { "command [argument]: " }; char *arstr[] = { "file structure: " }; char *artyp[] = { "data type: " }; char *aruse[] = { "Username: " }; char *armlf[] = { locfil, persn }; #ifdef MGET char *armret[] = { "foreign file-group descriptor: ", "flags: " }; char *armsto[] = {locfil, "remote directory-name, or <cr> if wild-carding: " }; /* */ /* some other multiple send and store guys */ char recurstor; char dialog; char mretname[32]; /* storage for the NLST temporary file */ #define MADEDIR 251 /* reply code for XMKD protocol */ char MadeDirectory[ARGSIZ]; /* storage for the string returned by a */ /* XMKD call */ /* variables for the file-name transformer */ #define FNARGSIZ ARGSIZ/2 #define FNTENEX 01 /* flag tenex-mode transforms */ #define FNITS 02 /* flag its mode transforms */ #define FNTOLCASE 04 /* flag lower-case transforms */ #define FNALPHA 010 /* flag alphanumerics only */ #define FNASK 020 /* flag ask user for each name */ #define FNCPY 040 /* flag use the name literally */ #define CTLV 026 /* tenex quotes things with ^V */ int fnflag = 0; /* where the flags live */ char fnprefix[FNARGSIZ/2]; /* potential prefix */ /* declarations of the dispatch functions, so the compiler doesn't complain */ int f_msto(), f_mkd(), f_rmdir(); #endif MGET int subshl(), help(), f_acct(), f_appe(), byedie(), f_cld(), f_cwd(); int f_dele(), f_list(), f_log(), f_mail(), f_mlfl(), f_mode(), f_pass(); int f_quot(), f_rena(), f_status(), f_stor(), f_stru(), f_type(), f_user(); int f_retr(), f_rate(), f_err(), f_nlst(), f_mret(), f_fsnd(), f_fget(); int abort(); /* */ struct ftucmd cmdtab[] = /* Command Table -- must be alphabetical */ { { "! ", ARG0, NULL, subshl, "invoke shell" }, { "ABORT", 0, NULL, abort, "debugging aid: take a core dump."}, { "ACCOUNT", ARG0OR1, NULL, f_acct, "specify account on foreign host" }, { "ACCT", ARG0OR1, NULL, f_acct, "specify account on foreign host" }, { "APPEND", ARG2, arsto, f_appe, "append local file to foreign file" }, { "BYE ", ARG0, NULL, byedie, "close the connection, and exit" }, { "CD ", ARG1, ardir, f_cld, "change directory on local host" }, { "CHDIR", ARG1, ardir, f_cld, "change directory on local host" }, { "CMDGET", ARG2, afget, f_fget, "retrieve file from foreign host into filter (command)", }, { "CMDSEND", ARG2, afsnd, f_fsnd, "send command output to foreign host file", }, { "CWD ", ARG1, arcwd, f_cwd, "change directory on foreign host" }, { "DELETE", ARG1, ardel, f_dele, "remove a file from the foreign host" }, { "GET ", ARG2, arret, f_retr, "retrieve a file from the foreign host" }, { "HELP", ARG0OR1, NULL, help, "briefly describe commands" }, { "LIST", ARG2, arlis, f_list, "get a directory listing to a local file" }, { "LOG ", ARG1OR2OR3, aruse, f_log, "log onto a foreign host" }, { "MAIL", ARG1, arper, f_mail, "send mail to a user on the foreign host" }, #ifdef MGET { "MGET", ARG1OR2, armret, f_mret, "multiple retrieve" }, { "MKDIR", ARG1, arcwd, f_mkd, "make a directory on the foreign host" }, #endif MGET { "MLFL", ARG1AND2, armlf, f_mlfl, "mail file to a foreign host" }, { "MODE", ARG1, armod, f_mode, "specify transfer mode (STREAM or BLOCK)" }, #ifdef MGET { "MRETRIEVE", ARG1OR2, armret, f_mret, "multiple retrieve" }, { "MSEND", ARG1OR2, armsto, f_msto, "multiple store" }, { "MSTORE", ARG1OR2, armsto, f_msto, "multiple store" }, #endif MGET { "NLST", ARG2, arlis, f_nlst, "get a directory listing to a local file" }, { "PASSWORD", ARG0OR1, NULL, f_pass, "tell the foreign host your password" }, { "QUIT", ARG0, NULL, byedie, "close the connection, and exit" }, { "QUOTE", ARG1OR2, arquo, f_quot, "send quoted string to ftp server" }, { "RATE", ARG0OR1, arate, f_rate, "print accumulated transfer rate, and optionally reset."}, { "RENAME", ARG2, arren, f_rena, "rename a file on the foreign host" }, { "RETRIEVE", ARG2, arret, f_retr, "retrieve a file from the foreign host" }, #ifdef MGET { "RMDIR", ARG1, ardir, f_rmdir, "remove a directory on the foreign host" }, #endif MGET { "SEND", ARG2, arsto, f_stor, "store a local file on the foreign host" }, { "STATUS", ARG0OR1, NULL, f_status, "tell status of the ftp connection" }, { "STORE", ARG2, arsto, f_stor, "store a local file on the foreign host" }, { "STRUCTURE", ARG1, arstr, f_stru, "specify structure of files (RECORD or FILE (unstructured))"}, { "TYPE", ARG1, artyp, f_type, "specify representation type (ASCII, IMAGE, or LOCAL)"}, { "USER", ARG1, aruse, f_user, "tell the foreign host your name" }, { MSOM, ARG0OR1, arper, f_mail, "send text to a user's tty on the foreign host"}, { MSND, ARG0OR1, NULL, f_mail, "send text to a user's tty on the foreign host"}, { NULL, 0, NULL, f_err, "command processor error" } }; /* */ char *errtab[] = { /* 0 */ "Unrecognized command\n", /* 1 */ "Ambiguous command\n", /* 2 */ "Command error\n", /* 3 */ "Wrong number of arguments\n", /* 4 */ "Command argument too long\n", /* 5 */ "Bad specification\n", /* 6 */ "Can't create file\n", /* 7 */ "File not found\n" }; /* */ ftpmain (otherhalf) int otherhalf; { register struct ftucmd *sp; extern struct ftucmd *getcmd(); NetInit (&dsfdr); NetInit (&dsfds); init (otherhalf); if (!setjmp (env)) rwait (1); /* wait for 200 hello */ for (;;) { if (uicount) abterr(); /* Returns to setjmp() */ prompt(); sp = getcmd(); putcmd (sp->ft_cnm); (*sp->ft_fn)(); } } prompt() { printf ("> "); } /* */ init (otherhalf) int otherhalf; { /* signal handling changed jsq BBN 5Aug79 */ extern exit(), usrint(), dieinit(), diequit(); dieinit (otherhalf, stderr); /* second arg is fd for fprintf */ get_stuff (&NetParams); if (signal (SIGINT, usrint) == SIG_IGN) signal (SIGINT, SIG_IGN);/* if parent shell */ if (signal (SIGQUIT, diequit) == SIG_IGN) signal (SIGQUIT, SIG_IGN);/* ignored, we do too */ signal (SIG_NETINT, SIG_IGN); inittime(); ttcnt = 0; ttptr = ttbuf; #ifdef MGET *mretname = 0; #endif MGET } /* */ logfn(s) /* interfaces to ftp_lib, skips over reply code. */ char * s; { printf (s+4); } usrint() /* Collect user interrupts */ { uicount++; abrtxfer++; /* stop any transfer in progress */ signal (SIGINT, usrint); } f_err() { puts ("ftpmain: command parser ERROR; dispatch on 0 command pointer"); puts ("please report this to BUG-FTP @ BBN-Unix"); } /* */ help() { register struct ftucmd *sp; register i,j; char flag; flag = i = j = 0; if (*arg1==0) { printf ("\nCommands known to this process:\n\n"); for (sp = cmdtab; (sp->ft_cnm); sp++) { printf ("%-*.*s", MAXSTRING, MAXSTRING, (sp->ft_cnm)); if (!(++j%6))printf ("\n"); } printf ("\nany unambiguous substring will invoke the given command\n"); printf ("for a brief description of a given command type \"help <command>\"\n"); printf ("for a brief description of all commands type \"help all\", or \"help * \"\n"); printf ("\"help server\" will ask the server process for help\n"); } else { getuc (arg1); if (match (arg1, "SERVER")) { putcmd ("HELP"); sndcmd(); rwait (12); return; } else if (match (arg1, "ALL")||match (arg1, "*")) flag++; for (sp = cmdtab; (sp->ft_cnm); sp++) { if (flag||match (arg1, sp->ft_cnm)) { printf ("%s\t %s\n", sp->ft_cnm, sp->ft_desc); i++; } } if (!i) printf ("command \"%s\" unrecognised\n", arg1); } } /* */ f_mode() { setspec ("Mode", &mode, mdcode); } f_type() { setspec ("Type", &type, tycode); } f_stru() { setspec ("Structure", &stru, strucode); } setspec (what, spec, tabl) char *what; int *spec; struct spctab tabl[]; { register struct spctab *p; getuc (arg1); for (p=tabl; p->typnm; p++) { if (match (arg1,p->typnm)) break; } if (!p->typarg) error ( (p->typnm ? "%s %s not implimented.\n" : "Bad %s: \"%s\"\n" ), what, arg1 ); else { putstr (p->typarg); sndcmd(); if (rwait (2) == 0) return; *spec = p->typnum; } } /* */ f_status() { putstr (arg1); sndcmd(); rwait (5); sleep (5); } f_rena() { putcmd ("RNFR"); putstr (arg1); sndcmd(); if (rwait (2), xreply != 350) return; putcmd ("RNTO"); putstr (arg2); sndcmd(); rwait (6); } /* */ f_cld() { if (chdir (arg1)<0) printf ("%s; can't change to local dir %s\n", errmsg (0), arg1); } f_cwd() /* makes this compatible with the MSTO stuff */ { cwd (arg1); } cwd (str) char *str; { putcmd ("CWD "); putstr (str); /* put destination in */ sndcmd(); return ( rwait (3), xreply != 251 ? -1 : 0 ); /* used by mstore() */ } /* */ f_rate() { printf(dumptime()); if (!strcmp("reset", arg1)) inittime(); } f_quot() { putcmd (arg1); if (arg2[0]) putstr (arg2); sndcmd(); rwait (-1); } f_dele() { putstr (arg1); sndcmd(); rwait (7); } f_user() { putstr (arg1); sndcmd(); rwait (1); } /* */ f_log() { putcmd ("USER"); f_user(); if (xreply == 331) { putcmd ("PASS"); strcpy (arg1,arg2); f_pass(); } if (xreply == 332) { strcpy (arg1, arg3); f_acct(); } } /* */ f_pass() { secret ("Password: "); } f_acct() { putcmd ("ACCT"); secret ("Account #: "); } secret (s) char *s; { struct sgttyb tmp; register int sav; register char *p; if (arg1[0] == '\0') { ttcnt = 0; gtty (1,&tmp); sav = tmp.sg_flags; tmp.sg_flags &= ~ECHO; stty (1,&tmp); /* Set noecho for password */ printf ("%s",s); fflush(stdout); p = getarg(); tmp.sg_flags = sav; stty (1,&tmp); printf ("\n"); /* Echo the absorbed newline */ if (p) { movarg (p,arg1,ARGSIZ-1); getarg(); /* Skip the newline */ } } putstr (arg1); sndcmd(); rwait (1); } /* */ fetch (src,dest) char *src, *dest; { int flag; int tfd; /* First find out if the destination file already existed */ if ((tfd = open (dest, 1)) == -1) flag = 1; /* Delete on error */ else { close (tfd); flag = 0; /* Don't delete on error, it already existed */ } if ((fdfil = fopen (dest, "w")) == NULL) error ("Can't create \"%s\"; %s\n", dest, errmsg (0)); errno = 0; putstr (src); sndcmd(); if (!chekds (0)) { rcvdata (&dsfdr, fdfil, logfn); net_close (&dsfdr); fclose (fdfil); fdfil = NULL; return rwait (4); } fclose (fdfil); fdfil = NULL; if (flag) unlink (dest); net_close (&dsfdr); if (errno == EINTR) abterr(); return 0; } /* */ f_nlst() { nlst (arg1,arg2); } nlst (src,dst) /* called by f_mret(), f_nlst() */ char *src,*dst; { putcmd ("NLST"); return fetch (src,dst); } f_list() { fetch (arg1,arg2); } f_retr() { putcmd ("RETR"); /* because get synonym might be given */ fetch (arg1,arg2); } /* */ f_fsnd() { if ((fdfil = popen (arg1, "r")) == NULL) { error (badcmd, arg1); } else { errno = 0; putcmd ("STOR"); putstr (arg2); sndcmd (); if (chekds (1)) { pclose (fdfil); fdfil = NULL; net_close (&dsfds); if (errno == EINTR) abterr (); } else { if (senddata (fdfil, &dsfds, logfn)) net_pclose (&dsfds); else net_close (&dsfds); rwait (4); } if (fdfil != NULL) { pclose (fdfil); fdfil = NULL; } return 0; } return -1; } /* */ f_fget() { if ((fdfil = popen(arg2, "w")) == NULL) error(badcmd,arg2); else { errno = 0; putcmd("RETR"); putstr(arg1); sndcmd(); if (chekds (0)) { pclose (fdfil); fdfil = NULL; net_close (&dsfdr); if (errno == EINTR) abterr (); } rcvdata (&dsfdr, fdfil, logfn); net_close (&dsfdr); pclose (fdfil); fdfil = NULL; return rwait (4); } return -1; } /* */ f_stor() { store (arg1, arg2); } store (s1,s2) char *s1, *s2; { sndallo (s1, 0); putcmd ("STOR"); snddat (s2, 0); } f_appe() { sndallo (arg1, 0); putcmd ("APPE"); snddat (arg2, 0); } /* */ sndallo (s, extra) char *s; unsigned extra; { long size; char sizestr[20]; if ((fdfil = fopen (s,"r")) == NULL) error ("can't open \"%s\"; %s\n", s, errmsg (0)); if (fstat (fileno(fdfil), &sttbf) == -1) error ("File \"%s\" not found; %s\n", s, errmsg (0)); size = 2*sttbf.st_size+extra; putcmd ("ALLO"); sprintf (sizestr, "%D", size); putstr (sizestr); sndcmd(); if (rwait (2) == 0 && xreply < 500) /* Some hosts don't like ALLOs */ { fclose (fdfil); fdfil = NULL; longjmp (env,1); } } /* */ snddat (s, mlflg) char *s; int mlflg; { int child; errno = 0; putstr (s); sndcmd (); if (chekds (1)) { fclose (fdfil); fdfil = NULL; net_close (&dsfds); if (errno == EINTR) abterr (); } else { if (mlflg) net_write (&dsfds, mlbf, strlen (mlbf)); if (senddata (fdfil, &dsfds, logfn)) net_pclose (&dsfds); else net_close(&dsfds); rwait (4); } if (fdfil != NULL) { fclose (fdfil); fdfil = NULL; } } /* */ f_mlfl() { setmlnm(); putstr ("TYPE A"); sndcmd(); if (rwait (2) == 0) return; type = TYPEA; putstr ("MODE S"); sndcmd(); if (rwait (2) == 0) return; mode = MODES; if ((fdfil = fopen (arg1,"r")) == NULL) error ("can't open \"%s\"; %s\n", arg1, errmsg (0)); putcmd ("MLFL"); snddat (arg2, 1); } /* */ f_mail() { register char *p, *q; if (arg1[0] == 0) { printf (persn); if (p = getarg()) { movarg (p,arg1,ARGSIZ-1); getarg(); } } putstr (arg1); sndcmd(); if (rwait (9), xreply != 354) return; setmlnm(); putstr (mlbf); sndcmd(); ttptr = ttbuf; for (;;) { if ((ttcnt = strlen(fgets (ttbuf,TTBSIZ, stdin))) <= 0) break; if (ttbuf[0] == '.' && ttbuf[1] == '\n') { ttptr = &ttbuf[2]; ttcnt -= 2; break; } p = ttbuf; q = linbuf; while (ttcnt--) if (*p == '\n') { linptr = q; sndcmd(); q = linbuf; p++; } else *q++ = *p++; } linbuf[0] = '.'; linptr = &linbuf[1]; sndcmd(); rwait (10); } /* */ subshl() { /* changed to handle signals, exit, and wait properly jsq BBN 4Aug79*/ int ps, pid, i; int (*oldemt)(), (*oldint)(), (*oldquit)(); extern usrint(); if ((pid = VFORK()) == 0) { close (TTYMON); net_vclose (&NetParams); net_vclose (&dsfds); net_vclose (&dsfdr); signal (SIGEMT, SIG_DFL); execl ("/bin/sh","ftp-sh",0); fprintf (stderr, "/bin/sh missing.\n"); VEXIT (-1); } oldemt = signal (SIGEMT, SIG_IGN); oldquit = signal (SIGQUIT, SIG_IGN); oldint = signal (SIGINT, SIG_IGN); while (wait (&ps) != pid); signal (SIGEMT, oldemt); signal (SIGQUIT, oldquit); signal (SIGINT, oldint); } /* */ rget() { int i; errno = 0; if ((i = read (pipnum, &xreply, sizeof xreply /*, 1, fdpip*/)) <= 0) { if (uicount) { uicount--; xreply = 0; longjmp(env,1); } if ((i<0) || (errno != 0)) abort(); die (1, "%s: Host exited; %s\n", progname, errmsg (0)); } return xreply; } #ifdef MGET getdirname() /* get name of created directory */ { int i, j; *MadeDirectory = '\0'; i = read (pipnum, &j, sizeof j /*, 1, fdpip*/); if (i>0) i = read (pipnum, MadeDirectory, j /*, 1, fdpip*/); if (*MadeDirectory == '\0') return (0); MadeDirectory[j] = '\0'; /* convert to real string */ return (1); } #endif MGET /* */ rwait (n) int n; { register int rnum; while ((rnum = rget ()) < 200) if (n < 0) break; /* special hack to return low reply */ if (rnum == 421) byedie(); # ifdef MGET if (rnum == 251) return getdirname(); # endif MGET if (rnum < 200 && n < 0) return 0; /* hack for now. */ switch (rnum / 100) { case 1: case 2: return 1; case 3: case 4: case 5: return 0; default: abort (); } } /* */ byedie() { putcmd (QUIT); sndcmd(); rwait (1); printf(dumptime()); die (0, NULL); } abterr() { register char *p, *q; uicount = 0; q = linbuf; p = aborstr; /* <IAC> <IP> <IAC> <DM> ABOR */ while (*p) *q++ = *p++; linptr = q; urgon (&NetParams); /* send INS to match DM */ sndcmd(); urgoff (&NetParams); /* send INS to match DM */ if (fdfil != NULL) { fclose (fdfil); fdfil = NULL; } while (rwait (8) && xreply!= 225 && xreply!=226); #ifdef MGET if (*mretname) { unlink (mretname); *mretname = 0; } #ifdef MGET longjmp (env,1); } /* */ /* VARARGS */ error (n) char *n; { ttcnt = 0; ttptr = ttbuf; printf ("%r", &n); fflush(stdout); if (fdfil != NULL) { fclose (fdfil); fdfil = NULL; } longjmp (env,1); } putcmd (s) /* put in 4 chars of cmd */ char *s; { register char *p, *q; register int i; p = linbuf; q = s; for (i=0; i<4; i++) *p++ = *q++; linptr = p; } putarg (s) /* put in argument with no space */ char *s; { register char *p, *q; p = linptr; q = s; while (*q) *p++ = *q++; linptr = p; } putstr (s) /* put in space, then arg */ char *s; { *linptr++ = ' '; putarg (s); } /* */ struct ftucmd *getcmd() { register struct ftucmd *sp; register char *p; while ((p = getarg()) == 0) prompt(); movarg (p,arg1,MAXSTRING); getuc (arg1); for (sp = cmdtab; (sp->ft_cnm); sp++) { if (match (arg1, sp->ft_cnm)) goto win; } error (errtab[0]); win: if ((sp+1)->ft_cnm && match (arg1, (sp+1)->ft_cnm)) error (errtab[1]); if ((p = getarg()) == 0) switch (sp->ft_nargs) { case ARG0OR1: arg1[0] = '\0'; case ARG0: return (sp); case ARG1: case ARG2: case ARG1AND2: case ARG1OR2: case ARG1OR2OR3: printf ((sp->ft_info)[0]); if ((p = getarg()) == 0) error (errtab[2]); } else if (sp->ft_nargs == ARG0) error (errtab[3]); movarg (p, arg1, ARGSIZ-1); if ((p = getarg()) == 0) switch (sp->ft_nargs) { case ARG1AND2: printf ((sp->ft_info)[1]); if (p = getarg()) break; case ARG1OR2: /* arg 2 is optional */ case ARG1OR2OR3: arg2[0] = '\0'; case ARG1: case ARG0OR1: return (sp); case ARG2: printf ((sp->ft_info)[1]); if ((p = getarg()) == 0) error (errtab[2]); } else if (sp->ft_nargs & (ARG1|ARG0OR1)) error (errtab[3]); movarg (p, arg2, ARGSIZ-1); if (p = getarg()) { if (sp->ft_nargs == ARG1OR2OR3) movarg (p,arg3,ARGSIZ-1); else error (errtab[3]); } else arg3[0] = '\0'; return (sp); } /* */ movarg (s, buf, maxch) char *s, *buf; int maxch; { register int i; register char *p, *q; char quote; p = s; q = buf; i = 0; if (*p == '"' || *p == '\'') { quote = *p++; i += 2; maxch += 2; } else quote = '\0'; if (quote) do if ((*q++ = *p++) == quote) goto out; while (i++ < maxch); else do if (issep (*q++ = *p++)) goto out; while (i++ < maxch); error (errtab[4]); out: *--q = '\0'; ttcnt -= i; ttptr += i; } /* */ char *getarg() { for (;;) { if (ttcnt <= 0) { if (fgets (ttbuf, TTBSIZ, stdin) == NULL) ttcnt = 0; else ttcnt = strlen (ttbuf); if (ttcnt <= 0) { if (uicount) longjmp (env,1); byedie(); } ttptr = ttbuf; } while (ttcnt-- > 0) switch (*ttptr++) { case '\n': return (0); case ' ': case '\t': continue; default: ++ttcnt; return (--ttptr); } } } /* */ match (s1, s2) char *s1, *s2; { register char *p1, *p2; p1 = s1; p2 = s2; while (*p1 == *p2++ && *p1) p1++; return (*p1 ? 0 : 1); } issep (c) char (c); { return (!c) || isspace (c); } /* */ setmlnm() { register int c; FILE *ib; register char *p; char *q, ruid, noflg, pbuf[120], xbuf[10]; ruid = getuid(); if (getpw (ruid, pbuf)) pbuf[0] = 0; p = pbuf; q = xbuf; while (*p && *p != ':') *q++ = *p++; *q = '\0'; if ((ib = fopen ("/change/users", "r")) == NULL) noflg = 0; else for (;;) { p = pbuf; while ((c = getc (ib)) >= 0) { if (c == '\n') break; *p++ = c; } *p = '\0'; if (c < 0) { noflg = 0; break; } p = pbuf; while (issep (*p) == 0) p++; *p = '\0'; p = pbuf; q = xbuf; while ((c = *q++ - *p) == 0 && *p++); if (c > 0) continue; if (c < 0) noflg = 0; else noflg = 1; break; } if (noflg) { while (issep (*p++)); q = p - 1; p = &pbuf[39]; while (issep (*p--)); *(p+2) = '\0'; } mlptr = mlbf; mlstr ("From: "); mlstr (xbuf); if (noflg) { mlstr (" ("); mlstr (q); mlstr (")"); } mlstr (" at "); mlstr (HOST_ID); /* AGN */ mlstr ("\r\n\r\n"); /* Blank line for end of header */ *mlptr = '\0'; } /* */ mlstr (s) char *s; { register char *p, *q; p = s; q = mlptr; while (*q++ = *p++); mlptr = --q; } #ifdef MGET #define SUCCESS 1 #define FAIL 0 #define ERROR -1 #define CRLF "\r\n" #define NLIST 1 /* flags to next_filstr to tell it what its */ #define DIRLIST 2 /* looking at--either a local directory or a nlst */ /* from the server */ f_mkd() { mkd (arg1); } mkd (s) char *s; { putcmd ("XMKD"); putstr (s); sndcmd(); return (rwait (13) ? -1 : 0); /* make sure the directory is created */ } f_rmdir() { putcmd ("XRMD"); putstr (arg1); sndcmd(); rwait (7); } /* */ f_msto() { char dirplate[ARGSIZ]; /* if the wild-carded string includes a path */ char template[ARGSIZ]; /* where to store the wild-carded string */ char remroot[ARGSIZ]; /* where to store the foreign root */ int ps, child, dirflg, i; register char *p, *lastslash; recurstor = ps = dirflg = 0; /* copy the args to a safe place */ movarg (arg1, dirplate, ARGSIZ-1); if (arg2[0] != '\0') { movarg (arg2, remroot, ARGSIZ-1); recurstor++; } /* now, scan the first argument, and if it is a pathname rather than a */ /* file in this directory, juggle the books, a bit. */ for (p = lastslash = dirplate; *p; p++) if (*p == '/') { lastslash = p; dirflg++; } if (dirflg) *lastslash++ = '\0'; /* terminate the directory string */ movarg (lastslash, template, ARGSIZ-1); if (dirflg) { if (*dirplate == '\0') /* oops, a file in the root directory... */ { dirplate[0] = '/'; dirplate[1] = '\0'; } } if (recurstor||dirflg) { /* done in a lower fork so we can chdir freely */ if ((child = fork()) == 0) { signal (SIGINT, SIG_DFL); /* let interrupts clobber child */ progname = "ftpmain_child"; if (dirflg) if (chdir (dirplate)<0) { printf ("%s: can't get to \"%s\"; %s\n", progname, dirplate, errmsg (0)); exit (-1); } sendabunch (template,remroot); printf(dumptime()); exit (0); } else if (child < 0) { printf ("%s: can't fork; %s\n", progname, errmsg (0)); } else { errno = 0; while ((i = wait (&ps))!=child && i>0) { ; } if (errno == EINTR) { kill (child, 9); /* make sure the child does indeed die, in */ /* case it was ignoring interrupts at the */ /* time (see the checkds()) routine */ } } } else sendabunch (template,NULL); recurstor = dialog = 0; } /* */ sendabunch (str,foroot) /* basically for sending a whole directory (masked by template) full of */ /* stuff. */ char *str, *foroot; { register FILE *dfds; /* fds for the directory */ register int i; struct stat statbuf; struct { int inode; char name_ptr[15]; /* extra one for a null at the end */ } file; if (recurstor) { if (chdir (str)<0) { printf ("%s; can't chdir to \"%s\"\n", errmsg (0), str); return; } printf ("make directory \"%s\"\n", ((foroot==NULL)?foroot:str)); if (mkd ((foroot==NULL)?foroot:str)<0) { printf ("making directory %s failed\n", str); chdir (".."); /* undo the effects of the chdir above */ return; } else if (cwd (desyntactify (MadeDirectory))<0) { printf ("moving to directory \"%s\" failed\n", str); chdir (".."); /* undo the effects of the chdir above */ return; } } /* open up the directory, and iterate through its contents, masking */ /* each with template to determine whether it should be asked for */ if ((dfds = fopen (".", "r")) == NULL) { char dot[80]; /* place for abspath to stringify */ abspath (".", dot, &dot[sizeof (dot)]); printf ("can't open directory \"%s\"; %s\n", dot, errmsg (0)); return; } /* */ /* skip the directory over the '.' & '..' entries */ fseek (dfds,(long)(2*(sizeof file)), 0); file.name_ptr[14] = '\0'; /* make sure there's a null on the end */ /* iterate through the directory */ while ((i = fread (&file, sizeof file, 1, dfds)) <= 0) { if (file.inode != 0) { sleep (1); /* allow time for socket to be fully closed */ if (uicount) { recurstor = 0; fclose (dfds); send(); abterr(); } if (recurstor) { /************************************************************/ /* note that an interrupt here does not require us to clean */ /* up all the files we have openned, as interrupts are not */ /* caught, and this child process simply exits... */ /************************************************************/ stat (file.name_ptr, &statbuf); if (statbuf.st_mode&S_IFDIR) { /* send the command to make the directory, & /* ship the directory */ /* recurse into the subdirectory */ sendabunch (file.name_ptr,NULL); } else{ printf ("store \"%s\" as \"%s\"\n",file.name_ptr, file.name_ptr); store (file.name_ptr,file.name_ptr); } } else if (glob (str, file.name_ptr) == SUCCESS) { printf ("store \"%s\" as \"%s\":\n", file.name_ptr, file.name_ptr); store (file.name_ptr,file.name_ptr); } } } /* */ if (i == NULL) { char dot[80]; /* place for abspath to stringify */ abspath (".", dot, &dot[sizeof (dot)]); printf ("error reading directory \"%s\"; %s\n", dot, errmsg (0)); } /* if nothing left in this directory, change back to parent */ fclose (dfds); if (recurstor) { putcmd ("XCUP"); sndcmd(); rwait (2); chdir (".."); } } /* */ char * desyntactify (str) /* strip the quotes from the returned */ char *str; /* directory. NOTE: acts directly on its argument */ { register char *p, *q; q = p = str; while (*p) { if (*p == '"') p++; *q++ = *p++; } *q = '\0'; return (str); } /* */ glob (tmpl,str) char *str; char *tmpl; /* str is a candidate to match tmpl. tmpl contains junk in the normal glob-type format, e.g. '*' matches anything in str, '?' matches any single char, and "[...]" in tmpl gives an acceptable set of guys in str to match, "[...a-z...]" matches any char between a & z. */ { register char *t, *s; t = tmpl; s = str; while (*s && *t == *s ) { t++; s++; } if (*t == '\0' && *s == '\0') return (SUCCESS); switch (*t++) { /* tmpl & str have diverged; if it is because *tmpl is a */ /* globular character, then do the appropriate thing. */ /* otherwise, return FAIL */ case '*': /****************************************************************/ /* algorithm for '*': */ /* if we've used up string, */ /* if there's nothing left to tmpl, success. */ /* if tmpl expects more, fail. */ /* otherwise, recurse on the stuff after the asterisk in tmpl */ /* and str++ */ /****************************************************************/ while (1) /* mung */ { if (*t == '\0') return (SUCCESS); if (*s == '\0') { if (*t != '\0') return (FAIL); return (SUCCESS); } if ((glob (t, s++)) == SUCCESS) return (SUCCESS); } case '?': return (glob (t, ++s)); case '[': if (one_of (t, *s++) == SUCCESS) { while (*t && *t++ != ']'); return (glob (t, s)); } return (FAIL); default: return (FAIL); } } /* */ one_of (tmpl, ch) char *tmpl; int ch; /* [...a-z...] handler */ { register char *t; t = tmpl; while (*t) { switch (*t) { case ']': return (FAIL); case '-': if (ch >= *(t-1) && ch <= *(t+1) && *(t+1) != ']') return (SUCCESS); goto next; default: if (ch == *t) return (SUCCESS); } next: t++; } return (ERROR); } /* */ f_mret() { char name[256]; char dstn[256]; FILE *mretfile; /* generate a unique filename */ sprintf (mretname, "/tmp/ftp=%d.%d", getpid (), getuid()); if(mretfile != NULL) /* if an earlier mret was interrupted, then */ { fclose(mretfile); /* this file already exists... */ unlink(mretname); } if (fnedinit (arg2) < 0) return; printf ("getting list of names from server....\n"); if (nlst (arg1, mretname) == 0) { printf ("nlst failed\n"); unlink (mretname); abterr (); return; } if ((mretfile = fopen (mretname, "r")) == NULL) { printf ("can't open temporary file \"%s\"; %s\n", mretname, errmsg (0)); unlink (mretname); return; } while (mretfile != NULL) /* until mretfile is exhausted */ { sleep (20); /* give system enough time to clean up from */ if (uicount) /* the last transfer */ { fclose (mretfile); unlink (mretname); mretfile = NULL; abterr (); } if (fgets (name, sizeof name, mretfile) != NULL) { name[strlen(name)-1] = '\0'; /* backup over newline */ if (fned (name, dstn, sizeof (dstn)) < 0) { printf ("\"%s\" not retrieved\n", name); } else { if (!(fnflag & FNASK)) printf ("retrieve \"%s\" as \"%s\"\n", name, dstn); putcmd ("RETR"); fetch (name, dstn); } } } fclose (mretfile); unlink (mretname); *mretname = 0; } /* */ char * mrethlp[] = { "'?' prints this informatio;n'-flags' sets up a string-processor to", "transform a foreign file-name into something Unix would understand", "\nFlags to set up file-name processor:\n", " T - Tenex/Tops20-mode: strips the <...> directory, and the protection", " information from the filename; also it lowers the case of the", " letters in the file-name, removes control-V's, and replaces", " Tenex's semi-colons before the version number with periods", " I - ITS mode: strips the \"DSK: DIR;\" from the beginning of the", " filename, lowers the case of the letters in the filename, and", " replaces spaces with '_'", " M - Multics mode: not implimented yet", " a - alphanumerics only: non-alphanumeric letters in the remote", " filename are stripped (NOTE: '.' is regarded as alphabetic)", " l - convert uppercase to lower-case", " p/foo/ -", " prefix the string 'foo' to each file-name ('\\' will escape '/')", " this flag may be combined with any other", " ? - rather than trying to mold the remote filename into a Unix", " filename, just ask the user for a name to store this file", NULL }; /* */ fnedinit (key) char *key; { register char *p; register char ** q; fnflag = 0; *fnprefix = '\0'; if (!*key) { fnflag |= FNCPY; return (1); } if (*key == '?') { for (q=mrethlp; *q != NULL; q++) puts(*q); return (-1); } if (*key++ != '-') { printf ("Flags must begin with hyphens\n"); return (-1); } while (*key) { switch (*key++) { case 'T': fnflag |= FNTENEX | FNTOLCASE; break; case 'I': fnflag |= FNITS | FNTOLCASE; break; case 'l': fnflag |= FNTOLCASE | FNCPY; break; case 'a': fnflag |= FNALPHA | FNCPY; break; case '?': /* asking precludes all others */ *fnprefix = '\0'; fnflag = FNASK; return 0; case 'p': fnflag |= FNCPY; p = fnprefix; if (*key++ != '/') { printf ("no '/' bordering prefix\n"); return (-1); } while (*key && *key != '/') { if (*key == '\\') key++; *p++ = *key++; } if (*key) key++; else return (1); *p = '\0'; break; default: printf ("bad flag: '%c'\n", *(--key)); return (-1); } } return (0); } /* */ fned (src, dest, destsize) /* file-name editor -- does transformations on file-names */ char *src, *dest; { register char *p, *d; register int c; d = dest; /* enter prefix */ if (*(p = fnprefix)) while (*p && d < &(dest[destsize-1])) *d++ = *p++; if (fnflag & FNASK) { printf ("retrieve \"%s\" as: ", src); if ((p = getarg()) <= 0) /* ignore newline */ if ((p = getarg()) <= 0) return (-1); movarg (p, d, (destsize-1)); return (1); } p = src; if (fnflag & FNTENEX) { /* strip the leading directory, and the trailing protection */ /* first, the directory, if its there... */ if (*p == '<') while (*p && *p++ != '>') ; /* look for the third dot-or-semicolon */ c = 0; while (*p && d < &(dest[destsize-1])) { if ((*p == ';' || *p == '.') && c++ >= 2) break; else { /* control-V is a quoting character */ if (*p == CTLV) p++; if (*p == ';') { p++; *d++ = '.'; } else *d++ = *p++; } } } else if (fnflag & FNITS) { while (*p && *p++ != ';') ; do { if (*p == ' ') *d++ = '_'; else *d++ = *p; } while (*p++ && d < &(dest[destsize-1])); } else if (fnflag & FNCPY) while (*p && d < &(dest[destsize-1])) *d++ = *p++; *d = '\0'; /* now do individual character transformations */ d = dest; /* skip past the prefix */ if (*(p = fnprefix)) while (*p++) d++; do { if ((fnflag & FNALPHA) && !(isdigit (c = *d) || isalpha (c) || c == '.')) { p = d; do { /* flush this character */ *p = *(p+1); /* bump up the rest */ } while (*p++); } if ((fnflag & FNTOLCASE) && (isupper (*d))) *d++ = tolower (*d); else d++; } while (*d); return (1); } #endif MGET /* */ #define IOBSIZ 120 char ibuf[IOBSIZ], *iptr; int icnt = 0; int cmdflg = 0; extern int synchno; int ins(); ftp_printer(otherhalf) int otherhalf; /* the arguments to this process */ { register char *p; int n; extern exit(), diequit(), dieinit(); extern char *atoiv(), *get_dirstr(); dieinit(otherhalf, stdout); signal(SIGINT, SIG_IGN); /* Ignore interrupt--for main FTP */ signal(SIGQUIT, SIG_IGN); /* Ignore quit, so quit in inferior shell of ftpmain works. */ signal(SIGURG,ins); fclose(stdin); fclose(stdout); for (;;) { linein(); n = iconv(linbuf); fputs(linbuf, stderr); if (n) { if (write(pipnum, &n, sizeof n) != sizeof(n)) die(1, "%s: can't write reply-code to ftpmain; %s\n", progname, errmsg(0)); if (n == MADEDIR) { if (p = get_dirstr(linbuf)) { n = strlen(p); write(pipnum, &n, sizeof n); write(pipnum, p, n); } else /* write a 0 if no directory string */ { n = 0; write(pipnum, &n, sizeof n); } } } } } iconv(s) char *s; { register int k, i, c; k = 0; for (i = 0; i < 3; i++) { c = s[i]; if(c < '0' || c > '9') return(0); k = k*10 + c - '0'; } if(s[3] == '-') return(0); /* this is a continued comment */ return(k); } char *get_dirstr(s) char *s; { register char *p, *q; q = p = &(s[4]); if (*p++ != '"') return 0; while (*p) { if (*p++ == '"') /* check if this is an escaped quote */ { if (*p == '"') { p++; continue; } else /* not an escaped quote, return */ { *p = '\0'; return q; } } } *p = '\0'; return q; } /* */ linein() /* Get a line into linbuf */ { register int c, ovflg; register char *linptr; int retflg; linptr = linbuf; retflg = ovflg = 0; for (;;) { if(linptr >= (linbuf + LINSIZ - 2)) ovflg++; c = getch(); if(cmdflg) { cmdflg = 0; /* "Interesting" Commands */ if(c == TNDM) { # ifdef TCP extern tsturg(); synchno = tsturg(&NetParams); # endif TCP # ifdef NCP --synchno; # endif NCP linptr = linbuf; ovflg = 0; continue; } /* Other Telnet Commands */ if(synchno == 0) { switch(c) { case TNEC: if(linptr>linbuf) { linptr--; ovflg = 0; } continue; case TNEL: linptr = linbuf; ovflg = 0; continue; case TNDO: case TNWILL: tpopt(c); continue; case TNIAC: if(ovflg == 0) *linptr++ = c; } } continue; /* Command ignored if synchno or not implemented */ } if(c == TNIAC) cmdflg++; else if(synchno == 0) switch(c) { case '\r': retflg++; continue; case '\n': *linptr++ = c; *linptr++ = '\0'; return; case '\0': if(retflg) { if(ovflg == 0) *linptr++ = '\r'; retflg = 0; } continue; default: if(ovflg == 0) *linptr++ = c; } } } /* */ tpopt(c) char c; { char bf[3]; bf[0] = TNIAC; bf[1] = (c == TNDO ? TNWONT : TNDONT); bf[2] = getch(); if (net_write(&NetParams, bf, 3) < 0) die(3, "%s: can't write options to net; %s\n", errmsg(0)); } getch() { if(--icnt < 0) { retry: if ((icnt = net_read(&NetParams, ibuf, IOBSIZ)) < 0) { if (errno == EINTR) goto retry; else if (errno == ENETSTAT) { get_stuff(&NetParams); if (NetParams.ns.n_state & URXTIMO) { printf("Host not responding\n"); goto retry; } else if (NetParams.ns.n_state & UURGENT) goto retry; } } if(icnt-- < 0) die(4, "%s: net input closed; %s\n", progname, errmsg(0)); iptr = ibuf; } return(*iptr++ & 0377); } /* */ /* User Telnet and FTP - modified for Illinois NCP from old Rand user telnet. * * Note: BBN-UNIX only uses it to invoke User FTP. User Telnet is now a * completely different program. Dan Franklin (BBN) * * Changed to use long host numbers jsq BBN 3-27-79. * * Leave signals alone: child will set according to what parent had set * jsq BBN 5Aug79 * * Pass arguments to both sides of ftp: pid of other side, name of other * host, name of this host. All host name-number conversions are now in this * process, and children can now mop up on each other when either one dies. * * Changed to flush the telnet code, as we don't use it any more dm 3-19-80 * * jsq BBN 5April80 reformatted using indent, put proper names for files * to exec back. * * changed to work with NCP or TCP dm 11-19-80 */ char ttibuf[32]; main (argc, argv) int argc; char *argv[]; { int badhost; int pid, parent; portsock socket; char *host; int ftpip[2]; netaddr hnum; register int count; signal (SIG_NETINT, SIG_IGN); progname = argv[0]; socket = (portsock)0; if (argc > 1) { host = argv[1]; hnum = gethost(host); badhost = isbadhost(hnum); if (badhost) printf ("Unknown host name: \"%s\"\n", host); if (argc > 2) socket = ATOSOCK (argv[2]); } while (badhost) { printf ("Host: "); if (fgets (ttibuf, sizeof ttibuf, stdin) == NULL) exit (1); count = strlen (ttibuf); if (ttibuf[count-1] == '\n') ttibuf[count-1] = 0; while (count-- >= 0) if (ttibuf[count] == '?') { printf("use host(1) or prhost(1) to find host names\n"); break; } if ((count < 0) && (ttibuf[0])) { hnum = gethost(ttibuf); badhost = isbadhost(hnum); if (badhost) printf ("Unknown host name: \"%s\"\n", ttibuf); else host = ttibuf; } } printf ("Trying %s (%s)\n", host, hostfmt(hnum, 1)); if (net_open(&NetParams, hnum, (socket?socket:(portsock)FTPSOCK)) < 0) { printf ("%s cannot connect: %s\n", progname, errmsg (0)); exit (1); } printf ("Connections established.\n\n"); us = thisname (0); them = hostname (hnum); strcpy ((HOST_ID = malloc(strlen(them)+1)), them); getuc (HOST_ID); parent = getpid(); pipe (ftpip); if ((pid = fork()) == -1) { printf ("%s: can't fork %s\n", progname, errmsg (0)); exit (1); } if (pid == 0) { close(ftpip[0]); /* fdpip = fdopen(ftpip[1],"w"); */ pipnum = ftpip[1]; argv[0] = progname = "ftp_printer"; ftp_printer(parent); exit (1); } close(ftpip[1]); /* fdpip = fdopen(ftpip[0]); Sorry, this loses. */ pipnum = ftpip[0]; argv[0] = progname = "ftpmain"; ftpmain(pid); } /* * convert to upper case */ getuc(s) register char *s; { register int c; while (c = *s) *s++ = islower(c)? toupper(c) : c; }