Upgrade bugs.webkit.org to Bugzilla 4.2
[WebKit-https.git] / Websites / bugs.webkit.org / Bugzilla.pm
1 # -*- Mode: perl; indent-tabs-mode: nil -*-
2 #
3 # The contents of this file are subject to the Mozilla Public
4 # License Version 1.1 (the "License"); you may not use this file
5 # except in compliance with the License. You may obtain a copy of
6 # the License at http://www.mozilla.org/MPL/
7 #
8 # Software distributed under the License is distributed on an "AS
9 # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10 # implied. See the License for the specific language governing
11 # rights and limitations under the License.
12 #
13 # The Original Code is the Bugzilla Bug Tracking System.
14 #
15 # The Initial Developer of the Original Code is Netscape Communications
16 # Corporation. Portions created by Netscape are
17 # Copyright (C) 1998 Netscape Communications Corporation. All
18 # Rights Reserved.
19 #
20 # Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
21 #                 Erik Stambaugh <erik@dasbistro.com>
22 #                 A. Karl Kornel <karl@kornel.name>
23 #                 Marc Schumann <wurblzap@gmail.com>
24
25 package Bugzilla;
26
27 use strict;
28
29 # We want any compile errors to get to the browser, if possible.
30 BEGIN {
31     # This makes sure we're in a CGI.
32     if ($ENV{SERVER_SOFTWARE} && !$ENV{MOD_PERL}) {
33         require CGI::Carp;
34         CGI::Carp->import('fatalsToBrowser');
35     }
36 }
37
38 use Bugzilla::Config;
39 use Bugzilla::Constants;
40 use Bugzilla::Auth;
41 use Bugzilla::Auth::Persist::Cookie;
42 use Bugzilla::CGI;
43 use Bugzilla::Extension;
44 use Bugzilla::DB;
45 use Bugzilla::Install::Localconfig qw(read_localconfig);
46 use Bugzilla::Install::Requirements qw(OPTIONAL_MODULES);
47 use Bugzilla::Install::Util qw(init_console);
48 use Bugzilla::Template;
49 use Bugzilla::User;
50 use Bugzilla::Error;
51 use Bugzilla::Util;
52 use Bugzilla::Field;
53 use Bugzilla::Flag;
54 use Bugzilla::Token;
55
56 use File::Basename;
57 use File::Spec::Functions;
58 use DateTime::TimeZone;
59 use Date::Parse;
60 use Safe;
61
62 #####################################################################
63 # Constants
64 #####################################################################
65
66 # Scripts that are not stopped by shutdownhtml being in effect.
67 use constant SHUTDOWNHTML_EXEMPT => qw(
68     editparams.cgi
69     checksetup.pl
70     migrate.pl
71     recode.pl
72 );
73
74 # Non-cgi scripts that should silently exit.
75 use constant SHUTDOWNHTML_EXIT_SILENTLY => qw(
76     whine.pl
77 );
78
79 # shutdownhtml pages are sent as an HTTP 503. After how many seconds
80 # should search engines attempt to index the page again?
81 use constant SHUTDOWNHTML_RETRY_AFTER => 3600;
82
83 #####################################################################
84 # Global Code
85 #####################################################################
86
87 # $::SIG{__DIE__} = i_am_cgi() ? \&CGI::Carp::confess : \&Carp::confess;
88
89 # Note that this is a raw subroutine, not a method, so $class isn't available.
90 sub init_page {
91     if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
92         init_console();
93     }
94     elsif (Bugzilla->params->{'utf8'}) {
95         binmode STDOUT, ':utf8';
96     }
97
98     if (${^TAINT}) {
99         # Some environment variables are not taint safe
100         delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
101         # Some modules throw undefined errors (notably File::Spec::Win32) if
102         # PATH is undefined.
103         $ENV{'PATH'} = '';
104     }
105
106     # Because this function is run live from perl "use" commands of
107     # other scripts, we're skipping the rest of this function if we get here
108     # during a perl syntax check (perl -c, like we do during the
109     # 001compile.t test).
110     return if $^C;
111
112     # IIS prints out warnings to the webpage, so ignore them, or log them
113     # to a file if the file exists.
114     if ($ENV{SERVER_SOFTWARE} && $ENV{SERVER_SOFTWARE} =~ /microsoft-iis/i) {
115         $SIG{__WARN__} = sub {
116             my ($msg) = @_;
117             my $datadir = bz_locations()->{'datadir'};
118             if (-w "$datadir/errorlog") {
119                 my $warning_log = new IO::File(">>$datadir/errorlog");
120                 print $warning_log $msg;
121                 $warning_log->close();
122             }
123         };
124     }
125
126     my $script = basename($0);
127
128     # Because of attachment_base, attachment.cgi handles this itself.
129     if ($script ne 'attachment.cgi') {
130         do_ssl_redirect_if_required();
131     }
132
133     # If Bugzilla is shut down, do not allow anything to run, just display a
134     # message to the user about the downtime and log out.  Scripts listed in 
135     # SHUTDOWNHTML_EXEMPT are exempt from this message.
136     #
137     # This code must go here. It cannot go anywhere in Bugzilla::CGI, because
138     # it uses Template, and that causes various dependency loops.
139     if (Bugzilla->params->{"shutdownhtml"}
140         && !grep { $_ eq $script } SHUTDOWNHTML_EXEMPT)
141     {
142         # Allow non-cgi scripts to exit silently (without displaying any
143         # message), if desired. At this point, no DBI call has been made
144         # yet, and no error will be returned if the DB is inaccessible.
145         if (!i_am_cgi()
146             && grep { $_ eq $script } SHUTDOWNHTML_EXIT_SILENTLY)
147         {
148             exit;
149         }
150
151         # For security reasons, log out users when Bugzilla is down.
152         # Bugzilla->login() is required to catch the logincookie, if any.
153         my $user;
154         eval { $user = Bugzilla->login(LOGIN_OPTIONAL); };
155         if ($@) {
156             # The DB is not accessible. Use the default user object.
157             $user = Bugzilla->user;
158             $user->{settings} = {};
159         }
160         my $userid = $user->id;
161         Bugzilla->logout();
162
163         my $template = Bugzilla->template;
164         my $vars = {};
165         $vars->{'message'} = 'shutdown';
166         $vars->{'userid'} = $userid;
167         # Generate and return a message about the downtime, appropriately
168         # for if we're a command-line script or a CGI script.
169         my $extension;
170         if (i_am_cgi() && (!Bugzilla->cgi->param('ctype')
171                            || Bugzilla->cgi->param('ctype') eq 'html')) {
172             $extension = 'html';
173         }
174         else {
175             $extension = 'txt';
176         }
177         if (i_am_cgi()) {
178             # Set the HTTP status to 503 when Bugzilla is down to avoid pages
179             # being indexed by search engines.
180             print Bugzilla->cgi->header(-status => 503, 
181                 -retry_after => SHUTDOWNHTML_RETRY_AFTER);
182         }
183         my $t_output;
184         $template->process("global/message.$extension.tmpl", $vars, \$t_output)
185             || ThrowTemplateError($template->error);
186         print $t_output . "\n";
187         exit;
188     }
189 }
190
191 #####################################################################
192 # Subroutines and Methods
193 #####################################################################
194
195 sub template {
196     my $class = shift;
197     $class->request_cache->{template} ||= Bugzilla::Template->create();
198     return $class->request_cache->{template};
199 }
200
201 sub template_inner {
202     my ($class, $lang) = @_;
203     my $cache = $class->request_cache;
204     my $current_lang = $cache->{template_current_lang}->[0];
205     $lang ||= $current_lang || '';
206     $class->request_cache->{"template_inner_$lang"}
207         ||= Bugzilla::Template->create(language => $lang);
208     return $class->request_cache->{"template_inner_$lang"};
209 }
210
211 our $extension_packages;
212 sub extensions {
213     my ($class) = @_;
214     my $cache = $class->request_cache;
215     if (!$cache->{extensions}) {
216         # Under mod_perl, mod_perl.pl populates $extension_packages for us.
217         if (!$extension_packages) {
218             $extension_packages = Bugzilla::Extension->load_all();
219         }
220         my @extensions;
221         foreach my $package (@$extension_packages) {
222             my $extension = $package->new();
223             if ($extension->enabled) {
224                 push(@extensions, $extension);
225             }        
226         }
227         $cache->{extensions} = \@extensions;
228     }
229     return $cache->{extensions};
230 }
231
232 sub feature {
233     my ($class, $feature) = @_;
234     my $cache = $class->request_cache;
235     return $cache->{feature}->{$feature}
236         if exists $cache->{feature}->{$feature};
237
238     my $feature_map = $cache->{feature_map};
239     if (!$feature_map) {
240         foreach my $package (@{ OPTIONAL_MODULES() }) {
241             foreach my $f (@{ $package->{feature} }) {
242                 $feature_map->{$f} ||= [];
243                 push(@{ $feature_map->{$f} }, $package->{module});
244             }
245         }
246         $cache->{feature_map} = $feature_map;
247     }
248
249     if (!$feature_map->{$feature}) {
250         ThrowCodeError('invalid_feature', { feature => $feature });
251     }
252
253     my $success = 1;
254     foreach my $module (@{ $feature_map->{$feature} }) {
255         # We can't use a string eval and "use" here (it kills Template-Toolkit,
256         # see https://rt.cpan.org/Public/Bug/Display.html?id=47929), so we have
257         # to do a block eval.
258         $module =~ s{::}{/}g;
259         $module .= ".pm";
260         eval { require $module; 1; } or $success = 0;
261     }
262     $cache->{feature}->{$feature} = $success;
263     return $success;
264 }
265
266 sub cgi {
267     my $class = shift;
268     $class->request_cache->{cgi} ||= new Bugzilla::CGI();
269     return $class->request_cache->{cgi};
270 }
271
272 sub input_params {
273     my ($class, $params) = @_;
274     my $cache = $class->request_cache;
275     # This is how the WebService and other places set input_params.
276     if (defined $params) {
277         $cache->{input_params} = $params;
278     }
279     return $cache->{input_params} if defined $cache->{input_params};
280
281     # Making this scalar makes it a tied hash to the internals of $cgi,
282     # so if a variable is changed, then it actually changes the $cgi object
283     # as well.
284     $cache->{input_params} = $class->cgi->Vars;
285     return $cache->{input_params};
286 }
287
288 sub localconfig {
289     my $class = shift;
290     $class->request_cache->{localconfig} ||= read_localconfig();
291     return $class->request_cache->{localconfig};
292 }
293
294 sub params {
295     my $class = shift;
296     $class->request_cache->{params} ||= Bugzilla::Config::read_param_file();
297     return $class->request_cache->{params};
298 }
299
300 sub user {
301     my $class = shift;
302     $class->request_cache->{user} ||= new Bugzilla::User;
303     return $class->request_cache->{user};
304 }
305
306 sub set_user {
307     my ($class, $user) = @_;
308     $class->request_cache->{user} = $user;
309 }
310
311 sub sudoer {
312     my $class = shift;    
313     return $class->request_cache->{sudoer};
314 }
315
316 sub sudo_request {
317     my ($class, $new_user, $new_sudoer) = @_;
318     $class->request_cache->{user}   = $new_user;
319     $class->request_cache->{sudoer} = $new_sudoer;
320     # NOTE: If you want to log the start of an sudo session, do it here.
321 }
322
323 sub page_requires_login {
324     return $_[0]->request_cache->{page_requires_login};
325 }
326
327 sub login {
328     my ($class, $type) = @_;
329
330     return $class->user if $class->user->id;
331
332     my $authorizer = new Bugzilla::Auth();
333     $type = LOGIN_REQUIRED if $class->cgi->param('GoAheadAndLogIn');
334
335     if (!defined $type || $type == LOGIN_NORMAL) {
336         $type = $class->params->{'requirelogin'} ? LOGIN_REQUIRED : LOGIN_NORMAL;
337     }
338
339     # Allow templates to know that we're in a page that always requires
340     # login.
341     if ($type == LOGIN_REQUIRED) {
342         $class->request_cache->{page_requires_login} = 1;
343     }
344
345     my $authenticated_user = $authorizer->login($type);
346     
347     # At this point, we now know if a real person is logged in.
348     # We must now check to see if an sudo session is in progress.
349     # For a session to be in progress, the following must be true:
350     # 1: There must be a logged in user
351     # 2: That user must be in the 'bz_sudoer' group
352     # 3: There must be a valid value in the 'sudo' cookie
353     # 4: A Bugzilla::User object must exist for the given cookie value
354     # 5: That user must NOT be in the 'bz_sudo_protect' group
355     my $token = $class->cgi->cookie('sudo');
356     if (defined $authenticated_user && $token) {
357         my ($user_id, $date, $sudo_target_id) = Bugzilla::Token::GetTokenData($token);
358         if (!$user_id
359             || $user_id != $authenticated_user->id
360             || !detaint_natural($sudo_target_id)
361             || (time() - str2time($date) > MAX_SUDO_TOKEN_AGE))
362         {
363             $class->cgi->remove_cookie('sudo');
364             ThrowUserError('sudo_invalid_cookie');
365         }
366
367         my $sudo_target = new Bugzilla::User($sudo_target_id);
368         if ($authenticated_user->in_group('bz_sudoers')
369             && defined $sudo_target
370             && !$sudo_target->in_group('bz_sudo_protect'))
371         {
372             $class->set_user($sudo_target);
373             $class->request_cache->{sudoer} = $authenticated_user;
374             # And make sure that both users have the same Auth object,
375             # since we never call Auth::login for the sudo target.
376             $sudo_target->set_authorizer($authenticated_user->authorizer);
377
378             # NOTE: If you want to do any special logging, do it here.
379         }
380         else {
381             delete_token($token);
382             $class->cgi->remove_cookie('sudo');
383             ThrowUserError('sudo_illegal_action', { sudoer => $authenticated_user,
384                                                     target_user => $sudo_target });
385         }
386     }
387     else {
388         $class->set_user($authenticated_user);
389     }
390
391     return $class->user;
392 }
393
394 sub logout {
395     my ($class, $option) = @_;
396
397     # If we're not logged in, go away
398     return unless $class->user->id;
399
400     $option = LOGOUT_CURRENT unless defined $option;
401     Bugzilla::Auth::Persist::Cookie->logout({type => $option});
402     $class->logout_request() unless $option eq LOGOUT_KEEP_CURRENT;
403 }
404
405 sub logout_user {
406     my ($class, $user) = @_;
407     # When we're logging out another user we leave cookies alone, and
408     # therefore avoid calling Bugzilla->logout() directly.
409     Bugzilla::Auth::Persist::Cookie->logout({user => $user});
410 }
411
412 # just a compatibility front-end to logout_user that gets a user by id
413 sub logout_user_by_id {
414     my ($class, $id) = @_;
415     my $user = new Bugzilla::User($id);
416     $class->logout_user($user);
417 }
418
419 # hack that invalidates credentials for a single request
420 sub logout_request {
421     my $class = shift;
422     delete $class->request_cache->{user};
423     delete $class->request_cache->{sudoer};
424     # We can't delete from $cgi->cookie, so logincookie data will remain
425     # there. Don't rely on it: use Bugzilla->user->login instead!
426 }
427
428 sub job_queue {
429     my $class = shift;
430     require Bugzilla::JobQueue;
431     $class->request_cache->{job_queue} ||= Bugzilla::JobQueue->new();
432     return $class->request_cache->{job_queue};
433 }
434
435 sub dbh {
436     my $class = shift;
437     # If we're not connected, then we must want the main db
438     $class->request_cache->{dbh} ||= $class->dbh_main;
439
440     return $class->request_cache->{dbh};
441 }
442
443 sub dbh_main {
444     my $class = shift;
445     $class->request_cache->{dbh_main} ||= Bugzilla::DB::connect_main();
446     return $class->request_cache->{dbh_main};
447 }
448
449 sub languages {
450     my $class = shift;
451     return Bugzilla::Install::Util::supported_languages();
452 }
453
454 sub error_mode {
455     my ($class, $newval) = @_;
456     if (defined $newval) {
457         $class->request_cache->{error_mode} = $newval;
458     }
459     return $class->request_cache->{error_mode}
460         || (i_am_cgi() ? ERROR_MODE_WEBPAGE : ERROR_MODE_DIE);
461 }
462
463 # This is used only by Bugzilla::Error to throw errors.
464 sub _json_server {
465     my ($class, $newval) = @_;
466     if (defined $newval) {
467         $class->request_cache->{_json_server} = $newval;
468     }
469     return $class->request_cache->{_json_server};
470 }
471
472 sub usage_mode {
473     my ($class, $newval) = @_;
474     if (defined $newval) {
475         if ($newval == USAGE_MODE_BROWSER) {
476             $class->error_mode(ERROR_MODE_WEBPAGE);
477         }
478         elsif ($newval == USAGE_MODE_CMDLINE) {
479             $class->error_mode(ERROR_MODE_DIE);
480         }
481         elsif ($newval == USAGE_MODE_XMLRPC) {
482             $class->error_mode(ERROR_MODE_DIE_SOAP_FAULT);
483         }
484         elsif ($newval == USAGE_MODE_JSON) {
485             $class->error_mode(ERROR_MODE_JSON_RPC);
486         }
487         elsif ($newval == USAGE_MODE_EMAIL) {
488             $class->error_mode(ERROR_MODE_DIE);
489         }
490         elsif ($newval == USAGE_MODE_TEST) {
491             $class->error_mode(ERROR_MODE_TEST);
492         }
493         else {
494             ThrowCodeError('usage_mode_invalid',
495                            {'invalid_usage_mode', $newval});
496         }
497         $class->request_cache->{usage_mode} = $newval;
498     }
499     return $class->request_cache->{usage_mode}
500         || (i_am_cgi()? USAGE_MODE_BROWSER : USAGE_MODE_CMDLINE);
501 }
502
503 sub installation_mode {
504     my ($class, $newval) = @_;
505     ($class->request_cache->{installation_mode} = $newval) if defined $newval;
506     return $class->request_cache->{installation_mode}
507         || INSTALLATION_MODE_INTERACTIVE;
508 }
509
510 sub installation_answers {
511     my ($class, $filename) = @_;
512     if ($filename) {
513         my $s = new Safe;
514         $s->rdo($filename);
515
516         die "Error reading $filename: $!" if $!;
517         die "Error evaluating $filename: $@" if $@;
518
519         # Now read the param back out from the sandbox
520         $class->request_cache->{installation_answers} = $s->varglob('answer');
521     }
522     return $class->request_cache->{installation_answers} || {};
523 }
524
525 sub switch_to_shadow_db {
526     my $class = shift;
527
528     if (!$class->request_cache->{dbh_shadow}) {
529         if ($class->params->{'shadowdb'}) {
530             $class->request_cache->{dbh_shadow} = Bugzilla::DB::connect_shadow();
531         } else {
532             $class->request_cache->{dbh_shadow} = $class->dbh_main;
533         }
534     }
535
536     $class->request_cache->{dbh} = $class->request_cache->{dbh_shadow};
537     # we have to return $class->dbh instead of {dbh} as
538     # {dbh_shadow} may be undefined if no shadow DB is used
539     # and no connection to the main DB has been established yet.
540     return $class->dbh;
541 }
542
543 sub switch_to_main_db {
544     my $class = shift;
545
546     $class->request_cache->{dbh} = $class->dbh_main;
547     return $class->dbh_main;
548 }
549
550 sub fields {
551     my ($class, $criteria) = @_;
552     $criteria ||= {};
553     my $cache = $class->request_cache;
554
555     # We create an advanced cache for fields by type, so that we
556     # can avoid going back to the database for every fields() call.
557     # (And most of our fields() calls are for getting fields by type.)
558     #
559     # We also cache fields by name, because calling $field->name a few
560     # million times can be slow in calling code, but if we just do it
561     # once here, that makes things a lot faster for callers.
562     if (!defined $cache->{fields}) {
563         my @all_fields = Bugzilla::Field->get_all;
564         my (%by_name, %by_type);
565         foreach my $field (@all_fields) {
566             my $name = $field->name;
567             $by_type{$field->type}->{$name} = $field;
568             $by_name{$name} = $field;
569         }
570         $cache->{fields} = { by_type => \%by_type, by_name => \%by_name };
571     }
572
573     my $fields = $cache->{fields};
574     my %requested;
575     if (my $types = delete $criteria->{type}) {
576         $types = ref($types) ? $types : [$types];
577         %requested = map { %{ $fields->{by_type}->{$_} || {} } } @$types;
578     }
579     else {
580         %requested = %{ $fields->{by_name} };
581     }
582
583     my $do_by_name = delete $criteria->{by_name};
584
585     # Filtering before returning the fields based on
586     # the criterias.
587     foreach my $filter (keys %$criteria) {
588         foreach my $field (keys %requested) {
589             if ($requested{$field}->$filter != $criteria->{$filter}) {
590                 delete $requested{$field};
591             }
592         }
593     }
594
595     return $do_by_name ? \%requested : [values %requested];
596 }
597
598 sub active_custom_fields {
599     my $class = shift;
600     if (!exists $class->request_cache->{active_custom_fields}) {
601         $class->request_cache->{active_custom_fields} =
602           Bugzilla::Field->match({ custom => 1, obsolete => 0 });
603     }
604     return @{$class->request_cache->{active_custom_fields}};
605 }
606
607 sub has_flags {
608     my $class = shift;
609
610     if (!defined $class->request_cache->{has_flags}) {
611         $class->request_cache->{has_flags} = Bugzilla::Flag->any_exist;
612     }
613     return $class->request_cache->{has_flags};
614 }
615
616 sub local_timezone {
617     my $class = shift;
618
619     if (!defined $class->request_cache->{local_timezone}) {
620         $class->request_cache->{local_timezone} =
621           DateTime::TimeZone->new(name => 'local');
622     }
623     return $class->request_cache->{local_timezone};
624 }
625
626 # This creates the request cache for non-mod_perl installations.
627 # This is identical to Install::Util::_cache so that things loaded
628 # into Install::Util::_cache during installation can be read out
629 # of request_cache later in installation.
630 our $_request_cache = $Bugzilla::Install::Util::_cache;
631
632 sub request_cache {
633     if ($ENV{MOD_PERL}) {
634         require Apache2::RequestUtil;
635         # Sometimes (for example, during mod_perl.pl), the request
636         # object isn't available, and we should use $_request_cache instead.
637         my $request = eval { Apache2::RequestUtil->request };
638         return $_request_cache if !$request;
639         return $request->pnotes();
640     }
641     return $_request_cache;
642 }
643
644 # Private methods
645
646 # Per-process cleanup. Note that this is a plain subroutine, not a method,
647 # so we don't have $class available.
648 sub _cleanup {
649     my $main   = Bugzilla->request_cache->{dbh_main};
650     my $shadow = Bugzilla->request_cache->{dbh_shadow};
651     foreach my $dbh ($main, $shadow) {
652         next if !$dbh;
653         $dbh->bz_rollback_transaction() if $dbh->bz_in_transaction;
654         $dbh->disconnect;
655     }
656     undef $_request_cache;
657
658     # These are both set by CGI.pm but need to be undone so that
659     # Apache can actually shut down its children if it needs to.
660     foreach my $signal (qw(TERM PIPE)) {
661         $SIG{$signal} = 'DEFAULT' if $SIG{$signal} && $SIG{$signal} eq 'IGNORE';
662     }
663 }
664
665 sub END {
666     # Bugzilla.pm cannot compile in mod_perl.pl if this runs.
667     _cleanup() unless $ENV{MOD_PERL};
668 }
669
670 init_page() if !$ENV{MOD_PERL};
671
672 1;
673
674 __END__
675
676 =head1 NAME
677
678 Bugzilla - Semi-persistent collection of various objects used by scripts
679 and modules
680
681 =head1 SYNOPSIS
682
683   use Bugzilla;
684
685   sub someModulesSub {
686     Bugzilla->dbh->prepare(...);
687     Bugzilla->template->process(...);
688   }
689
690 =head1 DESCRIPTION
691
692 Several Bugzilla 'things' are used by a variety of modules and scripts. This
693 includes database handles, template objects, and so on.
694
695 This module is a singleton intended as a central place to store these objects.
696 This approach has several advantages:
697
698 =over 4
699
700 =item *
701
702 They're not global variables, so we don't have issues with them staying around
703 with mod_perl
704
705 =item *
706
707 Everything is in one central place, so it's easy to access, modify, and maintain
708
709 =item *
710
711 Code in modules can get access to these objects without having to have them
712 all passed from the caller, and the caller's caller, and....
713
714 =item *
715
716 We can reuse objects across requests using mod_perl where appropriate (eg
717 templates), whilst destroying those which are only valid for a single request
718 (such as the current user)
719
720 =back
721
722 Note that items accessible via this object are demand-loaded when requested.
723
724 For something to be added to this object, it should either be able to benefit
725 from persistence when run under mod_perl (such as the a C<template> object),
726 or should be something which is globally required by a large ammount of code
727 (such as the current C<user> object).
728
729 =head1 METHODS
730
731 Note that all C<Bugzilla> functionality is method based; use C<Bugzilla-E<gt>dbh>
732 rather than C<Bugzilla::dbh>. Nothing cares about this now, but don't rely on
733 that.
734
735 =over 4
736
737 =item C<template>
738
739 The current C<Template> object, to be used for output
740
741 =item C<template_inner>
742
743 If you ever need a L<Bugzilla::Template> object while you're already
744 processing a template, use this. Also use it if you want to specify
745 the language to use. If no argument is passed, it uses the last
746 language set. If the argument is "" (empty string), the language is
747 reset to the current one (the one used by Bugzilla->template).
748
749 =item C<cgi>
750
751 The current C<cgi> object. Note that modules should B<not> be using this in
752 general. Not all Bugzilla actions are cgi requests. Its useful as a convenience
753 method for those scripts/templates which are only use via CGI, though.
754
755 =item C<input_params>
756
757 When running under the WebService, this is a hashref containing the arguments
758 passed to the WebService method that was called. When running in a normal
759 script, this is a hashref containing the contents of the CGI parameters.
760
761 Modifying this hashref will modify the CGI parameters or the WebService
762 arguments (depending on what C<input_params> currently represents).
763
764 This should be used instead of L</cgi> in situations where your code
765 could be being called by either a normal CGI script or a WebService method,
766 such as during a code hook.
767
768 B<Note:> When C<input_params> represents the CGI parameters, any
769 parameter specified more than once (like C<foo=bar&foo=baz>) will appear
770 as an arrayref in the hash, but any value specified only once will appear
771 as a scalar. This means that even if a value I<can> appear multiple times,
772 if it only I<does> appear once, then it will be a scalar in C<input_params>,
773 not an arrayref.
774
775 =item C<user>
776
777 C<undef> if there is no currently logged in user or if the login code has not
778 yet been run.  If an sudo session is in progress, the C<Bugzilla::User>
779 corresponding to the person who is being impersonated.  If no session is in
780 progress, the current C<Bugzilla::User>.
781
782 =item C<set_user>
783
784 Allows you to directly set what L</user> will return. You can use this
785 if you want to bypass L</login> for some reason and directly "log in"
786 a specific L<Bugzilla::User>. Be careful with it, though!
787
788 =item C<sudoer>
789
790 C<undef> if there is no currently logged in user, the currently logged in user
791 is not in the I<sudoer> group, or there is no session in progress.  If an sudo
792 session is in progress, returns the C<Bugzilla::User> object corresponding to
793 the person who logged in and initiated the session.  If no session is in
794 progress, returns the C<Bugzilla::User> object corresponding to the currently
795 logged in user.
796
797 =item C<sudo_request>
798 This begins an sudo session for the current request.  It is meant to be 
799 used when a session has just started.  For normal use, sudo access should 
800 normally be set at login time.
801
802 =item C<login>
803
804 Logs in a user, returning a C<Bugzilla::User> object, or C<undef> if there is
805 no logged in user. See L<Bugzilla::Auth|Bugzilla::Auth>, and
806 L<Bugzilla::User|Bugzilla::User>.
807
808 =item C<page_requires_login>
809
810 If the current page always requires the user to log in (for example,
811 C<enter_bug.cgi> or any page called with C<?GoAheadAndLogIn=1>) then
812 this will return something true. Otherwise it will return false. (This is
813 set when you call L</login>.)
814
815 =item C<logout($option)>
816
817 Logs out the current user, which involves invalidating user sessions and
818 cookies. Three options are available from
819 L<Bugzilla::Constants|Bugzilla::Constants>: LOGOUT_CURRENT (the
820 default), LOGOUT_ALL or LOGOUT_KEEP_CURRENT.
821
822 =item C<logout_user($user)>
823
824 Logs out the specified user (invalidating all his sessions), taking a
825 Bugzilla::User instance.
826
827 =item C<logout_by_id($id)>
828
829 Logs out the user with the id specified. This is a compatibility
830 function to be used in callsites where there is only a userid and no
831 Bugzilla::User instance.
832
833 =item C<logout_request>
834
835 Essentially, causes calls to C<Bugzilla-E<gt>user> to return C<undef>. This has the
836 effect of logging out a user for the current request only; cookies and
837 database sessions are left intact.
838
839 =item C<fields>
840
841 This is the standard way to get arrays or hashes of L<Bugzilla::Field>
842 objects when you need them. It takes the following named arguments
843 in a hashref:
844
845 =over
846
847 =item C<by_name>
848
849 If false (or not specified), this method will return an arrayref of
850 the requested fields. The order of the returned fields is random.
851
852 If true, this method will return a hashref of fields, where the keys
853 are field names and the valules are L<Bugzilla::Field> objects.
854
855 =item C<type>
856
857 Either a single C<FIELD_TYPE_*> constant or an arrayref of them. If specified,
858 the returned fields will be limited to the types in the list. If you don't
859 specify this argument, all fields will be returned.
860
861 =back
862
863 =item C<error_mode>
864
865 Call either C<Bugzilla->error_mode(Bugzilla::Constants::ERROR_MODE_DIE)>
866 or C<Bugzilla->error_mode(Bugzilla::Constants::ERROR_MODE_DIE_SOAP_FAULT)> to
867 change this flag's default of C<Bugzilla::Constants::ERROR_MODE_WEBPAGE> and to
868 indicate that errors should be passed to error mode specific error handlers
869 rather than being sent to a browser and finished with an exit().
870
871 This is useful, for example, to keep C<eval> blocks from producing wild HTML
872 on errors, making it easier for you to catch them.
873 (Remember to reset the error mode to its previous value afterwards, though.)
874
875 C<Bugzilla->error_mode> will return the current state of this flag.
876
877 Note that C<Bugzilla->error_mode> is being called by C<Bugzilla->usage_mode> on
878 usage mode changes.
879
880 =item C<usage_mode>
881
882 Call either C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_CMDLINE)>
883 or C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_XMLRPC)> near the
884 beginning of your script to change this flag's default of
885 C<Bugzilla::Constants::USAGE_MODE_BROWSER> and to indicate that Bugzilla is
886 being called in a non-interactive manner.
887
888 This influences error handling because on usage mode changes, C<usage_mode>
889 calls C<Bugzilla->error_mode> to set an error mode which makes sense for the
890 usage mode.
891
892 C<Bugzilla->usage_mode> will return the current state of this flag.
893
894 =item C<installation_mode>
895
896 Determines whether or not installation should be silent. See 
897 L<Bugzilla::Constants> for the C<INSTALLATION_MODE> constants.
898
899 =item C<installation_answers>
900
901 Returns a hashref representing any "answers" file passed to F<checksetup.pl>,
902 used to automatically answer or skip prompts.
903
904 =item C<dbh>
905
906 The current database handle. See L<DBI>.
907
908 =item C<dbh_main>
909
910 The main database handle. See L<DBI>.
911
912 =item C<languages>
913
914 Currently installed languages.
915 Returns a reference to a list of RFC 1766 language tags of installed languages.
916
917 =item C<switch_to_shadow_db>
918
919 Switch from using the main database to using the shadow database.
920
921 =item C<switch_to_main_db>
922
923 Change the database object to refer to the main database.
924
925 =item C<params>
926
927 The current Parameters of Bugzilla, as a hashref. If C<data/params>
928 does not exist, then we return an empty hashref. If C<data/params>
929 is unreadable or is not valid perl, we C<die>.
930
931 =item C<local_timezone>
932
933 Returns the local timezone of the Bugzilla installation,
934 as a DateTime::TimeZone object. This detection is very time
935 consuming, so we cache this information for future references.
936
937 =item C<job_queue>
938
939 Returns a L<Bugzilla::JobQueue> that you can use for queueing jobs.
940 Will throw an error if job queueing is not correctly configured on
941 this Bugzilla installation.
942
943 =item C<feature>
944
945 Tells you whether or not a specific feature is enabled. For names
946 of features, see C<OPTIONAL_MODULES> in C<Bugzilla::Install::Requirements>.
947
948 =back