# HG changeset patch # User Timo Sirainen # Date 1097881971 -10800 # Node ID 3c3ac12be307c78dd3e8c2c2d27447fac2bdd804 # Parent cc35dc9b9cdfc8ff5124d5b8fe421a3a9c407867 Created generic asynchronous SQL API and implemented MySQL and PostgreSQL drivers. MySQL is implemented synchronously because it's API doesn't provide async way to do it. Replaced pgsql and mysql userdb/passdb with generic sql userdb/passdb. diff -r cc35dc9b9cdf -r 3c3ac12be307 configure.in --- a/configure.in Sat Oct 16 01:56:10 2004 +0300 +++ b/configure.in Sat Oct 16 02:12:51 2004 +0300 @@ -1,7 +1,7 @@ AC_INIT(src) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(dovecot, 1.0-test46) +AM_INIT_AUTOMAKE(dovecot, 1.0-test48) AM_MAINTAINER_MODE @@ -1117,14 +1117,14 @@ fi AC_CHECK_HEADER(libpq-fe.h, [ if test "$PGSQL_INCLUDE" != ""; then - AUTH_CFLAGS="$AUTH_CFLAGS -I$PGSQL_INCLUDE" + SQL_CFLAGS="$SQL_CFLAGS -I$PGSQL_INCLUDE" fi if test "$PGSQL_LIBDIR" != ""; then - AUTH_LIBS="$AUTH_LIBS -L$PGSQL_LIBDIR" + SQL_LIBS="$SQL_LIBS -L$PGSQL_LIBDIR" fi - AUTH_LIBS="$AUTH_LIBS -lpq" - AC_DEFINE(USERDB_PGSQL,, Build with PostgreSQL support) - AC_DEFINE(PASSDB_PGSQL,, Build with PostgreSQL support) + SQL_LIBS="$SQL_LIBS -lpq" + AC_DEFINE(HAVE_PGSQL,, Build with PostgreSQL support) + have_sql=yes userdb="$userdb pgsql" passdb="$passdb pgsql" ]) @@ -1170,12 +1170,12 @@ fi AC_CHECK_HEADER(mysql.h, [ if test "$MYSQL_INCLUDE" != ""; then - AUTH_CFLAGS="$AUTH_CFLAGS -I$MYSQL_INCLUDE" + SQL_CFLAGS="$SQL_CFLAGS -I$MYSQL_INCLUDE" fi if test "$MYSQL_LIBDIR" != ""; then - AUTH_LIBS="$AUTH_LIBS -L$MYSQL_LIBDIR" + SQL_LIBS="$SQL_LIBS -L$MYSQL_LIBDIR" fi - AUTH_LIBS="$AUTH_LIBS $mysql_lib" + SQL_LIBS="$SQL_LIBS $mysql_lib" AC_CHECK_LIB(mysqlclient, mysql_ssl_set, [ AC_DEFINE(HAVE_MYSQL_SSL,, Define if your MySQL library has SSL functions) @@ -1194,8 +1194,8 @@ ]) ]) - AC_DEFINE(USERDB_MYSQL,, Build with MySQL support) - AC_DEFINE(PASSDB_MYSQL,, Build with MySQL support) + AC_DEFINE(HAVE_MYSQL,, Build with MySQL support) + have_sql=yes userdb="$userdb mysql" passdb="$passdb mysql" ]) @@ -1205,6 +1205,12 @@ LIBS=$old_LIBS fi +if test "$have_sql" = yes; then + AC_DEFINE(PASSDB_SQL,, Build with SQL support) + AC_DEFINE(USERDB_SQL,, Build with SQL support) + AUTH_LIBS="$AUTH_LIBS $SQL_LIBS" +fi + if test $want_vpopmail = yes; then vpopmail_home="`echo ~vpopmail`" vpop_libdeps="$vpopmail_home/etc/lib_deps" @@ -1257,6 +1263,8 @@ AC_SUBST(AUTH_CFLAGS) AC_SUBST(AUTH_LIBS) +AC_SUBST(SQL_CFLAGS) +AC_SUBST(SQL_LIBS) dnl ** dnl ** Index file compatibility flags @@ -1328,6 +1336,7 @@ doc/Makefile src/Makefile src/lib/Makefile +src/lib-sql/Makefile src/lib-auth/Makefile src/lib-charset/Makefile src/lib-imap/Makefile diff -r cc35dc9b9cdf -r 3c3ac12be307 doc/Makefile.am --- a/doc/Makefile.am Sat Oct 16 01:56:10 2004 +0300 +++ b/doc/Makefile.am Sat Oct 16 02:12:51 2004 +0300 @@ -15,6 +15,5 @@ mkcert.sh \ dovecot-openssl.cnf \ dovecot-ldap.conf \ - dovecot-mysql.conf \ - dovecot-pgsql.conf \ + dovecot-sql.conf \ $(doc_DATA) diff -r cc35dc9b9cdf -r 3c3ac12be307 doc/dovecot-mysql.conf --- a/doc/dovecot-mysql.conf Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -# 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(255) 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 - -# Parameters for SSL connection to MySQL 4.x -# -# Only ssl_ca or ssl_ca_path is required. Set ssl_cert and ssl_key -# to use x509 authentication instead of username/password pair. - -#ssl_cert = /etc/mysql/ssl/client-cert.pem -#ssl_key = /etc/mysql/ssl/client-key.pem -#ssl_ca = /etc/mysql/ssl/cacert.pem -#ssl_ca_path = /etc/mysql/ssl/ -#ssl_cipher = HIGH - -# 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 -r cc35dc9b9cdf -r 3c3ac12be307 doc/dovecot-pgsql.conf --- a/doc/dovecot-pgsql.conf Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -# For the pgsql 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 pgsql 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 -# ); - -# Database connection string. -# -# For available options, see the PostgreSQL documention for the PQconnectdb -# function of libpq. -# -# Examples: -# connect = host=192.168.1.1 dbname=users -# connect = host=sql.example.com dbname=virtual user=virtual password=blarg -# -#connect = dbname=virtual user=virtual - -# 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 getting user's groups from /etc/group) -# 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 -r cc35dc9b9cdf -r 3c3ac12be307 doc/dovecot-sql.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/dovecot-sql.conf Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,90 @@ +# For the sql 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 sql 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(255) NOT NULL, +# uid INTEGER NOT NULL, +# gid INTEGER NOT NULL, +# active CHAR(1) DEFAULT 'Y' NOT NULL +# ); + +# Database driver: mysql, pgsql +#driver = + +# Database connection string. This is driver-specific setting. +# +# pgsql: +# For available options, see the PostgreSQL documention for the +# PQconnectdb function of libpq. +# +# mysql: +# Basic options emulate PostgreSQL option names: +# host, port, user, password, dbname +# +# But also adds some new settings: +# client_flags - See MySQL manual +# ssl_ca, ssl_ca_path - Set either one or both to enable SSL +# ssl_cert, ssl_key - For sending client-side certificates to server +# ssl_cipher - Set minimum allowed cipher security (default: HIGH) +# +# You can connect to UNIX sockets by using host: host=/var/run/mysql.sock +# Note that currently you can't use spaces in parameters. +# +# Examples: +# connect = host=192.168.1.1 dbname=users +# connect = host=sql.example.com dbname=virtual user=virtual password=blarg +# +#connect = dbname=virtual user=virtual + +# 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 getting user's groups from /etc/group) +# 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 -r cc35dc9b9cdf -r 3c3ac12be307 src/Makefile.am --- a/src/Makefile.am Sat Oct 16 01:56:10 2004 +0300 +++ b/src/Makefile.am Sat Oct 16 02:12:51 2004 +0300 @@ -2,4 +2,4 @@ POP3D = pop3-login pop3 endif -SUBDIRS = lib lib-ntlm lib-settings lib-charset lib-mail lib-imap lib-index lib-storage lib-auth auth master login-common imap-login imap $(POP3D) util +SUBDIRS = lib lib-sql lib-ntlm lib-settings lib-charset lib-mail lib-imap lib-index lib-storage lib-auth auth master login-common imap-login imap $(POP3D) util diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/Makefile.am --- a/src/auth/Makefile.am Sat Oct 16 01:56:10 2004 +0300 +++ b/src/auth/Makefile.am Sat Oct 16 02:12:51 2004 +0300 @@ -6,6 +6,7 @@ INCLUDES = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-sql \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-ntlm \ -DAUTH_MODULE_DIR=\""$(moduledir)/auth"\" \ @@ -24,6 +25,7 @@ libpassword.a \ ../lib-settings/libsettings.a \ ../lib-ntlm/libntlm.a \ + ../lib-sql/libsql.a \ ../lib/liblib.a \ $(AUTH_LIBS) \ $(RAND_LIBS) \ @@ -34,8 +36,7 @@ auth-master-connection.c \ auth-module.c \ db-ldap.c \ - db-mysql.c \ - db-pgsql.c \ + db-sql.c \ db-passwd-file.c \ main.c \ mech.c \ @@ -56,16 +57,14 @@ passdb-checkpassword.c \ passdb-shadow.c \ passdb-vpopmail.c \ - passdb-mysql.c \ - passdb-pgsql.c \ + passdb-sql.c \ userdb.c \ userdb-ldap.c \ userdb-passwd.c \ userdb-passwd-file.c \ userdb-static.c \ userdb-vpopmail.c \ - userdb-mysql.c \ - userdb-pgsql.c + userdb-sql.c noinst_HEADERS = \ auth-client-connection.h \ @@ -73,8 +72,7 @@ auth-master-connection.h \ auth-module.h \ db-ldap.h \ - db-mysql.h \ - db-pgsql.h \ + db-sql.h \ db-passwd-file.h \ common.h \ mech.h \ diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/auth-master-connection.c --- a/src/auth/auth-master-connection.c Sat Oct 16 01:56:10 2004 +0300 +++ b/src/auth/auth-master-connection.c Sat Oct 16 02:12:51 2004 +0300 @@ -30,6 +30,7 @@ struct master_userdb_request { struct auth_master_connection *conn; unsigned int id; + struct auth_request *auth_request; }; static void master_output(void *context); @@ -93,6 +94,7 @@ master_send(master_request->conn, "%s", str_c(str)); } } + auth_request_destroy(master_request->auth_request); i_free(master_request); } @@ -131,12 +133,10 @@ master_request = i_new(struct master_userdb_request, 1); master_request->conn = conn; master_request->id = id; + master_request->auth_request = request; conn->refcount++; userdb->lookup(request, userdb_callback, master_request); - - /* the auth request is finished, we don't need it anymore */ - auth_request_destroy(request); } return TRUE; } diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/db-mysql.c --- a/src/auth/db-mysql.c Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,230 +0,0 @@ -/* 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 -#include -#include - -#define DEF(type, name) { type, #name, offsetof(struct mysql_settings, name) } - -static struct setting_def setting_defs[] = { - DEF(SET_STR, db_host), - DEF(SET_INT, db_port), - DEF(SET_STR, db_unix_socket), - DEF(SET_STR, db), - DEF(SET_STR, db_user), - DEF(SET_STR, db_passwd), - DEF(SET_INT, db_client_flags), - DEF(SET_STR, ssl_key), - DEF(SET_STR, ssl_cert), - DEF(SET_STR, ssl_ca), - DEF(SET_STR, ssl_ca_path), - DEF(SET_STR, ssl_cipher), - 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) NULL, - MEMBER(db) NULL, - MEMBER(db_user) NULL, - MEMBER(db_passwd) NULL, - MEMBER(db_client_flags) 0, - MEMBER(ssl_key) NULL, - MEMBER(ssl_cert) NULL, - MEMBER(ssl_ca) NULL, - MEMBER(ssl_ca_path) NULL, - MEMBER(ssl_cipher) "HIGH", - 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 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 = NULL; - int failed; - - if (verbose_debug) - i_info("MySQL: Performing query: %s", query); - - if (!conn->connected) { - if (!db_mysql_connect(conn)) { - request->callback(conn, request, NULL); - return; - } - } - - failed = mysql_query(conn->mysql, query) != 0; - if (failed) { - /* query failed */ - switch (mysql_errno(conn->mysql)) { - case CR_SERVER_GONE_ERROR: - case CR_SERVER_LOST: - /* connection lost - try immediate reconnect */ - if (!db_mysql_connect(conn)) - break; - if (mysql_query(conn->mysql, query) == 0) { - failed = FALSE; - break; - } - /* query failed, fallback to error handler */ - default: - i_error("MySQL: Error executing query \"%s\": %s", - query, mysql_error(conn->mysql)); - break; - } - } - - if (!failed) { - /* query succeeded */ - if ((res = mysql_store_result(conn->mysql)) == NULL) { - /* something went wrong on storing result */ - failed = TRUE; - i_error("MySQL: Error retrieving results: %s", - mysql_error(conn->mysql)); - } - } - - request->callback(conn, request, res); - if (!failed) - mysql_free_result(res); - i_free(request); -} - -int db_mysql_connect(struct mysql_connection *conn) -{ - int use_ssl = FALSE; - - 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; - } - } - -#ifdef HAVE_MYSQL_SSL - if (conn->set.ssl_ca != NULL || conn->set.ssl_ca_path != NULL) { - mysql_ssl_set(conn->mysql, conn->set.ssl_key, - conn->set.ssl_cert, - conn->set.ssl_ca, - conn->set.ssl_ca_path -#ifdef HAVE_MYSQL_SSL_CIPHER - ,conn->set.ssl_cipher -#endif - ); - use_ssl = TRUE; - } -#endif - - if (mysql_real_connect(conn->mysql, conn->set.db_host, - conn->set.db_user, conn->set.db_passwd, - conn->set.db, - conn->set.db_port, - conn->set.db_unix_socket, - conn->set.db_client_flags) == NULL) { - i_error("MySQL: Can't connect to database %s: %s", - conn->set.db, mysql_error(conn->mysql)); - } else { - conn->connected = TRUE; - i_info("MySQL: connected to %s%s", conn->set.db_host, - use_ssl ? "using SSL" : ""); - } - - return conn->connected; -} - -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; - if (!settings_read(config_path, NULL, parse_setting, NULL, conn)) - exit(FATAL_DEFAULT); - - if (conn->set.db == NULL) - i_fatal("MySQL: db variable isn't set in config file"); - if (conn->set.db_user == NULL) - i_fatal("MySQL: db_user variable isn't set in config file"); - - 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 -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/db-mysql.h --- a/src/auth/db-mysql.h Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -#ifndef __DB_MYSQL_H -#define __DB_MYSQL_H - -#include -#include - -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; - unsigned int db_port; - const char *db_unix_socket; - const char *db; - const char *db_user; - const char *db_passwd; - unsigned int db_client_flags; - const char *ssl_key; - const char *ssl_cert; - const char *ssl_ca; - const char *ssl_ca_path; - const char *ssl_cipher; - 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); - -int db_mysql_connect(struct mysql_connection *conn); - -#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/db-pgsql.c --- a/src/auth/db-pgsql.c Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ - -#include "config.h" -#undef HAVE_CONFIG_H - -#if defined(PASSDB_PGSQL) || defined(USERDB_PGSQL) - -#include "common.h" -#include "network.h" -#include "str.h" -#include "settings.h" -#include "db-pgsql.h" - -#include -#include - -#define DEF(type, name) { type, #name, offsetof(struct pgsql_settings, name) } - -static struct setting_def setting_defs[] = { - DEF(SET_STR, connect), - DEF(SET_STR, password_query), - DEF(SET_STR, user_query), - DEF(SET_STR, default_pass_scheme) -}; - -struct pgsql_settings default_pgsql_settings = { - MEMBER(connect) "dbname=virtual user=virtual", - 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 pgsql_connection *pgsql_connections = NULL; - -static void pgsql_conn_close(struct pgsql_connection *conn); - -const char *db_pgsql_escape(const char *str) -{ - char *esc_str; - size_t len = strlen(str); - - /* @UNSAFE */ - esc_str = t_malloc(len*2+1); - PQescapeString(esc_str, str, len); - return esc_str; -} - -void db_pgsql_query(struct pgsql_connection *conn, const char *query, - struct pgsql_request *request) -{ - PGresult *res; - int failed; - - if (!conn->connected) { - if (!db_pgsql_connect(conn)) { - request->callback(conn, request, NULL); - return; - } - } - - if (verbose_debug) - i_info("PGSQL: Performing query: %s", query); - - res = PQexec(conn->pg, query); - switch (PQresultStatus(res)) { - case PGRES_EMPTY_QUERY: - case PGRES_COMMAND_OK: - case PGRES_TUPLES_OK: - break; - - default: - /* probably lost connection */ - i_info("PGSQL: Query failed, reconnecting"); - PQclear(res); - PQreset(conn->pg); - - res = PQexec(conn->pg, query); - break; - } - - if (PQresultStatus(res) == PGRES_TUPLES_OK) - failed = FALSE; - else { - i_error("PGSQL: Query \"%s\" failed: %s", - query, PQresultErrorMessage(res)); - failed = TRUE; - } - - request->callback(conn, request, failed ? NULL : res); - PQclear(res); - i_free(request); -} - -int db_pgsql_connect(struct pgsql_connection *conn) -{ - if (conn->connected) - return TRUE; - - i_assert(conn->pg == NULL); - - conn->pg = PQconnectdb(conn->set.connect); - if (PQstatus(conn->pg) != CONNECTION_OK) { - i_error("PGSQL: Can't connect to database %s", - conn->set.connect); - PQfinish(conn->pg); - conn->pg = NULL; - return FALSE; - } - - conn->connected = TRUE; - return TRUE; -} - -static void pgsql_conn_close(struct pgsql_connection *conn) -{ - conn->connected = FALSE; - - if (conn->pg != NULL) { - PQfinish(conn->pg); - conn->pg = NULL; - } -} - -static struct pgsql_connection *pgsql_conn_find(const char *config_path) -{ - struct pgsql_connection *conn; - - for (conn = pgsql_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 pgsql_connection *conn = context; - - return parse_setting_from_defs(conn->pool, setting_defs, - &conn->set, key, value); -} - -struct pgsql_connection *db_pgsql_init(const char *config_path) -{ - struct pgsql_connection *conn; - pool_t pool; - - conn = pgsql_conn_find(config_path); - if (conn != NULL) { - conn->refcount++; - return conn; - } - - pool = pool_alloconly_create("pgsql_connection", 1024); - conn = p_new(pool, struct pgsql_connection, 1); - conn->pool = pool; - - conn->refcount = 1; - - conn->config_path = p_strdup(pool, config_path); - conn->set = default_pgsql_settings; - if (!settings_read(config_path, NULL, parse_setting, NULL, conn)) - exit(FATAL_DEFAULT); - - conn->next = pgsql_connections; - pgsql_connections = conn; - return conn; -} - -void db_pgsql_unref(struct pgsql_connection *conn) -{ - if (--conn->refcount > 0) - return; - - pgsql_conn_close(conn); - pool_unref(conn->pool); -} - -#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/db-pgsql.h --- a/src/auth/db-pgsql.h Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -#ifndef __DB_PGSQL_H -#define __DB_PGSQL_H - -#include - -struct pgsql_connection; -struct pgsql_request; - -typedef void pgqsl_query_callback_t(struct pgsql_connection *conn, - struct pgsql_request *request, - PGresult *res); - -struct pgsql_settings { - const char *connect; - const char *password_query; - const char *user_query; - const char *allowed_chars; - const char *default_pass_scheme; -}; - -struct pgsql_connection { - struct pgsql_connection *next; - - pool_t pool; - int refcount; - - char *config_path; - struct pgsql_settings set; - - PGconn *pg; - - unsigned int connected:1; -}; - -struct pgsql_request { - pgqsl_query_callback_t *callback; - void *context; -}; - -const char *db_pgsql_escape(const char *str); - -void db_pgsql_query(struct pgsql_connection *conn, const char *query, - struct pgsql_request *request); - -struct pgsql_connection *db_pgsql_init(const char *config_path); -void db_pgsql_unref(struct pgsql_connection *conn); - -int db_pgsql_connect(struct pgsql_connection *conn); - -#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/db-sql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/db-sql.c Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,106 @@ +/* Copyright (C) 2003-2004 Timo Sirainen */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#if defined(PASSDB_SQL) || defined(USERDB_SQL) +#include "common.h" +#include "settings.h" +#include "db-sql.h" + +#include +#include + +#define DEF(type, name) { type, #name, offsetof(struct sql_settings, name) } + +static struct setting_def setting_defs[] = { + DEF(SET_STR, driver), + DEF(SET_STR, connect), + DEF(SET_STR, password_query), + DEF(SET_STR, user_query), + DEF(SET_STR, default_pass_scheme) +}; + +struct sql_settings default_sql_settings = { + MEMBER(driver) NULL, + MEMBER(connect) NULL, + 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 sql_connection *connections = NULL; + +static struct sql_connection *sql_conn_find(const char *config_path) +{ + struct sql_connection *conn; + + for (conn = 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 sql_connection *conn = context; + + return parse_setting_from_defs(conn->pool, setting_defs, + &conn->set, key, value); +} + +struct sql_connection *db_sql_init(const char *config_path) +{ + struct sql_connection *conn; + pool_t pool; + + conn = sql_conn_find(config_path); + if (conn != NULL) { + conn->refcount++; + return conn; + } + + pool = pool_alloconly_create("sql_connection", 1024); + conn = p_new(pool, struct sql_connection, 1); + conn->pool = pool; + + conn->refcount = 1; + + conn->config_path = p_strdup(pool, config_path); + conn->set = default_sql_settings; + if (!settings_read(config_path, NULL, parse_setting, NULL, conn)) + exit(FATAL_DEFAULT); + + if (conn->set.driver == NULL) { + i_fatal("sql: driver not set in configuration file %s", + config_path); + } + if (conn->set.connect == NULL) { + i_fatal("sql: connect string not set in configuration file %s", + config_path); + } + + conn->next = connections; + connections = conn; + return conn; +} + +void db_sql_connect(struct sql_connection *conn) +{ + if (conn->db == NULL) + conn->db = sql_init(conn->set.driver, conn->set.connect); +} + +void db_sql_unref(struct sql_connection *conn) +{ + if (--conn->refcount > 0) + return; + + sql_deinit(conn->db); + pool_unref(conn->pool); +} + +#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/db-sql.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/db-sql.h Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,30 @@ +#ifndef __DB_SQL_H +#define __DB_SQL_H + +#include "sql-api.h" + +struct sql_settings { + const char *driver; + const char *connect; + const char *password_query; + const char *user_query; + const char *default_pass_scheme; +}; + +struct sql_connection { + struct sql_connection *next; + + pool_t pool; + int refcount; + + char *config_path; + struct sql_settings set; + struct sql_db *db; +}; + +struct sql_connection *db_sql_init(const char *config_path); +void db_sql_unref(struct sql_connection *conn); + +void db_sql_connect(struct sql_connection *conn); + +#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/passdb-mysql.c --- a/src/auth/passdb-mysql.c Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,184 +0,0 @@ -/* 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 -#include - -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", - get_log_prefix(auth_request)); - } - } else if (mysql_num_rows(res) > 1) { - i_error("mysql(%s): Multiple matches for user", - get_log_prefix(auth_request)); - } else if (mysql_num_fields(res) != 1) { - i_error("mysql(%s): Password query returned " - "more than one field", - get_log_prefix(auth_request)); - } 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", - get_log_prefix(auth_request), scheme); - } else if (ret == 0) { - if (verbose) { - i_info("mysql(%s): Password mismatch", - get_log_prefix(auth_request)); - } - } - - 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, - auth_request_get_var_expand_table(auth_request, - str_escape)); - 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_preinit(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_init(const char *args __attr_unused__) -{ - (void)db_mysql_connect(passdb_mysql_conn->conn); -} - -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_preinit, - passdb_mysql_init, - passdb_mysql_deinit, - - mysql_verify_plain, - mysql_lookup_credentials -}; - -#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/passdb-pgsql.c --- a/src/auth/passdb-pgsql.c Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ - -#include "config.h" -#undef HAVE_CONFIG_H - -#ifdef PASSDB_PGSQL - -#include "common.h" -#include "str.h" -#include "strescape.h" -#include "var-expand.h" -#include "password-scheme.h" -#include "db-pgsql.h" -#include "passdb.h" - -#include -#include -#include - -struct passdb_pgsql_connection { - struct pgsql_connection *conn; -}; - -struct passdb_pgsql_request { - struct pgsql_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_pgsql_connection *passdb_pgsql_conn; - -static void pgsql_handle_request(struct pgsql_connection *conn, - struct pgsql_request *request, PGresult *res) -{ - struct passdb_pgsql_request *pgsql_request = - (struct passdb_pgsql_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 (PQntuples(res) == 0) { - if (verbose) { - i_info("pgsql(%s): Unknown user", - get_log_prefix(auth_request)); - } - } else if (PQntuples(res) > 1) { - i_error("pgsql(%s): Multiple matches for user", - get_log_prefix(auth_request)); - } else if (PQnfields(res) != 1) { - i_error("pgsql(%s): Password query returned " - "more than one field", - get_log_prefix(auth_request)); - } else { - password = t_strdup(PQgetvalue(res, 0, 0)); - } - } - - scheme = password_get_scheme(&password); - if (scheme == NULL) { - scheme = conn->set.default_pass_scheme; - i_assert(scheme != NULL); - } - - if (pgsql_request->credentials != -1) { - passdb_handle_credentials(pgsql_request->credentials, - user, password, scheme, - pgsql_request->callback.lookup_credentials, - auth_request); - return; - } - - /* verify plain */ - if (password == NULL) { - pgsql_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN, - auth_request); - return; - } - - ret = password_verify(pgsql_request->password, password, - scheme, user); - if (ret < 0) { - i_error("pgsql(%s): Unknown password scheme %s", - get_log_prefix(auth_request), scheme); - } else if (ret == 0) { - if (verbose) { - i_info("pgsql(%s): Password mismatch", - get_log_prefix(auth_request)); - } - } - - pgsql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK : - PASSDB_RESULT_PASSWORD_MISMATCH, - auth_request); -} - -static void pgsql_lookup_pass(struct auth_request *auth_request, - struct pgsql_request *pgsql_request) -{ - struct pgsql_connection *conn = passdb_pgsql_conn->conn; - const char *query; - string_t *str; - - str = t_str_new(512); - var_expand(str, conn->set.password_query, - auth_request_get_var_expand_table(auth_request, - db_pgsql_escape)); - query = str_c(str); - - pgsql_request->callback = pgsql_handle_request; - pgsql_request->context = auth_request; - - db_pgsql_query(conn, query, pgsql_request); -} - -static void -pgsql_verify_plain(struct auth_request *request, const char *password, - verify_plain_callback_t *callback) -{ - struct passdb_pgsql_request *pgsql_request; - - pgsql_request = i_malloc(sizeof(struct passdb_pgsql_request) + - strlen(password)); - pgsql_request->credentials = -1; - pgsql_request->callback.verify_plain = callback; - strcpy(pgsql_request->password, password); - - pgsql_lookup_pass(request, &pgsql_request->request); -} - -static void pgsql_lookup_credentials(struct auth_request *request, - enum passdb_credentials credentials, - lookup_credentials_callback_t *callback) -{ - struct passdb_pgsql_request *pgsql_request; - - pgsql_request = i_new(struct passdb_pgsql_request, 1); - pgsql_request->credentials = credentials; - pgsql_request->callback.lookup_credentials = callback; - - pgsql_lookup_pass(request, &pgsql_request->request); -} - -static void passdb_pgsql_preinit(const char *args) -{ - struct pgsql_connection *conn; - - passdb_pgsql_conn = i_new(struct passdb_pgsql_connection, 1); - passdb_pgsql_conn->conn = conn = db_pgsql_init(args); -} - -static void passdb_pgsql_init(const char *args __attr_unused__) -{ - (void)db_pgsql_connect(passdb_pgsql_conn->conn); -} - -static void passdb_pgsql_deinit(void) -{ - db_pgsql_unref(passdb_pgsql_conn->conn); - i_free(passdb_pgsql_conn); -} - -struct passdb_module passdb_pgsql = { - passdb_pgsql_preinit, - passdb_pgsql_init, - passdb_pgsql_deinit, - - pgsql_verify_plain, - pgsql_lookup_credentials -}; - -#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/passdb-sql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/passdb-sql.c Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,173 @@ +/* Copyright (C) 2004 Timo Sirainen, Alex Howansky */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#ifdef PASSDB_SQL + +#include "common.h" +#include "str.h" +#include "strescape.h" +#include "var-expand.h" +#include "password-scheme.h" +#include "db-sql.h" +#include "passdb.h" + +#include +#include + +struct passdb_sql_request { + struct auth_request *auth_request; + enum passdb_credentials credentials; + union { + verify_plain_callback_t *verify_plain; + lookup_credentials_callback_t *lookup_credentials; + } callback; + + char password[1]; +}; + +static struct sql_connection *passdb_sql_conn; + +static void sql_query_callback(struct sql_result *result, void *context) +{ + struct passdb_sql_request *sql_request = context; + struct auth_request *auth_request = sql_request->auth_request; + const char *user, *password, *scheme; + int ret; + + user = auth_request->user; + password = NULL; + + ret = sql_result_next_row(result); + if (ret < 0) { + i_error("sql(%s): Password query failed: %s", + get_log_prefix(auth_request), + sql_result_get_error(result)); + } else if (ret == 0) { + if (verbose) { + i_info("sql(%s): Unknown user", + get_log_prefix(auth_request)); + } + } else { + password = sql_result_find_field_value(result, "password"); + if (password != NULL) + password = t_strdup(password); + else { + i_error("sql(%s): Password query didn't return " + "password, or it was NULL", + get_log_prefix(auth_request)); + } + + /* make sure there was only one row returned */ + if (sql_result_next_row(result) > 0) { + i_error("sql(%s): Password query returned multiple " + "matches", get_log_prefix(auth_request)); + password = NULL; + } + } + + scheme = password_get_scheme(&password); + if (scheme == NULL) { + scheme = passdb_sql_conn->set.default_pass_scheme; + i_assert(scheme != NULL); + } + + if (sql_request->credentials != -1) { + passdb_handle_credentials(sql_request->credentials, + user, password, scheme, + sql_request->callback.lookup_credentials, + auth_request); + return; + } + + /* verify plain */ + if (password == NULL) { + sql_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN, + auth_request); + return; + } + + ret = password_verify(sql_request->password, password, scheme, user); + if (ret < 0) { + i_error("sql(%s): Unknown password scheme %s", + get_log_prefix(auth_request), scheme); + } else if (ret == 0) { + if (verbose) { + i_info("sql(%s): Password mismatch", + get_log_prefix(auth_request)); + } + } + + sql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK : + PASSDB_RESULT_PASSWORD_MISMATCH, + auth_request); +} + +static void sql_lookup_pass(struct passdb_sql_request *sql_request) +{ + string_t *query; + + query = t_str_new(512); + var_expand(query, passdb_sql_conn->set.password_query, + auth_request_get_var_expand_table(sql_request->auth_request, + str_escape)); + + sql_query(passdb_sql_conn->db, str_c(query), + sql_query_callback, sql_request); +} + +static void sql_verify_plain(struct auth_request *request, const char *password, + verify_plain_callback_t *callback) +{ + struct passdb_sql_request *sql_request; + + sql_request = i_malloc(sizeof(struct passdb_sql_request) + + strlen(password)); + sql_request->auth_request = request; + sql_request->credentials = -1; + sql_request->callback.verify_plain = callback; + strcpy(sql_request->password, password); + + sql_lookup_pass(sql_request); +} + +static void sql_lookup_credentials(struct auth_request *request, + enum passdb_credentials credentials, + lookup_credentials_callback_t *callback) +{ + struct passdb_sql_request *sql_request; + + sql_request = i_new(struct passdb_sql_request, 1); + sql_request->auth_request = request; + sql_request->credentials = credentials; + sql_request->callback.lookup_credentials = callback; + + sql_lookup_pass(sql_request); +} + +static void passdb_sql_preinit(const char *args) +{ + passdb_sql_conn = db_sql_init(args); +} + +static void passdb_sql_init(const char *args __attr_unused__) +{ + db_sql_connect(passdb_sql_conn); +} + +static void passdb_sql_deinit(void) +{ + db_sql_unref(passdb_sql_conn); +} + +struct passdb_module passdb_sql = { + passdb_sql_preinit, + passdb_sql_init, + passdb_sql_deinit, + + sql_verify_plain, + sql_lookup_credentials +}; + +#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/passdb.c --- a/src/auth/passdb.c Sat Oct 16 01:56:10 2004 +0300 +++ b/src/auth/passdb.c Sat Oct 16 02:12:51 2004 +0300 @@ -150,13 +150,9 @@ if (strcasecmp(name, "ldap") == 0) passdb = &passdb_ldap; #endif -#ifdef PASSDB_PGSQL - if (strcasecmp(name, "pgsql") == 0) - passdb = &passdb_pgsql; -#endif -#ifdef PASSDB_MYSQL - if (strcasecmp(name, "mysql") == 0) - passdb = &passdb_mysql; +#ifdef PASSDB_SQL + if (strcasecmp(name, "sql") == 0) + passdb = &passdb_sql; #endif #ifdef HAVE_MODULES passdb_module = passdb != NULL ? NULL : auth_module_open(name); diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/passdb.h --- a/src/auth/passdb.h Sat Oct 16 01:56:10 2004 +0300 +++ b/src/auth/passdb.h Sat Oct 16 02:12:51 2004 +0300 @@ -64,8 +64,7 @@ extern struct passdb_module passdb_checkpassword; extern struct passdb_module passdb_vpopmail; extern struct passdb_module passdb_ldap; -extern struct passdb_module passdb_pgsql; -extern struct passdb_module passdb_mysql; +extern struct passdb_module passdb_sql; void passdb_preinit(void); void passdb_init(void); diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/userdb-mysql.c --- a/src/auth/userdb-mysql.c Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +0,0 @@ -/* 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 -#include - -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 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")); - user.gid = atoi(my_get_str(res, row, "gid")); - urequest->userdb_callback(&user, request->context); - } else { - urequest->userdb_callback(NULL, request->context); - } -} - -static void userdb_mysql_lookup(struct auth_request *auth_request, - 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, - auth_request_get_var_expand_table(auth_request, - str_escape)); - query = str_c(str); - - request = i_malloc(sizeof(struct userdb_mysql_request) + - strlen(auth_request->user)); - request->request.callback = mysql_handle_request; - request->request.context = context; - request->userdb_callback = callback; - strcpy(request->username, auth_request->user); - - db_mysql_query(conn, query, &request->request); -} - -static void userdb_mysql_preinit(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_init(const char *args) -{ - (void)db_mysql_connect(userdb_mysql_conn->conn); -} - -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_preinit, - userdb_mysql_init, - userdb_mysql_deinit, - - userdb_mysql_lookup -}; - -#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/userdb-pgsql.c --- a/src/auth/userdb-pgsql.c Sat Oct 16 01:56:10 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -/* Copyright (C) 2003 Alex Howansky, Timo Sirainen */ - -#include "config.h" -#undef HAVE_CONFIG_H - -#ifdef USERDB_PGSQL - -#include "common.h" -#include "str.h" -#include "strescape.h" -#include "var-expand.h" -#include "db-pgsql.h" -#include "userdb.h" - -#include -#include -#include - -struct userdb_pgsql_connection { - struct pgsql_connection *conn; -}; - -struct userdb_pgsql_request { - struct pgsql_request request; - userdb_callback_t *userdb_callback; - - char username[1]; /* variable width */ -}; - -static struct userdb_pgsql_connection *userdb_pgsql_conn; - -static int is_result_valid(PGresult *res) -{ - if (PQresultStatus(res) != PGRES_TUPLES_OK) { - i_error("PGSQL: Query failed"); - return FALSE; - } - - if (PQntuples(res) == 0) { - if (verbose) - i_error("PGSQL: Authenticated user not found"); - return FALSE; - } - - if (PQfnumber(res, "uid") == -1) { - i_error("PGSQL: User query did not return 'uid' field"); - return FALSE; - } - - if (PQfnumber(res, "gid") == -1) { - i_error("PGSQL: User query did not return 'gid' field"); - return FALSE; - } - - return TRUE; -} - -static const char *pg_get_str(PGresult *res, const char *field) -{ - int fieldnum; - - fieldnum = PQfnumber(res, field); - return fieldnum == -1 ? NULL : PQgetvalue(res, 0, fieldnum); -} - -static void pgsql_handle_request(struct pgsql_connection *conn __attr_unused__, - struct pgsql_request *request, PGresult *res) -{ - struct userdb_pgsql_request *urequest = - (struct userdb_pgsql_request *) request; - struct user_data user; - - if (res != NULL && is_result_valid(res)) { - memset(&user, 0, sizeof(user)); - user.virtual_user = urequest->username; - user.system_user = pg_get_str(res, "system_user"); - user.home = pg_get_str(res, "home"); - user.mail = pg_get_str(res, "mail"); - user.uid = atoi(PQgetvalue(res, 0, PQfnumber(res, "uid"))); - user.gid = atoi(PQgetvalue(res, 0, PQfnumber(res, "gid"))); - urequest->userdb_callback(&user, request->context); - } else { - urequest->userdb_callback(NULL, request->context); - } -} - -static void userdb_pgsql_lookup(struct auth_request *auth_request, - userdb_callback_t *callback, void *context) -{ - struct pgsql_connection *conn = userdb_pgsql_conn->conn; - struct userdb_pgsql_request *request; - const char *query; - string_t *str; - - str = t_str_new(512); - var_expand(str, conn->set.user_query, - auth_request_get_var_expand_table(auth_request, - db_pgsql_escape)); - query = str_c(str); - - request = i_malloc(sizeof(struct userdb_pgsql_request) + - strlen(auth_request->user)); - request->request.callback = pgsql_handle_request; - request->request.context = context; - request->userdb_callback = callback; - strcpy(request->username, auth_request->user); - - db_pgsql_query(conn, query, &request->request); -} - -static void userdb_pgsql_preinit(const char *args) -{ - struct pgsql_connection *conn; - - userdb_pgsql_conn = i_new(struct userdb_pgsql_connection, 1); - userdb_pgsql_conn->conn = conn = db_pgsql_init(args); -} - -static void userdb_pgsql_init(const char *args __attr_unused__) -{ - (void)db_pgsql_connect(userdb_pgsql_conn->conn); -} - -static void userdb_pgsql_deinit(void) -{ - db_pgsql_unref(userdb_pgsql_conn->conn); - i_free(userdb_pgsql_conn); -} - -struct userdb_module userdb_pgsql = { - userdb_pgsql_preinit, - userdb_pgsql_init, - userdb_pgsql_deinit, - - userdb_pgsql_lookup -}; - -#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/userdb-sql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/userdb-sql.c Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,116 @@ +/* Copyright (C) 2004 Timo Sirainen, Alex Howansky */ + +#include "config.h" +#undef HAVE_CONFIG_H + +#ifdef USERDB_SQL + +#include "common.h" +#include "str.h" +#include "strescape.h" +#include "var-expand.h" +#include "db-sql.h" +#include "userdb.h" + +#include +#include + +struct userdb_sql_request { + struct auth_request *auth_request; + userdb_callback_t *callback; + void *context; +}; + +static struct sql_connection *userdb_sql_conn; + +static void sql_query_callback(struct sql_result *result, void *context) +{ + struct userdb_sql_request *sql_request = context; + struct auth_request *auth_request = sql_request->auth_request; + struct user_data user; + const char *uid, *gid; + int ret; + + uid = gid = NULL; + ret = sql_result_next_row(result); + if (ret < 0) { + i_error("sql(%s): User query failed: %s", + get_log_prefix(auth_request), + sql_result_get_error(result)); + } else if (ret == 0) { + if (verbose) { + i_error("sql(%s): User not found", + get_log_prefix(auth_request)); + } + } else { + uid = sql_result_find_field_value(result, "uid"); + if (uid == NULL) { + i_error("sql(%s): Password query didn't return uid, " + "or it was NULL", get_log_prefix(auth_request)); + } + gid = sql_result_find_field_value(result, "gid"); + if (gid == NULL) { + i_error("sql(%s): Password query didn't return gid, " + "or it was NULL", get_log_prefix(auth_request)); + } + } + + if (uid == NULL || gid == NULL) + sql_request->callback(NULL, sql_request->context); + else { + memset(&user, 0, sizeof(user)); + user.virtual_user = auth_request->user; + user.system_user = + sql_result_find_field_value(result, "system_user"); + user.home = sql_result_find_field_value(result, "home"); + user.mail = sql_result_find_field_value(result, "mail"); + user.uid = (uid_t)strtoul(uid, NULL, 10); + user.gid = (gid_t)strtoul(gid, NULL, 10); + sql_request->callback(&user, sql_request->context); + } +} + +static void userdb_sql_lookup(struct auth_request *auth_request, + userdb_callback_t *callback, void *context) +{ + struct userdb_sql_request *sql_request; + string_t *query; + + query = t_str_new(512); + var_expand(query, userdb_sql_conn->set.user_query, + auth_request_get_var_expand_table(auth_request, + str_escape)); + + sql_request = i_new(struct userdb_sql_request, 1); + sql_request->callback = callback; + sql_request->context = context; + sql_request->auth_request = auth_request; + + sql_query(userdb_sql_conn->db, str_c(query), + sql_query_callback, sql_request); +} + +static void userdb_sql_preinit(const char *args) +{ + userdb_sql_conn = db_sql_init(args); +} + +static void userdb_sql_init(const char *args __attr_unused__) +{ + db_sql_connect(userdb_sql_conn); +} + +static void userdb_sql_deinit(void) +{ + db_sql_unref(userdb_sql_conn); +} + +struct userdb_module userdb_sql = { + userdb_sql_preinit, + userdb_sql_init, + userdb_sql_deinit, + + userdb_sql_lookup +}; + +#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/userdb.c --- a/src/auth/userdb.c Sat Oct 16 01:56:10 2004 +0300 +++ b/src/auth/userdb.c Sat Oct 16 02:12:51 2004 +0300 @@ -52,13 +52,9 @@ if (strcasecmp(name, "ldap") == 0) userdb = &userdb_ldap; #endif -#ifdef USERDB_PGSQL - if (strcasecmp(name, "pgsql") == 0) - userdb = &userdb_pgsql; -#endif -#ifdef USERDB_MYSQL - if (strcasecmp(name, "mysql") == 0) - userdb = &userdb_mysql; +#ifdef USERDB_SQL + if (strcasecmp(name, "sql") == 0) + userdb = &userdb_sql; #endif #ifdef HAVE_MODULES userdb_module = userdb != NULL ? NULL : auth_module_open(name); diff -r cc35dc9b9cdf -r 3c3ac12be307 src/auth/userdb.h --- a/src/auth/userdb.h Sat Oct 16 01:56:10 2004 +0300 +++ b/src/auth/userdb.h Sat Oct 16 02:12:51 2004 +0300 @@ -31,8 +31,7 @@ extern struct userdb_module userdb_passwd_file; extern struct userdb_module userdb_vpopmail; extern struct userdb_module userdb_ldap; -extern struct userdb_module userdb_pgsql; -extern struct userdb_module userdb_mysql; +extern struct userdb_module userdb_sql; void userdb_preinit(void); void userdb_init(void); diff -r cc35dc9b9cdf -r 3c3ac12be307 src/lib-sql/.cvsignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-sql/.cvsignore Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,8 @@ +*.la +*.lo +*.o +.deps +.libs +Makefile +Makefile.in +so_locations diff -r cc35dc9b9cdf -r 3c3ac12be307 src/lib-sql/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-sql/Makefile.am Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,14 @@ +noinst_LIBRARIES = libsql.a + +INCLUDES = \ + -I$(top_srcdir)/src/lib \ + $(SQL_CFLAGS) + +libsql_a_SOURCES = \ + driver-mysql.c \ + driver-pgsql.c \ + sql-api.c + +noinst_HEADERS = \ + sql-api.h \ + sql-api-private.h diff -r cc35dc9b9cdf -r 3c3ac12be307 src/lib-sql/driver-mysql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-sql/driver-mysql.c Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,354 @@ +/* Copyright (C) 2003-2004 Timo Sirainen, Alex Howansky */ + +#include "lib.h" +#include "sql-api-private.h" + +#ifdef HAVE_MYSQL +#include +#include +#include +#include + +struct mysql_db { + struct sql_db api; + + pool_t pool; + const char *host, *user, *password, *dbname, *unix_socket; + const char *ssl_cert, *ssl_key, *ssl_ca, *ssl_ca_path, *ssl_cipher; + unsigned int port, client_flags; + MYSQL *mysql; + + time_t last_connect; + + unsigned int connected:1; + unsigned int ssl:1; +}; + +struct mysql_result { + struct sql_result api; + MYSQL_RES *result; + MYSQL_ROW row; + + MYSQL_FIELD *fields; + unsigned int fields_count; +}; + +extern struct sql_result driver_mysql_result; +extern struct sql_result driver_mysql_error_result; + +static int driver_mysql_connect(struct mysql_db *db) +{ + const char *unix_socket, *host; + time_t now; + + if (db->connected) + return TRUE; + + /* don't try reconnecting more than once a second */ + now = time(NULL); + if (db->last_connect == now) + return FALSE; + db->last_connect = now; + + if (*db->host == '/') { + unix_socket = db->host; + host = NULL; + } else { + unix_socket = NULL; + host = db->host; + } + + if (mysql_real_connect(db->mysql, host, db->user, db->password, + db->dbname, db->port, unix_socket, + db->client_flags) == NULL) { + i_error("mysql: Connect failed to %s: %s", + db->dbname, mysql_error(db->mysql)); + return FALSE; + } else { + i_info("mysql: Connected to %s%s", db->dbname, + db->ssl ? " using SSL" : ""); + db->connected = TRUE; + return TRUE; + } +} + +static void driver_mysql_parse_connect_string(struct mysql_db *db, + const char *connect_string) +{ + const char *const *args, *name, *value; + const char **field; + + db->ssl_cipher = "HIGH"; + + t_push(); + args = t_strsplit_spaces(connect_string, " "); + for (; *args != NULL; args++) { + value = strchr(*args, '='); + if (value == NULL) { + i_fatal("mysql: Missing value in connect string: %s", + *args); + } + name = t_strdup_until(*args, value); + value++; + + field = NULL; + if (strcmp(name, "host") == 0 || strcmp(name, "hostaddr") == 0) + field = &db->host; + else if (strcmp(name, "user") == 0) + field = &db->user; + else if (strcmp(name, "password") == 0) + field = &db->password; + else if (strcmp(name, "dbname") == 0) + field = &db->dbname; + else if (strcmp(name, "port") == 0) + db->port = atoi(value); + else if (strcmp(name, "client_flags") == 0) + db->client_flags = atoi(value); + else if (strcmp(name, "ssl_cert") == 0) + field = &db->ssl_cert; + else if (strcmp(name, "ssl_key") == 0) + field = &db->ssl_key; + else if (strcmp(name, "ssl_ca") == 0) + field = &db->ssl_ca; + else if (strcmp(name, "ssl_ca_path") == 0) + field = &db->ssl_ca_path; + else if (strcmp(name, "ssl_cipher") == 0) + field = &db->ssl_cipher; + else + i_fatal("mysql: Unknown connect string: %s", name); + + if (field != NULL) + *field = p_strdup(db->pool, value); + } + t_pop(); +} + +static struct sql_db *driver_mysql_init(const char *connect_string) +{ + struct mysql_db *db; + pool_t pool; + + pool = pool_alloconly_create("mysql driver", 256); + + db = p_new(pool, struct mysql_db, 1); + db->pool = pool; + db->api = driver_mysql_db; + db->mysql = mysql_init(NULL); + if (db->mysql == NULL) + i_fatal("mysql_init() failed"); + + driver_mysql_parse_connect_string(db, connect_string); + if (db->ssl_ca != NULL || db->ssl_ca_path != NULL) { +#ifdef HAVE_MYSQL_SSL + mysql_ssl_set(db->mysql, db->ssl_key, db->ssl_cert, + db->ssl_ca, db->ssl_ca_path +#ifdef HAVE_MYSQL_SSL_CIPHER + , db->ssl_cipher +#endif + ); + db->ssl = TRUE; +#else + i_fatal("mysql: SSL support not compiled in " + "(remove ssl_ca and ssl_ca_path settings)"); +#endif + } + (void)driver_mysql_connect(db); + return &db->api; +} + +static void driver_mysql_deinit(struct sql_db *_db) +{ + struct mysql_db *db = (struct mysql_db *)_db; + + mysql_close(db->mysql); + pool_unref(db->pool); +} + +static int driver_mysql_do_query(struct mysql_db *db, const char *query) +{ + int i; + + for (i = 0; i < 2; i++) { + if (!driver_mysql_connect(db)) + return 0; + + if (mysql_query(db->mysql, query) == 0) + return 1; + + /* failed */ + switch (mysql_errno(db->mysql)) { + case CR_SERVER_GONE_ERROR: + case CR_SERVER_LOST: + /* connection lost - try immediate reconnect */ + db->connected = FALSE; + break; + default: + return -1; + } + } + + /* connected -> lost it -> connected -> lost again */ + return 0; +} + +static void driver_mysql_exec(struct sql_db *_db, const char *query) +{ + struct mysql_db *db = (struct mysql_db *)_db; + + (void)driver_mysql_do_query(db, query); +} + +static void driver_mysql_query(struct sql_db *_db, const char *query, + sql_query_callback_t *callback, void *context) +{ + struct mysql_db *db = (struct mysql_db *)_db; + struct sql_result error_result; + struct mysql_result result; + + switch (driver_mysql_do_query(db, query)) { + case 0: + /* not connected */ + callback(&sql_not_connected_result, context); + return; + + case 1: + /* query ok */ + memset(&result, 0, sizeof(result)); + result.api = driver_mysql_result; + result.result = mysql_store_result(db->mysql); + if (result.result == NULL) + break; + + callback(&result.api, context); + return; + case -1: + /* error */ + break; + } + + /* error */ + error_result = driver_mysql_error_result; + error_result.db = _db; + callback(&error_result, context); +} + +static int driver_mysql_result_next_row(struct sql_result *_result) +{ + struct mysql_result *result = (struct mysql_result *)_result; + + result->row = mysql_fetch_row(result->result); + return result->row != NULL; +} + +static void driver_mysql_result_fetch_fields(struct mysql_result *result) +{ + if (result->fields != NULL) + return; + + result->fields_count = mysql_num_fields(result->result); + result->fields = mysql_fetch_fields(result->result); +} + +static unsigned int +driver_mysql_result_get_fields_count(struct sql_result *_result) +{ + struct mysql_result *result = (struct mysql_result *)_result; + + driver_mysql_result_fetch_fields(result); + return result->fields_count; +} + +static const char * +driver_mysql_result_get_field_name(struct sql_result *_result, unsigned int idx) +{ + struct mysql_result *result = (struct mysql_result *)_result; + + driver_mysql_result_fetch_fields(result); + i_assert(idx < result->fields_count); + return result->fields[idx].name; +} + +static int driver_mysql_result_find_field(struct sql_result *_result, + const char *field_name) +{ + struct mysql_result *result = (struct mysql_result *)_result; + unsigned int i; + + driver_mysql_result_fetch_fields(result); + for (i = 0; i < result->fields_count; i++) { + if (strcmp(result->fields[i].name, field_name) == 0) + return i; + } + return -1; +} + +static const char * +driver_mysql_result_get_field_value(struct sql_result *_result, + unsigned int idx) +{ + struct mysql_result *result = (struct mysql_result *)_result; + + return (const char *)result->row[idx]; +} + +static const char * +driver_mysql_result_find_field_value(struct sql_result *result, + const char *field_name) +{ + int idx; + + idx = driver_mysql_result_find_field(result, field_name); + if (idx < 0) + return NULL; + return driver_mysql_result_get_field_value(result, idx); +} + +static const char *const * +driver_mysql_result_get_values(struct sql_result *_result) +{ + struct mysql_result *result = (struct mysql_result *)_result; + + return (const char *const *)result->row; +} + +static const char *driver_mysql_result_get_error(struct sql_result *result) +{ + struct mysql_db *db = (struct mysql_db *)result->db; + + return mysql_error(db->mysql); +} + +struct sql_db driver_mysql_db = { + driver_mysql_init, + driver_mysql_deinit, + driver_mysql_exec, + driver_mysql_query +}; + +struct sql_result driver_mysql_result = { + NULL, + + driver_mysql_result_next_row, + driver_mysql_result_get_fields_count, + driver_mysql_result_get_field_name, + driver_mysql_result_find_field, + driver_mysql_result_get_field_value, + driver_mysql_result_find_field_value, + driver_mysql_result_get_values, + driver_mysql_result_get_error +}; + +static int +driver_mysql_result_error_next_row(struct sql_result *result __attr_unused__) +{ + return -1; +} + +struct sql_result driver_mysql_error_result = { + NULL, + + driver_mysql_result_error_next_row, + NULL, NULL, NULL, NULL, NULL, NULL, + driver_mysql_result_get_error +}; +#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/lib-sql/driver-pgsql.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-sql/driver-pgsql.c Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,572 @@ +/* Copyright (C) 2004 Timo Sirainen */ + +#include "lib.h" +#include "ioloop.h" +#include "sql-api-private.h" + +#ifdef HAVE_PGSQL +#include +#include +#include + +struct pgsql_db { + struct sql_db api; + + pool_t pool; + const char *connect_string; + PGconn *pg; + + struct io *io; + enum io_condition io_dir; + + struct pgsql_queue *queue, **queue_tail; + struct timeout *queue_to; + + time_t last_connect; + unsigned int connecting:1; + unsigned int connected:1; + unsigned int querying:1; +}; + +struct pgsql_result { + struct sql_result api; + PGresult *pgres; + + unsigned int rownum, rows; + unsigned int fields_count; + const char **fields; + const char **values; + + sql_query_callback_t *callback; + void *context; +}; + +struct pgsql_queue { + struct pgsql_queue *next; + + time_t created; + char *query; + struct pgsql_result *result; +}; + +extern struct sql_result driver_pgsql_result; + +static void queue_send_next(struct pgsql_db *db); + +static void driver_pgsql_close(struct pgsql_db *db) +{ + if (db->io != NULL) { + io_remove(db->io); + db->io = NULL; + } + db->io_dir = 0; + + PQfinish(db->pg); + db->pg = NULL; + + db->connecting = FALSE; + db->connected = FALSE; + db->querying = FALSE; +} + +static const char *last_error(struct pgsql_db *db) +{ + const char *msg; + size_t len; + + msg = PQerrorMessage(db->pg); + if (msg == NULL) + return "(no error set)"; + + /* Error message should contain trailing \n, we don't want it */ + len = strlen(msg); + return len == 0 || msg[len-1] != '\n' ? msg : + t_strndup(msg, len-1); +} + +static void connect_callback(void *context) +{ + struct pgsql_db *db = context; + enum io_condition io_dir = 0; + int ret; + + while ((ret = PQconnectPoll(db->pg)) == PGRES_POLLING_ACTIVE) + ; + + switch (ret) { + case PGRES_POLLING_READING: + io_dir = IO_READ; + break; + case PGRES_POLLING_WRITING: + io_dir = IO_WRITE; + break; + case PGRES_POLLING_OK: + i_info("pgsql: Connected to %s", PQdb(db->pg)); + db->connecting = FALSE; + db->connected = TRUE; + break; + case PGRES_POLLING_FAILED: + i_error("pgsql: Connect failed to %s: %s", + PQdb(db->pg), last_error(db)); + driver_pgsql_close(db); + return; + } + + if (db->io_dir != io_dir) { + if (db->io != NULL) + io_remove(db->io); + db->io = io_dir == 0 ? NULL : + io_add(PQsocket(db->pg), io_dir, connect_callback, db); + db->io_dir = io_dir; + } +} + +static void driver_pgsql_connect(struct pgsql_db *db) +{ + time_t now; + + /* don't try reconnecting more than once a second */ + now = time(NULL); + if (db->connecting || db->last_connect == now) + return; + db->last_connect = now; + + db->pg = PQconnectStart(db->connect_string); + if (db->pg == NULL) + i_fatal("pgsql: PQconnectStart() failed (out of memory)"); + + if (PQstatus(db->pg) == CONNECTION_BAD) { + i_error("pgsql: Connect failed to %s: %s", + PQdb(db->pg), last_error(db)); + driver_pgsql_close(db); + } else { + /* nonblocking connecting begins. */ + db->io = io_add(PQsocket(db->pg), IO_WRITE, + connect_callback, db); + db->io_dir = IO_WRITE; + db->connecting = TRUE; + } +} + +static struct sql_db *driver_pgsql_init(const char *connect_string) +{ + struct pgsql_db *db; + + db = i_new(struct pgsql_db, 1); + db->connect_string = i_strdup(connect_string); + db->api = driver_pgsql_db; + db->queue_tail = &db->queue; + + (void)driver_pgsql_connect(db); + return &db->api; +} + +static void driver_pgsql_deinit(struct sql_db *_db) +{ + struct pgsql_db *db = (struct pgsql_db *)_db; + + driver_pgsql_close(db); + i_free(db); +} + +static void consume_results(void *context) +{ + struct pgsql_db *db = context; + + do { + if (!PQconsumeInput(db->pg)) { + db->connected = FALSE; + break; + + } + if (PQisBusy(db->pg)) + return; + + } while (PQgetResult(db->pg) != NULL); + + io_remove(db->io); + db->io = NULL; + + db->querying = FALSE; + if (db->queue != NULL && db->connected) + queue_send_next(db); +} + +static void result_finish(struct pgsql_result *result) +{ + struct pgsql_db *db = (struct pgsql_db *)result->api.db; + + if (result->callback != NULL) + result->callback(&result->api, result->context); + if (result->pgres != NULL) { + PQclear(result->pgres); + + /* we'll have to read the rest of the results as well */ + i_assert(db->io == NULL); + db->io = io_add(PQsocket(db->pg), IO_READ, + consume_results, db); + consume_results(db); + } else { + db->querying = FALSE; + } + + i_free(result->fields); + i_free(result->values); + i_free(result); + + if (db->queue != NULL && !db->querying && db->connected) + queue_send_next(db); +} + +static void get_result(void *context) +{ + struct pgsql_result *result = context; + struct pgsql_db *db = (struct pgsql_db *)result->api.db; + + if (!PQconsumeInput(db->pg)) { + db->connected = FALSE; + result_finish(result); + return; + } + + if (PQisBusy(db->pg)) { + if (db->io == NULL) { + db->io = io_add(PQsocket(db->pg), IO_READ, + get_result, result); + } + return; + } + + if (db->io != NULL) { + io_remove(db->io); + db->io = NULL; + } + + result->pgres = PQgetResult(db->pg); + result_finish(result); +} + +static void flush_callback(void *context) +{ + struct pgsql_result *result = context; + struct pgsql_db *db = (struct pgsql_db *)result->api.db; + int ret; + + ret = PQflush(db->pg); + if (ret > 0) + return; + + io_remove(db->io); + db->io = NULL; + + if (ret < 0) { + db->connected = FALSE; + result_finish(result); + } else { + /* all flushed */ + get_result(result); + } +} + +static void send_query(struct pgsql_result *result, const char *query) +{ + struct pgsql_db *db = (struct pgsql_db *)result->api.db; + int ret; + + i_assert(db->io == NULL); + i_assert(!db->querying); + i_assert(db->connected); + + if (!PQsendQuery(db->pg, query)) { + db->connected = FALSE; + result_finish(result); + return; + } + + ret = PQflush(db->pg); + if (ret < 0) { + db->connected = FALSE; + result_finish(result); + return; + } + + db->querying = TRUE; + if (ret > 0) { + /* write blocks */ + db->io = io_add(PQsocket(db->pg), IO_WRITE, + flush_callback, result); + } else { + get_result(result); + } +} + +static void queue_send_next(struct pgsql_db *db) +{ + struct pgsql_queue *queue; + + queue = db->queue; + db->queue = queue->next; + + send_query(queue->result, queue->query); + + i_free(queue->query); + i_free(queue); +} + +static void queue_timeout(void *context) +{ + struct pgsql_db *db = context; + + if (db->querying) + return; + + if (!db->connected) { + driver_pgsql_connect(db); + return; + } + + if (db->queue != NULL) + queue_send_next(db); + + if (db->queue == NULL) { + timeout_remove(db->queue_to); + db->queue_to = NULL; + } +} + +static void +driver_pgsql_queue_query(struct pgsql_result *result, const char *query) +{ + struct pgsql_db *db = (struct pgsql_db *)result->api.db; + struct pgsql_queue *queue; + + queue = i_new(struct pgsql_queue, 1); + queue->created = time(NULL); + queue->query = i_strdup(query); + queue->result = result; + + *db->queue_tail = queue; + + if (db->queue_to == NULL) + db->queue_to = timeout_add(5000, queue_timeout, db); +} + +static void do_query(struct pgsql_result *result, const char *query) +{ + struct pgsql_db *db = (struct pgsql_db *)result->api.db; + + if (db->querying) { + /* only one query at a time */ + driver_pgsql_queue_query(result, query); + return; + } + + if (!db->connected) { + /* try connecting again */ + driver_pgsql_connect(db); + driver_pgsql_queue_query(result, query); + return; + } + + if (db->queue == NULL) + send_query(result, query); + else { + /* there's already queries queued, send them first */ + driver_pgsql_queue_query(result, query); + queue_send_next(db); + } +} + +static void exec_callback(struct sql_result *result, + void *context __attr_unused__) +{ + struct pgsql_db *db = (struct pgsql_db *)result->db; + + i_error("pgsql: sql_exec() failed: %s", last_error(db)); +} + +static void driver_pgsql_exec(struct sql_db *db, const char *query) +{ + struct pgsql_result *result; + + result = i_new(struct pgsql_result, 1); + result->api = driver_pgsql_result; + result->api.db = db; + result->callback = exec_callback; + + do_query(result, query); +} + +static void driver_pgsql_query(struct sql_db *db, const char *query, + sql_query_callback_t *callback, void *context) +{ + struct pgsql_result *result; + + result = i_new(struct pgsql_result, 1); + result->api = driver_pgsql_result; + result->api.db = db; + result->callback = callback; + result->context = context; + + do_query(result, query); +} + +static int driver_pgsql_result_next_row(struct sql_result *_result) +{ + struct pgsql_result *result = (struct pgsql_result *)_result; + struct pgsql_db *db = (struct pgsql_db *)_result->db; + + if (result->rows != 0) { + /* second time we're here */ + return ++result->rownum < result->rows; + } + + if (result->pgres == NULL) + return -1; + + switch (PQresultStatus(result->pgres)) { + case PGRES_COMMAND_OK: + /* no rows returned */ + return 0; + case PGRES_TUPLES_OK: + result->rows = PQntuples(result->pgres); + return result->rows > 0; + case PGRES_EMPTY_QUERY: + case PGRES_NONFATAL_ERROR: + /* nonfatal error */ + return -1; + default: + /* treat as fatal error */ + db->connected = FALSE; + return -1; + } +} + +static void driver_pgsql_result_fetch_fields(struct pgsql_result *result) +{ + unsigned int i; + + if (result->fields != NULL) + return; + + /* @UNSAFE */ + result->fields_count = PQnfields(result->pgres); + result->fields = i_new(const char *, result->fields_count); + for (i = 0; i < result->fields_count; i++) + result->fields[i] = PQfname(result->pgres, i); +} + +static unsigned int +driver_pgsql_result_get_fields_count(struct sql_result *_result) +{ + struct pgsql_result *result = (struct pgsql_result *)_result; + + driver_pgsql_result_fetch_fields(result); + return result->fields_count; +} + +static const char * +driver_pgsql_result_get_field_name(struct sql_result *_result, unsigned int idx) +{ + struct pgsql_result *result = (struct pgsql_result *)_result; + + driver_pgsql_result_fetch_fields(result); + i_assert(idx < result->fields_count); + return result->fields[idx]; +} + +static int driver_pgsql_result_find_field(struct sql_result *_result, + const char *field_name) +{ + struct pgsql_result *result = (struct pgsql_result *)_result; + unsigned int i; + + driver_pgsql_result_fetch_fields(result); + for (i = 0; i < result->fields_count; i++) { + if (strcmp(result->fields[i], field_name) == 0) + return i; + } + return -1; +} + +static const char * +driver_pgsql_result_get_field_value(struct sql_result *_result, + unsigned int idx) +{ + struct pgsql_result *result = (struct pgsql_result *)_result; + + if (PQgetisnull(result->pgres, result->rownum, idx)) + return NULL; + + return PQgetvalue(result->pgres, result->rownum, idx); +} + +static const char * +driver_pgsql_result_find_field_value(struct sql_result *result, + const char *field_name) +{ + int idx; + + idx = driver_pgsql_result_find_field(result, field_name); + if (idx < 0) + return NULL; + return driver_pgsql_result_get_field_value(result, idx); +} + +static const char *const * +driver_pgsql_result_get_values(struct sql_result *_result) +{ + struct pgsql_result *result = (struct pgsql_result *)_result; + unsigned int i; + + if (result->values == NULL) { + driver_pgsql_result_fetch_fields(result); + result->values = i_new(const char *, result->fields_count); + } + + /* @UNSAFE */ + for (i = 0; i < result->fields_count; i++) { + result->values[i] = + driver_pgsql_result_get_field_value(_result, i); + } + + return result->values; +} + +static const char *driver_pgsql_result_get_error(struct sql_result *_result) +{ + struct pgsql_result *result = (struct pgsql_result *)_result; + const char *msg; + size_t len; + + msg = PQresultErrorMessage(result->pgres); + if (msg == NULL) + return "(no error set)"; + + /* Error message should contain trailing \n, we don't want it */ + len = strlen(msg); + return len == 0 || msg[len-1] != '\n' ? msg : + t_strndup(msg, len-1); +} + +struct sql_db driver_pgsql_db = { + driver_pgsql_init, + driver_pgsql_deinit, + driver_pgsql_exec, + driver_pgsql_query +}; + +struct sql_result driver_pgsql_result = { + NULL, + + driver_pgsql_result_next_row, + driver_pgsql_result_get_fields_count, + driver_pgsql_result_get_field_name, + driver_pgsql_result_find_field, + driver_pgsql_result_get_field_value, + driver_pgsql_result_find_field_value, + driver_pgsql_result_get_values, + driver_pgsql_result_get_error +}; + +#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/lib-sql/sql-api-private.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-sql/sql-api-private.h Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,39 @@ +#ifndef __SQL_API_PRIVATE_H +#define __SQL_API_PRIVATE_H + +#include "sql-api.h" + +struct sql_db { + struct sql_db *(*init)(const char *connect_string); + void (*deinit)(struct sql_db *db); + + void (*exec)(struct sql_db *db, const char *query); + void (*query)(struct sql_db *db, const char *query, + sql_query_callback_t *callback, void *context); +}; + +struct sql_result { + struct sql_db *db; + + int (*next_row)(struct sql_result *result); + + unsigned int (*get_fields_count)(struct sql_result *result); + const char *(*get_field_name)(struct sql_result *result, + unsigned int idx); + int (*find_field)(struct sql_result *result, const char *field_name); + + const char *(*get_field_value)(struct sql_result *result, + unsigned int idx); + const char *(*find_field_value)(struct sql_result *result, + const char *field_name); + const char *const *(*get_values)(struct sql_result *result); + + const char *(*get_error)(struct sql_result *result); +}; + +extern struct sql_db driver_mysql_db; +extern struct sql_db driver_pgsql_db; + +extern struct sql_result sql_not_connected_result; + +#endif diff -r cc35dc9b9cdf -r 3c3ac12be307 src/lib-sql/sql-api.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-sql/sql-api.c Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,97 @@ +/* Copyright (c) 2004 Timo Sirainen */ + +#include "lib.h" +#include "sql-api-private.h" + +struct sql_db *sql_init(const char *db_driver, const char *connect_string) +{ +#ifdef HAVE_PGSQL + if (strcmp(db_driver, "pgsql") == 0) + return driver_pgsql_db.init(connect_string); +#endif +#ifdef HAVE_MYSQL + if (strcmp(db_driver, "mysql") == 0) + return driver_mysql_db.init(connect_string); +#endif + + i_fatal("Unknown database driver '%s'", db_driver); +} + +void sql_deinit(struct sql_db *db) +{ + db->deinit(db); +} + +void sql_exec(struct sql_db *db, const char *query) +{ + db->exec(db, query); +} + +void sql_query(struct sql_db *db, const char *query, + sql_query_callback_t *callback, void *context) +{ + db->query(db, query, callback, context); +} + +int sql_result_next_row(struct sql_result *result) +{ + return result->next_row(result); +} + +unsigned int sql_result_get_fields_count(struct sql_result *result) +{ + return result->get_fields_count(result); +} + +const char *sql_result_get_field_name(struct sql_result *result, + unsigned int idx) +{ + return result->get_field_name(result, idx); +} + +int sql_result_find_field(struct sql_result *result, const char *field_name) +{ + return result->find_field(result, field_name); +} + +const char *sql_result_get_field_value(struct sql_result *result, + unsigned int idx) +{ + return result->get_field_value(result, idx); +} + +const char *sql_result_find_field_value(struct sql_result *result, + const char *field_name) +{ + return result->find_field_value(result, field_name); +} + +const char *const *sql_result_get_values(struct sql_result *result) +{ + return result->get_values(result); +} + +const char *sql_result_get_error(struct sql_result *result) +{ + return result->get_error(result); +} + +static int +sql_result_not_connected_next_row(struct sql_result *result __attr_unused__) +{ + return -1; +} + +static const char * +sql_result_not_connected_get_error(struct sql_result *result __attr_unused__) +{ + return "Not connected to database"; +} + +struct sql_result sql_not_connected_result = { + NULL, + + sql_result_not_connected_next_row, + NULL, NULL, NULL, NULL, NULL, NULL, + sql_result_not_connected_get_error +}; diff -r cc35dc9b9cdf -r 3c3ac12be307 src/lib-sql/sql-api.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-sql/sql-api.h Sat Oct 16 02:12:51 2004 +0300 @@ -0,0 +1,46 @@ +#ifndef __SQL_API_H +#define __SQL_API_H + +/* This SQL API is designed to work asynchronously. The underlying drivers + however may not. */ + +struct sql_db; +struct sql_result; + +typedef void sql_query_callback_t(struct sql_result *result, void *context); + +/* Initialize database connections. db_driver is the database driver name, + eg. "mysql" or "pgsql". connect_string is driver-specific. */ +struct sql_db *sql_init(const char *db_driver, const char *connect_string); +void sql_deinit(struct sql_db *db); + +/* Execute SQL query without waiting for results. */ +void sql_exec(struct sql_db *db, const char *query); +/* Execute SQL query and return result in callback. */ +void sql_query(struct sql_db *db, const char *query, + sql_query_callback_t *callback, void *context); + +/* Go to next row, returns 1 if ok, 0 if this was the last row or -1 if error + occured. This needs to be the first call for result. */ +int sql_result_next_row(struct sql_result *result); + +/* Return number of fields in result. */ +unsigned int sql_result_get_fields_count(struct sql_result *result); +/* Return name of the given field index. */ +const char *sql_result_get_field_name(struct sql_result *result, + unsigned int idx); +/* Return field index for given name, or -1 if not found. */ +int sql_result_find_field(struct sql_result *result, const char *field_name); + +/* Returns value of given field as string. */ +const char *sql_result_get_field_value(struct sql_result *result, + unsigned int idx); +const char *sql_result_find_field_value(struct sql_result *result, + const char *field_name); +/* Return all values of current row. */ +const char *const *sql_result_get_values(struct sql_result *result); + +/* Return last error message in result. */ +const char *sql_result_get_error(struct sql_result *result); + +#endif