33
33
use base qw(Exporter);
34
@Bugzilla::Util::EXPORT = qw(trick_taint detaint_natural
34
@Bugzilla::Util::EXPORT = qw(is_tainted trick_taint detaint_natural
36
36
html_quote url_quote xml_quote
37
37
css_class_quote html_light_quote url_decode
38
38
i_am_cgi get_netaddr correct_urlbase
39
lsearch ssl_require_redirect use_attachbase
40
diff_arrays diff_strings
41
41
trim wrap_hard wrap_comment find_wrap_point
42
42
format_time format_time_decimal validate_date
44
44
file_mod_time is_7bit_clean
45
45
bz_crypt generate_random_password
46
46
validate_email_syntax clean_text
47
get_text disable_utf8);
49
49
use Bugzilla::Constants;
54
use DateTime::TimeZone;
57
use Scalar::Util qw(tainted);
55
# This is from the perlsec page, slightly modified to remove a warning
57
# This function makes use of the fact that the presence of
58
# tainted data anywhere within an expression renders the
59
# entire expression tainted.
60
# Don't ask me how it works...
62
return not eval { my $foo = join('',@_), kill 0; 1; };
62
67
Carp::confess("Undef to trick_taint") unless defined $_[0];
238
218
return exists $ENV{'SERVER_SOFTWARE'} ? 1 : 0;
241
sub ssl_require_redirect {
244
# If currently not in a protected SSL
245
# connection, determine if a redirection is
246
# needed based on value in Bugzilla->params->{ssl}.
247
# If we are already in a protected connection or
248
# sslbase is not set then no action is required.
249
if (uc($ENV{'HTTPS'}) ne 'ON'
250
&& $ENV{'SERVER_PORT'} != 443
251
&& Bugzilla->params->{'sslbase'} ne '')
253
# System is configured to never require SSL
254
# so no redirection is needed.
256
if Bugzilla->params->{'ssl'} eq 'never';
258
# System is configured to always require a SSL
259
# connection so we need to redirect.
261
if Bugzilla->params->{'ssl'} eq 'always';
263
# System is configured such that if we are inside
264
# of an authenticated session, then we need to make
265
# sure that all of the connections are over SSL. Non
266
# authenticated sessions SSL is not mandatory.
267
# For XMLRPC requests, if the method is User.login
268
# then we always want the connection to be over SSL
269
# if the system is configured for authenticated
270
# sessions since the user's username and password
271
# will be passed before the user is logged in.
273
if Bugzilla->params->{'ssl'} eq 'authenticated sessions'
274
&& (Bugzilla->user->id
275
|| (defined $method && $method eq 'User.login'));
281
221
sub correct_urlbase {
282
222
my $ssl = Bugzilla->params->{'ssl'};
283
223
return Bugzilla->params->{'urlbase'} if $ssl eq 'never';
282
my ($oldstr, $newstr) = @_;
284
# Split the old and new strings into arrays containing their values.
285
$oldstr =~ s/[\s,]+/ /g;
286
$newstr =~ s/[\s,]+/ /g;
287
my @old = split(" ", $oldstr);
288
my @new = split(" ", $newstr);
290
my ($rem, $add) = diff_arrays(\@old, \@new);
292
my $removed = join (", ", @$rem);
293
my $added = join (", ", @$add);
295
return ($removed, $added);
348
298
sub wrap_comment {
349
299
my ($comment, $cols) = @_;
350
300
my $wrappedcomment = "";
362
312
$wrappedcomment .= ($line . "\n");
365
# Due to a segfault in Text::Tabs::expand() when processing tabs with
366
# Unicode (see http://rt.perl.org/rt3/Public/Bug/Display.html?id=52104),
367
# we have to remove tabs before processing the comment. This restriction
368
# can go away when we require Perl 5.8.9 or newer.
370
315
$wrappedcomment .= (wrap('', '', $line) . "\n");
408
353
sub format_time {
409
my ($date, $format, $timezone) = @_;
354
my ($date, $format) = @_;
411
# If $format is not set, try to guess the correct date format.
356
# If $format is undefined, try to guess the correct date format.
358
if (!defined($format)) {
413
359
if ($date =~ m/^(\d{4})[-\.](\d{2})[-\.](\d{2}) (\d{2}):(\d{2})(:(\d{2}))?$/) {
415
361
if (defined $sec) {
416
$format = "%Y-%m-%d %T %Z";
362
$format = "%Y-%m-%d %T";
418
$format = "%Y-%m-%d %R %Z";
364
$format = "%Y-%m-%d %R";
421
# Default date format. See DateTime for other formats available.
422
$format = "%Y-%m-%d %R %Z";
367
# Default date format. See Date::Format for other formats available.
368
$format = "%Y-%m-%d %R";
426
# strptime($date) returns an empty array if $date has an invalid date format.
427
my @time = strptime($date);
429
unless (scalar @time) {
430
# If an unknown timezone is passed (such as MSK, for Moskow), strptime() is
431
# unable to parse the date. We try again, but we first remove the timezone.
432
$date =~ s/\s+\S+$//;
433
@time = strptime($date);
437
# Fix a bug in strptime() where seconds can be undefined in some cases.
440
# strptime() counts years from 1900, and months from 0 (January).
441
# We have to fix both values.
442
my $dt = DateTime->new({year => 1900 + $time[5],
447
# DateTime doesn't like fractional seconds.
448
second => int($time[0]),
449
# If importing, use the specified timezone, otherwise
450
# use the timezone specified by the server.
451
time_zone => Bugzilla->local_timezone->offset_as_string($time[6])
452
|| Bugzilla->local_timezone});
454
# Now display the date using the given timezone,
455
# or the user's timezone if none is given.
456
$dt->set_time_zone($timezone || Bugzilla->user->timezone);
457
$date = $dt->strftime($format);
370
# By default, we want the timezone to be displayed.
374
# Search for %Z or %z, meaning we want the timezone to be displayed.
375
# Till bug 182238 gets fixed, we assume Bugzilla->params->{'timezone'}
377
$show_timezone = ($format =~ s/\s?%Z$//i);
380
# str2time($date) is undefined if $date has an invalid date format.
381
my $time = str2time($date);
384
$date = time2str($format, $time);
385
$date .= " " . Bugzilla->params->{'timezone'} if $show_timezone;
460
388
# Don't let invalid (time) strings to be passed to templates!
487
my ($password, $salt) = @_;
490
if (!defined $salt) {
491
# If you don't use a salt, then people can create tables of
492
# hashes that map to particular passwords, and then break your
493
# hashing very easily if they have a large-enough table of common
494
# (or even uncommon) passwords. So we generate a unique salt for
495
# each password in the database, and then just prepend it to
497
$salt = generate_random_password(PASSWORD_SALT_LENGTH);
498
$algorithm = PASSWORD_DIGEST_ALGORITHM;
501
# We append the algorithm used to the string. This is good because then
502
# we can change the algorithm being used, in the future, without
503
# disrupting the validation of existing passwords. Also, this tells
504
# us if a password is using the old "crypt" method of hashing passwords,
505
# because the algorithm will be missing from the string.
506
if ($salt =~ /{([^}]+)}$/) {
510
my $crypted_password;
512
# Wide characters cause crypt to die
513
if (Bugzilla->params->{'utf8'}) {
514
utf8::encode($password) if utf8::is_utf8($password);
517
# Crypt the password.
518
$crypted_password = crypt($password, $salt);
520
# HACK: Perl has bug where returned crypted password is considered
521
# tainted. See http://rt.perl.org/rt3/Public/Bug/Display.html?id=59998
522
unless(tainted($password) || tainted($salt)) {
523
trick_taint($crypted_password);
527
my $hasher = Digest->new($algorithm);
528
# We only want to use the first characters of the salt, no
529
# matter how long of a salt we may have been passed.
530
$salt = substr($salt, 0, PASSWORD_SALT_LENGTH);
531
$hasher->add($password, $salt);
532
$crypted_password = $salt . $hasher->b64digest . "{$algorithm}";
417
# The list of characters that can appear in a salt. Salts and hashes
418
# are both encoded as a sequence of characters from a set containing
419
# 64 characters, each one of which represents 6 bits of the salt/hash.
420
# The encoding is similar to BASE64, the difference being that the
421
# BASE64 plus sign (+) is replaced with a forward slash (/).
422
my @saltchars = (0..9, 'A'..'Z', 'a'..'z', '.', '/');
424
# Generate the salt. We use an 8 character (48 bit) salt for maximum
425
# security on systems whose crypt uses MD5. Systems with older
426
# versions of crypt will just use the first two characters of the salt.
428
for ( my $i=0 ; $i < 8 ; ++$i ) {
429
$salt .= $saltchars[rand(64)];
432
# Crypt the password.
433
my $cryptedpassword = crypt($password, $salt);
535
435
# Return the crypted password.
536
return $crypted_password;
436
return $cryptedpassword;
539
439
sub generate_random_password {
601
501
$vars->{'message'} = $name;
603
if (!$template->process('global/message.txt.tmpl', $vars, \$message)) {
604
require Bugzilla::Error;
605
Bugzilla::Error::ThrowTemplateError($template->error());
503
$template->process('global/message.txt.tmpl', $vars, \$message)
504
|| ThrowTemplateError($template->error());
607
505
# Remove the indenting that exists in messages.html.tmpl.
608
506
$message =~ s/^ //gm;
856
742
Removes any leading or trailing whitespace from a string. This routine does not
857
743
modify the existing string.
745
=item C<diff_strings($oldstr, $newstr)>
747
Takes two strings containing a list of comma- or space-separated items
748
and returns what items were removed from or added to the new one,
749
compared to the old one. Returns a list, where the first entry is a scalar
750
containing removed items, and the second entry is a scalar containing added
859
753
=item C<wrap_hard($string, $size)>
861
755
Wraps a string, so that a line is I<never> longer than C<$size>.
927
817
=item C<format_time($time)>
929
Takes a time and converts it to the desired format and timezone.
930
If no format is given, the routine guesses the correct one and returns
931
an empty array if it cannot. If no timezone is given, the user's timezone
932
is used, as defined in his preferences.
819
Takes a time, converts it to the desired format and appends the timezone
820
as defined in editparams.cgi, if desired. This routine will be expanded
821
in the future to adjust for user preferences regarding what timezone to
934
824
This routine is mainly called from templates to filter dates, see
935
"FILTER time" in L<Bugzilla::Template>.
825
"FILTER time" in Templates.pm. In this case, $format is undefined and
826
the routine has to "guess" the date format that was passed to $dbh->sql_date_format().
937
829
=item C<format_time_decimal($time)>
960
=item C<bz_crypt($password, $salt)>
962
Takes a string and returns a hashed (encrypted) value for it, using a
963
random salt. An optional salt string may also be passed in.
965
Please always use this function instead of the built-in perl C<crypt>
966
function, when checking or setting a password. Bugzilla does not use
852
=item C<bz_crypt($password)>
854
Takes a string and returns a C<crypt>ed value for it, using a random salt.
856
Please always use this function instead of the built-in perl "crypt"
857
when initially encrypting a password.
969
859
=begin undocumented