head	1.14;
access;
symbols;
locks; strict;
comment	@# @;


1.14
date	2001.08.28.21.46.26;	author kmaples;	state Exp;
branches;
next	1.13;

1.13
date	2001.08.16.23.13.01;	author kmaples;	state Exp;
branches;
next	1.12;

1.12
date	2001.08.16.18.09.07;	author kmaples;	state Exp;
branches;
next	1.11;

1.11
date	2001.08.16.17.03.11;	author kmaples;	state Exp;
branches;
next	1.10;

1.10
date	2001.08.14.23.08.47;	author kmaples;	state Exp;
branches;
next	1.9;

1.9
date	2001.07.31.20.10.07;	author kmaples;	state Exp;
branches;
next	1.8;

1.8
date	2001.07.25.20.20.17;	author kmaples;	state Exp;
branches;
next	1.7;

1.7
date	2001.07.12.20.40.11;	author kmaples;	state Exp;
branches;
next	1.6;

1.6
date	2001.07.12.05.47.18;	author kmaples;	state Exp;
branches;
next	1.5;

1.5
date	2001.07.03.06.02.22;	author kmaples;	state Exp;
branches;
next	1.4;

1.4
date	2001.06.14.00.09.02;	author kmaples;	state Exp;
branches;
next	1.3;

1.3
date	2001.06.13.01.08.38;	author kmaples;	state Exp;
branches;
next	1.2;

1.2
date	2001.05.29.23.25.21;	author kmaples;	state Exp;
branches;
next	1.1;

1.1
date	2001.04.30.20.47.10;	author kmaples;	state Exp;
branches;
next	;


desc
@@


1.14
log
@Issue number: 4053
Removed 'platform' and 'operating system' from database enums(); both are
now abstracted into their own tables, though no interface is currently being
provided for making edits to these fields.  Cleaned up and refactored code
relating to handling of normalized/denormalized data, abstracted and refactored
misc. table handling routines, improved internal caching of db results, etc.
@
text
@#!/usr/bin/perl
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Issuezilla Issue Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Terry Weissman <terry@@mozilla.org>,
#                 Bryce Nesbitt <bryce-mozilla@@nextbus.com>
#                 Dan Mosedale <dmose@@mozilla.org>

# To recreate the shadow database,  run "processmail regenerate" .

#use diagnostics;
use strict;
use RelationSet;
use IzUsersAndGroups;

require "globals.pl";
require "CGI.pl";

# Begin session (CGI.pl):
initSession();

$| = 1;

umask(077);

# Global for number of file locks on the maillock file:
$::lockcount      = 0;

# Flag that the shadow database is to be rebuilt: 
my $regenerate    = 0;

# Global for email address to exclude from notification:
my $nametoexclude = "";

# Global flag indicating that the above was excluded:
my $didexclude = 0;

# Global hash of users who have been been emailed, e.g.
# '$seen{'me@@foo.org'} = 1;'
my %seen;

# Global array of email addresses of users which have been mailed:
my @@sentlist;

# Global array of users to CC:
my @@forcecc;

# To confirm that the right values are getting passed in: 
#warn "processmail: db $::db_name\n";
#warn "processmail: data $::data_dir\n";
#warn "processmail: shad $::shadow_dir\n";

#-----------------
sub SendMail {
#-----------------
    # Perform the actual mail-related nonesense
    ## NPM -- removed "sendmailnow" param, not relevant to qmail
    
    my $mailtext = shift;
    my $mailcmd  = '|/var/qmail/bin/qmail-inject -froot';
    
    if(open(SENDMAIL, $mailcmd)){
    	print SENDMAIL $mailtext;
    	close SENDMAIL;
    } else {
    	return(0);
    }
    return(1);
}
#-----------------
sub Lock {
#-----------------
    if ($::lockcount <= 0) {
        $::lockcount = 0;
        if (!open(LOCKFID, ">>$::data_dir/maillock")) {
            mkdir "$::data_dir", 0700;
            chmod 0700, "$::data_dir";
            open(LOCKFID, ">>$::data_dir/maillock") || die "Can't open lockfile.";
        }
        my $val = flock(LOCKFID,2);
        if (!$val) { # '2' is magic 'exclusive lock' const.
            print "Lock failed: $val\n";
        }
        chmod 0600, "$::data_dir/maillock";
    }
    $::lockcount++;
}
#-----------------
sub Unlock {
#-----------------
    $::lockcount--;
    if ($::lockcount <= 0) {
        flock(LOCKFID,8);       # '8' is magic 'unlock' const.
        close LOCKFID;
    }
}

#-----------------
sub FileSize {
#-----------------
    my ($filename) = (@@_);
    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
        $atime,$mtime,$ctime,$blksize,$blocks)
        = stat($filename);
    if (defined $size) {
        return $size;
    }
    return -1;
}

#-----------------
sub Different {
#-----------------
    my ($file1, $file2) = (@@_);
    my $size1 = FileSize($file1);
    my $size2 = FileSize($file2);
    if ($size1 != $size2) {
        return 1;
    }
    open(FID1, "<$file1") || die "Can't open $file1";
    open(FID2, "<$file2") || die "Can't open $file2";
    my $d1; 
    my $d2; 
    if (read(FID1, $d1, $size1) ne $size1) {
        die "Can't read $size1 bytes from $file1";
    }
    if (read(FID2, $d2, $size2) ne $size2) {
        die "Can't read $size2 bytes from $file2";
    }
    close FID1;
    close FID2;
    return ($d1 ne $d2);
}
#-----------------
sub DescCC {
#-----------------
    my $cclist = shift();

    return "" if ( $cclist->size() == 0 );

    return "Cc: " . $cclist->toString() . "\n";
}
#-----------------
sub DescDependencies {
#-----------------
    my ($id) = (@@_);
    if (!Param("usedependencies")) {
        return "";
    }
    my $result = "";
    my $me = "blocked";
    my $target = "dependson";
    my $title = "IssuesThisDependsOn";
    for (1..2) {
        SendSQL("select $target from dependencies where $me = $id order by $target");
        my @@list;
        while (MoreSQLData()) {
            push(@@list, FetchOneColumn());
        }
        if (@@list) {
            my @@verbose;
            my $count = 0;
            foreach my $i (@@list) {
                SendSQL("select issue_status, resolution from issues where issue_id = $i");
                my ($issue_status, $resolution) = (FetchSQLData());
                my $desc;
                if ($issue_status eq "NEW" || $issue_status eq "STARTED" ||
                    $issue_status eq "REOPENED") {
                    $desc = "";
                } else {
                    $desc = "[$resolution]";
                }
                push(@@verbose, $i . "$desc");
                $count++;
            }
            if ($count > 5) {
                $result .= "$title: Big list (more than 5) has been omitted\n";
            } else {
                $result .= "$title: " . join(', ', @@verbose) . "\n";
            }
        }
        my $tmp = $me;
        $me = $target;
        $target = $tmp;
        $title = "OtherIssuesDependingOnThis";
    }
    return $result;
}
#-----------------
sub GetIssueText {
#-----------------
    my ($id) = (@@_);
    undef %::issue;
    
    my @@collist = ("issue_id", "component", "version", "rep_platform", "op_sys",
                   "issue_status", "resolution", "priority", "issue_type",
                   "assigned_to", "reporter", "issue_file_loc",
                   "short_desc", "subcomponent", "qa_contact", "target_milestone",
                   "status_whiteboard");

    my $query = "select " . join(", ", @@collist) .
        " from issues where issue_id = $id";

    SendSQL($query);

    my @@row;
    if (!(@@row = FetchSQLData())) {
        return "";
    }
    foreach my $field (@@collist) {
        $::issue{$field} = shift @@row;
        if (!defined $::issue{$field}) {
            $::issue{$field} = "";
        }
    }

    # Perform translation of ids to strings:
    $::issue{'assigned_to'}  = getUsernameFromId($::issue{'assigned_to'});
    $::issue{'reporter'}     = getUsernameFromId($::issue{'reporter'});
    $::issue{'op_sys'}       = GetOpSysNameFromId($::issue{'op_sys'});
    $::issue{'rep_platform'} = GetPlatformNameFromId($::issue{'rep_platform'});

    my $qa_contact = "";
    my $target_milestone = "";
    my $status_whiteboard = "";

    if (Param('useqacontact') && $::issue{'qa_contact'} > 0) {
        $::issue{'qa_contact'} = getUsernameFromId($::issue{'qa_contact'});
        $qa_contact = "$::issue{'qa_contact'}\n";
    } else {
        $::issue{'qa_contact'} = "";
    }

    if (Param('usetargetmilestone') && $::issue{'target_milestone'} ne "") {
        $target_milestone = "TargetMilestone: $::issue{'target_milestone'}\n";
    }
    if (Param('usestatuswhiteboard') && $::issue{'status_whiteboard'} ne "") {
        $status_whiteboard = "StatusWhiteboard: $::issue{'status_whiteboard'}\n";
    }

    $::issue{'long_desc'} = GetLongDescriptionAsText($id);

    my $cclist = new RelationSet();
    
    $cclist->mergeFromDB("select who from cc where issue_id = $id");
    
    my @@voterlist;
    SendSQL("select votes.who from votes where votes.issue_id = $id");
    
    while (MoreSQLData()) {
        my $v = FetchOneColumn();
        push(@@voterlist, getUsernameFromId($v));
    }
    
    $::issue{'cclist'} = $cclist->toString();
    $::issue{'voterlist'} = join(',', @@voterlist);

    if (Param("prettyasciimail")) {
        my $short_desc;
        # Add the short description if permitted:
        $short_desc = $::iz_send_diff ? $::issue{'short_desc'} : 'SUMMARY';
		# Format the data in the prettyascii format:
        my $prettymail = FormatPrettyAscii(
        	$short_desc, $id, $::issue{'component'}, 
        	$::issue{'issue_status'}, $::issue{'version'}, $::issue{'resolution'},
        	$::issue{'rep_platform'}, $::issue{'issue_type'}, $::issue{'op_sys'}, 
        	$::issue{'priority'}, $::issue{'subcomponent'}, $::issue{'assigned_to'},
        	$::issue{'reporter'}, $qa_contact, DescCC($cclist),
        	$target_milestone, ${status_whiteboard}, $::issue{'issue_file_loc'},
        	DescDependencies($id)       
        );
        
        # Add the longdescription if permitted:
        if($::iz_send_diff){
            $prettymail .= 
                '|                              DESCRIPTION                                   |';
            $prettymail .= "\n";   
            $prettymail .= $::issue{'long_desc'};
        }  
        
        return($prettymail);

    } else {
        #KPM: Note: tests of '$::iz_send_diff' are to filter out
        # 'sensitive' information from the emails if that flag
        # is set in localconfig: 

        my $mailtext = "Issue\#: $id
Component: $::issue{'component'}
Version: $::issue{'version'}
Platform: $::issue{'rep_platform'}
OS/Version: $::issue{'op_sys'}
Status: $::issue{'issue_status'}   
Resolution: $::issue{'resolution'}
Issue type: $::issue{'issue_type'}
Priority: $::issue{'priority'}
Subcomponent: $::issue{'subcomponent'}
AssignedTo: $::issue{'assigned_to'}                            
ReportedBy: $::issue{'reporter'}               
$qa_contact$target_milestone${status_whiteboard}URL: $::issue{'issue_file_loc'}\n";

        $mailtext .= DescCC($cclist) . "\n";
        $mailtext .= "Summary: $::issue{'short_desc'}\n" if $::iz_send_diff;
        $mailtext .= DescDependencies($id) . "\n";
        $mailtext .= "$::issue{'long_desc'}\n" if $::iz_send_diff;

        return($mailtext);
    }
}
#-----------------
sub fixaddresses {
#-----------------
    my ($field, $list) = (@@_);
    my @@result;
    foreach my $username (@@$list) {
        if (!defined $username || $username eq "") {
            next;
        }

        my $emailnotification = getEmailPreferenceFromUsername($username);       
        next unless usernameCanViewIssue($username, $::issue{'issue_id'});        
		
		# Skip disabled users:
		next unless usernameIsActive($username);
		
        if ($emailnotification eq "CConly") {
            if ($field ne "cc") {
                next;
            }
        }
        if ($emailnotification eq "ExcludeSelfChanges" &&
           (lc($username) eq $nametoexclude)) {
            $didexclude = 1;
            next;
        }
        
        if (!defined $::nomail{$username} && !defined $seen{$username}) {
            push(@@result, NormalizeEmailAddress($username));
            $seen{$username} = 1;
        }
    }
    return join(", ",  @@result);
}
#-----------------
sub Log {
#-----------------
    my ($str) = (@@_);
    Lock();
    open(FID, ">>$::data_dir/maillog") || die "Can't write to $::data_dir/maillog";
    print FID time2str("%D %H:%M", time()) . ": $str\n";
    close FID;
    Unlock();
}
#-----------------
sub FormatTriple {
#-----------------
    my ($a, $b, $c) = (@@_);

    # Zero out the line accumulator:
    local $^A = '';

    my $temp = formline << 'END;', $a, $b, $c;
^>>>>>>>>>>>>>>>>>>>>>>>>>|^<<<<<<<<<<<<<<<<<<<<<<<<<|^<<<<<<<<<<<<<<<<<<<<<<<<<~~
END;

    return $^A;
}
#-----------------
sub FormatDouble {
#-----------------
    my ($a, $b) = (@@_);
    $a .= ":";

    # Zero out the line accumulator:
    local $^A = '';

    my $temp = formline << 'END;', $a, $b;
^>>>>>>>>>>>>>>>>>>>>>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
END;

    return $^A;
}
#-----------------
sub FormatPrettyAscii {
#-----------------
    # Grab the values to format:
    my @@vals = @@_;
    
    # Zero out the line accumulator:
    local $^A = '';
    
    my $text = formline <<'END;', @@vals;
+============================================================================+
| @@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
+----------------------------------------------------------------------------+
|      Issue #: @@<<<<<<<<<<<              Component: @@<<<<<<<<<<<<<<<<<<<<<< |
|       Status: @@<<<<<<<<<<<<<<<<<<         Version: @@<<<<<<<<<<<<<<<<<<<<<< |
|   Resolution: @@<<<<<<<<<<<<<<<<<<        Platform: @@<<<<<<<<<<<<<<<<<<<<<< |
|   Issue type: @@<<<<<<<<<<<<<<<<<<      OS/Version: @@<<<<<<<<<<<<<<<<<<<<<< |
|     Priority: @@<<<<<<<<<<<<<<<<<<    Subcomponent: @@<<<<<<<<<<<<<<<<<<<<<< |
+----------------------------------------------------------------------------+
|  Assigned To: @@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
|  Reported By: @@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
|  ~QA Contact: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
|  ~   CC list: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
+----------------------------------------------------------------------------+
| ~  Milestone: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
|~  Whiteboard: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
|          URL: @@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
|~Dependencies: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
+============================================================================+
END;
    return($^A);

}
#-----------------
sub NewProcessOneIssue {
#-----------------
    my ($id) = (@@_);

    my @@headerlist;
    my %values;
    my %defmailhead;
    my %fielddescription;

    my $msg = "";

    SendSQL("SELECT name, description, mailhead FROM fielddefs " .
            "ORDER BY sortkey");
            
    while (MoreSQLData()) {
        my ($field, $description, $mailhead) = (FetchSQLData());
        push(@@headerlist, $field);
        $defmailhead{$field} = $mailhead;
        $fielddescription{$field} = $description;
    }
        
    SendSQL("SELECT " . join(',', @@::log_columns) . ", lastdiffed, now() " .
            "FROM issues WHERE issue_id = $id");
            
    my @@row = FetchSQLData();
    foreach my $i (@@::log_columns) {
        $values{$i} = shift(@@row);
    }
    my ($start, $end) = (@@row);
    my $ccSet = new RelationSet();
    $ccSet->mergeFromDB("SELECT who FROM cc WHERE issue_id = $id");
    $values{'cc'} = $ccSet->toString();
    
    my @@voterlist;
    SendSQL("select votes.who from votes where votes.issue_id = $id");
    
    while (MoreSQLData()) {
        my $v = FetchOneColumn();
        push(@@voterlist, getUsernameFromId($v));
    }

    $values{'assigned_to'}    = getUsernameFromId($values{'assigned_to'});
    $values{'reporter'}       = getUsernameFromId($values{'reporter'});

    if ($values{'qa_contact'}) {
        $values{'qa_contact'} = getUsernameFromId($values{'qa_contact'});
    }

    my @@diffs;


    SendSQL("SELECT who, fielddefs.description, " .
            "       issue_when, oldvalue, newvalue " .
            "FROM issues_activity, fielddefs " .
            "WHERE issue_id = $id " .
            "  AND fielddefs.fieldid = issues_activity.fieldid " .
            "  AND issue_when > '$start' " .
            "  AND issue_when <= '$end' " .
            "ORDER BY issue_when"
            );

    while (MoreSQLData()) {
        my @@row = FetchSQLData();

        # Convert 'who' to login name:
        $row[0] = getUsernameFromId($row[0]);
                


        push(@@diffs, \@@row);
    }

    my $difftext = "";
    my $lastwho = "";
    foreach my $ref (@@diffs) {
        my ($who, $what, $when, $old, $new) = (@@$ref);
        if ($who ne $lastwho) {
            $lastwho = $who;
            $difftext .= "\n" . NormalizeEmailAddress($who) . " changed:\n\n";
            $difftext .= FormatTriple("What    ", "Old Value", "New Value");
            $difftext .= ('=' x 80) . "\n";
        }
        # Check if we're suppressing summaries:
        if($what =~ m/Summary/ && !$::iz_send_diff){
            ($old, $new) = ('---', '---');  
        }  

        $difftext .= FormatTriple($what, $old, $new);
        $difftext .= ('-' x 80) . "\n";
    }

    $difftext = trim($difftext);


    my $deptext = "";

    SendSQL("SELECT issues_activity.issue_id, fielddefs.name, " .
            "       oldvalue, newvalue " .
            "FROM issues_activity, dependencies, fielddefs ".
            "WHERE issues_activity.issue_id = dependencies.dependson " .
            "  AND dependencies.blocked = $id " .
            "  AND fielddefs.fieldid = issues_activity.fieldid" .
            "  AND (fielddefs.name = 'issue_status' " .
            "    OR fielddefs.name = 'resolution') " .
            "  AND issue_when > '$start' " .
            "  AND issue_when <= '$end' " .
            "ORDER BY issue_when, issue_id");
    
    my $thisdiff = "";
    my $lastissue = "";
    my $interestingchange = 0;
    while (MoreSQLData()) {
        my ($issue, $what, $old, $new) = (FetchSQLData());
        if ($issue ne $lastissue) {
            if ($interestingchange) {
                $deptext .= $thisdiff;
            }
            $lastissue = $issue;
            $thisdiff =
                "\nThis issue depends on issue $issue, which changed state:\n\n";
            $thisdiff .= FormatTriple("What    ", "Old Value", "New Value");
            $thisdiff .= ('-' x 80) . "\n";
            $interestingchange = 0;
        }
        $thisdiff .= FormatTriple($fielddescription{$what}, $old, $new);
        $difftext .= ('-' x 80) . "\n";
        if ($what eq 'issue_status' && IsOpenedState($old) ne IsOpenedState($new)) {
            $interestingchange = 1;
        }
    }
    if ($interestingchange) {
        $deptext .= $thisdiff;
    }

    $deptext = trim($deptext);

    if ($deptext) {
        $difftext = trim($difftext . "\n\n" . $deptext);
    }

    my $newcomments = GetLongDescriptionAsText($id, $start, $end);  
    
    my $count = 0;
    my @@personlist = ($values{'assigned_to'}, $values{'reporter'},
                      split(/,/, $values{'cc'}),
                      @@voterlist,
                      @@forcecc);
    if ($values{'qa_contact'}) { push @@personlist, $values{'qa_contact'} }
    for my $person (@@personlist) {
        $count++;
        next unless $person;
        &NewProcessOnePerson($person, $count, \@@headerlist, \%values,
                             \%defmailhead, \%fielddescription, $difftext, 
                             $newcomments, $start, $id, 1);
    }

    SendSQL("UPDATE issues SET lastdiffed = '$end', delta_ts = delta_ts " .
            "WHERE issue_id = $id");
}
#-----------------
sub NewProcessOnePerson ($$\@@\%\%\%$$$$) {
#-----------------
	# Note: $person is username, NOT email address:
    my ($person, $count, $hlRef, $valueRef, $dmhRef, $fdRef, $difftext, 
        $newcomments, $start, $id, $checkWatchers) = @@_;

    my %values = %$valueRef;
    my @@headerlist = @@$hlRef;
    my %defmailhead = %$dmhRef;
    my %fielddescription = %$fdRef;

    if ($seen{$person}) {
      return;
    }

    my %user = %{ getUserRecordFromUsername($person) };    
    my ($userid, $emailnotification, $newemailtech) = 
        ($user{'userid'}, $user{'emailnotification'}, $user{'newemailtech'});
    my @@groups = getGroupsUserBelongsToFromUsername($person);

    # check for watchers, and recurse if we find any, but tell the
    # recursive call not to check for watchers 
    #
    if (Param("supportwatchers") && $checkWatchers)  {
      my $personId   = getIdFromUsername($person);
      my $watcherSet = new RelationSet();
      $watcherSet->mergeFromDB("SELECT watcher FROM watch WHERE" .
                               " watched = $personId");

      foreach my $watcher ( $watcherSet->toArray() ) {
        my $name = getUsernameFromId($watcher);
        &NewProcessOnePerson($name, $count, \@@headerlist, \%values,
                            \%defmailhead, \%fielddescription, $difftext, 
                            $newcomments, $start, $id, 0);
      }

    }

    if (!$newemailtech || !Param('newemailtech')) {
      return;
    }
    $seen{$person} = 1;

    # if this person doesn't have permission to see info on this issue, 
    # return.
    #
    # XXX - I _think_ this currently means that if an issue is suddenly given
    # more restrictive permissions, people without those permissions won't
    # see the action of restricting the issue itself; the issue will just 
    # quietly disappear from their radar.
    #
    
    return unless usernameCanViewIssue($person, $id);
    
    if ($emailnotification eq "ExcludeSelfChanges" &&
        lc($person) eq $nametoexclude) {
      $didexclude = 1;
      return;
    }

    # "$count < 3" means "this person is either assigned_to or reporter"
    #
    if ($emailnotification eq "CCOnly" && $count < 3) {
      return;
    }
    
    my %mailhead = %defmailhead;
    
    my $head = "";
    
    foreach my $f (@@headerlist) {

      if ($mailhead{$f}) {

        # Since we're going to be mailing this out, time to translate
        # any values-by-id back to their strings:
        my $fid = GetFieldIdFromName($f);
        my $value = DenormalizeFieldIdData($fid, $values{$f});

        if (!defined $value) {
          # Probaby ought to whine or something. ###
          next;
        }
        my $desc = $fielddescription{$f};

        #KPM: check to see if we're suppressing summary
        # descriptions:
        if($desc =~ m/Summary/ && !$::iz_send_diff){
            next;
        }

        $head .= FormatDouble($desc, $value);
      }
    }
    
    if ($difftext eq "" && $newcomments eq "") {
      # Whoops, no differences!
      return;
    }
    
    my $isnew = ($start !~ m/[1-9]/);
    
    my %substs;

    my $email_addy = NormalizeEmailAddress($person);
# 09/13/2000 cyeh@@bluemartini.com
# If an issue is changed, don't put the word "Changed" in the subject mail
# since if the issue didn't change, you wouldn't be getting mail
# in the first place! see http://issuezilla.mozilla.org/show_bug.cgi?id=29820
# for details.
    $substs{"neworchanged"} = $isnew ? "New" : "";
    $substs{"to"} = $email_addy;
    $substs{"cc"} = '';
    $substs{"issueid"} = $id;

    if($::iz_send_diff){
        # It's ok to send bug details via email:    
        if ($isnew) {
          $substs{"diffs"} = $head . "\n\n" . $newcomments;
        } else {
          $substs{"diffs"} = $difftext . "\n\n" . $newcomments;
        }
        $substs{"summary"} = $values{'short_desc'};
    } else {
        # It's NOT ok to send bug details via email:
        if ($isnew) {
          $substs{"diffs"} = $head . "\n\n";
        } else {
          $substs{"diffs"} = $difftext . "\n\n";
        }
        $substs{"summary"} = '-summary-';
    } 


    my $template = Param("newchangedmail");

    my $msg = PerformSubsts($template, \%substs);

    my $sendmailparam = "-ODeliveryMode=deferred";
    if (Param("sendmailnow")) {
       $sendmailparam = "";
    }

    SendMail( trim($msg) . "\n" );

    push(@@sentlist, $person);
    
}


#-----------------
sub ProcessOneIssue {
#-----------------
  # unusual way of saying '$i = shift':
  my $i = $_[0];

  # looks like a passthrough:
  NewProcessOneIssue($i);

  my $old = "$::shadow_dir/$i";
  my $new = "$::shadow_dir/$i.tmp.$$";
  my $diffs = "$::shadow_dir/$i.diffs.$$";
  my $verb = "Changed";

  # build the shadow dir, if not present (?), and create the old file, if need be:  
  if (!stat($old)) {
    mkdir "$::shadow_dir", 0700;
    chmod 0700, "$::shadow_dir";
        open(OLD, ">$old") || die "Couldn't create null $old";
        close OLD;
        $verb = "New";
  }
  my $text = GetIssueText($i);
  if ($text eq "") {
      die "Couldn't find issue $i.";
  }
  if(open(FID, ">$new")){
        print FID $text;
        close FID;
  }
  if (Different($old, $new)) {
      system("diff -c -b -L Old $old -L New $new > $diffs");
      my $tolist = fixaddresses("to",
                                [$::issue{'assigned_to'}, $::issue{'reporter'},
                                 $::issue{'qa_contact'}]);
      my @@combinedcc;
      foreach my $v (split(/,/, "$::issue{'cclist'},$::issue{'voterlist'}")) {
          push @@combinedcc, $v;
      }
      push (@@combinedcc, (@@forcecc));
      my $cclist = fixaddresses("cc", \@@combinedcc);
      my $logstr = "Issue $i $verb";
      if ($tolist ne "" || $cclist ne "") {
          my %substs;

          $substs{"fullissuereport"} = $text; # added ability to include the full issue report
          $substs{"to"} = $tolist;
          $substs{"cc"} = $cclist;
          $substs{"issueid"} = $i;
          $substs{"diffs"} = "";
          open(DIFFS, "<$diffs") || die "Can't open $diffs";
          while (<DIFFS>) {
              $substs{"diffs"} .= $_;
          }
          close DIFFS;
          $substs{"neworchanged"} = $verb;
          $substs{"summary"} = $::issue{'short_desc'};
          my $msg = PerformSubsts(Param("changedmail"), \%substs);

          if (!$regenerate) {
              # Note: fixaddresses may result in a Cc: only.  This seems
              # harmless.
              my $sendmailparam = "-ODeliveryMode=deferred";
              if (Param("sendmailnow")) {
                  $sendmailparam = "";
              }
                             
              SendMail($msg);   

              foreach my $n (split(/[, ]+/, "$tolist,$cclist")) {
                  if ($n ne "") {
					  # Since this was a 'to' or 'cc' string, it may
					  # contain the domain info, which for consistency with
					  # newemailtech, should be stripped out for display
					  # purposes: 
					  $n = DenormalizeEmailAddress($n);	
	
                      push(@@sentlist, $n);
                  }
              }

              $logstr = "$logstr; mail sent to $tolist, $cclist";
          }
      }
      unlink($diffs);
      Log($logstr);
  }
  if (@@sentlist) {
      print "<B>Email sent to:</B> " . join(", ", @@sentlist) . "\n";
      if ($didexclude) {
          print qq{<B>Excluding:</B> $nametoexclude (<a href="userprefs.cgi?bank=diffs">change your preferences</a> if you wish not to be excluded)\n};
      }
  }
  rename($new, $old) || die "Can't rename $new to $old";
  chmod 0600, $old;
  if ($regenerate) {
      print "$i ";
  }
  %seen = ();
  @@sentlist = ();
}

# Code starts here
# MAIN ----------------------------------------------------------------#

ConnectToDatabase();
GetVersionTable();
Lock();

# KPM: not at all clear what it is that this is trying to do:
if (open(FID, "<$::data_dir/nomail")) {
    while (<FID>) {
        $::nomail{trim($_)} = 1;
    }
    close FID;
}

# KPM: command line option; believed unused in IZ except when 
# checksetup.pl is run:
# To recreate the shadow database,  run "processmail regenerate" .
if ($#ARGV >= 0 && $ARGV[0] eq "regenerate") {
    $regenerate = 1;
    shift @@ARGV;
    SendSQL("select issue_id from issues order by issue_id");
    my @@regenerate_list;
    while (my @@row = FetchSQLData()) {
        push @@regenerate_list, $row[0];
    }
    foreach my $i (@@regenerate_list) {
        ProcessOneIssue($i);
        Unlock();
        Lock();
    }
    print("\n");
    exit;
}

# /processmail", "-forcecc", $origCcString, $id, $::FORM{'who'}
# add some folks to the global @@forcecc array:
if ($#ARGV >= 0 && $ARGV[0] eq "-forcecc") {
    shift(@@ARGV);
    foreach my $i (split(/,/, shift(@@ARGV))) {
        push(@@forcecc, trim($i));
    }
}

if (($#ARGV < 0) || ($#ARGV > 1)) {
    print "Usage error: processmail {issueid} {nametoexclude}\nOr: processmail regenerate\n";
    exit;
}

if ($#ARGV == 1) {
    $nametoexclude = lc($ARGV[1]);
}

if ($ARGV[0] eq "rescanall") {
    print "<br> Collecting issue ids...\n";
    SendSQL("select issue_id from issues where to_days(now()) - to_days(delta_ts) <= 2 order by issue_id");
    my @@list;
    while (my @@row = FetchSQLData()) {
        push @@list, $row[0];
    }
    foreach my $id (@@list) {
        $ARGV[0] = $id;
        print "<br> Doing issue $id\n";
        ProcessOneIssue($ARGV[0]);
    }
} else {
    ProcessOneIssue($ARGV[0]);
}

exit;








@


1.13
log
@Issue number: 5381
If diffs are used, don't display the full path to the files.
@
text
@d233 6
a238 2
    $::issue{'assigned_to'} = getUsernameFromId($::issue{'assigned_to'});
    $::issue{'reporter'}    = getUsernameFromId($::issue{'reporter'});
a277 1

d303 1
a303 1
        
d430 1
d476 1
d496 1
a496 1
        
d499 3
a501 1
        
a583 1

d664 1
d666 6
a671 1
        my $value = $values{$f};
@


1.12
log
@Issue number: 5381
Fixed bug causing email tech preferences to be disregarded; normalized
display of usernames when mail is sent; changed processmail to disregard
accounts which have been disabled.
@
text
@d762 1
a762 1
      system("diff -c -b $old $new > $diffs");
@


1.11
log
@Issue number: 5381
Made sure that scripts which were meant to be called as crons take the
database to act against as a command-line arg.
@
text
@d335 4
a338 1

a577 2
        $person = NormalizeEmailAddress($person);

d589 1
a617 1
        $name = NormalizeEmailAddress($name); 
d685 1
a685 1
    $person = NormalizeEmailAddress($person);
d692 1
a692 1
    $substs{"to"} = $person;
d802 6
@


1.10
log
@Fixed duplicate 'qa contact' header.
@
text
@d64 5
@


1.9
log
@Issue number: 4544
Fixed handling of email addresses, misc. bug fixes.
@
text
@d236 1
a236 1
        $qa_contact = "QAContact: $::issue{'qa_contact'}\n";
@


1.8
log
@Issue number: 4544
Cleaning up handling of usernames
@
text
@d343 1
a343 1
            push(@@result, $::iz_mail_pfx . $username . Param('emailsuffix'));
d496 1
a496 1
            $difftext .= "\n$who" . Param('emailsuffix') . " changed:\n\n";
d568 1
d570 1
a570 4
        my $match = "^[^@@, ]*@@[^@@, ]*\.[^@@, ]*\$";
        if ($person !~ /$match/) {
          $person = $person . Param('emailsuffix');
        }
d610 3
a612 3
        
        &NewProcessOnePerson(getUsernameFromId($watcher) . Param('emailsuffix'), 
                            $count, \@@headerlist, \%values,
d679 1
a679 1
    $person = $::iz_mail_pfx . $person . Param('emailsuffix');
@


1.7
log
@Issue number: 4544
Got processmail nominally working.
@
text
@d343 1
a343 1
            push(@@result, $username . Param('emailsuffix'));
d681 1
a681 1
    $person .= Param('emailsuffix');
@


1.6
log
@Issue number: 4544
Misc. fixes to permit proper use of libraries and namespaces (incomplete).
Included code in CGI.pl and globals.pl qualified to package 'main'; live code
in CGI.pl moved to routine 'initSession()', and files expecting execution of
this code via 'require' now explicitly call this routine.

This is undoubtedly not the last time this problem will be visited in the course
of this process.
@
text
@d210 1
a210 1
                   "status_whiteboard", "groupset");
d253 1
d255 1
a255 5

#p# lookup against hash:
#p# $href = getAllUsersById();
     
    SendSQL("select profiles.login_name from votes, profiles where votes.issue_id = $id and profiles.userid = votes.who");
d259 1
a259 1
        push(@@voterlist, $v);
d323 2
a324 9
    foreach my $i (@@$list) {
        if (!defined $i || $i eq "") {
            next;
        }
#p# $record_href = getUserRecordFromUsername($uid);
        SendSQL("select emailnotification, groupset & $::issue{'groupset'} from profiles where login_name = " .
                SqlQuote($i));
        my ($emailnotification, $groupset) = (FetchSQLData());
        if ($groupset ne $::issue{'groupset'}) {
d327 4
d337 1
a337 1
           (lc($i) eq $nametoexclude)) {
d342 3
a344 3
        if (!defined $::nomail{$i} && !defined $seen{$i}) {
            push(@@result, $i . Param('emailsuffix'));
            $seen{$i} = 1;
a453 3
#p# lookups against:
#p# $href = getAllUsersById();

d455 2
a456 2
    SendSQL("SELECT profiles.login_name FROM votes, profiles " .
            "WHERE votes.issue_id = $id AND profiles.userid = votes.who");
d458 2
a459 1
        push(@@voterlist, FetchOneColumn());
d462 2
a463 2
    $values{'assigned_to'} = getUsernameFromId($values{'assigned_to'});
    $values{'reporter'} = getUsernameFromId($values{'reporter'});
d471 1
a471 1
    SendSQL("SELECT profiles.login_name, fielddefs.description, " .
d473 1
a473 1
            "FROM issues_activity, fielddefs, profiles " .
a475 1
            "  AND profiles.userid = who " .
d483 4
a601 6
#d#    SendSQL("SELECT userid, emailnotification, newemailtech," .
#d#            "       groupset & $values{'groupset'} " .
#d#            "FROM profiles WHERE login_name = " . SqlQuote($person));
#d#    my ($userid, $emailnotification, $newemailtech, 
#d#        $groupset) =  (FetchSQLData());
    
d634 2
a635 6
#d#    if ($groupset ne $values{'groupset'}) {
#d#    if user's groupset does not equal ... 
#p# NOTE: there's no reason to repeatedly get this array; it should be global.
    foreach(getGroupsIssueBelongsTo($id)){
       return unless usernameHasPermission($person, $_);
    }
@


1.5
log
@Interim checkin; transitioning users and groups management to IzUsersAndGroups.pm
@
text
@d34 4
d602 10
a611 7
#p# $record_href = getUserRecordFromId($uid);
        
    SendSQL("SELECT userid, emailnotification, newemailtech," .
            "       groupset & $values{'groupset'} " .
            "FROM profiles WHERE login_name = " . SqlQuote($person));
    my ($userid, $emailnotification, $newemailtech, 
        $groupset) =  (FetchSQLData());
d617 1
a617 1
      my $personId = getIdFromUsername($person);
d645 5
a649 2
    if ($groupset ne $values{'groupset'}) {
      return;
d651 1
a651 1

@


1.4
log
@Changed names to protect the innocent.
@
text
@d224 2
a225 2
    $::issue{'assigned_to'} = DBID_to_name($::issue{'assigned_to'});
    $::issue{'reporter'}    = DBID_to_name($::issue{'reporter'});
d231 1
a231 1
        $::issue{'qa_contact'} = DBID_to_name($::issue{'qa_contact'});
d466 2
a467 2
    $values{'assigned_to'} = DBID_to_name($values{'assigned_to'});
    $values{'reporter'} = DBID_to_name($values{'reporter'});
d469 1
a469 1
        $values{'qa_contact'} = DBID_to_name($values{'qa_contact'});
d610 1
a610 1
      my $personId = DBname_to_id($person);
d617 1
a617 1
        &NewProcessOnePerson(DBID_to_name($watcher) . Param('emailsuffix'), 
@


1.3
log
@Issue number: 4544
Second round of tagging.  Rudimentary abstraction lib w/ stubs, and
corresponding tags in the relevant scripts as to the rough calls which
should be made to fulfill the direct calls being made to the profiles table.
@
text
@d30 1
a30 1
use IzPermissions;
@


1.2
log
@2635; making sure that email limited to 80 chars/line.
@
text
@d30 1
d250 4
a253 1
    
d326 1
d456 3
d597 2
@


1.1
log
@Initial checkin of IssueZilla as a module in advance of removing it from
the sandbox module
@
text
@d365 1
a365 1
^>>>>>>>>>>>>>>>>>>>>>>>>>|^<<<<<<<<<<<<<<<<<<<<<<<<<<|^<<<<<<<<<<<<<<<<<<<<<<<<<<~~
d380 1
a380 1
^>>>>>>>>>>>>>>>>>>>>>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
d491 1
a491 1
            $difftext .= ('=' x 82) . "\n";
d499 1
a499 1
        $difftext .= ('-' x 82) . "\n";
d532 1
a532 1
            $thisdiff .= ('-' x 82) . "\n";
d536 1
a536 1
        $difftext .= ('-' x 82) . "\n";
@

