Here's another patch. This one isn't much different from the last patch,
except it tries a little harder to make sure that the required fields
are returned by the query (like the pgsql module does).
One question about how t_strdup works. How and who should memory
returned by t_strdup be freed? My patch has a function my_get_str()
which returns the value of t_strdup, and then I do this:
user.uid = atoi(my_get_str(res, row, "uid")); /* XXX leak */
user.gid = atoi(my_get_str(res, row, "gid")); /* XXX leak */
Will this result in a memory leak?
Matt
diff -u -r --new-file work/dovecot-0.99.10/doc/dovecot-mysql.conf work.patched/dovecot-0.99.10/doc/dovecot-mysql.conf
--- work/dovecot-0.99.10/doc/dovecot-mysql.conf Wed Dec 31 16:00:00 1969
+++ work.patched/dovecot-0.99.10/doc/dovecot-mysql.conf Tue Sep 30 12:24:00 2003
@@ -0,0 +1,70 @@
+# For the mysql passdb module, you'll need a database with a table that
+# contains fields for at least the userid and password. If you want to
+# use the user@domain syntax, you might want to have a separate domain
+# field as well.
+#
+# If your users all have the same uig/gid, and have predictable home
+# directories, you can use the static userdb module to generate the home
+# dir based on the userid and domain. In this case, you won't need fields
+# for home, uid, or gid in the database.
+#
+# If you prefer to use the mysql userdb module, you'll want to add fields
+# for home, uid, and gid. Here is an example table:
+#
+# CREATE TABLE users (
+# userid VARCHAR(128) NOT NULL,
+# password VARCHAR(64) NOT NULL,
+# home VARCHAR(256) NOT NULL,
+# uid INTEGER NOT NULL,
+# gid INTEGER NOT NULL,
+# active CHAR(1) DEFAULT 'Y' NOT NULL
+# );
+
+db_host = localhost
+db_port = 3306
+#db_unix_socket = /var/tmp/mysql.sock
+db = users
+db_user = dovecot-db
+db_passwd = opensesame
+db_client_flags = 0
+
+# Default password scheme.
+#
+# Currently supported schemes include PLAIN, PLAIN-MD5, DIGEST-MD5, and CRYPT.
+#
+#default_pass_scheme = PLAIN-MD5
+
+# Query to retrieve the password.
+#
+# The query should return one row, one column. If more than one row or column
+# is returned, authentication will automatically fail.
+#
+# Available substitutions:
+# %u = entire userid
+# %n = user part of user@domain
+# %d = domain part of user@domain
+#
+# Example:
+# password_query = SELECT password FROM users WHERE userid = '%n' AND domain = '%d'
+# password_query = SELECT password FROM users WHERE userid = '%u' AND active = 'Y'
+#
+#password_query = SELECT password FROM users WHERE userid = '%u'
+
+# Query to retrieve the user information.
+#
+# The query must return only one row. The columns to return are:
+# home - Home directory
+# mail - MAIL environment
+# system_user - System user name (for initgroups())
+# uid - System UID
+# gid - System GID
+#
+# Either home or mail is required. uid and gid are required. If more than one
+# row is returned or there's missing fields, login will automatically fail.
+#
+# Examples
+# user_query = SELECT home, uid, gid FROM users WHERE userid = '%n' AND domain = '%d'
+# user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u'
+# user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u'
+#
+#user_query = SELECT home, uid, gid FROM users WHERE userid = '%u'
diff -u -r --new-file work/dovecot-0.99.10/src/auth/Makefile.in work.patched/dovecot-0.99.10/src/auth/Makefile.in
--- work/dovecot-0.99.10/src/auth/Makefile.in Thu Jun 26 09:15:11 2003
+++ work.patched/dovecot-0.99.10/src/auth/Makefile.in Tue Sep 30 12:24:00 2003
@@ -99,10 +99,10 @@
dovecot_auth_LDADD = ../lib-settings/libsettings.a ../lib/liblib.a $(AUTH_LIBS) $(RAND_LIBS) $(MODULE_LIBS)
-dovecot_auth_SOURCES = auth-module.c db-ldap.c db-pgsql.c db-passwd-file.c login-connection.c main.c master-connection.c md5crypt.c mech.c mech-anonymous.c mech-cyrus-sasl2.c mech-plain.c mech-digest-md5.c mycrypt.c passdb.c passdb-ldap.c passdb-passwd.c passdb-passwd-file.c passdb-pam.c passdb-shadow.c passdb-vpopmail.c passdb-pgsql.c password-scheme.c userdb.c userdb-ldap.c userdb-passwd.c userdb-passwd-file.c userdb-static.c userdb-vpopmail.c userdb-pgsql.c
+dovecot_auth_SOURCES = auth-module.c db-ldap.c db-pgsql.c db-mysql.c db-passwd-file.c login-connection.c main.c master-connection.c md5crypt.c mech.c mech-anonymous.c mech-cyrus-sasl2.c mech-plain.c mech-digest-md5.c mycrypt.c passdb.c passdb-ldap.c passdb-passwd.c passdb-passwd-file.c passdb-pam.c passdb-shadow.c passdb-vpopmail.c passdb-pgsql.c passdb-mysql.c password-scheme.c userdb.c userdb-ldap.c userdb-passwd.c userdb-passwd-file.c userdb-static.c userdb-vpopmail.c userdb-pgsql.c userdb-mysql.c
-noinst_HEADERS = auth-login-interface.h auth-master-interface.h auth-mech-desc.h auth-module.h db-ldap.h db-pgsql.h db-passwd-file.h common.h login-connection.h master-connection.h md5crypt.h mech.h mycrypt.h passdb.h password-scheme.h userdb.h userdb-vpopmail.h
+noinst_HEADERS = auth-login-interface.h auth-master-interface.h auth-mech-desc.h auth-module.h db-ldap.h db-pgsql.h db-mysql.h db-passwd-file.h common.h login-connection.h master-connection.h md5crypt.h mech.h mycrypt.h passdb.h password-scheme.h userdb.h userdb-vpopmail.h
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ../../config.h
@@ -116,17 +116,17 @@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
dovecot_auth_OBJECTS = auth-module.$(OBJEXT) db-ldap.$(OBJEXT) \
-db-pgsql.$(OBJEXT) db-passwd-file.$(OBJEXT) login-connection.$(OBJEXT) \
+db-pgsql.$(OBJEXT) db-mysql.$(OBJEXT) db-passwd-file.$(OBJEXT) login-connection.$(OBJEXT) \
main.$(OBJEXT) master-connection.$(OBJEXT) md5crypt.$(OBJEXT) \
mech.$(OBJEXT) mech-anonymous.$(OBJEXT) mech-cyrus-sasl2.$(OBJEXT) \
mech-plain.$(OBJEXT) mech-digest-md5.$(OBJEXT) mycrypt.$(OBJEXT) \
passdb.$(OBJEXT) passdb-ldap.$(OBJEXT) passdb-passwd.$(OBJEXT) \
passdb-passwd-file.$(OBJEXT) passdb-pam.$(OBJEXT) \
passdb-shadow.$(OBJEXT) passdb-vpopmail.$(OBJEXT) \
-passdb-pgsql.$(OBJEXT) password-scheme.$(OBJEXT) userdb.$(OBJEXT) \
+passdb-pgsql.$(OBJEXT) passdb-mysql.$(OBJEXT) password-scheme.$(OBJEXT) userdb.$(OBJEXT) \
userdb-ldap.$(OBJEXT) userdb-passwd.$(OBJEXT) \
userdb-passwd-file.$(OBJEXT) userdb-static.$(OBJEXT) \
-userdb-vpopmail.$(OBJEXT) userdb-pgsql.$(OBJEXT)
+userdb-vpopmail.$(OBJEXT) userdb-pgsql.$(OBJEXT) userdb-mysql.$(OBJEXT)
dovecot_auth_DEPENDENCIES = ../lib-settings/libsettings.a \
../lib/liblib.a
dovecot_auth_LDFLAGS =
@@ -295,6 +295,13 @@
../../src/lib/strfuncs.h ../../src/lib/network.h \
../../src/lib/str.h ../../src/lib-settings/settings.h \
db-pgsql.h
+db-mysql.o: db-mysql.c ../../config.h common.h ../../src/lib/lib.h \
+ ../../src/lib/compat.h ../../src/lib/macros.h \
+ ../../src/lib/failures.h ../../src/lib/data-stack.h \
+ ../../src/lib/mempool.h ../../src/lib/imem.h \
+ ../../src/lib/strfuncs.h ../../src/lib/network.h \
+ ../../src/lib/str.h ../../src/lib-settings/settings.h \
+ db-mysql.h
login-connection.o: login-connection.c common.h ../../src/lib/lib.h \
../../config.h ../../src/lib/compat.h ../../src/lib/macros.h \
../../src/lib/failures.h ../../src/lib/data-stack.h \
@@ -388,6 +395,14 @@
../../src/lib/str.h ../../src/lib/strescape.h \
../../src/lib/var-expand.h password-scheme.h db-pgsql.h \
passdb.h mech.h auth-login-interface.h
+passdb-mysql.o: passdb-mysql.c ../../config.h common.h \
+ ../../src/lib/lib.h ../../src/lib/compat.h \
+ ../../src/lib/macros.h ../../src/lib/failures.h \
+ ../../src/lib/data-stack.h ../../src/lib/mempool.h \
+ ../../src/lib/imem.h ../../src/lib/strfuncs.h \
+ ../../src/lib/str.h ../../src/lib/strescape.h \
+ ../../src/lib/var-expand.h password-scheme.h db-mysql.h \
+ passdb.h mech.h auth-login-interface.h
passdb-shadow.o: passdb-shadow.c ../../config.h common.h \
../../src/lib/lib.h ../../src/lib/compat.h \
../../src/lib/macros.h ../../src/lib/failures.h \
@@ -428,6 +443,13 @@
../../src/lib/imem.h ../../src/lib/strfuncs.h \
../../src/lib/str.h ../../src/lib/strescape.h \
../../src/lib/var-expand.h db-pgsql.h userdb.h
+userdb-mysql.o: userdb-mysql.c ../../config.h common.h \
+ ../../src/lib/lib.h ../../src/lib/compat.h \
+ ../../src/lib/macros.h ../../src/lib/failures.h \
+ ../../src/lib/data-stack.h ../../src/lib/mempool.h \
+ ../../src/lib/imem.h ../../src/lib/strfuncs.h \
+ ../../src/lib/str.h ../../src/lib/strescape.h \
+ ../../src/lib/var-expand.h db-mysql.h userdb.h
userdb-static.o: userdb-static.c ../../config.h common.h \
../../src/lib/lib.h ../../src/lib/compat.h \
../../src/lib/macros.h ../../src/lib/failures.h \
diff -u -r --new-file work/dovecot-0.99.10/src/auth/db-mysql.c work.patched/dovecot-0.99.10/src/auth/db-mysql.c
--- work/dovecot-0.99.10/src/auth/db-mysql.c Wed Dec 31 16:00:00 1969
+++ work.patched/dovecot-0.99.10/src/auth/db-mysql.c Tue Sep 30 12:24:00 2003
@@ -0,0 +1,181 @@
+/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#if defined(PASSDB_MYSQL) || defined(USERDB_MYSQL)
+
+#include "common.h"
+#include "network.h"
+#include "str.h"
+#include "settings.h"
+#include "db-mysql.h"
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#define DEF(type, name) { type, #name, offsetof(struct mysql_settings, name) }
+
+static struct setting_def setting_defs[] = {
+ DEF(SET_STR, db_host),
+ DEF(SET_STR, db_port),
+ DEF(SET_STR, db_unix_socket),
+ DEF(SET_STR, db),
+ DEF(SET_STR, db_user),
+ DEF(SET_STR, db_passwd),
+ DEF(SET_STR, db_client_flags),
+ DEF(SET_STR, password_query),
+ DEF(SET_STR, user_query),
+ DEF(SET_STR, default_pass_scheme)
+};
+
+struct mysql_settings default_mysql_settings = {
+ MEMBER(db_host) "localhost",
+ MEMBER(db_port) "0",
+ MEMBER(db_unix_socket) "/var/tmp/mysql.sock",
+ MEMBER(db) "email_accounts",
+ MEMBER(db_user) "dovecot",
+ MEMBER(db_passwd) "changeme",
+ MEMBER(db_client_flags) "0",
+ MEMBER(password_query) "SELECT password FROM users WHERE userid = '%u'",
+ MEMBER(user_query) "SELECT home, uid, gid FROM users WHERE userid = '%u'",
+ MEMBER(default_pass_scheme) "PLAIN-MD5"
+};
+
+static struct mysql_connection *mysql_connections = NULL;
+
+static int mysql_conn_open(struct mysql_connection *conn);
+static void mysql_conn_close(struct mysql_connection *conn);
+
+void db_mysql_query(struct mysql_connection *conn, const char *query,
+ struct mysql_request *request)
+{
+ MYSQL_RES *res;
+ int failed;
+
+ if (!conn->connected) {
+ if (!mysql_conn_open(conn)) {
+ request->callback(conn, request, NULL);
+ return;
+ }
+ }
+
+ if (verbose_debug)
+ i_info("MYSQL: Performing query: %s", query);
+
+ if (mysql_query(conn->mysql, query))
+ i_info("MYSQL: Error executing query \"%s\": %s", query,
+ mysql_error(conn->mysql));
+
+ if ((res = mysql_store_result(conn->mysql)))
+ failed = FALSE;
+ else {
+ i_info("MYSQL: Error retrieving results: %s",
+ mysql_error(conn->mysql));
+ failed = TRUE;
+ }
+
+ request->callback(conn, request, failed ? NULL : res);
+ mysql_free_result(res);
+ i_free(request);
+}
+
+static int mysql_conn_open(struct mysql_connection *conn)
+{
+ if (conn->connected)
+ return TRUE;
+
+ if (conn->mysql == NULL) {
+ conn->mysql = mysql_init(NULL);
+ if (conn->mysql == NULL) {
+ i_error("MYSQL: mysql_init failed");
+ return FALSE;
+ }
+
+ if (!mysql_real_connect(conn->mysql, conn->set.db_host,
+ conn->set.db_user, conn->set.db_passwd,
+ conn->set.db,
+ atoi(conn->set.db_port),
+ conn->set.db_unix_socket,
+ strtoul(conn->set.db_client_flags,
+ NULL, 10))) {
+ i_error("MYSQL: Can't connect to database %s: %s",
+ conn->set.db, mysql_error(conn->mysql));
+ return FALSE;
+ }
+ }
+
+ conn->connected = TRUE;
+ return TRUE;
+}
+
+static void mysql_conn_close(struct mysql_connection *conn)
+{
+ conn->connected = FALSE;
+
+ if (conn->mysql != NULL) {
+ mysql_close(conn->mysql);
+ conn->mysql = NULL;
+ }
+}
+
+static struct mysql_connection *mysql_conn_find(const char *config_path)
+{
+ struct mysql_connection *conn;
+
+ for (conn = mysql_connections; conn != NULL; conn = conn->next) {
+ if (strcmp(conn->config_path, config_path) == 0)
+ return conn;
+ }
+
+ return NULL;
+}
+
+static const char *parse_setting(const char *key, const char *value,
+ void *context)
+{
+ struct mysql_connection *conn = context;
+
+ return parse_setting_from_defs(conn->pool, setting_defs,
+ &conn->set, key, value);
+}
+
+struct mysql_connection *db_mysql_init(const char *config_path)
+{
+ struct mysql_connection *conn;
+ pool_t pool;
+
+ conn = mysql_conn_find(config_path);
+ if (conn != NULL) {
+ conn->refcount++;
+ return conn;
+ }
+
+ pool = pool_alloconly_create("mysql_connection", 1024);
+ conn = p_new(pool, struct mysql_connection, 1);
+ conn->pool = pool;
+
+ conn->refcount = 1;
+
+ conn->config_path = p_strdup(pool, config_path);
+ conn->set = default_mysql_settings;
+ settings_read(config_path, parse_setting, conn);
+
+ (void)mysql_conn_open(conn);
+
+ conn->next = mysql_connections;
+ mysql_connections = conn;
+ return conn;
+}
+
+void db_mysql_unref(struct mysql_connection *conn)
+{
+ if (--conn->refcount > 0)
+ return;
+
+ mysql_conn_close(conn);
+ pool_unref(conn->pool);
+}
+
+#endif
diff -u -r --new-file work/dovecot-0.99.10/src/auth/db-mysql.h work.patched/dovecot-0.99.10/src/auth/db-mysql.h
--- work/dovecot-0.99.10/src/auth/db-mysql.h Wed Dec 31 16:00:00 1969
+++ work.patched/dovecot-0.99.10/src/auth/db-mysql.h Tue Sep 30 12:24:00 2003
@@ -0,0 +1,51 @@
+#ifndef __DB_MYSQL_H
+#define __DB_MYSQL_H
+
+#include <mysql.h>
+
+struct mysql_connection;
+struct mysql_request;
+
+typedef void mysql_query_callback_t(struct mysql_connection *conn,
+ struct mysql_request *request,
+ MYSQL_RES *res);
+
+struct mysql_settings {
+ const char *db_host;
+ const char *db_port;
+ const char *db_unix_socket;
+ const char *db;
+ const char *db_user;
+ const char *db_passwd;
+ const char *db_client_flags;
+ const char *password_query;
+ const char *user_query;
+ const char *default_pass_scheme;
+};
+
+struct mysql_connection {
+ struct mysql_connection *next;
+
+ pool_t pool;
+ int refcount;
+
+ char *config_path;
+ struct mysql_settings set;
+
+ MYSQL *mysql;
+
+ unsigned int connected:1;
+};
+
+struct mysql_request {
+ mysql_query_callback_t *callback;
+ void *context;
+};
+
+void db_mysql_query(struct mysql_connection *conn, const char *query,
+ struct mysql_request *request);
+
+struct mysql_connection *db_mysql_init(const char *config_path);
+void db_mysql_unref(struct mysql_connection *conn);
+
+#endif
diff -u -r --new-file work/dovecot-0.99.10/src/auth/master-connection.c work.patched/dovecot-0.99.10/src/auth/master-connection.c
--- work/dovecot-0.99.10/src/auth/master-connection.c Mon May 26 08:27:13 2003
+++ work.patched/dovecot-0.99.10/src/auth/master-connection.c Tue Sep 30 12:24:00 2003
@@ -55,7 +55,7 @@
reply.virtual_user_idx = reply_add(buf, user->virtual_user);
reply.mail_idx = reply_add(buf, user->mail);
- p = strstr(user->home, "/./");
+ p = user->home ? strstr(user->home, "/./") : NULL;
if (p == NULL) {
reply.home_idx = reply_add(buf, user->home);
reply.chroot_idx = reply_add(buf, NULL);
diff -u -r --new-file work/dovecot-0.99.10/src/auth/passdb-mysql.c work.patched/dovecot-0.99.10/src/auth/passdb-mysql.c
--- work/dovecot-0.99.10/src/auth/passdb-mysql.c Wed Dec 31 16:00:00 1969
+++ work.patched/dovecot-0.99.10/src/auth/passdb-mysql.c Tue Sep 30 12:24:00 2003
@@ -0,0 +1,171 @@
+/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef PASSDB_MYSQL
+
+#include "common.h"
+#include "str.h"
+#include "strescape.h"
+#include "var-expand.h"
+#include "password-scheme.h"
+#include "db-mysql.h"
+#include "passdb.h"
+
+#include <mysql.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct passdb_mysql_connection {
+ struct mysql_connection *conn;
+};
+
+struct passdb_mysql_request {
+ struct mysql_request request;
+
+ enum passdb_credentials credentials;
+ union {
+ verify_plain_callback_t *verify_plain;
+ lookup_credentials_callback_t *lookup_credentials;
+ } callback;
+
+ char password[1];
+};
+
+static struct passdb_mysql_connection *passdb_mysql_conn;
+
+static void mysql_handle_request(struct mysql_connection *conn,
+ struct mysql_request *request, MYSQL_RES *res)
+{
+ struct passdb_mysql_request *mysql_request =
+ (struct passdb_mysql_request *) request;
+ struct auth_request *auth_request = request->context;
+ const char *user, *password, *scheme;
+ int ret = 0;
+
+ user = auth_request->user;
+ password = NULL;
+
+ if (res != NULL) {
+ if (mysql_num_rows(res) == 0) {
+ if (verbose)
+ i_info("mysql(%s): Unknown user", user);
+ } else if (mysql_num_rows(res) > 1) {
+ i_error("mysql(%s): Multiple matches for user", user);
+ } else if (mysql_num_fields(res) != 1) {
+ i_error("mysql(%s): Password query returned "
+ "more than one field", user);
+ } else {
+ MYSQL_ROW row;
+
+ row = mysql_fetch_row(res);
+ if (row)
+ password = t_strdup(row[0]);
+ }
+ }
+
+ scheme = password_get_scheme(&password);
+ if (scheme == NULL) {
+ scheme = conn->set.default_pass_scheme;
+ i_assert(scheme != NULL);
+ }
+
+ if (mysql_request->credentials != -1) {
+ passdb_handle_credentials(mysql_request->credentials,
+ user, password, scheme,
+ mysql_request->callback.lookup_credentials,
+ auth_request);
+ return;
+ }
+
+ /* verify plain */
+ if (password == NULL) {
+ mysql_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN,
+ auth_request);
+ return;
+ }
+
+ ret = password_verify(mysql_request->password, password,
+ scheme, user);
+ if (ret < 0)
+ i_error("mysql(%s): Unknown password scheme %s", user, scheme);
+ else if (ret == 0) {
+ if (verbose)
+ i_info("mysql(%s): Password mismatch", user);
+ }
+
+ mysql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK :
+ PASSDB_RESULT_PASSWORD_MISMATCH,
+ auth_request);
+}
+
+static void mysql_lookup_pass(struct auth_request *auth_request,
+ struct mysql_request *mysql_request)
+{
+ struct mysql_connection *conn = passdb_mysql_conn->conn;
+ const char *query;
+ string_t *str;
+
+ str = t_str_new(512);
+ var_expand(str, conn->set.password_query,
+ str_escape(auth_request->user), NULL);
+ query = str_c(str);
+
+ mysql_request->callback = mysql_handle_request;
+ mysql_request->context = auth_request;
+
+ db_mysql_query(conn, query, mysql_request);
+}
+
+static void
+mysql_verify_plain(struct auth_request *request, const char *password,
+ verify_plain_callback_t *callback)
+{
+ struct passdb_mysql_request *mysql_request;
+
+ mysql_request = i_malloc(sizeof(struct passdb_mysql_request) +
+ strlen(password));
+ mysql_request->credentials = -1;
+ mysql_request->callback.verify_plain = callback;
+ strcpy(mysql_request->password, password);
+
+ mysql_lookup_pass(request, &mysql_request->request);
+}
+
+static void mysql_lookup_credentials(struct auth_request *request,
+ enum passdb_credentials credentials,
+ lookup_credentials_callback_t *callback)
+{
+ struct passdb_mysql_request *mysql_request;
+
+ mysql_request = i_new(struct passdb_mysql_request, 1);
+ mysql_request->credentials = credentials;
+ mysql_request->callback.lookup_credentials = callback;
+
+ mysql_lookup_pass(request, &mysql_request->request);
+}
+
+static void passdb_mysql_init(const char *args)
+{
+ struct mysql_connection *conn;
+
+ passdb_mysql_conn = i_new(struct passdb_mysql_connection, 1);
+ passdb_mysql_conn->conn = conn = db_mysql_init(args);
+}
+
+static void passdb_mysql_deinit(void)
+{
+ db_mysql_unref(passdb_mysql_conn->conn);
+ i_free(passdb_mysql_conn);
+}
+
+struct passdb_module passdb_mysql = {
+ passdb_mysql_init,
+ passdb_mysql_deinit,
+
+ mysql_verify_plain,
+ mysql_lookup_credentials
+};
+
+#endif
diff -u -r --new-file work/dovecot-0.99.10/src/auth/passdb.c work.patched/dovecot-0.99.10/src/auth/passdb.c
--- work/dovecot-0.99.10/src/auth/passdb.c Sun May 18 05:26:28 2003
+++ work.patched/dovecot-0.99.10/src/auth/passdb.c Tue Sep 30 12:24:00 2003
@@ -110,6 +110,10 @@
if (strcasecmp(name, "pgsql") == 0)
passdb = &passdb_pgsql;
#endif
+#ifdef PASSDB_MYSQL
+ if (strcasecmp(name, "mysql") == 0)
+ passdb = &passdb_mysql;
+#endif
#ifdef HAVE_MODULES
passdb_module = passdb != NULL ? NULL : auth_module_open(name);
if (passdb_module != NULL) {
diff -u -r --new-file work/dovecot-0.99.10/src/auth/passdb.h work.patched/dovecot-0.99.10/src/auth/passdb.h
--- work/dovecot-0.99.10/src/auth/passdb.h Thu Mar 20 07:46:33 2003
+++ work.patched/dovecot-0.99.10/src/auth/passdb.h Tue Sep 30 12:24:00 2003
@@ -58,6 +58,7 @@
extern struct passdb_module passdb_vpopmail;
extern struct passdb_module passdb_ldap;
extern struct passdb_module passdb_pgsql;
+extern struct passdb_module passdb_mysql;
void passdb_init(void);
void passdb_deinit(void);
diff -u -r --new-file work/dovecot-0.99.10/src/auth/userdb-mysql.c work.patched/dovecot-0.99.10/src/auth/userdb-mysql.c
--- work/dovecot-0.99.10/src/auth/userdb-mysql.c Wed Dec 31 16:00:00 1969
+++ work.patched/dovecot-0.99.10/src/auth/userdb-mysql.c Tue Sep 30 12:24:00 2003
@@ -0,0 +1,160 @@
+/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef USERDB_MYSQL
+
+#include "common.h"
+#include "str.h"
+#include "strescape.h"
+#include "var-expand.h"
+#include "db-mysql.h"
+#include "userdb.h"
+
+#include <mysql.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct userdb_mysql_connection {
+ struct mysql_connection *conn;
+};
+
+struct userdb_mysql_request {
+ struct mysql_request request;
+ userdb_callback_t *userdb_callback;
+
+ char username[1]; /* variable width */
+};
+
+static struct userdb_mysql_connection *userdb_mysql_conn;
+
+static int is_result_valid(MYSQL_RES *res)
+{
+ int i, n_fields, found;
+ MYSQL_FIELD *fields;
+
+ if (res == NULL) {
+ i_error("MYSQL: Query failed");
+ return FALSE;
+ }
+
+ if (mysql_num_rows(res) == 0) {
+ if (verbose)
+ i_error("MYSQL: Authenticated user not found");
+ return FALSE;
+ }
+
+ n_fields = mysql_num_fields(res);
+ fields = mysql_fetch_fields(res);
+
+ /* Make sure the 'uid' field exists. */
+ for (found = 0, i = 0; i < n_fields; i++)
+ if (strcmp("uid", fields[i].name) == 0) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ i_error("MYSQL: User query did not return 'uid' field");
+ return FALSE;
+ }
+
+ /* Make sure the 'gid' field exists. */
+ for (found = 0, i = 0; i < n_fields; i++)
+ if (strcmp("gid", fields[i].name) == 0) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ i_error("MYSQL: User query did not return 'gid' field");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static const char *my_get_str(MYSQL_RES *res, MYSQL_ROW row, const char *field)
+{
+ int i, n_fields;
+ unsigned long *lengths;
+ MYSQL_FIELD *fields;
+
+ n_fields = mysql_num_fields(res);
+ lengths = mysql_fetch_lengths(res);
+ fields = mysql_fetch_fields(res);
+ for (i = 0; i < n_fields; i++)
+ if (strcmp(field, fields[i].name) == 0)
+ return (const char *) lengths[i] == 0 ?
+ NULL : t_strdup(row[i]);
+
+ return NULL;
+}
+
+static void mysql_handle_request(struct mysql_connection *conn __attr_unused__,
+ struct mysql_request *request, MYSQL_RES *res)
+{
+ struct userdb_mysql_request *urequest =
+ (struct userdb_mysql_request *) request;
+ struct user_data user;
+ MYSQL_ROW row;
+
+ if (res != NULL && is_result_valid(res) &&
+ (row = mysql_fetch_row(res))) {
+ memset(&user, 0, sizeof(user));
+ user.virtual_user = urequest->username;
+ user.system_user = my_get_str(res, row, "system_user");
+ user.home = my_get_str(res, row, "home");
+ user.mail = my_get_str(res, row, "mail");
+ user.uid = atoi(my_get_str(res, row, "uid")); /* XXX leak */
+ user.gid = atoi(my_get_str(res, row, "gid")); /* XXX leak */
+ urequest->userdb_callback(&user, request->context);
+ } else {
+ urequest->userdb_callback(NULL, request->context);
+ }
+}
+
+static void userdb_mysql_lookup(const char *user, userdb_callback_t *callback,
+ void *context)
+{
+ struct mysql_connection *conn = userdb_mysql_conn->conn;
+ struct userdb_mysql_request *request;
+ const char *query;
+ string_t *str;
+
+ str = t_str_new(512);
+ var_expand(str, conn->set.user_query, str_escape(user), NULL);
+ query = str_c(str);
+
+ request = i_malloc(sizeof(struct userdb_mysql_request) + strlen(user));
+ request->request.callback = mysql_handle_request;
+ request->request.context = context;
+ request->userdb_callback = callback;
+ strcpy(request->username, user);
+
+ db_mysql_query(conn, query, &request->request);
+}
+
+static void userdb_mysql_init(const char *args)
+{
+ struct mysql_connection *conn;
+
+ userdb_mysql_conn = i_new(struct userdb_mysql_connection, 1);
+ userdb_mysql_conn->conn = conn = db_mysql_init(args);
+}
+
+static void userdb_mysql_deinit(void)
+{
+ db_mysql_unref(userdb_mysql_conn->conn);
+ i_free(userdb_mysql_conn);
+}
+
+struct userdb_module userdb_mysql = {
+ userdb_mysql_init,
+ userdb_mysql_deinit,
+
+ userdb_mysql_lookup
+};
+
+#endif
diff -u -r --new-file work/dovecot-0.99.10/src/auth/userdb.c work.patched/dovecot-0.99.10/src/auth/userdb.c
--- work/dovecot-0.99.10/src/auth/userdb.c Sun May 18 05:26:28 2003
+++ work.patched/dovecot-0.99.10/src/auth/userdb.c Tue Sep 30 12:24:00 2003
@@ -49,6 +49,10 @@
if (strcasecmp(name, "pgsql") == 0)
userdb = &userdb_pgsql;
#endif
+#ifdef USERDB_MYSQL
+ if (strcasecmp(name, "mysql") == 0)
+ userdb = &userdb_mysql;
+#endif
#ifdef HAVE_MODULES
userdb_module = userdb != NULL ? NULL : auth_module_open(name);
if (userdb_module != NULL) {
diff -u -r --new-file work/dovecot-0.99.10/src/auth/userdb.h work.patched/dovecot-0.99.10/src/auth/userdb.h
--- work/dovecot-0.99.10/src/auth/userdb.h Sun May 18 05:26:28 2003
+++ work.patched/dovecot-0.99.10/src/auth/userdb.h Tue Sep 30 12:24:00 2003
@@ -29,6 +29,7 @@
extern struct userdb_module userdb_vpopmail;
extern struct userdb_module userdb_ldap;
extern struct userdb_module userdb_pgsql;
+extern struct userdb_module userdb_mysql;
void userdb_init(void);
void userdb_deinit(void);