From 78f3e7210e34a72cc9e20e165036cd85088c6fde Mon Sep 17 00:00:00 2001 From: Darren 'Tadgy' Austin Date: Fri, 13 Oct 2023 18:46:55 +0100 Subject: [PATCH] Add searchapi.cgi. --- cgi-bin/searchapi.cgi | 89 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100755 cgi-bin/searchapi.cgi diff --git a/cgi-bin/searchapi.cgi b/cgi-bin/searchapi.cgi new file mode 100755 index 0000000..55e5232 --- /dev/null +++ b/cgi-bin/searchapi.cgi @@ -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