home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 3 / PDCD_3.iso / internet / tcpipsrc / FTP / c / FTPSERV < prev    next >
Encoding:
Text File  |  1995-01-24  |  20.7 KB  |  845 lines

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