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

Ftp客户端

开发平台:

Unix_Linux

  1. /*
  2.  * ProFTPD - FTP server daemon
  3.  * Copyright (c) 1997, 1998 Public Flood Software
  4.  * Copyright (C) 1999, MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
  5.  *  
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
  19.  */
  20. /*
  21.  * Core FTPD module
  22.  * $Id: mod_core.c,v 1.16 1999/10/01 07:57:31 macgyver Exp $
  23.  *
  24.  * 11/5/98 Habeeb J. Dihu aka MacGyver (macgyver@tos.net): added
  25.  *  wu-ftpd style CDPath support.
  26.  */
  27. #include "conf.h"
  28. #include "privs.h"
  29. #include <ctype.h>
  30. #ifdef HAVE_REGEX_H
  31. #include <regex.h>
  32. #endif
  33. /* This is declared static to this module because it's not needed,
  34.  * except for the HELP command.
  35.  */
  36. static struct {
  37.   char *cmd;
  38.   char *syntax;
  39.   int implemented;
  40. } _help[] = {
  41.   { C_USER, "<sp> username", TRUE },
  42.   { C_PASS, "<sp> password", TRUE },
  43.   { C_ACCT, "is not implemented", FALSE },
  44.   { C_CWD,  "<sp> pathname", TRUE },
  45.   { C_XCWD, "<sp> pathname", TRUE },
  46.   { C_CDUP, "(up one directory)", TRUE },
  47.   { C_XCUP, "(up one directory)", TRUE },
  48.   { C_SMNT, "is not implemented", FALSE },
  49.   { C_QUIT, "(close control connection)", TRUE },
  50.   { C_REIN, "is not implemented", FALSE },
  51.   { C_PORT, "<sp> h1,h2,h3,h4,p1,p2", TRUE },
  52.   { C_PASV, "(returns address/port)", TRUE },
  53.   { C_TYPE, "<sp> type-code (A or I)", TRUE },
  54.   { C_STRU, "is not implemented", FALSE },
  55.   { C_MODE, "is not implemented (always S)", FALSE },
  56.   { C_RETR, "<sp> pathname", TRUE },
  57.   { C_STOR, "<sp> pathname", TRUE },
  58.   { C_STOU, "is not implemented", FALSE },
  59.   { C_APPE, "<sp> pathname", TRUE },
  60.   { C_ALLO, "is not implemented", FALSE },
  61.   { C_REST, "<sp> byte-count", TRUE },
  62.   { C_RNFR, "<sp> pathname", TRUE },
  63.   { C_RNTO, "<sp> pathname", TRUE },
  64.   { C_ABOR, "(abort current operation)", TRUE },
  65.   { C_DELE, "<sp> pathname", TRUE },
  66.   { C_MDTM, "<sp> pathname", TRUE },
  67.   { C_RMD,  "<sp> pathname", TRUE },
  68.   { C_XRMD, "<sp> pathname", TRUE },
  69.   { C_MKD,  "<sp> pathname", TRUE },
  70.   { C_XMKD, "<sp> pathname", TRUE },
  71.   { C_PWD,  "(returns current working directory)", TRUE },
  72.   { C_XPWD, "(returns current working directory)", TRUE },
  73.   { C_SIZE, "<sp> pathname", TRUE },
  74.   { C_LIST, "[<sp> pathname]", TRUE },
  75.   { C_NLST, "[<sp> (pathname)]", TRUE },
  76.   { C_SITE, "<sp> string", TRUE },
  77.   { C_SYST, "(returns system type)", TRUE },
  78.   { C_STAT, "[<sp> pathname]", TRUE },
  79.   { C_HELP, "[<sp> command]", TRUE },
  80.   { C_NOOP, "(no operation)", TRUE },
  81.   { NULL,   NULL,           FALSE }
  82. };
  83. extern module site_module;
  84. extern xaset_t *servers;
  85. /* from mod_site */
  86. extern modret_t *site_dispatch(cmd_rec*);
  87. MODRET set_servername(cmd_rec *cmd)
  88. {
  89.   server_rec *s = cmd->server;
  90.   CHECK_ARGS(cmd,1);
  91.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL);
  92.   s->ServerName = pstrdup(s->pool,cmd->argv[1]);
  93.   return HANDLED(cmd);
  94. }
  95. MODRET set_servertype(cmd_rec *cmd)
  96. {
  97.   CHECK_ARGS(cmd,1);
  98.   CHECK_CONF(cmd,CONF_ROOT);
  99.   if(!strcasecmp(cmd->argv[1],"inetd"))
  100.     ServerType = SERVER_INETD;
  101.   else if(!strcasecmp(cmd->argv[1],"standalone"))
  102.     ServerType = SERVER_STANDALONE;
  103.   else
  104.     CONF_ERROR(cmd,"type must be either 'inetd' or 'standalone'.");
  105.   return HANDLED(cmd);
  106. }
  107. MODRET add_transferlog(cmd_rec *cmd)
  108. {
  109.   CHECK_ARGS(cmd,1);
  110.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  111.   add_config_param_str("TransferLog",1,(void*)cmd->argv[1]);
  112.   return HANDLED(cmd);
  113. }
  114. MODRET set_wtmplog(cmd_rec *cmd)
  115. {
  116.   int b;
  117.   CHECK_ARGS(cmd,1);
  118.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  119.   if(strcasecmp(cmd->argv[1],"NONE") == 0)
  120.     b = 0;
  121.   else
  122.     b = get_boolean(cmd,1);
  123.   if(b != -1)
  124.     add_config_param("WtmpLog",1,(void*)b);
  125.   return HANDLED(cmd);
  126. }
  127. MODRET add_bind(cmd_rec *cmd)
  128. {
  129.   CHECK_ARGS(cmd,1);
  130.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL);
  131.   add_config_param_str("Bind",1,(void*)cmd->argv[1]);
  132.   return HANDLED(cmd);
  133. }
  134. MODRET set_serveradmin(cmd_rec *cmd)
  135. {
  136.   server_rec *s = cmd->server;
  137.   CHECK_ARGS(cmd,1);
  138.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL);
  139.   s->ServerAdmin = pstrdup(s->pool,cmd->argv[1]);
  140.   return HANDLED(cmd);
  141. }
  142. MODRET set_usereversedns(cmd_rec *cmd)
  143. {
  144.   int b;
  145.   CHECK_ARGS(cmd,1);
  146.   CHECK_CONF(cmd,CONF_ROOT);
  147.   if((b = get_boolean(cmd,1)) == -1)
  148.     CONF_ERROR(cmd,"expected boolean argument.");
  149.   ServerUseReverseDNS = b;
  150.   return HANDLED(cmd);
  151. }
  152. MODRET set_scoreboardpath(cmd_rec *cmd)
  153. {
  154.   CHECK_ARGS(cmd,1);
  155.   CHECK_CONF(cmd,CONF_ROOT);
  156.   log_run_setpath(cmd->argv[1]);
  157.   return HANDLED(cmd);
  158. }
  159. MODRET set_serverport(cmd_rec *cmd)
  160. {
  161.   server_rec *s = cmd->server;
  162.   int port;
  163.   CHECK_ARGS(cmd,1);
  164.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL);
  165.   port = atoi(cmd->argv[1]);
  166.   if(port < 0 || port > 65535)
  167.     CONF_ERROR(cmd,"value must be between 0 and 65535");
  168.   s->ServerPort = port;
  169.   return HANDLED(cmd);
  170. }
  171. MODRET set_deferwelcome(cmd_rec *cmd)
  172. {
  173.   int b;
  174.   CHECK_ARGS(cmd,1);
  175.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  176.   if((b = get_boolean(cmd,1)) == -1)
  177.     CONF_ERROR(cmd,"expected boolean argument.");
  178.   add_config_param("DeferWelcome",1,(void*)b);
  179.   return HANDLED(cmd);
  180. }
  181. MODRET set_serverident(cmd_rec *cmd)
  182. {
  183.   int b;
  184.   config_rec *c;
  185.   
  186.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  187.   if(cmd->argc < 2 || cmd->argc > 3)
  188.     CONF_ERROR(cmd,"invalid number of arguments");
  189.   
  190.   if((b = get_boolean(cmd,1)) == -1)
  191.     CONF_ERROR(cmd,"expected boolean argument.");
  192.   if(b && cmd->argc == 3) {
  193.     c = add_config_param("ServerIdent",2,(void*)!b,NULL);
  194.     c->argv[1] = pstrdup(permanent_pool,cmd->argv[2]);
  195.   } else
  196.     add_config_param("ServerIdent",1,(void*)!b);
  197.   return HANDLED(cmd);
  198. }
  199. MODRET set_defaultserver(cmd_rec *cmd)
  200. {
  201.   int b;
  202.   server_rec *s;
  203.   CHECK_ARGS(cmd,1);
  204.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL);
  205.   if((b = get_boolean(cmd,1)) == -1)
  206.     CONF_ERROR(cmd,"expected boolean argument.");
  207.   if(!b)
  208.     return HANDLED(cmd);
  209.   /* DefaultServer is not allowed if already set somewhere */
  210.   for(s = (server_rec*)servers->xas_list; s; s=s->next)
  211.     if(find_config(s->conf,CONF_PARAM,"DefaultServer",FALSE)) {
  212.       CONF_ERROR(cmd,"DefaultServer has already been set.");
  213.     }
  214.   add_config_param("DefaultServer",1,(void*)b);
  215.   return HANDLED(cmd);
  216. }
  217. MODRET set_maxinstances(cmd_rec *cmd)
  218. {
  219.   int max;
  220.   char *endp;
  221.   CHECK_ARGS(cmd,1);
  222.   CHECK_CONF(cmd,CONF_ROOT);
  223.   if(!strcasecmp(cmd->argv[1],"none"))
  224.     max = 0;
  225.   else {
  226.     max = (int)strtol(cmd->argv[1],&endp,10);
  227.     if((endp && *endp) || max < 1)
  228.       CONF_ERROR(cmd,"argument must be 'none' or a number greater than 0.");
  229.   }
  230.   ServerMaxInstances = max;
  231.   return HANDLED(cmd);
  232. }
  233. MODRET _set_timeout(int *v, cmd_rec *cmd)
  234. {
  235.   int timeout;
  236.   char *endp;
  237.   CHECK_ARGS(cmd,1);
  238.   CHECK_CONF(cmd,CONF_ROOT);
  239.   timeout = (int)strtol(cmd->argv[1],&endp,10);
  240.   if((endp && *endp) || timeout < 0 || timeout > 65535)
  241.     CONF_ERROR(cmd,"timeout values must be between 0 and 65535");
  242.   *v = timeout;
  243.   return HANDLED(cmd);
  244. }
  245. MODRET set_maxclients(cmd_rec *cmd)
  246. {
  247.   int max;
  248.   char *endp;
  249.   config_rec *c;
  250.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  251.   if(cmd->argc < 2 || cmd->argc > 3)
  252.     CONF_ERROR(cmd,"invalid number of arguments");
  253.   if(!strcasecmp(cmd->argv[1],"none"))
  254.     max = -1;
  255.   else {
  256.     max = (int)strtol(cmd->argv[1],&endp,10);
  257.     if((endp && *endp) || max < 1) 
  258.       CONF_ERROR(cmd,"argument must be 'none' or a number greater than 0.");
  259.   }
  260.   if(cmd->argc == 3) {
  261.     c = add_config_param("MaxClients",2,(void*)max,NULL);
  262.     c->argv[1] = pstrdup(permanent_pool,cmd->argv[2]);   
  263.   } else
  264.     add_config_param("MaxClients",1,(void*)max);
  265.   return HANDLED(cmd);
  266. }
  267. MODRET set_maxhostclients(cmd_rec *cmd)
  268. {
  269.   int max;
  270.   char *endp;
  271.   config_rec *c;
  272.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  273.   if(cmd->argc < 2 || cmd->argc > 3)
  274.     CONF_ERROR(cmd,"invalid number of arguments");
  275.   if(!strcasecmp(cmd->argv[1],"none"))
  276.     max = -1;
  277.   else {
  278.     max = (int)strtol(cmd->argv[1],&endp,10);
  279.     if((endp && *endp) || max < 1)
  280.       CONF_ERROR(cmd,"argument must be 'none' or a number greater than 0.");
  281.   }
  282.   if(cmd->argc == 3) {
  283.     c = add_config_param("MaxClientsPerHost",2,(void*)max,NULL);
  284.     c->argv[1] = pstrdup(c->pool,cmd->argv[2]);   
  285.   } else
  286.     add_config_param("MaxClientsPerHost",1,(void*)max);
  287.   return HANDLED(cmd);
  288. }
  289. MODRET set_maxloginattempts(cmd_rec *cmd)
  290. {
  291.   int max;
  292.   char *endp;
  293.   CHECK_ARGS(cmd,1);
  294.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  295.   if(!strcasecmp(cmd->argv[1],"none"))
  296.     max = 0;
  297.   else {
  298.     max = (int)strtol(cmd->argv[1],&endp,10);
  299.     if((endp && *endp) || max < 1)
  300.       CONF_ERROR(cmd,"argument must be 'none' or a number greater than 0.");
  301.   }
  302.   add_config_param("MaxLoginAttempts",1,(void*)max);
  303.   return HANDLED(cmd);
  304. }
  305. MODRET set_useftpusers(cmd_rec *cmd)
  306. {
  307.   config_rec *c;
  308.   CHECK_ARGS(cmd,1);
  309.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  310.   c = add_config_param("UseFtpUsers",1,(void*)get_boolean(cmd,1));
  311.   c->flags |= CF_MERGEDOWN;
  312.   return HANDLED(cmd);
  313. }
  314. MODRET set_timeoutlogin(cmd_rec *cmd)
  315. {
  316.   return _set_timeout(&TimeoutLogin,cmd);
  317. }
  318. MODRET set_timeoutidle(cmd_rec *cmd)
  319. {
  320.   return _set_timeout(&TimeoutIdle,cmd);
  321. }
  322. MODRET set_timeoutnoxfer(cmd_rec *cmd)
  323. {
  324.   return _set_timeout(&TimeoutNoXfer,cmd);
  325. }
  326. MODRET set_timeoutstalled(cmd_rec *cmd)
  327. {
  328.   return _set_timeout(&TimeoutStalled,cmd);
  329. }
  330. MODRET set_socketbindtight(cmd_rec *cmd)
  331. {
  332.   CHECK_ARGS(cmd,1);
  333.   CHECK_CONF(cmd,CONF_ROOT);
  334.   SocketBindTight = get_boolean(cmd,1);
  335.   return HANDLED(cmd);  
  336. }
  337. MODRET set_multilinerfc2228(cmd_rec *cmd)
  338. {
  339.   CHECK_ARGS(cmd,1);
  340.   CHECK_CONF(cmd,CONF_ROOT);
  341.   MultilineRFC2228 = get_boolean(cmd,1);
  342.   return HANDLED(cmd);
  343. }
  344. MODRET set_identlookups(cmd_rec *cmd)
  345. {
  346.   CHECK_ARGS(cmd,1);
  347.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  348.   add_config_param("IdentLookups",1,get_boolean(cmd,1));
  349.   return HANDLED(cmd);
  350. }
  351. MODRET set_tcpbacklog(cmd_rec *cmd)
  352. {
  353.   int backlog;
  354.   CHECK_ARGS(cmd,1);
  355.   CHECK_CONF(cmd,CONF_ROOT);
  356.   backlog = atoi(cmd->argv[1]);
  357.   if(backlog < 1 || backlog > 255)
  358.     CONF_ERROR(cmd,"parameter must be a number between 1 and 255.");
  359.   tcpBackLog = backlog;
  360.   return HANDLED(cmd);
  361. }
  362. MODRET set_tcpreceivewindow(cmd_rec *cmd)
  363. {
  364.   int rwin;
  365.   CHECK_ARGS(cmd,1);
  366.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL);
  367.   rwin = atoi(cmd->argv[1]);
  368.   if(rwin < 1024)
  369.     CONF_ERROR(cmd,"parameter must be number equal to or greater than 1024.");
  370.   cmd->server->tcp_rwin = rwin;
  371.   cmd->server->tcp_rwin_override = 1;
  372.   return HANDLED(cmd);
  373. }
  374. MODRET set_tcpsendwindow(cmd_rec *cmd)
  375. {
  376.   int swin;
  377.   CHECK_ARGS(cmd,1);
  378.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL);
  379.   swin = atoi(cmd->argv[1]);
  380.   if(swin < 1024)
  381.     CONF_ERROR(cmd,"parameter must be number equal to or greater than 1024.");
  382.   cmd->server->tcp_swin = swin;
  383.   cmd->server->tcp_swin_override = 1;
  384.   return HANDLED(cmd);
  385. }
  386. MODRET set_tcpnodelay(cmd_rec *cmd)
  387. {
  388.   CHECK_ARGS(cmd,1);
  389.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  390.   add_config_param("tcpNoDelay",1,get_boolean(cmd,1));
  391.   return HANDLED(cmd);
  392. }
  393. MODRET set_user(cmd_rec *cmd)
  394. {
  395.   struct passwd *pw = NULL;
  396.   CHECK_ARGS(cmd,1);
  397.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  398.   /* 1.1.7, no longer force user/group lookup inside <Anonymous>
  399.    * it's now defered until authentication occurs.
  400.    */
  401.   if(!cmd->config || cmd->config->config_type != CONF_ANON) {
  402.     if((pw = auth_getpwnam(cmd->tmp_pool,cmd->argv[1])) == NULL) {
  403.       auth_endpwent(cmd->tmp_pool);
  404.       CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"Unknown user '",
  405.                              cmd->argv[1],"'.",NULL));
  406.     }
  407.   }
  408.   /* The extra cast is required to avoid compiler warning */
  409.   if(pw) {
  410.     add_config_param("User",1,(void*)((int)pw->pw_uid));
  411.     /* We don't need extra fds sitting around open */
  412.     auth_endpwent(cmd->tmp_pool);
  413.   }
  414.   add_config_param_str("UserName",1,(void*)cmd->argv[1]);
  415.   return HANDLED(cmd);
  416. }
  417. MODRET set_group(cmd_rec *cmd)
  418. {
  419.   struct group *grp = NULL;
  420.   CHECK_ARGS(cmd,1);
  421.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  422.   if(!cmd->config || cmd->config->config_type != CONF_ANON) {
  423.     if((grp = auth_getgrnam(cmd->tmp_pool,cmd->argv[1])) == NULL) {
  424.       auth_endgrent(cmd->tmp_pool);
  425.       CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"Unknown group '",
  426.                              cmd->argv[1],"'.",NULL));
  427.     }
  428.   }
  429.   /* The extra cast is needed to avoid compiler warning */
  430.   if(grp) {
  431.     add_config_param("Group",1,(void*)((int)grp->gr_gid));
  432.     auth_endgrent(cmd->tmp_pool);
  433.   }
  434.   add_config_param_str("GroupName",1,(void*)cmd->argv[1]);
  435.   return HANDLED(cmd);
  436. }
  437. MODRET add_userpassword(cmd_rec *cmd)
  438. {
  439.   CHECK_ARGS(cmd,2);
  440.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  441.   add_config_param_str("UserPassword",2,cmd->argv[1],cmd->argv[2]);
  442.   return HANDLED(cmd);
  443. }
  444. MODRET add_grouppassword(cmd_rec *cmd)
  445. {
  446.   CHECK_ARGS(cmd,2);
  447.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  448.   add_config_param_str("GroupPassword",2,cmd->argv[1],cmd->argv[2]);
  449.   return HANDLED(cmd);
  450. }
  451. MODRET set_accessgrantmsg(cmd_rec *cmd)
  452. {
  453.   CHECK_ARGS(cmd,1);
  454.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  455.   add_config_param_str("AccessGrantMsg",1,cmd->argv[1]);
  456.   return HANDLED(cmd);
  457. }
  458. MODRET set_umask(cmd_rec *cmd)
  459. {
  460.   config_rec *c;
  461.   char *endp;
  462.   int _umask;
  463.   CHECK_ARGS(cmd,1);
  464.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_DIR|CONF_ANON|CONF_GLOBAL);
  465.   _umask = strtol(cmd->argv[1],&endp,8);
  466.   if(endp && *endp)
  467.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"'",cmd->argv[1],"' is not "
  468.                            "a valid umask.",NULL));
  469.   c = add_config_param("Umask",1,(void*)_umask);
  470.   c->flags |= CF_MERGEDOWN;
  471.   return HANDLED(cmd);
  472. }
  473. MODRET set_requirevalidshell(cmd_rec *cmd)
  474. {
  475.   config_rec *c;
  476.   CHECK_ARGS(cmd,1);
  477.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  478.   c = add_config_param("RequireValidShell",1,get_boolean(cmd,1));
  479.   c->flags |= CF_MERGEDOWN;
  480.   return HANDLED(cmd);
  481. }
  482. MODRET set_syslogfacility(cmd_rec *cmd)
  483. {
  484.   int i;
  485.   struct {
  486.     char *name;
  487.     int facility;
  488.   } factable[] = {
  489.   { "AUTH", LOG_AUTHPRIV },
  490.   { "AUTHPRIV", LOG_AUTHPRIV },
  491. #ifdef HAVE_LOG_FTP
  492.   { "FTP", LOG_FTP },
  493. #endif
  494. #ifdef HAVE_LOG_CRON
  495.   { "CRON", LOG_CRON },
  496. #endif  
  497.   { "DAEMON", LOG_DAEMON },
  498.   { "KERN", LOG_KERN },
  499.   { "LOCAL0", LOG_LOCAL0 },
  500.   { "LOCAL1", LOG_LOCAL1 },
  501.   { "LOCAL2", LOG_LOCAL2 },
  502.   { "LOCAL3", LOG_LOCAL3 },
  503.   { "LOCAL4", LOG_LOCAL4 },
  504.   { "LOCAL5", LOG_LOCAL5 },
  505.   { "LOCAL6", LOG_LOCAL6 },
  506.   { "LOCAL7", LOG_LOCAL7 },
  507.   { "LPR", LOG_LPR },
  508.   { "MAIL", LOG_MAIL },
  509.   { "NEWS", LOG_NEWS },
  510.   { "USER", LOG_USER },
  511.   { "UUCP", LOG_UUCP },
  512.   { NULL, 0 } };
  513.   CHECK_ARGS(cmd,1);
  514.   CHECK_CONF(cmd,CONF_ROOT);
  515.   for(i = 0; factable[i].name; i++) {
  516.     if(!strcasecmp(cmd->argv[1],factable[i].name)) {
  517.       log_closesyslog();
  518.       log_setfacility(factable[i].facility);
  519.       block_signals();
  520.       PRIVS_ROOT
  521. switch(log_opensyslog(NULL)) {
  522. case -2:
  523.   PRIVS_RELINQUISH
  524.   unblock_signals();
  525.   CONF_ERROR(cmd, "you are attempting to log to a world writeable directory");
  526.   break;
  527.   
  528. case -1:
  529.   PRIVS_RELINQUISH
  530.   unblock_signals();
  531.   CONF_ERROR(cmd, "unable to open syslog");
  532.   break;
  533.   
  534. default:
  535.   break;
  536. }
  537.       PRIVS_RELINQUISH
  538.       unblock_signals();
  539.       return HANDLED(cmd);
  540.     }
  541.   }
  542.   CONF_ERROR(cmd, "argument must be a valid syslog facility");
  543. }
  544. MODRET set_showsymlinks(cmd_rec *cmd)
  545. {
  546.   config_rec *c;
  547.   CHECK_ARGS(cmd,1);
  548.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  549.   c = add_config_param("ShowSymlinks",1,get_boolean(cmd,1));
  550.   c->flags |= CF_MERGEDOWN;
  551.   return HANDLED(cmd);
  552. }
  553. MODRET set_regex(cmd_rec *cmd, char *param, char *type) {
  554. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  555.   regex_t *preg;
  556.   config_rec *c;
  557.   int ret;
  558.   
  559.   CHECK_ARGS(cmd, 1);
  560.   CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_ANON | CONF_GLOBAL);
  561.   
  562.   log_debug(DEBUG4,"Compiling %s regex '%s'", type, cmd->argv[1]);
  563.   preg = calloc(1, sizeof(regex_t));
  564.   log_debug(DEBUG4,"Allocated %s regex at location %p", type, preg);
  565.   
  566.   if((ret = regcomp(preg, cmd->argv[1], REG_EXTENDED | REG_NOSUB)) != 0) {
  567.     char errmsg[200];
  568.     regerror(ret, preg, errmsg, 200);
  569.     regfree(preg);
  570.     
  571.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"'",cmd->argv[1],
  572.    "' failed regex compilation: ",errmsg,NULL));
  573.   }
  574.   
  575.   c = add_config_param(param, 1, preg);
  576.   c->flags |= CF_MERGEDOWN;
  577.   return HANDLED(cmd);
  578. #else /* no regular expression support at the moment */
  579.   CONF_ERROR(cmd, "The ", param, " directive cannot be used on this system, "
  580.      "as you do not have POSIX compliant regex support.");
  581. #endif
  582. }
  583. MODRET set_allowfilter(cmd_rec *cmd) {
  584.   return set_regex(cmd, "AllowFilter", "allow");
  585. }
  586. MODRET set_denyfilter(cmd_rec *cmd) {
  587.   return set_regex(cmd, "DenyFilter", "deny");
  588. }
  589. MODRET set_pathallowfilter(cmd_rec *cmd) {
  590.   return set_regex(cmd, "PathAllowFilter", "allow");
  591. }
  592. MODRET set_pathdenyfilter(cmd_rec *cmd) {
  593.   return set_regex(cmd, "PathDenyFilter", "deny");
  594. }
  595. MODRET set_allowforeignaddress(cmd_rec *cmd)
  596. {
  597.   int b;
  598.   CHECK_ARGS(cmd,1);
  599.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  600.   if((b = get_boolean(cmd,1)) == -1)
  601.     CONF_ERROR(cmd,"expected boolean argument.");
  602.   add_config_param("AllowForeignAddress",1,(void*)b);
  603.   return HANDLED(cmd);
  604. }
  605. MODRET set_commandbuffersize(cmd_rec *cmd)
  606. {
  607.   int size;
  608.   CHECK_ARGS(cmd, 1);
  609.   CHECK_CONF(cmd, CONF_ROOT | CONF_VIRTUAL | CONF_GLOBAL);
  610.   
  611.   size = atoi(cmd->argv[1]);
  612.   
  613.   add_config_param("CommandBufferSize", 1, (void*) size);
  614.   return HANDLED(cmd);
  615. }
  616. MODRET add_cdpath(cmd_rec *cmd)
  617. {
  618.   CHECK_ARGS(cmd,1);
  619.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  620.   add_config_param_str("CDPath",1,(void*)cmd->argv[1]);
  621.   return HANDLED(cmd);
  622. }
  623. MODRET add_directory(cmd_rec *cmd)
  624. {
  625.   config_rec *c;
  626.   char *dir,*rootdir = NULL;
  627.   int flags = 0;
  628.   CHECK_ARGS(cmd,1);
  629.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  630.   dir = cmd->argv[1];
  631.   if(*dir != '/' && *dir != '~' && 
  632.      (!cmd->config || cmd->config->config_type != CONF_ANON))
  633.     CONF_ERROR(cmd,"relative pathname not allowed in non-anonymous blocks.");
  634.   /* If in anonymous mode, and path is relative, just cat anon root
  635.    * and relative path
  636.    *
  637.    * NOTE [Flood,9/97]: This is no longer necessary, because we don't
  638.    * interpolate anonymous dirs at run-time.
  639.    *
  640.    */
  641.   if(cmd->config && cmd->config->config_type == CONF_ANON &&
  642.      *dir != '/' && *dir != '~') {
  643.     if(strcmp(dir,"*") != 0)
  644.       dir = pdircat(cmd->tmp_pool,"/",dir,NULL);
  645.     rootdir = cmd->config->name;
  646.   }
  647.   else {
  648.     /* if the directory begins with ~, two possibilities:
  649.      * ~username/... : resolve to absolute path for ~username
  650.      * ~/... : intended to be defered until authenciation, where
  651.      *         ~ will be replaced w/ user's home dir
  652.      */
  653.     if(*dir == '~' && (!*(dir+1) || *(dir+1) == '/'))
  654.       flags |= CF_DEFER;
  655.     else {
  656.       dir = dir_best_path(cmd->tmp_pool,dir);
  657.       if(!dir)
  658.         CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,cmd->argv[1],": ",
  659.                        strerror(errno),NULL));
  660.     }
  661.   }
  662.   c = start_sub_config(dir);
  663.   c->argc = 2;
  664.   c->argv = pcalloc(c->pool,3*sizeof(void*));
  665.   if(rootdir)
  666.     c->argv[1] = pstrdup(permanent_pool,rootdir);
  667.   c->config_type = CONF_DIR;
  668.   c->flags |= flags;
  669.   return HANDLED(cmd);
  670. }
  671. MODRET set_allowretrieverestart(cmd_rec *cmd)
  672. {
  673.   config_rec *c;
  674.   CHECK_ARGS(cmd,1);
  675.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_DIR|CONF_ANON|CONF_GLOBAL);
  676.   c = add_config_param("AllowRetrieveRestart",1,
  677.                        (void*)get_boolean(cmd,1));
  678.   c->flags |= CF_MERGEDOWN;
  679.   return HANDLED(cmd);
  680. }
  681. MODRET set_allowstorerestart(cmd_rec *cmd)
  682. {
  683.   config_rec *c;
  684.   CHECK_ARGS(cmd,1);
  685.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_DIR|CONF_ANON|CONF_GLOBAL);
  686.   c = add_config_param("AllowStoreRestart",1,
  687.                        (void*)get_boolean(cmd,1));
  688.   c->flags |= CF_MERGEDOWN;
  689.   return HANDLED(cmd);
  690. }
  691. MODRET add_hidenoaccess(cmd_rec *cmd)
  692. {
  693.   config_rec *c;
  694.   CHECK_ARGS(cmd,0);
  695.   CHECK_CONF(cmd,CONF_DIR|CONF_ANON);
  696.   c = add_config_param("HideNoAccess",1,(void*)1);
  697.   c->flags |= CF_MERGEDOWN;
  698.   return HANDLED(cmd);
  699. }
  700. MODRET add_anonymousgroup(cmd_rec *cmd)
  701. {
  702.   config_rec *c;
  703.   int argc;
  704.   char **argv;
  705.   array_header *acl;
  706.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  707.   if(cmd->argc < 2)
  708.     CONF_ERROR(cmd,"syntax: AnonymousGroup <group-expression>");
  709.   argv = cmd->argv;
  710.   argc = cmd->argc - 1;
  711.   acl = parse_group_expression(cmd->tmp_pool,&argc,argv);
  712.   c = add_config_param("AnonymousGroup",0);
  713.   c->argc = argc;
  714.   c->argv = pcalloc(c->pool,(argc+1) * sizeof(char*));
  715.   argv = (char**)c->argv;
  716.   if(argc && acl)
  717.     while(argc--) {
  718.       *argv++ = pstrdup(permanent_pool,*((char**)acl->elts));
  719.       acl->elts = ((char**)acl->elts) + 1;
  720.     }
  721.   *argv = NULL;
  722.   return HANDLED(cmd);
  723. }
  724. MODRET add_hideuser(cmd_rec *cmd)
  725. {
  726.   config_rec *c;
  727.   struct passwd *pw;
  728.   CHECK_ARGS(cmd,1);
  729.   CHECK_CONF(cmd,CONF_DIR|CONF_ANON);
  730.   pw = auth_getpwnam(cmd->tmp_pool,cmd->argv[1]);
  731.   if(!pw)
  732.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"'",cmd->argv[1],"' is not "
  733.                    "a valid user.",NULL));
  734.   c = add_config_param("HideUser",1,(void*)((int)pw->pw_uid));
  735.   c->flags |= CF_MERGEDOWN;
  736.   return HANDLED(cmd);
  737. }
  738. MODRET add_hidegroup(cmd_rec *cmd)
  739. {
  740.   config_rec *c;
  741.   struct group *gr;
  742.   CHECK_ARGS(cmd,1);
  743.   CHECK_CONF(cmd,CONF_DIR|CONF_ANON);
  744.   gr = auth_getgrnam(cmd->tmp_pool,cmd->argv[1]);
  745.   if(!gr)
  746.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"'",cmd->argv[1],"' is not "
  747.                    "a valid group.",NULL));
  748.   c = add_config_param("HideGroup",1,(void*)((int)gr->gr_gid));
  749.   c->flags |= CF_MERGEDOWN;
  750.   return HANDLED(cmd);
  751. }
  752. MODRET add_groupowner(cmd_rec *cmd)
  753. {
  754.   config_rec *c;
  755.   CHECK_ARGS(cmd,1);
  756.   CHECK_CONF(cmd,CONF_ANON|CONF_DIR|CONF_DYNDIR);
  757.   c = add_config_param_str("GroupOwner",1,cmd->argv[1]);
  758.   c->flags |= CF_MERGEDOWN;
  759.   return HANDLED(cmd);
  760. }
  761. MODRET set_allowoverwrite(cmd_rec *cmd)
  762. {
  763.   config_rec *c;
  764.   CHECK_ARGS(cmd,1);
  765.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_DIR|CONF_GLOBAL);
  766.   c = add_config_param("AllowOverwrite",1,(void*)get_boolean(cmd,1));
  767.   c->flags |= CF_MERGEDOWN;
  768.   return HANDLED(cmd);
  769. }
  770. MODRET set_hiddenstor(cmd_rec *cmd)
  771. {
  772.   config_rec *c;
  773.   CHECK_ARGS(cmd,1);
  774.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_DIR|CONF_GLOBAL);
  775.   c = add_config_param("HiddenStor",1,(void*)get_boolean(cmd,1));
  776.   c->flags |= CF_MERGEDOWN;
  777.   return HANDLED(cmd);
  778. }
  779. MODRET end_directory(cmd_rec *cmd)
  780. {
  781.   CHECK_ARGS(cmd,0);
  782.   CHECK_CONF(cmd,CONF_DIR);
  783.   end_sub_config();
  784.   return HANDLED(cmd);
  785. }
  786. MODRET add_anonymous(cmd_rec *cmd)
  787. {
  788.   config_rec *c;
  789.   char *dir;
  790.   CHECK_ARGS(cmd,1);
  791.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  792.   dir = cmd->argv[1];
  793.   if(*dir != '/' && *dir != '~')
  794.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"(",dir,") absolute pathname "
  795.                "required.",NULL));
  796.   if(strchr(dir,'*'))
  797.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"(",dir,") wildcards not allowed "
  798.                "in pathname.",NULL));
  799.   if(!strcmp(dir,"/"))
  800.     CONF_ERROR(cmd,"'/' not permitted for anonymous root directory.");
  801.   if(*(dir+strlen(dir)-1) != '/')
  802.     dir = pstrcat(cmd->tmp_pool,dir,"/",NULL);
  803.   if(!dir)
  804.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,cmd->argv[1],": ",
  805.                strerror(errno),NULL));
  806.   c = start_sub_config(dir);
  807.   c->config_type = CONF_ANON;
  808.   return HANDLED(cmd);
  809. }
  810. MODRET set_anonrequirepassword(cmd_rec *cmd)
  811. {
  812.   CHECK_ARGS(cmd,1);
  813.   CHECK_CONF(cmd,CONF_ANON);
  814.   add_config_param("AnonRequirePassword",1,
  815.                    (void*)get_boolean(cmd,1));
  816.   return HANDLED(cmd);
  817. }
  818. MODRET end_anonymous(cmd_rec *cmd)
  819. {
  820.   CHECK_ARGS(cmd,0);
  821.   CHECK_CONF(cmd,CONF_ANON);
  822.   end_sub_config();
  823.   return HANDLED(cmd);
  824. }
  825. MODRET add_global(cmd_rec *cmd)
  826. {
  827.   config_rec *c;
  828.   CHECK_ARGS(cmd,0);
  829.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL);
  830.   c = start_sub_config("Global");
  831.   c->config_type = CONF_GLOBAL;
  832.   return HANDLED(cmd);
  833. }
  834. MODRET end_global(cmd_rec *cmd)
  835. {
  836.   CHECK_ARGS(cmd,0);
  837.   CHECK_CONF(cmd,CONF_GLOBAL);
  838.   end_sub_config();
  839.   return HANDLED(cmd);
  840. }
  841. MODRET add_limit(cmd_rec *cmd)
  842. {
  843.   config_rec *c;
  844.   int cargc;
  845.   char **argv,**cargv;
  846.   if(cmd->argc < 2)
  847.     CONF_ERROR(cmd,"directive requires one or more FTP commands.");
  848.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_DIR|CONF_ANON|CONF_DYNDIR|CONF_GLOBAL);
  849.   c = start_sub_config("Limit");
  850.   c->config_type = CONF_LIMIT;
  851.   cargc = cmd->argc-1;
  852.   cargv = cmd->argv+1;
  853.   c->argc = cmd->argc-1;
  854.   c->argv = pcalloc(c->pool,cmd->argc*sizeof(void*));
  855.   argv = (char**)c->argv;
  856.   while(cargc--)
  857.     *argv++ = pstrdup(permanent_pool,*cargv++);
  858.   *argv = NULL;
  859.   return HANDLED(cmd);
  860. }
  861. MODRET add_order(cmd_rec *cmd)
  862. {
  863.   int order = -1,argc = cmd->argc;
  864.   char *arg = "",**argv = cmd->argv+1;
  865.   CHECK_CONF(cmd,CONF_LIMIT);
  866.   while(--argc && *argv)
  867.     arg = pstrcat(cmd->tmp_pool,arg,*argv++,NULL);
  868.   if(!strcasecmp(arg,"allow,deny"))
  869.     order = ORDER_ALLOWDENY;
  870.   else if(!strcasecmp(arg,"deny,allow"))
  871.     order = ORDER_DENYALLOW;
  872.   else
  873.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"'",arg,"': invalid argument",NULL));
  874.   add_config_param("Order",1,(void*)order);
  875.   return HANDLED(cmd);
  876. }
  877. MODRET _add_allow_deny_user(cmd_rec *cmd, char *name)
  878. {
  879.   config_rec *c;
  880.   char **argv;
  881.   int argc;
  882.   array_header *acl = NULL;
  883.   CHECK_CONF(cmd,CONF_LIMIT);
  884.   if(cmd->argc < 2)
  885.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"syntax: ",name,
  886.                " <user-expression>",NULL));
  887.   argv = cmd->argv;
  888.   argc = cmd->argc-1;
  889.   acl = parse_user_expression(cmd->tmp_pool,&argc,argv);
  890.   c = add_config_param(name,0);
  891.   c->argc = argc;
  892.   c->argv = pcalloc(c->pool,(argc+1) * sizeof(char*));
  893.   argv = (char**)c->argv;
  894.   while(argc--) {
  895.     *argv++ = pstrdup(permanent_pool,*((char**)acl->elts));
  896.     acl->elts = ((char**)acl->elts) + 1;
  897.   }
  898.   *argv = NULL;
  899.   return HANDLED(cmd);
  900. }
  901. MODRET _add_allow_deny_group(cmd_rec *cmd, char *name)
  902. {
  903.   config_rec *c;
  904.   char **argv;
  905.   int argc;
  906.   array_header *acl = NULL;
  907.   CHECK_CONF(cmd,CONF_LIMIT);
  908.   if(cmd->argc < 2)
  909.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"syntax: ",name,
  910.                " <group-expression>",NULL));
  911.   argv = cmd->argv;
  912.   argc = cmd->argc-1;
  913.   acl = parse_group_expression(cmd->tmp_pool,&argc,argv);
  914.   c = add_config_param(name,0);
  915.   c->argc = argc;
  916.   c->argv = pcalloc(c->pool,(argc+1) * sizeof(char*));
  917.   argv = (char**)c->argv;
  918.   while(argc--) {
  919.     *argv++ = pstrdup(permanent_pool,*((char**)acl->elts));
  920.     acl->elts = ((char**)acl->elts) + 1;
  921.   }
  922.   *argv = NULL;
  923.   return HANDLED(cmd);
  924. }
  925. MODRET add_allowgroup(cmd_rec *cmd)
  926. {
  927.   return _add_allow_deny_group(cmd,"AllowGroup");
  928. }
  929. MODRET add_denygroup(cmd_rec *cmd)
  930. {
  931.   return _add_allow_deny_group(cmd,"DenyGroup");
  932. }
  933. MODRET add_allowuser(cmd_rec *cmd)
  934. {
  935.   return _add_allow_deny_user(cmd,"AllowUser");
  936. }
  937. MODRET add_denyuser(cmd_rec *cmd)
  938. {
  939.   return _add_allow_deny_user(cmd,"DenyUser");
  940. }
  941. MODRET _add_allow_deny(cmd_rec *cmd, char *name)
  942. {
  943.   int argc;
  944.   char *s,*ent,**argv;
  945.   array_header *acl;
  946.   config_rec *c;
  947.   CHECK_CONF(cmd,CONF_LIMIT);
  948.   /* Syntax: allow [from] [all|none]|host|network[,...] */
  949.   acl = make_array(cmd->tmp_pool,cmd->argc,sizeof(char*));
  950.   argc = cmd->argc-1; argv = cmd->argv;
  951.   /* Skip optional "from" keyword */
  952.   while(argc && *(argv+1)) {
  953.     if(!strcasecmp("from",*(argv+1))) {
  954.       argv++; argc--; continue;
  955.     } else if(!strcasecmp("all",*(argv+1))) {
  956.       *((char**)push_array(acl)) = "ALL";
  957.       argc = 0;
  958.     } else if(!strcasecmp("none",*(argv+1))) {
  959.       *((char**)push_array(acl)) = "NONE";
  960.       argc = 0;
  961.     }
  962.     break;
  963.   }
  964.   while(argc-- && *(++argv)) {
  965.     s = pstrdup(cmd->tmp_pool,*argv);
  966.     /* parse the string into coma-delimited entries */
  967.     while((ent = get_token(&s,",")) != NULL)
  968.       if(*ent) {
  969.         if(!strcasecmp(ent,"all") || !strcasecmp(ent,"none")) {
  970.           acl->nelts = 0; argc = 0; break;
  971.         }
  972.         *((char**)push_array(acl)) = ent;
  973.       }
  974.   }
  975.   if(!acl->nelts)
  976.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"syntax: ",name,
  977.                    " [from] [all|none]|host|network[,...]",NULL));
  978.   c = add_config_param(name,0);
  979.   c->argc = acl->nelts;
  980.   c->argv = pcalloc(c->pool,(c->argc+1) * sizeof(char*));
  981.   argv = (char**)c->argv;
  982.   while(acl->nelts--) {
  983.     *argv++ = pstrdup(permanent_pool,*((char**)acl->elts));
  984.     acl->elts = ((char**)acl->elts) + 1;
  985.   }
  986.   *argv = NULL;
  987.   return HANDLED(cmd);
  988. }
  989. MODRET add_allow(cmd_rec *cmd)
  990. {
  991.   return _add_allow_deny(cmd,"Allow");
  992. }
  993. MODRET add_deny(cmd_rec *cmd)
  994. {
  995.   return _add_allow_deny(cmd,"Deny");
  996. }
  997. MODRET set_denyall(cmd_rec *cmd)
  998. {
  999.   CHECK_ARGS(cmd,0);
  1000.   CHECK_CONF(cmd,CONF_LIMIT|CONF_ANON);
  1001.   add_config_param("DenyAll",1,(void*)1);
  1002.   return HANDLED(cmd);
  1003. }
  1004. MODRET set_allowall(cmd_rec *cmd)
  1005. {
  1006.   CHECK_ARGS(cmd,0);
  1007.   CHECK_CONF(cmd,CONF_LIMIT|CONF_ANON);
  1008.   add_config_param("AllowAll",1,(void*)1);
  1009.   return HANDLED(cmd);
  1010. }
  1011. MODRET end_limit(cmd_rec *cmd)
  1012. {
  1013.   CHECK_ARGS(cmd,0);
  1014.   CHECK_CONF(cmd,CONF_LIMIT);
  1015.   end_sub_config();
  1016.   return HANDLED(cmd);
  1017. }
  1018. MODRET set_ignorehidden(cmd_rec *cmd)
  1019. {
  1020.   config_rec *c;
  1021.   CHECK_ARGS(cmd,1);
  1022.   CHECK_CONF(cmd,CONF_LIMIT);
  1023.   c = add_config_param("IgnoreHidden",1,(void*)get_boolean(cmd,1));
  1024.   return HANDLED(cmd);
  1025. }
  1026. MODRET add_useralias(cmd_rec *cmd)
  1027. {
  1028.   CHECK_ARGS(cmd,2);
  1029.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1030.   add_config_param_str("UserAlias",2,(void*)cmd->argv[1],(void*)cmd->argv[2]);
  1031.   return HANDLED(cmd);
  1032. }
  1033. MODRET set_displaylogin(cmd_rec *cmd)
  1034. {
  1035.   config_rec *c;
  1036.   CHECK_ARGS(cmd,1);
  1037.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1038.   c = add_config_param_str("DisplayLogin",1,(void*)cmd->argv[1]);
  1039.   c->flags |= CF_MERGEDOWN;
  1040.   return HANDLED(cmd);
  1041. }
  1042. MODRET set_displayconnect(cmd_rec *cmd)
  1043. {
  1044.   config_rec *c;
  1045.   CHECK_ARGS(cmd,1);
  1046.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  1047.   c = add_config_param_str("DisplayConnect",1,(void*)cmd->argv[1]);
  1048.   c->flags |= CF_MERGEDOWN;
  1049.   return HANDLED(cmd);
  1050. }
  1051. MODRET set_displayfirstchdir(cmd_rec *cmd)
  1052. {
  1053.   config_rec *c;
  1054.   CHECK_ARGS(cmd,1);
  1055.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_DIR|CONF_GLOBAL);
  1056.   c = add_config_param_str("DisplayFirstChdir",1,(void*)cmd->argv[1]);
  1057.   c->flags |= CF_MERGEDOWN;
  1058.   return HANDLED(cmd);
  1059. }
  1060. MODRET set_displayquit(cmd_rec *cmd)
  1061. {
  1062.   config_rec *c;
  1063.   CHECK_ARGS(cmd,1);
  1064.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1065.   c = add_config_param_str("DisplayQuit",1,(void*)cmd->argv[1]);
  1066.   c->flags |= CF_MERGEDOWN;
  1067.   return HANDLED(cmd);
  1068. }
  1069. MODRET set_displaygoaway(cmd_rec *cmd)
  1070. {
  1071.   config_rec *c;
  1072.   CHECK_ARGS(cmd,1);
  1073.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1074.   c = add_config_param_str("DisplayGoAway",1,(void*)cmd->argv[1]);
  1075.   c->flags |= CF_MERGEDOWN;
  1076.   return HANDLED(cmd);
  1077. }
  1078. MODRET set_authaliasonly(cmd_rec *cmd)
  1079. {
  1080.   int b;
  1081.   CHECK_ARGS(cmd,1);
  1082.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1083.   if((b = get_boolean(cmd,1)) == -1)
  1084.     CONF_ERROR(cmd,"expected boolean argument.");
  1085.   add_config_param("AuthAliasOnly",1,(void*)b);
  1086.   return HANDLED(cmd);
  1087. }
  1088. MODRET add_virtualhost(cmd_rec *cmd)
  1089. {
  1090.   server_rec *s;
  1091.   CHECK_ARGS(cmd,1);
  1092.   CHECK_CONF(cmd,CONF_ROOT);
  1093.   if(cmd->server != main_server)
  1094.     CONF_ERROR(cmd,"directive cannot be nested.");
  1095.   s = start_new_server(cmd->argv[1]);
  1096.   if(!s)
  1097.     CONF_ERROR(cmd,"unable to create virtual server configuration.");
  1098.   s->ServerPort = main_server->ServerPort;
  1099.   return HANDLED(cmd);
  1100. }
  1101. MODRET end_virtualhost(cmd_rec *cmd)
  1102. {
  1103.   CHECK_ARGS(cmd,0);
  1104.   if(cmd->server == main_server)
  1105.     CONF_ERROR(cmd,"must be matched with <VirtualServer> directive.");
  1106.   end_new_server();
  1107.   return HANDLED(cmd);
  1108. }
  1109. /* Display a file via a given response numeric.  File is displayed
  1110.  * in normal RFC959 multline mode, unless MultilineRFC2228 is set.
  1111.  * Returns: -1 on error
  1112.  *          0 if file display
  1113.  */
  1114. int core_display_file(const char *numeric, const char *fn)
  1115. {
  1116.   fsdir_t *fp;
  1117.   char buf[1024];
  1118.   int len, max, fd, classes_enabled;
  1119.   unsigned long fs_size = 0;
  1120.   pool *p;
  1121.   xaset_t *s;
  1122.   char *outs,*mg_time,mg_size[12],mg_max[12] = "unlimited", mg_class_limit[12];
  1123.   char mg_cur[12],mg_xfer_bytes[12],mg_cur_class[12], config_class_users[128];
  1124.   short first = 1;
  1125. #if defined(HAVE_SYS_STATVFS_H) || defined(HAVE_SYS_VFS_H)
  1126.   fs_size = get_fs_size((char*)fn);
  1127.   snprintf(mg_size, sizeof(mg_size), "%lu", fs_size);
  1128. #endif
  1129.   if((fp = fs_open_canon(fn,O_RDONLY,&fd)) == NULL)
  1130.     return -1;
  1131.   p = make_sub_pool(permanent_pool);
  1132.   s = (session.anon_config ? session.anon_config->subset : main_server->conf);
  1133.   mg_time = fmt_time(time(NULL));
  1134.   snprintf(mg_size, sizeof(mg_size), "%lu",fs_size);
  1135.   max = get_param_int(s,"MaxClients",FALSE);
  1136.   snprintf(mg_cur, sizeof(mg_cur), "%d",(int)get_param_int(main_server->conf,
  1137.           "CURRENT-CLIENTS",FALSE)+1);
  1138.   if((classes_enabled = get_param_int(CURRENT_CONF,"Classes",FALSE)) < 0)
  1139.     classes_enabled = 0;
  1140.   
  1141.   if (classes_enabled && session.class && session.class->name) {
  1142. snprintf(config_class_users,sizeof(config_class_users),"%s-%s","CURRENT-CLIENTS-CLASS",session.class->name);
  1143. snprintf(mg_cur_class,sizeof(mg_cur_class),"%d",(int)get_param_int(main_server->conf,config_class_users,FALSE)+1);
  1144. snprintf(mg_class_limit, sizeof(mg_class_limit), "%u",
  1145.  session.class->max_connections);
  1146.   } else
  1147. mg_cur_class[0] = 0;
  1148.   snprintf(mg_xfer_bytes, sizeof(mg_xfer_bytes), "%lu",
  1149.    session.total_bytes >> 10);
  1150.   if(max != -1)
  1151.     snprintf(mg_max, sizeof(mg_max), "%d",max);
  1152.   while(fs_gets(buf,sizeof(buf),fp,fd) != NULL) {
  1153.     buf[1023] = '';
  1154.     len = strlen(buf);
  1155.     while(len && (buf[len-1] == 'r' || buf[len-1] == 'n')) {
  1156.       buf[len-1] = '';
  1157.       len--;
  1158.     }
  1159.     outs = sreplace(p,buf,
  1160.              "%T",mg_time,
  1161.              "%F",mg_size,
  1162.      "%C",(session.cwd[0] ? session.cwd : "(none)"),
  1163.      "%R",(session.c && session.c->remote_name ?
  1164.    session.c->remote_name : "(unknown)"),
  1165.      "%L",main_server->ServerFQDN,
  1166.              "%u",session.ident_user,
  1167.      "%U",(char*)get_param_ptr(main_server->conf,"USER",FALSE),
  1168.      "%K",mg_xfer_bytes,
  1169.      "%M",mg_max,
  1170.              "%N",mg_cur,
  1171.      "%E",main_server->ServerAdmin,
  1172.      "%V",main_server->ServerName,
  1173.      "%x",(classes_enabled && session.class) ? session.class->name : "",
  1174.      "%y",mg_cur_class,
  1175.      "%z",mg_class_limit,
  1176.              NULL);
  1177.     if(first) {
  1178.       send_response_raw("%s-%s",numeric,outs);
  1179.       first=0;
  1180.     } else {
  1181.       if(MultilineRFC2228)
  1182.         send_response_raw("%s-%s",numeric,outs);
  1183.       else
  1184.         send_response_raw(" %s",outs);
  1185.       }
  1186.   }
  1187.   fs_close(fp,fd);
  1188.   return 0;
  1189. }
  1190. MODRET cmd_quit(cmd_rec *cmd)
  1191. {
  1192.   char *display = NULL;
  1193.   
  1194.   if(session.flags & SF_ANON)
  1195. display = (char*)get_param_ptr(session.anon_config->subset, "DisplayQuit", FALSE);
  1196.   if(!display)
  1197. display = (char*)get_param_ptr(cmd->server->conf, "DisplayQuit", FALSE);
  1198.   if(display) {
  1199. core_display_file(R_221, display);
  1200. /* hack or feature, core_display_file() always puts a hyphen
  1201.  * on the last line
  1202.  */
  1203. send_response(R_221, "");
  1204.   } else {
  1205.         send_response(R_221, "Goodbye.");
  1206.   }
  1207.   
  1208.   log_pri(LOG_NOTICE, "FTP session closed.");
  1209.   end_login(0);
  1210.   return HANDLED(cmd); /* Avoid compiler warning */
  1211. }
  1212. /* per RFC959, directory responses for MKD and PWD should be
  1213.  * "dir_name" (w/ quote).  For directories that CONTAIN quotes,
  1214.  * the add'l quotes must be duplicated.
  1215.  */
  1216. static char *quote_dir(cmd_rec *cmd, char *dir)
  1217. {
  1218.   return sreplace(cmd->tmp_pool,dir,""","""",NULL);
  1219. }
  1220. MODRET cmd_pwd(cmd_rec *cmd)
  1221. {
  1222.   if(cmd->argc != 1) {
  1223.     add_response_err(R_500,"'%s' not understood.",get_full_cmd(cmd));
  1224.     return ERROR(cmd);
  1225.   }
  1226.   
  1227.   add_response(R_257,""%s" is current directory.",
  1228.                 quote_dir(cmd,session.vwd));
  1229.   return HANDLED(cmd);
  1230. }
  1231. MODRET cmd_pasv(cmd_rec *cmd)
  1232. {
  1233.   union {
  1234.     p_in_addr_t addr;
  1235.     unsigned char u[4];
  1236.   } addr;
  1237.   union {
  1238.     unsigned short port;
  1239.     unsigned char u[2];
  1240.   } port;
  1241.   if(cmd->argc != 1) {
  1242.     add_response_err(R_500,"'%s' not understood.",get_full_cmd(cmd));
  1243.     return ERROR(cmd);
  1244.   }
  1245.   /* If we already have a passive listen data connection open,
  1246.    * kill it.
  1247.    */
  1248.   if(session.d) {
  1249.     inet_close(session.d->pool,session.d);
  1250.     session.d = NULL;
  1251.   }
  1252.   session.d = inet_create_connection(session.pool,NULL,-1,
  1253. session.c->local_ipaddr,INPORT_ANY,FALSE);
  1254.   if(!session.d)
  1255.      return ERROR_MSG(cmd,R_425,
  1256.                      "Unable to build data connection: Internal error.");
  1257.   inet_setblock(session.pool,session.d);
  1258.   inet_listen(session.pool,session.d,1);
  1259.   session.d->inf = io_open(session.pool,session.d->listen_fd,IO_READ);
  1260.   /* Now tell the client our address/port */
  1261.   session.data_port = session.d->local_port;
  1262.   session.flags |= SF_PASSIVE;
  1263.   addr.addr = *session.d->local_ipaddr;
  1264.   port.port = htons(session.data_port);
  1265.   log_debug(DEBUG1,"Entering Passive Mode (%u,%u,%u,%u,%u,%u)",
  1266. (int)addr.u[0],(int)addr.u[1],(int)addr.u[2],
  1267. (int)addr.u[3],(int)port.u[0],(int)port.u[1]);
  1268.   add_response(R_227, "Entering Passive Mode (%u,%u,%u,%u,%u,%u)",
  1269.                 (int)addr.u[0],(int)addr.u[1],(int)addr.u[2],
  1270.                 (int)addr.u[3],(int)port.u[0],(int)port.u[1]);
  1271.   return HANDLED(cmd);
  1272. }
  1273. MODRET cmd_port(cmd_rec *cmd)
  1274. {
  1275.   union {
  1276.     p_in_addr_t addr;
  1277.     unsigned char u[4];
  1278.   } addr;
  1279.   union {
  1280.     unsigned short port;
  1281.     unsigned char u[2];
  1282.   } port;
  1283.   char *a,*endp,*arg;
  1284.   int i,cnt = 0;
  1285.   int allow_foreign_addr = 0;
  1286.   if(cmd->argc != 2) {
  1287.     add_response_err(R_500,"'%s' not understood.",get_full_cmd(cmd));
  1288.     return ERROR(cmd);
  1289.   }
  1290.   /* Format is h1,h2,h3,h4,p1,p2 (ASCII in network order) */
  1291.   a = pstrdup(cmd->tmp_pool,cmd->argv[1]);
  1292.   while(a && *a && cnt < 6) {
  1293.     arg = strsep(&a,",");
  1294.     if(!arg && a && *a) {
  1295.       arg = a;
  1296.       a = NULL;
  1297.     } else if(!arg)
  1298.       break;
  1299.     i = strtol(arg,&endp,10);
  1300.     if(*endp || i < 0 || i > 255)
  1301.       break;
  1302.     if(cnt < 4)
  1303.       addr.u[cnt++] = (unsigned char)i;
  1304.     else
  1305.       port.u[cnt++ - 4] = (unsigned char)i;
  1306.   }
  1307.   if(cnt != 6 || (a && *a))
  1308.     return ERROR_MSG(cmd,R_501,"Illegal PORT command.");
  1309.   /* Make sure that the address specified matches the address
  1310.    * that the control connection is coming from.
  1311.    */
  1312.   allow_foreign_addr = get_param_int(TOPLEVEL_CONF,"AllowForeignAddress",FALSE);
  1313.   if(allow_foreign_addr != 1) {
  1314.     if(addr.addr.s_addr != session.c->remote_ipaddr->s_addr ||
  1315.        !port.port) {
  1316.       log_pri(LOG_WARNING,"refused PORT %s from %s (address mismatch)",
  1317.        cmd->arg,session.c->remote_name);
  1318.       return ERROR_MSG(cmd,R_500,"Illegal PORT command.");
  1319.     }
  1320.   }
  1321.   /* Additionally, make sure that the port number used is a "high
  1322.    * numbered" port, to avoid bounce attacks
  1323.    */
  1324.   if(ntohs(port.port) < 1024) {
  1325.     log_pri(LOG_WARNING,"refused PORT %s from %s (bounce attack)",
  1326.                         cmd->arg,session.c->remote_name);
  1327.     return ERROR_MSG(cmd,R_500,"Illegal PORT command.");
  1328.   }
  1329.   memcpy(&session.data_addr, &addr.addr, sizeof(session.data_addr));
  1330.   session.data_port = ntohs(port.port);
  1331.   session.flags &= (SF_ALL^SF_PASSIVE);
  1332.   /* If we already have a data connection open, kill it.
  1333.    */
  1334.   if(session.d) {
  1335.     inet_close(session.d->pool,session.d);
  1336.     session.d = NULL;
  1337.   }
  1338.   add_response(R_200,"PORT command successful.");
  1339.   return HANDLED(cmd);
  1340. }
  1341. MODRET cmd_help(cmd_rec *cmd)
  1342. {
  1343.   int i,c = 0;
  1344.   char buf[9];
  1345.   if(cmd->argc == 1) {
  1346.     /* Print help for all commands */
  1347.     char *outa[8];
  1348.     char *outs = "";
  1349.     bzero(outa,sizeof(outa));
  1350.     add_response(R_214,
  1351.       "The following commands are recognized (* =>'s unimplemented).");
  1352.     for(i = 0; _help[i].cmd; i++) {
  1353.       if(_help[i].implemented)
  1354.         outa[c++] = _help[i].cmd;
  1355.       else
  1356.         outa[c++] = pstrcat(cmd->tmp_pool,_help[i].cmd,"*",NULL);
  1357.       /* 8 rows */
  1358.       if(((i+1) % 8 == 0) || !_help[i+1].cmd) {
  1359.         int j;
  1360.         for(j = 0; j < 8; j++) {
  1361.           if(outa[j]) {
  1362.             snprintf(buf, sizeof(buf), "%-8s",outa[j]);
  1363.             outs = pstrcat(cmd->tmp_pool,outs,buf,NULL);
  1364.           } else
  1365.             break;
  1366.         }
  1367.         if(*outs)
  1368.           add_response(R_214,"%s",outs);
  1369.         outs = "";
  1370.         c = 0;
  1371.         bzero(outa,sizeof(outa));
  1372.       }
  1373.     }
  1374.     add_response(R_214,"Direct comments to %s.",
  1375.                          (cmd->server->ServerAdmin ? cmd->server->ServerAdmin :
  1376.                           "ftp-admin"));
  1377.   } else {
  1378.     char *cp;
  1379.     for(cp = cmd->argv[1]; *cp; cp++)
  1380.       *cp = toupper(*cp);
  1381.     if(!strcmp(cmd->argv[1],"SITE"))
  1382.       return call_module(&site_module,site_dispatch,cmd);
  1383.     for(i = 0; _help[i].cmd; i++)
  1384.       if(!strcasecmp(cmd->argv[1],_help[i].cmd)) {
  1385.         add_response(R_214,"Syntax: %s %s",cmd->argv[1],_help[i].syntax);
  1386.         return HANDLED(cmd);
  1387.       }
  1388.     add_response_err(R_502,"Unknown command '%s'.",cmd->argv[1]);
  1389.     return ERROR(cmd);
  1390.   }
  1391.   return HANDLED(cmd);
  1392. }
  1393. MODRET cmd_syst(cmd_rec *cmd)
  1394. {
  1395.   add_response(R_215,"UNIX Type: L8");
  1396.   return HANDLED(cmd);
  1397. }
  1398. int core_chmod(cmd_rec *cmd, char *dir, mode_t mode) {
  1399.   if(!dir_check(cmd->tmp_pool, "SITE_CHMOD", "WRITE", dir, NULL))
  1400.     return -1;
  1401.   return fs_chmod(dir,mode);
  1402. }
  1403. MODRET _chdir(cmd_rec *cmd,char *ndir)
  1404. {
  1405.   char *display = NULL;
  1406.   char *dir,*odir,*cdir;
  1407.   config_rec *cdpath;
  1408.   int showsymlinks;
  1409.   
  1410.   odir = ndir;
  1411.   showsymlinks = get_param_int(TOPLEVEL_CONF,"ShowSymlinks",FALSE);
  1412.   if(showsymlinks == -1)
  1413.     showsymlinks = 1;
  1414.   if(showsymlinks) {
  1415.     dir = dir_realpath(cmd->tmp_pool,ndir);
  1416.     if(!dir || !dir_check_full(cmd->tmp_pool,cmd->argv[0],cmd->group,dir,NULL) ||
  1417.         fs_chdir(dir,0) == -1) {
  1418.       for(cdpath = find_config(main_server->conf,CONF_PARAM,"CDPath",TRUE);
  1419.   cdpath != NULL; cdpath =
  1420.     find_config_next(cdpath,cdpath->next,CONF_PARAM,"CDPath",TRUE)) {
  1421. cdir = (char *) malloc(strlen(cdpath->argv[0]) + strlen(ndir) + 2);
  1422. snprintf(cdir, strlen(cdpath->argv[0]) + strlen(ndir) + 2,
  1423.  "%s%s%s", (char *) cdpath->argv[0],
  1424.  ((char *) cdpath->argv[0])[strlen(cdpath->argv[0]) - 1] == '/' ? "" : "/",
  1425.  ndir);
  1426. dir = dir_realpath(cmd->tmp_pool,cdir);
  1427. free(cdir);
  1428. if(dir &&
  1429.    dir_check_full(cmd->tmp_pool,cmd->argv[0],cmd->group,dir,NULL) &&
  1430.    fs_chdir(dir,0) != -1) {
  1431.   break;
  1432. }
  1433.       }
  1434.       if(!cdpath) {
  1435. add_response_err(R_550,"%s: %s",odir,strerror(errno));
  1436. return ERROR(cmd);
  1437.       }
  1438.     }
  1439.   } else {
  1440.     /* virtualize the chdir */
  1441.     ndir = dir_virtual_chdir(cmd->tmp_pool,ndir);
  1442.     dir = dir_realpath(cmd->tmp_pool,ndir);
  1443.     if(!dir || !dir_check_full(cmd->tmp_pool,cmd->argv[0],cmd->group,dir,NULL) ||
  1444.         fs_chdir_canon(ndir,1) == -1) {
  1445.       for(cdpath = find_config(main_server->conf,CONF_PARAM,"CDPath",TRUE);
  1446.   cdpath != NULL; cdpath =
  1447.     find_config_next(cdpath,cdpath->next,CONF_PARAM,"CDPath",TRUE)) {
  1448. cdir = (char *) malloc(strlen(cdpath->argv[0]) + strlen(ndir) + 2);
  1449. snprintf(cdir, strlen(cdpath->argv[0]) + strlen(ndir) + 2,
  1450.  "%s%s%s", (char *) cdpath->argv[0],
  1451. ((char *)cdpath->argv[0])[strlen(cdpath->argv[0]) - 1] == '/' ? "" : "/",
  1452. ndir);
  1453. ndir = dir_virtual_chdir(cmd->tmp_pool,cdir);
  1454. dir = dir_realpath(cmd->tmp_pool,ndir);
  1455. free(cdir);
  1456. if(dir &&
  1457.    dir_check_full(cmd->tmp_pool,cmd->argv[0],cmd->group,dir,NULL) &&
  1458.    fs_chdir_canon(ndir,1) != -1) {
  1459.   break;
  1460. }
  1461.       }
  1462.       if(!cdpath) {
  1463. add_response_err(R_550,"%s: %s",odir,strerror(errno));
  1464. return ERROR(cmd);
  1465.       }
  1466.     }
  1467.   }
  1468.   sstrncpy(session.cwd,fs_getcwd(),sizeof(session.cwd));
  1469.   sstrncpy(session.vwd,fs_getvwd(),sizeof(session.vwd));
  1470.   log_run_cwd(session.cwd);
  1471.   if(session.dir_config)
  1472.     display = (char*)get_param_ptr(session.dir_config->subset,
  1473.                                    "DisplayFirstChdir",FALSE);
  1474.   if(!display && session.anon_config)
  1475.     display = (char*)get_param_ptr(session.anon_config->subset,
  1476.                                    "DisplayFirstChdir",FALSE);
  1477.   if(!display)
  1478.     display = (char*)get_param_ptr(cmd->server->conf,
  1479.                                    "DisplayFirstChdir",FALSE);
  1480.   if(display) {
  1481.     config_rec *c;
  1482.     time_t last;
  1483.     struct stat sbuf;
  1484.     c = find_config(cmd->server->conf,CONF_USERDATA,session.cwd,FALSE);
  1485.     if(!c) {
  1486.       time(&last);
  1487.       c = add_config_set(&cmd->server->conf,session.cwd);
  1488.       c->config_type = CONF_USERDATA;
  1489.       c->argc = 1;
  1490.       c->argv = pcalloc(c->pool,sizeof(void**) * 2);
  1491.       c->argv[0] = (void*)last;
  1492.       last = (time_t)0L;
  1493.     } else {
  1494.       last = (time_t)c->argv[0];
  1495.       c->argv[0] = (void*)time(NULL);
  1496.     }
  1497.     if(fs_stat(display,&sbuf) != -1 && !S_ISDIR(sbuf.st_mode) &&
  1498.        sbuf.st_mtime > last)
  1499.       core_display_file(R_250,display);
  1500.   }
  1501.   add_response(R_250,"CWD command successful.");
  1502.   return HANDLED(cmd);
  1503. }
  1504. MODRET cmd_rmd(cmd_rec *cmd)
  1505. {
  1506.   char *dir;
  1507. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1508.   regex_t *preg;
  1509. #endif
  1510.   if(cmd->argc < 2) {
  1511.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1512.     return ERROR(cmd);
  1513.   }
  1514. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1515.   preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathAllowFilter",FALSE);
  1516.   if(preg && regexec(preg,cmd->arg,0,NULL,0) != 0) {
  1517.     add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1518.     return ERROR(cmd);
  1519.   }
  1520.   preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathDenyFilter",FALSE);
  1521.   
  1522.   if(preg && regexec(preg,cmd->arg,0,NULL,0) == 0) {
  1523.     add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1524.     return ERROR(cmd);
  1525.   }
  1526. #endif
  1527.   dir = dir_realpath(cmd->tmp_pool,cmd->arg);
  1528.   if(!dir || !dir_check(cmd->tmp_pool,cmd->argv[0],cmd->group,dir,NULL) ||
  1529.      rmdir(dir) == -1) {
  1530.     add_response_err(R_550,"%s: %s",cmd->arg,strerror(errno));
  1531.     return ERROR(cmd);
  1532.   } else
  1533.     add_response(R_250,"%s command successful.",cmd->argv[0]);
  1534.   return HANDLED(cmd);
  1535. }
  1536. MODRET cmd_mkd(cmd_rec *cmd)
  1537. {
  1538.   char *dir;
  1539. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1540.   regex_t *preg;
  1541. #endif
  1542.   if(cmd->argc < 2) {
  1543.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1544.     return ERROR(cmd);
  1545.   }
  1546.   if(strchr(cmd->arg,'*')) {
  1547.     add_response_err(R_550,"%s: Invalid directory name", cmd->argv[1]);
  1548.     return ERROR(cmd);
  1549.   }
  1550. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1551.     preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathAllowFilter",FALSE);
  1552.     if(preg && regexec(preg,cmd->arg,0,NULL,0) != 0) {
  1553.       add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1554.       return ERROR(cmd);
  1555.     }
  1556.     preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathDenyFilter",FALSE);
  1557.     if(preg && regexec(preg,cmd->arg,0,NULL,0) == 0) {
  1558.       add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1559.       return ERROR(cmd);
  1560.     }
  1561. #endif
  1562.   dir = dir_canonical_path(cmd->tmp_pool,cmd->arg);
  1563.   if(!dir || !dir_check_canon(cmd->tmp_pool,cmd->argv[0],cmd->group,dir,NULL) ||
  1564.      mkdir(dir,0777) == -1) {
  1565.     add_response_err(R_550,"%s: %s",cmd->argv[1],strerror(errno));
  1566.     return ERROR(cmd);
  1567.   } else {
  1568.     if(session.fsgid) {
  1569.       struct stat sbuf;
  1570.       fs_stat(dir,&sbuf);
  1571.       if(chown(dir,(uid_t)-1,(gid_t)session.fsgid) == -1)
  1572.         log_pri(LOG_WARNING,"chown() failed: %s",strerror(errno));
  1573.       else
  1574.         chmod(dir,sbuf.st_mode);
  1575.     }
  1576.     add_response(R_257,""%s" - directory successfully created.",
  1577.                   quote_dir(cmd,dir));
  1578.   }
  1579.     
  1580.   return HANDLED(cmd);
  1581. }
  1582. MODRET cmd_cwd(cmd_rec *cmd)
  1583. {
  1584.   
  1585.   if(cmd->argc < 2) {
  1586.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1587.     return ERROR(cmd);
  1588.   }
  1589.   return _chdir(cmd,cmd->arg);
  1590. }
  1591. MODRET cmd_cdup(cmd_rec *cmd)
  1592. {
  1593.   if(cmd->argc != 1) {
  1594.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1595.     return ERROR(cmd);
  1596.   }
  1597.   return _chdir(cmd,"..");
  1598. }
  1599. /* Returns the modification time of a file.  This is not in RFC959,
  1600.  * but supposedly will be in the future.  Command/response:
  1601.  * - MDTM <sp> path-name <crlf>
  1602.  * - 213 <sp> YYYYMMDDHHMMSS <crlf>
  1603.  *
  1604.  * We return the time as GMT, not localtime.  WU-ftpd returns localtime,
  1605.  * which seems like a Bad Thing<tm> to me.  However, my reasoning might
  1606.  * not be correct.
  1607.  */
  1608. MODRET cmd_mdtm(cmd_rec *cmd)
  1609. {
  1610.   char *path;
  1611.   char buf[16];
  1612.   struct tm *tm;
  1613.   struct stat sbuf;
  1614.   
  1615.   if(cmd->argc < 2) {
  1616.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1617.     return ERROR(cmd);
  1618.   }
  1619.   path = dir_realpath(cmd->tmp_pool,cmd->arg);
  1620.   if(!path || !dir_check(cmd->tmp_pool,cmd->argv[0],cmd->group,path,NULL) ||
  1621.      fs_stat(path,&sbuf) == -1) {
  1622.     add_response_err(R_550,"%s: %s",cmd->argv[1],strerror(errno));
  1623.     return ERROR(cmd);
  1624.   } else {
  1625.     if(!S_ISREG(sbuf.st_mode)) {
  1626.       add_response_err(R_550,"%s: not a plain file.",cmd->argv[1]);
  1627.       return ERROR(cmd);
  1628.     } else {
  1629.       tm = gmtime(&sbuf.st_mtime);
  1630.       if(tm)
  1631.         snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
  1632.                 tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,
  1633.                 tm->tm_hour,tm->tm_min,tm->tm_sec);
  1634.       else
  1635.         snprintf(buf, sizeof(buf), "00000000000000");        
  1636.       add_response(R_213,"%s",buf);
  1637.     }
  1638.   }
  1639.   return HANDLED(cmd);
  1640. }
  1641. MODRET cmd_size(cmd_rec *cmd)
  1642. {
  1643.   char *path;
  1644.   struct stat sbuf;
  1645.   unsigned long st_size;
  1646.   if(cmd->argc < 2) {
  1647.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1648.     return ERROR(cmd);
  1649.   }
  1650.   path = dir_realpath(cmd->tmp_pool,cmd->arg);
  1651.   if(!path || !dir_check(cmd->tmp_pool,cmd->argv[0],cmd->group,path,NULL) ||
  1652.       fs_stat(path,&sbuf) == -1) {
  1653.     add_response_err(R_550,"%s: %s",cmd->arg,strerror(errno));
  1654.     return ERROR(cmd);
  1655.   } else {
  1656.     if(!S_ISREG(sbuf.st_mode)) {
  1657.       add_response_err(R_550,"%s: not a regular file.",cmd->arg);
  1658.       return ERROR(cmd);
  1659.     } else {
  1660.       st_size = sbuf.st_size;
  1661. #if 0
  1662.       /* This code disabled in 1.1.6pl2, it allowed a possible DoS when sizeing
  1663.        * large files in ascii mode.
  1664.        */
  1665.       if(session.flags & (SF_ASCII|SF_ASCII_OVERRIDE)) {
  1666.         int fd,cnt;
  1667. char buf[4096];
  1668.         if((fp = fs_open(path,O_RDONLY,&fd)) != NULL) {
  1669.           st_size = 0;
  1670.           while((cnt = fs_read(fp,fd,buf,sizeof(buf))) > 0) {
  1671.             st_size += cnt;
  1672.             while(cnt--)
  1673.               if(buf[cnt] == 'n') st_size++;
  1674.           }
  1675.           
  1676.           fs_close(fp,fd);
  1677.         }
  1678.       }
  1679. #endif
  1680.       add_response(R_213,"%lu",st_size);
  1681.     }
  1682.   }
  1683.   return HANDLED(cmd);
  1684. }
  1685. MODRET cmd_dele(cmd_rec *cmd)
  1686. {
  1687.   char *path;
  1688. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1689.   regex_t *preg;
  1690. #endif
  1691.   if(cmd->argc < 2) {
  1692.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1693.     return ERROR(cmd);
  1694.   }
  1695. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1696.   preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathAllowFilter",FALSE);
  1697.   if(preg && regexec(preg,cmd->arg,0,NULL,0) != 0) {
  1698.     add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1699.     return ERROR(cmd);
  1700.   }
  1701.   preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathDenyFilter",FALSE);
  1702.   if(preg && regexec(preg,cmd->arg,0,NULL,0) == 0) {
  1703.     add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1704.     return ERROR(cmd);
  1705.   }
  1706. #endif
  1707.   path = dir_realpath(cmd->tmp_pool,cmd->arg);
  1708.   if(!path || !dir_check(cmd->tmp_pool,cmd->argv[0],cmd->group,path,NULL) ||
  1709.      fs_unlink(path) == -1) {
  1710.     add_response_err(R_550,"%s: %s",cmd->arg,strerror(errno));
  1711.     return ERROR(cmd);
  1712.   }
  1713.   
  1714.   if(session.flags & SF_ANON) {
  1715.     log_xfer(0, session.c->remote_name, 0,
  1716.      path, (session.flags & SF_ASCII ? 'a' : 'b'),
  1717.      'd', 'a', session.anon_user);
  1718.   } else {
  1719.     log_xfer(0,session.c->remote_name, 0, path,
  1720.      (session.flags & SF_ASCII ? 'a' : 'b'),
  1721.      'd', 'r', session.user);
  1722.   }
  1723.   
  1724.   add_response(R_250,"%s command successful.",cmd->argv[0]);
  1725.   return HANDLED(cmd);
  1726. }
  1727. MODRET cmd_rnto(cmd_rec *cmd)
  1728. {
  1729.   char *path;
  1730. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1731.   regex_t *preg;
  1732. #endif
  1733.   if(cmd->argc < 2) {
  1734.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1735.     return ERROR(cmd);
  1736.   }
  1737.   if(!session.xfer.path) {
  1738.     if(session.xfer.p) {
  1739.       destroy_pool(session.xfer.p);
  1740.       bzero(&session.xfer,sizeof(session.xfer));
  1741.     }
  1742.     add_response_err(R_503,"Bad sequence of commands.");
  1743.     return ERROR(cmd);
  1744.   }
  1745. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1746.   preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathAllowFilter",FALSE);
  1747.   if(preg && regexec(preg,cmd->arg,0,NULL,0) != 0) {
  1748.     add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1749.     return ERROR(cmd);
  1750.   }
  1751.   preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathDenyFilter",FALSE);
  1752.   if(preg && regexec(preg,cmd->arg,0,NULL,0) == 0) {
  1753.     add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1754.     return ERROR(cmd);
  1755.   }
  1756. #endif
  1757.   path = dir_canonical_path(cmd->tmp_pool,cmd->arg);
  1758.   if(!path || !dir_check_canon(cmd->tmp_pool,cmd->argv[0],cmd->group,path,NULL) 
  1759.      || rename(session.xfer.path,path) == -1) {
  1760.     add_response_err(R_550,"rename: %s",strerror(errno));
  1761.     destroy_pool(session.xfer.p);
  1762.     bzero(&session.xfer,sizeof(session.xfer));
  1763.     return ERROR(cmd);
  1764.   }
  1765.   add_response(R_250,"rename successful.");
  1766.   destroy_pool(session.xfer.p);
  1767.   bzero(&session.xfer,sizeof(session.xfer));
  1768.   return HANDLED(cmd);
  1769. }
  1770. MODRET cmd_rnfr(cmd_rec *cmd)
  1771. {
  1772.   char *path;
  1773. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1774.   regex_t *preg;
  1775. #endif
  1776.   if(cmd->argc < 2) {
  1777.     add_response_err(R_500,"'%s' is an unknown command.",get_full_cmd(cmd));
  1778.     return ERROR(cmd);
  1779.   }
  1780. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  1781.   preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathAllowFilter",FALSE);
  1782.   if(preg && regexec(preg,cmd->arg,0,NULL,0) != 0) {
  1783.     add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1784.     return ERROR(cmd);
  1785.   }
  1786.   preg = (regex_t*)get_param_ptr(TOPLEVEL_CONF,"PathDenyFilter",FALSE);
  1787.   if(preg && regexec(preg,cmd->arg,0,NULL,0) == 0) {
  1788.     add_response_err(R_550,"%s: Forbidden filename",cmd->arg);
  1789.     return ERROR(cmd);
  1790.   }
  1791. #endif
  1792.   path = dir_realpath(cmd->tmp_pool,cmd->arg);
  1793.   if(!path || !dir_check(cmd->tmp_pool,cmd->argv[0],cmd->group,path,NULL) ||
  1794.      !exists(path)) {
  1795.     add_response_err(R_550,"%s: %s",cmd->argv[1],strerror(errno));
  1796.     return ERROR(cmd);
  1797.   }
  1798.   /* We store the path in session.xfer.path */
  1799.   if(session.xfer.p) {
  1800.     destroy_pool(session.xfer.p);
  1801.     bzero(&session.xfer,sizeof(session.xfer));
  1802.   }
  1803.   session.xfer.p = make_sub_pool(session.pool);
  1804.   session.xfer.path = pstrdup(session.xfer.p,path);
  1805.   add_response(R_350,"File or directory exists, ready for destination name.");
  1806.   return HANDLED(cmd);
  1807. }
  1808. MODRET cmd_site(cmd_rec *cmd)
  1809. {
  1810.   char *cp;
  1811.   cmd->argc--;
  1812.   cmd->argv++;
  1813.   if(cmd->argc)
  1814.     for(cp = cmd->argv[0]; *cp; cp++)
  1815.       *cp = toupper(*cp);
  1816.   return call_module(&site_module,site_dispatch,cmd);
  1817. }
  1818. MODRET cmd_noop(cmd_rec *cmd)
  1819. {
  1820.   add_response(R_200,"NOOP command successful.");
  1821.   return HANDLED(cmd);
  1822. }
  1823. MODRET set_defaulttransfermode(cmd_rec *cmd)
  1824. {                                               
  1825.   config_rec *c;
  1826.   CHECK_ARGS(cmd,1);
  1827.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  1828.   if (strcasecmp(cmd->argv[1], "ascii") != 0 && strcasecmp(cmd->argv[1], "binary") != 0) {
  1829. CONF_ERROR(cmd, "parameter must be 'ascii' or 'binary'.");
  1830.   } else
  1831.    c = add_config_param_str("DefaultTransferMode", 1, cmd->argv[1]);
  1832.   c->flags |= CF_MERGEDOWN;
  1833.   return HANDLED(cmd);
  1834. }
  1835. MODRET set_classes(cmd_rec *cmd)
  1836. {
  1837.   int b;
  1838.   config_rec *c;
  1839.   CHECK_ARGS(cmd,1);
  1840.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1841.   b = get_boolean(cmd,1);
  1842.   if (b == -1)
  1843.     CONF_ERROR(cmd, "requires a boolean value");
  1844.   c = add_config_param("Classes",1,(void*)b);
  1845.   c->flags |= CF_MERGEDOWN;
  1846.   return HANDLED(cmd);
  1847. }
  1848. static cdir_t *cdir_list = NULL;
  1849. static class_t *class_list = NULL;
  1850. static cdir_t *add_cdir(class_t *class, u_int_32 address, u_int_8 netmask)
  1851. {
  1852. cdir_t *n;
  1853. n = calloc(1, sizeof(cdir_t));
  1854. n->class = class;
  1855. while (netmask--) {
  1856. n->netmask >>= 1;
  1857. n->netmask |= 0x80000000;
  1858. }
  1859. n->address = address & n->netmask;
  1860. n->next = cdir_list;
  1861. cdir_list = n;                 
  1862. return n;
  1863. }
  1864. static cdir_t *find_cdir(u_int_32 address)
  1865. {
  1866. cdir_t *c, *f;
  1867. c = cdir_list;
  1868. f = NULL;
  1869. while (c) {
  1870. /* within cdir range ? && more specific netmask ? */
  1871. if (((address & c->netmask) == c->address) && (f == NULL || (f && (f->netmask < c->netmask))))
  1872. f = c;
  1873. c = c->next;
  1874. }
  1875. return f;
  1876. }
  1877. static class_t *add_class(char *name)
  1878. {
  1879. class_t *n;
  1880. n = calloc(1, sizeof(class_t));
  1881. n->name = strdup(name);
  1882. n->max_connections = 100;
  1883. n->next = class_list;
  1884. class_list = n;                 
  1885. return n;
  1886. }
  1887. static class_t *get_class(char *name)
  1888. {
  1889. class_t *c;
  1890. c = class_list;
  1891. while (c) {
  1892. if (name && strcmp(name, c->name) == 0)
  1893. return c;
  1894. c = c->next;
  1895. }
  1896. return NULL;
  1897. }
  1898. class_t *find_class(p_in_addr_t *addr, char *remote_name)
  1899. {
  1900. cdir_t *ip;
  1901. class_t *c, *f;
  1902. if ((ip = find_cdir(ntohl(addr->s_addr))) != NULL)
  1903. return ip->class;
  1904. c = class_list;
  1905. f = NULL;
  1906. while (c) {
  1907. if (c->preg && (regexec(c->preg, remote_name, 0, NULL, 0)) == 0) 
  1908. if (f == NULL || (f && f->max_connections < c->max_connections))
  1909. f = c;
  1910. c = c->next;
  1911. }
  1912. if (f)
  1913. return f;
  1914. else
  1915. return get_class("default");
  1916. }
  1917. MODRET set_class(cmd_rec *cmd)
  1918. {
  1919.   int bits, ret;
  1920.   class_t *n;
  1921.   regex_t *preg;
  1922.   p_in_addr_t *res;
  1923.   char *ptr, ipaddress[20], errmsg[80];
  1924.   CHECK_ARGS(cmd,3);
  1925.   CHECK_CONF(cmd,CONF_ROOT);
  1926.   /* setup "default" class if necesarry */
  1927.   if((n = get_class("default")) == NULL)
  1928.     n = add_class("default");
  1929.   
  1930.   /* find class, add if necessary */
  1931.   if((n = get_class(cmd->argv[1])) == NULL)
  1932.     n = add_class(cmd->argv[1]);
  1933.   
  1934.   /* what to do ? */
  1935.   if(strcasecmp(cmd->argv[2], "limit") == 0) {
  1936.     ret = atoi(cmd->argv[3]);
  1937.     if (ret < 0)
  1938.       ret = 100;
  1939.     n->max_connections = ret;
  1940.     log_debug(DEBUG4, "class '%s' maxconnections set to %d",
  1941.       n->name, n->max_connections);
  1942.   } else if (strcasecmp(cmd->argv[2], "regex") == 0) {
  1943.     preg = calloc(1, sizeof(regex_t));
  1944.     
  1945.     if((ret = regcomp(preg, cmd->argv[3],
  1946.       REG_EXTENDED|REG_NOSUB|REG_ICASE)) != 0) {
  1947.       regerror(ret, preg, errmsg, sizeof(errmsg));
  1948.       regfree(preg);
  1949.       
  1950.       n->preg = NULL;
  1951.       log_pri(LOG_ERR, "failed regexp '%s' compilation: ", cmd->argv[3]);
  1952.     } else {
  1953.       n->preg = preg;
  1954.     }
  1955.   } else if(strcasecmp(cmd->argv[2], "ip") == 0) {
  1956.     sstrncpy(ipaddress, cmd->argv[3], sizeof(ipaddress));
  1957.     if((ptr = strchr(ipaddress, '/')) == NULL) {
  1958.       log_pri(LOG_ERR, "class '%s' ipmask %s skipped.",
  1959.       cmd->argv[1], cmd->argv[3]);
  1960.       CONF_ERROR(cmd, "wrong syntax error.");
  1961.     } else {
  1962.       bits = atol(ptr + 1);
  1963.       
  1964.       if (bits < 0 || bits > 32) {
  1965. log_pri(LOG_ERR, "class '%s' ipmask %s skipped: wrong netmask",
  1966. cmd->argv[1], cmd->argv[3]);
  1967.       }
  1968.       
  1969.       *ptr = 0;
  1970.     }
  1971.     
  1972.     if((res = inet_getaddr(cmd->pool, ipaddress)) != NULL) {
  1973.       add_cdir(n, ntohl(res->s_addr), bits);
  1974.       log_debug(DEBUG4, "class '%s' ipmask %p/%d added.",
  1975. cmd->argv[1], res, bits);
  1976.     } else {
  1977.       log_pri(LOG_ERR, "Class '%s' ip could not parse '%s'",
  1978.       cmd->argv[1], cmd->argv[3]);
  1979.     }
  1980.   } else {
  1981.     CONF_ERROR(cmd, "unknown argument.");
  1982.   }
  1983.   
  1984.   return HANDLED(cmd);
  1985. }
  1986. /* Configuration directive table */
  1987. static conftable core_conftable[] = {
  1988.   { "ServerName", set_servername,  NULL },
  1989.   { "ServerIdent", set_serverident, NULL },
  1990.   { "ServerType", set_servertype, NULL },
  1991.   { "ServerAdmin", set_serveradmin, NULL },
  1992.   { "UseReverseDNS", set_usereversedns, NULL },
  1993.   { "ScoreboardPath", set_scoreboardpath, NULL },
  1994.   { "TransferLog", add_transferlog, NULL },
  1995.   { "WtmpLog", set_wtmplog, NULL },
  1996.   { "Bind", add_bind, NULL },
  1997.   { "Port", set_serverport,  NULL },
  1998.   { "SocketBindTight", set_socketbindtight, NULL },
  1999.   { "IdentLookups", set_identlookups, NULL },
  2000.   { "tcpBackLog", set_tcpbacklog, NULL },
  2001.   { "tcpReceiveWindow", set_tcpreceivewindow, NULL },
  2002.   { "tcpSendWindow", set_tcpsendwindow, NULL },
  2003.   { "tcpNoDelay", set_tcpnodelay, NULL },
  2004.   { "DeferWelcome", set_deferwelcome, NULL },
  2005.   { "DefaultServer", set_defaultserver, NULL },
  2006.   { "MultilineRFC2228", set_multilinerfc2228, NULL },
  2007.   { "User", set_user, NULL },
  2008.   { "Group", set_group,  NULL },
  2009.   { "UserPassword", add_userpassword, NULL },
  2010.   { "GroupPassword", add_grouppassword, NULL },
  2011.   { "Umask", set_umask, NULL },
  2012.   { "MaxLoginAttempts", set_maxloginattempts, NULL },
  2013.   { "MaxClients", set_maxclients, NULL },
  2014.   { "MaxClientsPerHost", set_maxhostclients, NULL },
  2015.   { "MaxInstances", set_maxinstances, NULL },
  2016.   { "RequireValidShell", set_requirevalidshell, NULL },
  2017.   { "ShowSymlinks", set_showsymlinks, NULL },
  2018.   { "SyslogFacility", set_syslogfacility, NULL },
  2019.   { "TimeoutLogin", set_timeoutlogin, NULL },
  2020.   { "TimeoutIdle", set_timeoutidle, NULL },
  2021.   { "TimeoutNoTransfer", set_timeoutnoxfer, NULL },
  2022.   { "TimeoutStalled", set_timeoutstalled, NULL },
  2023.   { "UseFtpUsers", set_useftpusers, NULL },
  2024.   { "AccessGrantMsg", set_accessgrantmsg, NULL },
  2025.   { "AnonymousGroup", add_anonymousgroup, NULL },
  2026.   { "<VirtualHost>", add_virtualhost, NULL },
  2027.   { "</VirtualHost>", end_virtualhost, NULL },
  2028.   { "<Directory>", add_directory, NULL },
  2029.   { "CDPath", add_cdpath, NULL },
  2030.   { "HideNoAccess", add_hidenoaccess, NULL },
  2031.   { "HideUser", add_hideuser, NULL },
  2032.   { "HideGroup", add_hidegroup, NULL },
  2033.   { "GroupOwner", add_groupowner, NULL },
  2034.   { "AllowOverwrite", set_allowoverwrite, NULL },
  2035.   { "HiddenStor", set_hiddenstor, NULL },
  2036.   { "DisplayFirstChdir", set_displayfirstchdir, NULL },
  2037.   { "AuthAliasOnly", set_authaliasonly, NULL },
  2038.   { "AllowRetrieveRestart", set_allowretrieverestart, NULL },
  2039.   { "AllowStoreRestart", set_allowstorerestart, NULL },
  2040.   { "</Directory>", end_directory, NULL },
  2041.   { "<Limit>", add_limit, NULL },
  2042.   { "IgnoreHidden", set_ignorehidden, NULL },
  2043.   { "Order", add_order, NULL },
  2044.   { "Allow", add_allow, NULL },
  2045.   { "Deny", add_deny, NULL },
  2046.   { "AllowGroup", add_allowgroup, NULL },
  2047.   { "DenyGroup", add_denygroup, NULL },
  2048.   { "AllowUser", add_allowuser, NULL },
  2049.   { "DenyUser", add_denyuser, NULL },
  2050.   { "AllowAll", set_allowall, NULL },
  2051.   { "DenyAll", set_denyall, NULL },
  2052.   { "</Limit>",  end_limit,  NULL },
  2053.   { "DisplayLogin", set_displaylogin, NULL },
  2054.   { "DisplayConnect", set_displayconnect, NULL },
  2055.   { "DisplayQuit", set_displayquit, NULL },
  2056.   { "DisplayGoAway", set_displaygoaway, NULL },
  2057.   { "<Anonymous>", add_anonymous, NULL },
  2058.   { "UserAlias", add_useralias,  NULL },
  2059.   { "AnonRequirePassword", set_anonrequirepassword, NULL },
  2060.   { "CommandBufferSize", set_commandbuffersize, NULL },
  2061.   { "AllowFilter", set_allowfilter, NULL },
  2062.   { "DenyFilter", set_denyfilter, NULL },
  2063.   { "PathAllowFilter", set_pathallowfilter, NULL },
  2064.   { "PathDenyFilter", set_pathdenyfilter, NULL },
  2065.   { "AllowForeignAddress", set_allowforeignaddress, NULL },
  2066.   { "</Anonymous>", end_anonymous, NULL },
  2067.   { "<Global>", add_global, NULL },
  2068.   { "</Global>", end_global, NULL },
  2069.   { "DefaultTransferMode", set_defaulttransfermode, NULL },
  2070.   { "Class", set_class, NULL },
  2071.   { "Classes", set_classes, NULL },
  2072.   { NULL, NULL, NULL }
  2073. };
  2074. cmdtable core_commands[] = {
  2075.   { CMD, C_HELP, G_NONE,  cmd_help, FALSE, FALSE, CL_INFO },
  2076.   { CMD, C_PORT, G_NONE,  cmd_port, TRUE, FALSE, CL_MISC },
  2077.   { CMD, C_PASV, G_NONE,  cmd_pasv, TRUE, FALSE, CL_MISC },
  2078.   { CMD, C_SYST, G_NONE,  cmd_syst, TRUE, FALSE, CL_INFO },
  2079.   { CMD, C_PWD,  G_NONE,  cmd_pwd, TRUE, FALSE, CL_INFO|CL_DIRS },
  2080.   { CMD, C_XPWD, G_NONE,  cmd_pwd, TRUE, FALSE, CL_INFO|CL_DIRS },
  2081.   { CMD, C_CWD,  G_DIRS,  cmd_cwd, TRUE, FALSE, CL_DIRS },
  2082.   { CMD, C_XCWD, G_DIRS,  cmd_cwd, TRUE, FALSE, CL_DIRS },
  2083.   { CMD, C_MKD,  G_WRITE, cmd_mkd, TRUE, FALSE, CL_DIRS|CL_WRITE },
  2084.   { CMD, C_XMKD, G_WRITE, cmd_mkd, TRUE, FALSE, CL_DIRS|CL_WRITE },
  2085.   { CMD, C_RMD,  G_WRITE, cmd_rmd, TRUE, FALSE, CL_DIRS|CL_WRITE },
  2086.   { CMD, C_XRMD, G_WRITE, cmd_rmd, TRUE, FALSE, CL_DIRS|CL_WRITE },
  2087.   { CMD, C_CDUP, G_DIRS,  cmd_cdup, TRUE, FALSE, CL_DIRS },
  2088.   { CMD, C_XCUP, G_DIRS,  cmd_cdup, TRUE, FALSE, CL_DIRS },
  2089.   { CMD, C_SITE, G_NONE,  cmd_site, TRUE, FALSE, CL_MISC },
  2090.   { CMD, C_DELE, G_WRITE, cmd_dele, TRUE, FALSE, CL_WRITE },
  2091.   { CMD, C_MDTM, G_DIRS,  cmd_mdtm, TRUE, FALSE, CL_INFO },
  2092.   { CMD, C_RNFR, G_DIRS,  cmd_rnfr, TRUE, FALSE, CL_MISC },
  2093.   { CMD, C_RNTO, G_WRITE, cmd_rnto, TRUE, FALSE, CL_MISC },
  2094.   { CMD, C_SIZE, G_READ,  cmd_size, TRUE, FALSE, CL_INFO },
  2095.   { CMD, C_QUIT, G_NONE,  cmd_quit, FALSE, TRUE,  CL_INFO },
  2096.   { CMD, C_NOOP, G_NONE,  cmd_noop, FALSE, TRUE,  CL_MISC },
  2097.   { 0, NULL }
  2098. };
  2099. /* Module interface */
  2100. module core_module = {
  2101.   NULL,NULL, /* always NULL */
  2102.   0x20, /* API Version 2.0 */
  2103.   "core",
  2104.   core_conftable,
  2105.   core_commands,
  2106.   NULL,
  2107.   NULL,NULL                    /* No initialization needed */
  2108. };