Ticket #270: send-quarantine-digests-refactor.patch

File send-quarantine-digests-refactor.patch, 15.3 kB (added by erik@…, 3 years ago)

patch send...digests.pl bug fixes, refactoring per #270

  • send-quarantine-digests.pl

    old new  
    1 #!/usr/bin/perl 
    2 #-T 
     1#!/usr/bin/perl -T 
    32 
    43# $Id$ 
    54 
     
    9897    #           = "received_date DIRECTION" 
    9998    #           = "recipient_id DIRECTION" 
    10099    # Where DIRECTION is ASC or DESC 
    101     my %sort = (); 
    102     $sort{'ham'} = "score DESC";   # puts the high scores at the top 
    103     $sort{'spam'} = "score ASC";   # puts the low scroes at the top 
    104     $sort{'virus'} = "received_date DESC"; 
    105     $sort{'banned_file'} = "received_date DESC"; 
    106     $sort{'bad_header'} = "received_date DESC"; 
     100my %sort = ( 
     101            ham         => "score DESC",  # puts the high scores at the top 
     102            spam        => "score ASC",   # puts the low scroes at the top 
     103            virus       => "received_date DESC", 
     104            banned_file => "received_date DESC", 
     105            bad_header  => "received_date DESC", 
     106           ); 
    107107     
    108108    my $titles = {  'spam'        =>  "Spam Quarantine", 
    109109                    'virus'       =>  "Virus Quarantine", 
     
    121121# End of user-configurable portion.  There should be no need to modify # 
    122122# anything below this point.                                           # 
    123123######################################################################## 
    124  
    125 # The organization of this file makes this a bit obtuse 
    126 my $isPg = 0; 
    127  
    128     # Retrieve the string value associated with a key in the database.cfg file. 
    129     sub get_string_key($$) 
    130     { 
    131        my ($file, $key) = @_; 
    132  
    133        if ($file =~ /\n[ \t]*$key[ \t]*=[ \t]*\"(.*)\"/) { 
    134           return ($1); 
    135        } else { 
    136           die ("Maia: [get_db_string_key] Unable to find $key value in $file\n"); 
    137        } 
    138     } 
    139  
    140     # Read a single value from Maia's configuration table. 
    141     sub get_config_value($$) { 
    142         my($dbh, $key) = @_; 
    143         my($sth, @row, $select); 
    144         my $value = undef; 
    145  
    146         $select = "SELECT " . $key . " FROM maia_config WHERE id = 0"; 
    147         $select = $1 if $select =~ /^(.*)$/si; # untaint 
    148         $sth = $dbh->prepare($select) 
    149                    or die (sprintf("Maia: [get_config_value] Couldn't prepare query: %s", $dbh->errstr)); 
    150         $sth->execute() 
    151             or die (sprintf("Maia: [get_config_value] Couldn't execute query: %s", $dbh->errstr)); 
    152         if (@row = $sth->fetchrow_array()) { 
    153             $value = $row[0]; 
    154         } 
    155         $sth->finish; 
    156  
    157         return $value; 
    158     } 
    159  
    160     #random phrase generated from password generator 
    161     #credit: http://web.uconn.edu/~cdavid/cgi-bin/book/make_password_html.pl 
    162     sub phrase_generate(){   
    163       my $ug    = new Data::UUID;   
    164       my $uuid = $ug->create_hex();   
    165       $uuid =~ s/0x(.*)/$1/;   
    166      
    167       my (@passset,$rnd_passwd,$randum_num);   
    168       my ($randum_num);   
    169       @passset = ('A'..'Z','0'..'9');   
    170       $rnd_passwd = "";   
    171       for (my $i = 0; $i<32;$i++){   
    172         $randum_num = int(rand($#passset+1));   
    173         $rnd_passwd .= @passset[$randum_num];   
    174       }   
    175      
    176       return $uuid . $rnd_passwd ;  
    177     } 
    178  
    179     sub generate_confirm_token($$) 
    180     { 
    181       my ($dbh, $maia_user_id) = @_; 
    182       my $spamexpiry = get_config_value($dbh, "expiry_period"); 
    183       my $hamexpiry = get_config_value($dbh, "ham_cache_expiry_period"); 
    184       my $days = $spamexpiry > $hamexpiry ? $spamexpiry : $hamexpiry; 
    185  
    186       my $unique_string = phrase_generate(); 
    187  
    188       my $insert = "INSERT INTO maia_tokens (token_system, token, data, expires) " . 
    189                    "VALUES ('digest',?,?, " . 
    190                        ($isPg ? "NOW() + INTERVAL ? DAY" 
    191                               : "DATE_ADD(NOW(), INTERVAL ? DAY)") . 
    192                            ")"; 
    193       my $sth = $dbh->prepare($insert); 
    194       $sth->execute($unique_string, $maia_user_id,$days) or die (sprintf("Maia: [send-quarantine-reminders] Couldn't execute query: %s", $dbh->errstr));; 
    195  
    196       return $unique_string; 
    197     } 
     124sub get_string_key($$); 
     125sub get_config_value($$); 
     126sub phrase_generate(); 
     127sub generate_confirm_token($$); 
    198128 
    199129    # Read the database configuration file into memory once 
    200130    open DB_CFG, "<" . $cfg 
     
    209139    # Connect to the database 
    210140    my $dsn = get_string_key($db_cfg, "dsn"); 
    211141    # The organization of this file makes this a bit obtuse 
    212     $isPg = $dsn =~ /^dbi:Pg/; 
     142my $isPg = $dsn =~ /^dbi:Pg/; 
    213143    my $username = get_string_key($db_cfg, "username"); 
    214144    my $password = get_string_key($db_cfg, "password"); 
    215145    my $dbh = DBI->connect($dsn, $username, $password) 
    216146                       or die("Can't connect to SQL database"); 
    217147 
    218     my($select, $sth, $sth2, @row, @row2, $user_id, $user_email); 
     148my($query, $sth, @row, %config_value_cache); 
    219149    my($admin_email, $smtp_server, $smtp_port); 
    220     $select = "SELECT admin_email, " . 
    221                      "smtp_server, " . 
    222                      "smtp_port " . 
    223                "FROM maia_config WHERE id = 0"; 
    224     $sth = $dbh->prepare($select) 
    225                or die (sprintf("Maia: [send-quarantine-reminders] Couldn't prepare query: %s", $dbh->errstr)); 
     150 
     151$query = <<"endSQL;"; 
     152  SELECT admin_email, smtp_server, smtp_port 
     153  FROM   maia_config 
     154  WHERE  id = 0 
     155endSQL; 
     156 
     157$sth = $dbh->prepare($query) 
     158    or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr)); 
    226159    $sth->execute() 
    227         or die (sprintf("Maia: [send-quarantine-reminders] Couldn't execute query: %s", $dbh->errstr)); 
     160    or die (sprintf("Maia: [send-quarantine-digests] Couldn't execute query: %s", $dbh->errstr)); 
     161 
    228162    if (@row = $sth->fetchrow_array()) { 
    229163        $admin_email = $1 if $row[0] =~ /^(.+@.+\..+)$/si; # untaint 
    230164        $smtp_server = $1 if $row[1] =~ /^(.+)$/si; # untaint 
    231165        $smtp_port = $1 if $row[2] =~ /^([1-9]+[0-9]*)$/si; # untaint 
    232166    } 
    233     $sth->finish; 
     167$sth->finish(); 
    234168 
    235169    my $bgcolor; 
    236     my $rowcount; 
    237     my $token; 
    238     my $received_date; 
    239     my $score; 
    240     my $sender; 
    241     my $subject; 
    242170    my $at_least_one = 0; 
    243171    my $timestamp = time(); 
    244172    my ($secs, $mins, $hours, $day, $mon, $year) = localtime($timestamp); 
    245173    my $dbtimestamp = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year+1900, $mon+1, $day, $hours, $mins, $secs); 
    246174 
    247     $select = "SELECT maia_users.id, users.email " . 
    248               "FROM maia_users, users " . 
    249               "WHERE maia_users.primary_email_id = users.id " . 
    250               "AND maia_users.quarantine_digest_interval > 0 " . 
    251               "AND (maia_users.quarantine_digest_interval <= " . 
    252               ($isPg ? "(( ROUND(DATE_PART('epoch', NOW())) - ROUND(DATE_PART('epoch', maia_users.last_digest_sent))) / 60)" 
    253                      : "((UNIX_TIMESTAMP() - UNIX_TIMESTAMP(maia_users.last_digest_sent)) / 60) ") . 
    254                    "OR maia_users.last_digest_sent IS NULL) " . 
    255               "ORDER BY maia_users.id ASC"; 
    256     my $sth3 = $dbh->prepare($select) 
     175my $unixTime = $isPg ? "( ROUND(DATE_PART('epoch', NOW())) - ROUND(DATE_PART('epoch', maia_users.last_digest_sent)))" 
     176                     : "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(maia_users.last_digest_sent)"; 
     177 
     178$query = <<"endSQL;"; 
     179  SELECT   maia_users.id, users.email 
     180  FROM     maia_users, users 
     181  WHERE    maia_users.primary_email_id = users.id 
     182           AND maia_users.quarantine_digest_interval > 0 
     183           AND (maia_users.quarantine_digest_interval <= ($unixTime / 60) 
     184                OR maia_users.last_digest_sent IS NULL) 
     185  ORDER BY maia_users.id ASC 
     186endSQL; 
     187 
     188my $users_sth = $dbh->prepare($query) 
    257189                or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr)); 
    258     $sth3->execute() 
     190$users_sth->execute() 
    259191        or die (sprintf("Maia: [send-quarantine-digests] Couldn't execute query: %s", $dbh->errstr)); 
    260192 
    261     while (my @row3 = $sth3->fetchrow_array()) { 
    262         $user_id = $1 if $row3[0] =~ /^(\d+)$/si; # untaint 
    263         $user_email = $1 if $row3[1] =~ /^(.+@.+\..+)$/si; # untaint 
     193# Preparing the same statement over & over is wasteful. Granted, 
     194# performance doesn't really matter in this application, but there is no 
     195# sense in wasting db resources (which COULD be at a premium). 
     196 
     197my %report_statements; 
     198while (my($element, $sort) = each(%sort)) { 
     199    next if exists $report_statements{$sort}; 
     200 
     201    $query = <<"endSQL;"; 
     202  SELECT   mmr.token, mm.received_date, mm.score, 
     203           mm.sender_email, mm.subject 
     204  FROM     maia_mail AS mm, maia_mail_recipients AS mmr 
     205  WHERE    mm.id = mmr.mail_id 
     206           AND mmr.type = ? 
     207           AND mmr.recipient_id = ? 
     208  ORDER BY mm.$sort 
     209endSQL; 
     210 
     211    $report_statements{$sort} = $dbh->prepare($query) 
     212        or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr)); 
     213} 
     214 
     215$query = <<"endSQL;"; 
     216  UPDATE maia_users SET last_digest_sent = ? WHERE id = ? 
     217endSQL; 
     218my $update_sth = $dbh->prepare($query) 
     219    or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr)); 
     220 
     221my $date_add = $isPg ? "NOW() + INTERVAL ? DAY" 
     222                     : "DATE_ADD(NOW(), INTERVAL ? DAY)"; 
     223$query = <<"endSQL;"; 
     224  INSERT INTO maia_tokens (token_system, token, data, expires) 
     225  VALUES ('digest', ?, ?, $date_add) 
     226endSQL; 
     227 
     228my $confirm_sth = $dbh->prepare($query); 
     229 
     230while (my @row3 = $users_sth->fetchrow_array()) { 
     231    my $user_id = $1 if $row3[0] =~ /^(\d+)$/si; # untaint 
     232    my $user_email = $1 if $row3[1] =~ /^(.+@.+\..+)$/si; # untaint 
    264233 
    265234        #regenerate template vars for every user, to minimize memory usage 
    266235        my %vars = ( 
     
    281250         
    282251        # We need to use an array to Separate Report Elements, since hashes can't keep reliable ordering 
    283252        my $report_count = 0; 
     253    $at_least_one = 0; 
    284254        for my $report_element (@report_order) { 
     255        $sth = $report_statements{$sort{$report_element}}; 
    285256         
    286             $select = "SELECT maia_mail_recipients.token, maia_mail.received_date, maia_mail.score, " . 
    287                 "maia_mail.sender_email, maia_mail.subject " . 
    288                 "FROM maia_mail, maia_mail_recipients " . 
    289                 "WHERE maia_mail.id = maia_mail_recipients.mail_id " . 
    290                 "AND maia_mail_recipients.type = '$report_type{$report_element}' " . 
    291                 "AND maia_mail_recipients.recipient_id = ? " . 
    292                 "ORDER BY maia_mail.$sort{$report_element}"; 
    293  
    294             $sth = $dbh->prepare($select) 
    295                 or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr)); 
    296             $sth->execute($user_id) 
     257        $sth->execute($report_type{$report_element}, $user_id) 
    297258                or die (sprintf("Maia: [send-quarantine-digests] Couldn't execute query: %s", $dbh->errstr)); 
    298259            if ($sth->rows > 0) { 
    299260                $at_least_one = 1; 
    300                 $rowcount = 0; 
     261            my $rowcount = 0; 
    301262                while (@row = $sth->fetchrow_array()) { 
    302                     $token = $1 if $row[0] =~  /^([a-zA-Z0-9]+)$/si; # untaint 
    303                     $received_date = $1 if $row[1] =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})$/si; # untaint 
    304                     $score = $1 if $row[2] =~ /^(-?\d+\.\d+)$/si; # untaint 
    305                     $sender = $1 if $row[3] =~ /^(.+\@.+\..+)$/si; # untaint 
    306                     $subject = $1 if $row[4] =~ /^(.*)$/si; # untaint 
     263                my $token = $1 if $row[0] =~  /^([a-zA-Z0-9]+)$/si; # untaint 
     264                my $received_date = $1 if $row[1] =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})$/si; # untaint 
     265                my $score = $1 if $row[2] =~ /^(-?\d+\.\d+)$/si; # untaint 
     266                my $sender = $1 if $row[3] =~ /^(.+\@.+\..+)$/si; # untaint 
     267                my $subject = $1 if $row[4] =~ /^(.*)$/si; # untaint 
    307268                    if ($subject eq "") { 
    308269                        $subject = "(no subject)"; 
    309270                    } 
     
    313274                    if ($report_element eq 'ham' || $report_element eq 'spam') { 
    314275                        $vars{'list'}[$report_count]{$report_element}[$rowcount]{'score'} =  $score; 
    315276                    } 
     277 
    316278                    $vars{'list'}[$report_count]{$report_element}[$rowcount]{'sender'} =  $sender; 
    317279                    $vars{'list'}[$report_count]{$report_element}[$rowcount]{'subject'} =  $subject; 
    318280                     
    319281                    $rowcount++; 
    320282                } 
     283            $report_count++ 
    321284            } 
    322285            $sth->finish(); 
    323             $report_count++ 
    324286        } 
    325287         
     288    # Send out the e-mail 
     289    if ($at_least_one) { 
    326290        $vars{'confirm_token'} = generate_confirm_token($dbh, $user_id); 
    327291        my $output = ''; 
    328292        my $template = Template->new({INCLUDE_PATH => $template_dir, 
     
    330294        $template->process("digest.tpl", \%vars)  
    331295       || die "Template process failed: ", $template->error(), "\n"; 
    332296 
    333         # Send out the e-mail 
    334         if ($at_least_one) { 
    335297            print "Sending quarantine digest to <" . $user_email . ">\n"; 
     298if(0){ 
    336299            my($smtp) = Net::SMTP->new($smtp_server, Port => $smtp_port); 
    337300            die "Couldn't connect to SMTP server" unless $smtp; 
    338301            $smtp->mail($admin_email); 
     
    342305            $smtp->dataend(); 
    343306            $smtp->quit(); 
    344307 
    345             my $update = "UPDATE maia_users SET last_digest_sent = ? WHERE id = ?"; 
    346             $sth = $dbh->prepare($update) 
    347                        or die (sprintf("Maia: [send-quarantine-digests] Couldn't prepare query: %s", $dbh->errstr)); 
    348             $sth->execute($dbtimestamp, $user_id) 
     308        $update_sth->execute($dbtimestamp, $user_id) 
    349309                or die (sprintf("Maia: [send-quarantine-digests] Couldn't execute query: %s", $dbh->errstr)); 
     310        $update_sth->finish(); 
    350311     } 
    351  
    352  
    353312    } 
    354     $sth3->finish(); 
     313} 
     314 
     315# not strictly necessary, since we're about to disconnect, but good 
     316# policy on the whole. 
     317$users_sth->finish(); 
    355318  
    356319    # Disconnect from the database 
    357320    $dbh->disconnect; 
    358321 
    359322    # We're done. 
    360323    exit; 
     324 
     325# Retrieve the string value associated with a key in the database.cfg file. 
     326sub get_string_key($$) { 
     327    my ($file, $key) = @_; 
     328 
     329    if ($file =~ /\n[ \t]*$key[ \t]*=[ \t]*\"(.*)\"/) { 
     330        return ($1); 
     331    } else { 
     332        die ("Maia: [get_db_string_key] Unable to find $key value in $file\n"); 
     333    } 
     334} 
     335 
     336# Read a single value from Maia's configuration table. 
     337sub get_config_value($$) { 
     338    my($dbh, $key) = @_; 
     339    my($sth, @row, $select); 
     340    my $value = undef; 
     341 
     342    return $config_value_cache{$key} if (exists $config_value_cache{$key}); 
     343 
     344    $select = "SELECT " . $key . " FROM maia_config WHERE id = 0"; 
     345    $select = $1 if $select =~ /^(.*)$/si; # untaint 
     346    $sth = $dbh->prepare($select) 
     347        or die (sprintf("Maia: [get_config_value] Couldn't prepare query: %s", $dbh->errstr)); 
     348    $sth->execute() 
     349        or die (sprintf("Maia: [get_config_value] Couldn't execute query: %s", $dbh->errstr)); 
     350    if (@row = $sth->fetchrow_array()) { 
     351        $value = $row[0]; 
     352    } 
     353    $sth->finish; 
     354    $config_value_cache{$key} = $value; 
     355    return $value; 
     356} 
     357 
     358#random phrase generated from password generator 
     359#credit: http://web.uconn.edu/~cdavid/cgi-bin/book/make_password_html.pl 
     360sub phrase_generate() { 
     361    my $ug    = new Data::UUID; 
     362    my $uuid = $ug->create_hex(); 
     363    $uuid =~ s/0x(.*)/$1/; 
     364 
     365    my (@passset,$rnd_passwd,$randum_num); 
     366    @passset = ('A'..'Z','0'..'9'); 
     367    $rnd_passwd = ""; 
     368    for (my $i = 0; $i<32;$i++) { 
     369        $randum_num = int(rand($#passset+1)); 
     370        $rnd_passwd .= $passset[$randum_num]; 
     371    } 
     372 
     373    return $uuid . $rnd_passwd ; 
     374} 
     375 
     376sub generate_confirm_token($$) { 
     377    my ($dbh, $maia_user_id) = @_; 
     378    my $spamexpiry = get_config_value($dbh, "expiry_period"); 
     379    my $hamexpiry = get_config_value($dbh, "ham_cache_expiry_period"); 
     380    my $days = $spamexpiry > $hamexpiry ? $spamexpiry : $hamexpiry; 
     381 
     382    my $unique_string = phrase_generate(); 
     383 
     384    $confirm_sth->execute($unique_string, $maia_user_id,$days) or die (sprintf("Maia: [send-quarantine-reminders] Couldn't execute query: %s", $dbh->errstr));; 
     385    $confirm_sth->finish(); 
     386    return $unique_string; 
     387}