#!/bin/sh
# ============================================================================
# fix-cups-bannertopdf.sh
#
# Временный фикс (workaround) для поломки печати в ALT Linux Sisyphus:
#   cups-filters 2.0.1 + libcupsfilters 2.2.1
#   -> "cfFilterChain: pdftopdf (PID ...) stopped with status 1"
#      при печати тестовой страницы / баннеров на driverless (IPP Everywhere)
#      принтерах (Pantum, HP, Brother и т.п.).
#
# Что делает:
#   1. Ставит обёртки над фильтрами bannertopdf, pdftoraster, rastertopwg:
#      - bannertopdf: вывод в temp-файл (fix: обрезание PDF на pipe);
#      - все три: чтение stdin при имени входного файла "-" (fix: open("-")).
#   2. Правки MIME-правил (/usr/share/cups/mime/):
#      - баннеры идут через бинарник bannertopdf (не внутрь universal);
#      - vnd.cups-pdf -> vnd.cups-raster -> image/urf классической цепочкой
#        pdftoraster -> rastertopwg (в обход сломанной внутренней цепочки
#        фильтра universal).
#   3. Перезапускает CUPS.
#
# Использование:
#   sudo ./fix-cups-bannertopdf.sh           # применить фикс
#   sudo ./fix-cups-bannertopdf.sh --undo    # откатить (восстановить пакетное состояние)
#   ./fix-cups-bannertopdf.sh --check        # показать состояние (root не нужен)
#   ./fix-cups-bannertopdf.sh --help         # эта справка
#
# Скрипт идемпотентен: повторный запуск безопасен, ничего не ломает.
# Оригиналы фильтров сохраняются как <фильтр>.real; MIME-файлы бэкапятся
# как <файл>.fixbak. При обновлении пакетов cups-filters/cups обёртки и
# правки будут перезаписаны — после обновления проверьте печать и, при
# необходимости, примените скрипт заново.
# ============================================================================

ACTION=apply

for a in "$@"; do
    case "$a" in
        --undo|-u) ACTION=undo ;;
        --check|-c) ACTION=check ;;
        --help|-h)
            sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//'
            exit 0
            ;;
        *)
            echo "Неизвестный аргумент: $a" >&2
            echo "Использование: $0 [--undo|--check|--help]" >&2
            exit 2
            ;;
    esac
done

# --- определение путей ------------------------------------------------------
if [ -d /usr/lib/cups/filter ]; then
    FDIR=/usr/lib/cups/filter
elif [ -d /usr/libexec/cups/filter ]; then
    FDIR=/usr/libexec/cups/filter
else
    echo "ОШИБКА: каталог фильтров CUPS не найден" >&2
    exit 1
fi

MIME_DIR=/usr/share/cups/mime
UNIV="$MIME_DIR/cupsfilters-universal.convs"
CONVS="$MIME_DIR/cupsfilters.convs"

WRAPPER_MARK="cups-filters 2.0.1 workaround"
# регулярное выражение, покрывающее оба варианта комментария-маркера:
#  "cups-filters 2.0.1 workaround: ..." (этот скрипт)
#  "Workaround for cups-filters 2.0.1 bug: ..." (ранние версии фикса)
WRAPPER_MARK_RE="cups-filters 2\\.0\\.1 (workaround|bug)"

FILTERS="bannertopdf pdftoraster rastertopwg"

# --- проверка состояния -----------------------------------------------------
check() {
    echo "== Состояние фикса =="
    echo "Каталог фильтров: $FDIR"
    for name in $FILTERS; do
        main="$FDIR/$name"
        real="$FDIR/$name.real"
        if grep -qiE "$WRAPPER_MARK_RE" "$main" 2>/dev/null; then
            st="обёртка"
            [ -x "$real" ] || st="$st (ВНИМАНИЕ: $real отсутствует!)"
        elif [ -f "$main" ]; then
            st="оригинал"
        else
            st="ОТСУТСТВУЕТ"
        fi
        printf '  %-14s %s\n' "$name:" "$st"
    done
    echo
    echo "== MIME-правила =="
    for f in "$UNIV" "$CONVS"; do
        if [ -f "$f" ]; then
            echo "  $f"
            [ -f "$f.fixbak" ] && echo "    бэкап: $f.fixbak"
        else
            echo "  $f — НЕТ ФАЙЛА"
        fi
    done
    if [ -f "$UNIV" ]; then
        grep -qE "^[[:space:]]*application/vnd.cups-pdf-banner[[:space:]]\+application/vnd.universal-input" "$UNIV" \
            && echo "    banner passthrough: НЕ закомментирован (не применено)" \
            || echo "    banner passthrough: отключён (применено)"
        grep -qE "^[[:space:]]*application/vnd.cups-pdf[[:space:]]\+application/vnd.universal-input" "$UNIV" \
            && echo "    vnd.cups-pdf passthrough: НЕ закомментирован (не применено)" \
            || echo "    vnd.cups-pdf passthrough: отключён (применено)"
    fi
    if [ -f "$CONVS" ]; then
        grep -qsF "application/vnd.cups-pdf-banner application/vnd.cups-pdf 10 bannertopdf" "$CONVS" \
            && echo "    правило bannertopdf: есть" || echo "    правило bannertopdf: НЕТ"
        grep -qsF "application/vnd.cups-raster     image/urf                  66 rastertopwg" "$CONVS" \
            && echo "    правило rastertopwg: есть" || echo "    правило rastertopwg: НЕТ"
    fi
    echo
    echo "== Версии пакетов =="
    rpm -q cups-filters libcupsfilters libcupsfilters2 2>/dev/null || true
}

# --- установка обёртки ------------------------------------------------------
install_wrapper() {
    name=$1
    main="$FDIR/$name"
    real="$FDIR/$name.real"

    if grep -qiE "$WRAPPER_MARK_RE" "$main" 2>/dev/null; then
        echo "  [$name] обёртка уже установлена"
        [ -x "$real" ] || echo "  [$name] ВНИМАНИЕ: $real отсутствует — оригинал потерян!"
        return 0
    fi

    if [ ! -f "$main" ]; then
        echo "  [$name] ОШИБКА: $main не найден, пропуск" >&2
        return 1
    fi

    if [ ! -f "$real" ]; then
        mv "$main" "$real" || { echo "  [$name] ОШИБКА: не удалось сохранить оригинал" >&2; return 1; }
        echo "  [$name] оригинал сохранён: $real"
    else
        echo "  [$name] $real уже существует, оригинал не трогаю"
    fi

    if [ "$name" = "bannertopdf" ]; then
        cat > "$main" <<'WRAPPER'
#!/bin/sh
# cups-filters 2.0.1 workaround (bannertopdf): при выводе в pipe (не seekable
# поток) PDF обрезается — xref/trailer не дописывается. Пишем результат во
# временный файл, затем отдаём в stdout. Оригинал: $0.real
real="$0.real"
[ -x "$real" ] || { echo "ERROR: $real not found" >&2; exit 1; }

tmp=$(mktemp /tmp/bannertopdf.XXXXXX) || exit 1
trap 'rm -f "$tmp"' EXIT HUP INT TERM

if [ "$#" -ge 6 ] && [ "$6" = "-" ]; then
    in2=$(mktemp /tmp/bannertopdf_in.XXXXXX) || exit 1
    cat > "$in2" || { rm -f "$in2"; exit 1; }
    "$real" "$1" "$2" "$3" "$4" "$5" "$in2" > "$tmp"
    rc=$?
    rm -f "$in2"
else
    "$real" "$@" > "$tmp"
    rc=$?
fi

cat "$tmp"
exit $rc
WRAPPER
    else
        cat > "$main" <<'WRAPPER'
#!/bin/sh
# cups-filters 2.0.1 workaround: фильтры падают при имени входного файла "-"
# (open("-") -> ENOENT). Буферизуем stdin во временный файл и передаём путь.
# Оригинал: $0.real
real="$0.real"
[ -x "$real" ] || { echo "ERROR: $real not found" >&2; exit 1; }

if [ "$#" -ge 6 ] && [ "$6" = "-" ]; then
    in=$(mktemp /tmp/$(basename "$0")_in.XXXXXX) || exit 1
    trap 'rm -f "$in"' EXIT HUP INT TERM
    cat > "$in" || exit 1
    exec "$real" "$1" "$2" "$3" "$4" "$5" "$in"
fi
exec "$real" "$@"
WRAPPER
    fi

    chmod 755 "$main" || return 1
    echo "  [$name] обёртка установлена: $main"
    return 0
}

# --- MIME-правки ------------------------------------------------------------
backup_if_needed() {
    f=$1
    [ -f "$f.fixbak" ] || cp -a "$f" "$f.fixbak"
}

# закомментировать строку, если она активна; вернёт 0, если была изменена
# (паттерны в синтаксисе GNU BRE: [::space::] и \+ работают в grep и sed)
comment_line() {
    f=$1
    pat=$2
    if grep -q "^[[:space:]]*$pat" "$f"; then
        sed -i "s|^\([[:space:]]*$pat\)|# \1|" "$f"
        echo "  [mime] закомментировано в $(basename "$f"): $pat"
        return 0
    fi
    return 1
}

# добавить правило, если его ещё нет; вернёт 0, если добавлено
append_rule() {
    f=$1
    rule=$2
    if grep -qsF -- "$rule" "$f"; then
        echo "  [mime] уже есть в $(basename "$f"): $rule"
        return 1
    fi
    printf '%s\n' "$rule" >> "$f"
    echo "  [mime] добавлено в $(basename "$f"): $rule"
    return 0
}

apply() {
    echo "== Применение фикса =="
    changed=0

    for name in $FILTERS; do
        install_wrapper "$name" && changed=1
    done

    if [ -f "$UNIV" ] && [ -f "$CONVS" ]; then
        backup_if_needed "$UNIV"
        backup_if_needed "$CONVS"
        comment_line "$UNIV" 'application/vnd.cups-pdf-banner[[:space:]]\+application/vnd.universal-input' && changed=1
        comment_line "$UNIV" 'application/vnd.cups-pdf[[:space:]]\+application/vnd.universal-input' && changed=1
        append_rule "$CONVS" 'application/vnd.cups-pdf-banner application/vnd.cups-pdf 10 bannertopdf' && changed=1
        append_rule "$CONVS" 'application/vnd.cups-pdf        application/vnd.cups-raster 66 pdftoraster' && changed=1
        append_rule "$CONVS" 'application/vnd.cups-raster     image/urf                  66 rastertopwg' && changed=1
    else
        echo "  ОШИБКА: $UNIV или $CONVS не найден" >&2
    fi

    if [ "$changed" -eq 1 ]; then
        restart_cups
    else
        echo "Изменений не потребовалось — фикс уже применён."
    fi
}

undo() {
    echo "== Откат фикса =="
    for name in $FILTERS; do
        main="$FDIR/$name"
        real="$FDIR/$name.real"
        if grep -qiE "$WRAPPER_MARK_RE" "$main" 2>/dev/null; then
            if [ -x "$real" ]; then
                rm -f "$main"
                mv "$real" "$main"
                echo "  [$name] обёртка убрана, оригинал восстановлен"
            else
                rm -f "$main"
                echo "  [$name] ВНИМАНИЕ: $real отсутствует — оригинал не восстановлен."
                echo "          Переустановите пакет: apt-get install --reinstall cups-filters cups"
            fi
        else
            echo "  [$name] обёртка не установлена"
        fi
    done

    for f in "$UNIV" "$CONVS"; do
        if [ -f "$f.fixbak" ]; then
            cp -a "$f.fixbak" "$f"
            rm -f "$f.fixbak"
            echo "  [mime] восстановлено: $f (из .fixbak)"
        elif [ -f "$f.bak" ]; then
            cp -a "$f.bak" "$f"
            echo "  [mime] восстановлено: $f (из .bak)"
        else
            echo "  [mime] бэкап не найден, пропуск: $f"
        fi
    done

    restart_cups
}

restart_cups() {
    if command -v systemctl >/dev/null 2>&1 && systemctl restart cups 2>/dev/null; then
        echo "CUPS перезапущен (systemctl)"
    elif service cups restart 2>/dev/null; then
        echo "CUPS перезапущен (service)"
    else
        echo "ВНИМАНИЕ: не удалось перезапустить CUPS — перезапустите вручную" >&2
    fi
}

# --- запуск ----------------------------------------------------------------
case "$ACTION" in
    check) check ;;
    apply)
        [ "$(id -u)" = 0 ] || { echo "Нужны права root: sudo $0" >&2; exit 1; }
        apply
        ;;
    undo)
        [ "$(id -u)" = 0 ] || { echo "Нужны права root: sudo $0 --undo" >&2; exit 1; }
        undo
        ;;
esac
