mod_ls.c
上传用户:pycemail
上传日期:2007-01-04
资源大小:329k
文件大小:31k
源码类别:

Ftp客户端

开发平台:

Unix_Linux

  1. /*
  2.  * ProFTPD - FTP server daemon
  3.  * Copyright (c) 1997, 1998 Public Flood Software
  4.  *  
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
  18.  */
  19. /*
  20.  * Directory listing module for proftpd
  21.  * $Id: mod_ls.c,v 1.17 1999/10/01 03:34:14 macgyver Exp $
  22.  */
  23. #include "conf.h"
  24. #ifndef GLOB_ABORTED
  25. #define GLOB_ABORTED GLOB_ABEND
  26. #endif
  27. #define MAP_UID(x) (fakeuser ? fakeuser : auth_uid_name(cmd->tmp_pool,(x)))
  28. #define MAP_GID(x) (fakegroup ? fakegroup : auth_gid_name(cmd->tmp_pool,(x)))
  29. static void addfile(cmd_rec*,const char *, const char *, time_t);
  30. static int outputfiles(cmd_rec*);
  31. static int listfile(cmd_rec*,pool*,const char *name);
  32. static int listdir(cmd_rec*,pool*,const char *name,int list_dotdirs);
  33. static int matches = 0;
  34. static char *default_options;
  35. static int showdotfiles,showsymlinks,showsymlinks_hold;
  36. static int cmp(const void *a, const void *b);
  37. static char *fakeuser,*fakegroup;
  38. static umode_t fakemode;
  39. static int fakemodep;
  40. static int ls_errno = 0;
  41. static time_t ls_curtime = 0;
  42. /* ls options */
  43. int opt_a = 0,
  44.     opt_C = 0,
  45.     opt_d = 0,
  46.     opt_F = 0,
  47.     opt_l = 0,
  48.     opt_R = 0,
  49.     opt_r = 0,
  50.     opt_t = 0,
  51.     opt_STAT = 0;
  52. static char cwd[MAXPATHLEN+1] = "";
  53. static void push_cwd(char *_cwd, int *symhold)
  54. {
  55.   if(!_cwd) _cwd = cwd;
  56.   if(!symhold) symhold = &showsymlinks_hold;
  57.   sstrncpy(_cwd, fs_getcwd(), MAXPATHLEN);
  58.   *symhold = showsymlinks;
  59. }
  60. static void pop_cwd(char *_cwd, int *symhold)
  61. {
  62.   if(!_cwd) _cwd = cwd;
  63.   if(!symhold) symhold = &showsymlinks_hold;
  64.   fs_chdir(_cwd,*symhold);
  65.   showsymlinks = *symhold;
  66. }
  67. static int ls_perms_full(pool *p, cmd_rec *cmd, const char *path, int *hidden)
  68. {
  69.   int ret,ishidden,canon = 0;
  70.   char *fullpath;
  71.   long _fakemode;
  72.   fullpath = dir_realpath(p,path);
  73.   if(!fullpath) {
  74.     fullpath = dir_canonical_path(p,path);
  75.     canon = 1;
  76.   } if(!fullpath)
  77.     fullpath = pstrdup(p,path);
  78.   
  79.   if(canon)
  80.     ret = dir_check_canon(p,cmd->argv[0],cmd->group,fullpath,&ishidden);
  81.   else
  82.     ret = dir_check(p,cmd->argv[0],cmd->group,fullpath,&ishidden);
  83.   if(hidden)
  84. *hidden = ishidden;
  85.   
  86.   if(ishidden)
  87.    return 0;
  88.   if(session.dir_config) {
  89.     showsymlinks = get_param_int(session.dir_config->subset,
  90.                                  "ShowSymlinks",FALSE);
  91.     if(showsymlinks == -1)
  92.       showsymlinks = 1;
  93.   }
  94.   _fakemode = get_param_int(CURRENT_CONF,"DirFakeMode",FALSE);
  95.   if(_fakemode != -1) {
  96.     fakemode = (umode_t)_fakemode;
  97.     fakemodep = 1;
  98.   } else
  99.     fakemodep = 0;
  100.   return ret;
  101. }
  102. static int ls_perms(pool *p, cmd_rec *cmd, const char *path, int *hidden)
  103. {
  104.   int ret,ishidden;
  105.   char fullpath[MAXPATHLEN];
  106.   long _fakemode;
  107.   if(*path == '~')
  108. return ls_perms_full(p,cmd,path,hidden);
  109.   if(*path != '/')
  110.    fs_clean_path(pdircat(p,fs_getcwd(),path,NULL),fullpath,MAXPATHLEN);
  111.   else
  112. fs_clean_path(path,fullpath,MAXPATHLEN);
  113.   
  114.   ret = dir_check(p,cmd->argv[0],cmd->group,fullpath,&ishidden);
  115.   if(hidden)
  116.   *hidden = ishidden;
  117.   
  118.   if(ishidden)
  119.     return 0;
  120.   if(session.dir_config) {
  121.     showsymlinks = get_param_int(session.dir_config->subset,
  122.                                  "ShowSymlinks",FALSE);
  123.     if(showsymlinks == -1)
  124.       showsymlinks = 1;
  125.   }
  126.   _fakemode = get_param_int(CURRENT_CONF,"DirFakeMode",FALSE);
  127.   if(_fakemode != -1) {
  128.     fakemode = (umode_t)_fakemode;
  129.     fakemodep = 1;
  130.   } else
  131.     fakemodep = 0;
  132.   return ret;
  133. }
  134. static
  135. int sendline(char *fmt, ...)
  136. {
  137.   va_list msg;
  138.   char buf[1025];
  139.   int ret;
  140.   va_start(msg,fmt);
  141.   vsnprintf(buf,sizeof(buf),fmt,msg);
  142.   va_end(msg);
  143.   buf[1024] = '';
  144.   ret = data_xfer(buf,strlen(buf));
  145.   if(ret < 0) {
  146.     log_debug(DEBUG3,"data_xfer returned %d, error = %s",
  147.               ret,strerror(session.d->outf->xerrno));
  148.   }
  149.   return ret;
  150. }
  151. static
  152. void ls_done(cmd_rec *cmd)
  153. {
  154.   data_close(FALSE);
  155. }
  156. static
  157. int listfile(cmd_rec *cmd, pool *p, const char *name)
  158. {
  159.   int rval = 0, len;
  160.   time_t mtime;
  161.   char m[1024],l[1024];
  162.   struct stat st;
  163.   char months[12][4] =
  164.   { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  165.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  166.   
  167.   struct tm *t;
  168.   char suffix[2];
  169.   
  170.   if(!p) p = cmd->tmp_pool;
  171.   
  172.   if(fs_lstat(name,&st) == 0) {
  173.     suffix[0] = suffix[1] = '';
  174.     if(S_ISLNK(st.st_mode) && !showsymlinks) {
  175.       /* attempt to fully dereference symlink */
  176.       struct stat l_st;
  177.       if(fs_stat(name,&l_st) != -1) {
  178.         memcpy(&st,&l_st,sizeof(st));
  179. if((len = fs_readlink(name, m, sizeof(m))) < 0)
  180.   return 0;
  181. m[len] = '';
  182.         if(!ls_perms_full(p,cmd,m,NULL))
  183.           return 0;
  184.       } else {
  185.         return 0;
  186.       }
  187.     } else if(S_ISLNK(st.st_mode)) {
  188.       if((len = fs_readlink(name, l, sizeof(l))) < 0)
  189. return 0;
  190.       
  191.       l[len] = '';
  192.       
  193.       if(!ls_perms_full(p,cmd,l,NULL))
  194.         return 0;
  195.     } else if(!ls_perms(p,cmd,name,NULL)) {
  196.       return 0;
  197.     }
  198.     mtime = st.st_mtime;
  199.     t = localtime((time_t*)&mtime);
  200.     if(!t) {
  201.       add_response_err(R_421,"Fatal error (localtime() returned NULL?!?)");
  202.       return -1;
  203.     }
  204.     if(opt_F) {
  205.       if(S_ISLNK(st.st_mode))
  206.         suffix[0] = '@';
  207.       else if(S_ISDIR(st.st_mode)) {
  208.         suffix[0] = '/';
  209.         rval = 1;
  210.       } else if(st.st_mode & 0111)
  211.         suffix[0] = '*';
  212.     }
  213.     if(opt_l) {
  214.       sstrncpy(m, " ---------", sizeof(m));
  215.       switch(st.st_mode & S_IFMT) {
  216.       case S_IFREG:
  217.         m[0] = '-';
  218.         break;
  219.       case S_IFLNK:
  220.         m[0] = 'l';
  221.         break;
  222.       case S_IFDIR:
  223.         m[0] = 'd';
  224.         rval = 1;
  225.         break;
  226.       }
  227.       if(m[0] != ' ') {
  228.         char nameline[MAXPATHLEN + MAXPATHLEN + 128];
  229.         char timeline[6];
  230.         umode_t mode = st.st_mode;
  231.         if(fakemodep)
  232.           mode = fakemode;
  233.         if(mode & 256)
  234.           m[1] = 'r';
  235.         if(mode & 128)
  236.           m[2] = 'w';
  237.         if(mode & 64 || ((mode & 256) && S_ISDIR(st.st_mode)))
  238.           m[3] = 'x';
  239.         if(mode & 32)
  240.           m[4] = 'r';
  241.         if(mode & 16)
  242.           m[5] = 'w';
  243.         if(mode & 8 || ((mode & 32) && S_ISDIR(st.st_mode)))
  244.           m[6] = 'x';
  245.         if(mode & 4)
  246.           m[7] = 'r';
  247.         if(mode & 2)
  248.           m[8] = 'w';
  249.         if(mode & 1 || ((mode & 4) && S_ISDIR(st.st_mode)))
  250.           m[9] = 'x';
  251.         if(ls_curtime - mtime > 180 * 24 * 60 * 60)
  252.           snprintf(timeline, sizeof(timeline), "%5d",t->tm_year+1900);
  253.         else
  254.           snprintf(timeline, sizeof(timeline), "%02d:%02d",t->tm_hour,t->tm_min);
  255.         snprintf(nameline, sizeof(nameline), "%s %3d %-8s %-8s %8d %s %2d %s %s", m,
  256.                 (int)st.st_nlink, MAP_UID((int)st.st_uid), 
  257.                 MAP_GID((int)st.st_gid),
  258.                 (unsigned int)st.st_size, months[t->tm_mon],
  259.                 t->tm_mday, timeline, name);
  260.         if(S_ISLNK(st.st_mode)) {
  261.           char *p = nameline + strlen(nameline);
  262.           suffix[0] = '';
  263.           if(opt_F && fs_stat(name, &st) == 0) {
  264.             if(S_ISLNK(st.st_mode))
  265.               suffix[0] = '@';
  266.             else if(S_ISDIR(st.st_mode))
  267.               suffix[0] = '/';
  268.             else if(st.st_mode & 0111)
  269.               suffix[0] = '*';
  270.           }
  271.           snprintf(p, sizeof(nameline) - strlen(nameline) - 4, " -> %s", l);
  272.         }
  273. if(opt_STAT)
  274.   add_response(R_211,"%s%s",nameline,suffix);
  275. else
  276.           addfile(cmd,nameline,suffix,mtime);
  277.       }
  278.     } else {
  279.       if(S_ISREG(st.st_mode) ||
  280.          S_ISDIR(st.st_mode) ||
  281.          S_ISLNK(st.st_mode))
  282.            addfile(cmd,name,suffix,mtime);
  283.     }
  284.   }
  285.   return rval;
  286. }
  287. static int colwidth = 0;
  288. static int filenames = 0;
  289. struct filename {
  290.   struct filename *down;
  291.   struct filename *right;
  292.   int top;
  293.   char line[1];
  294. };
  295. struct sort_filename {
  296.   time_t mtime;
  297.   char *name,*suffix;
  298. };
  299.   
  300. static struct filename *head = NULL;
  301. static struct filename *tail = NULL;
  302. static array_header *sort_arr = NULL;
  303. static pool *fpool = NULL;
  304.  
  305. static void addfile(cmd_rec *cmd, const char *name, const char *suffix, time_t mtime)
  306. {
  307.   struct  filename *p;
  308.   int l;
  309.   if(!name || !suffix)
  310.     return;
  311.   if(opt_t) {
  312.     struct sort_filename *s;
  313.     if(!fpool)
  314.       fpool = make_sub_pool(cmd->tmp_pool);
  315.     if(!sort_arr)
  316.       sort_arr = make_array(fpool,50,sizeof(struct sort_filename));
  317.     s = (struct sort_filename*)push_array(sort_arr);
  318.     s->mtime = mtime;
  319.     s->name = pstrdup(fpool,name);
  320.     s->suffix = pstrdup(fpool,suffix);
  321.     return;
  322.   }
  323.   matches++;
  324.   l = strlen(name) + strlen(suffix);
  325.   if(l > colwidth)
  326.     colwidth = l;
  327.   if(!fpool)
  328.     fpool = make_sub_pool(cmd->tmp_pool);
  329.   p = (struct filename*) pcalloc(fpool, sizeof(struct filename) + l + 1);
  330. #if 0
  331.   log_debug(DEBUG4,"alloc: %dn",sizeof(struct filename) + l + 1);
  332. #endif
  333.   snprintf(p->line, l + 1, "%s%s", name, suffix);
  334.   if(tail)
  335.     tail->down = p;
  336.   else
  337.     head = p;
  338.   tail = p;
  339.   filenames++;
  340. }
  341. #if 0
  342. static
  343. void RANGE(void *ptr)
  344. {
  345.   char *cp = (char*)ptr;
  346.   if(cp) {
  347.    *cp;
  348.   }
  349. }
  350. #endif
  351. static
  352. int _compare_file_mtime(const struct sort_filename *f1,
  353.                         const struct sort_filename *f2)
  354. {
  355.   if(f1->mtime > f2->mtime)
  356.     return -1;
  357.   else if(f1->mtime < f2->mtime)
  358.     return 1;
  359.   return 0;
  360. }
  361. static
  362. int _compare_file_mtime_reversed(const struct sort_filename *f1,
  363.                         const struct sort_filename *f2)
  364. {
  365.   return -_compare_file_mtime(f1, f2);
  366. }
  367. static
  368. void sortfiles(cmd_rec *cmd)
  369. {
  370.   struct sort_filename *s;
  371.   int i;
  372.   if(opt_t && sort_arr) {
  373.     qsort(sort_arr->elts, sort_arr->nelts, sizeof(struct sort_filename),
  374.           (int (*)(const void*,const void*))
  375.   (opt_r ? _compare_file_mtime_reversed : _compare_file_mtime));
  376.     opt_t = 0;
  377.     for(i = 0, s = (struct sort_filename*)sort_arr->elts; i < sort_arr->nelts; i++, s++)
  378.       addfile(cmd,s->name,s->suffix,s->mtime);
  379.     opt_t = 1;
  380.   }
  381.     
  382.   sort_arr = NULL;
  383. }
  384. static
  385. int outputfiles(cmd_rec *cmd)
  386. {
  387.   int n;
  388.   struct  filename *p;
  389.   struct filename *q;
  390.   if(opt_t)
  391.     sortfiles(cmd);
  392.   if(!head) /* nothing to display */
  393.     return 0;
  394.   tail->down = NULL;
  395.   tail = NULL;
  396.   colwidth = ( colwidth | 7 ) + 1;
  397.   if(opt_l || !opt_C)
  398.     colwidth = 75;
  399.   /* avoid division by 0 if colwidth > 75 */
  400.   if(colwidth > 75)
  401.     colwidth = 75;
  402.   p = head;
  403.   p->top = 1;
  404.   n = (filenames + (75 / colwidth)-1) / (75 / colwidth);
  405.   while(n && p) {
  406.     p = p->down;
  407.     if(p)
  408.       p->top = 0;
  409.     n--;
  410.   }
  411.   q = head;
  412.   while(p) {
  413.     p->top = q->top;
  414.     q->right = p;
  415.     q = q->down;
  416.     p = p->down;
  417.   }
  418.   while(q) {
  419.     q->right = NULL;
  420.     q = q->down;
  421.   }
  422.   p = head;
  423.   while(p && p->down && !p->down->top)
  424.     p = p->down;
  425.   if(p && p->down)
  426.     p->down = NULL;
  427. #if 0
  428.   if(opt_l)
  429.     if(sendline("total 0n") < 0)
  430.       return -1;
  431. #endif
  432.   
  433.   p = head;
  434.   while(p) {
  435.     q = p;
  436.     p = p->down;
  437.     while(q) {
  438.       char pad[6];
  439.       if(q->right) {
  440.         sstrncpy(pad, "ttttt", sizeof(pad));
  441.         pad[(colwidth + 7 - strlen(q->line)) / 8] = '';
  442.       } else {
  443.         sstrncpy(pad, "n", sizeof(pad));
  444.       }
  445.       if(sendline("%s%s", q->line, pad) < 0)
  446.         return -1;
  447.       q = q->right;
  448.     }
  449.   }
  450.   destroy_pool(fpool);
  451.   fpool = NULL;
  452.   sort_arr = NULL;
  453.   head = tail = NULL;
  454.   colwidth = 0;
  455.   filenames = 0;
  456.   return 0;
  457. }
  458. static
  459. void discard_output()
  460. {
  461.   if(fpool)
  462.     destroy_pool(fpool);
  463.   fpool = NULL;
  464.   head = tail = NULL;
  465.   colwidth = 0;
  466.   filenames = 0;
  467. }
  468. static int cmp(const void *a, const void *b)
  469. {
  470.   return strcmp(*(const char **)a, *(const char **)b);
  471. }
  472. static
  473. char **sreaddir(pool *workp, const char *dirname, const int sort)
  474. {
  475.   DIR  *d;
  476.   struct dirent *de;
  477.   struct stat st;
  478.   int i;
  479.   char **p;
  480.   char *s;
  481.   int dsize;
  482.   if(fs_stat(dirname,&st) < 0) 
  483.     return NULL;
  484.   if(!S_ISDIR(st.st_mode)) {
  485.     errno = ENOTDIR;
  486.     return NULL;
  487.   }
  488.   if((d = opendir(dirname)) == NULL)
  489.     return NULL;
  490.   dsize = st.st_size / 2 + 256;
  491. realloc_buf:
  492.   p = (char**)palloc(workp,dsize);
  493.   s = dsize + (char*)p;
  494.   i = 0;
  495.   while((de = readdir(d)) != NULL) {
  496.     if((unsigned int)p + (i+1)*sizeof(char*) + strlen(de->d_name) + 1
  497.          > (unsigned int)s) {
  498.       log_debug(DEBUG0,"reallocating sreaddir buffer from %d bytes to %d bytes.",
  499.       dsize,dsize*2);
  500.       dsize *= 2;
  501.       rewinddir(d);
  502.       goto realloc_buf;
  503.     }
  504.     s -= strlen(de->d_name) + 1;
  505.     sstrncpy(s, de->d_name, strlen(de->d_name) + 1);
  506.     p[i++] = s;
  507.   }
  508.   closedir(d);
  509.   p[i] = NULL;
  510.   if(sort)
  511. qsort(p,i,sizeof(char*),cmp);
  512.   
  513.   return p;
  514. }
  515. /* listdir required chdir first */
  516. static int listdir(cmd_rec *cmd, pool *workp, const char *name, int list_dotdirs)
  517. {
  518.   char **dir;
  519.   int dest_workp = 0;
  520.   if(XFER_ABORTED)
  521. return -1;
  522.   if(!workp) {
  523.     workp = make_sub_pool(cmd->tmp_pool);
  524.     dest_workp++;
  525.   } else {
  526.     workp = make_sub_pool(workp);
  527.     dest_workp++;
  528.   }
  529.   dir = sreaddir(workp,".",TRUE);
  530.   if(dir) {
  531.     char **s;
  532.     char **r;
  533.     int d = 0;
  534. #if 0
  535.     if(opt_l) {
  536.       if(opt_STAT)
  537.         add_response(R_211,"total 0");
  538.       else if(sendline("total 0n") < 0)
  539.         return -1;
  540.     }
  541. #endif
  542.     
  543.     s = dir;
  544.     while(*s) {
  545.       if(**s != '.') {
  546.         d = listfile(cmd,workp,*s);
  547.       } else if(!opt_a && showdotfiles != 1 && list_dotdirs) {
  548.         if( (((*s)[1] == '') ||
  549.             (((*s)[1] == '.') &&
  550.              ((*s)[2] == '')))) {
  551.           d = listfile(cmd,workp,*s);
  552.           if(d > 0)
  553.             d = 0;
  554.         }
  555.       } else if(opt_a || showdotfiles == 1) {
  556.         if(!opt_a && (((*s)[1] == '') ||
  557.            (((*s)[1] == '.') &&
  558.             ((*s)[2] == ''))))
  559.           d = 0;
  560. else
  561.   d = listfile(cmd,workp,*s);
  562.       } else {
  563.         d = 0;
  564.       }
  565.       if(!d)
  566.         *s = NULL;
  567.       s++;
  568.     }
  569.     if(outputfiles(cmd) < 0) {
  570.       if(dest_workp)
  571.         destroy_pool(workp);
  572.       return -1;
  573.     }
  574.     r = dir;
  575.     while(opt_R && r != s) {
  576.       char cwd[MAXPATHLEN];
  577.       int symhold;
  578.       if(*r && (strcmp(*r,".") == 0 || strcmp(*r,"..") == 0)) {
  579.         r++;
  580.         continue;
  581.       }
  582.       
  583.       push_cwd(cwd,&symhold);
  584.      
  585.       if(*r && ls_perms_full(workp,cmd,(char*)*r,NULL) 
  586.     && !fs_chdir_canon(*r,showsymlinks)) {
  587.         char *subdir;
  588. if(strcmp(name,".") == 0)
  589.           subdir = *r;
  590.         else
  591.           subdir = pdircat(workp,name,*r,NULL);
  592. if(opt_STAT) {
  593.   add_response(R_211,"");
  594.           add_response(R_211,"%s:",subdir);
  595. } else if(sendline("n%s:n",subdir) < 0) {
  596.           pop_cwd(cwd,&symhold);
  597.           if(dest_workp)
  598.             destroy_pool(workp);
  599.           return -1;
  600.         }
  601.         if(listdir(cmd,workp,subdir,list_dotdirs) < 0) {
  602.           pop_cwd(cwd,&symhold);
  603.           if(dest_workp)
  604.             destroy_pool(workp);
  605.           return -1;
  606.         }
  607.         pop_cwd(cwd,&symhold);
  608.       }
  609.       r++;
  610.     }
  611.   }
  612.   if(dest_workp)
  613.     destroy_pool(workp);
  614.   return 0;
  615. }
  616. static
  617. void ls_terminate()
  618. {
  619.   if(!opt_STAT) {
  620.     discard_output();
  621.     if(!XFER_ABORTED) {  /* an error has occured, other than client ABOR */
  622.       if(ls_errno)
  623.         data_abort(ls_errno,FALSE);
  624.       else
  625.         data_abort((session.d && session.d->outf ? 
  626.                    session.d->outf->xerrno : errno),FALSE);
  627.     }
  628.     ls_errno = 0;
  629.   } else if(ls_errno) {
  630.     add_response(R_211,"ERROR: %s",strerror(ls_errno));
  631.     ls_errno = 0;
  632.   }
  633. }
  634. static
  635. void _parse_options(char **opt, int *glob_flags)
  636. {
  637.   while(isspace((UCHAR)**opt))
  638.     opt++;
  639.   while(*opt && **opt == '-') {
  640.     while((*opt)++ && isalnum((UCHAR)**opt)) {
  641.       switch(**opt) {
  642.       case 'a':
  643.         opt_a++;
  644.         break;
  645.       case 'l':
  646.         opt_l++;
  647.         opt_C = 0;
  648.         break;
  649.       case '1':
  650.         opt_l = opt_C = 0;
  651.         break;
  652.       case 'C':
  653.         opt_l = 0;
  654.         opt_C++;
  655.         break;
  656.       case 'F':
  657.         opt_F++;
  658.         break;
  659.       case 'r':
  660.         opt_r++;
  661.         break;
  662.       case 'R':
  663.         opt_R++;
  664.         break;
  665.       case 'd':
  666.         opt_d++;
  667.         break;
  668.       case 't':
  669.         opt_t++;
  670.         if(glob_flags)
  671.           *glob_flags |= GLOB_NOSORT;;
  672.         break;
  673.       }
  674.     }
  675.     while(isspace((UCHAR)**opt)) 
  676.       (*opt)++;
  677.   }
  678. }
  679. static
  680. int dolist(cmd_rec *cmd, const char *opt, int clearflags)
  681. {
  682.   int skiparg = 0;
  683.   int glob_flags = GLOB_PERIOD;
  684.   char *arg = (char*)opt;
  685.   matches = 0;
  686.   ls_curtime = time(NULL);
  687.   if(clearflags)
  688.     opt_a = opt_C = opt_d = opt_F = opt_r = opt_R = opt_t = opt_STAT = 0;
  689.   if(default_options)
  690.     _parse_options(&default_options,&glob_flags);
  691.   _parse_options(&arg,&glob_flags);
  692.   /* open data connection */
  693.   if(!opt_STAT) {
  694.     session.flags |= SF_ASCII_OVERRIDE;
  695.     if(data_open(NULL,"file list",IO_WRITE,0) < 0)
  696.       return -1;
  697.   }
  698.   if(arg && *arg) {
  699.     int justone;
  700.     justone = 1;
  701.     while(arg) {
  702.       glob_t g;
  703.       int    a;
  704.       char   pbuffer[MAXPATHLEN];
  705.       char   *endarg = strchr(arg,' ');
  706.       if(endarg) {
  707.         *endarg++ = '';
  708.         justone = 0;
  709.       }
  710.       if(*arg == '~') {
  711.         struct passwd *pw;
  712. int i;
  713.         const char *p;
  714.         i = 0;
  715.         p = arg;
  716.         p++;
  717.         while(*p && *p != '/')
  718.           pbuffer[i++] = *p++;
  719.         pbuffer[i] = '';
  720.         if((pw = auth_getpwnam(cmd->tmp_pool,i ? pbuffer : session.user)))
  721.           snprintf(pbuffer, sizeof(pbuffer), "%s%s",pw->pw_dir,p);
  722.         else
  723.           *pbuffer = '';
  724.       } else
  725.         *pbuffer = '';
  726.       /* check perms on the directory/file we are about to scan */
  727.       if(!ls_perms_full(cmd->tmp_pool,cmd,(*pbuffer ? (char*)pbuffer:(char*)arg),NULL)) {
  728.         a = -1; skiparg = 1;
  729.       } else {
  730.         skiparg = 0;
  731.         a = fs_glob(*pbuffer ? pbuffer:arg, glob_flags,
  732.                  NULL, &g);
  733.       }
  734.       if(!a) {
  735.         char **path;
  736.         path = g.gl_pathv;
  737.         if(path && path[0] && path[1])
  738.           justone = 0;
  739.         while(path && *path) {
  740.           struct stat st;
  741.           /* if opt_a is not set, don't display dot files, except for
  742.            * ./ and ../
  743.            */
  744.           if(**path == '.' && !opt_a && showdotfiles != 1 && 
  745.      (*path)[1] != ''  && ((*path)[1] != '.' ||
  746.      ((*path)[1] == '.' && (*path)[2] != ''))) {
  747.             **path = '';
  748.             path++;
  749.             continue;
  750.           }
  751.           if(fs_lstat(*path,&st) == 0) {
  752.             mode_t target_mode,lmode;
  753.     target_mode = st.st_mode;
  754.             if(S_ISLNK(st.st_mode) && (lmode = file_mode((char*)*path)) != 0) {
  755.               if(!showsymlinks)
  756.                 st.st_mode = lmode;
  757.               target_mode = lmode;
  758.             }
  759.             if(opt_d || !(S_ISDIR(target_mode))) {
  760.               if(listfile(cmd,NULL,*path) < 0) {
  761.                 ls_terminate();
  762. fs_globfree(&g);
  763.                 return -1;
  764.               }
  765.               **path = '';
  766.             }
  767.           } else {
  768.             **path = '';
  769.           }
  770.           path++;
  771.         }
  772. if(outputfiles(cmd) < 0) {
  773.           ls_terminate();
  774.           fs_globfree(&g);
  775.   return -1;
  776.         }
  777.         path = g.gl_pathv;
  778.         while(path && *path) {
  779.           if(**path && ls_perms_full(cmd->tmp_pool,cmd,*path,NULL)) {
  780.             char cwd[MAXPATHLEN];
  781.             int symhold;
  782.             if(!justone) {
  783.               if(opt_STAT) {
  784. add_response(R_211,"");
  785. add_response(R_211,"%s:",*path);
  786.       } else
  787.                 sendline("n%s:n",*path);
  788.             }
  789.             push_cwd(cwd,&symhold);
  790.             if(!fs_chdir_canon(*path,showsymlinks)) {
  791.               int ret = listdir(cmd,NULL,*path,FALSE);
  792.               pop_cwd(cwd,&symhold);
  793.               if(ret < 0) {
  794.                 ls_terminate();
  795. fs_globfree(&g);
  796.                 return -1;
  797.               }
  798.             }
  799.           }
  800.           if(XFER_ABORTED) {
  801.     discard_output();
  802.             fs_globfree(&g);
  803.     return -1;
  804.   }
  805.           path++;
  806.         }
  807.       } else if(!skiparg) {
  808.         if(a == GLOB_NOSPACE) {
  809.           add_response(R_226,"Out of memory during globbing of %s", arg);
  810.         } else if(a == GLOB_ABORTED) {
  811.           add_response(R_226,"Read error during globbing of %s", arg);
  812.         } else if(a != GLOB_NOMATCH) {
  813.           add_response(R_226,"Unknown error during globbing of %s", arg);
  814.         }
  815.       }
  816.       if(!skiparg)
  817.         fs_globfree(&g);
  818.       arg = endarg;
  819.       if(XFER_ABORTED) {
  820. discard_output();
  821.         return -1;
  822.       }
  823.     }
  824.   } else {
  825.     if(ls_perms_full(cmd->tmp_pool,cmd,".",NULL)) {
  826.       if(opt_d) {
  827.         if(listfile(cmd,NULL,".") < 0) {
  828.           ls_terminate();
  829.           return -1;
  830.         }
  831.       } else {
  832.         if(listdir(cmd,NULL,".",FALSE) < 0) {
  833.           ls_terminate(); 
  834.   return -1;
  835.         }
  836.       }
  837.     }
  838.     if(outputfiles(cmd) < 0) {
  839.       ls_terminate();
  840.       return -1;
  841.     }
  842.   }
  843.   return 0;
  844. }
  845. /* display listing of a single file, no permission checking is done.
  846.  * error is only returned if the data connection cannot be opened
  847.  * or is aborted.
  848.  */
  849. static
  850. int nlstfile(cmd_rec *cmd, const char *file)
  851. {
  852. int err;
  853. /* If the data connection isn't open, open it now. */
  854. if((session.flags & SF_XFER) == 0) {
  855. if(data_open(NULL,"file list",IO_WRITE,0) < 0) {
  856. data_reset();
  857. return -1;
  858. }
  859. session.flags |= SF_ASCII_OVERRIDE;
  860. }
  861. if((err = sendline("%sn",file)) < 0)
  862. return err;
  863. return 1;
  864. }
  865. /* display listing of a directory, ACL checks performed on each entry,
  866.  * sent in NLST fashion.  Files which are inaccessible via ACL are skipped,
  867.  * error returned if data conn cannot be opened or is aborted.
  868.  */
  869. static int nlstdir(cmd_rec *cmd, const char *dir) {
  870.   char **list, *p, *f, file[MAXPATHLEN];
  871.   char cwd[MAXPATHLEN];
  872.   pool *workp;
  873.   int curdir = 0, i, symhold, count = 0;
  874.   mode_t mode;
  875.   
  876.   workp = make_sub_pool(cmd->tmp_pool);
  877.   
  878.   if(!*dir || (*dir == '.' && !dir[1]) || strncmp(dir,"./",2) == 0) {
  879.     curdir = 1;
  880.     dir = "";
  881.   } else {
  882.     push_cwd(cwd,&symhold);
  883.   }
  884.   
  885.   if(fs_chdir_canon(dir,showsymlinks)) {
  886.     destroy_pool(workp);
  887.     return 0;
  888.   }
  889.   
  890.   if((list = sreaddir(workp,".",FALSE)) == NULL) {
  891.     if(!curdir)
  892.       pop_cwd(cwd,&symhold);
  893.     destroy_pool(workp);
  894.     return 0;
  895.   }
  896.   
  897.   while(*list && count >= 0) {
  898.     p = *list; list++;
  899.     
  900.     if(*p == '.' && (((p[1] == '') || ((p[2] == '.') &&
  901.  (p[3] == '')))) )
  902.       continue;
  903.     
  904.     if(*p == '.' && showdotfiles != 1)
  905.       continue;
  906.     
  907.     if((i = fs_readlink(p, file, sizeof(file))) > 0) {
  908.       file[i] = '';
  909.       f = file;
  910.     } else {
  911.       f = p;
  912.     }
  913.     
  914.     if(ls_perms(workp,cmd,f,NULL)) {
  915.       /* If the data connection isn't open, open it now. */
  916.       if((session.flags & SF_XFER) == 0) {
  917. if(data_open(NULL,"file list",IO_WRITE,0) < 0) {
  918.   data_reset();
  919.   count = -1;
  920.   continue;
  921. }
  922. session.flags |= SF_ASCII_OVERRIDE;
  923.       }
  924.       
  925.       if((mode = file_mode(f)) == 0)
  926. continue;
  927.       
  928.       if(!curdir) {
  929. if(sendline("%s/%sn",dir,p) < 0)
  930.   count = -1;
  931. else count++;
  932.       } else {
  933. if(sendline("%sn",p) < 0)
  934.   count = -1;
  935. else count++;
  936.       }
  937.     }
  938.   }
  939.   
  940.   if(!curdir)
  941.     pop_cwd(cwd,&symhold);
  942.   
  943.   destroy_pool(workp);
  944.   
  945.   return count;
  946. }
  947. MODRET genericlist(cmd_rec *cmd)
  948. {
  949.   int err;
  950.   long _fakemode;
  951.   showsymlinks = get_param_int(TOPLEVEL_CONF,"ShowSymlinks",FALSE);
  952.   if(showsymlinks == -1)
  953.     showsymlinks = 1;
  954.   default_options = get_param_ptr(TOPLEVEL_CONF,"LsDefaultOptions",FALSE);
  955.   fakeuser = get_param_ptr(TOPLEVEL_CONF,"DirFakeUser",FALSE);
  956.   fakegroup = get_param_ptr(TOPLEVEL_CONF,"DirFakeGroup",FALSE);
  957.   _fakemode = (long)get_param_int(TOPLEVEL_CONF,"DirFakeMode",FALSE);
  958.   showdotfiles = get_param_int(TOPLEVEL_CONF,"ShowDotFiles",FALSE);
  959.   if(_fakemode != -1) {
  960.     fakemode = (umode_t)_fakemode;
  961.     fakemodep = 1;
  962.   } else
  963.     fakemodep = 0;
  964.   err = dolist(cmd,cmd->arg,TRUE);
  965.   if(XFER_ABORTED) {
  966.     data_abort(0,0);
  967.     err = -1;
  968.   } else if(session.flags & SF_XFER)
  969.     ls_done(cmd);
  970.   opt_l = 0;
  971.   return (err == -1 ? ERROR(cmd) : HANDLED(cmd));
  972. }
  973. MODRET fini_nlst(cmd_rec *cmd)
  974. {
  975.   data_cleanup();
  976.   return DECLINED(cmd);
  977. }
  978. MODRET cmd_stat(cmd_rec *cmd)
  979. {
  980.   char *arg = cmd->arg;
  981.   long _fakemode;
  982.   if(cmd->argc < 2) {
  983.     add_response_err(R_500,"'%s' not understood.",get_full_cmd(cmd));
  984.     return ERROR(cmd);
  985.   }
  986.   if(*arg == '-')
  987.     while(!isspace((UCHAR)*arg)) arg++;
  988.   while(isspace((UCHAR)*arg)) arg++;
  989.   showsymlinks = get_param_int(TOPLEVEL_CONF,"ShowSymlinks",FALSE);
  990.   if(showsymlinks == -1)
  991.     showsymlinks = 1;
  992.   default_options = get_param_ptr(TOPLEVEL_CONF,"LsDefaultOptions",FALSE);
  993.   fakeuser = get_param_ptr(TOPLEVEL_CONF,"DirFakeUser",FALSE);
  994.   fakegroup = get_param_ptr(TOPLEVEL_CONF,"DirFakeGroup",FALSE);
  995.   _fakemode = (long)get_param_int(TOPLEVEL_CONF,"DirFakeMode",FALSE);
  996.   showdotfiles = get_param_int(TOPLEVEL_CONF,"ShowDotFiles",FALSE);
  997.   
  998.   if(_fakemode != -1) {
  999.     fakemode = (umode_t)_fakemode;
  1000.     fakemodep = 1;
  1001.   } else
  1002.     fakemodep = 0;
  1003.   opt_C = opt_d = opt_F = opt_R;
  1004.   opt_a = opt_l = opt_STAT = 1;
  1005.   add_response(R_211,"status of %s:",arg);
  1006.   dolist(cmd,cmd->arg,FALSE);
  1007.   add_response(R_211,"End of Status");
  1008.   return HANDLED(cmd);
  1009. }
  1010. MODRET cmd_list(cmd_rec *cmd)
  1011. {
  1012.   MODRET ret;
  1013.   
  1014.   opt_l = 1;
  1015.   ret = genericlist(cmd);
  1016.   return ret;
  1017. }
  1018. /* NLST is a very simplistic directory listing, unlike LIST (which
  1019.  * emululates ls), it only sends a list of all files/directories
  1020.  * matching the glob(s).
  1021.  */
  1022. MODRET cmd_nlst(cmd_rec *cmd)
  1023. {
  1024. char *target,line[MAXPATHLEN];
  1025. int count = 0;
  1026. int ret = 0;
  1027. /* In case the client used NLST instead of LIST
  1028.  */
  1029. if(cmd->argc > 1 && cmd->argv[1][0] == '-')
  1030. return genericlist(cmd);
  1031. showsymlinks = get_param_int(TOPLEVEL_CONF,"ShowSymlinks",FALSE);
  1032. if(showsymlinks == -1)
  1033. showsymlinks = 1;
  1034. showdotfiles = get_param_int(TOPLEVEL_CONF,"ShowDotFiles",FALSE);
  1035. if(cmd->argc == 1)
  1036. target = ".";
  1037. else
  1038. target = cmd->arg;
  1039. /* If the target starts with '~' ... */
  1040. if(*target == '~') {
  1041. char pb[MAXPATHLEN];
  1042. struct passwd *pw;
  1043. int i;
  1044. const char *p;
  1045. i = 0;
  1046. p = target;
  1047. p++;
  1048. while(*p && *p !='/')
  1049. pb[i++] = *p++;
  1050. pb[i] = '';
  1051. if((pw = auth_getpwnam(cmd->tmp_pool,i ? pb : session.user))) {
  1052.   snprintf(pb, sizeof(pb), "%s%s",pw->pw_dir,p);
  1053.   sstrncpy(line, pb, sizeof(line));
  1054.   target = line;
  1055. }
  1056. }
  1057. /* If the target is a glob, get the listing of files/dirs to send
  1058.  */
  1059. if(strpbrk(target,"{[*?") != NULL) {
  1060. glob_t g;
  1061. char **path,*p;
  1062. if(fs_glob(target,GLOB_PERIOD,NULL,&g) != 0) {
  1063. add_response_err(R_550,"No files found.");
  1064. return ERROR(cmd);
  1065. }
  1066. /* Iterate through each matching entry */
  1067. path = g.gl_pathv;
  1068. while(path && *path && ret >= 0) {
  1069. struct stat st;
  1070. /* Skip ./ and ../ */
  1071. p = *path; path++;
  1072. if(*p == '.' && (
  1073. ((p[1] == '') ||
  1074. ((p[1] == '.') &&
  1075.  (p[2] == '')))) )
  1076. continue;
  1077. if(*p == '.' && showdotfiles != 1)
  1078. continue;
  1079. if(fs_stat(p,&st) == 0) {
  1080. /* If it's a directory, hand off to nlstdir */
  1081. if(S_ISDIR(st.st_mode))
  1082. ret = nlstdir(cmd,p);
  1083. else if(S_ISREG(st.st_mode) &&
  1084. ls_perms(cmd->tmp_pool,cmd,p,NULL))
  1085. ret = nlstfile(cmd,p);
  1086. if(ret > 0)
  1087. count += ret;
  1088. }
  1089. }
  1090. fs_globfree(&g);
  1091. } else {
  1092. /* A single target, if it's a directory, list the contents,
  1093.  * if it's a file, just list the file.
  1094.  */
  1095. int hidden;
  1096. struct stat st;
  1097. if(!ls_perms(cmd->tmp_pool,cmd,target,&hidden)) {
  1098. if(hidden)
  1099. add_response_err(R_550,"%s: No such file or directory",cmd->arg);
  1100. else
  1101. add_response_err(R_550,"%s: Permission denied",cmd->arg);
  1102. return ERROR(cmd);
  1103. }
  1104. /* Make sure the target is a file or directory,
  1105.  * and that we have access to it.
  1106.  */
  1107. if(fs_stat(target,&st) < 0) {
  1108. add_response_err(R_550,"%s: %s",
  1109. cmd->arg,strerror(errno));
  1110. return ERROR(cmd);
  1111. }
  1112. if(S_ISREG(st.st_mode))
  1113. ret = nlstfile(cmd,target);
  1114. else if(S_ISDIR(st.st_mode)) {
  1115. if(access(target,R_OK) != 0) {
  1116. add_response_err(R_550,"%s: %s",
  1117. cmd->arg,strerror(errno));
  1118. return ERROR(cmd);
  1119. }
  1120. ret = nlstdir(cmd,target);
  1121. } else {
  1122. add_response_err(R_550,"%s: Not a regular file",
  1123. cmd->arg,strerror(errno));
  1124. return ERROR(cmd);
  1125. }
  1126. if(ret > 0)
  1127. count += ret;
  1128. }
  1129. if(XFER_ABORTED) {
  1130. data_abort(0,0);
  1131. ret = -1;
  1132. } else {
  1133. if(ret == 0 && !count && (session.flags & SF_XFER) == 0) {
  1134. add_response_err(R_550,"No files found.");
  1135. ret = -1;
  1136. } else if(session.flags & SF_XFER)
  1137. ls_done(cmd);
  1138. /* Note that the data connection is NOT cleared here,
  1139.  * as an error in NLST still leaves data ready for
  1140.  * another command
  1141.  */
  1142. }
  1143. return (ret < 0 ? ERROR(cmd) : HANDLED(cmd));
  1144. }
  1145. MODRET _sethide(cmd_rec *cmd, const char *param)
  1146. {
  1147.   int bool;
  1148.   char *as = "ftp";
  1149.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1150.   if(cmd->argc < 2 || cmd->argc > 3)
  1151.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"syntax: ",
  1152.                    param," On|Off [<id to display>]",NULL));
  1153.   bool = get_boolean(cmd,1);
  1154.   if(bool > 0) {
  1155.     if(cmd->argc > 2)
  1156.       as = cmd->argv[2];
  1157.     add_config_param_str(param,1,as);
  1158.   } else if(!bool)
  1159.     add_config_param_str(param,0);
  1160.   return HANDLED(cmd);
  1161. }
  1162. MODRET set_dirfakeuser(cmd_rec *cmd)
  1163. {
  1164.   return _sethide(cmd,"DirFakeUser");
  1165. }
  1166. MODRET set_dirfakegroup(cmd_rec *cmd)
  1167. {
  1168.   return _sethide(cmd,"DirFakeGroup");
  1169. }
  1170. MODRET set_dirfakemode(cmd_rec *cmd)
  1171. {
  1172.   config_rec *c;
  1173.   unsigned long fake;
  1174.   char *endp;
  1175.   CHECK_ARGS(cmd,1);
  1176.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL|CONF_DIR);
  1177.   fake = (unsigned long)strtol(cmd->argv[1],&endp,8);
  1178.   if(endp && *endp)
  1179.     CONF_ERROR(cmd,"argument must be an octal number.");
  1180.   c = add_config_param("DirFakeMode",1,(void*)fake);
  1181.   c->flags |= CF_MERGEDOWN;
  1182.   return HANDLED(cmd);
  1183. }
  1184. MODRET set_lsdefaultoptions(cmd_rec *cmd)
  1185. {
  1186.   CHECK_ARGS(cmd,1);
  1187.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1188.   add_config_param_str("LsDefaultOptions",1,cmd->argv[1]);
  1189.   return HANDLED(cmd);
  1190. }
  1191. MODRET set_showdotfiles(cmd_rec *cmd)
  1192. {
  1193.   int b;
  1194.   
  1195.   CHECK_ARGS(cmd,1);
  1196.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1197.   if((b = get_boolean(cmd,1)) == -1)
  1198.     CONF_ERROR(cmd,"expected boolean argument.");
  1199.   add_config_param("ShowDotFiles",1,(void*)b);
  1200.   return HANDLED(cmd);
  1201. }
  1202. static conftable ls_config[] = {
  1203.   { "DirFakeUser", set_dirfakeuser, NULL },
  1204.   { "DirFakeGroup", set_dirfakegroup, NULL },
  1205.   { "DirFakeMode", set_dirfakemode, NULL },
  1206.   { "LsDefaultOptions", set_lsdefaultoptions, NULL },
  1207.   { "ShowDotFiles", set_showdotfiles, NULL },
  1208.   { NULL, NULL, NULL }
  1209. };
  1210. cmdtable ls_commands[] = {
  1211.   { CMD,   C_NLST, G_DIRS, cmd_nlst, TRUE, FALSE, CL_DIRS },
  1212.   { CMD, C_LIST, G_DIRS, cmd_list, TRUE, FALSE, CL_DIRS },
  1213.   { CMD,  C_STAT, G_DIRS, cmd_stat, TRUE, FALSE, CL_DIRS },
  1214.   { LOG_CMD, C_LIST, G_NONE, fini_nlst, FALSE, FALSE },
  1215.   { LOG_CMD, C_NLST, G_NONE, fini_nlst, FALSE, FALSE },
  1216.   { 0, NULL }
  1217. };
  1218. module ls_module = {
  1219.   NULL,NULL, /* Always NULL */
  1220.   0x20, /* API version */
  1221.   "ls", /* Module name */
  1222.   ls_config,
  1223.   ls_commands,
  1224.   NULL,
  1225.   NULL,NULL
  1226. };