Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
int
vsf_filename_passes_filter(const struct mystr* p_filename_str,
                           const struct mystr* p_filter_str,
                           unsigned int* iters)
{
    /* A simple routine to match a filename against a pattern.
     * This routine is used instead of e.g. fnmatch(3), because we should be
     * reluctant to trust the latter. fnmatch(3) involves _lots_ of string
     * parsing and handling. There is broad potential for any given fnmatch(3)
     * implementation to be buggy.
     *
     * Currently supported pattern(s):
     * - any number of wildcards, "*" or "?"
     * - {,} syntax (not nested)
     *
     * Note that pattern matching is only supported within the last path
     * component. For example, searching for /a/b/? will work, but searching
     * for /a/?/c will not.
     */
    struct mystr filter_remain_str = INIT_MYSTR;
    struct mystr name_remain_str = INIT_MYSTR;
    struct mystr temp_str = INIT_MYSTR;
    struct mystr brace_list_str = INIT_MYSTR;
    struct mystr new_filter_str = INIT_MYSTR;
    int ret = 0;
    char last_token = 0;
    int must_match_at_current_pos = 1;
    str_copy(&filter_remain_str, p_filter_str);
    str_copy(&name_remain_str, p_filename_str);

    while (!str_isempty(&filter_remain_str) && *iters < VSFTP_MATCHITERS_MAX)
    {
        static struct mystr s_match_needed_str;
        /* Locate next special token */
        struct str_locate_result locate_result =
            str_locate_chars(&filter_remain_str, "*?{");
        (*iters)++;
        /* Isolate text leading up to token (if any) - needs to be matched */
        if (locate_result.found)
        {
            unsigned int indexx = locate_result.index;
            str_left(&filter_remain_str, &s_match_needed_str, indexx);
            str_mid_to_end(&filter_remain_str, &temp_str, indexx + 1);
            str_copy(&filter_remain_str, &temp_str);
            last_token = locate_result.char_found;
        }
        else
        {
            /* No more tokens. Must match remaining filter string exactly. */
            str_copy(&s_match_needed_str, &filter_remain_str);
            str_empty(&filter_remain_str);
            last_token = 0;
        }
        if (!str_isempty(&s_match_needed_str))
        {
            /* Need to match something.. could be a match which has to start at
             * current position, or we could allow it to start anywhere
             */
            unsigned int indexx;
            locate_result = str_locate_str(&name_remain_str, &s_match_needed_str);
            if (!locate_result.found)
            {
                /* Fail */
                goto out;
            }
            indexx = locate_result.index;
            if (must_match_at_current_pos && indexx > 0)
            {
                goto out;
            }
            /* Chop matched string out of remainder */
            str_mid_to_end(&name_remain_str, &temp_str,
                           indexx + str_getlen(&s_match_needed_str));
            str_copy(&name_remain_str, &temp_str);
        }
        if (last_token == '?')
        {
            if (str_isempty(&name_remain_str))
            {
                goto out;
            }
            str_right(&name_remain_str, &temp_str, str_getlen(&name_remain_str) - 1);
            str_copy(&name_remain_str, &temp_str);
            must_match_at_current_pos = 1;
        }
        else if (last_token == '{')
        {
            struct str_locate_result end_brace =
                str_locate_char(&filter_remain_str, '}');
            must_match_at_current_pos = 1;
            if (end_brace.found)
            {
                str_split_char(&filter_remain_str, &temp_str, '}');
                str_copy(&brace_list_str, &filter_remain_str);
                str_copy(&filter_remain_str, &temp_str);
                str_split_char(&brace_list_str, &temp_str, ',');
                while (!str_isempty(&brace_list_str))
                {
                    str_copy(&new_filter_str, &brace_list_str);
                    str_append_str(&new_filter_str, &filter_remain_str);
                    if (vsf_filename_passes_filter(&name_remain_str, &new_filter_str,
                                                   iters))
                    {
                        ret = 1;
                        goto out;
                    }
                    str_copy(&brace_list_str, &temp_str);
                    str_split_char(&brace_list_str, &temp_str, ',');
                }
                goto out;
            }
            else if (str_isempty(&name_remain_str) ||
                     str_get_char_at(&name_remain_str, 0) != '{')
            {
                goto out;
            }
            else
            {
                str_right(&name_remain_str, &temp_str,
                          str_getlen(&name_remain_str) - 1);
                str_copy(&name_remain_str, &temp_str);
            }
        }
        else
        {
            must_match_at_current_pos = 0;
        }
    }
    /* Any incoming string left means no match unless we ended on the correct
     * type of wildcard.
     */
    if (str_getlen(&name_remain_str) > 0 && last_token != '*')
    {
        goto out;
    }
    /* OK, a match */
    ret = 1;
    if (*iters == VSFTP_MATCHITERS_MAX) {
        ret = 0;
    }
out:
    str_free(&filter_remain_str);
    str_free(&name_remain_str);
    str_free(&temp_str);
    str_free(&brace_list_str);
    str_free(&new_filter_str);
    return ret;
}