REGRESSION (r243153): [iOS] TestWebKitAPI.FocusPreservationTests.ChangingFocusedNodeR...
[WebKit-https.git] / Websites / bugs.webkit.org / Bugzilla.pm
index 65ddcc2..e4772e0 100644 (file)
@@ -1,30 +1,15 @@
-# -*- Mode: perl; indent-tabs-mode: nil -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
 #
-# 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): Bradley Baetz <bbaetz@student.usyd.edu.au>
-#                 Erik Stambaugh <erik@dasbistro.com>
-#                 A. Karl Kornel <karl@kornel.name>
-#                 Marc Schumann <wurblzap@gmail.com>
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
 
 package Bugzilla;
 
+use 5.10.1;
 use strict;
+use warnings;
 
 # We want any compile errors to get to the browser, if possible.
 BEGIN {
@@ -35,23 +20,24 @@ BEGIN {
     }
 }
 
-use Bugzilla::Config;
-use Bugzilla::Constants;
 use Bugzilla::Auth;
 use Bugzilla::Auth::Persist::Cookie;
 use Bugzilla::CGI;
-use Bugzilla::Extension;
+use Bugzilla::Config;
+use Bugzilla::Constants;
 use Bugzilla::DB;
-use Bugzilla::Install::Localconfig qw(read_localconfig);
-use Bugzilla::Install::Requirements qw(OPTIONAL_MODULES);
-use Bugzilla::Install::Util qw(init_console);
-use Bugzilla::Template;
-use Bugzilla::User;
 use Bugzilla::Error;
-use Bugzilla::Util;
+use Bugzilla::Extension;
 use Bugzilla::Field;
 use Bugzilla::Flag;
+use Bugzilla::Install::Localconfig qw(read_localconfig);
+use Bugzilla::Install::Requirements qw(OPTIONAL_MODULES have_vers);
+use Bugzilla::Install::Util qw(init_console include_languages);
+use Bugzilla::Memcached;
+use Bugzilla::Template;
 use Bugzilla::Token;
+use Bugzilla::User;
+use Bugzilla::Util;
 
 use File::Basename;
 use File::Spec::Functions;
@@ -84,7 +70,7 @@ use constant SHUTDOWNHTML_RETRY_AFTER => 3600;
 # Global Code
 #####################################################################
 
-# $::SIG{__DIE__} = i_am_cgi() ? \&CGI::Carp::confess : \&Carp::confess;
+#$::SIG{__DIE__} = i_am_cgi() ? \&CGI::Carp::confess : \&Carp::confess;
 
 # Note that this is a raw subroutine, not a method, so $class isn't available.
 sub init_page {
@@ -96,11 +82,28 @@ sub init_page {
     }
 
     if (${^TAINT}) {
+        my $path = '';
+        if (ON_WINDOWS) {
+            # On Windows, these paths are tainted, preventing
+            # File::Spec::Win32->tmpdir from using them. But we need
+            # a place to temporary store attachments which are uploaded.
+            foreach my $temp (qw(TMPDIR TMP TEMP WINDIR)) {
+                trick_taint($ENV{$temp}) if $ENV{$temp};
+            }
+            # Some DLLs used by Strawberry Perl are also in c\bin,
+            # see https://rt.cpan.org/Public/Bug/Display.html?id=99104
+            if (!ON_ACTIVESTATE) {
+                my $c_path = $path = dirname($^X);
+                $c_path =~ s/\bperl\b(?=\\bin)/c/;
+                $path .= ";$c_path";
+                trick_taint($path);
+            }
+        }
         # Some environment variables are not taint safe
         delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
         # Some modules throw undefined errors (notably File::Spec::Win32) if
         # PATH is undefined.
-        $ENV{'PATH'} = '';
+        $ENV{'PATH'} = $path;
     }
 
     # Because this function is run live from perl "use" commands of
@@ -136,8 +139,8 @@ sub init_page {
     #
     # This code must go here. It cannot go anywhere in Bugzilla::CGI, because
     # it uses Template, and that causes various dependency loops.
-    if (Bugzilla->params->{"shutdownhtml"}
-        && !grep { $_ eq $script } SHUTDOWNHTML_EXEMPT)
+    if (!grep { $_ eq $script } SHUTDOWNHTML_EXEMPT
+        and Bugzilla->params->{'shutdownhtml'})
     {
         # Allow non-cgi scripts to exit silently (without displaying any
         # message), if desired. At this point, no DBI call has been made
@@ -180,10 +183,8 @@ sub init_page {
             print Bugzilla->cgi->header(-status => 503, 
                 -retry_after => SHUTDOWNHTML_RETRY_AFTER);
         }
-        my $t_output;
-        $template->process("global/message.$extension.tmpl", $vars, \$t_output)
+        $template->process("global/message.$extension.tmpl", $vars)
             || ThrowTemplateError($template->error);
-        print $t_output . "\n";
         exit;
     }
 }
@@ -193,9 +194,7 @@ sub init_page {
 #####################################################################
 
 sub template {
-    my $class = shift;
-    $class->request_cache->{template} ||= Bugzilla::Template->create();
-    return $class->request_cache->{template};
+    return $_[0]->request_cache->{template} ||= Bugzilla::Template->create();
 }
 
 sub template_inner {
@@ -203,9 +202,7 @@ sub template_inner {
     my $cache = $class->request_cache;
     my $current_lang = $cache->{template_current_lang}->[0];
     $lang ||= $current_lang || '';
-    $class->request_cache->{"template_inner_$lang"}
-        ||= Bugzilla::Template->create(language => $lang);
-    return $class->request_cache->{"template_inner_$lang"};
+    return $cache->{"template_inner_$lang"} ||= Bugzilla::Template->create(language => $lang);
 }
 
 our $extension_packages;
@@ -240,7 +237,7 @@ sub feature {
         foreach my $package (@{ OPTIONAL_MODULES() }) {
             foreach my $f (@{ $package->{feature} }) {
                 $feature_map->{$f} ||= [];
-                push(@{ $feature_map->{$f} }, $package->{module});
+                push(@{ $feature_map->{$f} }, $package);
             }
         }
         $cache->{feature_map} = $feature_map;
@@ -251,22 +248,15 @@ sub feature {
     }
 
     my $success = 1;
-    foreach my $module (@{ $feature_map->{$feature} }) {
-        # We can't use a string eval and "use" here (it kills Template-Toolkit,
-        # see https://rt.cpan.org/Public/Bug/Display.html?id=47929), so we have
-        # to do a block eval.
-        $module =~ s{::}{/}g;
-        $module .= ".pm";
-        eval { require $module; 1; } or $success = 0;
+    foreach my $package (@{ $feature_map->{$feature} }) {
+        have_vers($package) or $success = 0;
     }
     $cache->{feature}->{$feature} = $success;
     return $success;
 }
 
 sub cgi {
-    my $class = shift;
-    $class->request_cache->{cgi} ||= new Bugzilla::CGI();
-    return $class->request_cache->{cgi};
+    return $_[0]->request_cache->{cgi} ||= new Bugzilla::CGI();
 }
 
 sub input_params {
@@ -286,21 +276,15 @@ sub input_params {
 }
 
 sub localconfig {
-    my $class = shift;
-    $class->request_cache->{localconfig} ||= read_localconfig();
-    return $class->request_cache->{localconfig};
+    return $_[0]->process_cache->{localconfig} ||= read_localconfig();
 }
 
 sub params {
-    my $class = shift;
-    $class->request_cache->{params} ||= Bugzilla::Config::read_param_file();
-    return $class->request_cache->{params};
+    return $_[0]->request_cache->{params} ||= Bugzilla::Config::read_param_file();
 }
 
 sub user {
-    my $class = shift;
-    $class->request_cache->{user} ||= new Bugzilla::User;
-    return $class->request_cache->{user};
+    return $_[0]->request_cache->{user} ||= new Bugzilla::User;
 }
 
 sub set_user {
@@ -309,8 +293,7 @@ sub set_user {
 }
 
 sub sudoer {
-    my $class = shift;    
-    return $class->request_cache->{sudoer};
+    return $_[0]->request_cache->{sudoer};
 }
 
 sub sudo_request {
@@ -388,6 +371,12 @@ sub login {
         $class->set_user($authenticated_user);
     }
 
+    if ($class->sudoer) {
+        $class->sudoer->update_last_seen_date();
+    } else {
+        $class->user->update_last_seen_date();
+    }
+
     return $class->user;
 }
 
@@ -426,38 +415,40 @@ sub logout_request {
 }
 
 sub job_queue {
-    my $class = shift;
     require Bugzilla::JobQueue;
-    $class->request_cache->{job_queue} ||= Bugzilla::JobQueue->new();
-    return $class->request_cache->{job_queue};
+    return $_[0]->request_cache->{job_queue} ||= Bugzilla::JobQueue->new();
 }
 
 sub dbh {
-    my $class = shift;
     # If we're not connected, then we must want the main db
-    $class->request_cache->{dbh} ||= $class->dbh_main;
-
-    return $class->request_cache->{dbh};
+    return $_[0]->request_cache->{dbh} ||= $_[0]->dbh_main;
 }
 
 sub dbh_main {
-    my $class = shift;
-    $class->request_cache->{dbh_main} ||= Bugzilla::DB::connect_main();
-    return $class->request_cache->{dbh_main};
+    return $_[0]->request_cache->{dbh_main} ||= Bugzilla::DB::connect_main();
 }
 
 sub languages {
-    my $class = shift;
     return Bugzilla::Install::Util::supported_languages();
 }
 
+sub current_language {
+    return $_[0]->request_cache->{current_language} ||= (include_languages())[0];
+}
+
 sub error_mode {
     my ($class, $newval) = @_;
     if (defined $newval) {
         $class->request_cache->{error_mode} = $newval;
     }
-    return $class->request_cache->{error_mode}
-        || (i_am_cgi() ? ERROR_MODE_WEBPAGE : ERROR_MODE_DIE);
+
+    # XXX - Once we require Perl 5.10.1, this test can be replaced by //.
+    if (exists $class->request_cache->{error_mode}) {
+        return $class->request_cache->{error_mode};
+    }
+    else {
+        return (i_am_cgi() ? ERROR_MODE_WEBPAGE : ERROR_MODE_DIE);
+    }
 }
 
 # This is used only by Bugzilla::Error to throw errors.
@@ -490,14 +481,23 @@ sub usage_mode {
         elsif ($newval == USAGE_MODE_TEST) {
             $class->error_mode(ERROR_MODE_TEST);
         }
+        elsif ($newval == USAGE_MODE_REST) {
+            $class->error_mode(ERROR_MODE_REST);
+        }
         else {
             ThrowCodeError('usage_mode_invalid',
                            {'invalid_usage_mode', $newval});
         }
         $class->request_cache->{usage_mode} = $newval;
     }
-    return $class->request_cache->{usage_mode}
-        || (i_am_cgi()? USAGE_MODE_BROWSER : USAGE_MODE_CMDLINE);
+
+    # XXX - Once we require Perl 5.10.1, this test can be replaced by //.
+    if (exists $class->request_cache->{usage_mode}) {
+        return $class->request_cache->{usage_mode};
+    }
+    else {
+        return (i_am_cgi()? USAGE_MODE_BROWSER : USAGE_MODE_CMDLINE);
+    }
 }
 
 sub installation_mode {
@@ -547,6 +547,11 @@ sub switch_to_main_db {
     return $class->dbh_main;
 }
 
+sub is_shadow_db {
+    my $class = shift;
+    return $class->request_cache->{dbh} != $class->dbh_main;
+}
+
 sub fields {
     my ($class, $criteria) = @_;
     $criteria ||= {};
@@ -592,7 +597,8 @@ sub fields {
         }
     }
 
-    return $do_by_name ? \%requested : [values %requested];
+    return $do_by_name ? \%requested
+        : [sort { $a->sortkey <=> $b->sortkey || $a->name cmp $b->name } values %requested];
 }
 
 sub active_custom_fields {
@@ -614,13 +620,8 @@ sub has_flags {
 }
 
 sub local_timezone {
-    my $class = shift;
-
-    if (!defined $class->request_cache->{local_timezone}) {
-        $class->request_cache->{local_timezone} =
-          DateTime::TimeZone->new(name => 'local');
-    }
-    return $class->request_cache->{local_timezone};
+    return $_[0]->process_cache->{local_timezone}
+             ||= DateTime::TimeZone->new(name => 'local');
 }
 
 # This creates the request cache for non-mod_perl installations.
@@ -641,19 +642,49 @@ sub request_cache {
     return $_request_cache;
 }
 
+sub clear_request_cache {
+    $_request_cache = {};
+    if ($ENV{MOD_PERL}) {
+        require Apache2::RequestUtil;
+        my $request = eval { Apache2::RequestUtil->request };
+        if ($request) {
+            my $pnotes = $request->pnotes;
+            delete @$pnotes{(keys %$pnotes)};
+        }
+    }
+}
+
+# This is a per-process cache.  Under mod_cgi it's identical to the
+# request_cache.  When using mod_perl, items in this cache live until the
+# worker process is terminated.
+our $_process_cache = {};
+
+sub process_cache {
+    return $_process_cache;
+}
+
+# This is a memcached wrapper, which provides cross-process and cross-system
+# caching.
+sub memcached {
+    return $_[0]->process_cache->{memcached} ||= Bugzilla::Memcached->_new();
+}
+
 # Private methods
 
 # Per-process cleanup. Note that this is a plain subroutine, not a method,
 # so we don't have $class available.
 sub _cleanup {
-    my $main   = Bugzilla->request_cache->{dbh_main};
-    my $shadow = Bugzilla->request_cache->{dbh_shadow};
+    my $cache = Bugzilla->request_cache;
+    my $main = $cache->{dbh_main};
+    my $shadow = $cache->{dbh_shadow};
     foreach my $dbh ($main, $shadow) {
         next if !$dbh;
         $dbh->bz_rollback_transaction() if $dbh->bz_in_transaction;
         $dbh->disconnect;
     }
-    undef $_request_cache;
+    my $smtp = $cache->{smtp};
+    $smtp->disconnect if $smtp;
+    clear_request_cache();
 
     # These are both set by CGI.pm but need to be undone so that
     # Apache can actually shut down its children if it needs to.
@@ -744,7 +775,7 @@ If you ever need a L<Bugzilla::Template> object while you're already
 processing a template, use this. Also use it if you want to specify
 the language to use. If no argument is passed, it uses the last
 language set. If the argument is "" (empty string), the language is
-reset to the current one (the one used by Bugzilla->template).
+reset to the current one (the one used by C<Bugzilla-E<gt>template>).
 
 =item C<cgi>
 
@@ -774,10 +805,10 @@ not an arrayref.
 
 =item C<user>
 
-C<undef> if there is no currently logged in user or if the login code has not
-yet been run.  If an sudo session is in progress, the C<Bugzilla::User>
-corresponding to the person who is being impersonated.  If no session is in
-progress, the current C<Bugzilla::User>.
+Default C<Bugzilla::User> object if there is no currently logged in user or
+if the login code has not yet been run.  If an sudo session is in progress,
+the C<Bugzilla::User> corresponding to the person who is being impersonated.
+If no session is in progress, the current C<Bugzilla::User>.
 
 =item C<set_user>
 
@@ -821,7 +852,7 @@ default), LOGOUT_ALL or LOGOUT_KEEP_CURRENT.
 
 =item C<logout_user($user)>
 
-Logs out the specified user (invalidating all his sessions), taking a
+Logs out the specified user (invalidating all their sessions), taking a
 Bugzilla::User instance.
 
 =item C<logout_by_id($id)>
@@ -847,7 +878,7 @@ in a hashref:
 =item C<by_name>
 
 If false (or not specified), this method will return an arrayref of
-the requested fields. The order of the returned fields is random.
+the requested fields.
 
 If true, this method will return a hashref of fields, where the keys
 are field names and the valules are L<Bugzilla::Field> objects.
@@ -862,8 +893,8 @@ specify this argument, all fields will be returned.
 
 =item C<error_mode>
 
-Call either C<Bugzilla->error_mode(Bugzilla::Constants::ERROR_MODE_DIE)>
-or C<Bugzilla->error_mode(Bugzilla::Constants::ERROR_MODE_DIE_SOAP_FAULT)> to
+Call either C<Bugzilla-E<gt>error_mode(Bugzilla::Constants::ERROR_MODE_DIE)>
+or C<Bugzilla-E<gt>error_mode(Bugzilla::Constants::ERROR_MODE_DIE_SOAP_FAULT)> to
 change this flag's default of C<Bugzilla::Constants::ERROR_MODE_WEBPAGE> and to
 indicate that errors should be passed to error mode specific error handlers
 rather than being sent to a browser and finished with an exit().
@@ -872,24 +903,24 @@ This is useful, for example, to keep C<eval> blocks from producing wild HTML
 on errors, making it easier for you to catch them.
 (Remember to reset the error mode to its previous value afterwards, though.)
 
-C<Bugzilla->error_mode> will return the current state of this flag.
+C<Bugzilla-E<gt>error_mode> will return the current state of this flag.
 
-Note that C<Bugzilla->error_mode> is being called by C<Bugzilla->usage_mode> on
+Note that C<Bugzilla-E<gt>error_mode> is being called by C<Bugzilla-E<gt>usage_mode> on
 usage mode changes.
 
 =item C<usage_mode>
 
-Call either C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_CMDLINE)>
-or C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_XMLRPC)> near the
+Call either C<Bugzilla-E<gt>usage_mode(Bugzilla::Constants::USAGE_MODE_CMDLINE)>
+or C<Bugzilla-E<gt>usage_mode(Bugzilla::Constants::USAGE_MODE_XMLRPC)> near the
 beginning of your script to change this flag's default of
 C<Bugzilla::Constants::USAGE_MODE_BROWSER> and to indicate that Bugzilla is
 being called in a non-interactive manner.
 
 This influences error handling because on usage mode changes, C<usage_mode>
-calls C<Bugzilla->error_mode> to set an error mode which makes sense for the
+calls C<Bugzilla-E<gt>error_mode> to set an error mode which makes sense for the
 usage mode.
 
-C<Bugzilla->usage_mode> will return the current state of this flag.
+C<Bugzilla-E<gt>usage_mode> will return the current state of this flag.
 
 =item C<installation_mode>
 
@@ -914,6 +945,10 @@ The main database handle. See L<DBI>.
 Currently installed languages.
 Returns a reference to a list of RFC 1766 language tags of installed languages.
 
+=item C<current_language>
+
+The currently active language.
+
 =item C<switch_to_shadow_db>
 
 Switch from using the main database to using the shadow database.
@@ -922,11 +957,17 @@ Switch from using the main database to using the shadow database.
 
 Change the database object to refer to the main database.
 
+=item C<is_shadow_db>
+
+Returns true if the currently active database is the shadow database.
+Returns false if a the currently active database is the man database, or if a
+shadow database is not configured or enabled.
+
 =item C<params>
 
-The current Parameters of Bugzilla, as a hashref. If C<data/params>
-does not exist, then we return an empty hashref. If C<data/params>
-is unreadable or is not valid perl, we C<die>.
+The current Parameters of Bugzilla, as a hashref. If C<data/params.json>
+does not exist, then we return an empty hashref. If C<data/params.json>
+is unreadable or is not valid, we C<die>.
 
 =item C<local_timezone>
 
@@ -946,3 +987,99 @@ Tells you whether or not a specific feature is enabled. For names
 of features, see C<OPTIONAL_MODULES> in C<Bugzilla::Install::Requirements>.
 
 =back
+
+=head1 B<CACHING>
+
+Bugzilla has several different caches available which provide different
+capabilities and lifetimes.
+
+The keys of all caches are unregulated; use of prefixes is suggested to avoid
+collisions.
+
+=over
+
+=item B<Request Cache>
+
+The request cache is a hashref which supports caching any perl variable for the
+duration of the current request. At the end of the current request the contents
+of this cache are cleared.
+
+Examples of its use include caching objects to avoid re-fetching the same data
+from the database, and passing data between otherwise unconnected parts of
+Bugzilla.
+
+=over
+
+=item C<request_cache>
+
+Returns a hashref which can be checked and modified to store any perl variable
+for the duration of the current request.
+
+=item C<clear_request_cache>
+
+Removes all entries from the C<request_cache>.
+
+=back
+
+=item B<Process Cache>
+
+The process cache is a hashref which support caching of any perl variable. If
+Bugzilla is configured to run using Apache mod_perl, the contents of this cache
+are persisted across requests for the lifetime of the Apache worker process
+(which varies depending on the SizeLimit configuration in mod_perl.pl).
+
+If Bugzilla isn't running under mod_perl, the process cache's contents are
+cleared at the end of the request.
+
+The process cache is only suitable for items which never change while Bugzilla
+is running (for example the path where Bugzilla is installed).
+
+=over
+
+=item C<process_cache>
+
+Returns a hashref which can be checked and modified to store any perl variable
+for the duration of the current process (mod_perl) or request (mod_cgi).
+
+=back
+
+=item B<Memcached>
+
+If Memcached is installed and configured, Bugzilla can use it to cache data
+across requests and between webheads. Unlike the request and process caches,
+only scalars, hashrefs, and arrayrefs can be stored in Memcached.
+
+Memcached integration is only required for large installations of Bugzilla -- if
+you have multiple webheads then configuring Memcached is recommended.
+
+=over
+
+=item C<memcached>
+
+Returns a C<Bugzilla::Memcached> object. An object is always returned even if
+Memcached is not available.
+
+See the documentation for the C<Bugzilla::Memcached> module for more
+information.
+
+=back
+
+=back
+
+=head1 B<Methods in need of POD>
+
+=over
+
+=item init_page
+
+=item extensions
+
+=item logout_user_by_id
+
+=item localconfig
+
+=item active_custom_fields
+
+=item has_flags
+
+=back