#!/bin/bash # Configuration options BASE_DIR="/data/sites" STORAGE_PREFIX="/storage/md0" IGNORE_REGEXES=('^/\.sandbox.*' '^/\.lftp.*' '^/dead\.letter' '.*\.rsync-tmp.*') HEADER_DARK="$BASE_DIR/${SERVER_NAME:-slackware.uk}/html/includes/search-header-dark.html" FOOTER_DARK="$BASE_DIR/${SERVER_NAME:-slackware.uk}/html/includes/search-footer-dark.html" HEADER_LIGHT="$BASE_DIR/${SERVER_NAME:-slackware.uk}/html/includes/search-header-light.html" FOOTER_LIGHT="$BASE_DIR/${SERVER_NAME:-slackware.uk}/html/includes/search-footer-light.html" LOCATEDB="/tmp/mirrors.db" LIMIT=1000 MAX_CONCURRENT=5 # Extglob is required. shopt -s extglob # Output an error in processing. die() { # $1 = The error message to output. # $2 = Whether to include the retry link ("false" to disable inclusion, everything else is true). cat <
Error: $1$([[ "${2,,}" != "false" ]] && printf "
\n Please click here to retry your search." "${REQUEST_SCHEME:-https}://${SERVER_NAME:-slackware.uk}/search?$QUERY_STRING")
EOF exit 1 } # Initial headers. printf "%s: %s\\n" "Content-type" "text/html" 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" # Extract cookies. while read -r -d " " RAW; do KEY="${RAW%%=*}" VALUE="${RAW#*=}" declare -A COOKIES+=( ["${KEY,,}"]="${VALUE,,}" ) done <<<"$HTTP_COOKIE " # The space at the end is required. # Include the themed header. if [[ "${COOKIES['theme']}" == "dark" ]]; then cat "$HEADER_DARK" else cat "$HEADER_LIGHT" fi # Limit the number of concurrent searches to avoid DoS. if (( $(lsof -t "$LOCATEDB" | wc -l) > (MAX_CONCURRENT - 1) )); then die "too many concurrent searches!" else exec 9<"$LOCATEDB" && flock -s -E 10 -w 2 9 || die "too many concurrent searches!" 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 :)" "false" # 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 "X_$KEY"="$(printf "%b" "${_//+/ }")" done <<<"${QUERY_STRING,,}&" # The & at the end is required. # Take a copy of q before it's modified, for the heading. Q_COPY="${X_q:-(empty)}" # Adjust 'X_q' for the locate command by wrapping search elements in *s. : "*${X_q// /* *}*" X_q="${_//\/*/*}" # HTML boilerplate. cat <
Search Results

Search query: $Q_COPY
Searches may contain multiple terms, include (basic) globs,
and descend down from the current directory.

EOF I=1 COUNT=0 while read -r ITEM; do # Only show 1000 items to prevent long load times. (( I == LIMIT + 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}" "${ITEM/$STORAGE_PREFIX}" "${ITEM/$STORAGE_PREFIX}" (( I++ )) (( COUNT++ )) done < <(locate -A -d "$LOCATEDB" -i -l "$(( LIMIT * 2 ))" "$STORAGE_PREFIX/${X_p##/}" $X_q | sort) # Tell the user about the results. if (( COUNT == 0 )); then printf " %s" "No results - try to widen your search." elif (( COUNT == LIMIT )); then printf " %s" "Maximum $LIMIT items shown - try to refine your search." else printf " %s" "$COUNT items found." fi # HTML boilerplate. cat <
EOF # Include footer. if [[ "${COOKIES['theme']}" == "dark" ]]; then cat "$FOOTER_DARK" else cat "$FOOTER_LIGHT" fi fi