Index: dv-opendkim-2.5.0.beta4/stats/mkdb.sqlite3
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ dv-opendkim-2.5.0.beta4/stats/mkdb.sqlite3	2011-11-26 15:56:57.000000000 +0100
@@ -0,0 +1,131 @@
+-- Copyright (c) 2010, 2011, The OpenDKIM Project.  All rights reserved.
+
+-- SQLITE3 command sequence to create a database to accumulate OpenDKIM
+-- statistics reports
+
+-- table of reporters
+create table if not exists reporters (
+	id integer not null,
+	name varchar(255) not null,
+	firstseen timestamp not null default CURRENT_TIMESTAMP,
+	enabled integer not null default 1,
+
+	primary key(id),
+	unique (name)
+);
+
+-- table of domain names we've seen
+create table if not exists domains (
+	id integer not null,
+	name varchar(255) not null,
+	firstseen timestamp not null default CURRENT_TIMESTAMP,
+
+	primary key(id),
+	unique (name)
+);
+
+-- table of IP addresses we've seen
+create table if not exists ipaddrs (
+	id integer not null,
+	addr varchar(64) not null,
+	firstseen timestamp not null default CURRENT_TIMESTAMP,
+
+	primary key(id),
+	unique (addr)
+);
+
+-- table of messages names we've seen
+create table if not exists messages (
+	id integer not null,
+	jobid varchar(64) not null,
+	reporter integer not null,
+	from_domain integer not null,
+	ip integer not null,
+	msgtime timestamp not null default CURRENT_TIMESTAMP,
+	size integer not null,
+	sigcount integer not null,
+	atps integer not null,
+	spam integer not null default 0,
+
+	primary key(id),
+
+
+
+	unique (reporter, jobid, msgtime),
+	foreign key(from_domain) references domains(id) on delete cascade,
+	foreign key(ip) references ipaddrs(id) on delete cascade,
+	foreign key(reporter) references reporters(id) on delete cascade
+);
+
+-- table of signatures we've seen
+create table if not exists signatures (
+	id integer not null,
+	message integer not null,
+	domain integer not null,
+	pass integer not null,
+	fail_body integer not null,
+	siglength integer not null,
+	sigerror integer not null,
+	dnssec integer not null,
+
+	primary key(id),
+
+	foreign key(message) references messages(id) on delete cascade,
+	foreign key(domain) references domains(id) on delete cascade
+);
+
+-- table mapping signature error codes to descriptions
+create table if not exists sigerrorcodes (
+	id integer not null,
+	name varchar(255) not null,
+
+	primary key (id)
+);
+
+insert into sigerrorcodes (id, name) values(-1,	"unknown error");
+insert into sigerrorcodes (id, name) values(0,	"no error");
+insert into sigerrorcodes (id, name) values(1,	"unsupported version");
+insert into sigerrorcodes (id, name) values(2,	"invalid domain (d=/i=)");
+insert into sigerrorcodes (id, name) values(3,	"signature expired");
+insert into sigerrorcodes (id, name) values(4,	"signature in the future");
+insert into sigerrorcodes (id, name) values(5,	"x= < t=");
+insert into sigerrorcodes (id, name) values(6,	"c= missing");
+insert into sigerrorcodes (id, name) values(7,	"c= invalid (header)");
+insert into sigerrorcodes (id, name) values(8,	"c= invalid (body)");
+insert into sigerrorcodes (id, name) values(9,     "a= missing");
+insert into sigerrorcodes (id, name) values(10,    "a= invalid");
+insert into sigerrorcodes (id, name) values(11,    "h= missing");
+insert into sigerrorcodes (id, name) values(12,    "l= invalid");
+insert into sigerrorcodes (id, name) values(13,    "q= invalid");
+insert into sigerrorcodes (id, name) values(14,    "q= option invalid");
+insert into sigerrorcodes (id, name) values(15,    "d= missing");
+insert into sigerrorcodes (id, name) values(16,    "d= empty");
+insert into sigerrorcodes (id, name) values(17,    "s= missing");
+insert into sigerrorcodes (id, name) values(18,    "s= empty");
+insert into sigerrorcodes (id, name) values(19,    "b= missing");
+insert into sigerrorcodes (id, name) values(20,    "b= empty");
+insert into sigerrorcodes (id, name) values(21,    "b= corrupt");
+insert into sigerrorcodes (id, name) values(22,    "no key found in DNS");
+insert into sigerrorcodes (id, name) values(23,    "DNS reply corrupt");
+insert into sigerrorcodes (id, name) values(24,    "DNS query failed");
+insert into sigerrorcodes (id, name) values(25,    "bh= missing");
+insert into sigerrorcodes (id, name) values(26,    "bh= empty");
+insert into sigerrorcodes (id, name) values(27,    "bh= corrupt");
+insert into sigerrorcodes (id, name) values(28,    "signature mismatch");
+insert into sigerrorcodes (id, name) values(29,    "unauthorized subdomain");
+insert into sigerrorcodes (id, name) values(30,    "multiple records returned");
+insert into sigerrorcodes (id, name) values(31,    "h= empty");
+insert into sigerrorcodes (id, name) values(32,    "h= missing required entries");
+insert into sigerrorcodes (id, name) values(33,    "l= value exceeds body size");
+insert into sigerrorcodes (id, name) values(34,    "field-must-be-signed failure");
+insert into sigerrorcodes (id, name) values(35,    "unknown key version");
+insert into sigerrorcodes (id, name) values(36,    "unknown key hash");
+insert into sigerrorcodes (id, name) values(37,    "signature-key hash mismatch");
+insert into sigerrorcodes (id, name) values(38,    "not an email key");
+insert into sigerrorcodes (id, name) values(39,    "key granularity mismatch");
+insert into sigerrorcodes (id, name) values(40,    "key type missing");
+insert into sigerrorcodes (id, name) values(41,    "key type unknown");
+insert into sigerrorcodes (id, name) values(42,    "key revoked");
+insert into sigerrorcodes (id, name) values(43,    "key could not be decoded");
+insert into sigerrorcodes (id, name) values(44,    "v= missing");
+insert into sigerrorcodes (id, name) values(45,    "v= empty");
Index: dv-opendkim-2.5.0.beta4/stats/opendkim-importstats.c
===================================================================
--- dv-opendkim-2.5.0.beta4.orig/stats/opendkim-importstats.c	2011-11-26 00:15:59.000000000 +0100
+++ dv-opendkim-2.5.0.beta4/stats/opendkim-importstats.c	2011-11-26 15:56:57.000000000 +0100
@@ -17,6 +17,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <time.h>
 #include <errno.h>
 
 /* OpenDKIM includes */
@@ -45,11 +46,26 @@
 #define	MAXLINE		2048
 #define	MAXREPORTER	256
 
+/* data structures */
+struct table
+{
+	char *          tbl_left;
+	char *          tbl_right;
+};
+
 /* globals */
 char *progname;
 char reporter[MAXREPORTER + 1];
 int verbose;
 
+struct table last_insert_id[] =
+{
+	{ "mysql",      "LAST_INSERT_ID()" },
+	{ "sqlite3",    "LAST_INSERT_ROWID()" },
+	{ "pgsql",      "LASTVAL()" },
+	{ NULL,         NULL }
+};
+
 /*
 **  SANITIZE -- sanitize a string
 **
@@ -147,6 +163,39 @@
 }
 
 /*
+**  SQL_MKTIME -- convert a UNIX time_t (as a string) to an SQL time string
+**
+**  Parameters:
+**     in -- input time
+**     out -- output buffer
+**     outlen -- bytes available at "out"
+**
+**  Return value:
+**     0 -- error
+**     >0 -- bytes of "out" used
+*/
+
+int
+sql_mktime(const char *in, char *out, size_t outlen)
+{
+	time_t convert;
+	struct tm *local;
+	char *p;
+
+	assert(in != NULL);
+	assert(out != NULL);
+
+	errno = 0;
+	convert = strtoul(in, &p, 10);
+	if (errno != 0 || *p != '\0')
+		return 0;
+
+	local = localtime(&convert);
+
+	return strftime(out, outlen, "%Y-%m-%d %H:%M:%S", local);
+}
+
+/*
 **  SQL_GET_INT -- retrieve a single integer from an SQL query
 **
 **  Parameters:
@@ -337,6 +386,7 @@
 	int hdrid;
 	int inversion = -1;
 	char *p;
+	char *lastrow = NULL;
 	char *dbhost = DEFDBHOST;
 	char *dbname = DEFDBNAME;
 	char *dbscheme = DEFDBSCHEME;
@@ -346,6 +396,7 @@
 	char **fields = NULL;
 	odbx_t *db = NULL;
 	char buf[MAXLINE + 1];
+	char timebuf[MAXLINE + 1];
 	char sql[MAXLINE + 1];
 	char safesql[MAXLINE * 2 + 1];
 
@@ -412,6 +463,22 @@
 		}
 	}
 
+	for (c = 0; ; c++)
+	{
+		if (strcasecmp(last_insert_id[c].tbl_left, dbscheme) == 0)
+		{
+			lastrow = last_insert_id[c].tbl_right;
+			break;
+		}
+	}
+
+	if (lastrow == NULL)
+	{
+		fprintf(stderr, "%s: scheme \"%s\" not currently supported\n",
+		        progname, dbscheme);
+		return EX_SOFTWARE;
+	}
+
 	/* try to connect to the database */
 	if (odbx_init(&db, dbscheme, dbhost, dbport) < 0)
 	{
@@ -597,7 +664,7 @@
 					}
 
 					snprintf(sql, sizeof sql,
-					         "SELECT LAST_INSERT_ID()");
+					         "SELECT %s", lastrow);
 
 					repid = sql_get_int(db, sql);
 					if (repid == -1)
@@ -647,7 +714,7 @@
 				}
 
 				snprintf(sql, sizeof sql,
-				         "SELECT LAST_INSERT_ID()");
+				         "SELECT %s", lastrow);
 
 				domid = sql_get_int(db, sql);
 				if (domid == -1)
@@ -703,7 +770,7 @@
 				else
 				{
 					snprintf(sql, sizeof sql,
-					         "SELECT LAST_INSERT_ID()");
+					         "SELECT %s", lastrow);
 
 					addrid = sql_get_int(db, sql);
 					if (addrid == -1)
@@ -740,9 +807,10 @@
 			}
 
 			/* see if this is a duplicate */
+			(void) sql_mktime(fields[4], timebuf, sizeof timebuf);
 			snprintf(sql, sizeof sql,
-			         "SELECT id FROM messages WHERE jobid = '%s' and reporter = %d and msgtime = from_unixtime(%s)",
-			         fields[0], repid, fields[4]);
+			         "SELECT id FROM messages WHERE jobid = '%s' and reporter = %d and msgtime = '%s'",
+			         fields[0], repid, timebuf);
 
 			msgid = sql_get_int(db, sql);
 			if (msgid == -1)
@@ -763,13 +831,14 @@
 				continue;
 			}
 
+			(void) sql_mktime(fields[4], timebuf, sizeof timebuf);
 			snprintf(sql, sizeof sql,
-			         "INSERT INTO messages (jobid, reporter, from_domain, ip, msgtime, size, sigcount, atps, spam) VALUES ('%s', %d, %d, %d, from_unixtime(%s), %s, %s, %s, %s)",
+			         "INSERT INTO messages (jobid, reporter, from_domain, ip, msgtime, size, sigcount, atps, spam) VALUES ('%s', %d, %d, %d, '%s', %s, %s, %s, %s)",
 			         fields[0],	/* jobid */
 			         repid,		/* reporter */
 			         domid,		/* from_domain */
 			         addrid,	/* ip */
-			         fields[4],	/* msgtime */
+			         timebuf,	/* msgtime */
 			         fields[5],	/* size */
 			         fields[6],	/* sigcount */
 			         fields[7],	/* atps */
@@ -783,7 +852,7 @@
 			}
 
 			/* get back the message ID */
-			snprintf(sql, sizeof sql, "SELECT LAST_INSERT_ID()");
+			snprintf(sql, sizeof sql, "SELECT %s", lastrow);
 
 			msgid = sql_get_int(db, sql);
 			if (msgid == -1)
@@ -861,8 +930,8 @@
 					return EX_SOFTWARE;
 				}
 
-				snprintf(sql, sizeof sql,
-				         "SELECT LAST_INSERT_ID()");
+				snprintf(sql, sizeof sql, "SELECT %s",
+				         lastrow);
 
 				domid = sql_get_int(db, sql);
 				if (domid == -1)
@@ -910,7 +979,7 @@
 			}
 
 			/* get back the signature ID */
-			snprintf(sql, sizeof sql, "SELECT LAST_INSERT_ID()");
+			snprintf(sql, sizeof sql, "SELECT %s", lastrow);
 
 			sigid = sql_get_int(db, sql);
 			if (sigid == -1)
@@ -996,9 +1065,11 @@
 			}
 			else
 			{
+				(void) sql_mktime(fields[2], timebuf,
+				                  sizeof timebuf);
 				snprintf(sql, sizeof sql,
-				         "SELECT id FROM messages WHERE jobid = '%s' and reporter = %d and msgtime = from_unixtime(%s)",
-				         fields[0], repid, fields[2]);
+				         "SELECT id FROM messages WHERE jobid = '%s' AND reporter = %d AND msgtime = '%s'",
+				         fields[0], repid, timebuf);
 			}
 
 			msgid = sql_get_int(db, sql);

