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