@ -13,241 +13,234 @@ use DBI;
use strict;
use strict;
#plugin level variables are here since version 0.02
#plugin level variables are here since version 0.02
my($dsn,$mail_table,$rcpt_table,$user,$passwd,$mail_id);
my ($dsn,$mail_table,$rcpt_table,$user,$passwd,$mail_id);
my($log_header);
my ($log_header);
my($log_all_body,$log_deny_body,$body_table);
my ($log_all_body,$log_deny_body,$body_table);
my($dbh,$sth);
my ($dbh,$sth);
sub register
sub register {
{my ($self,$qp) = (shift,shift);
my ($self,$qp) = (shift,shift);
$self->log(LOGERROR,"Bad count of parameters in log2sql plugin.") if @_ % 2;
$self->log(LOGERROR,"Bad count of parameters in log2sql plugin.") if @_ % 2;
my(%args) = @_;
my (%args) = @_;
$self->register_hook("connect", "connect_handler");
$self->register_hook("connect", "connect_handler");
$self->register_hook("mail", "mail_handler");
$self->register_hook("mail", "mail_handler");
$self->register_hook("rcpt", "rcpt_handler");
$self->register_hook("rcpt", "rcpt_handler");
$self->register_hook("data_post", "data_post_handler");
$self->register_hook("data_post", "data_post_handler");
$self->register_hook("queue", "queue_handler");
$self->register_hook("queue", "queue_handler");
$self->register_hook("deny", "deny_handler") if $args{D};
$self->register_hook("deny", "deny_handler") if $args{D};
$self->register_hook("disconnect", "disconnect_handler");
$self->register_hook("disconnect", "disconnect_handler");
$dsn = 'DBI';
$dsn .= ':'.($args{t} || 'mysql');
$dsn = 'DBI';
$dsn .= ':'.($args{d} || 'qpsmtpd');
$dsn .= ':'.($args{t} || 'mysql');
$dsn .= ';host='.$args{h} if $args{h};
$dsn .= ':'.($args{d} || 'qpsmtpd');
$dsn .= ';port='.$args{P} if $args{P};
$dsn .= ';host='.$args{h} if $args{h};
$dsn .= ';'.$args{o} if $args{o};
$dsn .= ';port='.$args{P} if $args{P};
$dsn .= ';'.$args{o} if $args{o};
$mail_table = $args{m} || 'messages';
$rcpt_table = $args{r} || 'rcpts';
$mail_table = $args{m} || 'messages';
$rcpt_table = $args{r} || 'rcpts';
$user = $args{u} || 'root';
$passwd = $args{p} || '';
$user = $args{u} || 'root';
$passwd = $args{p} || '';
$log_header = $args{H} || undef;
$log_header = $args{H} || undef;
$log_all_body = $args{B} || undef;
$log_deny_body = $args{DB} || undef;
$log_all_body = $args{B} || undef;
$body_table = $args{b} || 'message_body';
$log_deny_body = $args{DB} || undef;
}
$body_table = $args{b} || 'message_body';
}#end register
sub connect_handler {
my ($self,$transaction) = @_;
sub connect_handler
{my ($self,$transaction) = @_;
$self->log(LOGDEBUG,"DSN:$dsn");
$dbh = DBI->connect($dsn,$user,$passwd) || $self->log(LOGERROR,DBI::errstr);
$self->log(LOGDEBUG,"DSN:$dsn");
$dbh = DBI->connect($dsn,$user,$passwd)
# generate mail id
|| $self->log(LOGERROR,DBI::errstr);
$mail_id = $$.'.'.time.'.'.int(rand(10000));
# set note for other plugins
# generate mail id
$transaction->notes('log2sql_mail_id', $mail_id);
$mail_id = $$.'.'.time.'.'.int(rand(10000));
# quote the mail_id for later use:
# set note for other plugins
$mail_id = $dbh->quote($mail_id);
$transaction->notes('log2sql_mail_id', $mail_id);
# quote the mail_id for later use:
my ($sec,$min,$hour,$day,$mon,$year) = localtime;
$mail_id = $dbh->quote($mail_id);
$year += 1900;
$mon += 1;
my ($sec,$min,$hour,$day,$mon,$year) = localtime;
$year += 1900;
my ($statement) = "INSERT INTO ".$mail_table." (mail_id,".
$mon += 1;
$mail_table.".date_day,".
$mail_table.".date_time,".
my($statement) = "INSERT INTO ".$mail_table." (mail_id,".
"remote_ip,remote_host,remote_info) ".
$mail_table.".date_day,".
"VALUES(".
$mail_table.".date_time,".
$mail_id.",".
"remote_ip,remote_host,remote_info) ".
$dbh->quote($year.'-'.$mon.'-'.$day).",".
"VALUES(".
$dbh->quote($hour.':'.$min.':'.$sec).",".
$mail_id.",".
$dbh->quote($self->qp->connection->remote_ip).",".
$dbh->quote($year.'-'.$mon.'-'.$day).",".
$dbh->quote($self->qp->connection->remote_host).",".
$dbh->quote($hour.':'.$min.':'.$sec).",".
$dbh->quote($self->qp->connection->remote_info).")";
$dbh->quote($self->qp->connection->remote_ip).",".
$dbh->quote($self->qp->connection->remote_host).",".
$self->log(LOGDEBUG,"connect_handler statement:".$statement);
$dbh->quote($self->qp->connection->remote_info).")";
$self->log(LOGDEBUG,"connect_handler statement:".$statement);
$dbh->do($statement)
|| $self->log(LOGERROR,$dbh->errstr());
$dbh->do($statement) || $self->log(LOGERROR,$dbh->errstr());
return(DECLINED);
return(DECLINED);
}
}
sub mail_handler
sub mail_handler {
{my ($self, $transaction, $sender) = @_;
my ($self, $transaction, $sender) = @_;
my($statement) = "UPDATE ".$mail_table." SET sender=".
my ($statement) = "UPDATE ".$mail_table." SET sender=".
$dbh->quote($sender->user.'@'.$sender->host).
$dbh->quote($sender->user.'@'.$sender->host).
" WHERE mail_id=".$mail_id;
" WHERE mail_id=".$mail_id;
$self->log(LOGDEBUG,"mail_handler statement:".$statement);
$self->log(LOGDEBUG,"mail_handler statement:".$statement);
$dbh->do($statement)
$dbh->do($statement)
|| $self->log(LOGERROR,$dbh->errstr());
|| $self->log(LOGERROR,$dbh->errstr());
return(DECLINED);
return(DECLINED);
}
}
sub rcpt_handler
sub rcpt_handler {
{my ($self,$transaction,$recipient) = @_;
my ($self,$transaction,$recipient) = @_;
my($statement) = "INSERT INTO ".$rcpt_table." (mail_id,".
my ($statement) = "INSERT INTO ".$rcpt_table." (mail_id,".
"recipient) ".
"recipient) ".
"VALUES(".
"VALUES(".
$mail_id.",".
$mail_id.",".
$dbh->quote($recipient->user.'@'.$recipient->host).")";
$dbh->quote($recipient->user.'@'.$recipient->host).")";
$self->log(LOGDEBUG,"rcpt_handler statement:".$statement);
$self->log(LOGDEBUG,"rcpt_handler statement:".$statement);
$dbh->do($statement)
$dbh->do($statement) || $self->log(LOGERROR,$dbh->errstr());
|| $self->log(LOGERROR,$dbh->errstr());
return(DECLINED);
return(DECLINED);
}
}
sub data_post_handler
sub data_post_handler{
{my($self,$transaction)= @_;
my ($self,$transaction) = @_;
my($header) = $transaction->header();
my ($header) = $transaction->header();
# In data_post handler the header is not actual,
# In data_post handler the header is not actual,
# but in queue handler somethimes there are problems with
# but in queue handler somethimes there are problems with
# header, sizes, subject logs (don't know why?!?!)
# header, sizes, subject logs (don't know why?!?!)
#'touch' this message :-))
#'touch' this message :-))
$header->add("X-Sql-Log-ID",$mail_id);
$header->add("X-Sql-Log-ID",$mail_id);
my $subject = scalar $header->get("Subject");
my $subject = scalar $header->get("Subject");
chomp($subject);
chomp($subject);
# Only keep the first 255 chars to be sure it can be inserted in the table
# Only keep the first 255 chars to be sure it can be inserted in the table
$subject= substr $subject, 0, 254 if (length($subject) > 254);
$subject= substr $subject, 0, 254 if (length($subject) > 254);
my($statement) = "UPDATE ".$mail_table." SET subject=".
my ($statement) = "UPDATE ".$mail_table." SET subject=".
$dbh->quote($subject).
$dbh->quote($subject).
",header_size=".$dbh->quote(length($header->as_string())).
",header_size=".$dbh->quote(length($header->as_string())).
",body_size=".$dbh->quote($transaction->data_size()).
",body_size=".$dbh->quote($transaction->data_size()).
" WHERE mail_id=".$mail_id;
" WHERE mail_id=".$mail_id;
$self->log(LOGDEBUG,"data_post_handler statement:".$statement);
$self->log(LOGDEBUG,"data_post_handler statement:".$statement);
$dbh->do($statement)
$dbh->do($statement)
|| $self->log(LOGERROR,$dbh->errstr());
|| $self->log(LOGERROR,$dbh->errstr());
#if $args{H} then log the message header in the main 'messages' table
#if $args{H} then log the message header in the main 'messages' table
if ($log_header)
if ($log_header){
{ $statement = "UPDATE ".$mail_table." SET ".
$statement = "UPDATE ".$mail_table." SET ".
"header=".$dbh->quote($header->as_string()).
"header=".$dbh->quote($header->as_string()).
"WHERE mail_id=".$mail_id;
"WHERE mail_id=".$mail_id;
$self->log(LOGDEBUG,"data_post_handler log header statement:".$statement);
$self->log(LOGDEBUG,"data_post_handler log header statement:".$statement);
$dbh->do($statement)
$dbh->do($statement)
|| $self->log(LOGERROR,$dbh->errstr());
|| $self->log(LOGERROR,$dbh->errstr());
}#end log geader
}
#if args{B} then log message body in a separated table
#if args{B} then log message body in a separated table
if ($log_all_body)
if ($log_all_body) {
{my (@body) = ($transaction->header()->as_string());
my (@body) = ($transaction->header()->as_string());
$transaction->body_resetpos();
$transaction->body_resetpos();
while(my $line = $transaction->body_getline())
while(my $line = $transaction->body_getline()) {
{ push(@body,$line);
push(@body,$line);
}
}
$statement = "INSERT INTO ".$body_table." (mail_id,body)".
$statement = "INSERT INTO ".$body_table." (mail_id,body)".
" VALUES(".$mail_id.",".$dbh->quote(join('',@body)).")";
" VALUES(".$mail_id.",".$dbh->quote(join('',@body)).")";
$self->log(LOGDEBUG,"data_post body log statement:".$statement);
$self->log(LOGDEBUG,"data_post body log statement:".$statement);
$dbh->do($statement)
$dbh->do($statement)
|| $self->log(LOGERROR,$dbh->errstr());
|| $self->log(LOGERROR,$dbh->errstr());
}#end body log
}
return(DECLINED);
return(DECLINED);
}
}
sub queue_handler
sub queue_handler {
{my ($self,$transaction) = @_;
my ($self,$transaction) = @_;
my($status) = $transaction->header->get('X-Spam-Status') or return (DECLINED);
my ($status) = $transaction->header->get('X-Spam-Status') or return (DECLINED);
my($score) = ($status =~ m/hits=(\d+\.\d+)/)[0] || "0" ;
my ($score) = ($status =~ m/hits=(\d+\.\d+)/)[0] || "0" ;
my($statement) = "UPDATE ".$mail_table." SET ".
my ($statement) = "UPDATE ".$mail_table." SET ".
"spam_status=".$dbh->quote($score).
"spam_status=".$dbh->quote($score).
" WHERE mail_id=".$mail_id;
" WHERE mail_id=".$mail_id;
$self->log(LOGDEBUG,"queue_handler statement:".$statement);
$self->log(LOGDEBUG,"queue_handler statement:".$statement);
$dbh->do($statement)
$dbh->do($statement)
|| $self->log(LOGERROR,$dbh->errstr());
|| $self->log(LOGERROR,$dbh->errstr());
return(DECLINED);
return(DECLINED);
}
}
sub deny_handler
sub deny_handler {
{my ($self,$transaction,$plugin,$code,$msg) = @_;
my ($self,$transaction,$plugin,$code,$msg) = @_;
my($statement) = "UPDATE ".$mail_table." SET ".
my ($statement) = "UPDATE ".$mail_table." SET ".
"deny='YES'".
"deny='YES'".
",deny_plugin=".$dbh->quote($plugin).
",deny_plugin=".$dbh->quote($plugin).
",deny_code=".$dbh->quote($code).
",deny_code=".$dbh->quote($code).
",deny_msg=".$dbh->quote($msg).
",deny_msg=".$dbh->quote($msg).
" WHERE mail_id=".$mail_id;
" WHERE mail_id=".$mail_id;
$self->log(LOGDEBUG,"deny_handler statement:".$statement);
$self->log(LOGDEBUG,"deny_handler statement:".$statement);
$dbh->do($statement)
$dbh->do($statement) || $self->log(LOGERROR,$dbh->errstr());
|| $self->log(LOGERROR,$dbh->errstr());
#if configured to log the body of the denied messages
#if configured to log the body of the denied messages
if ($log_deny_body && ! $log_all_body)
if ($log_deny_body && ! $log_all_body) {
{my (@body) = ($transaction->header()->as_string());
my (@body) = ($transaction->header()->as_string());
$transaction->body_resetpos();
$transaction->body_resetpos();
while(my $line = $transaction->body_getline())
while (my $line = $transaction->body_getline()) {
{ push(@body,$line);
push(@body,$line);
}
}
$statement = "INSERT INTO ".$body_table." (mail_id,body)".
$statement = "INSERT INTO ".$body_table." (mail_id,body)".
" VALUES(".$mail_id.",".$dbh->quote(join('',@body)).")";
" VALUES(".$mail_id.",".$dbh->quote(join('',@body)).")";
$self->log(LOGDEBUG,"data_post body log statement:".$statement);
$dbh->do($statement)
$self->log(LOGDEBUG,"data_post body log statement:".$statement);
|| $self->log(LOGERROR,$dbh->errstr());
}#end body log
$dbh->do($statement) || $self->log(LOGERROR,$dbh->errstr());
}
return(DECLINED);
return(DECLINED);
}
}
sub disconnect_handler
sub disconnect_handler {
{my ($self,$transaction) = @_;
my ($self,$transaction) = @_;
$dbh->disconnect();
$dbh->disconnect();
return(DECLINED);
return(DECLINED);
}
}
=head1 NAME
=head1 NAME
@ -264,25 +257,25 @@ recipients table - one recipient per row. One message could have more than one r
The plugin accepts the following parameters:
The plugin accepts the following parameters:
t - type of the SQL database (mysql, postgresql, etc.)
t - type of the SQL database (mysql, postgresql, etc.)
d - database name (default qpsmtpd)
d - database name (default qpsmtpd)
h - hostname of the database server (optional)
h - hostname of the database server (optional)
P - tcp port of the database server (optional)
P - tcp port of the database server (optional)
o - dsn options (in format like DBI dsn options). For example: "o mysql_socket=/path/to/socket"
o - dsn options (in format like DBI dsn options). For example: "o mysql_socket=/path/to/socket"
m - database table for mail messages (default messages)
m - database table for mail messages (default messages)
r - database table for the recipients (default rcpts)
r - database table for the recipients (default rcpts)
u - database user (default root)
u - database user (default root)
p - database password (default '')
p - database password (default '')
H - log the header of the message in the main table for mail messages (e.g. 'messages'). For example "H Yes".
H - log the header of the message in the main table for mail messages (e.g. 'messages'). For example "H Yes".
=head1 CONFIGURATION OF THE ADDITIONAL FEATURES
=head1 CONFIGURATION OF THE ADDITIONAL FEATURES
You can configure the following additional features:
You can configure the following additional features:
D - log the deny information for denied messages in the 'messages' table. Example "D Yes". The 'deny' hook must be enabled in order to use this feature.
D - log the deny information for denied messages in the 'messages' table. Example "D Yes". The 'deny' hook must be enabled in order to use this feature.
B - log the body of all messages in the separated table (e.g. 'message_body'). Example "B Yes".
B - log the body of all messages in the separated table (e.g. 'message_body'). Example "B Yes".
DB - log the body of the denied messages. D parameter required. Example "DB Yes".
DB - log the body of the denied messages. D parameter required. Example "DB Yes".
b - sql table for the message bodies. Default: 'message_body'.
b - sql table for the message bodies. Default: 'message_body'.
As default the plugin sets a note 'log2sql_mail_id'. This chunk of data is passed to other plugins. As default the plugin sets a field 'X-Sql-Log-ID' in the message header.
As default the plugin sets a note 'log2sql_mail_id'. This chunk of data is passed to other plugins. As default the plugin sets a field 'X-Sql-Log-ID' in the message header.
@ -290,39 +283,40 @@ As default the plugin sets a note 'log2sql_mail_id'. This chunk of data is passe
Here are the CREATE statements for the tables. Assuming the 'messages' table is the table for logging main information for messages, 'rcpts' table is the table for recipients, 'message_body' is optional table if You want to log messages bodies (of the denied messages OR of all messages). This syntax is MySQL syntax. For other databases the syntax could be slightly different.
Here are the CREATE statements for the tables. Assuming the 'messages' table is the table for logging main information for messages, 'rcpts' table is the table for recipients, 'message_body' is optional table if You want to log messages bodies (of the denied messages OR of all messages). This syntax is MySQL syntax. For other databases the syntax could be slightly different.
CREATE TABLE `messages` (
CREATE TABLE `messages` (
`mail_id` varchar(255) NOT NULL default '',
`mail_id` varchar(255) NOT NULL default '',
`time` int(11) default '0',
`date_day` date,
`remote_ip` varchar(255) default NULL,
`date_time` time,
`remote_host` varchar(255) default NULL,
`remote_ip` varchar(255) default NULL,
`remote_info` varchar(255) default NULL,
`remote_host` varchar(255) default NULL,
`sender` varchar(255) default NULL,
`remote_info` varchar(255) default NULL,
`subject` text,
`sender` varchar(255) default NULL,
`header_size` int(11) default NULL,
`subject` varchar(255) default NULL,
`body_size` int(11) default NULL,
`header_size` int(11) default NULL,
`spam_status` float default NULL,
`body_size` int(11) default NULL,
`header` text,
`spam_status` float default NULL,
`deny` set('YES','NO') default NULL,
`header` text,
`deny_plugin` varchar(100) default '',
`deny` set('YES','NO') default NULL,
`deny_code` int(11) default '0',
`deny_plugin` varchar(100) default '',
`deny_msg` text,
`deny_code` int(11) default '0',
PRIMARY KEY (`mail_id`)
`deny_msg` text,
) TYPE=MyISAM;
PRIMARY KEY (`mail_id`)
) TYPE=MyISAM;
CREATE TABLE `rcpts` (
`rcpt_id` int(11) NOT NULL auto_increment,
CREATE TABLE `rcpts` (
`mail_id` varchar(255) NOT NULL default '',
`rcpt_id` int(11) NOT NULL auto_increment,
`recipient` varchar(255),
`mail_id` varchar(255) NOT NULL default '',
PRIMARY KEY (`rcpt_id`)
`recipient` varchar(255),
) TYPE=MyISAM;
PRIMARY KEY (`rcpt_id`)
) TYPE=MyISAM;
CREATE TABLE `message_body` (
`bdy_id` int(11) NOT NULL auto_increment,
CREATE TABLE `message_body` (
`mail_id` varchar(255) NOT NULL default '',
`bdy_id` int(11) NOT NULL auto_increment,
`body` text,
`mail_id` varchar(255) NOT NULL default '',
PRIMARY KEY (`bdy_id`)
`body` text,
) TYPE=MyISAM;
PRIMARY KEY (`bdy_id`)
) TYPE=MyISAM;
=head1 AUTHOR
=head1 AUTHOR