--- maia-1.0.0-postpatch/scripts/send-quarantine-digests.pl	2006-01-25 12:11:59.000000000 -0800
+++ /afs/slackers.net/users/erik/tmp/send-quarantine-digests.pl	2006-02-09 15:28:44.168394774 -0800
@@ -98,12 +98,13 @@
     #           = "received_date DIRECTION"
     #           = "recipient_id DIRECTION"
     # Where DIRECTION is ASC or DESC
-    my %sort = ();
-    $sort{'ham'} = "score DESC";   # puts the high scores at the top
-    $sort{'spam'} = "score ASC";   # puts the low scroes at the top
-    $sort{'virus'} = "received_date DESC";
-    $sort{'banned_file'} = "received_date DESC";
-    $sort{'bad_header'} = "received_date DESC";
+my %sort = (
+	    ham		=> "score DESC",  # puts the high scores at the top
+	    spam	=> "score ASC",   # puts the low scroes at the top
+	    virus	=> "received_date DESC",
+	    banned_file => "received_date DESC",
+	    bad_header  => "received_date DESC",
+	   );
     
     my $titles = {  'spam'        =>  "Spam Quarantine",
                     'virus'       =>  "Virus Quarantine",
@@ -121,80 +122,10 @@
 # End of user-configurable portion.  There should be no need to modify #
 # anything below this point.                                           #
 ########################################################################
-
-# The organization of this file makes this a bit obtuse
-my $isPg = 0;
-
-    # Retrieve the string value associated with a key in the database.cfg file.
-    sub get_string_key($$)
-    {
-       my ($file, $key) = @_;
-
-       if ($file =~ /\n[ \t]*$key[ \t]*=[ \t]*\"(.*)\"/) {
-          return ($1);
-       } else {
-          die ("Maia: [get_db_string_key] Unable to find $key value in $file\n");
-       }
-    }
-
-    # Read a single value from Maia's configuration table.
-    sub get_config_value($$) {
-        my($dbh, $key) = @_;
-        my($sth, @row, $select);
-        my $value = undef;
-
-        $select = "SELECT " . $key . " FROM maia_config WHERE id = 0";
-        $select = $1 if $select =~ /^(.*)$/si; # untaint
-        $sth = $dbh->prepare($select)
-                   or die (sprintf("Maia: [get_config_value] Couldn't prepare query: %s", $dbh->errstr));
-        $sth->execute()
-            or die (sprintf("Maia: [get_config_value] Couldn't execute query: %s", $dbh->errstr));
-        if (@row = $sth->fetchrow_array()) {
-            $value = $row[0];
-        }
-        $sth->finish;
-
-        return $value;
-    }
-
-    #random phrase generated from password generator
-    #credit: http://web.uconn.edu/~cdavid/cgi-bin/book/make_password_html.pl
-    sub phrase_generate(){  
-      my $ug    = new Data::UUID;  
-      my $uuid = $ug->create_hex();  
-      $uuid =~ s/0x(.*)/$1/;  
-    
-      my (@passset,$rnd_passwd,$randum_num);  
-      my ($randum_num);  
-      @passset = ('A'..'Z','0'..'9');  
-      $rnd_passwd = "";  
-      for (my $i = 0; $i<32;$i++){  
-        $randum_num = int(rand($#passset+1));  
-        $rnd_passwd .= @passset[$randum_num];  
-      }  
-    
-      return $uuid . $rnd_passwd ; 
-    }
-
-    sub generate_confirm_token($$)
-    {
-      my ($dbh, $maia_user_id) = @_;
-      my $spamexpiry = get_config_value($dbh, "expiry_period");
-      my $hamexpiry = get_config_value($dbh, "ham_cache_expiry_period");
-      my $days = $spamexpiry > $hamexpiry ? $spamexpiry : $hamexpiry;
-
-      my $unique_string = phrase_generate();
-
-      my $insert = "INSERT INTO maia_tokens (token_system, token, data, expires) " .
-                   "VALUES ('digest',?,?, " .
-		       ($isPg ? "NOW() + INTERVAL ? DAY"
-                              : "DATE_ADD(NOW(), INTERVAL ? DAY)") .
-			   ")";
-      my $sth = $dbh->prepare($insert);
-      $sth->execute($unique_string, $maia_user_id,$days) or die (sprintf("Maia: [send-quarantine-reminders] Couldn't execute query: %s", $dbh->errstr));;
-
-      return $unique_string;
-    }
+sub get_string_key($$);
+sub get_config_value($$);
+sub phrase_generate();
+sub generate_confirm_token($$);
 
     # Read the database configuration file into memory once
     open DB_CFG, "<" . $cfg
@@ -209,58 +140,97 @@
     # Connect to the database
     my $dsn = get_string_key($db_cfg, "dsn");
     # The organization of this file makes this a bit obtuse
-    $isPg = $dsn =~ /^dbi:Pg/;
+my $isPg = $dsn =~ /^dbi:Pg/;
     my $username = get_string_key($db_cfg, "username");
     my $password = get_string_key($db_cfg, "password");
     my $dbh = DBI->connect($dsn, $username, $password)
                        or die("Can't connect to SQL database");
 
-    my($select, $sth, $sth2, @row, @row2, $user_id, $user_email);
+my($query, $sth, @row, %config_value_cache);
     my($admin_email, $smtp_server, $smtp_port);
-    $select = "SELECT admin_email, " .
-                     "smtp_server, " .
-                     "smtp_port " .
-               "FROM maia_config WHERE id = 0";
-    $sth = $dbh->prepare($select)
-               or die (sprintf("Maia: [send-quarantine-reminders] Couldn't prepare query: %s", $dbh->errstr));
+
+$query = <<"endSQL;";
+  SELECT admin_email, smtp_server, smtp_port
+  FROM   maia_config
+  WHERE  id = 0
+endSQL;
+
+$sth = $dbh->prepare($query)
+    or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr));
     $sth->execute()
-        or die (sprintf("Maia: [send-quarantine-reminders] Couldn't execute query: %s", $dbh->errstr));
+    or die (sprintf("Maia: [send-quarantine-digests] Couldn't execute query: %s", $dbh->errstr));
+
     if (@row = $sth->fetchrow_array()) {
         $admin_email = $1 if $row[0] =~ /^(.+@.+\..+)$/si; # untaint
         $smtp_server = $1 if $row[1] =~ /^(.+)$/si; # untaint
         $smtp_port = $1 if $row[2] =~ /^([1-9]+[0-9]*)$/si; # untaint
     }
-    $sth->finish;
+$sth->finish();
 
     my $bgcolor;
-    my $rowcount;
-    my $token;
-    my $received_date;
-    my $score;
-    my $sender;
-    my $subject;
     my $at_least_one = 0;
     my $timestamp = time();
     my ($secs, $mins, $hours, $day, $mon, $year) = localtime($timestamp);
     my $dbtimestamp = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year+1900, $mon+1, $day, $hours, $mins, $secs);
 
-    $select = "SELECT maia_users.id, users.email " .
-              "FROM maia_users, users " .
-              "WHERE maia_users.primary_email_id = users.id " .
-              "AND maia_users.quarantine_digest_interval > 0 " .
-              "AND (maia_users.quarantine_digest_interval <= " .
-              ($isPg ? "(( ROUND(DATE_PART('epoch', NOW())) - ROUND(DATE_PART('epoch', maia_users.last_digest_sent))) / 60)"
-                     : "((UNIX_TIMESTAMP() - UNIX_TIMESTAMP(maia_users.last_digest_sent)) / 60) ") .
-                   "OR maia_users.last_digest_sent IS NULL) " .
-              "ORDER BY maia_users.id ASC";
-    my $sth3 = $dbh->prepare($select)
+my $unixTime = $isPg ? "( ROUND(DATE_PART('epoch', NOW())) - ROUND(DATE_PART('epoch', maia_users.last_digest_sent)))"
+                     : "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(maia_users.last_digest_sent)";
+
+$query = <<"endSQL;";
+  SELECT   maia_users.id, users.email
+  FROM     maia_users, users
+  WHERE    maia_users.primary_email_id = users.id
+           AND maia_users.quarantine_digest_interval > 0
+           AND (maia_users.quarantine_digest_interval <= ($unixTime / 60)
+                OR maia_users.last_digest_sent IS NULL)
+  ORDER BY maia_users.id ASC
+endSQL;
+
+my $users_sth = $dbh->prepare($query)
                 or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr));
-    $sth3->execute()
+$users_sth->execute()
         or die (sprintf("Maia: [send-quarantine-digests] Couldn't execute query: %s", $dbh->errstr));
 
-    while (my @row3 = $sth3->fetchrow_array()) {
-        $user_id = $1 if $row3[0] =~ /^(\d+)$/si; # untaint
-        $user_email = $1 if $row3[1] =~ /^(.+@.+\..+)$/si; # untaint
+# Preparing the same statement over & over is wasteful. Granted,
+# performance doesn't really matter in this application, but there is no
+# sense in wasting db resources (which COULD be at a premium).
+
+my %report_statements;
+while (my($element, $sort) = each(%sort)) {
+    next if exists $report_statements{$sort};
+
+    $query = <<"endSQL;";
+  SELECT   mmr.token, mm.received_date, mm.score,
+           mm.sender_email, mm.subject
+  FROM     maia_mail AS mm, maia_mail_recipients AS mmr
+  WHERE    mm.id = mmr.mail_id
+           AND mmr.type = ?
+           AND mmr.recipient_id = ?
+  ORDER BY mm.$sort
+endSQL;
+
+    $report_statements{$sort} = $dbh->prepare($query)
+	or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr));
+}
+
+$query = <<"endSQL;";
+  UPDATE maia_users SET last_digest_sent = ? WHERE id = ?
+endSQL;
+my $update_sth = $dbh->prepare($query)
+    or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr));
+
+my $date_add = $isPg ? "NOW() + INTERVAL ? DAY"
+		     : "DATE_ADD(NOW(), INTERVAL ? DAY)";
+$query = <<"endSQL;";
+  INSERT INTO maia_tokens (token_system, token, data, expires)
+  VALUES ('digest', ?, ?, $date_add)
+endSQL;
+
+my $confirm_sth = $dbh->prepare($query);
+
+while (my @row3 = $users_sth->fetchrow_array()) {
+    my $user_id = $1 if $row3[0] =~ /^(\d+)$/si; # untaint
+    my $user_email = $1 if $row3[1] =~ /^(.+@.+\..+)$/si; # untaint
 
         #regenerate template vars for every user, to minimize memory usage
         my %vars = (
@@ -281,29 +251,21 @@
         
 	# We need to use an array to Separate Report Elements, since hashes can't keep reliable ordering
 	my $report_count = 0;
+    $at_least_one = 0;
         for my $report_element (@report_order) {
+	$sth = $report_statements{$sort{$report_element}};
 	
-	    $select = "SELECT maia_mail_recipients.token, maia_mail.received_date, maia_mail.score, " .
-		"maia_mail.sender_email, maia_mail.subject " .
-		"FROM maia_mail, maia_mail_recipients " .
-		"WHERE maia_mail.id = maia_mail_recipients.mail_id " .
-		"AND maia_mail_recipients.type = '$report_type{$report_element}' " .
-		"AND maia_mail_recipients.recipient_id = ? " .
-		"ORDER BY maia_mail.$sort{$report_element}";
-
-	    $sth = $dbh->prepare($select)
-		or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr));
-	    $sth->execute($user_id)
+	$sth->execute($report_type{$report_element}, $user_id)
 		or die (sprintf("Maia: [send-quarantine-digests] Couldn't execute query: %s", $dbh->errstr));
 	    if ($sth->rows > 0) {
 		$at_least_one = 1;
-		$rowcount = 0;
+            my $rowcount = 0;
 		while (@row = $sth->fetchrow_array()) {
-		    $token = $1 if $row[0] =~  /^([a-zA-Z0-9]+)$/si; # untaint
-		    $received_date = $1 if $row[1] =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})$/si; # untaint
-		    $score = $1 if $row[2] =~ /^(-?\d+\.\d+)$/si; # untaint
-		    $sender = $1 if $row[3] =~ /^(.+\@.+\..+)$/si; # untaint
-		    $subject = $1 if $row[4] =~ /^(.*)$/si; # untaint
+                my $token = $1 if $row[0] =~  /^([a-zA-Z0-9]+)$/si; # untaint
+                my $received_date = $1 if $row[1] =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})$/si; # untaint
+                my $score = $1 if $row[2] =~ /^(-?\d+\.\d+)$/si; # untaint
+                my $sender = $1 if $row[3] =~ /^(.+\@.+\..+)$/si; # untaint
+                my $subject = $1 if $row[4] =~ /^(.*)$/si; # untaint
 		    if ($subject eq "") {
 			$subject = "(no subject)";
 		    }
@@ -313,16 +275,19 @@
 		    if ($report_element eq 'ham' || $report_element eq 'spam') {
 			$vars{'list'}[$report_count]{$report_element}[$rowcount]{'score'} =  $score;
 		    }
+
 		    $vars{'list'}[$report_count]{$report_element}[$rowcount]{'sender'} =  $sender;
 		    $vars{'list'}[$report_count]{$report_element}[$rowcount]{'subject'} =  $subject;
 		    
 		    $rowcount++;
 		}
+	    $report_count++
 	    }
 	    $sth->finish();
-	    $report_count++
 	}
 	
+    # Send out the e-mail
+    if ($at_least_one) {
         $vars{'confirm_token'} = generate_confirm_token($dbh, $user_id);
         my $output = '';
         my $template = Template->new({INCLUDE_PATH => $template_dir,
@@ -330,9 +295,8 @@
         $template->process("digest.tpl", \%vars) 
        || die "Template process failed: ", $template->error(), "\n";
 
-        # Send out the e-mail
-        if ($at_least_one) {
             print "Sending quarantine digest to <" . $user_email . ">\n";
+
             my($smtp) = Net::SMTP->new($smtp_server, Port => $smtp_port);
             die "Couldn't connect to SMTP server" unless $smtp;
             $smtp->mail($admin_email);
@@ -342,19 +306,83 @@
             $smtp->dataend();
             $smtp->quit();
 
-            my $update = "UPDATE maia_users SET last_digest_sent = ? WHERE id = ?";
-            $sth = $dbh->prepare($update)
-                       or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr));
-            $sth->execute($dbtimestamp, $user_id)
+        $update_sth->execute($dbtimestamp, $user_id)
                 or die (sprintf("Maia: [send-quarantine-digests] Couldn't execute query: %s", $dbh->errstr));
-     }
-
+	$update_sth->finish();
 
     }
-    $sth3->finish();
+}
+
+# not strictly necessary, since we're about to disconnect, but good
+# policy on the whole.
+$users_sth->finish();
  
     # Disconnect from the database
     $dbh->disconnect;
 
     # We're done.
     exit;
+
+# Retrieve the string value associated with a key in the database.cfg file.
+sub get_string_key($$) {
+    my ($file, $key) = @_;
+
+    if ($file =~ /\n[ \t]*$key[ \t]*=[ \t]*\"(.*)\"/) {
+        return ($1);
+    } else {
+        die ("Maia: [get_db_string_key] Unable to find $key value in $file\n");
+    }
+}
+
+# Read a single value from Maia's configuration table.
+sub get_config_value($$) {
+    my($dbh, $key) = @_;
+    my($sth, @row, $select);
+    my $value = undef;
+
+    return $config_value_cache{$key} if (exists $config_value_cache{$key});
+
+    $select = "SELECT " . $key . " FROM maia_config WHERE id = 0";
+    $select = $1 if $select =~ /^(.*)$/si; # untaint
+    $sth = $dbh->prepare($select)
+        or die (sprintf("Maia: [get_config_value] Couldn't prepare query: %s", $dbh->errstr));
+    $sth->execute()
+        or die (sprintf("Maia: [get_config_value] Couldn't execute query: %s", $dbh->errstr));
+    if (@row = $sth->fetchrow_array()) {
+        $value = $row[0];
+    }
+    $sth->finish;
+    $config_value_cache{$key} = $value;
+    return $value;
+}
+
+#random phrase generated from password generator
+#credit: http://web.uconn.edu/~cdavid/cgi-bin/book/make_password_html.pl
+sub phrase_generate() {
+    my $ug    = new Data::UUID;
+    my $uuid = $ug->create_hex();
+    $uuid =~ s/0x(.*)/$1/;
+
+    my (@passset,$rnd_passwd,$randum_num);
+    @passset = ('A'..'Z','0'..'9');
+    $rnd_passwd = "";
+    for (my $i = 0; $i<32;$i++) {
+        $randum_num = int(rand($#passset+1));
+        $rnd_passwd .= $passset[$randum_num];
+    }
+
+    return $uuid . $rnd_passwd ;
+}
+
+sub generate_confirm_token($$) {
+    my ($dbh, $maia_user_id) = @_;
+    my $spamexpiry = get_config_value($dbh, "expiry_period");
+    my $hamexpiry = get_config_value($dbh, "ham_cache_expiry_period");
+    my $days = $spamexpiry > $hamexpiry ? $spamexpiry : $hamexpiry;
+
+    my $unique_string = phrase_generate();
+
+    $confirm_sth->execute($unique_string, $maia_user_id,$days) or die (sprintf("Maia: [send-quarantine-reminders] Couldn't execute query: %s", $dbh->errstr));;
+    $confirm_sth->finish();
+    return $unique_string;
+}

