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