Add searchapi.cgi.

This commit is contained in:
Darren 'Tadgy' Austin 2023-10-13 18:46:55 +01:00
commit 78f3e7210e

89
cgi-bin/searchapi.cgi Executable file
View file

@ -0,0 +1,89 @@
#!/bin/bash
# Configuration.
STORAGE_PREFIX="/storage/md0"
LOCATE_DB="/tmp/mirrors.db"
DEF_RESULTS=500
MAX_RESULTS=3000
MAX_CONCURRENT=10
IGNORE_REGEXES=('^/\.sandbox.*' '^/\.lftp.*' '^/dead\.letter' '.*\.rsync-tmp.*')
# Extglob is required.
shopt -s extglob
# Output an error in processing.
die() {
# $1 = The error message to output.
printf "%s: %s\\n" "Status" "1"
printf "%s: %s\\n" "Error" "$1"
exit 1
}
# Initial headers.
printf "%s: %s\\n" "Content-type" "text/plain"
printf "%s: %s\\n" "Cache-Control" "no-cache, no-store, must-revalidate"
printf "%s: %s\\n" "Pragma" "no-cache"
printf "%s: %s\\n\\n" "Expires" "1"
# Limit the number of concurrent searches to avoid DoS.
if (( $(lsof -t "$LOCATE_DB" | wc -l) > (MAX_CONCURRENT - 1) )); then
die "Too many concurrent searches"
else
# Lock the DB for the lsof count above.
# shellcheck disable=SC2015
exec 9<"$LOCATE_DB" && flock -s -E 10 -w 2 9 || die "Too many concurrent searches"
# shellcheck disable=SC2154
while read -r -d '&' QUERY; do
# If the read returned an empty string, skip.
[[ -z "$QUERY" ]] && continue
# Extract the key and value to temporary variables.
KEY="${QUERY%%=*}"
VALUE="${QUERY#*=}"
# Check the key is valid as a variable name.
[[ ! "$KEY" =~ ^[[:digit:]_]*[[:alnum:]_]*$ ]] && die "Invalid query - don't try to be clever :)"
# Remove spaces from beginning and end of value.
: "${VALUE/#+(+)}"
: "${_/%+(+)}"
# Squash multiple spaces in value.
: "${_//+(+)/+}"
# Convert values from %-encoded form.
: "${_//%/\\x}"
# Define the variable from the key name.
declare "QS_$KEY"="$(printf "%b" "${_//+/ }")"
done <<<"${QUERY_STRING,,}&" # The & at the end is required.
# Adjust 'QS_q' for the locate command by wrapping search elements in *.
: "*${QS_q// /* *}*"
QS_q="${_//\/*/*}"
# Adjust 'QS_l' to not go over maximum limit of results or use default if not supplied.
[[ -z "$QS_l" ]] && QS_l="$DEF_RESULTS"
(( QS_l > MAX_RESULTS )) && QS_l="$MAX_RESULTS"
# Give success status as checks have passed.
printf "%s: %s\\n" "Status" "0"
I=1
# shellcheck disable=SC2154
while read -r ITEM; do
# Only show specified number of items.
(( I == (QS_l + 1) )) && break
# Remove paths we don't want the user to see.
while read -r -d " " REGEX; do
[[ "${ITEM/$STORAGE_PREFIX}" =~ $REGEX ]] && continue 2
done <<<"${IGNORE_REGEXES[@]}"
# List the item.
printf "%s\\n" "${ITEM/$STORAGE_PREFIX}"
(( I++ ))
done < <(locate -A -d "$LOCATE_DB" -i -l "$(( QS_l * 2 ))" "$STORAGE_PREFIX/${QS_p##/}" "$QS_q" | sort)
fi