123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- #!/usr/bin/env perl -wT
- # -*- Mode: perl; indent-tabs-mode: nil -*-
- #
- # The contents of this file are subject to the Mozilla Public
- # License Version 1.1 (the "License"); you may not use this file
- # except in compliance with the License. You may obtain a copy of
- # the License at http://www.mozilla.org/MPL/
- #
- # Software distributed under the License is distributed on an "AS
- # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- # implied. See the License for the specific language governing
- # rights and limitations under the License.
- #
- # The Original Code is the Bugzilla Bug Tracking System.
- #
- # Contributor(s): Terry Weissman <terry@mozilla.org>
- # Dan Mosedale <dmose@mozilla.org>
- # Alan Raetz <al_raetz@yahoo.com>
- # David Miller <justdave@syndicomm.com>
- # Christopher Aillon <christopher@aillon.com>
- # Gervase Markham <gerv@gerv.net>
- # Vlad Dascalu <jocuri@softhome.net>
- # Shane H. W. Travis <travis@sedsystems.ca>
- use strict;
- use lib qw(. lib);
- use Bugzilla;
- use Bugzilla::Constants;
- use Bugzilla::Search;
- use Bugzilla::Util;
- use Bugzilla::Error;
- use Bugzilla::User;
- use Bugzilla::Token;
- my $template = Bugzilla->template;
- local our $vars = {};
- ###############################################################################
- # Each panel has two functions - panel Foo has a DoFoo, to get the data
- # necessary for displaying the panel, and a SaveFoo, to save the panel's
- # contents from the form data (if appropriate).
- # SaveFoo may be called before DoFoo.
- ###############################################################################
- sub DoAccount {
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- ($vars->{'realname'}) = $dbh->selectrow_array(
- "SELECT realname FROM profiles WHERE userid = ?", undef, $user->id);
- if(Bugzilla->params->{'allowemailchange'}
- && Bugzilla->user->authorizer->can_change_email) {
- # First delete old tokens.
- Bugzilla::Token::CleanTokenTable();
- my @token = $dbh->selectrow_array(
- "SELECT tokentype, issuedate + " .
- $dbh->sql_interval(MAX_TOKEN_AGE, 'DAY') . ", eventdata
- FROM tokens
- WHERE userid = ?
- AND tokentype LIKE 'email%'
- ORDER BY tokentype ASC " . $dbh->sql_limit(1), undef, $user->id);
- if (scalar(@token) > 0) {
- my ($tokentype, $change_date, $eventdata) = @token;
- $vars->{'login_change_date'} = $change_date;
- if($tokentype eq 'emailnew') {
- my ($oldemail,$newemail) = split(/:/,$eventdata);
- $vars->{'new_login_name'} = $newemail;
- }
- }
- }
- }
- sub SaveAccount {
- my $cgi = Bugzilla->cgi;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my $pwd1 = $cgi->param('new_password1');
- my $pwd2 = $cgi->param('new_password2');
- if ($user->authorizer->can_change_password
- && ($cgi->param('Bugzilla_password') ne "" || $pwd1 ne "" || $pwd2 ne ""))
- {
- my ($oldcryptedpwd) = $dbh->selectrow_array(
- q{SELECT cryptpassword FROM profiles WHERE userid = ?},
- undef, $user->id);
- $oldcryptedpwd || ThrowCodeError("unable_to_retrieve_password");
- my $oldpassword = $cgi->param('Bugzilla_password');
- # Wide characters cause crypt to die
- if (Bugzilla->params->{'utf8'}) {
- utf8::encode($oldpassword) if utf8::is_utf8($oldpassword);
- }
- if (crypt($oldpassword, $oldcryptedpwd) ne $oldcryptedpwd)
- {
- ThrowUserError("old_password_incorrect");
- }
- if ($pwd1 ne "" || $pwd2 ne "")
- {
- $cgi->param('new_password1')
- || ThrowUserError("new_password_missing");
- validate_password($pwd1, $pwd2);
- if ($cgi->param('Bugzilla_password') ne $pwd1) {
- my $cryptedpassword = bz_crypt($pwd1);
- $dbh->do(q{UPDATE profiles
- SET cryptpassword = ?
- WHERE userid = ?},
- undef, ($cryptedpassword, $user->id));
- # Invalidate all logins except for the current one
- Bugzilla->logout(LOGOUT_KEEP_CURRENT);
- }
- }
- }
- if ($user->authorizer->can_change_email
- && Bugzilla->params->{"allowemailchange"}
- && $cgi->param('new_login_name'))
- {
- 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) {
- $cgi->param('Bugzilla_password')
- || ThrowUserError("old_password_required");
- use Bugzilla::Token;
- # Block multiple email changes for the same user.
- if (Bugzilla::Token::HasEmailChangeToken($user->id)) {
- ThrowUserError("email_change_in_progress");
- }
- # Before changing an email address, confirm one does not exist.
- validate_email_syntax($new_login_name)
- || ThrowUserError('illegal_email_address', {addr => $new_login_name});
- is_available_username($new_login_name)
- || ThrowUserError("account_exists", {email => $new_login_name});
- Bugzilla::Token::IssueEmailChangeToken($user, $old_login_name,
- $new_login_name);
- $vars->{'email_changes_saved'} = 1;
- }
- }
- my $realname = trim($cgi->param('realname'));
- trick_taint($realname); # Only used in a placeholder
- $dbh->do("UPDATE profiles SET realname = ? WHERE userid = ?",
- undef, ($realname, $user->id));
- }
- sub DoSettings {
- my $user = Bugzilla->user;
- my $settings = $user->settings;
- $vars->{'settings'} = $settings;
- my @setting_list = keys %$settings;
- $vars->{'setting_names'} = \@setting_list;
- $vars->{'has_settings_enabled'} = 0;
- # Is there at least one user setting enabled?
- foreach my $setting_name (@setting_list) {
- if ($settings->{"$setting_name"}->{'is_enabled'}) {
- $vars->{'has_settings_enabled'} = 1;
- last;
- }
- }
- $vars->{'dont_show_button'} = !$vars->{'has_settings_enabled'};
- }
- sub SaveSettings {
- my $cgi = Bugzilla->cgi;
- my $user = Bugzilla->user;
- my $settings = $user->settings;
- my @setting_list = keys %$settings;
- foreach my $name (@setting_list) {
- next if ! ($settings->{$name}->{'is_enabled'});
- my $value = $cgi->param($name);
- my $setting = new Bugzilla::User::Setting($name);
- if ($value eq "${name}-isdefault" ) {
- if (! $settings->{$name}->{'is_default'}) {
- $settings->{$name}->reset_to_default;
- }
- }
- else {
- $setting->validate_value($value);
- $settings->{$name}->set($value);
- }
- }
- $vars->{'settings'} = $user->settings(1);
- }
- sub DoEmail {
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
-
- ###########################################################################
- # User watching
- ###########################################################################
- if (Bugzilla->params->{"supportwatchers"}) {
- my $watched_ref = $dbh->selectcol_arrayref(
- "SELECT profiles.login_name FROM watch INNER JOIN profiles" .
- " ON watch.watched = profiles.userid" .
- " WHERE watcher = ?" .
- " ORDER BY profiles.login_name",
- undef, $user->id);
- $vars->{'watchedusers'} = $watched_ref;
- my $watcher_ids = $dbh->selectcol_arrayref(
- "SELECT watcher FROM watch WHERE watched = ?",
- undef, $user->id);
- my @watchers;
- foreach my $watcher_id (@$watcher_ids) {
- my $watcher = new Bugzilla::User($watcher_id);
- push (@watchers, Bugzilla::User::identity($watcher));
- }
- @watchers = sort { lc($a) cmp lc($b) } @watchers;
- $vars->{'watchers'} = \@watchers;
- }
- ###########################################################################
- # Role-based preferences
- ###########################################################################
- my $sth = $dbh->prepare("SELECT relationship, event " .
- "FROM email_setting " .
- "WHERE user_id = ?");
- $sth->execute($user->id);
- my %mail;
- while (my ($relationship, $event) = $sth->fetchrow_array()) {
- $mail{$relationship}{$event} = 1;
- }
- $vars->{'mail'} = \%mail;
- }
- sub SaveEmail {
- my $dbh = Bugzilla->dbh;
- my $cgi = Bugzilla->cgi;
- my $user = Bugzilla->user;
- if (Bugzilla->params->{"supportwatchers"}) {
- Bugzilla::User::match_field($cgi, { 'new_watchedusers' => {'type' => 'multi'} });
- }
- ###########################################################################
- # Role-based preferences
- ###########################################################################
- $dbh->bz_start_transaction();
- # Delete all the user's current preferences
- $dbh->do("DELETE FROM email_setting WHERE user_id = ?", undef, $user->id);
- # Repopulate the table - first, with normal events in the
- # relationship/event matrix.
- # Note: the database holds only "off" email preferences, as can be implied
- # from the name of the table - profiles_nomail.
- foreach my $rel (RELATIONSHIPS) {
- # Positive events: a ticked box means "send me mail."
- foreach my $event (POS_EVENTS) {
- if (defined($cgi->param("email-$rel-$event"))
- && $cgi->param("email-$rel-$event") == 1)
- {
- $dbh->do("INSERT INTO email_setting " .
- "(user_id, relationship, event) " .
- "VALUES (?, ?, ?)",
- undef, ($user->id, $rel, $event));
- }
- }
-
- # Negative events: a ticked box means "don't send me mail."
- foreach my $event (NEG_EVENTS) {
- if (!defined($cgi->param("neg-email-$rel-$event")) ||
- $cgi->param("neg-email-$rel-$event") != 1)
- {
- $dbh->do("INSERT INTO email_setting " .
- "(user_id, relationship, event) " .
- "VALUES (?, ?, ?)",
- undef, ($user->id, $rel, $event));
- }
- }
- }
- # Global positive events: a ticked box means "send me mail."
- foreach my $event (GLOBAL_EVENTS) {
- if (defined($cgi->param("email-" . REL_ANY . "-$event"))
- && $cgi->param("email-" . REL_ANY . "-$event") == 1)
- {
- $dbh->do("INSERT INTO email_setting " .
- "(user_id, relationship, event) " .
- "VALUES (?, ?, ?)",
- undef, ($user->id, REL_ANY, $event));
- }
- }
- $dbh->bz_commit_transaction();
- ###########################################################################
- # User watching
- ###########################################################################
- if (Bugzilla->params->{"supportwatchers"}
- && (defined $cgi->param('new_watchedusers')
- || defined $cgi->param('remove_watched_users')))
- {
- $dbh->bz_start_transaction();
- # Use this to protect error messages on duplicate submissions
- my $old_watch_ids =
- $dbh->selectcol_arrayref("SELECT watched FROM watch"
- . " WHERE watcher = ?", undef, $user->id);
- # The new information given to us by the user.
- my $new_watched_users = join(',', $cgi->param('new_watchedusers')) || '';
- my @new_watch_names = split(/[,\s]+/, $new_watched_users);
- my %new_watch_ids;
- foreach my $username (@new_watch_names) {
- my $watched_userid = login_to_id(trim($username), THROW_ERROR);
- $new_watch_ids{$watched_userid} = 1;
- }
- # Add people who were added.
- my $insert_sth = $dbh->prepare('INSERT INTO watch (watched, watcher)'
- . ' VALUES (?, ?)');
- foreach my $add_me (keys(%new_watch_ids)) {
- next if grep($_ == $add_me, @$old_watch_ids);
- $insert_sth->execute($add_me, $user->id);
- }
- if (defined $cgi->param('remove_watched_users')) {
- my @removed = $cgi->param('watched_by_you');
- # Remove people who were removed.
- my $delete_sth = $dbh->prepare('DELETE FROM watch WHERE watched = ?'
- . ' AND watcher = ?');
-
- my %remove_watch_ids;
- foreach my $username (@removed) {
- my $watched_userid = login_to_id(trim($username), THROW_ERROR);
- $remove_watch_ids{$watched_userid} = 1;
- }
- foreach my $remove_me (keys(%remove_watch_ids)) {
- $delete_sth->execute($remove_me, $user->id);
- }
- }
- $dbh->bz_commit_transaction();
- }
- }
- sub DoPermissions {
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my (@has_bits, @set_bits);
-
- my $groups = $dbh->selectall_arrayref(
- "SELECT DISTINCT name, description FROM groups WHERE id IN (" .
- $user->groups_as_string . ") ORDER BY name");
- foreach my $group (@$groups) {
- my ($nam, $desc) = @$group;
- push(@has_bits, {"desc" => $desc, "name" => $nam});
- }
- $groups = $dbh->selectall_arrayref('SELECT DISTINCT id, name, description
- FROM groups
- ORDER BY name');
- foreach my $group (@$groups) {
- my ($group_id, $nam, $desc) = @$group;
- if ($user->can_bless($group_id)) {
- push(@set_bits, {"desc" => $desc, "name" => $nam});
- }
- }
- # If the user has product specific privileges, inform him about that.
- foreach my $privs (PER_PRODUCT_PRIVILEGES) {
- next if $user->in_group($privs);
- $vars->{"local_$privs"} = $user->get_products_by_permission($privs);
- }
- $vars->{'has_bits'} = \@has_bits;
- $vars->{'set_bits'} = \@set_bits;
- }
- # No SavePermissions() because this panel has no changeable fields.
- sub DoSavedSearches {
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- if ($user->queryshare_groups_as_string) {
- $vars->{'queryshare_groups'} =
- Bugzilla::Group->new_from_list($user->queryshare_groups);
- }
- $vars->{'bless_group_ids'} = [map {$_->{'id'}} @{$user->bless_groups}];
- }
- sub SaveSavedSearches {
- my $cgi = Bugzilla->cgi;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- # We'll need this in a loop, so do the call once.
- my $user_id = $user->id;
- my $sth_insert_nl = $dbh->prepare('INSERT INTO namedqueries_link_in_footer
- (namedquery_id, user_id)
- VALUES (?, ?)');
- my $sth_delete_nl = $dbh->prepare('DELETE FROM namedqueries_link_in_footer
- WHERE namedquery_id = ?
- AND user_id = ?');
- my $sth_insert_ngm = $dbh->prepare('INSERT INTO namedquery_group_map
- (namedquery_id, group_id)
- VALUES (?, ?)');
- my $sth_update_ngm = $dbh->prepare('UPDATE namedquery_group_map
- SET group_id = ?
- WHERE namedquery_id = ?');
- my $sth_delete_ngm = $dbh->prepare('DELETE FROM namedquery_group_map
- WHERE namedquery_id = ?');
- # Update namedqueries_link_in_footer for this user.
- foreach my $q (@{$user->queries}, @{$user->queries_available}) {
- if (defined $cgi->param("link_in_footer_" . $q->id)) {
- $sth_insert_nl->execute($q->id, $user_id) if !$q->link_in_footer;
- }
- else {
- $sth_delete_nl->execute($q->id, $user_id) if $q->link_in_footer;
- }
- }
- # For user's own queries, update namedquery_group_map.
- foreach my $q (@{$user->queries}) {
- my $group_id;
- if ($user->in_group(Bugzilla->params->{'querysharegroup'})) {
- $group_id = $cgi->param("share_" . $q->id) || '';
- }
- if ($group_id) {
- # Don't allow the user to share queries with groups he's not
- # allowed to.
- next unless grep($_ eq $group_id, @{$user->queryshare_groups});
- # $group_id is now definitely a valid ID of a group the
- # user can share queries with, so we can trick_taint.
- detaint_natural($group_id);
- if ($q->shared_with_group) {
- $sth_update_ngm->execute($group_id, $q->id);
- }
- else {
- $sth_insert_ngm->execute($q->id, $group_id);
- }
- # If we're sharing our query with a group we can bless, we
- # have the ability to add link to our search to the footer of
- # direct group members automatically.
- if ($user->can_bless($group_id) && $cgi->param('force_' . $q->id)) {
- my $group = new Bugzilla::Group($group_id);
- my $members = $group->members_non_inherited;
- foreach my $member (@$members) {
- next if $member->id == $user->id;
- $sth_insert_nl->execute($q->id, $member->id)
- if !$q->link_in_footer($member);
- }
- }
- }
- else {
- # They have unshared that query.
- if ($q->shared_with_group) {
- $sth_delete_ngm->execute($q->id);
- }
- # Don't remove namedqueries_link_in_footer entries for users
- # subscribing to the shared query. The idea is that they will
- # probably want to be subscribers again should the sharing
- # user choose to share the query again.
- }
- }
- $user->flush_queries_cache;
-
- # Update profiles.mybugslink.
- my $showmybugslink = defined($cgi->param("showmybugslink")) ? 1 : 0;
- $dbh->do("UPDATE profiles SET mybugslink = ? WHERE userid = ?",
- undef, ($showmybugslink, $user->id));
- $user->{'showmybugslink'} = $showmybugslink;
- }
- ###############################################################################
- # Live code (not subroutine definitions) starts here
- ###############################################################################
- my $cgi = Bugzilla->cgi;
- # This script needs direct access to the username and password CGI variables,
- # so we save them before their removal in Bugzilla->login, and delete them
- # before login in case we might be in a sudo session.
- my $bugzilla_login = $cgi->param('Bugzilla_login');
- my $bugzilla_password = $cgi->param('Bugzilla_password');
- $cgi->delete('Bugzilla_login', 'Bugzilla_password') if ($cgi->cookie('sudo'));
- Bugzilla->login(LOGIN_REQUIRED);
- $cgi->param('Bugzilla_login', $bugzilla_login);
- $cgi->param('Bugzilla_password', $bugzilla_password);
- $vars->{'changes_saved'} = $cgi->param('dosave');
- my $current_tab_name = $cgi->param('tab') || "settings";
- # The SWITCH below makes sure that this is valid
- trick_taint($current_tab_name);
- $vars->{'current_tab_name'} = $current_tab_name;
- my $token = $cgi->param('token');
- check_token_data($token, 'edit_user_prefs') if $cgi->param('dosave');
- # Do any saving, and then display the current tab.
- SWITCH: for ($current_tab_name) {
- /^account$/ && do {
- SaveAccount() if $cgi->param('dosave');
- DoAccount();
- last SWITCH;
- };
- /^settings$/ && do {
- SaveSettings() if $cgi->param('dosave');
- DoSettings();
- last SWITCH;
- };
- /^email$/ && do {
- SaveEmail() if $cgi->param('dosave');
- DoEmail();
- last SWITCH;
- };
- /^permissions$/ && do {
- DoPermissions();
- last SWITCH;
- };
- /^saved-searches$/ && do {
- SaveSavedSearches() if $cgi->param('dosave');
- DoSavedSearches();
- last SWITCH;
- };
- ThrowUserError("unknown_tab",
- { current_tab_name => $current_tab_name });
- }
- delete_token($token) if $cgi->param('dosave');
- if ($current_tab_name ne 'permissions') {
- $vars->{'token'} = issue_session_token('edit_user_prefs');
- }
- # Generate and return the UI (HTML page) from the appropriate template.
- print $cgi->header();
- $template->process("account/prefs/prefs.html.tmpl", $vars)
- || ThrowTemplateError($template->error());
|