321 lines
12 KiB
Perl
Executable File
321 lines
12 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
# vim: set ts=4 sw=4 et :
|
|
|
|
# some variable names:
|
|
# - phonepostid, bid, blobid: all refer to a blob id.
|
|
# - dppid: display phonepost id; comparable to a ditemid, but for a phone post.
|
|
|
|
use strict;
|
|
|
|
use lib "$ENV{'LJHOME'}/cgi-bin";
|
|
use LJ::Blob;
|
|
|
|
package LJ::PhonePost;
|
|
|
|
my $datatypes = {
|
|
0 => { ext => 'mp3', mime => 'audio/mp3' },
|
|
1 => { ext => 'ogg', mime => 'application/ogg' },
|
|
2 => { ext => 'wav', mime => 'audio/wav' },
|
|
};
|
|
|
|
sub may_transcribe {
|
|
my ($u, $remote) = @_;
|
|
return 0 if $remote && $remote->{journaltype} ne 'P';
|
|
return 1 if $remote && $remote->{userid} == $u->{userid};
|
|
LJ::load_user_props($u, 'pp_transallow');
|
|
return 0 if $u->{pp_transallow} == -1;
|
|
my $groupmask = LJ::get_groupmask($u, $remote);
|
|
return 1 if ! $u->{pp_transallow} && $groupmask;
|
|
return ($groupmask & (1 << $u->{pp_transallow})) ? 1 : 0;
|
|
}
|
|
|
|
sub get_phonepost_entry {
|
|
my ($u, $bid) = @_;
|
|
my ($ppe, $memkey);
|
|
|
|
$memkey = [$u->{userid}, "ppe:$u->{userid}:$bid"];
|
|
$ppe = LJ::MemCache::get($memkey);
|
|
|
|
return $ppe if $ppe;
|
|
|
|
my $dbcr = LJ::get_cluster_def_reader($u);
|
|
$ppe = $dbcr->selectrow_hashref(qq{
|
|
SELECT ppe.jitemid, ppe.posttime, ppe.anum,
|
|
ppe.filetype, ub.length, ppe.lengthsecs, ppe.location
|
|
FROM phonepostentry ppe, userblob ub
|
|
WHERE ub.journalid=? AND ppe.userid=ub.journalid
|
|
AND ub.domain=? AND ub.blobid=? AND ppe.blobid=ub.blobid
|
|
}, undef, $u->{'userid'}, LJ::get_blob_domainid("phonepost"), $bid);
|
|
LJ::MemCache::set($memkey, $ppe || 0);
|
|
return $ppe;
|
|
}
|
|
|
|
sub apache_content {
|
|
my ($r, $u, $dppid) = @_;
|
|
my $bid = $dppid >> 8;
|
|
my $ppe = get_phonepost_entry($u, $bid);
|
|
return 404 unless $ppe && $ppe->{jitemid} && $ppe->{anum} == $dppid % 256;
|
|
|
|
# check security of item
|
|
my $logrow = LJ::get_log2_row($u, $ppe->{jitemid});
|
|
return 404 unless $logrow;
|
|
|
|
if ($u->{statusvis} eq 'S' || $logrow->{security} ne "public") {
|
|
# get the remote, ignoring IP, since the request is coming
|
|
# from Akamai/Speedera/etc and the IP won't match
|
|
my $remote = LJ::get_remote({ ignore_ip => 1 });
|
|
|
|
my %GET = $r->args;
|
|
|
|
my $viewall = 0;
|
|
my $viewsome = 0;
|
|
if ($remote && $GET{viewall} && LJ::check_priv($remote, "canview")) {
|
|
LJ::statushistory_add($u->{'userid'}, $remote->{'userid'},
|
|
"viewall", "phonepost: $u->{user}, itemid: $ppe->{jitemid}, statusvis: $u->{'statusvis'}");
|
|
|
|
$viewall = LJ::check_priv($remote, 'canview', '*');
|
|
$viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended');
|
|
}
|
|
|
|
unless ($viewall || $viewsome && $logrow->{security} eq 'public') {
|
|
return 403 unless LJ::can_view($remote, $logrow);
|
|
}
|
|
}
|
|
|
|
# future: if length is NULL, then it's an external reference and we redirect
|
|
$r->header_out("Cache-Control", "must-revalidate, private");
|
|
$r->header_out("Content-Length", $ppe->{length});
|
|
$r->content_type( $datatypes->{ $ppe->{filetype} }->{mime} );
|
|
|
|
# handle IMS requests
|
|
my $last_mod = LJ::time_to_http($ppe->{posttime});
|
|
if (my $ims = $r->header_in("If-Modified-Since")) {
|
|
return 304 if $ims eq $last_mod;
|
|
}
|
|
|
|
$r->header_out("Last-Modified", $last_mod);
|
|
if ($r->header_only) {
|
|
$r->send_http_header();
|
|
return 200;
|
|
}
|
|
|
|
my $buffer;
|
|
if ($ppe->{location} eq 'mogile') {
|
|
# Mogile
|
|
if ( !$LJ::REPROXY_DISABLE{phoneposts} &&
|
|
$r->header_in('X-Proxy-Capabilities') &&
|
|
$r->header_in('X-Proxy-Capabilities') =~ /\breproxy-file\b/i )
|
|
{
|
|
my @paths = LJ::mogclient()->get_paths( "pp:$u->{userid}:$bid", 1 );
|
|
|
|
# reproxy url
|
|
if ($paths[0] =~ m/^http:/) {
|
|
$r->header_out('X-REPROXY-URL', join(' ', @paths));
|
|
}
|
|
# reproxy file
|
|
else {
|
|
$r->header_out('X-REPROXY-FILE', $paths[0]);
|
|
}
|
|
$r->send_http_header();
|
|
}
|
|
else {
|
|
$buffer = LJ::mogclient()->get_file_data("pp:$u->{userid}:$bid");
|
|
$r->send_http_header();
|
|
return 500 unless $buffer && ref $buffer;
|
|
$r->print($$buffer);
|
|
}
|
|
|
|
}
|
|
else {
|
|
# BlobServer
|
|
$r->send_http_header();
|
|
my $ret = LJ::Blob::get_stream($u, 'phonepost',
|
|
$datatypes->{ $ppe->{filetype} }->{ext}, $bid, sub {
|
|
$buffer .= $_[0];
|
|
if (length($buffer) > 50_000) {
|
|
$r->print($buffer);
|
|
undef $buffer;
|
|
}
|
|
});
|
|
$r->print($buffer) if length($buffer);
|
|
return 500 unless $ret;
|
|
}
|
|
|
|
return 200;
|
|
}
|
|
|
|
# if $u_embed and $ditemid are given, they represent an entry
|
|
# in which a phonepost tag has been embedded.
|
|
|
|
sub make_link {
|
|
my ($remote, $uuid, $phonepostid, $mode, $u_embed, $ditemid) = @_;
|
|
$phonepostid += 0;
|
|
|
|
# mode can either be 'notrans', 'bare', or 'rss'
|
|
$mode = "notrans" if $mode && $mode !~ /bare|rss/;
|
|
|
|
my $u = ref $uuid ? $uuid : LJ::load_userid($uuid);
|
|
return $mode eq 'rss' ? "" : "<b>[Invalid user]</b>" unless $u;
|
|
my $userid = $u->{'userid'};
|
|
|
|
my $ppe = get_phonepost_entry($u, $phonepostid);
|
|
return $mode eq 'rss' ? "" : "<b>[Invalid audio link]</b>" unless $ppe;
|
|
|
|
if ($u_embed && $ditemid) {
|
|
# have to check whether the link is embeddable in this entry
|
|
if ($u_embed->{'userid'} == $userid &&
|
|
$ditemid>>8 == $ppe->{'jitemid'}) {
|
|
|
|
# it's the original entry, we're ok
|
|
} else {
|
|
my $accdenied = "<b>[Access to audio link denied]</b>";
|
|
|
|
# log2 row in which the tag is embedded
|
|
my $row = LJ::get_log2_row($u_embed, $ditemid >> 8);
|
|
|
|
# the original log2 row of this tag
|
|
my $row_orig = LJ::get_log2_row($u, $ppe->{'jitemid'});
|
|
return $mode eq 'rss' ? "" : $accdenied unless $row && $row_orig;
|
|
|
|
if ($row_orig->{'security'} eq "public" &&
|
|
$row->{'posterid'} == $userid &&
|
|
$u_embed->{'userid'} != $userid) {
|
|
|
|
# it's public and moved by the same
|
|
# user to a different journal, we're okay
|
|
} else {
|
|
return $mode eq 'rss' ? "" : $accdenied;
|
|
}
|
|
}
|
|
}
|
|
|
|
my $link;
|
|
my $dppid = ($phonepostid << 8) + $ppe->{anum};
|
|
my $ext = $datatypes->{ $ppe->{filetype} }->{ext};
|
|
my $path = LJ::run_hook("url_phonepost", $u, $dppid, $ext) ||
|
|
LJ::journal_base($u) . "/data/phonepost/$dppid.$ext";
|
|
|
|
# make link and just return that if in bare mode
|
|
$link = $ppe->{location} eq 'none' ?
|
|
"<img src='$LJ::IMGPREFIX/phonepost2.gif' alt='' width='35' height='33' />" :
|
|
"<a href='$path'><img src='$LJ::IMGPREFIX/phonepost2.gif' alt='' " .
|
|
"width='35' height='33' border='0' /></a>";
|
|
return $link if $mode eq 'bare';
|
|
|
|
my $K = $ppe->{length} ? int($ppe->{length} / 1024) . "K" : "";
|
|
my $secs = $ppe->{lengthsecs};
|
|
my $duration = $secs ? sprintf("%d:%02d", int($secs/60), $secs%60) : "";
|
|
|
|
# support rss 'enclosures' - podcasting.
|
|
if ($mode eq 'rss') {
|
|
$link = "<enclosure url=\"$path\" length=\"$ppe->{length}\" " .
|
|
"type=\"$datatypes->{ $ppe->{filetype} }->{mime}\" />";
|
|
return $link;
|
|
}
|
|
|
|
# return full table
|
|
my $ret = "<table cellspacing='5' cellpadding='0' border='0' class='ljphonepost'><tr>";
|
|
$ret .= "<td valign='top'>$link</td>";
|
|
$ret .= "<td valign='top'><strong>";
|
|
$ret .= $ppe->{location} eq 'none' ? "PhonePost" : "<a href='$path'>PhonePost</a>";
|
|
$ret .= "</strong><br /><em>$K</em> $duration</td>";
|
|
|
|
unless ($mode eq 'notrans') {
|
|
my $trans_url = "$LJ::SITEROOT/phonepost/transcribe.bml?user=$u->{user}&ppid=$dppid";
|
|
$ret .= "<td valign='top'><a href='$LJ::SITEROOT/phonepost/about.bml'><img src='$LJ::IMGPREFIX/help.gif' width='14' height='14' border='0' alt='(Help)' /></a></td>";
|
|
my $trans = LJ::PhonePost::get_latest_trans($u, $phonepostid);
|
|
if ($trans) {
|
|
my $by;
|
|
if ($trans->{revid} == 1) {
|
|
$by = LJ::ljuser(LJ::get_username($trans->{posterid}));
|
|
} else {
|
|
# multiple users transcribing, or just multiple transcriptions of one user?
|
|
my $memkey = [$u->{userid},"ppetu:$u->{userid}:$phonepostid"];
|
|
my $tu = LJ::MemCache::get($memkey);
|
|
unless (defined $tu) {
|
|
my $dbr = LJ::get_cluster_reader($u);
|
|
$tu = $dbr->selectrow_array("SELECT COUNT(DISTINCT(posterid)) " .
|
|
"FROM phoneposttrans " .
|
|
"WHERE journalid=? AND blobid=?",
|
|
undef, $u->{'userid'}, $phonepostid + 0);
|
|
LJ::MemCache::set($memkey, $tu);
|
|
}
|
|
$by = ($tu == 1) ? LJ::ljuser(LJ::get_username($trans->{posterid})) : "multiple users";
|
|
}
|
|
my $text = LJ::ehtml($trans->{body});
|
|
$text =~ s/\n/<br \/>/g;
|
|
|
|
$ret .= "<td valign='top'><blockquote cite='$path'>“$text”</blockquote><br />".
|
|
"<a href='$trans_url'>Transcribed</a> by: $by</td>";
|
|
} elsif (LJ::PhonePost::may_transcribe($u, $remote)) {
|
|
$ret .= "<td valign='top'>(<a href='$trans_url'>transcribe</a>)</td>";
|
|
} else {
|
|
$ret .= "<td valign='top'>(no transcription available)</td>";
|
|
}
|
|
}
|
|
|
|
$ret .= "</tr></table>";
|
|
return $ret;
|
|
|
|
}
|
|
|
|
sub get_latest_trans {
|
|
my ($u, $id) = @_;
|
|
$id += 0;
|
|
|
|
my $memkey = [$u->{userid},"ppelt:$u->{userid}:$id"];
|
|
my $lt = LJ::MemCache::get($memkey);
|
|
unless (defined $lt) {
|
|
my $dbcr = LJ::get_cluster_def_reader($u);
|
|
return undef unless $dbcr;
|
|
|
|
$lt = "";
|
|
my $latest = $dbcr->selectrow_array("SELECT MAX(revid) FROM phoneposttrans ".
|
|
"WHERE journalid=? AND blobid=?", undef,
|
|
$u->{userid}, $id);
|
|
if ($latest) {
|
|
$lt = $dbcr->selectrow_hashref("SELECT revid,posterid,posttime,subject,body ".
|
|
"FROM phoneposttrans WHERE journalid=? ".
|
|
"AND blobid=? AND revid=?", undef,
|
|
$u->{userid}, $id, $latest);
|
|
}
|
|
LJ::MemCache::set($memkey, $lt);
|
|
}
|
|
return $lt;
|
|
}
|
|
|
|
sub show_phoneposts {
|
|
my ($u_embed, $ditemid, $remote, $eventref) = @_;
|
|
|
|
my $replace = sub {
|
|
my $tag = shift;
|
|
my ($user, $userid, $uobj, $phid, $blobid, $dpid);
|
|
|
|
# old tag <lj-phonepost user='foo' phonepostid='1' />
|
|
# new tag <lj-phonepost journalid='1234567' dpid='10000422' />
|
|
if ($tag =~ m!^journalid=['"](\d+)['"]\s*dpid=['"](\d+)['"]\s*/?$!) {
|
|
($userid, $dpid)=($1,$2);
|
|
|
|
# prefer phonepostid and userid
|
|
$phid = $dpid >> 8 if $dpid;
|
|
|
|
} elsif ($tag =~ m!^(user=['"](\S+)['"])?\s*(phonepostid=['"](\d+)['"])?\s*(userid=['"](\d+)['"])?\s*(blobid=['"](\d+)['"])?\s*/?$!) {
|
|
($user,$phid,$userid,$blobid)=($2,$4,$6,$8);
|
|
|
|
# prefer phonepostid and userid
|
|
$phid = $blobid >> 8
|
|
unless $phid or not $blobid;
|
|
do {
|
|
$uobj = LJ::load_user($user);
|
|
$userid = $uobj->{'userid'};
|
|
} unless $userid or $user eq "";
|
|
}
|
|
|
|
return "<b>[Invalid audio link]</b>" unless $phid and $userid;
|
|
|
|
return make_link($remote, $userid, $phid, 0, $u_embed, $ditemid);
|
|
};
|
|
$$eventref =~ s!<lj-phonepost\s*([^>]*)>!$replace->($1)!eg;
|
|
}
|
|
|
|
1; |