#!/usr/bin/env bash
# =============================================================================
# onx-ftp-pure-user-add — Pure-FTPd PureDB virtual user create (cPanel pattern)
#
# v88 Agent 3 — Default FTP User System.
#
# Purpose:
#   Bir Linux hesabı için (account.username) varsayılan FTP kullanıcısı
#   oluşturur. Pure-FTPd'nin PureDB virtual user backend'ini kullanır
#   (pure-pw useradd + mkdb).
#
#   Uid/Gid Modu:
#     "system"  — account'un Linux uid/gid'sini kullan (cPanel pattern,
#                 önerilen). Account user'ın home dizinine yazılan FTP
#                 dosyaları gerçek user'a ait olur.
#     "virtual" — pure-ftpd'nin kendi VirtualUID/VirtualGID'sini kullan
#                 (örn 1000/1000). Çoklu-kiracı için izolasyon.
#
#   Idempotent: aynı user için 2. kez çağrılırsa "kullanici zaten var" hatası
#   yerine usermod-style update yapılır (pure-pw passwd + mod -d).
#
# Input (stdin JSON):
#   {
#     "username":       "onx_xxxx",        -- required
#     "password":       "<plain>",         -- required (pure-pw hash yapacak)
#     "homedir":        "/home/users/onx_xxxx",  -- required, must exist
#     "uid":            12345,             -- required when uid_gid_mode=system
#     "gid":            12345,             -- required when uid_gid_mode=system
#     "uid_gid_mode":   "system",          -- system|virtual, default "system"
#     "puredb_path":    "/etc/pure-ftpd/pureftpd.pdb",
#     "passwd_path":    "/etc/pure-ftpd/pureftpd.passwd"
#   }
#
# Output (stdout JSON):
#   {
#     "ok": true, "username": "onx_xxxx",
#     "homedir": "/home/users/onx_xxxx",
#     "uid": 12345, "gid": 12345,
#     "type": "system",
#     "mkdb_run": true,
#     "created": true   -- false ise mevcut user update edildi
#   }
#
# 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"
command -v pure-pw >/dev/null 2>&1 || onx_die 2 "pure-pw bulunamadi (pure-ftpd kurulu degil)"
onx_json_input

USERNAME=$(onx_json_field username)
PASSWORD=$(onx_json_field password)
HOMEDIR=$(onx_json_field  homedir)
UID_VAL=$(onx_json_field  uid)
GID_VAL=$(onx_json_field  gid)
MODE=$(onx_json_field     uid_gid_mode "system")
PUREDB=$(onx_json_field   puredb_path  "/etc/pure-ftpd/pureftpd.pdb")
PASSWD=$(onx_json_field   passwd_path  "/etc/pure-ftpd/pureftpd.passwd")

# ── Validation ───────────────────────────────────────────────────────────────
[[ -z "${USERNAME}" ]] && onx_die 1 "username zorunlu"
[[ -z "${PASSWORD}" ]] && onx_die 1 "password zorunlu"
[[ -z "${HOMEDIR}"  ]] && onx_die 1 "homedir zorunlu"

[[ "${USERNAME}" =~ ^onx_[a-z0-9_]{3,30}$ ]] \
    || onx_die 1 "username gecersiz: ${USERNAME}"

# Path traversal koruması: ev dizini /home altinda olmali
[[ "${HOMEDIR}" =~ ^/home/[a-zA-Z0-9._-]+(/[a-zA-Z0-9._/-]+)?$ ]] \
    || onx_die 1 "homedir gecersiz format: ${HOMEDIR}"
[[ "${HOMEDIR}" == *..* ]] && onx_die 1 "homedir path traversal iceriyor"

# D5-06: symlink ile jail kacisi — homedir'i (var olan symlink bilesenlerini izleyerek)
# coz; cozulmus yol HALA /home altinda olmali (symlink -> /etc, /var, baska kok reddi).
HOMEDIR_REAL="$(realpath -m "${HOMEDIR}" 2>/dev/null || printf '%s' "${HOMEDIR}")"
case "${HOMEDIR_REAL}" in
    /home/*) : ;;
    *) onx_die 1 "homedir symlink ile /home disina cikiyor: ${HOMEDIR} -> ${HOMEDIR_REAL}" ;;
esac

case "${MODE}" in
    system|virtual) ;;
    *) onx_die 1 "uid_gid_mode 'system' veya 'virtual' olmali (gelen: ${MODE})" ;;
esac

if [[ "${MODE}" == "system" ]]; then
    [[ -z "${UID_VAL}" ]] && onx_die 1 "uid zorunlu (mode=system)"
    [[ -z "${GID_VAL}" ]] && onx_die 1 "gid zorunlu (mode=system)"
    [[ "${UID_VAL}" =~ ^[0-9]+$ ]] || onx_die 1 "uid sayisal olmali"
    [[ "${GID_VAL}" =~ ^[0-9]+$ ]] || onx_die 1 "gid sayisal olmali"
    (( UID_VAL >= 100 && UID_VAL <= 65535 )) || onx_die 1 "uid 100-65535 araliginda olmali"
    (( GID_VAL >= 100 && GID_VAL <= 65535 )) || onx_die 1 "gid 100-65535 araliginda olmali"
    # D5-06: system mode'da homedir, UID'in hesap home'u altinda kalmali (symlink ile
    # baska hesabin agacina cozulemez — /home altinda olmasi yeterli degil).
    ACCT_HOME="$(getent passwd "${UID_VAL}" 2>/dev/null | cut -d: -f6)"
    if [[ -n "${ACCT_HOME}" ]]; then
        ACCT_HOME="${ACCT_HOME%/}"
        case "${HOMEDIR_REAL}" in
            "${ACCT_HOME}"|"${ACCT_HOME}"/*) : ;;
            *) onx_die 1 "homedir hesap home'u (${ACCT_HOME}) disinda: ${HOMEDIR} -> ${HOMEDIR_REAL}" ;;
        esac
    fi
else
    # virtual modunda uid/gid yoksa default sabitler (pure-ftpd convention)
    UID_VAL="${UID_VAL:-1000}"
    GID_VAL="${GID_VAL:-1000}"
fi

# ── Preflight: passwd dosyası ve homedir var mı? ─────────────────────────────
[[ -f "${PASSWD}" ]] || onx_die 2 "pureftpd.passwd bulunamadi (${PASSWD}); once 'ftp-pure-bootstrap' calistirin"
[[ -d "${HOMEDIR}" ]] || onx_die 2 "homedir mevcut degil: ${HOMEDIR}"

# ── User var mı? (idempotent kararı için) ────────────────────────────────────
CREATED="true"
if pure-pw show "${USERNAME}" -f "${PASSWD}" >/dev/null 2>&1; then
    CREATED="false"
fi

# ── Password'u pure-pw'e expect-friendly olarak ver ──────────────────────────
# pure-pw useradd/passwd interaktif çift parola sorar — printf %s\n %s\n ile besle.
# (-m bayrağı kullanılırsa mkdb otomatik çağrılır, biz manuel çağıracağız ki
#  exit code'u net görelim.)
PW_FEED="$(printf '%s\n%s\n' "${PASSWORD}" "${PASSWORD}")"

if [[ "${CREATED}" == "true" ]]; then
    # Yeni kullanıcı
    if ! printf '%s' "${PW_FEED}" | pure-pw useradd "${USERNAME}" \
        -u "${UID_VAL}" -g "${GID_VAL}" -d "${HOMEDIR}" \
        -f "${PASSWD}" 2>/dev/null; then
        onx_die 3 "pure-pw useradd basarisiz: ${USERNAME}"
    fi
else
    # Mevcut kullanıcı — şifre + homedir + uid/gid güncelle
    if ! printf '%s' "${PW_FEED}" | pure-pw passwd "${USERNAME}" \
        -f "${PASSWD}" 2>/dev/null; then
        onx_die 3 "pure-pw passwd basarisiz (update): ${USERNAME}"
    fi
    if ! pure-pw usermod "${USERNAME}" \
        -u "${UID_VAL}" -g "${GID_VAL}" -d "${HOMEDIR}" \
        -f "${PASSWD}" 2>/dev/null; then
        onx_die 3 "pure-pw usermod basarisiz (update): ${USERNAME}"
    fi
fi

# ── mkdb (DB'ye flush) ───────────────────────────────────────────────────────
MKDB_RUN="false"
if pure-pw mkdb "${PUREDB}" -f "${PASSWD}" 2>/dev/null; then
    MKDB_RUN="true"
else
    onx_die 3 "pure-pw mkdb basarisiz (passwd yazildi ama PureDB guncellenmedi)"
fi

onx_log "ftp-pure-user-add: user=${USERNAME} home=${HOMEDIR} uid=${UID_VAL} gid=${GID_VAL} mode=${MODE} created=${CREATED}"

jq -nc \
    --arg username "${USERNAME}" \
    --arg homedir  "${HOMEDIR}" \
    --argjson uid  "${UID_VAL}" \
    --argjson gid  "${GID_VAL}" \
    --arg type     "${MODE}" \
    --argjson mkdb_run "${MKDB_RUN}" \
    --argjson created  "${CREATED}" \
    '{
        ok: true,
        username: $username,
        homedir:  $homedir,
        uid: $uid, gid: $gid,
        type: $type,
        mkdb_run: $mkdb_run,
        created: $created
    }'
