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

操作系统开发

开发平台:

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