-/* $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
#include <mmpool.h>
#include <mmhash.h>
#include <mmserver.h>
-#include <mmsql.h>
#include <mmlog.h>
#include <mmstr.h>
#include <mmstring.h>
-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 $");
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;
{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},
{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 */
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;
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);
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);
/* 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,
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,
mmfreegidarray(gids);
ret = 0;
- mmsql_close();
- mmsql_exit();
} else {
printf("\nOut of memory\n\n");
syslog(LOG_NOTICE, "* Out of memory");
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)",
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");
}
+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)
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);
}
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)
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;
}
/* 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;
-/* $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
#include <mmpool.h>
#include <mmhash.h>
#include <mmserver.h>
-#include <mmsql.h>
#include <mmlog.h>
#include <mmstr.h>
#include <mmstring.h>
-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 $");
/*
* 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,
/*
* 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,
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;
{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},
{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;
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;
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");
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);
/* 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),
(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) {
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);
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,
mmfreegidarray(gids);
ret = 0;
- mmsql_close();
- mmsql_exit();
} else {
printf("\nOut of memory\n\n");
syslog(LOG_NOTICE, "* Out of memory");
* 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 {
*/
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);
}
* 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,
}
}
/* 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);
/* 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;
}
+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)
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);
}
* 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
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;
}
* 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;
}
/* 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;
}
/* Verifies if mailbox <toaddr> filters allow <fromaddr> 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;
}
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)
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.
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? */
*/
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;
* 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;
/* Saves a message to disk, into the <box> 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. <recvline> consists of the "Received:" header which will
* be written first, followed by the data held in the <fdbrb> buffer. The
* fullpath to the created filename will be stored into supplied <path>, which
bool ok = FALSE;
char filetime[16];
int i, fd;
+ u_int32_t r;
fd = -1;
*/
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
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;
}
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 */
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.
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;
}
/* 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;
}
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");
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 <path> 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);
-}