#!/bin/sh
# Sherlock OSINT Controller for SecuBox
# Username hunting across social networks

. /lib/functions.sh
config_load sherlock

CONTAINER="sherlock"
LXC_PATH="/srv/lxc"
DATA_DIR="/srv/sherlock"
IMAGE_URL="https://images.linuxcontainers.org/images/debian/bookworm/arm64/default"

log() {
    logger -t sherlockctl "$1"
    echo "[INFO] $1"
}

error() {
    logger -t sherlockctl -p err "$1"
    echo "[ERROR] $1" >&2
    exit 1
}

get_config() {
    config_get "$1" main "$2" "$3"
}

cmd_install() {
    log "Installing Sherlock container..."

    if [ -d "${LXC_PATH}/${CONTAINER}" ]; then
        log "Container already exists. Use 'reinstall' to recreate."
        return 0
    fi

    local ip
    get_config ip ip "192.168.255.56"

    mkdir -p "${DATA_DIR}"/{results,cache}

    log "Downloading Debian Bookworm image..."
    mkdir -p "${LXC_PATH}/${CONTAINER}"

    local rootfs="${LXC_PATH}/${CONTAINER}/rootfs"
    mkdir -p "$rootfs"

    # Get latest image date
    local image_date
    image_date=$(wget -qO- "${IMAGE_URL}/" | grep -o '[0-9]\{8\}_[0-9]\{2\}:[0-9]\{2\}' | tail -1)

    if [ -z "$image_date" ]; then
        error "Could not determine latest image date"
    fi

    log "Downloading image: ${image_date}"
    wget -q "${IMAGE_URL}/${image_date}/rootfs.tar.xz" -O /tmp/sherlock-rootfs.tar.xz || \
        error "Failed to download image"

    tar -xJf /tmp/sherlock-rootfs.tar.xz -C "$rootfs"
    rm -f /tmp/sherlock-rootfs.tar.xz

    # Create data directory in rootfs
    mkdir -p "${rootfs}/srv/sherlock"

    # Create LXC config
    cat > "${LXC_PATH}/${CONTAINER}/config" << EOF
lxc.uts.name = ${CONTAINER}
lxc.rootfs.path = dir:${rootfs}
lxc.include = /usr/share/lxc/config/common.conf

# Network
lxc.net.0.type = veth
lxc.net.0.link = br-lan
lxc.net.0.flags = up
lxc.net.0.ipv4.address = ${ip}/24
lxc.net.0.ipv4.gateway = 192.168.255.1

# Data mount
lxc.mount.entry = /srv/sherlock /srv/sherlock none bind 0 0

# Autostart
lxc.start.auto = 1
lxc.start.delay = 5
EOF

    # Create startup script
    cat > "${rootfs}/opt/start-sherlock.sh" << 'STARTUP'
#!/bin/bash
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export HOME="/root"

# Set DNS
echo "nameserver 192.168.255.1" > /etc/resolv.conf

# Wait for network
sleep 5

# Install dependencies if needed
if ! command -v sherlock &>/dev/null; then
    apt-get update
    apt-get install -y python3 python3-pip git curl

    # Install sherlock
    pip3 install sherlock-project --break-system-packages

    # Also clone repo for data files
    git clone https://github.com/sherlock-project/sherlock.git /opt/sherlock-git
fi

# Create web API server
cat > /opt/sherlock_api.py << 'PYEOF'
#!/usr/bin/env python3
"""
Sherlock Web API Server
REST API for username hunting across social networks
"""
import http.server
import socketserver
import json
import subprocess
import os
import threading
import time
from urllib.parse import parse_qs, urlparse
import uuid

PORT = 9090
RESULTS_DIR = "/srv/sherlock/results"
CACHE_DIR = "/srv/sherlock/cache"

# Store running tasks
tasks = {}

os.makedirs(RESULTS_DIR, exist_ok=True)
os.makedirs(CACHE_DIR, exist_ok=True)

def run_sherlock(username, task_id):
    """Run sherlock search in background"""
    output_file = os.path.join(RESULTS_DIR, f"{task_id}.json")

    try:
        result = subprocess.run(
            ["sherlock", username, "--output", output_file, "--json", os.path.join(RESULTS_DIR, f"{task_id}_raw.json")],
            capture_output=True,
            text=True,
            timeout=300
        )

        # Parse results
        found_accounts = []
        raw_file = os.path.join(RESULTS_DIR, f"{task_id}_raw.json")
        if os.path.exists(raw_file):
            with open(raw_file) as f:
                data = json.load(f)
                for site, info in data.items():
                    if info.get("status") == "Claimed":
                        found_accounts.append({
                            "site": site,
                            "url": info.get("url_user", ""),
                            "response_time": info.get("response_time_s", 0)
                        })

        tasks[task_id] = {
            "status": "completed",
            "username": username,
            "found_count": len(found_accounts),
            "accounts": found_accounts,
            "completed_at": time.time()
        }

    except subprocess.TimeoutExpired:
        tasks[task_id] = {"status": "timeout", "username": username}
    except Exception as e:
        tasks[task_id] = {"status": "error", "username": username, "error": str(e)}

class SherlockHandler(http.server.BaseHTTPRequestHandler):
    def log_message(self, format, *args):
        pass  # Suppress logging

    def send_json(self, data, status=200):
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        self.wfile.write(json.dumps(data).encode())

    def do_OPTIONS(self):
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.end_headers()

    def do_GET(self):
        parsed = urlparse(self.path)
        path = parsed.path

        if path == "/" or path == "/api":
            self.send_json({
                "status": "ok",
                "server": "SecuBox Sherlock OSINT Server",
                "version": "1.0.0",
                "endpoints": {
                    "/api/search?username=<name>": "Start username search (returns task_id)",
                    "/api/task/<task_id>": "Get task status/results",
                    "/api/tasks": "List all tasks"
                }
            })

        elif path == "/api/tasks":
            self.send_json({"tasks": tasks})

        elif path.startswith("/api/task/"):
            task_id = path.split("/")[-1]
            if task_id in tasks:
                self.send_json(tasks[task_id])
            else:
                self.send_json({"error": "Task not found"}, 404)

        elif path == "/api/search":
            params = parse_qs(parsed.query)
            username = params.get("username", [None])[0]

            if not username:
                self.send_json({"error": "username parameter required"}, 400)
                return

            # Check cache
            cache_file = os.path.join(CACHE_DIR, f"{username}.json")
            if os.path.exists(cache_file):
                cache_age = time.time() - os.path.getmtime(cache_file)
                if cache_age < 3600:  # 1 hour cache
                    with open(cache_file) as f:
                        self.send_json({"cached": True, **json.load(f)})
                    return

            # Start new search
            task_id = str(uuid.uuid4())[:8]
            tasks[task_id] = {"status": "running", "username": username, "started_at": time.time()}

            thread = threading.Thread(target=run_sherlock, args=(username, task_id))
            thread.daemon = True
            thread.start()

            self.send_json({"task_id": task_id, "status": "started", "username": username})

        else:
            self.send_json({"error": "Not found"}, 404)

if __name__ == "__main__":
    with socketserver.TCPServer(("", PORT), SherlockHandler) as httpd:
        print(f"Sherlock API Server running on port {PORT}")
        httpd.serve_forever()
PYEOF
chmod +x /opt/sherlock_api.py

# Start API server
cd /opt
exec python3 sherlock_api.py
STARTUP
    chmod +x "${rootfs}/opt/start-sherlock.sh"

    # Set init
    rm -f "${rootfs}/sbin/init"
    ln -sf /opt/start-sherlock.sh "${rootfs}/sbin/init"

    log "Sherlock container installed at ${ip}"
    log "Start with: sherlockctl start"
}

cmd_start() {
    log "Starting Sherlock container..."
    /usr/bin/lxc-start -n "$CONTAINER" || error "Failed to start container"

    sleep 10

    local ip
    get_config ip ip "192.168.255.56"

    if curl -s -o /dev/null -w "%{http_code}" "http://${ip}:9090/" | grep -q "200"; then
        log "Sherlock API is ready at http://${ip}:9090/"
    else
        log "Container started, waiting for API server..."
    fi
}

cmd_stop() {
    log "Stopping Sherlock container..."
    /usr/bin/lxc-stop -n "$CONTAINER" 2>/dev/null
    log "Container stopped"
}

cmd_restart() {
    cmd_stop
    sleep 2
    cmd_start
}

cmd_status() {
    local state
    state=$(/usr/bin/lxc-info -n "$CONTAINER" -s 2>/dev/null | awk '{print $2}')

    local ip
    get_config ip ip "192.168.255.56"

    echo "Container: $CONTAINER"
    echo "State: ${state:-NOT_FOUND}"
    echo "IP: $ip"
    echo "API Port: 9090"

    if [ "$state" = "RUNNING" ]; then
        local status
        status=$(curl -s "http://${ip}:9090/api" 2>/dev/null)
        if [ -n "$status" ]; then
            echo "API Status: Running"
        else
            echo "API Status: Starting..."
        fi
    fi
}

cmd_search() {
    local username="$1"
    if [ -z "$username" ]; then
        error "Usage: sherlockctl search <username>"
    fi

    local ip
    get_config ip ip "192.168.255.56"

    local result
    result=$(curl -s "http://${ip}:9090/api/search?username=${username}")
    echo "$result"
}

cmd_logs() {
    /usr/bin/lxc-attach -n "$CONTAINER" -- tail -f /var/log/sherlock.log 2>/dev/null || \
        /usr/bin/lxc-attach -n "$CONTAINER" -- cat /proc/1/fd/1
}

cmd_shell() {
    /usr/bin/lxc-attach -n "$CONTAINER" -- /bin/bash
}

cmd_configure_haproxy() {
    local domain
    get_config domain exposure domain "sherlock.gk2.secubox.in"

    local ip
    get_config ip ip "192.168.255.56"

    log "Configuring HAProxy vhost: $domain -> $ip:9090"

    haproxyctl vhost add "$domain" 2>/dev/null || true

    local routes="/srv/mitmproxy/haproxy-routes.json"
    if [ -f "$routes" ]; then
        python3 -c "
import json
with open('$routes') as f:
    d = json.load(f)
d['$domain'] = ['$ip', 9090]
with open('$routes', 'w') as f:
    json.dump(d, f, indent=2)
"
        cp "$routes" /srv/mitmproxy-in/haproxy-routes.json
        /etc/init.d/mitmproxy restart
    fi

    log "HAProxy configured: https://$domain/"
}

cmd_help() {
    cat << 'EOF'
Sherlock OSINT Controller for SecuBox
Username hunting across social networks

Usage: sherlockctl <command> [options]

Container Commands:
  install             Create Sherlock container
  start               Start the container
  stop                Stop the container
  restart             Restart the container
  status              Show container status
  logs                View logs
  shell               Open shell in container

OSINT Commands:
  search <username>   Search for username across platforms

Configuration:
  configure-haproxy   Setup HAProxy vhost for external access

Examples:
  sherlockctl install
  sherlockctl start
  sherlockctl search johndoe
  curl "http://192.168.255.56:9090/api/search?username=johndoe"
EOF
}

case "$1" in
    install) cmd_install ;;
    start) cmd_start ;;
    stop) cmd_stop ;;
    restart) cmd_restart ;;
    status) cmd_status ;;
    logs) cmd_logs ;;
    shell) cmd_shell ;;
    search) shift; cmd_search "$@" ;;
    configure-haproxy) cmd_configure_haproxy ;;
    help|--help|-h) cmd_help ;;
    *) cmd_help ;;
esac
