userprefs.cgi 12.6 KB
Newer Older
1
#!/usr/bin/perl -wT
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# -*- 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>
17
#                 Dan Mosedale <dmose@mozilla.org>
18
#                 Alan Raetz <al_raetz@yahoo.com>
19
#                 David Miller <justdave@syndicomm.com>
20
#                 Christopher Aillon <christopher@aillon.com>
21
#                 Gervase Markham <gerv@gerv.net>
22
#                 Vlad Dascalu <jocuri@softhome.net>
23 24 25

use strict;

26 27
use lib qw(.);

28
use Bugzilla;
29
use Bugzilla::Constants;
30

31 32
require "CGI.pl";

33
use Bugzilla::RelationSet;
34

35
# Use global template variables.
36
use vars qw($template $vars $userid);
37

38 39
# The default email flags leave all email on.
my $defaultflagstring = "ExcludeSelf~on~";
40

41 42
my @roles = ("Owner", "Reporter", "QAcontact", "CClist", "Voter");
my @reasons = ("Removeme", "Comments", "Attachments", "Status", "Resolved", 
43
               "Keywords", "CC", "Other", "Unconfirmed");
44 45 46

foreach my $role (@roles) {
    foreach my $reason (@reasons) {
47 48
        $defaultflagstring .= "email$role$reason~on~";
    }
49 50
}

51 52
# Remove final "~".
chop $defaultflagstring;
53

54 55 56 57 58 59 60
###############################################################################
# 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 {
61
    SendSQL("SELECT realname FROM profiles WHERE userid = $userid");
62
    $vars->{'realname'} = FetchSQLData();
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

    if(Param('allowemailchange')) {
        SendSQL("SELECT tokentype, issuedate + INTERVAL 3 DAY, eventdata 
                    FROM tokens
                    WHERE userid = $userid
                    AND tokentype LIKE 'email%' 
                    ORDER BY tokentype ASC 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;
            }
        }
    }
80 81 82
}

sub SaveAccount {
83
    my $cgi = Bugzilla->cgi;
84

85 86 87 88
    my $pwd1 = $cgi->param('new_password1');
    my $pwd2 = $cgi->param('new_password2');

    if ($cgi->param('Bugzilla_password') ne "" || 
89
        $pwd1 ne "" || $pwd2 ne "") 
90
    {
91
        my $old = SqlQuote($cgi->param('Bugzilla_password'));
92 93
        SendSQL("SELECT cryptpassword FROM profiles WHERE userid = $userid");
        my $oldcryptedpwd = FetchOneColumn();
94 95
        $oldcryptedpwd || ThrowCodeError("unable_to_retrieve_password");

96
        if (crypt($cgi->param('Bugzilla_password'), $oldcryptedpwd) ne 
97 98
                  $oldcryptedpwd) 
        {
99
            ThrowUserError("old_password_incorrect");
100
        }
101 102 103

        if ($pwd1 ne "" || $pwd2 ne "")
        {
104 105
            $cgi->param('new_password1')
              || ThrowUserError("new_password_missing");
106
            ValidatePassword($pwd1, $pwd2);
107 108 109 110 111
        
            my $cryptedpassword = SqlQuote(Crypt($pwd1));
            SendSQL("UPDATE profiles 
                     SET    cryptpassword = $cryptedpassword 
                     WHERE  userid = $userid");
112

113
            # Invalidate all logins except for the current one
114
            Bugzilla->logout(LOGOUT_KEEP_CURRENT);
115
        }
116 117
    }

118 119 120
    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'));
121 122

        if($old_login_name ne $new_login_name) {
123
            $cgi->param('Bugzilla_password') 
124
              || ThrowCodeError("old_password_required");
125

126
            use Bugzilla::Token;
127
            # Block multiple email changes for the same user.
128
            if (Bugzilla::Token::HasEmailChangeToken($userid)) {
129
                ThrowUserError("email_change_in_progress");
130 131 132 133 134
            }

            # Before changing an email address, confirm one does not exist.
            CheckEmailSyntax($new_login_name);
            trick_taint($new_login_name);
135 136
            ValidateNewUser($new_login_name)
              || ThrowUserError("account_exists", {email => $new_login_name});
137

138
            Bugzilla::Token::IssueEmailChangeToken($userid,$old_login_name,
139 140
                                                 $new_login_name);

141
            $vars->{'email_changes_saved'} = 1;
142 143
        }
    }
144

145
    SendSQL("UPDATE profiles SET " .
146
            "realname = " . SqlQuote(trim($cgi->param('realname'))) .
147 148 149 150
            " WHERE userid = $userid");
}


151
sub DoEmail {
152
    if (Param("supportwatchers")) {
153
        my $watcheduserSet = new Bugzilla::RelationSet;
154
        $watcheduserSet->mergeFromDB("SELECT watched FROM watch WHERE" .
155
                                    " watcher=$userid");
156
        $vars->{'watchedusers'} = $watcheduserSet->toString();
157
    }
158

159
    SendSQL("SELECT emailflags FROM profiles WHERE userid = $userid");
160

161
    my ($flagstring) = FetchSQLData();
162

163 164 165
    # If the emailflags haven't been set before, that means that this user 
    # hasn't been to the email pane of userprefs.cgi since the change to 
    # use emailflags. Create a default flagset for them, based on
166
    # static defaults. 
167 168 169 170
    if (!$flagstring) {
        $flagstring = $defaultflagstring;
        SendSQL("UPDATE profiles SET emailflags = " .
                SqlQuote($flagstring) . " WHERE userid = $userid");
171
    }
172

173 174 175 176
    # The 255 param is here, because without a third param, split will
    # trim any trailing null fields, which causes Perl to eject lots of
    # warnings. Any suitably large number would do.
    my %emailflags = split(/~/, $flagstring, 255);
177

178 179 180 181
    # Determine the value of the "excludeself" global email preference.
    # Note that the value of "excludeself" is assumed to be off if the
    # preference does not exist in the user's list, unlike other 
    # preferences whose value is assumed to be on if they do not exist.
182 183
    if (exists($emailflags{'ExcludeSelf'}) 
        && $emailflags{'ExcludeSelf'} eq 'on')
184 185 186 187 188 189 190
    {
        $vars->{'excludeself'} = 1;
    }
    else {
        $vars->{'excludeself'} = 0;
    }
    
191
    foreach my $flag (qw(FlagRequestee FlagRequester)) {
192 193 194 195
        $vars->{$flag} = 
          !exists($emailflags{$flag}) || $emailflags{$flag} eq 'on';
    }
    
196
    # Parse the info into a hash of hashes; the first hash keyed by role,
197 198 199 200 201 202 203 204 205 206 207 208
    # the second by reason, and the value being 1 or 0 for (on or off).
    # Preferences not existing in the user's list are assumed to be on.
    foreach my $role (@roles) {
        $vars->{$role} = {};
        foreach my $reason (@reasons) {
            my $key = "email$role$reason";
            if (!exists($emailflags{$key}) || $emailflags{$key} eq 'on') {
                $vars->{$role}{$reason} = 1;
            }
            else {
                $vars->{$role}{$reason} = 0;
            }
209
        }
210
    }
211 212
}

213 214 215
# Note: we no longer store "off" values in the database.
sub SaveEmail {
    my $updateString = "";
216
    my $cgi = Bugzilla->cgi;
217
    
218
    if (defined $cgi->param('ExcludeSelf')) {
219
        $updateString .= 'ExcludeSelf~on';
220
    } else {
221 222
        $updateString .= 'ExcludeSelf~';
    }
223
    
224
    foreach my $flag (qw(FlagRequestee FlagRequester)) {
225
        $updateString .= "~$flag~" . (defined $cgi->param($flag) ? "on" : "");
226 227
    }
    
228 229 230 231 232 233 234 235
    foreach my $role (@roles) {
        foreach my $reason (@reasons) {
            # Add this preference to the list without giving it a value,
            # which is the equivalent of setting the value to "off."
            $updateString .= "~email$role$reason~";
            
            # If the form field for this preference is defined, then we
            # know the checkbox was checked, so set the value to "on".
236
            $updateString .= "on" if defined $cgi->param("email$role$reason");
237
        }
238
    }
239 240 241 242
            
    SendSQL("UPDATE profiles SET emailflags = " . SqlQuote($updateString) . 
            " WHERE userid = $userid");

243
    if (Param("supportwatchers") && defined $cgi->param('watchedusers')) {
244 245 246 247 248 249 250
        # 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.
        SendSQL("LOCK TABLES watch WRITE, profiles READ");

        # what the db looks like now
251
        my $origWatchedUsers = new Bugzilla::RelationSet;
252 253 254 255
        $origWatchedUsers->mergeFromDB("SELECT watched FROM watch WHERE" .
                                       " watcher=$userid");

        # Update the database to look like the form
256
        my $newWatchedUsers = new Bugzilla::RelationSet($cgi->param('watchedusers'));
257 258 259 260 261 262 263 264 265 266
        my @CCDELTAS = $origWatchedUsers->generateSqlDeltas(
                                                         $newWatchedUsers, 
                                                         "watch", 
                                                         "watcher", 
                                                         $userid,
                                                         "watched");
        ($CCDELTAS[0] eq "") || SendSQL($CCDELTAS[0]);
        ($CCDELTAS[1] eq "") || SendSQL($CCDELTAS[1]);

        SendSQL("UNLOCK TABLES");       
267
    }
268 269 270
}


271 272 273
sub DoPermissions {
    my (@has_bits, @set_bits);
    
274 275 276 277 278
    SendSQL("SELECT DISTINCT name, description FROM groups, user_group_map " .
            "WHERE user_group_map.group_id = groups.id " .
            "AND user_id = $::userid " .
            "AND isbless = 0 " .
            "ORDER BY name");
279
    while (MoreSQLData()) {
280 281
        my ($nam, $desc) = FetchSQLData();
        push(@has_bits, {"desc" => $desc, "name" => $nam});
282
    }
283 284 285 286 287 288 289
    my @set_ids = ();
    SendSQL("SELECT DISTINCT name, description FROM groups " .
            "ORDER BY name");
    while (MoreSQLData()) {
        my ($nam, $desc) = FetchSQLData();
        if (UserCanBlessGroup($nam)) {
            push(@set_bits, {"desc" => $desc, "name" => $nam});
290 291
        }
    }
292 293 294
    
    $vars->{'has_bits'} = \@has_bits;
    $vars->{'set_bits'} = \@set_bits;    
295
}
296

297
# No SavePermissions() because this panel has no changeable fields.
298

299 300 301 302 303

sub DoSavedSearches() {
    $vars->{'queries'} = Bugzilla->user->queries;
}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
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;
}
319 320


321 322 323
###############################################################################
# Live code (not subroutine definitions) starts here
###############################################################################
324

325
Bugzilla->login(LOGIN_REQUIRED);
326 327 328

GetVersionTable();

329 330
my $cgi = Bugzilla->cgi;

331
$vars->{'changes_saved'} = $cgi->param('dosave');
332

333
my $current_tab_name = $cgi->param('tab') || "account";
334

335 336 337
# The SWITCH below makes sure that this is valid
trick_taint($current_tab_name);

338
$vars->{'current_tab_name'} = $current_tab_name;
339

340 341 342
# Do any saving, and then display the current tab.
SWITCH: for ($current_tab_name) {
    /^account$/ && do {
343
        SaveAccount() if $cgi->param('dosave');
344 345 346 347
        DoAccount();
        last SWITCH;
    };
    /^email$/ && do {
348
        SaveEmail() if $cgi->param('dosave');
349 350 351 352 353 354 355
        DoEmail();
        last SWITCH;
    };
    /^permissions$/ && do {
        DoPermissions();
        last SWITCH;
    };
356
    /^saved-searches$/ && do {
357
        SaveSavedSearches() if $cgi->param('dosave');
358 359 360
        DoSavedSearches();
        last SWITCH;
    };
361 362
    ThrowUserError("unknown_tab",
                   { current_tab_name => $current_tab_name });
363 364
}

365
# Generate and return the UI (HTML page) from the appropriate template.
366
print $cgi->header();
367 368
$template->process("account/prefs/prefs.html.tmpl", $vars)
  || ThrowTemplateError($template->error());