#!/usr/bin/env bash
# =============================================================================
# onx-ftp-pure-bootstrap — Pure-FTPd PureDB backend bootstrap (cPanel pattern)
#
# v88 Agent 3 — Default FTP User System.
#
# Purpose:
#   pure-ftpd'yi PureDB virtual users backend için idempotent şekilde
#   yapılandırır. /etc/pure-ftpd/pure-ftpd.conf'da kritik bayrakları set eder
#   (ChrootEveryone, NoAnonymous, PureDB), boş bir pureftpd.passwd dosyası
#   varsa onu yaratır, `pure-pw mkdb` ile PureDB'yi günceller ve servisi
#   enable+start eder.
#
#   Yan etki YOK: hâlihazırda doğru yapılandırılmış sunucularda hiç dosya
#   yeniden yazılmaz. Sadece eksik direktifler eklenir veya farklı değer
#   olanlar override edilir.
#
# Input (stdin JSON):
#   {
#     "min_uid":         1000,        -- opsiyonel, default 1000
#     "puredb_path":     "/etc/pure-ftpd/pureftpd.pdb",
#     "passwd_path":     "/etc/pure-ftpd/pureftpd.passwd",
#     "chroot_everyone": true,        -- default true
#     "no_anonymous":    true,        -- default true
#     "skip_systemctl":  false        -- test runner için
#   }
#
# Output (stdout JSON):
#   {
#     "ok": true,
#     "conf": "/etc/pure-ftpd/pure-ftpd.conf",
#     "puredb": "/etc/pure-ftpd/pureftpd.pdb",
#     "passwd": "/etc/pure-ftpd/pureftpd.passwd",
#     "directives_set": ["ChrootEveryone","NoAnonymous","MinUID","PureDB","CallUploadScript"],
#     "mkdb_run": true,
#     "service_enabled": true,
#     "service_active": true
#   }
#
# Exit codes: 0=ok 1=invalid-input 2=preflight-fail 3=exec-fail
# =============================================================================

set -euo pipefail

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=_lib/common.sh
source "${SCRIPT_DIR}/_lib/common.sh"

require_root
command -v jq >/dev/null 2>&1 || onx_die 2 "jq gerekli (pure-ftpd bootstrap)"

INPUT="$(cat 2>/dev/null || true)"
# Stdin opsiyonel — default'lara da izin var
if [[ -z "${INPUT}" ]]; then
    INPUT='{}'
fi
echo "${INPUT}" | jq -e 'type == "object"' >/dev/null 2>&1 \
    || onx_die 1 "stdin gecersiz JSON nesnesi"

MIN_UID=$(onx_json_get      "${INPUT}" "min_uid"         "1000")
PUREDB=$(onx_json_get       "${INPUT}" "puredb_path"     "/etc/pure-ftpd/pureftpd.pdb")
PASSWD=$(onx_json_get       "${INPUT}" "passwd_path"     "/etc/pure-ftpd/pureftpd.passwd")
CHROOT=$(onx_json_get_bool  "${INPUT}" "chroot_everyone" "true")
NO_ANON=$(onx_json_get_bool "${INPUT}" "no_anonymous"    "true")
SKIP_SYS=$(onx_json_get_bool "${INPUT}" "skip_systemctl" "false")

[[ "${MIN_UID}" =~ ^[0-9]+$ ]] || onx_die 1 "min_uid sayisal olmali"
(( MIN_UID >= 100 && MIN_UID <= 65535 )) || onx_die 1 "min_uid 100-65535 arasinda olmali"

CONF_FILE="/etc/pure-ftpd/pure-ftpd.conf"

# ── Preflight ────────────────────────────────────────────────────────────────
command -v pure-pw >/dev/null 2>&1 \
    || onx_die 2 "pure-pw komutu bulunamadi (pure-ftpd kurulu degil?)"

if [[ ! -d "/etc/pure-ftpd" ]]; then
    install -d -m 0755 /etc/pure-ftpd \
        || onx_die 3 "/etc/pure-ftpd olusturulamadi"
fi

# Confdan yoksa minimum bir tane yarat (RHEL paketi normalde sağlar; yine de
# bootstrap'i kuru bir kutuda da çalıştırabilelim)
if [[ ! -f "${CONF_FILE}" ]]; then
    cat > "${CONF_FILE}" <<'EOF_PUREDFLT'
# Auto-generated by onx-ftp-pure-bootstrap (v88)
# Tüm bilinen direktifler aşağıda set edilecek
EOF_PUREDFLT
    chmod 0644 "${CONF_FILE}"
fi

# ── set_directive KEY VALUE ──────────────────────────────────────────────────
# Tek bir direktifi (uncommented olarak) ekler veya değerini değiştirir.
# Mevcut yorumlu satırları (# ChrootEveryone ...) yok sayar — yeni active line
# pure-ftpd.conf'un sonuna eklenir. Doublication önlemi: önceki uncommented
# satır varsa silinir.
set_directive() {
    local key="$1" val="$2"
    # Uncommented satırı sil (case-sensitive key match)
    sed -i -E "/^[[:space:]]*${key}[[:space:]]+/d" "${CONF_FILE}"
    # Sonuna ekle
    printf '%s %s\n' "${key}" "${val}" >> "${CONF_FILE}"
}

DIRECTIVES_SET=()

if [[ "${CHROOT}" == "true" ]]; then
    set_directive "ChrootEveryone" "yes"
    DIRECTIVES_SET+=("ChrootEveryone")
fi
if [[ "${NO_ANON}" == "true" ]]; then
    set_directive "NoAnonymous" "yes"
    DIRECTIVES_SET+=("NoAnonymous")
fi
set_directive "MinUID" "${MIN_UID}";           DIRECTIVES_SET+=("MinUID")
set_directive "PureDB" "${PUREDB}";            DIRECTIVES_SET+=("PureDB")

# v3.61 FIX: PAM + Unix auth'u KAPAT — virtual-user-only (PureDB) backend için ŞART.
# Bug: bootstrap PureDB'yi set ediyordu ama PAMAuthentication/UnixAuthentication
# açık kalıyordu. Sonuç: pure-ftpd login'de PAM/unix_chkpwd ile Linux sistem
# şifresini deniyor (onx_y5g19r'nin Linux şifresi yok/farklı) → 530 auth fail,
# PureDB'deki gerçek FTP şifresi hiç denenmeden. Ayrıca güvenlik: PAM açık =
# sistem kullanıcıları FTP'ye girebilir + brute-force yüzeyi (üretimde
# saldırı logları görüldü). Virtual-user backend'de ikisi de "no" olmalı.
set_directive "PAMAuthentication"  "no";       DIRECTIVES_SET+=("PAMAuthentication")
set_directive "UnixAuthentication" "no";       DIRECTIVES_SET+=("UnixAuthentication")
set_directive "CallUploadScript" "no";         DIRECTIVES_SET+=("CallUploadScript")
# Pasif port aralığı — firewall ile uyumlu (49152-50175 = 1024 port)
set_directive "PassivePortRange" "49152 50175"; DIRECTIVES_SET+=("PassivePortRange")

# UTF-8 dosya adları — sadece pure-ftpd RFC2640 (--with-rfc2640 compile flag) ile
# build edildiyse. AlmaLinux 9 EPEL paketi bu flag olmadan derlenmiş; FSCharset
# eklenirse pure-ftpd start ederken "property not found line N: [FSCharset UTF-8]"
# hatası verir ve daemon ölür. Capability detection ile defansif.
PUREFTPD_BIN="$(command -v pure-ftpd 2>/dev/null || echo /usr/sbin/pure-ftpd)"
FSCHARSET_SUPPORTED="false"
if [[ -x "${PUREFTPD_BIN}" ]]; then
    if "${PUREFTPD_BIN}" --help 2>&1 | grep -qiE '(-8[[:space:]]|--fscharset|charset|rfc.?2640|8[[:space:]]+bit)'; then
        FSCHARSET_SUPPORTED="true"
    fi
fi
if [[ "${FSCHARSET_SUPPORTED}" == "true" ]]; then
    set_directive "FSCharset" "UTF-8"
    DIRECTIVES_SET+=("FSCharset")
else
    # Defansif: önceden yazılmış FSCharset satırı varsa kaldır (önceki bootstrap
    # versiyonu eklemişti, ondan kalma olabilir)
    sed -i -E '/^[[:space:]]*FSCharset[[:space:]]+/d' "${CONF_FILE}"
    onx_log "ftp-pure-bootstrap: pure-ftpd RFC2640 (FSCharset) desteklemiyor → atlandı"
fi

# ── passwd dosyasını yarat (boşsa) ───────────────────────────────────────────
if [[ ! -f "${PASSWD}" ]]; then
    touch "${PASSWD}"
    chmod 0600 "${PASSWD}"
fi

# ── mkdb (idempotent) ────────────────────────────────────────────────────────
MKDB_RUN="false"
if pure-pw mkdb "${PUREDB}" -f "${PASSWD}" 2>/dev/null; then
    MKDB_RUN="true"
else
    # Boş passwd ile mkdb bazı pure-ftpd versiyonlarında 1 döner; tolere et.
    # En azından dosyayı oluştur ki servis start ederken complain etmesin.
    if [[ ! -f "${PUREDB}" ]]; then
        : > "${PUREDB}"
        chmod 0600 "${PUREDB}"
    fi
fi

# ── systemd enable + start ───────────────────────────────────────────────────
SVC_ENABLED="false"
SVC_ACTIVE="false"
if [[ "${SKIP_SYS}" == "true" ]]; then
    onx_log "ftp-pure-bootstrap: skip_systemctl=true (test mode)"
elif command -v systemctl >/dev/null 2>&1; then
    # Unit isim varyasyonu (Alma/RHEL: pure-ftpd, Debian: pure-ftpd-mysql vs)
    UNIT=""
    for cand in pure-ftpd pureftpd; do
        if systemctl list-unit-files "${cand}.service" 2>/dev/null | grep -q "^${cand}.service"; then
            UNIT="${cand}.service"
            break
        fi
    done
    if [[ -z "${UNIT}" ]]; then
        onx_log "ftp-pure-bootstrap: systemd unit bulunamadi (pure-ftpd paketi eksik olabilir)"
    else
        if systemctl enable "${UNIT}" >/dev/null 2>&1; then SVC_ENABLED="true"; fi
        # Konfigürasyon değiştiği için restart (reload pure-ftpd'de güvenilmez)
        systemctl restart "${UNIT}" >/dev/null 2>&1 || systemctl start "${UNIT}" >/dev/null 2>&1 || true
        if systemctl is-active --quiet "${UNIT}" 2>/dev/null; then SVC_ACTIVE="true"; fi
    fi
fi

onx_log "ftp-pure-bootstrap: conf=${CONF_FILE} puredb=${PUREDB} mkdb=${MKDB_RUN} svc_active=${SVC_ACTIVE}"

# Build directives JSON array
DIRECTIVES_JSON="$(printf '%s\n' "${DIRECTIVES_SET[@]}" | jq -R . | jq -sc .)"

jq -nc \
    --arg conf "${CONF_FILE}" \
    --arg puredb "${PUREDB}" \
    --arg passwd "${PASSWD}" \
    --argjson directives "${DIRECTIVES_JSON}" \
    --argjson mkdb_run    "${MKDB_RUN}" \
    --argjson svc_enabled "${SVC_ENABLED}" \
    --argjson svc_active  "${SVC_ACTIVE}" \
    '{
        ok: true,
        conf: $conf,
        puredb: $puredb,
        passwd: $passwd,
        directives_set: $directives,
        mkdb_run: $mkdb_run,
        service_enabled: $svc_enabled,
        service_active:  $svc_active
    }'
