summaryrefslogtreecommitdiffstats
path: root/cgi-bin/find.cgi
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2025-12-28 21:17:30 +0800
committerSadeep Madurange <sadeep@asciimx.com>2025-12-30 21:46:51 +0800
commit9fec793abe0a73e5cd502a1d1e935e2413b85079 (patch)
treedf5827fbc7032d982c07c06f6d9783751cc6b62d /cgi-bin/find.cgi
parent06c5dc32086a1aaae6e6f48c017ea9b5e331cdf3 (diff)
downloadwww-9fec793abe0a73e5cd502a1d1e935e2413b85079.tar.gz
Search via CGI script.
Diffstat (limited to 'cgi-bin/find.cgi')
-rw-r--r--cgi-bin/find.cgi141
1 files changed, 141 insertions, 0 deletions
diff --git a/cgi-bin/find.cgi b/cgi-bin/find.cgi
new file mode 100644
index 0000000..bad12e7
--- /dev/null
+++ b/cgi-bin/find.cgi
@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+
+use File::Find;
+
+sub escape_html {
+ my $str = shift;
+ $str =~ s/&/&amp;/g;
+ $str =~ s/</&lt;/g;
+ $str =~ s/>/&gt;/g;
+ $str =~ s/"/&quot;/g;
+ $str =~ s/'/&#39;/g;
+ return $str;
+}
+
+my %params;
+if ($ENV{QUERY_STRING}) {
+ foreach my $pair (split /&/, $ENV{QUERY_STRING}) {
+ my ($key, $value) = split /=/, $pair;
+ $value =~ tr/+/ /;
+ $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+ $params{$key} = $value;
+ }
+}
+
+my $search_text = $params{'q'} || '';
+$search_text = substr($search_text, 0, 64);
+$search_text =~ s/[^a-zA-Z0-9 ]//g;
+
+my $directory = '../log/';
+my @results;
+
+my %excluded_files = (
+ 'index.html' => 1, # /log/index.html
+);
+
+if ($search_text =~ /\S/) {
+ find({
+ wanted => sub {
+ # Ignore directories and only process index.html
+ return unless -f $_ && $_ eq 'index.html';
+
+ # Calculate the relative path for the URL (prevents leaking server file structure)
+ my $rel_path = $File::Find::name;
+ $rel_path =~ s|^\Q$directory\E/?||;
+ return if $excluded_files{$rel_path};
+
+ if (open my $fh, '<', $_) {
+ my $content = do { local $/; <$fh> };
+ close $fh;
+
+ if ($content =~ /\Q$search_text\E/i) {
+
+ # Extract Title
+ my ($title) = $content =~ /<title>(.*?)<\/title>/is;
+ $title = $title ? escape_html($title) : $rel_path;
+
+ # Extract the first <p> tag content
+ my ($p_content) = $content =~ /<p[^>]*>(.*?)<\/p>/is;
+
+ # Process the snippet
+ my $snippet = $p_content || "";
+ $snippet =~ s/<[^>]*>//g; # Remove internal tags
+ $snippet =~ s/\s+/ /g; # Collapse whitespace
+
+ # Escape HTML entities AFTER stripping tags
+ # but BEFORE sending to the user to prevent XSS.
+ $snippet = escape_html(substr($snippet, 0, 50));
+ $snippet .= "..." if length($p_content || "") > 50;
+
+ push @results, {
+ path => $File::Find::name,
+ title => $title,
+ snippet => $snippet
+ };
+ }
+ }
+ },
+ no_chdir => 0,
+ follow => 0,
+ }, $directory);
+}
+
+print "Content-Type: text/html\n\n";
+
+my $list;
+if ($search_text eq '') {
+ $list = "<p>Please enter a search term above.</p>";
+} elsif (@results == 0) {
+ $list = "<p>No results found for \"<b>$search_text</b>\".</p>";
+} else {
+ $list = "<ul>";
+ foreach my $res (@results) {
+ my $url = $res->{path};
+ $list .= "<li><a href=\"/$url\">$res->{title}</a><br><small>$res->{snippet}</small></li>";
+ }
+ $list .= "</ul>";
+}
+
+my $safe_search_text = escape_html($search_text);
+
+print <<"HTML";
+<!DOCTYPE html>
+<html lang="en-us">
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>Search</title>
+ <link rel="stylesheet" href="/assets/css/main.css">
+ <link rel="stylesheet" href="/assets/css/skeleton.css">
+</head>
+<body>
+ <div id="nav-container" class="container">
+ <ul id="navlist" class="left">
+ <li><a href="/" class="link-decor-none">hme</a></li>
+ <li><a href="/log/" class="link-decor-none">log</a></li>
+ <li><a href="/projects/" class="link-decor-none">poc</a></li>
+ <li><a href="/about/" class="link-decor-none">abt</a></li>
+ <li class="active"><a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a></li>
+ <li><a href="/feed.xml" class="link-decor-none">rss</a></li>
+ </ul>
+ </div>
+ <main class="container" id="main">
+ <div class="container">
+ <h2>Search</h2>
+ <form action="" method="GET">
+ <input id="search-box" type="text" name="q" value="$safe_search_text">
+ <input id="search-btn" type="submit" value="Search">
+ </form>
+ $list
+ </div>
+ </main>
+ <div class="footer">
+ <div class="container">
+ <div class="twelve columns right container-2">
+ <p id="footer-text">&copy; ASCIIMX - 2025</p>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
+HTML