388 lines
13 KiB
Perl
388 lines
13 KiB
Perl
#!/usr/bin/perl
|
|
#
|
|
# This code is based on code originally created by the LiveJournal project
|
|
# owned and operated by Live Journal, Inc. The code has been modified and expanded
|
|
# by Dreamwidth Studios, LLC. These files were originally licensed under
|
|
# the terms of the license supplied by Live Journal, Inc, which can
|
|
# currently be found at:
|
|
#
|
|
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
|
|
#
|
|
# In accordance with the original license, this code and all its
|
|
# modifications are provided under the GNU General Public License.
|
|
# A copy of that license can be found in the LICENSE file included as
|
|
# part of this distribution.
|
|
#
|
|
#
|
|
# Authors:
|
|
# Afuna <coder.dw@afunamatata.com>
|
|
#
|
|
# Copyright (c) 2014 by Dreamwidth Studios, LLC.
|
|
|
|
package DW::Controller::Settings;
|
|
|
|
use strict;
|
|
use DW::Controller;
|
|
use DW::Routing;
|
|
use DW::Template;
|
|
use DW::FormErrors;
|
|
|
|
=head1 NAME
|
|
|
|
DW::Controller::Settings - Controller for settings/settings-related pages
|
|
|
|
=cut
|
|
|
|
DW::Routing->register_string( "/accountstatus", \&account_status_handler, app => 1 );
|
|
DW::Routing->register_string( "/changepassword", \&changepassword_handler, app => 1, );
|
|
|
|
sub account_status_handler {
|
|
my ($opts) = @_;
|
|
|
|
my ( $ok, $rv ) = controller( form_auth => 1, authas => { show_all => 1 } );
|
|
return $rv unless $ok;
|
|
|
|
my $r = $rv->{r};
|
|
my $remote = $rv->{remote};
|
|
my $u = $rv->{u};
|
|
my $get = $r->get_args;
|
|
|
|
my $ml_scope = "/settings/accountstatus.tt";
|
|
my @statusvis_options =
|
|
$u->is_suspended
|
|
? ( 'S' => LJ::Lang::ml("$ml_scope.journalstatus.select.suspended") )
|
|
: (
|
|
'V' => LJ::Lang::ml("$ml_scope.journalstatus.select.activated"),
|
|
'D' => LJ::Lang::ml("$ml_scope.journalstatus.select.deleted"),
|
|
);
|
|
my %statusvis_map = @statusvis_options;
|
|
|
|
my $errors = DW::FormErrors->new;
|
|
|
|
# TODO: this feels like a misuse of DW::FormErrors. Make a new class?
|
|
my $messages = DW::FormErrors->new;
|
|
my $warnings = DW::FormErrors->new;
|
|
|
|
my $post;
|
|
if ( $r->did_post && LJ::check_referer('/accountstatus') ) {
|
|
$post = $r->post_args;
|
|
my $new_statusvis = $post->{statusvis};
|
|
|
|
# are they suspended?
|
|
$errors->add( "", ".error.nochange.suspend" )
|
|
if $u->is_suspended;
|
|
|
|
# are they expunged?
|
|
$errors->add( "", '.error.nochange.expunged' )
|
|
if $u->is_expunged;
|
|
|
|
# invalid statusvis
|
|
$errors->add( "", '.error.invalid' )
|
|
unless $new_statusvis eq 'D' || $new_statusvis eq 'V';
|
|
|
|
my $did_change = $u->statusvis ne $new_statusvis;
|
|
|
|
# no need to change?
|
|
$messages->add(
|
|
"",
|
|
$u->is_community ? '.message.nochange.comm' : '.message.nochange',
|
|
{ statusvis => $statusvis_map{$new_statusvis} }
|
|
) unless $did_change;
|
|
|
|
if ( !$errors->exist && $did_change ) {
|
|
my $res = 0;
|
|
|
|
my $ip = $r->get_remote_ip;
|
|
|
|
my @date = localtime(time);
|
|
my $date = sprintf(
|
|
"%02d:%02d %02d/%02d/%04d",
|
|
@date[ 2, 1 ],
|
|
$date[3],
|
|
$date[4] + 1,
|
|
$date[5] + 1900
|
|
);
|
|
|
|
if ( $new_statusvis eq 'D' ) {
|
|
|
|
$res = $u->set_deleted;
|
|
|
|
$u->set_prop( delete_reason => $post->{reason} || "" );
|
|
|
|
if ($res) {
|
|
|
|
# sending ESN status was changed
|
|
LJ::Event::SecurityAttributeChanged->new(
|
|
$u,
|
|
{
|
|
action => 'account_deleted',
|
|
ip => $ip,
|
|
datetime => $date,
|
|
}
|
|
)->fire;
|
|
}
|
|
}
|
|
elsif ( $new_statusvis eq 'V' ) {
|
|
## Restore previous statusvis of journal. It may be different
|
|
## from 'V', it may be read-only, or locked, or whatever.
|
|
my @previous_status =
|
|
grep { $_ ne 'D' } $u->get_previous_statusvis;
|
|
my $new_status = $previous_status[0] || 'V';
|
|
my $method = {
|
|
V => 'set_visible',
|
|
L => 'set_locked',
|
|
M => 'set_memorial',
|
|
O => 'set_readonly',
|
|
R => 'set_renamed',
|
|
}->{$new_status};
|
|
$errors->add_string( "", "Can't set status '" . LJ::ehtml($new_status) . "'" )
|
|
unless $method;
|
|
|
|
unless ( $errors->exist ) {
|
|
$res = $u->$method;
|
|
|
|
$u->set_prop( delete_reason => "" );
|
|
|
|
if ($res) {
|
|
LJ::Event::SecurityAttributeChanged->new(
|
|
$u,
|
|
{
|
|
action => 'account_activated',
|
|
ip => $ip,
|
|
datetime => $date,
|
|
}
|
|
)->fire;
|
|
|
|
$did_change = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
# error updating?
|
|
$errors->add( "", ".error.db" ) unless $res;
|
|
|
|
unless ( $errors->exist ) {
|
|
$messages->add(
|
|
"",
|
|
$u->is_community
|
|
? '.message.success.comm'
|
|
: '.message.success',
|
|
{ statusvis => $statusvis_map{$new_statusvis} }
|
|
);
|
|
|
|
if ( $new_statusvis eq 'D' ) {
|
|
$messages->add(
|
|
"",
|
|
$u->is_community
|
|
? ".message.deleted.comm"
|
|
: ".message.deleted2",
|
|
{ sitenameshort => $LJ::SITENAMESHORT }
|
|
);
|
|
|
|
# are they leaving any community admin-less?
|
|
if ( $u->is_person ) {
|
|
my $cids = LJ::load_rel_target( $remote, "A" );
|
|
my @warn_comm_ids;
|
|
|
|
if ($cids) {
|
|
|
|
# verify there are visible maintainers for each community
|
|
foreach my $cid (@$cids) {
|
|
push @warn_comm_ids, $cid
|
|
unless grep { $_->is_visible }
|
|
values
|
|
%{ LJ::load_userids( @{ LJ::load_rel_user( $cid, 'A' ) } ) };
|
|
}
|
|
|
|
# and if not, warn them about it
|
|
if (@warn_comm_ids) {
|
|
my $commlist = '<ul>';
|
|
$commlist .= '<li>' . $_->ljuser_display . '</li>'
|
|
foreach values %{ LJ::load_userids(@warn_comm_ids) };
|
|
$commlist .= '</ul>';
|
|
|
|
$warnings->add(
|
|
"",
|
|
'.message.noothermaintainer',
|
|
{
|
|
commlist => $commlist,
|
|
manage_url => LJ::create_url("/communities/list"),
|
|
pagetitle => LJ::Lang::ml('/communities/list.tt.title'),
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
my $vars = {
|
|
form_url => LJ::create_url( undef, keep_args => ['authas'] ),
|
|
extra_delete_text => LJ::Hooks::run_hook( "accountstatus_delete_text", $u ),
|
|
statusvis_options => \@statusvis_options,
|
|
|
|
u => $u,
|
|
delete_reason => $u->prop('delete_reason'),
|
|
|
|
errors => $errors,
|
|
messages => $messages,
|
|
warnings => $warnings,
|
|
formdata => $post,
|
|
|
|
authas_form => $rv->{authas_form},
|
|
};
|
|
return DW::Template->render_template( 'settings/accountstatus.tt', $vars );
|
|
}
|
|
|
|
sub changepassword_handler {
|
|
my ($opts) = @_;
|
|
|
|
my ( $ok, $rv ) = controller( anonymous => 1 );
|
|
return $rv unless $ok;
|
|
|
|
my $r = $rv->{r};
|
|
my $get = $r->get_args;
|
|
my $post;
|
|
|
|
my $remote = $rv->{remote};
|
|
|
|
my ( $aa, $authu );
|
|
my $ml_scope = "/settings/changepassword.tt";
|
|
if ( my $auth = $get->{auth} ) {
|
|
my $lostinfo_url = LJ::create_url("/lostinfo");
|
|
|
|
return error_ml("$ml_scope.error.invalidarg")
|
|
unless $auth =~ /^(\d+)\.(.+)$/;
|
|
|
|
$aa = LJ::is_valid_authaction( $1, $2 );
|
|
return error_ml("$ml_scope.error.invalidarg")
|
|
unless $aa;
|
|
|
|
return error_ml( "$ml_scope.error.actionalreadyperformed", { url => $lostinfo_url } )
|
|
if $aa->{used} eq 'Y';
|
|
|
|
return $r->redirect($lostinfo_url)
|
|
unless $aa->{action} eq 'reset_password';
|
|
|
|
# confirmed the identity...
|
|
$authu = LJ::load_userid( $aa->{userid} );
|
|
|
|
# verify the email can still receive passwords
|
|
return error_ml( "$ml_scope.error.emailchanged", { url => $lostinfo_url } )
|
|
unless $authu->can_receive_password( $aa->{arg1} );
|
|
}
|
|
return error_ml("$ml_scope.error.identity")
|
|
if $remote && $remote->is_identity;
|
|
|
|
my $errors = DW::FormErrors->new;
|
|
if ( $r->did_post && $r->post_args->{mode} eq 'submit' ) {
|
|
$post = $r->post_args;
|
|
|
|
my $user = $authu ? $authu->user : LJ::canonical_username( $post->{user} );
|
|
my $password = $post->{password};
|
|
my $newpass1 = LJ::trim( $post->{newpass1} );
|
|
my $newpass2 = LJ::trim( $post->{newpass2} );
|
|
|
|
my $u = LJ::load_user($user);
|
|
$errors->add( "user", ".error.invaliduser" ) unless $u;
|
|
$errors->add( "user", ".error.identity" ) if $u && $u->is_identity;
|
|
$errors->add( "user", ".error.changetestaccount" )
|
|
if grep { $user eq $_ } @LJ::TESTACCTS;
|
|
|
|
unless ( $errors->exist ) {
|
|
if ( LJ::login_ip_banned($u) ) {
|
|
$errors->add( "user", "error.ipbanned" );
|
|
}
|
|
elsif (!$authu
|
|
&& !$u->check_password($password) )
|
|
{
|
|
$errors->add( "password", ".error.badoldpassword" );
|
|
LJ::handle_bad_login($u);
|
|
}
|
|
}
|
|
|
|
if ( !$newpass1 ) {
|
|
$errors->add( "newpass1", ".error.blankpassword" );
|
|
}
|
|
elsif ( $newpass1 ne $newpass2 ) {
|
|
$errors->add( "newpass2", ".error.badnewpassword" );
|
|
}
|
|
else {
|
|
my $checkpass = LJ::CreatePage->verify_password(
|
|
password => $newpass1,
|
|
u => $u
|
|
);
|
|
$errors->add( "newpass1", ".error.badcheck", { error => $checkpass } )
|
|
if $checkpass;
|
|
}
|
|
|
|
# don't allow changes if email address is not validated,
|
|
# unless they got the reset email
|
|
$errors->add( "newpass1", ".error.notvalidated" )
|
|
if $u->{status} ne 'A' && !$authu;
|
|
|
|
# now let's change the password
|
|
unless ( $errors->exist ) {
|
|
$u->infohistory_add( 'password', 'changed' );
|
|
$u->log_event( 'password_change', { remote => $remote } );
|
|
$u->set_password( $post->{newpass1} );
|
|
|
|
# if we used an authcode, we'll need to expire it now
|
|
LJ::mark_authaction_used($aa) if $authu;
|
|
|
|
# Kill all sessions, forcing user to relogin
|
|
$u->kill_all_sessions;
|
|
|
|
LJ::send_mail(
|
|
{
|
|
'to' => $u->email_raw,
|
|
'from' => $LJ::ADMIN_EMAIL,
|
|
'fromname' => $LJ::SITENAME,
|
|
'charset' => 'utf-8',
|
|
'subject' => LJ::Lang::ml("$ml_scope.email.subject"),
|
|
'body' => LJ::Lang::ml(
|
|
"$ml_scope.email.body2",
|
|
{
|
|
sitename => $LJ::SITENAME,
|
|
siteroot => $LJ::SITEROOT,
|
|
username => $u->{user},
|
|
}
|
|
),
|
|
}
|
|
);
|
|
|
|
my $success_ml =
|
|
$remote
|
|
? "settings/changepassword.tt.withremote"
|
|
: "settings/changepassword.tt";
|
|
return DW::Controller->render_success(
|
|
$success_ml,
|
|
{
|
|
url => LJ::create_url("/login"),
|
|
}
|
|
);
|
|
|
|
LJ::Hooks::run_hook( 'user_login', $u );
|
|
}
|
|
}
|
|
|
|
my $vars = {
|
|
|
|
needs_validation => !$authu
|
|
&& $remote
|
|
&& !$r->did_post
|
|
&& $remote->{status} ne 'A',
|
|
|
|
authu => $authu,
|
|
remote => $remote,
|
|
|
|
formdata => $post || { user => $remote ? $remote->user : "" },
|
|
errors => $errors,
|
|
};
|
|
return DW::Template->render_template( 'settings/changepassword.tt', $vars );
|
|
}
|
|
1;
|