FTPD.C
上传用户:sunrenlu
上传日期:2022-06-13
资源大小:1419k
文件大小:28k
源码类别:

操作系统开发

开发平台:

DOS

  1. /*
  2.  * Copyright (c) 1990, 1999 Erick Engelke
  3.  */
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <stdlib.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #include <time.h>
  10. #include <ctype.h>
  11. #include <io.h>
  12. #include <mem.h>
  13. #include <rtos.h>
  14. #include <net.h>
  15. #include <dir.h>
  16. #include <dos.h>
  17. #include <share.h>
  18. #if defined(__DJGPP__)
  19. #include <unistd.h>
  20. #define ltoa( x, buf, radix ) itoa( x, buf, radix )
  21. #define min(x,y) ( ( x < y ) ? x : y )
  22. #define movmem( x, y, len ) memmove( y, x, len )    /* reverse order */
  23. #endif  // DJGPP
  24. #define S_INIT  0
  25. #define S_WAIT  1
  26. #define S_CONN  2   /* connected and talking */
  27. #define S_DATA  4
  28. #define S_CLOS  5
  29. #define S_HALT  6   /* halt service */
  30. #define DIR_DELIM       '/'
  31. char *cmds[] = {
  32.     "ABOR", "QUIT", "PASS", "HELP", "NOOP", "USER", 
  33.     "MKD",  "XMKD", "RMD",  "XRMD", "CWD",  "XCWD", "PWD",  "XPWD",
  34.     "SITE", "DEL" , "TYPE", "SIZE", "PORT", "LIST", "NLST", "STOR", "RETR",
  35.     "RNFR", "RNTO"
  36.     };
  37. enum {
  38.      ABOR,   QUIT,   PASS,   HELP,   NOOP,   USER,
  39.      MKD,    XMKD,   RMD,    XRMD,   CWD,    XCWD,   PWD,    XPWD,
  40.      SITE,   DEL,    TYPE,   SIZE,   PORT,   LIST,   NLST,   STOR,   RETR,
  41.      RNFR,   RNTO,
  42.      ENDLIST };
  43. #define FTPDWELCOME "FTPD.WELCOME"
  44. #define FTPDPASS   "FTPD.PASSWORD"
  45. #define FTPDBUFSIZ "FTPD.BUFSIZ"
  46. #define FTPDPORT    21
  47. #define FTP_OP_DIR  0
  48. #define FTP_OP_GET  1
  49. #define FTP_OP_PUT  2
  50. #define FTP_OP_DON  3
  51. #define FTP_OP_NOP  4
  52. #define FTPDIRLEN  65
  53. #define FTPPATHLEN 81
  54. #define FTPD_BUF_MIN 512
  55. #define FTPD_BUF_NORM 2048
  56. typedef struct {
  57.     int         status;
  58.     tcp_Socket socket;
  59.     int         dstatus;
  60.     int         doperation;
  61.     int         dcmd;
  62.     tcp_Socket  dsocket;
  63.     struct ffblk ffblk;
  64.     int         dhandle;        /* for file transfer */
  65.     int         gotpass;
  66.     int         ascii;         /* 1 is ascii */
  67.     longword    biglen;         /* for file transfer completion stats */
  68.     word        bufhead;
  69.     word        buftail;
  70.     longword    hisip;          /* used for data connections */
  71.     word        hisport;
  72.     char       *errmsg;
  73.     char        dirpath[ FTPDIRLEN ];
  74.     char        filepath[ FTPPATHLEN ];
  75.     char       *ftpdbuffer;
  76. } ftpdses;
  77. int   ftpdbufferlen = FTPD_BUF_NORM;
  78. // static ftpdses *ftpd;
  79. char ftpdpassword[ 128 ];
  80. char ftpdwelcome[ 128 ];
  81. extern void *getvalue( char * p );
  82. extern int cmdcompare( char *p, char *command );
  83. typedef struct dostimedate {
  84.     unsigned xxx  : 5;
  85.     unsigned min  : 6;
  86.     unsigned hour : 5;
  87.     unsigned date : 5;
  88.     unsigned mon  : 4;
  89.     unsigned year : 7;
  90. };
  91. static char *dos2unix( char *src, char *dest)
  92. {
  93.     char ch;
  94.     char *r = dest;
  95.     while ( (ch = *src++ ) != 0 ) {
  96.         if ( ch == '\' ) ch = '/';
  97.         *dest ++ = ch;
  98.     }
  99.     *dest = 0;
  100.     return( r );
  101. }
  102. /*
  103.  * handlerrs - install an error handler which simply returns as though fail
  104.  *             was sellected by the user
  105.  */
  106. static void interrupt (*olderrhandler)();
  107. #if defined(__TURBOC__)||defined(__BORLANDC__)
  108. #pragma warn -par
  109. /*
  110.  * handlerrs - it prevents abort,retry,ignore,...
  111.  *      - coded Borland specific
  112.  *      - start = nonzero installs handler, zero resets it
  113.  */
  114. static void handlerrs( int start )
  115. {
  116.     dos_enter();
  117.     if ( start ) {
  118. olderrhandler = getvect( 0x24 );
  119. harderr( (int (*)()) hardretn );
  120.     } else
  121. setvect( 0x24, olderrhandler );
  122.     dos_exit();
  123. }
  124. #endif  // BORLANDC
  125. /*
  126.  * strins - copy little_string into big_string
  127.  *
  128.  * eg. strins( "0123456", "abcd", 2 ) creates "ab23456"
  129.  */
  130. static void strins( char *big, char *little, int len )
  131. {
  132.     int movlen;
  133.     movlen = min( strlen( little ), ((len > 0) ? len : -len));
  134.     if ( len > 0 ) movmem( little, big, movlen );
  135.     else movmem( little, big - (len + movlen), movlen );
  136. }
  137. /*
  138.  * dirformblock - create a unix "ls -l" style of output for this dir entry
  139.  *              - uses several DOS functions or single memory buffers
  140.  *                wrap with dos_enter() / dos_exit();
  141.  */
  142. static
  143. void
  144. dirformblock( struct ffblk* ff, char* formatted)
  145. {
  146.   struct dostimedate* pdos_time = (struct dostimedate*) &ff->ff_ftime;
  147.   struct tm unix_time;
  148.   time_t timer = time( NULL );
  149.   int current_year = localtime( &timer )->tm_year + 1900; /* UNIX year bias */
  150.   char buf[16];
  151.   int i;
  152.   memset(&unix_time, 0, sizeof(unix_time));
  153.   unix_time.tm_min  = pdos_time->min;
  154.   unix_time.tm_hour = pdos_time->hour;
  155.   unix_time.tm_mday = pdos_time->date;
  156.   unix_time.tm_mon  = pdos_time->mon - 1; /* DOS months 1-12, UNIX months 0-11 */
  157.   unix_time.tm_year = pdos_time->year + 1980 /* DOS year bias */ - 1900 /* UNIX year bias */;
  158.   
  159.   sprintf( formatted, "%c", ff->ff_attrib & FA_DIREC ? 'd' : '-' );
  160.   sprintf( buf, "r%cx", !(ff->ff_attrib & FA_RDONLY) ? 'w' : '-' );
  161.   for(i = 0; i<3; i++)
  162.     strcat( formatted, buf );
  163.   sprintf( strchr(formatted, 0), " 1 user users %10s ", ltoa( ff->ff_fsize, buf, 10 ) );
  164.   strftime( strchr(formatted, 0), -1,
  165.     pdos_time->year + 1980 == current_year ? "%b %d %H:%M " : "%b %d  %Y ", &unix_time
  166.   );
  167.   strcat(formatted, ff->ff_name);
  168.   strcat(formatted, "rn");
  169. }
  170. /*
  171.  * functions to handle paths
  172.  */
  173. static char *pathfile( ftpdses *f, char *fname )
  174. {
  175.     char *p;
  176.     /* better return bad name than overwrite buffers */
  177.     if ( (strlen( f->dirpath ) + strlen( fname ) - 2) >= FTPPATHLEN )
  178.         return( fname );
  179.     /* support hard coded D:xxx */
  180.     if ( f->dirpath[0] && (f->dirpath[1] == ':'))
  181.       f->filepath[0] = 0;
  182.     else
  183.       strcpy( f->filepath, ".\");
  184.     if ( *f->dirpath ) {
  185.         strcat( f->filepath, f->dirpath );
  186.         p = strchr( f->filepath , 0 );
  187.         p--;
  188.         if ( *p != '\' )
  189.           strcat( f->filepath, "\");
  190.     }
  191.     strcat( f->filepath, fname );
  192.     return( f->filepath );
  193. }
  194. static int changedir( ftpdses *f, char *path)
  195. {
  196.     struct stat st;
  197.     char *p;
  198.     int i;
  199.     int len;
  200.     /* fix unix styled slashes */
  201.     while ( (p = strchr( path, '/' )) != NULL ) *p = '\';
  202.     /* handle up one dir */
  203.     if ( (path[0] == '.') && (path[1] == '.' )) {
  204.         /* go up a subdir */
  205.         if ( (p = strrchr( f->dirpath, '\' ))!= NULL) {
  206.             *p = 0;
  207.         } else
  208.             f->dirpath[0] = 0;
  209.         if ( f->dirpath[0] && ( f->dirpath[1] == ':') && !f->dirpath[2] ) {
  210.             /* need trailing / */
  211.             strcat( f->dirpath,"\");
  212.         }
  213.         return( 0 );    /* say it was successful */
  214.     }
  215.     /*  root is . */
  216.     if ( (path[0] == DIR_DELIM ) && ( path[1] == 0 )) {
  217.         *f->dirpath = 0;
  218.         return( 0 );
  219.     }
  220.     /* AGW 2000.7.17 remove trailing /  */
  221.     if ((len = strlen( path )) > 0 ) {
  222.         char *p;
  223.         p = &path[ len - 1];
  224.         if ( *p == DIR_DELIM ) *p = 0; // if trailing \ then remove
  225.     }
  226.     /* if they didn't specify  then assume we are adding to path */
  227.     if ( *path == DIR_DELIM )
  228.         path++;     /* skip leading  for relative directory */
  229.     /* handle Drive:path... condition */
  230.     else if ( (path[0] != 0) && (path[1] == ':')) {
  231.         // do nothing, this is a good path
  232.     } else {
  233.         strcpy( f->filepath, f->dirpath );
  234.         if ( *f->dirpath )
  235.             strcat( f->filepath, "/");
  236.         strcat( f->filepath, path );
  237.         path = f->filepath;
  238.     }
  239.     /* convert to DOS path */
  240.     while ( (p = strchr( path, DIR_DELIM )) != NULL ) *p = '\';
  241.     dos_enter();
  242.     i = stat( path, &st );      /* no such file or directory */
  243.     dos_exit();
  244.     if ( i != 0 )
  245.         return( 1 );
  246.     if ((st.st_mode & S_IFDIR ) == 0 )
  247.         return( 1 ); /* not a directory */
  248.     /* use new directory path */
  249.     strncpy( f->dirpath, path, FTPDIRLEN - 1 );
  250.     f->dirpath[ FTPDIRLEN - 1] = 0;
  251.     return( 0 );
  252. }
  253. /*
  254.  * data_op_... operations on data socket
  255.  */
  256. /*
  257.  * data_op_get - does a "GET"
  258.  *             - could be optimized for speed by allocing a second
  259.  *               buffer and performing the DOS read while waiting for
  260.  *               the remote host to ack the data
  261.  */
  262. static void data_op_get( ftpdses *f, tcp_Socket *t )
  263. {
  264. //    int len;
  265.     unsigned diff, wrote, ofs;
  266.     int i;
  267.     if ( sock_tbused( t ) == 0 ){
  268.         diff = 0;
  269.         dos_enter();
  270.         i = _dos_read( f->dhandle, f->ftpdbuffer, ftpdbufferlen, &diff );
  271.         dos_exit();
  272.         if ( i != 0 ) {
  273.             f->errmsg = "550 Error while trying to read filern";
  274.             f->doperation = FTP_OP_DON;
  275.         } else if ( diff == 0 ) /* eof or possibly error condition */
  276.     f->doperation = FTP_OP_DON;
  277.         else {
  278.             // was good, sit in loop writing it out
  279.             wrote = ofs = 0;
  280.             do {
  281.                 sock_mode( t, TCP_MODE_NONAGLE );
  282.                 wrote = sock_fastwrite( t, f->ftpdbuffer + ofs, diff );
  283.                 ofs += wrote;
  284.                 diff -= wrote;
  285.                 if ( wrote == 0 ) rt_sleep( 0 );
  286.             } while ( diff > 0 );
  287.         }
  288.     }
  289. }
  290. static void data_op_init( ftpdses *f, word operation )
  291. {
  292.     f->doperation = operation;
  293.     f->dstatus = S_INIT;
  294.     f->status = S_DATA;
  295.     f->biglen = 0;
  296.     f->bufhead = f->buftail = 0;
  297.     f->errmsg = "250 Donern";
  298.     sock_puts( &f->socket, "150 Openning data connectionrn");
  299. }
  300. static void c_op_dir( ftpdses *f, char *path )
  301. {
  302.     char buffer[ 81 ];
  303.     // char  p;
  304.     int status;
  305.     f->dhandle = 0;
  306.     /* support pseudo-standard -L */
  307.     if ( path != NULL ) {
  308.         if ( !strnicmp( path, "-L", 2 )) {
  309.             path += 2;
  310.             if ( !*path ) path = NULL;
  311.         }
  312.     }
  313.     /* new for directories */
  314.     if ( path != NULL ) strcpy( buffer, pathfile( f, path ));
  315.     else strcpy( buffer, pathfile( f, "*.*" ));
  316. //    if (path != NULL ) movmem( path, buffer, sizeof(buffer) - 1 );
  317. //    else strcpy( buffer, "*.*" );
  318.     buffer[ sizeof( buffer ) - 1 ] = 0;
  319.     status = findfirst( buffer, &f->ffblk, FA_DIREC);
  320.     if ( !strchr( buffer, '*' ) &&
  321.          !strchr( buffer, '?' ) &&
  322.          !status                &&
  323.          (f->ffblk.ff_attrib & FA_DIREC ) &&
  324.          *f->ffblk.ff_name != '.') {
  325. strcat( buffer, "\*.*");
  326.         dos_enter();
  327. status = findfirst( buffer, &f->ffblk, FA_DIREC);
  328.         dos_exit();
  329.     }
  330.     if (!status) data_op_init( f, FTP_OP_DIR );
  331.     else sock_puts(&f->socket, "550 File or directory not foundrn");
  332. }
  333. static void (*oldinit)(char *, char *);
  334. static void newinit( char *directive, char *value )
  335. {
  336.     char *p;
  337.     if (!strcmp( directive, FTPDWELCOME )) {
  338.         strcpy( ftpdwelcome, "220-");
  339.         p = strchr( ftpdwelcome, 0 );
  340.         strncpy( p, value, sizeof( ftpdwelcome) -1 - strlen( ftpdwelcome) );
  341.         ftpdwelcome[ sizeof( ftpdwelcome ) - 1] = 0;
  342.     }
  343.     if (!strcmp( directive, FTPDPASS )) {
  344.         strncpy( ftpdpassword, value, sizeof(ftpdpassword)-1);
  345.         ftpdpassword[ sizeof(ftpdpassword) - 1] = 0;
  346.     } else if (!strcmp( directive, FTPDBUFSIZ ))
  347.         ftpdbufferlen = atoi( value );
  348.     else
  349.         if (oldinit) (*oldinit)( directive, value );
  350. }
  351. /*
  352.  * getport - reads the FTP port negotiation stuff
  353.  *         - return nonzero on success
  354.  *
  355.  */
  356. static int getport( ftpdses *f, char *text )
  357. {
  358.     byte a[6], *p;
  359.     int i;
  360.     for ( i = 0 ; (i < 6) && text ; ++i ) {
  361.         if (( p = strchr( text , ',' )) != NULL) *p++ = 0;
  362.         a[i] = atoi( text );
  363.         text = p;
  364.     }
  365.     if ( i == 6 ) {
  366.         f->hisip = intel( *(longword*)a );
  367.         f->hisport = intel16( *(word*)&a[4] );
  368.     } else {
  369.         f->hisip = 0L;          /* should be gained from his structure */
  370.         f->hisport = 0;
  371.     }
  372.     return( i == 6 );
  373. }
  374. /*
  375.  * parseftpline - called with a particular session and some data
  376.  *              - note, this baby supports multiple FTP sessions
  377.  *                by calling with different ftpdses values
  378.  */
  379. static void parseftpline( ftpdses *f, tcp_Socket *s, char *line )
  380. {
  381.     char *p, *q;
  382.     int cmdnum;
  383.     FILE* file;
  384.     /* remove blanks from line - p points to parameters */
  385.     p = strchr( line, ' ');
  386.     if ( p == NULL ) q = NULL;
  387.     else {
  388.         while ( *p == ' ') *p++ = 0;
  389.         if ((q = strchr( p, ' '))!=NULL)
  390.             while ( *q == ' ' ) *q++ = 0;
  391.     }
  392.     if (!*line) return;     /* nothing there */
  393.     for ( cmdnum = 0 ; cmdnum < ENDLIST ; ++cmdnum )
  394.         if ( !strnicmp( line, cmds[ cmdnum ], strlen( cmds[ cmdnum ] )))
  395.             break;
  396.     if ( cmdnum == ENDLIST ) {
  397.         sock_puts( s, "500 Command not understood: ");
  398.         sock_puts( s, line );
  399.         sock_puts( s, "rn");
  400.         /* check for unprivileged access */
  401.     } else if ( cmdnum > USER && f->gotpass == 0 )
  402.         sock_puts( s, "530 You are not logged in yetrn");
  403.         /* file commands need a port number */
  404.     else if ( cmdnum > PORT && ( f->hisport == 0 || f->hisip ==0L ))
  405.         sock_puts( s, "425 Sorry, need PORT command firstrn");
  406.         /* do the normal thing */
  407.     else switch( f->dcmd = cmdnum ) {
  408.         case ABOR   :
  409.             if (f->status == S_DATA ) {
  410.                 sock_abort( &f->dsocket );
  411.                 sock_puts( s, "426 Aborting at users requestrn");
  412.             } else
  413.                 sock_puts( s, "550 Abort what?rn");
  414.             break;
  415.         case QUIT   :
  416.             sock_puts( s, "221 Goodbyern");
  417.             sock_close( s );
  418.             sock_close( &f->dsocket );    /* if necessary */
  419.             tcp_tick( NULL );
  420.             f->status = S_CLOS;
  421.             break;
  422.         case USER   :
  423.             sock_puts( s, "331 Password requiredrn");
  424.             break;
  425.         case PASS   :
  426.             if ( (*ftpdpassword && *line && !strcmp( p, ftpdpassword )) || !*ftpdpassword ) {
  427.                 f->gotpass = 1;
  428.                 sock_puts( s, "230 User logged inrn");
  429.             } else {
  430.                 sock_puts( s, "530 Not quitern");
  431.                 f->gotpass = 0;
  432.             }
  433.             break;
  434.         case HELP   :
  435.             sock_puts( s, "214-HELP - list of command accepted by WATTCP FTP daemonrn");
  436.             sock_puts( s, "    ABOR  QUIT  USER  PASS  HELP  NOOP  DEL   TYPE  PORTrn");
  437.             sock_puts( s, "    LIST  NLST  SIZE  STOR  RETR  SITErn");
  438.             sock_puts( s, "    RMD   XRMD  MKD   XMKD  CWD   XCWD  PWD   XPWDrn");
  439.             sock_puts( s, "    RNFR  RNTOrn");
  440.             sock_puts( s, "214 OKrn");
  441.             break;
  442.         case RMD    :
  443.         case XRMD   :
  444.             dos_enter();
  445.             if ( rmdir( pathfile(f,p) ) == 0 )
  446.               sock_puts( s, "250 RMD workedrn");
  447.             else
  448.               sock_puts( s, "550 RMD failedrn");
  449.             dos_exit();
  450.             break;
  451.         case MKD    :
  452.         case XMKD   :
  453.             dos_enter();
  454. #if defined(__TURBOC__) || defined(__BORLANDC__)
  455.             if ( mkdir( pathfile(f,p)) == 0 )
  456. #elif defined(__DJGPP__)
  457.             /* posix needs a mode */
  458.             if ( mkdir( pathfile(f,p),0 ) == 0 )
  459. #endif
  460.               sock_puts( s, "250 MKD workedrn");
  461.             else
  462.               sock_puts( s, "550 MKD failedrn");
  463.             dos_exit();
  464.             break;
  465.         case PWD    :
  466.         case XPWD   :
  467.             // AGW 2000.6.17 added leading /
  468.             sock_puts( s, "257 "/");
  469.             // EE 2001.5.28 convert to Unix filename format
  470.             sock_puts( s, dos2unix( f->dirpath, f->ftpdbuffer ));
  471.             sock_puts( s, "" is current directory.rn");
  472.             break;
  473.         case CWD    :
  474.         case XCWD   :
  475.             if ( !changedir( f, p )) {
  476.                 sock_puts( s, "250 changed directory to ");
  477.                 sock_puts( s, dos2unix( f->dirpath, f->ftpdbuffer) );
  478.                 sock_puts( s, "rn");
  479.             } else
  480.                 sock_puts( s, "550 CWD failedrn");
  481.             break;
  482.         case DEL    :
  483.             dos_enter();
  484.             sock_puts( s, (!unlink(pathfile(f,p))) ?
  485.                 "250 File deleted - "" : "550 Not Deleted - "" );
  486.             dos_exit();
  487.             sock_puts( s, p );
  488.             sock_puts( s, ""rn");
  489.             break;
  490.         case PORT   :
  491.             if ( getport( f, p ))
  492.                 sock_puts( s, "200 Port command successfulrn");
  493.             else
  494.                 sock_puts( s, "501 Bad port commandrn");
  495.             break;
  496.         case NLST :
  497.         case LIST   :
  498.             c_op_dir( f, p );       /* start directory */
  499.             break;
  500.         case STOR   :               /* start a put */
  501.             dos_enter();
  502.             unlink( pathfile(f, p) );
  503. #if defined(__TURBOC__)||defined(__BORLANDC__)
  504.             if ( (f->dhandle =  open( pathfile(f,p), O_RDWR | O_CREAT |
  505.                 ((f->ascii) ? O_TEXT : O_BINARY) | O_DENYNONE, S_IWRITE)) == -1) {
  506. #elif defined(__DJGPP__)
  507.             if ( (f->dhandle =  open( pathfile(f,p), O_RDWR | O_CREAT |
  508.                 ((f->ascii) ? O_TEXT : O_BINARY) , S_IWRITE)) == -1) {
  509. #endif
  510.                 dos_exit();
  511.                 sock_puts( &f->socket, "550 Could not create file '");
  512.                 sock_puts( &f->socket, p );
  513.                 sock_puts( &f->socket, "'.rn");
  514.             } else {
  515.                 dos_exit();
  516.                 data_op_init( f, FTP_OP_PUT );
  517.             }
  518.             break;
  519.         case RETR   :               /* start a get */
  520.             dos_enter();
  521. #if defined(__TURBOC__)||defined(__BORLANDC__)
  522.             if ( (f->dhandle = open( pathfile(f,p), O_RDONLY ||
  523.                 ( /* (f->ascii) ? O_TEXT : */ O_BINARY))) == -1) {
  524. #elif defined(__DJGPP__)
  525.             if ( (f->dhandle = open( pathfile(f,p), O_RDONLY ||
  526.                 ( /* (f->ascii) ? O_TEXT : */ O_BINARY))) == -1) {
  527. #endif
  528.                 dos_exit();
  529.                 sock_puts( &f->socket, "550 Could not open file '");
  530.                 sock_puts( &f->socket, p );
  531.                 sock_puts( &f->socket, "' for readingrn");
  532.             } else {
  533.                 dos_exit();
  534.                 data_op_init( f, FTP_OP_GET );
  535.             }
  536.             break;
  537.         case SIZE   :
  538.             dos_enter();
  539.             file = fopen( p, "rb" );
  540.             if ( file == 0 || fseek( file, 0, SEEK_END ) != 0 ) {
  541.                 sock_puts( &f->socket, "550 Could not open file '");
  542.                 sock_puts( &f->socket, p );
  543.                 sock_puts( &f->socket, "' for readingrn");
  544.             } else {
  545.                 long int fsize = ftell(file);
  546.                 sock_puts( &f->socket, "213 " );
  547.                 ltoa( fsize, f->ftpdbuffer, 10 );
  548.                 sock_puts( &f->socket, f->ftpdbuffer );
  549.                 sock_puts( &f->socket, "n" );
  550.                 fclose( file );                
  551.             }
  552.             dos_exit();
  553.             break;
  554.             
  555.         case TYPE   :
  556.             *p = toupper( *p );
  557.             if ( *p == 'B' || *p == 'I' ) f->ascii = 0;
  558.             else if ( *p == 'A' ) f->ascii = 1;
  559.             else if ( *p == 'L' && *q == '8' ) f->ascii = 0;
  560.             else {
  561.                 sock_puts( s, "550 Type not understoodrn");
  562.                 p = NULL;
  563.             }
  564.             if (p)
  565.                 sock_puts( s, (f->ascii)? "200 ASCII modern":"200 Binary modern");
  566.             break;
  567.         case NOOP  :
  568.             sock_puts( s, "200 NOOPrn");
  569.             break;
  570.         case SITE :
  571.             if ( !strnicmp( p, "HALT", 4 )) {
  572.                 sock_puts( s, "221 Goodbye! Halting servicern");
  573.                 sock_close( s );
  574.                 sock_close( &f->dsocket );  /* if necessary */
  575.                 f->status = S_HALT;
  576.             } else {
  577.                 sock_puts( s, "500 command not understood 'SITE ");
  578.                 sock_puts( s, p );
  579.                 sock_puts( s, "'rn");
  580.             }
  581.             break;
  582.         case RNFR :  // Rename file from ...
  583.             pathfile( f, p ); // remember name and path in f->filepath
  584.             sock_puts( &f->socket, "350 File name pendingrn");
  585.             break;
  586.         case RNTO :  // Rename file to ...
  587.             {
  588.                 char *from_name = kstrdup( f->filepath );
  589.                 int err;
  590.                 dos_enter();
  591.                 err = rename( from_name, pathfile(f,p));
  592.                 dos_exit();
  593.                 kfree( from_name );
  594.                 if ( err )
  595.                     sock_puts( &f->socket, "550 File rename failedrn");
  596.                 else
  597.                     sock_puts( &f->socket, "250 File rename okrn");
  598.                 break;
  599.             }
  600.     }
  601. }
  602. /*
  603.  * ftpd_alt_tick - processes data when we are doing something and not
  604.  *                 awaiting user input
  605.  */
  606. static void ftpd_alt_tick( ftpdses *f)
  607. {
  608.     tcp_Socket *t;
  609.     int len, wlen;
  610.     t = &f->dsocket;
  611.     switch ( f->dstatus ) {
  612.         case S_INIT :
  613.                 tcp_open( t, FTPDPORT-1, f->hisip, f->hisport, NULL );
  614.                 f->dstatus = S_WAIT;
  615.         case S_WAIT :
  616.                 if ( sock_established( t )) f->dstatus = S_CONN;
  617.                 break;
  618.         case S_CONN :
  619.                 switch ( f->doperation ) {
  620.                     case FTP_OP_DIR :   /* directory operation */
  621.                             while ( sock_tbleft( t ) > 80 ) {
  622.                                 if( f->dcmd == NLST ) {
  623.                                     sock_puts( t, f->ffblk.ff_name );
  624.                                     sock_puts( t, "n" );
  625.                                 } else {
  626.                                     dos_enter();
  627.                                     dirformblock( &f->ffblk, f->ftpdbuffer );
  628.                                     dos_exit();
  629.                                     sock_puts( t, f->ftpdbuffer );
  630.                                 }
  631.                                 dos_enter();
  632.                                 if ( findnext( &f->ffblk ))
  633.                                     f->doperation = FTP_OP_DON;
  634.                                 dos_exit();
  635.                                 break;
  636.                             }
  637.                             break;
  638.                     case FTP_OP_PUT :   /* part of put */
  639.                             if ( (len = sock_fastread( t, f->ftpdbuffer, ftpdbufferlen)) > 0 ) {
  640.                                 dos_enter();
  641.                                 wlen = write( f->dhandle, f->ftpdbuffer, len );
  642.                                 dos_exit();
  643.                                 if (wlen < len || wlen == -1 ) {
  644.                                     f->doperation = FTP_OP_DON;
  645.                                     f->errmsg = "550 Error incurred while writing filern";
  646.                                 }
  647.                             }
  648.                             break;
  649.                     case FTP_OP_GET : data_op_get(f,t); break;
  650.                     case FTP_OP_DON : /* close socket .... */
  651.                             sock_close( t );
  652.                             dos_enter();
  653.                             if (f->dhandle) close( f->dhandle );
  654.                             dos_exit();
  655.                             f->dhandle = 0;
  656.                             sock_puts( &f->socket, f->errmsg );
  657.                             f->doperation = FTP_OP_NOP;
  658.                             break;
  659.                     case FTP_OP_NOP : break;
  660.                 }
  661.                 if (!tcp_tick( t )) {
  662.                     if ( f->doperation < FTP_OP_DON )
  663.                         f->doperation = FTP_OP_DON;
  664.                     else {
  665.                         sock_close( t );
  666.                         f->status = S_CONN;
  667.                         f->dstatus = S_INIT;
  668.                     }
  669.                 }
  670.                 break;
  671.     }
  672. }
  673. /*
  674.  * ftpd_tick - call this whenever you have a free moment
  675.  *           - modify this routine to use several ftpdses
  676.  *             structures if you wish
  677.  */
  678. void ftpd_tick( ftpdses *ftpd )
  679. {
  680.     tcp_Socket *t;
  681.     char *temp;
  682.     t = &ftpd->socket;
  683.     switch ( ftpd->status ) {
  684.         case S_INIT :   /* welcome */
  685.                         sock_abort( t );
  686.                         sock_abort( &ftpd->dsocket );
  687.                         /* save ftpdbuf */
  688.                         temp = ftpd->ftpdbuffer;
  689.                         memset( ftpd, 0, sizeof( ftpdses ));
  690.                         ftpd->ftpdbuffer = temp;
  691.                         ftpd->ascii = 0;
  692.                         tcp_listen( t, FTPDPORT, 0L, 0, NULL, 0 );
  693.                         ftpd->status = S_WAIT;
  694.                         break;
  695.         case S_WAIT :   if ( sock_established( t )) {
  696.                             sock_puts(t,"220-FTP Serverrn");
  697.                             if (*ftpdwelcome) {
  698.                                 sock_puts(t,ftpdwelcome);
  699. sock_puts(t,"rn");
  700.                             }
  701.                             sock_puts(t,"220 Loginrn");
  702.                             ftpd->status = S_CONN;
  703.                             sock_mode( t, TCP_MODE_ASCII );
  704.                         }
  705.                         if ( !tcp_tick( t ))
  706.                             ftpd->status = S_CONN;
  707.                         break;
  708.         case S_DATA :   sock_mode( t, TCP_MODE_BINARY );
  709.                         ftpd_alt_tick( ftpd );
  710.                         sock_mode( t, TCP_MODE_ASCII );
  711.                         /* fall through */
  712.                         if ( !tcp_tick( t )) {
  713.                             sock_abort( t );
  714.                             sock_abort( &ftpd->dsocket );
  715.                             ftpd->status = S_CLOS;
  716.                         }
  717. case S_CONN :   if ( sock_dataready( t )) {
  718.                             sock_gets( t, ftpd->ftpdbuffer, ftpdbufferlen);
  719.                             sock_mode( t, TCP_MODE_BINARY );
  720.                             parseftpline( ftpd, t, ftpd->ftpdbuffer );
  721.                             sock_mode( t, TCP_MODE_ASCII );
  722.                         }
  723. if (!tcp_tick( t )) {
  724.                             sock_close( t );
  725.                             ftpd->status = S_CLOS;
  726.                         }
  727.                         break;
  728.         case S_CLOS :   if ( !tcp_tick( t ) ) {
  729.                             ftpd->status = S_INIT;
  730.                             sock_close( t );
  731.                         }
  732.                         break;
  733.         case S_HALT :   break;
  734.     }
  735. }
  736. #ifdef __DJGPP__
  737. __attribute__((constructor))
  738. #endif
  739. static void ftpd_init()
  740. {
  741.     *ftpdpassword = 0;
  742.     *ftpdwelcome = 0;
  743. #if defined(__TURBOC__)||defined(__BORLANDC__)
  744.     oldinit = usr_init;
  745.     usr_init = newinit;
  746. #elif defined(__DJGPP__)
  747.     oldinit = _w32_usr_init;
  748.     _w32_usr_init = newinit;
  749. #endif
  750. }
  751. #pragma startup ftpd_init 100
  752. void ftpdthread( DWORD x )
  753. {
  754.     ftpdses *ftpd;
  755.     if (ftpdbufferlen < FTPD_BUF_MIN ) ftpdbufferlen = FTPD_BUF_MIN;
  756.     if ((ftpd = kcalloc( sizeof( ftpdses ), 1 )) == NULL )
  757.         rt_halt("allocating memory");
  758.     ftpd->status = S_INIT;
  759.     if ((ftpd->ftpdbuffer = kcalloc( 1, ftpdbufferlen)) == NULL )
  760.         rt_halt("allocating buffers");
  761. #if defined(__TURBOC__) || defined(__BORLANDC__)
  762.     handlerrs( 1 );
  763. #endif
  764.     if ( *ftpdpassword == 0 )
  765.         puts("WARNING: ftpd.password is not set in config file, any password will matchrn");
  766.     while (1) {
  767.         ftpd_tick( ftpd );
  768.         /* improve response only while doing real stuff */
  769.         if ( ftpd->status == S_HALT ) break;
  770.         else if ( ftpd->status == S_DATA ) rt_sleep(0);
  771.         else rt_sleep( 100 );
  772.     }
  773.     /* if SITE was used to halt us */
  774.     while ( tcp_tick( &ftpd->socket )) rt_sleep( 100 );
  775.     kfree( ftpd->ftpdbuffer );
  776.     kfree( ftpd );
  777. #if defined(__TURBOC__) || defined(__BORLANDC__)
  778.     handlerrs( 0 );
  779. #endif
  780. }