ftpd.c

来自「C实现的MUD,对大家基本入门网络游戏很有帮助!」· C语言 代码 · 共 1,581 行 · 第 1/3 页

C
1,581
字号
	mixed tmp;

	if ( UTYPE == BINARY ) {
		data_mode_name = "BINARY";
		data_mode = STREAM_BINARY;
		tmp = allocate_buffer( 0 );
	} else {
		data_mode_name = "ASCII";
		data_mode = STREAM;
		tmp = "";
	}

	if ( socket_info[ fd ][ DATA_FD ] ) {
		socket_write( fd, "425 Can't open data connection.\n" );
		return;
	}

	new_fd = socket_create( data_mode, "data_read_callback",
							"data_close_callback" );

	if ( new_fd < 0 ) {
		socket_write( fd, "425 Can't create data socket.\n" );
		return;
	}

	if ( ( ret = socket_connect( new_fd, sprintf("%s %d",
		  socket_info[ fd ][ DATA_ADDR ], socket_info[ fd ][ DATA_PORT ]),
		  "data_read_callback", "data_write_callback" ) ) < 0 ) {
		TP( "Error: " + socket_info[ fd ][ DATA_ADDR ] + " " +
			socket_info[ fd ][ DATA_PORT ] + "\n" );
		TP( socket_error( ret ) + "\n" );
		socket_write( fd, "425 Can't build data connection.\n" );
		socket_close( new_fd );
		return;
	}

	socket_info[ new_fd ] = ([
		PATH	  : path,
		PARENT_FD : fd,
		DATA	  : tmp,
		TYPE	  : DOWNLOAD,
		APPEND	: append
	]);

	socket_info[ fd ][ DATA_FD ] = new_fd;

	socket_write( fd, sprintf("150 Opening %s mode data connection for %s.\n",
		  data_mode_name, path) );
}  /* read_connection() */


void data_read_callback( int fd, mixed mess )
{
	if ( socket_info[ fd ][ TYPE ] != DOWNLOAD )
		return;

	socket_info[ socket_info[ fd ][ PARENT_FD ]][ LAST_DATA ] = time();

	if ( stringp( mess ) )
		mess = replace_string( mess, "\r", "" );

	socket_info[ fd ][ DATA ] += mess;
}  /* data_read_callback() */


void data_close_callback( int fd )
{
	int pos, size, res;

	if ( socket_info[ fd ][ TYPE ] == DOWNLOAD ) {
		if ( socket_info[ fd ][ APPEND ] != 1 )
			catch( rm( socket_info[ fd ][ PATH ] ) );
		else pos = file_size( socket_info[ fd ][ PATH ] );

		do {
			if (catch( res = write_buffer( UPATH, pos,
									   UDATA[pos..(pos+(BLOCK_SIZE-1))] ) ))
				socket_write( socket_info[ fd ][ PARENT_FD ],
					  "452 Error writing file.\n");
			if (res)
				pos += BLOCK_SIZE;
		} while (res);

		size = stringp( UDATA ) ? strlen( UDATA ) : sizeof( UDATA );

#ifdef FILE_LOCKING
		flock_wrapper( socket_info[ fd ][ PATH ], F_UNLOCK,
			  socket_info[ fd ][ PARENT_FD ]);
#endif

		if ( socket_info[ fd ][ APPEND ] == -1)
			socket_write( socket_info[ fd ][ PARENT_FD ],
				  sprintf("226 Transfer complete (unique file name:%s).\n",
				  socket_info[ fd ][ PATH ]) );
		else
			socket_write( socket_info[ fd ][ PARENT_FD ],
					  "226 Transfer complete.\n" );
	}

	/*
	 * only close data connections here
	 */
	if (!undefinedp( socket_info[fd][PARENT_FD] )) {
		socket_close( fd );
		map_delete( socket_info[ socket_info[ fd ][ PARENT_FD ] ],
			  DATA_FD );
	}
}  /* data_close_callback() */


protected void logout( int fd )
{
	socket_info[ fd ][ LOGGED_IN ] = 0;
	if ( UNAME ) {
#ifdef LOG_TIME
		log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_CONNECT
		log_file( LOG_FILE, sprintf("%s logged out from %s.\n", UNAME, USITE) );
#endif

	}
}  /* logout() */


/*
 * parse user command
 */
protected void parse_comm( int fd, string str )
{
	string *command, tmp, tmp2;
	mixed *misc;
	int port, i, s;

	TP( "Parsing " + str + ".\n" );
	command = explode( str, " " );
	socket_info[ fd ][ LAST_DATA ] = time();

	switch( lower_case( command[ 0 ] ) )  {
		case "port": // specify data connection port
			command = explode( implode( command[1..1000], " " ), "," );
			if ( sizeof( command ) < 6 )
				socket_write( fd, "550 Failed command.\n" );
			else {
				socket_info[ fd ][ DATA_ADDR ] = implode( command[ 0..3 ], "." );
				sscanf( command[ 4 ], "%d", i );
				port = i << 8;
				i = atoi( command[5] );
				port += i;
				socket_info[ fd ][ DATA_PORT ] = port;
				socket_write( fd, "200 PORT command successful.\n" );
			}
			break;
		case "user": // specify user name
			if ( socket_info[ fd ][ LOGGED_IN ] ) {
				logout( fd );
				UNAME = 0;
			}

			CHECK_CMD(1);

			if ( !check_access( command[ 1 ] ) ) {
				socket_write( fd, sprintf("530 User %s access denied.\n",
					  command[ 1 ]) );
#ifdef LOG_TIME
				log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_NO_CONNECT
				log_file( LOG_FILE, sprintf("%s denied access from %s.\n",
					  command[ 1 ], USITE) );
#endif
			} else {
#ifdef ANONYMOUS_FTP
				if ( command[ 1 ] == "anonymous" )
					socket_write( fd,
	"331 Guest login ok, send your complete E-mail address as password.\n" );
				else
#endif
					socket_write( fd, sprintf(
						  "331 Password required for %s.\n", command[ 1 ]) );
				UNAME = command[ 1 ];
			}
			break;
		case "pass": // specify password
			if ( socket_info[fd][LOGGED_IN] || !socket_info[fd][USER_NAME] ) {
				socket_write( fd, "503 Log in with USER first.\n" );
				break;
			}

			if ( !check_password( socket_info[fd][ USER_NAME ],
				  implode( command[ 1..sizeof( command ) - 1 ], " " ) ) ) {
#ifdef ANONYMOUS_FTP
				if (UNAME == "anonymous")
					socket_write( fd,
"530 Login failed.  Please send your complete E-mail address as password.\n" );
				else
#endif
					socket_write( fd, "530 Login incorrect.\n" );

#ifdef LOG_TIME
				log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_NO_CONNECT
				log_file( LOG_FILE, sprintf(
					  "%s failed/incorrect login from %s.\n", UNAME, USITE) );
#endif
				UNAME = 0;
			} else
#ifdef CHECK_SITE
			if (!check_site( UNAME, fd )) {
				/*
				 * Note: this particular response of 530 is not mentioned
				 * as a possible response to the PASS command in RFC959,
				 * because site checking is TMI specific.
				 */
				socket_write( fd, sprintf("530 User %s: Can't login from %s.\n",
					  UNAME, USITE) );
#ifdef LOG_TIME
				log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_NO_CONNECT
				log_file( LOG_FILE, sprintf("%s refused login from %s.\n",
					  UNAME, USITE) );
#endif
				UNAME = 0;
				lose_user( fd );
			} else
#endif
			{
				socket_info[ fd ][ LOGGED_IN ] = 1;
#ifdef ANONYMOUS_FTP
				if ( UNAME == "anonymous" )
					UCWD = FTP_DIR;
				else
#endif
					UCWD = HOME_DIR( UNAME );
#ifdef LOG_TIME
				log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_CONNECT
#ifdef ANONYMOUS_FTP
				if (UNAME == "anonymous")
					log_file( LOG_FILE, sprintf("%s (%s) connected from %s.\n",
						  UNAME, str[strlen(command[0]) + 1..<1], USITE) );
				else
#endif
					log_file( LOG_FILE, sprintf("%s connected from %s.\n",
						  UNAME, USITE) );
#endif

				if ( !directory_exists( UCWD ) ) {
					socket_write( fd, "230 No directory! Logging in with home="
#ifdef GUEST_WIZARD_FTP
						  PUB_DIR ".\n" );
					socket_info[ fd ][ CWD ] = PUB_DIR;
#else
						  "/\n" );
					socket_info[ fd ][ CWD ] = "/";
#endif
				} else {
#ifdef LOGIN_MSG
					tmp = read_file( LOGIN_MSG );
					tmp = tmp[0..<2];
					tmp = sprintf("230- %s\n",
						  replace_string( tmp, "\n", "\n230- " ) );
					socket_write( fd, tmp );
#endif
#ifdef ANONYMOUS_FTP
					if (UNAME == "anonymous")
						socket_write( fd,
					  "230 Guest login ok, access restrictions apply.\n" );
					else
#endif ANONYMOUS_FTP
					socket_write( fd, sprintf(
						  "230 User %s logged in successfully.\n", UNAME) );
				}
				socket_info[ fd ][ TYPE ] = STRING;
			}
			break;
		case "allo": // allocate storage (vacuously)
			socket_write( fd, "202 ALLO command ignored.\n" );
			break;
		case "noop": // do nothing
			socket_write( fd, "200 NOOP command successful.\n" );
			break;
		case "retr": // retrieve a file
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			if ((i = file_size(tmp)) == -2) {
				socket_write( fd, sprintf("550 %s: Not a plain file.\n",
					  command[1]));
			} else if (i == -1) {
				socket_write( fd, sprintf(
					  "550 %s: No such file or directory(%s).\n", command[1], tmp));
			} else if ( !check_valid_read( tmp, fd ) )  {
				PERMISSION_DENIED550(command[ 1 ]);
			} else {
#ifdef LOG_TIME
				log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_FILE
				log_file( LOG_FILE, sprintf("%s retr %s (%s).\n",
					  UNAME, tmp, (UTYPE == BINARY ? "BINARY" : "ASCII") ) );
#endif
				data_conn( fd, tmp, command[ 1 ], FILE );
			}
			break;
		case "stou": // store a file with a unique name
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			i = file_size(tmp);
			if (i >= 0 && check_valid_write( tmp, fd )) {
				for (i = MAX_UNIQUE_TRIES; i; i--) {
					if (file_size(sprintf("%s%d", tmp, i)) == -1)
						break;
				}
				if (i) {
					tmp = sprintf("%s%d", tmp, i);
#ifdef FILE_LOCKING
					if (flock_wrapper(tmp, F_LOCK, fd)) {   /* parent fd */
#endif

#ifdef LOG_TIME
						log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_FILE
						log_file( LOG_FILE, sprintf("%s stou %s (%s).\n", UNAME,
							  tmp, (UTYPE == BINARY ? "BINARY" : "ASCII") ) );
#endif
						read_connection( fd, tmp, -1 );
#ifdef FILE_LOCKING
					} else {
						PERMISSION_DENIED550(command[ 1 ]);
					}
#endif
				} else {
					socket_write( fd, "452 Unique file name cannot be created.\n");
				}
			} else {
				PERMISSION_DENIED553(command[ 1 ]);
			}
			break;
		case "stor": // store a file
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			if ( file_size(tmp) != -2 && check_valid_write( tmp, fd ) ) {
#ifdef FILE_LOCKING
				if (flock_wrapper(tmp, F_LOCK, fd)) {   /* parent fd */
#endif

#ifdef LOG_TIME
					log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_FILE
					log_file( LOG_FILE, sprintf("%s stor %s (%s).\n", UNAME,
						  tmp, (UTYPE == BINARY ? "BINARY" : "ASCII") ) );
#endif
					read_connection( fd, tmp, 0 );
#ifdef FILE_LOCKING
				} else {
					PERMISSION_DENIED550(command[ 1 ]);
				}
#endif
			} else {
				PERMISSION_DENIED553(command[ 1 ]);
			}
			break;
		case "appe": // append to a file
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			if ( file_size(tmp) >= 0 && check_valid_write( tmp, fd ) ) {
#ifdef FILE_LOCKING
				if (flock_wrapper(tmp, F_LOCK, fd)) {   /* parent fd */
#endif

#ifdef LOG_TIME
					log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_FILE
					log_file( LOG_FILE, sprintf("%s appe %s (%s).\n", UNAME,
						  tmp, (UTYPE == BINARY ? "BINARY" : "ASCII") ) );
#endif
					read_connection( fd, tmp, 1 );
#ifdef FILE_LOCKING
				} else {
					PERMISSION_DENIED550(command[ 1 ]);
				}
#endif
			} else {
				PERMISSION_DENIED553(command[ 1 ]);
			}
			break;
		case "dele": // delete a file
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			if ((i = file_size(tmp)) == -2) {
				socket_write( fd, sprintf("550 %s: Not a plain file.\n",
					  command[1]));
			} else if (i == -1) {
				socket_write( fd, sprintf(
					  "550 %s: No such file or directory.\n", command[1]));
			} else if ( check_valid_write( tmp, fd ) ) {
#ifdef FILE_LOCKING
				if (flock_wrapper(tmp, F_LOCK, fd)) {   /* parent fd */
#endif

#ifdef LOG_TIME
					log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_FILE
					log_file( LOG_FILE, sprintf("%s dele %s.\n", UNAME, tmp) );
#endif
					catch( i = rm(tmp) );
					if (i)
						socket_write( fd, "250 DELE command successful.\n" );
					else
						socket_write( fd, sprintf(
							  "550 Permission denied to %s.\n", command[ 1 ]) );
#ifdef FILE_LOCKING
					flock_wrapper(tmp, F_UNLOCK, fd);   /* parent fd */
				} else {
					PERMISSION_DENIED550(command[ 1 ]);
				}
#endif
			} else {
				PERMISSION_DENIED553(command[ 1 ]);
			}
			break;
		/* Supposed to return modified time in the format: YYYYMMDDHHMMSS */
		case "mdtm": // show last modification time of file
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			if ( check_valid_read( tmp, fd ) ) {
				if ((i = file_size(tmp)) == -2)
					socket_write( fd, sprintf("550 %s: Not a plain file.\n",
						  command[1]));
				else if (i == -1)
					socket_write( fd, sprintf(
						  "550 %s: No such file or directory.\n", command[1]));
				else {
					misc = stat(tmp);
					tmp2 = ctime(misc[1]);
					tmp2 = sprintf("%s%02d%c%c%s%s%s",
						  tmp2[20..23], member_array(tmp2[4..6], MONTHS),
						  tmp2[8] == ' ' ? '0' : tmp2[8],
						  tmp2[11..12], tmp2[14..15], tmp2[17..18]);
#ifdef LOG_TIME
					log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_CD_SIZE
					log_file( LOG_FILE, sprintf("%s mdtm %s.\n", UNAME, tmp) );
#endif
					socket_write( fd, sprintf("213 %s\n", tmp2) );
				}
			} else
				PERMISSION_DENIED550(command[ 1 ]);
			break;
		case "size": // return size of file
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			if ( check_valid_read( tmp, fd ) ) {
				if ( directory_exists( tmp ) )
					socket_write( fd, sprintf("550 %s: Not a plain file.\n",
						  command[ 1 ]) );
				else
					if ( !file_exists( tmp ) )
						socket_write( fd, sprintf("550 %s does not exist.\n",
							  command[ 1 ]) );
					else {
#ifdef LOG_TIME
						log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_CD_SIZE
						log_file( LOG_FILE, sprintf("%s size %s.\n",
							  UNAME, tmp) );
#endif
						socket_write( fd, sprintf("215 %d\n", file_size( tmp )) );
					}
			} else
				PERMISSION_DENIED550(command[ 1 ]);
			break;
		case "nlst": // give name list of files in directory
			CHECK_LOGIN();
			/* Send name list */
			if ((i = sizeof(command)) > 1 && command[1] == "-l") {
				if (i == 2)
					command[1] = ".";
				else
					command = ({ command[0] }) + command[2..s-1];
				// and fall through to "list"
			} else {
				/* Used by commands like "dir", "mget", and "mput" */
				if ( i > 1 )
					tmp = get_path( fd, command[ 1 ] );
				else
					tmp = socket_info[ fd ][ CWD ];

				if ( check_valid_read( tmp, fd ) ) {
					tmp2 = ls( tmp, 1, fd );
					if (tmp2 == "")
						socket_write( fd, "550 No files found.\n" );
					else
						data_conn( fd, tmp2, "ls", STRING );
				} else
					PERMISSION_DENIED550(tmp);
				break;
			}
		case "list": // give list files in a directory

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?