#!/usr/bin/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 # Dan Mosedale # Alan Raetz # David Miller # Christopher Aillon # Gervase Markham # Vlad Dascalu # Shane H. W. Travis use strict; use lib qw(.); use Bugzilla; use Bugzilla::Constants; use Bugzilla::Search; use Bugzilla::Util; use Bugzilla::User; require "CGI.pl"; # Use global template variables. use vars qw($template $vars $userid); ############################################################################### # 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; SendSQL("SELECT realname FROM profiles WHERE userid = $userid"); $vars->{'realname'} = FetchSQLData(); if(Param('allowemailchange')) { SendSQL("SELECT tokentype, issuedate + " . $dbh->sql_interval('3 DAY') . ", eventdata FROM tokens WHERE userid = $userid AND tokentype LIKE 'email%' ORDER BY tokentype ASC " . $dbh->sql_limit(1)); if(MoreSQLData()) { my ($tokentype, $change_date, $eventdata) = &::FetchSQLData(); $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 $pwd1 = $cgi->param('new_password1'); my $pwd2 = $cgi->param('new_password2'); if ($cgi->param('Bugzilla_password') ne "" || $pwd1 ne "" || $pwd2 ne "") { my $old = SqlQuote($cgi->param('Bugzilla_password')); SendSQL("SELECT cryptpassword FROM profiles WHERE userid = $userid"); my $oldcryptedpwd = FetchOneColumn(); $oldcryptedpwd || ThrowCodeError("unable_to_retrieve_password"); if (crypt($cgi->param('Bugzilla_password'), $oldcryptedpwd) ne $oldcryptedpwd) { ThrowUserError("old_password_incorrect"); } if ($pwd1 ne "" || $pwd2 ne "") { $cgi->param('new_password1') || ThrowUserError("new_password_missing"); ValidatePassword($pwd1, $pwd2); my $cryptedpassword = SqlQuote(bz_crypt($pwd1)); SendSQL("UPDATE profiles SET cryptpassword = $cryptedpassword WHERE userid = $userid"); # Invalidate all logins except for the current one Bugzilla->logout(LOGOUT_KEEP_CURRENT); } } if(Param("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($userid)) { ThrowUserError("email_change_in_progress"); } # Before changing an email address, confirm one does not exist. CheckEmailSyntax($new_login_name); trick_taint($new_login_name); is_available_username($new_login_name) || ThrowUserError("account_exists", {email => $new_login_name}); Bugzilla::Token::IssueEmailChangeToken($userid,$old_login_name, $new_login_name); $vars->{'email_changes_saved'} = 1; } } SendSQL("UPDATE profiles SET " . "realname = " . SqlQuote(trim($cgi->param('realname'))) . " WHERE userid = $userid"); } sub DoSettings { $vars->{'settings'} = Bugzilla->user->settings; my @setting_list = keys %{Bugzilla->user->settings}; $vars->{'setting_names'} = \@setting_list; } sub SaveSettings { my $cgi = Bugzilla->cgi; my $settings = Bugzilla->user->settings; my @setting_list = keys %{Bugzilla->user->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'} = Bugzilla->user->settings(1); } sub DoEmail { my $dbh = Bugzilla->dbh; ########################################################################### # User watching ########################################################################### if (Param("supportwatchers")) { my $watched_ref = $dbh->selectcol_arrayref( "SELECT profiles.login_name FROM watch INNER JOIN profiles" . " ON watch.watched = profiles.userid" . " WHERE watcher = ?", undef, $userid); $vars->{'watchedusers'} = join(',', @$watched_ref); my $watcher_ids = $dbh->selectcol_arrayref( "SELECT watcher FROM watch WHERE watched = ?", undef, $userid); 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 = Bugzilla->dbh->prepare("SELECT relationship, event " . "FROM email_setting " . "WHERE user_id = $userid"); $sth->execute(); 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; ########################################################################### # Role-based preferences ########################################################################### $dbh->bz_lock_tables("email_setting WRITE"); # Delete all the user's current preferences $dbh->do("DELETE FROM email_setting WHERE user_id = $userid"); # 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 ($userid, $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 ($userid, $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 ($userid, " . REL_ANY . ", $event)"); } } $dbh->bz_unlock_tables(); ########################################################################### # User watching ########################################################################### if (Param("supportwatchers") && defined $cgi->param('watchedusers')) { # Just in case. Note that this much locking is actually overkill: # we don't really care if anyone reads the watch table. So # some small amount of contention could be gotten rid of by # using user-defined locks rather than table locking. $dbh->bz_lock_tables('watch WRITE', 'profiles READ'); # what the db looks like now my $old_watch_ids = $dbh->selectcol_arrayref("SELECT watched FROM watch" . " WHERE watcher = ?", undef, $userid); # The new information given to us by the user. my @new_watch_names = split(/[,\s]+/, $cgi->param('watchedusers')); my @new_watch_ids = (); foreach my $username (@new_watch_names) { my $watched_userid = DBNameToIdAndCheck(trim($username)); push(@new_watch_ids, $watched_userid); } my ($removed, $added) = diff_arrays($old_watch_ids, \@new_watch_ids); # Remove people who were removed. my $delete_sth = $dbh->prepare('DELETE FROM watch WHERE watched = ?' . ' AND watcher = ?'); foreach my $remove_me (@$removed) { $delete_sth->execute($remove_me, $userid); } # Add people who were added. my $insert_sth = $dbh->prepare('INSERT INTO watch (watched, watcher)' . ' VALUES (?, ?)'); foreach my $add_me (@$added) { $insert_sth->execute($add_me, $userid); } $dbh->bz_unlock_tables(); } } sub DoPermissions { my (@has_bits, @set_bits); SendSQL("SELECT DISTINCT name, description FROM groups " . "INNER JOIN user_group_map " . "ON user_group_map.group_id = groups.id " . "WHERE user_id = $::userid " . "AND isbless = 0 " . "ORDER BY name"); while (MoreSQLData()) { my ($nam, $desc) = FetchSQLData(); push(@has_bits, {"desc" => $desc, "name" => $nam}); } my @set_ids = (); SendSQL("SELECT DISTINCT name, description FROM groups " . "ORDER BY name"); while (MoreSQLData()) { my ($nam, $desc) = FetchSQLData(); if (Bugzilla->user->can_bless($nam)) { push(@set_bits, {"desc" => $desc, "name" => $nam}); } } $vars->{'has_bits'} = \@has_bits; $vars->{'set_bits'} = \@set_bits; } # No SavePermissions() because this panel has no changeable fields. sub DoSavedSearches() { # 2004-12-13 - colin.ogilvie@gmail.com, bug 274397 # Need to work around the possibly missing query_format=advanced $vars->{'user'} = Bugzilla->user; my @queries = @{Bugzilla->user->queries}; my @newqueries; foreach my $q (@queries) { if ($q->{'query'} =~ /query_format=([^&]*)/) { my $format = $1; if (!IsValidQueryType($format)) { if ($format eq "") { $q->{'query'} =~ s/query_format=/query_format=advanced/; } else { $q->{'query'} .= '&query_format=advanced'; } } } else { $q->{'query'} .= '&query_format=advanced'; } push @newqueries, $q; } $vars->{'queries'} = \@newqueries; } sub SaveSavedSearches() { my $cgi = Bugzilla->cgi; my $dbh = Bugzilla->dbh; my @queries = @{Bugzilla->user->queries}; my $sth = $dbh->prepare("UPDATE namedqueries SET linkinfooter = ? WHERE userid = ? AND name = ?"); foreach my $q (@queries) { my $linkinfooter = defined($cgi->param("linkinfooter_$q->{'name'}")) ? 1 : 0; $sth->execute($linkinfooter, $userid, $q->{'name'}); } Bugzilla->user->flush_queries_cache; my $showmybugslink = defined($cgi->param("showmybugslink")) ? 1 : 0; $dbh->do("UPDATE profiles SET mybugslink = $showmybugslink " . "WHERE userid = " . Bugzilla->user->id); Bugzilla->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 my $bugzilla_login = $cgi->param('Bugzilla_login'); my $bugzilla_password = $cgi->param('Bugzilla_password'); Bugzilla->login(LOGIN_REQUIRED); $cgi->param('Bugzilla_login', $bugzilla_login); $cgi->param('Bugzilla_password', $bugzilla_password); GetVersionTable(); $vars->{'changes_saved'} = $cgi->param('dosave'); my $current_tab_name = $cgi->param('tab') || "account"; # The SWITCH below makes sure that this is valid trick_taint($current_tab_name); $vars->{'current_tab_name'} = $current_tab_name; # 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 }); } # 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());