home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / telecomm / nhclb120 / ftpserv.c < prev    next >
C/C++ Source or Header  |  1993-09-26  |  19KB  |  778 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define    LINELEN        128    /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include <ctype.h>
  7. #include "global.h"
  8. #include "config.h"
  9. #include "mbuf.h"
  10. #include "netuser.h"
  11. #include "timer.h"
  12. #include "tcp.h"
  13. #include "ftp.h"
  14. #include "telnet.h"
  15. #include "iface.h"
  16. #include "ax25.h"
  17. #include "lapb.h"
  18. #include "finger.h"
  19. #include "session.h"
  20. #include "nr4.h"
  21. #ifdef UNIX
  22. #include "unix.h"
  23. #undef    toupper
  24. #undef    tolower
  25. #include <string.h>
  26. #include <sys/types.h>
  27. time_t  time();
  28. #endif /* UNIX */
  29. static void ftpscs();
  30. static void ftpscr();
  31. static void ftpcommand();
  32.  
  33. static int pport();
  34. static int ftplogin();
  35.  
  36. /* Command table */
  37. static char *commands[] = {
  38.     "user",
  39. #define    USER_CMD    0
  40.     "acct",
  41. #define    ACCT_CMD    1
  42.     "pass",
  43. #define    PASS_CMD    2
  44.     "type",
  45. #define    TYPE_CMD    3
  46.     "list",
  47. #define    LIST_CMD    4
  48.     "cwd",
  49. #define    CWD_CMD        5
  50.     "dele",
  51. #define    DELE_CMD    6
  52.     "name",
  53. #define    NAME_CMD    7
  54.     "quit",
  55. #define    QUIT_CMD    8
  56.     "retr",
  57. #define    RETR_CMD    9
  58.     "stor",
  59. #define    STOR_CMD    10
  60.     "port",
  61. #define    PORT_CMD    11
  62.     "nlst",
  63. #define    NLST_CMD    12
  64.     "pwd",
  65. #define    PWD_CMD        13
  66.     "xpwd",            /* For compatibility with 4.2BSD */
  67. #define    XPWD_CMD    14
  68.     "mkd ",
  69. #define    MKD_CMD        15
  70.     "xmkd",            /* For compatibility with 4.2BSD */
  71. #define    XMKD_CMD    16
  72.     "xrmd",            /* For compatibility with 4.2BSD */
  73. #define    XRMD_CMD    17
  74.     "rmd ",
  75. #define    RMD_CMD        18
  76.     "stru",
  77. #define    STRU_CMD    19
  78.     "mode",
  79. #define    MODE_CMD    20
  80.     "help",
  81. #define HELP_CMD        21
  82.     NULLCHAR
  83. };
  84.  
  85. /* Response messages */
  86. static char helpmsg[]=  "214-Commands:\r\n\
  87. 214-\r\n\
  88. 214-USER ACCT PASS TYPE LIST CWD  DELE NAME QUIT RETR STOR PORT\r\n\
  89. 214-NLST PWD  XPWD MKD  XMKD XRMD RMD  STRU MODE HELP\r\n\
  90. 214 End\r\n";
  91. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  92. static char badcmd[] = "500 Unknown command\r\n";
  93. static char unsupp[] = "500 Unsupported command or option\r\n";
  94. static char givepass[] = "331 Enter PASS command\r\n";
  95. static char logged[] = "230 Logged in\r\n";
  96. static char typeok[] = "200 Type OK\r\n";
  97. static char only8[] = "501 Only logical bytesize 8 supported\r\n";
  98. static char deleok[] = "250 File deleted\r\n";
  99. static char mkdok[] = "200 MKD ok\r\n";
  100. static char delefail[] = "550 Delete failed\r\n";
  101. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  102. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  103. static char badport[] = "501 Bad port syntax\r\n";
  104. static char unimp[] = "502 Command not yet implemented\r\n";
  105. static char bye[] = "221 Goodbye!\r\n";
  106. static char nodir[] = "553 Can't read directory \"%s\"\r\n";
  107. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  108. static char sending[] = "150 Opening data connection for %s %s\r\n";
  109. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  110. static char portok[] = "200 Port command okay\r\n";
  111. static char rxok[] = "226 File received OK\r\n";
  112. static char txok[] = "226 File sent OK\r\n";
  113. static char noperm[] = "550 Permission denied\r\n";
  114. static char noconn[] = "425 Data connection reset\r\n";
  115. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  116. static char okay[] = "200 Ok\r\n";
  117.  
  118. static struct tcb *ftp_tcb;
  119.  
  120. /* Start up FTP service */
  121. ftp1(argc,argv)
  122. int argc;
  123. char *argv[];
  124. {
  125.     struct socket lsocket;
  126.     void ftpscr(),ftpscs();
  127.  
  128.     lsocket.address = ip_addr;
  129.     if(argc < 2)
  130.         lsocket.port = FTP_PORT;
  131.     else
  132.         lsocket.port = atoi(argv[1]);
  133.  
  134.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,(char *)NULL);
  135. }
  136. /* Shut down FTP server */
  137. ftp0()
  138. {
  139.     if(ftp_tcb != NULLTCB)
  140.         close_tcp(ftp_tcb);
  141. }
  142. /* FTP Server Control channel State change upcall handler */
  143. /*ARGSUSED*/
  144. static
  145. void
  146. ftpscs(tcb,old,new)
  147. struct tcb *tcb;
  148. char old,new;
  149. {
  150.     extern char hostname[],version[];
  151.     struct ftp *ftp,*ftp_create();
  152.     void ftp_delete();
  153.     char *inet_ntoa();
  154.     long t;
  155.     char *cp,*cp1;
  156.  
  157.     switch(new){
  158. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  159.  * leaving it unset waits for the three-way handshake to complete before
  160.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  161.  * so its use is not (yet) recommended.
  162. */
  163. #ifdef    QUICKSTART
  164.     case SYN_RECEIVED:
  165. #else
  166.     case ESTABLISHED:
  167. #endif
  168.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  169.             /* No space, kill connection */
  170.             close_tcp(tcb);
  171.             return;
  172.         }
  173.         ftp->control = tcb;        /* Downward link */
  174.         tcb->user = (char *)ftp;    /* Upward link */
  175.  
  176.         /* Set default data port */
  177.         ftp->port.address = tcb->conn.remote.address;
  178.         ftp->port.port = FTPD_PORT;
  179.  
  180.         /* Note current directory */
  181.         log(tcb,"open FTP");
  182.         time(&t);
  183.         cp = ctime(&t);
  184.         if((cp1 = index(cp,'\n')) != NULLCHAR)
  185.             *cp1 = '\0';
  186.         tprintf(ftp->control,banner,hostname,version,cp);
  187.         break;        
  188.     case CLOSE_WAIT:
  189.         close_tcp(tcb);
  190.         break;
  191.     case CLOSED:
  192.         log(tcb,"close FTP");
  193.         if((ftp = (struct ftp *)tcb->user) != NULLFTP ||
  194.            !tcpval(ftp->control)) {   /* control session existing ? */
  195.             if (ftp != NULLFTP)
  196.                 ftp_delete(ftp);
  197.         }
  198.         /* Check if server is being shut down */
  199.         if(tcb == ftp_tcb)
  200.             ftp_tcb = NULLTCB;
  201.         del_tcp(tcb);
  202.         break;
  203.     }
  204. }
  205.  
  206. /* FTP Server Control channel Receiver upcall handler */
  207. /*ARGSUSED*/
  208. static
  209. void
  210. ftpscr(tcb,cnt)
  211. struct tcb *tcb;
  212. int16 cnt;
  213. {
  214.     register struct ftp *ftp;
  215.     char c;
  216.     struct mbuf *bp;
  217.     void ftpcommand();
  218.  
  219.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  220.         /* Unknown connection, just kill it */
  221.         close_tcp(tcb);
  222.         return;
  223.     }
  224.     switch(ftp->state){
  225.     case COMMAND_STATE:
  226.         /* Assemble an input line in the session buffer. Return if incomplete */
  227.         recv_tcp(tcb,&bp,0);
  228.         while(pullup(&bp,&c,1) == 1){
  229.             switch(c){
  230.             case '\r':    /* Strip cr's */
  231.                 continue;
  232.             case '\n':    /* Complete line; process it */
  233.                 ftp->buf[ftp->cnt] = '\0';
  234.                 ftpcommand(ftp);
  235.                 ftp->cnt = 0;
  236.                 break;
  237.             default:    /* Assemble line */
  238.                 if(ftp->cnt != LINELEN-1)
  239.                     ftp->buf[ftp->cnt++] = c;
  240.                 break;
  241.             }
  242.         }
  243.         /* else no linefeed present yet to terminate command */
  244.         break;
  245.     case SENDING_STATE:
  246.     case RECEIVING_STATE:
  247.         /* Leave commands pending on receive queue until
  248.          * present command is done
  249.          */
  250.         break;
  251.     }
  252. }
  253.  
  254. /* FTP server data channel connection state change upcall handler */
  255. void
  256. ftpsds(tcb,old,new)
  257. struct tcb *tcb;
  258. char old,new;
  259. {
  260.     register struct ftp *ftp;
  261.  
  262.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  263.         /* Unknown connection. Kill it */
  264.         del_tcp(tcb);
  265.     } else if((old == FINWAIT1 || old == CLOSING) && ftp->state == SENDING_STATE){
  266.         /* We've received an ack of our FIN while sending; we're done */
  267.         ftp->state = COMMAND_STATE;
  268.         tprintf(ftp->control,txok);
  269.         /* Kick command parser if something is waiting */
  270.         if(ftp->control->rcvcnt != 0)
  271.             ftpscr(ftp->control,ftp->control->rcvcnt);
  272.     } else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){
  273.         /* FIN received on incoming file */
  274. #ifdef    CPM
  275.         if(ftp->type == ASCII_TYPE)
  276.             putc(CTLZ,ftp->fp);
  277. #endif
  278.         close_tcp(tcb);
  279.         if(ftp->fp != stdout)
  280.             fclose(ftp->fp);
  281.         ftp->fp = NULLFILE;
  282.         ftp->state = COMMAND_STATE;
  283.         tprintf(ftp->control,rxok);
  284.         /* Kick command parser if something is waiting */
  285.         if(ftp->control->rcvcnt != 0)
  286.             ftpscr(ftp->control,ftp->control->rcvcnt);
  287.     } else if(new == CLOSED){
  288.         if(tcb->reason != NORMAL){
  289.             /* Data connection was reset, complain about it */
  290.             tprintf(ftp->control,noconn);
  291.             /* And clean up */
  292.             if(ftp->fp != NULLFILE && ftp->fp != stdout)
  293.                 fclose(ftp->fp);
  294.             ftp->fp = NULLFILE;
  295.             ftp->state = COMMAND_STATE;
  296.             /* Kick command parser if something is waiting */
  297.             if(ftp->control->rcvcnt != 0)
  298.                 ftpscr(ftp->control,ftp->control->rcvcnt);
  299.         }
  300.         /* Clear only if another transfer hasn't already started */
  301.         if(ftp->data == tcb)
  302.             ftp->data = NULLTCB;
  303.         del_tcp(tcb);
  304.     }
  305. }
  306.  
  307. /* Parse and execute ftp commands */
  308. static
  309. void
  310. ftpcommand(ftp)
  311. register struct ftp *ftp;
  312. {
  313.     void ftpdr(),ftpdt(),ftpsds();
  314.     char *cmd,*arg,*cp,**cmdp,*file;
  315.     char *pathname();
  316.     char *mode;
  317.     struct socket dport;
  318.     int i;
  319.  
  320. #ifndef    CPM
  321.     FILE *dir();
  322. #endif
  323.  
  324.     cmd = ftp->buf;
  325.     if(ftp->cnt == 0){
  326.         /* Can't be a legal FTP command */
  327.         tprintf(ftp->control,badcmd);
  328.         return;
  329.     }    
  330.     cmd = ftp->buf;
  331.  
  332. #ifdef    UNIX
  333.     /* Translate first word to lower case */
  334.     for(cp = cmd;*cp != ' ' && *cp != '\0';cp++)
  335.         *cp = tolower(*cp);
  336. #else
  337.     /* Translate entire buffer to lower case */
  338.     for(cp = cmd;*cp != '\0';cp++)
  339.         *cp = tolower(*cp);
  340. #endif
  341.     /* Find command in table; if not present, return syntax error */
  342.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  343.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  344.             break;
  345.     if(*cmdp == NULLCHAR){
  346.         tprintf(ftp->control,badcmd);
  347.         return;
  348.     }
  349.     /* Allow only USER, PASS and QUIT before logging in */
  350.     if(ftp->cd == NULLCHAR || ftp->path[0] == NULLCHAR){
  351.         switch(cmdp-commands){
  352.         case USER_CMD:
  353.         case PASS_CMD:
  354.         case QUIT_CMD:
  355.             break;
  356.         default:
  357.             tprintf(ftp->control,notlog);
  358.             return;
  359.         }
  360.     }
  361.     arg = &cmd[strlen(*cmdp)];
  362.     while(*arg == ' ')
  363.         arg++;
  364.  
  365.     /* Execute specific command */
  366.     switch(cmdp-commands){
  367.     case USER_CMD:
  368.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  369.             close_tcp(ftp->control);
  370.             break;
  371.         }
  372.         strcpy(ftp->username,arg);
  373.         tprintf(ftp->control,givepass);
  374.          /* erase all user info from possible previous session */
  375.          for(i = 0; i < MAXPATH; i++){
  376.              if(ftp->path[i] != NULLCHAR){
  377.                  free(ftp->path[i]);
  378.                  ftp->path[i] = NULLCHAR;
  379.              }
  380.              ftp->perms[i] = 0;
  381.          }
  382.          if(ftp->cd != NULLCHAR){
  383.              free(ftp->cd);
  384.              ftp->cd = NULLCHAR;
  385.          }
  386.         break;
  387.           case HELP_CMD:
  388.         tprintf(ftp->control,helpmsg);
  389.         break;
  390.     case TYPE_CMD:
  391.         switch(arg[0]){
  392.         case 'A':
  393.         case 'a':    /* Ascii */
  394.             ftp->type = ASCII_TYPE;
  395.             tprintf(ftp->control,typeok);
  396.             break;
  397.         case 'l':
  398.         case 'L':
  399.             while(*arg != ' ' && *arg != '\0')
  400.                 arg++;
  401.             if(*arg == '\0' || *++arg != '8'){
  402.                 tprintf(ftp->control,only8);
  403.                 break;
  404.             }    /* Note fall-thru */
  405.         case 'B':
  406.         case 'b':    /* Binary */
  407.         case 'I':
  408.         case 'i':    /* Image */
  409.             ftp->type = IMAGE_TYPE;
  410.             tprintf(ftp->control,typeok);
  411.             break;
  412.         default:    /* Invalid */
  413.             tprintf(ftp->control,badtype,arg);
  414.             break;
  415.         }
  416.         break;
  417.     case QUIT_CMD:
  418.         tprintf(ftp->control,bye);
  419.         close_tcp(ftp->control);
  420.         break;
  421.     case RETR_CMD:
  422.         /* Disk operation; return ACK now */
  423.         tcp_output(ftp->control);
  424.         file = pathname(ftp->cd,arg);
  425.         if(ftp->type == IMAGE_TYPE)
  426.             mode = binmode[READ_BINARY];
  427.         else
  428.             mode = "r";
  429.         if(!permcheck(ftp,RETR_CMD,file)){
  430.              tprintf(ftp->control,noperm);
  431.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  432.             tprintf(ftp->control,cantopen,file);
  433.         } else {
  434.             log(ftp->control,"RETR %s",file);
  435.             dport.address = ip_addr;
  436.             dport.port = FTPD_PORT;
  437.             ftp->state = SENDING_STATE;
  438.             tprintf(ftp->control,sending,"RETR",arg);
  439.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  440.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  441.         }
  442.         free(file);
  443.         break;
  444.     case STOR_CMD:
  445.         /* Disk operation; return ACK now */
  446.         tcp_output(ftp->control);
  447.         file = pathname(ftp->cd,arg);
  448.         if(ftp->type == IMAGE_TYPE)
  449.             mode = binmode[WRITE_BINARY];
  450.         else
  451.             mode = "w";
  452.         if(!permcheck(ftp,STOR_CMD,file)){
  453.              tprintf(ftp->control,noperm);
  454.             free(file);
  455.              break;
  456.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  457.             tprintf(ftp->control,cantmake,file);
  458.         } else {
  459.             log(ftp->control,"STOR %s",file);
  460.             dport.address = ip_addr;
  461.             dport.port = FTPD_PORT;
  462.             ftp->state = RECEIVING_STATE;
  463.             tprintf(ftp->control,sending,"STOR",arg);
  464.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  465.              0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(char *)ftp);
  466.         }
  467.         free(file);
  468.         break;
  469.     case PORT_CMD:
  470.         if(pport(&ftp->port,arg) == -1){
  471.             tprintf(ftp->control,badport);
  472.         } else {
  473.             tprintf(ftp->control,portok);
  474.         }
  475.         break;
  476. #ifndef CPM
  477.     case LIST_CMD:
  478.         /* Disk operation; return ACK now */
  479.         tcp_output(ftp->control);
  480.  
  481.         file = pathname(ftp->cd,arg);
  482.         if(!permcheck(ftp,RETR_CMD,file)){
  483.              tprintf(ftp->control,noperm);
  484.         } else if((ftp->fp = dir(file,1)) == NULLFILE){
  485.             tprintf(ftp->control,nodir,file);
  486.         } else {
  487.             dport.address = ip_addr;
  488.             dport.port = FTPD_PORT;
  489.             ftp->state = SENDING_STATE;
  490.             tprintf(ftp->control,sending,"LIST",file);
  491.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  492.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  493.         }
  494.         free(file);
  495.         break;
  496.     case NLST_CMD:
  497.         /* Disk operation; return ACK now */
  498.         tcp_output(ftp->control);
  499.  
  500.         file = pathname(ftp->cd,arg);
  501.         if(!permcheck(ftp,RETR_CMD,file)){
  502.              tprintf(ftp->control,noperm);
  503.         } else if((ftp->fp = dir(file,0)) == NULLFILE){
  504.             tprintf(ftp->control,nodir,file);
  505.         } else {
  506.             dport.address = ip_addr;
  507.             dport.port = FTPD_PORT;
  508.             ftp->state = SENDING_STATE;
  509.             tprintf(ftp->control,sending,"NLST",file);
  510.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  511.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  512.         }
  513.         free(file);
  514.         break;
  515.     case CWD_CMD:
  516.         tcp_output(ftp->control);    /* Disk operation; return ACK now */
  517.  
  518.         file = pathname(ftp->cd,arg);
  519.         if(!permcheck(ftp,RETR_CMD,file)){
  520.              tprintf(ftp->control,noperm);
  521.             free(file);
  522. #if      (defined(MSDOS) || defined(ATARI_ST))
  523.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  524.         } else if(strcmp(file,"\\") == 0 || access(file,0) == 0){
  525. #else
  526.         } else if(access(file,0) == 0){    /* See if it exists */
  527. #endif /* MSDOS || ATARI_ST */
  528.             /* Succeeded, record in control block */
  529.             free(ftp->cd);
  530.             ftp->cd = file;
  531.             tprintf(ftp->control,pwdmsg,file);
  532.         } else {
  533.             /* Failed, don't change anything */
  534.             tprintf(ftp->control,nodir,file);
  535.             free(file);
  536.         }
  537.         break;
  538.     case XPWD_CMD:
  539.     case PWD_CMD:
  540.         tprintf(ftp->control,pwdmsg,ftp->cd);
  541.         break;
  542. #else
  543. /* for cpm */          
  544.     case LIST_CMD:
  545.     case NLST_CMD:
  546.     case CWD_CMD:
  547.     case XPWD_CMD:
  548.     case PWD_CMD:
  549. #endif /* ndef CPM */
  550.           case NAME_CMD:
  551.     case ACCT_CMD:        
  552.         tprintf(ftp->control,unimp);
  553.         break;
  554.     case DELE_CMD:
  555.         file = pathname(ftp->cd,arg);
  556.         if(!permcheck(ftp,DELE_CMD,file)){
  557.              tprintf(ftp->control,noperm);
  558.         } else if(unlink(file) == 0){
  559.             tprintf(ftp->control,deleok);
  560.         } else {
  561.             tprintf(ftp->control,delefail);
  562.         }
  563.         free(file);
  564.         break;
  565.     case PASS_CMD:
  566.         tcp_output(ftp->control);    /* Send the ack now */
  567.         ftplogin(ftp,arg);            
  568.         break;
  569. #ifndef    CPM
  570.     case XMKD_CMD:
  571.     case MKD_CMD:
  572.         file = pathname(ftp->cd,arg);
  573.         if(!permcheck(ftp,MKD_CMD,file)){
  574.             tprintf(ftp->control,noperm);
  575.         } else if(xmkdir(file,0777) == 0){
  576.             tprintf(ftp->control,mkdok);
  577.         } else {
  578.             tprintf(ftp->control,cantmake);
  579.         }
  580.         free(file);
  581.         break;
  582.     case XRMD_CMD:
  583.     case RMD_CMD:
  584.         file = pathname(ftp->cd,arg);
  585.         if(!permcheck(ftp,RMD_CMD,file)){
  586.              tprintf(ftp->control,noperm);
  587.         } else if(rmdir(file) == 0){
  588.             tprintf(ftp->control,deleok);
  589.         } else {
  590.             tprintf(ftp->control,delefail);
  591.         }
  592.         free(file);
  593.         break;
  594.     case STRU_CMD:
  595.         if(tolower(arg[0]) != 'f')
  596.             tprintf(ftp->control,unsupp);
  597.         else
  598.             tprintf(ftp->control,okay);
  599.         break;
  600.     case MODE_CMD:
  601.         if(tolower(arg[0]) != 's')
  602.             tprintf(ftp->control,unsupp);
  603.         else
  604.             tprintf(ftp->control,okay);
  605.         break;
  606.     }
  607. #endif
  608. }
  609. static
  610. int
  611. pport(sock,arg)
  612. struct socket *sock;
  613. char *arg;
  614. {
  615.     int32 n;
  616. /*    int atoi(); */
  617.     int i;
  618.  
  619.     n = 0;
  620.     for(i=0;i<4;i++){
  621.         n = atoi(arg) + (n << 8);
  622.         if((arg = index(arg,',')) == NULLCHAR)
  623.             return -1;
  624.         arg++;
  625.     }
  626.     sock->address = n;
  627.     n = atoi(arg);
  628.     if((arg = index(arg,',')) == NULLCHAR)
  629.         return -1;
  630.     arg++;
  631.     n = atoi(arg) + (n << 8);
  632.     sock->port = n;
  633.     return 0;
  634. }
  635. /* Attempt to log in the user whose name is in ftp->username and password
  636.  * in pass
  637.  */
  638. static
  639. ftplogin(ftp,pass)
  640. struct ftp *ftp;
  641. char *pass;
  642. {
  643.     char buf[80],*cp,*cp1,*getnenv();
  644.     FILE *fp;
  645.     int anony = 0;
  646.     int i;
  647.  
  648.     if((fp = fopen(userfile,"r")) == NULLFILE){
  649.         /* Userfile doesn't exist */
  650.         tprintf(ftp->control,noperm);
  651.         return;
  652.     }
  653.  
  654.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  655.         if(buf[0] == '#')
  656.             continue;    /* Comment */
  657.         if((cp = index(buf,' ')) == NULLCHAR)
  658.             /* Bogus entry */
  659.             continue;
  660.         *cp++ = '\0';        /* Now points to password */
  661.         if(strcmp(ftp->username,buf) == 0)
  662.             break;        /* Found user name */
  663.     }
  664.     if(feof(fp)){
  665.         /* User name not found in file */
  666.         fclose(fp);
  667.         tprintf(ftp->control,noperm);
  668.         return;
  669.     }
  670.     fclose(fp);
  671.     /* Look for space after password field in file */
  672.     if((cp1 = index(cp,' ')) == NULLCHAR){
  673.         /* Invalid file entry */
  674.         tprintf(ftp->control,noperm);
  675.         return;
  676.     }
  677.     *cp1++ = '\0';    /* Now points to first path field */
  678.     if(strcmp(cp,"*") == 0)
  679.         anony = 1;    /* User ID is password-free */
  680.     if(!anony && strcmp(cp,pass) != 0){
  681.         /* Password required, but wrong one given */
  682.         tprintf(ftp->control,noperm);
  683.         return;
  684.     }
  685.     for(i = 0; i< MAXPATH; i++){
  686.       if((cp = index(cp1,' ')) == NULLCHAR){
  687.         /* Permission field missing, assume end of line */
  688.         break;
  689.       }
  690.       *cp++ = '\0';    /* now points to permission field */
  691.       ftp->path[i] = malloc((unsigned)strlen(cp1)+1);
  692.       strcpy(ftp->path[i],cp1);
  693.       /* set the permission bits */
  694.       ftp->perms[i] = atoi(cp);
  695.       if ((cp1 = index(cp,' ')) == NULLCHAR){
  696.         /* no next path field, so assume end of line */
  697.         break;
  698.       }
  699.       *cp1++ = '\0'; /* cp1 now points to the next path field */
  700.     }
  701.  
  702.     /* Set up current directory and LAST specified path prefix */
  703.     for (i= MAXPATH - 1; i>=0; i--)
  704.       if (ftp->perms[i])
  705.         break;
  706.  
  707.     ftp->cd = malloc((unsigned)strlen(ftp->path[i])+1);
  708.     strcpy(ftp->cd,ftp->path[i]);
  709.     
  710.     tprintf(ftp->control,logged);
  711.     if(!anony)
  712.         log(ftp->control,"%s logged in",ftp->username);
  713.     else
  714.         log(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  715. }        
  716.  
  717. #if    (defined(MSDOS) || defined(ATARI_ST))
  718. /* Illegal characters in a DOS filename */
  719. char badchars[] = "\"[]|<>+=;,";
  720. #endif
  721.  
  722. /* Return 1 if the file operation is allowed, 0 otherwise */
  723. permcheck(ftp,op,file)
  724. struct ftp *ftp;
  725. int op;
  726. char *file;
  727. {
  728. #if    (defined(MSDOS) || defined(ATARI_ST))
  729.     char *cp;
  730. #endif
  731.     int i;
  732.  
  733.     if(file == NULLCHAR || ftp->path[0] == NULLCHAR)
  734.         return 0;    /* Probably hasn't logged in yet */
  735. #if    (defined(MSDOS) || defined(ATARI_ST))
  736.     /* Check for characters illegal in MS-DOS file names */
  737.     for(cp = badchars;*cp != '\0';cp++){
  738.         if(index(file,*cp) != NULLCHAR)
  739.             return 0;    
  740.     }
  741. #endif
  742. #if    (!AMIGA && !MAC)
  743.     /* The target file must be under the user's allowed search path */
  744.      for(i = 0; i < MAXPATH; i++)
  745.          if(ftp->path[i] != NULLCHAR &&
  746.             strncmp(file,ftp->path[i],strlen(ftp->path[i])) == 0)
  747.              break;
  748.  
  749.     if(i == MAXPATH)
  750.         return 0;
  751. #endif
  752.  
  753.     switch(op){
  754.     case RETR_CMD:
  755.         /* User must have permission to read files */
  756.         if(ftp->perms[i] & FTP_READ)
  757.             return 1;
  758.         return 0;
  759.     case DELE_CMD:
  760.     case RMD_CMD:
  761.         /* User must have permission to (over)write files */
  762.         if(ftp->perms[i] & FTP_WRITE)
  763.             return 1;
  764.         return 0;
  765.     case STOR_CMD:
  766.     case MKD_CMD:
  767.         /* User must have permission to (over)write files, or permission
  768.          * to create them if the file doesn't already exist
  769.          */
  770.         if(ftp->perms[i] & FTP_WRITE)
  771.             return 1;
  772.         if(access(file,2) == -1 && (ftp->perms[i] & FTP_CREATE))
  773.             return 1;
  774.         return 0;
  775.     }
  776.     return 0;    /* "can't happen" -- keep lint happy */
  777. }
  778.