/*
 * ==================================================================================
 * Utility Functions - Common utilities
 *
 * ISA Project - ICMP Covert Channel File Transfer System
 * Author: Roman Necas (xnecasr00)
 * Date: 13. October 2025
 *
 * DESCRIPTION:
 * This module provides essential utility functions used throughout the application.
 * It handles filename validation, path security, and session ID generation.
 *
 * KEY FUNCTIONS:
 * - validate_filename() - Security validation for filenames
 * - get_safe_filename() - Extract basename and prevent file overwrites
 * - generate_secure_session_id() - Cryptographically secure session ID generation
 * - calculate_icmp_checksum() - ICMP packet checksum calculation
 *
 * SECURITY FEATURES:
 * - Path traversal prevention (no ".." sequences)
 * - Absolute path rejection (no leading "/")
 * - Control character filtering
 * - Prohibited character checking
 * - Automatic filename collision resolution
 * - Cryptographically secure random number generation
 *
 * IMPLEMENTATION NOTES:
 * - Uses OpenSSL's RAND_bytes() for secure session IDs
 * - Falls back to pseudo-random if secure RNG fails
 * - Automatically generates alternative filenames if file exists
 * - Limits collision resolution attempts to 1000 to prevent infinite loops
 * ==================================================================================
 */

#include "secret.h"

/* External login variable for key derivation (defined in argument_parser.c) */
extern char *login;

void cleanup_config(config_t *config);

int crypto_init_encrypt(crypto_engine_t *crypto);
int crypto_init_decrypt(crypto_engine_t *crypto);
int crypto_finalize_encrypt(crypto_engine_t *crypto, uint8_t *output, int *output_len);
int crypto_finalize_decrypt(crypto_engine_t *crypto, uint8_t *output, int *output_len);
void crypto_get_salt(const crypto_engine_t *crypto, uint8_t *salt);
void crypto_get_iv(const crypto_engine_t *crypto, uint8_t *iv);

int network_send_reply(network_interface_t *net, const void *payload, size_t payload_len,
                      const struct sockaddr_storage *dest_addr, socklen_t dest_addr_len);

int file_manager_seek_to_position(file_manager_t *fm, uint64_t position);
int file_manager_get_fragment_info(const file_manager_t *fm, uint32_t *current, uint32_t *total);
uint64_t file_manager_get_file_size(const file_manager_t *fm);
uint64_t file_manager_get_bytes_processed(const file_manager_t *fm);
const char *file_manager_get_filename(const file_manager_t *fm);
int file_manager_is_complete(const file_manager_t *fm);
void file_manager_set_file_size(file_manager_t *fm, uint64_t file_size);
int file_manager_get_basename(const char *path, char *basename, size_t basename_len);

int create_start_packet(uint16_t session_id, const iftp_metadata_t *metadata,
                       uint8_t *packet_buffer, size_t *packet_len);
int create_data_packet(uint16_t session_id, uint32_t seq_num, uint32_t frag_current,
                      uint32_t frag_total, const void *data, size_t data_len,
                      uint8_t *packet_buffer, size_t *packet_len);
int create_end_packet(uint16_t session_id, uint32_t seq_num,
                     uint8_t *packet_buffer, size_t *packet_len);
int create_ack_packet(uint16_t session_id, uint32_t seq_num,
                     uint8_t *packet_buffer, size_t *packet_len);

/*
 * calculate_icmp_checksum - Calculate RFC 1071 Internet checksum
 *
 * Implements the standard Internet checksum algorithm used for ICMP packets.
 * This is a 16-bit one's complement of the one's complement sum of the data.
 *
 * @param data: Pointer to data buffer to checksum
 * @param len: Length of data in bytes
 * @return: 16-bit checksum value
 *
 * Algorithm:
 * 1. Sum all 16-bit words in the buffer
 * 2. Handle odd byte at end if length is odd
 * 3. Add carry bits back into sum (fold)
 * 4. Return one's complement of final sum
 *
 * Note: This checksum provides basic error detection but is not cryptographically secure.
 */
uint16_t calculate_icmp_checksum(const void *data, size_t len) {
    const uint16_t *ip = (const uint16_t *)data;
    uint32_t sum = 0;

    /* Sum all 16-bit words */
    while (len > 1) {
        sum += *ip++;
        len -= 2;
    }

    /* Handle odd byte at end (pad with zero byte) */
    if (len > 0) {
        sum += *(uint8_t*)ip << 8;
    }

    /* Fold 32-bit sum to 16 bits - add carry bits back */
    while (sum >> 16) {
        sum = (sum & 0xFFFF) + (sum >> 16);
    }

    /* Return one's complement */
    return ~sum;
}

/*
 * validate_filename - Comprehensive filename security validation for SERVER
 *
 * CRITICAL SECURITY FUNCTION: Validates filenames RECEIVED from network clients.
 *
 * IMPORTANT DISTINCTION:
 * - This validates SERVER-SIDE received filenames (from IFTP START packets)
 * - Used by server to protect against malicious clients
 * - Different from client source file paths (argument_parser.c) where user
 *   has full control and can use paths like "../test.file"
 *
 * This function performs multiple security checks on filenames to prevent
 * common attack vectors when receiving files from untrusted network sources:
 * - Path traversal attacks (.. sequences)
 * - Absolute path injection (leading /)
 * - Control characters and special characters
 * - Empty filenames
 *
 * @param filename: Filename string received from network to validate
 * @return: 0 on success (valid filename), -1 on validation failure
 *
 * Security considerations:
 * - Prevents "../../../etc/passwd" style attacks from malicious clients
 * - Blocks "/etc/passwd" absolute paths
 * - Filters ASCII control characters (0-31, 127)
 * - Rejects Windows/filesystem prohibited characters: < > : " | ? *
 * - Ensures basename is non-empty after path stripping
 *
 * Why this is different from client source validation:
 * - Client: User sending their own file (trusted, requires flexibility)
 * - Server: Receiving file from network (untrusted, requires strict validation)
 */
int validate_filename(const char *filename) {
    if (!filename || strlen(filename) == 0) {
        return -1;
    }

    /* Check for path traversal sequences in RECEIVED filenames */
    if (strstr(filename, "..") != NULL) {
        fprintf(stderr, "Error: Received filename contains '..' (potential directory traversal attack)\n");
        return -1;
    }

    /* Reject absolute paths in RECEIVED filenames */
    if (filename[0] == '/') {
        fprintf(stderr, "Error: Absolute path not allowed in received filename (security risk)\n");
        return -1;
    }

    /* Validate character set - reject control characters and prohibited characters */
    for (const char *p = filename; *p; p++) {
        if (*p == '\0') break;
        /* Control characters (including DEL) */
        if (*p < 32 || *p == 127) {
            fprintf(stderr, "Error: Filename contains invalid characters\n");
            return -1;
        }
        /* Prohibited characters in many filesystems */
        if (strchr("<>:\"|?*", *p) != NULL) {
            fprintf(stderr, "Error: Filename contains prohibited characters\n");
            return -1;
        }
    }

    /* Extract and validate basename (filename without directory path) */
    const char *basename = strrchr(filename, '/');
    basename = basename ? basename + 1 : filename;

    if (strlen(basename) == 0) {
        fprintf(stderr, "Error: Empty filename\n");
        return -1;
    }

    return 0;
}

/*
 * get_safe_filename - Extract basename and handle filename collisions
 *
 * Extracts the filename from a full path (strips directory components) and
 * automatically generates an alternative filename if the file already exists
 * in the current directory. This prevents accidental file overwrites.
 *
 * @param original_filename: Full path or filename from client
 * @param safe_filename: Output buffer for sanitized filename
 * @param max_len: Size of output buffer
 * @return: 0 on success, -1 on error
 *
 * Collision resolution strategy:
 * - If "test.txt" exists, tries "test_1.txt", "test_2.txt", etc.
 * - Preserves file extension in generated names
 * - Limits attempts to 1000 to prevent infinite loops
 *
 * Example:
 *   Input: "/home/user/document.pdf"
 *   Output: "document.pdf" (or "document_1.pdf" if exists)
 */
int get_safe_filename(const char *original_filename, char *safe_filename, size_t max_len) {
    if (!original_filename || !safe_filename || max_len == 0) {
        return -1;
    }

    /* Extract basename from path (everything after last '/' or entire string) */
    const char *basename = strrchr(original_filename, '/');
    if (basename) {
        basename++; /* Skip the '/' character */
    } else {
        basename = original_filename;
    }

    /* Validate extracted basename */
    if (strlen(basename) == 0) {
        return -1;
    }

    /* Copy basename to output buffer with null termination */
    strncpy(safe_filename, basename, max_len - 1);
    safe_filename[max_len - 1] = '\0';

    /* If file doesn't exist in current directory, use it as-is */
    if (access(safe_filename, F_OK) != 0) {
        return 0;
    }

    /* File collision detected - generate alternative name with counter */
    printf("Warning: File '%s' already exists\n", safe_filename);

    /* Separate filename into base and extension */
    char *dot = strrchr(safe_filename, '.');
    int counter = 1;
    char base_name[MAX_FILENAME_LEN];
    char extension[64] = "";

    if (dot && dot != safe_filename) {
        /* Has extension: split at last dot */
        size_t base_len = dot - safe_filename;
        strncpy(base_name, safe_filename, base_len);
        base_name[base_len] = '\0';
        strncpy(extension, dot, sizeof(extension) - 1);
        extension[sizeof(extension) - 1] = '\0';
    } else {
        /* No extension: use entire filename as base */
        strncpy(base_name, safe_filename, sizeof(base_name) - 1);
        base_name[sizeof(base_name) - 1] = '\0';
    }

    /* Try alternative names with incrementing counter until we find one that doesn't exist */
    do {
        snprintf(safe_filename, max_len, "%s_%d%s", base_name, counter, extension);
        counter++;
        /* Safety limit to prevent infinite loops */
        if (counter > 1000) {
            fprintf(stderr, "Error: Too many existing files with similar names\n");
            return -1;
        }
    } while (access(safe_filename, F_OK) == 0);

    printf("Using safe filename: %s\n", safe_filename);
    return 0;
}

/*
 * generate_secure_session_id - Generate cryptographically secure session ID
 *
 * Creates a 16-bit session identifier using OpenSSL's cryptographically secure
 * random number generator (RAND_bytes). Falls back to pseudo-random generation
 * if secure RNG fails. Session ID 0 is reserved and automatically replaced with 1.
 *
 * @return: 16-bit session ID (1-65535)
 *
 * Security considerations:
 * - Uses OpenSSL RAND_bytes() for cryptographic quality randomness
 * - Fallback uses time() and PID for pseudo-randomness (less secure)
 * - Session ID 0 is reserved for protocol purposes
 *
 * Note: Session IDs help distinguish concurrent transfers and prevent packet confusion
 */
uint16_t generate_secure_session_id(void) {
    uint16_t session_id;

    /* Attempt to generate cryptographically secure random session ID */
    if (RAND_bytes((unsigned char*)&session_id, sizeof(session_id)) != 1) {
        /* Secure RNG failed - fall back to pseudo-random (less secure but functional) */
        fprintf(stderr, "Warning: Failed to generate cryptographically secure session ID, using fallback\n");

        /* Seed with time and process ID for some entropy */
        srand(time(NULL) ^ getpid());
        session_id = (uint16_t)(rand() ^ (rand() << 8));
    }

    /* Reserve session ID 0 for protocol purposes (should not be used for transfers) */
    if (session_id == 0) {
        session_id = 1;
    }

    return session_id;
}