#!/usr/bin/env bash
#
# fix-wp-permissions.sh — reset file and folder permissions on a WordPress install.
#
# Defaults (WordPress-recommended):
#   directories      755
#   files            644
#   wp-config.php    640  (use --strict for 600)
#   .htaccess        644
#   wp-content/uploads  writable by the web server group (775 dirs / 664 files) with --group-write
#
# Run from a URL (pass options after "bash -s --"):
#   curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | bash -s -- --dry-run
#   curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | sudo bash -s -- -p /var/www/example.com -o deploy -g www-data --group-write
#
# Or locally:
#   ./fix-wp-permissions.sh [-p path] [-o owner] [-g group] [--group-write] [--strict] [--dry-run]
#
# With no -p, the current directory is used.

# Everything runs inside main(), called on the last line, so a partially
# downloaded script never executes.
main() {
set -euo pipefail

WP_PATH="$PWD"
OWNER=""
GROUP=""
GROUP_WRITE=false
STRICT=false
DRY_RUN=false

usage() {
  cat <<'USAGE'
Usage: fix-wp-permissions.sh [options]

  -p, --path DIR     WordPress root (default: current directory)
  -o, --owner USER   chown files to USER (needs root)
  -g, --group GROUP  chown files to GROUP, e.g. www-data (needs root)
      --group-write  make wp-content/uploads writable by the group (775/664)
      --strict       wp-config.php 600 instead of 640
  -n, --dry-run      show what would change, change nothing
  -h, --help         show this help

From a URL:
  curl -fsSL https://gist.veru.se/Veruse/fix-wp-permissions | bash -s -- --dry-run
USAGE
  exit "${1:-0}"
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    -p|--path)       WP_PATH="${2:-}"; shift 2 ;;
    -o|--owner)      OWNER="${2:-}"; shift 2 ;;
    -g|--group)      GROUP="${2:-}"; shift 2 ;;
    --group-write)   GROUP_WRITE=true; shift ;;
    --strict)        STRICT=true; shift ;;
    -n|--dry-run)    DRY_RUN=true; shift ;;
    -h|--help)       usage 0 ;;
    *) echo "Unknown option: $1" >&2; usage 1 ;;
  esac
done

# --- Sanity checks ---------------------------------------------------------

[[ -z "$WP_PATH" ]] && { echo "Error: --path is empty." >&2; usage 1; }
WP_PATH="$(cd "$WP_PATH" 2>/dev/null && pwd)" || { echo "Error: path not found." >&2; exit 1; }

if [[ "$WP_PATH" == "/" || "$WP_PATH" == "$HOME" && ! -d "$WP_PATH/wp-includes" ]]; then
  echo "Error: refusing to run on '$WP_PATH'." >&2
  exit 1
fi

if [[ ! -d "$WP_PATH/wp-includes" || ! -d "$WP_PATH/wp-admin" ]]; then
  echo "Error: '$WP_PATH' doesn't look like a WordPress root (no wp-includes/ or wp-admin/)." >&2
  exit 1
fi

# wp-config.php can live in the WP root or one level above it.
WP_CONFIG=""
if   [[ -f "$WP_PATH/wp-config.php" ]];       then WP_CONFIG="$WP_PATH/wp-config.php"
elif [[ -f "$WP_PATH/../wp-config.php" ]];    then WP_CONFIG="$(cd "$WP_PATH/.." && pwd)/wp-config.php"
fi

if [[ -n "$OWNER" || -n "$GROUP" ]] && [[ $EUID -ne 0 ]]; then
  echo "Error: changing ownership needs root. Re-run with sudo, or drop -o/-g." >&2
  exit 1
fi
[[ -n "$OWNER" ]] && ! id -u "$OWNER" &>/dev/null && { echo "Error: user '$OWNER' doesn't exist." >&2; exit 1; }
[[ -n "$GROUP" ]] && ! getent group "$GROUP" &>/dev/null && { echo "Error: group '$GROUP' doesn't exist." >&2; exit 1; }

run() {
  if $DRY_RUN; then echo "[dry-run] $*"; else "$@"; fi
}

echo "WordPress root : $WP_PATH"
echo "wp-config.php  : ${WP_CONFIG:-not found}"
echo "Owner:group    : ${OWNER:-(unchanged)}:${GROUP:-(unchanged)}"
echo "Uploads writable by group: $GROUP_WRITE"
$DRY_RUN && echo "DRY RUN — nothing will be changed."
echo

# --- Ownership -------------------------------------------------------------

if [[ -n "$OWNER" || -n "$GROUP" ]]; then
  echo "Setting ownership..."
  run chown -R "${OWNER}${GROUP:+:$GROUP}" "$WP_PATH"
  [[ -n "$WP_CONFIG" && "$WP_CONFIG" != "$WP_PATH/wp-config.php" ]] && \
    run chown "${OWNER}${GROUP:+:$GROUP}" "$WP_CONFIG"
fi

# --- Base permissions ------------------------------------------------------
# -P: don't follow symlinks; chmod on symlinks is skipped by -type d / -type f.

echo "Setting directories to 755..."
run find -P "$WP_PATH" -type d -exec chmod 755 {} +

echo "Setting files to 644..."
run find -P "$WP_PATH" -type f -exec chmod 644 {} +

# --- Uploads ---------------------------------------------------------------

UPLOADS="$WP_PATH/wp-content/uploads"
if $GROUP_WRITE && [[ -d "$UPLOADS" ]]; then
  echo "Making uploads group-writable (775/664)..."
  run find -P "$UPLOADS" -type d -exec chmod 775 {} +
  run find -P "$UPLOADS" -type f -exec chmod 664 {} +
  # setgid so new uploads inherit the group
  run find -P "$UPLOADS" -type d -exec chmod g+s {} +
fi

# --- Sensitive files -------------------------------------------------------

if [[ -n "$WP_CONFIG" ]]; then
  if $STRICT; then
    echo "Locking wp-config.php to 600..."
    run chmod 600 "$WP_CONFIG"
  else
    echo "Locking wp-config.php to 640..."
    run chmod 640 "$WP_CONFIG"
  fi
fi

[[ -f "$WP_PATH/.htaccess" ]] && run chmod 644 "$WP_PATH/.htaccess"

# Any scripts/binaries that genuinely need +x (e.g. in vendor/bin) can be
# re-added here, e.g.:
# run find "$WP_PATH/wp-content" -path '*/vendor/bin/*' -type f -exec chmod 755 {} +

echo
echo "Done."
$DRY_RUN || echo "Tip: load the site and test uploads, plugin updates and the media library."
}

main "$@"