📄 ftp.c
字号:
}/* Connect to the given host * Returns non-zero value if successfull */int ftp_connect_pasv() { int sock; sock = socket_connect( ftp_dtp_addr, ftp_dtp_port ); if( sock == -1 ) { ftp_seterror_err( "Could not connect socket" ); return 0; } else { ftp_dtp_socket = sock; return 1; }}int ftp_open( ) { int ret; if( ftp_connection != false ) return FTP_OK; fe_connection( fe_connecting ); DEBUG( DEBUG_FTP, "Opening socket to port %d\n", ftp_pi_port ); /* Open the socket. */ ftp_pi_socket = socket_connect( ftp_pi_addr, ftp_pi_port ); if( ftp_pi_socket < 0 ) { return FTP_CONNECT; } /* Read the hello message */ ret = ftp_read(); if( ret != FTP_OK ) { return FTP_HELLO; } ftp_connection = true; if( ftp_login( ) != FTP_OK ) {/* ftp_seterror( "Could not log in to server." ); */ ftp_connection = false; close( ftp_pi_socket ); return FTP_LOGIN; } fe_connection( fe_connected ); /* <tr@oxlug.org>: Don't know whether in ASCII or binary mode so try * to fix it. I don't like this one bit as ftp_settype can call * ftp_open. * joe: Note, ftp_using_ascii is set to -1 to avoid infinite * recursion. Alter with care. */ if(ftp_using_ascii != -1) { bool ascii = ftp_using_ascii; ftp_using_ascii = -1; return ftp_settype(ascii); } return FTP_OK;}int ftp_login( ) { char cmd[BUFSIZ]; int ret; DEBUG( DEBUG_FTP, "FTP: Logging in as %s...\n", ftp_username ); snprintf( cmd, BUFSIZ, "USER %s", ftp_username ); ret = ftp_exec( cmd ); if( ret == FTP_NEEDPASSWORD ) { DEBUG( DEBUG_FTP, "FTP: Supplying password...\n" ); snprintf( cmd, BUFSIZ, "PASS %s", ftp_password ); ret = ftp_exec( cmd ); } return ret;}/* Returns anything which ftp_read() does */int ftp_exec( const char *command ) { int tries = 0, ret = FTP_ERROR; while( ++tries < 3 ) { if( ftp_open( ) != FTP_OK ) break; if( strncmp( command, "PASS ", 5 ) == 0 ) { DEBUG( DEBUG_SOCKET, "> PASS xxxxxxxx\n" ); } else { DEBUG( DEBUG_SOCKET, "> %s\n", command ); } /* Send the line */ if( send_line( ftp_pi_socket, command ) == 0 ) { /* Sent the line... try and read the response */ ret = ftp_read(); if( ret != FTP_BROKEN ) { /* Read the response okay */ break; } } } /* Don't let FTP_BROKEN get out */ if( ret == FTP_BROKEN ) ret = FTP_ERROR; return ret;}/* Returns anything ftp_read() does, or FTP_BROKEN when the * socket read fails (indicating a broken socket). */int ftp_read() { int multiline, len, reply_code; char buffer[BUFSIZ]; multiline = 0; for(;;) { len = read_line( ftp_pi_socket, buffer, BUFSIZ ); if( len < 0 ) { /* It broke. */ ftp_seterror_err( "Could not read response line" ); ftp_connection = false; break; } DEBUG( DEBUG_SOCKET, "< %s", buffer ); if(len<5) /* this is a multi-liner, ignore it and carry on */ continue; /* parse the reply code from the line */ reply_code = get_reply_code(buffer); /* If we have a VALID reply code and we are currently * in a multi line response then this is the end of the * multi line response */ if(multiline && reply_code) multiline=0; /* Now, if we aren't in a multi line response... */ if(!multiline) { if(buffer[3]=='-') { /* A dash in char four denotes beginning of multi * line response 'xxx-' */ multiline=1; } else { /* Looks like we've got a real response line */ return ftp_response( buffer, reply_code ); } } } return FTP_BROKEN;}void ftp_seterror( const char *error ) { memset( ftp_error, 0, BUFSIZ ); strncpy( ftp_error, error, BUFSIZ );/* DEBUG( DEBUG_FTP, "FTP Error set: %s\n", ftp_error ); */}/* Sets the error string and appends ": strerror(errno)" */void ftp_seterror_err( const char *error ) { snprintf( ftp_error, BUFSIZ, "%s: %s", error, strerror(errno) ); DEBUG( DEBUG_FTP, "FTP Error set: %s\n", ftp_error );}int ftp_response( const char *response, const int code ) { char *newline; /* Set the error string up. */ ftp_seterror( response ); /* Chop the newline */ newline = strrchr( ftp_error, '\r' ); if( newline ) *newline = '\0'; switch( code ) { case 200: /* misc OK codes */ case 220: case 230: case 250: /* completed file action */ case 257: /* mkdir success */ return FTP_OK; case 226: /* received file okay */ return FTP_SENT; case 150: /* file transfer... ready for data */ case 125: return FTP_READY; case 550: /* couldn't complete file action */ return FTP_FILEBAD; case 331: /* got username, want password */ return FTP_NEEDPASSWORD; case 350: /* file action pending further info - RNFR */ return FTP_FILEMORE; case 221: /* They've closed the connection, the swine. */ ftp_connection = false; return FTP_CLOSED; case 421: /* service denied */ return FTP_DENIED; case 213: /* MDTM response, hopefully */ return ftp_read_mdtm( response ); case 227: /* PASV response, hopefully */ return ftp_read_pasv( response ); case 553: /* couldn't create directory */ return FTP_ERROR; default: return FTP_ERROR; }}/* Parses the 213 response to a MDTM command... on success, * returns FTP_MODTIME and sets ftp_modtime to the time in the response. * On failute, returns FTP_ERROR. */int ftp_read_mdtm( const char *response ) { struct tm time; char year[5], month[3], mday[3], hour[3], min[3], sec[3]; char *pnt; if( (pnt = strrchr( response, '\n' ))!=NULL ) *pnt='\0'; if( (pnt = strrchr( response, '\r' ))!=NULL ) *pnt='\0'; DEBUG( DEBUG_FTP, "Reading modtime: %s\n", response ); if( strlen( response ) != 18 ) { DEBUG( DEBUG_FTP, "Incorrect length response." ); return FTP_ERROR; } if( sscanf( response, "213 %4s%2s%2s" "%2s%2s%2s", year, month, mday, hour, min, sec ) < 6 ) { DEBUG( DEBUG_FTP, "sscanf couldn't parse it.\n" ); return FTP_ERROR; } DEBUG( DEBUG_FTP, "Parsed: %d/%d/%d %s:%s:%s\n", atoi(year), atoi(month), atoi(mday), hour, min, sec ); memset( &time, 0, sizeof( struct tm ) ); time.tm_year = atoi( year ) - 1900; /* years since 1900 */ time.tm_mon = atoi( month ) - 1; /* months since jan */ time.tm_mday = atoi( mday ); time.tm_hour = atoi( hour ); time.tm_min = atoi( min ); time.tm_sec = atoi( sec ); time.tm_isdst = -1; ftp_modtime = mktime( &time ); DEBUG( DEBUG_FTP, "Converted to: %s", ctime( &ftp_modtime ) ); return FTP_MODTIME;}/* Parses the response to a PASV command. * Sets ftp_dtp_port to the port and ftp_dtp_addr to the address given * in the response and returns FTP_PASSIVE on success. * On failure, returns FTP_ERROR; */int ftp_read_pasv( const char *response ) { int h1, h2, h3, h4, p1, p2; char *start; start = strchr( response, '(' ); /* get the host + port */ if( sscanf( ++start, "%d,%d,%d,%d,%d,%d", &h1, &h2, &h3, &h4, &p1, &p2 ) < 6 ) /* didn't match, give up */ return FTP_ERROR; /* Record this for future reference */ ftp_dtp_port = (p1<<8) | p2; ftp_dtp_addr.s_addr = htonl( (h1<<24) | (h2<<16) | (h3<<8) | h4 ); return FTP_PASSIVE;}/* Takes the response line from an FTP command and returns the value * of the response code, or 0 if none is found. */int get_reply_code( const char *buffer) { if(strlen(buffer) > 3) if( isdigit((unsigned)buffer[0]) && isdigit((unsigned)buffer[1]) && isdigit((unsigned)buffer[2]) ) /* looks good */ return atoi(buffer); return 0;}/* This one does the ls -laR, and tries it's best to parse the resulting * list. Currently implemented only for Unix-style servers, which go: * dirlist * new/directory/name: * dirlist * another/directory/name: * dirlist * etc. where dirlist is a straight ls -al listing */int ftp_fetch_gettree( const char *startdir, struct proto_file_t **files ) { struct proto_file_t *this_file, *last_file; char command[BUFSIZ], buffer[BUFSIZ], *pnt; char *curdir; /* Holds the path of the current directory */ char perms[BUFSIZ], filename[BUFSIZ], tmp[BUFSIZ]; int ret, filesize, buflen; bool afterblank; snprintf( command, BUFSIZ, "LIST -laR %s", startdir ); if( (ret = ftp_data_open( command )) != FTP_READY ) { return PROTO_ERROR; } /* The current directory is a 0-length string. */ curdir = malloc( 1 ); *curdir = '\0'; last_file = NULL; afterblank = false; while( read_line( ftp_dtp_socket, buffer, BUFSIZ ) >= 0 ) { /* Get rid of the EOL */ if( (pnt = strrchr( buffer, '\n' ))!=NULL ) *pnt='\0'; if( (pnt = strrchr( buffer, '\r' ))!=NULL ) *pnt='\0'; DEBUG( DEBUG_FTP, "[ls] < %s\n", buffer ); buflen = strlen( buffer ); if( buflen > 0 ) { if( strncmp( buffer, "total ", 6 ) == 0 ) { /* ignore the line */ DEBUG( DEBUG_FTP, "Line ignored.\n" ); } else if( *(buffer+buflen-1) == ':' && afterblank ) { /* A new directory name indicator, which goes: * `directory/name/here:' * We want directory names as: * `/directory/name/here' * Hence a bit of messing about. */ free( curdir ); buflen = buflen - strlen(startdir ); curdir = malloc( buflen + 1 ); strncpy( curdir, buffer+strlen(startdir), buflen ); *(curdir+buflen-1) = '/'; *(curdir+buflen) = '\0'; DEBUG( DEBUG_FTP, "Now in directory: %s\n", curdir ); } else { /* Weird bit at the end should pick up everything * to the EOL... filenames could have whitespace in. */ sscanf( buffer, "%s %s %s %s %d %s %s %s %[^*]", perms, tmp, tmp, tmp, &filesize, tmp, tmp, tmp, filename); if( perms!=NULL && filename!=NULL ) { if( *perms == '-' ) { /* Normal file. */ DEBUG( DEBUG_FTP, "File: %s, size %d\n", filename, filesize ); this_file = malloc( sizeof(struct proto_file_t) ); memset( this_file, 0, sizeof(struct proto_file_t) ); this_file->next = *files; *files = this_file; if( last_file==NULL ) last_file = this_file; this_file->filename = strdup( filename ); this_file->directory = strdup( curdir ); this_file->isdir = false; this_file->size = filesize; } else if( *perms == 'd' ) { /* Subdirectory */ if( strcmp( filename, "." ) == 0 || strcmp( filename, ".." ) == 0 ) { DEBUG( DEBUG_FTP, "Ignoring: %s\n", filename ); } else { DEBUG( DEBUG_FTP, "Subdir: %s\n", filename ); this_file = malloc( sizeof(struct proto_file_t) ); memset( this_file, 0, sizeof(struct proto_file_t) ); if( last_file==NULL ) { *files = this_file; } else { last_file->next = this_file; } last_file = this_file; this_file->filename = strdup( filename ); this_file->directory = strdup( curdir ); this_file->isdir = true; } } else { /* Summant else... ignore */ DEBUG( DEBUG_FTP, "Ignoring: %s\n", filename ); } } else { DEBUG( DEBUG_FTP, "Could not parse line.\n" ); } } } else { DEBUG( DEBUG_FTP, "Blank line.\n" ); afterblank = true; } } DEBUG( DEBUG_FTP, "Fetch finished successfully.\n" ); if( ftp_data_close( ) == FTP_SENT ) { return FTP_OK; } else { return FTP_ERROR; }}/* Sorts out the modtimes for all the files in the list. * Returns FTP_OK on success, else FTP_ERROR. */int ftp_fetch_walktree( const char *rootdir, struct proto_file_t *files ) { struct proto_file_t *this_file; char command[BUFSIZ]; /* Walk the files list. Okay, it's not really a tree */ for( this_file=files; this_file!=NULL; this_file=this_file->next ) { if( this_file->isdir ) { continue; } DEBUG( DEBUG_FTP, "File: %s%s%s\n", rootdir, this_file->directory, this_file->filename ); snprintf( command, BUFSIZ, "MDTM %s%s%s", rootdir, this_file->directory, this_file->filename ); if( ftp_exec( command ) == FTP_MODTIME ) { DEBUG( DEBUG_FTP, "Got modtime.\n" ); this_file->modtime = ftp_modtime; } else { DEBUG( DEBUG_FTP, "Didn't get modtime.\n" ); return FTP_ERROR; } } DEBUG( DEBUG_FTP, "Walk finished ok.\n" ); return FTP_OK;}/* Retrieves remote file listings. * This is a multi-stage operation. * First off, we do a LIST -R, which returns a recursive file listing. * Next, we go through the entire list of files returned and do a * MDTM on them to retrieve their accurate modtime. * If we can successfully retrieve every modtime, then we pass the * files one by one back up to the sites code. About as inefficient * as it gets. */int ftp_fetch( const char *startdir, struct proto_file_t **files ) { int ret; ret = ftp_fetch_gettree( startdir, files ); if( ret == FTP_OK ) { ret = ftp_fetch_walktree( startdir, *files ); } if( ret == FTP_OK ) { return PROTO_OK; } else { return PROTO_ERROR; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -