From: Matthew Mondor Date: Thu, 15 Mar 2007 17:01:57 +0000 (+0000) Subject: Converted mmmail to use PostgreSQL + file storage, instead of continueing X-Git-Url: http://git.pulsar-zone.net/?a=commitdiff_plain;h=cc98c06abb6a92a608865229f3d772f14446ee11;p=mmondor.git Converted mmmail to use PostgreSQL + file storage, instead of continueing to support MySQL on this branch, which should have been done long ago when MySQL lacked most useful features. Although MySQL v5+ with InnoDB starts to have more features, MySQL AB made deals with companies such as SCO and Microsoft, and the MySQL dual-license is controversial. Moreover, my audit of the InnoDB code convinced me that it was ugly as well as potentially subject to buffer overflow bugs, and to use a more mature system for production. mmmail was the last application remaining to port to pgsql before I can stop to maintain and run two database servers, so convenience and licensing (I almost only use BSD/MIT-style licensed software) were the main points to convert. New software developed in the last few years targetted postgres with impressive results. People wanting to improve mmmail with support for recent MySQL features can track the mysql-branch (although I'm unlikely to keep maintaining it). --- diff --git a/mmsoftware/mmmail/GNUmakefile b/mmsoftware/mmmail/GNUmakefile index f1e92e9..fd330ce 100644 --- a/mmsoftware/mmmail/GNUmakefile +++ b/mmsoftware/mmmail/GNUmakefile @@ -1,22 +1,21 @@ -# $Id: GNUmakefile,v 1.8 2007/03/13 20:28:22 mmondor Exp $ +# $Id: GNUmakefile,v 1.8.4.1 2007/03/15 17:01:47 mmondor Exp $ -MMLIBS := $(addprefix ../mmlib/,mmfd.o mmhash.o mmlimitrate.o mmsql.o \ -mmlog.o mmpool.o mmreadcfg.o mmserver.o mmstat.o mmstr.o \ -mmstring.o) $(addprefix ../pthread_util/,mm_pthread_msg.o \ -mm_pthread_poll.o mm_pthread_pool.o mm_pthread_sleep.o) +MMLIBS := $(addprefix ../mmlib/,mmfd.o mmhash.o mmlimitrate.o \ + mmlog.o mmpool.o mmreadcfg.o mmserver.o mmstat.o mmstr.o \ + mmstring.o) $(addprefix ../pthread_util/,mm_pthread_msg.o \ + mm_pthread_poll.o mm_pthread_pool.o mm_pthread_sleep.o) -MYSQL_CFLAGS := $(shell mysql_config --cflags) -MYSQL_LDFLAGS := $(shell mysql_config --libs) +PGSQL_CFLAGS := $(shell pg_config --cppflags) +PGSQL_LDFLAGS := $(shell pg_config --ldflags) +PGSQL_LDFLAGS += -lpq -CFLAGS += $(MYSQL_CFLAGS) -I. -I../mmlib -I../pthread_util -LDFLAGS += $(MYSQL_LDFLAGS) -lc -lcrypt -lpthread +CFLAGS += $(PGSQL_CFLAGS) -I. -I../mmlib -I../pthread_util +LDFLAGS += $(PGSQL_LDFLAGS) -lc -lcrypt -lpthread OBJS := src/mmsmtpd/mmsmtpd.o src/mmpop3d/mmpop3d.o src/mmrelayd/mmrelayd.o BINS := src/mmsmtpd/mmsmtpd src/mmpop3d/mmpop3d src/mmrelayd/mmrelayd CFLAGS += -Wall -#CFLAGS += -DMMMAIL_MYSQL -CFLAGS += -DMMMAIL_FILE #CFLAGS += -DNODETACH -DDEBUG -DPTHREAD_DEBUG #LDFLAGS += -lpthread_dbg diff --git a/mmsoftware/mmmail/etc/mmpop3d.conf b/mmsoftware/mmmail/etc/mmpop3d.conf index 2039090..20f7841 100644 --- a/mmsoftware/mmmail/etc/mmpop3d.conf +++ b/mmsoftware/mmmail/etc/mmpop3d.conf @@ -1,4 +1,4 @@ -; $Id: mmpop3d.conf,v 1.4 2003/07/12 02:21:25 mmondor Exp $ +; $Id: mmpop3d.conf,v 1.4.8.1 2007/03/15 17:01:50 mmondor Exp $ ; ; mmpop3d mmmail component configuration file (/etc/mmpop3d.conf) ; and # are considered comments, and can occur at start or end of line. @@ -17,9 +17,16 @@ ASYNC_PROCESSES 3 ; files will be required in the new root for instance. ;CHROOT_DIR "" ; +; Location of lock file used to determine if daemon already runs +LOCK_PATH "/var/run/mmpop3d.lock" +; ; Location of the path where to store our process ID PID_PATH "/var/run/mmpop3d.pid" ; +; Specifies where mail box directories should be created and messages +; stored in them. +MAIL_DIR "/var/mmmail-dir" +; ; User mmpop3d should run as USER "mmmail" ; Groups process should be part of @@ -99,17 +106,8 @@ STATFAIL_LOGIN FALSE STATFAIL_PASSWORD FALSE -; MySQL options -; ------------- -; -; Host name of MySQL server we should connect to (localhost for UNIX socket) -DB_HOST "localhost" -; -; MySQL user which has all access on DB_DATABASE -DB_USER "mmmail" -; -; MySQL authentication password for DB_USER -DB_PASSWORD "mmmailpassword" +; Database options +; ---------------- ; -; Name of mmmail MySQL database DB_USER owns, typically "mmmail" -DB_DATABASE "mmmail" +; PQconnectdb()-fed string +DB_INFO "dbname=mmmail" diff --git a/mmsoftware/mmmail/etc/mmsmtpd.conf b/mmsoftware/mmmail/etc/mmsmtpd.conf index c2227e2..80f37ea 100644 --- a/mmsoftware/mmmail/etc/mmsmtpd.conf +++ b/mmsoftware/mmmail/etc/mmsmtpd.conf @@ -1,4 +1,4 @@ -; $Id: mmsmtpd.conf,v 1.6 2005/03/05 15:33:33 mmondor Exp $ +; $Id: mmsmtpd.conf,v 1.6.8.1 2007/03/15 17:01:50 mmondor Exp $ ; ; mmsmtpd configuration file (/etc/mmsmtpd.conf) ; and # are considered comments, and can happen at start or end of line. @@ -23,10 +23,8 @@ LOCK_PATH "/var/run/mmsmtpd.lock" ; Location of the path where to store our process ID PID_PATH "/var/run/mmsmtpd.pid" ; -; If using MMMAIL_FILE for file storage of message bodies rather than -; MMMAIL_MYSQL, this specifies where mail box directories should be created -; and messages stored in them. This should also work fine using NFS. -; +; Specifies where mail box directories should be created and messages +; stored in them. MAIL_DIR "/var/mmmail-dir" ; ; User mmsmtpd should run as @@ -159,17 +157,8 @@ FLOOD_MESSAGES 20 FLOOD_EXPIRES 30 -; MySQL options -; ------------- -; -; Host name of MySQL server we should connect to (localhost for UNIX socket) -DB_HOST "localhost" -; -; MySQL user which has all access on DB_DATABASE -DB_USER "mmmail" -; -; MySQL authentication password for DB_USER -DB_PASSWORD "mmmailpassword" +; Database options +; ---------------- ; -; Name of mmmail MySQL database DB_USER owns, typically "mmmail" -DB_DATABASE "mmmail" +; PQconnectdb()-fed string +DB_INFO "dbname=mmmail" diff --git a/mmsoftware/mmmail/scripts/pgsql-tables.sql b/mmsoftware/mmmail/scripts/pgsql-tables.sql index eb682e0..106db85 100644 --- a/mmsoftware/mmmail/scripts/pgsql-tables.sql +++ b/mmsoftware/mmmail/scripts/pgsql-tables.sql @@ -1,4 +1,4 @@ ---- $Id: pgsql-tables.sql,v 1.1.2.6 2007/03/15 04:26:55 mmondor Exp $ +--- $Id: pgsql-tables.sql,v 1.1.2.7 2007/03/15 17:01:53 mmondor Exp $ BEGIN; @@ -79,11 +79,11 @@ CREATE TABLE "box" ( "user" varchar(32) NOT NULL REFERENCES "user" (id) ON DELETE CASCADE, - max_size bigint NOT NULL, - size bigint NOT NULL + max_size int NOT NULL, + size int NOT NULL DEFAULT 0, - max_msgs bigint NOT NULL, - msgs bigint NOT NULL + max_msgs int NOT NULL, + msgs int NOT NULL DEFAULT 0, time_created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -140,7 +140,7 @@ CREATE TABLE "mail" ( ON DELETE CASCADE, time_created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - size bigint NOT NULL, + size int NOT NULL, file varchar(255) NOT NULL UNIQUE, PRIMARY KEY (id) ); @@ -204,13 +204,13 @@ CREATE TABLE "relayqueue" ( ipaddr inet NOT NULL, todomain varchar(64) NOT NULL, touser varchar(64) NOT NULL, - size bigint NOT NULL, + size int NOT NULL, file varchar(255) NOT NULL, time_queued timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, time_lasttry timestamp, time_expires timestamp NOT NULL, - tries bigint NOT NULL + tries int NOT NULL DEFAULT 0, lasterror int NOT NULL DEFAULT 0, @@ -243,4 +243,17 @@ CREATE TABLE "session" ( +--- +--- Utility function to update the user's activity timestamps +--- +CREATE FUNCTION user_update(v_user varchar, v_box varchar) RETURNS boolean AS $usager_update$ +BEGIN + UPDATE box SET time_out = CURRENT_TIMESTAMP WHERE address = v_box; + UPDATE "user" SET time_activity = CURRENT_TIMESTAMP WHERE id = v_user; + RETURN TRUE; +END; +$usager_update$ LANGUAGE plpgsql; + + + COMMIT; diff --git a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.c b/mmsoftware/mmmail/src/mmpop3d/mmpop3d.c index 31d0c02..8eb8736 100644 --- a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.c +++ b/mmsoftware/mmmail/src/mmpop3d/mmpop3d.c @@ -1,7 +1,7 @@ -/* $Id: mmpop3d.c,v 1.41.4.1 2007/03/15 04:41:09 mmondor Exp $ */ +/* $Id: mmpop3d.c,v 1.41.4.2 2007/03/15 17:01:55 mmondor Exp $ */ /* - * Copyright (C) 2001-2004, Matthew Mondor + * Copyright (C) 2001-2007, Matthew Mondor * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -69,7 +69,6 @@ #include #include #include -#include #include #include #include @@ -82,9 +81,9 @@ -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ +MMCOPYRIGHT("@(#) Copyright (c) 2001-2007\n\ \tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmpop3d.c,v 1.41.4.1 2007/03/15 04:41:09 mmondor Exp $"); +MMRCSID("$Id: mmpop3d.c,v 1.41.4.2 2007/03/15 17:01:55 mmondor Exp $"); @@ -199,7 +198,6 @@ main(int argc, char **argv) char *conf_file = "/usr/local/etc/mmpop3d.conf"; int ret = -1, ngids; long facility; - char *db_host; bool strlist; cres_t cres; carg_t *cargp; @@ -212,10 +210,8 @@ main(int argc, char **argv) {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, {CAT_STR, CAF_NONE, 1, 1023, "LISTEN_IPS", CONF.LISTEN_IPS}, {CAT_STR, CAF_NONE, 1, 1023, "SERVER_NAMES", CONF.SERVER_NAMES}, - {CAT_STR, CAF_NONE, 1, 63, "DB_HOST", CONF.DB_HOST}, - {CAT_STR, CAF_NONE, 1, 31, "DB_USER", CONF.DB_USER}, - {CAT_STR, CAF_NONE, 1, 31, "DB_PASSWORD", CONF.DB_PASSWORD}, - {CAT_STR, CAF_NONE, 1, 31, "DB_DATABASE", CONF.DB_DATABASE}, + {CAT_STR, CAF_NONE, 1, 1023, "DB_INFO", CONF.DB_INFO}, + {CAT_STR, CAF_NONE, 1, 255, "MAIL_DIR", CONF.MAIL_DIR}, {CAT_VAL, CAF_NONE, 1, 32, "ASYNC_PROCESSES", &CONF.ASYNC_PROCESSES}, {CAT_VAL, CAF_NONE, 1, 9999, "ALLOC_BUFFERS", &CONF.ALLOC_BUFFERS}, {CAT_VAL, CAF_NONE, 0, 4, "LOG_LEVEL", &CONF.LOG_LEVEL}, @@ -257,13 +253,6 @@ main(int argc, char **argv) {async_checkpw, sizeof(struct async_checkpw_msg)}, {NULL, 0} }; - struct mmsql_threadsupport mmsqlfuncs = { - thread_mutex_create, - thread_mutex_destroy, - thread_mutex_lock, - thread_mutex_unlock, - pthread_sleep - }; mmstat_t vstat; /* Set defaults */ @@ -275,10 +264,8 @@ main(int argc, char **argv) mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); mm_strcpy(CONF.LISTEN_IPS, "127.0.0.1"); mm_strcpy(CONF.SERVER_NAMES, "pop3.localhost"); - mm_strcpy(CONF.DB_HOST, "localhost"); - mm_strcpy(CONF.DB_USER, "mmmail"); - mm_strcpy(CONF.DB_PASSWORD, "mmmailpassword"); - mm_strcpy(CONF.DB_DATABASE, "mmmail"); + mm_strcpy(CONF.DB_INFO, "dbname=mmmail"); + mm_strcpy(CONF.MAIL_DIR, "/var/mmmail-dir"); CONF.ASYNC_PROCESSES = 3; CONF.ALLOC_BUFFERS = 1; CONF.LOG_LEVEL = 3; @@ -342,8 +329,6 @@ main(int argc, char **argv) printf("\nOne of following groups unknown: '%s'\n\n", CONF.GROUPS); exit(-1); } - if (!(mm_strcmp(CONF.DB_HOST, "localhost"))) db_host = NULL; - else db_host = CONF.DB_HOST; /* Finally init everything */ openlog(DAEMON_NAME, LOG_PID | LOG_NDELAY, facility); @@ -369,12 +354,6 @@ main(int argc, char **argv) mmstat(&vstat, STAT_DELETE, 0, "mmpop3d|current|connections"); mmstat(&vstat, STAT_DELETE, 0, "mmpop3d|who|*"); mmstat_transact(&vstat, FALSE); - if (!(mmsql_open(db_host, CONF.DB_USER, CONF.DB_PASSWORD, - CONF.DB_DATABASE))) { - printf("\nCould not connect to MySQLd\n\n"); - syslog(LOG_NOTICE, "* Could not connect to MySQLd"); - exit(-1); - } make_daemon(CONF.PID_PATH, CONF.CHROOT_DIR); @@ -386,8 +365,8 @@ main(int argc, char **argv) /* Allocate necessary pools */ /* Client nodes */ - pool_init(&clenv_pool, "clenv_pool", malloc, free, NULL, NULL, - sizeof(clientenv), + pool_init(&clenv_pool, "clenv_pool", malloc, free, clenv_constructor, + clenv_destructor, sizeof(clientenv), (65536 * CONF.ALLOC_BUFFERS) / sizeof(clientenv), 0, 0); /* Mutexes pool for mmfd */ pool_init(&mutexes_pool, "mutexes_pool", malloc, free, NULL, NULL, @@ -401,7 +380,6 @@ main(int argc, char **argv) thread_init(); fdbcinit(&gfdf, &fdbc, CONF.GBANDWIDTH_IN * 1024, CONF.GBANDWIDTH_OUT * 1024); - mmsql_init(&mmsqlfuncs); tcp_server("-ERR Server too busy, try again\r\n", CONF.SERVER_NAMES, CONF.LISTEN_IPS, uid, gids, ngids, @@ -411,8 +389,6 @@ main(int argc, char **argv) mmfreegidarray(gids); ret = 0; - mmsql_close(); - mmsql_exit(); } else { printf("\nOut of memory\n\n"); syslog(LOG_NOTICE, "* Out of memory"); @@ -510,45 +486,32 @@ auth_user(clientenv *clenv) static int auth_pass(clientenv *clenv) { - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - char *cmdline = clenv->buffer, *args[3], line[1024], pwhash[36], id[33]; - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned long *lengths; + int nextstate = STATE_CURRENT; + fdbuf *fdb = clenv->fdb; + char *cmdline = clenv->buffer, *args[3], pwhash[36], id[33]; + PGresult *pgres; + const char *params[3]; if (clenv->mailbox != NULL) { if ((mm_straspl(args, cmdline, 2)) == 2) { /* Check if user and password are ok, * switch state if so, or drop client if not. */ - snprintf(line, 1023, - "SELECT user_id,user_passwd FROM box LEFT JOIN user " - "ON box_user=user_id WHERE box_address='%s' AND " - "user_active=1", - clenv->mailbox); - *id = 0; - *pwhash = 0; - if ((mysqlres = mmsql_query(line, mm_strlen(line))) != NULL) { - if ((mysql_num_rows(mysqlres)) == 1) { - if ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) - != NULL) { - if ((mysql_num_fields(mysqlres)) == 2) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - mm_memcpy(id, row[0], lengths[0]); - id[lengths[0]] = '\0'; - } - if (row[1] != NULL) { - mm_memcpy(pwhash, row[1], lengths[1]); - pwhash[lengths[1]] = '\0'; - } - } - } + *pwhash = *id = '\0'; + params[0] = clenv->mailbox; + params[1] = NULL; + if ((pgres = PQexecParams(clenv->pgconn, + "SELECT \"user\",passwd FROM box LEFT JOIN \"user\" ON " + "\"user\"=id WHERE address='$1' AND active='t'", + 1, NULL, params, NULL, NULL, 0)) != NULL) { + if (PQntuples(pgres) == 1) { + mm_strcpy(id, PQgetvalue(pgres, 0, 0)); + mm_strcpy(pwhash, PQgetvalue(pgres, 0, 1)); } - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("auth_pass", "mmsql_query(%s)", line); + PQclear(pgres); + } else { + /* XXX Log */ + } if (*pwhash == '\0') { /* User does not exist */ mmsyslog(0, LOGLEVEL, "%08X Failed login for %s (unexisting)", @@ -559,22 +522,22 @@ auth_pass(clientenv *clenv) clenv->mailbox = mmstrfree(clenv->mailbox); nextstate = STATE_END; reply(fdb, FALSE, "Authentication failiure"); - } else if(checkpw(clenv, args[1], pwhash)) { + } else if (checkpw(clenv, args[1], pwhash)) { char *domptr; /* Authentication successful, update box and user tables * and build messages index */ - snprintf(line, 1023, - "UPDATE box SET box_out=NOW() WHERE box_address='%s'", - clenv->mailbox); - if (!mmsql_command(line, mm_strlen(line))) - DEBUG_PRINTF("auth_pass", "mmsql_command(%s)", line); - snprintf(line, 1023, - "UPDATE user SET user_activity=NOW()," - "user_logins=user_logins+1 WHERE user_id='%s'", id); - if (!mmsql_command(line, mm_strlen(line))) - DEBUG_PRINTF("auth_pass", "mmsql_command(%s)", line); + params[0] = id; + params[1] = clenv->mailbox; + params[2] = NULL; + if ((pgres = PQexecParams(clenv->pgconn, + "SELECT user_update($1,$2)", 2, + NULL, params, NULL, NULL, 0)) != NULL) + PQclear(pgres); + else { + /* XXX Log */ + } clenv->login = TRUE; if (!do_buildindex(clenv)) { reply(fdb, FALSE, "Error"); @@ -880,6 +843,31 @@ reply(fdbuf *fdb, bool result, const char *fmt, ...) } +static bool +clenv_constructor(pnode_t *pn) +{ + clientenv *clenv = (clientenv *)pn; + + mmstat_init(&clenv->vstat, TRUE, TRUE); + mmstat_init(&clenv->pstat, TRUE, FALSE); + if ((clenv->pgconn = PQconnectdb(CONF.DB_INFO)) == NULL) + return FALSE; + + return TRUE; +} + +static void +clenv_destructor(pnode_t *pn) +{ + clientenv *clenv = (clientenv *)pn; + + if (clenv->pgconn != NULL) { + PQfinish(clenv->pgconn); + clenv->pgconn = NULL; + } +} + + /* Allocate and prepare a clenv. Returns NULL on error */ static clientenv * alloc_clientenv(void) @@ -890,11 +878,6 @@ alloc_clientenv(void) clenv = (clientenv *)pool_alloc(&clenv_pool, TRUE); pthread_mutex_unlock(&clenv_lock); - if (clenv != NULL) { - mmstat_init(&clenv->pstat, TRUE, FALSE); - mmstat_init(&clenv->vstat, TRUE, TRUE); - } - return (clenv); } @@ -1110,101 +1093,45 @@ valid_char(char c) static bool do_buildindex(clientenv *clenv) { - char line[1024]; - bool ok = FALSE; - msgnode *mnode; - int rows, i, numfields; - long size; - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned long *lengths; - - /* Query mysql server */ - snprintf(line, 1023, - "SELECT mail_id,mail_size,mail_file FROM mail WHERE " - "mail_box='%s'", clenv->mailbox); - numfields = 3; - - if ((mysqlres = mmsql_query(line, mm_strlen(line))) != NULL) { - - if ((rows = mysql_num_rows(mysqlres)) != 0) { - /* Allocate array and fill it */ - if ((mnode = (msgnode *)malloc(rows * sizeof(msgnode))) != NULL) { - - ok = TRUE; - clenv->size = 0; - for (i = 0; i < rows; i++) { - if ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) - != NULL) { - if ((mysql_num_fields(mysqlres)) == numfields) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - /* Note: this assumes that the field is 20 - * bytes - */ - mm_memcpy(mnode[i].id, row[0], lengths[0]); - mnode[i].id[lengths[0]] = '\0'; - mnode[i].retreived = mnode[i].deleted = FALSE; - if (row[1] != NULL) { - mm_memcpy(line, row[1], lengths[1]); - line[lengths[1]] = '\0'; - size = atol(line); - mnode[i].size = size; - clenv->size += size; - } else { - ok = FALSE; - break; - } - if (row[2] != NULL) { - mm_memcpy(mnode[i].file, row[2], - lengths[2]); - mnode[i].file[lengths[2]] = '\0'; - } else { - ok = FALSE; - break; - } - } else { - ok = FALSE; - break; - } - } else { - DEBUG_PRINTF("do_buildindex", - "mysql_num_fields() != %d", numfields); - ok = FALSE; - break; - } - } else { - DEBUG_PRINTF("do_buildindex", "mysql_fetch_rows()"); - ok = FALSE; - break; - } - } - - if (ok) { - clenv->index = mnode; - clenv->messages = rows; - clenv->newmessages = clenv->messages; - clenv->newsize = clenv->size; - } else - free(mnode); - - } else - DEBUG_PRINTF("do_buildindex", - "malloc(%d)", rows * sizeof(msgnode)); - } else + bool ok = FALSE; + msgnode *mnode; + PGresult *pgres; + int rows, i; + const char *params[2]; + + params[0] = clenv->mailbox; + params[1] = NULL; + if ((pgres = PQexecParams(clenv->pgconn, + "SELECT id,size,file FROM mail WHERE box=$1", 1, NULL, params, + NULL, NULL, 0)) != NULL) { + rows = PQntuples(pgres); + /* Allocate array and fill it */ + if ((mnode = (msgnode *)malloc(rows * sizeof(msgnode))) != NULL) { ok = TRUE; + clenv->size = 0; + for (i = 0; i < rows; i++) { + mm_strcpy(mnode[i].id, PQgetvalue(pgres, i, 0)); + (void) snprintf(mnode[i].file, 255, "%s/%s", CONF.MAIL_DIR, + PQgetvalue(pgres, i, 1)); + mnode[i].size = atol(PQgetvalue(pgres, i, 2)); + clenv->size += mnode[i].size; + } + clenv->index = mnode; + clenv->messages = clenv->newmessages = rows; + clenv->newsize = clenv->size; + } else + ok = FALSE; + PQclear(pgres); + } - mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("do_buildindex", "mmsql_query(%s)", line); - - return (ok); + return ok; } -/* Loads a message body, either using mmap(2) for file storage mode, or a - * MySQL query. This function along with do_message_free() are called by - * do_retreive(), do_top() and do_page(). +/* + * Loads a message body, using mmap(2). + * This function along with do_message_free() are called by do_retreive(), + * do_top() and do_page(). */ static bool do_message_load(msgdata *mdata, msgnode *mnode) @@ -1394,53 +1321,35 @@ do_page(fdbuf *fdb, msgnode *mnode, long page, long lines) static bool do_update(clientenv *clenv) { - char line[1024]; - bool ok = TRUE; - long i, t, messages, size; - msgnode *mnode; + bool ok = TRUE; + long i, t; + msgnode *mnode; + PGresult *pgres; if ((t = clenv->messages) > 0) { - /* Deleted messages and bytes */ - messages = 0; - size = 0; - mnode = clenv->index; - /* Delete messages that were marked, use a lock between our two calls - * which prevents mmsmtpd or other threads to interfere in-between + + /* + * Delete messages that were marked */ - mmsql_glock("mmmail_boxmail"); for (i = 0; i < t; i++) { + const char *params[2]; + + params[1] = NULL; if (mnode[i].deleted) { - snprintf(line, 1023, "DELETE FROM mail WHERE mail_id=%s", - mnode[i].id); - if (!mmsql_command(line, mm_strlen(line))) { - DEBUG_PRINTF("do_update", "mmsql_command(%s)", line); + params[0] = mnode[i].id; + if ((pgres = PQexecParams(clenv->pgconn, + "DELETE FROM mail WHERE id=$1", 1, NULL, params, NULL, + NULL, 0)) != NULL) + PQclear(pgres); + else ok = FALSE; - break; - } else { - /* Also unlink associated file */ - if (unlink(mnode[i].file) == -1) - mmsyslog(0, LOGLEVEL, "unlink(%s) == %s", - mnode[i].file, strerror(errno)); - messages++; - size += mnode[i].size; - } } } - if (messages > 0 && size > 0) { - /* Update mailbox size */ - snprintf(line, 1023, - "UPDATE box SET box_size=box_size-%ld," - "box_msgs=box_msgs-%ld WHERE box_address='%s'", - size, messages, clenv->mailbox); - if (!mmsql_command(line, mm_strlen(line))) - DEBUG_PRINTF("do_update", "mmsql_command(%s)", line); - } - mmsql_gunlock("mmmail_boxmail"); } - return (ok); + return ok; } @@ -1523,9 +1432,6 @@ handleclient(unsigned long id, int fd, clientlistnode *clientlnode, /* Allocate our clientenv to share with state functions */ if ((clenv = alloc_clientenv()) != NULL) { - /* Query some configuration options from the MySQL server, - * such as max_rcpts, max_mesg_lines, max_mesg_size, hostname - */ clenv->fdb = fdb; clenv->buffer = buffer; clenv->errors = 0; diff --git a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.h b/mmsoftware/mmmail/src/mmpop3d/mmpop3d.h index 0e6c62a..d066baf 100644 --- a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.h +++ b/mmsoftware/mmmail/src/mmpop3d/mmpop3d.h @@ -1,7 +1,7 @@ -/* $Id: mmpop3d.h,v 1.17.4.1 2007/03/15 04:41:09 mmondor Exp $ */ +/* $Id: mmpop3d.h,v 1.17.4.2 2007/03/15 17:01:55 mmondor Exp $ */ /* - * Copyright (C) 2001-2004, Matthew Mondor + * Copyright (C) 2001-2007, Matthew Mondor * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -56,6 +56,8 @@ #include +#include + @@ -75,11 +77,11 @@ #define ASYNC_CHECKPW 1 /* Error registration macro */ -#define REGISTER_ERROR(x) do { \ - (x)->errors++; \ - if (CONF.DELAY_ON_ERROR) \ - pthread_sleep((x)->errors); \ -} while(0) +#define REGISTER_ERROR(x) do { \ + (x)->errors++; \ + if (CONF.DELAY_ON_ERROR) \ + pthread_sleep((x)->errors); \ +} while (/* CONSTCOND */0) @@ -89,8 +91,8 @@ /* We store config file read results in this structure */ typedef struct config { char LOCK_PATH[256], CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256], - LOG_FACILITY[32], LISTEN_IPS[1024], SERVER_NAMES[1024], DB_HOST[64], - DB_USER[32], DB_PASSWORD[32], DB_DATABASE[32]; + LOG_FACILITY[32], LISTEN_IPS[1024], SERVER_NAMES[1024], DB_INFO[1024], + MAIL_DIR[256]; long ALLOC_BUFFERS, LOG_LEVEL, LISTEN_PORT, MAX_ERRORS, MAX_IPS, MAX_PER_IP, CONNECTION_RATE, CONNECTION_PERIOD, INPUT_TIMEOUT, BANDWIDTH_IN, BANDWIDTH_OUT, GBANDWIDTH_IN, GBANDWIDTH_OUT, @@ -102,7 +104,7 @@ typedef struct config { typedef struct msgnode { char id[24]; /* ID in the database */ char file[256]; /* Fullpath to message file */ - long size; /* Message size in bytes */ + long size; /* Message size in bytes */ bool retreived, deleted; /* Flags */ } msgnode; @@ -134,7 +136,8 @@ typedef struct clientenv { bool login; /* For all_quit() */ struct iface *iface; /* Current interface user connected through */ struct async_clenv *aclenv; /* Thread context for async_call() */ - mmstat_t vstat, pstat; /* mmstat(3) handles */ + mmstat_t vstat, pstat; /* Persistent mmstat(3) handles */ + PGconn *pgconn; /* Persistent db connection */ } clientenv; /* Used for mmfd thread support delegation/abstraction */ @@ -212,6 +215,8 @@ static bool do_page(fdbuf *, msgnode *, long, long); static bool do_update(clientenv *); static bool msgwrite(fdbuf *, const void *, size_t); static bool reply(fdbuf *, bool, const char *, ...); +static bool clenv_constructor(pnode_t *); +static void clenv_destructor(pnode_t *); static clientenv *alloc_clientenv(void); static bool init_clientenv(clientenv *); static clientenv *free_clientenv(clientenv *); diff --git a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c b/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c index 65145dd..4705c35 100644 --- a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c +++ b/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c @@ -1,7 +1,7 @@ -/* $Id: mmsmtpd.c,v 1.75.4.1 2007/03/15 04:41:12 mmondor Exp $ */ +/* $Id: mmsmtpd.c,v 1.75.4.2 2007/03/15 17:01:56 mmondor Exp $ */ /* - * Copyright (C) 2001-2004, Matthew Mondor + * Copyright (C) 2001-2007, Matthew Mondor * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,7 +68,6 @@ #include #include #include -#include #include #include #include @@ -81,9 +80,9 @@ -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ +MMCOPYRIGHT("@(#) Copyright (c) 2001-2007\n\ \tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmsmtpd.c,v 1.75.4.1 2007/03/15 04:41:12 mmondor Exp $"); +MMRCSID("$Id: mmsmtpd.c,v 1.75.4.2 2007/03/15 17:01:56 mmondor Exp $"); @@ -198,7 +197,7 @@ static fdfuncs gfdf = { /* * Used to speed up VALID_ADDR_CHAR() */ -static const unsigned char valid_addr_char_table[256] = { +static const int valid_addr_char_table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, @@ -219,7 +218,7 @@ static const unsigned char valid_addr_char_table[256] = { /* * And for VALID_HOST_CHAR() */ -static const unsigned char valid_addr_host_table[256] = { +static const int valid_addr_host_table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, @@ -257,7 +256,6 @@ main(int argc, char **argv) char *conf_file = "/usr/local/etc/mmsmtpd.conf"; int ngids, ret = -1; long facility; - char *db_host; bool strlist; cres_t cres; carg_t *cargp; @@ -270,10 +268,7 @@ main(int argc, char **argv) {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, {CAT_STR, CAF_NONE, 1, 1023, "SERVER_NAMES", CONF.SERVER_NAMES}, {CAT_STR, CAF_NONE, 1, 1023, "LISTEN_IPS", CONF.LISTEN_IPS}, - {CAT_STR, CAF_NONE, 1, 63, "DB_HOST", CONF.DB_HOST}, - {CAT_STR, CAF_NONE, 1, 31, "DB_USER", CONF.DB_USER}, - {CAT_STR, CAF_NONE, 1, 31, "DB_PASSWORD", CONF.DB_PASSWORD}, - {CAT_STR, CAF_NONE, 1, 31, "DB_DATABASE", CONF.DB_DATABASE}, + {CAT_STR, CAF_NONE, 1, 1023, "DB_INFO", CONF.DB_INFO}, {CAT_STR, CAF_NONE, 1, 255, "MAIL_DIR", CONF.MAIL_DIR}, {CAT_STR, CAF_NONE, 1, 255, "MMRELAYD_SOCKET_PATH", CONF.MMRELAYD_SOCKET_PATH}, @@ -339,13 +334,6 @@ main(int argc, char **argv) {async_resquery, sizeof(struct async_resquery_msg)}, {NULL, 0} }; - struct mmsql_threadsupport mmsqlfuncs = { - thread_mutex_create, - thread_mutex_destroy, - thread_mutex_lock, - thread_mutex_unlock, - pthread_sleep - }; mmstat_t vstat; pthread_t hosts_table_thread = NULL; pthread_t mmmail_db_gc_thread = NULL; @@ -360,10 +348,7 @@ main(int argc, char **argv) mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); mm_strcpy(CONF.SERVER_NAMES, "smtp.localhost"); mm_strcpy(CONF.LISTEN_IPS, "127.0.0.1"); - mm_strcpy(CONF.DB_HOST, "localhost"); - mm_strcpy(CONF.DB_USER, "mmmail"); - mm_strcpy(CONF.DB_PASSWORD, "mmmailpassword"); - mm_strcpy(CONF.DB_DATABASE, "mmmail"); + mm_strcpy(CONF.DB_INFO, "dbname=mmmail"); mm_strcpy(CONF.MAIL_DIR, "/var/mmmail-dir"); mm_strcpy(CONF.MMRELAYD_SOCKET_PATH, "/var/run/mmrelayd.sock"); CONF.ASYNC_PROCESSES = 3; @@ -447,8 +432,6 @@ main(int argc, char **argv) printf("\nOne of following groups unknown: '%s'\n\n", CONF.GROUPS); exit(-1); } - if (!(mm_strcmp(CONF.DB_HOST, "localhost"))) db_host = NULL; - else db_host = CONF.DB_HOST; if (*CONF.MAIL_DIR != '/') { printf("\nMAIL_DIR must be an absolute pathname to a directory\n\n"); @@ -491,12 +474,6 @@ main(int argc, char **argv) mmstat(&vstat, STAT_DELETE, 0, "mmsmtpd|who|*"); mmstat_transact(&vstat, FALSE); res_init(); - if (!(mmsql_open(db_host, CONF.DB_USER, CONF.DB_PASSWORD, - CONF.DB_DATABASE))) { - printf("\nCould not connect to MySQLd\n\n"); - syslog(LOG_NOTICE, "* Could not connect to MySQLd"); - exit(-1); - } make_daemon(CONF.PID_PATH, CONF.CHROOT_DIR); @@ -508,9 +485,10 @@ main(int argc, char **argv) /* Allocate necessary pools */ /* Client nodes */ - pool_init(&clenv_pool, "clenv_pool", malloc, free, NULL, NULL, - sizeof(clientenv), - (65536 * CONF.ALLOC_BUFFERS) / sizeof(clientenv), 0, 0); + pool_init(&clenv_pool, "clenv_pool", malloc, free, + clientenv_constructor, clientenv_destructor, + sizeof(clientenv), (65536 * CONF.ALLOC_BUFFERS) / sizeof(clientenv), + 0, 0); /* RCPT nodes */ pool_init(&rcpt_pool, "rcpt_pool", malloc, free, NULL, NULL, sizeof(rcptnode), @@ -521,7 +499,7 @@ main(int argc, char **argv) (16384 * CONF.ALLOC_BUFFERS) / sizeof(struct mutexnode), 0, 0); pthread_attr_init(&threadattr); - pthread_attr_setdetachstate(&threadattr, 0); + pthread_attr_setdetachstate(&threadattr, PTHREAD_CREATE_JOINABLE); /* Rate nodes */ if (CONF.FLOOD_PROTECTION) { @@ -532,7 +510,7 @@ main(int argc, char **argv) pthread_create(&hosts_table_thread, &threadattr, hosts_expire_thread, NULL); } - /* Launch box directories cleaning thread */ + /* Launch files gc cleaning thread */ pthread_create(&mmmail_db_gc_thread, &threadattr, db_gc_thread, NULL); /* mmstr nodes */ strlist = mmstrinit(malloc, free, 65536 * CONF.ALLOC_BUFFERS); @@ -545,7 +523,6 @@ main(int argc, char **argv) thread_init(); fdbcinit(&gfdf, &fdbc, CONF.GBANDWIDTH_IN * 1024, CONF.GBANDWIDTH_OUT * 1024); - mmsql_init(&mmsqlfuncs); tcp_server("402 Server too busy, try again\r\n", CONF.SERVER_NAMES, CONF.LISTEN_IPS, uid, gids, ngids, @@ -555,8 +532,6 @@ main(int argc, char **argv) mmfreegidarray(gids); ret = 0; - mmsql_close(); - mmsql_exit(); } else { printf("\nOut of memory\n\n"); syslog(LOG_NOTICE, "* Out of memory"); @@ -745,8 +720,7 @@ all_mail(clientenv *clenv) * If so, we also want to make sure not to perform any type * of envelope based filtering for this post. */ - valid = check_nofrom(clenv->c_ipaddr, clenv->c_hostname); - if (valid) + if ((valid = check_nofrom(clenv))) *addr = '\0'; clenv->nofrom = TRUE; } else { @@ -827,9 +801,9 @@ all_rcpt(clientenv *clenv) */ valid = FALSE; (void) mm_strcpy(foraddr, addr); - if (!(valid = local_address(&boxinfo, addr))) { - if (check_alias(addr)) { - if (!(valid = local_address(&boxinfo, addr))) + if (!(valid = local_address(clenv, &boxinfo, addr))) { + if (check_alias(clenv, addr)) { + if (!(valid = local_address(clenv, &boxinfo, addr))) mmsyslog(0, LOGLEVEL, "Invalid alias address (%s)", addr); } @@ -887,7 +861,8 @@ all_rcpt(clientenv *clenv) * with an empty FROM address from allowed servers */ if (boxinfo.filter && !clenv->nofrom) { - if (!box_filter_allow(addr, clenv->from, boxinfo.filter_type)) { + if (!box_filter_allow(clenv, addr, clenv->from, + boxinfo.filter_type)) { reason = RCPT_FILTER; if (CONF.STATFAIL_FILTER) mmstat(&clenv->pstat, STAT_UPDATE, 1, @@ -897,8 +872,8 @@ all_rcpt(clientenv *clenv) } } /* Make sure mailbox quota limits are respected */ - if (!((boxinfo.size <= boxinfo.max_size) && - (boxinfo.msgs <= boxinfo.max_msgs))) { + if (!((boxinfo.size < boxinfo.max_size) && + (boxinfo.msgs < boxinfo.max_msgs))) { mmsyslog(0, LOGLEVEL, "%s mailbox full (%ld,%ld %ld,%ld)", addr, boxinfo.max_size, boxinfo.size, boxinfo.max_msgs, boxinfo.msgs); @@ -912,6 +887,7 @@ all_rcpt(clientenv *clenv) /* Make sure that we only allow one RCPT per mailbox (alias already * redirected to it) + * XXX We probably should use a real hash table for RCPTs */ { register rcptnode *rnode; @@ -1141,6 +1117,31 @@ reply(fdbuf *fdb, int code, bool cont, const char *fmt, ...) } +static bool +clientenv_constructor(pnode_t *pn) +{ + clientenv *clenv = (clientenv *)pn; + + mmstat_init(&clenv->vstat, TRUE, TRUE); + mmstat_init(&clenv->pstat, TRUE, FALSE); + if ((clenv->pgconn = PQconnectdb(CONF.DB_INFO)) == NULL) + return FALSE; + + return TRUE; +} + +static void +clientenv_destructor(pnode_t *pn) +{ + clientenv *clenv = (clientenv *)pn; + + if (clenv->pgconn != NULL) { + PQfinish(clenv->pgconn); + clenv->pgconn = NULL; + } +} + + /* Allocate and prepare a clenv. Returns NULL on error */ static clientenv * alloc_clientenv(void) @@ -1151,11 +1152,6 @@ alloc_clientenv(void) clenv = (clientenv *)pool_alloc(&clenv_pool, TRUE); pthread_mutex_unlock(&clenv_lock); - if (clenv != NULL) { - mmstat_init(&clenv->vstat, TRUE, TRUE); - mmstat_init(&clenv->pstat, TRUE, FALSE); - } - return (clenv); } @@ -1218,15 +1214,14 @@ empty_rcpts(list_t *lst) * map it to the real address to redirect to, replacing supplied address. * The addr char array must at least be 64 bytes. Returns FALSE if no alias * exist for the address, or TRUE on success. - * XXX Could possibly use an async function, if we allow the async processes - * to connect to MySQLd. */ static bool -check_alias(char *addr) +check_alias(clientenv *clenv, char *addr) { - bool res = FALSE; - char oaddr[64], query[1024], *user, *domain; - MYSQL_RES *mysqlres; + bool res = FALSE; + char oaddr[64], *user, *domain; + PGresult *pgres; + const char *params[2]; /* We know that the address is valid, since it has been verified already. * Copy it to not modify our supplied address, and to keep a backup of the @@ -1237,39 +1232,31 @@ check_alias(char *addr) for (user = oaddr, domain = oaddr; *domain != '@'; domain++) ; *domain++ = '\0'; - snprintf(query, 1023, "SELECT alias_pattern,alias_box FROM alias " - "WHERE alias_domain='%s'", domain); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - char pat[64]; - int cur = 0, max = -1; - MYSQL_ROW *row; - unsigned long *lengths; - - /* Find best match */ - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) - != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL && row[1] != NULL) { - mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if ((cur = best_match(user, pat)) != -1) { - if (cur > max) { - /* Matches better, remember this one */ - max = cur; - mm_memcpy(addr, row[1], lengths[1]); - addr[lengths[1]] = '\0'; - } - } + params[0] = domain; + params[1] = NULL; + if ((pgres = PQexecParams(clenv->pgconn, + "SELECT pattern,box FROM alias WHERE alias=$1", 1, NULL, params, NULL, + NULL, 0)) != NULL) { + int i, t, cur = 0, max = -1; + const char *a = NULL; + + for (i = 0, t = PQntuples(pgres); i < t; i++) { + if ((cur = best_match(user, PQgetvalue(pgres, i, 0))) != -1) { + if (cur > max) { + /* Better match, remember this one */ + max = cur; + a = PQgetvalue(pgres, i, 1); } } - if (max > -1) - res = TRUE; } - mysqlres = mmsql_free_result(mysqlres); + if (max > -1) { + (void) mm_strcpy(addr, a); + res = TRUE; + } + PQclear(pgres); } - return (res); + return res; } @@ -1277,46 +1264,38 @@ check_alias(char *addr) * of both matched an entry. */ static bool -check_nofrom(const char *addr, const char *host) +check_nofrom(clientenv *clenv) { - bool res = FALSE; - MYSQL_RES *mysqlres; + bool res = FALSE; + PGresult *pgres; - if (addr == NULL && host == NULL) + if (clenv->c_ipaddr == NULL && clenv->c_hostname == NULL) return (FALSE); - if ((mysqlres = mmsql_query("SELECT nofrom_pattern FROM nofrom", -1)) + if ((pgres = PQexec(clenv->pgconn, "SELECT pattern FROM nofrom")) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - MYSQL_ROW *row; - unsigned long *lengths; - - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - char pat[64]; - - mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if (addr != NULL) { - if ((best_match(addr, pat)) != -1) { - res = TRUE; - break; - } - } - if (host != NULL) { - if ((best_match(host, pat)) != -1) { - res = TRUE; - break; - } - } + int i, t; + char *pat; + + for (i = 0, t = PQntuples(pgres); i < t; i++) { + pat = PQgetvalue(pgres, i, 0); + if (clenv->c_ipaddr != NULL) { + if ((best_match(clenv->c_ipaddr, pat)) != -1) { + res = TRUE; + break; + } + } + if (clenv->c_hostname != NULL) { + if ((best_match(clenv->c_hostname, pat)) != -1) { + res = TRUE; + break; } } } - mysqlres = mmsql_free_result(mysqlres); + PQclear(pgres); } - return (res); + return res; } @@ -1376,62 +1355,34 @@ best_match(const char *str, const char *pat) /* Returns FALSE if this address doesn't exist in our local mailboxes. - * Otherwise it returns information about the mailbox via supplied pointers. + * Otherwise it populates boxinfo structure with information about the + * mailbox. */ static bool -local_address(struct box_info *boxinfo, const char *address) +local_address(clientenv *clenv, struct box_info *boxinfo, const char *address) { bool res = FALSE; - char line[1024]; - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned int fields; - unsigned long *lengths; - - /* Query mysql to see if this address exists, and get limits/status */ - (void) snprintf(line, 1023, - "SELECT box_max_size,box_size,box_max_msgs,box_msgs,box_filter," - "box_filter_type FROM box WHERE box_address='%s'", address); - - if ((mysqlres = mmsql_query(line, mm_strlen(line))) != NULL) { - - if ((mysql_num_rows(mysqlres)) == 1 - && (row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - if ((fields = mysql_num_fields(mysqlres)) == 6) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL && row[1] != NULL && row[2] != NULL && - row[3] != NULL && row[4] != NULL && row[5] != NULL) { - int v; - - mm_memcpy(line, row[0], lengths[0]); - line[lengths[0]] = '\0'; - boxinfo->max_size = atol(line); - mm_memcpy(line, row[1], lengths[1]); - line[lengths[1]] = '\0'; - boxinfo->size = atol(line); - mm_memcpy(line, row[2], lengths[2]); - line[lengths[2]] = '\0'; - boxinfo->max_msgs = atol(line); - mm_memcpy(line, row[3], lengths[3]); - line[lengths[3]] = '\0'; - boxinfo->msgs = atol(line); - mm_memcpy(line, row[4], lengths[4]); - line[lengths[4]] = '\0'; - v = atol(line); - boxinfo->filter = (v == 1 ? TRUE : FALSE); - mm_memcpy(line, row[5], lengths[5]); - line[lengths[5]] = '\0'; - boxinfo->filter_type = *line; - res = TRUE; - } else - DEBUG_PRINTF("local_address", "row[x] == NULL"); - } else - DEBUG_PRINTF("local_address", "mysql_num_fields()"); + PGresult *pgres; + const char *params[2]; + + params[0] = address; + params[1] = NULL; + if ((pgres = PQexecParams(clenv->pgconn, + "SELECT max_size,size,max_msgs,msgs,filter,filter_type FROM box " + "WHERE address=$1", 1, NULL, params, NULL, NULL, 0)) != NULL) { + if (PQntuples(pgres) == 1) { + boxinfo->max_size = atol(PQgetvalue(pgres, 0, 0)); + boxinfo->size = atol(PQgetvalue(pgres, 0, 1)); + boxinfo->max_msgs = atol(PQgetvalue(pgres, 0, 2)); + boxinfo->msgs = atol(PQgetvalue(pgres, 0, 3)); + boxinfo->filter = (*(PQgetvalue(pgres, 0, 4)) == 't' ? + TRUE : FALSE); + boxinfo->filter_type = (*(PQgetvalue(pgres, 0, 5)) == 't' ? + TRUE : FALSE); + res = TRUE; } - - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("local_address", "mmsql_query(%s)", line); + PQclear(pgres); + } return res; } @@ -1439,46 +1390,31 @@ local_address(struct box_info *boxinfo, const char *address) /* Verifies if mailbox filters allow to post */ static bool -box_filter_allow(const char *toaddr, const char *fromaddr, char filter_type) +box_filter_allow(clientenv *clenv, const char *toaddr, const char *fromaddr, + bool filter_type) { bool res; - char query[1024]; - MYSQL_RES *mysqlres; + PGresult *pgres; + const char *params[2]; /* Default return code depends on filter type */ - res = (filter_type == 'A' ? TRUE : FALSE); - - snprintf(query, 1023, - "SELECT filter_pattern FROM filter WHERE filter_address='%s'", - toaddr); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - char pat[64]; - MYSQL_ROW *row; - unsigned long *lengths; - - /* - * Filter types are 'A' (allow by default) and - * 'D' (deny by * default). - */ - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - (void) mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if (best_match(fromaddr, pat) != -1) { - /* Inverse return code and break */ - res = !res; - break; - } - } + res = filter_type; + + params[0] = toaddr; + params[1] = NULL; + if ((pgres = PQexecParams(clenv->pgconn, + "SELECT pattern FROM filter WHERE address=$1", 1, NULL, params, + NULL, NULL, 0)) != NULL) { + int i, t; + + for (i = 0, t = PQntuples(pgres); i < t; i++) { + if (best_match(fromaddr, PQgetvalue(pgres, i, 0)) != -1) { + res = !res; + break; } - - } else - DEBUG_PRINTF("box_filter_allow", "mysql_num_rows()"); - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("box_filter_allow", "mmsql_query(%s)", query); + } + PQclear(pgres); + } return res; } @@ -1850,15 +1786,8 @@ do_data(clientenv *clenv) break; } - if (ok) { - /* XXX The following could easily simply be provided by separately - * compiled mmsmtpd modules, designed to support multple storage - * methods, as do_data_store() or such. But, we only support MySQL - * and file storage for now... Which suffices for me. - */ - + if (ok) ok = do_data_file(clenv, fdbrb); - } fdbfreebuf(&fdbrb); /* Internally only frees if not already freed */ if (ok) @@ -1890,28 +1819,6 @@ do_data_received(char *line, size_t len, clientenv *clenv, rcptnode *rnode, return mm_strlen(line); } -/* Used to update mailbox quotas. Isolated to prevent code duplication among - * different storage methods. Note that this must be done under a lock when - * necessary for consistency with actual message storage data. - */ -inline static bool -do_data_update(rcptnode *rnode, size_t len) -{ - char line[1024]; - bool ok = TRUE; - - snprintf(line, 1023, - "UPDATE box SET box_size=box_size+%ld," - "box_msgs=box_msgs+1,box_in=NOW() WHERE " - "box_address='%s'", - (long)len, rnode->address); - if (!mmsql_command(line, mm_strlen(line))) { - DEBUG_PRINTF("do_data", "mmsql_command(%s)", line); - ok = FALSE; - } - - return ok; -} /* Record statistics using mmstat(3) facility, called by do_data to prevent * code duplication among different storage methods. @@ -1956,9 +1863,8 @@ bool address_relay_allow(clientenv *clenv, int *reason, const char *addr) { bool res = TRUE; - char query[1024]; const char *domain; - MYSQL_RES *mysqlres; + PGresult *pgres; /* Is address to a local domain but unknown to us? */ @@ -1967,32 +1873,22 @@ address_relay_allow(clientenv *clenv, int *reason, const char *addr) */ for (domain = addr; *domain != '@'; domain++) ; domain++; + /* Query database entries and search for any matching pattern. */ - (void) snprintf(query, 1023, "SELECT relaylocal_pattern FROM relaylocal"); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - char pat[64]; - MYSQL_ROW *row; - unsigned long *lengths; - - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if (best_match(domain, pat) != -1) { - res = FALSE; - *reason = RCPT_UNKNOWN; - break; - } - } - } - } else - DEBUG_PRINTF("address_relay_allow", "mysql_num_rows()"); - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("address_relay_allow", "mmsql_query(%s)", query); + if ((pgres = PQexec(clenv->pgconn, "SELECT pattern FROM relaylocal")) + != NULL) { + int i, t; + for (i = 0, t = PQntuples(pgres); i < t; i++) { + if (best_match(domain, PQgetvalue(pgres, i, 0)) != -1) { + res = FALSE; + *reason = RCPT_UNKNOWN; + break; + } + } + PQclear(pgres); + } + /* Return with error immediately if address is locally handled */ if (!res) return res; @@ -2001,40 +1897,30 @@ address_relay_allow(clientenv *clenv, int *reason, const char *addr) * allowed to relay messages through us? Verify via the client's IP * address and/or hostname. */ - res = FALSE; - (void) snprintf(query, 1023, "SELECT relayfrom_pattern FROM relayfrom"); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - char pat[64]; - MYSQL_ROW *row; - unsigned long *lengths; - - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if (clenv->c_ipaddr != NULL) { - if (best_match(clenv->c_ipaddr, pat) != -1) { - res = TRUE; - break; - } - } - if (clenv->c_hostname != NULL) { - if (best_match(clenv->c_hostname, pat) != -1) { - res = TRUE; - break; - } - } + if ((pgres = PQexec(clenv->pgconn, "SELECT pattern FROM relayfrom")) + != NULL) { + int i, t; + char *pat; + + for (i = 0, t = PQntuples(pgres); i < t; i++) { + pat = PQgetvalue(pgres, i, 0); + if (clenv->c_ipaddr != NULL) { + if (best_match(clenv->c_ipaddr, pat) != -1) { + res = TRUE; + break; } } - } else - DEBUG_PRINTF("address_relay_allow", "mysql_num_rows()"); - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("address_relay_allow", "mmsql_query(%s)", query); + if (clenv->c_hostname != NULL) { + if (best_match(clenv->c_hostname, pat) != -1) { + res = TRUE; + break; + } + } + } + PQclear(pgres); + } if (!res) *reason = RCPT_RELAY; @@ -2062,7 +1948,7 @@ iso_time(char *str) /* Saves a message to disk, into the directory, creating the directory * if needed. It ensures to create a unique filename in that directory. The * directory and the file will be located into MAIL_DIR. Locks should be held - * as necessary before calling this function is atomic behavior is required + * as necessary before calling this function as atomic behavior is required * among other tasks. consists of the "Received:" header which will * be written first, followed by the data held in the buffer. The * fullpath to the created filename will be stored into supplied , which @@ -2075,6 +1961,7 @@ message_write(char *path, const char *recvline, size_t recvlen, bool ok = FALSE; char filetime[16]; int i, fd; + u_int32_t r; fd = -1; @@ -2093,11 +1980,14 @@ message_write(char *path, const char *recvline, size_t recvlen, */ iso_time(filetime); for (i = 0; i < 64; i++) { + r = (u_int32_t)random(); (void) snprintf(path, 255, "%s/%s/%s.%08X", CONF.MAIL_DIR, - box, filetime, (u_int32_t)random()); + box, filetime, r); if ((fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 00640)) != -1) break; } + /* Write final relative path in supplied buffer */ + (void) snprintf(path, 255, "%s/%s.%08X", box, filetime, r); /* Successfully created a new unique file, save message data. * We also verify the return value of close(2), but are not calling @@ -2113,11 +2003,16 @@ message_write(char *path, const char *recvline, size_t recvlen, mmsyslog(0, LOGLEVEL, "write()/close()|(%s) == %s", path, strerror(errno)); (void) close(fd); + (void) snprintf(path, 255, "%s/%s/%s.%08X", CONF.MAIL_DIR, box, + filetime, r); (void) unlink(path); } } else mmsyslog(0, LOGLEVEL, "open(%s) == %s", path, strerror(errno)); + if (!ok) + *path = '\0'; + return ok; } @@ -2158,58 +2053,40 @@ static bool do_data_queue_box(clientenv *clenv, const char *recvline, size_t recvlen, struct fdbrb_buffer *fdbrb, rcptnode *rnode) { - char line[1024], path[256]; - bool ok = TRUE; + char path[256], v[16]; + PGresult *pgres; + const char *params[4]; /* Obtain global lock. This ensures that both file and database data * are in sync and between both mmsmtpd and mmpop3d. Moreover, it even * allows proper serialization of operations over NFS. + * XXX We don't use this type of locking anymore for now. A file advisory + * lock might be wanted though, or a PostgreSQL advisory lock. */ - mmsql_glock("mmmail_boxmail"); - if (message_write(path, recvline, recvlen, fdbrb, rnode->address)) { - /* File written successfully, now write our corresponding MySQL - * entries. Note that we store the absolute fullpath to the - * message file into the database entry. Although this is not - * necessary, it may prove useful later on. - */ - (void) snprintf(line, 1023, - "INSERT INTO mail (mail_box,mail_created,mail_size," - "mail_file) VALUES('%s',NOW(),%ld,'%s')", - rnode->address, (long)fdbrb->current + recvlen, path); - if (mmsql_command(line, mm_strlen(line))) { - u_int64_t id; - - /* Obtain auto-increment value used in last command */ - id = mmsql_last_auto_id(); - - if (!(ok = do_data_update(rnode, fdbrb->current + recvlen))) { - /* Delete previous successful entry, since updating quota - * information did not succeed, and it must always be - * accurate according to actual mail data. - */ - (void) snprintf(line, 1023, - "DELETE FROM mail WHERE mail_id=%llu", id); - (void) mmsql_command(line, mm_strlen(line)); - } - } else { - mmsyslog(0, LOGLEVEL, "mmsql_command(%s)", line); - ok = FALSE; - } - /* If anything failed, delete stored message file. */ - if (!ok) - (void) unlink(path); - } else - ok = FALSE; + if (!message_write(path, recvline, recvlen, fdbrb, rnode->address)) + return FALSE; - /* We can finally safely release the global lock */ - mmsql_gunlock("mmmail_boxmail"); + (void) snprintf(v, 15, "%ld", (long)fdbrb->current + recvlen); + params[0] = rnode->address; + params[1] = v; + params[2] = path; + params[3] = NULL; + if ((pgres = PQexecParams(clenv->pgconn, + "INSERT INTO mail (box,size,file) VALUES($1,$2,$3)", 3, NULL, + params, NULL, NULL, 0)) != NULL) + PQclear(pgres); + else { + /* XXX Log */ + (void) snprintf(path, 255, "%s/%s", CONF.MAIL_DIR, path); + (void) unlink(path); - /* If everything successful, update mmstat statistics */ - if (ok) - do_data_stats(clenv, rnode, fdbrb->current + recvlen); + return FALSE; + } - return ok; + do_data_stats(clenv, rnode, fdbrb->current + recvlen); + + return TRUE; } /* Queue a message for relaying */ @@ -2217,13 +2094,15 @@ static bool do_data_queue_relay(clientenv *clenv, const char *recvline, size_t recvlen, struct fdbrb_buffer *fdbrb, rcptnode *rnode) { - char line[1024], path[256], *user, *domain, *restore; + char path[256], *user, *domain, *restore, v[16]; bool ok = TRUE; + PGresult *pgres; + const char *params[7]; /* This lock allows to maintain atomicity between the message file and * its corresponding database entry, between mmsmtpd(8) and mmrelayd(8). + * XXX We no longer currently use a lock. */ - mmsql_glock("mmmail_relayqueue"); /* We know that the address is valid in the rcpt node, separate it into * user and domain strings. @@ -2235,16 +2114,22 @@ do_data_queue_relay(clientenv *clenv, const char *recvline, size_t recvlen, if (message_write(path, recvline, recvlen, fdbrb, "relayqueue")) { /* Message file saved successfully, add corresponding DB entry */ - (void) snprintf(line, 1023, - "INSERT INTO relayqueue (relayqueue_from,relayqueue_ipaddr," - "relayqueue_todomain,relayqueue_touser,relayqueue_size," - "relayqueue_file,relayqueue_queued) " - "VALUES('%s','%s','%s','%s','%ld','%s',NOW())", - clenv->from, clenv->c_ipaddr, domain, user, - (long)fdbrb->current + recvlen, path); - if (!mmsql_command(line, mm_strlen(line))) { - /* SQL request failed, delete saved file */ - mmsyslog(0, LOGLEVEL, "mmsql_command(%s)", line); + (void) snprintf(v, 15, "%ld", (long)fdbrb->current + recvlen); + params[0] = clenv->from; + params[1] = clenv->c_ipaddr; + params[2] = domain; + params[3] = user; + params[4] = v; + params[5] = path; + params[6] = NULL; + if ((pgres = PQexecParams(clenv->pgconn, "INSERT INTO relayqueue " + "(\"from\",ipaddr,todomain,touser,size,file) " + "VALUES($1,$2,$3,$4,$5,$6)", 6, NULL, params, NULL, NULL, 0)) + != NULL) + PQclear(pgres); + else { + /* XXX Log */ + (void) snprintf(path, 255, "%s/%s", CONF.MAIL_DIR, path); (void) unlink(path); ok = FALSE; } @@ -2254,14 +2139,13 @@ do_data_queue_relay(clientenv *clenv, const char *recvline, size_t recvlen, /* Restore string to original value */ *restore = '@'; - mmsql_gunlock("mmmail_relayqueue"); - /* * We now want to notify mmrelayd that it should verify for ready to * relay mail as soon as possible instead of waiting until its next * scheduled round. */ - do_data_queue_notify(clenv); + if (ok) + do_data_queue_notify(clenv); return ok; } @@ -2723,85 +2607,106 @@ hosts_expire_thread_iterator(hashnode_t *hnod, void *udata) static void * db_gc_thread(void *args) { + PGconn *pgconn; int rounds; + if ((pgconn = PQconnectdb(CONF.DB_INFO)) == NULL) { + syslog(LOG_NOTICE, "Couldn't connect to database!"); + exit(EXIT_FAILURE); + } + for (rounds = 1; ; rounds++) { - MYSQL_RES *mysqlres; + PGresult *pgres; + bool ok; (void) pthread_sleep(60); /* - * Perform dangling mailbox directories cleanup + * Perform dangling mailbox directories cleanup every minute. + * We do this in a transaction and automatically revert if we couldn't + * delete an object for any reason. On success the sequence counter + * is also reset. We use the id field for sorting. */ - if ((mysqlres = mmsql_query("SELECT boxdelete_address FROM boxdelete", - -1)) != NULL) { - char addr[64]; - MYSQL_ROW *row; - unsigned long *lengths; - - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - char deladdr[64], query[1024]; - bool delete; - - delete = FALSE; - *deladdr = '\0'; - mm_memcpy(addr, row[0], lengths[0]); - addr[lengths[0]] = '\0'; - - /* - * Important security sanity checking: Make sure that - * this actually consists of a valid address. We - * wouldn't want to perform anything if arbitrary - * entries were filled by a malicious user, bypassing - * the HTTP frontend security somehow, or the MySQL - * ones. We also don't need to do anything if we are not - * using files for message storage. - */ - if (valid_address(NULL, deladdr, 64, addr, HOST_NORES)) { - MYSQL_RES *mysqlres2; - - /* - * And make sure that no box entry exists for it! - */ - (void) snprintf(query, 1023, - "SELECT box_address FROM box WHERE " - "box_address='%s'", deladdr); - if ((mysqlres2 = mmsql_query(query, mm_strlen(query))) - != NULL) { - if (mysql_num_rows(mysqlres2) == 0) - delete = TRUE; - (void) mmsql_free_result(mysqlres2); - } + if ((pgres = PQexec(pgconn, "BEGIN")) == NULL) + continue; + PQclear(pgres); + + ok = TRUE; + if ((pgres = PQexec(pgconn, + "SELECT id,type,path FROM file_gc_queue ORDER BY id")) + != NULL) { + int i, t; + PGresult *pgres2; + unsigned long long id; + char path[1024]; + + for (i = 0, t = PQntuples(pgres); i < t; i++) { + id = strtoull(PQgetvalue(pgres, i, 0), NULL, 10); + (void) snprintf(path, 1023, "%s/%s", CONF.MAIL_DIR, + PQgetvalue(pgres, i, 2)); + if (*(PQgetvalue(pgres, i, 1)) == 'f') { + if (unlink(path) != 0) { + ok = FALSE; + goto skip; + } + } else { + if (rmdir(path) != 0) { + ok = FALSE; + goto skip; } - /* Delete db entry unconditionally */ - (void) snprintf(query, 1023, - "DELETE FROM boxdelete WHERE " - "boxdelete_address='%s'", addr); - (void) mmsql_command(query, mm_strlen(query)); - /* Perform actual deletion, only if safe to */ - if (delete) - db_gc_thread_delete(deladdr); + } +skip: + if (!ok) { + mmsyslog(0, LOGLEVEL, "Couldn't delete '%s'", path); + (void) snprintf(path, 1023, + "DELETE FROM file_gc_queue WHERE id=%llu", id); + if ((pgres2 = PQexec(pgconn, path)) != NULL) + PQclear(pgres2); + ok = TRUE; + continue; } } - (void) mmsql_free_result(mysqlres); + PQclear(pgres); + } + if (ok) { + if ((pgres = PQexec(pgconn, "DELETE FROM file_gc_queue")) != NULL) + PQclear(pgres); + else + ok = FALSE; + } + if (ok) { + if ((pgres = PQexec(pgconn, + "SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence(" + "'file_gc_queue','id'),1,true)")) != NULL) + PQclear(pgres); + else + ok = FALSE; + } + if (ok) { + if ((pgres = PQexec(pgconn, "COMMIT")) != NULL) + PQclear(pgres); + } else { + if ((pgres = PQexec(pgconn, "ROLLBACK")) != NULL) + PQclear(pgres); } + /* + * Perform database optimizations every 24 hours. + */ if (rounds == 1440) { rounds = 0; - /* - * Perform database optimization every 24 hours - */ #define OPTIMIZE(s) do { \ - if ((mysqlres = mmsql_query("OPTIMIZE TABLE " s, -1)) != NULL) \ - (void) mmsql_free_result(mysqlres); \ + if ((pgres = PQexec(pgconn, "VACUUM ANALYZE " s)) != NULL) \ + PQclear(pgres); \ + else \ + mmsyslog(0, LOGLEVEL, "Couldn't optimize " s); \ } while (/* CONSTCOND */0) OPTIMIZE("alias"); OPTIMIZE("box"); OPTIMIZE("boxdelete"); + OPTIMIZE("file_gc_queue"); OPTIMIZE("filter"); OPTIMIZE("mail"); OPTIMIZE("nofrom"); @@ -2819,34 +2724,3 @@ db_gc_thread(void *args) pthread_exit(NULL); return NULL; } - -static void -db_gc_thread_delete(const char *addr) -{ - char dirpath[256], filepath[256]; - DIR *dir; - struct dirent ent, *res; - - /* - * Now holds the actual directory to delete mail from. We know - * that there only exist first level files in it, and we must ensure to - * delete all of them, as well as the actual mailbox directory afterwards. - */ - if ((dir = opendir(dirpath)) == NULL) { - syslog(LOG_NOTICE, "db_gc_thread_delete(%s) - opendir(%s) == %s", - addr, dirpath, strerror(errno)); - return; - } - - while (readdir_r(dir, &ent, &res) == 0 && res == &ent) { - (void) snprintf(filepath, 255, "%s/%s", dirpath, ent.d_name); - if (unlink(filepath) != 0) - syslog(LOG_NOTICE, "db_gc_thread_delete(%s) - unlink(%s) == %s", - addr, filepath, strerror(errno)); - } - if (rmdir(dirpath) != 0) - syslog(LOG_NOTICE, "db_gc_thread_delete(%s) - rmdir(%s) == %s", - addr, dirpath, strerror(errno)); - - closedir(dir); -} diff --git a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h b/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h index 612e393..9437dd4 100644 --- a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h +++ b/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h @@ -1,7 +1,7 @@ -/* $Id: mmsmtpd.h,v 1.34.4.1 2007/03/15 04:41:12 mmondor Exp $ */ +/* $Id: mmsmtpd.h,v 1.34.4.2 2007/03/15 17:01:57 mmondor Exp $ */ /* - * Copyright (C) 2001-2004, Matthew Mondor + * Copyright (C) 2001-2007, Matthew Mondor * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,6 +57,8 @@ #include #include +#include + @@ -108,11 +110,11 @@ enum data_reason { #define ASYNC_RESQUERY 1 /* Error registration macro */ -#define REGISTER_ERROR(x) do { \ - (x)->errors++; \ - if (CONF.DELAY_ON_ERROR) \ - pthread_sleep((x)->errors); \ -} while(FALSE) +#define REGISTER_ERROR(x) do { \ + (x)->errors++; \ + if (CONF.DELAY_ON_ERROR) \ + pthread_sleep((x)->errors); \ +} while (/* CONSTCOND */0) /* Evaluates if a character is valid for addresses and hostnames */ #define VALID_ADDR_CHAR(c) \ @@ -127,9 +129,8 @@ enum data_reason { /* We store config file read results in this structure */ typedef struct config { char LOCK_PATH[256], CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256], - LOG_FACILITY[32], SERVER_NAMES[1024], LISTEN_IPS[1024], DB_HOST[64], - DB_USER[32], DB_PASSWORD[32], DB_DATABASE[32], MAIL_DIR[256], - MMRELAYD_SOCKET_PATH[256]; + LOG_FACILITY[32], SERVER_NAMES[1024], LISTEN_IPS[1024], DB_INFO[1024], + MAIL_DIR[256], MMRELAYD_SOCKET_PATH[256]; long ALLOC_BUFFERS, LOG_LEVEL, LISTEN_PORT, MAX_ERRORS, MAX_IPS, MAX_PER_IP, CONNECTION_RATE, CONNECTION_PERIOD, INPUT_TIMEOUT, BANDWIDTH_IN, BANDWIDTH_OUT, GBANDWIDTH_IN, GBANDWIDTH_OUT, MAX_RCPTS, @@ -159,9 +160,10 @@ typedef struct clientenv { unsigned long messages; /* Messages user sent us */ unsigned long rcpts; /* Number of RCPT accepted */ struct iface *iface; /* Current interface user connected through */ - struct async_clenv *aclenv; /* Thread context for async_call() */ list_t rcpt; /* Cached recepients to send mail to */ - mmstat_t vstat, pstat; /* mmstat(3) handles */ + struct async_clenv *aclenv; /* Thread context for async_call() */ + mmstat_t vstat, pstat; /* Persistent mmstat(3) handles */ + PGconn *pgconn; /* Persistent pgsql connection */ } clientenv; /* Used for RCPT addresses */ @@ -207,9 +209,8 @@ typedef struct command { /* Information for a mailbox */ struct box_info { - long max_size, size, max_msgs, msgs; - bool filter; - char filter_type; + long max_size, size, max_msgs, msgs; + bool filter, filter_type; }; /* For fast command lookup */ @@ -276,16 +277,18 @@ static u_int32_t commandnode_keyhash(const void *, size_t); static int commandnode_keycmp(const void *, const void *, size_t); static bool reply(fdbuf *, int, bool, const char *, ...); +static bool clientenv_constructor(pnode_t *); +static void clientenv_destructor(pnode_t *); static clientenv *alloc_clientenv(void); static bool init_clientenv(clientenv *, bool); static clientenv *free_clientenv(clientenv *); static void empty_rcpts(list_t *); -static bool check_alias(char *); -static bool check_nofrom(const char *, const char *); +static bool check_alias(clientenv *, char *); +static bool check_nofrom(clientenv *); static int lock_check(const char *); static int best_match(const char *, const char *); -static bool local_address(struct box_info *, const char *); -static bool box_filter_allow(const char *, const char *, char); +static bool local_address(clientenv *, struct box_info *, const char *); +static bool box_filter_allow(clientenv *, const char *, const char *, bool); static void rfc_time(char *); static bool valid_address(clientenv *, char *, size_t, char *, int); static bool valid_host(clientenv *, char *, int, bool, bool); @@ -295,7 +298,6 @@ static int validate_msg_line(char *, ssize_t *, int *, void *); static bool do_data(clientenv *); inline static size_t do_data_received(char *, size_t, clientenv *, rcptnode *, const char *); -inline static bool do_data_update(rcptnode *, size_t); static void do_data_stats(clientenv *, rcptnode *, size_t); static bool address_relay_allow(clientenv *, int *, const char *); static void iso_time(char *); @@ -326,7 +328,6 @@ static void *hosts_expire_thread(void *); static bool hosts_expire_thread_iterator(hashnode_t *, void *); static void *db_gc_thread(void *); -static void db_gc_thread_delete(const char *);