ftpd.c

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

C
1,581
字号
			CHECK_LOGIN();
			/* Send directory file list (like exec'ing "ls") */
			if ( sizeof( command ) > 1 )
				tmp = get_path( fd, command[ 1 ] );
			else
				tmp = socket_info[ fd ][ CWD ];
			if ( check_valid_read( tmp, fd ) ) {
				tmp2 = ls( tmp, 0, fd );
				if (tmp2 == "")
					socket_write( fd, "550 No files found.\n");
				else
					data_conn( fd, tmp2, "ls", STRING );
			} else
				PERMISSION_DENIED550(tmp);
			break;
		case "xpwd": // print the current working directory (deprecated)
		case "pwd":  // print the current working directory
			CHECK_LOGIN();
			socket_write( fd, sprintf("257 \"%s\" is the current directory.\n",
				  socket_info[ fd ][ CWD ]) );
			break;
		case "xcup": // change to parent of current working directory (deprecated)
		case "cdup": // change to parent of current working directory
			if (sizeof(command) > 1)
				command[ 1 ] = "..";
			else
				command += ({ ".." });
		case "xcwd": // change working directory (deprecated)
		case "cwd":  // change working directory
			CHECK_LOGIN();
			if ( sizeof( command ) > 1 )
				tmp = get_path( fd, command[ 1 ] );
			else
				tmp = socket_info[ fd ][ CWD ];
			if ( check_valid_read( tmp, fd ) ) {
				tmp = get_path( fd, tmp );

				if ( !directory_exists( tmp ) ) {
					socket_write( fd, sprintf("550 %s: Not a directory.\n", tmp) );
					break;
				}
#ifdef LOG_TIME
				log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_CD_SIZE
				log_file( LOG_FILE, sprintf("%s cd to %s.\n", UNAME, tmp) );
#endif
				socket_info[ fd ][ CWD ] = tmp;
#ifdef MESSAGE_FILES
				if (file_size(tmp + "/.message") > 0) {
					tmp = read_file(tmp + "/.message");
					tmp = tmp[0..<2];
					tmp = sprintf("250- %s\n250 %s command successful.\n",
						  replace_string(tmp, "\n", "\n250- "), command[0]);
					socket_write(fd, tmp);
				} else
#endif
				socket_write( fd, sprintf("250 %s command successful.\n",
					  command[0]) );
			} else
				PERMISSION_DENIED550(command[ 1 ]);
			break;
		case "quit": // terminate session
			socket_write( fd, "221 Goodbye.\n" );
			logout( fd );
			lose_user( fd );
			break;
		case "type": // specify data transfer type
			CHECK_CMD(1);
			if ( command[ 1 ] == "I" ) {
				socket_info[ fd ][ TYPE ] = BINARY;
				socket_write( fd, "200 Type set to I.\n" );
			} else
				if ( command[ 1 ] == "A" ) {
					socket_info[ fd ][ TYPE ] = STRING;
					socket_write( fd, "200 Type set to A.\n" );
				} else
					socket_write( fd, sprintf("504 Type %s not implemented.\n",
						  command[ 1 ]) );
			break;
		case "stat": // return status of server
			socket_write( fd, sprintf(
				  "211 FTP server status: %s FTP Version %s\r\n",
				  THE_MUD_NAME, FTPD_VERSION) );
			socket_write( fd, sprintf("	 Connected to %s\r\n", USITE) );
			if ( socket_info[ fd ][ LOGGED_IN ] )
				socket_write( fd, sprintf("	Logged in as %s\r\n", UNAME) );
			else
				if ( UNAME )
					socket_write( fd, "	Waiting for password.\r\n" );
				else
					socket_write( fd, "	Waiting for user name.\r\n" );
			socket_write( fd, "	TYPE: ASCII, FORM: Nonprint; STRUcture: "
							  "File; transfer MODE: Stream.\r\n" );
			if ( socket_info[ fd ][ DATA_FD ] )
				socket_write( fd, "	Data connection open.\r\n" );
			else
				if ( socket_info[ fd ][ DATA_ADDR ] )
					socket_write( fd, sprintf("	%s %d\r\n",
						  socket_info[ fd ][ DATA_ADDR ],
						  socket_info[ fd ][ DATA_PORT ]) );
				else
					socket_write( fd, "	No data connection.\r\n" );
			socket_write( fd, "211 End of status.\n" );
			break;
		case "abor": // abort previous command
			/* Stops recvs and stors. At least thats what it is supposed to do. */
			if ( socket_info[ fd ][ DATA_FD ] ) {
				socket_write( fd,
					  "426 Transfer aborted. Data connection closed.\n");
#ifdef FILE_LOCKING
				if ( (socket_info[ fd ][ DATA_FD ])[ TYPE ] == DOWNLOAD )
					flock_wrapper( (socket_info[ fd ][ DATA_FD ])[ PATH ],
						  F_UNLOCK, socket_info[ fd ] );
#endif
				socket_close( socket_info[ fd ][ DATA_FD ] );
				map_delete( socket_info[fd], DATA_FD );
			}
			socket_write( fd, "226 Abort successful.\n" );
			break;
		case "syst": // show operating system type of server system
			socket_write( fd, "215 UNIX Type: MUD\n" );
			break;
		case "xmkd": // make a directory (deprecated)
		case "mkd":  // make a directory
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			if (file_size( tmp ) == -1) {
				if (check_valid_write(tmp, fd)) {
					if (mkdir(tmp)) {
#ifdef LOG_TIME
						log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_FILE
						log_file( LOG_FILE, sprintf("%s mkd %s.\n",
							  UNAME, tmp) );
#endif
						socket_write(fd, sprintf("257 %s command successful.\n",
							  command[ 0 ]) );
					} else
						socket_write(fd, sprintf("550 %s command failed.\n",
							  command[ 0 ]) );
				} else
					PERMISSION_DENIED550(command[ 1 ]);
			} else
				socket_write(fd, sprintf(
					  "550 %s: Directory or file already exists.\n",
					  command[ 1 ]));
			break;
		case "xrmd": // remove a directory (deprecated)
		case "rmd":  // remove a directory
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[ 1 ] );
			if (file_size( tmp ) == -2) {
				if (check_valid_write(tmp, fd)) {
					if (rmdir(tmp)) {
#ifdef LOG_TIME
						log_file( LOG_FILE, FTP_TIME );
#endif

#ifdef LOG_FILE
						log_file( LOG_FILE, sprintf("%s rmd %s.\n",
							  UNAME, tmp) );
#endif
						socket_write(fd, sprintf("250 %s command successful.\n",
							  command[ 0 ]) );
					} else
						socket_write(fd, sprintf("550 %s command failed.\n",
							  command[ 0 ]) );
				} else
					PERMISSION_DENIED550(command[ 1 ]);
			} else
				socket_write(fd, sprintf("550 %s: No such file or directory.\n",
					  command[ 1 ]) );
			break;
		case "site": // non-standard commands
			CHECK_LOGIN();
			CHECK_CMD(1);
			switch (lower_case(command[ 1 ])) {
				case "idle":
					if (sizeof(command) < 3) {
						socket_write(fd,
							  sprintf("200 Current IDLE time limit is %d seconds; max %d\n",
							  socket_info[fd][IDLE_SETTING], MAX_FTPD_IDLE));
						break;
					}

					if (!sscanf(command[2], "%d", i)) {
						socket_write(fd, "550 SITE IDLE command failed.\n");
						break;
					}

					i = (i < FTPD_TIMEOUT ? FTPD_TIMEOUT : (i > MAX_FTPD_IDLE ? MAX_FTPD_IDLE : i));
					socket_info[fd][IDLE_SETTING] = i;
					socket_write(fd, sprintf("200 Maximum IDLE time set to %d seconds\n", i));
					break;
				case "time":
					socket_write(fd,
						  sprintf("200 Local TIME is %s.\n", ctime(time())[4..15] ) );
					break;
				case "help":
					if (sizeof(command) > 1) {
						tmp = lower_case(command[1]);
						if (!undefinedp(sitecmdtab[ tmp ])) {
							misc = sitecmdtab[ tmp ];
							if (misc[1])
								socket_write(fd, sprintf("214 Syntax: %s %s.\n",
									  misc[0], misc[2]) );
							else
								socket_write(fd, sprintf("214 %s %s; unimplemented.\n",
									  misc[0], misc[2]) );
						} else {
							socket_write(fd, sprintf("502 Unknown command %s.\n",
								  command[ 1 ]) );
						}
					} else {
						socket_write(fd, "214- The following SITE commands are recognized "
							  "(* =>'s unimplemented).\n214-\n");
						misc = keys(sitecmdtab);
						s = sizeof(misc);
						tmp = "214- ";
						for (i = 0; i < s; i++) {
							tmp += sprintf("%c%-7s", sitecmdtab[misc[i]][1] ? ' ' : '*',
									   sitecmdtab[misc[i]][0]);
							if (i % 8 == 7) {
								socket_write(fd, tmp + "\n");
								tmp = "214- ";
							}
						}
						if (i % 8)
							socket_write(fd, tmp + "\n");
						socket_write(fd, sprintf("214-\n214 Direct comments to %s.\n",
							  FTP_BUGS_EMAIL));
					}
					break;
				case "chmod":
				case "umask":
					socket_write( fd, sprintf("502 '%s' command not implemented.\n",
						  command[ 0 ]) );
					break;
				default:
					socket_write( fd, sprintf("500 '%s %s' command "
						  "not understood.\n", command[ 0 ], command[ 1 ]) );
					break;
			}
			break;
		case "rnfr": // specify rename-from file name
			CHECK_LOGIN();
			CHECK_CMD(1);
			tmp = get_path( fd, command[1] );
			i = file_size( tmp );
			if (i == -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)) {
				socket_info[fd][FROMNAME] = tmp;
				socket_write(fd, "350 File exists, ready for destination name.\n");
			} else {
				PERMISSION_DENIED550(command[ 1 ]);
			}
			break;
		case "rnto": // specify rename-to file name
			CHECK_LOGIN();
			CHECK_CMD(1);
			if (!socket_info[fd][FROMNAME]) {
				socket_write(fd, "503 Bad sequence of commands.\n");
			} else {
				tmp = get_path( fd, command[1] );
				i = file_size( tmp );
				if (i != -1) {
					socket_write(fd, sprintf(
						  "550 %s: Directory or file already exists.\n",
						  command[ 1 ]) );
				} else if (!check_valid_write(tmp, fd)) {
					PERMISSION_DENIED550(command[ 1 ]);
				} else {
					tmp2 = socket_info[fd][FROMNAME];
					if (!check_valid_write(tmp2, fd)) {
						PERMISSION_DENIED550(tmp2);
						socket_info[fd][FROMNAME] = 0;
					} else {
#ifdef FILE_LOCKING
						if (flock_wrapper(tmp, F_LOCK, fd)) {
							if (flock_wrapper(tmp2, F_LOCK, fd)) {
#endif

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

#ifdef LOG_FILE
								log_file( LOG_FILE, sprintf("%s rnfr %s "
									  "rnto %s.\n", UNAME, tmp2, tmp) );
#endif
								if (catch( i = rename(tmp2, tmp) ) || i)
									socket_write(fd,
										  "550 RNTO command failed.\n");
								else
									socket_write(fd,
										  "250 RNTO command successful.\n");
#ifdef FILE_LOCKING
								flock_wrapper(tmp2, F_UNLOCK, fd);
								flock_wrapper(tmp, F_UNLOCK, fd);
								socket_info[fd][FROMNAME] = 0;
							} else {
								PERMISSION_DENIED550(tmp2);
								flock_wrapper(tmp, F_UNLOCK, fd);
							}
						} else {
							PERMISSION_DENIED550(command[ 1 ]);
						}
#endif
					}
				}
			}
			break;
		case "help": // give help information
			if (sizeof(command) > 1) {
				tmp = lower_case(command[1]);
				if (!undefinedp(cmdtab[ tmp ])) {
					misc = cmdtab[ tmp ];
					if (misc[1])
						socket_write(fd, sprintf("214 Syntax: %s %s.\n",
							  misc[0], misc[2]) );
					else
						socket_write(fd, sprintf("214 %s %s; unimplemented.\n",
							  misc[0], misc[2]) );
				} else {
					socket_write(fd, sprintf("502 Unknown command %s.\n",
						  command[ 1 ]) );
				}
			} else {
				socket_write(fd, "214- The following commands are recognized "
					  "(* =>'s unimplemented).\n214-\n");
				misc = keys(cmdtab);
				s = sizeof(misc);
				tmp = "214- ";
				for (i = 0; i < s; i++) {
					tmp += sprintf("%c%-7s", cmdtab[misc[i]][1] ? ' ' : '*',
							   cmdtab[misc[i]][0]);
					if (i % 8 == 7) {
						socket_write(fd, tmp + "\n");
						tmp = "214- ";
					}
				}
				if (i % 8)
					socket_write(fd, tmp + "\n");
				socket_write(fd, sprintf("214-\n214 Direct comments to %s.\n",
					  FTP_BUGS_EMAIL));
			}
			break;
		case "rest": // restart command/incomplete transfer
			if (sizeof(command) > 1) {
				rest_pos = atoi(command[1]);
				socket_write( fd, sprintf("350 Restarting at %d.\n", rest_pos) );
			} else
				socket_write( fd, "214 command without parameter.\n" );
			break;			
		case "acct": // specify account (ignored)
		case "mail": // mail to user
		case "mlfl": // mail file
		case "mode": // specify data transfer mode
		case "mrcp": // mail recipient
		case "mrsq": // mail recipient scheme question
		case "msnd": // mail send to terminal
		case "msam": // mail send to terminal and mailbox
		case "msom": // mail send to terminal or mailbox
		case "pasv": // prepare for server-to-server transfer
		case "rein": // reinitialize server state
		case "smnt": // structure mount
		case "stru": // specify data transfer/file structure
			socket_write( fd, sprintf("502 '%s' command not implemented.\n",
				  command[ 0 ]) );
			break;

		default:
			socket_write( fd, sprintf("500 '%s': command not understood.\n",
				  command[ 0 ]) );
			break;
	}
} /* parse_com() */


/*
 * accept user command
 */
void in_read_callback( int fd, string str )
{
	string *command;
	int i, j;

	if (undefinedp( socket_info[ fd ][ CONNECTED ] )
	||	!socket_info[ fd ][ CONNECTED ] )
		return;

	if (!str)  str = "";
	str = replace_string( str, "\r", "" );
	command = explode( str, "\n" ) - ({ 0 });
	j = sizeof(command);

	for ( i = 0; i < j; i++ )
		parse_comm( fd, command[i] );
}


/*
 * Handle when the socket closes unexpectedly
 */
void in_close_callback( int fd )
{
	/*
	 * Handle case where user aborts (eg CTRL-C) their ftp client, causing
	 * connection to close before getting to login prompt.
	 */
	if (undefinedp(socket_info[fd][CONNECTED])) return;

	logout( fd );
	lose_user( fd );
}

/*
 * resolve path (absolute vs relative) ... does not validate path here
 */
protected string get_path( int fd, string str )
{
	string apath;

	apath = resolve_path(socket_info[fd][CWD], str);

#ifdef ANONYMOUS_FTP
	if( UNAME == "anonymous" &&	strsrch(apath, FTP_DIR)!=0 )
		apath = FTP_DIR + apath;
#endif

	return apath;
}


protected void check_connections()
{
	int *sockets, i, limit;
	int pfd;

	sockets = keys( socket_info );
	i = sizeof( sockets );

	/*
	 * check control connections for idle users
	 */
	while ( i-- ) {
		if ( !undefinedp(socket_info[ sockets[i]][LAST_DATA]) ) {
			limit = time() - socket_info[ sockets[i]][IDLE_SETTING];
			if ( socket_info[ sockets[i]][LAST_DATA] < limit ) {
				socket_write( sockets[i],
					  "421 Timeout: closing control session.\n" );
				logout( sockets[i] );
				lose_user( sockets[i] );
			}
		}
	}

	sockets = keys( socket_info );
	i = sizeof( sockets );

	/*
	 * check data connections for orphans
	 */
	while ( i-- ) {
		if (!undefinedp( socket_info[ sockets[i] ][ PARENT_FD ] )) {
			pfd = socket_info[ sockets[i] ][ PARENT_FD ];
			if (undefinedp(socket_info[ pfd ])) {
#ifdef FILE_LOCKING
				if (!undefinedp(socket_info[ sockets[i] ][ TYPE ]) &&
					  socket_info[ sockets[i] ][ TYPE ] == DOWNLOAD ) {
					flock_wrapper( socket_info[ sockets[i] ][ PATH ], F_UNLOCK,
						  sockets[i] );
				}
#endif
				lose_connection( sockets[i] );
			}
		}
	}
	call_out( "check_connections", 3 * 60 );
}


void resolve_callback( string address, string resolved, int key )
{
	int id;

	/*
	 * An example where these two errors can occur:
	 *   update the ftpd daemon while a user is logging in
	 */
	if ( undefinedp( id = temp_map[ key ] ) ) {
#ifdef LOG_FILE
		log_file( LOG_FILE, sprintf("No such key in temp_map: %d.\n", key) )
#endif
		;
	} else if ( undefinedp( socket_info[ id ] )) {
#ifdef LOG_FILE
		log_file( LOG_FILE, sprintf("No such fd in socket_info: %d.\n", id) )
#endif
		;
	} else {
		if ( address && address != "" )
			socket_info[id][USER_SITE] = address;
		// else retain unresolved ip number address
	}
}

void remove()
{
	if (number_of_users
	&&	this_player(1)
	&&	wizhood(this_player()) != "(admin)" )
	  error( "Cannot destruct while there are active ftp sessions.\n" );
}

⌨️ 快捷键说明

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