ftpcli.c
上传用户:hepax88
上传日期:2007-01-03
资源大小:1101k
文件大小:28k
源码类别:

TCP/IP协议栈

开发平台:

Visual C++

  1. /* Internet FTP client (interactive user)
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include <stdio.h>
  5. #include "global.h"
  6. #include "mbuf.h"
  7. #include "session.h"
  8. #include "cmdparse.h"
  9. #include "proc.h"
  10. #include "tty.h"
  11. #include "socket.h"
  12. #include "ftp.h"
  13. #include "ftpcli.h"
  14. #include "commands.h"
  15. #include "netuser.h"
  16. #include "dirutil.h"
  17. #include "internet.h"
  18. #define POLLRATE 500 /* 500ms between more file polls */
  19. #define DIRBUF 256
  20. static int doascii(int argc,char *argv[],void *p);
  21. static int dobatch(int argc,char *argv[],void *p);
  22. static int dobinary(int argc,char *argv[],void *p);
  23. static int docompare(int argc,char *argv[],void *p);
  24. static int doftpcd(int argc,char *argv[],void *p);
  25. static int doget(int argc,char *argv[],void *p);
  26. static int dohash(int argc,char *argv[],void *p);
  27. static int doverbose(int argc,char *argv[],void *p);
  28. static int dolist(int argc,char *argv[],void *p);
  29. static int dols(int argc,char *argv[],void *p);
  30. static int domd5(int argc,char *argv[],void *p);
  31. static int domkdir(int argc,char *argv[],void *p);
  32. static int domcompare(int argc,char *argv[],void *p);
  33. static int domget(int argc,char *argv[],void *p);
  34. static int domput(int argc,char *argv[],void *p);
  35. static int doput(int argc,char *argv[],void *p);
  36. static int doquit(int argc,char *argv[],void *p);
  37. static int doread(int argc,char *argv[],void *p);
  38. static int dormdir(int argc,char *argv[],void *p);
  39. static int dotype(int argc,char *argv[],void *p);
  40. static int doupdate(int argc,char *argv[],void *p);
  41. static int getline(struct session *sp,char *prompt,char *buf,int n);
  42. static int getresp(struct ftpcli *ftp,int mincode);
  43. static long getsub(struct ftpcli *ftp,char *command,char *remotename,
  44. FILE *fp);
  45. static long putsub(struct ftpcli *ftp,char *remotename,char *localname);
  46. static int compsub(struct ftpcli *ftp,char *localname,char *remotename);
  47. static void sendport(FILE *fp,struct sockaddr_in *socket);
  48. static int keychar(int c);
  49. static char Notsess[] = "Not an FTP session!n";
  50. static struct cmds Ftpcmds[] = {
  51. "", donothing, 0, 0, NULL,
  52. "ascii", doascii, 0, 0, NULL,
  53. "batch", dobatch, 0, 0, NULL,
  54. "binary", dobinary, 0, 0, NULL,
  55. "cd", doftpcd, 0, 2, "cd <directory>",
  56. "compare", docompare, 0, 2, "compare <remotefile> [<localfile>]",
  57. "dir", dolist, 0, 0, NULL,
  58. "list", dolist, 0, 0, NULL,
  59. "get", doget, 0, 2, "get <remotefile> <localfile>",
  60. "hash", dohash, 0, 0, NULL,
  61. "ls", dols, 0, 0, NULL,
  62. "mcompare", domcompare, 0, 2, "mcompare <file> [<file> ...]",
  63. "md5", domd5, 0, 2, "md5 <file>",
  64. "mget", domget, 0, 2, "mget <file> [<file> ...]",
  65. "mkdir", domkdir, 0, 2, "mkdir <directory>",
  66. "mput", domput, 0, 2, "mput <file> [<file> ...]",
  67. "nlst", dols, 0, 0, NULL,
  68. "quit", doquit, 0, 0, NULL,
  69. "read", doread, 0, 2, "read <remotefile>",
  70. "rmdir", dormdir, 0, 2, "rmdir <directory>",
  71. "put", doput, 0, 2, "put <localfile> <remotefile>",
  72. "type", dotype, 0, 0, NULL,
  73. "update", doupdate, 0, 0, NULL,
  74. "verbose", doverbose, 0, 0, NULL,
  75. NULL, NULL, 0, 0, NULL,
  76. };
  77. /* Handle top-level FTP command */
  78. int
  79. doftp(argc,argv,p)
  80. int argc;
  81. char *argv[];
  82. void *p;
  83. {
  84. struct session *sp;
  85. struct ftpcli ftp;
  86. struct sockaddr_in fsocket;
  87. int resp,vsave;
  88. char *bufsav,*cp;
  89. FILE *control;
  90. int s;
  91. /* Allocate a session control block */
  92. if((sp = newsession(Cmdline,FTP,1)) == NULL){
  93. printf("Too many sessionsn");
  94. return 1;
  95. }
  96. sp->inproc = keychar;
  97. memset(&ftp,0,sizeof(ftp));
  98. ftp.control = ftp.data = NULL;
  99. ftp.verbose = V_NORMAL;
  100. sp->cb.ftp = &ftp; /* Downward link */
  101. ftp.session = sp; /* Upward link */
  102. fsocket.sin_family = AF_INET;
  103. if(argc < 3)
  104. fsocket.sin_port = IPPORT_FTP;
  105. else
  106. fsocket.sin_port = atoi(argv[2]);
  107. if(SETSIG(EABORT)){
  108. keywait(NULL,1);
  109. freesession(sp);
  110. return 1;
  111. }
  112. printf("Resolving %s...n",argv[1]);
  113. if((fsocket.sin_addr.s_addr = resolve(argv[1])) == 0){
  114. printf(Badhost,argv[1]);
  115. keywait(NULL,1);
  116. freesession(sp);
  117. return 1;
  118. }
  119. /* Open the control connection */
  120. if((s = socket(AF_INET,SOCK_STREAM,0)) == -1){
  121. printf("Can't create socketn");
  122. keywait(NULL,1);
  123. freesession(sp);
  124. return 1;
  125. }
  126. if(SETSIG(EABORT)){
  127. goto quit;
  128. }
  129. sp->network = control = ftp.control = fdopen(s,"r+t");
  130. settos(s,LOW_DELAY);
  131. printf("Trying %s...n",psocket(&fsocket));
  132. if(connect(s,(struct sockaddr *)&fsocket,sizeof(fsocket)) == -1){
  133. perror("Connect failed");
  134. goto quit;
  135. }
  136. printf("Connectedn");
  137. /* Wait for greeting from server */
  138. resp = getresp(&ftp,200);
  139. if(resp >= 400)
  140. goto quit;
  141. /* Now process responses and commands */
  142. if(SETSIG(EABORT)){
  143. /* Come back here after a ^C in command state */
  144. resp = 200;
  145. }
  146. while(resp != -1){
  147. switch(resp){
  148. case 220:
  149. /* Sign-on banner; prompt for and send USER command */
  150. getline(sp,"Enter user name: ",ftp.buf,LINELEN);
  151. /* Send the command only if the user response
  152.  * was non-null
  153.  */
  154. if(ftp.buf[0] != 'n'){
  155. fprintf(control,"USER %s",ftp.buf);
  156. resp = getresp(&ftp,200);
  157. } else
  158. resp = 200; /* dummy */
  159. break;
  160. case 331:
  161. /* turn off echo */
  162. sp->ttystate.echo = 0;
  163. getline(sp,"Password: ",ftp.buf,LINELEN);
  164. printf("n");
  165. /* Turn echo back on */
  166. sp->ttystate.echo = 1;
  167. /* Send the command only if the user response
  168.  * was non-null
  169.  */
  170. if(ftp.buf[0] != 'n'){
  171. fprintf(control,"PASS %s",ftp.buf);
  172. resp = getresp(&ftp,200);
  173. } else
  174. resp = 200; /* dummy */
  175. break;
  176. case 230: /* Successful login */
  177. /* Find out what type of system we're talking to */
  178. printf("ftp> systn");
  179. fprintf(control,"SYSTn");
  180. resp = getresp(&ftp,200);
  181. break;
  182. case 215:
  183. /* Response to SYST command */
  184. cp = strchr(ftp.line,' ');
  185. if(cp != NULL && strnicmp(cp+1,System,strlen(System)) == 0){
  186. ftp.type = IMAGE_TYPE;
  187. printf("Defaulting to binary moden");
  188. }
  189. resp = 200; /* dummy */
  190. break;
  191. default:
  192. /* Test the control channel first */
  193. if(sockstate(fileno(control)) == NULL){
  194. resp = -1;
  195. break;
  196. }
  197. getline(sp,"ftp> ",ftp.buf,LINELEN);
  198. /* Copy because cmdparse modifies the original */
  199. bufsav = strdup(ftp.buf);
  200. if((resp = cmdparse(Ftpcmds,ftp.buf,&ftp)) != -1){
  201. /* Valid command, free buffer and get another */
  202. FREE(bufsav);
  203. } else {
  204. /* Not a local cmd, send to remote server */
  205. fputs(bufsav,control);
  206. FREE(bufsav);
  207. /* Enable display of server response */
  208. vsave = ftp.verbose;
  209. ftp.verbose = V_NORMAL;
  210. resp = getresp(&ftp,200);
  211. ftp.verbose = vsave;
  212. }
  213. }
  214. }
  215. quit: cp = sockerr(fileno(control));
  216. printf("Closed: %sn",cp != NULL ? cp : "EOF");
  217. if(ftp.fp != NULL && ftp.fp != stdout)
  218. fclose(ftp.fp);
  219. if(ftp.data != NULL)
  220. fclose(ftp.data);
  221. if(ftp.control != NULL){
  222. fclose(ftp.control);
  223. ftp.control = NULL;
  224. sp->network = NULL;
  225. }
  226. keywait(NULL,1);
  227. if(ftp.session != NULL)
  228. freesession(ftp.session);
  229. return 0;
  230. }
  231. /* Control verbosity level */
  232. static int
  233. doverbose(argc,argv,p)
  234. int argc;
  235. char *argv[];
  236. void *p;
  237. {
  238. register struct ftpcli *ftp;
  239. if((ftp = (struct ftpcli *)p) == NULL)
  240. return -1;
  241. return setshort(&ftp->verbose,"Verbose",argc,argv);
  242. }
  243. /* Enable/disable command batching */
  244. static int
  245. dobatch(argc,argv,p)
  246. int argc;
  247. char *argv[];
  248. void *p;
  249. {
  250. register struct ftpcli *ftp;
  251. if((ftp = (struct ftpcli *)p) == NULL)
  252. return -1;
  253. return setbool(&ftp->batch,"Command batching",argc,argv);
  254. }
  255. /* Enable/disable update flag */
  256. static int
  257. doupdate(argc,argv,p)
  258. int argc;
  259. char *argv[];
  260. void *p;
  261. {
  262. register struct ftpcli *ftp;
  263. if((ftp = (struct ftpcli *)p) == NULL)
  264. return -1;
  265. return setbool(&ftp->update,"Update with MD5",argc,argv);
  266. }
  267. /* Set verbosity to high (convenience command) */
  268. static int
  269. dohash(argc,argv,p)
  270. int argc;
  271. char *argv[];
  272. void *p;
  273. {
  274. register struct ftpcli *ftp;
  275. if((ftp = (struct ftpcli *)p) == NULL)
  276. return -1;
  277. ftp->verbose = V_HASH;
  278. return 0;
  279. }
  280. /* Close session */
  281. static int
  282. doquit(argc,argv,p)
  283. int argc;
  284. char *argv[];
  285. void *p;
  286. {
  287. register struct ftpcli *ftp;
  288. ftp = (struct ftpcli *)p;
  289. if(ftp == NULL)
  290. return -1;
  291. fprintf(ftp->control,"QUITn");
  292. getresp(ftp,200); /* Get the closing message */
  293. getresp(ftp,200); /* Wait for the server to close */
  294. return -1;
  295. }
  296. /* Translate 'cd' to 'cwd' for convenience */
  297. static int
  298. doftpcd(argc,argv,p)
  299. int argc;
  300. char *argv[];
  301. void *p;
  302. {
  303. register struct ftpcli *ftp;
  304. ftp = (struct ftpcli *)p;
  305. if(ftp == NULL)
  306. return -1;
  307. fprintf(ftp->control,"CWD %sn",argv[1]);
  308. return getresp(ftp,200);
  309. }
  310. /* Translate 'mkdir' to 'xmkd' for convenience */
  311. static int
  312. domkdir(argc,argv,p)
  313. int argc;
  314. char *argv[];
  315. void *p;
  316. {
  317. register struct ftpcli *ftp;
  318. ftp = (struct ftpcli *)p;
  319. if(ftp == NULL)
  320. return -1;
  321. fprintf(ftp->control,"XMKD %sn",argv[1]);
  322. return getresp(ftp,200);
  323. }
  324. /* Translate 'rmdir' to 'xrmd' for convenience */
  325. static int
  326. dormdir(argc,argv,p)
  327. int argc;
  328. char *argv[];
  329. void *p;
  330. {
  331. register struct ftpcli *ftp;
  332. ftp = (struct ftpcli *)p;
  333. if(ftp == NULL)
  334. return -1;
  335. fprintf(ftp->control,"XRMD %sn",argv[1]);
  336. return getresp(ftp,200);
  337. }
  338. static int
  339. dobinary(argc,argv,p)
  340. int argc;
  341. char *argv[];
  342. void *p;
  343. {
  344. char *args[2];
  345. args[1] = "I";
  346. return dotype(2,args,p);
  347. }
  348. static int
  349. doascii(argc,argv,p)
  350. int argc;
  351. char *argv[];
  352. void *p;
  353. {
  354. char *args[2];
  355. args[1] = "A";
  356. return dotype(2,args,p);
  357. }
  358. /* Handle "type" command from user */
  359. static int
  360. dotype(argc,argv,p)
  361. int argc;
  362. char *argv[];
  363. void *p;
  364. {
  365. register struct ftpcli *ftp;
  366. ftp = (struct ftpcli *)p;
  367. if(ftp == NULL)
  368. return -1;
  369. if(argc < 2){
  370. switch(ftp->type){
  371. case IMAGE_TYPE:
  372. printf("Imagen");
  373. break;
  374. case ASCII_TYPE:
  375. printf("Asciin");
  376. break;
  377. case LOGICAL_TYPE:
  378. printf("Logical bytesize %un",ftp->logbsize);
  379. break;
  380. }
  381. return 0;
  382. }
  383. switch(*argv[1]){
  384. case 'i':
  385. case 'I':
  386. case 'b':
  387. case 'B':
  388. ftp->type = IMAGE_TYPE;
  389. break;
  390. case 'a':
  391. case 'A':
  392. ftp->type = ASCII_TYPE;
  393. break;
  394. case 'L':
  395. case 'l':
  396. ftp->type = LOGICAL_TYPE;
  397. ftp->logbsize = atoi(argv[2]);
  398. break;
  399. default:
  400. printf("Invalid type %sn",argv[1]);
  401. return 1;
  402. }
  403. return 0;
  404. }
  405. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  406. static int
  407. doget(argc,argv,p)
  408. int argc;
  409. char *argv[];
  410. void *p;
  411. {
  412. char *remotename,*localname;
  413. register struct ftpcli *ftp;
  414. FILE *fp;
  415. char *mode;
  416. ftp = (struct ftpcli *)p;
  417. if(ftp == NULL){
  418. printf(Notsess);
  419. return 1;
  420. }
  421. remotename = argv[1];
  422. if(argc < 3)
  423. localname = remotename;
  424. else
  425. localname = argv[2];
  426. switch(ftp->type){
  427. case IMAGE_TYPE:
  428. case LOGICAL_TYPE:
  429. mode = WRITE_BINARY;
  430. break;
  431. case ASCII_TYPE:
  432. mode = WRITE_TEXT;
  433. break;
  434. }
  435. if((fp = fopen(localname,mode)) == NULL){
  436. printf("Can't write %s",localname);
  437. perror("");
  438. return 1;
  439. }
  440. getsub(ftp,"RETR",remotename,fp);
  441. fclose(fp);
  442. return 0;
  443. }
  444. /* Read file direct to screen. Syntax: read <remote name> */
  445. static int
  446. doread(argc,argv,p)
  447. int argc;
  448. char *argv[];
  449. void *p;
  450. {
  451. register struct ftpcli *ftp;
  452. if((ftp = (struct ftpcli *)p) == NULL){
  453. printf(Notsess);
  454. return 1;
  455. }
  456. getsub(ftp,"RETR",argv[1],stdout);
  457. return 0;
  458. }
  459. /* Get a collection of files */
  460. static int
  461. domget(argc,argv,p)
  462. int argc;
  463. char *argv[];
  464. void *p;
  465. {
  466. register struct ftpcli *ftp;
  467. FILE *files,*fp;
  468. char *buf,*mode;
  469. int i;
  470. long r;
  471. if((ftp = (struct ftpcli *)p) == NULL){
  472. printf(Notsess);
  473. return 1;
  474. }
  475. switch(ftp->type){
  476. case IMAGE_TYPE:
  477. case LOGICAL_TYPE:
  478. mode = WRITE_BINARY;
  479. break;
  480. case ASCII_TYPE:
  481. mode = WRITE_TEXT;
  482. break;
  483. }
  484. buf = mallocw(DIRBUF);
  485. ftp->state = RECEIVING_STATE;
  486. for(i=1;i<argc;i++){
  487. files = tmpfile();
  488. r = getsub(ftp,"NLST",argv[i],files);
  489. if(ftp->abort)
  490. break; /* Aborted */
  491. if(r == -1){
  492. printf("Can't NLST %sn",argv[i]);
  493. continue;
  494. }
  495. /* The tmp file now contains a list of the remote files, so
  496.  * go get 'em. Break out if the user signals an abort.
  497.  */
  498. rewind(files);
  499. while(fgets(buf,DIRBUF,files) != NULL){
  500. rip(buf);
  501. if(!ftp->update || compsub(ftp,buf,buf) != 0){
  502. if((fp = fopen(buf,mode)) == NULL){
  503. printf("Can't write %s",buf);
  504. perror("");
  505. continue;
  506. }
  507. getsub(ftp,"RETR",buf,fp);
  508. fclose(fp);
  509. }
  510. if(ftp->abort){
  511. /* User abort */
  512. ftp->abort = 0;
  513. fclose(files);
  514. free(buf);
  515. ftp->state = COMMAND_STATE;
  516. return 1;
  517. }
  518. }
  519. fclose(files);
  520. }
  521. free(buf);
  522. ftp->state = COMMAND_STATE;
  523. ftp->abort = 0;
  524. return 0;
  525. }
  526. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  527. static int
  528. dolist(argc,argv,p)
  529. int argc;
  530. char *argv[];
  531. void *p;
  532. {
  533. register struct ftpcli *ftp;
  534. FILE *fp;
  535. ftp = (struct ftpcli *)p;
  536. if(ftp == NULL){
  537. printf(Notsess);
  538. return 1;
  539. }
  540. if(argc > 2)
  541. fp = fopen(argv[2],WRITE_TEXT);
  542. else
  543. fp = stdout;
  544. if(fp == NULL){
  545. printf("Can't write local file");
  546. perror("");
  547. return 1;
  548. }
  549. getsub(ftp,"LIST",argv[1],fp);
  550. return 0;
  551. }
  552. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  553. static int
  554. dols(argc,argv,p)
  555. int argc;
  556. char *argv[];
  557. void *p;
  558. {
  559. register struct ftpcli *ftp;
  560. FILE *fp;
  561. if((ftp = (struct ftpcli *)p) == NULL){
  562. printf(Notsess);
  563. return 1;
  564. }
  565. if(argc > 2)
  566. fp = fopen(argv[2],WRITE_TEXT);
  567. else
  568. fp = stdout;
  569. if(fp == NULL){
  570. printf("Can't write local file");
  571. perror("");
  572. return 1;
  573. }
  574. getsub(ftp,"NLST",argv[1],fp);
  575. return 0;
  576. }
  577. static int
  578. domd5(argc,argv,p)
  579. int argc;
  580. char *argv[];
  581. void *p;
  582. {
  583. char *remotename;
  584. register struct ftpcli *ftp;
  585. FILE *control;
  586. int resp;
  587. int typewait = 0;
  588. ftp = (struct ftpcli *)p;
  589. if(ftp == NULL){
  590. printf(Notsess);
  591. return 1;
  592. }
  593. control = ftp->control;
  594. remotename = argv[1];
  595. if(ftp->typesent != ftp->type){
  596. switch(ftp->type){
  597. case ASCII_TYPE:
  598. fprintf(control,"TYPE An");
  599. break;
  600. case IMAGE_TYPE:
  601. fprintf(control,"TYPE In");
  602. break;
  603. case LOGICAL_TYPE:
  604. fprintf(control,"TYPE L %dn",ftp->logbsize);
  605. break;
  606. }
  607. ftp->typesent = ftp->type;
  608. if(!ftp->batch){
  609. resp = getresp(ftp,200);
  610. if(resp == -1 || resp > 299)
  611. goto failure;
  612. } else
  613. typewait = 1;
  614. }
  615. fprintf(control,"XMD5 %sn",remotename);
  616. if(typewait)
  617. (void)getresp(ftp,200);
  618. (void)getresp(ftp,200);
  619. failure:;
  620. return 0;
  621. }
  622. static int
  623. docompare(argc,argv,p)
  624. int argc;
  625. char *argv[];
  626. void *p;
  627. {
  628. char *remotename,*localname;
  629. register struct ftpcli *ftp;
  630. ftp = (struct ftpcli *)p;
  631. if(ftp == NULL){
  632. printf(Notsess);
  633. return 1;
  634. }
  635. remotename = argv[1];
  636. if(argc > 2)
  637. localname = argv[2];
  638. else
  639. localname = remotename;
  640. if(compsub(ftp,localname,remotename) == 0)
  641. printf("Samen");
  642. else
  643. printf("Differentn");
  644. return 0;
  645. }
  646. /* Compare a collection of files */
  647. static int
  648. domcompare(argc,argv,p)
  649. int argc;
  650. char *argv[];
  651. void *p;
  652. {
  653. register struct ftpcli *ftp;
  654. FILE *files;
  655. char *buf;
  656. int i;
  657. long r;
  658. if((ftp = (struct ftpcli *)p) == NULL){
  659. printf(Notsess);
  660. return 1;
  661. }
  662. buf = mallocw(DIRBUF);
  663. ftp->state = RECEIVING_STATE;
  664. for(i=1;i<argc;i++){
  665. files = tmpfile();
  666. r = getsub(ftp,"NLST",argv[i],files);
  667. if(ftp->abort)
  668. break; /* Aborted */
  669. if(r == -1){
  670. printf("Can't NLST %sn",argv[i]);
  671. continue;
  672. }
  673. /* The tmp file now contains a list of the remote files, so
  674.  * go get 'em. Break out if the user signals an abort.
  675.  */
  676. rewind(files);
  677. while(fgets(buf,DIRBUF,files) != NULL){
  678. rip(buf);
  679. if(compsub(ftp,buf,buf) == 0)
  680. printf("%s - Samen",buf);
  681. else
  682. printf("%s - Differentn",buf);
  683. if(ftp->abort){
  684. /* User abort */
  685. ftp->abort = 0;
  686. fclose(files);
  687. free(buf);
  688. ftp->state = COMMAND_STATE;
  689. return 1;
  690. }
  691. }
  692. fclose(files);
  693. }
  694. free(buf);
  695. ftp->state = COMMAND_STATE;
  696. ftp->abort = 0;
  697. return 0;
  698. }
  699. /* Common subroutine to compare a local with a remote file
  700.  * Return 1 if files are different, 0 if they are the same
  701.  */
  702. static int
  703. compsub(ftp,localname,remotename)
  704. struct ftpcli *ftp;
  705. char *localname;
  706. char *remotename;
  707. {
  708. char *mode,*cp;
  709. FILE *control,*fp;
  710. int resp,i;
  711. int typewait = 0;
  712. uint8 remhash[16];
  713. uint8 lochash[16];
  714. control = ftp->control;
  715. switch(ftp->type){
  716. case IMAGE_TYPE:
  717. case LOGICAL_TYPE:
  718. mode = READ_BINARY;
  719. break;
  720. case ASCII_TYPE:
  721. mode = READ_TEXT;
  722. break;
  723. }
  724. if((fp = fopen(localname,mode)) == NULL){
  725. printf("Can't read local file %sn",localname);
  726. return 1;
  727. }
  728. if(ftp->typesent != ftp->type){
  729. switch(ftp->type){
  730. case ASCII_TYPE:
  731. fprintf(control,"TYPE An");
  732. break;
  733. case IMAGE_TYPE:
  734. fprintf(control,"TYPE In");
  735. break;
  736. case LOGICAL_TYPE:
  737. fprintf(control,"TYPE L %dn",ftp->logbsize);
  738. break;
  739. }
  740. ftp->typesent = ftp->type;
  741. if(!ftp->batch){
  742. resp = getresp(ftp,200);
  743. if(resp == -1 || resp > 299)
  744. goto failure;
  745. } else
  746. typewait = 1;
  747. }
  748. fprintf(control,"XMD5 %sn",remotename);
  749. /* Try to overlap the two MD5 operations */
  750. md5hash(fp,lochash,ftp->type == ASCII_TYPE);
  751. fclose(fp);
  752. if(typewait && (resp = getresp(ftp,200)) > 299)
  753. goto failure;
  754. if((resp = getresp(ftp,200)) > 299){
  755. if(resp == 500)
  756. ftp->update = 0; /* XMD5 not supported */
  757. goto failure;
  758. }
  759. if((cp = strchr(ftp->line,' ')) == NULL){
  760. printf("Error in responsen");
  761. goto failure;
  762. }
  763. /* Convert ascii/hex back to binary */
  764. readhex(remhash,cp,sizeof(remhash));
  765. if(ftp->verbose > 1){
  766. printf("Loc ");
  767. for(i=0;i<sizeof(lochash);i++)
  768. printf("%02x",lochash[i]);
  769. printf(" %sn",localname);
  770. }
  771. if(memcmp(lochash,remhash,sizeof(remhash)) == 0)
  772. return 0;
  773. else
  774. return 1;
  775. failure:;
  776. return 1;
  777. }
  778. /* Common code to LIST/NLST/RETR and mget
  779.  * Returns number of bytes received if successful
  780.  * Returns -1 on error
  781.  */
  782. static long
  783. getsub(ftp,command,remotename,fp)
  784. register struct ftpcli *ftp;
  785. char *command,*remotename;
  786. FILE *fp;
  787. {
  788. unsigned long total;
  789. FILE *control;
  790. int cnt,resp,i,savmode;
  791. struct sockaddr_in lsocket;
  792. struct sockaddr_in lcsocket;
  793. int32 startclk,rate;
  794. int vsave;
  795. int typewait = 0;
  796. int prevstate;
  797. int d;
  798. if(ftp == NULL)
  799. return -1;
  800. savmode = ftp->type;
  801. control = ftp->control;
  802. /* Open the data connection */
  803. d = socket(AF_INET,SOCK_STREAM,0);
  804. listen(d,0); /* Accept only one connection */
  805. switch(ftp->type){
  806. case IMAGE_TYPE:
  807. case LOGICAL_TYPE:
  808. ftp->data = fdopen(d,"r+b");
  809. break;
  810. case ASCII_TYPE:
  811. ftp->data = fdopen(d,"r+t");
  812. break;
  813. }
  814. prevstate = ftp->state;
  815. ftp->state = RECEIVING_STATE;
  816. /* Send TYPE message, if necessary */
  817. if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
  818. /* Directory listings are always in ASCII */
  819. ftp->type = ASCII_TYPE;
  820. }
  821. if(ftp->typesent != ftp->type){
  822. switch(ftp->type){
  823. case ASCII_TYPE:
  824. fprintf(control,"TYPE An");
  825. break;
  826. case IMAGE_TYPE:
  827. fprintf(control,"TYPE In");
  828. break;
  829. case LOGICAL_TYPE:
  830. fprintf(control,"TYPE L %dn",ftp->logbsize);
  831. break;
  832. }
  833. ftp->typesent = ftp->type;
  834. if(!ftp->batch){
  835. resp = getresp(ftp,200);
  836. if(resp == -1 || resp > 299)
  837. goto failure;
  838. } else
  839. typewait = 1;
  840. }
  841. /* Send the PORT message. Use the IP address
  842.  * on the local end of our control connection.
  843.  */
  844. i = SOCKSIZE;
  845. getsockname(d,(struct sockaddr *)&lsocket,&i); /* Get port number */
  846. i = SOCKSIZE;
  847. getsockname(fileno(ftp->control),(struct sockaddr *)&lcsocket,&i);
  848. lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  849. sendport(control,&lsocket);
  850. if(!ftp->batch){
  851. /* Get response to PORT command */
  852. resp = getresp(ftp,200);
  853. if(resp == -1 || resp > 299)
  854. goto failure;
  855. }
  856. /* Generate the command to start the transfer */
  857. if(remotename != NULL)
  858. fprintf(control,"%s %sn",command,remotename);
  859. else
  860. fprintf(control,"%sn",command);
  861. if(ftp->batch){
  862. /* Get response to TYPE command, if sent */
  863. if(typewait){
  864. resp = getresp(ftp,200);
  865. if(resp == -1 || resp > 299)
  866. goto failure;
  867. }
  868. /* Get response to PORT command */
  869. resp = getresp(ftp,200);
  870. if(resp == -1 || resp > 299)
  871. goto failure;
  872. }
  873. /* Get the intermediate "150" response */
  874. resp = getresp(ftp,100);
  875. if(resp == -1 || resp >= 400)
  876. goto failure;
  877. /* Wait for the server to open the data connection */
  878. cnt = 0;
  879. d = accept(d,NULL,&cnt);
  880. startclk = msclock();
  881. /* If output is to the screen, temporarily disable hash marking */
  882. vsave = ftp->verbose;
  883. if(vsave >= V_HASH && fp == NULL)
  884. ftp->verbose = V_NORMAL;
  885. total = recvfile(fp,ftp->data,ftp->type,ftp->verbose);
  886. /* Immediately close the data connection; some servers (e.g., TOPS-10)
  887.  * wait for the data connection to close completely before returning
  888.  * the completion message on the control channel
  889.  */
  890. fclose(ftp->data);
  891. ftp->data = NULL;
  892. #ifdef CPM
  893. if(fp != NULL && ftp->type == ASCII_TYPE)
  894. putc(CTLZ,fp);
  895. #endif
  896. if(remotename == NULL)
  897. remotename = "";
  898. if(total == -1){
  899. printf("%s %s: Error/abort during data transfern",command,remotename);
  900. } else if(ftp->verbose >= V_SHORT){
  901. startclk = msclock() - startclk;
  902. rate = 0;
  903. if(startclk != 0){ /* Avoid divide-by-zero */
  904. if(total < 4294967L) {
  905. rate = (total*1000)/startclk;
  906. } else { /* Avoid overflow */
  907. rate = total/(startclk/1000);
  908. }
  909. }
  910. printf("%s %s: %lu bytes in %lu sec (%lu/sec)n",
  911.  command,remotename, total,startclk/1000,rate);
  912. }
  913. /* Get the "Sent" message */
  914. getresp(ftp,200);
  915. ftp->state = prevstate;
  916. ftp->verbose = vsave;
  917. ftp->type = savmode;
  918. return total;
  919. failure:
  920. /* Error, quit */
  921. if(fp != NULL && fp != stdout)
  922. fclose(fp);
  923. fclose(ftp->data);
  924. ftp->data = NULL;
  925. ftp->state = prevstate;
  926. ftp->type = savmode;
  927. return -1;
  928. }
  929. /* Send a file. Syntax: put <local name> [<remote name>] */
  930. static int
  931. doput(argc,argv,p)
  932. int argc;
  933. char *argv[];
  934. void *p;
  935. {
  936. register struct ftpcli *ftp;
  937. char *remotename,*localname;
  938. if((ftp = (struct ftpcli *)p) == NULL){
  939. printf(Notsess);
  940. return 1;
  941. }
  942. localname = argv[1];
  943. if(argc < 3)
  944. remotename = localname;
  945. else
  946. remotename = argv[2];
  947. putsub(ftp,remotename,localname);
  948. return 0;
  949. }
  950. /* Put a collection of files */
  951. static int
  952. domput(argc,argv,p)
  953. int argc;
  954. char *argv[];
  955. void *p;
  956. {
  957. register struct ftpcli *ftp;
  958. FILE *files;
  959. int i;
  960. char *buf;
  961. if((ftp = (struct ftpcli *)p) == NULL){
  962. printf(Notsess);
  963. return 1;
  964. }
  965. if((files = tmpfile()) == NULL){
  966. printf("Can't list local filesn");
  967. return 1;
  968. }
  969. for(i=1;i<argc;i++)
  970. getdir(argv[i],0,files);
  971. rewind(files);
  972. buf = mallocw(DIRBUF);
  973. ftp->state = SENDING_STATE;
  974. while(fgets(buf,DIRBUF,files) != NULL){
  975. rip(buf);
  976. if(!ftp->update || compsub(ftp,buf,buf) != 0)
  977. putsub(ftp,buf,buf);
  978. if(ftp->abort)
  979. break; /* User abort */
  980. }
  981. fclose(files);
  982. free(buf);
  983. ftp->state = COMMAND_STATE;
  984. ftp->abort = 0;
  985. return 0;
  986. }
  987. /* Common code to put, mput.
  988.  * Returns number of bytes sent if successful
  989.  * Returns -1 on error
  990.  */
  991. static long
  992. putsub(ftp,remotename,localname)
  993. register struct ftpcli *ftp;
  994. char *remotename,*localname;
  995. {
  996. char *mode;
  997. int i,resp,d;
  998. unsigned long total;
  999. FILE *fp,*control;
  1000. struct sockaddr_in lsocket,lcsocket;
  1001. int32 startclk,rate;
  1002. int typewait = 0;
  1003. int prevstate;
  1004. control = ftp->control;
  1005. if(ftp->type == IMAGE_TYPE)
  1006. mode = READ_BINARY;
  1007. else
  1008. mode = READ_TEXT;
  1009. /* Open the file */
  1010. if((fp = fopen(localname,mode)) == NULL){
  1011. printf("Can't read %s: %sn",localname,sys_errlist[errno]);
  1012. return -1;
  1013. }
  1014. if(ftp->type == ASCII_TYPE && isbinary(fp)){
  1015. printf("Warning: type is ASCII and %s appears to be binaryn",localname);
  1016. }
  1017. /* Open the data connection */
  1018. d = socket(AF_INET,SOCK_STREAM,0);
  1019. ftp->data = fdopen(d,"w+");
  1020. listen(d,0);
  1021. prevstate = ftp->state;
  1022. ftp->state = SENDING_STATE;
  1023. /* Send TYPE message, if necessary */
  1024. if(ftp->typesent != ftp->type){
  1025. switch(ftp->type){
  1026. case ASCII_TYPE:
  1027. fprintf(control,"TYPE An");
  1028. break;
  1029. case IMAGE_TYPE:
  1030. fprintf(control,"TYPE In");
  1031. break;
  1032. case LOGICAL_TYPE:
  1033. fprintf(control,"TYPE L %dn",ftp->logbsize);
  1034. break;
  1035. }
  1036. ftp->typesent = ftp->type;
  1037. /* Get response to TYPE command */
  1038. if(!ftp->batch){
  1039. resp = getresp(ftp,200);
  1040. if(resp == -1 || resp > 299){
  1041. goto failure;
  1042. }
  1043. } else
  1044. typewait = 1;
  1045. }
  1046. /* Send the PORT message. Use the IP address
  1047.  * on the local end of our control connection.
  1048.  */
  1049. i = SOCKSIZE;
  1050. getsockname(d,(struct sockaddr *)&lsocket,&i);
  1051. i = SOCKSIZE;
  1052. getsockname(fileno(ftp->control),(struct sockaddr *)&lcsocket,&i);
  1053. lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  1054. sendport(control,&lsocket);
  1055. if(!ftp->batch){
  1056. /* Get response to PORT command */
  1057. resp = getresp(ftp,200);
  1058. if(resp == -1 || resp > 299){
  1059. goto failure;
  1060. }
  1061. }
  1062. /* Generate the command to start the transfer */
  1063. fprintf(control,"STOR %sn",remotename);
  1064. if(ftp->batch){
  1065. /* Get response to TYPE command, if sent */
  1066. if(typewait){
  1067. resp = getresp(ftp,200);
  1068. if(resp == -1 || resp > 299){
  1069. goto failure;
  1070. }
  1071. }
  1072. /* Get response to PORT command */
  1073. resp = getresp(ftp,200);
  1074. if(resp == -1 || resp > 299){
  1075. goto failure;
  1076. }
  1077. }
  1078. /* Get the intermediate "150" response */
  1079. resp = getresp(ftp,100);
  1080. if(resp == -1 || resp >= 400){
  1081. goto failure;
  1082. }
  1083. /* Wait for the data connection to open. Otherwise the first
  1084.  * block of data would go out with the SYN, and this may confuse
  1085.  * some other TCPs
  1086.  */
  1087. accept(d,NULL,(int *)NULL);
  1088. startclk = msclock();
  1089. total = sendfile(fp,ftp->data,ftp->type,ftp->verbose);
  1090. fflush(ftp->data);
  1091. shutdown(fileno(ftp->data),1); /* Send EOF (FIN) */
  1092. fclose(fp);
  1093. /* Wait for control channel ack before calculating transfer time;
  1094.  * this accounts for transmitted data in the pipe
  1095.  */
  1096. getresp(ftp,200);
  1097. fclose(ftp->data);
  1098. ftp->data = NULL;
  1099. if(total == -1){
  1100. printf("STOR %s: Error/abort during data transfern",remotename);
  1101. } else if(ftp->verbose >= V_SHORT){
  1102. startclk = msclock() - startclk;
  1103. rate = 0;
  1104. if(startclk != 0){ /* Avoid divide-by-zero */
  1105. if(total < 4294967L) {
  1106. rate = (total*1000)/startclk;
  1107. } else { /* Avoid overflow */
  1108. rate = total/(startclk/1000);
  1109. }
  1110. }
  1111. printf("STOR %s: %lu bytes in %lu sec (%lu/sec)n",
  1112.  remotename,total,startclk/1000,rate);
  1113. }
  1114. ftp->state = prevstate;
  1115. return total;
  1116. failure:
  1117. /* Error, quit */
  1118. fclose(fp);
  1119. fclose(ftp->data);
  1120. ftp->data = NULL;
  1121. ftp->state = prevstate;
  1122. return -1;
  1123. }
  1124. /* send PORT message */
  1125. static void
  1126. sendport(fp,socket)
  1127. FILE *fp;
  1128. struct sockaddr_in *socket;
  1129. {
  1130. /* Send PORT a,a,a,a,p,p message */
  1131. fprintf(fp,"PORT %u,%u,%u,%u,%u,%un",
  1132. hibyte(hiword(socket->sin_addr.s_addr)),
  1133. lobyte(hiword(socket->sin_addr.s_addr)),
  1134. hibyte(loword(socket->sin_addr.s_addr)),
  1135. lobyte(loword(socket->sin_addr.s_addr)),
  1136. hibyte(socket->sin_port),
  1137. lobyte(socket->sin_port));
  1138. }
  1139. /* Wait for, read and display response from FTP server. Return the result code.
  1140.  */
  1141. static int
  1142. getresp(ftp,mincode)
  1143. struct ftpcli *ftp;
  1144. int mincode; /* Keep reading until at least this code comes back */
  1145. {
  1146. int rval;
  1147. fflush(ftp->control);
  1148. for(;;){
  1149. /* Get line */
  1150. if(fgets(ftp->line,LINELEN,ftp->control) == NULL){
  1151. rval = -1;
  1152. break;
  1153. }
  1154. rip(ftp->line); /* Remove cr/lf */
  1155. rval = atoi(ftp->line);
  1156. if(rval >= 400 || ftp->verbose >= V_NORMAL)
  1157. printf("%sn",ftp->line); /* Display to user */
  1158. /* Messages with dashes are continued */
  1159. if(ftp->line[3] != '-' && rval >= mincode)
  1160. break;
  1161. }
  1162. return rval;
  1163. }
  1164. /* Issue a prompt and read a line from the user */
  1165. static int
  1166. getline(sp,prompt,buf,n)
  1167. struct session *sp;
  1168. char *prompt;
  1169. char *buf;
  1170. int n;
  1171. {
  1172. printf(prompt);
  1173. fflush(stdout);
  1174. fgets(buf,n,stdin);
  1175. return strlen(buf);
  1176. }
  1177. static int
  1178. keychar(c)
  1179. int c;
  1180. {
  1181. struct ftpcli *ftp;
  1182. if(c != CTLC)
  1183. return 1; /* Ignore all but ^C */
  1184. fprintf(Current->output,"^Cn");
  1185. ftp = Current->cb.ftp;
  1186. switch(ftp->state){
  1187. case COMMAND_STATE:
  1188. alert(Current->proc,EABORT);
  1189. break;
  1190. case SENDING_STATE:
  1191. /* Send a premature EOF.
  1192.  * Unfortunately we can't just reset the connection
  1193.  * since the remote side might end up waiting forever
  1194.  * for us to send something.
  1195.  */
  1196. shutdown(fileno(ftp->data),1); /* Note fall-thru */
  1197. ftp->abort = 1;
  1198. break;
  1199. case RECEIVING_STATE:
  1200. /* Just blow away the receive socket */
  1201. shutdown(fileno(ftp->data),2); /* Note fall-thru */
  1202. ftp->abort = 1;
  1203. break;
  1204. }
  1205. return 0;
  1206. }