dreamish/htdocs/login.bml

370 lines
16 KiB
Plaintext

<?_c
# This code was forked from 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.
_c?>
<?_code
{
use strict;
use vars qw($body %GET %POST $title);
$body = "";
my @errors = ();
# ! after username overrides expire to never
# < after username overrides ipfixed to yes
if ( $POST{user} =~ s/([!<]{1,2})$// ) {
$POST{expire} = 'never' if index( $1, "!" ) >= 0;
$POST{bindip} = 'yes' if index( $1, "<" ) >= 0;
}
my $user = LJ::canonical_username($POST{'user'});
my $password = $POST{'password'};
my $remote = LJ::get_remote();
my $cursess = $remote ? $remote->session : undef;
my $form_auth_ok = LJ::did_post() && LJ::check_form_auth();
my $old_remote = $remote;
$title = $remote ?
BML::ml(".loggedin.head2", { 'sitename' => $LJ::SITENAMESHORT} ) :
BML::ml(".login.title", { 'sitename' => $LJ::SITENAMESHORT} );
# we may want to redirect later in failed cases, based on this ugly blob of logic
# return 1 if $want_fail_redirect is handling the request, otherwise 0 and the caller should handle
# @error_codes is a list of error codes that should be added to the redirect URL.
my $want_fail_redirect = sub {
my @error_codes = @_;
if ($POST{ret_fail} && $POST{ret_fail} =~ m!^https?://([\.:\w-]+)!i &&
$LJ::REDIRECT_ALLOWED{$1})
{
BML::redirect($POST{ret_fail} . LJ::eurl(join(',', @error_codes)));
return 1;
}
return 0;
};
# we may want to redirect later in successfull cases, based on this ugly blob of logic
# return 1 if $want_success_redirect is handling the request, otherwise 0 and the caller should handle
my $want_success_redirect = sub {
my $redirect_url;
if ( $POST{returnto} ) {
# this passes in the URI of the page to redirect to on success, eg:
# /manage/profile/index?authas=test or whatever
$redirect_url = $POST{returnto};
if ($redirect_url =~ /^\//) {
$redirect_url = $LJ::SITEROOT . $redirect_url;
}
} elsif ( $POST{ret} && $POST{ret} != 1 ) {
$redirect_url = $POST{ret};
}
if ( $redirect_url && DW::Controller::validate_redirect_url( $redirect_url ) ) {
LJ::Hooks::run_hook( 'login_redirect_extra', $redirect_url );
BML::redirect( $redirect_url );
return 1;
}
return 0;
};
if ($remote && $remote->readonly) {
return if $want_fail_redirect->("database_readonly");
$body = LJ::bad_input("The database is temporarily in read-only mode, so creating new login sessions is temporarily down. Please try again later.");
return;
}
my $login_html = sub {
my $nojs = $GET{'nojs'};
my $getextra = $nojs ? '?nojs=1' : '';
if (@errors) {
return if $want_fail_redirect->(map {$_->[0]} @errors);
$body .= "<div style='clear: both'>\n";
$body .= LJ::error_list(map {$_->[1]} @errors);
$body .= "</div>\n";
} elsif ($remote && LJ::did_post() && $POST{'action:change'}) {
$body .= "<div style='clear: both'>\n";
my $text = $form_auth_ok? $ML{'.login.optionssaved'} : $ML{'error.invalidform'};
$body .= "<?warningbar <b>$text</b> warningbar?>\n";
$body .= "</div>\n";
} elsif (LJ::did_post() && $POST{'action:logout'}) {
my $url = $old_remote ? "$LJ::SITEROOT/logout?user=" . $old_remote->user : "$LJ::SITEROOT/logout";
return BML::redirect($url);
}
$body .= "<table summary='' cellpadding='0' cellspacing='0'><tr><td style='padding-right:20px; vertical-align: top'>";
if ($remote) {
my $base = $remote->journal_base;
$body .= "<?p " . BML::ml(".loggedin.text2",
{ 'username' => LJ::ljuser($remote)} ) . " p?>\n";
if ($remote->is_identity && !$remote->is_validated) {
$body .= "<?warningbar " . BML::ml('.loggedin.openidemail', { aopts1 => "href='$LJ::SITEROOT/changeemail'", aopts2 => "href='$LJ::SITEROOT/register'" }) . " warningbar?>";
}
$body .= "<br />\n";
if ( $GET{'continue_to'} ) {
$body .= "<div style='margin-left: 36px'>";
$body .= "<?h2 <a href=\"" . LJ::ehtml( $GET{'continue_to'} ) . "\">";
$body .= BML::ml( '.loggedin.continueto', { 'continue_url' => LJ::ehtml ( $GET{'continue_to'} ) } ) . "</a> h2?>\n";
$body .= "</div>\n";
$body .= "<br />\n";
}
unless ( $remote->is_identity ) {
$body .= "<div style='float: left; padding: 5px 10px 0px 10px;'>";
$body .= LJ::img( 'post', '' ) . "</div>\n";
$body .= "<div style='margin-left: 36px;'>";
$body .= "<?h2 <a href='$LJ::SITEROOT/update'>";
$body .= "$ML{'.loggedin.suggesthead1'}</a> h2?>\n";
$body .= "$ML{'.loggedin.suggest1'}<br />&nbsp;\n";
$body .= "</div>\n";
}
$body .= "<div style='float: left; padding: 5px 10px 0px 10px'>";
$body .= LJ::img( 'id_user', '', { alt => '' } ) . "</div>\n";
$body .= "<div style='margin-left: 36px'>";
$body .= "<?h2 <a href='$base/read'>";
$body .= "$ML{'.loggedin.suggesthead2'}</a> h2?>\n";
$body .= "$ML{'.loggedin.suggest2'}<br />&nbsp;</div>\n";
$body .= "<br />\n";
$body .= "<form action='login$getextra' method='post' id='login'>\n";
$body .= LJ::form_auth();
$body .= "<?h2 $ML{'.login.changelog'} h2?>";
$body .= "<table summary='' cellpadding='3' class='solid-neutral' style='width: 300px; border: 1px solid #aaa'>\n";
$body .= "<tr><td colspan='2' style='white-space: nowrap;'>";
# expiration
my $curexp = $cursess ? $cursess->exptype : "short";
$body .= "<tr valign='top'><td align='right'>";
$body .= "</td><td style='padding-bottom: 5px'>";
$body .= LJ::html_check(
{
'type' => 'check',
'name' => 'expire',
'id' => 'expire',
'value' => 'never',
'selected' => ($remote && $curexp eq 'long'),
'style' => 'margin-left: 0px; margin-bottom: 0px;',
});
$body .= "<label for='expire'>$ML{'.login.remember'}</label><br />\n";
$body .= "<span style='color: #666; font-size: 0.8em'>$ML{'.login.autologin'}</span></td></tr>\n";
$body .= "</tr>\n";
$body .= "<tr valign='top'><td>";
$body .= "</td><td style='padding-bottom: 10px'>";
my $curbind = $cursess && $cursess->ipfixed ? "yes" : "no";
$body .= LJ::html_check(
{
'type' => 'check',
'name' => 'bindip',
'id' => 'bindip',
'value' => 'yes',
'selected' => $curbind eq 'yes',
'style' => 'margin-left: 0px; margin-bottom: 0px;',
});
$body .= "<label for='bindip'>$ML{'.login.bindcookie'}</label>";
if (defined $LJ::HELPURL{'loginoptions'}) {
$body .= "<br /><font size='1'><a href='$LJ::HELPURL{'loginoptions'}'>$ML{'.login.bindcookie.learnmore'}</a></font>";
}
$body .= "</td></tr>\n";
$body .= "</tr>";
$body .= "<tr><td></td><td>";
$body .= "<input name='action:change' type='submit' value='$ML{'.login.btn.save'}' />&nbsp;&nbsp;";
$body .= "<input type='submit' name='action:logout' value='$ML{'.logout.btn1'}' />";
$body .= "</td></tr></table>\n";
$body .= "</form>\n";
} else {
$body .= "<div class='columns-2-r300 pkg'>\n";
$body .= "<div class='columns-2-left'>\n";
$body .= LJ::Widget::Login->render( get_ret => $GET{ret}, post_ret => $POST{ret},
returnto => $POST{returnto}, nojs => $nojs, user => $user );
$body .= "<div class='login-create-account'>\n";
$body .= "<hr class='hr' />\n";
$body .= "<h4>$ML{'.createaccount.header'}</h4>\n";
$body .= "<form action='/create' method='get'><input type='submit' value='$ML{'.createaccount.button'}' class='submit' /></form>\n";
$body .= "<ul>\n";
$body .= "<li>$ML{'.createaccount.whylogin.benefit1'}</li>";
$body .= "<li>$ML{'.createaccount.whylogin.benefit2'}</li>";
$body .= "<li>$ML{'.createaccount.whylogin.benefit3'}</li>";
$body .= "<li>$ML{'.createaccount.whylogin.benefit4'}</li>";
$body .= "<li>$ML{'.createaccount.whylogin.benefit5'}</li>";
$body .= "</ul>\n";
$body .= "</div><!-- end .login-create-account -->\n";
$body .= "</div><!-- end .columns-2-left -->\n";
}
$body .= "</td></tr></table>";
return $body;
};
my $logout_remote = sub {
$remote->kill_session if $remote;
my $r = DW::Request->get;
foreach ( qw(BMLschemepref) ) {
$r->delete_cookie( name => $_ ) if $r->cookie( $_ );
}
$remote = undef;
$cursess = undef;
LJ::set_remote( undef );
LJ::Hooks::run_hooks( "post_logout" );
};
if (LJ::did_post()) {
my $do_change = $POST{'action:change'};
my $do_login = $POST{'action:login'};
my $do_logout = $POST{'action:logout'};
# default action is to login:
if (! $do_change && ! $do_logout) {
$do_login = 1;
}
# if they're already logged in, change opts
if ($do_login && $remote) {
$do_login = 0;
$do_change = 1;
}
# can only change if logged in
if ($do_change && not defined $remote) {
$do_logout = 1;
$do_change = 0;
}
if ($do_logout) {
$logout_remote->();
DW::Stats::increment( 'dw.action.session.logout' );
$title = BML::ml(".login.title", { 'sitename' => $LJ::SITENAMESHORT} );
}
if ($do_change && $form_auth_ok) {
my $bindip;
$bindip = BML::get_remote_ip()
if $POST{'bindip'} eq "yes";
DW::Stats::increment( 'dw.action.session.update', 1, [ 'bindip:' . $bindip ? 'yes' : 'no',
'exptype:' . $POST{expire} eq 'never' ? 'long' : 'short' ] );
$cursess->set_ipfixed($bindip) or die "failed to set ipfixed";
$cursess->set_exptype($POST{expire} eq 'never' ? 'long' : 'short') or die "failed to set exptype";
$cursess->update_master_cookie;
}
if ($do_login)
{
my $u = LJ::load_user($user);
if (! $u) {
my $euser = LJ::eurl($user);
push @errors, [ unknown_user => BML::ml('.error.notuser', { 'aopts' => "href='$LJ::SITEROOT/create?user=$euser'" })]
unless $u;
} else {
push @errors, [ purged_user => "$ML{'error.purged.text'}" ] if $u->is_expunged;
push @errors, [ memorial_user => $ML{'error.memorial.text'} ] if $u->is_memorial;
push @errors, [ community_disabled_login => "$ML{'error.nocommlogin'}" ]
if $u->is_community && ! LJ::is_enabled('community-logins');
}
if ( $u && $u->is_readonly ) {
return if $want_fail_redirect->("database_readonly");
$body = LJ::bad_input("The database is temporarily in read-only mode, so creating new login sessions is temporarily down. Please try again later.");
DW::Stats::increment( 'dw.action.session.login_failed', 1, [ 'reason:database_readonly' ] );
return;
}
my ($banned, $ok);
$banned = $ok = 0;
$ok = LJ::auth_okay($u, $POST{password}, is_ip_banned => \$banned);
if ($banned) {
return if $want_fail_redirect->("banned_ip");
$body = LJ::bad_input("Your IP address is temporarily banned for exceeding the login failure rate.");
DW::Stats::increment( 'dw.action.session.login_failed', 1, [ 'reason:banned_ip' ] );
return;
}
if ($u && ! $ok) {
push @errors, [ bad_password => BML::ml( 'error.badpassword2', { aopts => "href='$LJ::SITEROOT/lostinfo'" } ) ];
}
push @errors, [ account_locked => 'This account is locked and cannot be logged in to at this time.' ]
if $u && $u->is_locked;
if (@errors) {
DW::Stats::increment( 'dw.action.session.login_failed', 1, [ "reason:$_->[0]" ] )
foreach @errors; # Many errors, increment a failure for each reason.
$login_html->();
return;
}
# at this point, $u is known good
$u->preload_props( "schemepref" );
my $exptype = ($POST{'expire'} eq "never" || $POST{'remember_me'}) ? "long" : "short";
my $bindip = ($POST{'bindip'} eq "yes") ? BML::get_remote_ip() : "";
$u->make_login_session($exptype, $bindip);
LJ::Hooks::run_hook('user_login', $u);
$cursess = $u->session;
DW::Stats::increment( 'dw.action.session.login_ok', 1, [ 'bindip:' . $bindip ? 'yes' : 'no',
"exptype:$exptype" ] );
return if $want_success_redirect->();
my $referer = BML::get_client_header('Referer');
if ( $POST{'ref'} =~ /\Q$LJ::DOMAIN\E/ && # page on our site
! LJ::check_referer( '/logout.bml', $POST{'ref'} ) && # but not the logout page
$POST{'ref'} !~ /[\n\r]/ ) { # and no newline spoofing
return BML::redirect("$POST{'ref'}");
} elsif ( $GET{'ret'} == 1 && LJ::check_referer() ) {
my $uniq = BML::get_request()->notes->{uniq};
LJ::MemCache::set( "loginout:$uniq", 1, time() + 15 ) if $uniq;
return BML::redirect( $referer || "$LJ::SITEROOT/" );
}
LJ::set_remote($u);
$remote = $u;
$title = BML::ml(".loggedin.head2", { 'sitename' => $LJ::SITENAMESHORT} );
}
}
return if $want_success_redirect->();
$login_html->();
return;
}
_code?><?_info
nocache=>1
_info?><?page
head<=
<=head
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?>