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