<commit_before># -*- 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 Bugzilla Bug 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): <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>

# Contains some global routines used throughout the CGI scripts of Bugzilla.

use diagnostics;
use strict;
use lib ".";

# use Carp;                       # for confess

# commented out the following snippet of code. this tosses errors into the
# CGI if you are perl 5.6, and doesn't if you have perl 5.003. 
# We want to check for the existence of the LDAP modules here.
# eval "use Mozilla::LDAP::Conn";
# my $have_ldap = $@ ? 0 : 1;

# Shut up misguided -w warnings about "used only once".  For some reason,
# "use vars" chokes on me when I try it here.

sub CGI_pl_sillyness {
    my $zz;
    $zz = %::MFORM;
    $zz = %::dontchange;
}

use CGI::Carp qw(fatalsToBrowser);

require 'globals.pl';

sub GeneratePersonInput {
    my ($field, $required, $def_value, $extraJavaScript) = (@_);
    $extraJavaScript ||= "";
    if ($extraJavaScript ne "") {
        $extraJavaScript = "onChange=\"$extraJavaScript\"";
    }
    return "<INPUT NAME=\"$field\" SIZE=32 $extraJavaScript VALUE=\"$def_value\">";
}

sub GeneratePeopleInput {
    my ($field, $def_value) = (@_);
    return "<INPUT NAME=\"$field\" SIZE=45 VALUE=\"$def_value\">";
}




# Implementations of several of the below were blatently stolen from CGI.pm,
# by <NAME>.


# Get rid of all the %xx encoding and the like from the given URL.

sub url_decode {
    my ($todecode) = (@_);
    $todecode =~ tr/+/ /;       # pluses become spaces
    $todecode =~ s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge;
    return $todecode;
}


# Quotify a string, suitable for putting into a URL.

sub url_quote {
    my($toencode) = (@_);
    $toencode=~s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg;
    return $toencode;
}


sub ParseUrlString {
    my ($buffer, $f, $m) = (@_);
    undef %$f;
    undef %$m;

    my %isnull;
    my $remaining = $buffer;
    while ($remaining ne "") {
        my $item;
        if ($remaining =~ /^([^&]*)&(.*)$/) {
            $item = $1;
            $remaining = $2;
        } else {
            $item = $remaining;
            $remaining = "";
        }

        my $name;
        my $value;
        if ($item =~ /^([^=]*)=(.*)$/) {
            $name = $1;
            $value = url_decode($2);
        } else {
            $name = $item;
            $value = "";
        }
        if ($value ne "") {
            if (defined $f->{$name}) {
                $f->{$name} .= $value;
                my $ref = $m->{$name};
                push @$ref, $value;
            } else {
                $f->{$name} = $value;
                $m->{$name} = [$value];
            }
        } else {
            $isnull{$name} = 1;
        }
    }
    if (%isnull) {
        foreach my $name (keys(%isnull)) {
            if (!defined $f->{$name}) {
                $f->{$name} = "";
                $m->{$name} = [];
            }
        }
    }
}


sub ProcessFormFields {
    my ($buffer) = (@_);
    return ParseUrlString($buffer, \%::FORM, \%::MFORM);
}


sub ProcessMultipartFormFields {
    my ($boundary) = @_;

    # Initialize variables that store whether or not we are parsing a header,
    # the name of the part we are parsing, and its value (which is incomplete
    # until we finish parsing the part).
    my $inheader = 1;
    my $fieldname = "";
    my $fieldvalue = "";

    # Read the input stream line by line and parse it into a series of parts,
    # each one containing a single form field and its value and each one
    # separated from the next by the value of $boundary.
    my $remaining = $ENV{"CONTENT_LENGTH"};
    while ($remaining > 0 && ($_ = <STDIN>)) {
        $remaining -= length($_);

        # If the current input line is a boundary line, save the previous
        # form value and reset the storage variables.
        if ($_ =~ m/^-*$boundary/) {
            if ( $fieldname ) {
                chomp($fieldvalue);
                $fieldvalue =~ s/\r$//;
                if ( defined $::FORM{$fieldname} ) {
                    $::FORM{$fieldname} .= $fieldvalue;
                    push @{$::MFORM{$fieldname}}, $fieldvalue;
                } else {
                    $::FORM{$fieldname} = $fieldvalue;
                    $::MFORM{$fieldname} = [$fieldvalue];
                }
            }

            $inheader = 1;
            $fieldname = "";
            $fieldvalue = "";

        # If the current input line is a header line, look for a blank line
        # (meaning the end of the headers), a Content-Disposition header
        # (containing the field name and, for uploaded file parts, the file 
        # name), or a Content-Type header (containing the content type for 
        # file parts).
        } elsif ( $inheader ) {
            if (m/^\s*$/) {
                $inheader = 0;
            } elsif (m/^Content-Disposition:\s*form-data\s*;\s*name\s*=\s*"([^\"]+)"/i) {
                $fieldname = $1;
                if (m/;\s*filename\s*=\s*"([^\"]+)"/i) {
                    $::FILE{$fieldname}->{'filename'} = $1;
                }
            } elsif ( m|^Content-Type:\s*([^/]+/[^\s;]+)|i ) {
                $::FILE{$fieldname}->{'contenttype'} = $1;
            }

        # If the current input line is neither a boundary line nor a header,
        # it must be part of the field value, so append it to the value.
        } else {
          $fieldvalue .= $_;
        }
    }
}


# check and see if a given field exists, is non-empty, and is set to a 
# legal value.  assume a browser bug and abort appropriately if not.
# if $legalsRef is not passed, just check to make sure the value exists and 
# is non-NULL
# 
sub CheckFormField (\%$;\@) {
    my ($formRef,                # a reference to the form to check (a hash)
        $fieldname,              # the fieldname to check
        $legalsRef               # (optional) ref to a list of legal values 
       ) = @_;

    if ( !defined $formRef->{$fieldname} ||
         trim($formRef->{$fieldname}) eq "" ||
         (defined($legalsRef) && 
          lsearch($legalsRef, $formRef->{$fieldname})<0) ){

        SendSQL("SELECT description FROM fielddefs WHERE name=" . SqlQuote($fieldname));
        my $result = FetchOneColumn();
        if ($result) {
            PuntTryAgain("A legal $result was not set.");
        }
        else {
            PuntTryAgain("A legal $fieldname was not set.");
            print Param("browserbugmessage");
        }
        PutFooter();
        exit 0;
      }
}

# check and see if a given field is defined, and abort if not
# 
sub CheckFormFieldDefined (\%$) {
    my ($formRef,                # a reference to the form to check (a hash)
        $fieldname,              # the fieldname to check
       ) = @_;

    if ( !defined $formRef->{$fieldname} ) {
        print "$fieldname was not defined; ";
        print Param("browserbugmessage");
        PutFooter();
        exit 0;
      }
}

sub ValidateBugID {
    # Validates and verifies a bug ID, making sure the number is a 
    # positive integer, that it represents an existing bug in the
    # database, and that the user is authorized to access that bug.

    my ($id) = @_;

    # Make sure the bug number is a positive integer.
    # Whitespace can be ignored because the SQL server will ignore it.
    $id =~ /^\s*([1-9][0-9]*)\s*$/
      || DisplayError("The bug number is invalid. If you are trying to use " .
                      "QuickSearch, you need to enable JavaScript in your " .
                      "browser. To help us fix this limitation, look " .
                      "<a href=\"http://bugzilla.mozilla.org/show_bug.cgi?id=70907\">here</a>.") 
      && exit;

    # Get the values of the usergroupset and userid global variables
    # and write them to local variables for use within this function,
    # setting those local variables to the default value of zero if
    # the global variables are undefined.

    # First check that the bug exists
    SendSQL("SELECT bug_id FROM bugs WHERE bug_id = $id");

    FetchOneColumn()
      || DisplayError("Bug #$id does not exist.")
        && exit;

    return if CanSeeBug($id, $::userid, $::usergroupset);

    # The user did not pass any of the authorization tests, which means they
    # are not authorized to see the bug.  Display an error and stop execution.
    # The error the user sees depends on whether or not they are logged in
    # (i.e. $::userid contains the user's positive integer ID).
    if ($::userid) {
        DisplayError("You are not authorized to access bug #$id.");
    } else {
        DisplayError(
          qq|You are not authorized to access bug #$id.  To see this bug, you
          must first <a href="show_bug.cgi?id=$id&amp;GoAheadAndLogIn=1">log in 
          to an account</a> with the appropriate permissions.|
        );
    }
    exit;

}

# check and see if a given string actually represents a positive
# integer, and abort if not.
# 
sub CheckPosInt($) {
    my ($number) = @_;              # the fieldname to check

    if ( $number !~ /^[1-9][0-9]*$/ ) {
        print "Received string \"$number\" when positive integer expected; ";
        print Param("browserbugmessage");
        PutFooter();
        exit 0;
      }
}

sub FormData {
    my ($field) = (@_);
    return $::FORM{$field};
}

sub html_quote {
    my ($var) = (@_);
    $var =~ s/\&/\&amp;/g;
    $var =~ s/</\&lt;/g;
    $var =~ s/>/\&gt;/g;
    return $var;
}

sub value_quote {
    my ($var) = (@_);
    $var =~ s/\&/\&amp;/g;
    $var =~ s/</\&lt;/g;
    $var =~ s/>/\&gt;/g;
    $var =~ s/"/\&quot;/g;
    # See bug http://bugzilla.mozilla.org/show_bug.cgi?id=4928 for 
    # explanaion of why bugzilla does this linebreak substitution. 
    # This caused form submission problems in mozilla (bug 22983, 32000).
    $var =~ s/\r\n/\&#013;/g;
    $var =~ s/\n\r/\&#013;/g;
    $var =~ s/\r/\&#013;/g;
    $var =~ s/\n/\&#013;/g;
    return $var;
}

sub navigation_header {
    if (defined $::COOKIE{"BUGLIST"} && $::COOKIE{"BUGLIST"} ne "" &&
        defined $::FORM{'id'}) {
        my @bugs = split(/:/, $::COOKIE{"BUGLIST"});
        my $cur = lsearch(\@bugs, $::FORM{"id"});
        print "<B>Bug List:</B> (@{[$cur + 1]} of @{[$#bugs + 1]})\n";
        print "<A HREF=\"show_bug.cgi?id=$bugs[0]\">First</A>\n";
        print "<A HREF=\"show_bug.cgi?id=$bugs[$#bugs]\">Last</A>\n";
        if ($cur > 0) {
            print "<A HREF=\"show_bug.cgi?id=$bugs[$cur - 1]\">Prev</A>\n";
        } else {
            print "<I><FONT COLOR=\#777777>Prev</FONT></I>\n";
        }
        if ($cur < $#bugs) {
            $::next_bug = $bugs[$cur + 1];
            print "<A HREF=\"show_bug.cgi?id=$::next_bug\">Next</A>\n";
        } else {
            print "<I><FONT COLOR=\#777777>Next</FONT></I>\n";
        }
        print qq{&nbsp;&nbsp;<A HREF="buglist.cgi?regetlastlist=1">Show list</A>\n};
    }
    print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF=query.cgi>Query page</A>\n";
    print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF=enter_bug.cgi>Enter new bug</A>\n"
}

# Adds <link> elements for bug lists. These can be inserted into the header by
# (ab)using the "jscript" parameter to PutHeader, which inserts an arbitrary
# string into the header. This function is modelled on the one above.
sub navigation_links($) {
    my ($buglist) = @_;
    
    my $retval = "";
    
    # We need to be able to pass in a buglist because when you sort on a column
    # the bugs in the cookie you are given will still be in the old order.
    # If a buglist isn't passed, we just use the cookie.
    $buglist ||= $::COOKIE{"BUGLIST"};
    
    if (defined $buglist && $buglist ne "") {
    my @bugs = split(/:/, $buglist);
        
        if (defined $::FORM{'id'}) {
            # We are on an individual bug
            my $cur = lsearch(\@bugs, $::FORM{"id"});

            if ($cur > 0) {
                $retval .= "<link rel=\"First\" href=\"show_bug.cgi?id=$bugs[0]\" />\n";
                $retval .= "<link rel=\"Prev\" href=\"show_bug.cgi?id=$bugs[$cur - 1]\" />\n";
            } 
            if ($cur < $#bugs) {
                $retval .= "<link rel=\"Next\" href=\"show_bug.cgi?id=$bugs[$cur + 1]\" />\n";
                $retval .= "<link rel=\"Last\" href=\"show_bug.cgi?id=$bugs[$#bugs]\" />\n";
            }

            $retval .= "<link rel=\"Up\" href=\"buglist.cgi?regetlastlist=1\" />\n";
            $retval .= "<link rel=\"Contents\" href=\"buglist.cgi?regetlastlist=1\" />\n";
        } else {
            # We are on a bug list
            $retval .= "<link rel=\"First\" href=\"show_bug.cgi?id=$bugs[0]\" />\n";
            $retval .= "<link rel=\"Next\" href=\"show_bug.cgi?id=$bugs[0]\" />\n";
            $retval .= "<link rel=\"Last\" href=\"show_bug.cgi?id=$bugs[$#bugs]\" />\n";
        }
    }
    
    return $retval;
} 

sub make_checkboxes {
    my ($src,$default,$isregexp,$name) = (@_);
    my $last = "";
    my $capitalized = "";
    my $popup = "";
    my $found = 0;
    $default = "" if !defined $default;

    if ($src) {
        foreach my $item (@$src) {
            if ($item eq "-blank-" || $item ne $last) {
                if ($item eq "-blank-") {
                    $item = "";
                }
                $last = $item;
                $capitalized = $item;
                $capitalized =~ tr/A-Z/a-z/;
                $capitalized =~ s/^(.?)(.*)/\u$1$2/;
                if ($isregexp ? $item =~ $default : $default eq $item) {
                    $popup .= "<INPUT NAME=$name TYPE=CHECKBOX VALUE=\"$item\" CHECKED>$capitalized<br>";
                    $found = 1;
                } else {
                    $popup .= "<INPUT NAME=$name TYPE=CHECKBOX VALUE=\"$item\">$capitalized<br>";
                }
            }
        }
    }
    if (!$found && $default ne "") {
        $popup .= "<INPUT NAME=$name TYPE=CHECKBOX CHECKED>$default";
    }
    return $popup;
}

#
# make_selection_widget: creates an HTML selection widget from a list of text strings.
# $groupname is the name of the setting (form value) that this widget will control
# $src is the list of options
# you can specify a $default value which is either a string or a regex pattern to match to
#    identify the default value
# $capitalize lets you optionally capitalize the option strings; the default is the value
#    of Param("capitalizelists")
# $multiple is 1 if several options are selectable (default), 0 otherwise.
# $size is used for lists to control how many items are shown. The default is 7. A list of
#    size 1 becomes a popup menu.
# $preferLists is 1 if selection lists should be used in favor of radio buttons and
#    checkboxes, and 0 otherwise. The default is the value of Param("preferlists").
#
# The actual widget generated depends on the parameter settings:
# 
#        MULTIPLE     PREFERLISTS    SIZE     RESULT
#       0 (single)        0           =1      Popup Menu (normal for list of size 1)
#       0 (single)        0           >1      Radio buttons
#       0 (single)        1           =1      Popup Menu (normal for list of size 1)
#       0 (single)        1           n>1     List of size n, single selection
#       1 (multi)         0           n/a     Check boxes; size ignored
#       1 (multi)         1           n/a     List of size n, multiple selection, of size n
#
sub make_selection_widget {
    my ($groupname,$src,$default,$isregexp,$multiple, $size, $capitalize, $preferLists) = (@_);
    my $last = "";
    my $popup = "";
    my $found = 0;
    my $displaytext = "";
    $groupname = "" if !defined $groupname;
    $default = "" if !defined $default;
    $capitalize = Param("capitalizelists") if !defined $capitalize;
    $multiple = 1 if !defined $multiple;
    $preferLists = Param("preferlists") if !defined $preferLists;
    $size = 7 if !defined $size;
    my $type = "LIST";
    if (!$preferLists) {
        if ($multiple) {
            $type = "CHECKBOX";
        } else {
            if ($size > 1) {
                $type = "RADIO";
            }
        }
    }

    if ($type eq "LIST") {
        $popup .= "<SELECT NAME=\"$groupname\"";
        if ($multiple) {
            $popup .= " MULTIPLE";
        }
        $popup .= " SIZE=$size>\n";
    }
    if ($src) {
        foreach my $item (@$src) {
            if ($item eq "-blank-" || $item ne $last) {
                if ($item eq "-blank-") {
                    $item = "";
                }
                $last = $item;
                $displaytext = $item;
                if ($capitalize) {
                    $displaytext =~ tr/A-Z/a-z/;
                    $displaytext =~ s/^(.?)(.*)/\u$1$2/;
                }   

                if ($isregexp ? $item =~ $default : $default eq $item) {
                    if ($type eq "CHECKBOX") {
                        $popup .= "<INPUT NAME=$groupname type=checkbox VALUE=\"$item\" CHECKED>$displaytext<br>";
                    } elsif ($type eq "RADIO") {
                        $popup .= "<INPUT NAME=$groupname type=radio VALUE=\"$item\" CHECKED>$displaytext<br>";
                    } else {
                        $popup .= "<OPTION SELECTED VALUE=\"$item\">$displaytext\n";
                    }
                    $found = 1;
                } else {
                    if ($type eq "CHECKBOX") {
                        $popup .= "<INPUT NAME=$groupname type=checkbox VALUE=\"$item\">$displaytext<br>";
                    } elsif ($type eq "RADIO") {
                        $popup .= "<INPUT NAME=$groupname type=radio VALUE=\"$item\">$displaytext<br>";
                    } else {
                        $popup .= "<OPTION VALUE=\"$item\">$displaytext";
                    }
                }
            }
        }
    }
    if (!$found && $default ne "") {
        if ($type eq "CHECKBOX") {
            $popup .= "<INPUT NAME=$groupname type=checkbox CHECKED>$default";
        } elsif ($type eq "RADIO") {
            $popup .= "<INPUT NAME=$groupname type=radio checked>$default";
        } else {
            $popup .= "<OPTION SELECTED>$default\n";
        }
    }
    if ($type eq "LIST") {
        $popup .= "</SELECT>";
    }
    return $popup;
}


$::CheckOptionValues = 1;

sub make_options {
    my ($src,$default,$isregexp) = (@_);
    my $last = "";
    my $popup = "";
    my $found = 0;
    $default = "" if !defined $default;

    if ($src) {
        foreach my $item (@$src) {
            if ($item eq "-blank-" || $item ne $last) {
                if ($item eq "-blank-") {
                    $item = "";
                }
                $last = $item;
                if ($isregexp ? $item =~ $default : $default eq $item) {
                    $popup .= "<OPTION SELECTED VALUE=\"$item\">$item\n";
                    $found = 1;
                } else {
                    $popup .= "<OPTION VALUE=\"$item\">$item\n";
                }
            }
        }
    }
    if (!$found && $default ne "") {
      if ( Param("strictvaluechecks") && $::CheckOptionValues &&
           ($default ne $::dontchange) && ($default ne "-All-") &&
           ($default ne "DUPLICATE") ) {
        print "Possible bug database corruption has been detected.  " .
              "Please send mail to " . Param("maintainer") . " with " .
              "details of what you were doing when this message " . 
              "appeared.  Thank you.\n";
        if (!$src) {
            $src = ["???null???"];
        }
        print "<pre>src = " . value_quote(join(' ', @$src)) . "\n";
        print "default = " . value_quote($default) . "</pre>";
        PutFooter();
#        confess "Gulp.";
        exit 0;
              
      } else {
        $popup .= "<OPTION SELECTED>$default\n";
      }
    }
    return $popup;
}


sub make_popup {
    my ($name,$src,$default,$listtype,$onchange) = (@_);
    my $popup = "<SELECT NAME=$name";
    if ($listtype > 0) {
        $popup .= " SIZE=5";
        if ($listtype == 2) {
            $popup .= " MULTIPLE";
        }
    }
    if (defined $onchange && $onchange ne "") {
        $popup .= " onchange=$onchange";
    }
    $popup .= ">" . make_options($src, $default,
                                 ($listtype == 2 && $default ne ""));
    $popup .= "</SELECT>";
    return $popup;
}


sub BuildPulldown {
    my ($name, $valuelist, $default) = (@_);

    my $entry = qq{<SELECT NAME="$name">};
    foreach my $i (@$valuelist) {
        my ($tag, $desc) = (@$i);
        my $selectpart = "";
        if ($tag eq $default) {
            $selectpart = " SELECTED";
        }
        if (!defined $desc) {
            $desc = $tag;
        }
        $entry .= qq{<OPTION$selectpart VALUE="$tag">$desc\n};
    }
    $entry .= qq{</SELECT>};
    return $entry;
}




sub PasswordForLogin {
    my ($login) = (@_);
    SendSQL("select cryptpassword from profiles where login_name = " .
            SqlQuote($login));
    my $result = FetchOneColumn();
    if (!defined $result) {
        $result = "";
    }
    return $result;
}


sub quietly_check_login() {
    $::usergroupset = '0';
    my $loginok = 0;
    $::disabledreason = '';
    $::userid = 0;
    if (defined $::COOKIE{"Bugzilla_login"} &&
        defined $::COOKIE{"Bugzilla_logincookie"}) {
        ConnectToDatabase();
        if (!defined $ENV{'REMOTE_HOST'}) {
            $ENV{'REMOTE_HOST'} = $ENV{'REMOTE_ADDR'};
        }
        SendSQL("SELECT profiles.userid, profiles.groupset, " .
                "profiles.login_name, " .
                "profiles.login_name = " .
                SqlQuote($::COOKIE{"Bugzilla_login"}) .
                " AND profiles.cryptpassword = logincookies.cryptpassword " .
                "AND logincookies.hostname = " .
                SqlQuote($ENV{"REMOTE_HOST"}) .
                ", profiles.disabledtext " .
                " FROM profiles, logincookies WHERE logincookies.cookie = " .
                SqlQuote($::COOKIE{"Bugzilla_logincookie"}) .
                " AND profiles.userid = logincookies.userid");
        my @row;
        if (@row = FetchSQLData()) {
            my ($userid, $groupset, $loginname, $ok, $disabledtext) = (@row);
            if ($ok) {
                if ($disabledtext eq '') {
                    $loginok = 1;
                    $::userid = $userid;
                    $::usergroupset = $groupset;
                    $::COOKIE{"Bugzilla_login"} = $loginname; # Makes sure case
                                                              # is in
                                                              # canonical form.
                } else {
                    $::disabledreason = $disabledtext;
                }
            }
        }
    }
    # if 'who' is passed in, verify that it's a good value
    if ($::FORM{'who'}) {
        my $whoid = DBname_to_id($::FORM{'who'});
        delete $::FORM{'who'} unless $whoid;
    }
    if (!$loginok) {
        delete $::COOKIE{"Bugzilla_login"};
    }
    return $loginok;
}




sub CheckEmailSyntax {
    my ($addr) = (@_);
    my $match = Param('emailregexp');
    if ($addr !~ /$match/ || $addr =~ /[\\\(\)<>&,;:"\[\] \t\r\n]/) {
        print "Content-type: text/html\n\n";

        # For security, escape HTML special characters.
        $addr = html_quote($addr);

        PutHeader("Check e-mail syntax");
        print "The e-mail address you entered\n";
        print "(<b>$addr</b>) didn't match our minimal\n";
        print "syntax checking for a legal email address.\n";
        print Param('emailregexpdesc') . "\n";
        print "It must also not contain any of these special characters: " .
              "<tt>\\ ( ) &amp; &lt; &gt; , ; : \" [ ]</tt> " .
              "or any whitespace.\n";
        print "<p>Please click <b>Back</b> and try again.\n";
        PutFooter();
        exit;
    }
}



sub MailPassword {
    my ($login, $password) = (@_);
    my $urlbase = Param("urlbase");
    my $template = Param("passwordmail");
    my $msg = PerformSubsts($template,
                            {"mailaddress" => $login . Param('emailsuffix'),
                             "login" => $login,
                             "password" => $password});

    open SENDMAIL, "|/usr/lib/sendmail -t";
    print SENDMAIL $msg;
    close SENDMAIL;

    print "The password for the e-mail address\n";
    print "$login has been e-mailed to that address.\n";
    print "<p>When the e-mail arrives, you can click <b>Back</b>\n";
    print "and enter your password in the form there.\n";
}


sub confirm_login {
    my ($nexturl) = (@_);

# Uncommenting the next line can help debugging...
#    print "Content-type: text/plain\n\n";

    ConnectToDatabase();
    # I'm going to reorganize some of this stuff a bit.  Since we're adding
    # a second possible validation method (LDAP), we need to move some of this
    # to a later section.  -<NAME>, 8/3/00
    my $enteredlogin = "";
    my $realcryptpwd = "";

    # If the form contains Bugzilla login and password fields, use Bugzilla's 
    # built-in authentication to authenticate the user (otherwise use LDAP below).
    if (defined $::FORM{"Bugzilla_login"} && defined $::FORM{"Bugzilla_password"}) {
        # Make sure the user's login name is a valid email address.
        $enteredlogin = $::FORM{"Bugzilla_login"};
        CheckEmailSyntax($enteredlogin);

        # Retrieve the user's ID and crypted password from the database.
        my $userid;
        SendSQL("SELECT userid, cryptpassword FROM profiles 
                 WHERE login_name = " . SqlQuote($enteredlogin));
        ($userid, $realcryptpwd) = FetchSQLData();

        # Make sure the user exists or throw an error (but do not admit it was a username
        # error to make it harder for a cracker to find account names by brute force).
        $userid
          || DisplayError("The username or password you entered is not valid.")
          && exit;

        # If this is a new user, generate a password, insert a record
        # into the database, and email their password to them.
        if ( defined $::FORM{"PleaseMailAPassword"} && !$userid ) {
            my $password = InsertNewUser($enteredlogin, "");
            print "Content-Type: text/html\n\n";
            PutHeader("Account Created");
            MailPassword($enteredlogin, $password);
            PutFooter();
            exit;
        }

        # Otherwise, authenticate the user.
        else {
            # Get the salt from the user's crypted password.
            my $salt = $<PASSWORD>;

            # Using the salt, crypt the password the user entered.
            my $enteredCryptedPassword = crypt( $::FORM{"Bugzilla_password"} , $salt );

            # Make sure the passwords match or throw an error.
            ($enteredCryptedPassword eq $<PASSWORD>)
              || DisplayError("The username or password you entered is not valid.")
              && exit;

            # If the user has successfully logged in, delete any password tokens
            # lying around in the system for them.
            use Token;
            my $token = Token::HasPasswordToken($userid);
            while ( $token ) {
                Token::Cancel($token, "user logged in");
                $token = Token::HasPasswordToken($userid);
            }
        }

     } elsif (Param("useLDAP") &&
              defined $::FORM{"LDAP_login"} &&
              defined $::FORM{"LDAP_password"}) {
       # If we're using LDAP for login, we've got an entirely different
       # set of things to check.

# see comment at top of file near eval
       # First, if we don't have the LDAP modules available to us, we can't
       # do this.
#       if(!$have_ldap) {
#         print "Content-type: text/html\n\n";
#         PutHeader("LDAP not enabled");
#         print "The necessary modules for LDAP login are not installed on ";
#         print "this machine.  Please send mail to ".Param("maintainer");
#         print " and notify him of this problem.\n";
#         PutFooter();
#         exit;
#       }

       # Next, we need to bind anonymously to the LDAP server.  This is
       # because we need to get the Distinguished Name of the user trying
       # to log in.  Some servers (such as iPlanet) allow you to have unique
       # uids spread out over a subtree of an area (such as "People"), so
       # just appending the Base DN to the uid isn't sufficient to get the
       # user's DN.  For servers which don't work this way, there will still
       # be no harm done.
       my $LDAPserver = Param("LDAPserver");
       if ($LDAPserver eq "") {
         print "Content-type: text/html\n\n";
         PutHeader("LDAP server not defined");
         print "The LDAP server for authentication has not been defined.  ";
         print "Please contact ".Param("maintainer")." ";
         print "and notify him of this problem.\n";
         PutFooter();
         exit;
       }

       my $LDAPport = "389";  #default LDAP port
       if($LDAPserver =~ /:/) {
         ($LDAPserver, $LDAPport) = split(":",$LDAPserver);
       }
       my $LDAPconn = new Mozilla::LDAP::Conn($LDAPserver,$LDAPport);
       if(!$LDAPconn) {
         print "Content-type: text/html\n\n";
         PutHeader("Unable to connect to LDAP server");
         print "I was unable to connect to the LDAP server for user ";
         print "authentication.  Please contact ".Param("maintainer");
         print " and notify him of this problem.\n";
         PutFooter();
         exit;
       }

       # We've got our anonymous bind;  let's look up this user.
       my $dnEntry = $LDAPconn->search(Param("LDAPBaseDN"),"subtree","uid=".$::FORM{"LDAP_login"});
       if(!$dnEntry) {
         print "Content-type: text/html\n\n";
         PutHeader("Login Failed");
         print "The username or password you entered is not valid.\n";
         print "Please click <b>Back</b> and try again.\n";
         PutFooter();
         exit;
       }

       # Now we get the DN from this search.  Once we've got that, we're
       # done with the anonymous bind, so we close it.
       my $userDN = $dnEntry->getDN;
       $LDAPconn->close;

       # Now we attempt to bind as the specified user.
       $LDAPconn = new Mozilla::LDAP::Conn($LDAPserver,$LDAPport,$userDN,$::FORM{"LDAP_password"});<commit_msg>SECURITY FIX bug 54901: If you were using LDAP authentication it would let you log in as anyone if you left the password blank. Patch by <NAME> <<EMAIL>> r= jmrobins, justdave<commit_after># -*- 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 Bugzilla Bug 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): <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>
#                 <NAME> <<EMAIL>>

# Contains some global routines used throughout the CGI scripts of Bugzilla.

use diagnostics;
use strict;
use lib ".";

# use Carp;                       # for confess

# commented out the following snippet of code. this tosses errors into the
# CGI if you are perl 5.6, and doesn't if you have perl 5.003. 
# We want to check for the existence of the LDAP modules here.
# eval "use Mozilla::LDAP::Conn";
# my $have_ldap = $@ ? 0 : 1;

# Shut up misguided -w warnings about "used only once".  For some reason,
# "use vars" chokes on me when I try it here.

sub CGI_pl_sillyness {
    my $zz;
    $zz = %::MFORM;
    $zz = %::dontchange;
}

use CGI::Carp qw(fatalsToBrowser);

require 'globals.pl';

sub GeneratePersonInput {
    my ($field, $required, $def_value, $extraJavaScript) = (@_);
    $extraJavaScript ||= "";
    if ($extraJavaScript ne "") {
        $extraJavaScript = "onChange=\"$extraJavaScript\"";
    }
    return "<INPUT NAME=\"$field\" SIZE=32 $extraJavaScript VALUE=\"$def_value\">";
}

sub GeneratePeopleInput {
    my ($field, $def_value) = (@_);
    return "<INPUT NAME=\"$field\" SIZE=45 VALUE=\"$def_value\">";
}




# Implementations of several of the below were blatently stolen from CGI.pm,
# by <NAME>.


# Get rid of all the %xx encoding and the like from the given URL.

sub url_decode {
    my ($todecode) = (@_);
    $todecode =~ tr/+/ /;       # pluses become spaces
    $todecode =~ s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge;
    return $todecode;
}


# Quotify a string, suitable for putting into a URL.

sub url_quote {
    my($toencode) = (@_);
    $toencode=~s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg;
    return $toencode;
}


sub ParseUrlString {
    my ($buffer, $f, $m) = (@_);
    undef %$f;
    undef %$m;

    my %isnull;
    my $remaining = $buffer;
    while ($remaining ne "") {
        my $item;
        if ($remaining =~ /^([^&]*)&(.*)$/) {
            $item = $1;
            $remaining = $2;
        } else {
            $item = $remaining;
            $remaining = "";
        }

        my $name;
        my $value;
        if ($item =~ /^([^=]*)=(.*)$/) {
            $name = $1;
            $value = url_decode($2);
        } else {
            $name = $item;
            $value = "";
        }
        if ($value ne "") {
            if (defined $f->{$name}) {
                $f->{$name} .= $value;
                my $ref = $m->{$name};
                push @$ref, $value;
            } else {
                $f->{$name} = $value;
                $m->{$name} = [$value];
            }
        } else {
            $isnull{$name} = 1;
        }
    }
    if (%isnull) {
        foreach my $name (keys(%isnull)) {
            if (!defined $f->{$name}) {
                $f->{$name} = "";
                $m->{$name} = [];
            }
        }
    }
}


sub ProcessFormFields {
    my ($buffer) = (@_);
    return ParseUrlString($buffer, \%::FORM, \%::MFORM);
}


sub ProcessMultipartFormFields {
    my ($boundary) = @_;

    # Initialize variables that store whether or not we are parsing a header,
    # the name of the part we are parsing, and its value (which is incomplete
    # until we finish parsing the part).
    my $inheader = 1;
    my $fieldname = "";
    my $fieldvalue = "";

    # Read the input stream line by line and parse it into a series of parts,
    # each one containing a single form field and its value and each one
    # separated from the next by the value of $boundary.
    my $remaining = $ENV{"CONTENT_LENGTH"};
    while ($remaining > 0 && ($_ = <STDIN>)) {
        $remaining -= length($_);

        # If the current input line is a boundary line, save the previous
        # form value and reset the storage variables.
        if ($_ =~ m/^-*$boundary/) {
            if ( $fieldname ) {
                chomp($fieldvalue);
                $fieldvalue =~ s/\r$//;
                if ( defined $::FORM{$fieldname} ) {
                    $::FORM{$fieldname} .= $fieldvalue;
                    push @{$::MFORM{$fieldname}}, $fieldvalue;
                } else {
                    $::FORM{$fieldname} = $fieldvalue;
                    $::MFORM{$fieldname} = [$fieldvalue];
                }
            }

            $inheader = 1;
            $fieldname = "";
            $fieldvalue = "";

        # If the current input line is a header line, look for a blank line
        # (meaning the end of the headers), a Content-Disposition header
        # (containing the field name and, for uploaded file parts, the file 
        # name), or a Content-Type header (containing the content type for 
        # file parts).
        } elsif ( $inheader ) {
            if (m/^\s*$/) {
                $inheader = 0;
            } elsif (m/^Content-Disposition:\s*form-data\s*;\s*name\s*=\s*"([^\"]+)"/i) {
                $fieldname = $1;
                if (m/;\s*filename\s*=\s*"([^\"]+)"/i) {
                    $::FILE{$fieldname}->{'filename'} = $1;
                }
            } elsif ( m|^Content-Type:\s*([^/]+/[^\s;]+)|i ) {
                $::FILE{$fieldname}->{'contenttype'} = $1;
            }

        # If the current input line is neither a boundary line nor a header,
        # it must be part of the field value, so append it to the value.
        } else {
          $fieldvalue .= $_;
        }
    }
}


# check and see if a given field exists, is non-empty, and is set to a 
# legal value.  assume a browser bug and abort appropriately if not.
# if $legalsRef is not passed, just check to make sure the value exists and 
# is non-NULL
# 
sub CheckFormField (\%$;\@) {
    my ($formRef,                # a reference to the form to check (a hash)
        $fieldname,              # the fieldname to check
        $legalsRef               # (optional) ref to a list of legal values 
       ) = @_;

    if ( !defined $formRef->{$fieldname} ||
         trim($formRef->{$fieldname}) eq "" ||
         (defined($legalsRef) && 
          lsearch($legalsRef, $formRef->{$fieldname})<0) ){

        SendSQL("SELECT description FROM fielddefs WHERE name=" . SqlQuote($fieldname));
        my $result = FetchOneColumn();
        if ($result) {
            PuntTryAgain("A legal $result was not set.");
        }
        else {
            PuntTryAgain("A legal $fieldname was not set.");
            print Param("browserbugmessage");
        }
        PutFooter();
        exit 0;
      }
}

# check and see if a given field is defined, and abort if not
# 
sub CheckFormFieldDefined (\%$) {
    my ($formRef,                # a reference to the form to check (a hash)
        $fieldname,              # the fieldname to check
       ) = @_;

    if ( !defined $formRef->{$fieldname} ) {
        print "$fieldname was not defined; ";
        print Param("browserbugmessage");
        PutFooter();
        exit 0;
      }
}

sub ValidateBugID {
    # Validates and verifies a bug ID, making sure the number is a 
    # positive integer, that it represents an existing bug in the
    # database, and that the user is authorized to access that bug.

    my ($id) = @_;

    # Make sure the bug number is a positive integer.
    # Whitespace can be ignored because the SQL server will ignore it.
    $id =~ /^\s*([1-9][0-9]*)\s*$/
      || DisplayError("The bug number is invalid. If you are trying to use " .
                      "QuickSearch, you need to enable JavaScript in your " .
                      "browser. To help us fix this limitation, look " .
                      "<a href=\"http://bugzilla.mozilla.org/show_bug.cgi?id=70907\">here</a>.") 
      && exit;

    # Get the values of the usergroupset and userid global variables
    # and write them to local variables for use within this function,
    # setting those local variables to the default value of zero if
    # the global variables are undefined.

    # First check that the bug exists
    SendSQL("SELECT bug_id FROM bugs WHERE bug_id = $id");

    FetchOneColumn()
      || DisplayError("Bug #$id does not exist.")
        && exit;

    return if CanSeeBug($id, $::userid, $::usergroupset);

    # The user did not pass any of the authorization tests, which means they
    # are not authorized to see the bug.  Display an error and stop execution.
    # The error the user sees depends on whether or not they are logged in
    # (i.e. $::userid contains the user's positive integer ID).
    if ($::userid) {
        DisplayError("You are not authorized to access bug #$id.");
    } else {
        DisplayError(
          qq|You are not authorized to access bug #$id.  To see this bug, you
          must first <a href="show_bug.cgi?id=$id&amp;GoAheadAndLogIn=1">log in 
          to an account</a> with the appropriate permissions.|
        );
    }
    exit;

}

# check and see if a given string actually represents a positive
# integer, and abort if not.
# 
sub CheckPosInt($) {
    my ($number) = @_;              # the fieldname to check

    if ( $number !~ /^[1-9][0-9]*$/ ) {
        print "Received string \"$number\" when positive integer expected; ";
        print Param("browserbugmessage");
        PutFooter();
        exit 0;
      }
}

sub FormData {
    my ($field) = (@_);
    return $::FORM{$field};
}

sub html_quote {
    my ($var) = (@_);
    $var =~ s/\&/\&amp;/g;
    $var =~ s/</\&lt;/g;
    $var =~ s/>/\&gt;/g;
    return $var;
}

sub value_quote {
    my ($var) = (@_);
    $var =~ s/\&/\&amp;/g;
    $var =~ s/</\&lt;/g;
    $var =~ s/>/\&gt;/g;
    $var =~ s/"/\&quot;/g;
    # See bug http://bugzilla.mozilla.org/show_bug.cgi?id=4928 for 
    # explanaion of why bugzilla does this linebreak substitution. 
    # This caused form submission problems in mozilla (bug 22983, 32000).
    $var =~ s/\r\n/\&#013;/g;
    $var =~ s/\n\r/\&#013;/g;
    $var =~ s/\r/\&#013;/g;
    $var =~ s/\n/\&#013;/g;
    return $var;
}

sub navigation_header {
    if (defined $::COOKIE{"BUGLIST"} && $::COOKIE{"BUGLIST"} ne "" &&
        defined $::FORM{'id'}) {
        my @bugs = split(/:/, $::COOKIE{"BUGLIST"});
        my $cur = lsearch(\@bugs, $::FORM{"id"});
        print "<B>Bug List:</B> (@{[$cur + 1]} of @{[$#bugs + 1]})\n";
        print "<A HREF=\"show_bug.cgi?id=$bugs[0]\">First</A>\n";
        print "<A HREF=\"show_bug.cgi?id=$bugs[$#bugs]\">Last</A>\n";
        if ($cur > 0) {
            print "<A HREF=\"show_bug.cgi?id=$bugs[$cur - 1]\">Prev</A>\n";
        } else {
            print "<I><FONT COLOR=\#777777>Prev</FONT></I>\n";
        }
        if ($cur < $#bugs) {
            $::next_bug = $bugs[$cur + 1];
            print "<A HREF=\"show_bug.cgi?id=$::next_bug\">Next</A>\n";
        } else {
            print "<I><FONT COLOR=\#777777>Next</FONT></I>\n";
        }
        print qq{&nbsp;&nbsp;<A HREF="buglist.cgi?regetlastlist=1">Show list</A>\n};
    }
    print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF=query.cgi>Query page</A>\n";
    print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF=enter_bug.cgi>Enter new bug</A>\n"
}

# Adds <link> elements for bug lists. These can be inserted into the header by
# (ab)using the "jscript" parameter to PutHeader, which inserts an arbitrary
# string into the header. This function is modelled on the one above.
sub navigation_links($) {
    my ($buglist) = @_;
    
    my $retval = "";
    
    # We need to be able to pass in a buglist because when you sort on a column
    # the bugs in the cookie you are given will still be in the old order.
    # If a buglist isn't passed, we just use the cookie.
    $buglist ||= $::COOKIE{"BUGLIST"};
    
    if (defined $buglist && $buglist ne "") {
    my @bugs = split(/:/, $buglist);
        
        if (defined $::FORM{'id'}) {
            # We are on an individual bug
            my $cur = lsearch(\@bugs, $::FORM{"id"});

            if ($cur > 0) {
                $retval .= "<link rel=\"First\" href=\"show_bug.cgi?id=$bugs[0]\" />\n";
                $retval .= "<link rel=\"Prev\" href=\"show_bug.cgi?id=$bugs[$cur - 1]\" />\n";
            } 
            if ($cur < $#bugs) {
                $retval .= "<link rel=\"Next\" href=\"show_bug.cgi?id=$bugs[$cur + 1]\" />\n";
                $retval .= "<link rel=\"Last\" href=\"show_bug.cgi?id=$bugs[$#bugs]\" />\n";
            }

            $retval .= "<link rel=\"Up\" href=\"buglist.cgi?regetlastlist=1\" />\n";
            $retval .= "<link rel=\"Contents\" href=\"buglist.cgi?regetlastlist=1\" />\n";
        } else {
            # We are on a bug list
            $retval .= "<link rel=\"First\" href=\"show_bug.cgi?id=$bugs[0]\" />\n";
            $retval .= "<link rel=\"Next\" href=\"show_bug.cgi?id=$bugs[0]\" />\n";
            $retval .= "<link rel=\"Last\" href=\"show_bug.cgi?id=$bugs[$#bugs]\" />\n";
        }
    }
    
    return $retval;
} 

sub make_checkboxes {
    my ($src,$default,$isregexp,$name) = (@_);
    my $last = "";
    my $capitalized = "";
    my $popup = "";
    my $found = 0;
    $default = "" if !defined $default;

    if ($src) {
        foreach my $item (@$src) {
            if ($item eq "-blank-" || $item ne $last) {
                if ($item eq "-blank-") {
                    $item = "";
                }
                $last = $item;
                $capitalized = $item;
                $capitalized =~ tr/A-Z/a-z/;
                $capitalized =~ s/^(.?)(.*)/\u$1$2/;
                if ($isregexp ? $item =~ $default : $default eq $item) {
                    $popup .= "<INPUT NAME=$name TYPE=CHECKBOX VALUE=\"$item\" CHECKED>$capitalized<br>";
                    $found = 1;
                } else {
                    $popup .= "<INPUT NAME=$name TYPE=CHECKBOX VALUE=\"$item\">$capitalized<br>";
                }
            }
        }
    }
    if (!$found && $default ne "") {
        $popup .= "<INPUT NAME=$name TYPE=CHECKBOX CHECKED>$default";
    }
    return $popup;
}

#
# make_selection_widget: creates an HTML selection widget from a list of text strings.
# $groupname is the name of the setting (form value) that this widget will control
# $src is the list of options
# you can specify a $default value which is either a string or a regex pattern to match to
#    identify the default value
# $capitalize lets you optionally capitalize the option strings; the default is the value
#    of Param("capitalizelists")
# $multiple is 1 if several options are selectable (default), 0 otherwise.
# $size is used for lists to control how many items are shown. The default is 7. A list of
#    size 1 becomes a popup menu.
# $preferLists is 1 if selection lists should be used in favor of radio buttons and
#    checkboxes, and 0 otherwise. The default is the value of Param("preferlists").
#
# The actual widget generated depends on the parameter settings:
# 
#        MULTIPLE     PREFERLISTS    SIZE     RESULT
#       0 (single)        0           =1      Popup Menu (normal for list of size 1)
#       0 (single)        0           >1      Radio buttons
#       0 (single)        1           =1      Popup Menu (normal for list of size 1)
#       0 (single)        1           n>1     List of size n, single selection
#       1 (multi)         0           n/a     Check boxes; size ignored
#       1 (multi)         1           n/a     List of size n, multiple selection, of size n
#
sub make_selection_widget {
    my ($groupname,$src,$default,$isregexp,$multiple, $size, $capitalize, $preferLists) = (@_);
    my $last = "";
    my $popup = "";
    my $found = 0;
    my $displaytext = "";
    $groupname = "" if !defined $groupname;
    $default = "" if !defined $default;
    $capitalize = Param("capitalizelists") if !defined $capitalize;
    $multiple = 1 if !defined $multiple;
    $preferLists = Param("preferlists") if !defined $preferLists;
    $size = 7 if !defined $size;
    my $type = "LIST";
    if (!$preferLists) {
        if ($multiple) {
            $type = "CHECKBOX";
        } else {
            if ($size > 1) {
                $type = "RADIO";
            }
        }
    }

    if ($type eq "LIST") {
        $popup .= "<SELECT NAME=\"$groupname\"";
        if ($multiple) {
            $popup .= " MULTIPLE";
        }
        $popup .= " SIZE=$size>\n";
    }
    if ($src) {
        foreach my $item (@$src) {
            if ($item eq "-blank-" || $item ne $last) {
                if ($item eq "-blank-") {
                    $item = "";
                }
                $last = $item;
                $displaytext = $item;
                if ($capitalize) {
                    $displaytext =~ tr/A-Z/a-z/;
                    $displaytext =~ s/^(.?)(.*)/\u$1$2/;
                }   

                if ($isregexp ? $item =~ $default : $default eq $item) {
                    if ($type eq "CHECKBOX") {
                        $popup .= "<INPUT NAME=$groupname type=checkbox VALUE=\"$item\" CHECKED>$displaytext<br>";
                    } elsif ($type eq "RADIO") {
                        $popup .= "<INPUT NAME=$groupname type=radio VALUE=\"$item\" CHECKED>$displaytext<br>";
                    } else {
                        $popup .= "<OPTION SELECTED VALUE=\"$item\">$displaytext\n";
                    }
                    $found = 1;
                } else {
                    if ($type eq "CHECKBOX") {
                        $popup .= "<INPUT NAME=$groupname type=checkbox VALUE=\"$item\">$displaytext<br>";
                    } elsif ($type eq "RADIO") {
                        $popup .= "<INPUT NAME=$groupname type=radio VALUE=\"$item\">$displaytext<br>";
                    } else {
                        $popup .= "<OPTION VALUE=\"$item\">$displaytext";
                    }
                }
            }
        }
    }
    if (!$found && $default ne "") {
        if ($type eq "CHECKBOX") {
            $popup .= "<INPUT NAME=$groupname type=checkbox CHECKED>$default";
        } elsif ($type eq "RADIO") {
            $popup .= "<INPUT NAME=$groupname type=radio checked>$default";
        } else {
            $popup .= "<OPTION SELECTED>$default\n";
        }
    }
    if ($type eq "LIST") {
        $popup .= "</SELECT>";
    }
    return $popup;
}


$::CheckOptionValues = 1;

sub make_options {
    my ($src,$default,$isregexp) = (@_);
    my $last = "";
    my $popup = "";
    my $found = 0;
    $default = "" if !defined $default;

    if ($src) {
        foreach my $item (@$src) {
            if ($item eq "-blank-" || $item ne $last) {
                if ($item eq "-blank-") {
                    $item = "";
                }
                $last = $item;
                if ($isregexp ? $item =~ $default : $default eq $item) {
                    $popup .= "<OPTION SELECTED VALUE=\"$item\">$item\n";
                    $found = 1;
                } else {
                    $popup .= "<OPTION VALUE=\"$item\">$item\n";
                }
            }
        }
    }
    if (!$found && $default ne "") {
      if ( Param("strictvaluechecks") && $::CheckOptionValues &&
           ($default ne $::dontchange) && ($default ne "-All-") &&
           ($default ne "DUPLICATE") ) {
        print "Possible bug database corruption has been detected.  " .
              "Please send mail to " . Param("maintainer") . " with " .
              "details of what you were doing when this message " . 
              "appeared.  Thank you.\n";
        if (!$src) {
            $src = ["???null???"];
        }
        print "<pre>src = " . value_quote(join(' ', @$src)) . "\n";
        print "default = " . value_quote($default) . "</pre>";
        PutFooter();
#        confess "Gulp.";
        exit 0;
              
      } else {
        $popup .= "<OPTION SELECTED>$default\n";
      }
    }
    return $popup;
}


sub make_popup {
    my ($name,$src,$default,$listtype,$onchange) = (@_);
    my $popup = "<SELECT NAME=$name";
    if ($listtype > 0) {
        $popup .= " SIZE=5";
        if ($listtype == 2) {
            $popup .= " MULTIPLE";
        }
    }
    if (defined $onchange && $onchange ne "") {
        $popup .= " onchange=$onchange";
    }
    $popup .= ">" . make_options($src, $default,
                                 ($listtype == 2 && $default ne ""));
    $popup .= "</SELECT>";
    return $popup;
}


sub BuildPulldown {
    my ($name, $valuelist, $default) = (@_);

    my $entry = qq{<SELECT NAME="$name">};
    foreach my $i (@$valuelist) {
        my ($tag, $desc) = (@$i);
        my $selectpart = "";
        if ($tag eq $default) {
            $selectpart = " SELECTED";
        }
        if (!defined $desc) {
            $desc = $tag;
        }
        $entry .= qq{<OPTION$selectpart VALUE="$tag">$desc\n};
    }
    $entry .= qq{</SELECT>};
    return $entry;
}




sub PasswordForLogin {
    my ($login) = (@_);
    SendSQL("select cryptpassword from profiles where login_name = " .
            SqlQuote($login));
    my $result = FetchOneColumn();
    if (!defined $result) {
        $result = "";
    }
    return $result;
}


sub quietly_check_login() {
    $::usergroupset = '0';
    my $loginok = 0;
    $::disabledreason = '';
    $::userid = 0;
    if (defined $::COOKIE{"Bugzilla_login"} &&
        defined $::COOKIE{"Bugzilla_logincookie"}) {
        ConnectToDatabase();
        if (!defined $ENV{'REMOTE_HOST'}) {
            $ENV{'REMOTE_HOST'} = $ENV{'REMOTE_ADDR'};
        }
        SendSQL("SELECT profiles.userid, profiles.groupset, " .
                "profiles.login_name, " .
                "profiles.login_name = " .
                SqlQuote($::COOKIE{"Bugzilla_login"}) .
                " AND profiles.cryptpassword = <PASSWORD> " .
                "AND logincookies.hostname = " .
                SqlQuote($ENV{"REMOTE_HOST"}) .
                ", profiles.disabledtext " .
                " FROM profiles, logincookies WHERE logincookies.cookie = " .
                SqlQuote($::COOKIE{"Bugzilla_logincookie"}) .
                " AND profiles.userid = logincookies.userid");
        my @row;
        if (@row = FetchSQLData()) {
            my ($userid, $groupset, $loginname, $ok, $disabledtext) = (@row);
            if ($ok) {
                if ($disabledtext eq '') {
                    $loginok = 1;
                    $::userid = $userid;
                    $::usergroupset = $groupset;
                    $::COOKIE{"Bugzilla_login"} = $loginname; # Makes sure case
                                                              # is in
                                                              # canonical form.
                } else {
                    $::disabledreason = $disabledtext;
                }
            }
        }
    }
    # if 'who' is passed in, verify that it's a good value
    if ($::FORM{'who'}) {
        my $whoid = DBname_to_id($::FORM{'who'});
        delete $::FORM{'who'} unless $whoid;
    }
    if (!$loginok) {
        delete $::COOKIE{"Bugzilla_login"};
    }
    return $loginok;
}




sub CheckEmailSyntax {
    my ($addr) = (@_);
    my $match = Param('emailregexp');
    if ($addr !~ /$match/ || $addr =~ /[\\\(\)<>&,;:"\[\] \t\r\n]/) {
        print "Content-type: text/html\n\n";

        # For security, escape HTML special characters.
        $addr = html_quote($addr);

        PutHeader("Check e-mail syntax");
        print "The e-mail address you entered\n";
        print "(<b>$addr</b>) didn't match our minimal\n";
        print "syntax checking for a legal email address.\n";
        print Param('emailregexpdesc') . "\n";
        print "It must also not contain any of these special characters: " .
              "<tt>\\ ( ) &amp; &lt; &gt; , ; : \" [ ]</tt> " .
              "or any whitespace.\n";
        print "<p>Please click <b>Back</b> and try again.\n";
        PutFooter();
        exit;
    }
}



sub MailPassword {
    my ($login, $password) = (@_);
    my $urlbase = Param("urlbase");
    my $template = Param("passwordmail");
    my $msg = PerformSubsts($template,
                            {"mailaddress" => $login . Param('emailsuffix'),
                             "login" => $login,
                             "password" => $password});

    open SENDMAIL, "|/usr/lib/sendmail -t";
    print SENDMAIL $msg;
    close SENDMAIL;

    print "The password for the e-mail address\n";
    print "$login has been e-mailed to that address.\n";
    print "<p>When the e-mail arrives, you can click <b>Back</b>\n";
    print "and enter your password in the form there.\n";
}


sub confirm_login {
    my ($nexturl) = (@_);

# Uncommenting the next line can help debugging...
#    print "Content-type: text/plain\n\n";

    ConnectToDatabase();
    # I'm going to reorganize some of this stuff a bit.  Since we're adding
    # a second possible validation method (LDAP), we need to move some of this
    # to a later section.  -<NAME>, 8/3/00
    my $enteredlogin = "";
    my $realcryptpwd = "";

    # If the form contains Bugzilla login and password fields, use Bugzilla's 
    # built-in authentication to authenticate the user (otherwise use LDAP below).
    if (defined $::FORM{"Bugzilla_login"} && defined $::FORM{"Bugzilla_password"}) {
        # Make sure the user's login name is a valid email address.
        $enteredlogin = $::FORM{"Bugzilla_login"};
        CheckEmailSyntax($enteredlogin);

        # Retrieve the user's ID and crypted password from the database.
        my $userid;
        SendSQL("SELECT userid, cryptpassword FROM profiles 
                 WHERE login_name = " . SqlQuote($enteredlogin));
        ($userid, $realcryptpwd) = FetchSQLData();

        # Make sure the user exists or throw an error (but do not admit it was a username
        # error to make it harder for a cracker to find account names by brute force).
        $userid
          || DisplayError("The username or password you entered is not valid.")
          && exit;

        # If this is a new user, generate a password, insert a record
        # into the database, and email their password to them.
        if ( defined $::FORM{"PleaseMailAPassword"} && !$userid ) {
            my $password = InsertNewUser($enteredlogin, "");
            print "Content-Type: text/html\n\n";
            PutHeader("Account Created");
            MailPassword($enteredlogin, $password);
            PutFooter();
            exit;
        }

        # Otherwise, authenticate the user.
        else {
            # Get the salt from the user's crypted password.
            my $salt = $realcryptpwd;

            # Using the salt, crypt the password the user entered.
            my $enteredCryptedPassword = crypt( $::FORM{"Bugzilla_password"} , $salt );

            # Make sure the passwords match or throw an error.
            ($enteredCryptedPassword eq $realcryptpwd)
              || DisplayError("The username or password you entered is not valid.")
              && exit;

            # If the user has successfully logged in, delete any password tokens
            # lying around in the system for them.
            use Token;
            my $token = Token::HasPasswordToken($userid);
            while ( $token ) {
                Token::Cancel($token, "user logged in");
                $token = Token::HasPasswordToken($userid);
            }
        }

     } elsif (Param("useLDAP") &&
              defined $::FORM{"LDAP_login"} &&
              defined $::FORM{"LDAP_password"}) {
       # If we're using LDAP for login, we've got an entirely different
       # set of things to check.

# see comment at top of file near eval
       # First, if we don't have the LDAP modules available to us, we can't
       # do this.
#       if(!$have_ldap) {
#         print "Content-type: text/html\n\n";
#         PutHeader("LDAP not enabled");
#         print "The necessary modules for LDAP login are not installed on ";
#         print "this machine.  Please send mail to ".Param("maintainer");
#         print " and notify him of this problem.\n";
#         PutFooter();
#         exit;
#       }

       # Next, we need to bind anonymously to the LDAP server.  This is
       # because we need to get the Distinguished Name of the user trying
       # to log in.  Some servers (such as iPlanet) allow you to have unique
       # uids spread out over a subtree of an area (such as "People"), so
       # just appending the Base DN to the uid isn't sufficient to get the
       # user's DN.  For servers which don't work this way, there will still
       # be no harm done.
       my $LDAPserver = Param("LDAPserver");
       if ($LDAPserver eq "") {
         print "Content-type: text/html\n\n";
         PutHeader("LDAP server not defined");
         print "The LDAP server for authentication has not been defined.  ";
         print "Please contact ".Param("maintainer")." ";
         print "and notify him of this problem.\n";
         PutFooter();
         exit;
       }

       my $LDAPport = "389";  #default LDAP port
       if($LDAPserver =~ /:/) {
         ($LDAPserver, $LDAPport) = split(":",$LDAPserver);
       }
       my $LDAPconn = new Mozilla::LDAP::Conn($LDAPserver,$LDAPport);
       if(!$LDAPconn) {
         print "Content-type: text/html\n\n";
         PutHeader("Unable to connect to LDAP server");
         print "I was unable to connect to the LDAP server for user ";
         print "authentication.  Please contact ".Param("maintainer");
         print " and notify him of this problem.\n";
         PutFooter();
         exit;
       }

       # if no password was provided, then fail the authentication
       # while it may be valid to not have an LDAP password, when you
       # bind without a password (regardless of the binddn value), you
       # will get an anonymous bind.  I do not know of a way to determine
       # whether a bind is anonymous or not without making changes to the
       # LDAP access control settings
       if ( ! $::FORM{"LDAP_password"} ) {
         print "Content-type: text/html\n\n";
         PutHeader("Login Failed");
         print "You did not provide a password.\n";
         print "Please click <b>Back</b> and try again.\n";
         PutFooter();
         exit;
       }

       # We've got our anonymous bind;  let's look up this user.
       my $dnEntry = $LDAPconn->search(Param("LDAPBaseDN"),"subtree","uid=".$::FORM{"LDAP_login"});
       if(!$dnEntry) {
         print "Content-type: text/html\n\n";
         PutHeader("Login Failed");
         print "The username or password you entered is not valid.\n";
         print "Please click <b>Back</b> and try again.\n";
         PutFooter();
         exit;
       }

       # Now we get the DN from this search.  Once we've got that, we're
       # done with the anonymous bind, so we close it.
       my $userDN = $dnEntry->getDN;
       $LDAPconn->close;

       # Now we attempt to bind as the specified user.
       $LDAPconn = new Mozilla::LDAP::Conn($LDAPserver,$LDAPport,$userDN,$::FORM{"LDAP_password"});
{'fraction_non_alphanumeric': 0.1481335020563113, 'fraction_numerical': 0.0052673204682062635, 'mean_word_length': 1.8821974014132665, 'pattern_counts': {'":': 2, '<': 235, '<?xml version=': 0, '>': 283, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5223596', 'n_tokens_mistral': 19681, 'n_tokens_neox': 18628, 'n_words': 5868}