#!/usr/bin/perl -w ############################################################################### # search.pl - this code is the search page # # Copyright (C) 1997 Rob "CmdrTaco" Malda # malda@slashdot.org # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # # $Id: search.pl,v 1.9 2000/07/11 11:45:01 pudge Exp $ ############################################################################### use strict; use lib '../'; use vars '%I'; use Slash; ################################################################# sub main { *I = getSlashConf(); getSlash(); # Set some defaults $I{F}{query} ||= ""; $I{F}{section} ||= ""; $I{F}{op} ||= ""; $I{F}{min} ||= "0"; $I{F}{max} ||= "30"; $I{F}{threshold} ||= $I{U}{threshold}; $I{F}{'last'} ||= $I{F}{min} + $I{F}{max}; # get rid of bad characters $I{F}{query} =~ s/[^A-Z0-9\'. ]//gi; # Precounting the results, even on fast servers seems to cause severe # performance problems, especially comment searching, where certain # table locks CAN'T be perform, hence making the httpds very unstable. # Counting is therefore removed until a solution can be determined. # #countSearchHits($I{F}{op} || 'stories') if ! $I{F}{hitcount}; #my $hitcount = "($I{F}{hitcount} total matches)" if $I{F}{hitcount}; header("$I{sitename}: Search $I{F}{query}", $I{F}{section}); titlebar("99%", "Searching $I{F}{query}"); searchForm(); if ($I{F}{op} eq "comments") { commentSearch() } elsif ($I{F}{op} eq "users") { userSearch() } elsif ($I{F}{op} eq "stories") { storySearch() } else { print "Invalid operation!
"; } writelog("search", $I{F}{query}) if $I{F}{op} =~ /^(comments|stories|users)$/; footer(); } ################################################################# sub linkSearch { my $C = shift; my $r; foreach (qw[threshold query min author op sid topic section total hitcount]) { my $x = ""; $x = $C->{$_} if defined $C->{$_}; $x = $I{F}{$_} if defined $I{F}{$_} && $x eq ""; $x =~ s/ /+/g; $r .= "$_=$x&" unless $x eq ""; } $r =~ s/&$//; $r = qq!$C->{'link'}!; } ################################################################# sub keysearch { my $keywords = shift; my @columns = @_; my @words = split m/ /, $keywords; my $sql; my $x = 0; foreach my $w (@words) { next if length $w < 3; last if $x++ > 3; foreach my $c (@columns) { $sql .= "+" if $sql; $sql .= "($c LIKE " . $I{dbh}->quote("%$w%") . ")"; } } # void context, does nothing? # substr $sql, 1, length($sql)-1; $sql = "0" unless $sql; $sql .= " as kw"; return $sql; } sub countSearchHits { my $searchtype = shift; my($sqlquery, $kw) = ('SELECT count(*) ', ''); if ($searchtype eq 'comments') { $kw = keysearch($I{F}{query}, 'subject', 'comment'); $sqlquery .= "FROM newstories, comments WHERE "; $sqlquery .= "newstories.sid=" . $I{dbh}->quote($I{F}{sid}) . " AND " if $I{F}{sid}; $sqlquery .= "points >= $I{F}{threshold} "; $sqlquery .= "AND section=" . $I{dbh}->quote($I{F}{section}) if $I{F}{section}; $sqlquery .= " AND" if $I{F}{query}; } elsif ($searchtype eq 'users') { $kw = keysearch($I{F}{query}, 'nickname', 'fakeemail'); $sqlquery .= "FROM users "; $sqlquery .= "WHERE " if $I{F}{query}; } elsif ($searchtype eq 'stories') { $kw = keysearch($I{F}{query}, 'title', 'introtext'); $sqlquery .= ' FROM stories '; $sqlquery .= $I{F}{section} ? <= 0'; WHERE ((displaystatus = 0 and "$I{F}{section}"="") OR (section="$I{F}{section}" and displaystatus>=0)) EOT $sqlquery .= " AND time < now() AND writestatus >= 0 "; $sqlquery .= " AND aid=" . $I{dbh}->quote($I{F}{author}) if $I{F}{author}; $sqlquery .= " AND section=" . $I{dbh}->quote($I{F}{section}) if $I{F}{section}; $sqlquery .= " AND tid=" . $I{dbh}->quote($I{F}{topic}) if $I{F}{topic}; $sqlquery .= " AND " if $I{F}{query}; } if ($I{F}{query}) { $kw =~ s/as kw$//; $kw =~ s/\+/ OR /g; $sqlquery .= " ($kw)"; } my $cursor = $I{dbh}->prepare($sqlquery); $cursor->execute; ($I{F}{hitcount}) = $cursor->fetchrow; # $I{F}{mycountingquery} = "($I{F}{hitcount}) <= $sqlquery
"; } ################################################################# sub searchForm { my $SECT = getSection($I{F}{section}); # Count the number of hits for the entire search if we haven't # done so and we have keywords to search with. # print "$I{F}{mycountingquery}
" if $I{F}{mycountingquery}; my $t = lc $I{sitename}; $t = $I{F}{topic} if $I{F}{topic}; my $tref = getTopic($t); print < EOT print < EOT $I{F}{op} ||= "stories"; my %ch; $ch{$I{F}{op}} = $I{F}{op} ? ' CHECKED' : ''; print < Stories Comments Users
EOT if ($I{F}{op} eq "stories") { selectTopic("topic", $I{F}{topic}); selectGeneric("authors", "author", "aid", "name", $I{F}{author}); } elsif ($I{F}{op} eq "comments") { print < EOT } selectSection("section", $I{F}{section}, $SECT) unless $I{F}{op} eq "users"; print "\n

\n\n"; } ################################################################# sub commentSearch { print <This search covers the name, email, subject and contents of each of the last 30,000 or so comments posted. Older comments are removed and currently only visible as static HTML.

EOT my $prev = $I{F}{min} - $I{F}{max}; print linkSearch({ 'link' => "$I{F}{min} previous matches...", min => $prev }), "

" if $prev >= 0; # select comment ID, comment Title, Author, Email, link to comment # and SID, article title, type and a link to the article my $sqlquery = "SELECT section, newstories.sid, aid, title, pid, subject, writestatus," . getDateFormat("time","d") . "," . getDateFormat("date","t") . ", uid, cid, "; $sqlquery .= keysearch($I{F}{query}, 'subject', 'comment') if $I{F}{query}; $sqlquery .= " 1 as kw " unless $I{F}{query}; $sqlquery .= " FROM newstories, comments WHERE newstories.sid=comments.sid "; $sqlquery .= " AND newstories.sid=" . $I{dbh}->quote($I{F}{sid}) if $I{F}{sid}; $sqlquery .= " AND points >= $I{F}{threshold} "; $sqlquery .= " AND section=" . $I{dbh}->quote($I{F}{section}) if $I{F}{section}; $sqlquery .= " ORDER BY kw DESC, date DESC, time DESC LIMIT $I{F}{min},$I{F}{max} "; if ($I{F}{sid}) { my($t) = sqlSelect("title", "newstories", "sid=" . $I{dbh}->quote($I{F}{sid}) ) || "discussion"; printf "Return to %s

", linkComment({ sid => $I{F}{sid}, pid => 0, subject => $t }); print "

"; return unless $I{F}{query}; } my $cursor = $I{dbh}->prepare($sqlquery); $cursor->execute; my $x = $I{F}{min}; while (my($section, $sid, $aid, $title, $pid, $subj, $ws, $sdate, $cdate, $uid, $cid, $match) = $cursor->fetchrow) { last if $I{F}{query} && !$match; $x++; my $href = $ws == 10 ? "$I{rootdir}/$section/$sid.shtml#$cid" : "$I{rootdir}/comments.pl?sid=$sid&pid=$pid#$cid"; my($cname, $cemail) = sqlSelect("nickname,fakeemail", "users", "uid=$uid"); printf <

%s $subj by $cname on $cdate
attached to $title posted on $sdate by $aid

EOT } $cursor->finish; print "No Matches Found for your query" if $x < 1; # Counting has been removed (see comment at top). # my $remaining = $I{F}{hitcount} - $I{F}{'last'}; print "

", linkSearch({ # 'link' => "$remaining Matches Left", 'link' => "More matches...", min => $x }) unless !$x || $x < $I{F}{max}; } ################################################################# sub userSearch { my $prev = int($I{F}{min}) - $I{F}{max}; print linkSearch({ 'link' => "$I{F}{min} previous matches...", min => $prev }), "

" if $prev >=0; # userSearch REALLY doesn't need to be ordered by keyword since you # only care if the substring is found. my $sqlquery = "SELECT fakeemail,nickname,uid "; $sqlquery .= " FROM users WHERE uid > 0 "; if ($I{F}{query}) { my $kw = keysearch($I{F}{query}, 'nickname', 'ifnull(fakeemail,"")'); $kw =~ s/as kw$//; $kw =~ s/\+/ OR /g; $sqlquery .= "AND ($kw) "; } $sqlquery .= "ORDER BY uid LIMIT $I{F}{min}, $I{F}{max}"; my $c = $I{dbh}->prepare($sqlquery); undef $c unless $c->execute; # print "

$sqlquery
"; return unless $c; my $total = $c->{rows}; my($x, $cnt) = 0; while(my $N = $c->fetchrow_hashref) { my $ln = $N->{nickname}; $ln =~ s/ /+/g; my $fake = $N->{fakeemail} ? <$N->{fakeemail} EOT print <$N->{nickname}   ($N->{uid}) $fake
EOT $x++; } $c->finish; print "No Matches Found for your query" if $x < 1; # Counting has been removed (see comment at top). # my $remaining = $I{F}{hitcount} - $I{F}{'last'}; print "

"; print linkSearch({ # 'link' => "$remaining matches left", 'link' => "More matches...", min => $I{F}{'last'}, # hitcount => $I{F}{hitcount} }) unless !$x || $x < $I{F}{max}; } ################################################################# sub storySearch { my $prev = $I{F}{min} - $I{F}{max}; print linkSearch({ 'link' => "$I{F}{min} previous matches...", min => $prev }), "

" if $prev >= 0; my $sqlquery = "SELECT aid,title,sid," . getDateFormat("time","t") . ", commentcount,section "; $sqlquery .= "," . keysearch($I{F}{query}, "title", "introtext") . " " if $I{F}{query}; $sqlquery .=" ,0 " unless $I{F}{query}; if ($I{F}{query} || $I{F}{topic}) { $sqlquery .= " FROM stories "; } else { $sqlquery .= " FROM newstories "; } $sqlquery .= $I{F}{section} ? <= 0'; WHERE ((displaystatus = 0 and "$I{F}{section}"="") OR (section="$I{F}{section}" and displaystatus>=0)) EOT $sqlquery .= " AND time=0 AND displaystatus>=0"; $sqlquery .= " AND aid=" . $I{dbh}->quote($I{F}{author}) if $I{F}{author}; $sqlquery .= " AND section=" . $I{dbh}->quote($I{F}{section}) if $I{F}{section}; $sqlquery .= " AND tid=" . $I{dbh}->quote($I{F}{topic}) if $I{F}{topic}; $sqlquery .= " ORDER BY "; $sqlquery .= " kw DESC, " if $I{F}{query}; $sqlquery .= " time DESC LIMIT $I{F}{min},$I{F}{max}"; # print "

$sqlquery

"; my $cursor = $I{dbh}->prepare($sqlquery); $cursor->execute; my($x, $cnt) = 0; print " "; while (my($aid, $title, $sid, $time, $commentcount, $section, $cnt) = $cursor->fetchrow) { last unless $cnt || ! $I{F}{query}; print ""; print $cnt ? $cnt : $x + $I{F}{min}; print " "; print linkStory({ section => $section, sid => $sid, 'link' => "$title" }), qq! by $aid on $time $commentcount
!; $x++; } $cursor->finish; print "No Matches Found for your query." if $x < 1; # Counting has been removed (see comment at top). # my $remaining = $I{F}{hitcount} - $I{F}{'last'}; print "

", linkSearch({ 'link' => "More Articles...", min => $I{F}{'last'} }) unless !$x || $x < $I{F}{max}; } main; $I{dbh}->disconnect if $I{dbh}; 1;