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

Ftp客户端

开发平台:

Unix_Linux

  1. /*
  2.  * ProFTPD: mod_sql -- SQL frontend and user interface.
  3.  * Time-stamp: <1999-10-04 03:58:01 root>
  4.  * Copyright (c) 1998-1999 Johnie Ingram.
  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. #define MOD_SQL_VERSION "mod_sqlpw/2.0"
  21. /* This is mod_sqlpw, contrib software for proftpd 1.2.0pre3 and above.
  22.    For more information contact Johnie Ingram <johnie@netgod.net>.
  23.    History Log:
  24.    * 1999-09-19: v2.0: Backend directives split into mod_mysql.
  25.      Runtime API added.  Username/passwords now escaped for SQL.
  26.    * 1999-09-16: v1.0: Added documentation, made directives more
  27.      generic ("MySQL" -> "SQL" except for MySQLInfo).  Dir part of
  28.      SQLLogDirs finally made optional.
  29.    * 1999-06-01: v0.3: fixed segfault, changed 'strncmp' typo which
  30.      should have been copy.  Made uid/gid support optional.
  31.    
  32.    * 1999-05-03: v0.2: Removed dead code, fix bug with interaction
  33.      with anon-user support.  Added MySQLLogHits and MySQLHomedir
  34.      directives.  Fixed atoi() invocation that could segfault.
  35.      Copious debugging code added.
  36.    * 1999-04-09: v0.1: Initial attempt (mod_mysql).
  37. */
  38. #include "conf.h"
  39. #ifdef HAVE_CRYPT_H
  40. #include <crypt.h>
  41. #endif
  42. #include <mysql.h>
  43. /* *INDENT-OFF* */
  44. /* The Debian defintion of nobody/nogroup, minus 1 (used in getgwnam).  */
  45. #define MOD_SQL_MAGIC_USER 65533
  46. #define MOD_SQL_MAGIC_GROUP 65533
  47. /* A uid or gid less than this is mapped to the magic numbers above
  48.    instead of simply rejected (which is arguably better, hmm.) */
  49. #define MOD_SQL_MIN_ID 999
  50. #define MOD_SQL_MAGIC_SHELL "/bin/sh"
  51. /* Maximum username field to expect, etc. */
  52. #define ARBITRARY_MAX                   128
  53. static struct {
  54.   char user [ARBITRARY_MAX];
  55.   struct passwd pw;           /* Scratch space for the getpwnam call. */
  56.   char *pass;                 /* The password from db. */
  57.   char *homedir;              /* Set when getpwnam is called. */
  58.   int pwnamed;                /* Set when getpwnam is called. */
  59.   int sqled;                  /* Set if sql auth was used. */
  60.   char *sql_usertable;        /* CREATE TABLE users ( */
  61.   char *sql_userid;           /* userid varchar(50) NOT NULL, */
  62.   char *sql_passwd;           /* passwd varchar(15), */
  63.   char *sql_uid;              /* uid int(5), */
  64.   char *sql_gid;              /* gid int(5), */
  65.   char *sql_fstor;            /* fstor int(11) NOT NULL DEFAULT '0', */
  66.   char *sql_fretr;            /* fretr int(11) NOT NULL DEFAULT '0', */
  67.   char *sql_bstor;            /* bstor int(11) NOT NULL DEFAULT '0', */
  68.   char *sql_bretr;            /* bretr int(11) NOT NULL DEFAULT '0', */
  69.   char *sql_fhdir;            /* fhdir varchar(255), */
  70.   char *sql_fhost;            /* fhost varchar(50), */
  71.   char *sql_faddr;            /* faddr char(15), */
  72.   char *sql_ftime;            /* ftime timestamp, */
  73.   char *sql_fcdir;            /* fcdir varchar(255), */
  74.   char *sql_frate;            /* frate int(11) NOT NULL DEFAULT '5', */
  75.   char *sql_fcred;            /* fcred int(2) NOT NULL DEFAULT '15', */
  76.   char *sql_brate;            /* brate int(11) NOT NULL DEFAULT '5', */
  77.   char *sql_bcred;            /* bcred int(2) NOT NULL DEFAULT '150000', */
  78.   char *sql_flogs;            /* flogs int(11) NOT NULL DEFAULT '0', */
  79.   char *sql_hittable;
  80.   char *sql_dir;
  81.   char *sql_filename;
  82.   char *sql_hits;
  83.   char *loginmsg;
  84.   int ok;
  85. } g;
  86. /* *INDENT-ON* */
  87. /* **************************************************************** */
  88. /* Functions make_cmd and dispatch liberally stolen from auth.c. */
  89. static cmd_rec *
  90. _make_cmd (pool * cp, int argc, ...)
  91. {
  92.   va_list args;
  93.   cmd_rec *c;
  94.   int i;
  95.   c = pcalloc (cp, sizeof (cmd_rec));
  96.   c->argc = argc;
  97.   c->symtable_index = -1;
  98.   c->argv = pcalloc (cp, sizeof (void *) * (argc + 1));
  99.   c->argv[0] = MOD_SQL_VERSION;
  100.   va_start (args, argc);
  101.   for (i = 0; i < argc; i++)
  102.     c->argv[i + 1] = (void *) va_arg (args, char *);
  103.   va_end (args);
  104.   return c;
  105. }
  106. static modret_t *
  107. _dispatch_sql (cmd_rec * cmd, char *match)
  108. {
  109.   authtable *m;
  110.   modret_t *mr = NULL;
  111.   m = mod_find_auth_symbol (match, &cmd->symtable_index, NULL);
  112.   while (m)
  113.     {
  114.       mr = call_module_auth (m->m, m->handler, cmd);
  115.       if (MODRET_ISHANDLED (mr) || MODRET_ISERROR (mr))
  116. break;
  117.       m = mod_find_auth_symbol (match, &cmd->symtable_index, m);
  118.     }
  119.   if (MODRET_ISERROR (mr))
  120.     log_debug (DEBUG0, "Aiee! sql internal!  %s", MODRET_ERRMSG (mr));
  121.   return mr;
  122. }
  123. MODRET
  124. modsql_open (cmd_rec * cmd)
  125. {
  126.   cmd_rec *c;
  127.   modret_t *mr;
  128.   c = _make_cmd (cmd ? cmd->tmp_pool : permanent_pool, 0);
  129.   mr = _dispatch_sql (c, "dbd_open");
  130.   if (c->tmp_pool)
  131.     destroy_pool (c->tmp_pool);
  132.   return mr;
  133. }
  134. MODRET
  135. modsql_close (cmd_rec * cmd)
  136. {
  137.   cmd_rec *c;
  138.   modret_t *mr;
  139.   c = _make_cmd (cmd ? cmd->tmp_pool : permanent_pool, 0);
  140.   mr = _dispatch_sql (c, "dbd_close");
  141.   if (c->tmp_pool)
  142.     destroy_pool (c->tmp_pool);
  143.   return mr;
  144. }
  145. MODRET
  146. modsql_update (cmd_rec * cmd, const char *query)
  147. {
  148.   cmd_rec *c;
  149.   modret_t *mr;
  150.   c = _make_cmd (cmd->tmp_pool, 1, query);
  151.   mr = _dispatch_sql (c, "dbd_update");
  152.   if (c->tmp_pool)
  153.     destroy_pool (c->tmp_pool);
  154.   return mr;
  155. }
  156. MODRET
  157. modsql_select (cmd_rec * cmd, const char *query)
  158. {
  159.   cmd_rec *c;
  160.   modret_t *mr;
  161.   c = _make_cmd (cmd->tmp_pool, 1, query);
  162.   mr = _dispatch_sql (c, "dbd_select");
  163.   if (c->tmp_pool)
  164.     destroy_pool (c->tmp_pool);
  165.   return mr;
  166. }
  167. MODRET
  168. modsql_queryuser (cmd_rec * cmd, const char *user, const char *query,
  169.   int update)
  170. {
  171.   char *realquery;
  172.   if (update)
  173.     realquery = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
  174.  " set ", query, " where ", g.sql_userid, " = '",
  175.  user, "'", NULL);
  176.   else
  177.     realquery = pstrcat (cmd->tmp_pool, "select ", query, " from ",
  178.  g.sql_usertable, " where ", g.sql_userid,
  179.  " = '", user, "'", NULL);
  180.   return (update) ? modsql_update (cmd, realquery)
  181.     : modsql_select (cmd, realquery);
  182. }
  183. /* **************************************************************** */
  184. static char *
  185. _uservar (cmd_rec * cmd, const char *user, const char *var)
  186. {
  187.   cmd_rec *c;
  188.   modret_t *mr;
  189.   char *query;
  190.   char **data;
  191.   query = pstrcat (cmd->tmp_pool, "select ", var, " from ",
  192.    g.sql_usertable, " where ", g.sql_userid,
  193.    " = '", user, "'", NULL);
  194.   c = _make_cmd (cmd->tmp_pool, 1, query);
  195.   mr = _dispatch_sql (c, "dbd_select");
  196.   if (c->tmp_pool)
  197.     destroy_pool (c->tmp_pool);
  198.   if (MODRET_ISHANDLED (mr))
  199.     {
  200.       data = mr->data;
  201.       return (data) ? data[0] : NULL;
  202.     }
  203.   return NULL;
  204. }
  205. /* Note: This function is called once by the master proftpd process
  206.    before it forks, so thankfully the homedir field is only set in the
  207.    child_init(). */
  208. MODRET
  209. auth_cmd_getpwnam (cmd_rec * cmd)
  210. {
  211.   const char *homedir;
  212.   if (!g.sql_fhdir && !g.homedir)
  213.     return DECLINED (cmd);
  214.   /* Only try this for the USER command (is also called twice after
  215.      PASS for some reason.  [update: 1 is dir_realpath/defroot related.] */
  216.   if (!g.homedir && !g.pwnamed++)
  217.     {
  218.       if ((homedir = _uservar (cmd, cmd->argv[0], g.sql_fhdir)))
  219. g.homedir = pstrdup (session.pool, homedir);
  220.     }
  221.   if (!g.homedir)
  222.     return DECLINED (cmd);
  223.   if (!g.pw.pw_name)
  224.     {
  225.       g.pw.pw_name = cmd->argv[0];
  226.       if (g.sql_uid)
  227. g.pw.pw_uid = atoi (_uservar (cmd, cmd->argv[0], g.sql_uid) ? : "0");
  228.       if (g.pw.pw_uid < MOD_SQL_MIN_ID)
  229. g.pw.pw_uid = MOD_SQL_MAGIC_USER;
  230.       if (g.sql_gid)
  231. g.pw.pw_gid = atoi (_uservar (cmd, cmd->argv[0], g.sql_gid) ? : "0");
  232.       if (g.pw.pw_gid < MOD_SQL_MIN_ID)
  233. g.pw.pw_gid = MOD_SQL_MAGIC_GROUP;
  234.       g.pw.pw_shell = MOD_SQL_MAGIC_SHELL;
  235.       g.pw.pw_dir = (char *) g.homedir;
  236.       log_debug (DEBUG3, "sqlpw: user "%s" (%i/%i) for %s",
  237.  cmd->argv[0], g.pw.pw_uid, g.pw.pw_gid, g.pw.pw_dir);
  238.       /* Copy username so proftpd anon handling won't confuse the issue. */
  239.       /* FIXME: unnecessary mysqlism */
  240.       mysql_escape_string (g.user, g.pw.pw_name, strlen (g.pw.pw_name));
  241.       g.user[ARBITRARY_MAX - 1] = 0;
  242.     }
  243.   return mod_create_data (cmd, &g.pw);
  244. }
  245. /* Returns 1 on successful match, 0 unsuccessful match, -1 on error. */
  246. static int
  247. _checkpass (cmd_rec * cmd, const char *user, const char *pass)
  248. {
  249.   int success = 0;
  250.   char *query;
  251.   int emptyok, cplain, ccrypt;
  252.   char **row;
  253.   MODRET mr;
  254.   emptyok = get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE);
  255.   cplain = get_param_int (CURRENT_CONF, "SQLPlaintextPasswords", FALSE);
  256.   ccrypt = get_param_int (CURRENT_CONF, "SQLEncryptedPasswords", FALSE);
  257.   query = pstrcat (cmd->tmp_pool, "select ", g.sql_passwd, " from ",
  258.    g.sql_usertable, " where ", g.sql_userid,
  259.    " = '", user, "'", " limit 2", NULL);
  260.   mr = modsql_select (cmd, query);
  261.   if (!(MODRET_HASDATA (mr)))
  262.     return -1;
  263.   row = mr->data;
  264.   if (row[0] == 0)
  265.     return 0;
  266.   if (!row[0])
  267.     {
  268.       log_debug (DEBUG4, "sqlpw: %s auth declined for NULL pass", user);
  269.       return 0;
  270.     }
  271.   if (row[1])
  272.     {
  273.       log_debug (DEBUG3, "sqlpw: %s pass result was not unique", user);
  274.       return -1;
  275.     }
  276.   if (emptyok == TRUE && !strlen (row[0]))
  277.     {
  278.       log_debug (DEBUG4, "sqlpw: warning: %s has empty password", user);
  279.       success = 1;
  280.     }
  281.   if (!success && cplain == TRUE && !strncasecmp (row[0], pass, 10))
  282.     {
  283.       success = 1;
  284.     }
  285.   /* Deliberate: ccrypt same if TRUE or -1 (unspecified). */
  286.   if (!success && ccrypt)
  287.     {
  288.       if (!strcmp (query = (char *) crypt (pass, row[0]), row[0]))
  289. success = 1;
  290.     }
  291.   if (!success)
  292.     log_debug (DEBUG5, "sqlpw: %s auth failed: '%s' != '%s'",
  293.        user, pass, row ? row[0] : "");
  294.   return success;
  295. }
  296. MODRET
  297. auth_cmd_auth (cmd_rec * cmd)
  298. {
  299.   char *user, *pass;
  300.   int return_type;
  301.   int retval = AUTH_NOPWD;
  302.   if (!g.sql_passwd || !g.homedir)
  303.     return DECLINED (cmd);
  304.   /* Figure out our default return style: Whether or not SQL should
  305.    * allow other auth modules a shot at this user or not is controlled
  306.    * by the parameter "SQLAuthoritative".  Like mod_pam this
  307.    * defaults to no.  */
  308.   return_type = get_param_int (CURRENT_CONF, "SQLAuthoritative", FALSE);
  309.   /* Just in case... */
  310.   if (cmd->argc != 2)
  311.     return return_type ? ERROR (cmd) : DECLINED (cmd);
  312.   if ((user = cmd->argv[0]) == NULL)
  313.     return return_type ? ERROR (cmd) : DECLINED (cmd);
  314.   if ((pass = cmd->argv[1]) == NULL)
  315.     return return_type ? ERROR (cmd) : DECLINED (cmd);
  316.   if (!g.pass
  317.       && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
  318.     {
  319.       char *query;
  320.       char passbuf[ARBITRARY_MAX];
  321.       /* FIXME: unnecessary mysqlism */
  322.       mysql_escape_string (passbuf, cmd->argv[1], strlen (cmd->argv[1]));
  323.       g.user[ARBITRARY_MAX - 1] = 0;
  324.       query = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
  325.        " set ", g.sql_passwd, " = '", passbuf,
  326.        "' where ", g.sql_userid, " = '", g.user, "'", 0);
  327.       log_debug (DEBUG3, "sqlpw: %s NULL pass set to '%s'", user,
  328.  cmd->argv[1], query);
  329.       modsql_update (cmd, query);
  330.     }
  331.   if (_checkpass (cmd, g.user, pass) != 1)
  332.     return return_type ? ERROR_INT (cmd, retval) : DECLINED (cmd);
  333.   g.sqled++;
  334.   return HANDLED (cmd);
  335. }
  336. MODRET
  337. auth_cmd_getstats (cmd_rec * cmd)
  338. {
  339.   MODRET mr;
  340.   char *query;
  341.   if (g.sql_fstor)
  342.     {
  343.       query = pstrcat (cmd->tmp_pool, "select ", g.sql_fstor, ", ",
  344.        g.sql_fretr, ", ", g.sql_bstor, ", ",
  345.        g.sql_bretr, " from ", g.sql_usertable, " where ",
  346.        g.sql_userid, " = '", g.user, "'", NULL);
  347.       mr = modsql_select (cmd, query);
  348.       if (MODRET_HASDATA (mr))
  349. return mr;
  350.     }
  351.   return DECLINED (cmd);
  352. }
  353. MODRET
  354. auth_cmd_getratio (cmd_rec * cmd)
  355. {
  356.   MODRET mr;
  357.   char *query;
  358.   if (g.sql_frate)
  359.     {
  360.       query = pstrcat (cmd->tmp_pool, "select ", g.sql_frate, ", ",
  361.        g.sql_fcred, ", ", g.sql_brate, ", ",
  362.        g.sql_bcred, " from ", g.sql_usertable, " where ",
  363.        g.sql_userid, " = '", g.user, "'", NULL);
  364.       mr = modsql_select (cmd, query);
  365.       if (MODRET_HASDATA (mr))
  366. return mr;
  367.     }
  368.   return DECLINED (cmd);
  369. }
  370. static authtable sqlpw_authtab[] = {
  371.   {0, "auth", auth_cmd_auth},
  372.   {0, "getpwnam", auth_cmd_getpwnam},
  373.   {0, "getstats", auth_cmd_getstats},
  374.   {0, "getratio", auth_cmd_getratio},
  375.   {0, NULL, NULL}
  376. };
  377. /* **************************************************************** */
  378. static void
  379. _setstats (cmd_rec * cmd, int fstor, int fretr, int bstor, int bretr)
  380. {
  381.   char query[ARBITRARY_MAX];
  382.   snprintf (query, sizeof (query),
  383.     "%s = %s + %i, %s = %s + %i, %s = %s + %i, %s = %s + %i",
  384.     g.sql_fstor, g.sql_fstor, fstor,
  385.     g.sql_fretr, g.sql_fretr, fretr,
  386.     g.sql_bstor, g.sql_bstor, bstor, g.sql_bretr, g.sql_bretr, bretr);
  387.   modsql_queryuser (cmd, g.user, query, TRUE);
  388. }
  389. MODRET
  390. pre_cmd_quit (cmd_rec * cmd)
  391. {
  392.   if (g.ok)
  393.     modsql_close (cmd);
  394.   return DECLINED (cmd);
  395. }
  396. MODRET
  397. cmd_user (cmd_rec * cmd)
  398. {
  399.   if (!g.user[0])
  400.     sstrncpy (g.user, cmd->argv[1], ARBITRARY_MAX);
  401.   if (g.sql_passwd)
  402.     {
  403.       g.pass = (char *) _uservar (cmd, cmd->argv[1], g.sql_passwd);
  404.       if (!g.pass
  405.   && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
  406. if (_uservar (cmd, cmd->argv[1], "1"))
  407.   add_response (R_331, "Changing password for %s -- "
  408. "this password will be saved.", cmd->argv[1]);
  409.     }
  410.   return DECLINED (cmd);
  411. }
  412. MODRET
  413. cmd_pass (cmd_rec * cmd)
  414. {
  415.   if (g.sql_passwd && !g.pass
  416.       && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
  417.     add_response (R_230, ""%s" is the new user "%s" password.",
  418.   cmd->argv[1], g.user);
  419.   /* This is called before the disconnect() in log_cmd_pass. */
  420.   if (g.sql_fcdir)
  421.     {
  422.       const char *d = _uservar (cmd, g.user, g.sql_fcdir);
  423.       if (d)
  424. add_response (R_230, ""%s" was last directory.", d);
  425.     }
  426.   return DECLINED (cmd);
  427. }
  428. MODRET
  429. post_cmd_pass (cmd_rec * cmd)
  430. {
  431.   if (g.sql_passwd && g.sqled)
  432.     session.anon_user = session.user = (char *) g.user;
  433.   return DECLINED (cmd);
  434. }
  435. MODRET
  436. post_cmd_stor (cmd_rec * cmd)
  437. {
  438.   if (g.sql_fstor)
  439.     _setstats (cmd, 1, 0, session.xfer.total_bytes, 0);
  440.   return DECLINED (cmd);
  441. }
  442. MODRET
  443. cmd_retr (cmd_rec * cmd)
  444. {
  445.   int i;
  446.   char *path, *filename, *query;
  447.   if (g.sql_hittable)
  448.     {
  449.       path = dir_realpath (cmd->tmp_pool, cmd->argv[1]);
  450.       if (g.sql_dir && g.sql_dir[0])
  451. {
  452.   for (i = strlen (path), filename = path + i;
  453.        *filename != '/' && i > 1; i--)
  454.     filename--;
  455.   *filename++ = 0;
  456.   query = pstrcat (cmd->tmp_pool, "update ", g.sql_hittable,
  457.    " set ", g.sql_hits, " = ", g.sql_hits,
  458.    " + 1 where ", g.sql_dir, " = '", ++path,
  459.    "' and ", g.sql_filename, " = '", filename,
  460.    "'", 0);
  461. }
  462.       else
  463. query = pstrcat (cmd->tmp_pool, "update ", g.sql_hittable,
  464.  " set ", g.sql_hits, " = ", g.sql_hits,
  465.  " + 1 where ", g.sql_filename, " = '", path, "'", 0);
  466.       modsql_update (cmd, query);
  467.     }
  468.   return DECLINED (cmd);
  469. }
  470. MODRET
  471. post_cmd_retr (cmd_rec * cmd)
  472. {
  473.   if (g.sql_fretr)
  474.     _setstats (cmd, 0, 1, 0, session.xfer.total_bytes);
  475.   return DECLINED (cmd);
  476. }
  477. MODRET
  478. log_cmd_pass (cmd_rec * cmd)
  479. {
  480.   char *query;
  481.   if (g.sql_fhost)
  482.     {
  483.       query = pstrcat (cmd->tmp_pool, g.sql_fhost, " = '",
  484.        session.c->remote_name, "', ", g.sql_faddr,
  485.        " = '", inet_ntoa (*session.c->remote_ipaddr),
  486.        "', ", g.sql_ftime, " = now()", NULL);
  487.       modsql_queryuser (cmd, g.user, query, TRUE);
  488.     }
  489.   if (g.sql_flogs)
  490.     {
  491.       query = pstrcat (cmd->tmp_pool, g.sql_flogs, " = ", g.sql_flogs,
  492.        " + 1", NULL);
  493.       modsql_queryuser (cmd, g.user, query, TRUE);
  494.     }
  495.   /* Autononpersistence: disconnect now if no other feature is being used. */
  496.   if (!g.sql_fstor && !g.sql_fcdir && !g.sql_hittable)
  497.     modsql_close (cmd);
  498.   return DECLINED (cmd);
  499. }
  500. MODRET
  501. log_cmd_cwd (cmd_rec * cmd)
  502. {
  503.   if (g.sql_fcdir)
  504.     {
  505.       char *query = pstrcat (cmd->tmp_pool, g.sql_fcdir, " = '",
  506.      session.cwd, "'", NULL);
  507.       modsql_queryuser (cmd, g.user, query, TRUE);
  508.     }
  509.   return DECLINED (cmd);
  510. }
  511. cmdtable sqlpw_cmdtab[] = {
  512. /* *INDENT-OFF* */
  513.   { PRE_CMD,  C_QUIT, G_NONE, pre_cmd_quit,  FALSE, FALSE },
  514.   { CMD,      C_USER, G_NONE, cmd_user,  FALSE, FALSE },
  515.   { CMD,      C_PASS, G_NONE, cmd_pass,  FALSE, FALSE },
  516.   { POST_CMD, C_PASS, G_NONE, post_cmd_pass,  FALSE, FALSE },
  517.   { POST_CMD, C_STOR, G_NONE, post_cmd_stor,  FALSE, FALSE },
  518.   { POST_CMD, C_RETR, G_NONE, post_cmd_retr,  FALSE, FALSE },
  519.   { CMD,      C_RETR, G_NONE, cmd_retr,  FALSE, FALSE },
  520.   { LOG_CMD,  C_PASS, G_NONE, log_cmd_pass,  FALSE, FALSE },
  521.   { LOG_CMD,  C_CWD, G_NONE, log_cmd_cwd,  FALSE, FALSE },
  522.   { LOG_CMD,  C_CDUP, G_NONE, log_cmd_cwd,  FALSE, FALSE },
  523.   { 0,       NULL }
  524. /* *INDENT-ON* */
  525. };
  526. /* **************************************************************** */
  527. MODRET
  528. set_sqlloghosts (cmd_rec * cmd)
  529. {
  530.   int b;
  531.   CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
  532.   switch (cmd->argc - 1)
  533.     {
  534.     default:
  535.       CONF_ERROR (cmd, "requires a boolean or 3 field names: "
  536.   "fhost faddr ftime");
  537.     case 1:
  538.       if ((b = get_boolean (cmd, 1)) == -1)
  539. CONF_ERROR (cmd, "requires a boolean or 3 field names: "
  540.     "fhost faddr ftime");
  541.       if (b)
  542. add_config_param_str ("SQLLogHosts", 3, "fhost", "faddr", "ftime");
  543.       break;
  544.     case 3:
  545.       add_config_param_str ("SQLLogHosts", 3,
  546.     (void *) cmd->argv[1], (void *) cmd->argv[2],
  547.     (void *) cmd->argv[3]);
  548.     }
  549.   return HANDLED (cmd);
  550. }
  551. MODRET
  552. set_sqllogstats (cmd_rec * cmd)
  553. {
  554.   int b;
  555.   CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
  556.   switch (cmd->argc - 1)
  557.     {
  558.     default:
  559.       CONF_ERROR (cmd, "requires a boolean or 4 field names: "
  560.   "fstor fretr bstor bretr");
  561.     case 1:
  562.       if ((b = get_boolean (cmd, 1)) == -1)
  563. CONF_ERROR (cmd, "requires a boolean or 4 field names: "
  564.     "fstor fretr bstor bretr");
  565.       if (b)
  566. add_config_param_str ("SQLLogStats", 4,
  567.       "fstor", "fretr", "bstor", "bretr");
  568.       break;
  569.     case 4:
  570.       add_config_param_str ("SQLLogStats", 4,
  571.     (void *) cmd->argv[1], (void *) cmd->argv[2],
  572.     (void *) cmd->argv[3], (void *) cmd->argv[4]);
  573.     }
  574.   return HANDLED (cmd);
  575. }
  576. MODRET
  577. set_sqlloghits (cmd_rec * cmd)
  578. {
  579.   CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
  580.   switch (cmd->argc - 1)
  581.     {
  582.     default:
  583.       CONF_ERROR (cmd, "requires a table or table plus 3 fields: "
  584.   "[table] filename count dir");
  585.     case 1:
  586.       add_config_param_str ("SQLLogHits", 4, (void *) cmd->argv[1],
  587.     "filename", "count", "");
  588.       break;
  589.     case 3:
  590.       add_config_param_str ("SQLLogHits", 4,
  591.     (void *) cmd->argv[1], (void *) cmd->argv[2],
  592.     (void *) cmd->argv[3], "");
  593.     case 4:
  594.       add_config_param_str ("SQLLogHits", 4,
  595.     (void *) cmd->argv[1], (void *) cmd->argv[2],
  596.     (void *) cmd->argv[3], (void *) cmd->argv[4]);
  597.     }
  598.   return HANDLED (cmd);
  599. }
  600. MODRET
  601. set_sqllogdirs (cmd_rec * cmd)
  602. {
  603.   int b;
  604.   CHECK_ARGS (cmd, 1);
  605.   CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
  606.   if ((b = get_boolean (cmd, 1)) == -1)
  607.     add_config_param_str ("SQLLogDirs", 1, (void *) cmd->argv[1]);
  608.   else if (b)
  609.     add_config_param_str ("SQLLogDirs", 1, "fcdir");
  610.   return HANDLED (cmd);
  611. }
  612. MODRET
  613. set_sqlratios (cmd_rec * cmd)
  614. {
  615.   int b;
  616.   CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
  617.   switch (cmd->argc - 1)
  618.     {
  619.     default:
  620.       CONF_ERROR (cmd, "requires a boolean or 4 field names: "
  621.   "frate fcred brate bcred");
  622.     case 1:
  623.       if ((b = get_boolean (cmd, 1)) == -1)
  624. CONF_ERROR (cmd, "requires a boolean or 4 field names: "
  625.     "frate fcred brate bcred");
  626.       if (b)
  627. add_config_param_str ("SQLRatios", 4,
  628.       "frate", "fcred", "brate", "bcred");
  629.       break;
  630.     case 4:
  631.       add_config_param_str ("SQLRatios", 4,
  632.     (void *) cmd->argv[1], (void *) cmd->argv[2],
  633.     (void *) cmd->argv[3], (void *) cmd->argv[4]);
  634.     }
  635.   return HANDLED (cmd);
  636. }
  637. MODRET
  638. set_sqlempty (cmd_rec * cmd)
  639. {
  640.   int b;
  641.   config_rec *c;
  642.   CHECK_ARGS (cmd, 1);
  643.   CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
  644.   if ((b = get_boolean (cmd, 1)) == -1)
  645.     {
  646.       if (!strcasecmp (cmd->argv[1], "set"))
  647. b = 2;
  648.       else
  649. CONF_ERROR (cmd, "requires 'set' or a boolean value");
  650.     }
  651.   c = add_config_param (cmd->argv[0], 1, (void *) b);
  652.   c->flags |= CF_MERGEDOWN;
  653.   return HANDLED (cmd);
  654. }
  655. MODRET
  656. add_globalstr (cmd_rec * cmd)
  657. {
  658.   CHECK_ARGS (cmd, 1);
  659.   CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
  660.   add_config_param_str (cmd->argv[0], 1, (void *) cmd->argv[1]);
  661.   return HANDLED (cmd);
  662. }
  663. MODRET
  664. add_globalbool (cmd_rec * cmd)
  665. {
  666.   int b;
  667.   config_rec *c;
  668.   CHECK_ARGS (cmd, 1);
  669.   CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
  670.   b = get_boolean (cmd, 1);
  671.   if (b == -1)
  672.     CONF_ERROR (cmd, "requires a boolean value");
  673.   c = add_config_param (cmd->argv[0], 1, (void *) b);
  674.   c->flags |= CF_MERGEDOWN;
  675.   return HANDLED (cmd);
  676. }
  677. static conftable sqlpw_conftab[] = {
  678. /* *INDENT-OFF* */
  679.   { "SQLLogHosts",            set_sqlloghosts,   NULL },
  680.   { "SQLLogStats",            set_sqllogstats,   NULL },
  681.   { "SQLLogHits",             set_sqlloghits,    NULL },
  682.   { "SQLLogDirs",             set_sqllogdirs,    NULL },
  683.   { "SQLRatios",              set_sqlratios,     NULL },
  684.   { "SQLEmptyPasswords",      set_sqlempty,      NULL },
  685.   { "SQLUserTable",           add_globalstr,     NULL },
  686.   { "SQLUsernameField",       add_globalstr,     NULL },
  687.   { "SQLUidField",            add_globalstr,     NULL },
  688.   { "SQLGidField",            add_globalstr,     NULL },
  689.   { "SQLPasswordField",       add_globalstr,     NULL },
  690.   { "SQLHomedirField",        add_globalstr,     NULL },
  691.   { "SQLLoginCountField",     add_globalstr,     NULL },
  692.   { "SQLHomedir",             add_globalstr,     NULL },
  693.   { "SQLAuthoritative",       add_globalbool,    NULL },
  694.   { "SQLEncryptedPasswords",  add_globalbool,    NULL },
  695.   { "SQLPlaintextPasswords",  add_globalbool,    NULL },
  696.   { NULL, NULL, NULL }
  697. /* *INDENT-ON* */
  698. };
  699. /* **************************************************************** */
  700. static int
  701. sqlpw_child_init ()
  702. {
  703.   config_rec *c;
  704.   MODRET mr;
  705.   memset (&g, 0, sizeof (g));
  706.   g.ok = TRUE;
  707.   g.sql_passwd = get_param_ptr (CURRENT_CONF, "SQLPasswordField", FALSE);
  708.   g.sql_flogs = get_param_ptr (CURRENT_CONF, "SQLLoginCountField", FALSE);
  709.   g.sql_uid = get_param_ptr (CURRENT_CONF, "SQLUidField", FALSE);
  710.   g.sql_gid = get_param_ptr (CURRENT_CONF, "SQLGidField", FALSE);
  711.   if (!(g.homedir = get_param_ptr (CURRENT_CONF, "SQLHomedir", FALSE)))
  712.     {
  713.       g.sql_fhdir = get_param_ptr (CURRENT_CONF, "SQLHomedirField", FALSE);
  714.     }
  715.   if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogHosts", FALSE)))
  716.     {
  717.       g.sql_fhost = c->argv[0];
  718.       g.sql_faddr = c->argv[1];
  719.       g.sql_ftime = c->argv[2];
  720.     }
  721.   if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogStats", FALSE)))
  722.     {
  723.       g.sql_fstor = c->argv[0];
  724.       g.sql_fretr = c->argv[1];
  725.       g.sql_bstor = c->argv[2];
  726.       g.sql_bretr = c->argv[3];
  727.     }
  728.   if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLRatios", FALSE)))
  729.     {
  730.       if (!g.sql_fstor)
  731. log_pri (LOG_WARNING, "sqlpw: warning: SQLRatios directive "
  732.  "ineffective without SQLLogStats on");
  733.       g.sql_frate = c->argv[0];
  734.       g.sql_fcred = c->argv[1];
  735.       g.sql_brate = c->argv[2];
  736.       g.sql_bcred = c->argv[3];
  737.     }
  738.   if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogHits", FALSE)))
  739.     {
  740.       g.sql_hittable = c->argv[0];
  741.       g.sql_filename = c->argv[1];
  742.       g.sql_hits = c->argv[2];
  743.       g.sql_dir = c->argv[3];
  744.     }
  745.   g.sql_fcdir = get_param_ptr (CURRENT_CONF, "SQLLogDirs", FALSE);
  746.   g.sql_usertable = get_param_ptr (CURRENT_CONF, "SQLUserTable",
  747.    FALSE) ? : "users";
  748.   g.sql_userid = get_param_ptr (CURRENT_CONF, "SQLUsernameField",
  749. FALSE) ? : "userid";
  750.   if (!(g.homedir || g.sql_fhdir) && !g.sql_fhost
  751.       && !g.sql_fstor && !g.sql_fcdir)
  752.     return 0;
  753.   mr = modsql_open (NULL);
  754.   if (MODRET_ISHANDLED (mr))
  755.     {
  756.       log_debug (DEBUG3, "%s: configured: %s%s%s%s%s%s%s%s",
  757.  MOD_SQL_VERSION,
  758.  (g.sql_passwd && (g.homedir || g.sql_fhdir))
  759.  ? "auth " : "",
  760.  g.homedir ? "homedir " : "",
  761.  g.sql_fhdir ? "homedirfield " : "",
  762.  g.sql_fhost ? "loghosts " : "",
  763.  g.sql_fstor ? "logstats " : "",
  764.  g.sql_frate ? "ratios " : "",
  765.  g.sql_hittable ? "loghits " : "",
  766.  g.sql_fcdir ? "logdirs " : "");
  767.     }
  768.   else
  769.     {
  770.       memset (&g, 0, sizeof (g));
  771.       log_debug (DEBUG3, "%s: unconfigured: no backend could connect"
  772.  MOD_SQL_VERSION);
  773.     }
  774.   return 0;
  775. }
  776. static int
  777. sqlpw_parent_init (void)
  778. {
  779.   /* FIXME: add db init stuff once parent_init() actually works. */
  780.   return 0;
  781. }
  782. module sqlpw_module = {
  783.   NULL, NULL, /* Always NULL */
  784.   0x20, /* API Version 2.0 */
  785.   "sql",
  786.   sqlpw_conftab, /* SQL configuration handler table */
  787.   sqlpw_cmdtab, /* SQL command handler table */
  788.   sqlpw_authtab, /* SQL authentication handler table */
  789.   sqlpw_parent_init, /* Pre-fork "parent mode" init */
  790.   sqlpw_child_init /* Post-fork "child mode" init */
  791. };