Index: editflagtypes.cgi =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/editflagtypes.cgi,v retrieving revision 1.49 diff -pu -r1.49 editflagtypes.cgi --- editflagtypes.cgi +++ editflagtypes.cgi @@ -575,14 +575,10 @@ sub validateCCList { my @addresses = split(/[, ]+/, $cc_list); # We do not call Util::validate_email_syntax because these - # addresses do not require to match 'emailregexp' and do not - # depend on 'emailsuffix'. So we limit ourselves to a simple - # sanity check: - # - match the syntax of a fully qualified email address; - # - do not contain any illegal character. + # addresses are not required to match 'emailregexp' and do not + # depend on 'emailsuffix'. foreach my $address (@addresses) { - ($address =~ /^[\w\.\+\-=]+@[\w\.\-]+\.[\w\-]+$/ - && $address !~ /[\\\(\)<>&,;:"\[\] \t\r\n]/) + validate_email_address($address) || ThrowUserError('illegal_email_address', {addr => $address, default => 1}); } Index: token.cgi =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/token.cgi,v retrieving revision 1.50 diff -pu -r1.50 token.cgi --- token.cgi +++ token.cgi @@ -111,13 +111,14 @@ if ( $::action eq 'reqpw' ) { unless (Bugzilla->user->authorizer->can_change_password) { ThrowUserError("password_change_requests_not_allowed"); } + my $login = strip_email_suffix($login_name); - validate_email_syntax($login_name) + validate_email_syntax($login) || ThrowUserError('illegal_email_address', {addr => $login_name}); my ($user_id) = $dbh->selectrow_array('SELECT userid FROM profiles WHERE ' . $dbh->sql_istrcmp('login_name', '?'), - undef, $login_name); + undef, $login); $user_id || ThrowUserError("account_inexistent"); } Index: userprefs.cgi =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/userprefs.cgi,v retrieving revision 1.114 diff -pu -r1.114 userprefs.cgi --- userprefs.cgi +++ userprefs.cgi @@ -122,7 +122,7 @@ sub SaveAccount { my $old_login_name = $cgi->param('Bugzilla_login'); my $new_login_name = trim($cgi->param('new_login_name')); - if($old_login_name ne $new_login_name) { + if ($old_login_name ne $new_login_name) { $cgi->param('Bugzilla_password') || ThrowUserError("old_password_required"); @@ -132,14 +132,15 @@ sub SaveAccount { ThrowUserError("email_change_in_progress"); } - # Before changing an email address, confirm one does not exist. - validate_email_syntax($new_login_name) + my $login = strip_email_suffix($new_login_name); + validate_email_syntax($login) || ThrowUserError('illegal_email_address', {addr => $new_login_name}); + # Before changing an email address, confirm one does not exist. is_available_username($new_login_name) || ThrowUserError("account_exists", {email => $new_login_name}); Bugzilla::Token::IssueEmailChangeToken($user->id, $old_login_name, - $new_login_name); + $login); $vars->{'email_changes_saved'} = 1; } Index: Bugzilla/Config.pm =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Config.pm,v retrieving revision 1.71 diff -pu -r1.71 Bugzilla/Config.pm --- Bugzilla/Config.pm +++ Bugzilla/Config.pm @@ -78,21 +78,28 @@ sub param_panels { } sub SetParam { - my ($name, $value) = @_; + my ($name, $value, $force) = @_; _load_params unless %params; die "Unknown param $name" unless (exists $params{$name}); my $entry = $params{$name}; - # sanity check the value + unless ($force) { + # sanity check the value - # XXX - This runs the checks. Which would be good, except that - # check_shadowdb creates the database as a side effect, and so the - # checker fails the second time around... - if ($name ne 'shadowdb' && exists $entry->{'checker'}) { - my $err = $entry->{'checker'}->($value, $entry); - die "Param $name is not valid: $err" unless $err eq ''; + # This runs the checks. + if (exists $entry->{'checker'}) { + my $err = $entry->{'checker'}->($value, $entry); + die "Param $name is not valid: $err" unless $err eq ''; + } + + # allow setters + if (exists $enter->{'setter'}) { + my $done = $entry->{'setter'}->($value, $entry); + return if $done eq 1; + die "Setting param $name failed: $done" unless !$done; + } } Bugzilla->params->{$name} = $value; Index: Bugzilla/Install.pm =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Install.pm,v retrieving revision 1.13 diff -pu -r1.13 Bugzilla/Install.pm --- Bugzilla/Install.pm +++ Bugzilla/Install.pm @@ -292,7 +292,7 @@ sub create_admin { print get_text('install_admin_get_email') . ' '; $login = ; chomp $login; - eval { Bugzilla::User->check_login_name_for_creation($login); }; + eval { $login = Bugzilla::User->check_login_name_for_creation($login); }; if ($@) { print $@ . "\n"; undef $login; Index: Bugzilla/User.pm =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/User.pm,v retrieving revision 1.153 diff -pu -r1.153 Bugzilla/User.pm --- Bugzilla/User.pm +++ Bugzilla/User.pm @@ -175,16 +175,17 @@ sub check_login_name_for_creation { my ($invocant, $name) = @_; $name = trim($name); $name || ThrowUserError('user_login_required'); - validate_email_syntax($name) + my $login = strip_email_suffix($name); + validate_email_syntax($login) || ThrowUserError('illegal_email_address', { addr => $name }); # Check the name if it's a new user, or if we're changing the name. - if (!ref($invocant) || $invocant->login ne $name) { - is_available_username($name) + if (!ref($invocant) || $invocant->login ne $login) { + is_available_username($login) || ThrowUserError('account_exists', { email => $name }); } - return $name; + return $login; } sub _check_password { Index: Bugzilla/Util.pm =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Util.pm,v retrieving revision 1.57 diff -pu -r1.57 Bugzilla/Util.pm --- Bugzilla/Util.pm +++ Bugzilla/Util.pm @@ -43,8 +43,8 @@ use base qw(Exporter); format_time format_time_decimal validate_date file_mod_time is_7bit_clean bz_crypt generate_random_password - validate_email_syntax clean_text - get_text); + validate_email_syntax validate_email_address + strip_email_suffix clean_text get_text); use Bugzilla::Constants; @@ -452,10 +452,16 @@ sub generate_random_password { return join("", map{ ('0'..'9','a'..'z','A'..'Z')[rand 62] } (1..$size)); } +sub validate_email_address { + my ($addr) = @_; + return ($addr =~ /^[\w\.\+\-=]+@[\w\.\-]+\.[\w\-]+$/ + && $addr !~ /[\\\(\)<>&,;:"\[\] \t\r\n]/); +} + sub validate_email_syntax { my ($addr) = @_; my $match = Bugzilla->params->{'emailregexp'}; - my $ret = ($addr =~ /$match/ && $addr !~ /[\\\(\)<>&,;:"\[\] \t\r\n]/); + my $ret = ($addr =~ /$match/ && validate_email_address($addr)); if ($ret) { # We assume these checks to suffice to consider the address untainted. trick_taint($_[0]); @@ -463,6 +469,17 @@ sub validate_email_syntax { return $ret ? 1 : 0; } +sub strip_email_suffix +{ + my ($login_name) = @_; + my $emailsuffix = Bugzilla->params->{'emailsuffix'}; + return $login_name unless length $emailsuffix; + my $login = $login_name; + $login = substr($login, 0, length $login - length $emailsuffix); + return $login if $login.$emailsuffix eq $login_name; + return $login_name; +} + sub validate_date { my ($date) = @_; my $date2; Index: Bugzilla/Auth/Login/CGI.pm =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Auth/Login/CGI.pm,v retrieving revision 1.7 diff -pu -r1.7 Bugzilla/Auth/Login/CGI.pm --- Bugzilla/Auth/Login/CGI.pm +++ Bugzilla/Auth/Login/CGI.pm @@ -51,7 +51,7 @@ sub get_login_info { return { failure => AUTH_NODATA }; } - return { username => $username, password => $password }; + return { username => strip_email_suffix($username), password => $password }; } sub fail_nodata { Index: Bugzilla/Config/Auth.pm =================================================================== RCS file: /cvsroot/mozilla/webtools/bugzilla/Bugzilla/Config/Auth.pm,v retrieving revision 1.2 diff -pu -r1.2 Bugzilla/Config/Auth.pm --- Bugzilla/Config/Auth.pm +++ Bugzilla/Config/Auth.pm @@ -120,7 +120,9 @@ sub get_param_list { { name => 'emailsuffix', type => 't', - default => '' + default => '', + checker => \&check_emailsuffix, + setter => \&set_emailsuffix }, { @@ -132,4 +134,55 @@ sub get_param_list { return @param_list; } +sub check_emailsuffix +{ + my ($emailsuffix) = @_; + my $msg = ''; + my $oldemailsuffix = Bugzilla->param->{'emailsuffix'}; + return '' if $oldemailsuffix !~ /\Q$emailsuffix\E$/ + && $emailsuffix !~ /\Q$oldemailsuffix\E$/; + my $len = length $emailsuffix - length $oldemailsuffix; + return '' if $len < 0; + + my $sql_emailsuffix = substr($emailsuffix, 0, $len); + $sql_emailsuffix =~ s/([\%\\])/\\$1/g; + $sql_emailsuffix = '%' . $sql_emailsuffix; + + $dbh->bz_lock_tables('profiles READ'); + my $sth = $dbh->prepare(q{SELECT login_name + FROM profiles + WHERE login_name NOT LIKE ?}); + $sth->execute($sql_emailsuffix); + $msg = 'Some addresses do not match new suffix" if $sth->fetchrow_array; + $dbh->bz_unlock_tables(); + + return $msg; +} + +sub set_emailsuffix +{ + my ($emailsuffix) = @_; + my $msg = ''; + my $oldemailsuffix = Bugzilla->param->{'emailsuffix'}; + return 0 if $oldemailsuffix !~ /\Q$emailsuffix\E$/ + && $emailsuffix !~ /\Q$oldemailsuffix\E$/; + my $len = length $oldemailsuffix - length $emailsuffix; + my $stmt = $len < 0 + ? "substr(login_name, 0, $len)" + : 'concat(login_name, ' . substr($oldemailsuffix, 0, $len) . ')'; + + $dbh->bz_lock_tables('profiles WRITE'); + my $sth = $dbh->prepare(q{UPDATE profiles + SET login_name = $stmt}); + # We must update the email suffix while locked + # otherwise someone might confuse us badly. + SetParam('emailsuffix', $emailsuffix, 1); + + # XXX this doesn't handle pending accounts. + # XXX I really don't care. + $dbh->bz_unlock_tables(); + + return 1; +} + 1;