ls.c
上传用户:ig0539
上传日期:2022-05-21
资源大小:181k
文件大小:13k
源码类别:

Ftp客户端

开发平台:

C/C++

  1. /*
  2.  * Part of Very Secure FTPd
  3.  * Licence: GPL v2
  4.  * Author: Chris Evans
  5.  * ls.c
  6.  *
  7.  * Would you believe, code to handle directory listing.
  8.  */
  9. #include "ls.h"
  10. #include "access.h"
  11. #include "str.h"
  12. #include "strlist.h"
  13. #include "sysstr.h"
  14. #include "sysutil.h"
  15. #include "tunables.h"
  16. static void build_dir_line(struct mystr* p_str,
  17.                            const struct mystr* p_filename_str,
  18.                            const struct vsf_sysutil_statbuf* p_stat,
  19.                            long curr_time);
  20. void
  21. vsf_ls_populate_dir_list(struct mystr_list* p_list,
  22.                          struct mystr_list* p_subdir_list,
  23.                          struct vsf_sysutil_dir* p_dir,
  24.                          const struct mystr* p_base_dir_str,
  25.                          const struct mystr* p_option_str,
  26.                          const struct mystr* p_filter_str,
  27.                          int is_verbose)
  28. {
  29.   struct mystr dirline_str = INIT_MYSTR;
  30.   struct mystr normalised_base_dir_str = INIT_MYSTR;
  31.   struct str_locate_result loc_result;
  32.   int a_option;
  33.   int r_option;
  34.   int t_option;
  35.   int F_option;
  36.   int do_stat = 0;
  37.   long curr_time = 0;
  38.   loc_result = str_locate_char(p_option_str, 'a');
  39.   a_option = loc_result.found;
  40.   loc_result = str_locate_char(p_option_str, 'r');
  41.   r_option = loc_result.found;
  42.   loc_result = str_locate_char(p_option_str, 't');
  43.   t_option = loc_result.found;
  44.   loc_result = str_locate_char(p_option_str, 'F');
  45.   F_option = loc_result.found;
  46.   loc_result = str_locate_char(p_option_str, 'l');
  47.   if (loc_result.found)
  48.   {
  49.     is_verbose = 1;
  50.   }
  51.   /* Invert "reverse" arg for "-t", the time sorting */
  52.   if (t_option)
  53.   {
  54.     r_option = !r_option;
  55.   }
  56.   if (is_verbose || t_option || F_option || p_subdir_list != 0)
  57.   {
  58.     do_stat = 1;
  59.   }
  60.   /* If the filter starts with a . then implicitly enable -a */
  61.   if (!str_isempty(p_filter_str) && str_get_char_at(p_filter_str, 0) == '.')
  62.   {
  63.     a_option = 1;
  64.   }
  65.   /* "Normalise" the incoming base directory string by making sure it
  66.    * ends in a '/' if it is nonempty
  67.    */
  68.   if (!str_equal_text(p_base_dir_str, "."))
  69.   {
  70.     str_copy(&normalised_base_dir_str, p_base_dir_str);
  71.   }
  72.   if (!str_isempty(&normalised_base_dir_str))
  73.   {
  74.     unsigned int len = str_getlen(&normalised_base_dir_str);
  75.     if (str_get_char_at(&normalised_base_dir_str, len - 1) != '/')
  76.     {
  77.       str_append_char(&normalised_base_dir_str, '/');
  78.     }
  79.   }
  80.   /* If we're going to need to do time comparisions, cache the local time */
  81.   if (is_verbose)
  82.   {
  83.     curr_time = vsf_sysutil_get_time_sec();
  84.   }
  85.   while (1)
  86.   {
  87.     int len;
  88.     static struct mystr s_next_filename_str;
  89.     static struct mystr s_next_path_and_filename_str;
  90.     static struct vsf_sysutil_statbuf* s_p_statbuf;
  91.     str_next_dirent(&s_next_filename_str, p_dir);
  92.     if (str_isempty(&s_next_filename_str))
  93.     {
  94.       break;
  95.     }
  96.     len = str_getlen(&s_next_filename_str);
  97.     if (len > 0 && str_get_char_at(&s_next_filename_str, 0) == '.')
  98.     {
  99.       if (!a_option && !tunable_force_dot_files)
  100.       {
  101.         continue;
  102.       }
  103.       if (!a_option &&
  104.           ((len == 2 && str_get_char_at(&s_next_filename_str, 1) == '.') ||
  105.            len == 1))
  106.       {
  107.         continue;
  108.       }
  109.     }
  110.     /* Don't show hidden directory entries */
  111.     if (!vsf_access_check_file_visible(&s_next_filename_str))
  112.     {
  113.       continue;
  114.     }
  115.     /* If we have an ls option which is a filter, apply it */
  116.     if (!str_isempty(p_filter_str))
  117.     {
  118.       if (!vsf_filename_passes_filter(&s_next_filename_str, p_filter_str))
  119.       {
  120.         continue;
  121.       }
  122.     }
  123.     /* Calculate the full path (relative to CWD) for lstat() and
  124.      * output purposes
  125.      */
  126.     str_copy(&s_next_path_and_filename_str, &normalised_base_dir_str);
  127.     str_append_str(&s_next_path_and_filename_str, &s_next_filename_str);
  128.     if (do_stat)
  129.     {
  130.       /* lstat() the file. Of course there's a race condition - the
  131.        * directory entry may have gone away whilst we read it, so
  132.        * ignore failure to stat
  133.        */
  134.       int retval = str_lstat(&s_next_path_and_filename_str, &s_p_statbuf);
  135.       if (vsf_sysutil_retval_is_error(retval))
  136.       {
  137.         continue;
  138.       }
  139.     }
  140.     if (is_verbose)
  141.     {
  142.       static struct mystr s_final_file_str;
  143.       /* If it's a damn symlink, we need to append the target */
  144.       str_copy(&s_final_file_str, &s_next_filename_str);
  145.       if (vsf_sysutil_statbuf_is_symlink(s_p_statbuf))
  146.       {
  147.         static struct mystr s_temp_str;
  148.         int retval = str_readlink(&s_temp_str, &s_next_path_and_filename_str);
  149.         if (retval == 0 && !str_isempty(&s_temp_str))
  150.         {
  151.           str_append_text(&s_final_file_str, " -> ");
  152.           str_append_str(&s_final_file_str, &s_temp_str);
  153.         }
  154.       }
  155.       if (F_option && vsf_sysutil_statbuf_is_dir(s_p_statbuf))
  156.       {
  157.         str_append_char(&s_final_file_str, '/');
  158.       }
  159.       build_dir_line(&dirline_str, &s_final_file_str, s_p_statbuf, curr_time);
  160.     }
  161.     else
  162.     {
  163.       /* Just emit the filenames - note, we prepend the directory for NLST
  164.        * but not for LIST
  165.        */
  166.       str_copy(&dirline_str, &s_next_path_and_filename_str);
  167.       if (F_option)
  168.       {
  169.         if (vsf_sysutil_statbuf_is_dir(s_p_statbuf))
  170.         {
  171.           str_append_char(&dirline_str, '/');
  172.         }
  173.         else if (vsf_sysutil_statbuf_is_symlink(s_p_statbuf))
  174.         {
  175.           str_append_char(&dirline_str, '@');
  176.         }
  177.       }
  178.       str_append_text(&dirline_str, "rn");
  179.     }
  180.     /* Add filename into our sorted list - sorting by filename or time. Also,
  181.      * if we are required to, maintain a distinct list of direct
  182.      * subdirectories.
  183.      */
  184.     {
  185.       static struct mystr s_temp_str;
  186.       const struct mystr* p_sort_str = 0;
  187.       const struct mystr* p_sort_subdir_str = 0;
  188.       if (!t_option)
  189.       {
  190.         p_sort_str = &s_next_filename_str;
  191.       }
  192.       else
  193.       {
  194.         str_alloc_text(&s_temp_str,
  195.                        vsf_sysutil_statbuf_get_sortkey_mtime(s_p_statbuf));
  196.         p_sort_str = &s_temp_str;
  197.         p_sort_subdir_str = &s_temp_str;
  198.       }
  199.       str_list_add(p_list, &dirline_str, p_sort_str);
  200.       if (p_subdir_list != 0 && vsf_sysutil_statbuf_is_dir(s_p_statbuf))
  201.       {
  202.         str_list_add(p_subdir_list, &s_next_filename_str, p_sort_subdir_str);
  203.       }
  204.     }
  205.   } /* END: while(1) */
  206.   str_list_sort(p_list, r_option);
  207.   if (p_subdir_list != 0)
  208.   {
  209.     str_list_sort(p_subdir_list, r_option);
  210.   }
  211.   str_free(&dirline_str);
  212.   str_free(&normalised_base_dir_str);
  213. }
  214. int
  215. vsf_filename_passes_filter(const struct mystr* p_filename_str,
  216.                            const struct mystr* p_filter_str)
  217. {
  218.   /* A simple routine to match a filename against a pattern.
  219.    * This routine is used instead of e.g. fnmatch(3), because we should be
  220.    * reluctant to trust the latter. fnmatch(3) involves _lots_ of string
  221.    * parsing and handling. There is broad potential for any given fnmatch(3)
  222.    * implementation to be buggy.
  223.    *
  224.    * Currently supported pattern(s):
  225.    * - any number of wildcards, "*" or "?"
  226.    * - {,} syntax (not nested)
  227.    *
  228.    * Note that pattern matching is only supported within the last path
  229.    * component. For example, searching for /a/b/? will work, but searching
  230.    * for /a/?/c will not.
  231.    */
  232.   struct mystr filter_remain_str = INIT_MYSTR;
  233.   struct mystr name_remain_str = INIT_MYSTR;
  234.   struct mystr temp_str = INIT_MYSTR;
  235.   struct mystr brace_list_str = INIT_MYSTR;
  236.   struct mystr new_filter_str = INIT_MYSTR;
  237.   int ret = 0;
  238.   char last_token = 0;
  239.   int must_match_at_current_pos = 1;
  240.   str_copy(&filter_remain_str, p_filter_str);
  241.   str_copy(&name_remain_str, p_filename_str);
  242.   while (!str_isempty(&filter_remain_str))
  243.   {
  244.     static struct mystr s_match_needed_str;
  245.     /* Locate next special token */
  246.     struct str_locate_result locate_result =
  247.       str_locate_chars(&filter_remain_str, "*?{");
  248.     /* Isolate text leading up to token (if any) - needs to be matched */
  249.     if (locate_result.found)
  250.     {
  251.       unsigned int indexx = locate_result.index;
  252.       str_left(&filter_remain_str, &s_match_needed_str, indexx);
  253.       str_mid_to_end(&filter_remain_str, &temp_str, indexx + 1);
  254.       str_copy(&filter_remain_str, &temp_str);
  255.       last_token = locate_result.char_found;
  256.     }
  257.     else
  258.     {
  259.       /* No more tokens. Must match remaining filter string exactly. */
  260.       str_copy(&s_match_needed_str, &filter_remain_str);
  261.       str_empty(&filter_remain_str);
  262.       last_token = 0;
  263.     }
  264.     if (!str_isempty(&s_match_needed_str))
  265.     {
  266.       /* Need to match something.. could be a match which has to start at
  267.        * current position, or we could allow it to start anywhere
  268.        */
  269.       unsigned int indexx;
  270.       locate_result = str_locate_str(&name_remain_str, &s_match_needed_str);
  271.       if (!locate_result.found)
  272.       {
  273.         /* Fail */
  274.         goto out;
  275.       }
  276.       indexx = locate_result.index;
  277.       if (must_match_at_current_pos && indexx > 0)
  278.       {
  279.         goto out;
  280.       }
  281.       /* Chop matched string out of remainder */
  282.       str_mid_to_end(&name_remain_str, &temp_str,
  283.                      indexx + str_getlen(&s_match_needed_str));
  284.       str_copy(&name_remain_str, &temp_str);
  285.     }
  286.     if (last_token == '?')
  287.     {
  288.       if (str_isempty(&name_remain_str))
  289.       {
  290.         goto out;
  291.       }
  292.       str_right(&name_remain_str, &temp_str, str_getlen(&name_remain_str) - 1);
  293.       str_copy(&name_remain_str, &temp_str);
  294.       must_match_at_current_pos = 1;
  295.     }
  296.     else if (last_token == '{')
  297.     {
  298.       struct str_locate_result end_brace =
  299.         str_locate_char(&filter_remain_str, '}');
  300.       must_match_at_current_pos = 1;
  301.       if (end_brace.found)
  302.       {
  303.         str_split_char(&filter_remain_str, &temp_str, '}');
  304.         str_copy(&brace_list_str, &filter_remain_str);
  305.         str_copy(&filter_remain_str, &temp_str);
  306.         str_split_char(&brace_list_str, &temp_str, ',');
  307.         while (!str_isempty(&brace_list_str))
  308.         {
  309.           str_copy(&new_filter_str, &brace_list_str);
  310.           str_append_str(&new_filter_str, &filter_remain_str);
  311.           if (vsf_filename_passes_filter(&name_remain_str, &new_filter_str))
  312.           {
  313.             ret = 1;
  314.             goto out;
  315.           }
  316.           str_copy(&brace_list_str, &temp_str);
  317.           str_split_char(&brace_list_str, &temp_str, ',');
  318.         }
  319.         goto out;
  320.       }
  321.       else if (str_isempty(&name_remain_str) ||
  322.                str_get_char_at(&name_remain_str, 0) != '{')
  323.       {
  324.         goto out;
  325.       }
  326.       else
  327.       {
  328.         str_right(&name_remain_str, &temp_str,
  329.                   str_getlen(&name_remain_str) - 1);
  330.         str_copy(&name_remain_str, &temp_str);
  331.       }
  332.     }
  333.     else
  334.     {
  335.       must_match_at_current_pos = 0;
  336.     }
  337.   }
  338.   /* Any incoming string left means no match unless we ended on the correct
  339.    * type of wildcard.
  340.    */
  341.   if (str_getlen(&name_remain_str) > 0 && last_token != '*')
  342.   {
  343.     goto out;
  344.   }
  345.   /* OK, a match */
  346.   ret = 1;
  347. out:
  348.   str_free(&filter_remain_str);
  349.   str_free(&name_remain_str);
  350.   str_free(&temp_str);
  351.   str_free(&brace_list_str);
  352.   str_free(&new_filter_str);
  353.   return ret;
  354. }
  355. static void
  356. build_dir_line(struct mystr* p_str, const struct mystr* p_filename_str,
  357.                const struct vsf_sysutil_statbuf* p_stat, long curr_time)
  358. {
  359.   static struct mystr s_tmp_str;
  360.   filesize_t size = vsf_sysutil_statbuf_get_size(p_stat);
  361.   /* Permissions */
  362.   str_alloc_text(p_str, vsf_sysutil_statbuf_get_perms(p_stat));
  363.   str_append_char(p_str, ' ');
  364.   /* Hard link count */
  365.   str_alloc_ulong(&s_tmp_str, vsf_sysutil_statbuf_get_links(p_stat));
  366.   str_lpad(&s_tmp_str, 4);
  367.   str_append_str(p_str, &s_tmp_str);
  368.   str_append_char(p_str, ' ');
  369.   /* User */
  370.   if (tunable_hide_ids)
  371.   {
  372.     str_alloc_text(&s_tmp_str, "ftp");
  373.   }
  374.   else
  375.   {
  376.     int uid = vsf_sysutil_statbuf_get_uid(p_stat);
  377.     struct vsf_sysutil_user* p_user = 0;
  378.     if (tunable_text_userdb_names)
  379.     {
  380.       p_user = vsf_sysutil_getpwuid(uid);
  381.     }
  382.     if (p_user == 0)
  383.     {
  384.       str_alloc_ulong(&s_tmp_str, (unsigned long) uid);
  385.     }
  386.     else
  387.     {
  388.       str_alloc_text(&s_tmp_str, vsf_sysutil_user_getname(p_user));
  389.     }
  390.   }
  391.   str_rpad(&s_tmp_str, 8);
  392.   str_append_str(p_str, &s_tmp_str);
  393.   str_append_char(p_str, ' ');
  394.   /* Group */
  395.   if (tunable_hide_ids)
  396.   {
  397.     str_alloc_text(&s_tmp_str, "ftp");
  398.   }
  399.   else
  400.   {
  401.     int gid = vsf_sysutil_statbuf_get_gid(p_stat);
  402.     struct vsf_sysutil_group* p_group = 0;
  403.     if (tunable_text_userdb_names)
  404.     {
  405.       p_group = vsf_sysutil_getgrgid(gid);
  406.     }
  407.     if (p_group == 0)
  408.     {
  409.       str_alloc_ulong(&s_tmp_str, (unsigned long) gid);
  410.     }
  411.     else
  412.     {
  413.       str_alloc_text(&s_tmp_str, vsf_sysutil_group_getname(p_group));
  414.     }
  415.   }
  416.   str_rpad(&s_tmp_str, 8);
  417.   str_append_str(p_str, &s_tmp_str);
  418.   str_append_char(p_str, ' ');
  419.   /* Size in bytes */
  420.   str_alloc_filesize_t(&s_tmp_str, size);
  421.   str_lpad(&s_tmp_str, 8);
  422.   str_append_str(p_str, &s_tmp_str);
  423.   str_append_char(p_str, ' ');
  424.   /* Date stamp */
  425.   str_append_text(p_str, vsf_sysutil_statbuf_get_date(p_stat,
  426.                                                       tunable_use_localtime,
  427.                                                       curr_time));
  428.   str_append_char(p_str, ' ');
  429.   /* Filename */
  430.   str_append_str(p_str, p_filename_str);
  431.   str_append_text(p_str, "rn");
  432. }