1
# -*- Mode: perl; indent-tabs-mode: nil -*-
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/
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.
13
# The Original Code is the Bugzilla Bug Tracking System.
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
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>
30
use Bugzilla::Constants;
32
use Bugzilla::Auth::Persist::Cookie;
35
use Bugzilla::Install::Localconfig qw(read_localconfig);
36
use Bugzilla::Template;
45
# This creates the request cache for non-mod_perl installations.
46
our $_request_cache = {};
48
#####################################################################
50
#####################################################################
52
# Scripts that are not stopped by shutdownhtml being in effect.
53
use constant SHUTDOWNHTML_EXEMPT => [
59
# Non-cgi scripts that should silently exit.
60
use constant SHUTDOWNHTML_EXIT_SILENTLY => [
64
#####################################################################
66
#####################################################################
68
# The following subroutine is for debugging purposes only.
69
# Uncommenting this sub and the $::SIG{__DIE__} trap underneath it will
70
# cause any fatal errors to result in a call stack trace to help track
73
#sub die_with_dignity {
77
# Carp::confess($err_msg);
79
#$::SIG{__DIE__} = \&Bugzilla::die_with_dignity;
83
# Some environment variables are not taint safe
84
delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
85
# Some modules throw undefined errors (notably File::Spec::Win32) if
89
# IIS prints out warnings to the webpage, so ignore them, or log them
90
# to a file if the file exists.
91
if ($ENV{SERVER_SOFTWARE} && $ENV{SERVER_SOFTWARE} =~ /microsoft-iis/i) {
92
$SIG{__WARN__} = sub {
94
my $datadir = bz_locations()->{'datadir'};
95
if (-w "$datadir/errorlog") {
96
my $warning_log = new IO::File(">>$datadir/errorlog");
97
print $warning_log $msg;
98
$warning_log->close();
103
# If Bugzilla is shut down, do not allow anything to run, just display a
104
# message to the user about the downtime and log out. Scripts listed in
105
# SHUTDOWNHTML_EXEMPT are exempt from this message.
107
# Because this is code which is run live from perl "use" commands of other
108
# scripts, we're skipping this part if we get here during a perl syntax
109
# check -- runtests.pl compiles scripts without running them, so we
110
# need to make sure that this check doesn't apply to 'perl -c' calls.
112
# This code must go here. It cannot go anywhere in Bugzilla::CGI, because
113
# it uses Template, and that causes various dependency loops.
114
if (!$^C && Bugzilla->params->{"shutdownhtml"}
115
&& lsearch(SHUTDOWNHTML_EXEMPT, basename($0)) == -1)
117
# Allow non-cgi scripts to exit silently (without displaying any
118
# message), if desired. At this point, no DBI call has been made
119
# yet, and no error will be returned if the DB is inaccessible.
120
if (lsearch(SHUTDOWNHTML_EXIT_SILENTLY, basename($0)) > -1
126
# For security reasons, log out users when Bugzilla is down.
127
# Bugzilla->login() is required to catch the logincookie, if any.
128
my $user = Bugzilla->login(LOGIN_OPTIONAL);
129
my $userid = $user->id;
132
my $template = Bugzilla->template;
134
$vars->{'message'} = 'shutdown';
135
$vars->{'userid'} = $userid;
136
# Generate and return a message about the downtime, appropriately
137
# for if we're a command-line script or a CGI script.
139
if (i_am_cgi() && (!Bugzilla->cgi->param('ctype')
140
|| Bugzilla->cgi->param('ctype') eq 'html')) {
146
print Bugzilla->cgi->header() if i_am_cgi();
148
$template->process("global/message.$extension.tmpl", $vars, \$t_output)
149
|| ThrowTemplateError($template->error);
150
print $t_output . "\n";
155
init_page() if !$ENV{MOD_PERL};
157
#####################################################################
158
# Subroutines and Methods
159
#####################################################################
163
request_cache()->{language} = "";
164
request_cache()->{template} ||= Bugzilla::Template->create();
165
return request_cache()->{template};
169
my ($class, $lang) = @_;
170
$lang = defined($lang) ? $lang : (request_cache()->{language} || "");
171
request_cache()->{language} = $lang;
172
request_cache()->{"template_inner_$lang"} ||= Bugzilla::Template->create();
173
return request_cache()->{"template_inner_$lang"};
178
request_cache()->{cgi} ||= new Bugzilla::CGI();
179
return request_cache()->{cgi};
183
request_cache()->{localconfig} ||= read_localconfig();
184
return request_cache()->{localconfig};
189
request_cache()->{params} ||= Bugzilla::Config::read_param_file();
190
return request_cache()->{params};
195
request_cache()->{user} ||= new Bugzilla::User;
196
return request_cache()->{user};
200
my ($class, $user) = @_;
201
$class->request_cache->{user} = $user;
206
return request_cache()->{sudoer};
211
my $new_user = shift;
212
my $new_sudoer = shift;
214
request_cache()->{user} = $new_user;
215
request_cache()->{sudoer} = $new_sudoer;
217
# NOTE: If you want to log the start of an sudo session, do it here.
223
my ($class, $type) = @_;
225
return Bugzilla->user if Bugzilla->user->id;
227
my $authorizer = new Bugzilla::Auth();
228
$type = LOGIN_REQUIRED if Bugzilla->cgi->param('GoAheadAndLogIn');
229
if (!defined $type || $type == LOGIN_NORMAL) {
230
$type = Bugzilla->params->{'requirelogin'} ? LOGIN_REQUIRED : LOGIN_NORMAL;
232
my $authenticated_user = $authorizer->login($type);
234
# At this point, we now know if a real person is logged in.
235
# We must now check to see if an sudo session is in progress.
236
# For a session to be in progress, the following must be true:
237
# 1: There must be a logged in user
238
# 2: That user must be in the 'bz_sudoer' group
239
# 3: There must be a valid value in the 'sudo' cookie
240
# 4: A Bugzilla::User object must exist for the given cookie value
241
# 5: That user must NOT be in the 'bz_sudo_protect' group
242
my $sudo_cookie = $class->cgi->cookie('sudo');
243
detaint_natural($sudo_cookie) if defined($sudo_cookie);
245
$sudo_target = new Bugzilla::User($sudo_cookie) if defined($sudo_cookie);
246
if (defined($authenticated_user) &&
247
$authenticated_user->in_group('bz_sudoers') &&
248
defined($sudo_cookie) &&
249
defined($sudo_target) &&
250
!($sudo_target->in_group('bz_sudo_protect'))
253
Bugzilla->set_user($sudo_target);
254
request_cache()->{sudoer} = $authenticated_user;
255
# And make sure that both users have the same Auth object,
256
# since we never call Auth::login for the sudo target.
257
$sudo_target->set_authorizer($authenticated_user->authorizer);
259
# NOTE: If you want to do any special logging, do it here.
262
Bugzilla->set_user($authenticated_user);
265
return Bugzilla->user;
269
my ($class, $option) = @_;
271
# If we're not logged in, go away
272
return unless user->id;
274
$option = LOGOUT_CURRENT unless defined $option;
275
Bugzilla::Auth::Persist::Cookie->logout({type => $option});
276
Bugzilla->logout_request() unless $option eq LOGOUT_KEEP_CURRENT;
280
my ($class, $user) = @_;
281
# When we're logging out another user we leave cookies alone, and
282
# therefore avoid calling Bugzilla->logout() directly.
283
Bugzilla::Auth::Persist::Cookie->logout({user => $user});
286
# just a compatibility front-end to logout_user that gets a user by id
287
sub logout_user_by_id {
288
my ($class, $id) = @_;
289
my $user = new Bugzilla::User($id);
290
$class->logout_user($user);
293
# hack that invalidates credentials for a single request
295
delete request_cache()->{user};
296
delete request_cache()->{sudoer};
297
# We can't delete from $cgi->cookie, so logincookie data will remain
298
# there. Don't rely on it: use Bugzilla->user->login instead!
304
# If we're not connected, then we must want the main db
305
request_cache()->{dbh} ||= request_cache()->{dbh_main}
306
= Bugzilla::DB::connect_main();
308
return request_cache()->{dbh};
314
if (defined $newval) {
315
request_cache()->{error_mode} = $newval;
317
return request_cache()->{error_mode}
318
|| Bugzilla::Constants::ERROR_MODE_WEBPAGE;
324
if (defined $newval) {
325
if ($newval == USAGE_MODE_BROWSER) {
326
$class->error_mode(ERROR_MODE_WEBPAGE);
328
elsif ($newval == USAGE_MODE_CMDLINE) {
329
$class->error_mode(ERROR_MODE_DIE);
331
elsif ($newval == USAGE_MODE_WEBSERVICE) {
332
$class->error_mode(ERROR_MODE_DIE_SOAP_FAULT);
334
elsif ($newval == USAGE_MODE_EMAIL) {
335
$class->error_mode(ERROR_MODE_DIE);
338
ThrowCodeError('usage_mode_invalid',
339
{'invalid_usage_mode', $newval});
341
request_cache()->{usage_mode} = $newval;
343
return request_cache()->{usage_mode}
344
|| Bugzilla::Constants::USAGE_MODE_BROWSER;
347
sub installation_mode {
348
my ($class, $newval) = @_;
349
($class->request_cache->{installation_mode} = $newval) if defined $newval;
350
return $class->request_cache->{installation_mode}
351
|| INSTALLATION_MODE_INTERACTIVE;
354
sub installation_answers {
355
my ($class, $filename) = @_;
360
die "Error reading $filename: $!" if $!;
361
die "Error evaluating $filename: $@" if $@;
363
# Now read the param back out from the sandbox
364
$class->request_cache->{installation_answers} = $s->varglob('answer');
366
return $class->request_cache->{installation_answers} || {};
369
sub switch_to_shadow_db {
372
if (!request_cache()->{dbh_shadow}) {
373
if (Bugzilla->params->{'shadowdb'}) {
374
request_cache()->{dbh_shadow} = Bugzilla::DB::connect_shadow();
376
request_cache()->{dbh_shadow} = request_cache()->{dbh_main};
380
request_cache()->{dbh} = request_cache()->{dbh_shadow};
381
# we have to return $class->dbh instead of {dbh} as
382
# {dbh_shadow} may be undefined if no shadow DB is used
383
# and no connection to the main DB has been established yet.
387
sub switch_to_main_db {
390
request_cache()->{dbh} = request_cache()->{dbh_main};
391
# We have to return $class->dbh instead of {dbh} as
392
# {dbh_main} may be undefined if no connection to the main DB
393
# has been established yet.
399
my $criteria = shift;
400
# This function may be called during installation, and Field::match
401
# may fail at that time. so we want to return an empty list in that
403
my $fields = eval { Bugzilla::Field->match($criteria) } || [];
407
sub custom_field_names {
408
# Get a list of custom fields and convert it into a list of their names.
409
return map($_->{name},
410
@{Bugzilla::Field->match({ custom=>1, obsolete=>0 })});
414
my ($class, $args) = @_;
415
$class->request_cache->{hook_args} = $args if $args;
416
return $class->request_cache->{hook_args};
420
if ($ENV{MOD_PERL}) {
421
require Apache2::RequestUtil;
422
return Apache2::RequestUtil->request->pnotes();
424
return $_request_cache;
429
# Per process cleanup
432
# When we support transactions, need to ->rollback here
433
my $main = request_cache()->{dbh_main};
434
my $shadow = request_cache()->{dbh_shadow};
435
$main->disconnect if $main;
436
$shadow->disconnect if $shadow && Bugzilla->params->{"shadowdb"};
437
undef $_request_cache;
441
# Bugzilla.pm cannot compile in mod_perl.pl if this runs.
442
_cleanup() unless $ENV{MOD_PERL};
451
Bugzilla - Semi-persistent collection of various objects used by scripts
459
Bugzilla->dbh->prepare(...);
460
Bugzilla->template->process(...);
465
Several Bugzilla 'things' are used by a variety of modules and scripts. This
466
includes database handles, template objects, and so on.
468
This module is a singleton intended as a central place to store these objects.
469
This approach has several advantages:
475
They're not global variables, so we don't have issues with them staying around
480
Everything is in one central place, so it's easy to access, modify, and maintain
484
Code in modules can get access to these objects without having to have them
485
all passed from the caller, and the caller's caller, and....
489
We can reuse objects across requests using mod_perl where appropriate (eg
490
templates), whilst destroying those which are only valid for a single request
491
(such as the current user)
495
Note that items accessible via this object are demand-loaded when requested.
497
For something to be added to this object, it should either be able to benefit
498
from persistence when run under mod_perl (such as the a C<template> object),
499
or should be something which is globally required by a large ammount of code
500
(such as the current C<user> object).
504
Note that all C<Bugzilla> functionality is method based; use C<Bugzilla-E<gt>dbh>
505
rather than C<Bugzilla::dbh>. Nothing cares about this now, but don't rely on
512
The current C<Template> object, to be used for output
514
=item C<template_inner>
516
If you ever need a L<Bugzilla::Template> object while you're already
517
processing a template, use this. Also use it if you want to specify
518
the language to use. If no argument is passed, it uses the last
519
language set. If the argument is "" (empty string), the language is
520
reset to the current one (the one used by Bugzilla->template).
524
The current C<cgi> object. Note that modules should B<not> be using this in
525
general. Not all Bugzilla actions are cgi requests. Its useful as a convenience
526
method for those scripts/templates which are only use via CGI, though.
530
C<undef> if there is no currently logged in user or if the login code has not
531
yet been run. If an sudo session is in progress, the C<Bugzilla::User>
532
corresponding to the person who is being impersonated. If no session is in
533
progress, the current C<Bugzilla::User>.
537
Allows you to directly set what L</user> will return. You can use this
538
if you want to bypass L</login> for some reason and directly "log in"
539
a specific L<Bugzilla::User>. Be careful with it, though!
543
C<undef> if there is no currently logged in user, the currently logged in user
544
is not in the I<sudoer> group, or there is no session in progress. If an sudo
545
session is in progress, returns the C<Bugzilla::User> object corresponding to
546
the person who logged in and initiated the session. If no session is in
547
progress, returns the C<Bugzilla::User> object corresponding to the currently
550
=item C<sudo_request>
551
This begins an sudo session for the current request. It is meant to be
552
used when a session has just started. For normal use, sudo access should
553
normally be set at login time.
557
Logs in a user, returning a C<Bugzilla::User> object, or C<undef> if there is
558
no logged in user. See L<Bugzilla::Auth|Bugzilla::Auth>, and
559
L<Bugzilla::User|Bugzilla::User>.
561
=item C<logout($option)>
563
Logs out the current user, which involves invalidating user sessions and
564
cookies. Three options are available from
565
L<Bugzilla::Constants|Bugzilla::Constants>: LOGOUT_CURRENT (the
566
default), LOGOUT_ALL or LOGOUT_KEEP_CURRENT.
568
=item C<logout_user($user)>
570
Logs out the specified user (invalidating all his sessions), taking a
571
Bugzilla::User instance.
573
=item C<logout_by_id($id)>
575
Logs out the user with the id specified. This is a compatibility
576
function to be used in callsites where there is only a userid and no
577
Bugzilla::User instance.
579
=item C<logout_request>
581
Essentially, causes calls to C<Bugzilla-E<gt>user> to return C<undef>. This has the
582
effect of logging out a user for the current request only; cookies and
583
database sessions are left intact.
587
Call either C<Bugzilla->error_mode(Bugzilla::Constants::ERROR_MODE_DIE)>
588
or C<Bugzilla->error_mode(Bugzilla::Constants::ERROR_MODE_DIE_SOAP_FAULT)> to
589
change this flag's default of C<Bugzilla::Constants::ERROR_MODE_WEBPAGE> and to
590
indicate that errors should be passed to error mode specific error handlers
591
rather than being sent to a browser and finished with an exit().
593
This is useful, for example, to keep C<eval> blocks from producing wild HTML
594
on errors, making it easier for you to catch them.
595
(Remember to reset the error mode to its previous value afterwards, though.)
597
C<Bugzilla->error_mode> will return the current state of this flag.
599
Note that C<Bugzilla->error_mode> is being called by C<Bugzilla->usage_mode> on
604
Call either C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_CMDLINE)>
605
or C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_WEBSERVICE)> near the
606
beginning of your script to change this flag's default of
607
C<Bugzilla::Constants::USAGE_MODE_BROWSER> and to indicate that Bugzilla is
608
being called in a non-interactive manner.
609
This influences error handling because on usage mode changes, C<usage_mode>
610
calls C<Bugzilla->error_mode> to set an error mode which makes sense for the
613
C<Bugzilla->usage_mode> will return the current state of this flag.
615
=item C<installation_mode>
617
Determines whether or not installation should be silent. See
618
L<Bugzilla::Constants> for the C<INSTALLATION_MODE> constants.
620
=item C<installation_answers>
622
Returns a hashref representing any "answers" file passed to F<checksetup.pl>,
623
used to automatically answer or skip prompts.
627
The current database handle. See L<DBI>.
629
=item C<switch_to_shadow_db>
631
Switch from using the main database to using the shadow database.
633
=item C<switch_to_main_db>
635
Change the database object to refer to the main database.
639
The current Parameters of Bugzilla, as a hashref. If C<data/params>
640
does not exist, then we return an empty hashref. If C<data/params>
641
is unreadable or is not valid perl, we C<die>.
645
If you are running inside a code hook (see L<Bugzilla::Hook>) this
646
is how you get the arguments passed to the hook.