package Slash; use strict; use vars qw($query $imagedir $rootdir $ssidir $sitename $slogan $currentSection $currentMode $userMode $dbh $datadir &getSlash &linkStory &getSection &adminMenu &selectForm &selectGeneric &selectTopic &selectSection &getvars &getvar &setvar &newvar &getapptags &getfile &geturl &prog2file &url2file &getUser &getblock &getsid &getsiddir &writelog &pollbooth &sqlSelectMany &sqlSelect &sqlSelectHash &sqlSelectHashref &sqlUpdate &sqlInsert &sqlconnect &stripByMode &stripBadHtml &approvetag &header &footer &prepEvalBlock &prepBlock &nukeBlockCache &blockCache &titlebar &fancybox &printComments &dispComment &dispStory &displayStory &sendEmail &pollItem &printComments2 &getOlderStories &displayStories &selectStories ¤tAdminUsers); use DBI; use Carp; sub BEGIN { use Exporter (); use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); $VERSION = 0.30; @ISA=qw(Exporter); @EXPORT=qw($query $imagedir $rootdir $ssidir $sitename $slogan $currentSection $currentMode $userMode $dbh $datadir &getSlash &linkStory &getSection &adminMenu &selectForm &selectGeneric &selectTopic &selectSection &getvars &getvar &setvar &newvar &getapptags &getfile &geturl &prog2file &url2file &getUser &getblock &getsid &getsiddir &writelog &pollbooth &sqlSelectMany &sqlSelect &sqlSelectHash &sqlSelectHashref &sqlUpdate &sqlInsert &sqlconnect &stripByMode &stripBadHtml &approvetag &header &footer &prepEvalBlock &prepBlock &nukeBlockCache &blockCache &titlebar &fancybox &printComments &dispComment &dispStory &displayStory &sendEmail &pollItem &printComments2 &getOlderStories &displayStories &selectStories ¤tAdminUsers); #Uncomment the following to enable stack traces: #$Carp::Verbose = 1; $SIG{__WARN__} = sub { carp $_[0] }; $SIG{__DIE__ } = sub { croak $_[0] }; use vars @EXPORT; } $dbh||=DBI->connect("DBI:mysql:slash", "slash", "wegotitallonUHF"); kill 9,$$ unless $dbh; ($imagedir,$rootdir,$datadir,$sitename,$slogan,$ssidir) =getvars("imagedir","rootdir","datadir","sitename","slogan","ssidir") unless $imagedir; $ssidir ||= $rootdir; my %blockBank; my @approvedtags = ( 'B','I','P .*','P','A', 'LI','OL','UL','EM','BR', 'STRONG','BLOCKQUOTE', 'HR','DIV .*','DIV','TT' ); sub getSlash { my ($r)=@_; #$r->content_type("text/html"); require CGI; #use CGI::Switch (); $query = ""; $query = new CGI; my @names = $query->param; my $FORM; foreach (@names) { $$FORM{$_}=$query->param($_) }; print "HTTP/1.1 200 OK Server: $ENV{SERVER_SOFTWARE}\n" unless $$FORM{ssi} eq "yes"; my ($uid,$passwd); my $op=$$FORM{op}; if($op eq "userlogin" and length $$FORM{upasswd} > 1) { ($uid,$passwd)=userLogin($$FORM{unickname},$$FORM{upasswd},$$FORM{expires}); } elsif($op eq "userclose") { print "Set-Cookie: ",$query->cookie(-name=>'user',-value=>' '),"\n"; } elsif($op eq "adminclose") { print "Set-Cookie: ",$query->cookie(-name=>'session',-value=>' '),"\n"; } elsif($query->cookie('user')) { ($uid,$passwd)=userCheckCookie($query->cookie('user')); } else { $uid=-1; } my $USER={getUser($uid,$passwd)}; ($$USER{aid},$$USER{aseclev},$$USER{asection},$$USER{url}) =getadmininfo($query->cookie('session')) if $query->cookie('session'); if($$FORM{op} eq "adminlogin") { ($$USER{aid},$$USER{aseclev})=setadmininfo($$FORM{aaid},$$FORM{apasswd}); } $currentMode=$$USER{mode}=$$FORM{mode}=$$FORM{mode} || $$USER{mode} || "thread"; $$USER{threshold}=$$FORM{threshold}=$$FORM{threshold} || $$USER{threshold} || "0"; $$USER{posttype}=$$FORM{posttype} || "plaintext"; $$USER{seclev}=$$USER{aseclev} if $$USER{asecleev} > $$USER{seclev}; print "Content-Type: text/html\n\n" unless $$FORM{ssi} eq "yes"; return ($FORM,$USER); } sub currentAdminUsers { my($USER)=@_; print "
Authors: "; my $c=sqlSelectMany("distinct aid,lasttitle","sessions", "aid!=".$dbh->quote($$USER{aid})); while(my ($aid,$lasttitle)=$c->fetchrow()) { print " " if $$USER{aseclev} > 10000; print "$aid"; print " " if $$USER{aseclev} > 10000; print " ($lasttitle) " if $lasttitle; } $c->finish(); } sub setupUser { my($section,$mode)=@_; $userMode=$mode eq "flat" ? "_F" : "" ; $currentSection=$section || ""; } sub linkStory { my($text,$mode,$sid,$sect)=@_; $sid=($mode eq "dynamic" or !$sect)?"article.pl?sid=$sid":"$sect/$sid$userMode".".shtml"; return "$text"; } sub getSection { my($section)=@_; return { title=>$slogan,artcount=>30,issue=>3 } unless $section; return sqlSelectHashref("*","sections", "section=".$dbh->quote($section)); } sub ssiHead { #print "$ssidir\n"; print "\n"; } sub ssiFoot { print "\n"; print "\n"; } sub adminMenu { my($USER)=@_; my $seclev=$$USER{aseclev}; return unless $seclev; print "\n"; print " [ Logout $$USER{aid} | Home | Stories | Topics " if $seclev > 0; print " | New " if $seclev > 10; my ($cnt)=sqlSelect("count(*)","submissions"); print " | $cnt Submissions | Blocks | Users | Polls " if $seclev > 499; print " | Sections " if ($seclev > 999 or ($$USER{asection} and $seclev > 499)); print " | Authors | Variables " if $seclev > 10000; print "]
\n\n" if $seclev > 0;
}
# What follows are a bunch of pseudo random functions for advanced HTML widget
# creation. Good if you happen to be lazy :)
sub selectForm
{
my ($table,$label,$default,$where)=@_;
my ($thiscode, $thisname)=sqlSelect("code,name",$table,
"code=".$dbh->quote($default) );
print "\n\n";
}
sub selectGeneric
{
my ($table,$label,$code,$name,$default,$where,$order,$limit)=@_;
my ($thiscode,$thisname)=sqlSelect("$code,$name",$table,"$code=".$dbh->quote($default)) if $default;
$thisname=$default unless $thisname;
print "\n\n";
}
sub selectTopic
{
my($name,$tid)=@_;
selectGeneric("topics",$name,"tid","alttext",$tid);
}
sub selectSection
{
my($name,$section,$SECT,$USER)=@_;
if($SECT && $$SECT{isolate}) {
print "";
} else {
my $where="isolate=0" unless $$USER{aseclev} > 499;
selectGeneric("sections",$name,"section","title",$section,$where);
}
}
sub getvars
{
my @invars=@_;
my @vars;
for(my $x=0;$x<@invars;$x++) {
($vars[$x])=sqlSelect("value","vars","name='$invars[$x]'");
}
return @vars;
}
sub getvar
{
my ($value, $desc)=sqlSelect("value,description","vars","name='$_[0]'");
}
# A few handy functions for getting dates for use with cookies- thanx nate.
sub cookietime
{
my ($time) = @_;
my @nums = gmtime($time);
foreach my $num (@nums) {
if (length($num)==1) { $num = 0 . $num; }
}
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)=@nums;
$year += 1900;
$wday = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday")[$wday];
$mon = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
"Sep", "Nov", "Dec")[$mon-1];
"$wday, $mday-$mon-$year $hour:$min:$sec GMT";
}
# thanks Michael Mittelstadt sqlSelectMany Error SQL Error \n";
if($$USER{mode} ne "archive") {
$message.=". $$S{bodytext} ";
my ($w, $m, $d, $h, $min, $ampm)=split(" ",$time);
$today||=$w;
last TODAY if (++$x >= $cnt and $today ne $w );
}
}
sub getOlderStories
{
my ($SECT,$FORM,$USER,$cursor)=@_;
my ($today,$stuff);
$cursor||=selectStories($SECT,$FORM,$USER);
$cursor->execute();
while(my ($sid, $section, $title, $time, $commentcount, $day)=$cursor->fetchrow) {
my ($w, $m, $d, $h, $min, $ampm)=split(" ",$time);
if($today ne $w) {
$today=$w;
$stuff.= " ";
$stuff.="" if $$SECT{issue} > 1;
$stuff.="$w";
$stuff.="" if $$SECT{issue} > 1;
$stuff.=" $m $d\n";
}
$stuff.=" " if $$SECT{issue};
$stuff.="
\n";
return undef;
kill 9,$$
}
}
sub sqlSelect
{
my ($select, $from, $where, $other)=@_;
my $sql="SELECT $select ";
$sql.="FROM $from " if $from;
$sql.="WHERE $where " if $where;
$sql.="$other" if $other;
$dbh||=sqlconnect();
my $c=$dbh->prepare($sql) or die "Sql has gone away\n";
if(not $c->execute()) {
print "\n
\n";
return undef;
}
my @r=$c->fetchrow();
$c->finish();
return @r;
}
sub sqlSelectHash
{
my $H=sqlSelectHashref(@_);
return map { $_ => $$H{$_} } keys %$H;
}
sub sqlSelectHashref
{
my ($select, $from, $where, $other)=@_;
my $sql="SELECT $select ";
$sql.="FROM $from " if $from;
$sql.="WHERE $where " if $where;
$sql.="$other" if $other;
$dbh||=sqlconnect();
my $c=$dbh->prepare($sql);
my $H = {};
return $H unless $c->execute();
$H=$c->fetchrow_hashref();
$c->finish();
return $H;
}
sub sqlUpdate
{
my($table,$where,%data)=@_;
my $sql="UPDATE $table SET";
foreach (keys %data) {
if (/^-/) {
s/^-//;
$sql.="\n $_ = $data{-$_} ";
} else {
$sql.="\n $_ = ".$dbh->quote($data{$_}).",";
}
}
chop($sql);
$sql.="\nWHERE $where\n";
$dbh||=sqlconnect();
if(!$dbh->do($sql)) {
open FOO,">>$datadir/logs/updatelog";
print FOO $sql;
close FOO;
}
}
sub sqlInsert
{
my($table,%data)=@_;
my($names,$values);
foreach (keys %data) {
if (/^-/) {$values.="\n ".$data{$_}.","; s/^-//;}
else { $values.="\n ".$dbh->quote($data{$_}).","; }
$names.="$_,";
}
chop($names);
chop($values);
my $sql="INSERT INTO $table ($names) VALUES($values)\n";
$dbh||=sqlconnect();
if(!$dbh->do($sql)) {
open FOO,">>$datadir/logs/insertlog";
print FOO $sql." ".$dbh->errstr;
close FOO;
}
}
sub sqlconnect
{
$dbh ||= DBI->connect("DBI:mysql:slash", "slash", "wegotitallonUHF");
# die "Unable to connect to SQL Server" unless $dbh;
kill 9, $$ unless $dbh;
return \$dbh;
}
# Some Random Dave Code:
sub stripByMode
{
my($str,$fmode,$seclev,@apptag)=@_;
$str=stripBadHtml($str,$seclev,@apptag);
if($fmode eq "plaintext" || $fmode eq "exttrans") {
$str=~s/[\n]/
/gi; # pp breaks
$str=~s/\
\
/
/gi;
} elsif($fmode eq "exttrans") {
$str=~s/\&/&/g;
$str=~s/\</g;
$str=~s/\>/>/g;
} elsif($fmode eq "nohtml") {
$str=~s/\<(.*?)\>//g;
}
return $str;
}
sub stripBadHtml
{
my ($str,$seclev,@apptag)=@_;
$str =~ s/(\S{90})/$1 /g;
$str =~ s/<(?!.*?>)//;
$str =~ s/<(.*?)>/approvetag($1,@apptag)/sge; #replace tags with approved ones
return $str;
}
sub approvetag
{
my ($tag,@apptag) = @_;
$tag =~ s/^\s*?(.*)\s*?$/$1/e; #trim leading and trailing spaces
if (uc(substr ($tag, 0, 2)) eq 'A ')
{
$tag =~ s/^.*?href="?(.*?)"?$/A HREF="$1"/i; #enforce "s
return "<" . $tag . ">";
}
foreach my $goodtag (@apptag)
{
$tag = uc $tag;
if ($tag eq $goodtag || $tag eq '/' . $goodtag)
{return "<" . $tag . ">";}
#check against my list of tags
}
return "";
}
# Look and Feel Functions Follow this Point
sub header
{
my ($title,$section,$mode,$ssi) =@_;
setupUser($section,$mode);
$title=~s/\<(.*?)\>//g;
print "
/|/gi;
my $execme=prepBlock($header);
print eval $execme;
print "\nError:$@\n" if $@;
}
sub footer
{
my ($ssi)=@_;
if($ssi eq "yes") {
ssiFoot();
return;
}
my ($section)=$currentSection;
my $motd=blockCache("motd");
my $closelayer="";
my $block=blockCache($section."_footer") || blockCache("footer");
my $menu=blockCache("mainmenu");
my $menu=prepBlock($menu);
my $vertmenu = $menu = eval $menu;
my $horizmenu=$menu;
$horizmenu=~s/\
/|/gi;
my $execme=prepBlock($block);
print eval $execme;
if($@) { print "Error:$@\n" }
}
sub prepEvalBlock
{
my ($b)=@_;
$b=~s/\r//g;
return $b;
}
sub prepBlock
{
my ($b)=@_;
$b=~s/\r//g;
$b=~s/"/\\"/g;
$b="\"$b\";";
return $b;
}
sub nukeBlockCache
{
%blockBank=();
}
sub blockCache
{
my ($bid)=@_;
($blockBank{$bid}) = sqlSelect("block","blocks","bid='$bid'")
unless ($blockBank{$bid});
if(!$blockBank{$bid} or $blockBank{$bid}==-1) {
$blockBank{$bid}=-1;
return "";
} else {
return $blockBank{$bid};
}
}
sub titlebar
{
my ($width, $title) = @_;
my $block=blockCache($currentSection."_titlebar") || blockCache("titlebar");
my $execme=prepBlock($block);
print eval $execme;
if($@) { print "\nError:$@\n" }
}
sub fancybox
{
my ($width, $title, $contents) = @_;
return unless ($title and $contents);
my $mainwidth=$width-4;
my $insidewidth=$mainwidth-8;
my $block=blockCache($currentSection."_fancybox") || blockCache("fancybox");
my $execme=prepBlock($block);
print eval $execme;
if($@) { print "Error:$@\n" }
}
sub printComments2
{
my ($USER,$sid,$pid,$cid)=@_;
$$USER{threshold}||="0";
$pid||="0";
my $message=blockCache("commentswarning")."
( Switch to Flat" : "thread>Threaded";
$message.=" mode ";
$message.=" | Reply" unless getvar("nocomment");
$message.= " )
\<
Down One |
This Page's Threshold: $$USER{threshold} |
Up One >
";
$message.="You are logged in as $$USER{nickname}" if $$USER{uid} > 0;
$message.=" and have $$USER{points} moderator points
left" if $$USER{points};
$message.="
" if $$USER{uid} > 0;
$message.="(Warning:this stuff
might be beta right now)
";
}
my ($commentstatus)=sqlSelect("commentstatus","stories","sid=".$dbh->quote($sid));
my $strsql="SELECT cid,date_format(date,\"\%W \%M \%d, \%Y \@\%h:\%i \%p\") as time,
name,email,url,subject,comment,
nickname,homepage,fakeemail,realname,
users.uid as uid,sig,
comments.points as points,pid,sid,pid
FROM comments,users
WHERE sid=".$dbh->quote($sid);
$strsql.=" AND comments.points >= ".$dbh->quote($$USER{threshold})."
AND comments.uid=users.uid
ORDER BY cid";
my $thisComment=$dbh->prepare($strsql);
$thisComment->execute();
my $comments;
while(my $C=$thisComment->fetchrow_hashref()) {
$$C{commentstatus}=$commentstatus;
$$C{comment}.="
".$$C{sig};
$$comments[$$C{cid}]=$C;
push @{$$comments[$$C{pid}]->{kids}}, $$C{cid};
}
$thisComment->finish();
# Mess with sort order, eg, @$comments[0]->{kids}
# if($$USER{commentorder} == 1) { Reverse
# } else { randomize }
my $lvl=0 if $$USER{mode} eq "flat" or $$USER{mode} eq "archive";
$lvl=1 if $$USER{mode} eq "index";
print " \n";
}
print "";
if($p) {
my $P=$$comments[$p];
print "\<\< $$P{subject}
by $$P{nickname} \n|";
}
if($$C{pid}) {
my $P=$$comments[$$C{pid}];
print " $$P{subject} by $$P{nickname} \n";
}
if($n) {
my $N=$$comments[$n];
print "| $$N{subject}
by $$N{nickname} \>\> \n";
}
print "
";
print "" if $lvl;
foreach my $cid (@{$$comments[$pid]->{kids}}) {
my $C=$$comments[$cid];
if($lvl<1) {
$$C{ppid}=0;
dispComment($USER,$C);
if($$C{kids}) {
print "
" if $lvl;
}
sub dispComment
{
my($USER, $C)=@_;
my $subj=$$C{subject};
my $score=$$C{score};
my $time=$$C{time};
my $comment=$$C{comment};
my $username="";
$username="$$C{nickname}
($$C{fakeemail})" if $$C{fakeemail};
$username||=$$C{nickname};
$$C{nickname}=~s/ /+/g;
my $userinfo;
$userinfo="(User
Info)" unless $$C{nickname} eq "Anonymous+Coward";
my $userurl="$$C{homepage} \n";
}
} else {
my $pcnt=@{$$comments[$$C{pid}]->{kids} }+0;
print "";
my $l=$lvl;
$l++ unless $$USER{mode} eq "archive" or $$USER{mode} eq "flat";
displayComments($USER,$sid,$$C{cid},$l,$comments);
print "
\n" if $pcnt > 49;
print "
\n" if $pcnt < 50;
if($$C{kids}) {
displayComments($USER,$sid,$$C{cid},$lvl+1,$comments);
}
}
}
print "
" if $$C{homepage};
my $score=" (Score:$$C{points})" if $$C{points};
my $template=blockCache($currentSection."_comment") || blockCache("comment");
my $execme=prepBlock($template);
print eval $execme;
if($@) { print "\nError:$@\n" }
if($$USER{mode} ne "archive") {
my($cid,$sid)=($$C{cid},$$C{sid});
print " \n";
print " [ ";
print "Reply to
this " if $$USER{commentstatus}==0;
# Go to parent
if($$C{pid} > 0) {
print " | Parent";
}
if($$USER{seclev}>0) {
print " | Moderate -
+
| Reparent 0
1
top ";
}
if($$USER{aseclev}>100) {
print " | cid = $$C{cid} $$C{pid} $$C{ppid} ";
print " | Delete";
print " ";
}
print " ] ";
}
}
sub dispStory
{
my($USER,$S,$A,$T,$full)=@_;
titlebar("99%",$$S{title});
my $template=blockCache($currentSection."_story") || blockCache("story");
my $bt=$full?"
";
my $author="$$S{aid}";
# Compatibility layer? :)
my ($tid,$topicimage,$width,$height,$alttext,$date,$dept,$introtext,$bodytext)=
($$T{tid},$$T{image},$$T{width},$$T{height},$$T{alttext},$$S{time},
$$S{dept},$$S{introtext},$bt);
my $execme=prepBlock($template);
print eval $execme;
print "\nError:$@\n" if $@;
}
sub displayStory
{
my ($USER,$sid, $full)=@_;
my $S=sqlSelectHashref("title,dept,time as sqltime,
date_format(time,\"\%W \%M \%d, \%Y \@\%h:\%i \%p\") as time,
introtext,sid,commentstatus,
bodytext,aid,tid,section,commentcount,displaystatus,writestatus",
"stories",
"stories.sid=".$dbh->quote($sid));
my $T=sqlSelectHashref("*","topics","tid=".$dbh->quote($$S{tid}));
my $A=sqlSelectHashref("*","authors","aid=".$dbh->quote($$S{aid}));
dispStory($USER,$S,$A,$T,$full);
return ($S,$A,$T);
}
sub pollItem
{
my ($answer, $imagewidth, $votes, $percent) =@_;
my $pi=blockCache("pollitem");
my $execme=prepBlock($pi);
print eval $execme;
if($@) { print "\nError:$@\n" }
}
# Blame Nate for this one :)
sub sendEmail
{
use Socket;
my ($addr, $subject, $content) = @_;
socket (SMTP, 'PF_INET', 'SOCK_STREAM', getprotobyname('tcp'))
or die "socket $!";
connect (SMTP, sockaddr_in(25, inet_aton("127.0.0.1")))
or die "connect $!";
my $line =
(";
print "$cc comment",
$cc>1?"s":"" if $cc;
print ", " if $$S{bodytext} and $cc;
print length($$S{bodytext})." bytes in body"
if($$S{bodytext});
if($$USER{seclev}) {
my ($mods)=sqlSelect("count(lastmod)",
"comments",
"sid='$sid' and lastmod>0");
print ", ",
$mods?$mods:"no",
" moderated comment",
$mods>1?"s":"",
"";
}
print ")";
}
print "
Older
Articles" if $$SECT{issue}==1 or $$SECT{issue}==3;
$stuff.="
Yesterday's
Edition\n" if $$SECT{issue}==2 or $$SECT{issue}==3;
}
$cursor->finish();
return $stuff;
}
sub CLOSE { $dbh->disconnect() if $dbh; }