home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR3 / KA9Q212.ZIP / FTPSERV.C < prev    next >
C/C++ Source or Header  |  1993-03-07  |  16KB  |  655 lines

  1. /* Internet FTP Server
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include <stdio.h>
  5. #include <ctype.h>
  6. #include <time.h>
  7. #ifdef    __TURBOC__
  8. #include <io.h>
  9. #include <dir.h>
  10. #endif
  11. #include "global.h"
  12. #include "mbuf.h"
  13. #include "proc.h"
  14. #include "socket.h"
  15. #include "dirutil.h"
  16. #include "commands.h"
  17. #include "files.h"
  18. #include "ftp.h"
  19. #include "ftpserv.h"
  20.  
  21. static void ftpserv __ARGS((int s,void *unused,void *p));
  22. static int pport __ARGS((struct sockaddr_in *sock,char *arg));
  23. static void ftplogin __ARGS((struct ftpserv *ftp,char *pass));
  24. static int sendit __ARGS((struct ftpserv *ftp,char *command,char *file));
  25. static int recvit __ARGS((struct ftpserv *ftp,char *command,char *file));
  26.  
  27. /* Command table */
  28. static char *commands[] = {
  29.     "user",
  30.     "acct",
  31.     "pass",
  32.     "type",
  33.     "list",
  34.     "cwd",
  35.     "dele",
  36.     "name",
  37.     "quit",
  38.     "retr",
  39.     "stor",
  40.     "port",
  41.     "nlst",
  42.     "pwd",
  43.     "xpwd",            /* For compatibility with 4.2BSD */
  44.     "mkd ",
  45.     "xmkd",            /* For compatibility with 4.2BSD */
  46.     "xrmd",            /* For compatibility with 4.2BSD */
  47.     "rmd ",
  48.     "stru",
  49.     "mode",
  50.     NULLCHAR
  51. };
  52.  
  53. /* Response messages */
  54. static char banner[] = "220 %s FTP version %s ready at %s\n";
  55. static char badcmd[] = "500 Unknown command\n";
  56. static char binwarn[] = "100 Warning: type is ASCII and %s appears to be binary\n";
  57. static char unsupp[] = "500 Unsupported command or option\n";
  58. static char givepass[] = "331 Enter PASS command\n";
  59. static char giveanonpass[]="331 Enter Email address as password\n";
  60. static char logged[] = "230 Logged in\n";
  61. static char typeok[] = "200 Type %s OK\n";
  62. static char only8[] = "501 Only logical bytesize 8 supported\n";
  63. static char deleok[] = "250 File deleted\n";
  64. static char mkdok[] = "200 MKD ok\n";
  65. static char delefail[] = "550 Delete failed: %s\n";
  66. static char pwdmsg[] = "257 \"%s\" is current directory\n";
  67. static char badtype[] = "501 Unknown type \"%s\"\n";
  68. static char badport[] = "501 Bad port syntax\n";
  69. static char unimp[] = "502 Command not yet implemented\n";
  70. static char bye[] = "221 Goodbye!\n";
  71. static char nodir[] = "553 Can't read directory \"%s\": %s\n";
  72. static char cantopen[] = "550 Can't read file \"%s\": %s\n";
  73. static char sending[] = "150 Opening data connection for %s %s\n";
  74. static char cantmake[] = "553 Can't create \"%s\": %s\n";
  75. static char writerr[] = "552 Write error: %s\n";
  76. static char portok[] = "200 Port command okay\n";
  77. static char rxok[] = "226 File received OK\n";
  78. static char txok[] = "226 File sent OK\n";
  79. static char noperm[] = "550 Permission denied\n";
  80. static char noconn[] = "425 Data connection reset\n";
  81. static char lowmem[] = "421 System overloaded, try again later\n";
  82. static char notlog[] = "530 Please log in with USER and PASS\n";
  83. static char userfirst[] = "503 Login with USER first.\n";
  84. static char okay[] = "200 Ok\n";
  85.  
  86. static int Sftp = -1;    /* Prototype socket for service */
  87.  
  88. /* Start up FTP service */
  89. int
  90. ftpstart(argc,argv,p)
  91. int argc;
  92. char *argv[];
  93. void *p;
  94. {
  95.     struct sockaddr_in lsocket;
  96.     int s;
  97.  
  98.     if(Sftp != -1){
  99.         /* Already running! */
  100.         return 0;
  101.     }
  102.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  103.     chname(Curproc,"FTP listener");
  104.  
  105.     lsocket.sin_family = AF_INET;
  106.     lsocket.sin_addr.s_addr = INADDR_ANY;
  107.     if(argc < 2)
  108.         lsocket.sin_port = IPPORT_FTP;
  109.     else
  110.         lsocket.sin_port = atoi(argv[1]);
  111.  
  112.     Sftp = socket(AF_INET,SOCK_STREAM,0);
  113.     bind(Sftp,(char *)&lsocket,sizeof(lsocket));
  114.     listen(Sftp,1);
  115.     for(;;){
  116.         if((s = accept(Sftp,NULLCHAR,(int *)NULL)) == -1)
  117.             break;    /* Service is shutting down */
  118.  
  119.         if(availmem() < Memthresh){
  120.             usprintf(s,lowmem);
  121.             shutdown(s,1);
  122.         } else {
  123.             /* Spawn a server */
  124.             newproc("ftpserv",2048,ftpserv,s,NULL,NULL,0);
  125.         }
  126.     }
  127.     return 0;
  128. }
  129. static void
  130. ftpserv(s,unused,p)
  131. int s;    /* Socket with user connection */
  132. void *unused;
  133. void *p;
  134. {
  135.     struct ftpserv ftp;
  136.     char **cmdp,buf[512],*arg,*cp,*cp1,*file,*mode;
  137.     long t;
  138.     int cnt,i;
  139.     struct sockaddr_in socket;
  140.  
  141.     sockmode(s,SOCK_ASCII);
  142.     memset((char *)&ftp,0,sizeof(ftp));    /* Start with clear slate */
  143.     ftp.data = -1;
  144.  
  145.     sockowner(s,Curproc);        /* We own it now */
  146.     ftp.control = s;
  147.     /* Set default data port */
  148.     i = SOCKSIZE;
  149.     getpeername(s,(char *)&socket,&i);
  150.     socket.sin_port = IPPORT_FTPD;
  151.     ASSIGN(ftp.port,socket);
  152.  
  153.     log(s,"open FTP");
  154.     time(&t);
  155.     cp = ctime(&t);
  156.     if((cp1 = strchr(cp,'\n')) != NULLCHAR)
  157.         *cp1 = '\0';
  158.     usprintf(s,banner,Hostname,Version,cp);
  159. loop:    if((cnt = recvline(s,buf,sizeof(buf))) == -1){
  160.         /* He closed on us */
  161.         goto finish;
  162.     }
  163.     if(cnt == 0){
  164.         /* Can't be a legal FTP command */
  165.         usprintf(ftp.control,badcmd);
  166.         goto loop;
  167.     }    
  168.     rip(buf);
  169. #ifdef    UNIX
  170.     /* Translate first word to lower case */
  171.     for(cp = buf;*cp != ' ' && *cp != '\0';cp++)
  172.         *cp = tolower(*cp);
  173. #else
  174.     /* Translate entire buffer to lower case */
  175.     for(cp = buf;*cp != '\0';cp++)
  176.         *cp = tolower(*cp);
  177. #endif
  178.     /* Find command in table; if not present, return syntax error */
  179.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  180.         if(strncmp(*cmdp,buf,strlen(*cmdp)) == 0)
  181.             break;
  182.     if(*cmdp == NULLCHAR){
  183.         usprintf(ftp.control,badcmd);
  184.         goto loop;
  185.     }
  186.     /* Allow only USER, PASS and QUIT before logging in */
  187.     if(ftp.cd == NULLCHAR || ftp.path == NULLCHAR){
  188.         switch(cmdp-commands){
  189.         case USER_CMD:
  190.         case PASS_CMD:
  191.         case QUIT_CMD:
  192.             break;
  193.         default:
  194.             usprintf(ftp.control,notlog);
  195.             goto loop;
  196.         }
  197.     }
  198.     arg = &buf[strlen(*cmdp)];
  199.     while(*arg == ' ')
  200.         arg++;
  201.  
  202.     /* Execute specific command */
  203.     switch(cmdp-commands){
  204.     case USER_CMD:
  205.         free(ftp.username);
  206.         ftp.username = strdup(arg);
  207.         if ((cp=userlookup(ftp.username,&cp1,NULLCHARP,NULLINT,NULL))!=NULLCHAR)
  208.         {
  209.             if (strcmp(cp1,"@")==0)
  210.                 usprintf(ftp.control,giveanonpass);
  211.             else
  212.                 usprintf(ftp.control,givepass);
  213.             free(cp);
  214.         }
  215.         else
  216.             usprintf(ftp.control,givepass);
  217.         break;
  218.     case TYPE_CMD:
  219.         switch(arg[0]){
  220.         case 'A':
  221.         case 'a':    /* Ascii */
  222.             ftp.type = ASCII_TYPE;
  223.             usprintf(ftp.control,typeok,arg);
  224.             break;
  225.         case 'l':
  226.         case 'L':
  227.             while(*arg != ' ' && *arg != '\0')
  228.                 arg++;
  229.             if(*arg == '\0' || *++arg != '8'){
  230.                 usprintf(ftp.control,only8);
  231.                 break;
  232.             }
  233.             ftp.type = LOGICAL_TYPE;
  234.             ftp.logbsize = 8;
  235.             usprintf(ftp.control,typeok,arg);
  236.             break;
  237.         case 'B':
  238.         case 'b':    /* Binary */
  239.         case 'I':
  240.         case 'i':    /* Image */
  241.             ftp.type = IMAGE_TYPE;
  242.             usprintf(ftp.control,typeok,arg);
  243.             break;
  244.         default:    /* Invalid */
  245.             usprintf(ftp.control,badtype,arg);
  246.             break;
  247.         }
  248.         break;
  249.     case QUIT_CMD:
  250.         usprintf(ftp.control,bye);
  251.         goto finish;
  252.     case RETR_CMD:
  253.         file = pathname(ftp.cd,arg);
  254.         switch(ftp.type){
  255.         case IMAGE_TYPE:
  256.         case LOGICAL_TYPE:
  257.             mode = READ_BINARY;
  258.             break;
  259.         case ASCII_TYPE:
  260.             mode = READ_TEXT;
  261.             break;
  262.         }
  263.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  264.              usprintf(ftp.control,noperm);
  265.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  266.             usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
  267.         } else {
  268.             log(ftp.control,"RETR %s",file);
  269.             if(ftp.type == ASCII_TYPE && isbinary(ftp.fp)){
  270.                 usprintf(ftp.control,binwarn,file);
  271.             }
  272.             sendit(&ftp,"RETR",file);
  273.         }
  274.         free(file);
  275.         break;
  276.     case STOR_CMD:
  277.         file = pathname(ftp.cd,arg);
  278.         switch(ftp.type){
  279.         case IMAGE_TYPE:
  280.         case LOGICAL_TYPE:
  281.             mode = WRITE_BINARY;
  282.             break;
  283.         case ASCII_TYPE:
  284.             mode = WRITE_TEXT;
  285.             break;
  286.         }
  287.         if(!permcheck(ftp.path,ftp.perms,STOR_CMD,file)){
  288.              usprintf(ftp.control,noperm);
  289.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  290.             usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  291.         } else {
  292.             log(ftp.control,"STOR %s",file);
  293.             recvit(&ftp,"STOR",file);
  294.         }
  295.         free(file);
  296.         break;
  297.     case PORT_CMD:
  298.         if(pport(&ftp.port,arg) == -1){
  299.             usprintf(ftp.control,badport);
  300.         } else {
  301.             usprintf(ftp.control,portok);
  302.         }
  303.         break;
  304. #ifndef CPM
  305.     case LIST_CMD:
  306.         file = pathname(ftp.cd,arg);
  307.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  308.              usprintf(ftp.control,noperm);
  309.         } else if((ftp.fp = dir(file,1)) == NULLFILE){
  310.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  311.         } else {
  312.             sendit(&ftp,"LIST",file);
  313.         }
  314.         free(file);
  315.         break;
  316.     case NLST_CMD:
  317.         file = pathname(ftp.cd,arg);
  318.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  319.              usprintf(ftp.control,noperm);
  320.         } else if((ftp.fp = dir(file,0)) == NULLFILE){
  321.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  322.         } else {
  323.             sendit(&ftp,"NLST",file);
  324.         }
  325.         free(file);
  326.         break;
  327.     case CWD_CMD:
  328.         file = pathname(ftp.cd,arg);
  329.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  330.              usprintf(ftp.control,noperm);
  331.             free(file);
  332. #ifdef    MSDOS
  333.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  334.         } else if(strcmp(file,"/") == 0 || access(file,0) == 0){
  335. #else
  336.         } else if(access(file,0) == 0){    /* See if it exists */
  337. #endif
  338.             /* Succeeded, record in control block */
  339.             free(ftp.cd);
  340.             ftp.cd = file;
  341.             usprintf(ftp.control,pwdmsg,file);
  342.         } else {
  343.             /* Failed, don't change anything */
  344.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  345.             free(file);
  346.         }
  347.         break;
  348.     case XPWD_CMD:
  349.     case PWD_CMD:
  350.         usprintf(ftp.control,pwdmsg,ftp.cd);
  351.         break;
  352. #else
  353.     case LIST_CMD:
  354.     case NLST_CMD:
  355.     case CWD_CMD:
  356.     case XPWD_CMD:
  357.     case PWD_CMD:
  358. #endif
  359.     case ACCT_CMD:        
  360.         usprintf(ftp.control,unimp);
  361.         break;
  362.     case DELE_CMD:
  363.         file = pathname(ftp.cd,arg);
  364.         if(!permcheck(ftp.path,ftp.perms,DELE_CMD,file)){
  365.              usprintf(ftp.control,noperm);
  366.         } else if(unlink(file) == 0){
  367.             log(ftp.control,"DELE %s",file);
  368.             usprintf(ftp.control,deleok);
  369.         } else {
  370.             usprintf(ftp.control,delefail,sys_errlist[errno]);
  371.         }
  372.         free(file);
  373.         break;
  374.     case PASS_CMD:
  375.         if(ftp.username == NULLCHAR)
  376.             usprintf(ftp.control,userfirst);
  377.         else
  378.             ftplogin(&ftp,arg);            
  379.         break;
  380. #ifndef    CPM
  381.     case XMKD_CMD:
  382.     case MKD_CMD:
  383.         file = pathname(ftp.cd,arg);
  384.         if(!permcheck(ftp.path,ftp.perms,MKD_CMD,file)){
  385.             usprintf(ftp.control,noperm);
  386. #ifdef    UNIX
  387.         } else if(mkdir(file,0777) == 0){
  388. #else
  389.         } else if(mkdir(file) == 0){
  390. #endif
  391.             log(ftp.control,"MKD %s",file);
  392.             usprintf(ftp.control,mkdok);
  393.         } else {
  394.             usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  395.         }
  396.         free(file);
  397.         break;
  398.     case XRMD_CMD:
  399.     case RMD_CMD:
  400.         file = pathname(ftp.cd,arg);
  401.         if(!permcheck(ftp.path,ftp.perms,RMD_CMD,file)){
  402.              usprintf(ftp.control,noperm);
  403.         } else if(rmdir(file) == 0){
  404.             log(ftp.control,"RMD %s",file);
  405.             usprintf(ftp.control,deleok);
  406.         } else {
  407.             usprintf(ftp.control,delefail,sys_errlist[errno]);
  408.         }
  409.         free(file);
  410.         break;
  411.     case STRU_CMD:
  412.         if(tolower(arg[0]) != 'f')
  413.             usprintf(ftp.control,unsupp);
  414.         else
  415.             usprintf(ftp.control,okay);
  416.         break;
  417.     case MODE_CMD:
  418.         if(tolower(arg[0]) != 's')
  419.             usprintf(ftp.control,unsupp);
  420.         else
  421.             usprintf(ftp.control,okay);
  422.         break;
  423.     }
  424. #endif
  425.     goto loop;
  426. finish:
  427.     log(ftp.control,"close FTP");
  428.     /* Clean up */
  429.     close_s(ftp.control);
  430.     if(ftp.data != -1)
  431.         close_s(ftp.data);
  432.     if(ftp.fp != NULLFILE)
  433.         fclose(ftp.fp);
  434.     free(ftp.username);
  435.     free(ftp.path);
  436.     free(ftp.cd);
  437. }
  438.  
  439. /* Shut down FTP server */
  440. int
  441. ftp0(argc,argv,p)
  442. int argc;
  443. char *argv[];
  444. void *p;
  445. {
  446.     close_s(Sftp);
  447.     Sftp = -1;
  448.     return 0;
  449. }
  450. static
  451. int
  452. pport(sock,arg)
  453. struct sockaddr_in *sock;
  454. char *arg;
  455. {
  456.     int32 n;
  457.     int i;
  458.  
  459.     n = 0;
  460.     for(i=0;i<4;i++){
  461.         n = atoi(arg) + (n << 8);
  462.         if((arg = strchr(arg,',')) == NULLCHAR)
  463.             return -1;
  464.         arg++;
  465.     }
  466.     sock->sin_addr.s_addr = n;
  467.     n = atoi(arg);
  468.     if((arg = strchr(arg,',')) == NULLCHAR)
  469.         return -1;
  470.     arg++;
  471.     n = atoi(arg) + (n << 8);
  472.     sock->sin_port = n;
  473.     return 0;
  474. }
  475.  
  476. /* Attempt to log in the user whose name is in ftp->username and password
  477.  * in pass
  478.  */
  479. static void
  480. ftplogin(ftp,pass)
  481. struct ftpserv *ftp;
  482. char *pass;
  483. {
  484.     char *path;
  485.     int anony = 0;
  486.  
  487.     path = mallocw(200);
  488.     if((ftp->perms = userlogin(ftp->username,pass,&path,200,&anony))
  489.        == -1){
  490.         usprintf(ftp->control,noperm);
  491.         free(path);
  492.         return;
  493.     }
  494.     /* Set up current directory and path prefix */
  495. #if    defined(AMIGAGONE)
  496.     ftp->cd = pathname("", path);
  497.     ftp->path = strdup(ftp->cd);
  498.     free(path);
  499. #else
  500.     ftp->cd = path;
  501.     ftp->path = strdup(path);
  502. #endif
  503.  
  504.     usprintf(ftp->control,logged);
  505.     if(!anony)
  506.         log(ftp->control,"%s logged in",ftp->username);
  507.     else
  508.         log(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  509. }
  510.  
  511. #ifdef    MSDOS
  512. /* Illegal characters in a DOS filename */
  513. static char badchars[] = "\"[]:|<>+=;,";
  514. #endif
  515.  
  516. /* Return 1 if the file operation is allowed, 0 otherwise */
  517. int
  518. permcheck(path,perms,op,file)
  519. char *path;
  520. int perms;
  521. int op;
  522. char *file;
  523. {
  524. #ifdef    MSDOS
  525.     char *cp;
  526. #endif
  527.  
  528.     if(file == NULLCHAR || path == NULLCHAR)
  529.         return 0;    /* Probably hasn't logged in yet */
  530. #ifdef    MSDOS
  531.     /* Check for characters illegal in MS-DOS file names */
  532.     for(cp = badchars;*cp != '\0';cp++){
  533.         if(strchr(file,*cp) != NULLCHAR)
  534.             return 0;    
  535.     }
  536. #endif
  537. #ifndef MAC
  538.     /* The target file must be under the user's allowed search path */
  539.     if(strncmp(file,path,strlen(path)) != 0)
  540.         return 0;
  541. #endif
  542.  
  543.     switch(op){
  544.     case RETR_CMD:
  545.         /* User must have permission to read files */
  546.         if(perms & FTP_READ)
  547.             return 1;
  548.         return 0;
  549.     case DELE_CMD:
  550.     case RMD_CMD:
  551.         /* User must have permission to (over)write files */
  552.         if(perms & FTP_WRITE)
  553.             return 1;
  554.         return 0;
  555.     case STOR_CMD:
  556.     case MKD_CMD:
  557.         /* User must have permission to (over)write files, or permission
  558.          * to create them if the file doesn't already exist
  559.          */
  560.         if(perms & FTP_WRITE)
  561.             return 1;
  562.         if(access(file,2) == -1 && (perms & FTP_CREATE))
  563.             return 1;
  564.         return 0;
  565.     }
  566.     return 0;    /* "can't happen" -- keep lint happy */
  567. }
  568. static int
  569. sendit(ftp,command,file)
  570. struct ftpserv *ftp;
  571. char *command;
  572. char *file;
  573. {
  574.     long total;
  575.     struct sockaddr_in dport;
  576.  
  577.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  578.     dport.sin_family = AF_INET;
  579.     dport.sin_addr.s_addr = INADDR_ANY;
  580.     dport.sin_port = IPPORT_FTPD;
  581.     bind(ftp->data,(char *)&dport,SOCKSIZE);
  582.     usprintf(ftp->control,sending,command,file);
  583.     if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
  584.         fclose(ftp->fp);
  585.         ftp->fp = NULLFILE;
  586.         close_s(ftp->data);
  587.         ftp->data = -1;
  588.         usprintf(ftp->control,noconn);
  589.         return -1;
  590.     }
  591.     /* Do the actual transfer */
  592.     total = sendfile(ftp->fp,ftp->data,ftp->type,0);
  593.  
  594.     if(total == -1){
  595.         /* An error occurred on the data connection */
  596.         usprintf(ftp->control,noconn);
  597.         shutdown(ftp->data,2);    /* Blow away data connection */
  598.     } else {
  599.         usprintf(ftp->control,txok);
  600.     }
  601.     fclose(ftp->fp);
  602.     ftp->fp = NULLFILE;
  603.     close_s(ftp->data);
  604.     ftp->data = -1;
  605.     if(total == -1)
  606.         return -1;
  607.     else
  608.         return 0;
  609. }
  610. static int
  611. recvit(ftp,command,file)
  612. struct ftpserv *ftp;
  613. char *command;
  614. char *file;
  615. {
  616.     struct sockaddr_in dport;
  617.     long total;
  618.  
  619.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  620.     dport.sin_family = AF_INET;
  621.     dport.sin_addr.s_addr = INADDR_ANY;
  622.     dport.sin_port = IPPORT_FTPD;
  623.     bind(ftp->data,(char *)&dport,SOCKSIZE);
  624.     usprintf(ftp->control,sending,command,file);
  625.     if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
  626.         fclose(ftp->fp);
  627.         ftp->fp = NULLFILE;
  628.         close_s(ftp->data);
  629.         ftp->data = -1;
  630.         usprintf(ftp->control,noconn);
  631.         return -1;
  632.     }
  633.     total = recvfile(ftp->fp,ftp->data,ftp->type,0);
  634.  
  635. #ifdef    CPM
  636.     if(ftp->type == ASCII_TYPE)
  637.         putc(CTLZ,ftp->fp);
  638. #endif
  639.     if(total == -1) {
  640.         /* An error occurred while writing the file */
  641.         usprintf(ftp->control,writerr,sys_errlist[errno]);
  642.         shutdown(ftp->data,2);    /* Blow it away */
  643.     } else {
  644.         usprintf(ftp->control,rxok);
  645.         close_s(ftp->data);
  646.     }
  647.     ftp->data = -1;
  648.     fclose(ftp->fp);
  649.     ftp->fp = NULLFILE;
  650.     if(total == -1)
  651.         return -1;
  652.     else
  653.         return 0;
  654. }
  655.