###############################################################################
# RSS.pm #
# $Date: 06.01.17 $ #
###############################################################################
# YaBB: Yet another Bulletin Board #
# Open-Source Community Software for Webmasters #
# Version: YaBB 2.7.00 #
# Packaged: June 1, 2017 #
# Distributed by: http://www.yabbforum.com #
# =========================================================================== #
# Copyright (c) 2000-2017 YaBB (www.yabbforum.com) - All Rights Reserved. #
# Software by: The YaBB Development Team #
# with assistance from the YaBB community. #
###############################################################################
use strict;
use warnings;
use CGI::Carp qw(fatalsToBrowser);
use English qw(-no_match_vars);
our $VERSION = '2.7.00';
our $rsspmver = 'YaBB 2.7.00 $Revision$';
our @rsspmmods = ();
our $rsspmmods = 0;
if (@rsspmmods) {
$rsspmmods = 1;
}
our ($action);
$action ||= q{};
if ( $action eq 'detailedversion' ) { return 1; }
## language/ paths ##
our (
$boardsdir, $datadir, $memberdir, $scripturl,
%croak, %error_txt, %maintxt,
);
## settings ##
our (
$accept_permafull, $accept_permalink, $cookiepassword,
$debug, $elenable, $enable_ubbc,
$gzcomp, $mbname, $perm_domain,
$perm_spacer, $rss_disabled, $rss_limit,
$rss_message, $rssemail, $showauthor,
$showdate, $symlink, $yymycharset,
);
## system ##
our (
$annboard, $boardname, $boardperms, $curboard, $currentboard,
$date, $iamadmin, $iamgmod, $mydesc, $staff,
$uid, $username, $yydesc, $yymain, $yytitle,
%board, %cat, %catinfo, %director, %INFO,
%subboard, %yy_cookies, @categoryorder, $iamguest,
);
# Change the error routine for here.
local $SIG{__WARN__} = sub { rss_error(@_) };
# Allow us to be called by a system()-like call
# This lets us send data to any language that supports capturing STDOUT.
# Usage is detailed in POD at the bottom.
if ( scalar @ARGV ) { shellaccess(); }
# Is RSS disabled?
if ($rss_disabled) { rss_error('not_allowed'); }
load_censor_list();
# Load YaBBC if it is enabled
if ($enable_ubbc) { require Sources::YaBBC; }
my ($cachedate);
# Read from a single board
sub rss_board {
my $board = $INFO{'board'};
if ( $board && ${ $board{$board} }[0] !~ m/[ht|f]tps?\:\/\//xsm ) {
my $topics = $INFO{'topics'} || $rss_limit || 10;
if ( $rss_limit && $topics > $rss_limit ) { $topics = $rss_limit; }
### Security check ###
get_rss_noaccess($board);
# Now, go into the board and look for the last X topics
open my $BRDTXT, '<', "$boardsdir/$board.txt"
|| rss_error( 'cannot_open', "$boardsdir/$board.txt", 1 );
my @threadlist = <$BRDTXT>;
close $BRDTXT or croak "$croak{'close'} $board.txt";
my $threadcount = @threadlist;
if ( $threadcount < $topics ) { $topics = $threadcount; }
@threadlist = splice @threadlist, 0, $topics;
# Sorting mode
if ( $rss_message == 2 ) {
@threadlist = sort @threadlist;
}
chomp @threadlist;
my $i = 0;
foreach my $t (@threadlist) {
no strict qw(refs);
my (
$mnum, $msub, $mname, $memail, $mdate,
$mreplies, $musername, $micon, $mstate, $ns
) = split /[|]/xsm, $t;
my $curnum = $mnum;
# See if this is a topic that we don't want displayed.
if ( $mstate =~ /h/xsm && !$iamadmin && !$iamgmod ) { next; }
# Does it need to be returned as a 304?
if ( $i == 0 ) { # Do this for the first request only
$cachedate = rfc822date($mdate);
if (
(
$ENV{'HTTP_IF_NONE_MATCH'}
&& $ENV{'HTTP_IF_NONE_MATCH'} eq $cachedate
)
|| ( $ENV{'HTTP_IF_MODIFIED_SINCE'}
&& $ENV{'HTTP_IF_MODIFIED_SINCE'} eq $cachedate )
)
{
send304notmodified();
# Comment this out to test with caching disabled
}
}
( $msub, undef ) = split_splice_move( $msub, 0 );
$msub = from_html($msub);
$msub = to_chars($msub);
# Censor the subject of the thread.
$msub = do_censor($msub);
my $postid = "$mreplies#$mreplies";
if ( $rss_message == 2 ) { $postid = '0#0'; }
$boardname = $INFO{'board'};
my $category = "$mbname/$boardname";
$category = from_html($category);
# Show the minimum stuff (topic title, link to it)
$yymain .= get_rss_mins( $curnum, $msub, $category );
my $post;
our ($TOPIC);
fopen( 'TOPIC', '<', "$datadir/$curnum.txt" )
|| rss_error( 'cannot_open', "$datadir/$curnum.txt", 1 );
if ( $rss_message == 1 ) {
# Open up the thread and read the last post.
while (<$TOPIC>) {
chomp;
if ($_) { $post = $_; }
}
}
elsif ( $rss_message == 2 ) {
$post = <$TOPIC>;
}
fclose('TOPIC') or croak "$croak{'close'} $curnum.txt";
our ($message);
if ($post) {
(
undef, undef, undef, undef, $musername,
undef, undef, undef, $message, $ns
) = split /[|]/xsm, $post;
}
$yymain .= get_rss_aut($musername);
if ($showdate) {
if ( $rss_message == 2 ) {
$mdate = $curnum;
} # Sort by topic creation if requested.
# Get the date how the user wants it.
my $realdate = rfc822date($mdate);
$yymain .= qq~ $realdate
~;
}
$yymain .= get_rss_msg( $message, $curnum, $ns, $musername );
# Finish up the item
$yymain .= q~
~;
$yymain =~ s/data-rel/rel/gxsm;
$i++; # Increment
}
my $brdname = ${ $board{$board} }[0];
$brdname = to_chars($brdname);
$yytitle = $brdname;
$mydesc = q{};
rss_template();
}
else {
our $yysetlocation = "$scripturl?action=RSSrecent";
redirectexit();
}
return;
}
sub rss_recent {
my $topics = $INFO{'topics'} || $rss_limit || 10;
if ( $rss_limit && $topics > $rss_limit ) { $topics = $rss_limit; }
$yytitle = "$topics $maintxt{'214b'}";
if ( $INFO{'catselect'} ) {
@categoryorder = ( $INFO{'catselect'} );
}
my ( $threadlist, $boardnme ) = get_rss_threadlist($topics);
my @threadlist = @{$threadlist};
my %boardname = %{$boardnme};
if ( !@threadlist ) {
my $category = ${ $catinfo{ $categoryorder[0] } }[0];
$yymain .= q~ -
~ . rss_description_trim($category) . q~
~
. rss_description_trim("$scripturl?cat=$categoryorder[0]") . q~
~
. rss_description_trim( $maintxt{'rss_nopost'} ) . qq~
\n~;
}
else {
foreach my $i ( 0 .. $#threadlist ) {
if ( $i == ( $topics - 1 ) ) { last; }
# Opening item stuff
my (
$mdate, $board, $mnum, $msub,
$mname, $memail, $modate, $mreplies,
$musername, $micon, $mstate
) = split /[|]/xsm, $threadlist[$i];
my $curnum = $mnum;
( $msub, undef ) = split_splice_move( $msub, 0 );
$msub = from_html($msub);
$msub = to_chars($msub);
# Censor the subject of the thread.
$msub = do_censor($msub);
# Does it need to be returned as a 304?
my ($permdate);
if ( $i == 0 ) { # Do this for the first request only
$cachedate = rfc822date($mdate);
if (
(
$ENV{'HTTP_IF_NONE_MATCH'}
&& $ENV{'HTTP_IF_NONE_MATCH'} eq $cachedate
)
|| ( $ENV{'HTTP_IF_MODIFIED_SINCE'}
&& $ENV{'HTTP_IF_MODIFIED_SINCE'} eq $cachedate )
)
{
send304notmodified();
# Comment this out to test with caching disabled
}
}
my $postid = "$mreplies#$mreplies";
if ( $rss_message == 2 ) { $postid = '0#0'; }
my $category = "$mbname/$boardname{$board}";
$category = from_html($category);
my $bn = $boardname{$board};
$bn = from_html($bn);
if ( $accept_permalink || $accept_permafull ) {
my $permsub = $msub;
$permdate = permtimer($curnum);
$permsub =~ s/ /$perm_spacer/gsm;
$yymain .= q~ -
~ . rss_description_trim("$bn - $msub") . q~
~
. rss_description_trim(
"$perm_domain/$symlink/$permdate/$board/$curnum")
. q~
~ . rss_description_trim($category) . q~
~
. rss_description_trim(
"$perm_domain/$symlink/$permdate/$board/$curnum")
. qq~\n~;
}
else {
$yymain .= q~
-
~ . rss_description_trim("$bn - $msub") . q~
~
. rss_description_trim("$scripturl?num=$curnum/$postid")
. q~
~ . rss_description_trim($category) . q~
~
. rss_description_trim("$scripturl?num=$curnum/$postid")
. qq~\n~;
}
my $post;
our ($TOPIC);
fopen( 'TOPIC', '<', "$datadir/$curnum.txt" )
|| rss_error( 'cannot_open', "$datadir/$curnum.txt", 1 );
if ( $rss_message == 1 ) {
# Open up the thread and read the last post.
while (<$TOPIC>) {
chomp;
if ($_) { $post = $_; }
}
}
elsif ( $rss_message == 2 ) {
# Open up the thread and read the first post.
$post = <$TOPIC>;
}
fclose('TOPIC') or croak "$croak{'close'} $curnum.txt";
my ( $message, $ns );
if ($post) {
(
undef, undef, undef, undef, $musername,
undef, undef, undef, $message, $ns
) = split /[|]/xsm, $post;
}
$yymain .= get_rss_aut($musername);
$yymain .= get_rss_showdate( $curnum, $mdate );
$yymain .= get_rss_mess( $message, $curnum, $ns, $username );
$yymain .= qq~
\n~;
$yymain =~ s/data-rel/rel/gxsm;
}
}
$boardname = to_chars($boardname);
$curboard ||= q{};
{
no strict qw(refs);
$yydesc = ${ $uid . $curboard }{'description'};
}
rss_template();
return;
}
sub rss_template { # print RSS output
# Generate the lastBuildDate
my $rssdate = rfc822date($date);
# Send out the "Last-Modified" and "ETag" headers so nice readers will ask before downloading.
our $last_modified = our $e_tag = $cachedate || $rssdate;
my $contenttype = 'text/xml';
print_output_header();
# Make the generator look better
my $rss_pmver = $rsspmver;
$rss_pmver =~ s/\$//gxsm;
# Removed per Corey's suggestion: http://www.yabbforum.com/community/YaBB.pl?num=1142571424/20#20
#my $docs = " http://$perm_domain\n" if $perm_domain;
my $mainlink = $scripturl;
$yytitle ||= 'error';
my $tit = "$yytitle - $mbname";
my $descr = q{};
if ( $INFO{'board'} ) {
$mainlink .= "?board=$INFO{'board'}";
$descr = $mydesc;
}
elsif ( $INFO{'catselect'} ) {
$mainlink .= "?catselect=$INFO{'catselect'}";
$descr = qq~$mydesc - $mbname~;
}
$tit = from_html($tit);
$descr = from_html($descr);
my $mn = $mbname;
$mn = from_html($mn);
our $output = qq~
~ . rss_description_trim($tit) . q~
~ . rss_description_trim($mainlink) . q~
~ . rss_description_trim($descr) . q~
~
. rss_description_trim("$maintxt{'w3c_lngcode'}") . q~
~ . rss_description_trim($mn) . qq~
$rssdate
http://blogs.law.harvard.edu/tech/rss
$rss_pmver
30
$yymain
~;
print_html_output_and_finish();
return;
}
sub rss_error {
# This routine is mostly a copy of fatal_error except it uses RSS templating
my ( $e, $t, $v ) = @_;
load_language('Error');
my ( $e_filename, $e_line, $e_subroutine, $l, $ot );
# Gets filename and line where fatal_error was called.
# Need to go further back to get correct subroutine name,
# otherwise will print fatal_error as current subroutine!
( undef, $e_filename, $e_line ) = caller 0;
( undef, undef, undef, $e_subroutine ) = caller 1;
( undef, $e_subroutine ) = split /::/xsm, $e_subroutine;
if ( $t || $e ) {
$ot = "$maintxt{'error_description'}: $error_txt{$e} $t";
}
if ( ( $debug == 1 or ( $debug == 2 && $iamadmin ) )
&& ( $e_filename || $e_line || $e_subroutine ) )
{
$l =
"
$maintxt{'error_location'}: $e_filename
$maintxt{'error_line'}: $e_line
$maintxt{'error_subroutine'}: $e_subroutine";
}
if ($v) { $v = "
$maintxt{'error_verbose'}: $OS_ERROR"; }
if ($elenable) {
$l ||= q{};
$v ||= q{};
fatal_error_logging("$ot$l$v");
}
my $tit = $error_txt{'error_occurred'};
$tit = from_html($tit);
my $ed = "$ot$l$v";
$ed = from_html($ed);
my $mn = from_html($mbname);
$yymain = q~
-
~ . rss_description_trim($tit) . q~
~ . rss_description_trim($ed) . q~
~ . rss_description_trim($mn) . q~
~;
rss_template();
return;
}
sub send304notmodified {
print "Status: 304 Not Modified\n\n" or croak "$croak{'print'} 304";
exit;
}
sub rfc822date {
# Takes a Unix timestamp and returns the RFC-822 date format
# of it: Sat, 07 Sep 2002 9:42:31 GMT
my @gmt_time = split /\s+/xsm, gmtime shift;
return
"$gmt_time[0], $gmt_time[2] $gmt_time[1] $gmt_time[4] $gmt_time[3] GMT";
}
sub rss_description_trim { # This formats the RSS
my @x = @_;
$x[0] =~ s/\s (class|style)\s*=\s*[\x22\x27].+?[\x27\x22]//gxsm;
$x[0] =~ s/&/&\x2338;/gxsm;
$x[0] =~ s/\x22/&\x2334;/gxsm;
$x[0] =~ s/\x27/&\x2339;/gxsm;
$x[0] =~ s/[ ]{2}/ &\x23160;/gxsm;
$x[0] =~ s/&\x2360;/gxsm;
$x[0] =~ s/>/&\x2362;/gxsm;
$x[0] =~ s/[|]/&\x23124;/gxsm;
$x[0] =~ s/[{]/&\x23123;/gxsm;
$x[0] =~ s/[}]/&\x23125;/gxsm;
return $x[0];
}
sub shellaccess {
# Parse the arguments
my (%arguments);
foreach my $i ( 0 .. $#ARGV ) {
if ( $ARGV[$i] =~ /\A\-/xsm ) {
my ( $option, $value );
$option = $ARGV[$i];
$option =~ s/\A\-\-?//xsm;
( $option, $value ) = split /\=/xsm, $option;
$arguments{$option} = $value || q{};
if ( !defined $arguments{$option} ) { $arguments{$option} = 1; }
}
}
### Requirements and Errors ###
require Paths;
require Variables::Settings;
require Sources::Subs;
require Sources::DateTime;
require Sources::Load;
load_cookie(); # Load the user's cookie (or set to guest)
load_usersettings(); # Load user settings
our $language =
what_language(); # Figure out which language file we should be using! :D
get_forum_master();
require Sources::Security;
# Is RSS disabled?
if ($rss_disabled) { rss_error('rss_disabled'); }
$gzcomp = 0; # Disable gzip so we can talk clearly
# Map %arguments to %INFO
foreach my $var (qw(action board catselect topics)) {
$INFO{$var} = $arguments{$var};
}
# Run the subroutine
require Sources::SubList;
my $act = $INFO{'action'};
my ( $file, $sub ) = split /&/xsm, $director{$act};
if ( $file eq 'RSS.pm' ) { &{$sub}(); }
exit;
}
sub get_rss_noaccess {
my ($board) = @_;
if ( access_check( $currentboard, q{}, $boardperms ) ne 'granted' ) {
rss_error('no_access');
}
if ( $board
&& $annboard
&& $annboard eq $board
&& !$iamadmin
&& !$iamgmod )
{
rss_error('no_access');
}
{
no strict qw(refs);
if ( ${ $uid . $currentboard }{'brdpasswr'} ) {
my $cookiename = "$cookiepassword$currentboard$username";
my $crypass = ${ $uid . $currentboard }{'brdpassw'};
if ( !$staff && $yy_cookies{$cookiename} ne $crypass ) {
rss_error('no_access');
}
}
}
return;
}
sub get_rss_mins {
my ( $curnum, $msub, $category ) = @_;
my $mins = q~ -
~ . rss_description_trim($msub) . q~
~;
$mins .= rss_description_trim("$scripturl?num=$curnum") . q~
~ . rss_description_trim($category) . q~
~;
$mins .= rss_description_trim("$scripturl?num=$curnum") . q~
~;
if ( $accept_permalink || $accept_permafull ) {
my $permdate = permtimer($curnum);
$mins = q~
-
~ . rss_description_trim($msub) . q~
~
. rss_description_trim(
"$perm_domain/$symlink/$permdate/$currentboard/$curnum")
. q~
~ . rss_description_trim($category) . q~
~
. rss_description_trim(
"$perm_domain/$symlink/$permdate/$currentboard/$curnum")
. q~
~;
}
return $mins;
}
sub get_rss_aut {
my ($usr) = @_;
my $aut = q{};
no strict qw(refs);
if ($showauthor) {
if ( -e "$memberdir/$usr.vars" ) {
load_user($usr);
if ( !${ $uid . $usr }{'hidemail'} ) {
$aut =
q~~
. rss_description_trim(
"${ $uid . $usr }{'email'} (${ $uid . $usr }{'realname'})")
. q~~;
}
else {
$aut =
q~ ~
. rss_description_trim(
"$rssemail (${ $uid . $usr }{'realname'})")
. qq~\n~;
}
}
}
return $aut;
}
sub get_rss_msg {
my ( $message, $curnum, $ns, $usr ) = @_;
my $mess = q{};
if ($message) {
( $message, undef ) = split_splice_move( $message, $curnum );
$message =~
s/\[code\s*(.*?)\]\n*(.+?)\n*\[\/code\]/$maintxt{'rsscode'}/eigxsm;
{
no strict qw(refs);
if ( $enable_ubbc && !$ns ) {
load_user($usr);
my $displayname = ${ $uid . $usr }{'realname'};
$message = do_ubbc( $message, q{}, $displayname );
}
}
$message = from_html($message);
$message = to_chars($message);
$message = do_censor($message);
$mess =
q~ ~
. rss_description_trim($message)
. q~
~;
}
return $mess;
}
sub get_rss_threadlist {
my ($topics) = @_;
my @threadlist;
my (%boardname);
foreach my $catid (@categoryorder) {
my @bdlist = @{ $cat{$catid} };
my ( $catname, $catperms ) = @{ $catinfo{$catid} };
my $cataccess = cat_access($catperms);
if ( !$cataccess ) { next; }
if ( $INFO{'catselect'} ) {
$yytitle = $catname;
$mydesc = $catname;
}
my ($boardview);
local *get_subboards = sub {
my @brd = @_;
foreach my $brd (@brd) {
( $boardname{$brd}, $boardperms, $boardview ) =
@{ $board{$brd} };
my $access = access_check( $brd, q{}, $boardperms );
if ( !$iamadmin && $access ne 'granted' ) { next; }
{
no strict qw(refs);
if ( !$iamguest && ${ $uid . $brd }{'brdpasswr'} ) {
my $cookiename = "$cookiepassword$brd$username";
my $crypass = ${ $uid . $brd }{'brdpassw'};
if (
!$staff
&& ( !$yy_cookies{$cookiename}
|| $yy_cookies{$cookiename} ne $crypass )
)
{
next;
}
}
}
our ($BOARD);
fopen( 'BOARD', '<', "$boardsdir/$brd.txt", 1 )
|| rss_error( 'cannot_open', "$boardsdir/$brd.txt", 1 );
foreach my $i ( 0 .. ( $topics - 1 ) ) {
my $buffer = <$BOARD>;
if ( !$buffer ) { last; }
chomp $buffer;
my (
$mnum, undef, undef, undef, $mdate,
undef, undef, undef, $mstate
) = split /[|]/xsm, $buffer;
$mdate ||= $mnum;
if ( $rss_message == 2 ) {
$mdate = $mnum;
} # Sort by topic creation if requested.
$mdate = sprintf '%010d', $mdate;
# Check if it's hidden. If so, don't show it
if ( $mstate
&& $mstate =~ /h/xsm
&& !$iamadmin
&& !$iamgmod )
{
next;
}
# Add it to an array, using $mdate as the first value so we can easily sort
push @threadlist, "$mdate|$brd|$buffer";
}
close $BOARD or croak "$croak{'close'} $brd.txt";
if ( $subboard{$brd} ) {
get_subboards( @{ $subboard{$brd} } );
}
}
};
foreach my $curbrd (@bdlist) {
get_subboards($curbrd);
}
}
@threadlist = reverse sort @threadlist;
return ( \@threadlist, \%boardname );
}
sub get_rss_showdate {
my ( $curnum, $mdate ) = @_;
my $rssd = q{};
if ($showdate) {
if ( $rss_message == 2 ) {
$mdate = $curnum;
} # Sort by topic creation if requested.
# Get the date how the user wants it.
my $realdate = rfc822date($mdate);
$rssd = qq~ $realdate\n~;
}
return $rssd;
}
sub get_rss_mess {
my ( $message, $curnum, $ns, $usr ) = @_;
my $mess = q{};
no strict qw(refs);
if ($message) {
( $message, undef ) = split_splice_move( $message, $curnum );
$message =~
s/\[code\s*(.*?)\]\n*(.+?)\n*\[\/code\]/$maintxt{'rsscode'}/eigxsm;
if ( $enable_ubbc && !$ns ) {
load_user($usr);
my $displayname = ${ $uid . $usr }{'realname'};
$message = do_ubbc( $message, q{}, $displayname );
}
$message = from_html($message);
$message = to_chars($message);
$message = do_censor($message);
$mess =
q~ ~
. rss_description_trim($message)
. qq~\n~;
}
return $mess;
}
1;