/** * Generates response message for client * @param cmd Current command * @param state Current connection state */ void response(Command *cmd, State *state) { switch(lookup_cmd(cmd->command)){ case USER: ftp_user(cmd,state); break; case PASS: ftp_pass(cmd,state); break; case PASV: ftp_pasv(cmd,state); break; case LIST: ftp_list(cmd,state); break; case CWD: ftp_cwd(cmd,state); break; case PWD: ftp_pwd(cmd,state); break; case MKD: ftp_mkd(cmd,state); break; case RMD: ftp_rmd(cmd,state); break; case RETR: ftp_retr(cmd,state); break; case STOR: ftp_stor(cmd,state); break; case DELE: ftp_dele(cmd,state); break; case SIZE: ftp_size(cmd,state); break; case ABOR: ftp_abor(state); break; case QUIT: ftp_quit(state); break; case TYPE: ftp_type(cmd,state); break; case CDUP: ftp_cdup(state); break; case HELP: ftp_help(cmd, state); break; case NLST: ftp_nlst(cmd, state); break; case RNFR: ftp_rnfr(cmd, state); break; case RNTO: ftp_rnto(cmd, state); break; case APPE: ftp_appe(cmd, state); break; case NOOP: if(state->logged_in){ state->message = "200 Zzz...\n"; }else{ state->message = "530 Please login with USER and PASS\n"; } write_state(state); break; default: state->message = "500 Unknown command\n"; write_state(state); break; } }
// // Get a remote file, calling the update function after each block arrives // Returns the total bytes received. // fi_errno MUST be checked for errors // Returns -2 for AmigaDos errors - Use IoErr() to report/handle them // Returns -3 for FTP server errors - Use the fi_reply and fi_iobuf fields to report/handle them // Returns -1 for rare unlikely errors - Could cause DisplayBeep() etc... // now uses IOBUFSIZE // // Update callback must specify 0xffffffff for total and length if REST fails // Update callback must specify 0xffffffff for unknown total // Update callback must specify 0 for length of final call // unsigned int get( struct ftp_info *info, int (*updatefn)(void *,unsigned int,unsigned int), void *updateinfo, char *remote_path, char *local_path, BOOL restart ) { // Needed because socket library base is in our task's tc_userdata field //struct opusftp_globals *ogp = info->fi_og; unsigned int total = 0xffffffff; // Length of file unsigned int bytes = 0; // Bytes received so far APTR f; // Output file int ds; // Data socket int b; // Byte count int reply; // FTP reply fd_set rd, ex; ULONG flags; struct timeval timer = {0}; int display_bytes; int done; D(bug( "get() '%s' -> '%s'\n", remote_path, local_path )); // Valid? if (!info) return 0; // No abort/error yet info->fi_aborted = 0; info->fi_errno = 0; info->fi_ioerr = 0; *info->fi_serverr = 0; // More validity if (!remote_path || !local_path) { info->fi_errno |= FTPERR_XFER_RARERR; return 0; } // init counters etc display_bytes = done = 0; // open output file for writing if ((f = OpenBuf( (char *)local_path, restart ? MODE_OLDFILE : MODE_NEWFILE, WBUFSIZE ))) { // Resuming? So find out where to resume from if (restart) { SeekBuf( f, 0, OFFSET_END ); bytes = SeekBuf( f, 0, OFFSET_CURRENT ); } // get connected - bytes is a market flag 0/x for RETR/REST if ((ds = dataconna( info, bytes, "RETR %s", remote_path )) < 0) { // Source (server error)? if (ds == -2 || ds == -3) { info->fi_errno |= FTPERR_XFER_SRCERR; stccpy( info->fi_serverr, info->fi_iobuf, IOBUFSIZE + 1 ); } else info->fi_errno |= FTPERR_XFER_RARERR; } else { char *p; // Reset bytes if REST failed if (bytes && (info->fi_flags & FTP_NO_REST)) { if (updatefn) (*updatefn)( updateinfo, 0xffffffff, 0xffffffff ); SeekBuf( f, 0, OFFSET_BEGINNING ); bytes = 0; } // First update tells callback where we're starting from if (updatefn) (*updatefn)( updateinfo, total, bytes ); // Does the RETR reply contain the file length? if ((p = strstr( info->fi_iobuf, " bytes)" ))) if (isdigit(*--p)) { while (isdigit(*p--)) ; total = atoi(p+2); } // do the transfer FD_ZERO( &rd ); FD_ZERO( &ex ); // set network timeout for the select wait call set_timeout(info,&timer); // loop fetch tcp data and save it while (!done) { // Note: these masks must be set before every call and // are all cleared by select wait. Examine the masks // afterwards to see what was set FD_SET( ds, &rd ); FD_SET( ds, &ex ); flags = SIGBREAKF_CTRL_D; if (WaitSelect( ds + 1 , &rd, NULL, &ex, &timer, &flags ) >= 0) { // Is there some data ready for us? if (FD_ISSET( ds, &rd )) { // then get it and store it if ((b = recv( ds, info->fi_iobuf, IOBUFSIZE, 0 ))) { // save data if (WriteBuf( f, info->fi_iobuf, b ) == b) { bytes += b; // progress bar uprate display_bytes += b; if (display_bytes >= UPDATE_BYTE_LIMIT) { if (updatefn) (*updatefn)( updateinfo, total, display_bytes ); display_bytes = 0; } } // Write Error else { info->fi_errno |= FTPERR_XFER_DSTERR; info->fi_ioerr = IoErr(); info->fi_aborted = 1; ftp_abor( info ); done = TRUE; } } else done = TRUE; } // did we get a signal to abort? if (!done && (flags & SIGBREAKF_CTRL_D)) { D(bug( "*** get() CTRL-D SIGNAL ***\n" )); info->fi_abortsignals = 0; info->fi_aborted = 1; ftp_abor( info ); // NEEDED why not just close socket? done = TRUE; } // did we get an exception? Other end closed connection maybe if (FD_ISSET( ds, &ex )) { D(bug("** get() socket exception\n")); // has been aborted from remote ? done = TRUE; } } else { // some socket error -ve a 0 == timeout D(bug( "** get() WaitSelect error\n" )); done = TRUE; } } // Final progress bar redraw at 100% (if it finished) if (!info->fi_aborted && !info->fi_errno && updatefn) (*updatefn)( updateinfo, total, 0 ); //D(bug( "--> close(%ld)\n", ds )); CloseSocket( ds ); #ifdef DEBUG // if (ui) // timeit( ui, bytes ); #endif // Get reply to socket closure Can TIMEOUT reply = getreply( info ); // If transfer was aborted, read the ABOR reply (426) // (This could get the reply to RETR (226) but it don't matter) if (info->fi_aborted && reply != 421) reply = getreply( info ); // RETR successful? - Don't set error if we forced it! // (This could get the reply to ABOR (225) but it don't matter) if (reply / 100 != COMPLETE) if (!info->fi_errno) { info->fi_errno |= FTPERR_XFER_SRCERR; stccpy( info->fi_serverr, info->fi_iobuf, IOBUFSIZE + 1 ); } } CloseBuf( f ); // Delete empty files if there was an error and we created it if (info->fi_errno && bytes == 0 && !restart) DeleteFile( (char *)local_path ); } else { info->fi_errno |= FTPERR_XFER_DSTERR; info->fi_ioerr = IoErr(); } return bytes; }