#include "path_sanitizer.h"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

namespace fetchml::common {

bool canonicalize_and_validate(const char* path, char* out_canonical, size_t out_size) {
    if (!path || !out_canonical || out_size == 0) {
        return false;
    }
    
    // Use realpath to canonicalize (resolves symlinks, removes .., etc.)
    char* resolved = realpath(path, nullptr);
    if (!resolved) {
        return false;
    }
    
    // Check size
    size_t len = strlen(resolved);
    if (len >= out_size) {
        free(resolved);
        return false;
    }
    
    // Copy to output
    memcpy(out_canonical, resolved, len + 1);
    free(resolved);
    
    // Additional validation: ensure no embedded nulls or control chars
    for (size_t i = 0; i < len; i++) {
        if (out_canonical[i] == '\0' || (unsigned char)out_canonical[i] < 32) {
            return false;
        }
    }
    
    return true;
}

int open_dir_nofollow(const char* path) {
    if (!path) return -1;
    
    // Open with O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC
    // O_NOFOLLOW ensures we don't follow symlinks
    // O_DIRECTORY ensures it's actually a directory
    return open(path, O_DIRECTORY | O_NOFOLLOW | O_RDONLY | O_CLOEXEC);
}

int openat_nofollow(int dir_fd, const char* filename, int flags, int mode) {
    if (dir_fd < 0 || !filename) return -1;
    
    // Use O_NOFOLLOW to prevent symlink attacks
    // Use openat to open relative to directory fd
    return openat(dir_fd, filename, flags | O_NOFOLLOW | O_CLOEXEC, mode);
}

} // namespace fetchml::common
