printwell-cli 0.1.11

Command-line tool for HTML to PDF conversion
Documentation
//! Encryption commands implementation.

use anyhow::{Context, Result};

use crate::cli::args::{DecryptArgs, EncryptArgs};
use crate::cli::utils::{resolve_optional_password, resolve_password};

pub fn encrypt(args: &EncryptArgs) -> Result<()> {
    use printwell::encrypt::{EncryptionAlgorithm, EncryptionOptions, Permissions, encrypt_pdf};

    let pdf_data = std::fs::read(&args.input)
        .with_context(|| format!("Failed to read input file: {}", args.input))?;

    // Resolve passwords from direct, file, or env sources
    let owner_password = resolve_password(
        args.owner_password.as_deref(),
        args.owner_password_file.as_deref(),
        args.owner_password_env.as_deref(),
        "owner password",
    )?;

    let user_password = resolve_optional_password(
        args.user_password.as_deref(),
        args.user_password_file.as_deref(),
        args.user_password_env.as_deref(),
        "user password",
    )?;

    // Build permissions
    let permissions = if args.form_perms.allow_all {
        Permissions::ALL
    } else {
        let mut p = Permissions::NONE;
        if args.print_perms.allow_print {
            p = p | Permissions::PRINT;
        }
        if args.content_perms.allow_copy {
            p = p | Permissions::COPY;
        }
        if args.edit_perms.allow_modify {
            p = p | Permissions::MODIFY;
        }
        if args.edit_perms.allow_annotate {
            p = p | Permissions::ANNOTATE;
        }
        if args.form_perms.allow_fill_forms {
            p = p | Permissions::FILL_FORMS;
        }
        if args.content_perms.allow_accessibility {
            p = p | Permissions::EXTRACT_ACCESSIBILITY;
        }
        if args.edit_perms.allow_assemble {
            p = p | Permissions::ASSEMBLE;
        }
        if args.print_perms.allow_print_hq {
            p = p | Permissions::PRINT_HIGH_QUALITY;
        }
        p
    };

    // Parse algorithm
    let algorithm = match args.algorithm.to_lowercase().as_str() {
        "aes256" | "aes-256" => EncryptionAlgorithm::Aes256,
        "aes128" | "aes-128" => EncryptionAlgorithm::Aes128,
        "rc4" | "rc4-128" => EncryptionAlgorithm::Rc4_128,
        _ => anyhow::bail!(
            "Invalid encryption algorithm: {}. Use: aes256, aes128, or rc4",
            args.algorithm
        ),
    };

    let options = EncryptionOptions {
        owner_password,
        user_password,
        permissions,
        algorithm,
        encrypt_metadata: true,
    };

    let result = encrypt_pdf(&pdf_data, &options).context("Failed to encrypt PDF")?;

    result
        .write_to_file(&args.output)
        .with_context(|| format!("Failed to write encrypted PDF to: {}", args.output))?;
    eprintln!("Encrypted PDF written to: {}", args.output);

    Ok(())
}

pub fn decrypt(args: &DecryptArgs) -> Result<()> {
    use printwell::encrypt::decrypt_pdf;

    let pdf_data = std::fs::read(&args.input)
        .with_context(|| format!("Failed to read input file: {}", args.input))?;

    // Resolve password from direct, file, or env sources
    let password = resolve_password(
        args.password.as_deref(),
        args.password_file.as_deref(),
        args.password_env.as_deref(),
        "decryption password",
    )?;

    let decrypted = decrypt_pdf(&pdf_data, &password)
        .context("Decryption failed - incorrect password or corrupted file")?;

    std::fs::write(&args.output, &decrypted)
        .with_context(|| format!("Failed to write decrypted PDF to: {}", args.output))?;

    eprintln!("Decrypted PDF written to: {}", args.output);

    Ok(())
}