Support 9210 CSV contacts format.
[PerlMail.git] / perlmail-submit
index 6879f3d..cc76cca 100755 (executable)
@@ -1,13 +1,33 @@
 #! /usr/bin/perl
-#
+# 
 #      $Id$
+# Copyright (C) 2002-2003 Jan Kratochvil <project-PerlMail@jankratochvil.net>
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
 
 use vars qw($VERSION);
 $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; };
 use strict;
 use warnings;
 
-use PerlMail::Config;
+use File::Basename;
+BEGIN {
+       use lib $ENV{"PERLMAIL_BASEDIR"} || File::Basename::dirname($0);
+       use PerlMail::Config;
+       }
 
 use Getopt::Long;
 use DBI;
@@ -18,25 +38,34 @@ use POSIX qw(mktime);
 use Fcntl qw(:flock);
 
 
+my $DBI_CACHE=0;       # Cache DBI requests - may cause: MySQL server has gone away
+
+
 open DBI_PWD,$DBI_pwd or die "open \"$DBI_pwd\": $!";
 $DBI_pwd=<DBI_PWD>;
 close DBI_PWD or warn "close DBI_pwd: $!";
 chomp $DBI_pwd;
 
-my $DBI=DBI->connect_cached("DBI:mysql:database=$DBI_database;host=","$DBI_user",$DBI_pwd,{
+my $DBI;
+sub DBI
+{
+       return $DBI if $DBI_CACHE && $DBI;
+       $DBI=DBI->connect("DBI:mysql:database=$DBI_database;host=","$DBI_user",$DBI_pwd,{
                "PrintError"=>0,        # handled by "RaiseError" below
                "RaiseError"=>1,
                "ShowErrorStatement"=>1,
                "AutoCommit"=>1,
                }) or confess "Failed DBI->connect(): $!";
+       return $DBI;
+}
 
 # $name,@$cols
 sub create_table
 {
 my($name,$cols)=@_;
 
-       eval { $DBI->do("drop table $name"); };
-       $DBI->do("create table $name (".join(",",@$cols).")");
+       eval { DBI()->do("drop table $name"); };
+       DBI()->do("create table $name (".join(",",@$cols).")");
 }
 
 sub initdb
@@ -48,7 +77,7 @@ sub initdb
                                        "retries int null default 0",   # null=>done, 0=not yet tried to submit
                                        ],
                        );
-       $DBI->do("alter table $DB_table add index (retries,id)");
+       DBI()->do("alter table $DB_table add index (retries,id)");
        print "done.\n";
        exit 0;
 }
@@ -65,7 +94,7 @@ sub store
                        "message"=>$message,
                        # assume "retries"=>0,
                        );
-       my $prep=$DBI->prepare_cached("insert into $DB_table (".join(",",keys(%row)).")"
+       my $prep=DBI()->prepare("insert into $DB_table (".join(",",keys(%row)).")"
                        ." values (".join(",",map("?",keys(%row))).")");
        $prep->execute(values(%row));
        print $prep->{"mysql_insertid"}."\n";
@@ -75,6 +104,7 @@ sub forkoff
 {
        my $pid=fork();
        confess if !defined $pid;
+       $DBI=undef();   # Prevent: Server has gone away
        exit 0 if $pid; # parent
        # child
 }
@@ -90,7 +120,7 @@ sub submitonce
                print "LOCKED\n";
                exit 0;
                }
-       my $sth=$DBI->prepare("select id,message from $DB_table where retries is not null"
+       my $sth=DBI()->prepare("select id,message from $DB_table where retries is not null"
                        # process only non-problematic mails during rerun
                        .($submitonce_run==1 ? "" : " and retries=0")
                        ." order by retries asc,id asc");
@@ -99,7 +129,7 @@ sub submitonce
        autoflush STDOUT 1;
        my $sock;
        while (my $row=$sth->fetchrow_hashref()) {
-               $DBI->do("update $DB_table set retries=retries+1 where id=".$row->{"id"});
+               DBI()->do("update $DB_table set retries=retries+1 where id=".$row->{"id"});
                if (!$sock) {
                        $sock=IO::Socket::INET->new(
                                        "PeerAddr"=>$PeerAddr,
@@ -120,7 +150,7 @@ sub submitonce
                        undef $sock;
                        }
                else {
-                       $DBI->do("update $DB_table set retries=null where id=".$row->{"id"});
+                       DBI()->do("update $DB_table set retries=null where id=".$row->{"id"});
                        }
                print $progresschar.$row->{"id"}.($got eq "1" ? "" : "=FAIL");
                $progresschar=",";
@@ -142,21 +172,33 @@ sub submit
        1 while submitonce();
 }
 
-sub pending
+sub print_messages
 {
-       my $sth=$DBI->prepare("select message from $DB_table where retries is not null order by id");
+my($cond)=@_;
+
+       my $sth=DBI()->prepare("select message from $DB_table $cond order by id");
        $sth->execute();
        while (my $row=$sth->fetchrow_hashref()) {
                print $row->{"message"},"\n";
                }
 }
 
+sub pending
+{
+       print_messages("where retries is not null");
+}
+
+sub dump
+{
+       print_messages("");
+}
+
 sub clean
 {
 my($keyword,$interval)=@_;
 
        # FIXME: SQL "now()" is raced against the block above
-       my $sth=$DBI->prepare("select id,time,retries from $DB_table where time>now()");
+       my $sth=DBI()->prepare("select id,time,retries from $DB_table where time>now()");
        $sth->execute();
        while (my $row=$sth->fetchrow_hashref()) {
                warn "Message time in future: ".join(",",map(
@@ -175,7 +217,7 @@ my($keyword,$interval)=@_;
        my $sec=0;
        $sec+=$1 while s/(\d+)s//g;
        die "Interval parse error; left \"$_\", parsed: $interval" if $_ ne "";
-       $sth=$DBI->prepare(($print ? "select id" : "delete")
+       $sth=DBI()->prepare(($print ? "select id" : "delete")
                        ." from $DB_table where retries is null and time<from_unixtime(unix_timestamp()-$sec)");
        $sth->execute();
        if (!$print) {
@@ -188,15 +230,32 @@ my($keyword,$interval)=@_;
                }
 }
 
+my $optwrap_err;
+sub optwrap
+{
+my($func,@args)=@_;
+
+       # Prevent successful return due to --forkoff in the case of failed --store when using:
+       # perlmail-submit --store --forkoff --submit
+       if (!eval { &{$func}(@args); 1; }) {
+               $optwrap_err||=$@||$!;
+               die "!FINISH";
+               die "NOTREACHED";
+               }
+}
 
 $Getopt::Long::ignorecase=0;
-die if !GetOptions(
-                 "initdb" ,\&initdb,
-                 "store"  ,\&store,
-                 "forkoff",\&forkoff,
-                 "submit" ,\&submit,
-                 "pending",\&pending,
-                 "clean:s",\&clean,
+# &GetOptions will return success due to: die "!FINISH"
+# but our error detection is done by $optwrap_err.
+GetOptions(
+                 "initdb" ,sub { optwrap \&initdb,@_; },
+                 "store"  ,sub { optwrap \&store,@_; },
+                 "forkoff",sub { optwrap \&forkoff,@_; },
+                 "submit" ,sub { optwrap \&submit,@_; },
+                 "pending",sub { optwrap \&pending,@_; },
+                 "dump"   ,sub { optwrap \&dump,@_; },
+                 "clean:s",sub { optwrap \&clean,@_; },
                "V|version",sub { print "perlmail-submit: $VERSION\n"; exit 0; },
                );
+die $optwrap_err if defined $optwrap_err;
 exit 0;