initial version

This commit is contained in:
Robin Appelman 2025-10-12 01:44:11 +02:00
commit b6ac4f9f4b
5 changed files with 375 additions and 5 deletions

183
Cargo.lock generated
View file

@ -2,6 +2,187 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "bitflags"
version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "main_error"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "155db5e86c6e45ee456bf32fad5a290ee1f7151c2faca27ea27097568da67d1a"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "originfox"
version = "0.1.0"
version = "1.0.0"
dependencies = [
"main_error",
"owo-colors",
"serde",
"serde_json",
"xattr",
]
[[package]]
name = "owo-colors"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "syn"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "xattr"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
dependencies = [
"libc",
"rustix",
]

View file

@ -1,6 +1,12 @@
[package]
name = "originfox"
version = "0.1.0"
description = "Save download origin data to extended attributes"
version = "1.0.0"
edition = "2024"
[dependencies]
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
main_error = "0.1.2"
xattr = "1.6.1"
owo-colors = "4.2.3"

29
addon/background.js Normal file
View file

@ -0,0 +1,29 @@
function onResponse(response) {
if (!response.success) {
console.error("Error setting attributes");
}
}
function onError(error) {
console.log(`Error: ${error}`);
}
function handleChanged(delta) {
if (delta.state && delta.state.current === "complete") {
browser.downloads
.search({
id: delta.id,
}).then(downloads => {
let download = downloads[0];
browser.runtime.sendNativeMessage("originfox", {
path: download.filename,
origin: download.url,
referrer: download.referrer,
}).then(onResponse, onError);
});
}
}
browser.downloads.onChanged.addListener(handleChanged);

19
addon/manifest.json Normal file
View file

@ -0,0 +1,19 @@
{
"description": "Save download origin data to extended attributes",
"manifest_version": 2,
"name": "originfox",
"version": "1.0",
"browser_specific_settings": {
"gecko": {
"id": "originfox@icewind.nl",
"strict_min_version": "50.0"
}
},
"background": {
"scripts": ["background.js"]
},
"permissions": ["nativeMessaging", "downloads"]
}

View file

@ -1,3 +1,138 @@
fn main() {
println!("Hello, world!");
use main_error::MainResult;
use owo_colors::OwoColorize;
use serde::{Deserialize, Serialize};
use std::env::{args, current_exe, home_dir};
use std::fs::write;
use std::io::{stdin, stdout, Error as IoError, ErrorKind, IsTerminal, Read, Write};
fn main() -> MainResult {
let mut args = args();
if let Some(arg) = args.nth(1) {
match arg.as_str() {
"--help" => {
help();
return Ok(());
}
"--generate-manifest" => {
stdout().write_all(generate_manifest()?.as_bytes())?;
return Ok(());
}
"--install-manifest" => {
let home_dir = home_dir().expect("can't determine home directory");
write(
format!(
"{}/.mozilla/native-messaging-hosts/originfox.json",
home_dir.display()
),
generate_manifest()?,
)?;
return Ok(());
}
_ => {}
}
}
let input = stdin();
if input.is_terminal() {
help();
return Ok(());
}
let mut input = input.lock();
let packet_bytes = read_packet_bytes(&mut input)?;
let packet: Packet = serde_json::from_slice(&packet_bytes)?;
let success = set_attr(packet).is_ok();
let response = encode(&Response { success })?;
stdout().write_all(&response)?;
Ok(())
}
fn help() {
println!(
"{} {} {}",
"Usage:".bright_yellow().bold(),
"originfox".blue().bold(),
"[OPTION]".green()
);
println!();
println!("{}", "Options:".bright_yellow().bold());
println!(
" {} Generate and print a native messaging manifest",
"--generate-manifest".blue().bold()
);
println!(
" {} Generate and install a native messaging manifest",
"--install-manifest".blue().bold()
);
}
#[derive(Debug, Deserialize)]
struct Packet {
path: String,
origin: String,
referrer: String,
}
#[derive(Debug, Serialize)]
struct Response {
success: bool,
}
fn set_attr(packet: Packet) -> Result<(), IoError> {
xattr::set(
&packet.path,
"user.xdg.origin.url",
packet.origin.as_bytes(),
)?;
xattr::set(
&packet.path,
"user.xdg.referrer.url",
packet.referrer.as_bytes(),
)?;
Ok(())
}
/// Read a packet consisting of an u32 size followed by the data
fn read_packet_bytes<R: Read>(mut reader: R) -> Result<Vec<u8>, IoError> {
let mut size_bits = [0u8; 4];
reader.read_exact(&mut size_bits)?;
let size = u32::from_ne_bytes(size_bits);
if size > 1024 * 1024 {
return Err(ErrorKind::FileTooLarge.into());
}
let mut buff = vec![0; size as usize];
reader.read_exact(&mut buff)?;
Ok(buff)
}
/// Write a packet consisting of an u32 size followed by the data
fn encode<T: Serialize>(message: T) -> Result<Vec<u8>, serde_json::Error> {
let mut buff = Vec::with_capacity(128);
buff.resize(4, 0);
serde_json::to_writer(&mut buff, &message)?;
let len = buff.len() - 4;
buff[0..4].copy_from_slice(&(len as u32).to_ne_bytes());
Ok(buff)
}
fn generate_manifest() -> Result<String, IoError> {
let self_path = current_exe()?;
let self_path = self_path.to_str().ok_or(ErrorKind::InvalidFilename)?;
Ok(format!(
r#"{{
"name": "originfox",
"description": "Save download origin data to extended attributes",
"path": "{}",
"type": "stdio",
"allowed_extensions": [ "originfox@icewind.nl" ]
}}"#,
self_path
))
}