static void handle_dir_common(struct vsf_session* p_sess, int full_details) { static struct mystr s_option_str; static struct mystr s_filter_str; static struct mystr s_dir_name_str; static struct vsf_sysutil_statbuf* s_p_dirstat; int remote_fd; int dir_allow_read = 1; struct vsf_sysutil_dir* p_dir = 0; str_empty(&s_option_str); str_empty(&s_filter_str); /* By default open the current directory */ str_alloc_text(&s_dir_name_str, "."); if (!pasv_active(p_sess) && !port_active(p_sess)) { vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first."); return; } /* Do we have an option? Going to be strict here - the option must come * first. e.g. "ls -a .." fine, "ls .. -a" not fine */ if (!str_isempty(&p_sess->ftp_arg_str) && str_get_char_at(&p_sess->ftp_arg_str, 0) == '-') { /* Chop off the '-' */ str_mid_to_end(&p_sess->ftp_arg_str, &s_option_str, 1); /* A space will separate options from filter (if any) */ str_split_char(&s_option_str, &s_filter_str, ' '); } else { /* The argument, if any, is just a filter */ str_copy(&s_filter_str, &p_sess->ftp_arg_str); } if (!str_isempty(&s_filter_str)) { /* First check - is it an outright directory, as in "ls /pub" */ p_dir = str_opendir(&s_filter_str); if (p_dir != 0) { /* Listing a directory! */ str_copy(&s_dir_name_str, &s_filter_str); str_free(&s_filter_str); } else { struct str_locate_result locate_result = str_locate_char(&s_filter_str, '/'); if (locate_result.found) { /* Includes a path! Reverse scan for / in the arg, to get the * base directory and filter (if any) */ str_copy(&s_dir_name_str, &s_filter_str); str_split_char_reverse(&s_dir_name_str, &s_filter_str, '/'); /* If we have e.g. "ls /.message", we just ripped off the leading * slash because it is the only one! */ if (str_isempty(&s_dir_name_str)) { str_alloc_text(&s_dir_name_str, "/"); } } } } if (p_dir == 0) { /* NOTE - failure check done below, it's not forgotten */ p_dir = str_opendir(&s_dir_name_str); } /* Fine, do it */ remote_fd = get_remote_transfer_fd(p_sess); if (vsf_sysutil_retval_is_error(remote_fd)) { goto dir_close_out; } vsf_cmdio_write(p_sess, FTP_DATACONN, "Here comes the directory listing."); if (p_sess->is_anonymous && p_dir && tunable_anon_world_readable_only) { vsf_sysutil_dir_stat(p_dir, &s_p_dirstat); if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat)) { dir_allow_read = 0; } } if (p_dir == 0 || !dir_allow_read) { vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer done (but failed to open directory)."); } else { (void) vsf_ftpdataio_transfer_dir(p_sess, remote_fd, p_dir, &s_dir_name_str, &s_option_str, &s_filter_str, full_details); } (void) dispose_remote_transfer_fd(p_sess); dir_close_out: if (p_dir) { vsf_sysutil_closedir(p_dir); } port_cleanup(p_sess); pasv_cleanup(p_sess); }
static int transfer_dir_internal(struct vsf_session* p_sess, int is_control, struct vsf_sysutil_dir* p_dir, const struct mystr* p_base_dir_str, const struct mystr* p_option_str, const struct mystr* p_filter_str, int is_verbose) { struct mystr_list dir_list = INIT_STRLIST; struct mystr_list subdir_list = INIT_STRLIST; struct mystr dir_prefix_str = INIT_MYSTR; struct mystr_list* p_subdir_list = 0; struct str_locate_result loc_result = str_locate_char(p_option_str, 'R'); int failed = 0; enum EVSFRWTarget target = kVSFRWData; if (is_control) { target = kVSFRWControl; } if (loc_result.found && tunable_ls_recurse_enable) { p_subdir_list = &subdir_list; } vsf_ls_populate_dir_list(&dir_list, p_subdir_list, p_dir, p_base_dir_str, p_option_str, p_filter_str, is_verbose); if (p_subdir_list) { int retval; str_copy(&dir_prefix_str, p_base_dir_str); str_append_text(&dir_prefix_str, ":\r\n"); retval = ftp_write_str(p_sess, &dir_prefix_str, target); if (retval != 0) { failed = 1; } } if (!failed) { failed = write_dir_list(p_sess, &dir_list, target); } /* Recurse into the subdirectories if required... */ if (!failed) { struct mystr sub_str = INIT_MYSTR; unsigned int num_subdirs = str_list_get_length(&subdir_list); unsigned int subdir_index; for (subdir_index = 0; subdir_index < num_subdirs; subdir_index++) { int retval; struct vsf_sysutil_dir* p_subdir; const struct mystr* p_subdir_str = str_list_get_pstr(&subdir_list, subdir_index); if (str_equal_text(p_subdir_str, ".") || str_equal_text(p_subdir_str, "..")) { continue; } str_copy(&sub_str, p_base_dir_str); str_append_char(&sub_str, '/'); str_append_str(&sub_str, p_subdir_str); p_subdir = str_opendir(&sub_str); if (p_subdir == 0) { /* Unreadable, gone missing, etc. - no matter */ continue; } str_alloc_text(&dir_prefix_str, "\r\n"); retval = ftp_write_str(p_sess, &dir_prefix_str, target); if (retval != 0) { failed = 1; vsf_sysutil_closedir(p_subdir); break; } retval = transfer_dir_internal(p_sess, is_control, p_subdir, &sub_str, p_option_str, p_filter_str, is_verbose); vsf_sysutil_closedir(p_subdir); if (retval != 0) { failed = 1; break; } } str_free(&sub_str); } str_list_free(&dir_list); str_list_free(&subdir_list); str_free(&dir_prefix_str); if (!failed) { return 0; } else { return -1; } }