#!/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.1.1.1 2000/10/05 21:22:12 alessio Exp $ ############################################################################### use strict; use lib '../'; use vars '%I'; use vars '%L'; use Slash; ################################################################# sub main { *I = getSlashConf(); *L = \%Slash::L; 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}: $L{Search} $I{F}{query}", $I{F}{section}); titlebar("99%", "$L{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 < $L{Stories} $L{Comments} $L{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 <$L{This_search_covers_the_name_email_subject_}

EOT my $prev = $I{F}{min} - $I{F}{max}; print linkSearch({ 'link' => "$I{F}{min} $L{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; $sdate = timeCalc($sdate); $cdate = timeCalc($cdate); $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 $L{by} $cname $L{on} $cdate
$L{attached_to} $title $L{posted_on} $sdate $L{by} $aid
EOT } $cursor->finish; print "$L{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 $L{Matches_Left}", 'link' => "$L{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} $L{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 "$L{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' => "$L{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} $L{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) { $time = timeCalc($time); last unless $cnt || ! $I{F}{query}; print $cnt ? $cnt : $x + $I{F}{min}; print " "; print linkStory({ section => $section, sid => $sid, 'link' => "$title" }), qq! $L{by} $aid $L{on} $time $commentcount
!; $x++; } $cursor->finish; print "$L{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;