This commit is contained in:
2019-02-06 00:49:12 +03:00
commit 8dbb1bb605
4796 changed files with 506072 additions and 0 deletions

124
local/cgi-bin/LJ/Auth.pm Normal file
View File

@@ -0,0 +1,124 @@
# This is the LiveJournal Authentication module.
# It contains useful authentication methods.
package LJ::Auth;
use strict;
use Digest::HMAC_SHA1 qw(hmac_sha1_hex);
use Digest::SHA1 qw(sha1_hex);
use Carp qw (croak);
# Generate an auth token for AJAX requests to use.
# Arguments: ($remote, $action, %postvars)
# $remote: remote user object
# $uri: what uri this is for
# %postvars: the expected post variables
# Returns: Auth token good for the current hour
sub ajax_auth_token {
my ($class, $remote, $uri, %postvars) = @_;
$remote = LJ::want_user($remote) || LJ::get_remote();
croak "No URI specified" unless $uri;
my ($stime, $secret) = LJ::get_secret();
my $postvars = join('&', map { $postvars{$_} } sort keys %postvars);
my $remote_session_id = $remote && $remote->session ? $remote->session->id : LJ::UniqCookie->current_uniq;
my $remote_userid = $remote ? $remote->id : 0;
my $chalbare = qq {ajax:$stime:$remote_userid:$remote_session_id:$uri:$postvars};
my $chalsig = sha1_hex($chalbare, $secret);
return qq{$chalbare:$chalsig};
}
# Checks an auth token sent by an ajax request
# Arguments: $remote, $uri, %POST variables
# Returns: bool whether or not key is good
sub check_ajax_auth_token {
my ($class, $remote, $uri, %postvars) = @_;
$remote = LJ::want_user($remote) || LJ::get_remote();
# get auth token out of post vars
my $auth_token = delete $postvars{auth_token} or return 0;
# recompute post vars
my $postvars = join('&', map { $postvars{$_} } sort keys %postvars);
# get vars out of token string
my ($c_ver, $stime, $remoteid, $sessid, $chal_uri, $chal_postvars, $chalsig) = split(':', $auth_token);
# get secret based on $stime
my $secret = LJ::get_secret($stime);
# no time?
return 0 unless $stime && $secret;
# right version?
return 0 unless $c_ver eq 'ajax';
# in logged-out case $remoteid is 0 and $sessid is uniq_cookie
my $req_remoteid = $remoteid > 0 ? $remote->id : 0;
my $req_sessid = $remoteid > 0 ? $remote->session->id : LJ::UniqCookie->current_uniq;
# do signitures match?
my $chalbare = qq {$c_ver:$stime:$remoteid:$sessid:$chal_uri:$chal_postvars};
my $realsig = sha1_hex($chalbare, $secret);
return 0 unless $realsig eq $chalsig;
return 0 unless
$remoteid == $req_remoteid && # remote id matches or logged-out 0=0
$sessid == $req_sessid && # remote sessid or logged-out uniq cookie match
$uri eq $chal_uri && # uri matches
$postvars eq $chal_postvars; # post vars to uri
return 1;
}
# this is similar to the above methods but doesn't require a session or remote
sub sessionless_auth_token {
my ($class, $uri, %reqvars) = @_;
croak "No URI specified" unless $uri;
my ($stime, $secret) = LJ::get_secret();
my $reqvars = join('&', map { $reqvars{$_} } sort keys %reqvars);
my $chalbare = qq {sessionless:$stime:$uri:$reqvars};
my $chalsig = sha1_hex($chalbare, $secret);
return qq{$chalbare:$chalsig};
}
sub check_sessionless_auth_token {
my ($class, $uri, %reqvars) = @_;
# get auth token out of post vars
my $auth_token = delete $reqvars{auth_token} or return 0;
# recompute post vars
my $reqvars = join('&', map { $reqvars{$_} } sort keys %reqvars);
# get vars out of token string
my ($c_ver, $stime, $chal_uri, $chal_reqvars, $chalsig) = split(':', $auth_token);
# get secret based on $stime
my $secret = LJ::get_secret($stime);
# no time?
return 0 unless $stime && $secret;
# right version?
return 0 unless $c_ver eq 'sessionless';
# do signitures match?
my $chalbare = qq {$c_ver:$stime:$chal_uri:$chal_reqvars};
my $realsig = sha1_hex($chalbare, $secret);
return 0 unless $realsig eq $chalsig;
# do other vars match?
return 0 unless $uri eq $chal_uri && $reqvars eq $chal_reqvars;
return 1;
}
1;

View File

@@ -0,0 +1,21 @@
package LJ::CSS::Cleaner;
use strict;
use warnings;
no warnings 'redefine';
use base 'CSS::Cleaner';
sub new {
my $class = shift;
return $class->SUPER::new( @_,
pre_hook => sub {
my $rref = shift;
$$rref =~ s/comment-bake-cookie/CLEANED/g;
return;
},
);
}
1;

View File

@@ -0,0 +1,47 @@
# this is a small wrapper around Unicode::MapUTF8, just so we can lazily-load it easier
# with Class::Autouse, and so we have a central place to init its charset aliases.
# and in the future if we switch transcoding packages, we can just do it here.
package LJ::ConvUTF8;
use strict;
use warnings;
use Unicode::MapUTF8 ();
BEGIN {
# declare some charset aliases
# we need this at least for cases when the only name supported
# by MapUTF8.pm isn't recognized by browsers
# note: newer versions of MapUTF8 know these
{
my %alias = ( 'windows-1251' => 'cp1251',
'windows-1252' => 'cp1252',
'windows-1253' => 'cp1253', );
foreach (keys %alias) {
next if Unicode::MapUTF8::utf8_supported_charset($_);
Unicode::MapUTF8::utf8_charset_alias($_, $alias{$_});
}
}
}
sub load {
1;
}
sub supported_charset {
my ($class, $charset) = @_;
return Unicode::MapUTF8::utf8_supported_charset($charset);
}
sub from_utf8 {
my ($class, $from_enc, $str) = @_;
return Unicode::MapUTF8::from_utf8({ -string=> $str, -charset => $from_enc });
}
sub to_utf8 {
my ($class, $to_enc, $str) = @_;
return Unicode::MapUTF8::to_utf8({ -string=> $str, -charset => $to_enc });
}
1;

View File

@@ -0,0 +1,466 @@
#!/usr/bin/perl
package LJ::EmbedModule;
use strict;
use Carp qw (croak);
use Class::Autouse qw (
LJ::Auth
HTML::TokeParser
);
# states for a finite-state machine we use in parse()
use constant {
# reading plain html without <object>, <embed> or <lj-embed>
REGULAR => 1,
# inside <object> or <embed> tag
IMPLICIT => 2,
# inside explicit <lj-embed> tag
EXPLICIT => 3,
# maximum embed width and height
MAX_WIDTH => 800,
MAX_HEIGHT => 800,
};
# can optionally pass in an id of a module to change its contents
# returns module id
sub save_module {
my ($class, %opts) = @_;
my $contents = $opts{contents} || '';
my $id = $opts{id};
my $journal = $opts{journal}
or croak "No journal passed to LJ::EmbedModule::save_module";
my $preview = $opts{preview};
# are we creating a new entry?
unless (defined $id) {
$id = LJ::alloc_user_counter($journal, 'D')
or die "Could not allocate embed module ID";
}
my $cmptext = 'C-' . LJ::text_compress($contents);
## embeds for preview are stored in a special table,
## where new items overwrites old ones
my $table_name = ($preview) ? 'embedcontent_preview' : 'embedcontent';
$journal->do("REPLACE INTO $table_name (userid, moduleid, content) VALUES ".
"(?, ?, ?)", undef, $journal->{'userid'}, $id, $cmptext);
die $journal->errstr if $journal->err;
# save in memcache
my $memkey = $class->memkey($journal->{'userid'}, $id, $preview);
LJ::MemCache::set($memkey, $contents);
return $id;
}
# changes <div class="ljembed"... tags from the RTE into proper lj-embed tags
sub transform_rte_post {
my ($class, $txt) = @_;
return $txt unless $txt && $txt =~ /ljembed/i;
# ghetto... shouldn't use regexes to parse this
$txt =~ s/<div\s*class="ljembed"\s*(embedid="(\d+)")?\s*>(((?!<\/div>).)*)<\/div>/<lj-embed id="$2">$3<\/lj-embed>/ig;
$txt =~ s/<div\s*(embedid="(\d+)")?\s*class="ljembed"\s*>(((?!<\/div>).)*)<\/div>/<lj-embed id="$2">$3<\/lj-embed>/ig;
return $txt;
}
# takes a scalarref to entry text and expands lj-embed tags
# REPLACE
sub expand_entry {
my ($class, $journal, $entryref, %opts) = @_;
$$entryref =~ s/(<lj\-embed[^>]+\/>)/$class->_expand_tag($journal, $1, $opts{edit}, %opts)/ge;
}
sub _expand_tag {
my $class = shift;
my $journal = shift;
my $tag = shift;
my $edit = shift;
my %opts = @_;
my %attrs = $tag =~ /(\w+)="?(\-?\d+)"?/g;
return '[invalid lj-embed, id is missing]' unless $attrs{id};
if ($edit) {
return '<lj-embed ' . join(' ', map {"$_=\"$attrs{$_}\""} keys %attrs) . ">\n" .
$class->module_content(moduleid => $attrs{id}, journalid => $journal->id) .
"\n<\/lj-embed>";
}
elsif ($opts{'content_only'}) {
return $class->module_content(moduleid => $attrs{id}, journalid => $journal->{'userid'});
}
else {
@opts{qw /width height/} = @attrs{qw/width height/};
return $class->module_iframe_tag($journal, $attrs{id}, %opts)
}
};
# take a scalarref to a post, parses any lj-embed tags, saves the contents
# of the tags and replaces them with a module tag with the id.
# REPLACE
sub parse_module_embed {
my ($class, $journal, $postref, %opts) = @_;
return unless $postref && $$postref;
return if LJ::conf_test($LJ::DISABLED{embed_module});
# fast track out if we don't have to expand anything
return unless $$postref =~ /lj\-embed|embed|object/i;
# do we want to replace with the lj-embed tags or iframes?
my $expand = $opts{expand};
# if this is editing mode, then we want to expand embed tags for editing
my $edit = $opts{edit};
# previews are a special case (don't want to permanantly save to db)
my $preview = $opts{preview};
# deal with old-fashion calls
if (($edit || $expand) && ! $preview) {
return $class->expand_entry($journal, $postref, %opts);
}
# ok, we can safely parse post text
# machine state
my $state = REGULAR;
my $p = HTML::TokeParser->new($postref);
my $newtxt = '';
my %embed_attrs = (); # ($eid, $ewidth, $eheight);
my $embed = '';
my @stack = ();
my $next_preview_id = 1;
while (my $token = $p->get_token) {
my ($type, $tag, $attr) = @$token;
$tag = lc $tag;
my $newstate = undef;
my $reconstructed = $class->reconstruct($token);
if ($state == REGULAR) {
if ($tag eq 'lj-embed' && $type eq 'S' && ! $attr->{'/'}) {
# <lj-embed ...>, not self-closed
# switch to EXPLICIT state
$newstate = EXPLICIT;
# save embed id, width and height if they do exist in attributes
$embed_attrs{id} = $attr->{id} if $attr->{id};
$embed_attrs{width} = ($attr->{width} > MAX_WIDTH ? MAX_WIDTH : $attr->{width}) if $attr->{width};
$embed_attrs{height} = ($attr->{height} > MAX_HEIGHT ? MAX_HEIGHT : $attr->{height}) if $attr->{height};
} elsif (($tag eq 'object' || $tag eq 'embed') && $type eq 'S') {
# <object> or <embed>
# switch to IMPLICIT state unless it is a self-closed tag
unless ($attr->{'/'}) {
$newstate = IMPLICIT;
# tag balance
push @stack, $tag;
}
# append the tag contents to new embed buffer, so we can convert in to lj-embed later
$embed .= $reconstructed;
} else {
# otherwise stay in REGULAR
$newtxt .= $reconstructed;
}
} elsif ($state == IMPLICIT) {
if ($tag eq 'object' || $tag eq 'embed') {
if ($type eq 'E') {
# </object> or </embed>
# update tag balance, but only if we have a valid balance up to this moment
pop @stack if $stack[-1] eq $tag;
# switch to REGULAR if tags are balanced (stack is empty), stay in IMPLICIT otherwise
$newstate = REGULAR unless @stack;
} elsif ($type eq 'S') {
# <object> or <embed>
# mind the tag balance, do not update it in case of a self-closed tag
push @stack, $tag unless $attr->{'/'};
}
}
# append to embed buffer
$embed .= $reconstructed;
} elsif ($state == EXPLICIT) {
if ($tag eq 'lj-embed' && $type eq 'E') {
# </lj-embed> - that's the end of explicit embed block, switch to REGULAR
$newstate = REGULAR;
} else {
# continue appending contents to embed buffer
$embed .= $reconstructed;
}
} else {
# let's be paranoid
die "Invalid state: '$state'";
}
# we decided to switch back to REGULAR and have something in embed buffer
# so let's save buffer as an embed module and start all over again
if (defined($newstate) && $newstate == REGULAR && $embed) {
$embed_attrs{id} = $class->save_module(
id => ($preview ? $next_preview_id++ : $embed_attrs{id}),
contents => $embed,
journal => $journal,
preview => $preview,
);
$newtxt .= "<lj-embed " . join(' ', map { exists $embed_attrs{$_} ? "$_=\"$embed_attrs{$_}\"" : () } qw / id width height /) . "/>";
$embed = '';
%embed_attrs = ();
}
# switch the state if we have a new one
$state = $newstate if defined $newstate;
}
# update passed text
$$postref = $newtxt;
}
sub module_iframe_tag {
my ($class, $u, $moduleid, %opts) = @_;
return '' if $LJ::DISABLED{embed_module};
my $journalid = $u->{'userid'};
$moduleid += 0;
# parse the contents of the module and try to come up with a guess at the width and height of the content
my $content = $class->module_content(moduleid => $moduleid, journalid => $journalid);
my $preview = $opts{preview};
my $width = 0;
my $height = 0;
my $p = HTML::TokeParser->new(\$content);
my $embedcodes;
# if the content only contains a whitelisted embedded video
# then we can skip the placeholders (in some cases)
my $no_whitelist = 0;
my $found_embed = 0;
# we don't need to estimate the dimensions if they are provided in tag attributes
unless ($opts{width} && $opts{height}) {
while (my $token = $p->get_token) {
my $type = $token->[0];
my $tag = $token->[1] ? lc $token->[1] : '';
my $attr = $token->[2]; # hashref
if ($type eq "S") {
my ($elewidth, $eleheight);
if ($attr->{width}) {
$elewidth = $attr->{width}+0;
$width = $elewidth if $elewidth > $width;
}
if ($attr->{height}) {
$eleheight = $attr->{height}+0;
$height = $eleheight if $eleheight > $height;
}
my $flashvars = $attr->{flashvars};
if ($tag eq 'object' || $tag eq 'embed') {
my $src;
next unless $src = $attr->{src};
# we have an object/embed tag with src, make a fake lj-template object
my @tags = (
['S', 'lj-template', {
name => 'video',
(defined $elewidth ? ( width => $width ) : ()),
(defined $eleheight ? ( height => $height ) : ()),
(defined $flashvars ? ( flashvars => $flashvars ) : ()),
}],
[ 'T', $src, {}],
['E', 'lj-template', {}],
);
$embedcodes = LJ::run_hook('expand_template_video', \@tags);
$found_embed = 1 if $embedcodes;
$found_embed &&= $embedcodes !~ /Invalid video/i;
$no_whitelist = !$found_embed;
} elsif ($tag ne 'param') {
$no_whitelist = 1;
}
}
}
# add padding
$width += 50 if $width;
$height += 50 if $height;
}
# use explicit values if we have them
$width = $opts{width} if $opts{width};
$height = $opts{height} if $opts{height};
$width ||= 240;
$height ||= 200;
# some dimension min/maxing
$width = 50 if $width < 50;
$width = MAX_WIDTH if $width > MAX_WIDTH;
$height = 50 if $height < 50;
$height = MAX_HEIGHT if $height > MAX_HEIGHT;
# safari caches state of sub-resources aggressively, so give
# each iframe a unique 'name' attribute
my $id = qq(name="embed_${journalid}_$moduleid");
my $auth_token = LJ::eurl(LJ::Auth->sessionless_auth_token('embedcontent', moduleid => $moduleid, journalid => $journalid, preview => $preview,));
my $iframe_url = qq {http://$LJ::EMBED_MODULE_DOMAIN/tools/embedcontent.bml?journalid=$journalid&moduleid=$moduleid&preview=$preview&auth_token=$auth_token};
my $iframe_tag = qq {<iframe src="$iframe_url" } .
qq{width="$width" height="$height" allowtransparency="true" frameborder="0" class="lj_embedcontent" $id></iframe>};
my $remote = LJ::get_remote();
my $do_placeholder;
if ($remote) {
return $iframe_tag if $opts{edit};
# show placeholder instead of iframe?
LJ::load_user_props($remote, "opt_embedplaceholders");
my $placeholder_prop = $remote->prop('opt_embedplaceholders');
$do_placeholder = $placeholder_prop && $placeholder_prop ne 'N';
# if placeholder_prop is not set, then show placeholder on a friends
# page view UNLESS the embedded content is only one embed/object
# tag and it's whitelisted video.
my $r = eval { Apache->request };
my $view = $r ? $r->notes("view") : '';
if (! $placeholder_prop && $view eq 'friends') {
# show placeholder if this is not whitelisted video
$do_placeholder = 1 if $no_whitelist;
}
}
else {
$do_placeholder = $BML::COOKIE{'flashpref'};
$do_placeholder = 0 unless $do_placeholder eq "0" || $do_placeholder eq "1";
}
return $iframe_tag unless $do_placeholder;
my $tmpcontent = $class->module_content(
journalid => $journalid,
moduleid => $moduleid,
0
);
$tmpcontent =~ s/.+param\s+name\s*?=\s*?"?movie"?\s*value\s*?=\s*?"?//sg; #"
$tmpcontent =~ s/("|\s).+//sg; #"
$tmpcontent = LJ::ehtml($tmpcontent);
# placeholder
return LJ::placeholder_link(
placeholder_html => $iframe_tag,
placeholder_link => $iframe_url,
width => $width,
height => $height,
img => "$LJ::IMGPREFIX/videoplaceholder.png",
img_title => $tmpcontent,
);
}
sub module_content {
my ($class, %opts) = @_;
my $moduleid = $opts{moduleid};
croak "No moduleid" unless defined $moduleid;
$moduleid += 0;
my $journalid = $opts{journalid}+0 or croak "No journalid";
my $journal = LJ::load_userid($journalid) or die "Invalid userid $journalid";
my $preview = $opts{preview};
# try memcache
my $memkey = $class->memkey($journalid, $moduleid, $preview);
my $content = LJ::MemCache::get($memkey);
my ($dbload, $dbid); # module id from the database
unless (defined $content) {
my $table_name = ($preview) ? 'embedcontent_preview' : 'embedcontent';
($content, $dbid) = $journal->selectrow_array("SELECT content, moduleid FROM $table_name WHERE " .
"moduleid=? AND userid=?",
undef, $moduleid, $journalid);
die $journal->errstr if $journal->err;
$dbload = 1;
}
$content ||= '';
LJ::text_uncompress(\$content) if $content =~ s/^C-//;
# clean js out of content
unless ($LJ::DISABLED{'embedmodule-cleancontent'}) {
LJ::CleanHTML::clean(\$content, {
addbreaks => 0,
tablecheck => 0,
mode => 'allow',
allow => [qw(object embed)],
deny => [qw(script iframe)],
remove => [qw(script iframe)],
ljcut_disable => 1,
cleancss => 0,
extractlinks => 0,
noautolinks => 1,
extractimages => 0,
noexpandembedded => 1,
transform_embed_nocheck => 1,
});
}
# if we got stuff out of database
if ($dbload) {
# save in memcache
LJ::MemCache::set($memkey, $content);
# if we didn't get a moduleid out of the database then this entry is not valid
return defined $dbid ? $content : "[Invalid lj-embed id $moduleid]";
}
# get rid of whitespace around the content
return LJ::trim($content) || '';
}
sub memkey {
my ($class, $journalid, $moduleid, $preview) = @_;
my $pfx = $preview ? 'embedcontpreview' : 'embedcont';
return [$journalid, "$pfx:$journalid:$moduleid"];
}
# create a tag string from HTML::TokeParser token
sub reconstruct {
my $class = shift;
my $token = shift;
my ($type, $tag, $attr, $attord) = @$token;
if ($type eq 'S') {
my $txt = "<$tag";
my $selfclose;
# preserve order of attributes. the original order is
# in element 4 of $token
foreach my $name (@$attord) {
if ($name eq '/') {
$selfclose = 1;
next;
}
# FIXME: ultra ghetto.
$attr->{$name} = LJ::no_utf8_flag($attr->{$name});
$txt .= " $name=\"" . LJ::ehtml($attr->{$name}) . "\"";
}
$txt .= $selfclose ? " />" : ">";
} elsif ($type eq 'E') {
return "</$tag>";
} else { # C, T, D or PI
return $tag;
}
}
1;

1138
local/cgi-bin/LJ/Entry.pm Normal file

File diff suppressed because it is too large Load Diff

184
local/cgi-bin/LJ/OpenID.pm Executable file
View File

@@ -0,0 +1,184 @@
package LJ::OpenID;
use strict;
use Digest::SHA1 qw(sha1 sha1_hex);
use LWPx::ParanoidAgent;
BEGIN {
$LJ::OPTMOD_OPENID_CONSUMER = $LJ::OPENID_CONSUMER ? eval "use Net::OpenID::Consumer; 1;" : 0;
$LJ::OPTMOD_OPENID_SERVER = $LJ::OPENID_SERVER ? eval "use Net::OpenID::Server; 1;" : 0;
}
# returns boolean whether consumer support is enabled and available
sub consumer_enabled {
return 0 unless $LJ::OPENID_CONSUMER;
return $LJ::OPTMOD_OPENID_CONSUMER || eval "use Net::OpenID::Consumer; 1;";
}
# returns boolean whether consumer support is enabled and available
sub server_enabled {
return 0 unless $LJ::OPENID_SERVER;
return $LJ::OPTMOD_OPENID_CONSUMER || eval "use Net::OpenID::Server; 1;";
}
sub server {
my ($get, $post) = @_;
return Net::OpenID::Server->new(
compat => $LJ::OPENID_COMPAT,
get_args => $get || {},
post_args => $post || {},
get_user => \&LJ::get_remote,
is_identity => sub {
my ($u, $ident) = @_;
return LJ::OpenID::is_identity($u, $ident, $get);
},
is_trusted => \&LJ::OpenID::is_trusted,
setup_url => "$LJ::SITEROOT/openid/approve.bml",
server_secret => \&LJ::OpenID::server_secret,
secret_gen_interval => 3600,
secret_expire_age => 86400 * 14,
);
}
# Returns a Consumer object
# When planning to verify identity, needs GET
# arguments passed in
sub consumer {
my $get_args = shift || {};
my $ua;
unless ($LJ::IS_DEV_SERVER) {
$ua = LWPx::ParanoidAgent->new(
timeout => 10,
max_size => 1024*300,
);
}
my $csr = Net::OpenID::Consumer->new(
ua => $ua,
args => $get_args,
cache => eval { LJ::MemCache::get_memcache() },
consumer_secret => \&LJ::OpenID::consumer_secret,
debug => $LJ::IS_DEV_SERVER || 0,
required_root => $LJ::SITEROOT,
);
return $csr;
}
sub consumer_secret {
my $time = shift;
return server_secret($time - $time % 3600);
}
sub server_secret {
my $time = shift;
my ($t2, $secret) = LJ::get_secret($time);
die "ASSERT: didn't get t2 (t1=$time)" unless $t2;
die "ASSERT: didn't get secret (t2=$t2)" unless $secret;
die "ASSERT: time($time) != t2($t2)\n" unless $t2 == $time;
return $secret;
}
sub is_trusted {
my ($u, $trust_root, $is_identity) = @_;
return 0 unless $u;
# we always look up $is_trusted, even if $is_identity is false, to avoid timing attacks
my $dbh = LJ::get_db_writer();
my ($endpointid, $duration) = $dbh->selectrow_array("SELECT t.endpoint_id, t.duration ".
"FROM openid_trust t, openid_endpoint e ".
"WHERE t.userid=? AND t.endpoint_id=e.endpoint_id AND e.url=?",
undef, $u->{userid}, $trust_root);
return 0 unless $endpointid;
return 1;
}
sub is_identity {
my ($u, $ident, $get) = @_;
return 0 unless $u && $u->{journaltype} eq "P";
my $user = $u->{user};
return 1 if
$ident eq "$LJ::SITEROOT/users/$user/" ||
$ident eq "$LJ::SITEROOT/~$user/" ||
$ident eq "http://$user.$LJ::USER_DOMAIN/";
return 0;
}
sub getmake_endpointid {
my $site = shift;
my $dbh = LJ::get_db_writer()
or return undef;
my $rv = $dbh->do("INSERT IGNORE INTO openid_endpoint (url) VALUES (?)", undef, $site);
my $end_id;
if ($rv > 0) {
$end_id = $dbh->{'mysql_insertid'};
} else {
$end_id = $dbh->selectrow_array("SELECT endpoint_id FROM openid_endpoint WHERE url=?",
undef, $site);
}
return $end_id;
}
sub add_trust {
my ($u, $site) = @_;
my $end_id = LJ::OpenID::getmake_endpointid($site)
or return 0;
my $dbh = LJ::get_db_writer()
or return undef;
my $rv = $dbh->do("REPLACE INTO openid_trust (userid, endpoint_id, duration, trust_time) ".
"VALUES (?,?,?,UNIX_TIMESTAMP())", undef, $u->{userid}, $end_id, "always");
return $rv;
}
# From Digest::HMAC
sub hmac_sha1_hex {
unpack("H*", &hmac_sha1);
}
sub hmac_sha1 {
hmac($_[0], $_[1], \&sha1, 64);
}
sub hmac {
my($data, $key, $hash_func, $block_size) = @_;
$block_size ||= 64;
$key = &$hash_func($key) if length($key) > $block_size;
my $k_ipad = $key ^ (chr(0x36) x $block_size);
my $k_opad = $key ^ (chr(0x5c) x $block_size);
&$hash_func($k_opad, &$hash_func($k_ipad, $data));
}
# Returns 1 if destination identity server
# is blocked
sub blocked_hosts {
my $csr = shift;
return do { my $dummy = 0; \$dummy; } if $LJ::IS_DEV_SERVER;
my $tried_local_id = 0;
$csr->ua->blocked_hosts(
sub {
my $dest = shift;
if ($dest =~ /((^|\.)\Q$LJ::DOMAIN\E$|demotivation\.me|anonymity\.com)/i) {
$tried_local_id = 1;
return 1;
}
return 0;
});
return \$tried_local_id;
}
1;

2739
local/cgi-bin/LJ/S2.pm Executable file

File diff suppressed because it is too large Load Diff

232
local/cgi-bin/LJ/S2/DayPage.pm Executable file
View File

@@ -0,0 +1,232 @@
#!/usr/bin/perl
#
use strict;
package LJ::S2;
sub DayPage
{
my ($u, $remote, $opts) = @_;
my $p = Page($u, $opts);
$p->{'_type'} = "DayPage";
$p->{'view'} = "day";
$p->{'entries'} = [];
my $user = $u->{'user'};
my $journalbase = LJ::journal_base($user, $opts->{'vhost'});
if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") {
$opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) .
"/calendar" . $opts->{'pathextra'};
return 1;
}
if ($u->{'opt_blockrobots'}) {
$p->{'head_content'} .= LJ::robot_meta_tags();
}
my $get = $opts->{'getargs'};
my $month = $get->{'month'};
my $day = $get->{'day'};
my $year = $get->{'year'};
my @errors = ();
if ($opts->{'pathextra'} =~ m!^/(\d\d\d\d)/(\d\d)/(\d\d)\b!) {
($month, $day, $year) = ($2, $3, $1);
}
$opts->{'errors'} = [];
if ($year !~ /^\d+$/) { push @{$opts->{'errors'}}, "Corrupt or non-existant year."; }
if ($month !~ /^\d+$/) { push @{$opts->{'errors'}}, "Corrupt or non-existant month."; }
if ($day !~ /^\d+$/) { push @{$opts->{'errors'}}, "Corrupt or non-existant day."; }
if ($month < 1 || $month > 12 || int($month) != $month) { push @{$opts->{'errors'}}, "Invalid month."; }
if ($year < 1970 || $year > 2038 || int($year) != $year) { push @{$opts->{'errors'}}, "Invalid year: $year"; }
if ($day < 1 || $day > 31 || int($day) != $day) { push @{$opts->{'errors'}}, "Invalid day."; }
if (scalar(@{$opts->{'errors'}})==0 && $day > LJ::days_in_month($month, $year)) { push @{$opts->{'errors'}}, "That month doesn't have that many days."; }
return if @{$opts->{'errors'}};
$p->{'date'} = Date($year, $month, $day);
# mysqldate_to_time(): ^(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d)(?::(\d\d))?)?$/ -> unixtime, with check.
# why not using get_recent_items() ?
#use Time::HiRes qw(gettimeofday tv_interval);
#my $t0 = [gettimeofday];
#my @elapsed;
#push @elapsed, (tv_interval ($t0));
#print STDERR "@elapsed \n";
my $secwhere = "AND security='public'";
my $viewall = 0;
my $viewsome = 0; # see public posts from suspended users
if ($remote) {
# do they have the viewall priv?
if ($get->{'viewall'} && LJ::check_priv($remote, "canview")) {
LJ::statushistory_add($u->{'userid'}, $remote->{'userid'},
"viewall", "day: $user, statusvis: $u->{'statusvis'}");
$viewall = LJ::check_priv($remote, 'canview', '*');
$viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended');
}
if ($remote->{'userid'} == $u->{'userid'} || $viewall) {
$secwhere = ""; # see everything
} elsif ($remote->{'journaltype'} eq 'P' || $remote->{'journaltype'} eq 'I') {
my $gmask = LJ::get_groupmask($u, $remote);
$secwhere = "AND (security='public' OR (security='usemask' AND allowmask & $gmask))"
if $gmask;
}
}
my $dbcr = LJ::get_cluster_reader($u);
unless ($dbcr) {
push @{$opts->{'errors'}}, "Database temporarily unavailable";
return;
}
# load the log items
my $dateformat = "%Y %m %d %H %i %s %w"; # yyyy mm dd hh mm ss day_of_week
my $sth = $dbcr->prepare("SELECT jitemid AS itemid, posterid, security, DATE_FORMAT(eventtime, \"$dateformat\") AS 'alldatepart', anum ".
"FROM log2 " .
"WHERE journalid=$u->{'userid'} AND year=$year AND month=$month AND day=$day $secwhere " .
"ORDER BY eventtime, logtime LIMIT 200");
$sth->execute;
my @items;
push @items, $_ while $_ = $sth->fetchrow_hashref;
#push @elapsed, (tv_interval ($t0));
LJ::fill_items_with_text_props(\@items, $u);
# load 'opt_ljcut_disable_lastn' prop for $remote.
LJ::load_user_props($remote, "opt_ljcut_disable_lastn");
my (%apu, %apu_lite); # alt poster users; UserLite objects
foreach (@items) {
next unless $_->{'posterid'} != $u->{'userid'};
$apu{$_->{'posterid'}} = undef;
}
if (%apu) {
LJ::load_userids_multiple([map { $_, \$apu{$_} } keys %apu], [$u]);
$apu_lite{$_} = UserLite($apu{$_}) foreach keys %apu;
}
my $userlite_journal = UserLite($u);
#push @elapsed, (tv_interval ($t0));
ENTRY:
foreach my $item (@items)
{
my ($posterid, $itemid, $security, $alldatepart, $anum) =
map { $item->{$_} } qw(posterid itemid security alldatepart anum);
my $props = $item->{'props'};
my $replycount = $props->{'replycount'};
my $subject = $item->{'text'}->[0];
my $text = $item->{'text'}->[1];
if ($get->{'nohtml'}) {
# quote all non-LJ tags
$subject =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
$text =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
}
# don't show posts from suspended users
next ENTRY if $apu{$posterid} && $apu{$posterid}->{'statusvis'} eq 'S' && ! $viewsome;
LJ::CleanHTML::clean_subject(\$subject) if $subject;
my $ditemid = $itemid*256 + $anum;
LJ::CleanHTML::clean_event(\$text, { 'preformatted' => $props->{'opt_preformatted'},
'cuturl' => LJ::item_link($u, $itemid, $anum),
'ljcut_disable' => $remote->{'opt_ljcut_disable_lastn'}, });
LJ::expand_embedded($u, $ditemid, $remote, \$text);
my $nc = "";
$nc .= "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'};
my $permalink = "$journalbase/$ditemid.html";
my $readurl = $permalink;
$readurl .= "?$nc" if $nc;
my $posturl = $permalink . "?mode=reply";
my $comments = CommentInfo({
'read_url' => $readurl,
'post_url' => $posturl,
'count' => $replycount,
'maxcomments' => ($replycount >= LJ::get_cap($u, 'maxcomments')) ? 1 : 0,
'enabled' => ($u->{'opt_showtalklinks'} eq "Y" && ! $props->{'opt_nocomments'}) ? 1 : 0,
'screened' => ($props->{'hasscreened'} && $remote &&
($remote->{'user'} eq $u->{'user'} || LJ::can_manage($remote, $u))) ? 1 : 0,
});
my $userlite_poster = $userlite_journal;
my $pu = $u;
if ($u->{'userid'} != $posterid) {
$userlite_poster = $apu_lite{$posterid} or die "No apu_lite for posterid=$posterid";
$pu = $apu{$posterid};
}
my $userpic = Image_userpic($pu, 0, $props->{'picture_keyword'});
my $entry = Entry($u, {
'subject' => $subject,
'text' => $text,
'dateparts' => $alldatepart,
'security' => $security,
'props' => $props,
'itemid' => $ditemid,
'journal' => $userlite_journal,
'poster' => $userlite_poster,
'comments' => $comments,
'userpic' => $userpic,
'permalink_url' => $permalink,
'enable_tags_compatibility' => [$opts->{enable_tags_compatibility}, $opts->{ctx}],
});
push @{$p->{'entries'}}, $entry;
}
#push @elapsed, (tv_interval ($t0));
#print STDERR "@elapsed \n";
if (@{$p->{'entries'}}) {
$p->{'has_entries'} = 1;
$p->{'entries'}->[0]->{'new_day'} = 1;
$p->{'entries'}->[-1]->{'end_day'} = 1;
}
# calculate previous day
my $pdyear = $year;
my $pdmonth = $month;
my $pdday = $day-1;
if ($pdday < 1)
{
if (--$pdmonth < 1)
{
$pdmonth = 12;
$pdyear--;
}
$pdday = LJ::days_in_month($pdmonth, $pdyear);
}
# calculate next day
my $nxyear = $year;
my $nxmonth = $month;
my $nxday = $day+1;
if ($nxday > LJ::days_in_month($nxmonth, $nxyear))
{
$nxday = 1;
if (++$nxmonth > 12) { ++$nxyear; $nxmonth=1; }
}
$p->{'prev_url'} = "$u->{'_journalbase'}/" . sprintf("%04d/%02d/%02d/", $pdyear, $pdmonth, $pdday);
$p->{'prev_date'} = Date($pdyear, $pdmonth, $pdday);
$p->{'next_url'} = "$u->{'_journalbase'}/" . sprintf("%04d/%02d/%02d/", $nxyear, $nxmonth, $nxday);
$p->{'next_date'} = Date($nxyear, $nxmonth, $nxday);
return $p;
}
1;

432
local/cgi-bin/LJ/S2/EntryPage.pm Executable file
View File

@@ -0,0 +1,432 @@
#!/usr/bin/perl
#
use strict;
package LJ::S2;
sub EntryPage
{
my ($u, $remote, $opts) = @_;
my $get = $opts->{'getargs'};
my $p = Page($u, $opts);
$p->{'_type'} = "EntryPage";
$p->{'view'} = "entry";
$p->{'comments'} = [];
$p->{'comment_pages'} = undef;
# setup viewall options
my ($viewall, $viewsome) = (0, 0);
if ($get->{viewall} && LJ::check_priv($remote, 'canview')) {
# we don't log here, as we don't know what entry we're viewing yet. the logging
# is done when we call EntryPage_entry below.
$viewall = LJ::check_priv($remote, 'canview', '*');
$viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended');
}
my ($entry, $s2entry) = EntryPage_entry($u, $remote, $opts);
return if $opts->{'suspendeduser'};
return if $opts->{'handler_return'};
$p->{'multiform_on'} = $remote &&
($remote->{'userid'} == $u->{'userid'} ||
$remote->{'userid'} == $entry->{'posterid'} ||
LJ::can_manage($remote, $u));
my $itemid = $entry->{'itemid'};
my $ditemid = $entry->{'itemid'} * 256 + $entry->{'anum'};
my $permalink = LJ::journal_base($u) . "/$ditemid.html";
my $stylemine = $get->{'style'} eq "mine" ? "style=mine" : "";
if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") {
$opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) .
"/$ditemid.html" . $opts->{'pathextra'};
return 1;
}
if ($u->{'opt_blockrobots'}) {
$p->{'head_content'} .= LJ::robot_meta_tags();
}
if ($LJ::UNICODE) {
$p->{'head_content'} .= '<meta http-equiv="Content-Type" content="text/html; charset='.$opts->{'saycharset'}."\" />\n";
}
$p->{'entry'} = $s2entry;
# add the comments
my %userpic;
my %user;
my $copts = {
'thread' => ($get->{'thread'} >> 8),
'page' => $get->{'page'},
'view' => $get->{'view'},
'userpicref' => \%userpic,
'userref' => \%user,
# user object is cached from call just made in EntryPage_entry
'up' => LJ::load_user($s2entry->{'poster'}->{'username'}),
'viewall' => $viewall,
# Okuklivanie tredov - daem upravlenie pol'zovatelyu -
'page_size' => $get->{'page_size'},
'max_subjects' => $get->{'max_subjects'},
'threading_point' => $get->{'threading_point'},
'uncollapse' => $get->{'uncollapse'},
};
my $userlite_journal = UserLite($u);
my @comments = LJ::Talk::load_comments($u, $remote, "L", $itemid, $copts);
my $pics = LJ::Talk::get_subjecticons()->{'pic'}; # hashref of imgname => { w, h, img }
my $convert_comments = sub {
my ($self, $destlist, $srclist, $depth) = @_;
foreach my $com (@$srclist) {
my $dtalkid = $com->{'talkid'} * 256 + $entry->{'anum'};
my $text = $com->{'body'};
if ($get->{'nohtml'}) {
# quote all non-LJ tags
$text =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
}
LJ::CleanHTML::clean_comment(\$text, { 'preformatted' => $com->{'props'}->{'opt_preformatted'},
'anon_comment' => !$com->{posterid}});
# local time in mysql format to gmtime
my $datetime = DateTime_unix(LJ::mysqldate_to_time($com->{'datepost'}));
if ($datetime == 0) {
$datetime = "Invalid date";
}
my $subject_icon = undef;
if (my $si = $com->{'props'}->{'subjecticon'}) {
my $pic = $pics->{$si};
$subject_icon = Image("$LJ::IMGPREFIX/talk/$pic->{'img'}",
$pic->{'w'}, $pic->{'h'}) if $pic;
}
my $comment_userpic;
if (my $pic = $userpic{$com->{'picid'}}) {
$comment_userpic = Image("$LJ::USERPIC_ROOT/$com->{'picid'}/$pic->{'userid'}",
$pic->{'width'}, $pic->{'height'});
}
my $reply_url = LJ::Talk::talkargs($permalink, "replyto=$dtalkid", $stylemine);
my $par_url;
if ($com->{'parenttalkid'}) {
my $dparent = ($com->{'parenttalkid'} << 8) + $entry->{'anum'};
$par_url = LJ::Talk::talkargs($permalink, "thread=$dparent", $stylemine) . "#t$dparent";
}
my $poster;
if ($com->{'posterid'}) {
if ($user{$com->{'posterid'}}) {
$poster = UserLite($user{$com->{'posterid'}});
} else {
$poster = {
'_type' => 'UserLite',
'username' => $com->{'userpost'},
'name' => $com->{'userpost'}, # we don't have this, so fake it
'journal_type' => 'P', # fake too, but only people can post, so correct
};
}
}
my $s2com = {
'_type' => 'Comment',
'journal' => $userlite_journal,
'metadata' => {
'picture_keyword' => $com->{'props'}->{'picture_keyword'},
},
'permalink_url' => "$permalink?thread=$dtalkid#t$dtalkid",
'reply_url' => $reply_url,
'poster' => $poster,
'replies' => [],
'subject' => LJ::ehtml($com->{'subject'}),
'subject_icon' => $subject_icon,
'talkid' => $dtalkid,
'text' => $text,
'userpic' => $comment_userpic,
'time' => $datetime,
'tags' => [],
'full' => $com->{'_loaded'} ? 1 : 0,
'depth' => $depth,
'parent_url' => $par_url,
'screened' => $com->{'state'} eq "S" ? 1 : 0,
'frozen' => $com->{'state'} eq "F" ? 1 : 0,
'link_keyseq' => [ 'delete_comment' ],
'anchor' => "t$dtalkid",
'dom_id' => "ljcmt$dtalkid",
};
# don't show info from suspended users
# FIXME: ideally the load_comments should only return these
# items if there are children, otherwise they should be hidden entirely
my $pu = $com->{'posterid'} ? $user{$com->{'posterid'}} : undef;
if ($pu && $pu->{'statusvis'} eq "S" && !$viewsome) {
$s2com->{'text'} = "";
$s2com->{'subject'} = "";
$s2com->{'full'} = 0;
$s2com->{'subject_icon'} = undef;
$s2com->{'userpic'} = undef;
}
# Conditionally add more links to the keyseq
my $link_keyseq = $s2com->{'link_keyseq'};
push @$link_keyseq, $s2com->{'screened'} ? 'unscreen_comment' : 'screen_comment';
push @$link_keyseq, $s2com->{'frozen'} ? 'unfreeze_thread' : 'freeze_thread';
if (@{$com->{'children'}}) {
$s2com->{'thread_url'} = LJ::Talk::talkargs($permalink, "thread=$dtalkid", $stylemine) . "#t$dtalkid";
}
# add the poster_ip metadata if remote user has
# access to see it.
$s2com->{'metadata'}->{'poster_ip'} = $com->{'props'}->{'poster_ip'} if
($com->{'props'}->{'poster_ip'} && $remote &&
($remote->{'userid'} == $entry->{'posterid'} ||
LJ::can_manage($remote, $u) || $viewall));
push @$destlist, $s2com;
$self->($self, $s2com->{'replies'}, $com->{'children'}, $depth+1);
}
};
$p->{'comments'} = [];
$convert_comments->($convert_comments, $p->{'comments'}, \@comments, 1);
# prepare the javascript data structure to put in the top of the page
# if the remote user is a manager of the comments
my $do_commentmanage_js = $p->{'multiform_on'};
if ($LJ::DISABLED{'commentmanage'}) {
if (ref $LJ::DISABLED{'commentmanage'} eq "CODE") {
$do_commentmanage_js = $LJ::DISABLED{'commentmanage'}->($remote);
} else {
$do_commentmanage_js = 0;
}
}
if ($do_commentmanage_js) {
my $js = "<script>\n// don't crawl this. read http://www.livejournal.com/developer/exporting.bml\n";
$js .= "var LJ_cmtinfo = {\n";
my $canAdmin = LJ::can_manage($remote, $u) ? 1 : 0;
$js .= "\tjournal: '$u->{user}',\n";
$js .= "\tcanAdmin: $canAdmin,\n";
$js .= "\tremote: '$remote->{user}',\n" if $remote;
my $recurse = sub {
my ($self, $array) = @_;
foreach my $i (@$array) {
my $has_threads = scalar @{$i->{'replies'}};
my $poster = $i->{'poster'} ? $i->{'poster'}{'username'} : "";
my $child_ids = join(',', map { $_->{'talkid'} } @{$i->{'replies'}});
$js .= "\t$i->{'talkid'}: { rc: [$child_ids], u: '$poster' },\n";
$self->($self, $i->{'replies'}) if $has_threads;
}
};
$recurse->($recurse, $p->{'comments'});
chop $js; chop $js; # remove final ",\n". stupid javascript.
$js .= "\n};\n" .
"var LJVAR;\n".
"if (!LJVAR) LJVAR = new Object();\n".
"LJVAR.imgprefix = \"$LJ::IMGPREFIX\";\n".
"</script>\n";
$p->{'head_content'} .= $js;
$p->{'head_content'} .= "<script src='$LJ::SITEROOT/js/commentmanage.js'></script>\n";
}
$p->{'viewing_thread'} = $get->{'thread'} ? 1 : 0;
# default values if there were no comments, because
# LJ::Talk::load_comments() doesn't provide them.
if ($copts->{'out_error'} eq 'noposts') {
$copts->{'out_pages'} = $copts->{'out_page'} = 1;
$copts->{'out_items'} = 0;
$copts->{'out_itemfirst'} = $copts->{'out_itemlast'} = undef;
}
$p->{'comment_pages'} = ItemRange({
'all_subitems_displayed' => ($copts->{'out_pages'} == 1),
'current' => $copts->{'out_page'},
'from_subitem' => $copts->{'out_itemfirst'},
'num_subitems_displayed' => scalar @comments,
'to_subitem' => $copts->{'out_itemlast'},
'total' => $copts->{'out_pages'},
'total_subitems' => $copts->{'out_items'},
'_url_of' => sub { return "$permalink?page=" . int($_[0]) .
($stylemine ? "&$stylemine" : ''); },
});
return $p;
}
sub EntryPage_fast_check
{
my ($u, $view, $remote, $opts) = @_;
return unless ($view eq "entry" || $view eq "reply");
my $get = $opts->{'getargs'};
my $r = $opts->{'r'};
my $uri = $r->uri;
my ($ditemid, $itemid, $anum);
unless ($uri =~ /(\d+)\.html/) {
$opts->{'handler_return'} = 404;
return;
}
$ditemid = $1;
$anum = $ditemid % 256;
$itemid = $ditemid >> 8;
my $entry = LJ::Talk::get_journal_item($u, $itemid, "props_only");
unless ($entry && $entry->{'anum'} == $anum) {
$opts->{'handler_return'} = 404;
return;
}
my $props = $entry->{'props'};
# do they have the viewall priv?
my $viewall = 0;
my $viewsome = 0;
if ($get->{'viewall'} && LJ::check_priv($remote, "canview")) {
LJ::statushistory_add($u->{'userid'}, $remote->{'userid'},
"viewall", "entry: $u->{'user'}, itemid: $itemid, statusvis: $u->{'statusvis'}");
$viewall = LJ::check_priv($remote, 'canview', '*');
$viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended');
}
# check using normal rules
unless (LJ::can_view($remote, $entry) || $viewall) {
$opts->{'handler_return'} = 403;
return;
}
my $pu = $u;
if ($entry->{'posterid'} != $entry->{'ownerid'}) {
$pu = LJ::load_userid($entry->{'posterid'});
}
if (($pu && $pu->{'statusvis'} eq 'S') && !$viewsome) {
$opts->{'suspendeduser'} = 1;
return;
}
# check If-Modified-Since
my $lastmod = $props->{'commentalter'};
my $revisiontime = $props->{' revtime'};
$lastmod = $revisiontime if $revisiontime && $revisiontime > $lastmod;
my $ims = $r->header_in('If-Modified-Since');
if ($ims) {
my $theirtime = LJ::http_to_time($ims);
if ($theirtime >= $lastmod && !$remote) {
# only for anonymous: logged users will be checked by Etag for exact match
# reply special: uniq challange string for regular users
if ($view eq "entry" || ($view eq "reply" && $r->header_in('User-Agent') =~ /$LJ::ROBOTS_REGEXP/)) {
$opts->{'notmodified'} = 1;
return;
}
}
}
$r->header_out("Last-Modified", LJ::time_to_http($lastmod));
}
sub EntryPage_entry
{
my ($u, $remote, $opts) = @_;
### NB! EntryPage_fast_check was previously called, so all checks passed.
my $get = $opts->{'getargs'};
my $r = $opts->{'r'};
my $uri = $r->uri;
my ($ditemid, $itemid, $anum);
unless ($uri =~ /(\d+)\.html/) {
$opts->{'handler_return'} = 404;
return;
}
$ditemid = $1;
$anum = $ditemid % 256;
$itemid = $ditemid >> 8;
my $entry = LJ::Talk::get_journal_item($u, $itemid);
unless ($entry && $entry->{'anum'} == $anum) {
$opts->{'handler_return'} = 404;
return;
}
### more checks skipped ###
my $props = $entry->{'props'};
my $userlite_journal = UserLite($u);
my $userlite_poster = $userlite_journal;
my $pu = $u;
if ($entry->{'posterid'} != $entry->{'ownerid'}) {
$pu = LJ::load_userid($entry->{'posterid'});
$userlite_poster = UserLite($pu);
}
my $replycount = $props->{'replycount'};
my $nc = "";
$nc .= "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'};
my $stylemine = $get->{'style'} eq "mine" ? "style=mine" : "";
my $userpic = Image_userpic($pu, 0, $props->{'picture_keyword'});
my $permalink = LJ::journal_base($u) . "/$ditemid.html";
my $readurl = LJ::Talk::talkargs($permalink, $nc, $stylemine);
my $posturl = LJ::Talk::talkargs($permalink, "mode=reply", $stylemine);
my $comments = CommentInfo({
'read_url' => $readurl,
'post_url' => $posturl,
'count' => $replycount,
'maxcomments' => ($replycount >= LJ::get_cap($u, 'maxcomments')) ? 1 : 0,
'enabled' => ($u->{'opt_showtalklinks'} eq "Y" && !
$props->{'opt_nocomments'}) ? 1 : 0,
'screened' => ($props->{'hasscreened'} && $remote &&
LJ::can_manage($remote, $u)) ? 1 : 0,
});
# format it
if ($opts->{'getargs'}->{'nohtml'}) {
# quote all non-LJ tags
$entry->{'subject'} =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
$entry->{'event'} =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
}
my $raw_subj = $entry->{'subject'};
LJ::CleanHTML::clean_subject(\$entry->{'subject'});
LJ::CleanHTML::clean_event(\$entry->{'event'}, $props->{'opt_preformatted'});
LJ::expand_embedded($u, $ditemid, $remote, \$entry->{'event'});
my $s2entry = Entry($u, {
'_rawsubject' => $raw_subj,
'subject' => $entry->{'subject'},
'text' => $entry->{'event'},
'dateparts' => $entry->{'alldatepart'},
'security' => $entry->{'security'},
'props' => $props,
'itemid' => $ditemid,
'comments' => $comments,
'journal' => $userlite_journal,
'poster' => $userlite_poster,
'new_day' => 0,
'end_day' => 0,
'userpic' => $userpic,
'permalink_url' => $permalink,
'enable_tags_compatibility' => [$opts->{enable_tags_compatibility}, $opts->{ctx}],
});
return ($entry, $s2entry);
}
1;

View File

@@ -0,0 +1,444 @@
#!/usr/bin/perl
#
use strict;
package LJ::S2;
eval "use LJR::Distributed;";
my $ljr = $@ ? 0 : 1;
if ($ljr) {
use LJR::Distributed;
}
sub FriendsPage
{
my ($u, $remote, $opts) = @_;
my $p = Page($u, $opts);
$p->{'_type'} = "FriendsPage";
$p->{'view'} = "friends";
$p->{'entries'} = [];
$p->{'friends'} = {};
$p->{'friends_title'} = LJ::ehtml($u->{'friendspagetitle'});
$p->{'filter_active'} = 0;
$p->{'filter_name'} = "";
my $sth;
my $user = $u->{'user'};
# see how often the remote user can reload this page.
# "friendsviewupdate" time determines what granularity time
# increments by for checking for new updates
my $nowtime = time();
# update delay specified by "friendsviewupdate"
my $newinterval = LJ::get_cap_min($remote, "friendsviewupdate") || 1;
# when are we going to say page was last modified? back up to the
# most recent time in the past where $time % $interval == 0
my $lastmod = $nowtime;
$lastmod -= $lastmod % $newinterval;
# see if they have a previously cached copy of this page they
# might be able to still use.
my $ims = $opts->{'r'}->header_in('If-Modified-Since');
if ($ims) {
my $theirtime = LJ::http_to_time($ims);
# send back a 304 Not Modified if they say they've reloaded this
# document in the last $newinterval seconds:
unless ($theirtime < $lastmod) {
$opts->{'handler_return'} = 304;
return 1;
}
}
$opts->{'r'}->header_out('Last-Modified', LJ::time_to_http($lastmod));
my $get = $opts->{'getargs'};
my $ret;
if ($get->{'mode'} eq "live") {
$ret .= "<html><head><title>${user}'s friends: live!</title></head>\n";
$ret .= "<frameset rows=\"100%,0%\" border=0>\n";
$ret .= " <frame name=livetop src=\"friends?mode=framed\">\n";
$ret .= " <frame name=livebottom src=\"friends?mode=livecond&amp;lastitemid=0\">\n";
$ret .= "</frameset></html>\n";
return $ret;
}
if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") {
$opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) . "/friends";
return 1;
}
LJ::load_user_props($remote, "opt_nctalklinks", "opt_stylemine", "opt_imagelinks", "opt_ljcut_disable_friends");
# load options for image links
my ($maximgwidth, $maximgheight) = (undef, undef);
($maximgwidth, $maximgheight) = ($1, $2)
if ($remote && $remote->{'userid'} == $u->{'userid'} &&
$remote->{'opt_imagelinks'} =~ m/^(\d+)\|(\d+)$/);
## never have spiders index friends pages (change too much, and some
## people might not want to be indexed)
$p->{'head_content'} .= LJ::robot_meta_tags();
my $itemshow = S2::get_property_value($opts->{'ctx'}, "page_friends_items")+0;
if ($itemshow < 1) { $itemshow = 20; }
elsif ($itemshow > 50) { $itemshow = 50; }
my $skip = $get->{'skip'}+0;
my $maxskip = ($LJ::MAX_SCROLLBACK_FRIENDS || 1000) - $itemshow;
if ($skip > $maxskip) { $skip = $maxskip; }
if ($skip < 0) { $skip = 0; }
my $itemload = $itemshow+$skip;
my $dayskip = $get->{'dayskip'}+0;
my $filter;
my $group;
my $common_filter = 1;
if (defined $get->{'filter'} && $remote && $remote->{'user'} eq $user) {
$filter = $get->{'filter'};
$common_filter = 0;
$p->{'filter_active'} = 1;
$p->{'filter_name'} = "";
} else {
if ($opts->{'pathextra'}) {
$group = $opts->{'pathextra'};
$group =~ s!^/!!;
$group =~ s!/$!!;
if ($group) { $group = LJ::durl($group); $common_filter = 0; }
}
if ($group) {
$p->{'filter_active'} = 1;
$p->{'filter_name'} = LJ::ehtml($group);
}
my $grp = LJ::get_friend_group($u, { 'name' => $group || "Default View" });
my $bit = $grp->{'groupnum'};
my $public = $grp->{'is_public'};
if ($bit && ($public || ($remote && $remote->{'user'} eq $user))) {
$filter = (1 << $bit);
} elsif ($group) {
$opts->{'badfriendgroup'} = 1;
return 1;
}
}
if ($opts->{'view'} eq "friendsfriends") {
$p->{'friends_mode'} = "friendsfriends";
}
if ($get->{'mode'} eq "livecond")
{
## load the itemids
my @items = LJ::get_friend_items({
'u' => $u,
'remote' => $remote,
'itemshow' => 1,
'skip' => 0,
'filter' => $filter,
'common_filter' => $common_filter,
});
my $first = @items ? $items[0]->{'itemid'} : 0;
$ret .= "time = " . scalar(time()) . "<br />";
$opts->{'headers'}->{'Refresh'} = "30;URL=$LJ::SITEROOT/users/$user/friends?mode=livecond&lastitemid=$first";
if ($get->{'lastitemid'} == $first) {
$ret .= "nothing new!";
} else {
if ($get->{'lastitemid'}) {
$ret .= "<b>New stuff!</b>\n";
$ret .= "<script language=\"JavaScript\">\n";
$ret .= "window.parent.livetop.location.reload(true);\n";
$ret .= "</script>\n";
$opts->{'trusted_html'} = 1;
} else {
$ret .= "Friends Live! started.";
}
}
return $ret;
}
## load the itemids
my %friends;
my %friends_row;
my @items = LJ::get_friend_items({
'u' => $u,
'remote' => $remote,
'itemshow' => $itemshow,
'skip' => $skip,
'dayskip' => $dayskip,
'filter' => $filter,
'common_filter' => $common_filter,
'friends_u' => \%friends,
'friends' => \%friends_row,
'showtypes' => $get->{'show'},
'friendsoffriends' => $opts->{'view'} eq "friendsfriends",
'dateformat' => 'S2',
});
while ($_ = each %friends) {
# we expect fgcolor/bgcolor to be in here later
$friends{$_}->{'fgcolor'} = $friends_row{$_}->{'fgcolor'} || '#ffffff';
$friends{$_}->{'bgcolor'} = $friends_row{$_}->{'bgcolor'} || '#000000';
}
return $p unless %friends;
my %posters;
{
my @posterids;
foreach my $item (@items) {
next if $friends{$item->{'posterid'}};
push @posterids, $item->{'posterid'};
}
LJ::load_userids_multiple([ map { $_ => \$posters{$_} } @posterids ])
if @posterids;
}
my %objs_of_picid;
my @userpic_load;
my %lite; # posterid -> s2_UserLite
my $get_lite = sub {
my $id = shift;
return $lite{$id} if $lite{$id};
return $lite{$id} = UserLite($posters{$id} || $friends{$id});
};
my $eventnum = 0;
my $hiddenentries = 0;
ENTRY:
foreach my $item (@items)
{
my ($friendid, $posterid, $itemid, $security, $alldatepart) =
map { $item->{$_} } qw(ownerid posterid itemid security alldatepart);
my $fru = $friends{$friendid};
my ($friend, $poster);
$friend = $poster = $fru->{'user'};
$p->{'friends'}->{$fru->{'user'}} ||= Friend($fru);
my $clusterid = $item->{'clusterid'}+0;
my $props = $item->{'props'};
my $replycount = $props->{'replycount'};
my $subject = $item->{'text'}->[0];
my $text = $item->{'text'}->[1];
if ($get->{'nohtml'}) {
# quote all non-LJ tags
$subject =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
$text =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
}
LJ::CleanHTML::clean_subject(\$subject) if $subject;
my $ditemid = $itemid * 256 + $item->{'anum'};
my $stylemine = "";
$stylemine .= "style=mine" if $remote && $remote->{'opt_stylemine'} &&
$remote->{'userid'} != $friendid;
LJ::CleanHTML::clean_event(\$text, { 'preformatted' => $props->{'opt_preformatted'},
'cuturl' => LJ::item_link($fru, $itemid, $item->{'anum'}, $stylemine),
'maximgwidth' => $maximgwidth,
'maximgheight' => $maximgheight,
'ljcut_disable' => $remote->{'opt_ljcut_disable_friends'}, });
LJ::expand_embedded($fru, $ditemid, $remote, \$text);
my $userlite_poster = $get_lite->($posterid);
my $userlite_journal = $get_lite->($friendid);
# get the poster user
my $po = $posters{$posterid} || $friends{$posterid};
# don't allow posts from suspended users
if ($po->{'statusvis'} eq 'S') {
$hiddenentries++; # Remember how many we've skipped for later
next ENTRY;
}
# do the picture
my $picid = 0;
my $picu = undef;
if ($friendid != $posterid && S2::get_property_value($opts->{ctx}, 'use_shared_pic')) {
# using the community, the user wants to see shared pictures
$picu = $fru;
# use shared pic for community
$picid = $fru->{defaultpicid};
} else {
# we're using the poster for this picture
$picu = $po;
# check if they specified one
$picid = LJ::get_picid_from_keyword($po, $props->{picture_keyword})
if $props->{picture_keyword};
# fall back on the poster's default
$picid ||= $po->{defaultpicid};
}
my $nc = "";
$nc .= "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'};
my $journalbase = LJ::journal_base($fru);
my $permalink = "$journalbase/$ditemid.html";
my $readurl = LJ::Talk::talkargs($permalink, $nc, $stylemine);
my $posturl = LJ::Talk::talkargs($permalink, "mode=reply", $stylemine);
my $synurl = "";
if ($ljr && $props->{'syn_link'}) {
my $rs = LJR::Distributed::match_remote_server($props->{'syn_link'});
if ($rs->{"servertype"} eq "lj") {
$readurl = $props->{'syn_link'};
$posturl = $props->{'syn_link'} . "?mode=reply";
$replycount = 'Read';
}
else {
$posturl = $props->{'syn_link'};
$replycount = undef;
}
$synurl = $props->{'syn_link'};
}
my $comments = CommentInfo({
'read_url' => $readurl,
'post_url' => $posturl,
'syn_url' => $synurl,
'count' => $replycount,
'maxcomments' => ($replycount >= LJ::get_cap($u, 'maxcomments')) ? 1 : 0,
'enabled' => ($fru->{'opt_showtalklinks'} eq "Y" &&
! $props->{'opt_nocomments'} ||
$props->{'syn_link'}
) ? 1 : 0,
'screened' => ($remote && LJ::can_manage($remote, $fru) && $props->{'hasscreened'}) ? 1 : 0,
});
my $moodthemeid = $u->{'opt_forcemoodtheme'} eq 'Y' ?
$u->{'moodthemeid'} : $fru->{'moodthemeid'};
my $entry = Entry($u, {
'subject' => $subject,
'text' => $text,
'dateparts' => $alldatepart,
'security' => $security,
'props' => $props,
'itemid' => $ditemid,
'journal' => $userlite_journal,
'poster' => $userlite_poster,
'comments' => $comments,
'new_day' => 0, # setup below
'end_day' => 0, # setup below
'userpic' => undef,
'permalink_url' => $permalink,
'base_url' => $journalbase,
'moodthemeid' => $moodthemeid,
'enable_tags_compatibility' => [$opts->{enable_tags_compatibility}, $opts->{ctx}],
});
$entry->{'_ymd'} = join('-', map { $entry->{'time'}->{$_} } qw(year month day));
if ($picid && $picu) {
push @userpic_load, [ $picu, $picid ];
push @{$objs_of_picid{$picid}}, \$entry->{'userpic'};
}
push @{$p->{'entries'}}, $entry;
$eventnum++;
} # end while
# set the new_day and end_day members.
if ($eventnum) {
for (my $i = 0; $i < $eventnum; $i++) {
my $entry = $p->{'entries'}->[$i];
$entry->{'new_day'} = 1;
my $last = $i;
for (my $j = $i+1; $j < $eventnum; $j++) {
my $ej = $p->{'entries'}->[$j];
if ($ej->{'_ymd'} eq $entry->{'_ymd'}) {
$last = $j;
}
}
$p->{'entries'}->[$last]->{'end_day'} = 1;
$i = $last;
}
}
# load the pictures that were referenced, then retroactively populate
# the userpic fields of the Entries above
my %userpics;
LJ::load_userpics(\%userpics, \@userpic_load);
foreach my $picid (keys %userpics) {
my $up = Image("$LJ::USERPIC_ROOT/$picid/$userpics{$picid}->{'userid'}",
$userpics{$picid}->{'width'},
$userpics{$picid}->{'height'});
foreach (@{$objs_of_picid{$picid}}) { $$_ = $up; }
}
# make the skip links
my $nav = {
'_type' => 'RecentNav',
'version' => 1,
'skip' => $skip,
'count' => $eventnum,
};
my $base = "$u->{'_journalbase'}/$opts->{'view'}";
if ($group) {
$base .= "/" . LJ::eurl($group);
}
# $linkfilter is distinct from $filter: if user has a default view,
# $filter is now set according to it but we don't want it to show in the links.
# $incfilter may be true even if $filter is 0: user may use filter=0 to turn
# off the default group
my $linkfilter = $get->{'filter'} + 0;
my $incfilter = defined $get->{'filter'};
# if we've skipped down, then we can skip back up
if ($skip) {
my %linkvars;
$linkvars{'filter'} = $linkfilter if $incfilter;
$linkvars{'show'} = $get->{'show'} if $get->{'show'} =~ /^\w+$/;
my $newskip = $skip - $itemshow;
if ($newskip > 0) { $linkvars{'skip'} = $newskip; }
else { $newskip = 0; }
$linkvars{'dayskip'} = $dayskip if $dayskip;
$nav->{'forward_url'} = LJ::make_link($base, \%linkvars);
$nav->{'forward_skip'} = $newskip;
$nav->{'forward_count'} = $itemshow;
}
## unless we didn't even load as many as we were expecting on this
## page, then there are more (unless there are exactly the number shown
## on the page, but who cares about that)
# Must remember to count $hiddenentries or we'll have no skiplinks when > 1
unless (($eventnum + $hiddenentries) != $itemshow || $skip == $maxskip) {
my %linkvars;
$linkvars{'filter'} = $linkfilter if $incfilter;
$linkvars{'show'} = $get->{'show'} if $get->{'show'} =~ /^\w+$/;
my $newskip = $skip + $itemshow;
$linkvars{'skip'} = $newskip;
$linkvars{'dayskip'} = $dayskip if $dayskip;
$nav->{'backward_url'} = LJ::make_link($base, \%linkvars);
$nav->{'backward_skip'} = $newskip;
$nav->{'backward_count'} = $itemshow;
}
$p->{'nav'} = $nav;
if ($get->{'mode'} eq "framed") {
$p->{'head_content'} .= "<base target='_top' />";
}
return $p;
}
1;

224
local/cgi-bin/LJ/S2/MonthPage.pm Executable file
View File

@@ -0,0 +1,224 @@
#!/usr/bin/perl
#
use strict;
package LJ::S2;
sub MonthPage
{
my ($u, $remote, $opts) = @_;
my $get = $opts->{'getargs'};
my $p = Page($u, $opts);
$p->{'_type'} = "MonthPage";
$p->{'view'} = "month";
$p->{'days'} = [];
my $ctx = $opts->{'ctx'};
my $dbcr = LJ::get_cluster_reader($u);
my $user = $u->{'user'};
my $journalbase = LJ::journal_base($user, $opts->{'vhost'});
if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") {
$opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) .
"/" . $opts->{'pathextra'};
return 1;
}
if ($u->{'opt_blockrobots'}) {
$p->{'head_content'} .= LJ::robot_meta_tags();
}
my ($year, $month);
if ($opts->{'pathextra'} =~ m!^/(\d\d\d\d)/(\d\d)\b!) {
($year, $month) = ($1, $2);
}
$opts->{'errors'} = [];
if ($month < 1 || $month > 12) { push @{$opts->{'errors'}}, "Invalid month: $month"; }
if ($year < 1970 || $year > 2038) { push @{$opts->{'errors'}}, "Invalid year: $year"; }
unless ($dbcr) { push @{$opts->{'errors'}}, "Database temporarily unavailable"; }
return if @{$opts->{'errors'}};
$p->{'date'} = Date($year, $month, 0);
# load the log items
my $dateformat = "%Y %m %d %H %i %s %w"; # yyyy mm dd hh mm ss day_of_week
my $sth;
my $secwhere = "AND l.security='public'";
my $viewall = 0;
my $viewsome = 0;
if ($remote) {
# do they have the viewall priv?
if ($get->{'viewall'} && LJ::check_priv($remote, "canview")) {
LJ::statushistory_add($u->{'userid'}, $remote->{'userid'},
"viewall", "month: $user, statusvis: $u->{'statusvis'}");
$viewall = LJ::check_priv($remote, 'canview', '*');
$viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended');
}
if ($remote->{'userid'} == $u->{'userid'} || $viewall) {
$secwhere = ""; # see everything
} elsif ($remote->{'journaltype'} eq 'P' || $remote->{'journaltype'} eq 'I') {
my $gmask = LJ::get_groupmask($u, $remote);
$secwhere = "AND (l.security='public' OR (l.security='usemask' AND l.allowmask & $gmask))"
if $gmask;
}
}
$sth = $dbcr->prepare("SELECT l.jitemid AS 'itemid', l.posterid, l.anum, l.day, ".
" DATE_FORMAT(l.eventtime, '$dateformat') AS 'alldatepart', ".
" l.replycount, l.security ".
"FROM log2 l ".
"WHERE l.journalid=? AND l.year=? AND l.month=? ".
"$secwhere LIMIT 2000");
$sth->execute($u->{'userid'}, $year, $month);
my @items;
push @items, $_ while $_ = $sth->fetchrow_hashref;
@items = sort { $a->{'alldatepart'} cmp $b->{'alldatepart'} } @items;
LJ::fill_items_with_text_props(\@items, $u, {'only_subject' => 1});
my (%pu, %pu_lite); # poster users; UserLite objects
foreach (@items) {
$pu{$_->{'posterid'}} = undef;
}
LJ::load_userids_multiple([map { $_, \$pu{$_} } keys %pu], [$u]);
$pu_lite{$_} = UserLite($pu{$_}) foreach keys %pu;
my %day_entries; # <day> -> [ Entry+ ]
my $opt_text_subjects = S2::get_property_value($ctx, "page_month_textsubjects");
my $userlite_journal = UserLite($u);
ENTRY:
foreach my $item (@items)
{
my ($posterid, $itemid, $security, $alldatepart, $replycount, $anum) =
map { $item->{$_} } qw(posterid itemid security alldatepart replycount anum);
my $day = $item->{'day'};
# don't show posts from suspended users
next unless $pu{$posterid};
next ENTRY if $pu{$posterid}->{'statusvis'} eq 'S' && !$viewsome;
my $subject = $item->{'text'}->[0];
my $props = $item->{'props'};
if ($opt_text_subjects) {
LJ::CleanHTML::clean_subject_all(\$subject);
} else {
LJ::CleanHTML::clean_subject(\$subject);
}
my $ditemid = $itemid*256 + $anum;
my $nc = "";
$nc .= "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'};
my $permalink = "$journalbase/$ditemid.html";
my $readurl = $permalink;
$readurl .= "?$nc" if $nc;
my $posturl = $permalink . "?mode=reply";
my $comments = CommentInfo({
'read_url' => $readurl,
'post_url' => $posturl,
'count' => $replycount,
'maxcomments' => ($replycount >= LJ::get_cap($u, 'maxcomments')) ? 1 : 0,
'enabled' => ($u->{'opt_showtalklinks'} eq "Y" && ! $props->{'opt_nocomments'}) ? 1 : 0,
'screened' => ($props->{'hasscreened'} && $remote &&
($remote->{'user'} eq $u->{'user'} || LJ::can_manage($remote, $u))) ? 1 : 0,
});
my $userlite_poster = $userlite_journal;
my $userpic = $p->{'journal'}->{'default_pic'};
if ($u->{'userid'} != $posterid) {
$userlite_poster = $pu_lite{$posterid};
$userpic = Image_userpic($pu{$posterid}, 0, $props->{'picture_keyword'});
}
my $entry = Entry($u, {
'subject' => $subject,
'text' => "",
'dateparts' => $alldatepart,
'security' => $security,
'props' => $props,
'itemid' => $ditemid,
'journal' => $userlite_journal,
'poster' => $userlite_poster,
'comments' => $comments,
'userpic' => $userpic,
'permalink_url' => $permalink,
});
push @{$day_entries{$day}}, $entry;
}
my $days_month = LJ::days_in_month($month, $year);
for my $day (1..$days_month) {
my $entries = $day_entries{$day} || [];
my $month_day = {
'_type' => 'MonthDay',
'date' => Date($year, $month, $day),
'day' => $day,
'has_entries' => scalar @$entries > 0,
'num_entries' => scalar @$entries,
'url' => $journalbase . sprintf("/%04d/%02d/%02d/", $year, $month, $day),
'entries' => $entries,
};
push @{$p->{'days'}}, $month_day;
}
# populate redirector
my $vhost = $opts->{'vhost'};
$vhost =~ s/:.*//;
$p->{'redir'} = {
'_type' => "Redirector",
'user' => $u->{'user'},
'vhost' => $vhost,
'type' => 'monthview',
'url' => "$LJ::SITEROOT/go.bml",
};
# figure out what months have been posted into
my $nowval = $year*12 + $month;
$p->{'months'} = [];
my $days = LJ::get_daycounts($u, $remote) || [];
my $lastmo;
foreach my $day (@$days) {
my ($oy, $om) = ($day->[0], $day->[1]);
my $mo = "$oy-$om";
next if $mo eq $lastmo;
$lastmo = $mo;
my $date = Date($oy, $om, 0);
my $url = $journalbase . sprintf("/%04d/%02d/", $oy, $om);
push @{$p->{'months'}}, {
'_type' => "MonthEntryInfo",
'date' => $date,
'url' => $url,
'redir_key' => sprintf("%04d%02d", $oy, $om),
};
my $val = $oy*12+$om;
if ($val < $nowval) {
$p->{'prev_url'} = $url;
$p->{'prev_date'} = $date;
}
if ($val > $nowval && ! $p->{'next_date'}) {
$p->{'next_url'} = $url;
$p->{'next_date'} = $date;
}
}
return $p;
}
1;

216
local/cgi-bin/LJ/S2/RecentPage.pm Executable file
View File

@@ -0,0 +1,216 @@
use strict;
package LJ::S2;
sub RecentPage
{
my ($u, $remote, $opts) = @_;
my $p = Page($u, $opts);
$p->{'_type'} = "RecentPage";
$p->{'view'} = "recent";
$p->{'entries'} = [];
my $user = $u->{'user'};
my $journalbase = LJ::journal_base($user, $opts->{'vhost'});
if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") {
$opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'});
return;
}
LJ::load_user_props($remote, "opt_nctalklinks", "opt_ljcut_disable_lastn");
my $get = $opts->{'getargs'};
if ($opts->{'pathextra'}) {
$opts->{'badargs'} = 1;
return 1;
}
if ($u->{'opt_blockrobots'} || $get->{'skip'}) {
$p->{'head_content'} .= LJ::robot_meta_tags();
}
$p->{'head_content'} .= qq{<link rel="openid.server" href="$LJ::OPENID_SERVER" />\n}
if LJ::OpenID::server_enabled();
my $itemshow = S2::get_property_value($opts->{'ctx'}, "page_recent_items")+0;
if ($itemshow < 1) { $itemshow = 20; }
elsif ($itemshow > 50) { $itemshow = 50; }
my $skip = $get->{'skip'}+0;
my $maxskip = $LJ::MAX_HINTS_LASTN-$itemshow;
if ($skip < 0) { $skip = 0; }
if ($skip > $maxskip) { $skip = $maxskip; }
my $dayskip = $get->{'dayskip'}+0;
# do they want to view all entries, regardless of security?
my $viewall = 0;
my $viewsome = 0;
if ($get->{'viewall'} && LJ::check_priv($remote, "canview")) {
LJ::statushistory_add($u->{'userid'}, $remote->{'userid'},
"viewall", "lastn: $user, statusvis: $u->{'statusvis'}");
$viewall = LJ::check_priv($remote, 'canview', '*');
$viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended');
}
## load the items
my $err;
my @items = LJ::get_recent_items({
'u' => $u,
'clustersource' => 'slave',
'viewall' => $viewall,
'remote' => $remote,
'itemshow' => $itemshow,
'skip' => $skip,
'dayskip' => $dayskip,
'tags' => $opts->{tags},
'dateformat' => 'S2',
'order' => ($u->{'journaltype'} eq "C" || $u->{'journaltype'} eq "Y") # community or syndicated
? "logtime" : "",
'err' => \$err,
});
die $err if $err;
my $lastdate = "";
my $itemnum = 0;
my $lastentry = undef;
my (%apu, %apu_lite); # alt poster users; UserLite objects
foreach (@items) {
next unless $_->{'posterid'} != $u->{'userid'};
$apu{$_->{'posterid'}} = undef;
}
if (%apu) {
LJ::load_userids_multiple([map { $_, \$apu{$_} } keys %apu], [$u]);
$apu_lite{$_} = UserLite($apu{$_}) foreach keys %apu;
}
my $userlite_journal = UserLite($u);
ENTRY:
foreach my $item (@items)
{
my ($posterid, $itemid, $security, $alldatepart) =
map { $item->{$_} } qw(posterid itemid security alldatepart);
my $props = $item->{'props'};
my $replycount = $props->{'replycount'};
my $subject = $item->{'text'}->[0];
my $text = $item->{'text'}->[1];
if ($get->{'nohtml'}) {
# quote all non-LJ tags
$subject =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
$text =~ s{<(?!/?lj)(.*?)>} {&lt;$1&gt;}gi;
}
# don't show posts from suspended users unless the user doing the viewing says to (and is allowed)
next ENTRY if $apu{$posterid} && $apu{$posterid}->{'statusvis'} eq 'S' && !$viewsome;
my $date = substr($alldatepart, 0, 10);
my $new_day = 0;
if ($date ne $lastdate) {
$new_day = 1;
$lastdate = $date;
$lastentry->{'end_day'} = 1 if $lastentry;
}
$itemnum++;
LJ::CleanHTML::clean_subject(\$subject) if $subject;
my $ditemid = $itemid * 256 + $item->{'anum'};
LJ::CleanHTML::clean_event(\$text, { 'preformatted' => $props->{'opt_preformatted'},
'cuturl' => LJ::item_link($u, $itemid, $item->{'anum'}),
'ljcut_disable' => $remote->{"opt_ljcut_disable_lastn"}, });
LJ::expand_embedded($u, $ditemid, $remote, \$text);
my $nc = "";
$nc .= "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'};
my $permalink = "$journalbase/$ditemid.html";
my $readurl = $permalink;
$readurl .= "?$nc" if $nc;
my $posturl = $permalink . "?mode=reply";
my $comments = CommentInfo({
'read_url' => $readurl,
'post_url' => $posturl,
'count' => $replycount,
'maxcomments' => ($replycount >= LJ::get_cap($u, 'maxcomments')) ? 1 : 0,
'enabled' => ($u->{'opt_showtalklinks'} eq "Y" && ! $props->{'opt_nocomments'}) ? 1 : 0,
'screened' => ($props->{'hasscreened'} && ($remote->{'user'} eq $u->{'user'}|| LJ::can_manage($remote, $u))) ? 1 : 0,
});
my $userlite_poster = $userlite_journal;
my $pu = $u;
if ($u->{'userid'} != $posterid) {
$userlite_poster = $apu_lite{$posterid} or die "No apu_lite for posterid=$posterid";
$pu = $apu{$posterid};
}
my $userpic = Image_userpic($pu, 0, $props->{'picture_keyword'});
my $entry = $lastentry = Entry($u, {
'subject' => $subject,
'text' => $text,
'dateparts' => $alldatepart,
'security' => $security,
'props' => $props,
'itemid' => $ditemid,
'journal' => $userlite_journal,
'poster' => $userlite_poster,
'comments' => $comments,
'new_day' => $new_day,
'end_day' => 0, # if true, set later
'userpic' => $userpic,
'permalink_url' => $permalink,
'enable_tags_compatibility' => [$opts->{enable_tags_compatibility}, $opts->{ctx}],
});
push @{$p->{'entries'}}, $entry;
} # end huge while loop
# mark last entry as closing.
$p->{'entries'}->[-1]->{'end_day'} = 1 if $itemnum;
#### make the skip links
my $nav = {
'_type' => 'RecentNav',
'version' => 1,
'skip' => $skip,
'count' => $itemnum,
};
# if we've skipped down, then we can skip back up
if ($skip) {
my $newskip = $skip - $itemshow;
$newskip = 0 if $newskip <= 0;
$nav->{'forward_skip'} = $newskip;
$nav->{'forward_url'} = LJ::make_link("$p->{base_url}/", { skip => ($newskip || ""), tag => (LJ::eurl($get->{tag}) || ""), dayskip => ($dayskip || "") });
$nav->{'forward_count'} = $itemshow;
}
# unless we didn't even load as many as we were expecting on this
# page, then there are more (unless there are exactly the number shown
# on the page, but who cares about that)
unless ($itemnum != $itemshow) {
$nav->{'backward_count'} = $itemshow;
if ($skip == $maxskip) {
my $date_slashes = $lastdate; # "yyyy mm dd";
$date_slashes =~ s! !/!g;
$nav->{'backward_url'} = "$p->{'base_url'}/day/$date_slashes";
} else {
my $newskip = $skip + $itemshow;
$nav->{'backward_url'} = LJ::make_link("$p->{'base_url'}/", { skip => ($newskip || ""), tag => (LJ::eurl($get->{tag}) || ""), dayskip => ($dayskip || "") });
$nav->{'backward_skip'} = $newskip;
}
}
$p->{'nav'} = $nav;
return $p;
}
1;

139
local/cgi-bin/LJ/S2/ReplyPage.pm Executable file
View File

@@ -0,0 +1,139 @@
#!/usr/bin/perl
#
use strict;
package LJ::S2;
sub ReplyPage
{
my ($u, $remote, $opts) = @_;
my $p = Page($u, $opts);
$p->{'_type'} = "ReplyPage";
$p->{'view'} = "reply";
my $get = $opts->{'getargs'};
my ($entry, $s2entry) = EntryPage_entry($u, $remote, $opts);
return if $opts->{'suspendeduser'};
return if $opts->{'handler_return'};
my $ditemid = $entry->{'itemid'}*256 + $entry->{'anum'};
$p->{'head_content'} .= $LJ::COMMON_CODE{'chalresp_js'};
if ($u->{'opt_blockrobots'}) {
$p->{'head_content'} .= LJ::robot_meta_tags();
}
$p->{'entry'} = $s2entry;
# setup the replying item
my $replyto = $s2entry;
my $parpost;
if ($get->{'replyto'}) {
my $re_talkid = int($get->{'replyto'} >> 8);
my $re_anum = $get->{'replyto'} % 256;
unless ($re_anum == $entry->{'anum'}) {
$opts->{'handler_return'} = 404;
return;
}
my $sql = "SELECT jtalkid, posterid, state, datepost FROM talk2 ".
"WHERE journalid=$u->{'userid'} AND jtalkid=$re_talkid ".
"AND nodetype='L' AND nodeid=$entry->{'jitemid'}";
foreach my $pass (1, 2) {
my $db = $pass == 1 ? LJ::get_cluster_reader($u) : LJ::get_cluster_def_reader($u);
$parpost = $db->selectrow_hashref($sql);
last if $parpost;
}
unless ($parpost and $parpost->{'state'} ne 'D') {
$opts->{'handler_return'} = 404;
return;
}
if ($parpost->{'state'} eq 'S' && !LJ::Talk::can_unscreen($remote, $u, $s2entry->{'poster'}->{'username'}, undef)) {
$opts->{'handler_return'} = 403;
return;
}
if ($parpost->{'state'} eq 'F') {
# frozen comment, no replies allowed
# FIXME: eventually have S2 ErrorPage to handle this and similar
# For now, this hack will work; this error is pretty uncommon anyway.
$opts->{status} = "403 Forbidden";
return "<p>This thread has been frozen; no more replies are allowed.</p>";
}
my $tt = LJ::get_talktext2($u, $re_talkid);
$parpost->{'subject'} = $tt->{$re_talkid}->[0];
$parpost->{'body'} = $tt->{$re_talkid}->[1];
$parpost->{'props'} =
LJ::load_talk_props2($u, [ $re_talkid ])->{$re_talkid} || {};
if($LJ::UNICODE && $parpost->{'props'}->{'unknown8bit'}) {
LJ::item_toutf8($u, \$parpost->{'subject'}, \$parpost->{'body'}, {});
}
LJ::CleanHTML::clean_comment(\$parpost->{'body'},
{ 'preformatted' => $parpost->{'props'}->{'opt_preformatted'},
'anon_comment' => !$parpost->{posterid} });
my $datetime = DateTime_unix(LJ::mysqldate_to_time($parpost->{'datepost'}));
my ($s2poster, $pu);
my $comment_userpic;
if ($parpost->{'posterid'}) {
$pu = LJ::load_userid($parpost->{'posterid'});
return $opts->{handler_return} = 403 if $pu->{statusvis} eq 'S'; # do not show comments by suspended users
$s2poster = UserLite($pu);
# FIXME: this is a little heavy:
$comment_userpic = Image_userpic($pu, 0, $parpost->{'props'}->{'picture_keyword'});
}
my $dtalkid = $re_talkid * 256 + $entry->{'anum'};
$replyto = {
'_type' => 'EntryLite',
'subject' => LJ::ehtml($parpost->{'subject'}),
'text' => $parpost->{'body'},
'userpic' => $comment_userpic,
'poster' => $s2poster,
'journal' => $s2entry->{'journal'},
'metadata' => {},
'permalink_url' => $u->{'_journalbase'} . "/$ditemid.html?view=$dtalkid#t$dtalkid",
'depth' => 1,
'time' => $datetime,
};
}
$p->{'replyto'} = $replyto;
$p->{'form'} = {
'_type' => "ReplyForm",
'_remote' => $remote,
'_u' => $u,
'_ditemid' => $ditemid,
'_parpost' => $parpost,
};
return $p;
}
package S2::Builtin::LJ;
sub ReplyForm__print
{
my ($ctx, $form) = @_;
my $remote = $form->{'_remote'};
my $u = $form->{'_u'};
my $parpost = $form->{'_parpost'};
my $parent = $parpost ? $parpost->{'jtalkid'} : 0;
$S2::pout->(LJ::Talk::talkform({ 'remote' => $remote,
'journalu' => $u,
'parpost' => $parpost,
'replyto' => $parent,
'ditemid' => $form->{'_ditemid'},
'form' => $form }));
}
1;

181
local/cgi-bin/LJ/S2/YearPage.pm Executable file
View File

@@ -0,0 +1,181 @@
#!/usr/bin/perl
#
use strict;
package LJ::S2;
sub YearPage
{
my ($u, $remote, $opts) = @_;
my $p = Page($u, $opts);
$p->{'_type'} = "YearPage";
$p->{'view'} = "archive";
my $user = $u->{'user'};
if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") {
$opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) .
"/calendar" . $opts->{'pathextra'};
return 1;
}
if ($u->{'opt_blockrobots'}) {
$p->{'head_content'} .= LJ::robot_meta_tags();
}
if ($LJ::UNICODE) {
$p->{'head_content'} .= '<meta http-equiv="Content-Type" content="text/html; charset='.$opts->{'saycharset'}."\" />\n";
}
my $get = $opts->{'getargs'};
my $count = LJ::S2::get_journal_day_counts($p);
my @years = sort { $a <=> $b } keys %$count;
my $maxyear = @years ? $years[-1] : undef;
my $year = $get->{'year'}; # old form was /users/<user>/calendar?year=1999
# but the new form is purtier: */calendar/2001
if (! $year && $opts->{'pathextra'} =~ m!^/(\d\d\d\d)/?\b!) {
$year = $1;
}
# else... default to the year they last posted.
$year ||= $maxyear;
$p->{'year'} = $year;
$p->{'years'} = [];
foreach (@years) {
push @{$p->{'years'}}, YearYear($_, "$p->{'base_url'}/$_/", $_ == $p->{'year'});
}
$p->{'months'} = [];
for my $month (1..12) {
push @{$p->{'months'}}, YearMonth($p, {
'month' => $month,
'year' => $year,
});
}
return $p;
}
sub YearMonth {
my ($p, $calmon) = @_;
my ($month, $year) = ($calmon->{'month'}, $calmon->{'year'});
$calmon->{'_type'} = 'YearMonth';
$calmon->{'weeks'} = [];
$calmon->{'url'} = sprintf("$p->{'_u'}->{'_journalbase'}/$year/%02d/", $month);
my $count = LJ::S2::get_journal_day_counts($p);
my $has_entries = $count->{$year} && $count->{$year}->{$month} ? 1 : 0;
$calmon->{'has_entries'} = $has_entries;
my $start_monday = 0; # FIXME: check some property to see if weeks start on monday
my $week = undef;
my $flush_week = sub {
my $end_month = shift;
return unless $week;
push @{$calmon->{'weeks'}}, $week;
if ($end_month) {
$week->{'post_empty'} =
7 - $week->{'pre_empty'} - @{$week->{'days'}};
}
$week = undef;
};
my $push_day = sub {
my $d = shift;
unless ($week) {
my $leading = $d->{'date'}->{'_dayofweek'}-1;
if ($start_monday) {
$leading = 6 if --$leading < 0;
}
$week = {
'_type' => 'YearWeek',
'days' => [],
'pre_empty' => $leading,
'post_empty' => 0,
};
}
push @{$week->{'days'}}, $d;
if ($week->{'pre_empty'} + @{$week->{'days'}} == 7) {
$flush_week->();
my $size = scalar @{$calmon->{'weeks'}};
}
};
my $day_of_week = LJ::day_of_week($year, $month, 1);
my $daysinmonth = LJ::days_in_month($month, $year);
for my $day (1..$daysinmonth) {
# so we don't auto-vivify years/months
my $daycount = $has_entries ? $count->{$year}->{$month}->{$day} : 0;
my $d = YearDay($p->{'_u'}, $year, $month, $day,
$daycount, $day_of_week+1);
$push_day->($d);
$day_of_week = ($day_of_week + 1) % 7;
}
$flush_week->(1); # end of month flag
my $nowval = $year * 12 + $month;
# determine the most recent month with posts that is older than
# the current time $month/$year. gives calendars the ability to
# provide smart next/previous links.
my $maxbefore;
while (my ($iy, $h) = each %$count) {
next if $iy > $year;
while (my $im = each %$h) {
next if $im >= $month;
my $val = $iy * 12 + $im;
if ($val < $nowval && $val > $maxbefore) {
$maxbefore = $val;
$calmon->{'prev_url'} = $p->{'_u'}->{'_journalbase'} . sprintf("/%04d/%02d/", $iy, $im);
$calmon->{'prev_date'} = Date($iy, $im, 0);
}
}
}
# same, except inverse: next month after current time with posts
my $minafter;
while (my ($iy, $h) = each %$count) {
next if $iy < $year;
while (my $im = each %$h) {
next if $im <= $month;
my $val = $iy * 12 + $im;
if ($val > $nowval && (!$minafter || $val < $minafter)) {
$minafter = $val;
$calmon->{'next_url'} = $p->{'_u'}->{'_journalbase'} . sprintf("/%04d/%02d/", $iy, $im);
$calmon->{'next_date'} = Date($iy, $im, 0);
}
}
}
return $calmon;
}
sub YearYear {
my ($year, $url, $displayed) = @_;
return { '_type' => "YearYear",
'year' => $year, 'url' => $url, 'displayed' => $displayed };
}
sub YearDay {
my ($u, $year, $month, $day, $count, $dow) = @_;
my $d = {
'_type' => 'YearDay',
'day' => $day,
'date' => Date($year, $month, $day, $dow),
'num_entries' => $count
};
if ($count) {
$d->{'url'} = sprintf("$u->{'_journalbase'}/$year/%02d/%02d/",
$month, $day);
}
return $d;
}
1;

2164
local/cgi-bin/LJ/TextMessage.pm Executable file

File diff suppressed because it is too large Load Diff

3712
local/cgi-bin/LJ/User.pm Executable file

File diff suppressed because it is too large Load Diff