use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
pub fn is_zip(path: &Path) -> bool {
check_magic_bytes(path, &[0x50, 0x4B, 0x03, 0x04])
}
pub fn is_gzip(path: &Path) -> bool {
check_magic_bytes(path, &[0x1F, 0x8B])
}
pub fn is_squashfs(path: &Path) -> bool {
check_magic_bytes(path, &[0x68, 0x73, 0x71, 0x73])
|| check_magic_bytes(path, &[0x73, 0x71, 0x73, 0x68])
}
pub fn is_nsis_installer(path: &Path) -> bool {
const CHUNK_SIZE: usize = 64 * 1024;
const NSIS_SIGNATURE: &[u8] = b"Nullsoft.NSIS.exehead";
let mut file = match File::open(path) {
Ok(f) => f,
Err(_) => return false,
};
let mut reader = BufReader::new(&mut file);
let overlap = NSIS_SIGNATURE.len().saturating_sub(1);
let mut buffer = vec![0u8; CHUNK_SIZE + overlap];
let mut carry_len = 0;
loop {
let bytes_read = match reader.read(&mut buffer[carry_len..carry_len + CHUNK_SIZE]) {
Ok(n) => n,
Err(_) => return false,
};
if bytes_read == 0 {
return false;
}
let search_len = carry_len + bytes_read;
if buffer[..search_len]
.windows(NSIS_SIGNATURE.len())
.any(|window| window == NSIS_SIGNATURE)
{
return true;
}
if overlap == 0 || search_len <= overlap {
return false;
}
let carry_start = search_len - overlap;
buffer.copy_within(carry_start..search_len, 0);
carry_len = overlap;
}
}
fn check_magic_bytes(path: &Path, magic: &[u8]) -> bool {
let mut file = match File::open(path) {
Ok(f) => f,
Err(_) => return false,
};
let mut buffer = vec![0u8; magic.len()];
match file.read_exact(&mut buffer) {
Ok(()) => buffer == magic,
Err(_) => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_is_zip() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(&[0x50, 0x4B, 0x03, 0x04, 0x00, 0x00])
.unwrap();
assert!(is_zip(file.path()));
let mut file2 = NamedTempFile::new().unwrap();
file2.write_all(&[0x1F, 0x8B, 0x08, 0x00]).unwrap();
assert!(!is_zip(file2.path()));
assert!(!is_zip(Path::new("/nonexistent/file.zip")));
}
#[test]
fn test_is_gzip() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(&[0x1F, 0x8B, 0x08, 0x00]).unwrap();
assert!(is_gzip(file.path()));
let mut file2 = NamedTempFile::new().unwrap();
file2.write_all(&[0x50, 0x4B, 0x03, 0x04]).unwrap();
assert!(!is_gzip(file2.path()));
}
#[test]
fn test_is_squashfs_little_endian() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(&[0x68, 0x73, 0x71, 0x73, 0x00, 0x00])
.unwrap();
assert!(is_squashfs(file.path()));
}
#[test]
fn test_is_squashfs_big_endian() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(&[0x73, 0x71, 0x73, 0x68, 0x00, 0x00])
.unwrap();
assert!(is_squashfs(file.path()));
}
#[test]
fn test_is_squashfs_negative() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(&[0x50, 0x4B, 0x03, 0x04]).unwrap();
assert!(!is_squashfs(file.path()));
assert!(!is_squashfs(Path::new("/nonexistent/file.squashfs")));
}
#[test]
fn test_is_nsis_installer() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(b"MZ\x90\x00").unwrap(); file.write_all(b"Nullsoft.NSIS.exehead").unwrap();
file.write_all(&[0u8; 100]).unwrap();
assert!(is_nsis_installer(file.path()));
let mut file2 = NamedTempFile::new().unwrap();
file2.write_all(&vec![0u8; 1000]).unwrap();
file2.write_all(b"Nullsoft.NSIS.exehead").unwrap();
assert!(is_nsis_installer(file2.path()));
let mut file3 = NamedTempFile::new().unwrap();
file3.write_all(b"This is not an NSIS installer").unwrap();
assert!(!is_nsis_installer(file3.path()));
assert!(!is_nsis_installer(Path::new("/nonexistent/setup.exe")));
}
#[test]
fn test_is_nsis_installer_beyond_initial_chunk() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(&vec![0u8; 70_000]).unwrap();
file.write_all(b"Nullsoft.NSIS.exehead").unwrap();
assert!(is_nsis_installer(file.path()));
}
#[test]
fn test_check_magic_bytes_short_file() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(&[0x50, 0x4B]).unwrap(); assert!(!check_magic_bytes(file.path(), &[0x50, 0x4B, 0x03, 0x04]));
}
#[test]
fn test_check_magic_bytes_empty_file() {
let file = NamedTempFile::new().unwrap();
assert!(!check_magic_bytes(file.path(), &[0x50, 0x4B]));
}
}