aboutsummaryrefslogtreecommitdiffstats
path: root/ftp
diff options
context:
space:
mode:
authormharo <mharo@FreeBSD.org>2001-02-12 05:26:53 +0800
committermharo <mharo@FreeBSD.org>2001-02-12 05:26:53 +0800
commitac9244809d9e40538e249613b80ab5af150ed979 (patch)
treef58ff2dcedfdfc3e24418738b76b2c1a22e01d47 /ftp
parent9b877bef5913cd5d71277a11410e293a11b14bbd (diff)
downloadfreebsd-ports-gnome-ac9244809d9e40538e249613b80ab5af150ed979.tar.gz
freebsd-ports-gnome-ac9244809d9e40538e249613b80ab5af150ed979.tar.zst
freebsd-ports-gnome-ac9244809d9e40538e249613b80ab5af150ed979.zip
update to 1.2.0rc3
Submitted by: Anders Nordby <anders@fix.no>
Diffstat (limited to 'ftp')
-rw-r--r--ftp/proftpd-devel/Makefile16
-rw-r--r--ftp/proftpd-devel/distinfo4
-rw-r--r--ftp/proftpd-devel/files/patch-ae115
-rw-r--r--ftp/proftpd-devel/files/patch-ak12
-rw-r--r--ftp/proftpd-devel/files/patch-ba253
-rw-r--r--ftp/proftpd-devel/files/patch-bb1023
-rw-r--r--ftp/proftpd-devel/files/patch-bc19
-rw-r--r--ftp/proftpd-devel/files/patch-bd21
-rw-r--r--ftp/proftpd-devel/files/patch-be21
-rw-r--r--ftp/proftpd/Makefile16
-rw-r--r--ftp/proftpd/distinfo4
-rw-r--r--ftp/proftpd/files/patch-ae115
-rw-r--r--ftp/proftpd/files/patch-ak12
-rw-r--r--ftp/proftpd/files/patch-ba253
-rw-r--r--ftp/proftpd/files/patch-bb1023
-rw-r--r--ftp/proftpd/files/patch-bc19
-rw-r--r--ftp/proftpd/files/patch-bd21
-rw-r--r--ftp/proftpd/files/patch-be21
18 files changed, 2822 insertions, 146 deletions
diff --git a/ftp/proftpd-devel/Makefile b/ftp/proftpd-devel/Makefile
index a2ce7f485de1..b9c44d2db57d 100644
--- a/ftp/proftpd-devel/Makefile
+++ b/ftp/proftpd-devel/Makefile
@@ -6,21 +6,20 @@
#
PORTNAME= proftpd
-PORTVERSION= 1.2.0rc2
-PORTREVISION= 1
+PORTVERSION= 1.2.0rc3
CATEGORIES= ftp
MASTER_SITES= ftp://ftp.tos.net/pub/proftpd/ \
ftp://ftp.stikman.com/pub/proftpd/ \
ftp://ftp.linux.co.uk/pub/packages/proftpd/ \
ftp://ftp.drenik.net/linux/ftp/proftpd/
-DISTNAME= ${PORTNAME}-1.2.0rc2
+DISTNAME= ${PORTNAME}-1.2.0rc3
MAINTAINER= mharo@FreeBSD.org
.if defined(WITH_LDAP)
MASTER_SITES+= http://www.horde.net/~jwm/software/proftpd-ldap/
-MOD_LDAP= mod_ldap-2.6.1.tar.gz
-DISTFILES= ${DISTNAME}.${EXTRACT_SUFX} ${MOD_LDAP}
+MOD_LDAP= mod_ldap-2.7.4.tar.gz
+DISTFILES= ${DISTNAME}${EXTRACT_SUFX} ${MOD_LDAP}
.endif
MAN1= ftpcount.1 ftpwho.1
@@ -34,7 +33,7 @@ CONFIGURE_ARGS= --localstatedir=/var/run \
--disable-sendfile
#allow user to override
-MODULES?= mod_ratio:mod_readme:mod_tar
+MODULES?= mod_ratio:mod_readme
.if defined(WITH_LDAP)
MODULES:=$(MODULES):mod_ldap
@@ -42,6 +41,11 @@ BUILD_DEPENDS+= ${LOCALBASE}/lib/libldap.a:${PORTSDIR}/net/openldap
CONFIGURE_ENV+= CFLAGS="-I/usr/local/include -g" LDFLAGS=-L/usr/local/lib
.endif
+# Currently not supported by the ProFTPd project, but it's patched in with
+# bugfixes as well. For myself (anders@fix.no):
+# patch-bc: bugid 330 (cached passwords not cleared properly)
+# patch-bd: bugid 405 (sqlauthorative broken)
+# patch-be: bugid 457 (speed improvement)
.if defined(WITH_MYSQL)
MODULES:=$(MODULES):mod_sqlpw:mod_mysql
LIB_DEPENDS+= mysqlclient.10:${PORTSDIR}/databases/mysql323-client
diff --git a/ftp/proftpd-devel/distinfo b/ftp/proftpd-devel/distinfo
index a8e3849d8fdb..3a77685f3ed8 100644
--- a/ftp/proftpd-devel/distinfo
+++ b/ftp/proftpd-devel/distinfo
@@ -1,2 +1,2 @@
-MD5 (proftpd-1.2.0rc2.tar.gz) = 13e48f0f4006738ffb9aa7c28ab0ae63
-MD5 (mod_ldap-2.6.1.tar.gz) = 0e866a3137641e20e546543ed1c91760
+MD5 (proftpd-1.2.0rc3.tar.gz) = 0b2b32cec7138cdc1ba4b2a99148bb93
+MD5 (mod_ldap-2.7.4.tar.gz) = 7b3e948d89d2089b5c0b03fb9d22c050
diff --git a/ftp/proftpd-devel/files/patch-ae b/ftp/proftpd-devel/files/patch-ae
index eb59e14d9b3f..50db35749dd1 100644
--- a/ftp/proftpd-devel/files/patch-ae
+++ b/ftp/proftpd-devel/files/patch-ae
@@ -1,6 +1,6 @@
---- modules/mod_auth.c.orig Fri Jul 28 10:51:59 2000
-+++ modules/mod_auth.c Fri Jul 28 10:52:17 2000
-@@ -72,7 +72,7 @@
+--- modules/mod_auth.c.old Sun Feb 11 19:42:37 2001
++++ modules/mod_auth.c Sun Feb 11 19:43:18 2001
+@@ -94,7 +94,7 @@
"closing control connection.",
TimeoutLogin);
@@ -9,7 +9,7 @@
(void*) 0, NULL);
/* should never be reached */
-@@ -608,7 +608,7 @@
+@@ -630,7 +630,7 @@
c = _auth_resolve_user(p,&user,&ourname,&anonname);
if(!user) {
@@ -18,7 +18,7 @@
origuser);
goto auth_failure;
}
-@@ -617,7 +617,7 @@
+@@ -639,7 +639,7 @@
aclp = login_check_limits(main_server->conf,FALSE,TRUE,&i);
if((pw = auth_getpwnam(p,user)) == NULL) {
@@ -27,7 +27,7 @@
goto auth_failure;
}
-@@ -678,14 +678,14 @@
+@@ -700,14 +700,14 @@
}
if(!login_check_limits(c->subset,FALSE,TRUE,&i) || (!aclp && !i) ){
@@ -44,7 +44,7 @@
origuser);
goto auth_failure;
}
-@@ -702,7 +702,7 @@
+@@ -724,7 +724,7 @@
if(c && origuser && strcasecmp(user,origuser) &&
get_param_int(c->subset,"AuthUsingAlias",FALSE) == 1) {
user_name = origuser;
@@ -53,7 +53,7 @@
user, user_name);
}
-@@ -730,22 +730,22 @@
+@@ -752,22 +752,22 @@
switch(authcode) {
case AUTH_NOPWD:
@@ -80,7 +80,7 @@
user);
goto auth_failure;
-@@ -768,12 +768,12 @@
+@@ -790,12 +790,12 @@
auth_setgrent(p);
if(!_auth_check_shell((c ? c->subset : main_server->conf),pw->pw_shell)) {
@@ -95,7 +95,7 @@
user, FTPUSERS_PATH);
goto auth_failure;
}
-@@ -845,7 +845,7 @@
+@@ -870,7 +870,7 @@
if(!login_check_limits((c ? c->subset : main_server->conf),FALSE,TRUE,&i))
{
@@ -104,7 +104,7 @@
origuser);
goto auth_failure;
}
-@@ -860,7 +860,7 @@
+@@ -885,7 +885,7 @@
if(c && c->subset)
resolve_anonymous_dirs(c->subset);
@@ -113,61 +113,70 @@
(c != NULL) ? "ANON" : "USER",
origuser);
-@@ -1172,7 +1172,7 @@
-
- if(failnopwprompt) {
- if(!user) {
-- log_pri(LOG_NOTICE, "USER %s (Login failed): Not a UserAlias.",
-+ log_pri(LOG_INFO, "USER %s (Login failed): Not a UserAlias.",
- origuser);
- send_response(R_530,"Login incorrect.");
- end_login(0);
-@@ -1189,7 +1189,7 @@
-
- if(c) {
- if(!login_check_limits(c->subset,FALSE,TRUE,&i) || (!aclp && !i) ) {
-- log_auth(LOG_NOTICE, "ANON %s: Limit access denies login.",
-+ log_auth(LOG_INFO, "ANON %s: Limit access denies login.",
- origuser);
- send_response(R_530,"Login incorrect.");
- end_login(0);
-@@ -1197,7 +1197,7 @@
- }
-
- if(!c && !aclp) {
-- log_auth(LOG_NOTICE, "USER %s: Limit access denies login.", origuser);
-+ log_auth(LOG_INFO, "USER %s: Limit access denies login.", origuser);
- send_response(R_530,"Login incorrect.");
+@@ -1267,7 +1267,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_auth(LOG_NOTICE, "Connection refused (max clients for class %s).",
++ log_auth(LOG_INFO, "Connection refused (max clients for class %s).",
+ session.class->name);
+
end_login(0);
- }
-@@ -1269,7 +1269,7 @@
- "Too many users in your class, "
- "please try again later.");
-
-- log_auth(LOG_NOTICE, "Connection refused (max clients for class %s).",
-+ log_auth(LOG_INFO, "Connection refused (max clients for class %s).",
- session.class->name);
-
- end_login(0);
-@@ -1304,7 +1304,7 @@
- send_response(R_530,"%s",
- sreplace(cmd->tmp_pool,maxstr,"%m",maxn,NULL));
+@@ -1299,7 +1299,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
- log_auth(LOG_NOTICE, "Connection refused (max clients per host %d).",
+ log_auth(LOG_INFO, "Connection refused (max clients per host %d).",
max);
end_login(0);
-@@ -1330,7 +1330,7 @@
- if(cur >= max) {
+@@ -1324,7 +1324,7 @@
send_response(R_530, "%s",
sreplace(cmd->tmp_pool, maxstr, "%m", maxn, NULL));
+
- log_auth(LOG_NOTICE, "Connection refused (max clients %d).", max);
+ log_auth(LOG_INFO, "Connection refused (max clients %d).", max);
+
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+@@ -1354,7 +1354,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_auth(LOG_NOTICE, "Connection refused (max clients per host %d).",
++ log_auth(LOG_INFO, "Connection refused (max clients per host %d).",
+ max);
+
end_login(0);
- }
+@@ -1401,7 +1401,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_pri(LOG_NOTICE, "USER %s (Login failed): Not a UserAlias.",
++ log_pri(LOG_INFO, "USER %s (Login failed): Not a UserAlias.",
+ origuser);
+ send_response(R_530,"Login incorrect.");
-@@ -1411,7 +1411,7 @@
+@@ -1422,7 +1422,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_auth(LOG_NOTICE, "ANON %s: Limit access denies login.",
++ log_auth(LOG_INFO, "ANON %s: Limit access denies login.",
+ origuser);
+ send_response(R_530, "Login incorrect.");
+
+@@ -1434,7 +1434,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_auth(LOG_NOTICE, "USER %s: Limit access denies login.", origuser);
++ log_auth(LOG_INFO, "USER %s: Limit access denies login.", origuser);
+ send_response(R_530, "Login incorrect.");
+
+ end_login(0);
+@@ -1528,7 +1528,7 @@
if(++auth_tries >= max) {
send_response(R_530,"Login incorrect");
diff --git a/ftp/proftpd-devel/files/patch-ak b/ftp/proftpd-devel/files/patch-ak
deleted file mode 100644
index 0d8bb08fee5b..000000000000
--- a/ftp/proftpd-devel/files/patch-ak
+++ /dev/null
@@ -1,12 +0,0 @@
---- modules/mod_xfer.c.orig Mon Jul 31 16:49:19 2000
-+++ modules/mod_xfer.c Mon Jul 31 16:50:02 2000
-@@ -268,7 +268,8 @@
-
- /* No PORT command has been issued.
- */
-- if(session.d != NULL || !(session.flags & SF_PORT)) {
-+ if(!(session.flags & SF_PASSIVE) &&
-+ (session.d != NULL || !(session.flags & SF_PORT))) {
- add_response_err(R_503, "No PORT command issued first.");
- return ERROR(cmd);
- }
diff --git a/ftp/proftpd-devel/files/patch-ba b/ftp/proftpd-devel/files/patch-ba
new file mode 100644
index 000000000000..b1d054b52edc
--- /dev/null
+++ b/ftp/proftpd-devel/files/patch-ba
@@ -0,0 +1,253 @@
+--- contrib/mod_mysql.c.old Sun Feb 11 19:47:54 2001
++++ contrib/mod_mysql.c Sun Feb 11 19:45:37 2001
+@@ -0,0 +1,250 @@
++/*
++ * ProFTPD: mod_mysql -- Support for connecting to MySQL databases.
++ * Time-stamp: <1999-10-04 03:21:21 root>
++ * Copyright (c) 1998-1999 Johnie Ingram.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#define MOD_MYSQL_VERSION "mod_mysql/2.0"
++
++/* -- DO NOT MODIFY THE LINE BELOW UNLESS YOU FEEL LIKE IT --
++ * $Libraries: -lm -lmysqlclient $
++ */
++
++/* This is mod_mysql, contrib software for proftpd 1.2.0pre3 and above.
++ For more information contact Johnie Ingram <johnie@netgod.net>.
++
++ History Log:
++
++ * 1999-09-19: v2.0: Most directives split into mod_sql; invented API.
++
++*/
++
++#include "conf.h"
++#include <mysql.h>
++
++/* *INDENT-OFF* */
++
++static MYSQL mod_mysql_server;
++static MYSQL *mysqldb = 0;
++
++/* Maximum username field to expect, etc. */
++#define ARBITRARY_MAX 128
++
++static struct
++{
++ char *sql_host; /* Data for connecting, set by MySQLInfo. */
++ char *sql_user;
++ char *sql_pass;
++ char *sql_dbname;
++
++ int ok;
++ int opens;
++} g;
++
++/* *INDENT-ON* */
++
++/* **************************************************************** */
++
++MODRET
++sql_cmd_close (cmd_rec * cmd)
++{
++ log_debug (DEBUG5, "mysql: close [%i] for %s", g.opens, cmd->argv[0]);
++
++ if (!g.ok || g.opens--)
++ return DECLINED (cmd);
++
++ if (mysqldb)
++ {
++ log_debug (DEBUG4, "mysql: disconnecting: %s", mysql_stat (mysqldb));
++ mysql_close (mysqldb);
++ }
++ mysqldb = NULL;
++ return DECLINED (cmd);
++}
++
++MODRET
++sql_cmd_open (cmd_rec * cmd)
++{
++ if (!g.ok)
++ return DECLINED (cmd);
++
++ g.opens++;
++ log_debug (DEBUG5, "mysql: open [%i] for %s", g.opens, cmd->argv[0]);
++ if (g.opens > 1)
++ return HANDLED (cmd);
++
++ if (!(mysqldb = mysql_connect (&mod_mysql_server, g.sql_host,
++ g.sql_user, g.sql_pass)))
++ {
++ log_pri (LOG_ERR, "mysql: client %s connect FAILED to %s@%s",
++ mysql_get_client_info (), g.sql_user, g.sql_host);
++ g.ok = FALSE;
++ g.opens = 0;
++ return DECLINED (cmd);
++ }
++ log_debug (DEBUG5, "mysql: connect OK %s -> %s (%s@%s)",
++ mysql_get_client_info (), mysql_get_server_info (mysqldb),
++ g.sql_user, g.sql_host);
++
++ return HANDLED (cmd);
++}
++
++MODRET
++_do_query (cmd_rec * cmd, const char *query, int update)
++{
++ int error = 1;
++
++ if (!g.ok)
++ return DECLINED (cmd);
++
++ block_signals ();
++
++ /* This forces a quick ping of the remote server, so we know if its there. */
++ if (mysqldb)
++ mysql_select_db (mysqldb, g.sql_dbname);
++
++ if (!mysqldb
++ || ((error = mysql_query (mysqldb, query))
++ && !strcasecmp (mysql_error (mysqldb), "mysql server has gone")))
++ {
++ /* We need to restart the server link. */
++ if (mysqldb)
++ log_pri (LOG_ERR, "mysql: server has wandered off (%s/%s)",
++ g.sql_host, g.sql_dbname);
++ sql_cmd_open (cmd);
++ if (!mysqldb)
++ return DECLINED (cmd);
++ error = mysql_select_db (mysqldb, g.sql_dbname)
++ || mysql_query (mysqldb, query);
++ }
++
++ unblock_signals ();
++
++ if (error)
++ {
++ log_debug (DEBUG4, "mysql: %s failed: \"%s\": %s",
++ (update) ? "update" : "select",
++ query, mysql_error (mysqldb));
++ return DECLINED (cmd);
++ }
++
++ log_debug (DEBUG5, "mysql: %s OK: [%s] \"%s\"",
++ (update) ? "update" : "select", g.sql_dbname, query);
++ return HANDLED (cmd);
++}
++
++MODRET
++sql_cmd_update (cmd_rec * cmd)
++{
++ return _do_query (cmd, cmd->argv[1], TRUE);
++}
++
++MODRET
++sql_cmd_select (cmd_rec * cmd)
++{
++ MODRET mr;
++ MYSQL_RES *result;
++ MYSQL_ROW sql_row;
++ int i, j;
++
++ mr = _do_query (cmd, cmd->argv[1], FALSE);
++ if (!MODRET_ISHANDLED (mr))
++ return DECLINED (mr);
++
++ if ((result = mysql_store_result (mysqldb)))
++ {
++ int rcount = mysql_num_rows (result);
++ int fcount = mysql_num_fields (result);
++ int count = rcount * fcount;
++
++ char **data = pcalloc (cmd->tmp_pool, sizeof (char *) * (count + 1));
++ for (i = 0, count = 0; i < rcount; i++)
++ {
++ sql_row = mysql_fetch_row (result);
++ for (j = 0; j < fcount; j++)
++ data[count++] = pstrdup (cmd->tmp_pool, sql_row[j]);
++ }
++ mysql_free_result (result);
++ data[count] = NULL;
++ mr->data = data;
++ }
++ return mr;
++}
++
++static authtable mysql_authtab[] = {
++ {0, "dbd_open", sql_cmd_open},
++ {0, "dbd_close", sql_cmd_close},
++ {0, "dbd_update", sql_cmd_update},
++ {0, "dbd_select", sql_cmd_select},
++
++ {0, NULL, NULL}
++};
++
++/* **************************************************************** */
++
++MODRET set_sqlinfo (cmd_rec * cmd)
++{
++ CHECK_ARGS (cmd, 4);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL | CONF_VIRTUAL);
++ add_config_param_str ("MySQLInfo", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], (void *) cmd->argv[4]);
++ return HANDLED (cmd);
++}
++
++static conftable mysql_conftab[] = {
++/* *INDENT-OFF* */
++
++ { "MySQLInfo", set_sqlinfo, NULL },
++
++ { 0, NULL }
++
++/* *INDENT-ON* */
++};
++
++/* **************************************************************** */
++
++static int
++mysql_modinit ()
++{
++ config_rec *c;
++
++ memset (&g, 0, sizeof (g));
++ if (!(c = find_config (CURRENT_CONF, CONF_PARAM, "MySQLInfo", FALSE)))
++ return 0;
++
++ g.sql_host = pstrdup (session.pool, c->argv[0]);
++ g.sql_user = pstrdup (session.pool, c->argv[1]);
++ g.sql_pass = pstrdup (session.pool, c->argv[2]);
++ g.sql_dbname = pstrdup (session.pool, c->argv[3]);
++ g.ok = TRUE;
++
++ log_debug (DEBUG5, "%s: configured: db %s at %s@%s",
++ MOD_MYSQL_VERSION, g.sql_dbname, g.sql_user, g.sql_host);
++ return 0;
++}
++
++module mysql_module = {
++ NULL, NULL, /* Always NULL */
++ 0x20, /* API Version 2.0 */
++ "mysql",
++ mysql_conftab, /* SQL configuration handler table */
++ NULL, /* SQL command handler table */
++ mysql_authtab, /* SQL authentication handler table */
++ mysql_modinit, /* Pre-fork "parent-mode" init */
++ mysql_modinit /* Post-fork "child mode" init */
++};
diff --git a/ftp/proftpd-devel/files/patch-bb b/ftp/proftpd-devel/files/patch-bb
new file mode 100644
index 000000000000..7d17d667f7dc
--- /dev/null
+++ b/ftp/proftpd-devel/files/patch-bb
@@ -0,0 +1,1023 @@
+--- contrib/mod_sqlpw.c.old Sun Feb 11 19:47:34 2001
++++ contrib/mod_sqlpw.c Sun Feb 11 19:45:37 2001
+@@ -0,0 +1,1020 @@
++/*
++ * ProFTPD: mod_sql -- SQL frontend and user interface.
++ * Time-stamp: <1999-10-04 03:58:01 root>
++ * Copyright (c) 1998-1999 Johnie Ingram.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#define MOD_SQL_VERSION "mod_sqlpw/2.0"
++
++/* This is mod_sqlpw, contrib software for proftpd 1.2.0pre3 and above.
++ For more information contact Johnie Ingram <johnie@netgod.net>.
++
++ History Log:
++
++ * 2000-03-16: Added SQLKeyField and SQLKey directive, which
++ should allow authentication of different logins for different
++ VirtualHosts to be in the same database table.
++ Jake Buchholz <jake@execpc.com>
++
++ * 1999-09-19: v2.0: Backend directives split into mod_mysql.
++ Runtime API added. Username/passwords now escaped for SQL.
++
++ * 1999-09-16: v1.0: Added documentation, made directives more
++ generic ("MySQL" -> "SQL" except for MySQLInfo). Dir part of
++ SQLLogDirs finally made optional.
++
++ * 1999-06-01: v0.3: fixed segfault, changed 'strncmp' typo which
++ should have been copy. Made uid/gid support optional.
++
++ * 1999-05-03: v0.2: Removed dead code, fix bug with interaction
++ with anon-user support. Added MySQLLogHits and MySQLHomedir
++ directives. Fixed atoi() invocation that could segfault.
++ Copious debugging code added.
++
++ * 1999-04-09: v0.1: Initial attempt (mod_mysql).
++
++*/
++
++#include "conf.h"
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#include <mysql.h>
++
++/* *INDENT-OFF* */
++
++/* The Debian defintion of nobody/nogroup, minus 1 (used in getgwnam). */
++#define MOD_SQL_MAGIC_USER 65533
++#define MOD_SQL_MAGIC_GROUP 65533
++
++/* A uid or gid less than this is mapped to the magic numbers above
++ instead of simply rejected (which is arguably better, hmm.) */
++#define MOD_SQL_MIN_ID 999
++
++#define MOD_SQL_MAGIC_SHELL "/bin/sh"
++
++/* Maximum username field to expect, etc. */
++#define ARBITRARY_MAX 128
++
++static struct {
++ char user [ARBITRARY_MAX];
++ struct passwd pw; /* Scratch space for the getpwnam call. */
++ char *pass; /* The password from db. */
++
++ char *homedir; /* Set when getpwnam is called. */
++ int pwnamed; /* Set when getpwnam is called. */
++ int sqled; /* Set if sql auth was used. */
++
++ char *sql_usertable; /* CREATE TABLE users ( */
++ char *sql_userid; /* userid varchar(50) NOT NULL, */
++ char *sql_passwd; /* passwd varchar(15), */
++
++ char *sql_uid; /* uid int(5), */
++ char *sql_gid; /* gid int(5), */
++
++ char *sql_fstor; /* fstor int(11) NOT NULL DEFAULT '0', */
++ char *sql_fretr; /* fretr int(11) NOT NULL DEFAULT '0', */
++ char *sql_bstor; /* bstor int(11) NOT NULL DEFAULT '0', */
++ char *sql_bretr; /* bretr int(11) NOT NULL DEFAULT '0', */
++
++ char *sql_fhdir; /* fhdir varchar(255), */
++
++ char *sql_fhost; /* fhost varchar(50), */
++ char *sql_faddr; /* faddr char(15), */
++ char *sql_ftime; /* ftime timestamp, */
++
++ char *sql_fcdir; /* fcdir varchar(255), */
++
++ char *sql_frate; /* frate int(11) NOT NULL DEFAULT '5', */
++ char *sql_fcred; /* fcred int(2) NOT NULL DEFAULT '15', */
++ char *sql_brate; /* brate int(11) NOT NULL DEFAULT '5', */
++ char *sql_bcred; /* bcred int(2) NOT NULL DEFAULT '150000', */
++
++ char *sql_flogs; /* flogs int(11) NOT NULL DEFAULT '0', */
++
++ char *sql_hittable;
++ char *sql_dir;
++ char *sql_filename;
++ char *sql_hits;
++
++ char *sql_keyfld; /* table column name that stores the key */
++ char *sql_key; /* the key to match db table entries against */
++
++ char *loginmsg;
++ int ok;
++
++} g;
++
++/* *INDENT-ON* */
++
++/* **************************************************************** */
++
++/* Functions make_cmd and dispatch liberally stolen from auth.c. */
++
++static cmd_rec *
++_make_cmd (pool * cp, int argc, ...)
++{
++ va_list args;
++ cmd_rec *c;
++ int i;
++
++ c = pcalloc (cp, sizeof (cmd_rec));
++ c->argc = argc;
++ c->symtable_index = -1;
++
++ c->argv = pcalloc (cp, sizeof (void *) * (argc + 1));
++ c->argv[0] = MOD_SQL_VERSION;
++ va_start (args, argc);
++ for (i = 0; i < argc; i++)
++ c->argv[i + 1] = (void *) va_arg (args, char *);
++ va_end (args);
++
++ return c;
++}
++
++static modret_t *
++_dispatch_sql (cmd_rec * cmd, char *match)
++{
++ authtable *m;
++ modret_t *mr = NULL;
++
++ m = mod_find_auth_symbol (match, &cmd->symtable_index, NULL);
++ while (m)
++ {
++ mr = call_module_auth (m->m, m->handler, cmd);
++ if (MODRET_ISHANDLED (mr) || MODRET_ISERROR (mr))
++ break;
++ m = mod_find_auth_symbol (match, &cmd->symtable_index, m);
++ }
++ if (MODRET_ISERROR (mr))
++ log_debug (DEBUG0, "Aiee! sql internal! %s", MODRET_ERRMSG (mr));
++ return mr;
++}
++
++MODRET
++modsql_open (cmd_rec * cmd)
++{
++ cmd_rec *c;
++ modret_t *mr;
++
++ c = _make_cmd (cmd ? cmd->tmp_pool : permanent_pool, 0);
++ mr = _dispatch_sql (c, "dbd_open");
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ return mr;
++}
++
++MODRET
++modsql_close (cmd_rec * cmd)
++{
++ cmd_rec *c;
++ modret_t *mr;
++
++ c = _make_cmd (cmd ? cmd->tmp_pool : permanent_pool, 0);
++ mr = _dispatch_sql (c, "dbd_close");
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ return mr;
++}
++
++MODRET
++modsql_update (cmd_rec * cmd, const char *query)
++{
++ cmd_rec *c;
++ modret_t *mr;
++
++ c = _make_cmd (cmd->tmp_pool, 1, query);
++ mr = _dispatch_sql (c, "dbd_update");
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ return mr;
++}
++
++MODRET
++modsql_select (cmd_rec * cmd, const char *query)
++{
++ cmd_rec *c;
++ modret_t *mr;
++
++ c = _make_cmd (cmd->tmp_pool, 1, query);
++ mr = _dispatch_sql (c, "dbd_select");
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ return mr;
++}
++
++MODRET
++modsql_queryuser (cmd_rec * cmd, const char *user, const char *query,
++ int update)
++{
++ char *realquery;
++
++ if ((update) && (!g.sql_keyfld))
++ realquery = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
++ " set ", query, " where ", g.sql_userid, " = '",
++ user, "'", NULL);
++ else if ((update) && (g.sql_keyfld))
++ realquery = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
++ " set ", query, " where ", g.sql_userid, " = '",
++ user, "' and ", g.sql_keyfld, " = '",
++ g.sql_key, "'", NULL);
++ else if ((!update) && (!g.sql_keyfld))
++ realquery = pstrcat (cmd->tmp_pool, "select ", query, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "'", NULL);
++ else
++ realquery = pstrcat (cmd->tmp_pool, "select ", query, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "' and ", g.sql_keyfld, " = '",
++ g.sql_key, "'", NULL);
++ return (update) ? modsql_update (cmd, realquery)
++ : modsql_select (cmd, realquery);
++}
++
++/* **************************************************************** */
++
++static char *
++_uservar (cmd_rec * cmd, const char *user, const char *var)
++{
++ cmd_rec *c;
++ modret_t *mr;
++ char *query;
++ char **data;
++
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "select ", var, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "'", NULL);
++ else
++ query = pstrcat (cmd->tmp_pool, "select ", var, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "' and ", g.sql_keyfld, " = '",
++ g.sql_key, "'", NULL);
++ c = _make_cmd (cmd->tmp_pool, 1, query);
++
++ mr = _dispatch_sql (c, "dbd_select");
++
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ if (MODRET_ISHANDLED (mr))
++ {
++ data = mr->data;
++ return (data) ? data[0] : NULL;
++ }
++
++ return NULL;
++}
++
++/* Note: This function is called once by the master proftpd process
++ before it forks, so thankfully the homedir field is only set in the
++ child_init(). */
++
++MODRET
++auth_cmd_getpwnam (cmd_rec * cmd)
++{
++ const char *homedir;
++
++ if (!g.sql_fhdir && !g.homedir)
++ return DECLINED (cmd);
++
++ /* Only try this for the USER command (is also called twice after
++ PASS for some reason. [update: 1 is dir_realpath/defroot related.] */
++ if (!g.homedir && !g.pwnamed++)
++ {
++ if ((homedir = _uservar (cmd, cmd->argv[0], g.sql_fhdir)))
++ g.homedir = pstrdup (session.pool, homedir);
++ }
++ if (!g.homedir)
++ return DECLINED (cmd);
++
++ if (!g.pw.pw_name)
++ {
++ g.pw.pw_name = pstrdup(session.pool, cmd->argv[0]);
++ if (g.sql_uid)
++ g.pw.pw_uid = atoi (_uservar (cmd, cmd->argv[0], g.sql_uid) ? : "0");
++ if (g.pw.pw_uid < MOD_SQL_MIN_ID)
++ g.pw.pw_uid = MOD_SQL_MAGIC_USER;
++ if (g.sql_gid)
++ g.pw.pw_gid = atoi (_uservar (cmd, cmd->argv[0], g.sql_gid) ? : "0");
++ if (g.pw.pw_gid < MOD_SQL_MIN_ID)
++ g.pw.pw_gid = MOD_SQL_MAGIC_GROUP;
++ g.pw.pw_shell = MOD_SQL_MAGIC_SHELL;
++ g.pw.pw_dir = (char *) g.homedir;
++ log_debug (DEBUG3, "sqlpw: user \"%s\" (%i/%i) for %s",
++ cmd->argv[0], g.pw.pw_uid, g.pw.pw_gid, g.pw.pw_dir);
++
++ /* Copy username so proftpd anon handling won't confuse the issue. */
++
++ /* FIXME: unnecessary mysqlism */
++ mysql_escape_string (g.user, g.pw.pw_name, strlen (g.pw.pw_name));
++ g.user[ARBITRARY_MAX - 1] = 0;
++ }
++
++ return mod_create_data (cmd, &g.pw);
++}
++
++/* Returns 1 on successful match, 0 unsuccessful match, -1 on error. */
++
++static int
++_checkpass (cmd_rec * cmd, const char *user, const char *pass)
++{
++ int success = 0;
++ char *query;
++ int emptyok, cplain, ccrypt;
++ char **row;
++ MODRET mr;
++
++ emptyok = get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE);
++ cplain = get_param_int (CURRENT_CONF, "SQLPlaintextPasswords", FALSE);
++ ccrypt = get_param_int (CURRENT_CONF, "SQLEncryptedPasswords", FALSE);
++
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_passwd, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "'", " limit 2", NULL);
++ else
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_passwd, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "' and ", g.sql_keyfld, " = '",
++ g.sql_key, "' limit 2", NULL);
++ mr = modsql_select (cmd, query);
++ if (!(MODRET_HASDATA (mr)))
++ return -1;
++
++ row = mr->data;
++ if (row[0] == 0)
++ return 0;
++
++ if (!row[0])
++ {
++ log_debug (DEBUG4, "sqlpw: %s auth declined for NULL pass", user);
++ return 0;
++ }
++
++ if (row[1])
++ {
++ log_debug (DEBUG3, "sqlpw: %s pass result was not unique", user);
++ return -1;
++ }
++
++ if (emptyok == TRUE && !strlen (row[0]))
++ {
++ log_debug (DEBUG4, "sqlpw: warning: %s has empty password", user);
++ success = 1;
++ }
++
++ if (!success && cplain == TRUE && !strncasecmp (row[0], pass, 10))
++ {
++ success = 1;
++ }
++
++ /* Deliberate: ccrypt same if TRUE or -1 (unspecified). */
++ if (!success && ccrypt)
++ {
++ if (!strcmp (query = (char *) crypt (pass, row[0]), row[0]))
++ success = 1;
++ }
++
++ if (!success)
++ log_debug (DEBUG5, "sqlpw: %s auth failed: '%s' != '%s'",
++ user, pass, row ? row[0] : "");
++ return success;
++}
++
++MODRET
++auth_cmd_auth (cmd_rec * cmd)
++{
++ char *user, *pass;
++ int return_type;
++ int retval = AUTH_NOPWD;
++
++ if (!g.sql_passwd || !g.homedir)
++ return DECLINED (cmd);
++
++ /* Figure out our default return style: Whether or not SQL should
++ * allow other auth modules a shot at this user or not is controlled
++ * by the parameter "SQLAuthoritative". Like mod_pam this
++ * defaults to no. */
++ if((return_type = get_param_int (CURRENT_CONF,
++ "SQLAuthoritative", FALSE)) == -1) {
++ return_type = 0;
++ }
++
++ /* Just in case... */
++ if (cmd->argc != 2)
++ return return_type ? ERROR (cmd) : DECLINED (cmd);
++ if ((user = cmd->argv[0]) == NULL)
++ return return_type ? ERROR (cmd) : DECLINED (cmd);
++ if ((pass = cmd->argv[1]) == NULL)
++ return return_type ? ERROR (cmd) : DECLINED (cmd);
++
++ if (!g.pass
++ && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
++ {
++ char *query;
++ char passbuf[ARBITRARY_MAX] = {'\0'};
++
++ /* FIXME: unnecessary mysqlism */
++ mysql_escape_string (passbuf, cmd->argv[1], strlen (cmd->argv[1]));
++ g.user[ARBITRARY_MAX - 1] = 0;
++
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
++ " set ", g.sql_passwd, " = '", passbuf,
++ "' where ", g.sql_userid, " = '", g.user, "'", 0);
++ else
++ query = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
++ " set ", g.sql_passwd, " = '", passbuf,
++ "' where ", g.sql_userid, " = '", g.user, "' and ",
++ g.sql_keyfld, " = '", g.sql_key, "'", 0);
++ log_debug (DEBUG3, "sqlpw: %s NULL pass set to '%s'", user,
++ cmd->argv[1], query);
++ modsql_update (cmd, query);
++ }
++
++ if (_checkpass (cmd, g.user, pass) != 1)
++ return return_type ? ERROR_INT (cmd, retval) : DECLINED (cmd);
++
++ g.sqled++;
++ return HANDLED (cmd);
++}
++
++MODRET
++auth_cmd_getstats (cmd_rec * cmd)
++{
++ MODRET mr;
++ char *query;
++
++ if (g.sql_fstor)
++ {
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_fstor, ", ",
++ g.sql_fretr, ", ", g.sql_bstor, ", ",
++ g.sql_bretr, " from ", g.sql_usertable, " where ",
++ g.sql_userid, " = '", g.user, "'", NULL);
++ else
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_fstor, ", ",
++ g.sql_fretr, ", ", g.sql_bstor, ", ",
++ g.sql_bretr, " from ", g.sql_usertable, " where ",
++ g.sql_userid, " = '", g.user, "' and ", g.sql_keyfld,
++ " = '", g.sql_key, "'", NULL);
++ mr = modsql_select (cmd, query);
++ if (MODRET_HASDATA (mr))
++ return mr;
++ }
++ return DECLINED (cmd);
++}
++
++MODRET
++auth_cmd_getratio (cmd_rec * cmd)
++{
++ MODRET mr;
++ char *query;
++
++ if (g.sql_frate)
++ {
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_frate, ", ",
++ g.sql_fcred, ", ", g.sql_brate, ", ",
++ g.sql_bcred, " from ", g.sql_usertable, " where ",
++ g.sql_userid, " = '", g.user, "'", NULL);
++ else
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_frate, ", ",
++ g.sql_fcred, ", ", g.sql_brate, ", ",
++ g.sql_bcred, " from ", g.sql_usertable, " where ",
++ g.sql_userid, " = '", g.user, "' and ", g.sql_keyfld,
++ " = '", g.sql_key, "'", NULL);
++ mr = modsql_select (cmd, query);
++ if (MODRET_HASDATA (mr))
++ return mr;
++ }
++ return DECLINED (cmd);
++}
++
++static authtable sqlpw_authtab[] = {
++
++ {0, "auth", auth_cmd_auth},
++ {0, "getpwnam", auth_cmd_getpwnam},
++ {0, "getstats", auth_cmd_getstats},
++ {0, "getratio", auth_cmd_getratio},
++ {0, NULL, NULL}
++};
++
++/* **************************************************************** */
++
++static void
++_setstats (cmd_rec * cmd, int fstor, int fretr, int bstor, int bretr)
++{
++ char query[ARBITRARY_MAX] = {'\0'};
++ snprintf (query, sizeof (query),
++ "%s = %s + %i, %s = %s + %i, %s = %s + %i, %s = %s + %i",
++ g.sql_fstor, g.sql_fstor, fstor,
++ g.sql_fretr, g.sql_fretr, fretr,
++ g.sql_bstor, g.sql_bstor, bstor, g.sql_bretr, g.sql_bretr, bretr);
++ modsql_queryuser (cmd, g.user, query, TRUE);
++}
++
++MODRET
++pre_cmd_quit (cmd_rec * cmd)
++{
++ if (g.ok)
++ modsql_close (cmd);
++ return DECLINED (cmd);
++}
++
++MODRET
++cmd_user (cmd_rec * cmd)
++{
++ if (!g.user[0])
++ sstrncpy (g.user, cmd->argv[1], ARBITRARY_MAX);
++
++ if (g.sql_passwd)
++ {
++ g.pass = (char *) _uservar (cmd, cmd->argv[1], g.sql_passwd);
++ if (!g.pass
++ && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
++ if (_uservar (cmd, cmd->argv[1], "1"))
++ add_response (R_331, "Changing password for %s -- "
++ "this password will be saved.", cmd->argv[1]);
++ }
++ return DECLINED (cmd);
++}
++
++MODRET
++cmd_pass (cmd_rec * cmd)
++{
++ if (g.sql_passwd && !g.pass
++ && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
++ add_response (R_230, "\"%s\" is the new user \"%s\" password.",
++ cmd->argv[1], g.user);
++
++ /* This is called before the disconnect() in log_cmd_pass. */
++ if (g.sql_fcdir)
++ {
++ const char *d = _uservar (cmd, g.user, g.sql_fcdir);
++ if (d)
++ add_response (R_230, "\"%s\" was last directory.", d);
++ }
++
++ return DECLINED (cmd);
++}
++
++MODRET
++post_cmd_pass (cmd_rec * cmd)
++{
++ if (g.sql_passwd && g.sqled)
++ session.anon_user = session.user = (char *) g.user;
++ return DECLINED (cmd);
++}
++
++MODRET
++post_cmd_stor (cmd_rec * cmd)
++{
++ if (g.sql_fstor)
++ _setstats (cmd, 1, 0, session.xfer.total_bytes, 0);
++ return DECLINED (cmd);
++}
++
++MODRET
++cmd_retr (cmd_rec * cmd)
++{
++ int i;
++ char *path, *filename, *query;
++ if (g.sql_hittable)
++ {
++ path = dir_realpath (cmd->tmp_pool, cmd->argv[1]);
++ if (g.sql_dir && g.sql_dir[0])
++ {
++ for (i = strlen (path), filename = path + i;
++ *filename != '/' && i > 1; i--)
++ filename--;
++ *filename++ = 0;
++ query = pstrcat (cmd->tmp_pool, "update ", g.sql_hittable,
++ " set ", g.sql_hits, " = ", g.sql_hits,
++ " + 1 where ", g.sql_dir, " = '", ++path,
++ "' and ", g.sql_filename, " = '", filename,
++ "'", 0);
++ }
++ else
++ query = pstrcat (cmd->tmp_pool, "update ", g.sql_hittable,
++ " set ", g.sql_hits, " = ", g.sql_hits,
++ " + 1 where ", g.sql_filename, " = '", path, "'", 0);
++ modsql_update (cmd, query);
++ }
++ return DECLINED (cmd);
++}
++
++MODRET
++post_cmd_retr (cmd_rec * cmd)
++{
++ if (g.sql_fretr)
++ _setstats (cmd, 0, 1, 0, session.xfer.total_bytes);
++ return DECLINED (cmd);
++}
++
++MODRET
++log_cmd_pass (cmd_rec * cmd)
++{
++ char *query;
++
++ if (g.sql_fhost)
++ {
++ query = pstrcat (cmd->tmp_pool, g.sql_fhost, " = '",
++ session.c->remote_name, "', ", g.sql_faddr,
++ " = '", inet_ntoa (*session.c->remote_ipaddr),
++ "', ", g.sql_ftime, " = now()", NULL);
++ modsql_queryuser (cmd, g.user, query, TRUE);
++ }
++ if (g.sql_flogs)
++ {
++ query = pstrcat (cmd->tmp_pool, g.sql_flogs, " = ", g.sql_flogs,
++ " + 1", NULL);
++ modsql_queryuser (cmd, g.user, query, TRUE);
++ }
++
++ /* Autononpersistence: disconnect now if no other feature is being used. */
++ if (!g.sql_fstor && !g.sql_fcdir && !g.sql_hittable)
++ modsql_close (cmd);
++ return DECLINED (cmd);
++}
++
++MODRET
++log_cmd_cwd (cmd_rec * cmd)
++{
++ if (g.sql_fcdir)
++ {
++ char *query = pstrcat (cmd->tmp_pool, g.sql_fcdir, " = '",
++ session.cwd, "'", NULL);
++ modsql_queryuser (cmd, g.user, query, TRUE);
++ }
++ return DECLINED (cmd);
++}
++
++static cmdtable sqlpw_cmdtab[] = {
++/* *INDENT-OFF* */
++
++ { PRE_CMD, C_QUIT, G_NONE, pre_cmd_quit, FALSE, FALSE },
++ { CMD, C_USER, G_NONE, cmd_user, FALSE, FALSE },
++ { CMD, C_PASS, G_NONE, cmd_pass, FALSE, FALSE },
++ { POST_CMD, C_PASS, G_NONE, post_cmd_pass, FALSE, FALSE },
++ { POST_CMD, C_STOR, G_NONE, post_cmd_stor, FALSE, FALSE },
++ { POST_CMD, C_RETR, G_NONE, post_cmd_retr, FALSE, FALSE },
++ { CMD, C_RETR, G_NONE, cmd_retr, FALSE, FALSE },
++ { LOG_CMD, C_PASS, G_NONE, log_cmd_pass, FALSE, FALSE },
++ { LOG_CMD, C_CWD, G_NONE, log_cmd_cwd, FALSE, FALSE },
++ { LOG_CMD, C_CDUP, G_NONE, log_cmd_cwd, FALSE, FALSE },
++
++ { 0, NULL }
++
++/* *INDENT-ON* */
++};
++
++/* **************************************************************** */
++
++MODRET
++set_sqlloghosts (cmd_rec * cmd)
++{
++ int b;
++
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ switch (cmd->argc - 1)
++ {
++ default:
++ CONF_ERROR (cmd, "requires a boolean or 3 field names: "
++ "fhost faddr ftime");
++ case 1:
++ if ((b = get_boolean (cmd, 1)) == -1)
++ CONF_ERROR (cmd, "requires a boolean or 3 field names: "
++ "fhost faddr ftime");
++ if (b)
++ add_config_param_str ("SQLLogHosts", 3, "fhost", "faddr", "ftime");
++ break;
++
++ case 3:
++ add_config_param_str ("SQLLogHosts", 3,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3]);
++ }
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqllogstats (cmd_rec * cmd)
++{
++ int b;
++
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ switch (cmd->argc - 1)
++ {
++ default:
++ CONF_ERROR (cmd, "requires a boolean or 4 field names: "
++ "fstor fretr bstor bretr");
++ case 1:
++ if ((b = get_boolean (cmd, 1)) == -1)
++ CONF_ERROR (cmd, "requires a boolean or 4 field names: "
++ "fstor fretr bstor bretr");
++ if (b)
++ add_config_param_str ("SQLLogStats", 4,
++ "fstor", "fretr", "bstor", "bretr");
++ break;
++
++ case 4:
++ add_config_param_str ("SQLLogStats", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], (void *) cmd->argv[4]);
++ }
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqlloghits (cmd_rec * cmd)
++{
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ switch (cmd->argc - 1)
++ {
++ default:
++ CONF_ERROR (cmd, "requires a table or table plus 3 fields: "
++ "[table] filename count dir");
++ case 1:
++ add_config_param_str ("SQLLogHits", 4, (void *) cmd->argv[1],
++ "filename", "count", "");
++ break;
++ case 3:
++ add_config_param_str ("SQLLogHits", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], "");
++
++ case 4:
++ add_config_param_str ("SQLLogHits", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], (void *) cmd->argv[4]);
++ }
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqllogdirs (cmd_rec * cmd)
++{
++ int b;
++
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ if ((b = get_boolean (cmd, 1)) == -1)
++ add_config_param_str ("SQLLogDirs", 1, (void *) cmd->argv[1]);
++ else if (b)
++ add_config_param_str ("SQLLogDirs", 1, "fcdir");
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqlratios (cmd_rec * cmd)
++{
++ int b;
++
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ switch (cmd->argc - 1)
++ {
++ default:
++ CONF_ERROR (cmd, "requires a boolean or 4 field names: "
++ "frate fcred brate bcred");
++ case 1:
++ if ((b = get_boolean (cmd, 1)) == -1)
++ CONF_ERROR (cmd, "requires a boolean or 4 field names: "
++ "frate fcred brate bcred");
++ if (b)
++ add_config_param_str ("SQLRatios", 4,
++ "frate", "fcred", "brate", "bcred");
++ break;
++
++ case 4:
++ add_config_param_str ("SQLRatios", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], (void *) cmd->argv[4]);
++ }
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqlempty (cmd_rec * cmd)
++{
++ int b;
++ config_rec *c;
++
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++
++ if ((b = get_boolean (cmd, 1)) == -1)
++ {
++ if (!strcasecmp (cmd->argv[1], "set"))
++ b = 2;
++ else
++ CONF_ERROR (cmd, "requires 'set' or a boolean value");
++ }
++ c = add_config_param (cmd->argv[0], 1, (void *) b);
++ c->flags |= CF_MERGEDOWN;
++ return HANDLED (cmd);
++}
++
++MODRET
++add_globalstr (cmd_rec * cmd)
++{
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ add_config_param_str (cmd->argv[0], 1, (void *) cmd->argv[1]);
++ return HANDLED (cmd);
++}
++
++MODRET
++add_virtualstr (cmd_rec * cmd)
++{
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL | CONF_VIRTUAL);
++ add_config_param_str (cmd->argv[0], 1, (void *) cmd->argv[1]);
++ return HANDLED (cmd);
++}
++
++MODRET
++add_globalbool (cmd_rec * cmd)
++{
++ int b;
++ config_rec *c;
++
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++
++ b = get_boolean (cmd, 1);
++ if (b == -1)
++ CONF_ERROR (cmd, "requires a boolean value");
++ c = add_config_param (cmd->argv[0], 1, (void *) b);
++ c->flags |= CF_MERGEDOWN;
++ return HANDLED (cmd);
++}
++
++MODRET
++add_virtualbool (cmd_rec * cmd)
++{
++ int b;
++ config_rec *c;
++
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL | CONF_VIRTUAL);
++
++ b = get_boolean (cmd, 1);
++ if (b == -1)
++ CONF_ERROR (cmd, "requires a boolean value");
++ c = add_config_param (cmd->argv[0], 1, (void *) b);
++ c->flags |= CF_MERGEDOWN;
++ return HANDLED (cmd);
++}
++
++static conftable sqlpw_conftab[] = {
++/* *INDENT-OFF* */
++
++ { "SQLLogHosts", set_sqlloghosts, NULL },
++ { "SQLLogStats", set_sqllogstats, NULL },
++ { "SQLLogHits", set_sqlloghits, NULL },
++ { "SQLLogDirs", set_sqllogdirs, NULL },
++ { "SQLRatios", set_sqlratios, NULL },
++ { "SQLEmptyPasswords", set_sqlempty, NULL },
++
++ { "SQLUserTable", add_globalstr, NULL },
++ { "SQLUsernameField", add_globalstr, NULL },
++ { "SQLUidField", add_globalstr, NULL },
++ { "SQLGidField", add_globalstr, NULL },
++ { "SQLPasswordField", add_globalstr, NULL },
++ { "SQLHomedirField", add_globalstr, NULL },
++ { "SQLLoginCountField", add_globalstr, NULL },
++ { "SQLHomedir", add_globalstr, NULL },
++
++ { "SQLKeyField", add_globalstr, NULL },
++ { "SQLKey", add_virtualstr, NULL },
++
++ { "SQLAuthoritative", add_virtualbool, NULL },
++ { "SQLEncryptedPasswords", add_globalbool, NULL },
++ { "SQLPlaintextPasswords", add_globalbool, NULL },
++
++ { NULL, NULL, NULL }
++
++/* *INDENT-ON* */
++};
++
++/* **************************************************************** */
++
++static int
++sqlpw_child_init ()
++{
++ config_rec *c;
++ MODRET mr;
++
++ memset (&g, 0, sizeof (g));
++ g.ok = TRUE;
++
++ g.sql_passwd = get_param_ptr (CURRENT_CONF, "SQLPasswordField", FALSE);
++ g.sql_flogs = get_param_ptr (CURRENT_CONF, "SQLLoginCountField", FALSE);
++ g.sql_uid = get_param_ptr (CURRENT_CONF, "SQLUidField", FALSE);
++ g.sql_gid = get_param_ptr (CURRENT_CONF, "SQLGidField", FALSE);
++
++ if (!(g.homedir = get_param_ptr (CURRENT_CONF, "SQLHomedir", FALSE)))
++ {
++ g.sql_fhdir = get_param_ptr (CURRENT_CONF, "SQLHomedirField", FALSE);
++ }
++ if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogHosts", FALSE)))
++ {
++ g.sql_fhost = c->argv[0];
++ g.sql_faddr = c->argv[1];
++ g.sql_ftime = c->argv[2];
++ }
++ if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogStats", FALSE)))
++ {
++ g.sql_fstor = c->argv[0];
++ g.sql_fretr = c->argv[1];
++ g.sql_bstor = c->argv[2];
++ g.sql_bretr = c->argv[3];
++ }
++ if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLRatios", FALSE)))
++ {
++ if (!g.sql_fstor)
++ log_pri (LOG_WARNING, "sqlpw: warning: SQLRatios directive "
++ "ineffective without SQLLogStats on");
++ g.sql_frate = c->argv[0];
++ g.sql_fcred = c->argv[1];
++ g.sql_brate = c->argv[2];
++ g.sql_bcred = c->argv[3];
++ }
++ if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogHits", FALSE)))
++ {
++ g.sql_hittable = c->argv[0];
++ g.sql_filename = c->argv[1];
++ g.sql_hits = c->argv[2];
++ g.sql_dir = c->argv[3];
++ }
++ g.sql_fcdir = get_param_ptr (CURRENT_CONF, "SQLLogDirs", FALSE);
++
++ g.sql_usertable = get_param_ptr (CURRENT_CONF, "SQLUserTable",
++ FALSE) ? : "users";
++ g.sql_userid = get_param_ptr (CURRENT_CONF, "SQLUsernameField",
++ FALSE) ? : "userid";
++
++ g.sql_keyfld = get_param_ptr (CURRENT_CONF, "SQLKeyField", FALSE);
++ g.sql_key = get_param_ptr (CURRENT_CONF, "SQLKey", FALSE);
++
++ if (!(g.homedir || g.sql_fhdir) && !g.sql_fhost
++ && !g.sql_fstor && !g.sql_fcdir)
++ return 0;
++
++ mr = modsql_open (NULL);
++ if (MODRET_ISHANDLED (mr))
++ {
++ log_debug (DEBUG3, "%s: configured: %s%s%s%s%s%s%s%s",
++ MOD_SQL_VERSION,
++ (g.sql_passwd && (g.homedir || g.sql_fhdir))
++ ? "auth " : "",
++ g.homedir ? "homedir " : "",
++ g.sql_fhdir ? "homedirfield " : "",
++ g.sql_fhost ? "loghosts " : "",
++ g.sql_fstor ? "logstats " : "",
++ g.sql_frate ? "ratios " : "",
++ g.sql_hittable ? "loghits " : "",
++ g.sql_fcdir ? "logdirs " : "");
++ }
++ else
++ {
++ memset (&g, 0, sizeof (g));
++ log_debug (DEBUG3, "%s: unconfigured: no backend could connect",
++ MOD_SQL_VERSION);
++ }
++ return 0;
++}
++
++static int
++sqlpw_parent_init (void)
++{
++ /* FIXME: add db init stuff once parent_init() actually works. */
++ return 0;
++}
++
++module sqlpw_module = {
++ NULL, NULL, /* Always NULL */
++ 0x20, /* API Version 2.0 */
++ "sql",
++ sqlpw_conftab, /* SQL configuration handler table */
++ sqlpw_cmdtab, /* SQL command handler table */
++ sqlpw_authtab, /* SQL authentication handler table */
++ sqlpw_parent_init, /* Pre-fork "parent mode" init */
++ sqlpw_child_init /* Post-fork "child mode" init */
++};
diff --git a/ftp/proftpd-devel/files/patch-bc b/ftp/proftpd-devel/files/patch-bc
new file mode 100644
index 000000000000..bd908a4fa9fa
--- /dev/null
+++ b/ftp/proftpd-devel/files/patch-bc
@@ -0,0 +1,19 @@
+--- contrib/mod_sqlpw.c.old Sun Feb 11 19:52:06 2001
++++ contrib/mod_sqlpw.c Sun Feb 11 19:53:42 2001
+@@ -302,8 +302,6 @@
+ if (!g.homedir)
+ return DECLINED (cmd);
+
+- if (!g.pw.pw_name)
+- {
+ g.pw.pw_name = pstrdup(session.pool, cmd->argv[0]);
+ if (g.sql_uid)
+ g.pw.pw_uid = atoi (_uservar (cmd, cmd->argv[0], g.sql_uid) ? : "0");
+@@ -323,7 +321,6 @@
+ /* FIXME: unnecessary mysqlism */
+ mysql_escape_string (g.user, g.pw.pw_name, strlen (g.pw.pw_name));
+ g.user[ARBITRARY_MAX - 1] = 0;
+- }
+
+ return mod_create_data (cmd, &g.pw);
+ }
diff --git a/ftp/proftpd-devel/files/patch-bd b/ftp/proftpd-devel/files/patch-bd
new file mode 100644
index 000000000000..7eb41bc857e8
--- /dev/null
+++ b/ftp/proftpd-devel/files/patch-bd
@@ -0,0 +1,21 @@
+--- contrib/mod_sqlpw.c.old Sun Feb 11 20:14:40 2001
++++ contrib/mod_sqlpw.c Sun Feb 11 20:16:00 2001
+@@ -400,9 +400,6 @@
+ int return_type;
+ int retval = AUTH_NOPWD;
+
+- if (!g.sql_passwd || !g.homedir)
+- return DECLINED (cmd);
+-
+ /* Figure out our default return style: Whether or not SQL should
+ * allow other auth modules a shot at this user or not is controlled
+ * by the parameter "SQLAuthoritative". Like mod_pam this
+@@ -418,6 +415,8 @@
+ if ((user = cmd->argv[0]) == NULL)
+ return return_type ? ERROR (cmd) : DECLINED (cmd);
+ if ((pass = cmd->argv[1]) == NULL)
++ return return_type ? ERROR (cmd) : DECLINED (cmd);
++ if (!g.sql_passwd || !g.homedir)
+ return return_type ? ERROR (cmd) : DECLINED (cmd);
+
+ if (!g.pass
diff --git a/ftp/proftpd-devel/files/patch-be b/ftp/proftpd-devel/files/patch-be
new file mode 100644
index 000000000000..7a6578032734
--- /dev/null
+++ b/ftp/proftpd-devel/files/patch-be
@@ -0,0 +1,21 @@
+--- contrib/mod_mysql.c.old Sun Feb 11 20:19:00 2001
++++ contrib/mod_mysql.c Sun Feb 11 20:21:29 2001
+@@ -100,6 +100,9 @@
+ mysql_get_client_info (), mysql_get_server_info (mysqldb),
+ g.sql_user, g.sql_host);
+
++ if (mysqldb)
++ mysql_select_db (mysqldb, g.sql_dbname);
++
+ return HANDLED (cmd);
+ }
+
+@@ -115,7 +118,7 @@
+
+ /* This forces a quick ping of the remote server, so we know if its there. */
+ if (mysqldb)
+- mysql_select_db (mysqldb, g.sql_dbname);
++ mysql_ping( mysqldb );
+
+ if (!mysqldb
+ || ((error = mysql_query (mysqldb, query))
diff --git a/ftp/proftpd/Makefile b/ftp/proftpd/Makefile
index a2ce7f485de1..b9c44d2db57d 100644
--- a/ftp/proftpd/Makefile
+++ b/ftp/proftpd/Makefile
@@ -6,21 +6,20 @@
#
PORTNAME= proftpd
-PORTVERSION= 1.2.0rc2
-PORTREVISION= 1
+PORTVERSION= 1.2.0rc3
CATEGORIES= ftp
MASTER_SITES= ftp://ftp.tos.net/pub/proftpd/ \
ftp://ftp.stikman.com/pub/proftpd/ \
ftp://ftp.linux.co.uk/pub/packages/proftpd/ \
ftp://ftp.drenik.net/linux/ftp/proftpd/
-DISTNAME= ${PORTNAME}-1.2.0rc2
+DISTNAME= ${PORTNAME}-1.2.0rc3
MAINTAINER= mharo@FreeBSD.org
.if defined(WITH_LDAP)
MASTER_SITES+= http://www.horde.net/~jwm/software/proftpd-ldap/
-MOD_LDAP= mod_ldap-2.6.1.tar.gz
-DISTFILES= ${DISTNAME}.${EXTRACT_SUFX} ${MOD_LDAP}
+MOD_LDAP= mod_ldap-2.7.4.tar.gz
+DISTFILES= ${DISTNAME}${EXTRACT_SUFX} ${MOD_LDAP}
.endif
MAN1= ftpcount.1 ftpwho.1
@@ -34,7 +33,7 @@ CONFIGURE_ARGS= --localstatedir=/var/run \
--disable-sendfile
#allow user to override
-MODULES?= mod_ratio:mod_readme:mod_tar
+MODULES?= mod_ratio:mod_readme
.if defined(WITH_LDAP)
MODULES:=$(MODULES):mod_ldap
@@ -42,6 +41,11 @@ BUILD_DEPENDS+= ${LOCALBASE}/lib/libldap.a:${PORTSDIR}/net/openldap
CONFIGURE_ENV+= CFLAGS="-I/usr/local/include -g" LDFLAGS=-L/usr/local/lib
.endif
+# Currently not supported by the ProFTPd project, but it's patched in with
+# bugfixes as well. For myself (anders@fix.no):
+# patch-bc: bugid 330 (cached passwords not cleared properly)
+# patch-bd: bugid 405 (sqlauthorative broken)
+# patch-be: bugid 457 (speed improvement)
.if defined(WITH_MYSQL)
MODULES:=$(MODULES):mod_sqlpw:mod_mysql
LIB_DEPENDS+= mysqlclient.10:${PORTSDIR}/databases/mysql323-client
diff --git a/ftp/proftpd/distinfo b/ftp/proftpd/distinfo
index a8e3849d8fdb..3a77685f3ed8 100644
--- a/ftp/proftpd/distinfo
+++ b/ftp/proftpd/distinfo
@@ -1,2 +1,2 @@
-MD5 (proftpd-1.2.0rc2.tar.gz) = 13e48f0f4006738ffb9aa7c28ab0ae63
-MD5 (mod_ldap-2.6.1.tar.gz) = 0e866a3137641e20e546543ed1c91760
+MD5 (proftpd-1.2.0rc3.tar.gz) = 0b2b32cec7138cdc1ba4b2a99148bb93
+MD5 (mod_ldap-2.7.4.tar.gz) = 7b3e948d89d2089b5c0b03fb9d22c050
diff --git a/ftp/proftpd/files/patch-ae b/ftp/proftpd/files/patch-ae
index eb59e14d9b3f..50db35749dd1 100644
--- a/ftp/proftpd/files/patch-ae
+++ b/ftp/proftpd/files/patch-ae
@@ -1,6 +1,6 @@
---- modules/mod_auth.c.orig Fri Jul 28 10:51:59 2000
-+++ modules/mod_auth.c Fri Jul 28 10:52:17 2000
-@@ -72,7 +72,7 @@
+--- modules/mod_auth.c.old Sun Feb 11 19:42:37 2001
++++ modules/mod_auth.c Sun Feb 11 19:43:18 2001
+@@ -94,7 +94,7 @@
"closing control connection.",
TimeoutLogin);
@@ -9,7 +9,7 @@
(void*) 0, NULL);
/* should never be reached */
-@@ -608,7 +608,7 @@
+@@ -630,7 +630,7 @@
c = _auth_resolve_user(p,&user,&ourname,&anonname);
if(!user) {
@@ -18,7 +18,7 @@
origuser);
goto auth_failure;
}
-@@ -617,7 +617,7 @@
+@@ -639,7 +639,7 @@
aclp = login_check_limits(main_server->conf,FALSE,TRUE,&i);
if((pw = auth_getpwnam(p,user)) == NULL) {
@@ -27,7 +27,7 @@
goto auth_failure;
}
-@@ -678,14 +678,14 @@
+@@ -700,14 +700,14 @@
}
if(!login_check_limits(c->subset,FALSE,TRUE,&i) || (!aclp && !i) ){
@@ -44,7 +44,7 @@
origuser);
goto auth_failure;
}
-@@ -702,7 +702,7 @@
+@@ -724,7 +724,7 @@
if(c && origuser && strcasecmp(user,origuser) &&
get_param_int(c->subset,"AuthUsingAlias",FALSE) == 1) {
user_name = origuser;
@@ -53,7 +53,7 @@
user, user_name);
}
-@@ -730,22 +730,22 @@
+@@ -752,22 +752,22 @@
switch(authcode) {
case AUTH_NOPWD:
@@ -80,7 +80,7 @@
user);
goto auth_failure;
-@@ -768,12 +768,12 @@
+@@ -790,12 +790,12 @@
auth_setgrent(p);
if(!_auth_check_shell((c ? c->subset : main_server->conf),pw->pw_shell)) {
@@ -95,7 +95,7 @@
user, FTPUSERS_PATH);
goto auth_failure;
}
-@@ -845,7 +845,7 @@
+@@ -870,7 +870,7 @@
if(!login_check_limits((c ? c->subset : main_server->conf),FALSE,TRUE,&i))
{
@@ -104,7 +104,7 @@
origuser);
goto auth_failure;
}
-@@ -860,7 +860,7 @@
+@@ -885,7 +885,7 @@
if(c && c->subset)
resolve_anonymous_dirs(c->subset);
@@ -113,61 +113,70 @@
(c != NULL) ? "ANON" : "USER",
origuser);
-@@ -1172,7 +1172,7 @@
-
- if(failnopwprompt) {
- if(!user) {
-- log_pri(LOG_NOTICE, "USER %s (Login failed): Not a UserAlias.",
-+ log_pri(LOG_INFO, "USER %s (Login failed): Not a UserAlias.",
- origuser);
- send_response(R_530,"Login incorrect.");
- end_login(0);
-@@ -1189,7 +1189,7 @@
-
- if(c) {
- if(!login_check_limits(c->subset,FALSE,TRUE,&i) || (!aclp && !i) ) {
-- log_auth(LOG_NOTICE, "ANON %s: Limit access denies login.",
-+ log_auth(LOG_INFO, "ANON %s: Limit access denies login.",
- origuser);
- send_response(R_530,"Login incorrect.");
- end_login(0);
-@@ -1197,7 +1197,7 @@
- }
-
- if(!c && !aclp) {
-- log_auth(LOG_NOTICE, "USER %s: Limit access denies login.", origuser);
-+ log_auth(LOG_INFO, "USER %s: Limit access denies login.", origuser);
- send_response(R_530,"Login incorrect.");
+@@ -1267,7 +1267,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_auth(LOG_NOTICE, "Connection refused (max clients for class %s).",
++ log_auth(LOG_INFO, "Connection refused (max clients for class %s).",
+ session.class->name);
+
end_login(0);
- }
-@@ -1269,7 +1269,7 @@
- "Too many users in your class, "
- "please try again later.");
-
-- log_auth(LOG_NOTICE, "Connection refused (max clients for class %s).",
-+ log_auth(LOG_INFO, "Connection refused (max clients for class %s).",
- session.class->name);
-
- end_login(0);
-@@ -1304,7 +1304,7 @@
- send_response(R_530,"%s",
- sreplace(cmd->tmp_pool,maxstr,"%m",maxn,NULL));
+@@ -1299,7 +1299,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
- log_auth(LOG_NOTICE, "Connection refused (max clients per host %d).",
+ log_auth(LOG_INFO, "Connection refused (max clients per host %d).",
max);
end_login(0);
-@@ -1330,7 +1330,7 @@
- if(cur >= max) {
+@@ -1324,7 +1324,7 @@
send_response(R_530, "%s",
sreplace(cmd->tmp_pool, maxstr, "%m", maxn, NULL));
+
- log_auth(LOG_NOTICE, "Connection refused (max clients %d).", max);
+ log_auth(LOG_INFO, "Connection refused (max clients %d).", max);
+
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+@@ -1354,7 +1354,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_auth(LOG_NOTICE, "Connection refused (max clients per host %d).",
++ log_auth(LOG_INFO, "Connection refused (max clients per host %d).",
+ max);
+
end_login(0);
- }
+@@ -1401,7 +1401,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_pri(LOG_NOTICE, "USER %s (Login failed): Not a UserAlias.",
++ log_pri(LOG_INFO, "USER %s (Login failed): Not a UserAlias.",
+ origuser);
+ send_response(R_530,"Login incorrect.");
-@@ -1411,7 +1411,7 @@
+@@ -1422,7 +1422,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_auth(LOG_NOTICE, "ANON %s: Limit access denies login.",
++ log_auth(LOG_INFO, "ANON %s: Limit access denies login.",
+ origuser);
+ send_response(R_530, "Login incorrect.");
+
+@@ -1434,7 +1434,7 @@
+ remove_config(cmd->server->conf, C_USER, FALSE);
+ remove_config(cmd->server->conf, C_PASS, FALSE);
+
+- log_auth(LOG_NOTICE, "USER %s: Limit access denies login.", origuser);
++ log_auth(LOG_INFO, "USER %s: Limit access denies login.", origuser);
+ send_response(R_530, "Login incorrect.");
+
+ end_login(0);
+@@ -1528,7 +1528,7 @@
if(++auth_tries >= max) {
send_response(R_530,"Login incorrect");
diff --git a/ftp/proftpd/files/patch-ak b/ftp/proftpd/files/patch-ak
deleted file mode 100644
index 0d8bb08fee5b..000000000000
--- a/ftp/proftpd/files/patch-ak
+++ /dev/null
@@ -1,12 +0,0 @@
---- modules/mod_xfer.c.orig Mon Jul 31 16:49:19 2000
-+++ modules/mod_xfer.c Mon Jul 31 16:50:02 2000
-@@ -268,7 +268,8 @@
-
- /* No PORT command has been issued.
- */
-- if(session.d != NULL || !(session.flags & SF_PORT)) {
-+ if(!(session.flags & SF_PASSIVE) &&
-+ (session.d != NULL || !(session.flags & SF_PORT))) {
- add_response_err(R_503, "No PORT command issued first.");
- return ERROR(cmd);
- }
diff --git a/ftp/proftpd/files/patch-ba b/ftp/proftpd/files/patch-ba
new file mode 100644
index 000000000000..b1d054b52edc
--- /dev/null
+++ b/ftp/proftpd/files/patch-ba
@@ -0,0 +1,253 @@
+--- contrib/mod_mysql.c.old Sun Feb 11 19:47:54 2001
++++ contrib/mod_mysql.c Sun Feb 11 19:45:37 2001
+@@ -0,0 +1,250 @@
++/*
++ * ProFTPD: mod_mysql -- Support for connecting to MySQL databases.
++ * Time-stamp: <1999-10-04 03:21:21 root>
++ * Copyright (c) 1998-1999 Johnie Ingram.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#define MOD_MYSQL_VERSION "mod_mysql/2.0"
++
++/* -- DO NOT MODIFY THE LINE BELOW UNLESS YOU FEEL LIKE IT --
++ * $Libraries: -lm -lmysqlclient $
++ */
++
++/* This is mod_mysql, contrib software for proftpd 1.2.0pre3 and above.
++ For more information contact Johnie Ingram <johnie@netgod.net>.
++
++ History Log:
++
++ * 1999-09-19: v2.0: Most directives split into mod_sql; invented API.
++
++*/
++
++#include "conf.h"
++#include <mysql.h>
++
++/* *INDENT-OFF* */
++
++static MYSQL mod_mysql_server;
++static MYSQL *mysqldb = 0;
++
++/* Maximum username field to expect, etc. */
++#define ARBITRARY_MAX 128
++
++static struct
++{
++ char *sql_host; /* Data for connecting, set by MySQLInfo. */
++ char *sql_user;
++ char *sql_pass;
++ char *sql_dbname;
++
++ int ok;
++ int opens;
++} g;
++
++/* *INDENT-ON* */
++
++/* **************************************************************** */
++
++MODRET
++sql_cmd_close (cmd_rec * cmd)
++{
++ log_debug (DEBUG5, "mysql: close [%i] for %s", g.opens, cmd->argv[0]);
++
++ if (!g.ok || g.opens--)
++ return DECLINED (cmd);
++
++ if (mysqldb)
++ {
++ log_debug (DEBUG4, "mysql: disconnecting: %s", mysql_stat (mysqldb));
++ mysql_close (mysqldb);
++ }
++ mysqldb = NULL;
++ return DECLINED (cmd);
++}
++
++MODRET
++sql_cmd_open (cmd_rec * cmd)
++{
++ if (!g.ok)
++ return DECLINED (cmd);
++
++ g.opens++;
++ log_debug (DEBUG5, "mysql: open [%i] for %s", g.opens, cmd->argv[0]);
++ if (g.opens > 1)
++ return HANDLED (cmd);
++
++ if (!(mysqldb = mysql_connect (&mod_mysql_server, g.sql_host,
++ g.sql_user, g.sql_pass)))
++ {
++ log_pri (LOG_ERR, "mysql: client %s connect FAILED to %s@%s",
++ mysql_get_client_info (), g.sql_user, g.sql_host);
++ g.ok = FALSE;
++ g.opens = 0;
++ return DECLINED (cmd);
++ }
++ log_debug (DEBUG5, "mysql: connect OK %s -> %s (%s@%s)",
++ mysql_get_client_info (), mysql_get_server_info (mysqldb),
++ g.sql_user, g.sql_host);
++
++ return HANDLED (cmd);
++}
++
++MODRET
++_do_query (cmd_rec * cmd, const char *query, int update)
++{
++ int error = 1;
++
++ if (!g.ok)
++ return DECLINED (cmd);
++
++ block_signals ();
++
++ /* This forces a quick ping of the remote server, so we know if its there. */
++ if (mysqldb)
++ mysql_select_db (mysqldb, g.sql_dbname);
++
++ if (!mysqldb
++ || ((error = mysql_query (mysqldb, query))
++ && !strcasecmp (mysql_error (mysqldb), "mysql server has gone")))
++ {
++ /* We need to restart the server link. */
++ if (mysqldb)
++ log_pri (LOG_ERR, "mysql: server has wandered off (%s/%s)",
++ g.sql_host, g.sql_dbname);
++ sql_cmd_open (cmd);
++ if (!mysqldb)
++ return DECLINED (cmd);
++ error = mysql_select_db (mysqldb, g.sql_dbname)
++ || mysql_query (mysqldb, query);
++ }
++
++ unblock_signals ();
++
++ if (error)
++ {
++ log_debug (DEBUG4, "mysql: %s failed: \"%s\": %s",
++ (update) ? "update" : "select",
++ query, mysql_error (mysqldb));
++ return DECLINED (cmd);
++ }
++
++ log_debug (DEBUG5, "mysql: %s OK: [%s] \"%s\"",
++ (update) ? "update" : "select", g.sql_dbname, query);
++ return HANDLED (cmd);
++}
++
++MODRET
++sql_cmd_update (cmd_rec * cmd)
++{
++ return _do_query (cmd, cmd->argv[1], TRUE);
++}
++
++MODRET
++sql_cmd_select (cmd_rec * cmd)
++{
++ MODRET mr;
++ MYSQL_RES *result;
++ MYSQL_ROW sql_row;
++ int i, j;
++
++ mr = _do_query (cmd, cmd->argv[1], FALSE);
++ if (!MODRET_ISHANDLED (mr))
++ return DECLINED (mr);
++
++ if ((result = mysql_store_result (mysqldb)))
++ {
++ int rcount = mysql_num_rows (result);
++ int fcount = mysql_num_fields (result);
++ int count = rcount * fcount;
++
++ char **data = pcalloc (cmd->tmp_pool, sizeof (char *) * (count + 1));
++ for (i = 0, count = 0; i < rcount; i++)
++ {
++ sql_row = mysql_fetch_row (result);
++ for (j = 0; j < fcount; j++)
++ data[count++] = pstrdup (cmd->tmp_pool, sql_row[j]);
++ }
++ mysql_free_result (result);
++ data[count] = NULL;
++ mr->data = data;
++ }
++ return mr;
++}
++
++static authtable mysql_authtab[] = {
++ {0, "dbd_open", sql_cmd_open},
++ {0, "dbd_close", sql_cmd_close},
++ {0, "dbd_update", sql_cmd_update},
++ {0, "dbd_select", sql_cmd_select},
++
++ {0, NULL, NULL}
++};
++
++/* **************************************************************** */
++
++MODRET set_sqlinfo (cmd_rec * cmd)
++{
++ CHECK_ARGS (cmd, 4);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL | CONF_VIRTUAL);
++ add_config_param_str ("MySQLInfo", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], (void *) cmd->argv[4]);
++ return HANDLED (cmd);
++}
++
++static conftable mysql_conftab[] = {
++/* *INDENT-OFF* */
++
++ { "MySQLInfo", set_sqlinfo, NULL },
++
++ { 0, NULL }
++
++/* *INDENT-ON* */
++};
++
++/* **************************************************************** */
++
++static int
++mysql_modinit ()
++{
++ config_rec *c;
++
++ memset (&g, 0, sizeof (g));
++ if (!(c = find_config (CURRENT_CONF, CONF_PARAM, "MySQLInfo", FALSE)))
++ return 0;
++
++ g.sql_host = pstrdup (session.pool, c->argv[0]);
++ g.sql_user = pstrdup (session.pool, c->argv[1]);
++ g.sql_pass = pstrdup (session.pool, c->argv[2]);
++ g.sql_dbname = pstrdup (session.pool, c->argv[3]);
++ g.ok = TRUE;
++
++ log_debug (DEBUG5, "%s: configured: db %s at %s@%s",
++ MOD_MYSQL_VERSION, g.sql_dbname, g.sql_user, g.sql_host);
++ return 0;
++}
++
++module mysql_module = {
++ NULL, NULL, /* Always NULL */
++ 0x20, /* API Version 2.0 */
++ "mysql",
++ mysql_conftab, /* SQL configuration handler table */
++ NULL, /* SQL command handler table */
++ mysql_authtab, /* SQL authentication handler table */
++ mysql_modinit, /* Pre-fork "parent-mode" init */
++ mysql_modinit /* Post-fork "child mode" init */
++};
diff --git a/ftp/proftpd/files/patch-bb b/ftp/proftpd/files/patch-bb
new file mode 100644
index 000000000000..7d17d667f7dc
--- /dev/null
+++ b/ftp/proftpd/files/patch-bb
@@ -0,0 +1,1023 @@
+--- contrib/mod_sqlpw.c.old Sun Feb 11 19:47:34 2001
++++ contrib/mod_sqlpw.c Sun Feb 11 19:45:37 2001
+@@ -0,0 +1,1020 @@
++/*
++ * ProFTPD: mod_sql -- SQL frontend and user interface.
++ * Time-stamp: <1999-10-04 03:58:01 root>
++ * Copyright (c) 1998-1999 Johnie Ingram.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#define MOD_SQL_VERSION "mod_sqlpw/2.0"
++
++/* This is mod_sqlpw, contrib software for proftpd 1.2.0pre3 and above.
++ For more information contact Johnie Ingram <johnie@netgod.net>.
++
++ History Log:
++
++ * 2000-03-16: Added SQLKeyField and SQLKey directive, which
++ should allow authentication of different logins for different
++ VirtualHosts to be in the same database table.
++ Jake Buchholz <jake@execpc.com>
++
++ * 1999-09-19: v2.0: Backend directives split into mod_mysql.
++ Runtime API added. Username/passwords now escaped for SQL.
++
++ * 1999-09-16: v1.0: Added documentation, made directives more
++ generic ("MySQL" -> "SQL" except for MySQLInfo). Dir part of
++ SQLLogDirs finally made optional.
++
++ * 1999-06-01: v0.3: fixed segfault, changed 'strncmp' typo which
++ should have been copy. Made uid/gid support optional.
++
++ * 1999-05-03: v0.2: Removed dead code, fix bug with interaction
++ with anon-user support. Added MySQLLogHits and MySQLHomedir
++ directives. Fixed atoi() invocation that could segfault.
++ Copious debugging code added.
++
++ * 1999-04-09: v0.1: Initial attempt (mod_mysql).
++
++*/
++
++#include "conf.h"
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#include <mysql.h>
++
++/* *INDENT-OFF* */
++
++/* The Debian defintion of nobody/nogroup, minus 1 (used in getgwnam). */
++#define MOD_SQL_MAGIC_USER 65533
++#define MOD_SQL_MAGIC_GROUP 65533
++
++/* A uid or gid less than this is mapped to the magic numbers above
++ instead of simply rejected (which is arguably better, hmm.) */
++#define MOD_SQL_MIN_ID 999
++
++#define MOD_SQL_MAGIC_SHELL "/bin/sh"
++
++/* Maximum username field to expect, etc. */
++#define ARBITRARY_MAX 128
++
++static struct {
++ char user [ARBITRARY_MAX];
++ struct passwd pw; /* Scratch space for the getpwnam call. */
++ char *pass; /* The password from db. */
++
++ char *homedir; /* Set when getpwnam is called. */
++ int pwnamed; /* Set when getpwnam is called. */
++ int sqled; /* Set if sql auth was used. */
++
++ char *sql_usertable; /* CREATE TABLE users ( */
++ char *sql_userid; /* userid varchar(50) NOT NULL, */
++ char *sql_passwd; /* passwd varchar(15), */
++
++ char *sql_uid; /* uid int(5), */
++ char *sql_gid; /* gid int(5), */
++
++ char *sql_fstor; /* fstor int(11) NOT NULL DEFAULT '0', */
++ char *sql_fretr; /* fretr int(11) NOT NULL DEFAULT '0', */
++ char *sql_bstor; /* bstor int(11) NOT NULL DEFAULT '0', */
++ char *sql_bretr; /* bretr int(11) NOT NULL DEFAULT '0', */
++
++ char *sql_fhdir; /* fhdir varchar(255), */
++
++ char *sql_fhost; /* fhost varchar(50), */
++ char *sql_faddr; /* faddr char(15), */
++ char *sql_ftime; /* ftime timestamp, */
++
++ char *sql_fcdir; /* fcdir varchar(255), */
++
++ char *sql_frate; /* frate int(11) NOT NULL DEFAULT '5', */
++ char *sql_fcred; /* fcred int(2) NOT NULL DEFAULT '15', */
++ char *sql_brate; /* brate int(11) NOT NULL DEFAULT '5', */
++ char *sql_bcred; /* bcred int(2) NOT NULL DEFAULT '150000', */
++
++ char *sql_flogs; /* flogs int(11) NOT NULL DEFAULT '0', */
++
++ char *sql_hittable;
++ char *sql_dir;
++ char *sql_filename;
++ char *sql_hits;
++
++ char *sql_keyfld; /* table column name that stores the key */
++ char *sql_key; /* the key to match db table entries against */
++
++ char *loginmsg;
++ int ok;
++
++} g;
++
++/* *INDENT-ON* */
++
++/* **************************************************************** */
++
++/* Functions make_cmd and dispatch liberally stolen from auth.c. */
++
++static cmd_rec *
++_make_cmd (pool * cp, int argc, ...)
++{
++ va_list args;
++ cmd_rec *c;
++ int i;
++
++ c = pcalloc (cp, sizeof (cmd_rec));
++ c->argc = argc;
++ c->symtable_index = -1;
++
++ c->argv = pcalloc (cp, sizeof (void *) * (argc + 1));
++ c->argv[0] = MOD_SQL_VERSION;
++ va_start (args, argc);
++ for (i = 0; i < argc; i++)
++ c->argv[i + 1] = (void *) va_arg (args, char *);
++ va_end (args);
++
++ return c;
++}
++
++static modret_t *
++_dispatch_sql (cmd_rec * cmd, char *match)
++{
++ authtable *m;
++ modret_t *mr = NULL;
++
++ m = mod_find_auth_symbol (match, &cmd->symtable_index, NULL);
++ while (m)
++ {
++ mr = call_module_auth (m->m, m->handler, cmd);
++ if (MODRET_ISHANDLED (mr) || MODRET_ISERROR (mr))
++ break;
++ m = mod_find_auth_symbol (match, &cmd->symtable_index, m);
++ }
++ if (MODRET_ISERROR (mr))
++ log_debug (DEBUG0, "Aiee! sql internal! %s", MODRET_ERRMSG (mr));
++ return mr;
++}
++
++MODRET
++modsql_open (cmd_rec * cmd)
++{
++ cmd_rec *c;
++ modret_t *mr;
++
++ c = _make_cmd (cmd ? cmd->tmp_pool : permanent_pool, 0);
++ mr = _dispatch_sql (c, "dbd_open");
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ return mr;
++}
++
++MODRET
++modsql_close (cmd_rec * cmd)
++{
++ cmd_rec *c;
++ modret_t *mr;
++
++ c = _make_cmd (cmd ? cmd->tmp_pool : permanent_pool, 0);
++ mr = _dispatch_sql (c, "dbd_close");
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ return mr;
++}
++
++MODRET
++modsql_update (cmd_rec * cmd, const char *query)
++{
++ cmd_rec *c;
++ modret_t *mr;
++
++ c = _make_cmd (cmd->tmp_pool, 1, query);
++ mr = _dispatch_sql (c, "dbd_update");
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ return mr;
++}
++
++MODRET
++modsql_select (cmd_rec * cmd, const char *query)
++{
++ cmd_rec *c;
++ modret_t *mr;
++
++ c = _make_cmd (cmd->tmp_pool, 1, query);
++ mr = _dispatch_sql (c, "dbd_select");
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ return mr;
++}
++
++MODRET
++modsql_queryuser (cmd_rec * cmd, const char *user, const char *query,
++ int update)
++{
++ char *realquery;
++
++ if ((update) && (!g.sql_keyfld))
++ realquery = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
++ " set ", query, " where ", g.sql_userid, " = '",
++ user, "'", NULL);
++ else if ((update) && (g.sql_keyfld))
++ realquery = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
++ " set ", query, " where ", g.sql_userid, " = '",
++ user, "' and ", g.sql_keyfld, " = '",
++ g.sql_key, "'", NULL);
++ else if ((!update) && (!g.sql_keyfld))
++ realquery = pstrcat (cmd->tmp_pool, "select ", query, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "'", NULL);
++ else
++ realquery = pstrcat (cmd->tmp_pool, "select ", query, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "' and ", g.sql_keyfld, " = '",
++ g.sql_key, "'", NULL);
++ return (update) ? modsql_update (cmd, realquery)
++ : modsql_select (cmd, realquery);
++}
++
++/* **************************************************************** */
++
++static char *
++_uservar (cmd_rec * cmd, const char *user, const char *var)
++{
++ cmd_rec *c;
++ modret_t *mr;
++ char *query;
++ char **data;
++
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "select ", var, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "'", NULL);
++ else
++ query = pstrcat (cmd->tmp_pool, "select ", var, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "' and ", g.sql_keyfld, " = '",
++ g.sql_key, "'", NULL);
++ c = _make_cmd (cmd->tmp_pool, 1, query);
++
++ mr = _dispatch_sql (c, "dbd_select");
++
++ if (c->tmp_pool)
++ destroy_pool (c->tmp_pool);
++ if (MODRET_ISHANDLED (mr))
++ {
++ data = mr->data;
++ return (data) ? data[0] : NULL;
++ }
++
++ return NULL;
++}
++
++/* Note: This function is called once by the master proftpd process
++ before it forks, so thankfully the homedir field is only set in the
++ child_init(). */
++
++MODRET
++auth_cmd_getpwnam (cmd_rec * cmd)
++{
++ const char *homedir;
++
++ if (!g.sql_fhdir && !g.homedir)
++ return DECLINED (cmd);
++
++ /* Only try this for the USER command (is also called twice after
++ PASS for some reason. [update: 1 is dir_realpath/defroot related.] */
++ if (!g.homedir && !g.pwnamed++)
++ {
++ if ((homedir = _uservar (cmd, cmd->argv[0], g.sql_fhdir)))
++ g.homedir = pstrdup (session.pool, homedir);
++ }
++ if (!g.homedir)
++ return DECLINED (cmd);
++
++ if (!g.pw.pw_name)
++ {
++ g.pw.pw_name = pstrdup(session.pool, cmd->argv[0]);
++ if (g.sql_uid)
++ g.pw.pw_uid = atoi (_uservar (cmd, cmd->argv[0], g.sql_uid) ? : "0");
++ if (g.pw.pw_uid < MOD_SQL_MIN_ID)
++ g.pw.pw_uid = MOD_SQL_MAGIC_USER;
++ if (g.sql_gid)
++ g.pw.pw_gid = atoi (_uservar (cmd, cmd->argv[0], g.sql_gid) ? : "0");
++ if (g.pw.pw_gid < MOD_SQL_MIN_ID)
++ g.pw.pw_gid = MOD_SQL_MAGIC_GROUP;
++ g.pw.pw_shell = MOD_SQL_MAGIC_SHELL;
++ g.pw.pw_dir = (char *) g.homedir;
++ log_debug (DEBUG3, "sqlpw: user \"%s\" (%i/%i) for %s",
++ cmd->argv[0], g.pw.pw_uid, g.pw.pw_gid, g.pw.pw_dir);
++
++ /* Copy username so proftpd anon handling won't confuse the issue. */
++
++ /* FIXME: unnecessary mysqlism */
++ mysql_escape_string (g.user, g.pw.pw_name, strlen (g.pw.pw_name));
++ g.user[ARBITRARY_MAX - 1] = 0;
++ }
++
++ return mod_create_data (cmd, &g.pw);
++}
++
++/* Returns 1 on successful match, 0 unsuccessful match, -1 on error. */
++
++static int
++_checkpass (cmd_rec * cmd, const char *user, const char *pass)
++{
++ int success = 0;
++ char *query;
++ int emptyok, cplain, ccrypt;
++ char **row;
++ MODRET mr;
++
++ emptyok = get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE);
++ cplain = get_param_int (CURRENT_CONF, "SQLPlaintextPasswords", FALSE);
++ ccrypt = get_param_int (CURRENT_CONF, "SQLEncryptedPasswords", FALSE);
++
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_passwd, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "'", " limit 2", NULL);
++ else
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_passwd, " from ",
++ g.sql_usertable, " where ", g.sql_userid,
++ " = '", user, "' and ", g.sql_keyfld, " = '",
++ g.sql_key, "' limit 2", NULL);
++ mr = modsql_select (cmd, query);
++ if (!(MODRET_HASDATA (mr)))
++ return -1;
++
++ row = mr->data;
++ if (row[0] == 0)
++ return 0;
++
++ if (!row[0])
++ {
++ log_debug (DEBUG4, "sqlpw: %s auth declined for NULL pass", user);
++ return 0;
++ }
++
++ if (row[1])
++ {
++ log_debug (DEBUG3, "sqlpw: %s pass result was not unique", user);
++ return -1;
++ }
++
++ if (emptyok == TRUE && !strlen (row[0]))
++ {
++ log_debug (DEBUG4, "sqlpw: warning: %s has empty password", user);
++ success = 1;
++ }
++
++ if (!success && cplain == TRUE && !strncasecmp (row[0], pass, 10))
++ {
++ success = 1;
++ }
++
++ /* Deliberate: ccrypt same if TRUE or -1 (unspecified). */
++ if (!success && ccrypt)
++ {
++ if (!strcmp (query = (char *) crypt (pass, row[0]), row[0]))
++ success = 1;
++ }
++
++ if (!success)
++ log_debug (DEBUG5, "sqlpw: %s auth failed: '%s' != '%s'",
++ user, pass, row ? row[0] : "");
++ return success;
++}
++
++MODRET
++auth_cmd_auth (cmd_rec * cmd)
++{
++ char *user, *pass;
++ int return_type;
++ int retval = AUTH_NOPWD;
++
++ if (!g.sql_passwd || !g.homedir)
++ return DECLINED (cmd);
++
++ /* Figure out our default return style: Whether or not SQL should
++ * allow other auth modules a shot at this user or not is controlled
++ * by the parameter "SQLAuthoritative". Like mod_pam this
++ * defaults to no. */
++ if((return_type = get_param_int (CURRENT_CONF,
++ "SQLAuthoritative", FALSE)) == -1) {
++ return_type = 0;
++ }
++
++ /* Just in case... */
++ if (cmd->argc != 2)
++ return return_type ? ERROR (cmd) : DECLINED (cmd);
++ if ((user = cmd->argv[0]) == NULL)
++ return return_type ? ERROR (cmd) : DECLINED (cmd);
++ if ((pass = cmd->argv[1]) == NULL)
++ return return_type ? ERROR (cmd) : DECLINED (cmd);
++
++ if (!g.pass
++ && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
++ {
++ char *query;
++ char passbuf[ARBITRARY_MAX] = {'\0'};
++
++ /* FIXME: unnecessary mysqlism */
++ mysql_escape_string (passbuf, cmd->argv[1], strlen (cmd->argv[1]));
++ g.user[ARBITRARY_MAX - 1] = 0;
++
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
++ " set ", g.sql_passwd, " = '", passbuf,
++ "' where ", g.sql_userid, " = '", g.user, "'", 0);
++ else
++ query = pstrcat (cmd->tmp_pool, "update ", g.sql_usertable,
++ " set ", g.sql_passwd, " = '", passbuf,
++ "' where ", g.sql_userid, " = '", g.user, "' and ",
++ g.sql_keyfld, " = '", g.sql_key, "'", 0);
++ log_debug (DEBUG3, "sqlpw: %s NULL pass set to '%s'", user,
++ cmd->argv[1], query);
++ modsql_update (cmd, query);
++ }
++
++ if (_checkpass (cmd, g.user, pass) != 1)
++ return return_type ? ERROR_INT (cmd, retval) : DECLINED (cmd);
++
++ g.sqled++;
++ return HANDLED (cmd);
++}
++
++MODRET
++auth_cmd_getstats (cmd_rec * cmd)
++{
++ MODRET mr;
++ char *query;
++
++ if (g.sql_fstor)
++ {
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_fstor, ", ",
++ g.sql_fretr, ", ", g.sql_bstor, ", ",
++ g.sql_bretr, " from ", g.sql_usertable, " where ",
++ g.sql_userid, " = '", g.user, "'", NULL);
++ else
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_fstor, ", ",
++ g.sql_fretr, ", ", g.sql_bstor, ", ",
++ g.sql_bretr, " from ", g.sql_usertable, " where ",
++ g.sql_userid, " = '", g.user, "' and ", g.sql_keyfld,
++ " = '", g.sql_key, "'", NULL);
++ mr = modsql_select (cmd, query);
++ if (MODRET_HASDATA (mr))
++ return mr;
++ }
++ return DECLINED (cmd);
++}
++
++MODRET
++auth_cmd_getratio (cmd_rec * cmd)
++{
++ MODRET mr;
++ char *query;
++
++ if (g.sql_frate)
++ {
++ if (!g.sql_keyfld)
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_frate, ", ",
++ g.sql_fcred, ", ", g.sql_brate, ", ",
++ g.sql_bcred, " from ", g.sql_usertable, " where ",
++ g.sql_userid, " = '", g.user, "'", NULL);
++ else
++ query = pstrcat (cmd->tmp_pool, "select ", g.sql_frate, ", ",
++ g.sql_fcred, ", ", g.sql_brate, ", ",
++ g.sql_bcred, " from ", g.sql_usertable, " where ",
++ g.sql_userid, " = '", g.user, "' and ", g.sql_keyfld,
++ " = '", g.sql_key, "'", NULL);
++ mr = modsql_select (cmd, query);
++ if (MODRET_HASDATA (mr))
++ return mr;
++ }
++ return DECLINED (cmd);
++}
++
++static authtable sqlpw_authtab[] = {
++
++ {0, "auth", auth_cmd_auth},
++ {0, "getpwnam", auth_cmd_getpwnam},
++ {0, "getstats", auth_cmd_getstats},
++ {0, "getratio", auth_cmd_getratio},
++ {0, NULL, NULL}
++};
++
++/* **************************************************************** */
++
++static void
++_setstats (cmd_rec * cmd, int fstor, int fretr, int bstor, int bretr)
++{
++ char query[ARBITRARY_MAX] = {'\0'};
++ snprintf (query, sizeof (query),
++ "%s = %s + %i, %s = %s + %i, %s = %s + %i, %s = %s + %i",
++ g.sql_fstor, g.sql_fstor, fstor,
++ g.sql_fretr, g.sql_fretr, fretr,
++ g.sql_bstor, g.sql_bstor, bstor, g.sql_bretr, g.sql_bretr, bretr);
++ modsql_queryuser (cmd, g.user, query, TRUE);
++}
++
++MODRET
++pre_cmd_quit (cmd_rec * cmd)
++{
++ if (g.ok)
++ modsql_close (cmd);
++ return DECLINED (cmd);
++}
++
++MODRET
++cmd_user (cmd_rec * cmd)
++{
++ if (!g.user[0])
++ sstrncpy (g.user, cmd->argv[1], ARBITRARY_MAX);
++
++ if (g.sql_passwd)
++ {
++ g.pass = (char *) _uservar (cmd, cmd->argv[1], g.sql_passwd);
++ if (!g.pass
++ && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
++ if (_uservar (cmd, cmd->argv[1], "1"))
++ add_response (R_331, "Changing password for %s -- "
++ "this password will be saved.", cmd->argv[1]);
++ }
++ return DECLINED (cmd);
++}
++
++MODRET
++cmd_pass (cmd_rec * cmd)
++{
++ if (g.sql_passwd && !g.pass
++ && get_param_int (CURRENT_CONF, "SQLEmptyPasswords", FALSE) == 2)
++ add_response (R_230, "\"%s\" is the new user \"%s\" password.",
++ cmd->argv[1], g.user);
++
++ /* This is called before the disconnect() in log_cmd_pass. */
++ if (g.sql_fcdir)
++ {
++ const char *d = _uservar (cmd, g.user, g.sql_fcdir);
++ if (d)
++ add_response (R_230, "\"%s\" was last directory.", d);
++ }
++
++ return DECLINED (cmd);
++}
++
++MODRET
++post_cmd_pass (cmd_rec * cmd)
++{
++ if (g.sql_passwd && g.sqled)
++ session.anon_user = session.user = (char *) g.user;
++ return DECLINED (cmd);
++}
++
++MODRET
++post_cmd_stor (cmd_rec * cmd)
++{
++ if (g.sql_fstor)
++ _setstats (cmd, 1, 0, session.xfer.total_bytes, 0);
++ return DECLINED (cmd);
++}
++
++MODRET
++cmd_retr (cmd_rec * cmd)
++{
++ int i;
++ char *path, *filename, *query;
++ if (g.sql_hittable)
++ {
++ path = dir_realpath (cmd->tmp_pool, cmd->argv[1]);
++ if (g.sql_dir && g.sql_dir[0])
++ {
++ for (i = strlen (path), filename = path + i;
++ *filename != '/' && i > 1; i--)
++ filename--;
++ *filename++ = 0;
++ query = pstrcat (cmd->tmp_pool, "update ", g.sql_hittable,
++ " set ", g.sql_hits, " = ", g.sql_hits,
++ " + 1 where ", g.sql_dir, " = '", ++path,
++ "' and ", g.sql_filename, " = '", filename,
++ "'", 0);
++ }
++ else
++ query = pstrcat (cmd->tmp_pool, "update ", g.sql_hittable,
++ " set ", g.sql_hits, " = ", g.sql_hits,
++ " + 1 where ", g.sql_filename, " = '", path, "'", 0);
++ modsql_update (cmd, query);
++ }
++ return DECLINED (cmd);
++}
++
++MODRET
++post_cmd_retr (cmd_rec * cmd)
++{
++ if (g.sql_fretr)
++ _setstats (cmd, 0, 1, 0, session.xfer.total_bytes);
++ return DECLINED (cmd);
++}
++
++MODRET
++log_cmd_pass (cmd_rec * cmd)
++{
++ char *query;
++
++ if (g.sql_fhost)
++ {
++ query = pstrcat (cmd->tmp_pool, g.sql_fhost, " = '",
++ session.c->remote_name, "', ", g.sql_faddr,
++ " = '", inet_ntoa (*session.c->remote_ipaddr),
++ "', ", g.sql_ftime, " = now()", NULL);
++ modsql_queryuser (cmd, g.user, query, TRUE);
++ }
++ if (g.sql_flogs)
++ {
++ query = pstrcat (cmd->tmp_pool, g.sql_flogs, " = ", g.sql_flogs,
++ " + 1", NULL);
++ modsql_queryuser (cmd, g.user, query, TRUE);
++ }
++
++ /* Autononpersistence: disconnect now if no other feature is being used. */
++ if (!g.sql_fstor && !g.sql_fcdir && !g.sql_hittable)
++ modsql_close (cmd);
++ return DECLINED (cmd);
++}
++
++MODRET
++log_cmd_cwd (cmd_rec * cmd)
++{
++ if (g.sql_fcdir)
++ {
++ char *query = pstrcat (cmd->tmp_pool, g.sql_fcdir, " = '",
++ session.cwd, "'", NULL);
++ modsql_queryuser (cmd, g.user, query, TRUE);
++ }
++ return DECLINED (cmd);
++}
++
++static cmdtable sqlpw_cmdtab[] = {
++/* *INDENT-OFF* */
++
++ { PRE_CMD, C_QUIT, G_NONE, pre_cmd_quit, FALSE, FALSE },
++ { CMD, C_USER, G_NONE, cmd_user, FALSE, FALSE },
++ { CMD, C_PASS, G_NONE, cmd_pass, FALSE, FALSE },
++ { POST_CMD, C_PASS, G_NONE, post_cmd_pass, FALSE, FALSE },
++ { POST_CMD, C_STOR, G_NONE, post_cmd_stor, FALSE, FALSE },
++ { POST_CMD, C_RETR, G_NONE, post_cmd_retr, FALSE, FALSE },
++ { CMD, C_RETR, G_NONE, cmd_retr, FALSE, FALSE },
++ { LOG_CMD, C_PASS, G_NONE, log_cmd_pass, FALSE, FALSE },
++ { LOG_CMD, C_CWD, G_NONE, log_cmd_cwd, FALSE, FALSE },
++ { LOG_CMD, C_CDUP, G_NONE, log_cmd_cwd, FALSE, FALSE },
++
++ { 0, NULL }
++
++/* *INDENT-ON* */
++};
++
++/* **************************************************************** */
++
++MODRET
++set_sqlloghosts (cmd_rec * cmd)
++{
++ int b;
++
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ switch (cmd->argc - 1)
++ {
++ default:
++ CONF_ERROR (cmd, "requires a boolean or 3 field names: "
++ "fhost faddr ftime");
++ case 1:
++ if ((b = get_boolean (cmd, 1)) == -1)
++ CONF_ERROR (cmd, "requires a boolean or 3 field names: "
++ "fhost faddr ftime");
++ if (b)
++ add_config_param_str ("SQLLogHosts", 3, "fhost", "faddr", "ftime");
++ break;
++
++ case 3:
++ add_config_param_str ("SQLLogHosts", 3,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3]);
++ }
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqllogstats (cmd_rec * cmd)
++{
++ int b;
++
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ switch (cmd->argc - 1)
++ {
++ default:
++ CONF_ERROR (cmd, "requires a boolean or 4 field names: "
++ "fstor fretr bstor bretr");
++ case 1:
++ if ((b = get_boolean (cmd, 1)) == -1)
++ CONF_ERROR (cmd, "requires a boolean or 4 field names: "
++ "fstor fretr bstor bretr");
++ if (b)
++ add_config_param_str ("SQLLogStats", 4,
++ "fstor", "fretr", "bstor", "bretr");
++ break;
++
++ case 4:
++ add_config_param_str ("SQLLogStats", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], (void *) cmd->argv[4]);
++ }
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqlloghits (cmd_rec * cmd)
++{
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ switch (cmd->argc - 1)
++ {
++ default:
++ CONF_ERROR (cmd, "requires a table or table plus 3 fields: "
++ "[table] filename count dir");
++ case 1:
++ add_config_param_str ("SQLLogHits", 4, (void *) cmd->argv[1],
++ "filename", "count", "");
++ break;
++ case 3:
++ add_config_param_str ("SQLLogHits", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], "");
++
++ case 4:
++ add_config_param_str ("SQLLogHits", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], (void *) cmd->argv[4]);
++ }
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqllogdirs (cmd_rec * cmd)
++{
++ int b;
++
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ if ((b = get_boolean (cmd, 1)) == -1)
++ add_config_param_str ("SQLLogDirs", 1, (void *) cmd->argv[1]);
++ else if (b)
++ add_config_param_str ("SQLLogDirs", 1, "fcdir");
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqlratios (cmd_rec * cmd)
++{
++ int b;
++
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ switch (cmd->argc - 1)
++ {
++ default:
++ CONF_ERROR (cmd, "requires a boolean or 4 field names: "
++ "frate fcred brate bcred");
++ case 1:
++ if ((b = get_boolean (cmd, 1)) == -1)
++ CONF_ERROR (cmd, "requires a boolean or 4 field names: "
++ "frate fcred brate bcred");
++ if (b)
++ add_config_param_str ("SQLRatios", 4,
++ "frate", "fcred", "brate", "bcred");
++ break;
++
++ case 4:
++ add_config_param_str ("SQLRatios", 4,
++ (void *) cmd->argv[1], (void *) cmd->argv[2],
++ (void *) cmd->argv[3], (void *) cmd->argv[4]);
++ }
++ return HANDLED (cmd);
++}
++
++MODRET
++set_sqlempty (cmd_rec * cmd)
++{
++ int b;
++ config_rec *c;
++
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++
++ if ((b = get_boolean (cmd, 1)) == -1)
++ {
++ if (!strcasecmp (cmd->argv[1], "set"))
++ b = 2;
++ else
++ CONF_ERROR (cmd, "requires 'set' or a boolean value");
++ }
++ c = add_config_param (cmd->argv[0], 1, (void *) b);
++ c->flags |= CF_MERGEDOWN;
++ return HANDLED (cmd);
++}
++
++MODRET
++add_globalstr (cmd_rec * cmd)
++{
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++ add_config_param_str (cmd->argv[0], 1, (void *) cmd->argv[1]);
++ return HANDLED (cmd);
++}
++
++MODRET
++add_virtualstr (cmd_rec * cmd)
++{
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL | CONF_VIRTUAL);
++ add_config_param_str (cmd->argv[0], 1, (void *) cmd->argv[1]);
++ return HANDLED (cmd);
++}
++
++MODRET
++add_globalbool (cmd_rec * cmd)
++{
++ int b;
++ config_rec *c;
++
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL);
++
++ b = get_boolean (cmd, 1);
++ if (b == -1)
++ CONF_ERROR (cmd, "requires a boolean value");
++ c = add_config_param (cmd->argv[0], 1, (void *) b);
++ c->flags |= CF_MERGEDOWN;
++ return HANDLED (cmd);
++}
++
++MODRET
++add_virtualbool (cmd_rec * cmd)
++{
++ int b;
++ config_rec *c;
++
++ CHECK_ARGS (cmd, 1);
++ CHECK_CONF (cmd, CONF_ROOT | CONF_GLOBAL | CONF_VIRTUAL);
++
++ b = get_boolean (cmd, 1);
++ if (b == -1)
++ CONF_ERROR (cmd, "requires a boolean value");
++ c = add_config_param (cmd->argv[0], 1, (void *) b);
++ c->flags |= CF_MERGEDOWN;
++ return HANDLED (cmd);
++}
++
++static conftable sqlpw_conftab[] = {
++/* *INDENT-OFF* */
++
++ { "SQLLogHosts", set_sqlloghosts, NULL },
++ { "SQLLogStats", set_sqllogstats, NULL },
++ { "SQLLogHits", set_sqlloghits, NULL },
++ { "SQLLogDirs", set_sqllogdirs, NULL },
++ { "SQLRatios", set_sqlratios, NULL },
++ { "SQLEmptyPasswords", set_sqlempty, NULL },
++
++ { "SQLUserTable", add_globalstr, NULL },
++ { "SQLUsernameField", add_globalstr, NULL },
++ { "SQLUidField", add_globalstr, NULL },
++ { "SQLGidField", add_globalstr, NULL },
++ { "SQLPasswordField", add_globalstr, NULL },
++ { "SQLHomedirField", add_globalstr, NULL },
++ { "SQLLoginCountField", add_globalstr, NULL },
++ { "SQLHomedir", add_globalstr, NULL },
++
++ { "SQLKeyField", add_globalstr, NULL },
++ { "SQLKey", add_virtualstr, NULL },
++
++ { "SQLAuthoritative", add_virtualbool, NULL },
++ { "SQLEncryptedPasswords", add_globalbool, NULL },
++ { "SQLPlaintextPasswords", add_globalbool, NULL },
++
++ { NULL, NULL, NULL }
++
++/* *INDENT-ON* */
++};
++
++/* **************************************************************** */
++
++static int
++sqlpw_child_init ()
++{
++ config_rec *c;
++ MODRET mr;
++
++ memset (&g, 0, sizeof (g));
++ g.ok = TRUE;
++
++ g.sql_passwd = get_param_ptr (CURRENT_CONF, "SQLPasswordField", FALSE);
++ g.sql_flogs = get_param_ptr (CURRENT_CONF, "SQLLoginCountField", FALSE);
++ g.sql_uid = get_param_ptr (CURRENT_CONF, "SQLUidField", FALSE);
++ g.sql_gid = get_param_ptr (CURRENT_CONF, "SQLGidField", FALSE);
++
++ if (!(g.homedir = get_param_ptr (CURRENT_CONF, "SQLHomedir", FALSE)))
++ {
++ g.sql_fhdir = get_param_ptr (CURRENT_CONF, "SQLHomedirField", FALSE);
++ }
++ if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogHosts", FALSE)))
++ {
++ g.sql_fhost = c->argv[0];
++ g.sql_faddr = c->argv[1];
++ g.sql_ftime = c->argv[2];
++ }
++ if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogStats", FALSE)))
++ {
++ g.sql_fstor = c->argv[0];
++ g.sql_fretr = c->argv[1];
++ g.sql_bstor = c->argv[2];
++ g.sql_bretr = c->argv[3];
++ }
++ if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLRatios", FALSE)))
++ {
++ if (!g.sql_fstor)
++ log_pri (LOG_WARNING, "sqlpw: warning: SQLRatios directive "
++ "ineffective without SQLLogStats on");
++ g.sql_frate = c->argv[0];
++ g.sql_fcred = c->argv[1];
++ g.sql_brate = c->argv[2];
++ g.sql_bcred = c->argv[3];
++ }
++ if ((c = find_config (CURRENT_CONF, CONF_PARAM, "SQLLogHits", FALSE)))
++ {
++ g.sql_hittable = c->argv[0];
++ g.sql_filename = c->argv[1];
++ g.sql_hits = c->argv[2];
++ g.sql_dir = c->argv[3];
++ }
++ g.sql_fcdir = get_param_ptr (CURRENT_CONF, "SQLLogDirs", FALSE);
++
++ g.sql_usertable = get_param_ptr (CURRENT_CONF, "SQLUserTable",
++ FALSE) ? : "users";
++ g.sql_userid = get_param_ptr (CURRENT_CONF, "SQLUsernameField",
++ FALSE) ? : "userid";
++
++ g.sql_keyfld = get_param_ptr (CURRENT_CONF, "SQLKeyField", FALSE);
++ g.sql_key = get_param_ptr (CURRENT_CONF, "SQLKey", FALSE);
++
++ if (!(g.homedir || g.sql_fhdir) && !g.sql_fhost
++ && !g.sql_fstor && !g.sql_fcdir)
++ return 0;
++
++ mr = modsql_open (NULL);
++ if (MODRET_ISHANDLED (mr))
++ {
++ log_debug (DEBUG3, "%s: configured: %s%s%s%s%s%s%s%s",
++ MOD_SQL_VERSION,
++ (g.sql_passwd && (g.homedir || g.sql_fhdir))
++ ? "auth " : "",
++ g.homedir ? "homedir " : "",
++ g.sql_fhdir ? "homedirfield " : "",
++ g.sql_fhost ? "loghosts " : "",
++ g.sql_fstor ? "logstats " : "",
++ g.sql_frate ? "ratios " : "",
++ g.sql_hittable ? "loghits " : "",
++ g.sql_fcdir ? "logdirs " : "");
++ }
++ else
++ {
++ memset (&g, 0, sizeof (g));
++ log_debug (DEBUG3, "%s: unconfigured: no backend could connect",
++ MOD_SQL_VERSION);
++ }
++ return 0;
++}
++
++static int
++sqlpw_parent_init (void)
++{
++ /* FIXME: add db init stuff once parent_init() actually works. */
++ return 0;
++}
++
++module sqlpw_module = {
++ NULL, NULL, /* Always NULL */
++ 0x20, /* API Version 2.0 */
++ "sql",
++ sqlpw_conftab, /* SQL configuration handler table */
++ sqlpw_cmdtab, /* SQL command handler table */
++ sqlpw_authtab, /* SQL authentication handler table */
++ sqlpw_parent_init, /* Pre-fork "parent mode" init */
++ sqlpw_child_init /* Post-fork "child mode" init */
++};
diff --git a/ftp/proftpd/files/patch-bc b/ftp/proftpd/files/patch-bc
new file mode 100644
index 000000000000..bd908a4fa9fa
--- /dev/null
+++ b/ftp/proftpd/files/patch-bc
@@ -0,0 +1,19 @@
+--- contrib/mod_sqlpw.c.old Sun Feb 11 19:52:06 2001
++++ contrib/mod_sqlpw.c Sun Feb 11 19:53:42 2001
+@@ -302,8 +302,6 @@
+ if (!g.homedir)
+ return DECLINED (cmd);
+
+- if (!g.pw.pw_name)
+- {
+ g.pw.pw_name = pstrdup(session.pool, cmd->argv[0]);
+ if (g.sql_uid)
+ g.pw.pw_uid = atoi (_uservar (cmd, cmd->argv[0], g.sql_uid) ? : "0");
+@@ -323,7 +321,6 @@
+ /* FIXME: unnecessary mysqlism */
+ mysql_escape_string (g.user, g.pw.pw_name, strlen (g.pw.pw_name));
+ g.user[ARBITRARY_MAX - 1] = 0;
+- }
+
+ return mod_create_data (cmd, &g.pw);
+ }
diff --git a/ftp/proftpd/files/patch-bd b/ftp/proftpd/files/patch-bd
new file mode 100644
index 000000000000..7eb41bc857e8
--- /dev/null
+++ b/ftp/proftpd/files/patch-bd
@@ -0,0 +1,21 @@
+--- contrib/mod_sqlpw.c.old Sun Feb 11 20:14:40 2001
++++ contrib/mod_sqlpw.c Sun Feb 11 20:16:00 2001
+@@ -400,9 +400,6 @@
+ int return_type;
+ int retval = AUTH_NOPWD;
+
+- if (!g.sql_passwd || !g.homedir)
+- return DECLINED (cmd);
+-
+ /* Figure out our default return style: Whether or not SQL should
+ * allow other auth modules a shot at this user or not is controlled
+ * by the parameter "SQLAuthoritative". Like mod_pam this
+@@ -418,6 +415,8 @@
+ if ((user = cmd->argv[0]) == NULL)
+ return return_type ? ERROR (cmd) : DECLINED (cmd);
+ if ((pass = cmd->argv[1]) == NULL)
++ return return_type ? ERROR (cmd) : DECLINED (cmd);
++ if (!g.sql_passwd || !g.homedir)
+ return return_type ? ERROR (cmd) : DECLINED (cmd);
+
+ if (!g.pass
diff --git a/ftp/proftpd/files/patch-be b/ftp/proftpd/files/patch-be
new file mode 100644
index 000000000000..7a6578032734
--- /dev/null
+++ b/ftp/proftpd/files/patch-be
@@ -0,0 +1,21 @@
+--- contrib/mod_mysql.c.old Sun Feb 11 20:19:00 2001
++++ contrib/mod_mysql.c Sun Feb 11 20:21:29 2001
+@@ -100,6 +100,9 @@
+ mysql_get_client_info (), mysql_get_server_info (mysqldb),
+ g.sql_user, g.sql_host);
+
++ if (mysqldb)
++ mysql_select_db (mysqldb, g.sql_dbname);
++
+ return HANDLED (cmd);
+ }
+
+@@ -115,7 +118,7 @@
+
+ /* This forces a quick ping of the remote server, so we know if its there. */
+ if (mysqldb)
+- mysql_select_db (mysqldb, g.sql_dbname);
++ mysql_ping( mysqldb );
+
+ if (!mysqldb
+ || ((error = mysql_query (mysqldb, query))