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

206
local/bin/checkconfig.pl Executable file
View File

@@ -0,0 +1,206 @@
#!/usr/bin/perl
#
my @errors;
my $err = sub {
return unless @_;
die "Problem:\n" . join('', map { " * $_\n" } @_);
};
my %dochecks; # these are the ones we'll actually do
my @checks = ( # put these in the order they should be checked in
"modules",
"modules_print",
"env",
"database"
);
foreach my $check (@checks) { $dochecks{$check} = 1; }
my $only = 0;
arg: foreach my $arg (@ARGV) {
($w, $c) = ($arg =~ /^-(no|only)(.*)/) or die "unknown option $arg";
die "only one '-onlyfoo' option may be specified" if $w eq "only" and $only++;
foreach my $check (@checks) {
if ($check eq $c) {
if ($w eq "only") { %dochecks = ( $check => 1 ); }
else { $dochecks{$check} = 0 }
next arg;
}
}
die "unknown check '$c' (known checks: " . join(", ", @checks) . ")\n";
}
my %modules = (
"DBI" => { 'deb' => 'libdbi-perl', },
"DBD::mysql" => { 'deb' => 'libdbd-mysql-perl', },
"Digest::MD5" => { 'deb' => 'libdigest-md5-perl', },
"Digest::SHA1" => { 'deb' => 'libdigest-sha1-perl', },
"Image::Size" => { 'deb' => 'libimage-size-perl', },
"MIME::Lite" => { 'deb' => 'libmime-lite-perl', },
"MIME::Words" => { 'deb' => 'libmime-perl', },
"Compress::Zlib" => { 'deb' => 'libcompress-zlib-perl', },
"Net::SMTP" => {
'deb' => 'libnet-perl',
'opt' => "Alternative to piping into sendmail to send mail.",
},
"Net::DNS" => {
'deb' => 'libnet-dns-perl',
},
"MIME::Base64" => { 'deb' => 'libmime-base64-perl' },
"URI::URL" => { 'deb' => 'liburi-perl' },
"HTML::Tagset" => { 'deb' => 'libhtml-tagset-perl' },
"HTML::Parser" => { 'deb' => 'libhtml-parser-perl', },
"LWP::Simple" => { 'deb' => 'libwww-perl', },
"LWP::UserAgent" => { 'deb' => 'libwww-perl', },
"GD" => { 'deb' => 'libgd-perl' },
"GD::Graph" => {
'deb' => 'libgd-graph-perl',
'opt' => 'Required to make graphs for the statistics page.',
},
"Mail::Address" => { 'deb' => 'libmailtools-perl', },
"Proc::ProcessTable" => {
'deb' => 'libproc-process-perl',
'opt' => "Better reliability for starting daemons necessary for high-traffic installations.",
},
"RPC::XML" => {
'deb' => 'librpc-xml-perl',
'opt' => 'Required for outgoing XMLRPC support',
},
"SOAP::Lite" => {
'deb' => 'libsoap-lite-perl',
'opt' => 'Required for XML-RPC support.',
},
"Unicode::MapUTF8" => { 'deb' => 'libunicode-maputf8-perl', },
"Storable" => {
'deb' => 'libstorable-perl',
},
"XML::RSS" => {
'deb' => 'libxml-rss-perl',
'opt' => 'Required for retrieving RSS off of other sites (syndication).',
},
"XML::Simple" => {
'deb' => 'libxml-simple-perl',
'ver' => 2.12,
},
"String::CRC32" => {
'deb' => 'libstring-crc32-perl',
'opt' => 'Required for palette-altering of PNG files. Only necessary if you plan to make your own S2 styles that use PNGs, not GIFs.',
},
"Time::HiRes" => { 'deb' => 'libtime-hires-perl' },
"IO::WrapTie" => { 'deb' => 'libio-stringy-perl' },
"XML::Atom" => {
'deb' => 'libxml-atom-perl',
'opt' => 'Required for AtomAPI support.',
},
"Math::BigInt::GMP" => {
'opt' => 'Aides Crypt::DH so it isn\'t crazy slow.',
},
"URI::Fetch" => {
'opt' => 'Required for OpenID support.',
},
"Crypt::DH" => {
'opt' => 'Required for OpenID support.',
},
);
sub check_modules_print {
print "[Printing required Perl Modules...]\n";
foreach my $mod (sort keys %modules) {
print $mod .
($modules{$mod}->{'ver'} ? " >= " . $modules{$mod}->{'ver'} : "") .
($modules{$mod}->{'opt'} ? " -- " . $modules{$mod}->{'opt'} : "") .
"\n";
}
}
sub check_modules {
print "[Checking for Perl Modules....]\n";
my @debs;
foreach my $mod (sort keys %modules) {
my $rv = eval "use $mod;";
if ($@) {
my $dt = $modules{$mod};
if ($dt->{'opt'}) {
print STDERR "Missing optional module $mod: $dt->{'opt'}\n";
} else {
push @errors, "Missing perl module: $mod";
}
push @debs, $dt->{'deb'} if $dt->{'deb'};
next;
}
my $ver_want = $modules{$mod}{ver};
my $ver_got = $mod->VERSION;
if ($ver_want && $ver_got && $ver_got < $ver_want) {
push @errors, "Out of date module: $mod (need $ver_want, $ver_got installed)";
}
}
if (@debs && -e '/etc/debian_version') {
print STDERR "\n# apt-get install ", join(' ', @debs), "\n\n";
}
$err->(@errors);
}
sub check_env {
print "[Checking LJ Environment...]\n";
$err->("\$LJHOME environment variable not set.")
unless $ENV{'LJHOME'};
$err->("\$LJHOME directory doesn't exist ($ENV{'LJHOME'})")
unless -d $ENV{'LJHOME'};
# before ljconfig.pl is called, we want to call the site-local checkconfig,
# otherwise ljconfig.pl might load ljconfig-local.pl, which maybe load
# new modules to implement site-specific hooks.
my $local_config = "$ENV{'LJHOME'}/bin/checkconfig-local.pl";
if (-e $local_config) {
my $good = eval { require $local_config; };
exit 1 unless $good;
}
$err->("No ljconfig.pl file found at $ENV{'LJHOME'}/cgi-bin/ljconfig.pl")
unless -e "$ENV{'LJHOME'}/cgi-bin/ljconfig.pl";
eval { require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl"; };
$err->("Failed to load ljlib.pl: $@") if $@;
# if SMTP_SERVER is set, then Net::SMTP is required, not optional.
if ($LJ::SMTP_SERVER && ! defined $Net::SMTP::VERSION) {
$err->("Net::SMTP isn't available, and you have \$LJ::SMTP_SERVER set.");
}
}
sub check_database {
print "[Checking Database...]\n";
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
my $dbh = LJ::get_dbh("master");
unless ($dbh) {
$err->("Couldn't get master database handle.");
}
foreach my $c (@LJ::CLUSTERS) {
my $dbc = LJ::get_cluster_master($c);
next if $dbc;
$err->("Couldn't get db handle for cluster \#$c");
}
if (%LJ::MOGILEFS_CONFIG && $LJ::MOGILEFS_CONFIG{hosts}) {
print "[Checking MogileFS client.]\n";
my $mog = LJ::mogclient();
die "Couldn't create mogilefs client." unless $mog;
}
}
foreach my $check (@checks) {
next unless $dochecks{$check};
my $cn = "check_".$check;
&$cn;
}
print "All good.\n";
print "NOTE: checkconfig.pl doesn't check everything yet\n";

View File

@@ -0,0 +1,84 @@
#!/usr/bin/perl
# remote user id of user to clean
my $ru_id = 491;
# database connection
my $queue_db = "livejournal";
my $queue_host = "localhost";
my $queue_login = "lj";
my $queue_pass = "lj-upyri";
use strict; # preventing my program from doing bad things
use DBI; # http://dbi.perl.org
$| = 1; # unbuffered (almost) output
my $dbh = DBI->connect(
"DBI:mysql:$queue_db:$queue_host",
$queue_login, $queue_pass,
{RaiseError => 0, AutoCommit => 1}
) || die "Can't open database connection: $DBI::errstr";
my $sth = $dbh->prepare("select * from ljr_remote_users where ru_id = $ru_id");
$sth->execute;
while (my $row = $sth->fetchrow_hashref) {
if ($row->{created_comments_maxid} > 0) {
die "User has created comments. Cannot clean cached comments (IMPLEMENT THIS!)\n";
}
my $sth1 = $dbh->prepare("select * from ljr_cached_users where ru_id = $ru_id");
$sth1->execute();
my $row1 = $sth1->fetchrow_hashref;
print
"You're about to delete cached comments for user [" .
$row1->{remote_username} . "].\n" .
"Please confirm by typing their username: "
;
while (<>) {
my $iu = $_;
if ($iu =~ /\s*(.*)\s*/) {
$iu = $1;
}
if ($iu ne $row1->{remote_username}) {
die "You have to learn to type letters to use this tool.\n";
}
last;
}
$sth1->finish;
print "deleting cached comprops...\n";
$sth1 = $dbh->prepare("select * from ljr_cached_comments where ru_id = $ru_id");
$sth1->execute();
while ($row1 = $sth1->fetchrow_hashref) {
my $sth2 = $dbh->prepare(
"delete from ljr_cached_comprops where cc_id = " . $row1->{cc_id}
);
$sth2->execute();
$sth2->finish;
print ".";
}
$sth1->finish;
print "deleting cached comments...\n";
$sth1 = $dbh->prepare(
"delete from ljr_cached_comments where ru_id = " . $ru_id
);
$sth1->execute();
$sth1->finish;
print "resetting cached counters...\n";
$sth1 = $dbh->prepare(
"update ljr_cached_users
set remote_meta_maxid = 0, cached_comments_maxid = 0
where ru_id = " . $ru_id);
$sth1->execute();
$sth1->finish;
}
$sth->finish;
$dbh->disconnect;

View File

@@ -0,0 +1,94 @@
#!/usr/bin/perl
# database connection
my $queue_db = "livejournal";
my $queue_host = "localhost";
my $queue_login = "lj";
my $queue_pass = "lj-upyri";
use strict; # preventing my program from doing bad things
use DBI; # http://dbi.perl.org
$| = 1; # unbuffered (almost) output
my $dbh = DBI->connect(
"DBI:mysql:$queue_db:$queue_host",
$queue_login, $queue_pass,
{RaiseError => 0, AutoCommit => 1}
) || die "Can't open database connection: $DBI::errstr";
my $username;
my $new_identity;
my $sth = $dbh->prepare(
"select * from identitymap where idtype='O' and identity like 'http://www.livejournal.com/%'");
$sth->execute;
while (my $row = $sth->fetchrow_hashref) {
$username = "";
if ($row->{"identity"} =~ /\/users\/(.+[^\/])\/??/ || $row->{"identity"} =~ /\/\~(.+[^\/])\/??/) {
my $new_id;
my $old_email;
my $new_email;
my $sth2;
$username = $1;
$new_identity = "";
if ($username =~ /^_/ || $username =~ /_$/) {
$new_identity = "http://users.livejournal.com/" . $username . "/";
}
else {
$new_identity = "http://" . $username . ".livejournal.com/";
}
$sth2 = $dbh->prepare("select email from user where userid = " . $row->{"userid"});
$sth2->execute;
if (my $row1 = $sth2->fetchrow_hashref) {
$old_email = $row1->{"email"};
}
$sth2->finish;
$sth2 = $dbh->prepare(
"select * from identitymap where idtype='O' and identity ='" . $new_identity . "'");
$sth2->execute();
if (my $row1 = $sth2->fetchrow_hashref) {
$new_id = $row1->{"userid"};
my $sth3 = $dbh->prepare("select email from user where userid = " . $new_id);
$sth3->execute;
if (my $row2 = $sth3->fetchrow_hashref) {
$new_email = $row2->{"email"};
}
$sth3->finish;
print
$username . "(" .
$row->{"userid"} . ", " . $old_email . "):(" .
$new_id . "," . $new_email . ")\n";
}
$sth2->finish;
if (!$new_id) {
$sth2 = $dbh->prepare(
"update identitymap set identity = '" . $new_identity . "' " .
"where idtype='O' and userid = " . $row->{"userid"}
);
$sth2->execute();
$sth2->finish;
}
else {
if (!$new_email) {
$sth2 = $dbh->prepare("update user set email = '" . $old_email . "' " .
"where userid = " . $new_id);
$sth2->execute();
$sth2->finish;
}
}
}
}
$sth->finish;
$dbh->disconnect;

View File

@@ -0,0 +1,113 @@
#!/usr/bin/perl
use strict;
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
require "$ENV{'LJHOME'}/cgi-bin/talklib.pl";
# remote user id of user to clean
my $ru_id = 27850;
# database connection
my $queue_db = "livejournal";
my $queue_host = "localhost";
my $queue_login = "lj";
my $queue_pass = "lj-upyri";
use strict; # preventing my program from doing bad things
use DBI; # http://dbi.perl.org
$| = 1; # unbuffered (almost) output
my $dbh = DBI->connect(
"DBI:mysql:$queue_db:$queue_host",
$queue_login, $queue_pass,
{RaiseError => 0, AutoCommit => 1}
) || die "Can't open database connection: $DBI::errstr";
my $sth = $dbh->prepare("select * from ljr_remote_users where ru_id = $ru_id");
$sth->execute;
while (my $row = $sth->fetchrow_hashref) {
if (! $row->{ru_id}) {
die "User not found.\n";
}
my $sth1 = $dbh->prepare("select * from ljr_cached_users where ru_id = $ru_id");
$sth1->execute();
my $row1 = $sth1->fetchrow_hashref;
print
"You're about to delete user [" .
$row1->{remote_username} . "].\n" .
"Please confirm by typing their username: "
;
while (<>) {
my $iu = $_;
if ($iu =~ /\s*(.*)\s*/) { $iu = $1; }
if ($iu ne $row1->{remote_username}) {
die "You have to learn to type letters to use this tool.\n";
}
last;
}
$sth1->finish;
print "deleting cached comprops and remote comments...\n";
$sth1 = $dbh->prepare("select * from ljr_cached_comments where ru_id = $ru_id");
$sth1->execute();
while ($row1 = $sth1->fetchrow_hashref) {
my $sth2 = $dbh->prepare("delete from ljr_cached_comprops where cc_id = " . $row1->{cc_id});
$sth2->execute();
$sth2->finish;
$sth2 = $dbh->prepare("delete from ljr_remote_comments where cc_id = " . $row1->{cc_id});
$sth2->execute();
$sth2->finish;
print ".";
}
$sth1->finish;
print "deleting cached comments...\n";
$sth1 = $dbh->prepare("delete from ljr_cached_comments where ru_id = $ru_id");
$sth1->execute();
$sth1->finish;
print "deleting ljr_cached_userpics...\n";
$sth1 = $dbh->prepare("delete from ljr_cached_userpics where ru_id = $ru_id");
$sth1->execute();
$sth1->finish;
print "deleting ljr_cached_users...\n";
$sth1 = $dbh->prepare("delete from ljr_cached_users where ru_id = $ru_id");
$sth1->execute();
$sth1->finish;
print "deleting local entries...\n";
my $lu;
$sth1 = $dbh->prepare("select * from ljr_remote_entries where ru_id = $ru_id");
$sth1->execute();
while ($row1 = $sth1->fetchrow_hashref) {
$lu = LJ::load_userid($row1->{"local_journalid"}) unless $lu;
LJ::delete_entry($lu, $row1->{"local_jitemid"});
}
$sth1->finish;
print "deleting ljr_remote_entries...\n";
$sth1 = $dbh->prepare("delete from ljr_remote_entries where ru_id = $ru_id");
$sth1->execute();
$sth1->finish;
print "deleting ljr_remote_userpics...\n";
$sth1 = $dbh->prepare("delete from ljr_remote_userpics where ru_id = $ru_id");
$sth1->execute();
$sth1->finish;
}
$sth->finish;
$sth = $dbh->prepare("delete from ljr_remote_users where ru_id = $ru_id");
$sth->execute;
$sth->finish;
$dbh->disconnect;

View File

@@ -0,0 +1,26 @@
#!/usr/bin/perl -w
use strict;
use Simple; # corrected LJ::Simple
use XML::Parser;
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
require "$ENV{'LJHOME'}/cgi-bin/talklib.pl";
require "ljr-defaults.pl";
require "ljr-links.pl";
require LJR::Distributed;
require "ipics.pl";
#require "ijournal.pl";
#require "icomments.pl";
my $e = import_pics(
"http://www.livejournal.com",
"sharlei",
"",
"imp_5204",
"", 1);
print $e->{errtext} ."\n" if $e->{err};

View File

@@ -0,0 +1,50 @@
#!/usr/bin/perl -w
#
# replaces text in all journal entries. use with caution.
#
BEGIN {
$ENV{'LJHOME'} = "/home/lj-admin/lj";
};
use strict;
use lib $ENV{'LJHOME'}."/cgi-bin";
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
my $dbh = LJ::get_dbh("master");
my ($journal, $from_text, $to_text, $do) = @ARGV;
die("usage: $0 journal_name from_text to_text")
unless $journal && $from_text && $to_text;
$do = 'no' unless $do;
my $u = LJ::load_user($journal);
die("Invalid journal [$journal]") unless $u;
print "Loaded: $u->{'name'} [$u->{'userid'}] \n";
my $sth = $dbh->prepare("select * from logtext2 where journalid = ?");
$sth->execute($u->{'userid'});
while (my $r = $sth->fetchrow_hashref()) {
if ($r->{'event'} =~ /$from_text/) {
print "journal entry [$r->{'jitemid'}] matches\n";
# print "journal entry [$r->{'jitemid'}] matches:\n$r->{'event'}\n";
$r->{'event'} =~ s/$from_text/$to_text/g;
# print "replaced:\n$r->{'event'}\n\n";
if ($do eq 'process') {
$dbh->do("UPDATE logtext2 set event = ? where journalid=? and jitemid=?",
undef, $r->{'event'}, $r->{'journalid'}, $r->{'jitemid'});
if ($dbh->err) {
die("Error while updating entry [$r->{'jitemid'}]: " . $dbh->errstr);
}
}
}
}
print "do not forget to restart memcache" if $do eq 'process';
print "finished\n";

View File

@@ -0,0 +1,53 @@
#!/usr/bin/perl -w
#
# converts s1usercache and s1stylecache blob fields
# which store Storable::freeze data from ::freeze to ::nfreeze
#
# 14oct07 petya@nigilist.ru
# initial revision
#
sub convert {
my ($dbh, $table, $unique, $field) = @_;
print "convert $table.$field ($unique)\n";
my $sql = "select * from $table;";
my $sth = $dbh->prepare($sql) or die "preparing: ", $dbh->errstr;
$sth->execute or die "executing: ", $dbh->errstr;
while (my $row = $sth->fetchrow_hashref) {
if ($row->{"$field"}) {
my $obj = Storable::thaw($row->{"$field"});
if ($obj) {
print $row->{"$unique"} . "\n";
$dbh->do("UPDATE $table SET $field=? WHERE $unique=?", undef,
Storable::nfreeze($obj), $row->{"$unique"}) ||
die "Error updating $table. Unique id: " . $row->{"$unique"} . "\n";
}
}
}
print "\n";
}
use strict;
use DBI;
use Storable;
$ENV{'LJHOME'} = "/home/lj-admin";
do $ENV{'LJHOME'} . "/lj/cgi-bin/ljconfig.pl";
my $host = $LJ::DBINFO{'master'}->{'host'};
my $user = $LJ::DBINFO{'master'}->{'user'};
my $pwd = $LJ::DBINFO{'master'}->{'pass'};
my $db = "prod_livejournal";
$| = 1; # turn off buffered output
# connect to the database.
my $dbh = DBI->connect( "DBI:mysql:mysql_socket=/tmp/mysql.sock;hostname=$host;port=3306;database=$db", $user, $pwd)
or die "Connecting : $DBI::errstr\n ";
#convert ($dbh, "s1stylecache", "styleid", "vars_stor");
#convert ($dbh, "s1usercache", "userid", "color_stor");
#convert ($dbh, "s1usercache", "userid", "override_stor");

View File

@@ -0,0 +1,128 @@
#!/usr/bin/perl
use strict;
use XMLRPC::Lite;
use Digest::MD5 qw(md5_hex);
use DBI;
use Time::Local;
use lib "$ENV{'LJHOME'}/cgi-bin";
do $ENV{'LJHOME'} . "/cgi-bin/ljconfig.pl";
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
use LJR::Viewuserstandalone;
use LJR::Gate;
use LJR::Distributed;
#
#îÁÓÔÒÏÊËÉ
#
#ó×ÏÊÓÔ×Á ÓÏÅÄÉÎÅÎÉÑ Ó ÂÁÚÏÊ
my $qhost = $LJ::DBINFO{'master'}->{'host'};
my $quser = $LJ::DBINFO{'master'}->{'user'};
my $qpass = $LJ::DBINFO{'master'}->{'pass'};
my $qsock = $LJ::DBINFO{'master'}->{'sock'};
my $qport = $LJ::DBINFO{'master'}->{'port'};
my $dbh = DBI->connect(
"DBI:mysql:mysql_socket=$qsock;hostname=$qhost;port=$qport;database=prod_ljgate",
$quser, $qpass, ) || die localtime(time) . ": Can't connect to database\n";
my $dbhljr = DBI->connect(
"DBI:mysql:mysql_socket=$qsock;hostname=$qhost;port=$qport;database=prod_livejournal",
$quser, $qpass, ) || die localtime(time) . ": Can't connect to database\n";
my $get_our = sub {
my ($userid) = @_;
my $sqh = $dbh->prepare("SELECT * FROM our_user where userid=?");
$sqh->execute($userid);
my $res = $sqh->fetchrow_hashref;
$sqh->finish;
return $res;
};
my $get_alien = sub {
my ($userid) = @_;
my $sqh = $dbh->prepare("SELECT * FROM alien where alienid=?");
$sqh->execute($userid);
my $res = $sqh->fetchrow_hashref;
$sqh->finish;
return $res;
};
my $get_lj_user = sub {
my ($user) = @_;
$user =~ s/\-/\_/g;
my $sqh = $dbhljr->prepare("SELECT * FROM user where user=?");
$sqh->execute($user);
my $res = $sqh->fetchrow_hashref;
$sqh->finish;
return $res;
};
my $count_gated_records = sub {
my ($userid) = @_;
my $sqh = $dbh->prepare("SELECT count(*) FROM rlj_lj_id where userid=?");
$sqh->execute($userid);
my ($res) = $sqh->fetchrow_array;
$sqh->finish;
return $res;
};
my $sqh = $dbh->prepare("SELECT userid,alienid FROM rlj2lj");
$sqh->execute;
my $result;
while ($result = $sqh->fetchrow_hashref) {
my $our = $get_our->($result->{'userid'});
my $alien = $get_alien->($result->{'alienid'});
if ($our && $alien && $alien->{'alienpass'}) {
my $ljuser = $get_lj_user->($our->{'our_user'});
my $ru = LJR::Distributed::get_remote_server("www.livejournal.com");
die $ru->{"errtext"} if $ru->{"err"};
$ru->{'username'} = $alien->{'alien'};
$ru = LJR::Distributed::get_cached_user($ru);
die $ru->{"errtext"} if $ru->{"err"};
print
$our->{'our_user'} .
" -> " .
$alien->{'alien'} . " ($ru->{'ru_id'}) " . "pass: " . $alien->{'alienpass'} .
"\n"
;
my $r = LJR::Distributed::update_export_settings($our->{'our_user'}, $ru->{'ru_id'}, $alien->{'alienpass'});
die $r->{'errtext'} if $r->{'err'};
if ($ljuser) {
print "ljr id: " . $ljuser->{'userid'};
}
else {
print "ljr id: error";
}
print "; ";
my $gated_records = $count_gated_records->($our->{'userid'});
print $gated_records;
print "\n";
# my $xmlrpc = LJR::Gate::Authenticate ("www.livejournal.com",
# $alien->{'alien'}, $alien->{'alienpass'});
# if ($xmlrpc->{'err_text'}) {
# print "err\n";
# }
# else {
# print "ok\n";
# }
}
else {
print
$result->{'userid'} . "($our->{'our_user'})" . " -> " .
$result->{'alienid'} . "($alien->{'alien'})" . "\n"
;
}
}
$sqh->finish;
$dbh->disconnect;
$dbhljr->disconnect;

4596
local/bin/ljrimport/Simple.pm Executable file

File diff suppressed because it is too large Load Diff

647
local/bin/ljrimport/icomments.pl Executable file
View File

@@ -0,0 +1,647 @@
#!/usr/bin/perl -w
use strict;
use Simple; # corrected LJ::Simple
use XML::Parser;
use POSIX;
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
require "ljr-defaults.pl";
require "ljr-links.pl";
require LJR::Distributed;
require LJR::unicode;
require "$ENV{'LJHOME'}/cgi-bin/talklib.pl";
require "ipics.pl";
my $err = sub {
my %res = ();
$res{"err"} = 1;
$res{"errtext"} = join ("\n", @_);
return \%res;
};
my $xmlerrt;
my $xmlerr = sub {
my $expat = shift;
my $cstack = "\ncallstack:";
my $i = 0;
while ( 1 ) {
my $tfunc = (caller($i))[3];
if ($tfunc && $tfunc ne "") {
if ($tfunc !~ /\_\_ANON\_\_/) {
$cstack .= " " . $tfunc;
}
$i = $i + 1;
}
else {
last;
}
}
$xmlerrt = join("\n", @_);
$xmlerrt .= $cstack;
$expat->finish();
};
my $dumpxml = sub {
my ($xdata, $username) = @_;
my $t = `date +"%T"`;
$t = substr($t, 0, length($t) - 1);
open(my $outfile, ">$ENV{'LJHOME'}/logs/err_" . $username . "_" . $t . ".xml");
print $outfile "$xdata";
close($outfile);
};
# kind of global variables
my $DEBUG=1;
my $got_max_commentid;
my $empty_num = 0;
my $ru; # current remote user (the one being cached and imported)
my $cmode;
my $xml_maxid = 0;
my $soft_cached_default_pics;
my $soft_cached_keyworded_pics;
my $posters_without_names; # lj bug workaround: http://rt.livejournal.org/Ticket/Display.html?id=762
sub cstatus_print {
my $statustr = join("", @_);
eval { LJR::Import::import_log($statustr); };
if ($@) {
print $@ . "\n";
}
if ($DEBUG) {
eval { LJR::Import::log_print($statustr); };
if ($@) {
print $statustr . "\n";
}
}
}
# comment processing
my $skip_tags = qr/^(livejournal|comments|usermaps|nextid)$/;
my $maxid = qr/^(maxid)$/;
my $comment_tag = qr/^(comment)$/;
my $usermap_tag = qr/^(usermap)$/;
my $data_tags = qr/^(subject|body|date)$/;
my $prop_tag = qr/^(property)$/;
LJ::load_props("talk");
return $err->("Can't load talkprops.") unless $LJ::CACHE_PROP{talk};
my @tprops;
foreach my $k (keys %{$LJ::CACHE_PROP{talk}}) {
push(@tprops, $k);
}
my $tprops = join("|", @tprops);
my $known_props = qr/^($tprops)$/;
my %comment = (); # current comment structure
my $cid = ""; # current tag (inside comment tag)
my $ctext = ""; # current value of $cid
# export_comments.bml xml handling routines
sub xmlh_comment_start() {
my $expat = shift;
my @params = @_;
if ($params[0] =~ /$skip_tags/) {
# skip valid but meaningless tags
}
elsif ($cmode eq "comment_meta" && $params[0] =~ /$usermap_tag/) {
shift @params; # skip "usermap"
my %usermap = ();
%usermap = @params;
if ($usermap{id} && $usermap{user}) {
my $r = {
serverid => $ru->{serverid},
userid => $usermap{id},
username => $usermap{user},
};
$r = LJR::Distributed::get_cached_user($r);
return $xmlerr->($expat, $r->{errtext}) if $r->{err};
$r = LJR::Distributed::get_imported_user($r);
return $xmlerr->($expat, $r->{errtext}) if $r->{err};
$r = LJR::Distributed::set_cu_field($r, "local_commenterid", $r->{commenterid});
return $xmlerr->($expat, $r->{errtext}) if $r->{err};
if (!$soft_cached_default_pics->{$usermap{id}}) {
my $iu = LJ::load_userid ($r->{local_commenterid});
# cstatus_print(
# "caching default userpic for " . $r->{username} .
# " (" . $r->{local_commenterid} . ":" . $iu->{user} . ")"
# );
my $e = import_pics(
$ru->{servername},
$usermap{user},
"",
$iu->{user},
"", 1);
return $xmlerr->($expat, "importing default userpic for [" . $usermap{user} . "]:", $e->{errtext})
if $e->{err};
$soft_cached_default_pics->{$usermap{id}} = 1;
}
}
elsif ($usermap{id} && !$usermap{user}) {
$posters_without_names->{$usermap{id}} = 1;
}
else {
return $xmlerr->($expat,
"Unknown XML-structure: " . join (" ", @params),
"at line " . $expat->current_line);
}
}
elsif ($params[0] =~ /$comment_tag/) {
# we're starting to process new comment
shift @params;
%comment = ();
%comment = @params; # grab all comment attributes
}
elsif ($cmode eq "comment_body" && $params[0] =~ /$data_tags/) {
$cid = $params[0];
$ctext = "";
}
elsif ($cmode eq "comment_body" && $params[0] =~ /$prop_tag/) {
shift @params; # skip "property"
# skip "name" attribute name
if (shift @params && $params[0] =~ /$known_props/) {
$cid = $params[0];
$ctext = "";
}
}
elsif ($params[0] =~ /$maxid/) {
$ctext = "";
}
else {
return $xmlerr->($expat,
"Unknown XML-structure: " . join (" ", @params),
"at line " . $expat->current_line);
}
}
sub xmlh_comment_end() {
my $expat = shift;
my @params = @_;
if ($params[0] =~ /$skip_tags/) {
# almost finished
}
elsif ($cmode eq "comment_meta" && $params[0] =~ /$usermap_tag/) {
# nop
}
elsif ($params[0] =~ /$comment_tag/) {
if ($cmode eq "comment_body") {
# print $comment{"id"} . "\n";
# print "COMMENT\n";
# while ((my $k, my $v) = each(%comment)) {
# print $k . ":" . $v . "\n";
# }
# print "/COMMENT\n";
$comment{ru_id} = $ru->{ru_id};
if (
$comment{props} &&
$comment{props}->{"picture_keyword"} &&
$comment{posterid} &&
!$soft_cached_keyworded_pics->{$comment{posterid}}->{$comment{props}->{"picture_keyword"}}
) {
my $r = {
serverid => $ru->{serverid},
userid => $comment{posterid},
};
$r = LJR::Distributed::get_cached_user($r);
return $xmlerr->($expat, $r->{errtext}) if $r->{err};
$r = LJR::Distributed::get_imported_user($r);
return $xmlerr->($expat, $r->{errtext} . "(userid: " . $comment{'posterid'} . ")") if $r->{err};
$r = LJR::Distributed::get_cu_field($r, "local_commenterid");
return $xmlerr->($expat, $r->{errtext}) if $r->{err};
my $iu = LJ::load_userid ($r->{local_commenterid});
#cstatus_print ("caching userpic " . $comment{props}->{"picture_keyword"} . " for " . $r->{username} . ":" . $iu->{user});
my $e = import_pics (
$ru->{servername},
$r->{username},
"",
$iu->{user},
$comment{props}->{"picture_keyword"},
0);
return $xmlerr->($expat, $e->{errtext}) if $e->{err};
$soft_cached_keyworded_pics->{$comment{posterid}}->{$comment{props}->{"picture_keyword"}} = 1;
}
LJR::Links::make_ljr_hrefs(
LJR::Links::get_server_url($ru->{"servername"}, "base"),
$ru->{"servername"}, \$comment{body}
);
if ($comment{'posterid'} && $posters_without_names->{$comment{'posterid'}}) {
$comment{'posterid'} = undef;
}
if (!$comment{'body'} && $comment{'state'} ne "D") {
$comment{'body'} = "LJR::Import warning: no comment body during import.";
}
my $c = LJR::Distributed::cache_comment (\%comment);
return $xmlerr->($expat, $c->{'errtext'}) if $c->{'err'};
if (!$ru->{cached_comments_maxid} ||
$comment{id} > $ru->{cached_comments_maxid}) {
$ru->{cached_comments_maxid} = $comment{id};
}
}
$got_max_commentid++;
$empty_num = 0;
}
elsif ($params[0] =~ /$data_tags/) {
$comment{$cid} = $ctext;
}
elsif ($params[0] =~ /$prop_tag/) {
$comment{props}->{$cid} = $ctext;
}
elsif ($params[0] =~ /$maxid/) {
$xml_maxid = $ctext;
if ($cmode eq "comment_body" && $xml_maxid > $ru->{remote_meta_maxid}) {
my $tmid = $got_max_commentid;
my $txid = $xml_maxid;
my $tempty = $empty_num;
$got_max_commentid = $ru->{remote_meta_maxid};
my $e = get_usermaps_cycled(
$ru->{servername},
$ru->{username},
$ru->{pass},
$got_max_commentid + 1);
return $xmlerr->($expat, $e->{errtext}) if $e->{err};
# restore comment_body xml-parsing mode
$xml_maxid = $txid;
$got_max_commentid = $tmid;
$empty_num = $tempty;
$cmode = "comment_body";
}
}
else {
return $xmlerr->($expat,
"Unknown tag: " . join (" ", @params),
"at line " . $expat->current_line
);
}
}
sub xmlh_comment_char() {
my $expat = shift;
my $tt = join("", @_);
$ctext = $ctext . $tt;
}
sub get_usermaps_cycled {
my ($server, $username, $pass, $startid) = @_;
my $comments_map = {};
my $do_login = 1;
$LJ::Simple::network_retries = $LJR::NETWORK_RETRIES;
$LJ::Simple::network_sleep = $LJR::NETWORK_SLEEP;
$LJ::Simple::LJ_Client = $LJR::LJ_CLIENT;
$LJ::Simple::UserAgent = $LJR::USER_AGENT;
my $i = 0;
my $remote_lj;
while (1) {
if ($do_login) {
$remote_lj = new LJ::Simple ({
site => $server,
user => $username,
pass => $pass,
pics => 0,
moods => 0,
});
return $err->("Can't login to remote site.", $LJ::Simple::error) unless defined $remote_lj;
if (!$remote_lj->GenerateCookie()) {
if (!$remote_lj->GenerateCookie()) {
return $err->("Can't generate login cookie.", $LJ::Simple::error);
}
}
$do_login = 0;
}
# do not process those which were processed once
if ($comments_map->{$startid}) {
$startid++;
next;
}
my $res = $remote_lj->GetRawData(
{"url" => "/export_comments.bml?get=comment_meta&startid=" . $startid}
);
if ($res && $res->{content}) {
$cmode = "comment_meta";
my $xdata = $res->{content};
LJR::unicode::force_utf8(\$xdata);
eval { LJ::text_out(\$xdata); };
my $p1 = new XML::Parser (
Handlers => {
Start => \&xmlh_comment_start,
End => \&xmlh_comment_end,
Char => \&xmlh_comment_char
});
$xml_maxid = 0;
$xmlerrt = "";
eval { $p1->parse($xdata); };
if ($@) {
if ($i < $LJR::NETWORK_RETRIES) {
if ($xdata =~ /Login Required/) {
$do_login = 1;
}
$i++;
LJR::NETWORK_SLEEP;
next;
}
else {
$dumpxml->($xdata, $username);
return $err->("Runtime error parsing XML (meta, $startid): ", $@);
}
}
if ($xmlerrt) {
$dumpxml->($xdata, $username);
return $err->("Error parsing XML (meta, $startid): ", $xmlerrt);
}
# xml was processed successfully
$comments_map->{$startid} = 1;
cstatus_print ("prefetched $got_max_commentid (skipped $empty_num) of $xml_maxid comments");
if ($got_max_commentid + $empty_num < $xml_maxid) {
$empty_num++;
$startid = $got_max_commentid + $empty_num;
next;
}
else {
$got_max_commentid = 0 unless $got_max_commentid;
$ru = LJR::Distributed::set_cu_field($ru, "remote_meta_maxid", $got_max_commentid);
return $err->($ru->{errtext}) if $ru->{err};
return undef;
}
}
else {
if ($i < $LJR::NETWORK_RETRIES) {
LJR::NETWORK_SLEEP; $i++; next;
}
else {
return $err->("can't get comments metadata: " . $LJ::Simple::error);
}
}
}
}
sub get_usermaps {
my ($server, $username, $pass, $startid) = @_;
$ru = LJR::Distributed::get_remote_server($server);
return $err->($ru->{"errtext"}) if $ru->{"err"};
$ru->{username} = $username;
$ru->{pass} = $pass;
$ru = LJR::Distributed::get_cached_user($ru);
return $err->($ru->{"errtext"}) if $ru->{"err"};
$got_max_commentid = $startid - 1;
cstatus_print ("caching commented users.");
my $e = get_usermaps_cycled($server, $username, $pass, $startid);
return $err->($e->{errtext}) if $e->{err};
}
sub get_comments_cycled {
my ($server, $username, $pass, $startid) = @_;
my $comments_map = {};
my $do_login = 1;
$ru = LJR::Distributed::get_remote_server($server) unless $ru->{serverid};
return $err->($ru->{"errtext"}) if $ru->{"err"};
$ru->{username} = $username;
$ru = LJR::Distributed::get_cached_user($ru);
return $err->($ru->{"errtext"}) if $ru->{"err"};
$LJ::Simple::network_retries = $LJR::NETWORK_RETRIES;
$LJ::Simple::network_sleep = $LJR::NETWORK_SLEEP;
$LJ::Simple::LJ_Client = $LJR::LJ_CLIENT;
$LJ::Simple::UserAgent = $LJR::USER_AGENT;
my $i = 0;
my $h_counter;
my $remote_lj;
while (1) {
if ($do_login) {
$remote_lj = new LJ::Simple ({
site => $server,
user => $username,
pass => $pass,
pics => 0,
moods => 0,
});
return $err->("Can't login to remote site.", $LJ::Simple::error) unless defined $remote_lj;
if (!$remote_lj->GenerateCookie()) {
if (!$remote_lj->GenerateCookie()) {
return $err->("Can't generate login cookie.", $LJ::Simple::error);
}
}
$do_login = 0;
}
# do not process those which were processed once
if ($comments_map->{$startid}) {
$startid++;
next;
}
my $res = $remote_lj->GetRawData(
{"url" => "/export_comments.bml?get=comment_body&props=1&startid=" . $startid}
);
if ($res && $res->{content}) {
my $xdata = $res->{content};
LJR::unicode::force_utf8(\$xdata);
eval { LJ::text_out(\$xdata); };
$cmode = "comment_body";
my $p1 = new XML::Parser (
Handlers => {
Start => \&xmlh_comment_start,
End => \&xmlh_comment_end,
Char => \&xmlh_comment_char
});
$xmlerrt = "";
eval { $p1->parse($xdata); };
if ($@) {
if ($i < $LJR::NETWORK_RETRIES) {
if ($xdata =~ /Login Required/) {
$do_login = 1;
}
$i++;
LJR::NETWORK_SLEEP;
next;
}
else {
$dumpxml->($xdata, $username);
return $err->("Runtime error parsing XML (body, $startid): ", $@);
}
}
if ($xmlerrt) {
$dumpxml->($xdata, $username);
return $err->("Error parsing XML (body, $startid): ", $xmlerrt);
}
# remember last cached comment number (which is equal to its id)
$ru = LJR::Distributed::set_cu_field(
$ru, "cached_comments_maxid",
$ru->{cached_comments_maxid});
return $err->($ru->{errtext}) if $ru->{err};
# xml was processed successfully
$comments_map->{$startid} = 1;
cstatus_print ("getting comments. last id: $ru->{cached_comments_maxid}, skipping: $empty_num, just walked: $startid, max: $ru->{remote_meta_maxid}");
if ($ru->{cached_comments_maxid} + $empty_num < $ru->{remote_meta_maxid}) {
if ($empty_num > 0) {
$empty_num =
(POSIX::floor($ru->{cached_comments_maxid} / 100) + 1) * 100 -
$ru->{cached_comments_maxid} +
$h_counter * 100;
$h_counter++;
}
else {
$empty_num++;
$h_counter = 0;
}
$startid = $ru->{cached_comments_maxid} + $empty_num;
next;
}
else {
return undef;
}
}
else {
if ($i < $LJR::NETWORK_RETRIES) {
LJR::NETWORK_SLEEP; $i++; next;
}
else {
return $err->("can't get comments: " . $LJ::Simple::error);
}
}
}
}
sub get_comments {
my ($server, $username, $pass, $startid) = @_;
cstatus_print ("caching comments");
LJ::disconnect_dbs();
$soft_cached_keyworded_pics = {};
$soft_cached_default_pics = {};
$posters_without_names = {};
$ru = LJR::Distributed::get_remote_server($server);
return $err->($ru->{"errtext"}) if $ru->{"err"};
$ru->{username} = $username;
$ru->{pass} = $pass;
$ru = LJR::Distributed::get_cached_user($ru);
return $err->($ru->{"errtext"}) if $ru->{"err"};
my $e; # for possible errors
$ru = LJR::Distributed::get_cu_field($ru, "cached_comments_maxid");
$ru->{cached_comments_maxid} = 0 if not defined $ru->{cached_comments_maxid};
# don't want to download cached comments again
$startid = $ru->{cached_comments_maxid} + 1
if $ru->{cached_comments_maxid} > $startid;
$ru = LJR::Distributed::get_cu_field($ru, "remote_meta_maxid");
$ru->{remote_meta_maxid} = 0 if not defined $ru->{remote_meta_maxid};
# try to minimize possible further delays
$got_max_commentid = $ru->{remote_meta_maxid};
$e = get_usermaps_cycled($server, $username, $pass, $got_max_commentid + 1);
return $err->($e->{errtext}) if $e->{err};
# get remote comments and cache them
$got_max_commentid = $startid - 1;
$e = get_comments_cycled($server, $username, $pass, $startid);
return $err->($e->{errtext}) if $e->{err};
$soft_cached_keyworded_pics = {};
$soft_cached_default_pics = {};
$posters_without_names = {};
return undef;
}
sub create_imported_comments {
my ($remote_site, $remote_user, $local_user) = @_;
LJ::disconnect_dbs();
my $ru = LJR::Distributed::get_remote_server($remote_site);
return $err->($ru->{"errtext"}) if $ru->{"err"};
$ru->{username} = $remote_user;
$ru = LJR::Distributed::get_cached_user($ru);
return $err->($ru->{"errtext"}) if $ru->{"err"};
cstatus_print("creating comments.");
$ru = LJR::Distributed::create_imported_comments($ru, $local_user);
return $err->($ru->{"errtext"}) if $ru->{"err"};
return undef;
}
return 1;

839
local/bin/ljrimport/ijournal.pl Executable file
View File

@@ -0,0 +1,839 @@
#!/usr/bin/perl -w
use strict;
use Simple; # corrected LJ::Simple
use POSIX;
use XML::Parser;
use Unicode::String;
use Unicode::MapUTF8 qw(to_utf8 from_utf8 utf8_supported_charset);
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
require "ljr-defaults.pl";
require "ljr-links.pl";
require LJR::Distributed;
require LJR::unicode;
require LWPx::ParanoidAgent;
require "ipics.pl";
require "$ENV{'LJHOME'}/cgi-bin/ljprotocol.pl";
my $DEBUG=1;
# shared variables (between flat and xml mode)
my $ru; # remote user
my %rfg=(); # remote friend groups
my ($ritem_id, $ranum, $rhtml_id); # remote entry ids
my $local_u; # local user being imported into
my $flags; # ljprotocol.pl flags
my $do_overwrite; # overwrite entries
my $warns;
# flat mode parameters
my $REMOTE_MAX_GET = 50; # livejournal.com lets us download no more than 50 events for a given day
# XML mode functions and variables
my $xmlerrt;
my $xmlerr = sub {
my $expat = shift;
my $cstack = "\ncallstack:";
my $i = 0;
while ( 1 ) {
my $tfunc = (caller($i))[3];
if ($tfunc && $tfunc ne "") {
if ($tfunc !~ /\_\_ANON\_\_/) {
$cstack .= " " . $tfunc;
}
$i = $i + 1;
}
else {
last;
}
}
$xmlerrt = join("\n", @_);
$xmlerrt .= $cstack;
$expat->finish();
};
my $dumptofile = sub {
my ($fdata, $filename, $ext) = @_;
my $t = `date +"%T"`;
$t = substr($t, 0, length($t) - 1);
open(my $outfile, ">$ENV{'LJHOME'}/logs/" . $filename . "_" . $t . $ext);
print $outfile "$fdata";
close($outfile);
};
my %xentry = (); # current entry
my $ctext = ""; # current field value
my $root_tags = qr/^(livejournal)$/;
my $entry_tag = qr/^(entry)$/;
my $entry_data_tag = qr/^(itemid|eventtime|logtime|subject|event|security|allowmask|current_music|current_mood)$/;
# error handling
my $err = sub {
my %res = ();
$res{"err"} = 1;
$res{"errtext"} = join ("\n", @_);
if ($warns) {
$res{"warns"} = $warns;
}
return \%res;
};
my $warn = sub {
print "WARNING: " . join ("\n", @_);
print "\n";
if (!$warns || length($warns) < 255) {
$warns .= $warns . join(" ", @_);
if (substr($warns, 0, 244) ne $warns) {
$warns = substr($warns, 0, 244) + "; and more";
}
}
};
sub jstatus_print {
my $statustr = join("", @_);
if ($DEBUG) {
eval { LJR::Import::import_log($statustr); };
if ($@) {
print $@ . "\n";
print $statustr . "\n";
}
}
}
# overwrite entry
sub check_overwrite {
my ($local_u, $ru_id, $ritem_id, $overwrite) = @_;
my $r = LJR::Distributed::get_local_itemid ($local_u, $ru_id, $ritem_id. "I");
return $err->($r->{"errtext"}) if $r->{"err"};
if ($r->{"itemid"} && $overwrite eq "1") {
my %req = (
'username' => $local_u->{'user'},
'ownerid' => $local_u->{'user'},
'clientversion' => $LJR::LJ_CLIENT,
'ver' => $LJ::PROTOCOL_VER,
'selecttype' => 'one',
'itemid' => $r->{"itemid"},
'getmenus' => 0,
'lineendings' => "unix",
'truncate' => 0,
);
my $err1;
my $items = LJ::Protocol::do_request("getevents", \%req, \$err1, $flags);
if ($err1) {
my $errstr = LJ::Protocol::error_message($err1);
return $err->($errstr);
}
my $h = @{$items->{events}}[0];
LJ::delete_entry($local_u, $h->{itemid});
$r = LJR::Distributed::get_local_itemid ($local_u, $ru_id, $ritem_id, "I");
return $err->($r->{"errtext"}) if $r->{"err"};
}
elsif ($r->{"itemid"} && $overwrite eq "0") {
return {"continue" => 0};
}
return {"continue" => 1};
}
# XML handlers
sub xmlh_entry_start() {
my $expat = shift;
my @params = @_;
if ($params[0] =~ /$root_tags/) {
# skip valid but meaningless tags
}
elsif ($params[0] =~ /$entry_tag/) {
# we're starting to process new entry
shift @params;
%xentry = ();
}
elsif ($params[0] =~ /$entry_data_tag/) {
$ctext = "";
}
else {
return $xmlerr->($expat,
"Unknown XML-structure: " . join (" ", @params),
"at line " . $expat->current_line);
}
}
sub xmlh_entry_end() {
my $expat = shift;
my @params = @_;
if ($params[0] =~ /$root_tags/) {
# almost finished
}
elsif ($params[0] =~ /$entry_tag/) {
my $xe = xml_create_entry(\%xentry);
return $xmlerr->($expat, "xml_create_entry: " . $xe->{errtext}) if $xe && $xe->{err};
}
elsif ($params[0] =~ /$entry_data_tag/) {
$xentry{$params[0]} = $ctext;
# print $params[0] . " => " . $ctext . "\n";
}
else {
return $xmlerr->($expat,
"Unknown tag: " . join (" ", @params),
"at line " . $expat->current_line
);
}
}
sub xmlh_entry_char() {
my $expat = shift;
my $tt = join("", @_);
$ctext = $ctext . $tt;
}
# should be called after populating shared variables (see section above)
sub xml_create_entry {
my ($xentry) = @_;
return $err->("XML import: can't extract remote itemid.") unless $xentry->{"itemid"};
$ritem_id = int($xentry->{"itemid"} / 256); # export.bml returns html_id instead of item_id
my $is_gated = LJR::Distributed::get_local_itemid($local_u, $ru->{'ru_id'}, $ritem_id, "E");
return $err->($is_gated->{"errtext"}) if $is_gated->{"err"};
return {"err" => 0} if $is_gated->{'itemid'};
my $r = check_overwrite($local_u, $ru->{'ru_id'}, $ritem_id, $do_overwrite);
return $err->($r->{"errtext"}) if $r->{"err"};
return unless $r->{"continue"};
my ($min,$hour,$mday,$mon,$year);
if ($xentry->{"eventtime"} =~ /(\d\d\d\d)\-(\d\d)\-(\d\d)\ (\d\d)\:(\d\d)/o) {
$year = $1;
$mon = $2;
$mday = $3;
$hour = $4;
$min = $5;
}
else {
return $err->("XML import: can't extract eventtime. remote itemid = " . $ritem_id);
}
my $moodid;
if ($xentry->{"current_mood"}) {
$moodid = LJ::mood_id($xentry->{"current_mood"});
}
LJR::Links::make_ljr_hrefs(
LJR::Links::get_server_url($ru->{"servername"}, "base"),
$ru->{"servername"}, \$xentry->{"event"}
);
# LJR::unicode::utf8ize(\$xentry->{"event"});
# LJR::unicode::utf8ize(\$xentry->{"subject"});
# LJR::unicode::utf8ize(\$xentry->{"current_mood"});
# LJR::unicode::utf8ize(\$xentry->{"current_music"});
# LJ now exports lj-polls (previously
# they exported only links to polls)
$xentry->{'event'} =~ s/<lj-poll>.+<\/lj-poll>//sog;
my %req = (
'mode' => 'postevent',
'ljr-import' => 1,
'ver' => $LJ::PROTOCOL_VER,
'clientversion' => $LJR::LJ_CLIENT,
'user' => $local_u->{'user'},
'username' => $local_u->{'user'},
'usejournal' => $local_u->{'user'},
'getmenus' => 0,
'lineendings' => "unix",
'event' => $xentry->{"event"},
'subject' => $xentry->{"subject"},
'year' => $year,
'mon' => $mon,
'day' => $mday,
'hour' => $hour,
'min' => $min,
'props' => {
'current_moodid' => $moodid,
'current_mood' => $xentry->{"current_mood"},
'current_music' => $xentry->{"current_music"},
'opt_preformatted' => 0,
'opt_nocomments' => 0,
'taglist' => "",
'picture_keyword' => "",
'opt_noemail' => 0,
'unknown8bit' => 0,
'opt_backdated' => 1,
},
);
if ($xentry->{"security"} eq "public" || $xentry->{"security"} eq "private") {
$req{'security'} = $xentry->{"security"};
$req{'allowmask'} = 0;
}
elsif ($xentry->{"security"} eq "usemask" && $xentry->{"allowmask"} == 1) {
$req{'security'} = 'usemask';
$req{'allowmask'} = 1;
}
else {
$req{'security'} = 'usemask';
my @groups = ();
foreach my $grp_id (keys %rfg) {
if ($xentry->{"allowmask"}+0 & 1 << $grp_id) {
push @groups, $rfg{$grp_id}->{name};
}
}
my $mask = 0;
while (my $grpname = shift @groups) {
my $group = LJ::get_friend_group($local_u, {'name' => $grpname});
if ($group) {
$mask = $mask | (1 << $group->{groupnum});
}
}
$req{'allowmask'} = $mask;
}
my %res = ();
LJ::do_request(\%req, \%res, $flags);
if ($res{"success"} ne "OK" && $res{"errmsg"} =~ "Missing required argument") {
$warn->($res{"errmsg"} . " while processing " . $xentry->{"eventtime"});
return;
}
if ($res{"success"} ne "OK" && $res{"errmsg"} =~ "Post too large") {
$dumptofile->($req{'event'}, "large_" . $local_u->{'user'}, ".raw");
}
return $err->($xentry->{"eventtime"} . ": " . $res{"errmsg"}) unless $res{"success"} eq "OK";
$r = LJR::Distributed::store_remote_itemid(
$local_u,
$res{"itemid"},
$ru->{ru_id},
$ritem_id,
$xentry->{"itemid"});
return $err->($xentry->{"eventtime"} . ": " . $r->{"errtext"}) if $r->{"err"};
return {"err" => 0};
}
# do the actual import
sub import_journal {
my (
$throttle_speed,
$remote_site, $remote_protocol, $remote_user, $remote_pass, $remote_shared_journal,
$local_user, $local_shared_journal, $overwrite
) = @_;
$do_overwrite = $overwrite;
LJ::disconnect_dbs(); # force reconnection to the database
if ($remote_shared_journal eq "") {
$remote_shared_journal = undef;
}
if ($local_shared_journal eq "") {
$local_shared_journal = undef;
}
my %gdc_hr = ();
my %req = ();
my %lfg = ();
my %res = ();
if ($remote_protocol ne "flat" && $remote_protocol ne "xml") {
return $err->("Unsupported remote protocol $remote_protocol.");
}
$LJ::Simple::network_retries = $LJR::NETWORK_RETRIES;
$LJ::Simple::network_sleep = $LJR::NETWORK_SLEEP;
$LJ::Simple::LJ_Client = $LJR::LJ_CLIENT;
$LJ::Simple::UserAgent = $LJR::USER_AGENT;
# login to the remote site
my $remote_lj = new LJ::Simple ({
site => $remote_site,
user => $remote_user,
pass => $remote_pass,
pics => 0,
moods => 0,
});
if (! defined $remote_lj) {
return $err->("Can't login to remote site.", $LJ::Simple::error);
}
if (!$remote_lj->GenerateCookie()) {
if (!$remote_lj->GenerateCookie()) {
return $err->("Can't generate login cookie.", $LJ::Simple::error);
}
}
# since we're able to login with supplied credentials --
# get and/or cache remote server and remote user ident
$ru = LJR::Distributed::get_remote_server($remote_site);
return $err->($ru->{"errtext"}) if $ru->{"err"};
# try to get userid
my $idres;
my $i1 = 0;
while(1) {
my $ua = LWPx::ParanoidAgent->new(timeout => 60);
$ua->agent($LJR::USER_AGENT);
# TODO: parameterize allpics.bml
my $url = $ru->{"servername"} . "/users/" . $remote_user . "/info/" ;
$idres = $ua->get($url);
if (!($idres && ($idres->is_success || $idres->code == 403)) && $i1 < $LJR::NETWORK_RETRIES) {
my $txt;
#foreach my $k (keys %$idres) {
# $txt .= $k . "->(" . $idres->{$k} ."), ";
#}
###_content->(500 DNS lookup timeout), _rc->(500), _headers->(HTTP::Headers=HASH(0x2d2ec70)), _msg->(DNS lookup timeout), _request->(HTTP::Request=HASH(0x2c61ac0)),
$txt .= "_msg->" . $idres->{'_msg'} . ", ";
foreach my $k (keys %{$idres->{'_headers'}}) {
$txt .= "\n" . $k . ": " . $idres->{'_headers'}->{$k} ;
}
print STDERR "*** $url $txt\n";
LJR::NETWORK_SLEEP(); $i1++; next;
}
else { last; }
}
if (!($idres && ($idres->is_success || $idres->code == 403))) {
return $err->("LWPx: Can't get remote user id: $remote_user\n");
}
if ($idres->content && $idres->content =~ /\<b\>$remote_user\<\/b\>\<\/a\>\ \((\d+)\)/s) {
$ru->{"userid"} = $1;
}
$ru->{"username"} = $remote_user;
$ru = LJR::Distributed::get_cached_user($ru); # populates $ru->{ru_id}
return $err->($ru->{"errtext"}) if $ru->{"err"};
# get local user object for user being imported into
$local_u = LJ::load_user($local_user, 1);
return $err->("Can't load local user $local_user.") unless $local_u;
$ru = LJR::Distributed::remote_local_assoc($ru, $local_u);
return $err->("error while getting remote-local association: " . $ru->{errtext})
if $ru->{err};
jstatus_print ("getting userpics");
my $e = import_pics(
$ru->{servername},
$ru->{username},
$remote_pass,
$local_user,
"", 0);
return $err->("Can't import " . $ru->{username} . ": " . $e->{errtext})
if $e->{err};
# clear duplicate protection
LJ::set_userprop($local_u, "dupsig_post", undef);
# needed everywhere
$flags = {
'u' => $local_u,
'noauth' => 1,
'BMAX_EVENT' => 150000,
'CMAX_EVENT' => 150000,
'no-cache' => 1,
'omit_underscore_check' => 1,
};
%req = ( 'mode' => 'login',
'ver' => $LJ::PROTOCOL_VER,
'clientversion' => $LJR::LJ_CLIENT,
'user' => $local_u->{'user'},
'getmenus' => 0,
);
%res = ();
LJ::do_request(\%req, \%res, $flags);
return $err->($res{'errmsg'}) unless $res{'success'} eq 'OK';
jstatus_print ("getting friend groups");
# get remote and local friend groups, mix them up, update on local server
if (! defined $remote_lj->GetFriendGroups(\%rfg)) {
return $err->("Failed to get groups on the remote site.", $LJ::Simple::error);
}
LJ::Protocol::do_request(
{
'mode' => 'getfriendgroups',
'user' => $local_u->{'user'},
'ver' => $LJ::PROTOCOL_VER,
'clientversion' => $LJR::LJ_CLIENT,
'includegroups' => 1,
'getmenus' => 0,
},
\%res,
$flags
);
if (! $res{'success'} eq "OK") {
return $err->("Unable to get local user" . $local_u->{'user'} . "groups",
$res{'success'} . ":" . $res{'errmsg'});
}
# convert it to LJ::Simple hash
while((my $k, my $v) = each %res) {
$k=~/^frgrp_([0-9]+)_(.*)$/o || next;
my ($id, $name) = ($1, $2);
if (!exists $lfg{$id}) {
$lfg{$id}={
id => $id,
public => 0,
};
}
($name eq "sortorder") && ($name="sort");
$lfg{$id}->{$name}=$v;
}
# add nonexisting remote groups (identified by name) to local server
foreach my $grp_id (keys %rfg) {
my $e = 0;
foreach my $lg (values %lfg) {
if ($lg->{name} eq $rfg{$grp_id}->{name}) {
$e = 1;
}
if ($lg->{name} =~ /default view/i) {
$e = 1;
}
}
if (!$e) {
my $egroup = 1;
foreach my $cgroup (sort { $a <=> $b } keys %lfg) {
if ($egroup == $cgroup) {
$egroup++;
}
}
if ($egroup < 31) {
$lfg{$egroup} = $rfg{$grp_id};
}
}
}
# create local friend groups (existing + copied)
my $i = 0;
%req = (
'mode' => "editfriendgroups",
'user' => $local_u->{'user'},
'clientversion' => $LJR::LJ_CLIENT,
'ver' => $LJ::PROTOCOL_VER,
);
# convert LJ::Simple hash back to ljprotocol hash
foreach my $grpid (keys %lfg) {
if ($grpid > 0 && $grpid < 31) {
my $pname = "efg_set_" . $grpid . "_name";
$req{$pname} = $lfg{$grpid}->{name};
$i++;
}
}
# do the actual request
LJ::do_request(\%req, \%res, $flags);
if (! $res{'success'} eq "OK") {
return $err->(
"Unable to update local user" . $local_u->{'user'} . "groups",
$res{'success'} . ":" . $res{'errmsg'}
);
}
# get remote days with entries
if (! defined $remote_lj->GetDayCounts(\%gdc_hr, undef)) {
return $err->("can't get day counts: ", $LJ::Simple::error);
}
# import entries by means of export.bml (XML format)
if ($remote_protocol eq "xml") {
my $mydc = {};
foreach (sort {$a<=>$b} keys %gdc_hr) {
my $timestamp = $_;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($timestamp);
$mon++;
$year = $year + 1900;
$mydc->{$year}->{$mon} = 0;
$mydc->{$year}->{$mon} = $mydc->{$year}->{$mon} + $gdc_hr{$timestamp};
}
foreach (sort {$a <=> $b} keys %{$mydc}) {
my $y = $_;
foreach (sort {$a <=> $b} keys %{$mydc->{$y}}) {
jstatus_print ("getting XML data and creating local entries for " . $_ . "/" . $y);
my $do_login = 0;
while (1) {
if ($do_login) {
$remote_lj = new LJ::Simple ({
site => $remote_site,
user => $remote_user,
pass => $remote_pass,
pics => 0,
moods => 0,
});
if (! defined $remote_lj) {
return $err->("Can't login to remote site.", $LJ::Simple::error);
}
if (!$remote_lj->GenerateCookie()) {
if (!$remote_lj->GenerateCookie()) {
return $err->("Can't generate login cookie.", $LJ::Simple::error);
}
}
}
my $res = $remote_lj->GetRawData({
"url" => "/export_do.bml",
"post-data" => {
"authas" => $remote_user, # "nit",
"format" => "xml",
"encid" => 2, # utf-8; for full listing see htdocs/export.bml
"header" => 1,
"year" => $y,
"month" => $_,
"field_itemid" => 1,
"field_eventtime" => 1,
"field_logtime" => 1,
"field_subject" => 1,
"field_event" => 1,
"field_security" => 1,
"field_allowmask" => 1,
"field_currents" => 1,
}});
if ($res && $res->{content}) {
my $xdata = $res->{content};
LJR::unicode::force_utf8(\$xdata);
my $p1 = new XML::Parser (
Handlers => {
Start => \&xmlh_entry_start,
End => \&xmlh_entry_end,
Char => \&xmlh_entry_char
});
eval { $p1->parse($xdata); };
if ($@) {
if ($i < $LJR::NETWORK_RETRIES) {
if ($@ =~ /not\ well\-formed\ \(invalid\ token\)/) {
# $xdata <?xml version="1.0" encoding='windows-1251'?>
}
if ($xdata =~ /Login Required/) {
$do_login = 1;
}
LJR::NETWORK_SLEEP(); $i++; next;
}
else {
$dumptofile->($xdata, "err_" . $remote_user, ".xml");
return $err->("Runtime error while parsing XML data: ", $@);
}
}
if ($xmlerrt) {
$dumptofile->($xdata, "err_" . $remote_user, ".xml");
return $err->("Error while parsing XML data: ", $xmlerrt);
}
last;
}
else {
return $err->("Can't get XML data..");
}
}
}
}
}
# import entries by means of flat protocol
if ($remote_protocol eq "flat") {
# process them, day by day, sleeping a little
foreach (sort {$a<=>$b} keys %gdc_hr) {
my $timestamp = $_;
# download all the entries for a day
if ($gdc_hr{$timestamp} < $REMOTE_MAX_GET) {
jstatus_print (
"getting remote and creating local entries for " .
strftime ("%a %b %e %Y", localtime($timestamp))
);
my %r_entries=(); # remote entries
if (! defined $remote_lj->GetEntries(\%r_entries,$remote_shared_journal,"day",($timestamp))) {
if ($LJ::Simple::error =~ "Cannot display this post") {
$warn->(strftime ("%a %b %e %Y", localtime($timestamp)) . ":" . $LJ::Simple::error);
next;
}
return $err->("can't get remote entries: " . strftime ("%a %b %e %Y", localtime($timestamp)) . ": ",
$LJ::Simple::error);
}
my $rkey=undef;
my $rentry=undef;
my $r;
ENTRIES: while (($rkey, $rentry) = each(%r_entries)) {
($ritem_id, $ranum, $rhtml_id) = $remote_lj->GetItemId($rentry);
my $tevent = $remote_lj->GetEntry($rentry);
my $is_gated = LJR::Distributed::get_local_itemid($local_u, $ru->{'ru_id'}, $ritem_id, "E");
return $err->($is_gated->{"errtext"}) if $is_gated->{"err"};
next ENTRIES if $is_gated->{'itemid'};
$r = check_overwrite($local_u, $ru->{'ru_id'}, $ritem_id, $do_overwrite);
return $err->($r->{"errtext"}) if $r->{"err"};
next ENTRIES unless $r->{"continue"};
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($remote_lj->GetDate($rentry));
$mon++;
$year = $year + 1900;
LJR::Links::make_ljr_hrefs(
LJR::Links::get_server_url($ru->{"servername"}, "base"),
$ru->{"servername"},
\$tevent
);
my $tsubject = $remote_lj->GetSubject($rentry);
my $tcurrent_mood = $remote_lj->Getprop_current_mood($rentry);
my $tcurrent_music = $remote_lj->Getprop_current_music($rentry);
my $ttaglist = $remote_lj->Getprop_taglist($rentry);
$ttaglist = LJ::trim($ttaglist);
my $tpicture_keyword = $remote_lj->Getprop_picture_keyword($rentry);
# LJR::unicode::utf8ize(\$tevent);
# LJR::unicode::utf8ize(\$tsubject);
# LJR::unicode::utf8ize(\$tcurrent_mood);
# LJR::unicode::utf8ize(\$tcurrent_music);
# LJR::unicode::utf8ize(\$ttaglist);
# LJR::unicode::utf8ize(\$tpicture_keyword);
%req = ( 'mode' => 'postevent',
'ljr-import' => 1,
'ver' => $LJ::PROTOCOL_VER,
'clientversion' => $LJR::LJ_CLIENT,
'user' => $local_u->{'user'},
'username' => $local_u->{'user'},
'usejournal' => $local_u->{'user'},
'getmenus' => 0,
'lineendings' => "unix",
'event' => $tevent,
'subject' => $tsubject,
'year' => $year,
'mon' => $mon,
'day' => $mday,
'hour' => $hour,
'min' => $min,
'props' => {
'current_moodid' => $rentry->{prop_current_moodid},
'current_mood' => $tcurrent_mood,
'current_music' => $tcurrent_music,
'opt_preformatted' => $remote_lj->Getprop_preformatted($rentry),
'opt_nocomments' => $remote_lj->Getprop_nocomments($rentry),
'taglist' => $ttaglist,
'picture_keyword' => $tpicture_keyword,
'opt_noemail' => $remote_lj->Getprop_noemail($rentry),
'unknown8bit' => $remote_lj->Getprop_unknown8bit($rentry),
'opt_backdated' => 1,
},
);
my @r_protection = $remote_lj->GetProtect($rentry);
if ($r_protection[0] eq "public" || $r_protection[0] eq "private") {
$req{'security'} = $r_protection[0];
$req{'allowmask'} = 0;
}
elsif ($r_protection[0] eq "friends") {
$req{'security'} = 'usemask';
$req{'allowmask'} = 1;
}
elsif ($r_protection[0] eq "groups") {
$req{'security'} = 'usemask';
shift @r_protection;
my $mask=0;
while (my $grpname = shift @r_protection) {
my $group = LJ::get_friend_group($local_u, {'name' => $grpname});
$mask = $mask | (1 << $group->{groupnum});
}
$req{'allowmask'} = $mask;
}
%res = ();
LJ::do_request(\%req, \%res, $flags);
if ($res{"success"} ne "OK" && $res{"errmsg"} =~ "Post too large") {
$dumptofile->($req{'event'}, "large_" . $local_u->{'user'}, ".raw");
}
if ($res{"success"} ne "OK" && $res{"errmsg"} =~ "Invalid text encoding") {
$warn->($res{"errmsg"});
next;
}
if ($res{"success"} ne "OK" && $res{"errmsg"} =~ "Invalid or malformed tag list") {
return $err->($res{"errmsg"} . ": [$ttaglist]");
}
return $err->($res{'errmsg'}) unless $res{'success'} eq 'OK';
$r = LJR::Distributed::store_remote_itemid(
$local_u,
$res{"itemid"},
$ru->{ru_id},
$ritem_id,
$rhtml_id);
return $err->($r->{"errtext"}) if $r->{"err"};
}
sleep($throttle_speed);
}
else {
$warn->("Too much entries for a day. " . $local_u->{'user'} . " " .
strftime ("%a %b %e %Y", localtime($timestamp))
);
}
} # process them day by day
}
if ($warns) {
my %warns = ('warns' => $warns);
return \%warns;
}
else {
return undef;
}
}
return 1;

533
local/bin/ljrimport/ipics.pl Executable file
View File

@@ -0,0 +1,533 @@
#!/usr/bin/perl
use strict;
use Image::Size ();
use Simple; # corrected LJ::Simple
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
require "ljr-defaults.pl";
require "ljr-links.pl";
require LJR::Distributed;
require LWPx::ParanoidAgent;
# error handling
my $err = sub {
my %res = ();
my $cstack = "\ncallstack:";
my $i = 0;
while ( 1 ) {
my $tfunc = (caller($i))[3];
if ($tfunc && $tfunc ne "") {
if ($tfunc !~ /\_\_ANON\_\_/) {
$cstack .= " " . $tfunc;
}
$i = $i + 1;
}
else {
last;
}
}
$res{"err"} = 1;
$res{"errtext"} = join ("\n", @_);
$res{"errtext"} .= $cstack;
return \%res;
};
# example:
my $DEBUG = 0;
sub cache_remote_pics {
my ($remote_site, $remote_user, $remote_pass, $local_userid) = @_;
my $ua;
my $res;
my %remote_urls = ();
my %remote_keywords = ();
my %remote_comments = ();
my $default_pic = "";
my $i = 0;
my $content;
# get remote pictures list with keywords
if ($remote_pass ne "") {
$LJ::Simple::network_retries = $LJR::NETWORK_RETRIES;
$LJ::Simple::network_sleep = $LJR::NETWORK_SLEEP;
$LJ::Simple::LJ_Client = $LJR::LJ_CLIENT;
$LJ::Simple::UserAgent = $LJR::USER_AGENT;
my $ljs_site = $remote_site;
if ($ljs_site =~ /^http\:\/\/(.*)/) {
$ljs_site = $1;
}
my $remote_lj = new LJ::Simple ({
site => $ljs_site,
user => $remote_user,
pass => $remote_pass,
pics => 0,
moods => 0,
});
return $err->("Can't login to remote site.", $LJ::Simple::error)
unless defined($remote_lj);
if (!$remote_lj->GenerateCookie()) {
return $err->("Can't generate login cookie.", $LJ::Simple::error);
}
$res = $remote_lj->GetRawData({
"url" => "/allpics.bml",
});
if (!($res && $res->{content})) {
return $err->("LJ::Simple: Can't get remote user pictures: $remote_user\n");
}
$content = $res->{content};
}
else {
while(1) {
$ua = LWPx::ParanoidAgent->new(timeout => 60);
$ua->agent($LJR::USER_AGENT);
# TODO: parameterize allpics.bml
$res = $ua->get($remote_site . "/allpics.bml?user=" . $remote_user);
if (!($res && $res->is_success) && $i < $LJR::NETWORK_RETRIES) {
LJR::NETWORK_SLEEP(); $i++; next;
}
else {
last;
}
}
if (!($res && $res->is_success)) {
return $err->("LWPx: Can't get remote user pictures: $remote_user\n");
}
$content = $res->content;
}
my $ru = LJR::Distributed::get_remote_server($remote_site);
return $err->($ru->{"errtext"}) if $ru->{"err"};
$ru->{username} = $remote_user;
$ru = LJR::Distributed::get_cached_user($ru);
$i = 0;
my $dbh = LJ::get_db_writer();
return $err->("Can't get database writer!") unless $dbh;
$dbh->do("DELETE FROM ljr_cached_userpics WHERE ru_id=?", undef, $ru->{ru_id});
return $err->($dbh->errstr) if $dbh->err;
my $iru;
my $userpic_base = LJR::Links::get_server_url($remote_site, "userpic_base");
# extract pic urls and keywords
if ($content =~ m!<\s*?body.*?>(.+)</body>!si) {
$content = $1;
while ($content =~
/\G.*?($userpic_base\/(\d+)\/(\d+))(.*?)($userpic_base\/(\d+)\/(\d+)|$)(.*)/sg
) {
my $picurl = $1;
my $props = $4;
my $cuserid = $3;
my $cpicid = $2;
$content = $5 . $8;
my $is_default = 0;
# save userid
if (!$iru->{ru_id}) {
$iru = LJR::Distributed::get_remote_server($remote_site);
return $err->($ru->{"errtext"}) if $ru->{"err"};
$iru->{username} = $remote_user;
$iru->{userid} = $cuserid;
$iru = LJR::Distributed::get_cached_user($iru);
return $err->($ru->{"errtext"}) if $ru->{"err"};
}
if ($props =~ /(.*?)Keywords\:\<\/b\>\ (.*?)\<br\ \/\>(.*?)\<\/td\>/s) {
$remote_keywords{$picurl} = $2;
$remote_comments{$picurl} = $3;
$remote_comments{$picurl} =~ s/^\s+|\s+$//;
}
if ($props =~ /\<u\>Default\<\/u\>/s) {
$default_pic = $picurl;
$is_default = 1;
}
my @keywords = "";
if ($remote_keywords{$picurl}) {
@keywords = split(/\s*,\s*/, $remote_keywords{$picurl});
@keywords = grep { s/^\s+//; s/\s+$//; $_; } @keywords;
}
elsif ($is_default) {
@keywords = ("");
}
foreach my $kw (@keywords) {
if($remote_urls{$cpicid}) {
$dbh->do("UPDATE ljr_cached_userpics set keyword=?, is_default=?, comments=?
where ru_id=? and remote_picid=?",
undef, $kw, $is_default, $remote_comments{$picurl},
$ru->{ru_id}, $cpicid);
return $err->($dbh->errstr) if $dbh->err;
}
else {
$dbh->do("INSERT INTO ljr_cached_userpics VALUES (?,?,?,?,?)",
undef, $ru->{ru_id}, $cpicid, $kw,
$is_default, $remote_comments{$picurl});
return $err->($dbh->errstr) if $dbh->err;
}
}
$remote_urls{$cpicid} = $picurl;
}
}
return undef;
}
sub import_pics {
my (
$remote_site, $remote_user, $remote_pass,
$local_user, $o_keyword, $o_default
) = @_;
my $MAX_UPLOAD = 40960;
my %remote_ids = ();
my %remote_urls = ();
my %remote_keywords = ();
my %remote_comments = ();
my $default_pic = "";
my $ru = LJR::Distributed::get_remote_server($remote_site);
return $err->($ru->{"errtext"}) if $ru->{"err"};
$ru->{username} = $remote_user;
$ru = LJR::Distributed::get_cached_user($ru);
# load user object (force, otherwise get error outside of apache)
my $u = LJ::load_user($local_user, 1);
return $err->("Invalid local user: " . $local_user) unless $u;
# prepare database connections (for different versions of user objects)
my ($dbcm, $dbcr, $sth);
$dbcm = LJ::get_cluster_master($u);
return $err->("Can't get cluster master!") unless $dbcm;
$dbcr = LJ::get_cluster_def_reader($u);
return $err->("Can't get cluster reader!") unless $dbcr;
my $dbh = LJ::get_db_writer();
return $err->("Can't get database writer!") unless $dbh;
my $dbr = LJ::get_db_reader();
return $err->("Can't get database reader!") unless $dbr;
my $e;
if (!$o_keyword && !$o_default) {
$e = cache_remote_pics($remote_site, $remote_user, $remote_pass, $u->{userid});
return $e if $e->{err};
}
else {
$sth = $dbr->prepare(
"SELECT ru_id FROM ljr_cached_userpics WHERE ru_id=? GROUP BY ru_id");
$sth->execute($ru->{ru_id});
my $ruid = $sth->fetchrow_hashref;
$sth->finish;
if (!$ruid) {
$e = cache_remote_pics($remote_site, $remote_user, $remote_pass, $u->{userid});
return $e if $e->{err};
}
}
# get ru->{userid} which should come up after caching remote pic props
$ru = LJR::Distributed::get_cached_user($ru);
if ($o_keyword) {
$sth = $dbr->prepare(
"SELECT remote_picid, keyword, is_default, comments " .
"FROM ljr_cached_userpics WHERE ru_id=? and keyword=?");
$sth->execute($ru->{ru_id}, $o_keyword);
}
elsif ($o_default) {
$sth = $dbr->prepare(
"SELECT remote_picid, keyword, is_default, comments " .
"FROM ljr_cached_userpics WHERE ru_id=? and is_default=1");
$sth->execute($ru->{ru_id});
}
else {
$sth = $dbr->prepare(
"SELECT remote_picid, keyword, is_default, comments " .
"FROM ljr_cached_userpics WHERE ru_id=?");
$sth->execute($ru->{ru_id});
}
my $i = 0;
while (my $rpic = $sth->fetchrow_hashref) {
my $picurl = $remote_site . "/userpic/" . $rpic->{remote_picid} . "/" . $ru->{userid};
$remote_ids{$i} = $rpic->{remote_picid};
$remote_urls{$rpic->{remote_picid}} = $picurl;
$remote_comments{$picurl} = $rpic->{comments};
$remote_keywords{$picurl} =
(($remote_keywords{$picurl}) ? $remote_keywords{$picurl} . "," : "") .
$rpic->{keyword};
if ($rpic->{is_default}) {
$default_pic = $picurl;
}
print
$picurl . ":" .
$remote_ids{$i} . ":" .
$remote_comments{$picurl} . ":" .
$remote_keywords{$picurl} . "\n"
if $DEBUG;
$i++;
}
$sth->finish;
RPICID: foreach my $rpicid (sort {$a <=> $b} values %remote_ids) {
my $local_picid = $dbr->selectrow_array(
"SELECT local_picid FROM ljr_remote_userpics " .
"WHERE ru_id=? and remote_picid=? and local_userid = ?",
undef, $ru->{ru_id}, $rpicid, $u->{userid});
if ($local_picid) {
my $r_picid = $dbr->selectrow_array(
"SELECT picid FROM userpic2 WHERE picid=?",
undef, $local_picid);
if (!$r_picid) {
$u->do("DELETE FROM ljr_remote_userpics WHERE local_picid=?", undef, $local_picid);
$local_picid = undef;
}
else {
next RPICID;
}
}
my %POST = ();
$POST{urlpic} = $remote_urls{$rpicid};
$POST{keywords} = $remote_keywords{$remote_urls{$rpicid}};
$POST{comments} = $remote_comments{$remote_urls{$rpicid}};
$POST{url} = "";
if ($default_pic eq $remote_urls{$rpicid}) {
$POST{make_default} = 1;
}
# get remote picture and validate it
my $ua;
my $res;
my ($sx, $sy, $filetype);
$i = 0;
while(1) {
$ua = LWPx::ParanoidAgent->new(
timeout => 60,
max_size => $MAX_UPLOAD + 1024);
$ua->agent($LJR::USER_AGENT);
$res = $ua->get($POST{urlpic});
# if the picture doesn't exist on the remote server
# then we get 404 http error and remove it from our cache
if ($res &&
($res->{"_rc"} eq 404 || $res->{"_rc"} eq 503)
) {
$dbh->do("DELETE FROM ljr_cached_userpics WHERE ru_id=? and remote_picid=?",
undef, $ru->{ru_id}, $rpicid);
return $err->($dbh->errstr) if $dbh->err;
next RPICID;
}
$POST{userpic} = $res->content if $res && $res->is_success;
($sx, $sy, $filetype) = Image::Size::imgsize(\$POST{'userpic'});
if (!(
$res && $res->is_success && defined($sx) &&
length($POST{'userpic'}) <= $MAX_UPLOAD &&
($filetype eq "GIF" || $filetype eq "JPG" || $filetype eq "PNG") &&
$sx <= 100 && $sy <= 100
) &&
$i < $LJR::NETWORK_RETRIES) {
LJR::NETWORK_SLEEP(); $i++; next;
}
else {
last;
}
}
if (!($res && $res->is_success)) {
return $err->("Can't get remote user picture: ",
$remote_user, $local_user, $o_keyword, $o_default, $POST{urlpic},
$res->status_line);
}
if (!defined $sx) {
print ("Invalid image: " . $POST{urlpic} . "\n");
next RPICID;
}
if (length($POST{'userpic'}) > $MAX_UPLOAD) {
return $err->("Picture " . $POST{urlpic} . "is too large");
}
return $err->("Unsupported filetype: " . $POST{urlpic})
unless ($filetype eq "GIF" || $filetype eq "JPG" || $filetype eq "PNG");
return $err->("Image too large: " . $POST{urlpic}) if ($sx > 150 || $sy > 150);
my $base64 = Digest::MD5::md5_base64($POST{'userpic'});
# see if it's a duplicate
my $picid;
my $contenttype;
if ($filetype eq "GIF") { $contenttype = 'G'; }
elsif ($filetype eq "PNG") { $contenttype = 'P'; }
elsif ($filetype eq "JPG") { $contenttype = 'J'; }
$picid = $dbcr->selectrow_array(
"SELECT picid FROM userpic2 WHERE userid=? AND fmt=? AND md5base64=?",
undef, $u->{'userid'}, $contenttype, $base64);
$picid = 0 unless defined($picid);
print "trying to insert into db\n" if $DEBUG;
# if picture isn't a duplicate, insert it
if ($picid == 0) {
# Make a new global picid
$picid = LJ::alloc_global_counter('P') or
return $err->('Unable to allocate new picture id');
$u->do(
"INSERT INTO userpic2 (picid, userid, fmt, width, height, " .
"picdate, md5base64, location, state) " .
"VALUES (?, ?, ?, ?, ?, NOW(), ?, ?, 'N')",
undef, $picid, $u->{'userid'}, $contenttype, $sx, $sy, $base64, undef);
return $err->($u->errstr) if $u->err;
my $clean_err = sub {
if ($picid) {
$u->do(
"DELETE FROM userpic2 WHERE userid=? AND picid=?",
undef, $u->{'userid'}, $picid);
$u->do(
"DELETE FROM userpicblob2 WHERE userid=? AND picid=?",
undef, $u->{'userid'}, $picid);
}
return $err->(@_);
};
### insert the blob
$u->do(
"INSERT INTO userpicblob2 (userid, picid, imagedata) VALUES (?,?,?)",
undef, $u->{'userid'}, $picid, $POST{'userpic'});
return $clean_err->($u->errstr) if $u->err;
# make it their default pic?
if ($POST{'make_default'}) {
LJ::update_user($u, { defaultpicid => $picid });
$u->{'defaultpicid'} = $picid;
}
# set default keywords?
if ($POST{'keywords'} && $POST{'keywords'} ne '') {
print "storing keywords\n" if $DEBUG;
$sth = $dbcr->prepare("SELECT kwid, picid FROM userpicmap2 WHERE userid=?");
$sth->execute($u->{'userid'});
my @exist_kwids;
while (my ($kwid, $picid) = $sth->fetchrow_array) {
$exist_kwids[$kwid] = $picid;
}
my @keywords = split(/\s*,\s*/, $POST{'keywords'});
@keywords = grep { s/^\s+//; s/\s+$//; $_; } @keywords;
my (@bind, @data);
my $c = 0;
foreach my $kw (@keywords) {
my $kwid = LJ::get_keyword_id($u, $kw);
next unless $kwid; # Houston we have a problem! This should always return an id.
if ($c > $LJ::MAX_USERPIC_KEYWORDS) {
return $clean_err->("Too many userpic keywords: " . LJ::ehtml($kw));
}
if ($exist_kwids[$kwid]) { # Already used on another picture
# delete existing pic while there's newer one
$u->do("
delete ljr_remote_userpics, ljr_cached_userpics
from ljr_cached_userpics, ljr_remote_userpics
where
ljr_cached_userpics.ru_id = ljr_remote_userpics.ru_id and
ljr_cached_userpics.remote_picid = ljr_remote_userpics.remote_picid and
ljr_remote_userpics.local_userid = ? and local_picid = ?",
undef, $u->{'userid'}, $exist_kwids[$kwid]);
$u->do("DELETE FROM userpicmap2 WHERE userid=? AND picid=?",
undef, $u->{'userid'}, $exist_kwids[$kwid]);
$u->do("DELETE FROM userpicblob2 WHERE userid=? AND picid=?",
undef, $u->{'userid'}, $exist_kwids[$kwid]);
$u->do("DELETE FROM userpic2 WHERE userid=? AND picid=?",
undef, $u->{'userid'}, $exist_kwids[$kwid]);
}
push @bind, '(?, ?, ?)';
push @data, $u->{'userid'}, $kwid, $picid;
$c++;
}
if (@data && @bind) {
my $bind = join(',', @bind);
$u->do(
"INSERT INTO userpicmap2 (userid, kwid, picid) VALUES $bind",
undef, @data);
}
}
# set default comments and the url
my (@data, @set);
if ($POST{'comments'} && $POST{'comments'} ne '') {
push @set, 'comment=?';
push @data, LJ::text_trim($POST{'comments'}, $LJ::BMAX_UPIC_COMMENT, $LJ::CMAX_UPIC_COMMENT);
}
if ($POST{'url'} ne '') {
push @set, 'url=?';
push @data, $POST{'url'};
}
if (@set) {
my $set = join(',', @set);
$u->do("UPDATE userpic2 SET $set WHERE userid=? AND picid=?",
undef, @data, $u->{'userid'}, $picid);
return $err->($u->errstr) if $u->err;
}
$u->do("INSERT INTO ljr_remote_userpics VALUES (?,?,?,?)",
undef, $ru->{ru_id}, $rpicid, $u->{userid}, $picid);
return $err->($u->errstr) if $u->err;
}
}
return undef;
}
return 1;

View File

@@ -0,0 +1,35 @@
#!/usr/bin/perl
package LJR;
$LJ_CLIENT = "LJR::Import/0.01";
$USER_AGENT = "LJR::Import/0.01; http://lj.rossia.org/; lj-admin\@rossia.org";
# How much times to retry if any network related error occurs
$NETWORK_RETRIES = 10; #was: 20
# Hom much seconds to wait before each retry
$NETWORK_SLEEP = 30; #was: 5
$DEBUG = 1;
sub NETWORK_SLEEP {
my $msg = shift;
if ($msg) {
$msg = " (" . $msg . ")";
}
else {
$msg = "";
}
my $t = `date +"%D %T"`;
print
substr($t, 0, length($t) - 1) .
" sleeping $NETWORK_SLEEP seconds due to network related error" . $msg . ".\n"
if $DEBUG;
sleep $NETWORK_SLEEP;
};
return 1;

106
local/bin/ljrimport/ljr-import.pl Executable file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/perl -w
package LJR::Import;
use strict; # preventing my program from doing bad things
use DBI; # http://dbi.perl.org
use POSIX ();
do $ENV{'LJHOME'} . "/cgi-bin/ljconfig.pl";
my $qhost = $LJ::DBINFO{'master'}->{'host'};
my $quser = $LJ::DBINFO{'master'}->{'user'};
my $qpass = $LJ::DBINFO{'master'}->{'pass'};
my $qdb = $LJ::DBINFO{'master'}->{'dbname'};
my $qsock = $LJ::DBINFO{'master'}->{'sock'};
my $qport = $LJ::DBINFO{'master'}->{'port'};
$| = 1; # unbuffered (almost) output
# global database handle
$LJR::Import::global_dbh = 0;
# global shutdown request received flag
$LJR::Import::cool_stop = 0;
my $history_id;
# POSIX unmasks the sigprocmask properly
my $sigset = POSIX::SigSet->new();
my $action = POSIX::SigAction->new(
'LJR::Import::sigTERM_handler', $sigset, &POSIX::SA_NODEFER);
POSIX::sigaction(&POSIX::SIGTERM, $action);
sub log_print {
my $msg = shift;
my $t = `date +"%D %T"`;
print substr($t, 0, length($t) - 1) . " $msg\n";
}
sub sigTERM_handler {
my $t = `date +"%D %T"`;
print substr($t, 0, length($t) - 1) . " ljr-import.pl: received shutdown request\n";
$LJR::Import::cool_stop = 1;
}
# configuration
my $speed_throttle = 10; # seconds
print "\n";
LJR::Import::log_print("started");
# main loop, throttled
while (!process_exit()) {
process_queue(); # process new requests for import if any
sleep ($speed_throttle); # sleep for a while
}
LJR::Import::log_print("ljr-import.pl: shutting down due to safe shutdown request");
sub import_log {
my ($istatus) = @_;
my $sth2 = $LJR::Import::global_dbh->prepare (
"update ljr_ihistory set
istatus = ?,
idate = now()
where importid = ?");
$sth2->execute($istatus, $history_id);
$sth2->finish;
}
sub process_exit {
return ($LJR::Import::cool_stop);
}
sub process_queue {
my $row;
my $row1;
my $sth2;
my $e;
$LJR::Import::global_dbh = DBI->connect(
"DBI:mysql:mysql_socket=$qsock;hostname=$qhost;port=$qport;database=$qdb",
$quser, $qpass,
{RaiseError => 0, AutoCommit => 1}
) || die "Can't open database connection: $DBI::errstr";
my $sth = $LJR::Import::global_dbh->prepare("SELECT * from ljr_iqueue order by priority, importid");
$sth->execute;
while (($row = $sth->fetchrow_hashref) && !process_exit()) {
my $r = system ("nice -n 19 ./ljr-importdo.pl");
if ($r != 0) {
$sth->finish;
$LJR::Import::global_dbh->disconnect;
return;
}
sleep($speed_throttle); # do not hurry
$sth->execute; # refresh the query
}
$sth->finish;
$LJR::Import::global_dbh->disconnect;
}

View File

@@ -0,0 +1,218 @@
#!/usr/bin/perl -w
package LJR::Import;
use strict; # preventing my program from doing bad things
use DBI; # http://dbi.perl.org
use POSIX ();
do $ENV{'LJHOME'} . "/cgi-bin/ljconfig.pl";
my $qhost = $LJ::DBINFO{'master'}->{'host'};
my $quser = $LJ::DBINFO{'master'}->{'user'};
my $qpass = $LJ::DBINFO{'master'}->{'pass'};
my $qdb = $LJ::DBINFO{'master'}->{'dbname'};
my $qsock = $LJ::DBINFO{'master'}->{'sock'};
my $qport = $LJ::DBINFO{'master'}->{'port'};
require "ijournal.pl";
require "icomments.pl";
$| = 1; # unbuffered (almost) output
# global database handle
$LJR::Import::global_dbh = 0;
# global shutdown request received flag
$LJR::Import::cool_stop = 0;
my $history_id;
# POSIX unmasks the sigprocmask properly
my $sigset = POSIX::SigSet->new();
my $action = POSIX::SigAction->new(
'LJR::Import::sigTERM_handler', $sigset, &POSIX::SA_NODEFER);
POSIX::sigaction(&POSIX::SIGTERM, $action);
sub log_print {
my $msg = shift;
my $t = `date +"%D %T"`;
print substr($t, 0, length($t) - 1) . " $msg\n";
}
sub sigTERM_handler {
my $t = `date +"%D %T"`;
print substr($t, 0, length($t) - 1) . " ljr-import-do.pl: received shutdown request\n";
$LJR::Import::cool_stop = 1;
}
# configuration
my $speed_throttle = 10; # seconds
process_queue_alone(); # there must be something there!
if (process_exit()) {
exit 1;
}
else {
exit 0;
}
sub import_log {
my ($istatus) = @_;
my $sth2 = $LJR::Import::global_dbh->prepare (
"update ljr_ihistory set
istatus = ?,
idate = now()
where importid = ?");
$sth2->execute($istatus, $history_id);
$sth2->finish;
}
sub process_exit {
return ($LJR::Import::cool_stop);
}
sub process_queue_alone {
my $row;
my $row1;
my $sth2;
my $e;
$LJR::Import::global_dbh = DBI->connect(
"DBI:mysql:mysql_socket=$qsock;hostname=$qhost;port=$qport;database=$qdb",
$quser, $qpass,
{RaiseError => 0, AutoCommit => 1}
) || die "Can't open database connection: $DBI::errstr";
my $sth = $LJR::Import::global_dbh->prepare("SELECT * from ljr_iqueue order by priority, importid");
$sth->execute;
while (($row = $sth->fetchrow_hashref) && !process_exit()) {
my $sth1 = $LJR::Import::global_dbh->prepare (
"SELECT * from ljr_iqueue where
local_user = '" . $row->{'local_user'} . "'
order by local_user desc, importid desc limit 1");
$sth1->execute; # find last user request for import
$row = $sth1->fetchrow_hashref;
# create history record
$sth2 = $LJR::Import::global_dbh->prepare (
"insert into ljr_ihistory values ('',?,?,'',?,?,?,?,now(),'STARTED',now())");
$sth2->execute (
$row->{'remote_site'}, $row->{'remote_user'}, $row->{'remote_protocol'},
$row->{'local_user'}, $row->{'opt_overwrite'}, $row->{'opt_comments'}
); # save import history (parameters, time when started)
$history_id = $LJR::Import::global_dbh->selectrow_array("SELECT LAST_INSERT_ID()");
$sth2->finish;
LJR::Import::log_print(
$row->{'local_user'} . " <- " .
$row->{'remote_site'} . "::" . $row->{'remote_user'} .
" (entries)"
);
$e = "";
$e = import_journal(
0, # throttle_speed (seconds)
$row->{'remote_site'}, # remote_site
$row->{'remote_protocol'},# remote_protocol
$row->{'remote_user'}, # remote_user
$row->{'remote_pass'}, # remote_pass
"", # remote shared journal (if any)
$row->{'local_user'}, # local_user
"", # local shared journal (if any)
$row->{'opt_overwrite'} # overwrite entries
);
if ($row->{'opt_comments'} && (!$e || !$e->{'err'})) {
LJR::Import::log_print(
$row->{'local_user'} . " <- " .
$row->{'remote_site'} . "::" . $row->{'remote_user'} .
" (caching comments)"
);
$e = get_comments(
$row->{'remote_site'},
$row->{'remote_user'},
$row->{'remote_pass'},
1);
if (!$e || !$e->{'err'}) {
LJR::Import::log_print(
$row->{'local_user'} . " <- " .
$row->{'remote_site'} . "::" . $row->{'remote_user'} .
" (creating comments)");
$e = create_imported_comments (
$row->{'remote_site'},
$row->{'remote_user'},
$row->{'local_user'});
}
}
if ($e->{'err'}) {
$sth2 = $LJR::Import::global_dbh->prepare (
"update ljr_ihistory " .
"set remote_pass = '" . $row->{'remote_pass'} . "' " .
"where importid = " . $history_id . " ;"
);
$sth2->execute; # save remote pass for debugging purposes
$sth2->finish;
my $boo = $e->{errtext};
$boo =~ s/\n//;
LJR::Import::log_print(
$row->{'local_user'} . " <- " .
$row->{'remote_site'} . "::" . $row->{'remote_user'} . " " . $boo
);
import_log($e->{errtext});
}
else {
$sth2 = $LJR::Import::global_dbh->prepare (
"update ljr_ihistory " .
"set remote_pass = '' " .
"where remote_site = '" . $row->{'remote_site'} . "' and " .
"remote_user = '" . $row->{'remote_user'} . "' ;"
);
$sth2->execute; # remove remote pass since the journal was imported successfully
$sth2->finish;
LJR::Import::log_print(
$row->{'local_user'} . " <- " .
$row->{'remote_site'} . "::" . $row->{'remote_user'} .
": successful"
);
if ($e->{'warns'}) {
import_log("SUCCESSFUL, but " . $e->{'warns'});
}
else {
import_log("SUCCESSFUL");
}
}
$sth1 = $LJR::Import::global_dbh->prepare (
"delete from ljr_iqueue " .
"where local_user = '" . $row->{'local_user'} . "'"
); # empty all the user's request after processing last one
$sth1->execute;
$sth1->finish;
$sth->finish;
# we're quitting!
# if (process_exit()) {
$LJR::Import::global_dbh->disconnect;
return;
# }
}
$sth->finish;
$LJR::Import::global_dbh->disconnect;
}

117
local/bin/ljrimport/ljr-links.pl Executable file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/perl
use strict;
package LJR::Links;
sub get_server_url {
my ($canonical_url, $type) = @_;
if ($canonical_url eq "http://www.livejournal.com") {
if ($type eq "base") {
return "livejournal.com";
}
if ($type eq "userpic_base") {
return "http://userpic.livejournal.com";
}
}
}
sub make_ljr_hrefs {
my ($server_patt, $server_full, $text) = @_;
my $content = $$text;
$$text = "";
my $url;
my $orig_url;
my $orig_url_text;
return unless $content;
# replace valid html hyperlinks (<a href=http://www.livejournal.com/users/username/111.html>url_text</a>)
# with <ljr-href url="/users/username/111.html" site="http://www.livejournal.com">url_text</ljr-href>
#
while ($content =~
/\G(.*?)(\<a.*?href.*?=(\s?\"\s?)?(.*?)(\s?\"\s?)?\>(.*?)\<\/a\>)(.*)/sgi
) {
$$text .= $1;
$orig_url = $2;
$orig_url_text = $6;
$url = $4;
$content = $7;
# relative link (to the server from which we're importing)
if ($url =~ /^(\/users\/.*?\/\d*?\.html(.*?$))/) { # (\?thread=\d*\#t\d*)|$
$$text .= "<ljr-href url=\"$1\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
# relative link to oldstyle talkread.bml
elsif ($url =~ /^\/talkread.bml?\?journal=(.*?)\&itemid=(\d+)/) {
$$text .= "<ljr-href url=\"/users/$1/$2.html\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
# absolute link to oldstyle talkread.bml
elsif ($url =~ /^http:\/\/(www\.|)$server_patt\/talkread.bml\?journal=(.*?)\&itemid=(\d+)/) {
$$text .= "<ljr-href url=\"/users/$2/$3.html\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
# free users own two types of urls (first is canonical)
# http://www.livejournal.com/users/free_user/123456.html
# http://www.livejournal.com/~free_user/123456.html
elsif ($url =~ /^http:\/\/(www\.|)$server_patt(((\/~(\w*?)\/)|(\/users\/.*?\/))(\d*?\.html(.*?$)))/) { # (\?thread=\d*\#t\d*)|$
if ($5) {
$$text .= "<ljr-href url=\"/users/$5/$7\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
else {
$$text .= "<ljr-href url=\"$2\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
}
# payed users might own http://payeduser.livejournal.com/123456.html urls
elsif ($url =~ /^http:\/\/(\w*?)\.$server_patt\/(\d*?\.html(.*?$))/) { # (\?thread=\d*\#t\d*)|$
$$text .= "<ljr-href url=\"/users/$1/$2\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
else {
$$text .= $orig_url;
}
}
$$text .= $content;
$content = $$text;
$$text = "";
# replace strings like http://www.livejournal.com/users/lookslikeentry/123456.html with
# <ljr-href url="/users/lookslikeentry/123456.html" site="http://www.livejournal.com">http://www.livejournal.com/users/lookslikeentry/123456.html</ljr-href>
#
# now these can be only absolute links starting with http://
while ($content =~
/\G(.*?(^|[\ \t\r\n\f]))(http:\/\/.*?)(($|[\ \t\r\n\f]).*)/sg
) {
$$text .= $1;
$orig_url = $3;
$orig_url_text = $3;
$url = $3;
$content = $4;
# free users (copied from above)
if ($url =~ /^http:\/\/(www\.|)$server_patt(((\/~(\w*?)\/)|(\/users\/.*?\/))(\d*?\.html(.*?$)))/) { # (\?thread=\d*\#t\d*)|$
if ($5) {
$$text .= "<ljr-href url=\"/users/$5/$7\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
else {
$$text .= "<ljr-href url=\"$2\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
}
# oldstyle talkread.bml
elsif ($url =~ /^http:\/\/(www\.|)$server_patt\/talkread.bml\?journal=(.*?)\&itemid=(\d+)/) {
$$text .= "<ljr-href url=\"/users/$2/$3.html\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
# payed users (copied from above)
elsif ($url =~ /^http:\/\/(\w*?)\.$server_patt\/(\d*?\.html(.*?$))/) { # (\?thread=\d*\#t\d*)|$
$$text .= "<ljr-href url=\"/users/$1/$2\" site=\"$server_full\">$orig_url_text</ljr-href>";
}
else {
$$text .= $orig_url;
}
}
$$text .= $content;
}
return 1;

View File

@@ -0,0 +1,17 @@
#!/bin/bash
export LJHOME=/home/lj-admin/lj
IMPORT_NAME=ljr-import
ipid=`ps -e --format=pid,cmd | grep $IMPORT_NAME | grep -v grep | cut --bytes=1-5`
if [ ! "$ipid" == "" ]; then
echo "LJR::Import found, PID: $ipid; shutdown with ljr-stop.sh first."
else
if [ "$LJHOME" != "" ]; then
cd $LJHOME/bin/ljrimport
./ljr-import.pl >> $LJHOME/logs/ljr-import.log 2>&1 &
else
echo \$LJHOME is not set.
fi
fi

12
local/bin/ljrimport/ljr-stop.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
IMPORT_NAME=ljr-import
ipid=`ps -e --format=pid,cmd | grep $IMPORT_NAME | grep -v grep | cut --bytes=1-5`
if [ ! "$ipid" == "" ]; then
echo "LJR::Import found, PID: $ipid; sending shutdown signal."
kill $ipid
else
echo "LJR::Import is not running."
fi

View File

@@ -0,0 +1,21 @@
#!/usr/bin/perl
#
$maint{'clean_challenges'} = sub
{
my $dbh = LJ::get_db_writer();
my $sth;
my $ctime = time();
my $deltime = $ctime - 60*60*24*14; # two weeks
print "current time: $ctime\n";
print "deleting challenges older than: $deltime\n";
$sth = $dbh->prepare("delete from challenges where challenge < 'c0:$deltime'");
$sth->execute();
if ($dbh->err) { die $dbh->errstr; }
print "done.\n";
};
1;

548
local/bin/maint/stats.pl Executable file
View File

@@ -0,0 +1,548 @@
#!/usr/bin/perl
#
use strict;
use vars qw(%maint);
require "$ENV{'LJHOME'}/cgi-bin/statslib.pl";
# filled in by ljmaint.pl, 0=quiet, 1=normal, 2=verbose
$LJ::Stats::VERBOSE = $LJ::LJMAINT_VERBOSE >= 2 ? 1 : 0;
$maint{'genstats'} = sub
{
my @which = @_ || qw(users countries
states gender clients
pop_interests meme pop_faq);
# popular faq items
LJ::Stats::register_stat
({ 'type' => "global",
'jobname' => "popfaq",
'statname' => "pop_faq",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
return undef unless $db;
my $sth = $db->prepare("SELECT faqid, COUNT(*) FROM faquses WHERE " .
"faqid<>0 GROUP BY 1 ORDER BY 2 DESC LIMIT 50");
$sth->execute;
die $db->errstr if $db->err;
my %ret;
while (my ($id, $count) = $sth->fetchrow_array) {
$ret{$id} = $count;
}
return \%ret;
},
});
# popular interests
LJ::Stats::register_stat
({ 'type' => "global",
'jobname' => "pop_interests",
'statname' => "pop_interests",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
return undef unless $db;
return {} if $LJ::DISABLED{'interests-popular'};
# see what the previous min was, then subtract 20% of max from it
my ($prev_min, $prev_max) = $db->selectrow_array("SELECT MIN(statval), MAX(statval) " .
"FROM stats WHERE statcat='pop_interests'");
my $stat_min = int($prev_min - (0.2*$prev_max));
$stat_min = 1 if $stat_min < 1;
my $sth = $db->prepare("SELECT interest, intcount FROM interests WHERE intcount>? " .
"ORDER BY intcount DESC, interest ASC LIMIT 400");
$sth->execute($stat_min);
die $db->errstr if $db->err;
my %ret;
while (my ($int, $count) = $sth->fetchrow_array) {
$ret{$int} = $count;
}
return \%ret;
},
});
# popular memes
LJ::Stats::register_stat
({ 'type' => "global",
'jobname' => "meme",
'statname' => "popmeme",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
return undef unless $db;
return {} if $LJ::DISABLED{'meme'};
my $sth = $db->prepare("SELECT url, count(*) FROM meme " .
"GROUP BY 1 ORDER BY 2 DESC LIMIT 100");
$sth->execute;
die $db->errstr if $db->err;
my %ret;
while (my ($url, $count) = $sth->fetchrow_array) {
$ret{$url} = $count;
}
return \%ret;
},
});
# clients
LJ::Stats::register_stat
({ 'type' => "global",
'jobname' => "clients",
'statname' => "client",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
return undef unless $db;
return {} if $LJ::DISABLED{'clientversionlog'};
my $tsql = "SELECT MAX(userid) FROM user";
$tsql .= " where userid < " . $LJ::LJR_IMPORTED_USERIDS if $LJ::LJR_IMPORTED_USERIDS;
my $usertotal = $db->selectrow_array($tsql);
my $blocks = LJ::Stats::num_blocks($usertotal);
my %ret;
foreach my $block (1..$blocks) {
my ($low, $high) = LJ::Stats::get_block_bounds($block);
$db = $db_getter->(); # revalidate connection
my $sth = $db->prepare("SELECT c.client, COUNT(*) AS 'count' FROM clients c, clientusage cu " .
"WHERE c.clientid=cu.clientid AND cu.userid BETWEEN $low AND $high " .
"AND cu.lastlogin > DATE_SUB(NOW(), INTERVAL 30 DAY) GROUP BY 1 ORDER BY 2");
$sth->execute;
die $db->errstr if $db->err;
while ($_ = $sth->fetchrow_hashref) {
$ret{$_->{'client'}} += $_->{'count'};
}
print LJ::Stats::block_status_line($block, $blocks);
}
return \%ret;
},
});
# user table analysis
LJ::Stats::register_stat
({ 'type' => "global",
'jobname' => "users",
'statname' => ["account", "newbyday", "age", "userinfo"],
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
return undef unless $db;
my $tsql = "SELECT MAX(userid) FROM user";
$tsql .= " where userid < " . $LJ::LJR_IMPORTED_USERIDS if $LJ::LJR_IMPORTED_USERIDS;
my $usertotal = $db->selectrow_array($tsql);
my $blocks = LJ::Stats::num_blocks($usertotal);
my %ret; # return hash, (statname => { arg => val } since 'statname' is arrayref above
# iterate over user table in batches
foreach my $block (1..$blocks) {
my ($low, $high) = LJ::Stats::get_block_bounds($block);
# user query: gets user,caps,age,status,allow_getljnews
$db = $db_getter->(); # revalidate connection
my $sth = $db->prepare
("SELECT user, caps, " .
"FLOOR((TO_DAYS(NOW())-TO_DAYS(bdate))/365.25) AS 'age', " .
"status, allow_getljnews " .
"FROM user WHERE userid BETWEEN $low AND $high");
$sth->execute;
die $db->errstr if $db->err;
while (my $rec = $sth->fetchrow_hashref) {
# account types
my $capnameshort = LJ::name_caps_short($rec->{'caps'});
$ret{'account'}->{$capnameshort}++;
# ages
$ret{'age'}->{$rec->{'age'}}++
if $rec->{'age'} > 4 && $rec->{'age'} < 110;
# users receiving news emails
$ret{'userinfo'}->{'allow_getljnews'}++
if $rec->{'status'} eq "A" && $rec->{'allow_getljnews'} eq "Y";
}
# userusage query: gets timeupdate,datereg,nowdate
my $sth = $db->prepare
("SELECT DATE_FORMAT(timecreate, '%Y-%m-%d') AS 'datereg', " .
"DATE_FORMAT(NOW(), '%Y-%m-%d') AS 'nowdate', " .
"UNIX_TIMESTAMP(timeupdate) AS 'timeupdate' " .
"FROM userusage WHERE userid BETWEEN $low AND $high");
$sth->execute;
die $db->errstr if $db->err;
while (my $rec = $sth->fetchrow_hashref) {
# date registered
$ret{'newbyday'}->{$rec->{'datereg'}}++
unless $rec->{'datereg'} eq $rec->{'nowdate'};
# total user/activity counts
$ret{'userinfo'}->{'total'}++;
if (my $time = $rec->{'timeupdate'}) {
my $now = time();
$ret{'userinfo'}->{'updated'}++;
$ret{'userinfo'}->{'updated_last30'}++ if $time > $now-60*60*24*30;
$ret{'userinfo'}->{'updated_last7'}++ if $time > $now-60*60*24*7;
$ret{'userinfo'}->{'updated_last1'}++ if $time > $now-60*60*24*1;
}
}
print LJ::Stats::block_status_line($block, $blocks);
}
return \%ret;
},
});
LJ::Stats::register_stat
({ 'type' => "clustered",
'jobname' => "countries",
'statname' => "country",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
my $cid = shift;
return undef unless $db && $cid;
my $upc = LJ::get_prop("user", "country");
die "Can't find country userprop. Database populated?\n" unless $upc;
my $tsql = "SELECT MAX(userid) FROM userproplite2";
$tsql .= " where userid < " . $LJ::LJR_IMPORTED_USERIDS if $LJ::LJR_IMPORTED_USERIDS;
my $usertotal = $db->selectrow_array($tsql);
my $blocks = LJ::Stats::num_blocks($usertotal);
my %ret;
foreach my $block (1..$blocks) {
my ($low, $high) = LJ::Stats::get_block_bounds($block);
$db = $db_getter->(); # revalidate connection
my $sth = $db->prepare("SELECT u.value, COUNT(*) AS 'count' FROM userproplite2 u " .
"LEFT JOIN clustertrack2 c ON u.userid=c.userid " .
"WHERE u.upropid=? AND u.value<>'' AND u.userid=c.userid " .
"AND u.userid BETWEEN $low AND $high " .
"AND (c.clusterid IS NULL OR c.clusterid=?)" .
"GROUP BY 1 ORDER BY 2");
$sth->execute($upc->{'id'}, $cid);
die "clusterid: $cid, " . $db->errstr if $db->err;
while ($_ = $sth->fetchrow_hashref) {
$ret{$_->{'value'}} += $_->{'count'};
}
print LJ::Stats::block_status_line($block, $blocks);
}
return \%ret;
},
});
LJ::Stats::register_stat
({ 'type' => "clustered",
'jobname' => "states",
'statname' => "stateus",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
my $cid = shift;
return undef unless $db && $cid;
my $upc = LJ::get_prop("user", "country");
die "Can't find country userprop. Database populated?\n" unless $upc;
my $ups = LJ::get_prop("user", "state");
die "Can't find state userprop. Database populated?\n" unless $ups;
my $tsql = "SELECT MAX(userid) FROM userproplite2";
$tsql .= " where userid < " . $LJ::LJR_IMPORTED_USERIDS if $LJ::LJR_IMPORTED_USERIDS;
my $usertotal = $db->selectrow_array($tsql);
my $blocks = LJ::Stats::num_blocks($usertotal);
my %ret;
foreach my $block (1..$blocks) {
my ($low, $high) = LJ::Stats::get_block_bounds($block);
$db = $db_getter->(); # revalidate connection
my $sth = $db->prepare("SELECT ua.value, COUNT(*) AS 'count' " .
"FROM userproplite2 ua, userproplite2 ub " .
"WHERE ua.userid=ub.userid AND ua.upropid=? AND " .
"ub.upropid=? and ub.value='US' AND ub.value<>'' " .
"AND ua.userid BETWEEN $low AND $high " .
"GROUP BY 1 ORDER BY 2");
$sth->execute($ups->{'id'}, $upc->{'id'});
die $db->errstr if $db->err;
while ($_ = $sth->fetchrow_hashref) {
$ret{$_->{'value'}} += $_->{'count'};
}
print LJ::Stats::block_status_line($block, $blocks);
}
return \%ret;
},
});
LJ::Stats::register_stat
({ 'type' => "clustered",
'jobname' => "gender",
'statname' => "gender",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
my $cid = shift;
return undef unless $db && $cid;
my $upg = LJ::get_prop("user", "gender");
die "Can't find gender userprop. Database populated?\n" unless $upg;
my $tsql = "SELECT MAX(userid) FROM userproplite2";
$tsql .= " where userid < " . $LJ::LJR_IMPORTED_USERIDS if $LJ::LJR_IMPORTED_USERIDS;
my $usertotal = $db->selectrow_array($tsql);
my $blocks = LJ::Stats::num_blocks($usertotal);
my %ret;
foreach my $block (1..$blocks) {
my ($low, $high) = LJ::Stats::get_block_bounds($block);
$db = $db_getter->(); # revalidate connection
my $sth = $db->prepare("SELECT value, COUNT(*) AS 'count' FROM userproplite2 up " .
"LEFT JOIN clustertrack2 c ON up.userid=c.userid " .
"WHERE up.upropid=? AND up.userid BETWEEN $low AND $high " .
"AND (c.clusterid IS NULL OR c.clusterid=?) GROUP BY 1");
$sth->execute($upg->{'id'}, $cid);
die "clusterid: $cid, " . $db->errstr if $db->err;
while ($_ = $sth->fetchrow_hashref) {
$ret{$_->{'value'}} += $_->{'count'};
}
print LJ::Stats::block_status_line($block, $blocks);
}
return \%ret;
},
});
# run stats
LJ::Stats::run_stats(@which);
#### dump to text file
print "-I- Dumping to a text file.\n";
{
my $dbh = LJ::Stats::get_db("dbh");
my $sth = $dbh->prepare("SELECT statcat, statkey, statval FROM stats ORDER BY 1, 2");
$sth->execute;
die $dbh->errstr if $dbh->err;
open (OUT, ">$LJ::HTDOCS/stats/stats.txt");
while (my @row = $sth->fetchrow_array) {
next if grep { $row[0] eq $_ } @LJ::PRIVATE_STATS;
print OUT join("\t", @row), "\n";
}
close OUT;
}
print "-I- Done.\n";
};
$maint{'genstats_size'} = sub {
LJ::Stats::register_stat
({ 'type' => "global",
'jobname' => "size-accounts",
'statname' => "size",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
return undef unless $db;
my $tsql = "SELECT MAX(userid) FROM user";
$tsql .= " where userid < " . $LJ::LJR_IMPORTED_USERIDS if $LJ::LJR_IMPORTED_USERIDS;
# not that this isn't a total of current accounts (some rows may have
# been deleted), but rather a total of accounts ever created
my $size = $db->selectrow_array($tsql);
return { 'accounts' => $size };
},
});
LJ::Stats::register_stat
({ 'type' => "clustered",
'jobname' => "size-accounts_active",
'statname' => "size",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
return undef unless $db;
my $period = 30; # one month is considered active
my $active = $db->selectrow_array
("SELECT COUNT(*) FROM clustertrack2 WHERE ".
"timeactive > UNIX_TIMESTAMP()-86400*$period");
return { 'accounts_active' => $active };
},
});
print "-I- Generating account size stats.\n";
LJ::Stats::run_stats("size-accounts", "size-accounts_active");
print "-I- Done.\n";
};
$maint{'genstats_weekly'} = sub
{
LJ::Stats::register_stat
({ 'type' => "global",
'jobname' => "supportrank",
'statname' => "supportrank",
'handler' =>
sub {
my $db_getter = shift;
return undef unless ref $db_getter eq 'CODE';
my $db = $db_getter->();
return undef unless $db;
my %supportrank;
my $rank = 0;
my $lastpoints = 0;
my $buildup = 0;
my $sth = $db->prepare
("SELECT u.userid, SUM(sp.points) AS 'points' " .
"FROM user u, supportpoints sp " .
"WHERE u.userid=sp.userid GROUP BY 1 ORDER BY 2 DESC");
$sth->execute;
die $db->errstr if $db->err;
while ($_ = $sth->fetchrow_hashref) {
if ($lastpoints != $_->{'points'}) {
$lastpoints = $_->{'points'};
$rank += (1 + $buildup);
$buildup = 0;
} else {
$buildup++;
}
$supportrank{$_->{'userid'}} = $rank;
}
# move old 'supportrank' stat to supportrank_prev
# no API for this :-/
{
my $dbh = LJ::Stats::get_db("dbh");
$dbh->do("DELETE FROM stats WHERE statcat='supportrank_prev'");
$dbh->do("UPDATE stats SET statcat='supportrank_prev' WHERE statcat='supportrank'");
}
return \%supportrank;
}
});
print "-I- Generating weekly stats.\n";
LJ::Stats::run_stats('supportrank');
print "-I- Done.\n";
};
$maint{'build_randomuserset'} = sub
{
## this sets up the randomuserset table daily (or whenever) that htdocs/random.bml uses to
## find a random user that is both 1) publicly listed in the directory, and 2) updated
## within the past 24 hours.
## note that if a user changes their privacy setting to not be in the database, it'll take
## up to 24 hours for them to be removed from the random.bml listing, but that's acceptable.
my $dbh = LJ::get_db_writer();
print "-I- Building randomuserset.\n";
$dbh->do("TRUNCATE TABLE randomuserset");
$dbh->do("REPLACE INTO randomuserset (userid) " .
"SELECT uu.userid FROM userusage uu, user u " .
"WHERE u.userid=uu.userid AND u.allow_infoshow='Y' " .
"AND uu.timeupdate > DATE_SUB(NOW(), INTERVAL 1 DAY) ORDER BY RAND() LIMIT 5000");
my $num = $dbh->selectrow_array("SELECT MAX(rid) FROM randomuserset");
$dbh->do("REPLACE INTO stats (statcat, statkey, statval) " .
"VALUES ('userinfo', 'randomcount', $num)");
print "-I- Done.\n";
};
$maint{'memeclean'} = sub
{
my $dbh = LJ::get_db_writer();
print "-I- Cleaning memes.\n";
my $sth = $dbh->prepare("SELECT statkey FROM stats WHERE statcat='popmeme'");
$sth->execute;
die $dbh->errstr if $dbh->err;
while (my $url = $sth->fetchrow_array) {
my $copy = $url;
LJ::run_hooks("canonicalize_url", \$copy);
unless ($copy) {
my $d = $dbh->quote($url);
$dbh->do("DELETE FROM stats WHERE statcat='popmeme' AND statkey=$d");
print " deleting: $url\n";
}
}
print "-I- Done.\n";
};
1;

655
local/bin/maint/synsuck.pl Executable file
View File

@@ -0,0 +1,655 @@
#!/usr/bin/perl
#
use strict;
use vars qw(%maint %maintinfo);
use lib "$ENV{'LJHOME'}/cgi-bin"; # extra XML::Encoding files in cgi-bin/XML/*
use LWP::UserAgent;
use XML::RSS;
use HTTP::Status;
use Image::Size;
require "ljprotocol.pl";
require "parsefeed.pl";
require "cleanhtml.pl";
require "talklib.pl";
require LWPx::ParanoidAgent;
require LJR::unicode;
use utf8;
binmode STDOUT, ":utf8";
my $dumpxml = sub {
my ($xdata, $username) = @_;
open(my $outfile, ">$ENV{'LJHOME'}/logs/syn_err_" . $username . ".xml");
print $outfile "$xdata";
close($outfile);
};
my $err = sub {
my ($msg) = @_;
print $msg . "\n";
return;
};
my $get_picture = sub {
my ($userid, $url) = @_;
my $MAX_UPLOAD = 102400;
my $ua = LWPx::ParanoidAgent->new(timeout => 30, max_size => $MAX_UPLOAD + 1024);
$ua->agent("Synsuck::Userpic; $LJ::SITENAME; $LJ::ADMIN_EMAIL");
my $res = $ua->get($url);
my $picdata = $res->content;
return $err->("some error while getting userpic") if !($res && $res->is_success);
return $err->("404 while getting userpic") if ($res && $res->{"_rc"} eq 404);
return $err->("userpic size is bigger than we want") if length($picdata) > $MAX_UPLOAD;
my ($sx, $sy, $filetype) = Image::Size::imgsize(\$picdata);
return $err->("can't ge userpic size") unless defined $sx;
return $err->("unknown userpic filetype") unless $filetype eq "GIF" || $filetype eq "JPG" || $filetype eq "PNG";
return $err->("userpic is bigger than we want") if $sx > 150 || $sy > 150;
my $contenttype;
if ($filetype eq "GIF") { $contenttype = 'G'; }
elsif ($filetype eq "PNG") { $contenttype = 'P'; }
elsif ($filetype eq "JPG") { $contenttype = 'J'; }
my $base64 = Digest::MD5::md5_base64($picdata);
my $picid = LJ::alloc_global_counter('P') or return;
my $dbh = LJ::get_db_writer();
$dbh->do(
"INSERT INTO userpic2" .
"(picid, userid, fmt, width, height, picdate, md5base64, location, state, url) " .
"VALUES (?, ?, ?, ?, ?, NOW(), ?, ?, 'N', ?)",
undef, $picid, $userid, $contenttype, $sx, $sy, $base64, undef, $url);
$dbh->do(
"INSERT INTO userpicblob2 (userid, picid, imagedata) VALUES (?,?,?)",
undef, $userid, $picid, $picdata);
my $su = LJ::load_userid($userid);
LJ::update_user($su, { defaultpicid => $picid });
};
$maintinfo{'synsuck'}{opts}{locking} = "per_host";
$maint{'synsuck'} = sub
{
my $maxcount = shift || 0;
my $verbose = $LJ::LJMAINT_VERBOSE;
my %child_jobs; # child pid => [ userid, lock ]
my $process_user = sub {
my $urow = shift;
return unless $urow;
my ($user, $userid, $synurl, $lastmod, $etag, $lastmod_feed, $readers) =
map { $urow->{$_} } qw(user userid synurl lastmod etag lastmod_feed numreaders);
# we're a child process now, need to invalidate caches and
# get a new database handle
LJ::start_request();
my $dbh = LJ::get_db_writer();
# see if things have changed since we last looked and acquired the lock.
# otherwise we could 1) check work, 2) get lock, and between 1 and 2 another
# process could do both steps. we don't want to duplicate work already done.
my $now_checknext = $dbh->selectrow_array("SELECT checknext FROM syndicated ".
"WHERE userid=?", undef, $userid);
return if $now_checknext ne $urow->{checknext};
my $ua = LWP::UserAgent->new("timeout" => 30);
my $reader_info = $readers ? "; $readers readers" : "";
$ua->agent("$LJ::SITENAME ($LJ::ADMIN_EMAIL; for $LJ::SITEROOT/users/$user/" . $reader_info . ")");
my $delay = sub {
my $minutes = shift;
my $status = shift;
# add some random backoff to avoid waves building up
$minutes += int(rand(5));
$dbh->do("UPDATE syndicated SET lastcheck=NOW(), checknext=DATE_ADD(NOW(), ".
"INTERVAL ? MINUTE), laststatus=? WHERE userid=?",
undef, $minutes, $status, $userid);
};
print "[$$] Synsuck: fetching $user ($synurl)\n" if $verbose;
my $req = HTTP::Request->new("GET", $synurl);
$req->header('If-Modified-Since', $lastmod)
if $lastmod;
$req->header('If-None-Match', $etag)
if $etag;
my ($content, $too_big);
my $max_size = $LJ::SYNSUCK_MAX_SIZE || 500; # in kb
my $res = eval {
$ua->request($req, sub {
if (length($content) > 1024*$max_size) { $too_big = 1; return; }
$content .= $_[0];
}, 4096);
};
if ($@) { $delay->(120, "lwp_death"); return; }
if ($too_big) { $delay->(60, "toobig"); return; }
if ($res->is_error()) {
# http error
print " HTTP error! " . $res->status_line() . "\n" if $verbose;
$delay->(3*60, "httperror");
# overload rssparseerror here because it's already there -- we'll
# never have both an http error and a parse error on the
# same request
LJ::set_userprop($userid, "rssparseerror", $res->status_line());
return;
}
# check if not modified
if ($res->code() == RC_NOT_MODIFIED) {
print " not modified.\n" if $verbose;
$delay->($readers ? 30 : 12*60, "notmodified");
return;
}
my $r_lastmod = $res->header('Last-Modified');
my $r_etag = $res->header('ETag');
# check again (feedburner.com, blogspot.com, etc.)
if (($etag && $etag eq $r_etag) || ($lastmod && $lastmod eq $r_lastmod)) {
print " not modified.\n" if $verbose;
$delay->($readers ? 30 : 12*60, "notmodified");
return;
}
# force utf8; this helps from time to time ???
LJR::unicode::force_utf8(\$content);
# parsing time...
my ($feed, $error) = LJ::ParseFeed::parse_feed($content);
if ($error) {
# parse error!
print "Parse error! $error\n" if $verbose;
$delay->(3*60, "parseerror");
$error =~ s! at /.*!!;
$error =~ s/^\n//; # cleanup of newline at the beggining of the line
LJ::set_userprop($userid, "rssparseerror", $error);
$dumpxml->($content, $user);
return;
}
my $r_lastmod_feed = $feed->{'lastmod'};
my $r_lastmod_lastBuildDate = $feed->{'lastBuildDate'};
print " $lastmod \n $r_lastmod \n $etag \n $r_etag \n $lastmod_feed \n $r_lastmod_feed \n $r_lastmod_lastBuildDate \n ";
# check last-modified for bogus web-servers
if ($lastmod_feed && $lastmod_feed eq $r_lastmod_feed) {
print " not modified..\n" if $verbose;
$delay->($readers ? 30 : 12*60, "notmodified");
return;
}
# print " $lastmod \n $r_lastmod \n $etag \n $r_etag \n $lastmod_feed \n $r_lastmod_feed \n $r_lastmod_lastBuildDate \n ";
# another sanity check
unless (ref $feed->{'items'} eq "ARRAY") {
$delay->(3*60, "noitems");
return;
}
# update userpic
my $cur_pic = $dbh->selectrow_array(
"SELECT url FROM userpic2, user WHERE user.userid=? and
userpic2.userid=user.userid and userpic2.picid=user.defaultpicid",
undef, $userid);
if (
($feed->{'image'} && $cur_pic && $cur_pic ne $feed->{'image'}) ||
($feed->{'image'} && !$cur_pic)
) {
$dbh->do("delete from userpic2 WHERE userid=?", undef, $userid);
$dbh->do("delete from userpicblob2 WHERE userid=?", undef, $userid);
$dbh->do("update user set user.defaultpicid=NULL where userid=?", undef, $userid);
print "[$$] Synsuck: $user -- trying to fetch userpic from: " .
$feed->{'image'} . " \n" if $verbose;
$get_picture->($userid, $feed->{'image'});
}
my @items = reverse @{$feed->{'items'}};
# delete existing items older than the age which can show on a
# friends view.
my $su = LJ::load_userid($userid);
my $udbh = LJ::get_cluster_master($su);
unless ($udbh) {
$delay->(15, "nodb");
return;
}
# TAG:LOG2:synsuck_delete_olderitems
# my $secs = ($LJ::MAX_FRIENDS_VIEW_AGE || 3600*24*14)+0; # 2 week default.
# my $sth = $udbh->prepare("SELECT jitemid, anum FROM log2 WHERE journalid=? AND ".
# "logtime < DATE_SUB(NOW(), INTERVAL $secs SECOND)");
# $sth->execute($userid);
# die $udbh->errstr if $udbh->err;
# while (my ($jitemid, $anum) = $sth->fetchrow_array) {
# print "DELETE itemid: $jitemid, anum: $anum... \n" if $verbose;
# if (LJ::delete_entry($su, $jitemid, 0, $anum)) {
# print "success.\n" if $verbose;
# } else {
# print "fail.\n" if $verbose;
# }
# }
my $count = $udbh->selectrow_array("SELECT COUNT(*) FROM log2 WHERE journalid=$userid");
print "count = $count \n";
my $extra = $count - $LJ::MAX_SCROLLBACK_FRIENDS_SINGLE_USER_ACTIVITY;
if ($extra > 0) {
my $sth = $udbh->prepare("SELECT jitemid FROM logprop2 WHERE journalid=$userid".
" ORDER BY jitemid ASC LIMIT $extra");
$sth->execute();
while (my ($jitemid) = $sth->fetchrow_array) {
print "DELETE itemid: $jitemid... \n" if $verbose;
if (LJ::delete_entry($su, $jitemid)) {
print "success.\n" if $verbose;
} else {
print "fail.\n" if $verbose;
}
}
}
# determine if link tags are good or not, where good means
# "likely to be a unique per item". some feeds have the same
# <link> element for each item, which isn't good.
# if we have unique ids, we don't compare link tags
my ($compare_links, $have_ids) = 0;
{
my %link_seen;
foreach my $it (@items) {
$have_ids = 1 if $it->{'id'};
next unless $it->{'link'};
$link_seen{$it->{'link'}} = 1;
}
$compare_links = 1 if !$have_ids and $feed->{'type'} eq 'rss' and
scalar(keys %link_seen) == scalar(@items);
}
# if we have unique links/ids, load them for syndicated
# items we already have on the server. then, if we have one
# already later and see it's changed, we'll do an editevent
# instead of a new post.
my %existing_item = ();
if ($have_ids || $compare_links) {
my $p = $have_ids ? LJ::get_prop("log", "syn_id") :
LJ::get_prop("log", "syn_link");
my $sth = $udbh->prepare("SELECT jitemid, value FROM logprop2 WHERE ".
"journalid=? AND propid=? ORDER BY jitemid DESC LIMIT 1000"); # need last 100
$sth->execute($su->{'userid'}, $p->{'id'});
while (my ($jitemid, $id) = $sth->fetchrow_array) {
if (!defined $existing_item{$id}) {
$existing_item{$id} = $jitemid;
# print "Got it: $jitemid, $id\n" if $verbose;
} else {
# remove duplicates - if any:
print "DELETE duplicated itemid: $jitemid, $id ...\n" if $verbose;
if (LJ::delete_entry($su, $jitemid)) {
print "success.\n" if $verbose;
} else {
print "fail.\n" if $verbose;
}
}
}
}
# post these items
my $newcount = 0;
my $errorflag = 0;
my $mindate; # "yyyy-mm-dd hh:mm:ss";
my $notedate = sub {
my $date = shift;
$mindate = $date if ! $mindate || $date lt $mindate;
};
LJ::load_user_props($su, { use_master => 1 }, "newesteventtime");
### if ($su->{'newesteventtime'} eq $oldevent->{'eventtime'}) {
### LJ::set_userprop($su, "newesteventtime", undef);
### }
foreach my $it (@items) {
if ($it->{'time'} && $it->{'time'} lt $su->{'newesteventtime'}) {
## print "---- " . $it->{'subject'} . "\n" if $verbose;
next;
} elsif ($it->{'time'} && $it->{'time'} eq $su->{'newesteventtime'}) {
print "==== " . $it->{'subject'} . "\n" if $verbose;
} else {
print "++++ " . $it->{'subject'} . "\n" if $verbose;
}
my $dig = LJ::md5_struct($it)->b64digest;
my $prevadd = $dbh->selectrow_array("SELECT MAX(dateadd) FROM synitem WHERE ".
"userid=? AND item=?", undef,
$userid, $dig);
if ($prevadd) {
$notedate->($prevadd);
next;
}
my $now_dateadd = $dbh->selectrow_array("SELECT NOW()");
die "unexpected format" unless $now_dateadd =~ /^\d\d\d\d\-\d\d\-\d\d \d\d:\d\d:\d\d$/;
$dbh->do("INSERT INTO synitem (userid, item, dateadd) VALUES (?,?,?)",
undef, $userid, $dig, $now_dateadd);
$notedate->($now_dateadd);
$newcount++;
print "[$$] $dig - $it->{'subject'}\n" if $verbose;
$it->{'text'} =~ s/^\s+//;
$it->{'text'} =~ s/\s+$//;
# process lj-cuts
while ($it->{'text'} =~ /\G(.*?)<a\ name\=\"cutid(\d+)\"\>\<\/a\>(.*)/sg) {
my $before = $1;
my $cutid = $2;
my $rcut = $3;
$rcut =~ s/<a\ name\=\"cutid$cutid\"\>\<\/a\>/\<\/lj\-cut\>/;
$it->{'text'} = $before . "<lj-cut>" . $rcut;
}
my $htmllink;
if (defined $it->{'link'}) {
$htmllink = "<p class='ljsyndicationlink'>" .
"<a href='$it->{'link'}'>$it->{'link'}</a></p>";
}
# Show the <guid> link if it's present and different than the
# <link>.
# [zilla: 267] Patch: Chaz Meyers <lj-zilla@thechaz.net>
if ( defined $it->{'id'} && $it->{'id'} ne $it->{'link'}
&& $it->{'id'} =~ m!^http://! )
{
$htmllink .= "<p class='ljsyndicationlink'>" .
"<a href='$it->{'id'}'>$it->{'id'}</a></p>";
}
# rewrite relative URLs to absolute URLs, but only invoke the HTML parser
# if we see there's some image or link tag, to save us some work if it's
# unnecessary (the common case)
if ($it->{'text'} =~ /<(?:img|a)\b/i) {
# TODO: support XML Base? http://www.w3.org/TR/xmlbase/
my $base_href = $it->{'link'} || $synurl;
LJ::CleanHTML::resolve_relative_urls(\$it->{'text'}, $base_href);
}
# $own_time==1 means we took the time from the feed rather than localtime
my ($own_time, $year, $mon, $day, $hour, $min);
if ($it->{'time'} &&
$it->{'time'} =~ m!^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d)!) {
$own_time = 1;
($year, $mon, $day, $hour, $min) = ($1,$2,$3,$4,$5);
} else {
$own_time = 0;
my @now = localtime();
($year, $mon, $day, $hour, $min) =
($now[5]+1900, $now[4]+1, $now[3], $now[2], $now[1]);
}
my $command = "postevent";
my $req = {
'username' => $user,
'ver' => 1,
'subject' => $it->{'subject'},
'event' => "$it->{'text'}",
'year' => $year,
'mon' => $mon,
'day' => $day,
'hour' => $hour,
'min' => $min,
'props' => {
'syn_link' => $it->{'link'},
'opt_nocomments' => 1,
},
};
$req->{'props'}->{'syn_id'} = $it->{'id'}
if $it->{'id'};
my $flags = {
'nopassword' => 1,
};
# if the post contains html linebreaks, assume it's preformatted.
if ($it->{'text'} =~ /<(?:p|br)\b/i) {
$req->{'props'}->{'opt_preformatted'} = 1;
}
# do an editevent if we've seen this item before
my $id = $have_ids ? $it->{'id'} : $it->{'link'};
my $old_itemid = $existing_item{$id};
if ($id && $old_itemid) {
$newcount--; # cancel increment above
$command = "editevent";
$req->{'itemid'} = $old_itemid;
# the editevent requires us to resend the date info, which
# we have to go fetch first, in case the feed doesn't have it
# TAG:LOG2:synsuck_fetch_itemdates
unless($own_time) {
my $origtime =
$udbh->selectrow_array("SELECT eventtime FROM log2 WHERE ".
"journalid=? AND jitemid=?", undef,
$su->{'userid'}, $old_itemid);
$origtime =~ /(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d)/;
$req->{'year'} = $1;
$req->{'mon'} = $2;
$req->{'day'} = $3;
$req->{'hour'} = $4;
$req->{'min'} = $5;
}
}
my $err;
my $res = LJ::Protocol::do_request($command, $req, \$err, $flags);
unless ($res && ! $err) {
print " Error: $err\n" if $verbose;
$errorflag = 1;
}
}
# delete some unneeded synitems. the limit 1000 is because
# historically we never deleted and there are accounts with
# 222,000 items on a myisam table, and that'd be quite the
# delete hit.
# the 14 day interval is because if a remote site deleted an
# entry, it's possible for the oldest item that was previously
# gone to reappear, and we want to protect against that a
# little.
if ($LJ::SYNITEM_CLEAN) {
$dbh->do("DELETE FROM synitem WHERE userid=? AND ".
"dateadd < ? - INTERVAL 14 DAY LIMIT 1000",
undef, $userid, $mindate);
}
$dbh->do("UPDATE syndicated SET oldest_ourdate=? WHERE userid=?",
undef, $mindate, $userid);
# bail out if errors, and try again shortly
if ($errorflag) {
$delay->(30, "posterror");
return;
}
# update syndicated account's userinfo if necessary
LJ::load_user_props($su, "url", "urlname");
{
my $title = $feed->{'title'};
$title = $su->{'user'} unless LJ::is_utf8($title);
$title =~ s/[\n\r]//g;
if ($title && $title ne $su->{'name'}) {
LJ::update_user($su, { name => $title });
}
if ($title) {
LJ::set_userprop($su, "urlname", $title);
} else {
LJ::set_userprop($su, "urlname", $su->{'url'});
}
my $link = $feed->{'link'};
if ($link && $link ne $su->{'url'}) {
LJ::set_userprop($su, "url", $link);
}
my $des = $feed->{'description'};
if ($des) {
my $bio;
if ($su->{'has_bio'} eq "Y") {
$bio = $udbh->selectrow_array("SELECT bio FROM userbio WHERE userid=?", undef,
$su->{'userid'});
}
if ($bio ne $des && $bio !~ /\[LJ:KEEP\]/) {
if ($des) {
$su->do("REPLACE INTO userbio (userid, bio) VALUES (?,?)", undef,
$su->{'userid'}, $des);
} else {
$su->do("DELETE FROM userbio WHERE userid=?", undef, $su->{'userid'});
}
LJ::update_user($su, { has_bio => ($des ? "Y" : "N") });
LJ::MemCache::delete([$su->{'userid'}, "bio:$su->{'userid'}"]);
}
}
}
# decide when to poll next (in minutes).
# FIXME: this is super lame. (use hints in RSS file!)
my $int = $newcount ? 15 : 30;
my $status = $newcount ? "ok" : "nonew";
my $updatenew = $newcount ? ", lastnew=NOW()" : "";
# update reader count while we're changing things, but not
# if feed is stale (minimize DB work for inactive things)
if ($newcount || ! defined $readers) {
$readers = $dbh->selectrow_array("SELECT COUNT(*) FROM friends WHERE ".
"friendid=?", undef, $userid);
}
# if readers are gone, don't check for a whole day
$int = 60*24 if $readers && $readers < 2 || !$readers;
$dbh->do("UPDATE syndicated SET checknext=DATE_ADD(NOW(), INTERVAL $int MINUTE), ".
"lastcheck=NOW(), lastmod=?, etag=?, lastmod_feed=? , laststatus=?, numreaders=? $updatenew ".
"WHERE userid=$userid", undef, $r_lastmod, $r_etag, $r_lastmod_feed, $status, $readers);
};
###
### child process management
###
# get the next user to be processed
my @all_users;
my $get_next_user = sub {
return shift @all_users if @all_users;
# need to get some more rows
my $dbh = LJ::get_db_writer();
my $current_jobs = join(",", map { $dbh->quote($_->[0]) } values %child_jobs);
my $in_sql = " AND u.userid NOT IN ($current_jobs)" if $current_jobs;
my $sth = $dbh->prepare("SELECT u.user, s.userid, s.synurl, s.lastmod, " .
" s.etag, s.lastmod_feed, s.numreaders, s.checknext " .
"FROM user u, syndicated s " .
"WHERE u.userid=s.userid AND u.statusvis='V' " .
"AND s.checknext < NOW()$in_sql " .
"ORDER BY RAND() LIMIT 500");
$sth->execute;
while (my $urow = $sth->fetchrow_hashref) {
push @all_users, $urow;
}
return undef unless @all_users;
return shift @all_users;
};
# fork and manage child processes
my $max_threads = $LJ::SYNSUCK_MAX_THREADS || 1;
print "[$$] PARENT -- using $max_threads workers\n" if $verbose;
my $threads = 0;
my $userct = 0;
my $keep_forking = 1;
while ( $maxcount == 0 || $userct < $maxcount ) {
if ($threads < $max_threads && $keep_forking) {
my $urow = $get_next_user->();
unless ($urow) {
$keep_forking = 0;
next;
}
my $lockname = "synsuck-user-" . $urow->{user};
my $lock = LJ::locker()->trylock($lockname);
next unless $lock;
print "Got lock on '$lockname'. Running\n" if $verbose;
# spawn a new process
if (my $pid = fork) {
# we are a parent, nothing to do?
$child_jobs{$pid} = [$urow->{'userid'}, $lock];
$threads++;
$userct++;
} else {
# handles won't survive the fork
LJ::disconnect_dbs();
$process_user->($urow);
exit 0;
}
# wait for child(ren) to die
} else {
my $child = wait();
last if $child == -1;
delete $child_jobs{$child};
$threads--;
}
}
# Now wait on any remaining children so we don't leave zombies behind.
while ( %child_jobs ) {
my $child = wait();
last if $child == -1;
delete $child_jobs{ $child };
$threads--;
}
print "[$$] $userct users processed\n" if $verbose;
return;
};
1;
# Local Variables:
# mode: perl
# c-basic-indent: 4
# indent-tabs-mode: nil
# End:

30
local/bin/maint/taskinfo.txt Executable file
View File

@@ -0,0 +1,30 @@
bday.pl:
bdaymail - Sends people birthday wishes & notifications
stats.pl:
build_randomuserset - builds/cleans table of users applicable for inclusion in random listings
genstats - Generates the nightly statistics
genstats_size - Generates the site size stats
genstats_weekly - Generates the weekly statistics
memeclean - Removes things from meme summary that are excluded by new URL cleaner rules
statspics.pl:
genstatspics - Makes a bunch of graphs to show on the statistics page.
clean_caches.pl:
clean_caches - removes old cache files
synsuck.pl:
synsuck - Polls needed remote, syndicated RSS/etc and updates journals.
captcha.pl:
gen_audio_captchas - Generate any needed new audio challenges.
gen_image_captchas - Generate any needed new graphical challenges.
clean_captchas - Purge old challenges from the database.
generic.pl:
joinmail - Generates daily email digests for community join requests
clean_spamreports - Clean out data from the spamreports table older than 90 days.
clean_challenges.pl:
clean_challenges - Deletes old records from challenges table

254
local/bin/qbufferd.pl Executable file
View File

@@ -0,0 +1,254 @@
#!/usr/bin/perl
#
# <LJDEP>
# lib: Proc::ProcessTable, cgi-bin/ljlib.pl
# </LJDEP>
use strict;
use Getopt::Long
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
require "$ENV{'LJHOME'}/cgi-bin/supportlib.pl";
require "$ENV{'LJHOME'}/cgi-bin/ljcmdbuffer.pl";
require "$ENV{'LJHOME'}/cgi-bin/talklib.pl";
my $opt_foreground;
my $opt_debug;
my $opt_stop;
exit 1 unless GetOptions('foreground' => \$opt_foreground,
'debug' => \$opt_debug,
'stop' => \$opt_stop,
);
BEGIN {
$LJ::OPTMOD_PROCTABLE = eval "use Proc::ProcessTable; 1;";
}
my $DELAY = $LJ::QBUFFERD_DELAY || 15;
my $pidfile = $LJ::QBUFFERD_PIDFILE || "$ENV{'LJHOME'}/var/qbufferd.pid";
my $pid;
if (-e $pidfile) {
open (PID, $pidfile);
chomp ($pid = <PID>);
close PID;
if ($opt_stop) {
if (kill 15, $pid) {
print "Shutting down qbufferd.\n";
} else {
print "qbufferd not running?\n";
}
exit;
}
if ($LJ::OPTMOD_PROCTABLE) {
my $processes = Proc::ProcessTable->new()->table;
if (grep { $_->cmndline =~ /perl.+qbufferd/ && $_->pid != $$ } @$processes) {
exit;
}
} else {
if (kill 0, $pid) {
# seems to still be running (at least something is with that pid)
exit;
}
}
}
if ($opt_stop) {
print "qbufferd not running?\n";
exit;
}
$SIG{'INT'} = \&stop_qbufferd;
$SIG{'TERM'} = \&stop_qbufferd;
$SIG{'HUP'} = sub {
# nothing. maybe later make a HUP force a flush?
};
if (!$opt_foreground && ($pid = fork))
{
unless (open (PID, ">$pidfile")) {
kill 15, $pid;
die "Couldn't write PID file. Exiting.\n";
}
print PID $pid, "\n";
close PID;
print "qbufferd started with pid $pid\n";
if (-s $pidfile) { print "pid file written ($pidfile)\n"; }
exit;
}
# Close filehandles unless running in --debug or --foreground mode.
unless ( $opt_debug || $opt_foreground ) {
close STDIN && open STDIN, "</dev/null";
close STDOUT && open STDOUT, "+>&STDIN";
close STDERR && open STDERR, "+>&STDIN";
}
# fork off a separate qbufferd process for all specified
# job types in @LJ::QBUFFERD_ISOLATE, and then
# another process for all other job types. The current process
# will keep tabs on all of those
my %isolated;
my %pids; # job -> pid
my %jobs; # pid -> job
my $working = 0; # 1 for processes that do actual work
my $my_job;
foreach my $job (@LJ::QBUFFERD_ISOLATE) {
$isolated{$job} = 1;
}
foreach my $job (@LJ::QBUFFERD_ISOLATE, "_others_") {
if (my $child = fork) {
# parent.
$pids{$job} = $child;
$jobs{$child} = $job;
next;
} else {
# child.
$0 .= " [$job]";
$my_job = $job;
$working = 1;
last;
}
}
# at this point, $my_job is either the specialized 'cmd' to run, or
# '_others_' to mean everything besides stuff with their own processes.
# $working is 1 for nonempty values of $my_job .
sub stop_qbufferd
{
# stop children
unless ($working) {
foreach my $job (keys %pids) {
my $child = $pids{$job};
print "Killing child pid $child job: $job\n" if $opt_debug;
kill 15, $child;
}
unlink $pidfile;
}
print "Quitting: " . ($working ? "job $my_job" : "parent") . "\n" if $opt_debug;
exit;
}
while(not $working) {
# controlling process's cycle
my $pid;
$pid = wait();
print "Child exited, pid $pid, job $jobs{$pid}\n" if $opt_debug;
if ($jobs{$pid}) {
my $job = $jobs{$pid};
print "Restarting job $job\n" if $opt_debug;
delete $pids{$job};
delete $jobs{$pid};
if (my $child = fork) {
# parent.
$pids{$job} = $child;
$jobs{$child} = $job;
} else {
# child.
$0 .= " [$job]";
$my_job = $job;
$working = 1; # go work
}
}
}
# the actual work begins here
my @all_jobs = qw(delitem weblogscom send_mail support_notify dirty);
foreach my $hook (keys %LJ::HOOKS) {
next unless $hook =~ /^cmdbuf:(\w+):run$/;
push @all_jobs, $1;
}
while (LJ::start_request())
{
my $cycle_start = time();
print "Starting cycle. Job $my_job\n" if $opt_debug;
# syndication (checks RSS that need to be checked)
if ($my_job eq "synsuck") {
system("$ENV{'LJHOME'}/bin/ljmaint.pl", "-v0", "synsuck");
print "Sleeping. Job $my_job\n" if $opt_debug;
my $elapsed = time() - $cycle_start;
sleep ($DELAY-$elapsed) if $elapsed < $DELAY;
next;
}
# do main cluster updates
my $dbh = LJ::get_dbh("master");
unless ($dbh) {
sleep 10;
next;
}
# keep track of what commands we've run the start hook for
my %started;
# handle clusters
foreach my $c (@LJ::CLUSTERS) {
print "Cluster: $c Job: $my_job\n" if $opt_debug;
my $db = LJ::get_cluster_master($c);
next unless $db;
my @check_jobs = ($my_job);
if ($my_job eq "_others_") { @check_jobs = grep { ! $isolated{$_} } @all_jobs; }
foreach my $cmd (@check_jobs) {
my $have_jobs = $db->selectrow_array("SELECT cbid FROM cmdbuffer WHERE cmd=? LIMIT 1",
undef, $cmd);
next unless $have_jobs;
print " Starting $cmd...\n" if $opt_debug;
unless ($started{$cmd}++) {
LJ::Cmdbuffer::flush($dbh, undef, "$cmd:start");
}
LJ::Cmdbuffer::flush($dbh, $db, $cmd);
print " Finished $cmd.\n" if $opt_debug;
# monitor process size and job counts to suicide if necessary
my $size = 0;
if (open(S, "/proc/$$/status")) {
my $file;
{ local $/ = undef; $file = <S>; }
$size = $1 if $file =~ /VmSize:.+?(\d+)/;
close S;
}
# is it our time to go?
my $kill_job_ct = LJ::Cmdbuffer::get_property($cmd, 'kill_job_ct') || 0;
my $kill_mem_size = LJ::Cmdbuffer::get_property($cmd, 'kill_mem_size') || 0;
if ($kill_job_ct && $started{$cmd} >= $kill_job_ct ||
$kill_mem_size && $size >= $kill_mem_size)
{
# trigger reload of current child process
print "Job suicide: $cmd. (size=$size, rpcs=" . ($started{dirty}+0) . ")\n"
if $opt_debug;
# run end hooks before dying
foreach my $cmd (keys %started) {
LJ::Cmdbuffer::flush($dbh, undef, "$cmd:finish");
}
exit 0;
}
}
}
# run the end hook for all commands we've run
foreach my $cmd (keys %started) {
LJ::Cmdbuffer::flush($dbh, undef, "$cmd:finish");
}
print "Sleeping. Job $my_job\n" if $opt_debug;
my $elapsed = time() - $cycle_start;
sleep ($DELAY-$elapsed) if $elapsed < $DELAY;
};

5240
local/bin/upgrading/en.dat Executable file

File diff suppressed because it is too large Load Diff

2103
local/bin/upgrading/en_LJ.dat Executable file

File diff suppressed because it is too large Load Diff

5072
local/bin/upgrading/ru.dat Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
################################################################
# LiveJournal.com's non-free (not GPL) layers. You can use the
# source to learn from, but not copy.
################################################################
# base filename layer type parent
3column/layout layout core1
3column/themes theme+ 3column/layout
anovelconundrum/layout layout core1
anovelconundrum/themes theme+ anovelconundrum/layout
boxer/layout layout core1
boxer/themes theme+ boxer/layout
component/layout layout core1
component/themes theme+ component/layout
cuteness/layout layout core1
flexiblesquares/layout layout core1
flexiblesquares/themes theme+ flexiblesquares/layout
nebula/layout layout core1
nebula/themes theme+ nebula/layout
opal/layout layout core1
opal/themes theme+ opal/layout
smoothsailing/layout layout core1
smoothsailing/themes theme+ smoothsailing/layout
tranquilityii/layout layout core1
tranquilityii/themes theme+ tranquilityii/layout
unearthed/layout layout core1
unearthed/themes theme+ unearthed/layout
s2layers-ljcomint.dat INCLUDE

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,233 @@
#NEWLAYER: 3column/blue
layerinfo "type" = "theme";
layerinfo "name" = "Blue";
layerinfo "redist_uniq" = "3column/blue";
set color_bg = "#e9f3fa";
set font_color = "#0d446b";
set link_color = "#0d446b";
set link_hover = "#0d446b";
set link_side = "#0d446b";
set link_side_h = "#0d446b";
set side_bg = "#e9f3fa";
set entries_bg = "#e9f3fa";
set entries_border = "#0d446b";
set side_t_color = "#0d446b";
set button_bg = "#e9f3fa";
set button_bg_h = "#b0cce0";
set entries_font_color = "#0d446b";
set side_border = "#0d446b";
set c_sub_color = "#b0cce0";
set c_sub_bg = "#0d446b";
set side_h_color = "#0d446b";
set side_h_border = "#0d446b";
set side_h_bg = "#b0cce0";
set sub_color = "#0d446b";
#NEWLAYER: 3column/dark
layerinfo "type" = "theme";
layerinfo "name" = "Dark";
layerinfo "redist_uniq" = "3column/dark";
set color_bg = "#000000";
set font_color = "#c0c0c0";
set link_color = "#202020";
set link_hover = "#FFFFFF";
set link_side = "#95432D";
set link_side_h = "#BA7625";
set side_bg = "#000000";
set side_t_color = "C0C0C0";
set side_border = "#000000";
set side_h_color = "#C0C0C0";
set side_h_border = "#C0C0C0";
set side_h_bg = "#000000";
set entries_bg = "#58494E";
set entries_border = "#FFFFFF";
set button_bg = "#FFFFFF";
set button_bg_h = "#c0c0c0";
set entries_font_color = "#000000";
set c_sub_color = "#000000";
set c_sub_bg = "#ffffff";
set sub_color = "#C0C0C0";
#NEWLAYER: 3column/earth
layerinfo "type" = "theme";
layerinfo "name" = "Earth";
layerinfo "redist_uniq" = "3column/earth";
set color_bg = "#ECECEA";
set font_color = "#4F4637";
set link_color = "#4F4637";
set link_hover = "#1D4629";
set link_side = "#7B6E59";
set link_side_h = "#1D4629";
set side_bg = "#DBCEB9";
set side_t_color = "4F4637";
set side_border = "#7B6E59";
set side_h_color = "#ECECEA";
set side_h_border = "#ECECEA";
set side_h_bg = "#7B6E59";
set entries_bg = "#AB9F8B";
set entries_border = "#7B6E59";
set button_bg = "#ECECEA";
set button_bg_h = "#4F4637";
set entries_font_color = "#4F4637";
set c_sub_color = "#4F4637";
set c_sub_bg = "#ECECEA";
set sub_color = "#4F4637";
#NEWLAYER: 3column/unnamed
layerinfo "type" = "theme";
layerinfo "name" = "Unnamed";
layerinfo "redist_uniq" = "3column/unnamed";
set color_bg = "#E5D5C6";
set font_color = "#000000";
set link_color = "#202020";
set link_hover = "#000000";
set link_side = "#95432D";
set link_side_h = "#D63C3C";
set side_bg = "#E5D5C6";
set side_t_color = "#000000";
set side_border = "#E5D5C6";
set side_h_color = "#000000";
set side_h_border = "#000000";
set side_h_bg = "#E5D5C6";
set entries_bg = "#E5D5C6";
set entries_border = "#E5D5C6";
set button_bg = "#E5D5C6";
set button_bg_h = "#ccab8a";
set entries_font_color = "#000000";
set c_sub_color = "#ccab8a";
set c_sub_bg = "#E5D5C6";
set sub_color = "#000000";
#NEWLAYER: 3column/greenenvy
layerinfo "type" = "theme";
layerinfo "name" = "Green of Envy";
layerinfo "redist_uniq" = "3column/greenenvy";
set color_bg = "#d8ece7";
set font_color = "#1b2d29";
set link_color = "#1b2d29";
set link_hover = "#1b2d29";
set link_side = "#1b2d29";
set link_side_h = "#1b2d29";
set side_bg = "#d8ece7";
set entries_bg = "#d8ece7";
set entries_border = "#1b2d29";
set side_t_color = "#1b2d29";
set button_bg = "#d8ece7";
set button_bg_h = "#b1d5cb";
set entries_font_color = "#1b2d29";
set side_border = "#1b2d29";
set c_sub_color = "#b1d5cb";
set c_sub_bg = "#1b2d29";
set side_h_color = "#1b2d29";
set side_h_border = "#1b2d29";
set side_h_bg = "#b1d5cb";
set sub_color = "#1b2d29";
#NEWLAYER: 3column/blackwhite
layerinfo "type" = "theme";
layerinfo "name" = "Black and White";
layerinfo "redist_uniq" = "3column/blackwhite";
set color_bg = "#FFFFFF";
set font_color = "#808080";
set link_color = "#808080";
set link_hover = "#202020";
set link_side = "#c0c0c0";
set link_side_h = "#FFFFFF";
set side_bg = "#000000";
set side_t_color = "808080";
set side_border = "#ffffff";
set side_h_color = "#c0c0c0";
set side_h_border = "#c0c0c0";
set side_h_bg = "#808080";
set entries_bg = "#ffffff";
set entries_border = "#000000";
set button_bg = "#c0c0c0";
set button_bg_h = "#808080";
set entries_font_color = "#000000";
set c_sub_color = "#ffffff";
set c_sub_bg = "#000000";
set sub_color = "#202020";
#NEWLAYER: 3column/agentorange
layerinfo "type" = "theme";
layerinfo "name" = "Agent Orange";
layerinfo "redist_uniq" = "3column/agentorange";
set color_bg = "#f7e6d3";
set font_color = "#582604";
set link_color = "#582604";
set link_hover = "#582604";
set link_side = "#582604";
set link_side_h = "#582604";
set side_bg = "#f7e6d3";
set entries_bg = "#f7e6d3";
set entries_border = "#582604";
set side_t_color = "#582604";
set button_bg = "#f7e6d3";
set button_bg_h = "#e0c7aa";
set entries_font_color = "#582604";
set side_border = "#582604";
set c_sub_color = "#e0c7aa";
set c_sub_bg = "#582604";
set side_h_color = "#582604";
set side_h_border = "#582604";
set side_h_bg = "#e0c7aa";
set sub_color = "#b64c06";
#NEWLAYER: 3column/purple
layerinfo "type" = "theme";
layerinfo "name" = "Purple";
layerinfo "redist_uniq" = "3column/purple";
set color_bg = "#e8cff1";
set font_color = "#410458";
set link_color = "#410458";
set link_hover = "#410458";
set link_side = "#410458";
set link_side_h = "#410458";
set side_bg = "#e8cff1";
set entries_bg = "#e8cff1";
set entries_border = "#410458";
set side_t_color = "#410458";
set button_bg = "#e8cff1";
set button_bg_h = "#d1aae0";
set entries_font_color = "#410458";
set side_border = "#410458";
set c_sub_color = "#d1aae0";
set c_sub_bg = "#410458";
set side_h_color = "#410458";
set side_h_border = "#410458";
set side_h_bg = "#d1aae0";
set sub_color = "#410458";
#NEWLAYER: 3column/mellowyellow
layerinfo "type" = "theme";
layerinfo "name" = "Mellow Yellow";
layerinfo "redist_uniq" = "3column/mellowyellow";
set color_bg = "#f8f6da";
set font_color = "#42331e";
set link_color = "#42331e";
set link_hover = "#8d452f";
set link_side = "#8d452f";
set link_side_h = "#42331e";
set side_bg = "#f2c98e";
set entries_bg = "#f8f6da";
set entries_border = "#42331e";
set side_t_color = "#42331e";
set button_bg = "#f2c98e";
set button_bg_h = "#e8c098";
set entries_font_color = "#42331e";
set side_border = "#42331e";
set c_sub_color = "#42331e";
set c_sub_bg = "#f2c98e";
set side_h_color = "#333333";
set side_h_border = "#333333";
set side_h_bg = "#f8f6da";
set sub_color = "#d39945";

View File

@@ -0,0 +1,999 @@
# -*-s2-*-
layerinfo "type" = "layout";
layerinfo "name" = "A Novel Conundrum";
layerinfo "source_viewable" = 1;
layerinfo "redist_uniq" = "anovelconundrum/layout";
layerinfo "author_name" = "taion";
propgroup colors {
property Color page_back {
des = "Page background";
}
property Color entry_text {
des = "Entry text color";
}
property Color text_weaker {
des = "Weaker text color";
}
property Color page_link {
des = "Link color";
}
property Color page_vlink {
des = "Visited link color";
}
property Color page_alink {
des = "Active link color";
}
}
# From my last e-mail with Taion, the plan was to rasterize the leading fonts so that
# appearance issues could be avoided. However, I don't have access to many of the fonts
# that he tested with, so I'll have to put that off for later.
# You will need access to Microsoft provided fonts for most accurate rendering, but
# we are working on specifying usable alternatives that are cross platform friendly.
propgroup fonts {
property use font_base;
property string font_fallback {
des = "Alternative font style";
}
property string font_type {
des = "Body font type";
note = "General font class for body text";
}
property int font_size {
des = "Body font size (points)";
}
property int font_leading {
des = "Body font leading (points)";
}
property string title_letterspacing {
des = "Letterspacing in titles";
}
property bool title_smallcaps {
des = "Smallcaps in titles";
}
property bool title_underline {
des = "Underlined titles";
}
property string font_flourish_base {
des = "Font face for decorative flourishes";
}
property string font_flourish_fallback {
des = "Alternate font face for decorative flourishes";
}
property string font_flourish_type {
des = "Font type for decorative flourishes";
}
property string font_secondary_base {
des = "Font face for secondary text";
}
property string font_secondary_fallback {
des = "Alternate font face for secondary text";
}
property string font_secondary_type {
des = "Font type for secondary text";
}
property int font_secondary_size {
des = "Secondary font size (points)";
note = "For best results, match optical x-height with the body font";
}
property string flourish_rm {
des = "Right margin for flourishes";
}
property string dc_rm {
des = "Right margin for drop caps";
}
}
propgroup presentation {
property string dingbar_url {
des = "URL to spacer image between portions of content";
note = "A default will be chosen for you if left blank.";
}
property use page_recent_items;
property use page_friends_items;
property string body_width {
des = "Text body width";
}
property int dcLen {
des = "Minimum length in characters before using drop caps";
}
property use view_entry_disabled;
property use use_shared_pic;
property use comment_userpic_style;
property bool show_entrynav_icons {
des = "Toggle to show the next, memory, edit, etc icons on the entry view page";
}
property string page_background_image {
des = "URL to an image to be used for the page background";
}
property use external_stylesheet;
}
propgroup text {
property use text_post_comment;
property use text_read_comments;
property use text_post_comment_friends;
property use text_read_comments_friends;
property use text_meta_music;
property use text_meta_mood;
property string text_dingbar_alt {
des = "Alternative text for dingbar images";
noui = 1;
}
}
# Set default colors
set entry_text = "#000000";
set text_weaker = "#666666";
set page_link = "#000000";
set page_vlink = "#666666";
set page_alink = "#333333";
set page_back = "#F5F5DC";
set body_width = "35em";
set dcLen = 200;
set show_entrynav_icons = true;
set page_background_image = "";
set font_base = "Palatino Linotype";
set font_fallback = "Book Antiqua";
set font_type = "serif";
set font_flourish_base = "Edwardian Script ITC\", \"Edwardian Script ITC Semi-Expanded";
set font_flourish_fallback = "Zapfino\", \"Viner Hand ITC";
set font_flourish_type = "cursive";
set font_secondary_base = "Frutiger Linotype";
set font_secondary_fallback = "Tahoma";
set font_secondary_type = "sans-serif";
set font_size = 10;
set font_leading = 14;
set title_smallcaps = true;
set title_underline = false;
set title_letterspacing = "0.08em";
set font_secondary_size = 9;
set flourish_rm = "0.093em";
set dc_rm = "0.2em";
set dingbar_url = "";
set text_poster_anonymous = "an anonymous reader";
set text_dingbar_alt = "* * *";
set text_view_recent = "Entries";
set text_view_friends = "Friends";
set text_view_archive = "Archive";
set text_view_userinfo = "Profile";
function prop_init() {
var PalItem start = PalItem(0, $*entry_text);
var PalItem end = PalItem(13, $*page_back);
if ($*dingbar_url == "") { $*dingbar_url = palimg_gradient("anovelconundrum/dingbar.gif", $start, $end); }
}
function print_stylesheet() {
print clean_url($*page_background_image) != "" ? "body { background-image: url($*page_background_image); }" : "";
"""
body {
margin-top: 1in;
margin-bottom: 0.6in;
}
body, td, h2, .caption, h1, h3 {
""";
if ($*font_base != "" or $*font_fallback != "" or $*font_type != "") {
"font-family: ";
if ($*font_base != "") {
"\"$*font_base\"";
if ($*font_fallback != "" or $*font_type != "") {
", ";
}
}
if ($*font_fallback != "") {
"\"$*font_fallback\"";
if ($*font_type != "") {
", ";
}
}
if ($*font_type != "") {
"$*font_type";
}
";\n";
}
"""
font-size: ${*font_size}pt;
line-height: ${*font_leading}pt;
font-weight: normal;
}
.caption, h1, h3 {
"""; if($*title_smallcaps) {"""
font-variant: small-caps;
text-transform: lowercase;
"""; }
""" letter-spacing: $*title_letterspacing;
}
h1 { font-size: 16pt; }
h3 { font-size: 12pt; }
h2, .noul, .ult {
font-style: italic;
margin: 0px;
}
.caption {
"""; if($*title_underline) {"""
text-decoration: underline;
"""; }
""" }
.flourish, .bodyl:first-letter {
""";
if ($*font_flourish_base != "" or $*font_flourish_fallback != "" or $*font_flourish_type != "") {
"font-family: ";
if ($*font_flourish_base != "") {
"\"$*font_flourish_base\"";
if ($*font_flourish_fallback != "" or $*font_flourish_type != "") {
", ";
}
}
if ($*font_flourish_fallback != "") {
"\"$*font_flourish_fallback\"";
if ($*font_flourish_type != "") {
", ";
}
}
if ($*font_flourish_type != "") {
"$*font_flourish_type";
}
";\n";
}
"""
}
.flourish {
margin-right: ${*flourish_rm};
z-index: 1;
font-size: 34pt;
position: relative;
top: 0.1em;
text-transform: uppercase;
}
.sfon, .index, .author, select, input {
""";
if ($*font_secondary_base != "" or $*font_secondary_fallback != "" or $*font_secondary_type != "") {
"font-family: ";
if ($*font_secondary_base != "") {
"\"$*font_secondary_base\"";
if ($*font_secondary_fallback != "" or $*font_secondary_type != "") {
", ";
}
}
if ($*font_secondary_fallback != "") {
"\"$*font_secondary_fallback\"";
if ($*font_secondary_type != "") {
", ";
}
}
if ($*font_secondary_type != "") {
"$*font_secondary_type";
}
";\n";
}
""" font-size: ${*font_secondary_size}pt;
line-height: ${*font_leading}pt;
}
.index {
width: 10em;
margin-right: 1.2em;
}
.bodybox { width: $*body_width; }
.body, .bodyl, .bodyns {
text-align: justify;
}
.bodyl:first-letter {
font-size: """ + (2* $*font_leading) + """pt;
margin-bottom: -""" + $*font_size + """pt;
margin-right: ${*dc_rm};
float: left;
border-bottom: none;
text-transform: uppercase;
line-height: """ + (2* $*font_leading) + """pt;
}
.bodyns:first-line, .sc, small, .sct {
"""; if($*title_smallcaps) {"""
font-variant: small-caps;
text-transform: lowercase;
"""; }
if($*title_underline) {"""
text-decoration: underline;
"""; }
""" letter-spacing: 0.05em;
}
.sct {
letter-spacing: $*title_letterspacing;
text-align: center;
}
.author {
float: right;
text-align: center;
margin-left: 1.5em;
margin-bottom: 0.5em;
}
.ywp {
width: 2em;
margin-left: 0.5em;
margin-right: 0.5em;
}
blockquote {
margin-top: ${*font_leading}pt;
margin-bottom: ${*font_leading}pt;
margin-left: 2em;
}
tt, pre, textarea {
font-family: "Lucida Console", monospace;
font-size:"""+ ((${*font_size}*4)/5) + """pt;
}
a {text-decoration: none;}
.body a, .bodyl a, .bodyns a, .bodynsl a, .author a, .ult a, .uts a { border-bottom: 1px dotted; }
.ljuser a, a img, .smallbar a, .noul a { border-bottom: none; }
a:hover, .ljuser a:hover { border-bottom: 1px solid; }
p {
text-indent: 1.5em;
margin: 0px;
padding: 0px;
}
blockquote + p { text-indent: 0px; }
.uts {
font-size: 80%;
font-style: italic;
text-align: center;
line-height: """ + $*font_size + """pt;
margin-bottom: """ + ($*font_leading-$*font_size) + """pt;
}
.smallbar {
font-size: 80%;
font-style: italic;
text-align: center;
line-height: """ + (2*$*font_leading) + """pt;
clear: right;
}
.ljcomsel {
position: relative;
top: 0.75pt;
height: 7.5pt;
padding-left: 1pt;
}
input#username, input#password { margin-right: 0.5em; }
.bs {
margin-top: """ + (2*$*font_leading) + """pt;
margin-bottom: """ + (2*$*font_leading) + """pt;
text-align: center;
line-height: ${*font_leading}pt;
}
""";
}
function find_lpar(string t) : int {
foreach var int i (reverse (0 .. ($t->length()-1))) {
if($t->substr($i,1)=="(") { return $i; }
}
return -1;
}
function render_title(string t, int len) {
foreach var int i (0 .. ($len-1)) {
var string pc = $t->substr($i-1,1);
var string cc = $t->substr($i,1);
if($cc==" ") { " &middot; "; }
elseif( $i > 0 and $pc != " ") { "$cc"; }
elseif ($cc=="A" or $cc=="B" or $cc=="C" or $cc=="D" or $cc=="E" or $cc=="F" or $cc=="G" or $cc=="H" or $cc=="I" or $cc=="J" or $cc=="K" or $cc=="L" or $cc=="M" or $cc=="N" or $cc=="O" or $cc=="P" or $cc=="Q" or $cc=="R" or $cc=="S" or $cc=="T" or $cc=="U" or $cc=="V" or $cc=="W" or $cc=="X" or $cc=="Y" or $cc=="Z") {
"<span class='flourish'>$cc</span>";
}
else { "$cc"; }
}
}
function render_body(string t) {
var int str=0;
var bool par=false;
var bool pars=false;
$str = ($t->substr(0,6) == "<br />" ? 6 : 0);
if($t->substr(0,3) == "<p>") { $str = 3; $pars=true; }
foreach var int i ($str .. ($t->length()-1)) {
if($t->substr($i,12) == "<br /><br />") {
$str=$i+12;
if($par) { "</p><p>"; }
else { "<p>"; $par=true; }
}
elseif($pars and $t->substr($i,4) == "</p>") { $str=$i+4; $pars=false; }
elseif($i >= $str) { print $t->substr($i,1); }
}
if($par) { "</p>"; }
}
function display_title(Page p) {
var string dtitle = $p.global_title;
var string stitle = $p->view_title();
$stitle = ($p.view == "recent" ? $p.global_subtitle : $stitle);
var int lenm=$stitle->length()-1;
var int i=$dtitle->length()+1;
if ($dtitle == "") {
$dtitle = $p.journal.name;
$i=$dtitle->length()+1;
}
if ($p.view == "friends") {
$dtitle = $p->view_title();
$i=find_lpar($dtitle);
if($i==-1) { $i = $lenm+2; }
}
"""<div align="center">
<h1>"""; render_title($dtitle,$i-1); """</h1>""";
if($p.view != "friends" and $stitle != "") { """<br /><h3 style="margin-top:-1em;">$stitle</h3>"""; }
elseif($p.view == "friends" and $i<$lenm) { "<br /><h3 style='margin-top:-1em;'>" + $dtitle->substr($i+1,($lenm-$i)-1) + "</h3>"; }
"""</div>""";
}
function Page::print() {
var string title = $this->title();
var string links;
var bool firstlink = true;
foreach var string v ($.views_order) {
if ($firstlink == false) {
$links = "$links &middot; ";
}
else {
$firstlink = false;
}
$links = $links + ("<a href='$.view_url{$v}'>"+lang_viewname($v)+"</a>");
}
"""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n<html>\n<head>\n""";
if ($*external_stylesheet) {
println """<link rel="stylesheet" href="$.stylesheet_url" type="text/css" />""";
} else {
println """<style type="text/css">"""; print_stylesheet(); "</style>";
}
$this->print_head();
"""<title>$title</title>
</head>
<body bgcolor="$*page_back" text="$*entry_text" link="$*page_link" vlink="$*page_vlink" alink="$*page_alink">""";
display_title($this);
"""<div style="text-align:center; margin-bottom: ${*font_leading}pt;">
<h2>$links</h2>
</div>
""";
$this->print_body();
"""
</body>
</html>
""";
}
function print_entry(Page p, Entry e) {
var string date=$e.time->date_format("short");
var string time=$e.time->time_format();
"""
<table cellpadding='0' cellspacing='0' border='0' align='center'>
<tr><td align="center" colspan="2"><img
src="$*dingbar_url" alt="$*text_dingbar_alt" class="bs"/></td></tr>
<tr>
<td align="right" valign="top" width="100"><div class="index">""";
""" <a href="$e.permalink_url">$time<br />
$date</a><br /><br />
""";
$e.comments->print();
""" </div></td>
<td valign="top">
<div class="bodybox">
<div class="author">""";
if (defined $e.userpic) {
"""<img border="0" src="$e.userpic.url" width="$e.userpic.width" height="$e.userpic.height" alt=""><br />""";
}
elseif ($e.poster.journal_type == "C") {
"""<img border="0" src="$*IMGDIR/community.gif" alt="" /><br />""";
}
elseif ($e.poster.journal_type == "Y") {
"""<img border="0" src="$*IMGDIR/syndicated.gif" alt="" /><br />""";
}
else {
"""<img border="0" src="$*IMGDIR/userinfo.gif" alt="" /><br />""";
}
"<a href=\"" +
$e.poster->base_url() + "/\">$e.poster.username</a>";
if ($e.security != "") {
$e.security_icon->print();
}
if ($e.poster.username != $e.journal.username and $e.journal.journal_type =="C") {
""",<br />
<img border="0" src="$*IMGDIR/community.gif" alt="" align="absmiddle" />
<a href=\"""" + $e.journal->base_url() + """/">$e.journal.username</a>""";
}
var string subject=$e.subject;
if($p.view=="entry") { $subject=""; }
"""</div>
<div class="caption">
$subject
</div>""";
if ($subject == "" and $p.view!="entry") {"<div class='bodyns'>";}
elseif ($e.text->length() > $*dcLen) {"<div class='bodyl'>";}
else {"<div class='body'>";}
render_body($e.text);
var string metadata;
if ($e.metadata) {
$metadata = "<div style='margin-top:${*font_leading}pt;'><table cellspacing='0' cellpadding='0' border='0'>";
foreach var string k ($e.metadata) {
var string text = $k;
var string val = $e.metadata{$k};
if ($k == "mood") {
$text = $*text_meta_mood;
} elseif ($k == "music") {
$text = $*text_meta_music;
}
if ($k == "mood" and defined $e.mood_icon) {
var Image i = $e.mood_icon;
$val = "<img src='$i.url' width='$i.width' height='$i.height' align='middle' alt='$val'> $val";
}
$metadata = """$metadata\n<tr><td align="right"><div class="sc" style="margin-right:1em;">$text:</div></td>
<td align="left">$val</td></tr>""";
}
$metadata = """$metadata</table></div>""";
}
"""$metadata</div></div></td>
</tr>""";
if ($p.view == "entry" and $*show_entrynav_icons)
{
"""<tr><td align="center" colspan="2" style="line-height:${*font_leading}pt;"><img
src="$*dingbar_url" alt="$*text_dingbar_alt" class="bs" /></td></tr>
<tr><td colspan="2"><div style='text-align: center; margin-top: 0px;'>""";
$e->print_linkbar();
"""</div></td></tr>""";
}
"</table>";
} # print_entry(Page,Entry,Color,Color)
function Entry::print_linkbar() {
## There's no point in showing previous/next links on pages which show
## multiple entries anyway, so we only print them on EntryPage and ReplyPage.
var Page p = get_page();
var Link link;
var bool show_interentry = ($p.view == "entry" or $p.view == "reply");
"<h2>";
if ($show_interentry) {
var Link prev = $this->get_link("nav_prev");
"""<a href="$prev.url">$prev.caption</a> &middot """;
}
if ($p.view == "entry" and $.comments.enabled) {
if ($.comments.maxcomments) {
"Maximum Comments Reached";
} else {
"<a href=\"$.comments.post_url\">Leave a Comment</a>";
}
" &middot; ";
}
var int i=0;
foreach var string k ($.link_keyseq) {
$link = $this->get_link($k);
if($link.caption != "") {
if($i>0) { " &middot; "; }
"<a href='$link.url'>$link.caption</a>";
$i++;
}
}
if ($show_interentry) {
var Link next = $this->get_link("nav_next");
""" &middot <a href="$next.url">$next.caption</a>""";
}
"</h2>";
}
function Page::print_entry(Entry e) {
print_entry($this, $e);
}
function FriendsPage::print_entry(Entry e) {
print_entry($this, $e);
}
function RecentPage::print_body() {
foreach var Entry e ($.entries) {
$this->print_entry($e);
}
"""
<div align="center" class="bs" style="line-height: ${*font_leading}pt;"><img
src="$*dingbar_url" style="text-align:center;" alt="$*text_dingbar_alt" /></div>
<div align="center"><h2>
""";
# go forward/backward if possible
if ($.nav.forward_url != "" or $.nav.backward_url != "") {
var string sep;
var string back;
var string forward;
if ($.nav.backward_url != "") {
$back = """<a href="$.nav.backward_url">Previous</a>""";
}
if ($.nav.forward_url != "") {
$forward = """<a href="$.nav.forward_url">Next</a>""";
}
if ($back != "" and $forward != "") { $sep = " &middot; "; }
"$back$sep$forward";
}
"</h2></div>";
}
function CommentInfo::print() {
if (not $.enabled) { return; }
if ($.count > 0 or $.screened) {
$this->print_readlink(); "<br />";
}
$this->print_postlink();
}
function YearPage::print_year_links() {
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div><div class="sct">Years</div><h2 style="text-align:center;">""";
var bool d=false;
foreach var YearYear y ($.years) {
if($d) { " &middot; "; }
else { $d=true; }
if ($y.displayed) {
"$y.year";
} else {
"<a href=\"$y.url\">$y.year</a>";
}
}
"</h2>";
}
function YearPage::print_month(YearMonth m) {
if (not $m.has_entries) { return; }
"""<div class="bs">
<table cellpadding="0" cellspacing="0" border="0" summary="" align="center">
<tr><center class="noul">
<a href="$m.url">""";
print $m->month_format();
"""</a>
<!-- now the headings for the week -->
<table align="center" cellpadding="0" cellspacing="0" border="0" summary="">
<tr align="center">
""";
foreach var int d (weekdays()) {
"<td align='center'>"+$*lang_dayname_short[$d]+"</td>\n";
}
"</tr>";
foreach var YearWeek w ($m.weeks) {
$w->print();
}
"""</table></center>""";
}
function YearWeek::print() {
"<tr valign='top'>";
if ($.pre_empty) { "<td colspan='$.pre_empty'></td>"; }
foreach var YearDay d ($.days) {
"""<td><div class="ywp"><div class="sfon">$d.day</div>""";
if ($d.num_entries) {
"""<div class="uts"><a href="$d.url">$d.num_entries</a></div>""";
} else {
"&nbsp;";
}
"</div></td>";
}
if ($.post_empty) { "<td colspan='$.post_empty'></td>"; }
"</tr>";
}
function DayPage::print_body() {
if (not $.has_entries) { print "<div class='sct'>" + ehtml($*text_noentries_day) + "</div>"; }
foreach var Entry e ($.entries) {
$this->print_entry($e);
}
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
var string tprev = ehtml($*text_day_prev);
var string tnext = ehtml($*text_day_next);
"""<center><h2><a href="$.prev_url">$tprev</a> &middot; <a href="$.next_url">$tnext</a></h2></center>""";
}
function MonthPage::print_body() {
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
"<center><div class='bodybox'><center><table border='0' cellspacing='0' cellpadding='0'><tr><td><dl>";
foreach var MonthDay d ($.days) {
if ($d.has_entries) {
"<dt><a href=\"$d.url\"><i>";
print lang_ordinal($d.day);
"</i></a>:</dt>\n<dd>";
$d->print_subjectlist();
"</dd>\n";
}
}
"</dl></td></tr></table></center></div></center>\n";
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
"<form method='post' action='$.redir.url'><center>";
$.redir->print_hiddens();
if ($.prev_url != "") { "<a href='$.prev_url' style='font-size:12pt;'>&#9756;</a> "; }
if (size $.months > 1) {
"<select name='redir_key'>\n";
foreach var MonthEntryInfo mei ($.months) {
var string sel;
if ($mei.date.year == $.date.year and $mei.date.month == $.date.month) {
$sel = " selected='selected'";
}
"<option value='$mei.redir_key'$sel>" + $mei.date->date_format($*lang_fmt_month_long) + "</option>";
}
"</select>\n<input type='submit' value='View' />";
}
if ($.next_url != "") { " <a href='$.next_url' style='font-size:12pt;'>&#9758;</a>\n"; }
"</center></form>";
}
function EntryPage::print_body() {
print_entry($this, $.entry);
if ($.entry.comments.enabled and $.comment_pages.total_subitems > 0) {
$.comment_pages->print();
$this->print_multiform_start();
"<div style='margin-top: 7pt;'></div>";
"<table align='center' cellspacing='0' border='0' cellpadding='0' style='display: none;'><tr><td>";
$this->print_comments($.comments);
"</td></tr></table>";
$.comment_pages->print();
if ($*show_entrynav_icons) {
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
"""<div class="bs">""";
$.entry->print_linkbar();
"""</div>""";
}
if ($this.multiform_on) {"""
<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>
<div style="text-align: center;">""";
$this->print_multiform_actionline();
$this->print_multiform_end();
"</div>";
}
}
}
function ItemRange::print() {
if ($.all_subitems_displayed) { return; }
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
"<center>";
"<a name='comments'></a><div style='width: $*body_width; text-align:center;'>";
"<h2>" + lang_page_of_pages($.current, $.total) + "</h2>";
var string url_prev = $this->url_of($.current - 1);
"<table cellspacing='0' cellpadding='0' border='0' align='center'><tr><td align='center' style='font-size: 14pt'>";
if ($.current != 1) {
print "<a href='$url_prev#comments'>&#9756;</a>";
} else {
print "&#9756;";
}
print " </td><td align='center'>";
foreach var int i (1..$.total) {
if ($i == $.current) { "$i"; }
else {
var string url_of = $this->url_of($i);
"<a href='$url_of#comments'>$i</a>";
}
if ($i != $.total) { ", "; }
}
"</td><td align='center' style='font-size: 14pt'> ";
var string url_next = $this->url_of($.current + 1);
if ($.current != $.total) {
print "<a href='$url_next#comments'>&#9758;</a>";
} else {
print "&#9758;";
}
"</td></tr></table></div></center>";
}
function EntryPage::print_comments(Comment[] cs) {
if (size $cs == 0) { return; }
foreach var Comment c ($cs) {
if($c.depth==1) {
"</td></tr></table>";
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
"<table align='center' cellspacing='0' border='0' cellpadding='0'><tr><td>";
}
var int indent = ($c.depth - 1) * 21;
"<div style='margin-left: ${indent}pt;'>\n";
if ($c.full) {
$this->print_comment($c);
} else {
$this->print_comment_partial($c);
}
"</div>";
$this->print_comments($c.replies);
}
}
function EntryPage::print_comment(Comment c) {
var string poster = defined $c.poster ? $c.poster->as_string() : $*text_poster_anonymous;
var string sub_icon;
if (defined $c.subject_icon) {
$sub_icon = $c.subject_icon->as_string();
}
"<a name='$c.anchor'></a>";
"<div class='bodybox'" + ($c.depth>1? " style='margin-top:${*font_leading}pt;'" : "") + ">";
if (defined $c.userpic and $*comment_userpic_style != "off") {
var int w = $c.userpic.width;
var int h = $c.userpic.height;
# WARNING: this will later be done by the system (it'll be a
# constructional property), so don't copy this hack into your
# layout layers or you'll be messed up later.
if ($*comment_userpic_style == "small") {
$w = $w / 2;
$h = $h / 2;
}
"<img src='$c.userpic.url' width='$w' height='$h' alt='[User Picture]' style='float: right;' class='author' />";
}
### From, date, etc
"<div class='noul'>";
if ($c.screened) { "<span style='color:$*text_weaker;'>(Screened) </span>"; }
"On " + $c.time->date_format("long") + ", " + $c.time->time_format() + ", $poster ";
if ($c.metadata{"poster_ip"}) { "(" + $c.metadata{"poster_ip"} + ") "; }
"<a href='$c.permalink_url'>";
if ($c.depth == 1) { "commented"; }
else { "replied"; }
"</a>:</div>";
print (defined $c.subject_icon or $c.subject != "") ? "<div class='caption'>$c.subject_icon $c.subject</div>" : "";
"<div class='body'>";
render_body($c.text);
print "</div><div class='smallbar'>";
if ($c.frozen) {
"Replies Frozen";
} else {
"<a href='$c.reply_url'>Reply</a>";
}
if ($c.parent_url != "") { " &middot; <a href='$c.parent_url'>Parent</a>"; }
if ($c.thread_url != "") { " &middot; <a href='$c.thread_url'>Thread</a>"; }
$c->print_linkbar();
if ($this.multiform_on) {
" &middot; ";
"<label for='ljcomsel_$c.talkid'>$*text_multiform_check</label>";
$c->print_multiform_check();
}
"</div></div>";
}
function EntryPage::print_comment_partial(Comment c) {
var string poster = defined $c.poster ? $c.poster->as_string() : $*text_poster_anonymous;
var bool subj = $c.subject != "";
"<div class='ult' style='width:$*body_width; margin-top:${*font_leading}pt;'>";
"&mdash;&thinsp;On " + $c.time->date_format("long") + ", " + $c.time->time_format() + ", $poster ";
if ($c.metadata{"poster_ip"}) { "(" + $c.metadata{"poster_ip"} + ") "; }
if($subj) { """replied, <a href="$c.permalink_url">&ldquo;$c.subject&rdquo;</a>"""; }
else { """posted <a href="$c.permalink_url">a reply</a>"""; }
".</div>";
}
function Comment::print_linkbar() {
var Link link;
foreach var string k ($.link_keyseq) {
$link = $this->get_link($k);
($link.caption != "") ? " &middot; <a href='$link.url'>$link.caption</a>" : "";
}
}
function ReplyPage::print_body() {
var bool ent = $.replyto.permalink_url == $.entry.permalink_url;
if($ent) {
print_entry($this, $.entry);
}
else {
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
var EntryLite c = $.replyto;
var string poster = defined $c.poster ? $c.poster->as_string() : $*text_poster_anonymous;
"<center><div style='width: $*body_width;'>";
if (defined $c.userpic and $*comment_userpic_style != "off") {
var int w = $c.userpic.width;
var int h = $c.userpic.height;
# WARNING: this will later be done by the system (it'll be a
# constructional property), so don't copy this hack into your
# layout layers or you'll be messed up later.
if ($*comment_userpic_style == "small") {
$w = $w / 2;
$h = $h / 2;
}
"<img src='$c.userpic.url' width='$w' height='$h' alt='[User Picture]' style='float: right;' class='author' />";
}
### From, date, etc
"<div class='noul'>";
"On " + $c.time->date_format("long") + ", " + $c.time->time_format() + ", $poster ";
if ($c.metadata{"poster_ip"}) { "(" + $c.metadata{"poster_ip"} + ") "; }
"<a href='$c.permalink_url'>commented:</a></div>";
print ($c.subject != "") ? "<div class='caption'>$c.subject</div>" : "";
"<div class='body'>";
render_body($c.text);
"</div></div></center>";
}
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
"<div style='text-align:center;'><h2><a href='$.entry.comments.read_url'>Read Comments</a></h2></div>";
"""<div class="bs">
<img src="$*dingbar_url" alt="$*text_dingbar_alt" />
</div>""";
if (not $.entry.comments.enabled) {
print "<div class='sct'>$*text_reply_nocomments</div>";
return;
}
"<center><h3 style='margin-top:0px; line-height:" + (2*$*font_leading) + "pt;'>Reply " + ($ent ? "to this entry" : "to this comment") + ":</h3>";
$.form->print();
"</center>";
}

View File

@@ -0,0 +1,179 @@
#NEWLAYER: anovelconundrum/renaissance
layerinfo "type" = "theme";
layerinfo "name" = "Renaissance";
layerinfo "redist_uniq" = "anovelconundrum/renaissance";
set font_secondary_fallback = "";
set font_secondary_size = 10;
set font_secondary_type = "serif";
set font_leading = 12;
set page_back = "#FAF2C8";
set font_fallback = "";
set font_secondary_base = "Garamond";
set font_base = "Garamond";
#NEWLAYER: anovelconundrum/modern
layerinfo "type" = "theme";
layerinfo "name" = "Modern";
layerinfo "redist_uniq" = "anovelconundrum/modern";
set font_leading = 14;
set page_back = "#F5F5E8";
set font_fallback = "";
set font_base = "Georgia";
set font_flourish_base = "";
set font_flourish_fallback = "";
set font_flourish_type = "";
set flourish_rm = "0.05em";
set dc_rm = "0.1em";
set dcLen = 5000;
#NEWLAYER: anovelconundrum/clippy
layerinfo "type" = "theme";
layerinfo "name" = "Clippy";
layerinfo "redist_uniq" = "anovelconundrum/clippy";
set font_leading = 13;
set page_back = "#FFFFFF";
set font_fallback = "Times";
set font_base = "Times New Roman";
set font_secondary_base = "Helvetica";
set font_secondary_fallback = "Arial";
set font_secondary_type = "sans-serif";
set font_secondary_size = 8;
set font_flourish_base = "";
set font_flourish_fallback = "";
set font_flourish_type = "";
set flourish_rm = "0.05em";
set dc_rm = "0.1em";
set flourish_rm = "0.08em";
set dcLen = 99999;
set body_width = "65ex";
#NEWLAYER: anovelconundrum/childsplay
layerinfo "type" = "theme";
layerinfo "name" = "Child's Play";
layerinfo "redist_uniq" = "anovelconundrum/childsplay";
set page_back = "#FFE6FF";
set font_base = "Comic Sans";
set font_fallback = "Chalkboard";
set font_type = "cursive";
set font_secondary_base = "";
set font_secondary_fallback = "";
set font_secondary_type = "";
set font_flourish_base = "";
set font_flourish_fallback = "";
set font_flourish_type = "";
set flourish_rm = "0.05em";
set dc_rm = "0.12em";
set flourish_rm = "0.08em";
set dcLen = 1000;
#NEWLAYER: anovelconundrum/laketahoe
layerinfo "type" = "theme";
layerinfo "name" = "Lake Tahoe";
layerinfo "redist_uniq" = "anovelconundrum/laketahoe";
set page_back = "#E6E6FF";
set font_base = "Tahoma";
set font_fallback = "Verdana";
set font_type = "sans-serif";
set font_secondary_base = "";
set font_secondary_fallback = "";
set font_secondary_type = "";
set font_flourish_base = "";
set font_flourish_fallback = "";
set font_flourish_type = "";
set flourish_rm = "0.05em";
set dc_rm = "0.14em";
set flourish_rm = "0.08em";
set dcLen = 500;
#NEWLAYER: anovelconundrum/deardiary
layerinfo "type" = "theme";
layerinfo "name" = "Dear Diary";
layerinfo "redist_uniq" = "anovelconundrum/deardiary";
set page_back = "#f9f7d6";
set font_base = "Lucida Handwriting";
set font_fallback = "";
set font_type = "cursive";
set font_leading = 16;
set body_width = "38em";
set font_secondary_base = "";
set font_secondary_fallback = "";
set font_secondary_type = "";
set font_flourish_base = "";
set font_flourish_fallback = "";
set font_flourish_type = "";
set flourish_rm = "0.05em";
set dc_rm = "0.12em";
set flourish_rm = "0.05em";
set dcLen = 500;
#NEWLAYER: anovelconundrum/scribal
layerinfo "type" = "theme";
layerinfo "name" = "Scribal";
layerinfo "redist_uniq" = "anovelconundrum/scribal";
set page_back = "#FAF2C8";
set font_base = "Lucida Calligraphy";
set font_fallback = "";
set font_type = "cursive";
set font_leading = 15;
set title_smallcaps = false;
set title_underline = true;
set font_secondary_base = "";
set font_secondary_fallback = "";
set font_secondary_type = "";
set font_flourish_base = "";
set font_flourish_fallback = "";
set font_flourish_type = "";
set flourish_rm = "0.05em";
set dc_rm = "0.12em";
set flourish_rm = "0.05em";
set dcLen = 500;
#NEWLAYER: anovelconundrum/glossy
layerinfo "type" = "theme";
layerinfo "name" = "Glossy Magazine";
layerinfo "redist_uniq" = "anovelconundrum/glossy";
set page_back = "#F4FBFF";
set font_base = "Lucida Bright";
set font_fallback = "Georgia";
set font_type = "serif";
set font_secondary_base = "Lucida Sans";
set font_secondary_fallback = "";
set font_secondary_type = "sans-serif";
set font_secondary_size = 9;
set font_flourish_base = "Agency FB";
set font_flourish_fallback = "";
set font_flourish_type = "";
set flourish_rm = "0.05em";
set dc_rm = "0.08em";
set flourish_rm = "0.02em";
set dcLen = 1200;
#NEWLAYER: anovelconundrum/retro
layerinfo "type" = "theme";
layerinfo "name" = "Retrossential";
layerinfo "redist_uniq" = "anovelconundrum/retro";
set page_back = "#f9f9e3";
set font_base = "Rockwell";
set font_fallback = "";
set font_type = "serif";
set font_size = 11;
set font_secondary_base = "";
set font_secondary_fallback = "";
set font_secondary_type = "";
set font_secondary_size = 10;
set font_flourish_base = "";
set font_flourish_fallback = "";
set font_flourish_type = "";
set flourish_rm = "0.05em";
set dc_rm = "0.12em";
set flourish_rm = "0.05em";
set dcLen = 500;

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,225 @@
#NEWLAYER: boxer/lipstick
layerinfo "type" = "theme";
layerinfo "name" = "Lipstick";
layerinfo redist_uniq = "boxer/lipstick";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#B46F60";
set entry_link = "#ffeae5";
set entry_bg = "#eebbaf";
set nav_scale = "darker";
set info_font = "#A55745";
set bg_color = "#fac7bb";
set entry_font = "#A55745";
set info_link_visited = "#ffeae5";
set page_background_pattern = "background-hearts.gif";
set entry_link_visited = "#ffffff";
set info_bg = "#e5a799";
set info_link = "#FFF9F8";
#NEWLAYER: boxer/greyskies
layerinfo "type" = "theme";
layerinfo "name" = "Grey Skies";
layerinfo redist_uniq = "boxer/greyskies";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#9aaeb6";
set entry_link = "#997D63";
set entry_bg = "#f2eae2";
set nav_scale = "lighter";
set info_font = "#627a84";
set bg_color = "#dde1e3";
set entry_font = "#7f97a0";
set info_link_visited = "#CEDBDF";
set page_background_pattern = "background-vert-stripes.gif";
set entry_link_visited = "#b4a18f";
set info_bg = "#8c9ca2";
set info_link = "#b9c7cc";
#NEWLAYER: boxer/eggplant
layerinfo "type" = "theme";
layerinfo "name" = "Eggplant";
layerinfo redist_uniq = "boxer/eggplant";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#4e6559";
set entry_link = "#9FAE8D";
set entry_bg = "#635A48";
set nav_scale = "lighter";
set info_font = "#899BA3";
set bg_color = "#9fae8d";
set entry_font = "#E7DFCF";
set info_link_visited = "#b9c7cc";
set entry_link_visited = "#A6B2AC";
set info_bg = "#464640";
set info_link = "#b9c7cc";
#NEWLAYER: boxer/lonelyturquoise
layerinfo "type" = "theme";
layerinfo "name" = "Lonely Turquoise";
layerinfo redist_uniq = "boxer/lonelyturquoise";
layerinfo "source_viewable" = 0;
set page_background_pattern = "background-lg-boxes.gif";
set color_nav_bg = "#7f7f7f";
set entry_link_visited = "#ededed";
set entry_link = "#ffffff";
set entry_bg = "#ABABAB";
set info_font = "#000000";
set info_bg = "#00b1ae";
set bg_color = "#ffffff";
set entry_font = "#363636";
set info_link = "#006260";
#NEWLAYER: boxer/legallypink
layerinfo "type" = "theme";
layerinfo "name" = "Legally Pink";
layerinfo redist_uniq = "boxer/legallypink";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#ff089b";
set entry_link_visited = "#70086F";
set entry_link = "#4D0870";
set entry_bg = "#ec008c";
set nav_scale = "lighter";
set info_font = "#ec008c";
set bg_color = "#f49ac1";
set entry_font = "#fbdbee";
set info_link_visited = "#CE1182";
set page_background_pattern = "background-hearts.gif";
set info_bg = "#ffadde";
set info_link = "#E3026A";
#NEWLAYER: boxer/greybrowngreen
layerinfo "type" = "theme";
layerinfo "name" = "Grey Brown Green";
layerinfo redist_uniq = "boxer/greybrowngreen";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#2f372e";
set entry_link = "#212919";
set entry_bg = "#586C44";
set nav_scale = "lighter";
set info_font = "#A1926C";
set bg_color = "#000000";
set entry_font = "#D9E1D2";
set info_link_visited = "#B1AD9B";
set entry_link_visited = "#2C3F28";
set info_bg = "#433a1e";
set info_link = "#B1AD9B";
#NEWLAYER: boxer/precious
layerinfo "type" = "theme";
layerinfo "name" = "Precious";
layerinfo redist_uniq = "boxer/precious";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#A17682";
set entry_link = "#0F486E";
set entry_bg = "#c6cfd5";
set nav_scale = "lighter";
set info_font = "#F7FDFC";
set bg_color = "#d9e1d9";
set entry_font = "#6C7982";
set info_link_visited = "#E4ECEB";
set entry_link_visited = "#3F6A86";
set info_bg = "#a2b2b1";
set info_link = "#E4ECEB";
#NEWLAYER: boxer/purple
layerinfo "type" = "theme";
layerinfo "name" = "Purple";
layerinfo redist_uniq = "boxer/purple";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#78629C";
set entry_link = "#F9F5FC";
set entry_bg = "#62536C";
set nav_scale = "lighter";
set info_font = "#A38FA3";
set bg_color = "#8B77A9";
set entry_font = "#DCD3E2";
set info_link_visited = "#A799BF";
set entry_link_visited = "#ffffff";
set info_bg = "#472B47";
set info_link = "#A799BF";
#NEWLAYER: boxer/green
layerinfo "type" = "theme";
layerinfo "name" = "Green";
layerinfo redist_uniq = "boxer/green";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#748A25";
set entry_link = "#EAF9D4";
set entry_bg = "#748A25";
set nav_scale = "lighter";
set info_font = "#004C3C";
set bg_color = "#254634";
set entry_font = "#E2EED0";
set info_link_visited = "#00EEBC";
set entry_link_visited = "#EAF9D4";
set info_bg = "#0E9B7D";
set info_link = "#61EFD1";
#NEWLAYER: boxer/purpange
layerinfo "type" = "theme";
layerinfo "name" = "Purpange";
layerinfo redist_uniq = "boxer/purpange";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#151e1a";
set nav_scale = "lighter";
set entry_link = "#ffffff";
set entry_bg = "#E7513E";
set info_font = "#f6cab7";
set bg_color = "#ffffff";
set entry_font = "#FDE9E6";
set info_link_visited = "#afe900";
set page_background_pattern = "background-stitched.gif";
set entry_link_visited = "#ffffff";
set info_bg = "#91125f";
set info_link = "#afe900";
#NEWLAYER: boxer/purplesalmon
layerinfo "type" = "theme";
layerinfo "name" = "Purple Salmon";
layerinfo redist_uniq = "boxer/purplesalmon";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#AA6B90";
set entry_link = "#633F4B";
set entry_bg = "#CEADB8";
set nav_scale = "same";
set info_font = "#F0B4AA";
set bg_color = "#151E1A";
set entry_font = "#6D5B61";
set info_link_visited = "#7E3D33";
set entry_link_visited = "#633F4B";
set info_bg = "#D26E5E";
set info_link = "#7E3D33";
#NEWLAYER: boxer/refriedtribute
layerinfo "type" = "theme";
layerinfo "name" = "Refried Tribute";
layerinfo redist_uniq = "boxer/refriedtribute";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#666666";
set entry_link = "#336699";
set entry_bg = "#e6e6e6";
set info_font = "#999966";
set bg_color = "#ffffff";
set entry_font = "#656565";
set info_link_visited = "#4d4d4d";
set page_background_pattern = "none";
set entry_link_visited = "#336699";
set info_bg = "#cccc99";
set info_link = "#666666";
#NEWLAYER: boxer/blue
layerinfo "type" = "theme";
layerinfo "name" = "Blue";
layerinfo redist_uniq = "boxer/blue";
layerinfo "source_viewable" = 0;
set color_nav_bg = "#3A6A74";
set entry_link = "#E0DFCE";
set entry_bg = "#4F7B8A";
set info_font = "#908F78";
set bg_color = "#435754";
set entry_font = "#E7EEF0";
set info_link_visited = "#5D5C50";
set entry_link_visited = "#E0DFCE";
set info_bg = "#E0DFCE";
set info_link = "#4E5964";

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
#NEWLAYER: component/aquatic
layerinfo "type" = "theme";
layerinfo "name" = "Aquatic";
layerinfo redist_uniq = "component/aquatic";
layerinfo "source_viewable" = 0;
set entry_fgcolor = "#515151";
set comp_bgcolor = "#51ada6";
set header_link = "#ffffff";
set entry_link = "#0C5D57";
set entry_bgcolor = "#ffffff";
set header_fgcolor = "#c7d1cc";
set main_bgcolor = "#51ada6";
set calendar_active = "#318D86";
set header_bgcolor = "#0e3b4a";
set calendar_inactive = "#ffffff";
set comp_fgcolor = "#F7FEFF";
#NEWLAYER: component/bluetiful
layerinfo "type" = "theme";
layerinfo "name" = "Bluetiful";
layerinfo redist_uniq = "component/bluetiful";
layerinfo "source_viewable" = 0;
set entry_fgcolor = "#606060";
set comp_bgcolor = "#f4f4f4";
set header_link = "#f7fcfb";
set entry_link = "#2d3679";
set entry_bgcolor = "#eff5ff";
set header_fgcolor = "#327192";
set main_bgcolor = "#a2c5e3";
set header_bgcolor = "#6da6c4";
set comments_bgcolor = "#84b5d2";
#NEWLAYER: component/darkness
layerinfo "type" = "theme";
layerinfo "name" = "Darkness";
layerinfo redist_uniq = "component/darkness";
layerinfo "source_viewable" = 0;
set comments_screened_bgcolor = "#22620e";
set calendar_fgcolor = "#5b6c7a";
set calendar_active = "#5b6c7a";
set calendar_inactive = "#adbac5";
set comp_fgcolor = "#dadde0";
set entry_fgcolor = "#dddcd6";
set comp_bgcolor = "#758491";
set entry_link = "#adb6be";
set header_link = "#c7ccd1";
set entry_bgcolor = "#32343d";
set main_bgcolor = "#71837A";
set header_fgcolor = "#c7d1cc";
set header_bgcolor = "#728d80";
set comments_bgcolor = "#5a5a5a";
#NEWLAYER: component/jedicloak
layerinfo "type" = "theme";
layerinfo "name" = "Jedi Cloak";
layerinfo redist_uniq = "component/jedicloak";
layerinfo "source_viewable" = 0;
set entry_fgcolor = "#F1EDE5";
set comp_bgcolor = "#988b67";
set header_link = "#dddbd6";
set entry_link = "#615020";
set entry_bgcolor = "#ada386";
set comments_screened_bgcolor = "#a29f97";
set header_fgcolor = "#bcb59f";
set main_bgcolor = "#6e5845";
set calendar_active = "#817452";
set header_bgcolor = "#6e6449";
set calendar_inactive = "#988b67";
set comp_fgcolor = "#383325";
set comments_bgcolor = "#948B6F";
#NEWLAYER: component/mellow
layerinfo "type" = "theme";
layerinfo "name" = "Mellow";
layerinfo redist_uniq = "component/mellow";
layerinfo "source_viewable" = 0;
set comments_screened_bgcolor = "#D6C8CC";
set page_background_image = "";
set calendar_active = "#D9E1D9";
set calendar_inactive = "#A2B2B1";
set comp_fgcolor = "878D8D";
set entry_fgcolor = "#355351";
set comp_bgcolor = "#C6CFD5";
set entry_link = "#6B7F8D";
set header_link = "6B7F8D";
set entry_bgcolor = "#A2B2B1";
set main_bgcolor = "#D6C8CC";
set header_fgcolor = "#96A891";
set header_bgcolor = "#D9E1D9";
set comments_bgcolor = "#BCC4C3";
#NEWLAYER: component/mocha
layerinfo "type" = "theme";
layerinfo "name" = "Mocha";
layerinfo redist_uniq = "component/mocha";
layerinfo "source_viewable" = 0;
set page_background_image = "";
set entry_fgcolor = "#807C75";
set comp_bgcolor = "#b1bbb4";
set comp_fgcolor = "#78746e";
set header_link = "#716C53";
set entry_link = "#67716a";
set entry_bgcolor = "#ebe6d1";
set comments_screened_bgcolor = "#b2c9b9";
set header_fgcolor = "#7a6f43";
set main_bgcolor = "#d8d0ae";
set calendar_active = "#8da193";
set header_bgcolor = "#d8d0ae";
set calendar_inactive = "#d3e1d7";
set comments_bgcolor = "#dbdedc";
#NEWLAYER: component/orangesoda
layerinfo "type" = "theme";
layerinfo "name" = "Orange Soda";
layerinfo redist_uniq = "component/orangesoda";
layerinfo "source_viewable" = 0;
set entry_fgcolor = "#8a5b33";
set comp_bgcolor = "#c4bd8e";
set header_link = "#FFFFFF";
set entry_link = "#613815";
set entry_bgcolor = "#dbd7b9";
set comments_screened_bgcolor = "#cbc9b9";
set header_fgcolor = "#d7d2af";
set main_bgcolor = "#cd9338";
set calendar_active = "#cd9338";
set header_bgcolor = "#c6710a";
set calendar_inactive = "#dbd7b9";
set comments_bgcolor = "#e4deb5";
#NEWLAYER: component/pinkelephants
layerinfo "type" = "theme";
layerinfo "name" = "Pink Elephants";
layerinfo redist_uniq = "component/pinkelephants";
layerinfo "source_viewable" = 0;
set comments_screened_bgcolor = "#e29994";
set page_background_image = "";
set calendar_active = "#a8a8a7";
set calendar_inactive = "#e29994";
set comp_fgcolor = "#ad5a55";
set entry_fgcolor = "#ffffff";
set comp_bgcolor = "#f7b3ae";
set entry_link = "#777776";
set header_link = "#FFFFFF";
set entry_bgcolor = "#a8a8a7";
set main_bgcolor = "#e29994";
set header_fgcolor = "#ad5a55";
set header_bgcolor = "#e29994";
set comments_bgcolor = "#9c9c9c";
#NEWLAYER: component/sugarspice
layerinfo "type" = "theme";
layerinfo "name" = "Sugar and Spice";
layerinfo redist_uniq = "component/sugarspice";
layerinfo "source_viewable" = 0;
set entry_fgcolor = "#927DAD";
set comp_bgcolor = "#D5EBFA";
set header_link = "#6e5593";
set entry_link = "#759EBA";
set entry_bgcolor = "#D5E9D7";
set comments_screened_bgcolor = "#BEC9BF";
set header_fgcolor = "#6E5593";
set main_bgcolor = "#FCD9C4";
set calendar_active = "#FCD9C4";
set header_bgcolor = "#D4CAE1";
set calendar_inactive = "#D4CAE1";
set comp_fgcolor = "AA9ABF";
set comments_bgcolor = "#B9DABC";
#NEWLAYER: component/greensuperhero
layerinfo "type" = "theme";
layerinfo "name" = "That Green Superhero";
layerinfo redist_uniq = "component/greensuperhero";
layerinfo "source_viewable" = 0;
set comments_screened_bgcolor = "#c99bd7";
set page_background_image = "";
set calendar_active = "#bc6ed4";
set calendar_inactive = "#c99bd7";
set comp_fgcolor = "#bc87cb";
set entry_fgcolor = "#424242";
set comp_bgcolor = "#efefe1";
set entry_link = "#027040";
set header_link = "#ffffff";
set entry_bgcolor = "#e4ede9";
set main_bgcolor = "#16b16c";
set header_fgcolor = "#d0efe2";
set header_bgcolor = "#049957";
set comments_bgcolor = "#bed8cc";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,615 @@
#NEWLAYER: flexiblesquares/autumn
layerinfo "redist_uniq" = "flexiblesquares/autumn";
layerinfo "type" = "theme";
layerinfo "name" = "Autumn";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#351805";
set page_fgcolor = "#000000";
set content_bgcolor = "#6a3106";
set entry_bgcolor = "#eee293";
set entry_fgcolor = "#661f0c";
set border_color = "#000000";
set entrytitle_bgcolor = "#8b4e1d";
set outer_table_bgcolor = "#c47b51";
set sidebar_header_bgcolor = "#8b4e1d";
set sidebar_fgcolor = "#000000";
set header_footer_bgcolor = "#eee293";
set header_footer_fgcolor = "#661f0c";
set link_color = "#608729";
set link_hover_color = "#661f0c";
set comments_link_color = "#FFFFFF";
set comments_link_hover = "#c47b51";
set sidebar_link_color = "#608729";
set sidebar_link_hover = "#eee293";
set header_footer_link_color = "#608729";
set header_footer_link_hover = "#661f0c";
#NEWLAYER: flexiblesquares/blackwhite
layerinfo "redist_uniq" = "flexiblesquares/blackwhite";
layerinfo "type" = "theme";
layerinfo "name" = "Black and White and Red All Over";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#cccccc";
set page_fgcolor = "#ff0000";
set content_bgcolor = "#000000";
set entry_bgcolor = "#ffffff";
set entry_fgcolor = "#000000";
set border_color = "#ffffff";
set entrytitle_bgcolor = "#cc0000";
set outer_table_bgcolor = "#666666";
set sidebar_header_bgcolor = "#cc0000";
set sidebar_fgcolor = "#ffffff";
set header_footer_bgcolor = "#000000";
set header_footer_fgcolor = "#cc0000";
set date_fgcolor = "#000000";
set subject_fgcolor = "#000000";
set link_color = "#999999";
set link_hover_color = "#000000";
set comments_link_color = "#FFFFFF";
set comments_link_hover = "#000000";
set sidebar_link_color = "#999999";
set sidebar_link_hover = "#ffffff";
set header_footer_link_color = "#ffffff";
set header_footer_link_hover = "#999999";
#NEWLAYER: flexiblesquares/freshpaint
layerinfo "redist_uniq" = "flexiblesquares/freshpaint";
layerinfo "type" = "theme";
layerinfo "name" = "Fresh Paint";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#993366";
set page_fgcolor = "#006633";
set content_bgcolor = "#ff9933";
set entry_bgcolor = "#ffff99";
set entry_fgcolor = "#000000";
set border_color = "#6699cc";
set entrytitle_bgcolor = "#ccff66";
set outer_table_bgcolor = "#330033";
set sidebar_header_bgcolor = "#ccff66";
set sidebar_fgcolor = "#000000";
set header_footer_bgcolor = "#ffff99";
set header_footer_fgcolor = "#006633";
set date_fgcolor = "#000000";
set subject_fgcolor = "#000000";
set link_color = "#993333";
set link_hover_color = "#330033";
set comments_link_color = "#993333";
set comments_link_hover = "#330033";
set sidebar_link_color = "#993333";
set sidebar_link_hover = "#330033";
set header_footer_link_color = "#993333";
set header_footer_link_hover = "#330033";
#NEWLAYER: flexiblesquares/nautical
layerinfo "redist_uniq" = "flexiblesquares/nautical";
layerinfo "type" = "theme";
layerinfo "name" = "Nautical";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#bad2f6";
set page_fgcolor = "#000000";
set content_bgcolor = "#213659";
set entry_bgcolor = "#ffffff";
set entry_fgcolor = "#000000";
set border_color = "#d4e2f8";
set entrytitle_bgcolor = "#f0ef8e";
set outer_table_bgcolor = "#335288";
set sidebar_header_bgcolor = "#f0ef8e";
set sidebar_fgcolor = "#7fa1d5";
set header_footer_bgcolor = "#213659";
set header_footer_fgcolor = "#f0ef8e";
set date_fgcolor = "#000000";
set subject_fgcolor = "#000000";
set link_color = "#7fa1d5";
set link_hover_color = "#000000";
set comments_link_color = "#335288";
set comments_link_hover = "#7fa1d5";
set sidebar_link_color = "#f0ef8e";
set sidebar_link_hover = "#000000";
set header_footer_link_color = "#7fa1d5";
set header_footer_link_hover = "#ffffff";
#NEWLAYER: flexiblesquares/machine
layerinfo "redist_uniq" = "flexiblesquares/machine";
layerinfo "type" = "theme";
layerinfo "name" = "Machine";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#000000";
set page_fgcolor = "#C1C1C1";
set content_bgcolor = "#000000";
set entry_bgcolor = "#000000";
set entry_fgcolor = "#cbd3d8";
set border_color = "#7e8997";
set entrytitle_bgcolor = "#555a60";
set outer_table_bgcolor = "#000000";
set sidebar_header_bgcolor = "#555a60";
set sidebar_fgcolor = "#C1C1C1";
set header_footer_bgcolor = "#000000";
set header_footer_fgcolor = "#C1C1C1";
set date_fgcolor = "#C1C1C1";
set subject_fgcolor = "#C1C1C1";
set link_color = "#8797a0";
set link_hover_color = "#ffffff";
set comments_link_color = "#8797a0";
set comments_link_hover = "#ffffff";
set sidebar_link_color = "#8797a0";
set sidebar_link_hover = "#ffffff";
set header_footer_link_color = "#8797a0";
set header_footer_link_hover = "#ffffff";
#NEWLAYER: flexiblesquares/pastelspring
layerinfo "redist_uniq" = "flexiblesquares/pastelspring";
layerinfo "type" = "theme";
layerinfo "name" = "Pastel Spring";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#ffffff";
set page_fgcolor = "#993333";
set content_bgcolor = "#ffffcc";
set entry_bgcolor = "#ffffff";
set entry_fgcolor = "#000000";
set border_color = "#ebccb1";
set entrytitle_bgcolor = "#ebccb1";
set outer_table_bgcolor = "#dae3b2";
set sidebar_header_bgcolor = "#ebccb1";
set sidebar_fgcolor = "#993333";
set header_footer_bgcolor = "#ffffff";
set header_footer_fgcolor = "#9FA876";
set date_fgcolor = "#993333";
set subject_fgcolor = "#993333";
set link_color = "#9d7980";
set link_hover_color = "#000000";
set comments_link_color = "#993333";
set comments_link_hover = "#ffffff";
set sidebar_link_color = "#9d7980";
set sidebar_link_hover = "#9FA876";
set header_footer_link_color = "#9F3E3E";
set header_footer_link_hover = "#ebccb1";
#NEWLAYER: flexiblesquares/pinkpunk
layerinfo "redist_uniq" = "flexiblesquares/pinkpunk";
layerinfo "type" = "theme";
layerinfo "name" = "Pink Punk";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#000000";
set page_fgcolor = "#cc0066";
set content_bgcolor = "#000000";
set entry_bgcolor = "#ffccff";
set entry_fgcolor = "#000000";
set border_color = "#000000";
set entrytitle_bgcolor = "#ff6699";
set outer_table_bgcolor = "#cc0066";
set sidebar_header_bgcolor = "#ff6699";
set sidebar_fgcolor = "#ffccff";
set header_footer_bgcolor = "#ffccff";
set header_footer_fgcolor = "#000000";
set date_fgcolor = "#000000";
set subject_fgcolor = "#000000";
set link_color = "#6666cc";
set link_hover_color = "#ff6699";
set comments_link_color = "#ffccff";
set comments_link_hover = "#6666cc";
set sidebar_link_color = "#6666cc";
set sidebar_link_hover = "#ff6699";
set header_footer_link_color = "#6666cc";
set header_footer_link_hover = "#ff6699";
#NEWLAYER: flexiblesquares/purpleperiwinkle
layerinfo "redist_uniq" = "flexiblesquares/purpleperiwinkle";
layerinfo "type" = "theme";
layerinfo "name" = "Purple Periwinkle";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#2f0b31";
set page_fgcolor = "#2f0b31";
set content_bgcolor = "#671368";
set entry_bgcolor = "#f4a3e9";
set entry_fgcolor = "#2f0b31";
set border_color = "#2f0b31";
set entrytitle_bgcolor = "#9488d8";
set outer_table_bgcolor = "#a13ea7";
set sidebar_header_bgcolor = "#9488d8";
set sidebar_fgcolor = "#DADADA";
set header_footer_bgcolor = "#f4a3e9";
set header_footer_fgcolor = "#2f0b31";
set date_fgcolor = "#2f0b31";
set subject_fgcolor = "#2f0b31";
set link_color = "#dc039d";
set link_hover_color = "#7e025a";
set comments_link_color = "#7e025a";
set comments_link_hover = "#f4a3e9";
set sidebar_link_color = "#dc039d";
set sidebar_link_hover = "#FFCCCC";
set header_footer_link_color = "#dc039d";
set header_footer_link_hover = "#7e025a";
#NEWLAYER: flexiblesquares/grays
layerinfo "redist_uniq" = "flexiblesquares/grays";
layerinfo "type" = "theme";
layerinfo "name" = "Grays";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#111111";
set page_fgcolor = "#ffffff";
set content_bgcolor = "#333333";
set entry_bgcolor = "#666666";
set entry_fgcolor = "#ffffff";
set border_color = "#ffffff";
set entrytitle_bgcolor = "#000000";
set outer_table_bgcolor = "#222222";
set sidebar_header_bgcolor = "#000000";
set sidebar_fgcolor = "#ffffff";
set header_footer_bgcolor = "#666666";
set header_footer_fgcolor = "#ffffff";
set date_fgcolor = "#ffffff";
set subject_fgcolor = "#ffffff";
set link_color = "#ffcc33";
set link_hover_color = "#ff9933";
set comments_link_color = "#ffcc33";
set comments_link_hover = "#ff9933";
set sidebar_link_color = "#ffcc33";
set sidebar_link_hover = "#ff9933";
set header_footer_link_color = "#ffcc33";
set header_footer_link_hover = "#ff9933";
#NEWLAYER: flexiblesquares/lemongrapetang
layerinfo "redist_uniq" = "flexiblesquares/lemongrapetang";
layerinfo "type" = "theme";
layerinfo "name" = "Lemon Grapefruit Tangerine";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#ffff66";
set page_fgcolor = "#993333";
set content_bgcolor = "#cc3333";
set entry_bgcolor = "#ffffcc";
set entry_fgcolor = "#000000";
set border_color = "#ff9999";
set entrytitle_bgcolor = "#ff9933";
set outer_table_bgcolor = "#ffcc33";
set sidebar_header_bgcolor = "#ff9933";
set sidebar_fgcolor = "#000000";
set header_footer_bgcolor = "#ffffcc";
set header_footer_fgcolor = "#993333";
set date_fgcolor = "#000000";
set subject_fgcolor = "#000000";
set link_color = "#ff9999";
set link_hover_color = "#993333";
set comments_link_color = "#993333";
set comments_link_hover = "#000000";
set sidebar_link_color = "#ff9999";
set sidebar_link_hover = "#000000";
set header_footer_link_color = "#ff9999";
set header_footer_link_hover = "#993333";
#NEWLAYER: flexiblesquares/tanteal
layerinfo "redist_uniq" = "flexiblesquares/tanteal";
layerinfo "type" = "theme";
layerinfo "name" = "Tan and Teal";
layerinfo author_name = "sub_divided/cyrnelle";
set page_bgcolor = "#003333";
set page_fgcolor = "#000000";
set content_bgcolor = "#cc9966";
set entry_bgcolor = "#ffffcc";
set entry_fgcolor = "#000000";
set border_color = "#ffffff";
set entrytitle_bgcolor = "#996633";
set outer_table_bgcolor = "#ffcc99";
set sidebar_header_bgcolor = "#996633";
set sidebar_fgcolor = "#000000";
set header_footer_bgcolor = "#ffffcc";
set header_footer_fgcolor = "#000000";
set date_fgcolor = "#000000";
set subject_fgcolor = "#000000";
set link_color = "#993333";
set link_hover_color = "#603913";
set comments_link_color = "#ffffcc";
set comments_link_hover = "#000000";
set sidebar_link_color = "#993333";
set sidebar_link_hover = "#000000";
set header_footer_link_color = "#993333";
set header_footer_link_hover = "#000000";
#NEWLAYER: flexiblesquares/gentledawn
layerinfo "redist_uniq" = "flexiblesquares/gentledawn";
layerinfo "type" = "theme";
layerinfo "name" = "Gentle Dawn";
layerinfo author_name = "Yati Mansor";
set page_bgcolor = "#747D86";
set page_fgcolor = "#605734";
set content_bgcolor = "#FCCA7D";
set entry_bgcolor = "#FFD3B9";
set entry_fgcolor = "#605734";
set border_color = "#908350";
set entrytitle_bgcolor = "#BAA969";
set outer_table_bgcolor = "#DABD63";
set sidebar_header_bgcolor = "#BAA969";
set sidebar_fgcolor = "#605734";
set header_footer_bgcolor = "#FFD3B9";
set header_footer_fgcolor = "#605734";
set link_color = "#7E858D";
set link_hover_color = "#C68DA5";
set comments_link_color = "#FFFFFF";
set comments_link_hover = "#605734";
set sidebar_link_color = "#7E858D";
set sidebar_link_hover = "#C68DA5";
set header_footer_link_color = "#7E858D";
set header_footer_link_hover = "#C68DA5";
#NEWLAYER: flexiblesquares/icyblue
layerinfo "redist_uniq" = "flexiblesquares/icyblue";
layerinfo "type" = "theme";
layerinfo "name" = "Icy Blue";
layerinfo author_name = "Yati Mansor";
set page_bgcolor = "#C6C2F8";
set page_fgcolor = "#4F4D65";
set content_bgcolor = "#D3DCFC";
set entry_bgcolor = "#DDEFFF";
set entry_fgcolor = "#4F4D65";
set border_color = "#FFFFFF";
set entrytitle_bgcolor = "#FFFFFF";
set date_fgcolor = "#4F4D65";
set subject_fgcolor = "#4F4D65";
set outer_table_bgcolor = "#CED2FA";
set sidebar_header_bgcolor = "#FFFFFF";
set sidebar_fgcolor = "#4F4D65";
set header_footer_bgcolor = "#FFFFFF";
set header_footer_fgcolor = "#4F4D65";
set link_color = "#5DABAA";
set link_hover_color = "#779595";
set comments_link_color = "#5DABAA";
set comments_link_hover = "#779595";
set sidebar_link_color = "#5DABAA";
set sidebar_link_hover = "#779595";
set header_footer_link_color = "#5DABAA";
set header_footer_link_hover = "#779595";
#NEWLAYER: flexiblesquares/chocolatemilkshake
layerinfo "redist_uniq" = "flexiblesquares/chocolatemilkshake";
layerinfo "type" = "theme";
layerinfo "name" = "Chocolate Milkshake";
layerinfo author_name = "Yati Mansor";
set page_bgcolor = "#746E4A";
set page_fgcolor = "#000000";
set content_bgcolor = "#BEA16D";
set entry_bgcolor = "#ECD6B2";
set entry_fgcolor = "#000000";
set border_color = "#FFFFFF";
set entrytitle_bgcolor = "#867A52";
set date_fgcolor = "#000000";
set subject_fgcolor = "#000000";
set outer_table_bgcolor = "#A18D5F";
set sidebar_header_bgcolor = "#867A52";
set sidebar_fgcolor = "#000000";
set header_footer_bgcolor = "#ECD6B2";
set header_footer_fgcolor = "#000000";
set link_color = "#867A52";
set link_hover_color = "#790000";
set comments_link_color = "#ECD6B2";
set comments_link_hover = "#790000";
set sidebar_link_color = "#790000";
set sidebar_link_hover = "#ECD6B2";
set header_footer_link_color = "#867A52";
set header_footer_link_hover = "#790000";
#NEWLAYER: flexiblesquares/eclipse
layerinfo "redist_uniq" = "flexiblesquares/eclipse";
layerinfo "type" = "theme";
layerinfo "name" = "Eclipse";
layerinfo author_name = "Yati Mansor";
set page_bgcolor = "#000000";
set page_fgcolor = "#000000";
set content_bgcolor = "#EEA802";
set entry_bgcolor = "#FBE274";
set entry_fgcolor = "#000000";
set border_color = "#000000";
set entrytitle_bgcolor = "#CA5D00";
set date_fgcolor = "#000000";
set subject_fgcolor = "#000000";
set outer_table_bgcolor = "#921800";
set sidebar_header_bgcolor = "#CA5D00";
set sidebar_fgcolor = "#000000";
set header_footer_bgcolor = "#EEA802";
set header_footer_fgcolor = "#000000";
set link_color = "#790000";
set link_hover_color = "#000000";
set comments_link_color = "#ECD6B2";
set comments_link_hover = "#790000";
set sidebar_link_color = "#790000";
set sidebar_link_hover = "#ffffff";
set header_footer_link_color = "#790000";
set header_footer_link_hover = "#ffffff";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,269 @@
#NEWLAYER: nebula/ohblue
layerinfo "type" = "theme";
layerinfo "name" = "Oh blue";
layerinfo "redist_uniq" = "nebula/ohblue";
set col_entry_bg = "#44366d";
set col_stronger_fg = "#1b0c66";
set col_cmtbarscrn_bg = "#ffffff";
set col_cmtbarone_bg = "#9f9cfd";
set col_neutral_fg = "#2f2e55";
set col_cmtbartwo_bg = "#adb3e5";
set col_sidebar_link = "#2f2e55";
set col_weaker_bg = "#d9dcff";
set col_strong_fg = "#2f2e55";
set col_neutral_bg = "#ab5c90";
set col_cmtbarscrn_fg = "#1b0c66";
set col_stronger_bg = "#b8bef8";
set col_cmtbartwo_fg = "#000000";
set col_weak_bg = "#404380";
set col_cmtbarone_fg = "#000000";
set col_weaker_fg = "#04025a";
set col_entry_link = "#000000";
set col_entry_vlink = "#000000";
set col_strong_bg = "#7e7cb1";
set col_weak_fg = "#9ba2e8";
set col_sidebar_vlink = "#1f1d46";
#NEWLAYER: nebula/ohgreen
layerinfo "type" = "theme";
layerinfo "name" = "Oh green";
layerinfo "redist_uniq" = "nebula/ohgreen";
set col_entry_bg = "#4ca65b";
set col_stronger_fg = "#12661a";
set col_cmtbarscrn_bg = "#ffffff";
set col_cmtbarone_bg = "#bffdbe";
set col_neutral_fg = "#12661a";
set col_cmtbartwo_bg = "#72e580";
set col_sidebar_link = "#12661a";
set col_weaker_bg = "#0d6315";
set col_strong_fg = "#12661a";
set col_neutral_bg = "#000000";
set col_cmtbarscrn_fg = "#000000";
set col_stronger_bg = "#9df89f";
set col_cmtbartwo_fg = "#000000";
set col_weak_bg = "#08390b";
set col_cmtbarone_fg = "#000000";
set col_weaker_fg = "#0e2a05";
set col_entry_link = "#000000";
set col_entry_vlink = "#000000";
set col_strong_bg = "#4f9f58";
set col_weak_fg = "#08390b";
set col_sidebar_vlink = "#12661a";
#NEWLAYER: nebula/ohpurple
layerinfo "type" = "theme";
layerinfo "name" = "Oh purple";
layerinfo "redist_uniq" = "nebula/ohpurple";
set col_cmtbartwo_bg = "#D89EE5";
set col_cmtbarscrn_fg = "#000000";
set col_entry_bg = "#eebfed";
set col_weaker_bg = "#eebfed";
set col_stronger_bg = "#efd5f8";
set col_cmtbartwo_fg = "#000000";
set col_sidebar_link = "#592b7d";
set col_strong_bg = "#9c6e9f";
set col_cmtbarone_bg = "#EECCFD";
set col_stronger_fg = "#c459d1";
set col_weaker_fg = "#0e2a05";
set col_entry_link = "#000000";
set col_entry_vlink = "#000000";
set col_sidebar_vlink = "#2d1e41";
set col_strong_fg = "#614a62";
set col_weak_bg = "#614a62";
set col_neutral_bg = "#000000";
set col_cmtbarone_fg = "#000000";
set col_cmtbarscrn_bg = "#ffffff";
set col_weak_fg = "#a1319a";
set col_neutral_fg = "#614a62";
#NEWLAYER: nebula/ohblack
layerinfo "type" = "theme";
layerinfo "name" = "Oh black";
layerinfo "redist_uniq" = "nebula/ohblack";
set col_cmtbartwo_bg = "#ffffff";
set col_cmtbarscrn_fg = "#000000";
set col_entry_bg = "#ffffff";
set col_weaker_bg = "#ffffff";
set col_stronger_bg = "#ffffff";
set col_cmtbartwo_fg = "#000000";
set col_sidebar_link = "#606060";
set col_strong_bg = "#ffffff";
set col_cmtbarone_bg = "#ffffff";
set col_stronger_fg = "#000000";
set col_weaker_fg = "#000000";
set col_entry_link = "#606060";
set col_entry_vlink = "#606060";
set col_sidebar_vlink = "#606060";
set col_strong_fg = "#000000";
set col_weak_bg = "#ffffff";
set col_neutral_bg = "#ffffff";
set col_cmtbarone_fg = "#000000";
set col_cmtbarscrn_bg = "#ffffff";
set col_weak_fg = "#000000";
set col_neutral_fg = "#000000";
#NEWLAYER: nebula/ohcontrast
layerinfo "type" = "theme";
layerinfo "name" = "Oh contrast";
layerinfo "redist_uniq" = "nebula/ohcontrast";
set col_cmtbartwo_bg = "#000000";
set col_cmtbarscrn_fg = "#ffffff";
set col_entry_bg = "#000000";
set col_weaker_bg = "#000000";
set col_stronger_bg = "#000000";
set col_cmtbartwo_fg = "#ffffff";
set col_sidebar_link = "#c0c0c0";
set col_strong_bg = "#000000";
set col_cmtbarone_bg = "#000000";
set col_stronger_fg = "#ffffff";
set col_weaker_fg = "#000000";
set col_entry_link = "#c0c0c0";
set col_entry_vlink = "#c0c0c0";
set col_sidebar_vlink = "#c0c0c0";
set col_strong_fg = "#ffffff";
set col_weak_bg = "#000000";
set col_neutral_bg = "#000000";
set col_cmtbarone_fg = "#ffffff";
set col_cmtbarscrn_bg = "#000000";
set col_weak_fg = "#ffffff";
set col_neutral_fg = "#ffffff";
#NEWLAYER: nebula/ohbedtime
layerinfo "type" = "theme";
layerinfo "name" = "Oh bed time";
layerinfo "redist_uniq" = "nebula/ohbedtime";
set col_cmtbartwo_bg = "#5c60b6";
set col_cmtbarscrn_fg = "#ffffff";
set col_entry_bg = "#ffffff";
set col_weaker_bg = "#5e5b7d";
set col_stronger_bg = "#8381da";
set col_cmtbartwo_fg = "#ffffff";
set col_sidebar_link = "#000000";
set col_strong_bg = "#8381da";
set col_cmtbarone_bg = "#605f9a";
set col_stronger_fg = "#ffffff";
set col_weaker_fg = "#000000";
set col_entry_link = "#000000";
set col_entry_vlink = "#000000";
set col_sidebar_vlink = "#000000";
set col_strong_fg = "#ffffff";
set col_weak_bg = "#ffffff";
set col_neutral_bg = "#000000";
set col_cmtbarone_fg = "#ffffff";
set col_cmtbarscrn_bg = "#879afa";
set col_weak_fg = "#3c3c60";
set col_neutral_fg = "#080255";
#NEWLAYER: nebula/ohgreenywhite
layerinfo "type" = "theme";
layerinfo "name" = "Oh greeny white";
layerinfo "redist_uniq" = "nebula/ohgreenywhite";
set col_entry_bg = "#ffffff";
set col_stronger_fg = "#ffffff";
set col_cmtbarscrn_bg = "#93f390";
set col_cmtbarone_bg = "#7fe278";
set col_neutral_fg = "#550301";
set col_cmtbartwo_bg = "#4a8f41";
set col_sidebar_link = "#000000";
set col_weaker_bg = "#546847";
set col_strong_fg = "#ffffff";
set col_neutral_bg = "#000000";
set col_cmtbarscrn_fg = "#ffffff";
set col_stronger_bg = "#69ac59";
set col_cmtbartwo_fg = "#ffffff";
set col_weak_bg = "#ffffff";
set col_cmtbarone_fg = "#ffffff";
set col_weaker_fg = "#000000";
set col_entry_link = "#000000";
set col_entry_vlink = "#000000";
set col_strong_bg = "#69ac59";
set col_weak_fg = "#366030";
set col_sidebar_vlink = "#000000";
#NEWLAYER: nebula/ohchocchip
layerinfo "type" = "theme";
layerinfo "name" = "Oh choc chip";
layerinfo "redist_uniq" = "nebula/ohchocchip";
set col_cmtbartwo_bg = "#4A8F41";
set col_cmtbarscrn_fg = "#ffffff";
set col_entry_bg = "#977e3b";
set col_weaker_bg = "#546847";
set col_stronger_bg = "#69ac59";
set col_cmtbartwo_fg = "#ffffff";
set col_sidebar_link = "#000000";
set col_strong_bg = "#69ac59";
set col_cmtbarone_bg = "#7FE278";
set col_stronger_fg = "#5b4b26";
set col_weaker_fg = "#000000";
set col_entry_link = "#000000";
set col_entry_vlink = "#000000";
set col_sidebar_vlink = "#000000";
set col_strong_fg = "#5b4b26";
set col_weak_bg = "#5b4b26";
set col_neutral_bg = "#000000";
set col_cmtbarone_fg = "#ffffff";
set col_cmtbarscrn_bg = "#93F390";
set col_weak_fg = "#ffffff";
set col_neutral_fg = "#550301";
#NEWLAYER: nebula/ohcoffee
layerinfo "type" = "theme";
layerinfo "name" = "Oh coffee";
layerinfo "redist_uniq" = "nebula/ohcoffee";
set col_cmtbartwo_bg = "#AFB13E";
set col_cmtbarscrn_fg = "#ffffff";
set col_entry_bg = "#977e3b";
set col_weaker_bg = "#effe8f";
set col_stronger_bg = "#fbe46a";
set col_cmtbartwo_fg = "#ffffff";
set col_sidebar_link = "#000000";
set col_strong_bg = "#fbe46a";
set col_cmtbarone_bg = "#E2C956";
set col_stronger_fg = "#5b4b26";
set col_weaker_fg = "#000000";
set col_entry_link = "#000000";
set col_entry_vlink = "#000000";
set col_sidebar_vlink = "#000000";
set col_strong_fg = "#5b4b26";
set col_weak_bg = "#5b4b26";
set col_neutral_bg = "#000000";
set col_cmtbarone_fg = "#ffffff";
set col_cmtbarscrn_bg = "#FCDA3E";
set col_weak_fg = "#ffffff";
set col_neutral_fg = "#550301";
#NEWLAYER: nebula/ohred
layerinfo "type" = "theme";
layerinfo "name" = "Oh red";
layerinfo "redist_uniq" = "nebula/ohred";
set col_cmtbartwo_bg = "#983035";
set col_cmtbarscrn_fg = "#ffffff";
set col_entry_bg = "#d90d12";
set col_weaker_bg = "#fe0410";
set col_stronger_bg = "#dd6568";
set col_cmtbartwo_fg = "#ffffff";
set col_sidebar_link = "#000000";
set col_strong_bg = "#dd6568";
set col_cmtbarone_bg = "#d25c64";
set col_stronger_fg = "#ffffff";
set col_weaker_fg = "#000000";
set col_entry_link = "#000000";
set col_entry_vlink = "#000000";
set col_sidebar_vlink = "#000000";
set col_strong_fg = "#ffffff";
set col_weak_bg = "#bc0811";
set col_neutral_bg = "#000000";
set col_cmtbarone_fg = "#ffffff";
set col_cmtbarscrn_bg = "#D38086";
set col_weak_fg = "#ffffff";
set col_neutral_fg = "#550301";

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,105 @@
#NEWLAYER: opal/irishluck
layerinfo "type" = "theme";
layerinfo "name" = "The Luck of the Irish";
layerinfo redist_uniq = "opal/irishluck";
layerinfo "source_viewable" = 1;
set color_med = "#8BA485";
set color_fg_font = "#01331E";
set color_fg = "#A1CB78";
set color_med_font = "#E5FCDE";
set color_bg = "#0B4107";
#NEWLAYER: opal/undersea
layerinfo "type" = "theme";
layerinfo redist_uniq = "opal/undersea";
layerinfo "source_viewable" = 1;
layerinfo "name" = "Under the Sea";
set color_med = "#98A3A4";
set color_fg = "#93CBCA";
set color_med_font = "#526160";
set color_bg = "#124D51";
#NEWLAYER: opal/forest
layerinfo "type" = "theme";
layerinfo redist_uniq = "opal/forest";
layerinfo "source_viewable" = 1;
layerinfo "name" = "Natural Forest";
set color_link = "#4C6502";
set color_med = "#90a47a";
set color_bg_font = "#ffeabf";
set color_fg = "#cacba9";
set color_med_font = "#eaf2cc";
set color_bg = "#514541";
set color_visited = "#846D06";
#NEWLAYER: opal/greybeard
layerinfo "type" = "theme";
layerinfo redist_uniq = "opal/greybeard";
layerinfo "source_viewable" = 1;
layerinfo "name" = "Greybeard";
set color_link = "#05445C";
set color_med = "#6a6a6a";
set color_fg_font = "#dddddd";
set color_bg = "#424242";
set color_visited = "#390856";
set color_fg = "#7c7c7c";
set color_med_font = "#E2E2E2";
#NEWLAYER: opal/desert
layerinfo "type" = "theme";
layerinfo redist_uniq = "opal/desert";
layerinfo "source_viewable" = 1;
layerinfo "name" = "Spring Desert";
set color_link = "#4c6502";
set color_med = "#bc7f48";
set color_fg_font = "#794234";
set color_fg = "#cea36f";
set color_med_font = "#F2E6C8";
set color_bg = "#795021";
#NEWLAYER: opal/carnvial
layerinfo "type" = "theme";
layerinfo redist_uniq = "opal/carnvial";
layerinfo "source_viewable" = 1;
layerinfo "name" = "Carnvial";
set color_med = "#9681b7";
set color_fg = "#E9973F";
set color_bg = "#2d4b9b";
#NEWLAYER: opal/snowcone
layerinfo "type" = "theme";
layerinfo redist_uniq = "opal/snowcone";
layerinfo "source_viewable" = 1;
layerinfo "name" = "Snow Cone";
set color_med = "#7c9bc1";
set color_fg_font = "#4F6C9C";
set color_fg = "#bfe2f8";
set color_bg = "#8d54a0";
#NEWLAYER: opal/adobeclay
layerinfo "type" = "theme";
layerinfo redist_uniq = "opal/adobeclay";
layerinfo "source_viewable" = 1;
layerinfo "name" = "Adobe Clay";
set color_link = "#FF9422";
set color_med = "#a4562b";
set color_fg_font = "#33100b";
set color_fg = "#c1643b";
set color_med_font = "#fce7d5";
set color_bg = "#7b4832";
set color_visited = "#FFC437";
#NEWLAYER: opal/gentle
layerinfo "type" = "theme";
layerinfo redist_uniq = "opal/gentle";
layerinfo "source_viewable" = 1;
layerinfo "name" = "Gentle";
set color_bg_font = "#616568";
set color_fg = "#ebdbd0";
set color_visited = "#A97F63";
set color_link = "#bd9a82";
set color_med = "#b3c5c5";
set color_bg = "#a2b2b1";
set color_med_font = "#878894";
set color_fg_font = "#757575";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,612 @@
#NEWLAYER: smoothsailing/starry
layerinfo "redist_uniq" = "smoothsailing/starry";
layerinfo "type" = "theme";
layerinfo "name" = "Starry Night";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#000000";
set color_header_title_text = "#FFFFB9";
set color_header_subtitle_text = "#dddddd";
set color_header_menubar_background = "#000044";
set color_header_menubar_text = "#FFFFB9";
set color_header_menubar_background_hover = "#000000";
set color_header_menubar_text_hover = "#dddddd";
set color_header_borders = "#89a2be";
set color_body_links = "#ffffb9";
set color_body_links_visited = "#dddddd";
set color_body_titlebar_background = "#aaaaaa";
set color_body_titlebar_text = "#000044";
set color_body_footer_background = "#000000";
set color_body_footer_text = "#ffffb9";
set color_body_background = "#000044";
set color_body_text = "#FFFFB9";
set color_body_entrytitle_background = "#000000";
set color_body_entrytitle_background_alternate = "#aaaaaa";
set color_body_entrytitle_border = "#89A2BE";
set color_body_entrytitle_text = "#89a2be";
set color_body_entrytitle_links = "#89a2be";
set color_body_entry_background = "#000044";
set color_body_entry_userinfo_background = "#000044";
set color_body_entry_text = "#FFFFB9";
set color_month_borders = "#89A2BE";
set color_month_title_background = "#aaaaaa";
set color_month_title_text = "#000000";
set color_month_dates = "#aaaaaa";
set color_month_postcount = "#FFFFB9";
#NEWLAYER: smoothsailing/pink
layerinfo "redist_uniq" = "smoothsailing/pink";
layerinfo "type" = "theme";
layerinfo "name" = "Pretty in Pink";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#CC4E5C";
set color_header_title_text = "#FFEEFF";
set color_header_subtitle_text = "#FFB6C1";
set color_header_menubar_background = "#e6828f";
set color_header_menubar_text = "#FFEEFF";
set color_header_menubar_background_hover = "#CC4E5C";
set color_header_menubar_text_hover = "#FFEEFF";
set color_header_borders = "#FFEEFF";
set color_body_links = "#CC4E5C";
set color_body_links_visited = "#CC4E5C";
set color_body_titlebar_background = "#FFB6C1";
set color_body_titlebar_text = "#CC4E5C";
set color_body_footer_background = "#FFB6C1";
set color_body_footer_text = "#CC4E5C";
set color_body_background = "#FFEEFF";
set color_body_text = "#CC4E5C";
set color_body_entrytitle_background = "#FFB6C1";
set color_body_entrytitle_background_alternate = "#e6828f";
set color_body_entrytitle_border = "#CC4E5C";
set color_body_entrytitle_text = "#CC4E5C";
set color_body_entrytitle_links = "#CC4E5C";
set color_body_entry_text = "#CC4E5C";
set color_body_entry_background = "#FFEEFF";
set color_body_entry_userinfo_background = "#FFEEFF";
set color_month_borders = "#CC4E5C";
set color_month_title_background = "#FFB6C1";
set color_month_title_text = "#CC4E5C";
set color_month_dates = "#CC4E5C";
set color_month_postcount = "#e6828f";
#NEWLAYER: smoothsailing/easter
layerinfo "redist_uniq" = "smoothsailing/easter";
layerinfo "type" = "theme";
layerinfo "name" = "Easter Basket";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#00B789";
set color_header_title_text = "#FFFF82";
set color_header_subtitle_text = "#005100";
set color_header_menubar_background = "#FFFF82";
set color_header_menubar_text = "#005100";
set color_header_menubar_background_hover = "#C7A0CB";
set color_header_menubar_text_hover = "#005100";
set color_header_borders = "#009300";
set color_body_links = "#005100";
set color_body_links_visited = "#005100";
set color_body_titlebar_background = "#C7A0CB";
set color_body_titlebar_text = "#005100";
set color_body_footer_background = "#C7A0CB";
set color_body_footer_text = "#005100";
set color_body_background = "#DDDFF0";
set color_body_text = "#005100";
set color_body_entrytitle_background = "#FFFF82";
set color_body_entrytitle_background_alternate = "#C7A0CB";
set color_body_entrytitle_border = "#009300";
set color_body_entrytitle_text = "#005100";
set color_body_entrytitle_links = "#005100";
set color_body_entry_text = "#005100";
set color_body_entry_background = "#DDDFF0";
set color_body_entry_userinfo_background = "#DDDFF0";
set color_month_borders = "#00B789";
set color_month_title_background = "#FFFF82";
set color_month_title_text = "#005100";
set color_month_dates = "#C7A0CB";
set color_month_postcount = "#005100";
#NEWLAYER: smoothsailing/mocha
layerinfo "redist_uniq" = "smoothsailing/mocha";
layerinfo "type" = "theme";
layerinfo "name" = "Mochachino";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#666666";
set color_header_title_text = "#e6e6e6";
set color_header_subtitle_text = "#B1A68B";
set color_header_menubar_background = "#635a48";
set color_header_menubar_text = "#b1a68b";
set color_header_menubar_background_hover = "#b1a68b";
set color_header_menubar_text_hover = "#666666";
set color_header_borders = "#e6e6e6";
set color_body_links = "#464640";
set color_body_links_visited = "#000000";
set color_body_titlebar_background = "#B1A68B";
set color_body_titlebar_text = "#e6e6e6";
set color_body_footer_background = "#b1a68b";
set color_body_footer_text = "#e6e6e6";
set color_body_background = "#e6e6e6";
set color_body_entrytitle_background = "#b1a68b";
set color_body_entrytitle_background_alternate = "#796D51";
set color_body_entrytitle_border = "#666666";
set color_body_entrytitle_text = "#e6e6e6";
set color_body_entrytitle_links = "#e6e6e6";
set color_body_entry_text = "#464640";
set color_body_entry_background = "#e6e6e6";
set color_body_entry_userinfo_background = "#e6e6e6";
set color_month_borders = "#666666";
set color_month_title_background = "#e6e6e6";
set color_month_title_text = "#464640";
set color_month_dates = "#464640";
set color_month_postcount = "#796D51";
#NEWLAYER: smoothsailing/yellow
layerinfo "redist_uniq" = "smoothsailing/yellow";
layerinfo "type" = "theme";
layerinfo "name" = "Insane Yellow";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#202020";
set color_header_title_text = "#FFFF04";
set color_header_subtitle_text = "#dddddd";
set color_header_menubar_background = "#000000";
set color_header_menubar_text = "#FFFFB9";
set color_header_menubar_background_hover = "#202020";
set color_header_menubar_text_hover = "#FFFFB9";
set color_header_borders = "#FFFF04";
set color_body_links = "#aaaaaa";
set color_body_links_visited = "#dddddd";
set color_body_titlebar_background = "#aaaaaa";
set color_body_titlebar_text = "#000000";
set color_body_footer_background = "#202020";
set color_body_footer_text = "#ffffb9";
set color_body_background = "#000000";
set color_body_text = "#FFFFB9";
set color_body_entrytitle_background = "#202020";
set color_body_entrytitle_background_alternate = "#aaaaaa";
set color_body_entrytitle_border = "#FFFF04";
set color_body_entrytitle_text = "#FFFF04";
set color_body_entrytitle_links = "#FFFF04";
set color_body_entry_background = "#000000";
set color_body_entry_userinfo_background = "#000000";
set color_body_entry_text = "#aaaaaa";
set color_month_borders = "#FFFFB9";
set color_month_title_background = "#aaaaaa";
set color_month_title_text = "#000000";
set color_month_dates = "#aaaaaa";
set color_month_postcount = "#FFFFB9";
#NEWLAYER: smoothsailing/forest
layerinfo "redist_uniq" = "smoothsailing/forest";
layerinfo "type" = "theme";
layerinfo "name" = "In the Forest";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#635a48";
set color_header_title_text = "#e7dfcf";
set color_header_subtitle_text = "#607c6d";
set color_header_menubar_background = "#899ba3";
set color_header_menubar_text = "#464640";
set color_header_menubar_background_hover = "#e7dfcf";
set color_header_menubar_text_hover = "#464640";
set color_header_borders = "#464640";
set color_body_links = "#e7dfcf";
set color_body_links_visited = "#e7dfcf";
set color_body_titlebar_background = "#e7dfcf";
set color_body_titlebar_text = "#464640";
set color_body_footer_background = "#e7dfcf";
set color_body_footer_text = "#464640";
set color_body_background = "#607c6d";
set color_body_text = "#e7dfcf";
set color_body_entrytitle_background = "#635a48";
set color_body_entrytitle_background_alternate = "#464640";
set color_body_entrytitle_border = "#e7dfcf";
set color_body_entrytitle_text = "#e7dfcf";
set color_body_entrytitle_links = "#e7dfcf";
set color_body_entry_userinfo_background = "#626b5b";
set color_body_entry_background = "#626b5b";
set color_body_entry_text = "#e7dfcf";
set color_month_borders = "#464640";
set color_month_title_background = "#e7dfcf";
set color_month_title_text = "#464640";
set color_month_dates = "#899ba3";
set color_month_postcount = "#e7dfcf";
#NEWLAYER: smoothsailing/red
layerinfo "redist_uniq" = "smoothsailing/red";
layerinfo "type" = "theme";
layerinfo "name" = "Seeing Red";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#bb0000";
set color_header_title_text = "#ffffff";
set color_header_subtitle_text = "#ffffff";
set color_header_menubar_background = "#880000";
set color_header_menubar_text = "#ffffff";
set color_header_menubar_background_hover = "#ffffff";
set color_header_menubar_text_hover = "#bb0000";
set color_header_borders = "#ffffff";
set color_body_links = "#880000";
set color_body_links_visited = "#000000";
set color_body_titlebar_background = "#bb0000";
set color_body_titlebar_text = "#ffffff";
set color_body_footer_background = "#bb0000";
set color_body_footer_text = "#ffffff";
set color_body_background = "#f6f6f6";
set color_body_text = "#880000";
set color_body_entrytitle_background = "#bb0000";
set color_body_entrytitle_background_alternate = "#880000";
set color_body_entrytitle_border = "#000000";
set color_body_entrytitle_text = "#ffffff";
set color_body_entrytitle_links = "#ffffff";
set color_body_entry_background = "#f6f6f6";
set color_body_entry_text = "#880000";
set color_month_borders = "#880000";
set color_month_title_background = "#bb0000";
set color_month_title_text = "#ffffff";
set color_month_dates = "#880000";
set color_month_postcount = "#bb0000";
#NEWLAYER: smoothsailing/army
layerinfo "redist_uniq" = "smoothsailing/army";
layerinfo "type" = "theme";
layerinfo "name" = "Army Attire";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#444444";
set color_header_title_text = "#cccc99";
set color_header_subtitle_text = "#e6e6e6";
set color_header_menubar_background = "#665d34";
set color_header_menubar_text = "#cccc99";
set color_header_menubar_background_hover = "#cccc99";
set color_header_menubar_text_hover = "#444444";
set color_header_borders = "#e6e6e6";
set color_body_links = "#444444";
set color_body_links_visited = "#000000";
set color_body_titlebar_background = "#cccc99";
set color_body_titlebar_text = "#665d34";
set color_body_footer_background = "#cccc99";
set color_body_footer_text = "#665d34";
set color_body_background = "#e6e6e6";
set color_body_entrytitle_background = "#cccc99";
set color_body_entrytitle_background_alternate = "#665d34";
set color_body_entrytitle_border = "#444444";
set color_body_entrytitle_text = "#665d34";
set color_body_entrytitle_links = "#665d34";
set color_body_entry_text = "#444444";
set color_body_entry_background = "#e6e6e6";
set color_body_entry_userinfo_background = "#e6e6e6";
set color_month_borders = "#444444";
set color_month_title_background = "#e6e6e6";
set color_month_title_text = "#444444";
set color_month_dates = "#444444";
set color_month_postcount = "#665d34";
#NEWLAYER: smoothsailing/midnight
layerinfo "redist_uniq" = "smoothsailing/midnight";
layerinfo "type" = "theme";
layerinfo "name" = "Midnight";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#000000";
set color_header_title_text = "#BEAA61";
set color_header_subtitle_text = "#76456B";
set color_header_menubar_background = "#76456B";
set color_header_menubar_text = "#dddddd";
set color_header_menubar_background_hover = "#000000";
set color_header_menubar_text_hover = "#dddddd";
set color_header_borders = "#dddddd";
set color_body_links = "#aaaaaa";
set color_body_links_visited = "#dddddd";
set color_body_titlebar_background = "#BEAA61";
set color_body_titlebar_text = "#000000";
set color_body_footer_background = "#76456B";
set color_body_footer_text = "#dddddd";
set color_body_background = "#000000";
set color_body_text = "#aaaaaa";
set color_body_entrytitle_background = "#76456B";
set color_body_entrytitle_background_alternate = "#888888";
set color_body_entrytitle_border = "#cccccc";
set color_body_entrytitle_text = "#dddddd";
set color_body_entrytitle_links = "#dddddd";
set color_body_entry_userinfo_background = "#000000";
set color_body_entry_background = "#000000";
set color_body_entry_text = "#aaaaaa";
set color_month_borders = "#dddddd";
set color_month_title_background = "#aaaaaa";
set color_month_title_text = "#000000";
set color_month_dates = "#aaaaaa";
set color_month_postcount = "#BEAA61";
#NEWLAYER: smoothsailing/violet
layerinfo "redist_uniq" = "smoothsailing/violet";
layerinfo "type" = "theme";
layerinfo "name" = "Very Violet";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#7c52ab";
set color_header_subtitle_text = "#dbcfe9";
set color_header_menubar_background = "#bfaad7";
set color_header_menubar_background_hover = "#7c52ab";
set color_body_links_visited = "#563976";
set color_body_titlebar_background = "#dbcfe9";
set color_body_footer_background = "#bfaad7";
set color_body_entrytitle_background = "#dbcfe9";
#NEWLAYER: smoothsailing/purple
layerinfo "redist_uniq" = "smoothsailing/purple";
layerinfo "type" = "theme";
layerinfo "name" = "Bruised Purple";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_body_entrytitle_border = "#d4ddee";
set color_body_footer_text = "#454b74";
set color_month_title_text = "#8792bd";
set color_header_borders = "#8792bd";
set color_body_links_visited = "#dddddd";
set color_header_menubar_text_hover = "#ffffff";
set color_body_titlebar_text = "#454b74";
set color_body_entrytitle_background_alternate = "#d4ddee";
set color_body_entrytitle_links = "#6b7da6";
set color_body_entry_background = "#454b74";
set color_header_title_background = "#454b74";
set color_body_entry_userinfo_background = "#454b74";
set color_month_dates = "#8792bd";
set color_body_links = "#ececec";
set color_body_background = "#6b7da6";
set color_month_postcount = "#8792bd";
set color_body_entry_text = "#8792bd";
set color_body_text = "#ffffff";
set color_body_entrytitle_background = "#d4ddee";
set color_header_menubar_text = "#ffffff";
set color_body_entrytitle_text = "#454b74";
#NEWLAYER: smoothsailing/parrot
layerinfo "redist_uniq" = "smoothsailing/parrot";
layerinfo "type" = "theme";
layerinfo "name" = "Parrot Feathers";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#009900";
set color_header_title_text = "#f8f8f8";
set color_header_subtitle_text = "#66ff66";
set color_header_menubar_background = "#33cc33";
set color_header_menubar_text = "#ffffff";
set color_header_menubar_background_hover = "#66ff66";
set color_header_menubar_text_hover = "#ffffff";
set color_header_borders = "#ffffff";
set color_body_links = "#006600";
set color_body_links_visited = "#000000";
set color_body_titlebar_background = "#66cc66";
set color_body_titlebar_text = "#ffffff";
set color_body_footer_background = "#66cc66";
set color_body_footer_text = "#ffffff";
set color_body_background = "#f8f8f8";
set color_body_text = "#000000";
set color_body_entrytitle_background = "#66cc66";
set color_body_entrytitle_background_alternate = "#66ff66";
set color_body_entrytitle_border = "#006600";
set color_body_entrytitle_text = "#000000";
set color_body_entrytitle_links = "#000000";
set color_body_entry_background = "#f8f8f8";
set color_body_entry_userinfo_background = "#f8f8f8";
set color_body_entry_text = "#000000";
set color_month_borders = "#006600";
set color_month_title_background = "#f8f8f8";
set color_month_title_text = "#000000";
set color_month_dates = "#000000";
set color_month_postcount = "#006600";
#NEWLAYER: smoothsailing/toxic
layerinfo "redist_uniq" = "smoothsailing/toxic";
layerinfo "type" = "theme";
layerinfo "name" = "Toxic Teal";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#7cb3b0";
set color_header_title_text = "#496a68";
set color_header_subtitle_text = "#c9eae8";
set color_header_menubar_background = "#aededb";
set color_header_menubar_text = "#496a68";
set color_header_menubar_background_hover = "#c9eae8";
set color_header_menubar_text_hover = "#496a68";
set color_header_borders = "#496a68";
set color_body_links = "#496a68";
set color_body_links_visited = "#000000";
set color_body_titlebar_background = "#c9eae8";
set color_body_titlebar_text = "#496a68";
set color_body_footer_background = "#c9eae8";
set color_body_footer_text = "#496a68";
set color_body_background = "#496a68";
set color_body_text = "#aededb";
set color_body_entrytitle_background = "#7cb3b0";
set color_body_entrytitle_background_alternate = "#6e9e9b";
set color_body_entrytitle_border = "#c9eae8";
set color_body_entrytitle_text = "#000000";
set color_body_entrytitle_links = "#000000";
set color_body_entry_background = "#aededb";
set color_body_entry_userinfo_background = "#aededb";
set color_body_entry_text = "#000000";
set color_month_borders = "#496a68";
set color_month_title_background = "#c9eae8";
set color_month_title_text = "#000000";
set color_month_dates = "#000000";
set color_month_postcount = "#496a68";
#NEWLAYER: smoothsailing/banana
layerinfo "redist_uniq" = "smoothsailing/banana";
layerinfo "type" = "theme";
layerinfo "name" = "Banana Tree";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#3D5D2E";
set color_header_title_text = "#F6F6BB";
set color_header_subtitle_text = "#E1D751";
set color_header_menubar_background = "#E1D751";
set color_header_menubar_text = "#3D5D2E";
set color_header_menubar_background_hover = "#9A722E";
set color_header_menubar_text_hover = "#F6F6BB";
set color_header_borders = "#F6F6BB";
set color_body_links = "#3D5D2E";
set color_body_links_visited = "#000000";
set color_body_titlebar_background = "#9A722E";
set color_body_titlebar_text = "#F6F6BB";
set color_body_footer_background = "#3D5D2E";
set color_body_footer_text = "#F6F6BB";
set color_body_background = "#F6F6BB";
set color_body_text = "#3D5D2E";
set color_body_entrytitle_background = "#3D5D2E";
set color_body_entrytitle_background_alternate = "#9A722E";
set color_body_entrytitle_border = "#9A722E";
set color_body_entrytitle_text = "#E1D751";
set color_body_entrytitle_links = "#E1D751";
set color_body_entry_background = "#F6F6BB";
set color_body_entry_userinfo_background = "#F6F6BB";
set color_body_entry_text = "#000000";
set color_month_borders = "#9A722E";
set color_month_title_background = "#F6F6BB";
set color_month_title_text = "#3D5D2E";
set color_month_dates = "#000000";
set color_month_postcount = "#3D5D2E";
#NEWLAYER: smoothsailing/fire
layerinfo "redist_uniq" = "smoothsailing/fire";
layerinfo "type" = "theme";
layerinfo "name" = "Under Fire";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#95261F";
set color_header_title_text = "#F5EBC2";
set color_header_subtitle_text = "#CA6036";
set color_header_menubar_background = "#CA6036";
set color_header_menubar_text = "#F5EBC2";
set color_header_menubar_background_hover = "#DEB450";
set color_header_menubar_text_hover = "#95261F";
set color_header_borders = "#F5EBC2";
set color_body_links = "#95261F";
set color_body_links_visited = "#000000";
set color_body_titlebar_background = "#DEB450";
set color_body_titlebar_text = "#95261F";
set color_body_footer_background = "#95261F";
set color_body_footer_text = "#F5EBC2";
set color_body_background = "#F5EBC2";
set color_body_text = "#000000";
set color_body_entrytitle_background = "#95261F";
set color_body_entrytitle_background_alternate = "#CA6036";
set color_body_entrytitle_border = "#DEB450";
set color_body_entrytitle_text = "#F5EBC2";
set color_body_entrytitle_links = "#F5EBC2";
set color_body_entry_background = "#F5EBC2";
set color_body_entry_userinfo_background = "#F5EBC2";
set color_body_entry_text = "#000000";
set color_month_borders = "#95261F";
set color_month_title_background = "#F5EBC2";
set color_month_title_text = "#95261F";
set color_month_dates = "#95261F";
set color_month_postcount = "#CA6036";
#NEWLAYER: smoothsailing/chocolate
layerinfo "redist_uniq" = "smoothsailing/chocolate";
layerinfo "type" = "theme";
layerinfo "name" = "Chocolate Caramel Creme";
layerinfo author_name = "Michael Raffoul";
layerinfo author_email = "masterslacker@livejournal.com";
set color_header_title_background = "#6D4506";
set color_header_title_text = "#E1D6C0";
set color_header_subtitle_text = "#CF9B48";
set color_header_menubar_background = "#A2701F";
set color_header_menubar_text = "#E1D6C0";
set color_header_menubar_background_hover = "#CF9B48";
set color_header_menubar_text_hover = "#E1D6C0";
set color_header_borders = "#E1D6C0";
set color_body_links = "#6D4506";
set color_body_links_visited = "#000000";
set color_body_titlebar_background = "#CF9B48";
set color_body_titlebar_text = "#E1D6C0";
set color_body_footer_background = "#6D4506";
set color_body_footer_text = "#E1D6C0";
set color_body_background = "#E1D6C0";
set color_body_text = "#000000";
set color_body_entrytitle_background = "#A2701F";
set color_body_entrytitle_background_alternate = "#CF9B48";
set color_body_entrytitle_border = "#6D4506";
set color_body_entrytitle_text = "#E1D6C0";
set color_body_entrytitle_links = "#E1D6C0";
set color_body_entry_background = "#E1D6C0";
set color_body_entry_userinfo_background = "#E1D6C0";
set color_body_entry_text = "#000000";
set color_month_borders = "#A2701F";
set color_month_title_background = "#E1D6C0";
set color_month_title_text = "#6D4506";
set color_month_dates = "#6D4506";
set color_month_postcount = "#A2701F";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,707 @@
#NEWLAYER: tranquilityii/plain
layerinfo "redist_uniq" = "tranquilityii/plain";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - Plain Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#fff";
set c_menu_border = "#fff";
set c_header_background = "#fff";
set c_header_border = "#fff";
set c_page_title = "#000";
set c_page_background = "#fff";
set c_page_text = "#333";
set c_page_link = "#036";
set c_page_link_visited = "#036";
set c_page_link_hover = "#069";
set c_page_link_active = "#069";
set c_menu_background = "#fff";
set c_menu_link = "#000";
set c_menu_link_visited = "#000";
set c_menu_link_hover = "#f00";
set c_menu_link_active = "#f00";
set c_menu_text_color = "#000";
set c_menu_header_color = "#000";
set c_menu_current = "#000";
set c_entry_background = "#fff";
set c_entry_link = "#000";
set c_entry_link_visited = "#000";
set c_entry_link_hover = "#000";
set c_entry_link_active = "#000";
set c_entry_text_color = "#000";
set c_entry_title_color = "#000";
set c_entry_border = "#999";
set c_meta_background = "#fff";
set c_meta_link = "#000";
set c_meta_link_visited = "#000";
set c_meta_link_hover = "#000";
set c_meta_link_active = "#000";
set c_meta_text_color = "#000";
set c_footer_background = "#fff";
set c_footer_link = "#000";
set c_footer_link_visited = "#000";
set c_footer_link_hover = "#000";
set c_footer_link_active = "#000";
set c_footer_text_color = "#000";
set c_comment_one_link = "#000";
set c_comment_one_link_visited = "#000";
set c_comment_one_link_hover = "#000";
set c_comment_one_link_active = "#000";
set c_comment_one_text_color = "#000";
set c_comment_one_title_color = "#000";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#999";
set c_comment_two_link = "#000";
set c_comment_two_link_visited = "#000";
set c_comment_two_link_hover = "#000";
set c_comment_two_link_active = "#000";
set c_comment_two_text_color = "#000";
set c_comment_two_title_color = "#000";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#999";
set c_comment_screened_link = "#000";
set c_comment_screened_link_visited = "#000";
set c_comment_screened_link_hover = "#000";
set c_comment_screened_link_active = "#000";
set c_comment_screened_text_color = "#000";
set c_comment_screened_title_color = "#000";
set c_comment_screened_background = "#ccc";
set c_comment_screened_border = "#999";
#NEWLAYER: tranquilityii/earth
layerinfo "redist_uniq" = "tranquilityii/earth";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - Earth Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#424B15";
set c_header_background = "#667320";
set c_header_border = "#667320";
set c_page_title = "#fff";
set c_page_background = "#667320";
set c_page_text = "#333";
set c_page_link = "#000";
set c_page_link_visited = "#666";
set c_page_link_hover = "#999";
set c_page_link_active = "#999";
set c_menu_border = "#424B15";
set c_menu_background = "#88992B";
set c_menu_link = "#fff";
set c_menu_link_visited = "#424B15";
set c_menu_link_hover = "#424B15";
set c_menu_link_active = "#f00";
set c_menu_text_color = "#fff";
set c_menu_header_color = "#fff";
set c_menu_current = "#fff";
set f_menu_header_size = "140%";
set c_entry_background = "#fff";
set c_entry_link = "#99602B";
set c_entry_link_visited = "#BF7836";
set c_entry_link_hover = "#734820";
set c_entry_link_active = "#734820";
set c_entry_text_color = "#333";
set c_entry_title_color = "#000";
set c_entry_border = "#BFBFBF";
set c_meta_background = "#fff";
set c_meta_link = "#99602B";
set c_meta_link_visited = "#BF7836";
set c_meta_link_hover = "#734820";
set c_meta_link_active = "#734820";
set c_meta_text_color = "#666";
set c_footer_background = "#667320";
set c_footer_link = "#000";
set c_footer_link_visited = "#000";
set c_footer_link_hover = "#fff";
set c_footer_link_active = "#000";
set c_footer_text_color = "#000";
set c_comment_one_link = "#99602B";
set c_comment_one_link_visited = "#BF7836";
set c_comment_one_link_hover = "#734820";
set c_comment_one_link_active = "#734820";
set c_comment_one_text_color = "#333";
set c_comment_one_title_color = "#000";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#999";
set c_comment_two_link = "#99602B";
set c_comment_two_link_visited = "#BF7836";
set c_comment_two_link_hover = "#734820";
set c_comment_two_link_active = "#734820";
set c_comment_two_text_color = "#333";
set c_comment_two_title_color = "#000";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#999";
set c_comment_screened_link = "#99602B";
set c_comment_screened_link_visited = "#BF7836";
set c_comment_screened_link_hover = "#734820";
set c_comment_screened_link_active = "#734820";
set c_comment_screened_text_color = "#333";
set c_comment_screened_title_color = "#000";
set c_comment_screened_background = "#ccc";
set c_comment_screened_border = "#999";
#NEWLAYER: tranquilityii/money
layerinfo "redist_uniq" = "tranquilityii/money";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - Money Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#68754D";
set c_header_background = "#ACC280";
set c_header_border = "#ACC280";
set c_page_title = "#3D3D3D";
set c_page_background = "#ACC280";
set c_page_text = "#3D3D3D";
set c_page_link = "#000";
set c_page_link_visited = "#666";
set c_page_link_hover = "#999";
set c_page_link_active = "#999";
set c_menu_border = "#68754D";
set c_menu_background = "#8A9C67";
set c_menu_link = "#000";
set c_menu_link_visited = "#000";
set c_menu_link_hover = "#EBF7D4";
set c_menu_link_active = "#ebf7d4";
set c_menu_text_color = "#000";
set c_menu_header_color = "#000";
set c_menu_current = "#EBF7D4";
set f_menu_header_size = "140%";
set c_entry_background = "#fff";
set c_entry_link = "#679C67";
set c_entry_link_visited = "#80C280";
set c_entry_link_hover = "#4D754D";
set c_entry_link_active = "#4D754D";
set c_entry_text_color = "#000";
set c_entry_title_color = "#000";
set c_entry_border = "#3D3D3D";
set c_meta_background = "#fff";
set c_meta_link = "#679C67";
set c_meta_link_visited = "#80C280";
set c_meta_link_hover = "#4D754D";
set c_meta_link_active = "#4D754D";
set c_meta_text_color = "#3D3D3D";
set c_footer_background = "#ACC280";
set c_footer_link = "#000";
set c_footer_link_visited = "#000";
set c_footer_link_hover = "#fff";
set c_footer_link_active = "#000";
set c_footer_text_color = "#000";
set c_comment_one_link = "#679C67";
set c_comment_one_link_visited = "#80C280";
set c_comment_one_link_hover = "#4D754D";
set c_comment_one_link_active = "#4D754D";
set c_comment_one_text_color = "#000";
set c_comment_one_title_color = "#000";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#999";
set c_comment_two_link = "#679C67";
set c_comment_two_link_visited = "#80C280";
set c_comment_two_link_hover = "#4D754D";
set c_comment_two_link_active = "#4D754D";
set c_comment_two_text_color = "#000";
set c_comment_two_title_color = "#000";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#999";
set c_comment_screened_link = "#679C67";
set c_comment_screened_link_visited = "#80C280";
set c_comment_screened_link_hover = "#4D754D";
set c_comment_screened_link_active = "#4D754D";
set c_comment_screened_text_color = "#000";
set c_comment_screened_title_color = "#000";
set c_comment_screened_background = "#ccc";
set c_comment_screened_border = "#999";
set f_page = "Tahoma, Verdana, Helvetica, sans-serif";
set f_page_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_page_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_link = "Verdana, Helvetica, sans-serif";
set f_menu = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_header = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_current = "Verdana, Helvetica, sans-serif";
set f_entry = "Tahoma, Verdana, Helvetica, sans-serif";
set f_entry_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_entry_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_meta = "Verdana, Helvetica, sans-serif";
set f_meta_link = "Verdana, Helvetica, sans-serif";
set f_footer = "Tahoma, Verdana, Helvetica, sans-serif";
set f_footer_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment_link = "Tahoma, Verdana, Helvetica, sans-serif";
#NEWLAYER: tranquilityii/winterice
layerinfo "redist_uniq" = "tranquilityii/winterice";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - WinterIce Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#000";
set c_header_background = "#694D73";
set c_header_border = "#694D73";
set c_page_title = "#000";
set c_page_background = "#694D73";
set c_page_text = "#3D3D3D";
set c_page_link = "#000";
set c_page_link_visited = "#666";
set c_page_link_hover = "#999";
set c_page_link_active = "#999";
set c_menu_border = "#694D73";
set c_menu_background = "#694D73";
set c_menu_link = "#fff";
set c_menu_link_visited = "#fff";
set c_menu_link_hover = "#C5A2D0";
set c_menu_link_active = "#C5A2D0";
set c_menu_text_color = "#fff";
set c_menu_header_color = "#fff";
set c_menu_current = "#C5A2D0";
set f_menu_header_size = "140%";
set c_entry_background = "#fff";
set c_entry_link = "#5E71AE";
set c_entry_link_visited = "#5E71AE";
set c_entry_link_hover = "#808FBF";
set c_entry_link_active = "#808FBF";
set c_entry_text_color = "#000";
set c_entry_title_color = "#000";
set c_entry_border = "#3D3D3D";
set c_meta_background = "#fff";
set c_meta_link = "#B080BF";
set c_meta_link_visited = "#C29FCB";
set c_meta_link_hover = "#854D93";
set c_meta_link_active = "#854D93";
set c_meta_text_color = "#3D3D3D";
set c_footer_background = "#694D73";
set c_footer_link = "#fff";
set c_footer_link_visited = "#fff";
set c_footer_link_hover = "#fff";
set c_footer_link_active = "#fff";
set c_footer_text_color = "#fff";
set c_comment_one_link = "#5E71AE";
set c_comment_one_link_visited = "#5E71AE";
set c_comment_one_link_hover = "#808FBF";
set c_comment_one_link_active = "#808FBF";
set c_comment_one_text_color = "#000";
set c_comment_one_title_color = "#000";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#999";
set c_comment_two_link = "#5E71AE";
set c_comment_two_link_visited = "#5E71AE";
set c_comment_two_link_hover = "#808FBF";
set c_comment_two_link_active = "#808FBF";
set c_comment_two_text_color = "#000";
set c_comment_two_title_color = "#000";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#999";
set c_comment_screened_link = "#5E71AE";
set c_comment_screened_link_visited = "#5E71AE";
set c_comment_screened_link_hover = "#808FBF";
set c_comment_screened_link_active = "#808FBF";
set c_comment_screened_text_color = "#000";
set c_comment_screened_title_color = "#000";
set c_comment_screened_background = "#ccc";
set c_comment_screened_border = "#999";
set f_page = "Tahoma, Verdana, Helvetica, sans-serif";
set f_page_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_page_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_link = "Verdana, Helvetica, sans-serif";
set f_menu = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_header = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_current = "Verdana, Helvetica, sans-serif";
set f_entry = "Tahoma, Verdana, Helvetica, sans-serif";
set f_entry_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_entry_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_meta = "Verdana, Helvetica, sans-serif";
set f_meta_link = "Verdana, Helvetica, sans-serif";
set f_footer = "Tahoma, Verdana, Helvetica, sans-serif";
set f_footer_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment_link = "Tahoma, Verdana, Helvetica, sans-serif";
#NEWLAYER: tranquilityii/fire
layerinfo "redist_uniq" = "tranquilityii/fire";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - Fire Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#A65000";
set c_header_background = "#A65000";
set c_header_border = "#A65000";
set c_page_title = "#000";
set c_page_background = "#A65000";
set c_page_text = "#3D3D3D";
set c_page_link = "#000";
set c_page_link_visited = "#666";
set c_page_link_hover = "#999";
set c_page_link_active = "#999";
set c_menu_border = "#A68700";
set c_menu_background = "#CCA700";
set c_menu_link = "#fff";
set c_menu_link_visited = "#FFD100";
set c_menu_link_hover = "#CC4100";
set c_menu_link_active = "#CC4100";
set c_menu_text_color = "#fff";
set c_menu_header_color = "#fff";
set c_menu_current = "#CC4100";
set f_menu_header_size = "140%";
set c_entry_background = "#fff";
set c_entry_link = "#CC6300";
set c_entry_link_visited = "#F27500";
set c_entry_link_hover = "#A65000";
set c_entry_link_active = "#A65000";
set c_entry_text_color = "#333";
set c_entry_title_color = "#333";
set c_entry_border = "#666";
set c_meta_background = "#fff";
set c_meta_link = "#CCA700";
set c_meta_link_visited = "#F2C600";
set c_meta_link_hover = "#A68700";
set c_meta_link_active = "#A68700";
set c_meta_text_color = "#3D3D3D";
set c_footer_background = "#A65000";
set c_footer_link = "#fff";
set c_footer_link_visited = "#fff";
set c_footer_link_hover = "#fff";
set c_footer_link_active = "#fff";
set c_footer_text_color = "#fff";
set c_comment_one_link = "#CC6300";
set c_comment_one_link_visited = "#F27500";
set c_comment_one_link_hover = "#A65000";
set c_comment_one_link_active = "#A65000";
set c_comment_one_text_color = "#333";
set c_comment_one_title_color = "#000";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#999";
set c_comment_two_link = "#CC6300";
set c_comment_two_link_visited = "#F27500";
set c_comment_two_link_hover = "#A65000";
set c_comment_two_link_active = "#A65000";
set c_comment_two_text_color = "#333";
set c_comment_two_title_color = "#000";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#999";
set c_comment_screened_link = "#CC6300";
set c_comment_screened_link_visited = "#F27500";
set c_comment_screened_link_hover = "#A65000";
set c_comment_screened_link_active = "#A65000";
set c_comment_screened_text_color = "#333";
set c_comment_screened_title_color = "#000";
set c_comment_screened_background = "#ccc";
set c_comment_screened_border = "#999";
#NEWLAYER: tranquilityii/mobile
layerinfo "redist_uniq" = "tranquilityii/mobile";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - Mobile Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#fff";
set c_header_background = "#fff";
set c_header_border = "#fff";
set c_page_title = "#000";
set c_page_background = "#fff";
set c_page_text = "#000";
set c_page_link = "#00f";
set c_page_link_visited = "#999";
set c_page_link_hover = "#f00";
set c_page_link_active = "#f00";
set c_menu_border = "#fff";
set c_menu_background = "#fff";
set c_menu_link = "#00f";
set c_menu_link_visited = "#999";
set c_menu_link_hover = "#f00";
set c_menu_link_active = "#f00";
set c_menu_text_color = "#000";
set c_menu_header_color = "#00";
set c_menu_current = "#000";
set f_menu_header_size = "140%";
set c_entry_background = "#fff";
set c_entry_link = "#00f";
set c_entry_link_visited = "#999";
set c_entry_link_hover = "#f00";
set c_entry_link_active = "#f00";
set c_entry_text_color = "#000";
set c_entry_title_color = "#000";
set c_entry_border = "#fff";
set c_meta_background = "#fff";
set c_meta_link = "#00f";
set c_meta_link_visited = "#999";
set c_meta_link_hover = "#f00";
set c_meta_link_active = "#f00";
set c_meta_text_color = "#000";
set c_footer_background = "#fff";
set c_footer_link = "#00f";
set c_footer_link_visited = "#999";
set c_footer_link_hover = "#f00";
set c_footer_link_active = "#f00";
set c_footer_text_color = "#000";
set c_comment_one_link = "#00f";
set c_comment_one_link_visited = "#999";
set c_comment_one_link_hover = "#f00";
set c_comment_one_link_active = "#f00";
set c_comment_one_text_color = "#000";
set c_comment_one_title_color = "#000";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#fff";
set c_comment_two_link = "#00f";
set c_comment_two_link_visited = "#999";
set c_comment_two_link_hover = "#f00";
set c_comment_two_link_active = "#f00";
set c_comment_two_text_color = "#000";
set c_comment_two_title_color = "#000";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#fff";
set c_comment_screened_link = "#00f";
set c_comment_screened_link_visited = "#999";
set c_comment_screened_link_hover = "#f00";
set c_comment_screened_link_active = "#f00";
set c_comment_screened_text_color = "#000";
set c_comment_screened_title_color = "#000";
set c_comment_screened_background = "#999";
set c_comment_screened_border = "#fff";
set menu_disable_summary = true;
set css_style_overrides = "hr { display: block; } #menu { float: none; width: auto; } #content { margin-left: 0; } #container { margin: 0; padding: 0; text-align: left; width: 100%; }";
#NEWLAYER: tranquilityii/pinky
layerinfo "redist_uniq" = "tranquilityii/pinky";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - Pinky Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#242E35";
set c_header_background = "#242E35";
set c_header_border = "#242E35";
set c_page_title = "#fff";
set c_page_background = "#242E35";
set c_page_text = "#fff";
set c_page_link = "#f0c";
set c_page_link_visited = "#FF82E5";
set c_page_link_hover = "#B30090";
set c_page_link_active = "#B30090";
set c_menu_border = "#B4A361";
set c_menu_background = "#ECE9D8";
set c_menu_link = "#000";
set c_menu_link_visited = "#999";
set c_menu_link_hover = "#000";
set c_menu_link_active = "#000";
set c_menu_text_color = "#333";
set c_menu_header_color = "#333";
set c_menu_current = "#000";
set f_menu_header_size = "140%";
set c_entry_background = "#F8F7F1";
set c_entry_link = "#f0c";
set c_entry_link_visited = "#FF82E5";
set c_entry_link_hover = "#B30090";
set c_entry_link_active = "#B30090";
set c_entry_text_color = "#333";
set c_entry_title_color = "#666";
set c_entry_border = "000";
set c_meta_background = "#F8F7F1";
set c_meta_link = "#f0c";
set c_meta_link_visited = "#FF82E5";
set c_meta_link_hover = "#B30090";
set c_meta_link_active = "#B30090";
set c_meta_text_color = "#666";
set c_footer_background = "#242E35";
set c_footer_link = "#fff";
set c_footer_link_visited = "#fff";
set c_footer_link_hover = "#fff";
set c_footer_link_active = "#fff";
set c_footer_text_color = "#fff";
set c_comment_one_link = "#f0c";
set c_comment_one_link_visited = "#FF82E5";
set c_comment_one_link_hover = "#B30090";
set c_comment_one_link_active = "#B30090";
set c_comment_one_text_color = "#333";
set c_comment_one_title_color = "#666";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#999";
set c_comment_two_link = "#f0c";
set c_comment_two_link_visited = "#FF82E5";
set c_comment_two_link_hover = "#B30090";
set c_comment_two_link_active = "#B30090";
set c_comment_two_text_color = "#333";
set c_comment_two_title_color = "#666";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#999";
set c_comment_screened_link = "#f0c";
set c_comment_screened_link_visited = "#FF82E5";
set c_comment_screened_link_hover = "#B30090";
set c_comment_screened_link_active = "#B30090";
set c_comment_screened_text_color = "#333";
set c_comment_screened_title_color = "#666";
set c_comment_screened_background = "#ccc";
set c_comment_screened_border = "#999";
set f_page = "Tahoma, Verdana, Helvetica, sans-serif";
set f_page_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_page_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_link = "Verdana, Helvetica, sans-serif";
set f_menu = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_header = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_current = "Verdana, Helvetica, sans-serif";
set f_entry = "Tahoma, Verdana, Helvetica, sans-serif";
set f_entry_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_entry_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_meta = "Verdana, Helvetica, sans-serif";
set f_meta_link = "Verdana, Helvetica, sans-serif";
set f_footer = "Tahoma, Verdana, Helvetica, sans-serif";
set f_footer_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment_link = "Tahoma, Verdana, Helvetica, sans-serif";
#NEWLAYER: tranquilityii/fresh
layerinfo "redist_uniq" = "tranquilityii/fresh";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - Fresh Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#7A97B2";
set c_header_background = "#7A97B2";
set c_header_border = "#7A97B2";
set c_page_title = "#273849";
set c_page_background = "#242E35";
set c_page_text = "#fff";
set c_page_link = "#036";
set c_page_link_visited = "#036";
set c_page_link_hover = "#069";
set c_page_link_active = "#069";
set c_menu_border = "#CFE0E6";
set c_menu_background = "#CFE0E6";
set c_menu_link = "#000";
set c_menu_link_visited = "#999";
set c_menu_link_hover = "#000";
set c_menu_link_active = "#000";
set c_menu_text_color = "#333";
set c_menu_header_color = "#333";
set c_menu_current = "#000";
set f_menu_header_size = "140%";
set c_entry_background = "#fff";
set c_entry_link = "#036";
set c_entry_link_visited = "#036";
set c_entry_link_hover = "#069";
set c_entry_link_active = "#069";
set c_entry_text_color = "#333";
set c_entry_title_color = "#999";
set c_entry_border = "000";
set c_meta_background = "#fff";
set c_meta_link = "#036";
set c_meta_link_visited = "#036";
set c_meta_link_hover = "#069";
set c_meta_link_active = "#069";
set c_meta_text_color = "#999";
set c_footer_background = "#7A97B2";
set c_footer_link = "#fff";
set c_footer_link_visited = "#fff";
set c_footer_link_hover = "#fff";
set c_footer_link_active = "#fff";
set c_footer_text_color = "#fff";
set c_comment_one_link = "#036";
set c_comment_one_link_visited = "#036";
set c_comment_one_link_hover = "#069";
set c_comment_one_link_active = "#069";
set c_comment_one_text_color = "#333";
set c_comment_one_title_color = "#999";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#999";
set c_comment_two_link = "#036";
set c_comment_two_link_visited = "#036";
set c_comment_two_link_hover = "#069";
set c_comment_two_link_active = "#069";
set c_comment_two_text_color = "#333";
set c_comment_two_title_color = "#999";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#999";
set c_comment_screened_link = "#036";
set c_comment_screened_link_visited = "#036";
set c_comment_screened_link_hover = "#069";
set c_comment_screened_link_active = "#069";
set c_comment_screened_text_color = "#333";
set c_comment_screened_title_color = "#999";
set c_comment_screened_background = "#ccc";
set c_comment_screened_border = "#999";
set f_page = "Tahoma, Verdana, Helvetica, sans-serif";
set f_page_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_page_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_link = "Verdana, Helvetica, sans-serif";
set f_menu = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_header = "Tahoma, Verdana, Helvetica, sans-serif";
set f_menu_current = "Verdana, Helvetica, sans-serif";
set f_entry = "Tahoma, Verdana, Helvetica, sans-serif";
set f_entry_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_entry_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_meta = "Verdana, Helvetica, sans-serif";
set f_meta_link = "Verdana, Helvetica, sans-serif";
set f_footer = "Tahoma, Verdana, Helvetica, sans-serif";
set f_footer_link = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment_title = "Tahoma, Verdana, Helvetica, sans-serif";
set f_comment_link = "Tahoma, Verdana, Helvetica, sans-serif";
#NEWLAYER: tranquilityii/auto
layerinfo "redist_uniq" = "tranquilityii/auto";
layerinfo "type" = "theme";
layerinfo "name" = "Tranquility II - Auto Theme";
layerinfo "author_name" = "Matthew Vince";
set c_main_border = "#515151";
set c_header_background = "#515151";
set c_header_border = "#515151";
set c_page_title = "#fff";
set c_page_background = "#515151";
set c_page_text = "#333";
set c_page_link = "#00B4FF";
set c_page_link_visited = "#00B4FF";
set c_page_link_hover = "#006894";
set c_page_link_active = "#006894";
set c_menu_border = "#ccc";
set c_menu_background = "#eee";
set c_menu_link = "#000";
set c_menu_link_visited = "#999";
set c_menu_link_hover = "#000";
set c_menu_link_active = "#000";
set c_menu_text_color = "#333";
set c_menu_header_color = "#333";
set c_menu_current = "#000";
set f_menu_header_size = "140%";
set c_entry_background = "#fff";
set c_entry_link = "#00B4FF";
set c_entry_link_visited = "#00B4FF";
set c_entry_link_hover = "#006894";
set c_entry_link_active = "#006894";
set c_entry_text_color = "#333";
set c_entry_title_color = "#999";
set c_entry_border = "000";
set c_meta_background = "#fff";
set c_meta_link = "#036";
set c_meta_link_visited = "#036";
set c_meta_link_hover = "#069";
set c_meta_link_active = "#069";
set c_meta_text_color = "#999";
set c_footer_background = "#515151";
set c_footer_link = "#fff";
set c_footer_link_visited = "#fff";
set c_footer_link_hover = "#fff";
set c_footer_link_active = "#fff";
set c_footer_text_color = "#fff";
set c_comment_one_link = "#00B4FF";
set c_comment_one_link_visited = "#00B4FF";
set c_comment_one_link_hover = "#006894";
set c_comment_one_link_active = "#006894";
set c_comment_one_text_color = "#333";
set c_comment_one_title_color = "#999";
set c_comment_one_background = "#fff";
set c_comment_one_border = "#999";
set c_comment_two_link = "#00B4FF";
set c_comment_two_link_visited = "#00B4FF";
set c_comment_two_link_hover = "#006894";
set c_comment_two_link_active = "#006894";
set c_comment_two_text_color = "#333";
set c_comment_two_title_color = "#999";
set c_comment_two_background = "#f2f2f2";
set c_comment_two_border = "#999";
set c_comment_screened_link = "#00B4FF";
set c_comment_screened_link_visited = "#00B4FF";
set c_comment_screened_link_hover = "#006894";
set c_comment_screened_link_active = "#006894";
set c_comment_screened_text_color = "#333";
set c_comment_screened_title_color = "#999";
set c_comment_screened_background = "#ccc";
set c_comment_screened_border = "#999";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
#NEWLAYER: unearthed/rampant
layerinfo "type" = "theme";
layerinfo "name" = "Rampant Tenderfoot";
layerinfo redist_uniq = "unearthed/rampant";
set stronger_bgcolor = "#ff3300";
set strong_bgcolor = "#ff8000";
set neutral_bgcolor = "#ffb200";
set weak_bgcolor = "#ffb380";
set weaker_bgcolor = "#ffcc80";
#NEWLAYER: unearthed/acrid
layerinfo "type" = "theme";
layerinfo "name" = "Acrid Slide";
layerinfo redist_uniq = "unearthed/acrid";
set title_texture = "rough";
set stronger_bgcolor = "#33ff00";
set strong_bgcolor = "#0066b2";
set neutral_bgcolor = "#00B266";
set weak_bgcolor = "#99ff80";
set weaker_bgcolor = "#ccffbf";
#NEWLAYER: unearthed/leisure
layerinfo "type" = "theme";
layerinfo "name" = "Leisure Renewal";
layerinfo redist_uniq = "unearthed/leisure";
set title_texture = "camouflage";
set stronger_bgcolor = "#285577";
set strong_bgcolor = "#3c3773";
set neutral_bgcolor = "#287755";
set weak_bgcolor = "#c0c0ec";
set weaker_bgcolor = "#b1ecd3";
#NEWLAYER: unearthed/craftis
layerinfo "type" = "theme";
layerinfo "name" = "Craftis";
layerinfo redist_uniq = "unearthed/craftis";
set title_texture = "camouflage";
set stronger_bgcolor = "#dd0000";
set strong_bgcolor = "#aa4f39";
set neutral_bgcolor = "#d2d2d2";
set weak_bgcolor = "#fcffcf";
set weaker_bgcolor = "#eeeeee";
#NEWLAYER: unearthed/unveiled
layerinfo "type" = "theme";
layerinfo "name" = "Unveiled Metal";
layerinfo redist_uniq = "unearthed/unveiled";
set title_texture = "brushed_metal";
set stronger_bgcolor = "#666666";
set strong_bgcolor = "#999999";
set neutral_bgcolor = "#333333";
set weak_bgcolor = "#cccccc";
set weaker_bgcolor = "#eeeeee";
#NEWLAYER: unearthed/wait
layerinfo "type" = "theme";
layerinfo "name" = "Wait Screw";
layerinfo redist_uniq = "unearthed/wait";
set title_texture = "chalk";
set stronger_bgcolor = "#009999";
set strong_bgcolor = "#ff6600";
set neutral_bgcolor = "#66cccc";
set weak_bgcolor = "#ffb380";
set weaker_bgcolor = "#eeeeee";
#NEWLAYER: unearthed/drinkdna
layerinfo "type" = "theme";
layerinfo "name" = "Drinking DNA";
layerinfo redist_uniq = "unearthed/drinkdna";
set title_texture = "cork";
set stronger_bgcolor = "#4C9978";
set strong_bgcolor = "#468C8C";
set neutral_bgcolor = "#53A653";
set weak_bgcolor = "#ACE6E6";
set weaker_bgcolor = "#eeeeee";
#NEWLAYER: unearthed/gainful
layerinfo "type" = "theme";
layerinfo "name" = "Gainful Magic";
layerinfo redist_uniq = "unearthed/gainful";
set title_texture = "fibers";
set stronger_bgcolor = "#DFDFA7";
set strong_bgcolor = "#FFFF80";
set neutral_bgcolor = "#AAAA39";
set weak_bgcolor = "#eeeeee";
set weaker_bgcolor = "#FFFFBF";
#NEWLAYER: unearthed/inaccurate
layerinfo "type" = "theme";
layerinfo "name" = "Inaccurate Lingo";
layerinfo redist_uniq = "unearthed/inaccurate";
set title_texture = "stucco";
set stronger_bgcolor = "#FF8080";
set strong_bgcolor = "#FF9980";
set neutral_bgcolor = "#E572A5";
set weak_bgcolor = "#F2B6D0";
set weaker_bgcolor = "#eeeeee";
#NEWLAYER: unearthed/current
layerinfo "type" = "theme";
layerinfo "name" = "Current Protector";
layerinfo redist_uniq = "unearthed/current";
set title_texture = "type";
set stronger_bgcolor = "#B5D9D9";
set strong_bgcolor = "#285577";
set neutral_bgcolor = "#226666";
set weak_bgcolor = "#7DA0BB";
set weaker_bgcolor = "#B5D9D9";

View File

@@ -0,0 +1,25 @@
;; -*- coding: utf-8 -*-
/manage/siteopts.bml.btn.lang=Сменити язык
dystopia.hello_loggedin=Здорово буть
langname.en=Аглицкий
langname.ru=Руской
langname.sb=Сибирской
ljrlook.nav.editfriends=Тамыри
ljrlook.nav.friends=Тамыри
ljrlook.nav.hello=Здорово буть
lynx.nav.friends=Тамыри
poll.security.friends=Тамыри
xcolibur.nav.journal.friends=Тамыри
xcolibur.nav.manage.friends=Тамыри

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
domain:100:faq
domain:101:journal/news
# EnglishLJ is child of English for general domain:
#lang:100:en_LJ:English (LJ):sim:en
#langdomain:en_LJ:general
# EnglishLJ is root of FAQ & news journal:
#langdomain:en_LJ:faq:1
#langdomain:en_LJ:journal/news:1
# Russian
lang:106:ru:Russian:diff:en
langdomain:ru:general
langdomain:ru:journal/news
langdomain:ru:faq
# Ukrainian
lang:131:uk:Ukrainian:diff:en
langdomain:uk:general
langdomain:uk:journal/news
langdomain:uk:faq
# Siberian
lang:206:sb:Siberian:diff:en
langdomain:sb:general
langdomain:sb:journal/news
langdomain:sb:faq

View File

@@ -0,0 +1,84 @@
#
# texttool.pl newitems override
# format:
# <code calculated by texttool.pl><space><actual code which should be used>
#
/modify.bml.colortheme.defaulttheme /modify_do.bml.colortheme.defaulttheme
/modify.bml.colortheme.color.head2 /modify_do.bml.colortheme.color.head2
/modify.bml.colortheme.customcolors /modify_do.bml.colortheme.customcolors
/modify.bml.overrides.warning /modify_do.bml.overrides.warning
/modify.bml.moodicons.head /modify_do.bml.moodicons.head
/modify.bml.availablestyles.userstyles /modify_do.bml.availablestyles.userstyles
/modify.bml.overrides.head /modify_do.bml.overrides.head
/modify.bml.moodicons.personal /modify_do.bml.moodicons.personal
/modify.bml.domainalias.example /modify_do.bml.domainalias.example
/modify.bml.friends.opt.usesharedpic.about /modify_do.bml.friends.opt.usesharedpic.about
/modify.bml.overrides.note /modify_do.bml.overrides.note
/modify.bml.colortheme.about /modify_do.bml.colortheme.about
/modify.bml.done.text /modify_do.bml.done.text
/modify.bml.success.head /modify_do.bml.success.head
/modify.bml.pagelayoutstyle.warning /modify_do.bml.pagelayoutstyle.warning
/modify.bml.friends.head /modify_do.bml.friends.head
/modify.bml.error.stylenotavailable /modify_do.bml.error.stylenotavailable
/modify.bml.colortheme.color.head1 /modify_do.bml.colortheme.color.head1
/modify.bml.friends.opt.usesharedpic.head /modify_do.bml.friends.opt.usesharedpic.head
/modify.bml.pagelayoutstyle.about /modify_do.bml.pagelayoutstyle.about
/modify.bml.domainalias.head /modify_do.bml.domainalias.head
/modify.bml.moodicons.opt.forcefriends.about /modify_do.bml.moodicons.opt.forcefriends.about
/modify.bml.colortheme.head /modify_do.bml.colortheme.head
/modify.bml.moodicons.preview /modify_do.bml.moodicons.preview
/modify.bml.pagelayoutstyle.head /modify_do.bml.pagelayoutstyle.head
/modify.bml.moodicons.about /modify_do.bml.moodicons.about
/modify.bml.friends.about /modify_do.bml.friends.about
/modify.bml.colortheme.area.head /modify_do.bml.colortheme.area.head
/modify.bml.availablestyles.head /modify_do.bml.availablestyles.head
/modify.bml.success.text /modify_do.bml.success.text
/modify.bml.domainalias.about /modify_do.bml.domainalias.about
/modify.bml.error.dupdomainalias /modify_do.bml.error.dupdomainalias
/modify.bml.journaloptions.about /modify_do.bml.journaloptions.about
/modify.bml.moodicons.select /modify_do.bml.moodicons.select
/modify.bml.domainalias.domainname /modify_do.bml.domainalias.domainname
/modify.bml.done.head /modify_do.bml.done.head
/modify.bml.overrides.about /modify_do.bml.overrides.about
/modify.bml.overrides.box.head /modify_do.bml.overrides.box.head
/modify.bml.journaloptions.head /modify_do.bml.journaloptions.head
/modify.bml.availablestyles.disabledstyles /modify_do.bml.availablestyles.disabledstyles
/modify.bml.domainalias.helptext /modify_do.bml.domainalias.helptext
/modify.bml.done.btn.savechanges /modify_do.bml.done.btn.savechanges
/friends/edit.bml.opt.delete /friends/edit_do.bml.opt.delete
/friends/edit.bml.name /friends/edit_do.bml.name
/friends/edit.bml.friend /friends/edit_do.bml.friend
/friends/edit.bml.needmore /friends/edit_do.bml.needmore
/friends/edit.bml.success.text /friends/edit_do.bml.success.text
/friends/edit.bml.btn.toggle /friends/edit_do.bml.btn.toggle
/friends/edit.bml.success.head /friends/edit_do.bml.success.head
/friends/edit.bml.error.updating /friends/edit_do.bml.error.updating
/friends/edit.bml.yourfriends.text /friends/edit_do.bml.yourfriends.text
/friends/edit.bml.user /friends/edit_do.bml.user
/friends/edit.bml.nofriends.head /friends/edit_do.bml.nofriends.head
/friends/edit.bml.fellowfriends.head /friends/edit_do.bml.fellowfriends.head
/friends/edit.bml.foreground /friends/edit_do.bml.foreground
/friends/edit.bml.done.text /friends/edit_do.bml.done.text
/friends/edit.bml.addfriends.text /friends/edit_do.bml.addfriends.text
/friends/edit.bml.addfriends.head /friends/edit_do.bml.addfriends.head
/friends/edit.bml.fellowfriends.text /friends/edit_do.bml.fellowfriends.text
/friends/edit.bml.btn.save /friends/edit_do.bml.btn.save
/friends/edit.bml.hover /friends/edit_do.bml.hover
/friends/edit.bml.opt.addtolist /friends/edit_do.bml.opt.addtolist
/friends/edit.bml.yourfriends.head /friends/edit_do.bml.yourfriends.head
/friends/edit.bml.background /friends/edit_do.bml.background
/friends/edit.bml.done.head /friends/edit_do.bml.done.head
/friends/edit.bml.nofriends.text /friends/edit_do.bml.nofriends.text
.opt.bannedfrom /talkpost.bml.opt.bannedfrom
.opt.loggedin /talkpost.bml.opt.loggedin
.label.picturetouse2 /talkpost.bml.label.picturetouse2
.noaccount /talkpost.bml.noaccount
/editjournal.bml.pickentry.head /editjournal_do.bml.pickentry.head
/editjournal.bml.error.modify /editjournal_do.bml.error.modify
/editjournal.bml.pickentry.text /editjournal_do.bml.pickentry.text
/editjournal.bml.error.nofind /editjournal_do.bml.error.nofind
/editjournal.bml.error.getting /editjournal_do.bml.error.getting

758
local/bin/upgrading/texttool.pl Executable file
View File

@@ -0,0 +1,758 @@
#!/usr/bin/perl
#
# This program deals with inserting/extracting text/language data
# from the database.
#
use strict;
use Getopt::Long;
my $opt_help = 0;
my $opt_local_lang;
my $opt_extra;
my $opt_only;
my $opt_override;
my $opt_verbose;
my $opt_do;
my @additems;
exit 1 unless
GetOptions(
"help" => \$opt_help,
"local-lang=s" => \$opt_local_lang,
"extra=s" => \$opt_extra,
"override|r" => \$opt_override,
"verbose" => \$opt_verbose,
"only=s" => \$opt_only,
"do" => \$opt_do,
"additems:s{,}" => \@additems,
);
my $mode = shift @ARGV;
help() if $opt_help or not defined $mode;
sub help
{
die "Usage: texttool.pl <command> [options]
Where 'command' is one of:
load Runs the following four commands in order:
popstruct Populate lang data from text[-local].dat into db
poptext Populate text from en.dat, etc into database.
--extra=<file> specifies an alternative input file
--override (-v) specifies existing values should be overwritten
for all languages. (for developer use only)
copyfaq If site is translating FAQ, copy FAQ data into trans area
loadcrumbs Load crumbs from ljcrumbs.pl and ljcrumbs-local.pl.
makeusable Setup internal indexes necessary after loading text
dumptext Dump lang text based on text[-local].dat information
check Check validity of text[-local].dat files
wipedb Remove all language/text data from database, including crumbs.
wipecrumbs Remove all crumbs from the database, leaving other text alone.
newitems Search files in htdocs, cgi-bin, & bin and insert
necessary text item codes in database
remove takes two extra arguments: domain name and code, and removes
that code and its text in all languages
Optional `options`:
--help The page you're looking at
--verbose More output
--local-lang=.. If given, works on local site files too
--do If given, lets newitems actually create items in database
--additems Takes multiple parameters. If given specifies
what exactly new items should be added.
";
}
## make sure $LJHOME is set so we can load & run everything
unless (-d $ENV{'LJHOME'}) {
die "LJHOME environment variable is not set, or is not a directory.\n".
"You must fix this before you can run this database update script.";
}
require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
require "$ENV{'LJHOME'}/cgi-bin/ljlang.pl";
require "$ENV{'LJHOME'}/cgi-bin/weblib.pl";
my %dom_id; # number -> {}
my %dom_code; # name -> {}
my %lang_id; # number -> {}
my %lang_code; # name -> {}
my @lang_domains;
my $set = sub {
my ($hash, $key, $val, $errmsg) = @_;
die "$errmsg$key\n" if exists $hash->{$key};
$hash->{$key} = $val;
};
foreach my $scope ("general", "local")
{
my $file = $scope eq "general" ? "text.dat" : "text-local.dat";
my $ffile = "$ENV{'LJHOME'}/bin/upgrading/$file";
unless (-e $ffile) {
next if $scope eq "local";
die "$file file not found; odd: did you delete it?\n";
}
open (F, $ffile) or die "Can't open file: $file: $!\n";
while (<F>) {
s/\s+$//; s/^\#.+//;
next unless /\S/;
my @vals = split(/:/, $_);
my $what = shift @vals;
# language declaration
if ($what eq "lang") {
my $lang = {
'scope' => $scope,
'lnid' => $vals[0],
'lncode' => $vals[1],
'lnname' => $vals[2],
'parentlnid' => 0, # default. changed later.
'parenttype' => 'diff',
};
$lang->{'parenttype'} = $vals[3] if defined $vals[3];
if (defined $vals[4]) {
unless (exists $lang_code{$vals[4]}) {
die "Can't declare language $lang->{'lncode'} with missing parent language $vals[4].\n";
}
$lang->{'parentlnid'} = $lang_code{$vals[4]}->{'lnid'};
}
$set->(\%lang_id, $lang->{'lnid'}, $lang, "Language already defined with ID: ");
$set->(\%lang_code, $lang->{'lncode'}, $lang, "Language already defined with code: ");
}
# domain declaration
if ($what eq "domain") {
my $dcode = $vals[1];
my ($type, $args) = split(m!/!, $dcode);
my $dom = {
'scope' => $scope,
'dmid' => $vals[0],
'type' => $type,
'args' => $args || "",
};
$set->(\%dom_id, $dom->{'dmid'}, $dom, "Domain already defined with ID: ");
$set->(\%dom_code, $dcode, $dom, "Domain already defined with parameters: ");
}
# langdomain declaration
if ($what eq "langdomain") {
my $ld = {
'lnid' =>
(exists $lang_code{$vals[0]} ? $lang_code{$vals[0]}->{'lnid'} :
die "Undefined language: $vals[0]\n"),
'dmid' =>
(exists $dom_code{$vals[1]} ? $dom_code{$vals[1]}->{'dmid'} :
die "Undefined domain: $vals[1]\n"),
'dmmaster' => $vals[2] ? "1" : "0",
};
push @lang_domains, $ld;
}
}
close F;
}
if ($mode eq "check") {
print "all good.\n";
exit 0;
}
## make sure we can connect
my $dbh = LJ::get_dbh("master");
my $sth;
unless ($dbh) {
die "Can't connect to the database.\n";
}
# indenter
my $idlev = 0;
my $out = sub {
my @args = @_;
while (@args) {
my $a = shift @args;
if ($a eq "+") { $idlev++; }
elsif ($a eq "-") { $idlev--; }
elsif ($a eq "x") { $a = shift @args; die " "x$idlev . $a . "\n"; }
else { print " "x$idlev, $a, "\n"; }
}
};
my @good = qw(load popstruct poptext dumptext newitems wipedb makeusable copyfaq remove
wipecrumbs loadcrumbs);
popstruct() if $mode eq "popstruct" or $mode eq "load";
poptext(@ARGV) if $mode eq "poptext" or $mode eq "load";
copyfaq() if $mode eq "copyfaq" or $mode eq "load";
loadcrumbs() if $mode eq "loadcrumbs" or $mode eq "load";
makeusable() if $mode eq "makeusable" or $mode eq "load";
dumptext(@ARGV) if $mode eq "dumptext";
newitems() if $mode eq "newitems";
wipedb() if $mode eq "wipedb";
wipecrumbs() if $mode eq "wipecrumbs";
remove(@ARGV) if $mode eq "remove" and scalar(@ARGV) == 2;
help() unless grep { $mode eq $_ } @good;
exit 0;
sub makeusable
{
$out->("Making usable...", '+');
my $rec = sub {
my ($lang, $rec) = @_;
my $l = $lang_code{$lang};
$out->("x", "Bogus language: $lang") unless $l;
my @children = grep { $_->{'parentlnid'} == $l->{'lnid'} } values %lang_code;
foreach my $cl (@children) {
$out->("$l->{'lncode'} -- $cl->{'lncode'}");
my %need;
# push downwards everything that has some valid text in some language (< 4)
$sth = $dbh->prepare("SELECT dmid, itid, txtid FROM ml_latest WHERE lnid=$l->{'lnid'} AND staleness < 4");
$sth->execute;
while (my ($dmid, $itid, $txtid) = $sth->fetchrow_array) {
$need{"$dmid:$itid"} = $txtid;
}
$sth = $dbh->prepare("SELECT dmid, itid, txtid FROM ml_latest WHERE lnid=$cl->{'lnid'}");
$sth->execute;
while (my ($dmid, $itid, $txtid) = $sth->fetchrow_array) {
delete $need{"$dmid:$itid"};
}
while (my $k = each %need) {
my ($dmid, $itid) = split(/:/, $k);
my $txtid = $need{$k};
my $stale = $cl->{'parenttype'} eq "diff" ? 3 : 0;
$dbh->do("INSERT INTO ml_latest (lnid, dmid, itid, txtid, chgtime, staleness) VALUES ".
"($cl->{'lnid'}, $dmid, $itid, $txtid, NOW(), $stale)");
die $dbh->errstr if $dbh->err;
}
$rec->($cl->{'lncode'}, $rec);
}
};
$rec->("en", $rec);
$out->("-", "done.");
}
sub copyfaq
{
my $faqd = LJ::Lang::get_dom("faq");
my $ll = LJ::Lang::get_root_lang($faqd);
unless ($ll) { return; }
my $domid = $faqd->{'dmid'};
$out->("Copying FAQ...", '+');
my %existing;
$sth = $dbh->prepare("SELECT i.itcode FROM ml_items i, ml_latest l ".
"WHERE l.lnid=$ll->{'lnid'} AND l.dmid=$domid AND l.itid=i.itid AND i.dmid=$domid");
$sth->execute;
$existing{$_} = 1 while $_ = $sth->fetchrow_array;
# faq category
$sth = $dbh->prepare("SELECT faqcat, faqcatname FROM faqcat");
$sth->execute;
while (my ($cat, $name) = $sth->fetchrow_array) {
next if exists $existing{"cat.$cat"};
my $opts = { 'childrenlatest' => 1 };
LJ::Lang::set_text($dbh, $domid, $ll->{'lncode'}, "cat.$cat", $name, $opts);
}
# faq items
$sth = $dbh->prepare("SELECT faqid, question, answer FROM faq");
$sth->execute;
while (my ($faqid, $q, $a) = $sth->fetchrow_array) {
next if
exists $existing{"$faqid.1question"} and
exists $existing{"$faqid.2answer"};
my $opts = { 'childrenlatest' => 1 };
LJ::Lang::set_text($dbh, $domid, $ll->{'lncode'}, "$faqid.1question", $q, $opts);
LJ::Lang::set_text($dbh, $domid, $ll->{'lncode'}, "$faqid.2answer", $a, $opts);
}
$out->('-', "done.");
}
sub wipedb
{
$out->("Wiping DB...", '+');
foreach (qw(domains items langdomains langs latest text)) {
$out->("deleting from $_");
$dbh->do("DELETE FROM ml_$_");
}
$out->("-", "done.");
}
sub wipecrumbs
{
$out->('Wiping DB of all crumbs...', '+');
# step 1: get all items that are crumbs. [from ml_items]
my $genid = $dom_code{'general'}->{'dmid'};
my @crumbs;
my $sth = $dbh->prepare("SELECT itcode FROM ml_items
WHERE dmid = $genid AND itcode LIKE 'crumb.\%'");
$sth->execute;
while (my ($itcode) = $sth->fetchrow_array) {
# push onto list
push @crumbs, $itcode;
}
# step 2: remove the items that have these unique dmid/itids
foreach my $code (@crumbs) {
$out->("deleting $code");
remove("general", $code);
}
# done
$out->('-', 'done.');
}
sub loadcrumbs
{
$out->('Loading all crumbs into DB...', '+');
# get domain id of 'general' and language id of 'en'
my $genid = $dom_code{'general'}->{'dmid'};
my $loclang = $LJ::LANGS[0] || 'en';
# list of crumbs
my @crumbs;
foreach (keys %LJ::CRUMBS_LOCAL) { push @crumbs, $_; }
foreach (keys %LJ::CRUMBS) { push @crumbs, $_; }
# begin iterating, order doesn't matter...
foreach my $crumbkey (@crumbs) {
$out->("inserting crumb.$crumbkey");
my $crumb = LJ::get_crumb($crumbkey);
my $local = $LJ::CRUMBS_LOCAL{$crumbkey} ? 1 : 0;
# see if it exists
my $itid = $dbh->selectrow_array("SELECT itid FROM ml_items
WHERE dmid = $genid AND itcode = 'crumb.$crumbkey'")+0;
LJ::Lang::set_text($genid, $local ? $loclang : 'en', "crumb.$crumbkey", $crumb->[0])
unless $itid;
}
# done
$out->('-', 'done.');
}
sub popstruct
{
$out->("Populating structure...", '+');
foreach my $l (values %lang_id) {
$out->("Inserting language: $l->{'lnname'}");
$dbh->do("INSERT INTO ml_langs (lnid, lncode, lnname, parenttype, parentlnid) ".
"VALUES (" . join(",", map { $dbh->quote($l->{$_}) } qw(lnid lncode lnname parenttype parentlnid)) . ")");
}
foreach my $d (values %dom_id) {
$out->("Inserting domain: $d->{'type'}\[$d->{'args'}\]");
$dbh->do("INSERT INTO ml_domains (dmid, type, args) ".
"VALUES (" . join(",", map { $dbh->quote($d->{$_}) } qw(dmid type args)) . ")");
}
$out->("Inserting language domains ...");
foreach my $ld (@lang_domains) {
$dbh->do("INSERT IGNORE INTO ml_langdomains (lnid, dmid, dmmaster) VALUES ".
"(" . join(",", map { $dbh->quote($ld->{$_}) } qw(lnid dmid dmmaster)) . ")");
}
$out->("-", "done.");
}
sub poptext
{
my @langs = @_;
push @langs, (keys %lang_code) unless @langs;
$out->("Populating text...", '+');
my %source; # lang -> file, or "[extra]" when given by --extra= argument
if ($opt_extra) {
$source{'[extra]'} = $opt_extra;
} else {
foreach my $lang (@langs) {
my $file = "$ENV{'LJHOME'}/bin/upgrading/${lang}.dat";
next if $opt_only && $lang ne $opt_only;
next unless -e $file;
$source{$lang} = $file;
}
}
my %existing_item; # langid -> code -> 1
foreach my $source (keys %source)
{
$out->("$source", '+');
my $file = $source{$source};
open (D, $file)
or $out->('x', "Can't open $source data file");
# fixed language in *.dat files, but in extra files
# it switches as it goes.
my $l;
if ($source ne "[extra]") { $l = $lang_code{$source}; }
my $bml_prefix = "";
my $addcount = 0;
my $lnum = 0;
my ($code, $text);
my %metadata;
while (my $line = <D>) {
$lnum++;
my $del;
my $action_line;
if ($line =~ /^==(LANG|BML):\s*(\S+)/) {
$out->('x', "Bogus directives in non-extra file.")
if $source ne "[extra]";
my ($what, $val) = ($1, $2);
if ($what eq "LANG") {
$l = $lang_code{$val};
$out->('x', 'Bogus ==LANG switch to: $what') unless $l;
$bml_prefix = "";
} elsif ($what eq "BML") {
$out->('x', 'Bogus ==BML switch to: $what')
unless $val =~ m!^/.+\.bml$!;
$bml_prefix = $val;
}
} elsif ($line =~ /^(\S+?)=(.*)/) {
($code, $text) = ($1, $2);
$action_line = 1;
} elsif ($line =~ /^\!\s*(\S+)/) {
$del = $code;
$action_line = 1;
} elsif ($line =~ /^(\S+?)\<\<\s*$/) {
($code, $text) = ($1, "");
while (<D>) {
$lnum++;
last if $_ eq ".\n";
s/^\.//;
$text .= $_;
}
chomp $text; # remove file new-line (we added it)
$action_line = 1;
} elsif ($line =~ /^[\#\;]/) {
# comment line
next;
} elsif ($line =~ /\S/) {
$out->('x', "$source:$lnum: Bogus format.");
}
if ($code =~ m!^\.!) {
$out->('x', "Can't use code with leading dot: $code")
unless $bml_prefix;
$code = "$bml_prefix$code";
}
if ($code =~ /\|(.+)/) {
$metadata{$1} = $text;
next;
}
next unless $action_line;
$out->('x', 'No language defined!') unless $l;
# load existing items for target language
unless (exists $existing_item{$l->{'lnid'}}) {
$existing_item{$l->{'lnid'}} = {};
my $sth = $dbh->prepare(qq{
SELECT i.itcode
FROM ml_latest l, ml_items i
WHERE i.dmid=1 AND l.dmid=1 AND i.itid=l.itid AND l.lnid=$l->{'lnid'}
});
$sth->execute;
$existing_item{$l->{'lnid'}}->{$_} = 1
while $_ = $sth->fetchrow_array;
}
# do deletes
if (defined $del) {
remove("general", $del)
if delete $existing_item{$l->{'lnid'}}->{$del};
next;
}
# if override is set (development option) then delete
if ($opt_override && $existing_item{$l->{'lnid'}}->{$code}) {
remove("general", $code);
delete $existing_item{$l->{'lnid'}}->{$code};
}
unless ($existing_item{$l->{'lnid'}}->{$code}) {
$addcount++;
my $staleness = $metadata{'staleness'}+0;
my $res = LJ::Lang::set_text($dbh, 1, $l->{'lncode'}, $code, $text,
{ 'staleness' => $staleness,
'notes' => $metadata{'notes'}, });
$out->("set: $code") if $opt_verbose;
unless ($res) {
$out->('x', "ERROR: " . LJ::Lang::last_error());
}
}
%metadata = ();
}
close D;
$out->("added: $addcount", '-');
}
$out->("-", "done.");
# dead phrase removal
$out->("Removing dead phrases...", '+');
foreach my $file ("deadphrases.dat", "deadphrases-local.dat") {
my $ffile = "$ENV{'LJHOME'}/bin/upgrading/$file";
next unless -s $ffile;
$out->("File: $file");
open (DP, $ffile) or die;
while (my $li = <DP>) {
$li =~ s/\#.*//;
next unless $li =~ /\S/;
$li =~ s/\s+$//;
my ($dom, $it) = split(/\s+/, $li);
next unless exists $dom_code{$dom};
my $dmid = $dom_code{$dom}->{'dmid'};
my @items;
if ($it =~ s/\*$/\%/) {
my $sth = $dbh->prepare("SELECT itcode FROM ml_items WHERE dmid=? AND itcode LIKE ?");
$sth->execute($dmid, $it);
push @items, $_ while $_ = $sth->fetchrow_array;
} else {
@items = ($it);
}
foreach (@items) {
remove($dom, $_, 1);
}
}
close DP;
}
$out->('-', "Done.");
}
sub dumptext
{
my @langs = @_;
unless (@langs) { @langs = keys %lang_code; }
$out->('Dumping text...', '+');
foreach my $lang (@langs)
{
$out->("$lang");
my $l = $lang_code{$lang};
open (D, ">$ENV{'LJHOME'}/bin/upgrading/${lang}.dat")
or $out->('x', "Can't open $lang.dat");
print D ";; -*- coding: utf-8 -*-\n";
my $sth = $dbh->prepare("SELECT i.itcode, t.text, l.staleness, i.notes FROM ".
"ml_items i, ml_latest l, ml_text t ".
"WHERE l.lnid=$l->{'lnid'} AND l.dmid=1 ".
"AND i.dmid=1 AND l.itid=i.itid AND ".
"t.dmid=1 AND t.txtid=l.txtid AND ".
# only export mappings that aren't inherited:
"t.lnid=$l->{'lnid'} ".
"ORDER BY i.itcode");
$sth->execute;
die $dbh->errstr if $dbh->err;
my $writeline = sub {
my ($k, $v) = @_;
if ($v =~ /\n/) {
$v =~ s/\n\./\n\.\./g;
print D "$k<<\n$v\n.\n";
} else {
print D "$k=$v\n";
}
};
while (my ($itcode, $text, $staleness, $notes) = $sth->fetchrow_array) {
$writeline->("$itcode|staleness", $staleness)
if $staleness;
$writeline->("$itcode|notes", $notes)
if $notes =~ /\S/;
$writeline->($itcode, $text);
print D "\n";
}
close D;
}
$out->('-', 'done.');
}
sub newitems
{
$out->("Searching for referenced text codes...", '+');
my $top = $ENV{'LJHOME'};
my %overrides;
if (open (O, "$top/bin/upgrading/texttool-overrides")) {
while (<O>) {
my $l = $_;
if ($l !~ /^#/ && $l !~ /^[\s]*$/) {
$l =~ s/[\r\n]//;
my @a = split ,$l;
$overrides{$a[0]} = $a[1];
}
}
close O;
}
my @files;
push @files, qw(htdocs cgi-bin bin);
my %items; # $scope -> $key -> 1;
while (@files)
{
my $file = shift @files;
my $ffile = "$top/$file";
next unless -e $ffile;
if (-d $ffile) {
$out->("dir: $file") if $opt_verbose;
opendir (MD, $ffile) or die "Can't open $file";
while (my $f = readdir(MD)) {
next if $f eq "." || $f eq ".." ||
$f =~ /^\.\#/ || $f =~ /(\.png|\.gif|~|\#)$/;
unshift @files, "$file/$f";
}
closedir MD;
}
if (-f $ffile) {
my $scope = "general";
# $scope = "general" if -e "$top/cvs/livejournal/$file";
open (F, $ffile) or die "Can't open $file";
my $line = 0;
my $correct_code = sub {
my ($code, $file) = @_;
if ($code =~ /^\./ && $file =~ m!^htdocs/!) {
$code = "$file$code";
$code =~ s!^htdocs!!;
}
if ($overrides{$code}) {
$code = $overrides{$code};
}
return $code;
};
my $valid_code = sub {
my ($code, $file) = @_;
if ($code =~ /\$/ || $file eq "bin/upgrading/texttool.pl") {
print STDERR "SKIPPING: $file [$code]\n";
return 0;
}
return 1;
};
while (<F>) {
$line++;
if (/^#/) {
next;
}
while (/BML::ml\([\"\'](.+?)[\"\']/g) {
my $code = $1;
$code = $correct_code->($code, $file);
$out->("BML::ml: $file $code") if $opt_verbose;
$items{$scope}->{$code} = 1 if $valid_code->($code, $file);
}
while (/\(=_ML\s+(.+?)\s+_ML=\)/g) {
my $code = $1;
$code = $correct_code->($code, $file);
$out->("_ML: $code") if $opt_verbose;
$items{$scope}->{$code} = 1 if $valid_code->($code, $file);
}
while (/\$ML{'(.+?)'}/g) {
my $code = $1;
$code = $correct_code->($code, $file);
$out->("\$ML: $file $code") if $opt_verbose;
$items{$scope}->{$code} = 1 if $valid_code->($code, $file);
}
}
close F;
}
}
$out->(sprintf("%d general and %d local found.",
scalar keys %{$items{'general'}},
scalar keys %{$items{'local'}}));
# [ General ]
my %e_general; # code -> 1
$out->("Checking which general items already exist in database...");
my $sth = $dbh->prepare("SELECT i.itcode FROM ml_items i, ml_latest l WHERE ".
"l.dmid=1 AND l.lnid=1 AND i.dmid=1 AND i.itid=l.itid ");
$sth->execute;
while (my $it = $sth->fetchrow_array) { $e_general{$it} = 1; }
$out->(sprintf("%d found", scalar keys %e_general));
foreach my $it (keys %{$items{'general'}}) {
next if exists $e_general{$it};
if (@additems && grep(/^$it$/, @additems)) {
if ($opt_do) {
my $res = LJ::Lang::set_text($dbh, 1, "en", $it, undef, { 'staleness' => 4 });
$out->("Adding general: $it ... $res");
}
else {
$out->("Would add general: $it");
}
}
else {
$out->("Skipped: $it");
}
}
if ($opt_local_lang) {
my $ll = $lang_code{$opt_local_lang};
die "Bogus --local-lang argument\n" unless $ll;
die "Local-lang '$ll->{'lncode'}' parent isn't 'en'\n"
unless $ll->{'parentlnid'} == 1;
$out->("Checking which local items already exist in database...");
my %e_local;
$sth = $dbh->prepare("SELECT i.itcode FROM ml_items i, ml_latest l WHERE ".
"l.dmid=1 AND l.lnid=$ll->{'lnid'} AND i.dmid=1 AND i.itid=l.itid ");
$sth->execute;
while (my $it = $sth->fetchrow_array) { $e_local{$it} = 1; }
$out->(sprintf("%d found\n", scalar keys %e_local));
foreach my $it (keys %{$items{'local'}}) {
next if exists $e_general{$it};
next if exists $e_local{$it};
my $res = LJ::Lang::set_text($dbh, 1, $ll->{'lncode'}, $it, undef, { 'staleness' => 4 });
$out->("Adding local: $it ... $res");
}
}
$out->('-', 'done.');
}
sub remove {
my ($dmcode, $itcode, $no_error) = @_;
my $dmid;
if (exists $dom_code{$dmcode}) {
$dmid = $dom_code{$dmcode}->{'dmid'};
} else {
$out->("x", "Unknown domain code $dmcode.");
}
my $qcode = $dbh->quote($itcode);
my $itid = $dbh->selectrow_array("SELECT itid FROM ml_items WHERE dmid=$dmid AND itcode=$qcode");
return if $no_error && !$itid;
$out->("x", "Unknown item code $itcode.") unless $itid;
$out->("Removing item $itcode from domain $dmcode ($itid)...", "+");
# need to delete everything from: ml_items ml_latest ml_text
$dbh->do("DELETE FROM ml_items WHERE dmid=$dmid AND itid=$itid");
my $txtids = "";
my $sth = $dbh->prepare("SELECT txtid FROM ml_latest WHERE dmid=$dmid AND itid=$itid");
$sth->execute;
while (my $txtid = $sth->fetchrow_array) {
$txtids .= "," if $txtids;
$txtids .= $txtid;
}
$dbh->do("DELETE FROM ml_latest WHERE dmid=$dmid AND itid=$itid");
$dbh->do("DELETE FROM ml_text WHERE dmid=$dmid AND txtid IN ($txtids)");
$out->("-","done.");
}

15252
local/bin/upgrading/uk.dat Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2068
local/cgi-bin/Apache/BML.pm Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
#
# Note: this is a very early version of a CSS cleaner. The plan is to eventually
# make it a white-listing CSS cleaner (deny by default) with a nice
# interface where you can build policy about what's allowed, like
# HTML::Sanitize/::Scrub/etc, but for now this is almost a null cleaner,
# just parsing and reserializing the CSS, removing two trivial ways to
# inject javascript.
#
# The plan now is to integrate this interface into LiveJournal, then improve
# this module over time.
#
# Note2: we tried 4 different CSS parsers for this module to use, and all 4 sucked.
# so for now this module sucks, until we can find a suitable parser. for the
# record, CSS::Tiny, CSS, and CSS::SAC all didn't work. and csstidy wasn't
# incredibly hot either. CSS.pm's grammar was buggy, and CSS::SAC had the
# best interface (SAC) but terrible parsing of selectors. we'll probably
# have to write our own, based on the Mozilla CSS parsing code.
package CSS::Cleaner;
use strict;
use vars qw($VERSION);
$VERSION = '0.01';
sub new {
my $class = shift;
my %opts = @_;
my $self = bless {}, $class;
if (defined( $opts{rule_handler} )) {
my $rule_handler = $opts{rule_handler};
die "rule_handler needs to be a coderef if supplied" unless ref( $rule_handler ) eq 'CODE';
$self->{rule_handler} = $rule_handler;
}
if (defined( $opts{pre_hook} )) {
my $pre_hook = $opts{pre_hook};
die "pre_hook needs to be a coderef if supplied" unless ref( $pre_hook ) eq 'CODE';
$self->{pre_hook} = $pre_hook;
}
return $self;
}
# cleans CSS
sub clean {
my ($self, $target) = @_;
$self->_stupid_clean(\$target);
return $target;
}
# cleans CSS properties, as if it were in a style="" attribute
sub clean_property {
my ($self, $target) = @_;
$self->_stupid_clean(\$target);
return $target;
}
# this is so stupid. see notes at top.
# returns 1 if it was okay, 0 if possibly malicious
sub _stupid_clean {
my ($self, $ref) = @_;
my $reduced = $$ref;
if (defined( $self->{pre_hook} )) {
$self->{pre_hook}->( \$reduced );
}
$reduced =~ s/&\#(\d+);?/chr($1)/eg;
$reduced =~ s/&\#x(\w+);?/chr(hex($1))/eg;
if ($reduced =~ /[\x00-\x08\x0B\x0C\x0E-\x1F]/) {
$$ref = "/* suspect CSS: low bytes */";
return;
}
if ($reduced =~ /[\x7f-\xff]/) {
$$ref = "/* suspect CSS: high bytes */";
return;
}
# returns 1 if something bad was found
my $check_for_bad = sub {
if ($reduced =~ m!<\w!) {
$$ref = "/* suspect CSS: start HTML tag? */";
return 1;
}
my $with_white = $reduced;
$reduced =~ s/[\s\x0b]+//g;
if ($reduced =~ m!\\[a-f0-9]!i) {
$$ref = "/* suspect CSS: backslash hex */";
return;
}
$reduced =~ s/\\//g;
if ($reduced =~ /\@(import|charset)([\s\x0A\x0D]*[^\x0A\x0D]*)/i) {
my $what = $1;
my $value = $2;
if (defined( $self->{rule_handler} )) {
return $self->{rule_handler}->( $ref, $what, $value );
} else {
$$ref = "/* suspect CSS: $what rule */";
return;
}
}
if ($reduced =~ /&\#/) {
$$ref = "/* suspect CSS: found irregular &# */";
return;
}
if ($reduced =~ m!</!) {
$$ref = "/* suspect CSS: close HTML tag */";
return;
}
# returns 1 if bad phrases found
my $check_phrases = sub {
my $str = shift;
if ($$str =~ m/(\bdata:\b|javascript|jscript|livescript|vbscript|expression|eval|cookie
|\bwindow\b|\bparent\b|\bthis\b|behaviou?r|moz-binding)/ix) {
my $what = lc $1;
$$ref = "/* suspect CSS: potential scripting: $what */";
return 1;
}
return 0;
};
return 1 if $check_phrases->(\$reduced);
# restore whitespace
$reduced = $with_white;
$reduced =~ s!/\*.*?\*/!!sg;
$reduced =~ s!\<\!--.*?--\>!!sg;
$reduced =~ s/[\s\x0b]+//g;
$reduced =~ s/\\//g;
return 1 if $check_phrases->(\$reduced);
return 0;
};
# check for bad stuff before/after removing comment lines
return 0 if $check_for_bad->();
$reduced =~ s!//.*!!g;
return 0 if $check_for_bad->();
return 1;
}
1;

1164
local/cgi-bin/Cache/Memcached.pm Executable file

File diff suppressed because it is too large Load Diff

94
local/cgi-bin/Golem.pm Normal file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/perl -w
package Golem;
use strict;
require "$ENV{'LJHOME'}/cgi-bin/Golem/dblib.pl";
require "$ENV{'LJHOME'}/cgi-bin/Golem/loglib.pl";
require "$ENV{'LJHOME'}/cgi-bin/Golem/netlib.pl";
require "$ENV{'LJHOME'}/cgi-bin/Golem/proplib.pl";
require "$ENV{'LJHOME'}/cgi-bin/Golem/textlib.pl";
# *** LJR apis conversion layer
#
# check out
our $on = 1;
our $counter_prefix = "golem_";
sub get_db {
return LJ::get_db_writer();
}
# Golem tags are not ported to LJR
sub unset_row_tag {
return 1;
}
# *** LJR apis conversion layer
sub get_callstack {
my $cstack;
my $i = 0;
while ( 1 ) {
my $tfunc = (caller($i))[3];
if ($tfunc && $tfunc ne "") {
if ($tfunc !~ /\_\_ANON\_\_/ &&
$tfunc !~ /.*::get_callstack/) {
$cstack .= "\t" . $tfunc . "\n";
}
$i = $i + 1;
}
else {
last;
}
}
return "\nCallstack:\n" . $cstack . "\n";
}
sub err {
if (ref($_[0])) {
my $dbh = shift;
$dbh->rollback;
}
my $errstr = shift || "";
my $previous_object;
if (ref($_[0]) eq 'HASH') {
$previous_object = shift;
}
if ($previous_object) {
$previous_object->{'err'} = 1;
$previous_object->{'errstr'} = $errstr . Golem::get_callstack();
return $previous_object;
}
else {
my %res = (
"err" => 1,
"errstr" => $errstr . Golem::get_callstack(),
);
return \%res;
}
}
sub die {
my ($message, $suppress_callstack) = @_;
print STDERR "$message";
unless ($suppress_callstack) {
print STDERR Golem::get_callstack();
}
else {
print "\n";
}
exit 1;
}
1;

View File

@@ -0,0 +1,746 @@
#!/usr/bin/perl -w
#
# Generic database routines
#
package Golem;
use strict;
use Golem;
# courtesy of LiveJournal.org
sub disconnect_dbs {
foreach my $h (($Golem::DB, $Golem::PlanerDB, $Golem::CalendarDB, $Golem::OtrsDB, $Golem::SwerrsDB)) {
if ($h) {
$h->disconnect();
$h = undef;
}
}
print STDERR localtime() . " [$$]: closed db connections\n" if $ENV{'GOLEM_DEBUG'};
}
# build DSN connection string based on database info hashref,
# courtesy of livejournal.org
#
# $DBINFO = {
# 'master' => {
# 'dbname' => "golem_kohts",
# 'host' => "localhost",
# 'port' => 3306,
# 'user' => "root",
# 'pass' => "",
# 'sock' => "",
# 'encoding' => "utf8",
# },
# };
#
sub make_dbh_fdsn {
my ($db) = @_;
my $fdsn = "DBI:mysql";
$fdsn .= ":$db->{'dbname'}";
$fdsn .= ";host=$db->{'host'}" if $db->{'host'};
$fdsn .= ";port=$db->{'port'}" if $db->{'port'};
$fdsn .= ";mysql_socket=$db->{'sock'}" if $db->{'sock'};
$fdsn .= "|$db->{'user'}|$db->{'pass'}";
return $fdsn;
}
# test if connection is still available
# (should check for replication, etc. here)
#
sub connection_bad {
my ($dbh, $try) = @_;
return 1 unless $dbh;
my $ss = eval {
#
# $dbh->selectrow_hashref("SHOW SLAVE STATUS");
#
# on a real slave
#
# $ss = {
# 'Skip_counter' => '0',
# 'Master_Log_File' => 'ararita-bin.882',
# 'Connect_retry' => '60',
# 'Master_Host' => 'ararita.lenin.ru',
# 'Relay_Master_Log_File' => 'ararita-bin.882',
# 'Relay_Log_File' => 'laylah-relay-bin.323',
# 'Slave_IO_Running' => 'Yes',
# 'Slave_SQL_Running' => 'Yes',
# 'Master_Port' => '3306',
# 'Exec_master_log_pos' => '17720151',
# 'Relay_log_space' => '19098333',
# 'Relay_Log_Pos' => '19098333',
# 'Last_errno' => '0',
# 'Last_error' => '',
# 'Replicate_do_db' => 'prod_livejournal,prod_livejournal',
# 'Read_Master_Log_Pos' => '17720151',
# 'Master_User' => 'replication',
# 'Replicate_ignore_db' => ''
# };
$dbh->selectrow_hashref("select name from _dbi");
};
if ($dbh->err && $dbh->err != 1227) {
print STDERR localtime() . " [$$]: " . $dbh->errstr . "\n" if $ENV{'GOLEM_DEBUG'};
return 1;
}
if ($ss && $ss->{'name'} ne '??') {
return 0;
}
elsif ($ss && $ss->{'name'} eq '??') {
print STDERR localtime() . " [$$]: DBI returned garbage: $ss->{'name'}\n" if $ENV{'GOLEM_DEBUG'};
return 1;
}
elsif (!$ss) {
print STDERR localtime() . " [$$]: DBI returned nothing\n" if $ENV{'GOLEM_DEBUG'};
return 1;
}
}
# LJR modification; redefined in cgi-bin/Golem.pmGolem.pm
# so it works correctly with original LJ code
#
sub golem_get_db {
my ($params, $opts) = @_;
$opts = {} unless $opts;
$params = {} unless $params;
if ($Golem::DB) {
if (! connection_bad($Golem::DB)) {
return $Golem::DB;
}
else {
print STDERR localtime() . " [$$]: new connection: was bad\n" if $ENV{'GOLEM_DEBUG'};
$Golem::DB->disconnect;
}
}
else {
print STDERR localtime() . " [$$]: new connection: had none\n" if $ENV{'GOLEM_DEBUG'};
}
undef $Golem::DB;
# DB connection defaults (unless programmer specified them)
#
$params->{'RaiseError'} = 0 unless defined($params->{'RaiseError'});
$params->{'PrintError'} = 1 unless defined($params->{'PrintError'});
$params->{'AutoCommit'} = 1 unless defined($params->{'AutoCommit'});
Golem::die("No Golem::DBINFO master defined")
unless $Golem::DBINFO->{'master'};
my $dbinfo = $Golem::DBINFO->{'master'};
my $fdsn = make_dbh_fdsn($dbinfo);
$Golem::DB = DBI->connect($fdsn, $dbinfo->{'user'}, $dbinfo->{'pass'}, $params);
while (!$Golem::DB && $opts->{'retry_forever'}) {
Golem::do_log("database not available, retrying", {"stderr" => 1});
sleep 1;
$Golem::DB = DBI->connect($fdsn, $dbinfo->{'user'}, $dbinfo->{'pass'}, $params);
}
Golem::die("Unable to connect to database: " . DBI->errstr)
unless $Golem::DB;
$Golem::DB->do("SET NAMES " . $dbinfo->{'encoding'})
if $dbinfo->{'encoding'};
if (connection_bad($Golem::DB)) {
print STDERR "got fresh new bad handle, retrying\n" if $ENV{'GOLEM_DEBUG'};
$Golem::DB = undef;
$Golem::DB = Golem::get_db();
}
$Golem::default_dc_obj = Golem::get_dc($Golem::default_dc);
return $Golem::DB;
}
sub get_planer_db {
my ($params) = @_;
return $Golem::PlanerDB if $Golem::PlanerDB;
$params = {RaiseError => 0, PrintError => 1, AutoCommit => 1}
unless $params;
$Golem::PlanerDB = DBI->connect("DBI:Sybase:server=argo3.yandex.ru;database=planer;",
"helpdesk", "gkfyshfcnfvfys123", $params);
return $Golem::PlanerDB;
}
sub get_calendar_db {
my ($params) = @_;
return $Golem::CalendarDB if $Golem::CalendarDB;
$params = {RaiseError => 0, PrintError =>1, AutoCommit => 1}
unless $params;
$Golem::CalendarDB = DBI->connect("DBI:Sybase:server=argo3.yandex.ru;database=momdb;",
"staffreader", "cegthgfhjkm678", $params);
return $Golem::CalendarDB;
}
sub get_otrs_db {
my ($params) = @_;
return $Golem::OtrsDB if $Golem::OtrsDB;
$params = {RaiseError => 0, PrintError =>1, AutoCommit => 1}
unless $params;
$Golem::OtrsDB = DBI->connect("DBI:mysql:database=otrs_utf8:host=casa.yandex.ru:port=3306",
"userorder", "xuo9Bahf", $params);
return $Golem::OtrsDB;
}
sub get_swerrs_db {
my ($params) = @_;
return $Golem::SwerrsDB if $Golem::SwerrsDB;
$params = { RaiseError => 0, PrintError => 1, AutoCommit => 1 }
unless $params;
$Golem::SwerrsDB = DBI->connect("DBI:mysql:racktables:localhost:3306", "swerrs", "V7Hl}O]Usr", $params);
return $Golem::SwerrsDB;
}
sub sth_bind_array {
my ($sth, $bound_values) = @_;
my $i = 0;
foreach my $b (@{$bound_values}) {
$i++;
Golem::die("error binding params")
unless $sth->bind_param($i, $b) ;
}
}
# courtesy of LiveJournal.org
# see also: http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id
#
sub alloc_global_counter {
my ($tag, $recurse) = @_;
my $dbh = Golem::get_db();
my $newmax;
# in case name `counter` is already occupied
# by some user table
my $counter_prefix = "";
$counter_prefix = $Golem::counter_prefix
if defined($Golem::counter_prefix);
my $rs = $dbh->do("UPDATE ${counter_prefix}counter SET max=LAST_INSERT_ID(max+1) WHERE tag=?", undef, $tag);
if ($rs > 0) {
$newmax = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
return $newmax;
}
return undef if $recurse;
# no prior counter rows - initialize one.
# if this is a table then trying default id column
if ($Golem::SCHEMA_CACHE->{'tables'}->{$tag}) {
$newmax = $dbh->selectrow_array("SELECT MAX(id) FROM `$tag`");
}
else {
Golem::die("alloc_global_counter: unknown tag [$tag], unable to get max value.");
}
$newmax += 0;
$dbh->do("INSERT IGNORE INTO ${counter_prefix}counter (tag, max) VALUES (?,?)",
undef, $tag, $newmax) || return undef;
return Golem::alloc_global_counter($tag, 1);
}
# get schema table definition,
# prepare in-memory table structure
#
sub get_schema_table {
my ($table_name, $opts) = @_;
return $Golem::SCHEMA_CACHE->{'tables'}->{$table_name}
if $Golem::SCHEMA_CACHE->{'tables'}->{$table_name} &&
!$opts->{'force'};
delete($Golem::SCHEMA_CACHE->{'tables'}->{$table_name})
if $Golem::SCHEMA_CACHE->{'tables'}->{$table_name};
$Golem::SCHEMA_CACHE->{'tables'}->{$table_name}->{'fields'} = {};
my $t = $Golem::SCHEMA_CACHE->{'tables'}->{$table_name};
my $dbh = Golem::get_db();
Golem::debug_sql("describe `$table_name`");
my $sth = $dbh->prepare("describe `$table_name`");
$sth->execute();
Golem::die("Error describing table [$table_name]: " . $dbh->errstr)
if $dbh->err;
my $select_all_sql = "";
while (my $r = $sth->fetchrow_hashref) {
my $field_name = $r->{'Field'};
$t->{'fields'}->{$field_name} = $r;
if ($r->{'Type'} =~ /^enum\((.+)\)/o) {
my $enums = $1;
foreach my $etype (split(/,/o, $enums)) {
$etype =~ s/'//go;
$t->{'fields'}->{$field_name}->{'enum'}->{$etype} = 1;
}
}
if ($r->{'Type'} eq 'timestamp') {
$select_all_sql .= "UNIX_TIMESTAMP(`$field_name`) as `$field_name`, ";
}
else {
$select_all_sql .= "`$field_name`, ";
}
if ($r->{'Key'} eq 'PRI') {
$t->{'primary_key'}->{$field_name} = 1;
}
}
chop($select_all_sql);
chop($select_all_sql);
$Golem::SCHEMA_CACHE->{'tables'}->{$table_name}->{'select_all_sql'} = $select_all_sql;
return $Golem::SCHEMA_CACHE->{'tables'}->{$table_name};
}
# function tells whether field is data field or some special field
# like host.id (incremented with alloc_global_counter) or
# like host.last_updated (automatically updated when record is updated)
# maybe we should filter them by name instead of using db structure hints?
sub is_data_field {
my ($table_name, $field_name, $opts) = @_;
$opts = {} unless $opts;
my $table = Golem::get_schema_table($table_name);
my $table_fields = $table->{'fields'};
if ($table_fields->{$field_name}) {
if ($table_fields->{$field_name}->{'Default'} &&
$table_fields->{$field_name}->{'Default'} eq 'CURRENT_TIMESTAMP' &&
!$opts->{'ignore_default_current_timestamp'} ) {
return 0;
}
# if ($table_fields->{$field_name}->{'Key'} &&
# $table_fields->{$field_name}->{'Key'} eq 'PRI') {
#
# return 0;
# }
# we have to distinguish between host.id and host_rackmap.host;
# both are PRIMARY keys, but
# 1) we shouldn't ever update host.id
# 2) we have to update host_rackmap.host with host.id
# when creating record corresponding to host record
if ($field_name eq "id" && !$opts->{'manual_id_management'}) {
return 0;
}
return 1;
}
else {
return 0;
}
}
sub __insert {
my ($table_name, $record_hashref, $opts) = @_;
Golem::die("Severe programmer error: __insert expects table name as first parameter!")
unless $table_name;
Golem::die("Severe programmer error: __insert expects record hashref as second parameter!")
unless ref($record_hashref) eq 'HASH';
$opts = {} unless ref($opts) eq 'HASH';
my $dbh;
if ($opts->{'dbh'}) {
$dbh = $opts->{'dbh'};
}
else {
$dbh = Golem::get_db();
}
$dbh->{'PrintError'} = $opts->{'PrintError'}
if defined($opts->{'PrintError'});
my $sth;
my $table = Golem::get_schema_table($table_name);
my $table_fields = $table->{'fields'};
# continue only if there's data for the table
# or if there's a flag saying we should create
# empty record with defaults
my $have_data_for_the_table = 0;
while (my ($o, $v) = each(%{$record_hashref})) {
if (Golem::is_data_field($table_name, $o, $opts)) {
$have_data_for_the_table = 1;
}
}
unless ($have_data_for_the_table || $opts->{'create_empty_record'}) {
return $dbh;
}
my @record_fields;
my @record_values;
foreach my $o (keys %{$record_hashref}) {
# $record_hashref might contain more fields than present in database.
# we only choose those which are in db
if ($table_fields->{$o} && Golem::is_data_field($table_name, $o, $opts)) {
# enum validation
if ($table_fields->{$o}->{'enum'}) {
Golem::die("Enum [$table_name.$o] value is not specified and doesn't have default value")
if !defined($record_hashref->{$o}) && $table_fields->{$o}->{'Default'} eq '';
Golem::die("Enum [$table_name.$o] can't be [$record_hashref->{$o}]")
if $record_hashref->{$o} && !$table_fields->{$o}->{'enum'}->{$record_hashref->{$o}};
# if they passed empty value for enum
# and there's some default -- silently
# decide to use it
unless ($record_hashref->{$o}) {
delete($record_hashref->{$o});
next;
}
}
push @record_fields, $o;
push @record_values, $record_hashref->{$o};
}
}
if ($table_fields->{"id"} && !$opts->{'manual_id_management'}) {
if ($record_hashref->{"id"}) {
Golem::die("Severe database structure or programmer error: __insert got id [$record_hashref->{'id'}]
when creating record for table [$table_name]; won't overwrite.\n");
}
$record_hashref->{"id"} = Golem::alloc_global_counter($table_name);
# check that id is not taken and
# die with severe error otherwise
#
my $t_id = $dbh->selectrow_array("select id from `$table_name` where id = ?",
undef, $record_hashref->{"id"});
if ($t_id && $t_id eq $record_hashref->{"id"}) {
Golem::die("Severe database error: __insert got [$t_id] for table [$table_name] " .
"from alloc_global_counter which already exists!\n" .
"Probable somebody is populating [$table_name] without Golem::__insert()\n");
}
push @record_fields, "id";
push @record_values, $record_hashref->{"id"};
}
my $sql;
my @bound_values;
$sql = "INSERT INTO `$table_name` ( ";
foreach my $o (@record_fields) {
$sql = $sql . " `$o`,";
}
chop($sql);
$sql .= " ) VALUES ( ";
my $i = 0;
foreach my $o (@record_values) {
# we represent timestamp datatype as unixtime (http://en.wikipedia.org/wiki/Unix_time)
# doing all the conversions almost invisible to the end user
#
# if the value being written is 0 then we're not using FROM_UNIXTIME(value)
# (which generates warnings) just value
#
if ($table_fields->{$record_fields[$i]}->{'Type'} eq 'timestamp' && $o && $o != 0) {
Golem::die("Programmer error: __insert got hashref with invalid data for $table_name.$record_fields[$i] (should be unixtime)")
unless $o =~ /^[0-9]+$/o;
$sql = $sql . "FROM_UNIXTIME(?),";
}
else {
$sql = $sql . "?,";
}
push @bound_values, $o;
$i++;
}
chop($sql);
$sql .= " )";
Golem::debug_sql($sql, \@bound_values);
$sth = $dbh->prepare($sql);
Golem::sth_bind_array($sth, \@bound_values);
$sth->execute();
if ($dbh->err && $dbh->{'PrintError'}) {
Golem::do_log("got error [" . $dbh->err . "] [" . $dbh->errstr . "]" .
" while executing [$sql] with values (" . join(",", @bound_values) . ")",
{'stderr' => 1});
}
return $dbh;
}
sub __update {
my ($table_name, $record_hashref, $opts) = @_;
Golem::die("Severe programmer error: __update expects table name as first parameter!")
unless $table_name;
Golem::die("Severe programmer error: __update expects record hashref as second parameter!")
unless ref($record_hashref) eq 'HASH';
$opts = {} unless ref($opts) eq 'HASH';
my $dbh;
if ($opts->{'dbh'}) {
$dbh = $opts->{'dbh'};
}
else {
$dbh = Golem::get_db();
}
my $sth;
my $table = Golem::get_schema_table($table_name);
my $table_fields = $table->{'fields'};
my $unique_fields_arrayref = [keys %{$table->{'primary_key'}}];
if ($opts->{'unique_fields'}) {
$unique_fields_arrayref = $opts->{'unique_fields'};
}
# continue only if there's data for the table
# in the in-memory hash or if there's a flag
# saying we should create empty record with defaults
#
my $have_data_for_the_table = 0;
while (my ($o, $v) = each(%{$record_hashref})) {
if (Golem::is_data_field($table_name, $o)) {
my $is_unique = 0;
foreach my $u (@{$unique_fields_arrayref}) {
if ($u eq $o) {
$is_unique = 1;
}
}
next if $is_unique;
$have_data_for_the_table = 1;
}
}
unless ($have_data_for_the_table || $opts->{'create_empty_record'}) {
return $dbh;
}
my $sql;
my @bound_values;
$sql = "SELECT " . $table->{'select_all_sql'} . " from `$table_name` WHERE ";
foreach my $f (@{$unique_fields_arrayref}) {
if ($table_fields->{$f}->{'Type'} eq 'timestamp' && $record_hashref->{$f} != 0) {
Golem::die("Programmer error: __update got hashref with invalid data for $table_name.$f (should be unixtime)")
unless $record_hashref->{$f} =~ /^[0-9]+$/o;
$sql .= " `$f` = FROM_UNIXTIME(?) and ";
}
else {
$sql .= " `$f` = ? and ";
}
push @bound_values, $record_hashref->{$f};
}
# remove last "and "
chop($sql);
chop($sql);
chop($sql);
chop($sql);
$sth = $dbh->prepare($sql);
Golem::sth_bind_array($sth, \@bound_values);
$sth->execute();
# create record if it doesn't exist: useful when updating
# records in dependent tables (hosts_resps, hosts_netmap, host_rackmap)
# when master table exists.
unless ($sth->rows) {
if ($opts->{"create_nonexistent"}) {
$dbh = Golem::__insert($table_name, $record_hashref, $opts);
return $dbh;
}
else {
Golem::die("Programmer error: requested to update non-existent record with no create_nonexistent option");
}
}
my $existing_row;
while(my $r = $sth->fetchrow_hashref()) {
Golem::debug_sql($sql, \@bound_values);
Golem::die("more than 1 record fetched with should-be-unique lookup")
if $existing_row;
$existing_row = $r;
}
# check that existing record differs somehow from record to be written
my $records_differ = 0;
while (my ($k, $v) = each %{$existing_row}) {
if (Golem::is_data_field($table_name, $k)) {
# what a mess!
utf8::decode($record_hashref->{$k});
utf8::decode($v);
if (
($record_hashref->{$k} && $v && $v ne $record_hashref->{$k}) ||
(! $record_hashref->{$k} && $v) ||
($record_hashref->{$k} && ! $v)
) {
Golem::debug_sql("in-memory [$table_name] object field [$k] differs: [" .
($record_hashref->{$k} ? $record_hashref->{$k} : "") . "
] -- [" .
($v ? $v : "") .
"]");
$records_differ = 1;
last;
}
}
}
# don't update database if that wouldn't actually
# change any data; we should save A LOT of time here
#
return $dbh unless $records_differ;
@bound_values = ();
$sql = "";
while (my ($o, $v) = each(%{$record_hashref})) {
# $record_hashref might contain more fields than present in database.
# we only choose those which are in db
if ($table_fields->{$o} && Golem::is_data_field($table_name, $o)) {
if ($table_fields->{$o}->{'Type'} eq 'timestamp' && $record_hashref->{$o} && $record_hashref->{$o} != 0) {
Golem::die("Programmer error: __update got hashref with invalid data for $table_name.$o (should be unixtime)")
unless $record_hashref->{$o} =~ /^[0-9]+$/o;
$sql = $sql . " `$o` = FROM_UNIXTIME(?),";
}
else {
$sql = $sql . " `$o` = ?,";
}
push @bound_values, $record_hashref->{$o};
}
}
chop($sql);
$sql = "UPDATE `$table_name` SET " . $sql . " WHERE ";
foreach my $f (@{$unique_fields_arrayref}) {
$sql .= " `$f` = ? and ";
push @bound_values, $record_hashref->{$f};
}
# remove last "and "
chop($sql);
chop($sql);
chop($sql);
chop($sql);
Golem::debug_sql($sql, \@bound_values);
$sth = $dbh->prepare($sql);
Golem::sth_bind_array($sth, \@bound_values);
$sth->execute();
if ($dbh->err) {
Golem::do_log("error executing: $sql; bound values: " . join(",", @bound_values), {"stderr" => 1});
}
return $dbh;
}
sub __delete {
my ($table_name, $record_hashref, $opts) = @_;
Golem::die("Severe programmer error: __delete expects table name as first parameter!")
unless $table_name;
Golem::die("Severe programmer error: __delete expects record hashref as second parameter!")
unless ref($record_hashref) eq 'HASH';
$opts = {} unless ref($opts) eq 'HASH';
my $dbh;
if ($opts->{'dbh'}) {
$dbh = $opts->{'dbh'};
}
else {
$dbh = Golem::get_db();
}
my $sth;
my $table = Golem::get_schema_table($table_name);
my $table_fields = $table->{'fields'};
my $unique_fields_arrayref = [keys %{$table->{'primary_key'}}];
if ($opts->{'unique_fields'}) {
$unique_fields_arrayref = $opts->{'unique_fields'};
}
my @bound_values = ();
my $sql = "DELETE FROM `$table_name` WHERE ";
foreach my $f (@{$unique_fields_arrayref}) {
$sql .= " `$f` = ? and ";
push @bound_values, $record_hashref->{$f};
}
# remove last "and "
chop($sql);
chop($sql);
chop($sql);
chop($sql);
$sth = $dbh->prepare($sql);
Golem::sth_bind_array($sth, \@bound_values);
$sth->execute();
if ($dbh->err) {
Golem::do_log("error executing: $sql; bound values: " . join(",", @bound_values), {"stderr" => 1});
}
return $dbh;
}
1;

View File

@@ -0,0 +1,197 @@
#!/usr/bin/perl -w
#
# Logging related routines
#
package Golem;
use strict;
use Golem;
use Data::Dumper;
sub dumper {
my ($v) = @_;
return Dumper($v);
}
sub debug_sql {
my ($text, $bound_values, $opts) = @_;
if ($Golem::debug_sql) {
$opts = {} unless $opts;
$text = "SQL: [" . ($text ? $text : "") . "]";
if ($bound_values && ref($bound_values) eq 'ARRAY') {
$text .= " bound_values [" . Golem::safe_join(",", @{$bound_values}) . "]";
}
if ($opts->{'stderr'}) {
print STDERR localtime() . " " . $text . "\n";
}
else {
print localtime() . " " . $text . "\n";
}
}
}
sub debug2 {
my ($text, $opts) = @_;
if ($Golem::debug2) {
debug($text, $opts);
}
}
sub debug {
my ($text, $opts) = @_;
$opts = {} unless $opts;
if ($Golem::debug) {
my $stamp = localtime() . ": ";
utf8::encode($text) if utf8::is_utf8($text);
if ($opts->{'stderr'}) {
if (ref($text)) {
print STDERR join("", map { "$stamp$_\n" } Dumper($text));
}
else {
print STDERR $stamp . ($text ? $text : "") . "\n";
}
}
else {
if (ref($text)) {
print join("", map { "$stamp$_\n" } Dumper($text));
}
else {
print $stamp . ($text ? $text : "") . "\n";
}
}
}
}
# safe_open and safe_close are copied
# from ps_farm.pm (should be one library actually)
sub safe_open {
my ($filename, $mode, $timeout) = @_;
$timeout = 30 unless $timeout;
$mode = "open" unless $mode;
if ($mode eq "overwrite" || $mode eq ">") {
$mode = ">";
}
elsif ($mode eq "append" || $mode eq ">>") {
$mode = ">>";
}
else {
$mode = "";
}
my $fh;
my $i=0;
while (! open($fh, "${mode}${filename}")) {
if ($i > $timeout) {
print STDERR "Unable to open $filename\n";
return 0;
}
print STDERR "still trying to open $filename\n";
$i = $i + 1;
sleep 1;
}
while (! flock($fh, 2)) {
if ($i > $timeout) {
print STDERR "Unable to lock $filename\n";
return 0;
}
print STDERR "still trying to lock $filename\n";
$i = $i + 1;
sleep 1;
}
my $fh1;
if (!open($fh1, "${mode}${filename}")) {
$i = $i + 1;
if ($i > $timeout) {
print STDERR "Unable to open and lock $filename\n";
return 0;
}
print STDERR "Locked $filename, but it's gone. Retrying...\n";
return safe_open($filename, $mode, $timeout - 1);
}
else {
close($fh1);
return $fh;
}
}
sub safe_close {
my ($fh) = @_;
return flock($fh, 8) && close($fh);
}
sub do_log {
my ($message, $opts) = @_;
my $module;
my $stderr = 0;
if (ref($opts) eq 'HASH') {
if ($opts->{'module'}) {
$module = "[$opts->{'module'}] ";
}
if ($opts->{'stderr'}) {
$stderr = $opts->{'stderr'};
}
}
else {
$module = $opts;
$module = "[$module] " if $module;
}
$message = "" unless $message;
utf8::encode($message) if utf8::is_utf8($message);
$module = "[" . $0 . "] " unless $module;
my $message_eol = chop($message);
my $message_formatted =
localtime() . " " . $module .
$message .
$message_eol .
($message_eol eq "\n" ? "" : "\n");
if ($stderr) {
print STDERR $message_formatted;
}
elsif ($Golem::debug) {
print $message_formatted;
}
if (defined($Golem::LOG)) {
my $fh = safe_open($Golem::LOG, ">>");
Golem::die ("Unable to open $Golem::LOG\n") unless $fh;
# binmode($fh, ":utf8");
print $fh $message_formatted;
safe_close($fh);
}
else {
print STDERR "No Golem::LOG is configured. Logging to STDERR\n";
print STDERR $message_formatted;
}
}
1;

View File

@@ -0,0 +1,330 @@
#!/usr/bin/perl -w
#
# networking related routines
#
package Golem;
use strict;
use Golem;
sub get_external_ipinfo {
my ($ip) = @_;
return undef unless $ip && $Golem::noc_nets_cgi;
my $ipinfo;
my @net_info;
my $wget_options = "";
$wget_options = "--timeout=${Golem::noc_nets_cgi_timeout}"
if $Golem::noc_nets_cgi_timeout;
open(NOCNET,"wget $wget_options -q -O - ${Golem::noc_nets_cgi}?name=$ip |");
Golem::debug("wget $wget_options -q -O - ${Golem::noc_nets_cgi}?name=$ip |");
my $line;
while($line=<NOCNET>) {
if ($line=~/^<tr align=center>/) {
chomp($line);
$line =~ s/<\/td><td>/\|/g;
$line =~ s/(<td>|<tr align=center>|<\/tr>|<\/td>)//g;
@net_info= split(/\|/,$line,5);
}
}
close(NOCNET);
if (@net_info) {
$ipinfo->{'ip'} = $net_info[0];
$ipinfo->{'subnet'} = $net_info[1];
$ipinfo->{'router'} = $net_info[2];
$ipinfo->{'iface'} = $net_info[3];
$ipinfo->{'vlan'} = $ipinfo->{'iface'};
$ipinfo->{'vlan'} =~ /(\d+)/;
$ipinfo->{'vlan'} = $1;
$ipinfo->{'router_if_addr'} = $net_info[4];
}
return $ipinfo;
}
# --- ghetto code
sub int2maskpart {
my ($i) = @_;
return "0" unless defined($i);
my $j = 0;
my $bits = "";
while ($j < 8) {
if ($i <= $j) {
$bits .= "0";
}
else {
$bits .= "1";
}
$j++;
}
return oct("0b" . $bits);
}
sub mask2netmask {
my ($j) = @_;
my @ip;
my $i;
for ($i = 1; $i <= int($j / 8); $i++) {
push @ip, int2maskpart(8);
}
while ($i < 5) {
push @ip, int2maskpart($j % 8);
$j = 0;
$i++;
}
return join(".", @ip);
}
# convert string representation of ipv4 address into integer
sub ipv4_str2int {
my ($ip_string) = @_;
if ($ip_string =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
return (16777216 * $1) + (65536 * $2) + (256 * $3) + $4;
}
else {
return 0;
}
}
# convert integer representation of ipv4 address into string
sub ipv4_int2str {
my ($ip_int) = @_;
if ($ip_int >= 0 && $ip_int <= 4294967295) {
my $w = ($ip_int / 16777216) % 256;
my $x = ($ip_int / 65536) % 256;
my $y = ($ip_int / 256) % 256;
my $z = $ip_int % 256;
return $w . "." . $x . "." . $y . "." . $z;
}
else {
return 0;
}
}
# /24 -> +255
# /27 -> +31
# /28 -> +15
# /29 -> +7
sub ipv4_mask2offset {
my ($mask) = @_;
$mask ||= 0;
my $offset = 2 ** (32 - $mask);
return $offset - 1;
}
sub get_net {
my ($ip, $mask, $opts) = @_;
Golem::trim(\$ip);
Golem::die("Programmer error: get_net expects net and mask")
unless
($ip && $mask && Golem::is_digital($mask)) ||
($ip =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/);
if ($ip =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/o) {
$ip = ipv4_str2int($1);
$mask = $2;
}
elsif ($ip =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/o) {
$ip = ipv4_str2int($1);
}
my $dbh = Golem::get_db();
my $sth = $dbh->prepare("SELECT * FROM net_v4 WHERE ip = ? and mask = ?");
$sth->execute($ip, $mask);
my $net = $sth->fetchrow_hashref();
$sth->finish();
if ($net->{'id'}) {
return Golem::get_net_by_id($net->{'id'}, $opts);
}
else {
return 0;
}
}
sub get_net_by_id {
my ($id, $opts) = @_;
Golem::die("Programmer error: get_net_by_id expects network id")
unless $id;
$opts = {} unless $opts;
my $dbh = Golem::get_db();
my $sth = $dbh->prepare("SELECT * FROM net_v4 WHERE id = ?");
$sth->execute($id);
my $r = $sth->fetchrow_hashref();
if ($r->{'id'}) {
$r->{'ip_str'} = Golem::ipv4_int2str($r->{'ip'});
$r->{'net_with_mask'} = $r->{'ip_str'} . "/" . $r->{'mask'};
if ($r->{'name'} =~ /VLAN([\d]+)/o) {
$r->{'vlan'} = $1;
}
else {
$r->{'vlan'} = "";
}
if ($opts->{'with_props'} || $opts->{'with_all'}) {
$r = Golem::load_props("net_v4", $r);
}
return $r;
}
else {
return 0;
}
}
sub insert_net {
my ($net) = @_;
Golem::die("Programmer error: insert_net expects net object")
unless $net && ($net->{'ip_str'} || $net->{'ip'}) &&
$net->{'mask'} && $net->{'name'};
if ($net->{'ip_str'}) {
$net->{'ip'} = Golem::ipv4_str2int($net->{'ip_str'});
}
my $enet = Golem::get_net($net->{'ip'}, $net->{'mask'});
return Golem::err("net already exists [$enet->{'ip_str'}/$enet->{'$mask'} ($enet->{'id'})]")
if $enet;
my $dbh = Golem::__insert("net_v4", $net);
return Golem::err($dbh->errstr, $net)
if $dbh->err;
$net->{'id'} = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
return Golem::get_net_by_id($net->{'id'});
}
sub save_net {
my ($net) = @_;
Golem::die("Programmer error: save_net expects net object")
unless $net && $net->{'id'} &&
($net->{'ip_str'} || $net->{'ip'}) &&
$net->{'mask'} && $net->{'name'};
if ($net->{'ip_str'}) {
$net->{'ip'} = Golem::ipv4_str2int($net->{'ip_str'});
}
my $enet = Golem::get_net($net->{'ip'}, $net->{'mask'}, {"with_all" => 1});
if ($enet) {
my $dbh = Golem::__update("net_v4", $net);
return Golem::err($dbh->errstr, $net)
if $dbh->err;
$net = Golem::save_props("net_v4", $net);
}
else {
return Golem::insert_net($net);
}
return $net;
}
sub delete_net {
my ($net) = @_;
Golem::die("Programmer error: delete_net expects net object")
unless $net && $net->{'id'};
my $dbh = Golem::get_db();
Golem::unset_row_tag("net_v4", $net->{'id'});
$dbh->do("DELETE from net_v4prop where net_v4id = ?", undef, $net->{'id'});
$dbh->do("DELETE from net_v4propblob where net_v4id = ?", undef, $net->{'id'});
$dbh->do("DELETE FROM net_v4 WHERE net_v4.id = ?", undef, $net->{'id'});
return Golem::err($dbh->errstr, $net)
if $dbh->err;
return {};
}
sub get_containing_net {
my ($ip, $opts) = @_;
Golem::die("Programmer error: get_containing_net expects ip address")
unless $ip;
Golem::trim(\$ip);
if ($ip =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/o) {
$ip = Golem::ipv4_str2int($1);
}
my $dbh = Golem::get_db();
# choose nearest (by ip desc) and smallest (by mask desc) known network
my $sth = $dbh->prepare("SELECT * FROM net_v4 WHERE ip < ? and mask <> 32
order by ip desc, mask desc");
$sth->execute($ip);
my $net;
while(my $r = $sth->fetchrow_hashref()) {
# choose first network that includes tested ip address if any
if ($r->{'ip'} + ipv4_mask2offset($r->{'mask'}) ge $ip) {
return Golem::get_net_by_id($r->{'id'}, $opts);
}
}
return 0;
}
sub get_net_by_vlan {
my ($vlan, $opts) = @_;
Golem::die("Programmer error: get_net_by_vlan expects vlan name")
unless $vlan;
$vlan =~ s/vlan//go;
$vlan =~ s/\s//go;
my $dbh = Golem::get_db();
my $sth = $dbh->prepare("SELECT * FROM net_v4 WHERE mask <> 32 and name like ? order by name");
$sth->execute("VLAN${vlan}%");
while (my $r = $sth->fetchrow_hashref()) {
if ($r->{'name'} =~ /VLAN${vlan}\s/o) {
return Golem::get_net($r->{'ip'}, $r->{'mask'}, $opts);
}
}
return 0;
}
sub is_ipv4 {
my ($str) = @_;
return $str =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/o;
}
1;

View File

@@ -0,0 +1,393 @@
#!/usr/bin/perl -w
#
# Properties manipulation routines
#
#
# This is an object property library which
# implements DB -> MEMORY (load_props)
# and MEMORY -> DB (save_props) transition
# of object properties
#
# Object is defined as a record in some TABLE
# (called owner). To be able to use properties
# for the given TABLE you should create
# another two tables: TABLEprop and TABLEpropblob
#
# Example for host object:
#
# CREATE TABLE `hostprop` (
# `hostid` int(11) NOT NULL,
# `propid` smallint(6) NOT NULL default '0',
# `propseq` int(11) NOT NULL default '0',
# `value` varchar(1024) default NULL,
# PRIMARY KEY (`hostid`,`propid`,`propseq`),
# KEY `prop` (`propid`)
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
#
# CREATE TABLE `hostpropblob` (
# `hostid` int(11) NOT NULL,
# `propid` smallint(6) NOT NULL default '0',
# `propseq` int(11) NOT NULL default '0',
# `value` blob,
# PRIMARY KEY (`hostid`,`propid`,`propseq`),
# KEY `prop` (`propid`)
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
#
# After that you should create "allowed" properties
# for the owner using `gconsole.pl --create-proplist`
#
# You could see all the defined properties for the owner
# using gconsole.pl --list-proplist OWNER
#
#
# For the owner (tables) which primary key is not simple
# id auto_increment (as in the example above) the following
# TABLEprop and TABLEpropblob structure should be used:
#
package Golem;
use strict;
use Golem;
use Storable;
# check that given property owner is valid
# currently there are two valid property owners: host, user
#
sub check_prop_owner {
my ($owner) = @_;
Golem::die("Programmer error: check_prop_owner got empty prop owner")
unless $owner;
my $props = {
"eventhistory" => 1,
"host" => 1,
"user" => 1,
"net_v4" => 1,
};
Golem::die("Programmer error: not valid property owner [$owner]")
unless defined($props->{$owner});
}
# get property definition record(s) from database
# for the specified owner
#
sub get_proplist {
my ($owner, $hpname) = @_;
Golem::die("Programmer error: get_proplist expects at least owner")
unless $owner;
Golem::check_prop_owner($owner);
my $dbh = Golem::get_db();
my $sth;
my $ret;
if ($hpname) {
$sth = $dbh->prepare("SELECT * FROM proplist WHERE owner = ? and name = ?");
$sth->execute($owner, $hpname);
$ret = $sth->fetchrow_hashref();
$ret = 0 unless $ret->{'id'};
}
else {
$sth = $dbh->prepare("SELECT * FROM proplist where owner = ?");
$sth->execute($owner);
while (my $r = $sth->fetchrow_hashref()) {
$ret->{$r->{'name'}} = $r;
}
}
return $ret;
}
sub create_proplist {
my ($owner, $op) = @_;
Golem::die("Programmer error: create_proplist expects at least owner and property name")
unless $owner && $op && $op->{'name'};
Golem::check_prop_owner($owner);
my $eop = Golem::get_proplist($owner, $op->{'name'});
Golem::die("proplist record already exists [$eop->{'name'} ($eop->{'id'})]")
if $eop;
$op->{'owner'} = $owner;
my $dbh = Golem::__insert("proplist", $op);
Golem::die("Error creating proplist: " . $dbh->errstr, $op)
if $dbh->err;
$op->{'id'} = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
Golem::do_log("new proplist record: [$op->{'owner'}/$op->{'name'}] ($op->{'id'})");
return $op;
}
sub delete_proplist {
my ($owner, $op) = @_;
Golem::die("Programmer error: delete_proplist expects proplist record")
unless $op && $op->{'id'} && $op->{'name'};
Golem::check_prop_owner($owner);
my $eop = Golem::get_proplist($owner, $op->{'name'});
return Golem::err("delete_proplist: invalid proplist record")
unless $eop;
my $dbh = Golem::get_db();
my $objs_with_prop =
$dbh->selectrow_array("select count(*) from ${owner}prop where propid = ?", undef, $op->{'id'}) +
$dbh->selectrow_array("select count(*) from ${owner}propblob where propid = ?", undef, $op->{'id'})
;
return Golem::err("delete_proplist: unable to delete proplist record; $objs_with_prop records are using it")
if $objs_with_prop;
$dbh->do("delete from proplist where id = ?", undef, $op->{'id'});
return Golem::err("error while deleting from proplist: " . $dbh->errstr)
if $dbh->err;
return {};
}
# read object properties for the given owner
# (exactly matches table name)
#
sub load_props {
my ($owner, $o) = @_;
Golem::die("Programmer error: load_props expects owner name and object")
unless $owner && ref($o);
Golem::check_prop_owner($owner);
my $table = Golem::get_schema_table($owner);
my $pk = $table->{'primary_key'};
my $dbh = Golem::get_db();
my $sth;
$o->{'props'}->{'data'} = {};
foreach my $t ("${owner}prop", "${owner}propblob") {
my $sql = "select * from $t inner join proplist on proplist.id = $t.propid WHERE 1 ";
my @bound_values = ();
while (my ($pk_field, $dummy) = each %{$table->{'primary_key'}}) {
if ($pk_field eq 'id') {
$sql .= " and `${owner}id` = ? ";
}
else {
$sql .= " and `$pk_field` = ? ";
}
Golem::die("Programmer error: load_props got empty value for primary key field [$pk_field] of table [$owner]")
unless defined($o->{$pk_field});
push (@bound_values, $o->{$pk_field});
}
$sth = $dbh->prepare($sql);
Golem::sth_bind_array($sth, \@bound_values);
$sth->execute();
while (my $r = $sth->fetchrow_hashref()) {
my $v;
if ($t eq "${owner}prop") {
$v = $r->{'value'};
}
if ($t eq "${owner}propblob") {
# print STDERR Storable::thaw($r->{'value'});
$v = Storable::thaw($r->{'value'});
}
if ($r->{'datatype'} eq 'array' || $r->{'datatype'} eq 'arrayblob') {
$o->{'props'}->{'data'}->{$r->{'name'}} = []
unless defined($o->{'props'}->{'data'}->{$r->{'name'}});
push (@{$o->{'props'}->{'data'}->{$r->{'name'}}}, $v);
}
else {
$o->{'props'}->{'data'}->{$r->{'name'}} = $v;
}
}
}
$o->{'props'}->{'loaded'} = 1;
return $o;
}
# save properties from memory into database
# checks that properties were loaded using load_props
# (for advanced users: "loaded" is the key of check,
# it is set by load_props which could be emulated)
#
sub save_props {
my ($owner, $o) = @_;
Golem::die("Programmer error: save_props expects owner name and object")
unless $owner && ref($o);
Golem::check_prop_owner($owner);
return Golem::err("Programmer error: save_props should be called only after calling load_props")
unless $o->{'props'}->{'loaded'};
my $table = Golem::get_schema_table($owner);
my $pk = $table->{'primary_key'};
my $dbh = Golem::get_db();
while (my ($k, $v) = each %{$o->{'props'}->{'data'}}) {
my $op = Golem::get_proplist($owner, $k);
unless ($op) {
Golem::do_log("Non-existent $owner property name [$k], skipping.", {"stderr" => 1});
next;
}
my $do_save = sub {
my ($value, $seq) = @_;
$seq = 0 unless defined($seq);
my $tpref = "";
my $db_value;
if ($op->{'datatype'} eq 'blob' || $op->{'datatype'} eq 'arrayblob') {
$db_value = Storable::nfreeze($value);
$tpref = "blob";
}
else {
$db_value = $value;
if ($op->{'datatype'} eq 'bool') {
$db_value = $value ? 1 : 0;
}
}
my $prop_ref = {
"propid" => $op->{'id'},
"propseq" => $seq,
"value" => $db_value,
};
while (my ($pk_field, $dummy) = each %{$table->{'primary_key'}}) {
if ($pk_field eq 'id') {
$prop_ref->{"${owner}id"} = $o->{$pk_field};
}
else {
$prop_ref->{$pk_field} = $o->{$pk_field};
}
}
$dbh = Golem::__update("${owner}prop${tpref}", $prop_ref, {"create_nonexistent" => 1});
Golem::die("save_props: error while replacing ${owner} props: " . $dbh->errstr)
if $dbh->err;
};
if ($op->{'datatype'} eq 'array' || $op->{'datatype'} eq 'arrayblob') {
my $i = 0;
foreach my $array_value (@{$v}) {
$do_save->($array_value, $i);
$i = $i + 1;
}
my $tpref = "";
if ($op->{'datatype'} eq 'arrayblob') {
$tpref = "blob";
}
my $sql = "delete from ${owner}prop${tpref} where 1 ";
my @bound_values = ();
while (my ($pk_field, $dummy) = each %{$table->{'primary_key'}}) {
if ($pk_field eq 'id') {
$sql .= " and ${owner}id = ? ";
}
else {
$sql .= " and $pk_field = ? ";
}
push (@bound_values, $o->{$pk_field});
}
$sql .= " and propid = ? and propseq >= ?";
push (@bound_values, $op->{'id'});
push (@bound_values, $i);
my $sth = $dbh->prepare($sql);
Golem::sth_bind_array($sth, \@bound_values);
$sth->execute();
}
else {
$do_save->($v, 0);
}
}
return $o;
}
# deletes object property if no objects
# are associated with the property
#
# input
# owner type (for the listing see check_prop_owner)
# object (with $obj->{'id'} defined)
# property name to delete
#
sub delete_prop {
my ($owner, $obj, $propname) = @_;
Golem::die("Programmer error: delete_prop expects owner and object")
unless $owner && ref($obj);
Golem::check_prop_owner($owner);
my $op = Golem::get_proplist($owner, $propname);
return Golem::err("delete_prop: invalid propname [$propname]")
unless $op;
my $table = Golem::get_schema_table($owner);
my $pk = $table->{'primary_key'};
my $dbh = Golem::get_db();
my $tpref = "";
if ($op->{'datatype'} eq 'blob' || $op->{'datatype'} eq 'blobarray') {
$tpref = "blob";
}
my $sql = "delete from ${owner}prop${tpref} where 1 ";
my @bound_values = ();
while (my ($pk_field, $dummy) = each %{$table->{'primary_key'}}) {
if ($pk_field eq 'id') {
$sql .= " and ${owner}id = ? ";
}
else {
$sql .= " and $pk_field = ? ";
}
push (@bound_values, $obj->{$pk_field});
}
$sql .= " and propid = ? ";
push (@bound_values, $op->{'id'});
my $sth = $dbh->prepare($sql);
Golem::sth_bind_array($sth, \@bound_values);
$sth->execute();
Golem::die("delete_prop: error deleting $owner [$obj->{'id'}] property [$propname]: " . $dbh->errstr)
if $dbh->err;
return {};
}
1;

View File

@@ -0,0 +1,222 @@
#!/usr/bin/perl -w
#
# Text manipulation routines
#
# parts courtesy of LiveJournal.org
#
package Golem;
use strict;
use Golem;
# <LJFUNC>
# name: LJ::decode_url_string
# class: web
# des: Parse URL-style arg/value pairs into a hash.
# args: buffer, hashref
# des-buffer: Scalar or scalarref of buffer to parse.
# des-hashref: Hashref to populate.
# returns: boolean; true.
# </LJFUNC>
sub decode_url_string
{
my $a = shift;
my $buffer = ref $a ? $a : \$a;
my $hashref = shift; # output hash
my $keyref = shift; # array of keys as they were found
my $pair;
my @pairs = split(/&/, $$buffer);
@$keyref = @pairs;
my ($name, $value);
foreach $pair (@pairs)
{
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$name =~ tr/+/ /;
$name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$hashref->{$name} .= $hashref->{$name} ? ",$value" : $value;
}
return 1;
}
# <LJFUNC>
# name: LJ::ehtml
# class: text
# des: Escapes a value before it can be put in HTML.
# args: string
# des-string: string to be escaped
# returns: string escaped.
# </LJFUNC>
sub ehtml
{
# fast path for the commmon case:
return $_[0] unless $_[0] =~ /[&\"\'<>]/o;
# this is faster than doing one substitution with a map:
my $a = $_[0];
$a =~ s/\&/&amp;/g;
$a =~ s/\"/&quot;/g;
$a =~ s/\'/&\#39;/g;
$a =~ s/</&lt;/g;
$a =~ s/>/&gt;/g;
return $a;
}
sub esql {
return $_[0] unless $_[0] =~ /[\'\"\\]/o;
my $a = $_[0];
$a =~ s/\'//go;
$a =~ s/\"//go;
$a =~ s/\\//go;
return $a;
}
# <LJFUNC>
# name: LJ::is_ascii
# des: checks if text is pure ASCII.
# args: text
# des-text: text to check for being pure 7-bit ASCII text.
# returns: 1 if text is indeed pure 7-bit, 0 otherwise.
# </LJFUNC>
sub is_ascii {
my $text = shift;
return ($text !~ m/[^\x01-\x7f]/o);
}
sub is_digital {
my $text = shift;
return ( $text =~ /\d+/o );
}
# tests if there's data in configuration file string:
# i.e. if it's not empty and is not commented
#
sub have_data {
my ($line) = @_;
if ($line =~ /^\s*#/o || $line =~ /^[\s]*$/o) {
return 0;
}
return 1;
}
# fixes user input strings (passed as array of references),
# currently only trims
#
# used when importing:
# - 1C data
#
sub fix_input {
my (@i) = @_;
foreach my $v (@i) {
Golem::die("Programmer error: check_input expects only scalar references")
unless ref($v) eq 'SCALAR';
Golem::do_log("Inaccurate spacing trimmed [$$v]", {"stderr" => 1})
if Golem::trim($v);
}
}
# given scalar string trims white space
# at the beginning and in the end and
# returns trimmed string
#
# given scalar reference trims white space
# at the beginning and in the end and
# returns true if trimming occured and false otherwise
# NOTE: modifies the original string
# reference to which was given as input parameter
sub trim {
my ($string) = @_;
if (ref($string) eq 'SCALAR') {
my $tstr = $$string;
return 0 if $tstr eq ''; # nothing to trim, do not waste cpu cycles
$tstr =~ s/^\s+//so;
$tstr =~ s/\s+$//so;
if ($tstr ne $$string) {
$$string = $tstr;
return 1;
}
else {
return 0;
}
}
else {
return "" if $string eq ''; # nothing to trim, do not waste cpu cycles
$string =~ s/^\s+//so;
$string =~ s/\s+$//so;
return $string;
}
}
# same as standard perl join except for it doesn't
# output "uninitialized value in join" when joining
# list with undef values; and we use those lists
# when binding params to DBI query
#
sub safe_join {
my ($delimiter, @arr) = @_;
my $joined_text = "";
$delimiter = "" unless $delimiter;
foreach my $bv (@arr) {
$joined_text .= ($bv ? $bv : "") . $delimiter;
}
my $i;
for ($i = 0; $i < length($delimiter); $i++) {
chop($joined_text);
}
return $joined_text;
}
# should be used when you need to concatenate string
# which might be undefined and you want empty string ("")
# instead of perl warnings about uninitialized values
#
sub safe_string {
my ($str) = @_;
if ($str) {
return $str;
}
else {
return "";
}
}
# inserts $symbol every $range characters in a $line
sub div_line {
my ($line, $range, $symbol) = @_;
Golem::die("Programmer error: div_line expects at least string")
unless $line;
$range = 70 unless $range;
$symbol = ' ' unless $symbol;
my $result = '';
for (my $i = 0 ; $i <= int(length($line)/$range) ; $i++) {
$result .= substr($line,$i*$range,$range) . $symbol;
}
chop($result);
return $result;
}
1;

View File

@@ -0,0 +1,207 @@
#!/usr/bin/perl
#
package HTMLCleaner;
use strict;
use base 'HTML::Parser';
use CSS::Cleaner;
sub new {
my ($class, %opts) = @_;
my $p = new HTML::Parser('api_version' => 3);
$p->handler('start' => \&start, 'self, tagname, attr, attrseq, text' );
$p->handler('end' => \&end, 'self, tagname' );
$p->handler('text' => \&text, 'self, text' );
$p->handler('declaration' => \&decl, 'self, tokens' );
$p->{'output'} = $opts{'output'} || sub {};
$p->{'cleaner'} = CSS::Cleaner->new;
$p->{'valid_stylesheet'} = $opts{'valid_stylesheet'} || sub { 1 };
$p->{'allow_password_input'} = $opts{'allow_password_input'} || 0;
bless $p, $class;
}
my %bad_attr = (map { $_ => 1 }
qw(datasrc datafld));
my %eat_tag = (map { $_ => 1 }
qw(script iframe object applet embed param));
my @eating; # push tagname whenever we start eating a tag
sub start {
my ($self, $tagname, $attr, $seq, $text) = @_;
$tagname =~ s/<//;
my $slashclose = 0; # xml-style
if ($tagname =~ s!/(.*)!!) {
if (length($1)) { push @eating, "$tagname/$1"; } # basically halt parsing
else { $slashclose = 1; }
}
my @allowed_tags = ('lj-embed');
push @eating, $tagname if
$eat_tag{$tagname} && ! grep { lc $tagname eq $_ } @allowed_tags;
return if @eating;
my $clean_res = eval {
my $cleantag = $tagname;
$cleantag =~ s/^.*://s;
$cleantag =~ s/[^\w]//g;
no strict 'subs';
my $meth = "CLEAN_$cleantag";
my $code = $self->can($meth)
or return 1; # don't clean, if no element-specific cleaner method
return $code->($self, $seq, $attr);
};
return if !$@ && !$clean_res;
my $ret = "<$tagname";
foreach (@$seq) {
if ($_ eq "/") { $slashclose = 1; next; }
next if $bad_attr{lc($_)};
next if /^on/i;
next if /(?:^=)|[\x0b\x0d]/;
if ($_ eq "style") {
$attr->{$_} = $self->{cleaner}->clean_property($attr->{$_});
}
if ($tagname eq 'input' && $_ eq 'type' && $attr->{'type'} =~ /^password$/i && !$self->{'allow_password_input'}) {
delete $attr->{'type'};
}
my $nospace = $attr->{$_};
$nospace =~ s/[\s\0]//g;
# IE is brain-dead and lets javascript:, vbscript:, and about: have spaces mixed in
if ($nospace =~ /(?:(?:(?:vb|java)script)|about):/i) {
delete $attr->{$_};
}
$ret .= " $_=\"" . ehtml($attr->{$_}) . "\"";
}
$ret .= " /" if $slashclose;
$ret .= ">";
if ($tagname eq "style") {
$self->{'_eating_style'} = 1;
$self->{'_style_contents'} = "";
}
$self->{'output'}->($ret);
}
sub CLEAN_meta {
my ($self, $seq, $attr) = @_;
# don't allow refresh because it can refresh to javascript URLs
# don't allow content-type because they can set charset to utf-7
# why do we even allow meta tags?
my $equiv = lc $attr->{"http-equiv"};
if ($equiv) {
$equiv =~ s/[\s\x0b]//;
return 0 if $equiv =~ /refresh|content-type|link|set-cookie/;
}
return 1;
}
sub CLEAN_link {
my ($self, $seq, $attr) = @_;
if ($attr->{rel} =~ /\bstylesheet\b/i) {
my $href = $attr->{href};
return 0 unless $href =~ m!^https?://([^/]+?)(/.*)$!;
my ($host, $path) = ($1, $2);
my $rv = $self->{'valid_stylesheet'}->($href, $host, $path);
if ($rv == 1) {
return 1;
}
if ($rv) {
$attr->{href} = $rv;
return 1;
}
return 0;
}
# Allow blank <link> tags through so RSS S2 styles can work again without the 'rel="alternate"' hack
return 1 if (keys( %$attr ) == 0);
return 1 if $attr->{rel} =~ /^(?:service|openid)\.\w+$/;
my %okay = map { $_ => 1 } (qw(icon shortcut alternate next prev index made start search top help up author edituri file-list previous home contents bookmark chapter section subsection appendix glossary copyright child));
return 1 if $okay{lc($attr->{rel})};
# Allow link tags with only an href tag. This is an implied rel="alternate"
return 1 if (exists( $attr->{href} ) and (keys( %$attr ) == 1));
# Allow combinations of rel attributes through as long as all of them are valid, most notably "shortcut icon"
return 1 unless grep { !$okay{$_} } split( /\s+/, $attr->{rel} );
# unknown link tag
return 0;
}
sub end {
my ($self, $tagname) = @_;
if (@eating) {
pop @eating if $eating[-1] eq $tagname;
return;
}
if ($self->{'_eating_style'}) {
$self->{'_eating_style'} = 0;
$self->{'output'}->($self->{cleaner}->clean($self->{'_style_contents'}));
}
$self->{'output'}->("</$tagname>");
}
sub text {
my ($self, $text) = @_;
return if @eating;
if ($self->{'_eating_style'}) {
$self->{'_style_contents'} .= $text;
return;
}
# this string is magic [hack]. (See $out_straight in
# cgi-bin/LJ/S2.pm) callers can print "<!-- -->" to HTML::Parser
# just to make it flush, since HTML::Parser has no
# ->flush_outstanding text tag.
return if $text eq "<!-- -->";
# the parser gives us back text whenever it's confused
# on really broken input. sadly, IE parses really broken
# input, so let's escape anything going out this way.
$self->{'output'}->(eangles($text));
}
sub decl {
my ($self, $tokens) = @_;
$self->{'output'}->("<!" . join(" ", map { eangles($_) } @$tokens) . ">");
}
sub eangles {
my $a = shift;
$a =~ s/</&lt;/g;
$a =~ s/>/&gt;/g;
return $a;
}
sub ehtml {
my $a = shift;
$a =~ s/\&/&amp;/g;
$a =~ s/\"/&quot;/g;
$a =~ s/\'/&\#39;/g;
$a =~ s/</&lt;/g;
$a =~ s/>/&gt;/g;
return $a;
}
1;

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

1458
local/cgi-bin/LJR/Distributed.pm Executable file

File diff suppressed because it is too large Load Diff

51
local/cgi-bin/LJR/GD.pm Normal file
View File

@@ -0,0 +1,51 @@
use strict;
use GD::Simple;
package LJR::GD;
sub generate_number {
my ($num, $fontname, $fontcolor, $stuff) = @_;
$num =~ s/^(\ +)//g;
$num =~ s/(\ +)$//g;
my $font;
if ($fontname eq "gdTinyFont") {
$font = GD::Font->Tiny();
}
elsif ($fontname eq "gdSmallFont") {
$font = GD::Font->Small();
}
elsif ($fontname eq "gdLargeFont") {
$font = GD::Font->Large();
}
elsif ($fontname eq "gdMediumBoldFont") {
$font = GD::Font->MediumBold();
}
elsif ($fontname eq "gdGiantFont") {
$font = GD::Font->Giant();
}
else {
$font = GD::Font->Small();
}
my $cell_width = $font->width;
my $cell_height = $font->height;
my $cols = length($stuff) > length($num) ? length($stuff) : length($num);
my $width = int($cols * $cell_width + $cell_width / 3);
my $height = $cell_height + 1;
my $img = GD::Simple->new($width,$height);
$img->font($font);
$img->moveTo(1,$font->height + 1);
$img->transparent("white");
$img->bgcolor("white");
$img->fgcolor($fontcolor);
my $str = (length($num) < length($stuff) ?
substr($stuff, 0, length($stuff) - length($num)) :
"") . $num;
$img->string($str);
return $img;
}
return 1;

234
local/cgi-bin/LJR/Gate.pm Normal file
View File

@@ -0,0 +1,234 @@
use strict;
use XMLRPC::Lite;
use Digest::MD5;
use Time::Local;
use LJR::Distributed;
use LJR::xmlrpc;
use LJR::Viewuserstandalone;
require "$ENV{'LJHOME'}/cgi-bin/ljpoll.pl";
package LJR::Gate;
$LJR::Gate::clientver = 'LJR::Gate/0.02';
sub Authenticate {
my ($server, $user, $pass) = @_;
my $xmlrpc = new XMLRPC::Lite;
$xmlrpc->proxy("http://" . $server . "/interface/xmlrpc", timeout => 60);
my $xmlrpc_ret = LJR::xmlrpc::xmlrpc_call($xmlrpc, "LJ.XMLRPC.getchallenge");
return $xmlrpc_ret if $xmlrpc_ret->{"err_text"};
my $challenge = $xmlrpc_ret->{'result'}->{'challenge'};
my $response = Digest::MD5::md5_hex($challenge . Digest::MD5::md5_hex($pass));
my $xmlrpc_req = {
'username' => $user,
'auth_method' => 'challenge',
'auth_challenge' => $challenge,
'auth_response' => $response,
'ver' => 1,
'clientver' => $LJR::Gate::clientver,
};
$xmlrpc_ret = LJR::xmlrpc::xmlrpc_call($xmlrpc, "LJ.XMLRPC.login", $xmlrpc_req);
return $xmlrpc_ret if $xmlrpc_ret->{"err_text"};
return $xmlrpc;
}
sub ExportEntry {
my ($u, $req, $security, $jitemid, $anum) = @_;
return "User [" . $u->{'user'} . "] is not gated." unless LJR::Distributed::is_gated_local($u->{'user'});
my $dbr = LJ::get_db_reader();
return "Can't get database reader!" unless $dbr;
my $r;
$r = $dbr->selectrow_hashref (
"SELECT * FROM ljr_export_settings WHERE user=?",
undef, $u->{'user'});
my $ru;
$ru = LJR::Distributed::get_cached_user({ 'ru_id' => $r->{'ru_id'}});
$ru = LJR::Distributed::get_remote_server_byid($ru);
my $xmlrpc = new XMLRPC::Lite;
$xmlrpc->proxy($ru->{'servername'} . "/interface/xmlrpc", timeout => 60);
my $xmlrpc_ret;
my $xmlrpc_req;
my $challenge;
my $response;
my $real_event;
my $real_subject;
my $last_status;
if ($req->{'event'} !~ /\S/) {
$last_status = "removed entry.";
$real_event = $req->{'event'};
$real_subject = $req->{'subject'};
}
else {
my $item_url = LJ::item_link($u, $jitemid, $anum);
$last_status = "exported <a href=$item_url>entry</a>";
$real_event = LJR::Viewuserstandalone::expand_ljuser_tags($req->{'event'});
$real_subject = LJR::Viewuserstandalone::expand_ljuser_tags($req->{'subject'});
my $i=0;
while ($real_event =~ /lj-cut/ig) { $i++ };
while ($real_event =~ /\/lj-cut/ig) { $i-- };
if ($i gt 0) {
$real_event .= "</lj-cut>";
}
LJ::Poll::replace_polls_with_links(\$real_event);
LJ::EmbedModule->expand_entry($u, \$real_event, ('content_only' => 1));
unless ($req->{'props'}->{'opt_nocomments'}) {
LJR::Distributed::sign_exported_gate_entry($u, $jitemid, $anum, \$real_event);
}
}
$security = $req->{'sequrity'} if !$security && $req->{'security'};
$security = "public" unless $security;
$xmlrpc_req = {
'username' => $ru->{'username'},
'auth_method' => 'challenge',
'ver' => 1,
'clientver' => $LJR::Gate::clientver,
'subject' => $real_subject,
'event' => $real_event,
'year' => $req->{'year'},
'mon' => $req->{'mon'},
'day' => $req->{'day'},
'hour' => $req->{'hour'},
'min' => $req->{'min'},
'security' => $security,
'allowmask' => $req->{'allowmask'},
'props' => {
'current_moodid' => $req->{'props'}->{'current_moodid'},
'current_mood' => $req->{'props'}->{'current_mood'},
'current_music' => $req->{'props'}->{'current_music'},
'picture_keyword' => $req->{'props'}->{'picture_keyword'},
'taglist' => $req->{'props'}->{'taglist'},
'opt_backdated' => $req->{'props'}->{'opt_backdated'},
'opt_preformatted' => $req->{'props'}->{'opt_preformatted'},
'opt_nocomments' => 1,
},
};
my $is_invalid_remote_journal = sub {
my ($error_message) = @_;
if (
$error_message =~ /Invalid password/ ||
$error_message =~ /Selected journal no longer exists/ ||
$error_message =~ /account is suspended/ ||
$error_message =~ /Invalid username/
) {
return 1;
}
return 0;
};
my $is_invalid_remote_entry = sub {
my ($error_message) = @_;
if ($error_message =~ /Can\'t edit post from requested journal/) {
return 1;
}
return 0;
};
my $post_new_event = sub {
$xmlrpc_ret = LJR::xmlrpc::xmlrpc_call($xmlrpc, "LJ.XMLRPC.getchallenge");
return $xmlrpc_ret->{"err_text"} if $xmlrpc_ret->{"err_text"};
$challenge = $xmlrpc_ret->{'result'}->{'challenge'};
$response = Digest::MD5::md5_hex($challenge . Digest::MD5::md5_hex($r->{'remote_password'}));
$xmlrpc_req->{'auth_challenge'} = $challenge;
$xmlrpc_req->{'auth_response'} = $response;
my $item_time = Time::Local::timelocal(0, $req->{'min'}, $req->{'hour'},
$req->{'day'}, $req->{'mon'} - 1, $req->{'year'});
if ((time - $item_time) > 60*60*24) {
$xmlrpc_req->{'props'}->{'opt_backdated'} = 1;
}
$xmlrpc_ret = LJR::xmlrpc::xmlrpc_call($xmlrpc, "LJ.XMLRPC.postevent", $xmlrpc_req);
if ($xmlrpc_ret->{'err_text'}) {
if ($is_invalid_remote_journal->($xmlrpc_ret->{'err_text'})) {
$r = LJR::Distributed::update_export_status($u->{'user'}, 0, "ERROR: " . $xmlrpc_ret->{'err_text'});
}
else {
$r = LJR::Distributed::update_export_status($u->{'user'}, 1, "ERROR: " . $xmlrpc_ret->{'err_text'});
}
return $xmlrpc_ret->{"err_text"} . " " . ($r->{'err'} ? $r->{'errtext'} : "");
}
my $rhtml_id = $xmlrpc_ret->{'result'}->{'itemid'} * 256 +
$xmlrpc_ret->{'result'}->{'anum'};
$r = LJR::Distributed::store_remote_itemid(
$u,
$jitemid,
$ru->{'ru_id'},
$xmlrpc_ret->{'result'}->{'itemid'},
$rhtml_id,
"E"
);
return
"store_remote_itemid: " . $u->{'user'} . "," .
$jitemid . "," . $ru->{'ru_id'} . "," .
$xmlrpc_ret->{'result'}->{'itemid'} . "," . $rhtml_id . ": " .
$r->{"errtext"} if $r->{"err"};
};
my $ritem = LJR::Distributed::get_remote_itemid($u->{'userid'}, $jitemid, "E");
if ($ritem && ($req->{'props'}->{'revnum'} || $req->{'event'} !~ /\S/)) {
$xmlrpc_ret = LJR::xmlrpc::xmlrpc_call($xmlrpc, "LJ.XMLRPC.getchallenge");
return $xmlrpc_ret->{"err_text"} if $xmlrpc_ret->{"err_text"};
$challenge = $xmlrpc_ret->{'result'}->{'challenge'};
$response = Digest::MD5::md5_hex($challenge . Digest::MD5::md5_hex($r->{'remote_password'}));
$xmlrpc_req->{'auth_challenge'} = $challenge;
$xmlrpc_req->{'auth_response'} = $response;
$xmlrpc_req->{'itemid'} = $ritem->{'ritemid'};
$xmlrpc_ret = LJR::xmlrpc::xmlrpc_call($xmlrpc, "LJ.XMLRPC.editevent", $xmlrpc_req);
if ($xmlrpc_ret->{'err_text'}) {
if ($is_invalid_remote_entry->($xmlrpc_ret->{'err_text'})) {
LJR::Distributed::remove_remote_itemid($u, $jitemid, $ru->{'ru_id'}, $ritem->{'ritemid'}, "E");
my $errmsg = $post_new_event->();
return $errmsg if $errmsg;
}
elsif ($is_invalid_remote_journal->($xmlrpc_ret->{'err_text'})) {
$r = LJR::Distributed::update_export_status($u->{'user'}, 0, "ERROR: " . $xmlrpc_ret->{'err_text'});
return $xmlrpc_ret->{"err_text"} . " " . ($r->{'err'} ? $r->{'errtext'} : "");
}
$r = LJR::Distributed::update_export_status($u->{'user'}, 1, "ERROR: " . $xmlrpc_ret->{'err_text'});
return $xmlrpc_ret->{"err_text"} . " " . ($r->{'err'} ? $r->{'errtext'} : "");
}
if ($req->{'event'} !~ /\S/) {
LJR::Distributed::remove_remote_itemid($u, $jitemid, $ru->{'ru_id'}, $ritem->{'ritemid'}, "E");
}
}
else {
my $errmsg = $post_new_event->();
return $errmsg if $errmsg;
}
$r = LJR::Distributed::update_export_status($u->{'user'}, 1, "OK: $last_status");
return $r->{'errtext'} if $r->{'err'};
return;
}

241
local/cgi-bin/LJR/Viewuser.pm Executable file
View File

@@ -0,0 +1,241 @@
package LJR::Viewuser;
use strict;
use Carp;
use lib "$ENV{'LJHOME'}/cgi-bin";
use DBI;
use DBI::Role;
use DBIx::StateKeeper;
# A function to canonicalize sitename: take one of the possible
# abbreviations for a given known site, and returns the siteid
# from the list. Otherwise, assume that abbreivation is actually the
# full URL, and return it "as is", without the possible leading http://.
#
# We check the known servers database for "site=servername" or "site
# contains serverurl without the leading www"; make additional
# explicit matchings if necessary (presently none are necessary), et
# voila.
#
sub canonical_sitenum {
my ($site)=@_;
# Cut away leading http://
$site =~ s|http://(.*)|$1|;
my $dbh = LJ::get_db_reader();
my $sth = $dbh->prepare(
"SELECT serverid FROM alienservers WHERE servername=?"
);
$sth->execute($site);
return LJ::error($dbh) if $dbh->err;
#
# Match $site=servername (e.g. "LJ")
#
if ($sth->rows) {
my ($guu) = $sth->fetchrow_array;
return $guu;
}
$sth->finish;
$sth = $dbh->prepare(
"SELECT serverid, REPLACE(serverurl, 'www.', '') FROM alienservers"
);
$sth->execute;
return LJ::error($dbh) if $dbh->err;
#
# Scan all known servers and match "serverurl without www is
# contained in $site"
#
while (my ($hale, $guu) = $sth->fetchrow_array) {
if (index ($site, $guu) !=-1) {
return $hale;
}
}
if ( (lc($site) eq "ljr") || ($site =~ m/.*${LJ::DOMAIN}.*/) )
#
# 0 means ourselves
#
{return 0;}
# elsif ( ($site eq "LJ") || ($site =~ m/.*livejournal\.com.*/) )
# {return 1;}
# elsif ( ($site eq "GJ") || ($site =~ m/.*greatestjournal\.com.*/) )
# {return 2;}
# elsif ( ($site eq "NPJ") || ($site =~ m/.*npj\.ru.*/) )
# {return 3;}
else {return $site};
}
#
# Provides a representation of a user.
#
# Format: we receive a username and a site, where site is either a
# number or a string. If a non-zero number, this is a known site; we take
# information about it from the alianservers table in the db. If
# zero, site is ourselves. If a string, we do not know anything about
# the site and treat it as an OpenID guest; we assume site is the URL.
#
# We return the HTML code.
#
# <lj user="username" site="sitename"> should be expand to
# ljuser( $username, {'site'=> canonical_sitenum($sitename),
# 'type'=>'P'} )
#
# For lj comm, replace 'P' with 'C'
#
sub ljuser {
# we assume $opts->{'site'} to be a siteid of a known site or a full
# URL of a site we do not have in db
my $user = shift;
my $opts = shift;
my $u;
my $native=0;
my $known=0;
my $name="";
my $url;
my $uicon;
my $cicon;
my $commdir;
my $udir;
my $lj_type;
# If site is not given, assume native (siteid=0)
unless ($opts->{'site'}) {$opts->{'site'}=0;}
# Check if site is a number
if($opts->{'site'} =~ m/(\d+)/)
{ $known=1; }
if($known) {
# Site a number (known site)
$opts->{'site'} = $opts->{'site'}+0;
# now we've got default - $LJ::DOMAIN
if ($opts->{'site'}==0){
# local
$url=$LJ::DOMAIN;
$cicon='community.gif'; # default local commicon
$uicon='userinfo.gif'; # default local usericon
$commdir='community/';
$udir='users/';
$lj_type='Y';
$native=1;
} else {
# alien but known --
# go to db to get $name
my $dbh = LJ::get_db_writer();
my $sth = $dbh->prepare("SELECT serverurl, servername, udir, uicon, cdir, cicon, ljtype FROM alienservers WHERE serverid=?");
$sth->execute($opts->{'site'});
($url, $name, $udir, $uicon, $commdir, $cicon, $lj_type) = $sth->fetchrow_array;
$native=0;
}
} else {
# site is not a number -- unknown alien site
$name=$opts->{'site'};
$url=$opts->{'site'};
$uicon='openid-profile.gif'; # default unknown alien usericon
$cicon='openid-profile.gif'; # default unknown alien commicon
$commdir='';
$udir='';
$lj_type='N';
$native=0;
}
if ($native){
# If the user is local, we do some processing: check validity, check
# whether user or community, etc.
# my $do_dynamic = $LJ::DYNAMIC_LJUSER || ($user =~ /^ext_/);
# if ($do_dynamic && ! isu($user) && ! $opts->{'type'}) {
# Try to automatically pick the user type, but still
# make something if we can't (user doesn't exist?)
$user = LJ::load_user($user) || $user;
my $hops = 0;
# Traverse the renames to the final journal
while (ref $user and $user->{'journaltype'} eq 'R'
and ! $opts->{'no_follow'} && $hops++ < 5) {
LJ::load_user_props($user, 'renamedto');
last unless length $user->{'renamedto'};
$user = LJ::load_user($user->{'renamedto'});
}
# }
if (LJ::isu($user)) {
$u = $user;
$opts->{'type'} = $user->{'journaltype'};
# Mark accounts as deleted that aren't visible, memorial, or locked
$opts->{'del'} = $user->{'statusvis'} ne 'V' &&
$user->{'statusvis'} ne 'M' &&
$user->{'statusvis'} ne 'L';
$user = $user->{'user'};
}
}
# End of local-specific part
my $andfull = $opts->{'full'} ? "&amp;mode=full" : "";
my $img = $opts->{'imgroot'} || $LJ::IMGPREFIX;
my $strike = $opts->{'del'} ? ' text-decoration: line-through;' : '';
my $make_tag = sub {
my ($s, $n, $fil, $dir) = @_;
$n = lc ($n);
if ($n eq ""){
return "<span class='ljruser' style='white-space: nowrap;$strike'><a href='http://$s/userinfo.bml?user=$user$andfull'><img src='$img/$fil' alt='[info]' style='vertical-align: bottom; border: 0;' /></a><a href='http://$s/$dir$user/'><b>$user</b></a></span>";
} else {
if ($lj_type eq 'Y') {
# If the site is known and has an lj-type engine, then we now how to
# refer to userinfo; make the info icon link to this
return "<span class='ljruser' style='white-space: nowrap;$strike'><a href='http://$s/userinfo.bml?user=$user$andfull'><img src='$img/$fil' alt='[info]' style='vertical-align: bottom; border: 0;' /></a><a href='http://$s/$dir$user/'><b>$user\@$n</b></a></span>";
} elsif ($known) {
# If not lj-type, but known, let the info icon link to the user journal
return "<span class='ljruser' style='white-space: nowrap;$strike'><a href='http://$s/$dir$user/'><img src='$img/$fil' alt='[info]' style='vertical-align: bottom; border: 0;' /></a><a href='http://$s/$dir$user/'><b>$user\@$n</b></a></span>";
} else {
# Unknown site. Treat as openid
return "<span class='ljruser' style='white-space: nowrap;$strike'><a href='http://$s/$dir$user/'><img src='$img/$fil' alt='[info]' style='vertical-align: bottom; border: 0;' /></a><a href='http://$s/$dir$user/'><b>$user</b> [$n]</a></span>";
}
}
};
if ($opts->{'type'} eq 'C') {
return $make_tag->( $url, $name, $cicon, $commdir);
} elsif ($opts->{'type'} eq 'Y') {
return $make_tag->( $url, $name, 'syndicated.gif', 'users/');
} elsif ($opts->{'type'} eq 'N') {
return $make_tag->( $url, $name, 'newsinfo.gif', 'users/');
} elsif ($opts->{'type'} eq 'I') {
return $u->ljuser_display($opts);
} else {
return $make_tag->( $url, $name, $uicon, $udir);
}
};
1;

View File

@@ -0,0 +1,210 @@
package LJR::Viewuserstandalone;
use strict;
# A function to canonicalize sitename: take one of the possible
# abbreviations for a given known site, and returns the siteid
# from the list. Otherwise, assume that abbreivation is actually the
# full URL, and return it "as is", without the possible leading http://.
# Right now we work case-by-case, since the number of known
# abbreviations is small.
#
#
# Known sites:
#
# 0 -- local
# 1 -- www.livejournal.com
# 2 -- greatestjournal.com
# 3 -- npj.ru
# 4 -- dreamwidth.org
# TODO: add third level domains
sub canonical_sitenum {
my ($site)=@_;
if ( ($site eq "LJR") || ($site =~ m/.*lj\.rossia\.org.*/) )
{return 0;}
elsif ( ($site eq "LJ") || ($site =~ m/.*livejournal\.com.*/) )
{return 1;}
elsif ( ($site eq "GJ") || ($site =~ m/.*greatestjournal\.com.*/) )
{return 2;}
elsif ( ($site eq "NPJ") || ($site =~ m/.*npj\.ru.*/) )
{return 3;}
elsif ( ($site eq "DW") || ($site eq "dw") || ($site =~ m/.*dreamwidth\.org.*/) )
{return 4;}
else {return $site;}
}
#
# Provides a representation of a user.
#
# Format: we receive a username and a site, where site is either a
# number or a string. If a non-zero number, this is a known site; we take
# information about it from the alianservers table in the db. If
# zero, site is ourselves. If a string, we do not know anything about
# the site and treat it as an OpenID guest; we assume site is the URL.
#
# We return the HTML code.
#
# <lj user="username" site="sitename"> should be expand to
# ljuser( $username, {'site'=> canonical_sitenum($sitename),
# 'type'=>'P','imgroot'=>''} )
#
# For lj comm, replace 'P' with 'C'; 'imgroot' should be equal to the
# current value of $LJ::IMGPREFIX -- right now it is differs
# between test and production!!
#
sub ljuser {
# we assume $opts->{'site'} to be a siteid of a known site or a full
# URL of a site we do not have in db
my $user = shift;
my $opts = shift;
my $u;
my $name="";
my $url;
my $uicon;
my $cicon;
my $commdir;
my $udir;
my $lj_type;
# If site is not given, assume native (siteid=0)
unless ($opts->{'site'}) {$opts->{'site'}=0;}
# Check if site is a number
if($opts->{'site'} =~ m/(\d+)/) {
# Site a number (known site)
$opts->{'site'} = $opts->{'site'}+0;
# now we've got default - $LJ::DOMAIN
if ($opts->{'site'}==0){
# local
$url='lj.rossia.org';
$cicon='community.gif'; # default local commicon
$uicon='userinfo.gif'; # default local usericon
$commdir='community/';
$udir='users/';
$lj_type='Y';
} elsif ($opts->{'site'}==1) {
# LJ
$name="LJ";
$url='www.livejournal.com';
$cicon='community-lj.gif';
$uicon='userinfo-lj.gif';
$commdir='community/';
$udir='users/';
$lj_type='Y';
} elsif ($opts->{'site'}==2) {
# GJ
$name="GJ";
$url='www.greatestjournal.com';
$cicon='community-lj.gif';
$uicon='userinfo-lj.gif';
$commdir='community/';
$udir='users/';
$lj_type='Y';
} elsif ($opts->{'site'}==3) {
# LJ
$name="NPJ";
$url='www.npj.ru';
$cicon='community-npj.gif';
$uicon='userinfo-npj.gif';
$commdir='';
$udir='';
$lj_type='N';
} elsif ($opts->{'site'}==4) {
# DW
$name="DW";
$url='www.dreamwidth.org';
$cicon='community-dw.gif';
$uicon='userinfo-dw.gif';
$commdir='community/';
$udir='users/';
$lj_type='Y';
} else { return "[Unknown LJ user tag]"; }
} else {
# site is not a number -- unknown alien site
$name=$opts->{'site'};
$url=$opts->{'site'};
$uicon=''; # default unknown alien usericon
$cicon=''; # default unknown alien commicon
$commdir='community';
$udir='users';
$lj_type='N';
}
my $andfull = $opts->{'full'} ? "&amp;mode=full" : "";
my $img = $opts->{'imgroot'};
my $make_tag = sub {
my ($s, $n, $fil, $dir) = @_;
if ($n eq ""){
return "<span class='ljruser' style='white-space: nowrap;'><a href='http://$s/userinfo.bml?user=$user$andfull'><img src='$img/$fil' alt='[info]' style='vertical-align: bottom; border: 0;' /></a><a href='http://$s/$dir$user/'><b>$user</b></a></span>";
} else {
if ($lj_type eq 'Y') {
# If the site is known and has an lj-type engine, then we now how to
# refer to userinfo; make the info icon link to this
return "<span class='ljruser' style='white-space: nowrap;'><a href='http://$s/userinfo.bml?user=$user$andfull'><img src='$img/$fil' alt='[info]' style='vertical-align: bottom; border: 0;' /></a><a href='http://$s/$dir$user/'><b>$user</b> [$n]</a></span>";
} else {
# If not lj-type, let the info icon link to the user journal
return "<span class='ljruser' style='white-space: nowrap;'><a href='http://$s/$dir$user/'><img src='$img/$fil' alt='[info]' style='vertical-align: bottom; border: 0;' /></a><a href='http://$s/$dir$user/'><b>$user</b> [$n]</a></span>";
}
}
};
if ($opts->{'type'} eq 'C') {
return $make_tag->( $url, $name, $cicon, $commdir);
} else {
return $make_tag->( $url, $name, $uicon, $udir);
}
}
sub expand_ljuser_tags {
my ($string)=@_;
return "" unless $string;
my $imgroot='http://lj.rossia.org/img';
$string=~ s/<lj\s+user=\"?(\w+)\"?\s+site=\"?([^"]+)\"?\s*\/?>/
ljuser($1,{
'site'=>canonical_sitenum($2),
'type'=>'P','imgroot'=>$imgroot,
})
/egxi;
$string=~ s/<lj\s+comm=\"?(\w+)\"?\s+site=\"?([^"]+)\"?\s*\/?>/
ljuser($1,{
'site'=>canonical_sitenum($2),
'type'=>'C','imgroot'=>$imgroot,
})
/egxi;
$string=~ s/<ljr\s+user=\"?(\w+)\"?\s*\/?>/
ljuser($1,{
'site'=>0,
'type'=>'P','imgroot'=>$imgroot,
})
/egxi;
$string=~ s/<ljr\s+comm=\"?(\w+)\"?\s*\/?>/
ljuser($1,{
'site'=>0,
'type'=>'C','imgroot'=>$imgroot,
})
/egxi;
return $string;
}
1;

View File

@@ -0,0 +1,25 @@
package LJ::Poll;
use strict;
sub replace_polls_with_links {
my ($event) = @_;
my $dbr = LJ::get_db_reader();
while ($$event =~ /<lj-poll-(\d+)>/g) {
my $pollid = $1;
my $name = $dbr->selectrow_array("SELECT name FROM poll WHERE pollid=?",
undef, $pollid);
if ($name) {
LJ::Poll::clean_poll(\$name);
} else {
$name = "#$pollid";
}
$$event =~ s!<lj-poll-$pollid>!<div><a href="$LJ::SITEROOT/poll/?id=$pollid">View Poll: $name</a></div>!g;
}
}
return 1;

View File

@@ -0,0 +1,53 @@
use strict;
package LJR::unicode;
use XML::Parser;
use Unicode::MapUTF8 qw(to_utf8 from_utf8 utf8_supported_charset);
sub utf8ize {
my $text_in = shift;
$$text_in = pack("C*", unpack("C*", $$text_in)) if $$text_in;
}
sub force_utf8 {
my $xdata = shift;
my %error_lines;
my $finished = 0;
my @xlines;
my $orig_xdata = $$xdata;
my $p1 = new XML::Parser ();
while (!$finished) {
eval { $p1->parse($$xdata); };
if ($@ && $@ =~ /not\ well\-formed\ \(invalid\ token\)\ at\ line\ (\d+)\,/) {
my $error_line = $1;
$error_lines{$error_line} ++;
if ($error_lines{$error_line} > 1) {
$$xdata = $orig_xdata;
$finished = 1;
}
else {
@xlines = split(/\n/, $$xdata);
my $output = to_utf8({ -string => $xlines[$error_line - 1], -charset => 'latin1' });
$xlines[$error_line - 1] = $output;
$$xdata = join("\n", @xlines);
}
}
# unknown error or no error, doesn't matter
elsif ($@) {
$$xdata = $orig_xdata;
$finished = 1;
}
else {
$finished = 1;
}
}
}
return 1;

View File

@@ -0,0 +1,28 @@
use strict;
package LJR::xmlrpc;
sub xmlrpc_call {
my ($xmlrpc, $method, $request) = @_;
my $res;
if ($xmlrpc) {
$res = $xmlrpc->call ($method, $request);
if ($res && $res->fault) {
$res->{"err_text"} = $method . ": " . "XML-RPC Error [" . $res->faultcode . "]: " . $res->faultstring;
}
elsif (!$res) {
$res->{"err_text"} = $method . ": " . "Unknown XML-RPC Error.";
}
$res->{"result"} = $res->result;
}
else {
$res->{"err_text"} = "Invalid xmlrpc object";
}
return $res;
}
return 1;

View File

@@ -0,0 +1,585 @@
package LWPx::ParanoidAgent;
require LWP::UserAgent;
use vars qw(@ISA $VERSION);
@ISA = qw(LWP::UserAgent);
$VERSION = '1.02';
require HTTP::Request;
require HTTP::Response;
use HTTP::Status ();
use strict;
use Net::DNS;
use LWP::Debug;
sub new {
my $class = shift;
my %opts = @_;
my $blocked_hosts = delete $opts{blocked_hosts} || [];
my $whitelisted_hosts = delete $opts{whitelisted_hosts} || [];
my $resolver = delete $opts{resolver};
$opts{timeout} ||= 15;
my $self = LWP::UserAgent->new( %opts );
$self->{'blocked_hosts'} = $blocked_hosts;
$self->{'whitelisted_hosts'} = $whitelisted_hosts;
$self->{'resolver'} = $resolver;
$self = bless $self, $class;
return $self;
}
# returns seconds remaining given a request
sub _time_remain {
my $self = shift;
my $req = shift;
my $now = time();
my $start_time = $req->{_time_begin} || $now;
return $start_time + $self->{timeout} - $now;
}
sub _resolve {
my ($self, $host, $request, $timeout, $depth) = @_;
my $res = $self->resolver;
$depth ||= 0;
die "CNAME recursion depth limit exceeded.\n" if $depth > 10;
die "Suspicious results from DNS lookup" if $self->_bad_host($host);
# return the IP address if it looks like one and wasn't marked bad
return ($host) if $host =~ /^\d+\.\d+\.\d+\.\d+$/;
my $sock = $res->bgsend($host)
or die "No sock from bgsend";
my $rin = '';
vec($rin, fileno($sock), 1) = 1;
my $nf = select($rin, undef, undef, $self->_time_remain($request));
die "DNS lookup timeout" unless $nf;
my $packet = $res->bgread($sock)
or die "DNS bgread failure";
$sock = undef;
my @addr;
my $cname;
foreach my $rr ($packet->answer) {
if ($rr->type eq "A") {
die "Suspicious DNS results from A record\n" if $self->_bad_host($rr->address);
push @addr, $rr->address;
} elsif ($rr->type eq "CNAME") {
# will be checked for validity in the recursion path
$cname = $rr->cname;
}
}
return @addr if @addr;
return () unless $cname;
return $self->_resolve($cname, $request, $timeout, $depth + 1);
}
sub _host_list_match {
my $self = shift;
my $list_name = shift;
my $host = shift;
foreach my $rule (@{ $self->{$list_name} }) {
if (ref $rule eq "CODE") {
return 1 if $rule->($host);
} elsif (ref $rule) {
# assume regexp
return 1 if $host =~ /$rule/;
} else {
return 1 if $host eq $rule;
}
}
}
sub _bad_host {
my $self = shift;
my $host = lc(shift);
return 0 if $self->_host_list_match("whitelisted_hosts", $host);
return 1 if $self->_host_list_match("blocked_hosts", $host);
return 1 if
$host =~ /^localhost$/i || # localhost is bad. even though it'd be stopped in
# a later call to _bad_host with the IP address
$host =~ /\s/i; # any whitespace is questionable
# Let's assume it's an IP address now, and get it into 32 bits.
# Uf at any time something doesn't look like a number, then it's
# probably a hostname and we've already either whitelisted or
# blacklisted those, so we'll just say it's okay and it'll come
# back here later when the resolver finds an IP address.
my @parts = split(/\./, $host);
return 0 if @parts > 4;
# un-octal/un-hex the parts, or return if there's a non-numeric part
my $overflow_flag = 0;
foreach (@parts) {
return 0 unless /^\d+$/ || /^0x[a-f\d]+$/;
local $SIG{__WARN__} = sub { $overflow_flag = 1; };
$_ = oct($_) if /^0/;
}
# a purely numeric address shouldn't overflow.
return 1 if $overflow_flag;
my $addr; # network order packed IP address
if (@parts == 1) {
# a - 32 bits
return 1 if
$parts[0] > 0xffffffff;
$addr = pack("N", $parts[0]);
} elsif (@parts == 2) {
# a.b - 8.24 bits
return 1 if
$parts[0] > 0xff ||
$parts[1] > 0xffffff;
$addr = pack("N", $parts[0] << 24 | $parts[1]);
} elsif (@parts == 3) {
# a.b.c - 8.8.16 bits
return 1 if
$parts[0] > 0xff ||
$parts[1] > 0xff ||
$parts[2] > 0xffff;
$addr = pack("N", $parts[0] << 24 | $parts[1] << 16 | $parts[2]);
} else {
# a.b.c.d - 8.8.8.8 bits
return 1 if
$parts[0] > 0xff ||
$parts[1] > 0xff ||
$parts[2] > 0xff ||
$parts[3] > 0xff;
$addr = pack("N", $parts[0] << 24 | $parts[1] << 16 | $parts[2] << 8 | $parts[3]);
}
my $haddr = unpack("N", $addr); # host order IP address
return 1 if
($haddr & 0xFF000000) == 0x00000000 || # 0.0.0.0/8
($haddr & 0xFF000000) == 0x0A000000 || # 10.0.0.0/8
($haddr & 0xFF000000) == 0x7F000000 || # 127.0.0.0/8
($haddr & 0xFFF00000) == 0xAC100000 || # 172.16.0.0/12
($haddr & 0xFFFF0000) == 0xA9FE0000 || # 169.254.0.0/16
($haddr & 0xFFFF0000) == 0xC0A80000 || # 192.168.0.0/16
$haddr == 0xFFFFFFFF || # 255.255.255.255
($haddr & 0xF0000000) == 0xE0000000; # multicast addresses
# as final IP address check, pass in the canonical a.b.c.d decimal form
# to the blacklisted host check to see if matches as bad there.
my $can_ip = join(".", map { ord } split //, $addr);
return 1 if $self->_host_list_match("blocked_hosts", $can_ip);
# looks like an okay IP address
return 0;
}
sub request {
my ($self, $req, $arg, $size, $previous) = @_;
# walk back to the first request, and set our _time_begin to its _time_begin, or if
# we're the first, then use current time. used by LWPx::Protocol::http_paranoid
my $first_res = $previous; # previous is the previous response that invoked this request
$first_res = $first_res->previous while $first_res && $first_res->previous;
$req->{_time_begin} = $first_res ? $first_res->request->{_time_begin} : time();
my $host = $req->uri->host;
if ($self->_bad_host($host)) {
my $err_res = HTTP::Response->new(403, "Unauthorized access to blocked host");
$err_res->request($req);
$err_res->header("Client-Date" => HTTP::Date::time2str(time));
$err_res->header("Client-Warning" => "Internal response");
$err_res->header("Content-Type" => "text/plain");
$err_res->content("403 Unauthorized access to blocked host\n");
return $err_res;
}
return $self->SUPER::request($req, $arg, $size, $previous);
}
sub redirect_ok
{
# RFC 2616, section 10.3.2 and 10.3.3 say:
# If the 30[12] status code is received in response to a request other
# than GET or HEAD, the user agent MUST NOT automatically redirect the
# request unless it can be confirmed by the user, since this might
# change the conditions under which the request was issued.
# Note that this routine used to be just:
# return 0 if $_[1]->method eq "POST"; return 1;
my($self, $new_request, $response) = @_;
my $method = $response->request->method;
return 0 unless grep $_ eq $method,
@{ $self->requests_redirectable || [] };
if ($new_request->url->scheme eq 'file') {
$response->header("Client-Warning" =>
"Can't redirect to a file:// URL!");
return 0;
}
$self->{'final_url'} = $new_request->uri;
# Otherwise it's apparently okay...
return 1;
}
# taken from LWP::UserAgent and modified slightly. (proxy support removed,
# and map http and https schemes to separate protocol handlers)
sub send_request
{
my ($self, $request, $arg, $size) = @_;
$self->_request_sanity_check($request);
my ($method, $url) = ($request->method, $request->uri);
local($SIG{__DIE__}); # protect against user defined die handlers
# Check that we have a METHOD and a URL first
return _new_response($request, &HTTP::Status::RC_BAD_REQUEST, "Method missing")
unless $method;
return _new_response($request, &HTTP::Status::RC_BAD_REQUEST, "URL missing")
unless $url;
return _new_response($request, &HTTP::Status::RC_BAD_REQUEST, "URL must be absolute")
unless $url->scheme;
return _new_response($request, &HTTP::Status::RC_BAD_REQUEST,
"ParanoidAgent doesn't support going through proxies. ".
"In that case, do your paranoia at your proxy instead.")
if $self->_need_proxy($url);
my $scheme = $url->scheme;
return _new_response($request, &HTTP::Status::RC_BAD_REQUEST, "Only http and https are supported by ParanoidAgent")
unless $scheme eq "http" || $scheme eq "https";
LWP::Debug::trace("$method $url");
my $protocol;
{
# Honor object-specific restrictions by forcing protocol objects
# into class LWP::Protocol::nogo.
my $x;
if($x = $self->protocols_allowed) {
if(grep lc($_) eq $scheme, @$x) {
LWP::Debug::trace("$scheme URLs are among $self\'s allowed protocols (@$x)");
}
else {
LWP::Debug::trace("$scheme URLs aren't among $self\'s allowed protocols (@$x)");
require LWP::Protocol::nogo;
$protocol = LWP::Protocol::nogo->new;
}
}
elsif ($x = $self->protocols_forbidden) {
if(grep lc($_) eq $scheme, @$x) {
LWP::Debug::trace("$scheme URLs are among $self\'s forbidden protocols (@$x)");
require LWP::Protocol::nogo;
$protocol = LWP::Protocol::nogo->new;
}
else {
LWP::Debug::trace("$scheme URLs aren't among $self\'s forbidden protocols (@$x)");
}
}
# else fall thru and create the protocol object normally
}
unless ($protocol) {
LWP::Protocol::implementor("${scheme}_paranoid", "LWPx::Protocol::${scheme}_paranoid");
eval "require LWPx::Protocol::${scheme}_paranoid;";
if ($@) {
$@ =~ s/ at .* line \d+.*//s; # remove file/line number
my $response = _new_response($request, &HTTP::Status::RC_NOT_IMPLEMENTED, $@);
return $response;
}
$protocol = eval { LWP::Protocol::create($scheme eq "http" ? "http_paranoid" : "https_paranoid", $self) };
if ($@) {
$@ =~ s/ at .* line \d+.*//s; # remove file/line number
my $response = _new_response($request, &HTTP::Status::RC_NOT_IMPLEMENTED, $@);
if ($scheme eq "https") {
$response->message($response->message . " (Crypt::SSLeay not installed)");
$response->content_type("text/plain");
$response->content(<<EOT);
LWP will support https URLs if the Crypt::SSLeay module is installed.
More information at <http://www.linpro.no/lwp/libwww-perl/README.SSL>.
EOT
}
return $response;
}
}
# Extract fields that will be used below
my ($timeout, $cookie_jar, $use_eval, $parse_head, $max_size) =
@{$self}{qw(timeout cookie_jar use_eval parse_head max_size)};
my $response;
my $proxy = undef;
if ($use_eval) {
# we eval, and turn dies into responses below
eval {
$response = $protocol->request($request, $proxy,
$arg, $size, $timeout);
};
if ($@) {
$@ =~ s/ at .* line \d+.*//s; # remove file/line number
$response = _new_response($request,
&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
$@);
}
}
else {
$response = $protocol->request($request, $proxy,
$arg, $size, $timeout);
# XXX: Should we die unless $response->is_success ???
}
$response->request($request); # record request for reference
$cookie_jar->extract_cookies($response) if $cookie_jar;
$response->header("Client-Date" => HTTP::Date::time2str(time));
return $response;
}
# blocked hostnames, compiled patterns, or subrefs
sub blocked_hosts
{
my $self = shift;
if (@_) {
my @hosts = @_;
$self->{'blocked_hosts'} = \@hosts;
return;
}
return @{ $self->{'blocked_hosts'} };
}
# whitelisted hostnames, compiled patterns, or subrefs
sub whitelisted_hosts
{
my $self = shift;
if (@_) {
my @hosts = @_;
$self->{'whitelisted_hosts'} = \@hosts;
return;
}
return @{ $self->{'whitelisted_hosts'} };
}
# get/set Net::DNS resolver object
sub resolver
{
my $self = shift;
if (@_) {
$self->{'resolver'} = shift;
require UNIVERSAL ;
die "Not a Net::DNS::Resolver object" unless
UNIVERSAL::isa($self->{'resolver'}, "Net::DNS::Resolver");
}
return $self->{'resolver'} ||= Net::DNS::Resolver->new;
}
# Taken directly from LWP::UserAgent because it was private there, and we can't depend on it
# staying there in future versions: needed by our modified version of send_request
sub _need_proxy
{
my($self, $url) = @_;
$url = $HTTP::URI_CLASS->new($url) unless ref $url;
my $scheme = $url->scheme || return;
if (my $proxy = $self->{'proxy'}{$scheme}) {
if (@{ $self->{'no_proxy'} }) {
if (my $host = eval { $url->host }) {
for my $domain (@{ $self->{'no_proxy'} }) {
if ($host =~ /\Q$domain\E$/) {
LWP::Debug::trace("no_proxy configured");
return;
}
}
}
}
LWP::Debug::debug("Proxied to $proxy");
return $HTTP::URI_CLASS->new($proxy);
}
LWP::Debug::debug('Not proxied');
undef;
}
# Taken directly from LWP::UserAgent because it was private there, and we can't depend on it
# staying there in future versions: needed by our modified version of send_request
sub _request_sanity_check {
my($self, $request) = @_;
# some sanity checking
if (defined $request) {
if (ref $request) {
Carp::croak("You need a request object, not a " . ref($request) . " object")
if ref($request) eq 'ARRAY' or ref($request) eq 'HASH' or
!$request->can('method') or !$request->can('uri');
}
else {
Carp::croak("You need a request object, not '$request'");
}
}
else {
Carp::croak("No request object passed in");
}
}
# Taken directly from LWP::UserAgent because it was private there, and we can't depend on it
# staying there in future versions: needed by our modified version of send_request
sub _new_response {
my($request, $code, $message) = @_;
my $response = HTTP::Response->new($code, $message);
$response->request($request);
$response->header("Client-Date" => HTTP::Date::time2str(time));
$response->header("Client-Warning" => "Internal response");
$response->header("Content-Type" => "text/plain");
$response->content("$code $message\n");
return $response;
}
1;
__END__
=head1 NAME
LWPx::ParanoidAgent - subclass of LWP::UserAgent that protects you from harm
=head1 SYNOPSIS
require LWPx::ParanoidAgent;
my $ua = LWPx::ParanoidAgent->new;
# this is 10 seconds overall, from start to finish. not just between
# socket reads. and it includes all redirects. so attackers telling
# you to download from a malicious tarpit webserver can only stall
# you for $n seconds
$ua->timeout(10);
# setup extra block lists, in addition to the always-enforced blocking
# of private IP addresses, loopbacks, and multicast addresses
$ua->blocked_hosts(
"foo.com",
qr/\.internal\.company\.com$/i,
sub { my $host = shift; return 1 if is_bad($host); },
);
$ua->whitelisted_hosts(
"brad.lj",
qr/^192\.168\.64\.3?/,
sub { ... },
);
# get/set the DNS resolver object that's used
my $resolver = $ua->resolver;
$ua->resolver(Net::DNS::Resolver->new(...));
# and then just like a normal LWP::UserAgent, because it is one.
my $response = $ua->get('http://search.cpan.org/');
...
if ($response->is_success) {
print $response->content; # or whatever
}
else {
die $response->status_line;
}
=head1 DESCRIPTION
The C<LWPx::ParanoidAgent> is a class subclassing C<LWP::UserAgent>,
but paranoid against attackers. It's to be used when you're fetching
a remote resource on behalf of a possibly malicious user.
This class can do whatever C<LWP::UserAgent> can (callbacks, uploads from
files, etc), except proxy support is explicitly removed, because in
that case you should do your paranoia at your proxy.
Also, the schemes are limited to http and https, which are mapped to
C<LWPx::Protocol::http_paranoid> and
C<LWPx::Protocol::https_paranoid>, respectively, which are forked
versions of the same ones without the "_paranoid". Subclassing them
didn't look possible, as they were essentially just one huge function.
This class protects you from connecting to internal IP ranges (unless you
whitelist them), hostnames/IPs that you blacklist, remote webserver
tarpitting your process (the timeout parameter is changed to be a global
timeout over the entire process), and all combinations of redirects and
DNS tricks to otherwise tarpit and/or connect to internal resources.
=head1 CONSTRUCTOR
=over 4
=item C<new>
my $ua = LWPx::ParanoidAgent->new([ %opts ]);
In addition to any constructor options from L<LWP::UserAgent>, you may
also set C<blocked_hosts> (to an arrayref), C<whitelisted_hosts> (also
an arrayref), and C<resolver>, a Net::DNS::Resolver object.
=back
=head1 METHODS
=over 4
=item $csr->B<resolver>($net_dns_resolver)
=item $csr->B<resolver>
Get/set the L<Net::DNS::Resolver> object used to lookup hostnames.
=item $csr->B<blocked_hosts>(@host_list)
=item $csr->B<blocked_hosts>
Get/set the the list of blocked hosts. The items in @host_list may be
compiled regular expressions (with qr//), code blocks, or scalar
literals. In any case, the thing that is match, passed in, or
compared (respectively), is all of the given hostname, given IP
address, and IP address in canonical a.b.c.d decimal notation. So if
you want to block "1.2.3.4" and the user entered it in a mix of
network/host form in a mix of decimal/octal/hex, you need only block
"1.2.3.4" and not worry about the details.
=item $csr->B<whitelisted_hosts>(@host_list)
=item $csr->B<whitelisted_hosts>
Like blocked hosts, but matching the hosts/IPs that bypass blocking
checks. The only difference is the IP address isn't canonicalized
before being whitelisted-matched, mostly because it doesn't make sense
for somebody to enter in a good address in a subversive way.
=back
=head1 SEE ALSO
See L<LWP::UserAgent> to see how to use this class.
=head1 WARRANTY
This module is supplied "as-is" and comes with no warranty, expressed
or implied. It tries to protect you from harm, but maybe it will.
Maybe it will destroy your data and your servers. You'd better audit
it and send me bug reports.
=head1 BUGS
Maybe. See the warranty above.
=head1 COPYRIGHT
Copyright 2005 Brad Fitzpatrick
Lot of code from the the base class, copyright 1995-2004 Gisle Aas.
This library is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

View File

@@ -0,0 +1,231 @@
use strict;
use Carp ();
############################################################################
package Net::OpenID::Association;
use fields (
'server', # author-identity identity server endpoint
'secret', # the secret for this association
'handle', # the 255-character-max ASCII printable handle (33-126)
'expiry', # unixtime, adjusted, of when this association expires
'type', # association type
);
use Storable ();
use Digest::SHA1 qw(sha1);
sub new {
my Net::OpenID::Association $self = shift;
$self = fields::new( $self ) unless ref $self;
my %opts = @_;
for my $f (qw( server secret handle expiry type )) {
$self->{$f} = delete $opts{$f};
}
Carp::croak("unknown options: " . join(", ", keys %opts)) if %opts;
return $self;
}
sub handle {
my $self = shift;
die if @_;
$self->{'handle'};
}
sub secret {
my $self = shift;
die if @_;
$self->{'secret'};
}
sub server {
my Net::OpenID::Association $self = shift;
Carp::croak("Too many parameters") if @_;
return $self->{server};
}
sub expired {
my Net::OpenID::Association $self = shift;
return time() > $self->{'expiry'};
}
sub usable {
my Net::OpenID::Association $self = shift;
return 0 unless $self->{'handle'} =~ /^[\x21-\x7e]{1,255}$/;
return 0 unless $self->{'expiry'} =~ /^\d+$/;
return 0 unless $self->{'secret'};
return 0 if $self->expired;
return 1;
}
# return a handle for an identity server, or undef if
# no local storage/cache is available, in which case the caller
# goes into dumb consumer mode. will do a POST and allocate
# a new assoc_handle if none is found, or has expired
sub server_assoc {
my ($csr, $server) = @_;
# closure to return undef (dumb consumer mode) and log why
my $dumb = sub {
$csr->_debug("server_assoc: dumb mode: $_[0]");
return undef;
};
my $cache = $csr->cache;
return $dumb->("no_cache") unless $cache;
# try first from cached association handle
if (my $handle = $cache->get("shandle:$server")) {
my $assoc = handle_assoc($csr, $server, $handle);
if ($assoc && $assoc->usable) {
$csr->_debug("Found association from cache (handle=$handle)");
return $assoc;
}
}
# make a new association
my $dh = _default_dh();
my %post = (
"openid.mode" => "associate",
"openid.assoc_type" => "HMAC-SHA1",
"openid.session_type" => "DH-SHA1",
"openid.dh_consumer_public" => OpenID::util::bi2arg($dh->pub_key),
);
my $req = HTTP::Request->new(POST => $server);
$req->header("Content-Type" => "application/x-www-form-urlencoded");
$req->content(join("&", map { "$_=" . OpenID::util::eurl($post{$_}) } keys %post));
$csr->_debug("Associate mode request: " . $req->content);
my $ua = $csr->ua;
my $res = $ua->request($req);
# uh, some failure, let's go into dumb mode?
return $dumb->("http_failure_no_associate") unless $res && $res->is_success;
my $recv_time = time();
my $content = $res->content;
my %args = OpenID::util::parse_keyvalue($content);
$csr->_debug("Response to associate mode: [$content] parsed = " . join(",", %args));
return $dumb->("unknown_assoc_type") unless $args{'assoc_type'} eq "HMAC-SHA1";
my $stype = $args{'session_type'};
return $dumb->("unknown_session_type") if $stype && $stype ne "DH-SHA1";
# protocol version 1.1
my $expires_in = $args{'expires_in'};
# protocol version 1.0 (DEPRECATED)
if (! $expires_in) {
if (my $issued = OpenID::util::w3c_to_time($args{'issued'})) {
my $expiry = OpenID::util::w3c_to_time($args{'expiry'});
my $replace_after = OpenID::util::w3c_to_time($args{'replace_after'});
# seconds ahead (positive) or behind (negative) the server is
$expires_in = ($replace_after || $expiry) - $issued;
}
}
# between 1 second and 2 years
return $dumb->("bogus_expires_in") unless $expires_in > 0 && $expires_in < 63072000;
my $ahandle = $args{'assoc_handle'};
my $secret;
if ($stype ne "DH-SHA1") {
$secret = OpenID::util::d64($args{'mac_key'});
} else {
my $server_pub = OpenID::util::arg2bi($args{'dh_server_public'});
my $dh_sec = $dh->compute_secret($server_pub);
$secret = OpenID::util::d64($args{'enc_mac_key'}) ^ sha1(OpenID::util::bi2bytes($dh_sec));
}
return $dumb->("secret_not_20_bytes") unless length($secret) == 20;
my %assoc = (
handle => $ahandle,
server => $server,
secret => $secret,
type => $args{'assoc_type'},
expiry => $recv_time + $expires_in,
);
my $assoc = Net::OpenID::Association->new( %assoc );
return $dumb->("assoc_undef") unless $assoc;
$cache->set("hassoc:$server:$ahandle", Storable::nfreeze(\%assoc));
$cache->set("shandle:$server", $ahandle);
return $assoc;
}
# returns association, or undef if it can't be found
sub handle_assoc {
my ($csr, $server, $handle) = @_;
# closure to return undef (dumb consumer mode) and log why
my $dumb = sub {
$csr->_debug("handle_assoc: dumb mode: $_[0]");
return undef;
};
return $dumb->("no_handle") unless $handle;
my $cache = $csr->cache;
return $dumb->("no_cache") unless $cache;
my $frozen = $cache->get("hassoc:$server:$handle");
return $dumb->("not_in_cache") unless $frozen;
my $param = eval { Storable::thaw($frozen) };
return $dumb->("not_a_hashref") unless ref $param eq "HASH";
return Net::OpenID::Association->new( %$param );
}
sub invalidate_handle {
my ($csr, $server, $handle) = @_;
my $cache = $csr->cache
or return;
$cache->set("hassoc:$server:$handle", "");
}
sub _default_dh {
my $dh = Crypt::DH->new;
$dh->p("155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443");
$dh->g("2");
$dh->generate_keys;
return $dh;
}
1;
__END__
=head1 NAME
Net::OpenID::Association - a relationship with an identity server
=head1 DESCRIPTION
Internal class.
=head1 COPYRIGHT, WARRANTY, AUTHOR
See L<Net::OpenID::Consumer> for author, copyrignt and licensing information.
=head1 SEE ALSO
L<Net::OpenID::Consumer>
L<Net::OpenID::VerifiedIdentity>
L<Net::OpenID::Server>
Website: L<http://www.danga.com/openid/>

View File

@@ -0,0 +1,952 @@
# LICENSE: You're free to distribute this under the same terms as Perl itself.
use strict;
use Carp ();
use LWP::UserAgent;
use URI::Fetch 0.02;
############################################################################
package Net::OpenID::Consumer;
use vars qw($VERSION);
$VERSION = "0.12";
use fields (
'cache', # the Cache object sent to URI::Fetch
'ua', # LWP::UserAgent instance to use
'args', # how to get at your args
'consumer_secret', # scalar/subref
'required_root', # the default required_root value, or undef
'last_errcode', # last error code we got
'last_errtext', # last error code we got
'debug', # debug flag or codeblock
);
use Net::OpenID::ClaimedIdentity;
use Net::OpenID::VerifiedIdentity;
use Net::OpenID::Association;
use MIME::Base64 ();
use Digest::SHA1 ();
use Crypt::DH 0.05;
use Time::Local;
use HTTP::Request;
sub new {
my Net::OpenID::Consumer $self = shift;
$self = fields::new( $self ) unless ref $self;
my %opts = @_;
$self->{ua} = delete $opts{ua};
$self->args ( delete $opts{args} );
$self->cache ( delete $opts{cache} );
$self->consumer_secret ( delete $opts{consumer_secret} );
$self->required_root ( delete $opts{required_root} );
$self->{debug} = delete $opts{debug};
Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts;
return $self;
}
sub cache { &_getset; }
sub consumer_secret { &_getset; }
sub required_root { &_getset; }
sub _getset {
my Net::OpenID::Consumer $self = shift;
my $param = (caller(1))[3];
$param =~ s/.+:://;
if (@_) {
my $val = shift;
Carp::croak("Too many parameters") if @_;
$self->{$param} = $val;
}
return $self->{$param};
}
sub _debug {
my Net::OpenID::Consumer $self = shift;
return unless $self->{debug};
if (ref $self->{debug} eq "CODE") {
$self->{debug}->($_[0]);
} else {
print STDERR "[DEBUG Net::OpenID::Consumer] $_[0]\n";
}
}
# given something that can have GET arguments, returns a subref to get them:
# Apache
# Apache::Request
# CGI
# HASH of get args
# CODE returning get arg, given key
# ...
sub args {
my Net::OpenID::Consumer $self = shift;
if (my $what = shift) {
Carp::croak("Too many parameters") if @_;
my $getter;
if (! ref $what){
Carp::croak("No args defined") unless $self->{args};
return $self->{args}->($what);
} elsif (ref $what eq "HASH") {
$getter = sub { $what->{$_[0]}; };
} elsif (ref $what eq "CGI") {
$getter = sub { scalar $what->param($_[0]); };
} elsif (ref $what eq "Apache") {
my %get = $what->args;
$getter = sub { $get{$_[0]}; };
} elsif (ref $what eq "Apache::Request") {
$getter = sub { scalar $what->param($_[0]); };
} elsif (ref $what eq "CODE") {
$getter = $what;
} else {
Carp::croak("Unknown parameter type ($what)");
}
if ($getter) {
$self->{args} = $getter;
}
}
$self->{args};
}
sub ua {
my Net::OpenID::Consumer $self = shift;
$self->{ua} = shift if @_;
Carp::croak("Too many parameters") if @_;
# make default one on first access
unless ($self->{ua}) {
my $ua = $self->{ua} = LWP::UserAgent->new;
$ua->timeout(10);
}
$self->{ua};
}
sub _fail {
my Net::OpenID::Consumer $self = shift;
my ($code, $text) = @_;
$text ||= {
'no_identity_server' => "The provided URL doesn't declare its OpenID identity server.",
'empty_url' => "No URL entered.",
'bogus_url' => "Invalid URL.",
'no_head_tag' => "URL provided doesn't seem to have a head tag.",
'url_fetch_err' => "Error fetching the provided URL.",
}->{$code};
$self->{last_errcode} = $code;
$self->{last_errtext} = $text;
$self->_debug("fail($code) $text");
wantarray ? () : undef;
}
sub json_err {
my Net::OpenID::Consumer $self = shift;
return OpenID::util::js_dumper({
err_code => $self->{last_errcode},
err_text => $self->{last_errtext},
});
}
sub err {
my Net::OpenID::Consumer $self = shift;
$self->{last_errcode} . ": " . $self->{last_errtext};
}
sub errcode {
my Net::OpenID::Consumer $self = shift;
$self->{last_errcode};
}
sub errtext {
my Net::OpenID::Consumer $self = shift;
$self->{last_errtext};
}
sub _get_url_contents {
my Net::OpenID::Consumer $self = shift;
my ($url, $final_url_ref, $hook) = @_;
$final_url_ref ||= do { my $dummy; \$dummy; };
my $ures = URI::Fetch->fetch($url,
UserAgent => $self->ua,
Cache => $self->cache,
ContentAlterHook => $hook,
)
or return $self->_fail("url_fetch_error", "Error fetching URL: " . URI::Fetch->errstr);
# who actually uses HTTP gone response status? uh, nobody.
if ($ures->status == URI::Fetch::URI_GONE()) {
return $self->_fail("url_gone", "URL is no longer available");
}
my $res = $ures->http_response;
$$final_url_ref = $res->request->uri->as_string;
return $ures->content;
}
sub _find_semantic_info {
my Net::OpenID::Consumer $self = shift;
my $url = shift;
my $final_url_ref = shift;
my $trim_hook = sub {
my $htmlref = shift;
# trim everything past the body. this is in case the user doesn't
# have a head document and somebody was able to inject their own
# head. -- brad choate
$$htmlref =~ s/<body\b.*//is;
};
my $doc = $self->_get_url_contents($url, $final_url_ref, $trim_hook) or
return;
# find <head> content of document (notably: the first head, if an attacker
# has added others somehow)
return $self->_fail("no_head_tag", "Couldn't find OpenID servers due to no head tag")
unless $doc =~ m!<head[^>]*>(.*?)</head>!is;
my $head = $1;
my $ret = {
'openid.server' => undef,
'openid.delegate' => undef,
'foaf' => undef,
'foaf.maker' => undef,
'rss' => undef,
'atom' => undef,
};
# analyze link/meta tags
while ($head =~ m!<(link|meta)\b([^>]+)>!g) {
my ($type, $val) = ($1, $2);
my $temp;
# OpenID servers / delegated identities
# <link rel="openid.server" href="http://www.livejournal.com/misc/openid.bml" />
if ($type eq "link" &&
$val =~ /\brel=.openid\.(server|delegate)./i && ($temp = $1) &&
$val =~ m!\bhref=[\"\']([^\"\']+)[\"\']!i) {
$ret->{"openid.$temp"} = $1;
next;
}
# FOAF documents
#<link rel="meta" type="application/rdf+xml" title="FOAF" href="http://brad.livejournal.com/data/foaf" />
if ($type eq "link" &&
$val =~ m!title=.foaf.!i &&
$val =~ m!rel=.meta.!i &&
$val =~ m!type=.application/rdf\+xml.!i &&
$val =~ m!href=[\"\']([^\"\']+)[\"\']!i) {
$ret->{"foaf"} = $1;
next;
}
# FOAF maker info
# <meta name="foaf:maker" content="foaf:mbox_sha1sum '4caa1d6f6203d21705a00a7aca86203e82a9cf7a'" />
if ($type eq "meta" &&
$val =~ m!name=.foaf:maker.!i &&
$val =~ m!content=([\'\"])(.*?)\1!i) {
$ret->{"foaf.maker"} = $2;
next;
}
if ($type eq "meta" &&
$val =~ m!name=.foaf:maker.!i &&
$val =~ m!content=([\'\"])(.*?)\1!i) {
$ret->{"foaf.maker"} = $2;
next;
}
# RSS
# <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.livejournal.com/~brad/data/rss" />
if ($type eq "link" &&
$val =~ m!rel=.alternate.!i &&
$val =~ m!type=.application/rss\+xml.!i &&
$val =~ m!href=[\"\']([^\"\']+)[\"\']!i) {
$ret->{"rss"} = $1;
next;
}
# Atom
# <link rel="alternate" type="application/atom+xml" title="Atom" href="http://www.livejournal.com/~brad/data/rss" />
if ($type eq "link" &&
$val =~ m!rel=.alternate.!i &&
$val =~ m!type=.application/atom\+xml.!i &&
$val =~ m!href=[\"\']([^\"\']+)[\"\']!i) {
$ret->{"atom"} = $1;
next;
}
}
# map the 4 entities that the spec asks for
my $emap = {
'lt' => '<',
'gt' => '>',
'quot' => '"',
'amp' => '&',
};
foreach my $k (keys %$ret) {
next unless $ret->{$k};
$ret->{$k} =~ s/&(\w+);/$emap->{$1} || ""/eg;
}
$self->_debug("semantic info ($url) = " . join(", ", %$ret));
return $ret;
}
sub _find_openid_server {
my Net::OpenID::Consumer $self = shift;
my $url = shift;
my $final_url_ref = shift;
my $sem_info = $self->_find_semantic_info($url, $final_url_ref) or
return;
return $self->_fail("no_identity_server") unless $sem_info->{"openid.server"};
$sem_info->{"openid.server"};
}
# returns Net::OpenID::ClaimedIdentity
sub claimed_identity {
my Net::OpenID::Consumer $self = shift;
my $url = shift;
Carp::croak("Too many parameters") if @_;
# trim whitespace
$url =~ s/^\s+//;
$url =~ s/\s+$//;
return $self->_fail("empty_url", "Empty URL") unless $url;
# do basic canonicalization
$url = "http://$url" if $url && $url !~ m!^\w+://!;
return $self->_fail("bogus_url", "Invalid URL") unless $url =~ m!^https?://!i;
# add a slash, if none exists
$url .= "/" unless $url =~ m!^http://.+/!i;
my $final_url;
my $sem_info = $self->_find_semantic_info($url, \$final_url) or
return;
my $id_server = $sem_info->{"openid.server"} or
return $self->_fail("no_identity_server");
return Net::OpenID::ClaimedIdentity->new(
identity => $final_url,
server => $id_server,
consumer => $self,
delegate => $sem_info->{'openid.delegate'},
);
}
sub user_cancel {
my Net::OpenID::Consumer $self = shift;
return $self->args("openid.mode") eq "cancel";
}
sub user_setup_url {
my Net::OpenID::Consumer $self = shift;
my %opts = @_;
my $post_grant = delete $opts{'post_grant'};
Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts;
return $self->_fail("bad_mode") unless $self->args("openid.mode") eq "id_res";
my $setup_url = $self->args("openid.user_setup_url");
OpenID::util::push_url_arg(\$setup_url, "openid.post_grant", $post_grant)
if $setup_url && $post_grant;
return $setup_url;
}
sub verified_identity {
my Net::OpenID::Consumer $self = shift;
my %opts = @_;
my $rr = delete $opts{'required_root'} || $self->{required_root};
Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts;
return $self->_fail("bad_mode") unless $self->args("openid.mode") eq "id_res";
# the asserted identity (the delegated one, if there is one, since the protocol
# knows nothing of the original URL)
my $a_ident = $self->args("openid.identity") or return $self->_fail("no_identity");
my $sig64 = $self->args("openid.sig") or return $self->_fail("no_sig");
my $returnto = $self->args("openid.return_to") or return $self->_fail("no_return_to");
my $signed = $self->args("openid.signed");
my $real_ident = $self->args("oic.identity") || $a_ident;
# check that returnto is for the right host
return $self->_fail("bogus_return_to") if $rr && $returnto !~ /^\Q$rr\E/;
# check age/signature of return_to
my $now = time();
{
my ($sig_time, $sig) = split(/\-/, $self->args("oic.time") || "");
# complain if more than an hour since we sent them off
return $self->_fail("time_expired") if $sig_time < $now - 3600;
# also complain if the signature is from the future by more than 30 seconds,
# which compensates for potential clock drift between nodes in a web farm.
return $self->_fail("time_in_future") if $sig_time - 30 > $now;
# and check that the time isn't faked
my $c_secret = $self->_get_consumer_secret($sig_time);
my $good_sig = substr(OpenID::util::hmac_sha1_hex($sig_time, $c_secret), 0, 20);
return $self->_fail("time_bad_sig") unless $sig eq $good_sig;
}
my $final_url;
my $sem_info = $self->_find_semantic_info($real_ident, \$final_url);
return $self->_fail("unexpected_url_redirect") unless $final_url eq $real_ident;
my $server = $sem_info->{"openid.server"} or
return $self->_fail("no_identity_server");
# if openid.delegate was used, check that it was done correctly
if ($a_ident ne $real_ident) {
return $self->_fail("bogus_delegation") unless $sem_info->{"openid.delegate"} eq $a_ident;
}
my $assoc_handle = $self->args("openid.assoc_handle");
$self->_debug("verified_identity: assoc_handle: $assoc_handle");
my $assoc = Net::OpenID::Association::handle_assoc($self, $server, $assoc_handle);
if ($assoc) {
$self->_debug("verified_identity: verifying with found association");
return $self->_fail("expired_association")
if $assoc->expired;
# verify the token
my $token = "";
foreach my $p (split(/,/, $signed)) {
$token .= "$p:" . $self->args("openid.$p") . "\n";
}
my $good_sig = OpenID::util::b64(OpenID::util::hmac_sha1($token, $assoc->secret));
return $self->_fail("signature_mismatch") unless $sig64 eq $good_sig;
} else {
$self->_debug("verified_identity: verifying using HTTP (dumb mode)");
# didn't find an association. have to do dumb consumer mode
# and check it with a POST
my %post = (
"openid.mode" => "check_authentication",
"openid.assoc_handle" => $assoc_handle,
"openid.signed" => $signed,
"openid.sig" => $sig64,
);
# and copy in all signed parameters that we don't already have into %post
foreach my $param (split(/,/, $signed)) {
next unless $param =~ /^\w+$/;
next if $post{"openid.$param"};
$post{"openid.$param"} = $self->args("openid.$param");
}
# if the server told us our handle as bogus, let's ask in our
# check_authentication mode whether that's true
if (my $ih = $self->args("openid.invalidate_handle")) {
$post{"openid.invalidate_handle"} = $ih;
}
my $req = HTTP::Request->new(POST => $server);
$req->header("Content-Type" => "application/x-www-form-urlencoded");
$req->content(join("&", map { "$_=" . OpenID::util::eurl($post{$_}) } keys %post));
my $ua = $self->ua;
my $res = $ua->request($req);
# uh, some failure, let's go into dumb mode?
return $self->_fail("naive_verify_failed_network") unless $res && $res->is_success;
my $content = $res->content;
my %args = OpenID::util::parse_keyvalue($content);
# delete the handle from our cache
if (my $ih = $args{'invalidate_handle'}) {
Net::OpenID::Association::invalidate_handle($self, $server, $ih);
}
# bad but works. check out
# http://www.livejournal.com/community/lj_everywhere/194480.html?thread=724400#t724400
#
if ($content !~ /error:bad_handle/) {
return $self->_fail("naive_verify_failed_return: $content") unless
$args{'is_valid'} eq "true" || # protocol 1.1
$args{'lifetime'} > 0; # DEPRECATED protocol 1.0
}
}
$self->_debug("verified identity! = $real_ident");
# verified!
return Net::OpenID::VerifiedIdentity->new(
identity => $real_ident,
foaf => $sem_info->{"foaf"},
foafmaker => $sem_info->{"foaf.maker"},
rss => $sem_info->{"rss"},
atom => $sem_info->{"atom"},
consumer => $self,
);
}
sub supports_consumer_secret { 1; }
sub _get_consumer_secret {
my Net::OpenID::Consumer $self = shift;
my $time = shift;
my $ss;
if (ref $self->{consumer_secret} eq "CODE") {
$ss = $self->{consumer_secret};
} elsif ($self->{consumer_secret}) {
$ss = sub { return $self->{consumer_secret}; };
} else {
Carp::croak("You haven't defined a consumer_secret value or subref.\n");
}
my $sec = $ss->($time);
Carp::croak("Consumer secret too long") if length($sec) > 255;
return $sec;
}
package OpenID::util;
# From Digest::HMAC
sub hmac_sha1_hex {
unpack("H*", &hmac_sha1);
}
sub hmac_sha1 {
hmac($_[0], $_[1], \&Digest::SHA1::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));
}
sub parse_keyvalue {
my $reply = shift;
my %ret;
$reply =~ s/\r//g;
foreach (split /\n/, $reply) {
next unless /^(\S+?):(.*)/;
$ret{$1} = $2;
}
return %ret;
}
sub ejs
{
my $a = $_[0];
$a =~ s/[\"\'\\]/\\$&/g;
$a =~ s/\r?\n/\\n/gs;
$a =~ s/\r//;
return $a;
}
# Data::Dumper for JavaScript
sub js_dumper {
my $obj = shift;
if (ref $obj eq "HASH") {
my $ret = "{";
foreach my $k (keys %$obj) {
$ret .= "$k: " . js_dumper($obj->{$k}) . ",";
}
chop $ret;
$ret .= "}";
return $ret;
} elsif (ref $obj eq "ARRAY") {
my $ret = "[" . join(", ", map { js_dumper($_) } @$obj) . "]";
return $ret;
} else {
return $obj if $obj =~ /^\d+$/;
return "\"" . ejs($obj) . "\"";
}
}
sub eurl
{
my $a = $_[0];
$a =~ s/([^a-zA-Z0-9_\,\-.\/\\\: ])/uc sprintf("%%%02x",ord($1))/eg;
$a =~ tr/ /+/;
return $a;
}
sub push_url_arg {
my $uref = shift;
$$uref =~ s/[&?]$//;
my $got_qmark = ($$uref =~ /\?/);
while (@_) {
my $key = shift;
my $value = shift;
$$uref .= $got_qmark ? "&" : ($got_qmark = 1, "?");
$$uref .= eurl($key) . "=" . eurl($value);
}
}
sub time_to_w3c {
my $time = shift || time();
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
$mon++;
$year += 1900;
return sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ",
$year, $mon, $mday,
$hour, $min, $sec);
}
sub w3c_to_time {
my $hms = shift;
return 0 unless
$hms =~ /^(\d{4,4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/;
my $time;
eval {
$time = Time::Local::timegm($6, $5, $4, $3, $2 - 1, $1);
};
return 0 if $@;
return $time;
}
sub bi2bytes {
my $bigint = shift;
die "Can't deal with negative numbers" if $bigint->is_negative;
my $bits = $bigint->as_bin;
die unless $bits =~ s/^0b//;
# prepend zeros to round to byte boundary, or to unset high bit
my $prepend = (8 - length($bits) % 8) || ($bits =~ /^1/ ? 8 : 0);
$bits = ("0" x $prepend) . $bits if $prepend;
return pack("B*", $bits);
}
sub bi2arg {
return b64(bi2bytes($_[0]));
}
sub b64 {
my $val = MIME::Base64::encode_base64($_[0]);
$val =~ s/\s+//g;
return $val;
}
sub d64 {
return MIME::Base64::decode_base64($_[0]);
}
sub bytes2bi {
return Math::BigInt->new("0b" . unpack("B*", $_[0]));
}
sub arg2bi {
return undef unless defined $_[0] and $_[0] ne "";
# don't acccept base-64 encoded numbers over 700 bytes. which means
# those over 4200 bits.
return Math::BigInt->new("0") if length($_[0]) > 700;
return bytes2bi(MIME::Base64::decode_base64($_[0]));
}
__END__
=head1 NAME
Net::OpenID::Consumer - library for consumers of OpenID identities
=head1 SYNOPSIS
use Net::OpenID::Consumer;
my $csr = Net::OpenID::Consumer->new(
ua => LWPx::ParanoidAgent->new,
cache => Some::Cache->new,
args => $cgi,
consumer_secret => ...,
required_root => "http://site.example.com/",
);
# a user entered, say, "bradfitz.com" as their identity. The first
# step is to fetch that page, parse it, and get a
# Net::OpenID::ClaimedIdentity object:
my $claimed_identity = $csr->claimed_identity("bradfitz.com");
# now your app has to send them at their identity server's endpoint
# to get redirected to either a positive assertion that they own
# that identity, or where they need to go to login/setup trust/etc.
my $check_url = $claimed_identity->check_url(
return_to => "http://example.com/openid-check.app?yourarg=val",
trust_root => "http://example.com/",
);
# so you send the user off there, and then they come back to
# openid-check.app, then you see what the identity server said;
if (my $setup_url = $csr->user_setup_url) {
# redirect/link/popup user to $setup_url
} elsif ($csr->user_cancel) {
# restore web app state to prior to check_url
} elsif (my $vident = $csr->verified_identity) {
my $verified_url = $vident->url;
print "You are $verified_url !";
} else {
die "Error validating identity: " . $csr->err;
}
=head1 DESCRIPTION
This is the Perl API for (the consumer half of) OpenID, a distributed
identity system based on proving you own a URL, which is then your
identity. More information is available at:
http://www.danga.com/openid/
=head1 CONSTRUCTOR
=over 4
=item C<new>
my $csr = Net::OpenID::Consumer->new([ %opts ]);
You can set the C<ua>, C<cache>, C<consumer_secret>, C<required_root>,
and C<args> in the constructor. See the corresponding method
descriptions below.
=back
=head1 METHODS
=over 4
=item $csr->B<ua>($user_agent)
=item $csr->B<ua>
Getter/setter for the LWP::UserAgent (or subclass) instance which will
be used when web donwloads are needed. It's highly recommended that
you use LWPx::ParanoidAgent, or at least read its documentation so
you're aware of why you should care.
=item $csr->B<cache>($cache)
=item $csr->B<cache>
Getter/setter for the optional (but recommended!) cache instance you
want to use for storing fetched parts of pages. (identity server
public keys, and the E<lt>headE<gt> section of user's HTML pages)
The $cache object can be anything that has a -E<gt>get($key) and
-E<gt>set($key,$value) methods. See L<URI::Fetch> for more
information. This cache object is just passed to L<URI::Fetch>
directly.
=item $nos->B<consumer_secret>($scalar)
=item $nos->B<consumer_secret>($code)
=item $code = $nos->B<consumer_secret>; ($secret) = $code->($time);
The consumer secret is used to generate self-signed nonces for the
return_to URL, to prevent spoofing.
In the simplest (and least secure) form, you configure a static secret
value with a scalar. If you use this method and change the scalar
value, any outstanding requests from the last 30 seconds or so will fail.
The more robust (but more complicated) form is to supply a subref that
returns a secret based on the provided I<$time>, a unix timestamp.
And if one doesn't exist for that time, create, store and return it
(with appropriate locking so you never return different secrets for
the same time.)
Your secret may not exceed 255 characters.
=item $csr->B<args>($ref)
=item $csr->B<args>($param)
=item $csr->B<args>
Can be used in 1 of 3 ways:
1. Setting the way which the Consumer instances obtains GET parameters:
$csr->args( $reference )
Where $reference is either a HASH ref, CODE ref, Apache $r,
Apache::Request $apreq, or CGI.pm $cgi. If a CODE ref, the subref
must return the value given one argument (the parameter to retrieve)
2. Get a paramater:
my $foo = $csr->args("foo");
When given an unblessed scalar, it retrieves the value. It croaks if
you haven't defined a way to get at the parameters.
3. Get the getter:
my $code = $csr->args;
Without arguments, returns a subref that returns the value given a
parameter name.
=item $nos->B<required_root>($url_prefix)
=item $url_prefix = $nos->B<required_root>
If provided, this is the required string that all return_to URLs must
start with. If it doesn't match, it'll be considered invalid (spoofed
from another site)
=item $csr->B<claimed_identity>($url)
Given a user-entered $url (which could be missing http://, or have
extra whitespace, etc), returns either a Net::OpenID::ClaimedIdentity
object, or undef on failure.
Note that this identity is NOT verified yet. It's only who the user
claims they are, but they could be lying.
If this method returns undef, you can rely on the following errors
codes (from $csr->B<errcode>) to decide what to present to the user:
=over 8
=item no_identity_server
=item empty_url
=item bogus_url
=item no_head_tag
=item url_fetch_err
=back
=item $csr->B<user_setup_url>( [ %opts ] )
Returns the URL the user must return to in order to login, setup trust,
or do whatever the identity server needs them to do in order to make
the identity assertion which they previously initiated by entering
their claimed identity URL. Returns undef if this setup URL isn't
required, in which case you should ask for the verified_identity.
The base URL this this function returns can be modified by using the
following options in %opts:
=over
=item C<post_grant>
What you're asking the identity server to do with the user after they
setup trust. Can be either C<return> or C<close> to return the user
back to the return_to URL, or close the browser window with
JavaScript. If you don't specify, the behavior is undefined (probably
the user gets a dead-end page with a link back to the return_to URL).
In any case, the identity server can do whatever it wants, so don't
depend on this.
=back
=item $csr->B<user_cancel>
Returns true if the user declined to share their identity, false
otherwise. (This function is literally one line: returns true if
"openid.mode" eq "cancel")
It's then your job to restore your app to where it was prior to
redirecting them off to the user_setup_url, using the other query
parameters that you'd sent along in your return_to URL.
=item $csr->B<verified_identity>( [ %opts ] )
Returns a Net::OpenID::VerifiedIdentity object, or undef.
Verification includes double-checking the reported identity URL
declares the identity server, verifying the signature, etc.
The options in %opts may contain:
=over
=item C<required_root>
Sets the required_root just for this request. Values returns to its
previous value afterwards.
=back
=item $csr->B<err>
Returns the last error, in form "errcode: errtext"
=item $csr->B<errcode>
Returns the last error code.
=item $csr->B<errtext>
Returns the last error text.
=item $csr->B<json_err>
Returns the last error code/text in JSON format.
=back
=head1 COPYRIGHT
This module is Copyright (c) 2005 Brad Fitzpatrick.
All rights reserved.
You may distribute under the terms of either the GNU General Public
License or the Artistic License, as specified in the Perl README file.
If you need more liberal licensing terms, please contact the
maintainer.
=head1 WARRANTY
This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
=head1 SEE ALSO
OpenID website: http://www.danga.com/openid/
L<Net::OpenID::ClaimedIdentity> -- part of this module
L<Net::OpenID::VerifiedIdentity> -- part of this module
L<Net::OpenID::Server> -- another module, for acting like an OpenID server
=head1 AUTHORS
Brad Fitzpatrick <brad@danga.com>

View File

@@ -0,0 +1,32 @@
ljrlook.nav.create
ljrlook.nav.update
ljrlook.nav.fullupdate
ljrlook.nav.site
ljrlook.nav.news
ljrlook.nav.paidaccounts
ljrlook.nav.edit
ljrlook.nav.modify
ljrlook.nav.editinfo
ljrlook.nav.editfriends
ljrlook.nav.editjournal
ljrlook.nav.editpics
ljrlook.nav.changepassword
ljrlook.nav.communities.manage
ljrlook.nav.frills
ljrlook.nav.customize
ljrlook.nav.createstyle
ljrlook.nav.editstyle
ljrlook.nav.needhelp
ljrlook.nav.lostinfo
ljrlook.nav.support.faq
ljrlook.nav.support
ljrlook.nav.hello
ljrlook.nav.yourjournal
ljrlook.nav.recent
ljrlook.nav.calendar
ljrlook.nav.friends
ljrlook.nav.userinfo
ljrlook.nav.memories
ljrlook.nav.logout
ljrlook.nav.login

View File

@@ -0,0 +1,267 @@
#
# Welcome to GENERIC.LOOK for the WhiteBlue scheme
#
# by....
# Brad Fitzpatrick
# brad@danga.com
#
######################### little stuff
_parent=>global.look
AL=>{P}<I><A HREF="%%DATA1%%">%%DATA2%%</A></I> <IMG SRC="/img/external_link.gif" WIDTH=16 HEIGHT=11 ALIGN=ABSMIDDLE>
AWAYLINK=>{P}<I><A HREF="%%DATA1%%">%%DATA2%%</A></I> <IMG SRC="/img/external_link.gif" WIDTH=16 HEIGHT=11 ALIGN=ABSMIDDLE>
H1=>{D}<P><FONT FACE="Arial,Helvetica" COLOR="#CC0000"><B>%%DATA%%</B></FONT>
H1/FOLLOW_CHOICES=>{D}<FONT FACE="Arial,Helvetica" COLOR="#CC0000"><B>%%DATA%%</B></FONT>
HEAD1=>{D}<P><FONT FACE="Arial,Helvetica" COLOR="#CC0000"><B>%%DATA%%</B></FONT>
H2=>{D}<P><FONT FACE="Arial,Helvetica" COLOR="#CC0000" SIZE=-1><B>%%DATA%%</B></FONT>
HEAD2=>{D}<P><FONT FACE="Arial,Helvetica" COLOR="#CC0000" SIZE=-1><B>%%DATA%%</B></FONT>
# Banner Header: search results banner, content desriptor, etc...
BH=>{D}<P ALIGN=CENTER><FONT FACE="Arial,Helvetica" COLOR="#CC0000" SIZE=-1><B>%%DATA%%</B></FONT>
GRIN=>&lt;grin&gt;
HR=><P ALIGN="CENTER"><FONT COLOR=BLUE>*</FONT></P>
NEWLINE=>{D}<BR>&nbsp;&nbsp;&nbsp;&nbsp;
P=>{D}<BR>%%DATA%%
P/FOLLOW_P=>{D}<BR><IMG SRC="/img/dot.gif" WIDTH=1 VSPACE=6 HEIGHT=1><BR>%%DATA%%
STANDOUTO<=
{D}<CENTER><FONT SIZE=1><BR></FONT><TABLE ALIGN=CENTER CELLPADDING=8 BORDER=1 BGCOLOR=#CCCCFF BORDERCOLORLIGHT=#DDDDFF
BORDERCOLORDARK=#BBBBFF><TR><TD VALIGN=CENTER>
%%DATA%%
</TD></TR></TABLE></CENTER>
<=STANDOUTO
STANDOUT<=
{D}<CENTER><FONT SIZE=1><BR></FONT>
<table cellspacing=0 cellpadding=0 border=0 bgcolor="#ccccff">
<tr>
<td width=7 align=left valign=top>
<img width=7 height=7 src="/img/corn_nw.gif" alt=""></td>
<td height=7>
<img height=7 src="/img/dot.gif" alt=""></td>
<td width=7 valign=top align=right>
<img height=7 src="/img/corn_ne.gif" alt=""></td>
</tr><tr>
<td width=7>
<img width=7 height=1 src="/img/dot.gif" alt=""></td>
<td valign=top>
%%DATA%%
</td>
<td width=7>
<img width=7 height=1 src="/img/dot.gif" alt=""></td>
</tr><tr>
<td width=7 align=left valign=top>
<img width=7 height=7 src="/img/corn_sw.gif" alt=""></td>
<td height=7>
<img height=7 src="/img/dot.gif" alt=""></td>
<td width=7 valign=top align=right>
<img height=7 src="/img/corn_se.gif" alt=""></td>
</tr>
</table>
</CENTER>
<=STANDOUT
SOERROR=><div style='background-color:#f3f4fe; color:red; font-weight:bold; text-align:center'>%%data%%</div>
EMAILEX=><div style='width: 50%; font-family: courier; background-color: #efefef; border: dotted #cdcdcd 2px; padding: 5px;'>%%data%%</div>
######################### choices stuff
CHOICE=>{P}<DT><A HREF="%%DATA2%%"><FONT FACE="Arial,Helvetica"><B>%%DATA1%%</B></FONT></A><DD><FONT SIZE="2">%%DATA3%%</FONT>
CHOICES<=
{F}<P><DIV CLASS="choice"><TABLE WIDTH="100%" CELLPADDING="2" CELLSPACING="5">
<TR>
<TD VALIGN=TOP WIDTH="50%">
<DL>
%%ITEMS%%
</DL>
</TD>
<TD VALIGN=TOP WIDTH="50%">
<DL>
%%ITEMSB%%
</DL>
</TD>
</TR>
</TABLE></DIV>
<=CHOICES
##################################################################################
################################### MAIN PAGE ####################################
##################################################################################
PAGE<=
{Fps}<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML><?load_page_info?>
<HEAD>
<title><?_code {
my $elhash = $_[2];
return $elhash->{'WINDOWTITLE'} || $elhash->{'TITLE'};
} _code?></title>
%%head%%
<?_code
use strict;
my $crumb_up;
if(LJ::get_active_crumb() ne '')
{
my $parentcrumb = LJ::get_parent_crumb();
$crumb_up = "<link rel='up' title='$parentcrumb->[0]' href='$parentcrumb->[1]' />";
}
return $crumb_up;
_code?>
</HEAD>
<BODY BGCOLOR=#FFFFFF TOPMARGIN="0" LEFTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0" LINK=#0000C0 VLINK=#600060 %%bodyopts%%>
<TABLE WIDTH=100% BORDER=0 CELLPADDING=0 CELLSPACING=0 BACKGROUND="/img/bluewhite/bluefade.jpg">
<TR WIDTH=100%>
<TD VALIGN=BOTTOM ALIGN=LEFT HEIGHT=100>
<TABLE BACKGROUND="" HEIGHT=95 WIDTH=100% BORDER=0>
<TR>
<TD WIDTH=3>&nbsp;</TD>
<TD HEIGHT=53 WIDTH=406 VALIGN=BOTTOM>
<?_code
$is_home = (BML::get_uri() =~ m!^/(index\.bml)?!);
if (0 && $is_home)
{
return '<IMG SRC="/img/bluewhite/title.gif" WIDTH=600 HEIGHT=53><!-- ';
}
return "";
_code?>
<FONT SIZE=6 COLOR="#000a3f" FACE="Arial, Helvetica"><B>%%TITLE%%</B></FONT>
<?_code
if (0 && $is_home)
{
return ' -->';
}
return "";
_code?>
</TD>
<TD VALIGN=TOP ALIGN=RIGHT>
<?_code
unless ($is_home) {
return "<A HREF=\"/\"><IMG SRC=\"/img/bluewhite/home.gif\" WIDTH=35 HEIGHT=36 BORDER=0></A>&nbsp;";
}
return "";
_code?>
</TD>
</TR>
</TABLE>
</TD></TR>
<TR><TD bgcolor="#FFFFFF"><?breadcrumbs?></TD></TR>
</TABLE>
<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0>
<TR VALIGN=TOP>
<TD WIDTH=155 BGCOLOR=#d7d9e8 NOWRAP><IMG SRC="/img/bluewhite/hline.gif" WIDTH=155 HEIGHT=25 ALT="">
<TABLE WIDTH=153 BORDER=0 CELLSPACING=0 CELLPADDING=0>
<TR><TD>
<FONT FACE="Arial,Helvetica" SIZE=-1>
<?_code
$ret = "";
sub dump_entry
{
my ($ret, $listref, $depth) = @_;
foreach my $mi (@$listref)
{
if ($depth==0) {
$$ret .= "<P><IMG SRC=\"/img/bluewhite/bullet.gif\" WIDTH=10 HEIGHT=10 HSPACE=2 ALIGN=ABSMIDDLE>";
} else {
$$ret .= "&nbsp;" x ($depth*3+1);
$$ret .= $mi->{'cont'} ? "&nbsp;&nbsp;" : "- ";
}
my $name = $mi->{'name'};
$name =~ s/ /&nbsp;/g;
if (! defined $mi->{'uri'}) {
if ($depth == 0) {
$$ret .= "<B>$name</B><BR>";
} else {
$$ret .= "$name<BR>";
}
} elsif ($mi->{'match'} ?
(BML::get_uri() =~ /$mi->{'match'}/) :
(BML::get_uri() eq $mi->{'uri'})
){
$$ret .= "<B><SPAN style=\"background-color: #FFFFFF\"><FONT COLOR=#0000D0>$name</FONT></SPAN></B><BR>";
} else {
$$ret .= "<A HREF=\"$mi->{'uri'}\">$name</A><BR>";
}
if ($mi->{'children'} &&
($mi->{'recursematch'} ? BML::get_uri() =~ /$mi->{'recursematch'}/ : 1)) {
&dump_entry($ret, $mi->{'children'}, $depth+1);
}
}
}
&dump_entry(\$ret, \@sidebar, 0);
return $ret;
_code?>
</FONT>
</TD></TR></TABLE>
</TD>
<TD ALIGN=LEFT BACKGROUND="/img/bluewhite/vline.gif" WIDTH=25 NOWRAP>
<IMG SRC="/img/bluewhite/linetop.gif" WIDTH=25 HEIGHT=25 ALT=""><BR>
<IMG SRC="/img/bluewhite/vline.gif" WIDTH=25 HEIGHT=800 ALT="">
</TD>
<TD>
<IMG SRC="/img/dot.gif" WIDTH=1 HEIGHT=3><BR>
%%BODY%%
</TD>
<TD WIDTH=20>&nbsp;</TD>
</TR>
<!-- table closure row -->
<TR>
<TD WIDTH=155 NOWRAP><IMG SRC="/img/bluewhite/sidebarfade.gif" WIDTH=155 HEIGHT=25 ALT=""></TD>
<TD WIDTH=25 NOWRAP><IMG SRC="/img/bluewhite/sidebarfade_line.gif" WIDTH=25 HEIGHT=25 ALT=""></TD></TD>
<TD>
&nbsp;
</TD>
<TD WIDTH=20>&nbsp;</TD>
</TR>
</TABLE>
<!-- /table closure row -->
<!--<TABLE WIDTH=100%>
<TR>
<TD ALIGN=RIGHT>
<FONT FACE="Arial, Helvetica" SIZE="-2">
<A HREF="/privacy.bml">Privacy Policy</A> -
<A HREF="/coppa.bml">COPPA</A><BR>
<A HREF="/disclaimer.bml">Legal Disclaimer</A> -
<A HREF="/sitemap.bml">Site Map</A><BR>
</FONT>
</TD>
</TR>
</TABLE>
-->
</BODY>
</HTML>
<=PAGE

View File

@@ -0,0 +1,622 @@
# LiveJournal.com-specific library
#
# This file is NOT licensed under the GPL. As with everything in the
# "ljcom" CVS repository, this file is the property of Danga
# Interactive and is made available to the public only as a reference
# as to the best way to modify/extend the base LiveJournal server code
# (which is licensed under the GPL).
#
# Feel free to read and learn from things in "ljcom", but don't use
# our schemes because we don't want your site looking like
# LiveJournal.com (our logo and site scheme are our identity and we
# don't want to confuse users)
#
# Instead, use/modify one of the schemes in the "livejournal" repository.
# (Ideally you'd make your own entirely)
#
_parent=>global.look
help=>{Ds}<a href="%%data%%"><img src="<?imgprefix?>/help.gif" alt="(<?_ml Help _ml?>)" title="(<?_ml Help _ml?>)" width='14' height='14' hspace='2' align='absmiddle' border='0'></a>
h1=>{D}<p><span class="heading">%%data%%</span>
h1/follow_choices=>{D}<span class="heading">%%data%%</span>
h2=>{D}<p><span class="heading2">%%data%%</span>
# Banner Header: search results banner, content desriptor, etc...
bh=>{D}<p align="center"><font face="Arial,Helvetica" color="#cc0000" size="-1"><b>%%data%%</b></font>
grin=>{S}&lt;grin&gt;
hr=>{S}<p align="center"><font color=#660066>*</font></p>
newline=>{S}<br />&nbsp;&nbsp;&nbsp;&nbsp;
p=>{DRp}<br />%%data%%
p/follow_p=>{DRps}<br /><img src="<?imgprefix?>/dot.gif" width="1" vspace="6" height="1"><br />%%data%%
emcolor=>{S}#a7c7e8
emcolorlite=>{S}#d9e9f9
altcolor1=>{S}#d9e9f9
altcolor2=>{S}#a7c7e8
de=>{DRp}<span style="color:#909090;">%%data%%</span>
standout<=
{DRps}<center><font size="1"><br /></font>
<table cellspacing="0" cellpadding="0" border="0" bgcolor="<?emcolor?>">
<tr align="left">
<td width="7" align="left" valign="top">
<img width="7" height="7" src="<?imgprefix?>/dys/corn_nw.gif" alt="/"></td>
<td height="7">
<img height="7" src="<?imgprefix?>/dot.gif" alt=""></td>
<td width="7" valign="top" align="right">
<img height="7" src="<?imgprefix?>/dys/corn_ne.gif" alt="\"></td>
</tr><tr align="left">
<td width="7">
<img width="7" height="1" src="<?imgprefix?>/dot.gif" alt=""></td>
<td valign="top">
%%data%%
</td>
<td width="7">
<img width="7" height="1" src="<?imgprefix?>/dot.gif" alt=""></td>
</tr><tr>
<td width="7" align=left valign=top>
<img width="7" height="7" src="<?imgprefix?>/dys/corn_sw.gif" alt="\"></td>
<td height="7">
<img height="7" src="<?imgprefix?>/dot.gif" alt=""></td>
<td width="7" valign=top align=right>
<img height="7" src="<?imgprefix?>/dys/corn_se.gif" alt="/"></td>
</tr>
</table>
</center>
<=standout
warningbar<=
{DRps}<div class="warningbar" style="background-image: URL('<?imgprefix?>/message-warning.gif');">
%%data%%
</div>
<=warningbar
errorbar<=
{DRps}<div class="errorbar" style="background-image: URL('<?imgprefix?>/message-error.gif');">
%%data%%
</div>
<=errorbar
soerror=><div style='background-color:#d0eef9; color:red; font-weight:bold; text-align:center'>%%data%%</div>
emailex=><div style='width: 50%; font-family: courier; background-color: #efefef; border: dotted #cdcdcd 2px; padding: 5px;'>%%data%%</div>
######################### choices stuff
choice=>{PRps}<dt><img src="<?imgprefix?>/dys/b_purp.gif" align="absmiddle" width="8" height="8"> <a href="%%data2%%"><font face="Arial,Helvetica"><b>%%data1%%</b></font></a><dd><font size="2">%%data3%%</font>
choices<=
{FRp}<p><div class="choice"><table width="100%" cellpadding="2" cellspacing="5">
<tr>
<td valign="top" width="50%">
<dl>
%%items%%
</dl>
</td>
<td valign="top" width="50%">
<dl>
%%itemsb%%
</dl>
</td>
</tr>
</table></div>
<=choices
ENTRYFORMCSS<=
{Ss}
<style type="text/css">
#EntryForm #MetaInfo {
width: 100%;
}
#EntryForm th {
font-size: .85em;
}
#EntryForm #SubmitBar {
background-color: #dfdfdf;
padding: 5px;
text-align: center;
border: 1px outset #000;
margin-left: auto; margin-right: auto;
}
#MetaInfo tr {
padding-bottom: 10px;
}
#metainfo th {
text-align: left;
}
#mood_preview {
display: none;
}
#datetime_box input, #datetime_box select {
margin-right: 2px;
}
#EntryForm legend {
font-weight: bold;
}
#EntryForm #Options {
margin-left: 0; margin-right: 0; padding: 0;
background-color: #dfdfdf;
border: 1px outset #000;
}
#EntryForm #Options th {
text-align: left;
}
#EntryForm #infobox {
text-align: center;
}
#EntryForm #infobox table {
background-color: #dfdfdf;
border: 2px solid <?emcolor?>;
}
#EntryForm textarea {
border: 1px inset #000;
padding: 2px;
}
#EntryForm #Security option {
padding-left: 18px;
}
#EntryForm #security_public {
background-image: url("<?imgprefix?>/userinfo.gif");
background-repeat: no-repeat;
}
#EntryForm #security_private {
background-image: url("<?imgprefix?>/icon_private.gif");
background-repeat: no-repeat;
}
#EntryForm #security_friends, #EntryForm #security_custom {
background-image: url("<?imgprefix?>/icon_protected.gif");
background-repeat: no-repeat;
}
#EntryForm #UserpicPreviewImage {
border: 1px solid #000;
}
#EntryForm {
width: 100%;
}
</style>
<=ENTRYFORMCSS
##################################################################################
################################### MAIN PAGE ####################################
##################################################################################
PAGE<=
{Fps}<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<?_code
{
my $remote = LJ::get_remote(); # will be requested later and returned from cache
return LJ::LJcom::expresslane_html_comment($remote, $_[0]->{r});
}
_code?>
<head>
<link rel="SHORTCUT ICON" href="<?siteroot?>/favicon.ico">
<link rel="home" title="Home" href="/" />
<link rel="contents" title="Site Map" href="/site/" />
<link rel="help" title="Technical Support" href="/support/" />
<title><?_code {
my $elhash = $_[2];
return $elhash->{'WINDOWTITLE'} || $elhash->{'TITLE'};
} _code?></title>
<?metactype?>
<style type="text/css">
<!--
p, td { font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif; }
li { font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif; }
body { font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif; margin: 0px; }
.navtext { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; color: #FF9900; font-weight: bold}
.navlinks { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; color: #FFFFFF; text-decoration: underline}
a:link { font-family: Verdana, Arial, Helvetica, sans-serif; color: #000066; }
a:visited { font-family: Verdana, Arial, Helvetica, sans-serif; color: #000066; }
a:active { font-family: Verdana, Arial, Helvetica, sans-serif; color: #006699; }
.wtext { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; font-weight: bold; color: #FFFFFF}
.login { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px}
.wtextunbld { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; color: #FFFFFF }
.copy { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; color: #000000}
.heading { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 13px; color: #660066; font-weight: bold}
.heading2 { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 13px; color: #660066; font-style: italic }
.talk-comment { margin-top: 1em; }
.lesstop { margin-top: 2px; }
.formitem { color: #343434; font-size: 1em; }
.formnumber { font-weight: bold; margin-top: 1.8em; font-size: .9em; }
.formitemName { font-weight: bold; font-size: .9em; margin-top: 1.8em; }
.formitemDesc { margin-top: .4em; margin-bottom: .4em; color: #505050; }
.formitemNote { color: #da6320; font-size: .9em; margin-top: .4em; margin-bottom: .4em; }
.formitemFlag { color: #CE0000; font-size: .9em; margin-top: .4em; margin-bottom: .4em; }
.borderedtable { border: solid 1px black; }
.borderedtable th { background-color: #dddddd; border-bottom: solid 1px black; padding-left: 10px; padding-right: 10px; white-space: nowrap; font-size: 0.8em; }
#Comments q { padding-left: 2.5em; font-style: italic; }
.errorbar {
color: #000;
font: 12px Verdana, Arial, Sans-Serif;
background-color: #FFEEEE;
background-repeat: repeat-x;
border: 1px solid #FF9999;
padding: 8px;
margin-top: auto; margin-bottom: auto;
margin-left: auto; margin-right: auto;
width: auto;
text-align: left;
}
.warningbar {
color: #000;
font: 12px Verdana, Arial, Sans-Serif;
background-color: #FFFFDD;
background-repeat: repeat-x;
border: 1px solid #FFCC33;
padding: 8px;
margin-top: auto; margin-bottom: auto;
margin-left: auto; margin-right: auto;
width: auto;
text-align: left;
}
-->
</style>
<script language="JavaScript">
window.onerror = null; // damn javascript.
</script>
<?_code return (! LJ::get_remote() &&
! $LJ::IS_SSL &&
! $LJ::REQ_HEAD_HAS{'chalresp_js'}++) ?
$LJ::COMMON_CODE{'chalresp_js'} : "";
_code?>
<?_code
use strict;
my $crumb_up;
if(LJ::get_active_crumb() ne '')
{
my $parentcrumb = LJ::get_parent_crumb();
$crumb_up = "<link rel='up' title='$parentcrumb->[0]' href='$parentcrumb->[1]' />";
}
return $crumb_up;
_code?>
%%head%%
</head>
<body bgcolor="#FFFFFF" background="<?imgprefix?>/dys/bg.gif" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0" text="#000000" link="#660066" vlink="#000066" alink="#CC6600" %%bodyopts%%>
<basefont face="Verdana,Arial,Helvetica">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr align="left" valign="top">
<td colspan='2'>
<table width='100%' border="0" cellspacing="0" cellpadding="0" background="<?imgprefix?>/dys/bg_top.gif">
<tr>
<td><a href="<?siteroot?>/"><img src="<?imgprefix?>/dys/logo1.gif" width="122" height="51" border="0"></a></td>
<td width="163" align="left" valign="top"><a href="<?siteroot?>/"><img src="<?imgprefix?>/dys/logo2.gif" width="170" height="51" border="0"></a></td>
<td background="<?imgprefix?>/dys/bg_top.gif" align="left" valign="top" width="244">&nbsp;</td>
<td background="<?imgprefix?>/dys/bg_top.gif" align="left" valign="top" width="100%">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<!-- logo, then search & logged in links bar stack on top of each other -->
<tr align="left" valign="top">
<td width="<?_ml dystopia.nav.width _ml?>" height="49"
><?_code
unless ($BML::COOKIE{'langpref'}) {
return '<img src="<?imgprefix?>/dys/logo3-lang.gif" width="122" height="52" border="0" ismap="ismap" usemap="#setlang"><map name="setlang"><area href="/manage/siteopts.bml" shape="rect" coords="50,25,122,50"></map>';
} else {
return '<img src="<?imgprefix?>/dys/logo3.gif" width="122" height="52" border="0">';
}
_code?></td>
<td height="49">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<colgroup span="3">
<col width="19%" />
<col width="34%" />
<col width="47%" />
</colgroup>
<!-- search bar -->
<tr valign="top">
<td height="24" width="19%" align="left">&nbsp;</td>
<form action="/multisearch.bml">
<td height="24" align="right" valign="middle" colspan="2" nowrap="nowrap">
<font face="verdana, arial, sans-serif" color=#333333 size=-2>
<span class="wtextunbld"><label for='searchlj'><?_ml dystopia.searchlj _ml?></label>&nbsp;</span>
<?_code
#BML:cache
my $ret;
my ($cur, $val) = ("user", "");
my ($uri, $args) = (BML::get_uri(), BML::get_query_string());
if ($uri eq '/interests.bml' && $args =~ /int=(.+?)(&|$)/) {
$cur = "int";
$val = LJ::durl($1);
}
if ($FORM{'s_loc'}) {
$cur = "region";
}
my $hval = LJ::ehtml($val);
$ret .= "<input id='searchlj' type='text' name='q' size='15' class='login' value='$hval'> ";
$ret .= '<select style="FONT-SIZE: 10px; FONT-FAMILY: verdana, arial, helvetica" name=type>';
foreach my $it (
["user", BML::ml("Username")],
["email", BML::ml("Email")],
["region", BML::ml("dystopia.search.region")],
["int", BML::ml("dystopia.search.int")],
["aolim", BML::ml("dystopia.search.aolim")],
["icq", BML::ml("dystopia.search.icq")],
["yahoo", BML::ml("dystopia.search.yahoo")],
["msn", BML::ml("dystopia.search.msn")],
["jabber", BML::ml("dystopia.search.jabber")],
) {
next if ($it->[0] eq "region" && $LJ::DISABLED{'directory'});
my $sel = $cur eq $it->[0] ? " SELECTED" : "";
$ret .= "<option value=$it->[0]$sel>$it->[1]";
}
return BML::noparse($ret);
_code?>
</select>
<img src="<?imgprefix?>/dot.gif" width="1" height="5">
<input type=submit value="<?_ml btn.search _ml?>" class="login">
</font>
</td></form>
</tr>
<!-- /search livejournal bar -->
<!-- logged in bar -->
<tr>
<td height="27" class="wtext" width="53%" colspan="2" nowrap="nowrap" valign="middle">
<?_code
#BML:cache
if (LJ::get_remote()) {
return BML::noparse(BML::ml("dystopia.hello_loggedin", { 'username' => LJ::get_remote()->{'user'} }));
} else {
return BML::noparse(BML::ml("dystopia.hello_anonymous"))
}
_code?></td>
<td height="27" width="47%" nowrap="nowrap" align="right" valign="middle">
<a href="/"><span class="navlinks"><?_ml dystopia.nav.home _ml?></span></a> <span class="navtext">|</span>
<a href="/site/"><span class="navlinks"><?_ml dystopia.nav.sitemap _ml?></span></a> <span class="navtext">|</span>
<a href="/news.bml"><span class="navlinks"><?_ml dystopia.nav.news _ml?></span></a> <span class="navtext">|</span>
<a href="/manage/siteopts.bml"><span class="navlinks"><?_ml dystopia.nav.siteopts _ml?></span></a> <span class="navtext">|</span>
<a href="/support/"><span class="navlinks"><?_ml Help _ml?></span></a>
<?_code
#BML:cache
my $r = LJ::get_remote();
if ($r) {
return BML::noparse(' <span class="navtext">|</span> <a href="/logout.bml?user=' . "$r->{'user'}&amp;sessid=$r->{'_session'}->{'sessid'}" . '"><span class="navlinks">' . BML::ml("dystopia.nav.logout") . '</span></a>');
}
return;
_code?>
<img src="<?imgprefix?>/dys/5x5.gif" width="10" height="5"></td>
</tr>
<!-- /logged in bar -->
</table>
</td>
</tr>
<!-- /logo, search, logged in bar -->
<!-- left sidebar and body -->
<tr align="left" valign="top">
<td bgcolor="#336699" width="<?_ml dystopia.nav.width _ml?>" height="813">
<table width="<?_ml dystopia.nav.width _ml?>" border="0" cellspacing="0" cellpadding="10">
<?_code
#BML:cache
my @nav;
my $remote = LJ::get_remote();
if ($remote) {
push @nav, { 'name' => BML::ml('dystopia.navhead.journal'),
'links' => [ { 'url' => '/update.bml',
'text' => BML::ml('dystopia.nav.updatejournal'), },
{ 'url' => "/users/$remote->{'user'}/",
'text' => BML::ml('dystopia.nav.journalrecent'), },
{ 'url' => "/users/$remote->{'user'}/calendar",
'text' => BML::ml('dystopia.nav.journalcalendar'), },
{ 'url' => "/users/$remote->{'user'}/friends",
'text' => BML::ml('dystopia.nav.journalfriends'),
'extra' => '/friends/filter.bml', },
{ 'url' => "/userinfo.bml?user=$remote->{'user'}",
'text' => BML::ml('dystopia.nav.journalinfo'),
'extra' => "/userinfo.bml?user=$remote->{'user'}&mode=full",
},
{ 'url' => "/tools/memories.bml?user=$remote->{'user'}",
'text' => BML::ml('dystopia.nav.memories'), },
{ 'url' => "/editjournal.bml",
'text' => BML::ml('dystopia.nav.editentries'), },
],
};
push @nav, { 'name' => BML::ml('dystopia.navhead.settings'),
'links' => [ { 'url' => '/manage/',
'text' => BML::ml('dystopia.nav.manage') },
{ 'url' => '/editinfo.bml',
'text' => BML::ml('dystopia.nav.personalinfo') },
{ 'url' => "/friends/edit.bml",
'text' => BML::ml('dystopia.nav.editfriends'), },
{ 'url' => "/editpics.bml",
'text' => BML::ml('dystopia.nav.editpics'), },
{ 'url' => "/changepassword.bml",
'text' => BML::ml('dystopia.nav.editpassword'), },
{ 'url' => "/modify.bml",
'text' => BML::ml('dystopia.nav.modifyjournal'), },
{ 'url' => "/styles/edit.bml",
'text' => BML::ml('dystopia.nav.editstyle'), },
],
};
}
else
{
push @nav, { 'name' => BML::ml('dystopia.navhead.welcome'),
'links' => [
{ 'url' => '/login.bml',
'text' => BML::ml('dystopia.nav.login'), },
{ 'url' => '/create.bml',
'text' => BML::ml('dystopia.nav.createjournal'), },
{ 'url' => "/update.bml",
'text' => BML::ml('dystopia.nav.updatejournal'), },
],
};
}
push @nav, { 'name' => BML::ml('dystopia.navhead.findusers'),
'links' => [
{ 'url' => '/random.bml',
'text' => BML::ml('dystopia.nav.findrandom'), },
$LJ::DISABLED{'directory'} ? () :
(
{ 'url' => '/directory.bml',
'text' => BML::ml('dystopia.nav.findregion'), }
),
{ 'url' => '/community/',
'text' => BML::ml('dystopia.nav.findcomm'), },
{ 'url' => '/interests.bml',
'text' => BML::ml('dystopia.nav.findint'), },
$LJ::DISABLED{'directory'} ? () :
(
{ 'url' => '/directorysearch.bml',
'text' => BML::ml('dystopia.nav.finddir'), }
),
],
};
push @nav, { 'name' => 'LiveJournal',
'links' => [
{ 'url' => '/download/',
'text' => BML::ml('dystopia.nav.download'), },
{ 'url' => '/paidaccounts/',
'text' => BML::ml('dystopia.nav.paidaccts'), },
{ 'url' => '/pay/',
'text' => BML::ml('dystopia.nav.paymentarea'), },
],
};
push @nav, { 'name' => BML::ml('dystopia.navhead.help'),
'links' => [ { 'url' => '/support/faq.bml',
'text' => BML::ml('dystopia.nav.faq'), },
{ 'url' => '/support/',
'text' => BML::ml('dystopia.nav.support'), },
{ 'url' => '/lostinfo.bml',
'text' => BML::ml('dystopia.nav.lostinfo'), },
{ 'url' => '/developer/',
'text' => BML::ml('dystopia.nav.developer'), },
{ 'url' => '/press/staff.bml',
'text' => BML::ml('dystopia.nav.contact'), },
],
};
push @nav, { 'name' => BML::ml('dystopia.navhead.legal'),
'links' => [ { 'url' => '/tos.html',
'text' => BML::ml('dystopia.nav.legaltos'), },
{ 'url' => '/privacy.bml',
'text' => BML::ml('dystopia.nav.legalprivacy'), },
# { 'url' => '/legal/dmca.bml',
# 'text' => BML::ml('dystopia.nav.legaldmca'), },
],
};
my $ret = $LJ::DYS_LEFT_TOP;
foreach my $sec (@nav) {
$ret .= "<tr align=left valign=top><td><p><span class=navtext>$sec->{'name'}</span><br />";
foreach my $l (@{$sec->{'links'}}) {
$ret .= "<a href=\"$l->{'url'}\"><span class=navlinks>$l->{'text'}</span></a>";
if ($l->{'extra'}) {
$ret .= " <a href=\"$l->{'extra'}\"><span class=navlinks>...</span></a>";
}
$ret .= "<br />";
}
$ret .= "</td></tr>";
}
return BML::noparse($ret);
_code?>
<tr align="left" valign="top">
<td>&nbsp;</td>
</tr>
<tr align="left" valign="top">
<td>&nbsp;</td>
</tr>
<tr align="left" valign="top">
<td>&nbsp;</td>
</tr>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
</td>
<td height="813" bgcolor="#FFFFFF">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<!-- login bar -->
<?_code
#BML:cache
#WITHPORTAL: unless ($remote || BML::get_uri() eq "/") {
my $remote = LJ::get_remote();
unless ($remote || BML::get_uri eq '/login.bml') {
my $button;
my $logincaption = BML::ml('dystopia.btn.login');
if ($logincaption eq 'LOGIN') {
if (! $LJ::IS_SSL) {
$button = "<input type=image onclick='return sendForm()' src='$LJ::IMGPREFIX/dys/login_but.gif' width='48' height='15' border='0'>";
} else {
$button = "<input type=image src='$LJ::IMGPREFIX/dys/login_but.gif' width='48' height='15' border='0'>";
}
} else {
if (! $LJ::IS_SSL) {
$button = "<input type='submit' onclick='return sendForm()' value='$ML{'dystopia.btn.login'}' style='margin-top: 0px; margin-bottom: 1px; font-weight: bold; height: 19px; border: 1px solid #ffffff; background: #336699 none; color: #ffffff; padding-left: 0px; padding-right: 0px'></td>";
} else {
$button = "<input type='submit' value='$ML{'dystopia.btn.login'}' style='margin-top: 0px; margin-bottom: 1px; font-weight: bold; height: 19px; border: 1px solid #ffffff; background: #336699 none; color: #ffffff; padding-left: 0px; padding-right: 0px'></td>";
}
}
my $chal = LJ::challenge_generate(300);
return <<"END_LOGIN_BAR";
<form action="/login.bml" method="post" id='login'>
<input type="hidden" name="mode" value="login" />
<input type='hidden' name='chal' id='login_chal' value='$chal' />
<input type='hidden' name='response' id='login_response' value='' />
<tr>
<td align="right" valign="top" bgcolor="#FFFFFF">
<table border='0' cellspacing='0' cellpadding='0' width='200' align='right'>
<tr>
<td align="left" valign="bottom" bgcolor="#660066"><img src="<?imgprefix?>/dys/lg_crnrgif.gif" width="14" height="23"></td>
<td align="right" valign="middle" bgcolor="#660066" class="wtextunbld" nowrap="nowrap">&nbsp;$ML{'Username'}:&nbsp;</td>
<td align="center" valign="top" bgcolor="#660066" class="wtext" nowrap="nowrap"><input type="text" name="user" size="15" maxlength="15" class="login" style="<?loginboxstyle?>"></td>
<td align="right" valign="middle" bgcolor="#660066" class="wtextunbld" nowrap="nowrap">&nbsp;$ML{'Password'}:&nbsp;</td>
<td align="center" valign="top" bgcolor="#660066" class="wtext" nowrap="nowrap"><input type="password" name="password" size="10" id='xc_password' class="login"></td>
<td align="center" valign="middle" bgcolor="#660066" nowrap="nowrap">&nbsp;$button</tr>
</table>
</td></tr>
</form>
END_LOGIN_BAR
}
return;
_code?>
<!-- /login bar -->
<tr align="left" valign="top" bgcolor="#ffffff">
<td height="585" colspan="7">
<!-- body area -->
<table border="0" cellspacing="0" cellpadding="10" width="100%"><tr><td>
<?breadcrumbs?>
%%pretitle%%
<font size="+2" face="Verdana, Arial, Helvetica" color=#000066>%%title%%</font><p>
%%body%%
</td></tr></table>
<!-- /body area -->
</td></tr></table>
</td></tr></table>
</body></html>
<=PAGE

View File

@@ -0,0 +1,379 @@
_parent=>../../lj-bml-blocks.pl
loginboxstyle=>{Ss}background: url(<?imgprefix?>/userinfo.gif) no-repeat; background-color: #fff; background-position: 0px 1px; padding-left: 18px; color: #00C; font-weight: bold;
commloginboxstyle=>{Ss}background: url(<?imgprefix?>/community.gif) no-repeat; background-color: #fff; background-position: 0px 2px; padding-left: 19px; color: #00C; font-weight: bold;
SECURITYPRIVATE=>{Ss}<img src="<?imgprefix?>/icon_private.gif" width=16 height=16 align=absmiddle>
SECURITYPROTECTED=>{Ss}<img src="<?imgprefix?>/icon_protected.gif" width=14 height=15 align=absmiddle>
LJUSER=>{DRs}<span class='ljuser' style='white-space:nowrap;'><a href='/userinfo.bml?user=%%data%%'><img src='<?imgprefix?>/userinfo.gif' alt='userinfo' width='17' height='17' style='vertical-align:bottom;border:0;' /></a><a href='/users/%%data%%/'><b>%%data%%</b></a></span>
LJCOMM=>{DRs}<span class='ljuser' style='white-space:nowrap;'><a href='/userinfo.bml?user=%%data%%'><img src='<?imgprefix?>/community.gif' alt='userinfo' width='16' height='16' style='vertical-align:bottom;border:0;' /></a><a href='/community/%%data%%/'><b>%%data%%</b></a></span>
LJUSERF=>{DRs}<span class='ljuser' style='white-space:nowrap;'><a href='/userinfo.bml?user=%%data%%&amp;mode=full'><img src='<?imgprefix?>/userinfo.gif' alt='userinfo' width='17' height='17' style='vertical-align:bottom;border:0;' /></a><a href='/users/%%data%%/'><b>%%data%%</b></a></span>
HELP=>{DR}(<a href="%%data%%"><i>help</i></a>)
INERR=>{DR}<font color="#ff0000"><b>%%data%%</b></font>
SOERROR=>{DR}<div><b>%%data%%</b></div>
EMAILEX=><div style='font-family: courier; border: solid black 1px; padding: 5px;'>%%data%%</div>
ENTRYFORMCSS<=
{Ss}
<style type="text/css">
#EntryForm #MetaInfo {
width: 100%;
}
#EntryForm th {
font-size: 1em;
}
#EntryForm #SubmitBar {
background-color: #dfdfdf;
padding: 5px;
text-align: center;
border: 1px outset #000;
margin-left: auto; margin-right: auto;
}
#MetaInfo tr {
padding-bottom: 10px;
}
#metainfo th {
text-align: left;
}
#mood_preview {
display: none;
}
#datetime_box input, #datetime_box select {
margin-right: 2px;
}
#EntryForm legend {
font-weight: bold;
}
#EntryForm #Options {
margin-left: 0; margin-right: 0; padding: 0;
background-color: #dfdfdf;
border: 1px outset #000;
}
#EntryForm #Options th {
text-align: left;
}
#EntryForm #infobox {
text-align: center;
}
#EntryForm #infobox table {
background-color: #dfdfdf;
border: 2px solid <?emcolor?>;
}
#EntryForm textarea {
border: 1px inset #000;
padding: 2px;
}
#EntryForm #Security option {
padding-left: 18px;
}
#EntryForm #security_public {
background-image: url("<?imgprefix?>/userinfo.gif");
background-repeat: no-repeat;
}
#EntryForm #security_private {
background-image: url("<?imgprefix?>/icon_private.gif");
background-repeat: no-repeat;
}
#EntryForm #security_friends, #EntryForm #security_custom {
background-image: url("<?imgprefix?>/icon_protected.gif");
background-repeat: no-repeat;
}
#EntryForm #UserpicPreviewImage {
border: 1px solid #000;
}
#EntryForm {
width: 100%;
}
</style>
<=ENTRYFORMCSS
NEEDLOGIN<=
<?h1 <?_ml bml.needlogin.head _ml?> h1?>
<?p <?_ml bml.needlogin.body2 _ml?> p?>
<=NEEDLOGIN
BADINPUT<=
<?h1 <?_ml bml.badinput.head _ml?> h1?>
<?p <?_ml bml.badinput.body _ml?> p?>
<=BADINPUT
REQUIREPOST=><?_ml bml.requirepost _ml?>
LOAD_PAGE_INFO<=
<?_code
#line 3
@sidebar = ({ 'name' => 'Home',
'uri' => '/',
'match' => "^/(index\\.bml)?(\\?.*)?\$",
'children' => [
{ 'name' => BML::ml('ljrlook.nav.create'),
'uri' => '/create.bml', },
{ 'name' => BML::ml('ljrlook.nav.update'),
'uri' => '/update.bml',
'children' => [
{ 'name' => BML::ml('ljrlook.nav.fullupdate'),
'uri' => '/update.bml?mode=full', }
],
},
# { 'name' => 'Download',
# 'uri' => '/download/', },
],
},
{ 'name' => BML::ml('ljrlook.nav.site'),
'children' => [
{ 'name' => BML::ml('ljrlook.nav.news'),
'match' => '^/news\\.bml\$',
'uri' => '/community/ljr_news/', },
{ 'name' => BML::ml('ljrlook.nav.siteopts'),
'uri' => '/manage/siteopts.bml', },
{ 'name' => 'Sitemap',
'uri' => '/site/', },
{ 'name' => BML::ml('ljrlook.nav.paidaccounts'),
'uri' => '/paidaccounts/',
# 'recursematch' => '^/paidaccounts/',
# 'children' => [
# { 'name' => 'Is this safe?',
# 'uri' => '/paidaccounts/whysafe.bml', },
# { 'name' => 'Progress',
# 'uri' => '/paidaccounts/progress.bml', },
# ],
},
{ 'name' => BML::ml('ljrlook.nav.ljfif'),
'uri' => '/users/ljr_fif/friends', },
# { 'name' => 'To-Do list',
# 'uri' => '/todo.bml', },
# { 'name' => 'Contributors',
# 'uri' => '/contributors.bml', },
],
},
# { 'name' => 'Find Users',
# 'children' => [
# { 'name' => 'Random!',
# 'uri' => '/random.bml', },
# { 'name' => 'By Region',
# 'uri' => '/directory.bml', },
# { 'name' => 'By Interest',
# 'uri' => '/interests.bml', },
# { 'name' => 'Search',
# 'uri' => '/directorysearch.bml', }
# ], },
{ 'name' => BML::ml('ljrlook.nav.edit'),
'children' => [
{ 'name' => BML::ml('ljrlook.nav.editinfo'),
'uri' => '/editinfo.bml', },
# { 'name' => 'Settings', cont => 1,
# 'uri' => '/editinfo.bml', },
{ 'name' => BML::ml('ljrlook.nav.editfriends'),
'uri' => '/friends/edit.bml', },
{ 'name' => BML::ml('ljrlook.nav.editjournal'),
'uri' => '/editjournal.bml', },
{ 'name' => BML::ml('ljrlook.nav.editpics'),
'uri' => '/editpics.bml', },
{ 'name' => BML::ml('ljrlook.nav.changepassword'),
'uri' => '/changepassword.bml', },
{ 'name' => BML::ml('ljrlook.nav.modify'),
'uri' => '/modify.bml', },
# { 'name' => 'Import',
# 'uri' => '/import.bml' },
],
},
{ 'name' => BML::ml('ljrlook.nav.communities.manage'),
'uri' => '/community/manage.bml'
},
# { 'name' => 'Developer Area',
# 'uri' => '/developer/',
# 'match' => "^/developer/\$",
# 'recursematch' => "^/developer/",
# 'children' => [
# { 'name' => 'Style System',
# 'uri' => '/developer/styles.bml',
# 'children' => [
# { 'name' => 'View Types',
# 'uri' => '/developer/views.bml', },
# { 'name' => 'Variable List',
# 'uri' => '/developer/varlist.bml', },
# ],
# },
# { 'name' => 'Embedding',
# 'uri' => '/developer/embedding.bml', },
# { 'name' => 'Protocol',
# 'uri' => '/developer/protocol.bml',
# 'children' => [
# { 'name' => 'Mode List',
# 'uri' => '/developer/modelist.bml', }
# ],
# },
# ],
# },
# { 'name' => BML::ml('ljrlook.nav.frills'),#Styles,customization
# 'children' => [
{ 'name' => BML::ml('ljrlook.nav.customize'),
'uri' => '/customize/', },
# { 'name' => BML::ml('ljrlook.nav.createstyle'),
# 'uri' => '/createstyle.bml', },
# { 'name' => BML::ml('ljrlook.nav.editstyle'),
# 'uri' => '/editstyle.bml', },
# ],
# },
{ 'name' => BML::ml('ljrlook.nav.needhelp'),
'children' => [
{ 'name' => BML::ml('ljrlook.nav.lostinfo'),
'uri' => '/lostinfo.bml', },
{ 'name' => BML::ml('ljrlook.nav.support.faq'),
'uri' => '/support/faq.bml', },
# { 'name' => 'Questions',
# 'uri' => '/support/faq.bml', cont => 1, },
{ 'name' => BML::ml('ljrlook.nav.support'),
'uri' => '/support/', },
],
},
);
my $remote = LJ::get_remote();
my $remuser = $remote ? $remote->{'user'} : "";
my $hello_name = $remote ? LJ::User::display_name($remote) : "";
my $uri = BML::get_uri();
if ($remuser ne "" && $uri ne "/logout.bml")
{
my $subdomain = $remuser;
$subdomain =~ s/_/-/g;
unshift @sidebar, { 'name' => BML::ml('ljrlook.nav.hello').", ".$hello_name."!",
'children' => [
{ 'name' => BML::ml('ljrlook.nav.yourjournal'),
'children' => [
{ 'name' => BML::ml('ljrlook.nav.recent'),
'uri' => "/users/$remuser/", },
{ 'name' => BML::ml('ljrlook.nav.calendar'),
'uri' => "/users/$remuser/calendar", },
{ 'name' => BML::ml('ljrlook.nav.friends'),
'uri' => "/users/$remuser/friends",
'extra' => "/friendsfilter.bml",
},
],
},
{ 'name' => BML::ml('ljrlook.nav.userinfo'),
'uri' => "/userinfo.bml?user=$remuser", },
{ 'name' => BML::ml('ljrlook.nav.memories'),
'uri' => "/memories.bml?user=$remuser", },
{ 'name' => BML::ml('ljrlook.nav.logout'),
'uri' => '/logout.bml', },
]
};
} elsif ($uri ne "/login.bml") {
unshift @sidebar, { 'name' => BML::ml('ljrlook.nav.login'),,
'uri' => '/login.bml', }
}
return "";
_code?>
<=LOAD_PAGE_INFO
AL=>{P}<i><a href="%%data1%%">%%data2%%</a></i> <img src="/img/external_link.gif" width='16' height='11' align='absmiddle' />
AWAYLINK=>{P}<i><a href="%%data1%%">%%data2%%</a></i> <img src="/img/external_link.gif" width='16' height='11' align='absmiddle' />
H1=>{D}<h1>%%data%%</h1>
H2=>{D}<h2>%%data%%</h2>
# Banner Header: search results banner, content desriptor, etc...
BH=>{D}<p align='center'><font face="Arial,Helvetica" color="#CC0000" size='-1'><b>%%data%%</b></font>
GRIN=>{S}&lt;grin&gt;
HR=>{S}<hr />
NEWLINE=>{S}<BR>&nbsp;&nbsp;&nbsp;&nbsp;
P=>{D}<P>%%data%%</P>
STANDOUT<=
{D}<blockquote>
<hr />
%%data%%
<hr />
</blockquote>
<=STANDOUT
ERRORBAR<=
{D}<blockquote>
<hr />
%%data%%
<hr />
</blockquote>
<=ERRORBAR
WARNINGBAR<=
{D}<blockquote>
<hr />
%%data%%
<hr />
</blockquote>
<=WARNINGBAR
BADCONTENT<=
<?h1 <?_ml Error _ml?> h1?>
<?p <?_ml bml.badcontent.body _ml?> p?>
<=BADCONTENT
DE<=
%%data%%
<=DE
EMCOLOR=>{S}#c0c0c0
HOTCOLOR=>{S}#ff0000
EMCOLORLITE=>{S}#e2e2e2
ALTCOLOR1=>{S}#eeeeee
ALTCOLOR2=>{S}#dddddd
screenedbarcolor=>{S}#d0d0d0
CHOICE=>{P}<dt><a href="%%data2%%"><font size="+1"><tt><b>%%data1%%</b></tt></font></a><dd><font size="2">%%data3%%</font>
CHOICES<=
{F}<table width="100%" cellpadding="2" cellspacing="5">
<tr>
<td valign='top' width="50%">
<dl>
%%items%%
</dl>
</td>
<td valign='top' width="50%">
<dl>
%%itemsb%%
</dl>
</td>
</tr>
</table>
<=CHOICES
PAGE<=
{Fp}<html>
<head><title>%%title%%</title>%%head%%</head>
<body %%bodyopts%%>
%%body%%
</body>
</html>
<=PAGE
BREADCRUMBS<=
{Fp}<?_code
# where are we
my @crumbs = LJ::get_crumb_path();
return unless @crumbs;
my @ret;
my $count = 0;
foreach my $crumb (@crumbs) {
# put crumbs together
next unless $crumb->[3]; # no blank crumbs
if ($crumb->[3] eq 'dynamic') {
# dynamic
unshift @ret, "<b>$crumb->[0]</b>";
$count++;
} else {
# non-dynamic
unshift @ret, $count++ == 0 ?
"<b>$ML{'crumb.'.$crumb->[3]}</b>" :
$crumb->[1] ne '' ?
"<a href=\"$crumb->[1]\">$ML{'crumb.'.$crumb->[3]}</a>" :
"$ML{'crumb.'.$crumb->[3]}";
}
}
return "<div id='ljbreadcrumbs'>" . join(" : ", @ret) . "</div>";
_code?>
<=BREADCRUMBS

Some files were not shown because too many files have changed in this diff Show More