diff --git a/Cargo.lock b/Cargo.lock index bb5fb79..1e83fcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,28 +64,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "aws-lc-rs" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.37.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - [[package]] name = "base64" version = "0.22.1" @@ -129,29 +107,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "clap" version = "4.5.60" @@ -211,36 +175,17 @@ dependencies = [ "roff", ] -[[package]] -name = "cmake" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] - [[package]] name = "colorchoice" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - [[package]] name = "core-foundation" -version = "0.10.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -264,10 +209,20 @@ dependencies = [ ] [[package]] -name = "dunce" -version = "1.0.5" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" @@ -275,6 +230,21 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -284,12 +254,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "futures-channel" version = "0.3.32" @@ -339,19 +303,6 @@ dependencies = [ "slab", ] -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - [[package]] name = "getrandom" version = "0.3.4" @@ -359,11 +310,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", "wasip2", - "wasm-bindgen", ] [[package]] @@ -433,18 +382,18 @@ dependencies = [ ] [[package]] -name = "hyper-rustls" -version = "0.27.7" +name = "hyper-tls" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ - "http", + "bytes", + "http-body-util", "hyper", "hyper-util", - "rustls", - "rustls-pki-types", + "native-tls", "tokio", - "tokio-rustls", + "tokio-native-tls", "tower-service", ] @@ -601,38 +550,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - [[package]] name = "js-sys" version = "0.3.88" @@ -649,6 +566,12 @@ version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "litemap" version = "0.8.1" @@ -661,12 +584,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - [[package]] name = "memchr" version = "2.8.0" @@ -684,6 +601,23 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -697,10 +631,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] -name = "openssl-probe" -version = "0.2.1" +name = "openssl" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] [[package]] name = "percent-encoding" @@ -720,6 +692,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "potential_utf" version = "0.1.4" @@ -729,15 +707,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -747,62 +716,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" version = "1.0.44" @@ -818,35 +731,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - [[package]] name = "reqwest" version = "0.13.2" @@ -862,19 +746,17 @@ dependencies = [ "http-body", "http-body-util", "hyper", - "hyper-rustls", + "hyper-tls", "hyper-util", "js-sys", "log", + "native-tls", "percent-encoding", "pin-project-lite", - "quinn", - "rustls", "rustls-pki-types", - "rustls-platform-verifier", "sync_wrapper", "tokio", - "tokio-rustls", + "tokio-native-tls", "tower", "tower-http", "tower-service", @@ -884,20 +766,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - [[package]] name = "roff" version = "0.2.2" @@ -905,35 +773,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" [[package]] -name = "rustc-hash" -version = "2.1.1" +name = "rustix" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustls" -version = "0.23.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "aws-lc-rs", - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] @@ -942,64 +791,15 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ - "web-time", "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" version = "0.1.28" @@ -1011,9 +811,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.7.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags", "core-foundation", @@ -1102,12 +902,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "2.0.117" @@ -1140,43 +934,16 @@ dependencies = [ ] [[package]] -name = "thiserror" -version = "1.0.69" +name = "tempfile" +version = "3.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.52.0", ] [[package]] @@ -1189,21 +956,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.49.0" @@ -1219,12 +971,12 @@ dependencies = [ ] [[package]] -name = "tokio-rustls" -version = "0.26.4" +name = "tokio-native-tls" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ - "rustls", + "native-tls", "tokio", ] @@ -1304,12 +1056,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "url" version = "2.5.8" @@ -1335,14 +1081,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "walkdir" -version = "2.5.0" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "want" @@ -1437,49 +1179,12 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - [[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.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -1507,21 +1212,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -1555,12 +1245,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -1573,12 +1257,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -1591,12 +1269,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1621,12 +1293,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -1639,12 +1305,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -1657,12 +1317,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -1675,12 +1329,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -1728,26 +1376,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.8.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zerofrom" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index cee52ac..bc1ff64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ base64 = "0.22.1" clap = { version = "4.5.60", features = ["derive"] } clap_complete = "4.5.66" clap_mangen = "0.2.31" -reqwest = { version = "0.13.2", default-features = false, features = ["blocking", "rustls"] } +reqwest = { version = "0.13.2", default-features = false, features = ["blocking", "native-tls"] } [lints.rust] warnings = "deny" diff --git a/README.md b/README.md index 104ae84..14c1fb5 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ target/release/ca-certs ### Meson (packaging-friendly) -Builds the Rust binary and installs the man page + shell completions from `contrib/`. +Builds the Rust binary and generates man/completion artifacts during install. ```bash meson setup build @@ -156,7 +156,7 @@ Run tests: cargo test ``` -The repository also includes a generated man page and shell completions under `contrib/`. +Man page and shell completions are generated by `ca-certs gen-artifacts`. ## License diff --git a/contrib/ca-certs.8 b/contrib/ca-certs.8 deleted file mode 100644 index 1744559..0000000 --- a/contrib/ca-certs.8 +++ /dev/null @@ -1,31 +0,0 @@ -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.TH ca-certs 8 "ca-certs 0.1.0" -.SH NAME -ca\-certs \- Manage Linux CA trust store sources and extracted bundles (p11\-kit/trust) -.SH SYNOPSIS -\fBca\-certs\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] <\fIsubcommands\fR> -.SH DESCRIPTION -Manage Linux CA trust store sources and extracted bundles (p11\-kit/trust) -.SH OPTIONS -.TP -\fB\-h\fR, \fB\-\-help\fR -Print help -.TP -\fB\-V\fR, \fB\-\-version\fR -Print version -.SH SUBCOMMANDS -.TP -ca\-certs\-add(8) -Add a PEM CA certificate to trust\-source/anchors and refresh extracted outputs -.TP -ca\-certs\-extract(8) -Regenerate extracted trust bundles (like update\-ca\-trust extract) -.TP -ca\-certs\-certdata(8) -Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet) -.TP -ca\-certs\-help(8) -Print this message or the help of the given subcommand(s) -.SH VERSION -v0.1.0 diff --git a/contrib/completions/_ca-certs b/contrib/completions/_ca-certs deleted file mode 100644 index 2220bb6..0000000 --- a/contrib/completions/_ca-certs +++ /dev/null @@ -1,401 +0,0 @@ -#compdef ca-certs - -autoload -U is-at-least - -_ca-certs() { - typeset -A opt_args - typeset -a _arguments_options - local ret=1 - - if is-at-least 5.2; then - _arguments_options=(-s -S -C) - else - _arguments_options=(-s -C) - fi - - local context curcontext="$curcontext" state line - _arguments "${_arguments_options[@]}" : \ -'-h[Print help]' \ -'--help[Print help]' \ -'-V[Print version]' \ -'--version[Print version]' \ -":: :_ca-certs_commands" \ -"*::: :->ca-certs" \ -&& ret=0 - case $state in - (ca-certs) - words=($line[1] "${words[@]}") - (( CURRENT += 1 )) - curcontext="${curcontext%:*:*}:ca-certs-command-$line[1]:" - case $line[1] in - (add) -_arguments "${_arguments_options[@]}" : \ -'-n+[Output certificate name (without extension). Defaults to the input filename stem]:NAME:_default' \ -'--name=[Output certificate name (without extension). Defaults to the input filename stem]:NAME:_default' \ -'-o+[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:OUTPUT:_files' \ -'--output=[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:OUTPUT:_files' \ -'--root=[Target root filesystem (for chroot/image builds)]:ROOT:_files' \ -'--force[Overwrite an existing anchor if contents differ]' \ -'--no-extract[Add certificate but do not run extraction]' \ -'--dry-run[Print actions without modifying files]' \ -'-h[Print help]' \ -'--help[Print help]' \ -':cert -- Path to a PEM-encoded CA certificate:_files' \ -&& ret=0 -;; -(extract) -_arguments "${_arguments_options[@]}" : \ -'-o+[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:OUTPUT:_files' \ -'--output=[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:OUTPUT:_files' \ -'--root=[Target root filesystem (for chroot/image builds)]:ROOT:_files' \ -'--dry-run[Print actions without modifying files]' \ -'-h[Print help]' \ -'--help[Print help]' \ -&& ret=0 -;; -(certdata) -_arguments "${_arguments_options[@]}" : \ -'-h[Print help]' \ -'--help[Print help]' \ -":: :_ca-certs__certdata_commands" \ -"*::: :->certdata" \ -&& ret=0 - - case $state in - (certdata) - words=($line[1] "${words[@]}") - (( CURRENT += 1 )) - curcontext="${curcontext%:*:*}:ca-certs-certdata-command-$line[1]:" - case $line[1] in - (fetch) -_arguments "${_arguments_options[@]}" : \ -'--url=[Source URL for certdata.txt]:URL:_default' \ -'--log-url=[Override log URL used to determine the latest revision (optional)]:LOG_URL:_default' \ -'-o+[Output file path]:OUTPUT:_files' \ -'--output=[Output file path]:OUTPUT:_files' \ -'--force[Overwrite an existing file if contents differ]' \ -'--no-revision-check[Always download even if the local file revision matches the remote revision]' \ -'--no-parse[Skip parsing/summary after download]' \ -'--dry-run[Print actions without modifying files]' \ -'-h[Print help]' \ -'--help[Print help]' \ -&& ret=0 -;; -(parse) -_arguments "${_arguments_options[@]}" : \ -'--limit=[Print up to N object summaries after the aggregate stats]:LIMIT:_default' \ -'-h[Print help]' \ -'--help[Print help]' \ -'::input -- Path to certdata.txt:_files' \ -&& ret=0 -;; -(convert) -_arguments "${_arguments_options[@]}" : \ -'--root=[Target root filesystem (for chroot/image builds)]:ROOT:_files' \ -'--output=[Destination p11-kit trust source file inside the target root]:OUTPUT:_files' \ -'-o+[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:EXTRACT_OUTPUT:_files' \ -'--extract-output=[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:EXTRACT_OUTPUT:_files' \ -'--force[Overwrite an existing output file if contents differ]' \ -'--no-extract[Skip running trust extraction after writing the Mozilla source bundle]' \ -'--dry-run[Print actions without modifying files]' \ -'-h[Print help]' \ -'--help[Print help]' \ -'::input -- Path to certdata.txt:_files' \ -&& ret=0 -;; -(sync) -_arguments "${_arguments_options[@]}" : \ -'--url=[Source URL for certdata.txt]:URL:_default' \ -'--log-url=[Override log URL used to determine the latest revision (optional)]:LOG_URL:_default' \ -'--certdata-output=[Local certdata.txt path used for fetch and convert]:CERTDATA_OUTPUT:_files' \ -'--root=[Target root filesystem (for chroot/image builds)]:ROOT:_files' \ -'--mozilla-output=[Destination p11-kit trust source file inside the target root]:MOZILLA_OUTPUT:_files' \ -'-o+[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:EXTRACT_OUTPUT:_files' \ -'--extract-output=[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:EXTRACT_OUTPUT:_files' \ -'--force[Overwrite existing files if contents differ]' \ -'--no-revision-check[Always download even if the local certdata revision matches the remote revision]' \ -'--no-parse[Skip parsing/summary after download]' \ -'--no-extract[Skip running trust extraction after writing the Mozilla source bundle]' \ -'--dry-run[Print actions without modifying files]' \ -'-h[Print help]' \ -'--help[Print help]' \ -&& ret=0 -;; -(help) -_arguments "${_arguments_options[@]}" : \ -":: :_ca-certs__certdata__help_commands" \ -"*::: :->help" \ -&& ret=0 - - case $state in - (help) - words=($line[1] "${words[@]}") - (( CURRENT += 1 )) - curcontext="${curcontext%:*:*}:ca-certs-certdata-help-command-$line[1]:" - case $line[1] in - (fetch) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(parse) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(convert) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(sync) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(help) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; - esac - ;; -esac -;; - esac - ;; -esac -;; -(gen-artifacts) -_arguments "${_arguments_options[@]}" : \ -'--out-dir=[Directory to write generated packaging artifacts into]:OUT_DIR:_files' \ -'*--shell=[Shell completions to generate]:SHELLS:(bash fish zsh)' \ -'--dry-run[Print actions without modifying files]' \ -'-h[Print help]' \ -'--help[Print help]' \ -&& ret=0 -;; -(help) -_arguments "${_arguments_options[@]}" : \ -":: :_ca-certs__help_commands" \ -"*::: :->help" \ -&& ret=0 - - case $state in - (help) - words=($line[1] "${words[@]}") - (( CURRENT += 1 )) - curcontext="${curcontext%:*:*}:ca-certs-help-command-$line[1]:" - case $line[1] in - (add) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(extract) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(certdata) -_arguments "${_arguments_options[@]}" : \ -":: :_ca-certs__help__certdata_commands" \ -"*::: :->certdata" \ -&& ret=0 - - case $state in - (certdata) - words=($line[1] "${words[@]}") - (( CURRENT += 1 )) - curcontext="${curcontext%:*:*}:ca-certs-help-certdata-command-$line[1]:" - case $line[1] in - (fetch) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(parse) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(convert) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(sync) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; - esac - ;; -esac -;; -(gen-artifacts) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; -(help) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; - esac - ;; -esac -;; - esac - ;; -esac -} - -(( $+functions[_ca-certs_commands] )) || -_ca-certs_commands() { - local commands; commands=( -'add:Add a PEM CA certificate to trust-source/anchors and refresh extracted outputs' \ -'extract:Regenerate extracted trust bundles (like update-ca-trust extract)' \ -'certdata:Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)' \ -'gen-artifacts:' \ -'help:Print this message or the help of the given subcommand(s)' \ - ) - _describe -t commands 'ca-certs commands' commands "$@" -} -(( $+functions[_ca-certs__add_commands] )) || -_ca-certs__add_commands() { - local commands; commands=() - _describe -t commands 'ca-certs add commands' commands "$@" -} -(( $+functions[_ca-certs__certdata_commands] )) || -_ca-certs__certdata_commands() { - local commands; commands=( -'fetch:Fetch certdata.txt from an NSS source URL' \ -'parse:Parse a local certdata.txt file and print a summary' \ -'convert:Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' \ -'sync:Run the full pipeline\: fetch certdata.txt, convert to p11-kit source, then extract outputs' \ -'help:Print this message or the help of the given subcommand(s)' \ - ) - _describe -t commands 'ca-certs certdata commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__convert_commands] )) || -_ca-certs__certdata__convert_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata convert commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__fetch_commands] )) || -_ca-certs__certdata__fetch_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata fetch commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__help_commands] )) || -_ca-certs__certdata__help_commands() { - local commands; commands=( -'fetch:Fetch certdata.txt from an NSS source URL' \ -'parse:Parse a local certdata.txt file and print a summary' \ -'convert:Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' \ -'sync:Run the full pipeline\: fetch certdata.txt, convert to p11-kit source, then extract outputs' \ -'help:Print this message or the help of the given subcommand(s)' \ - ) - _describe -t commands 'ca-certs certdata help commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__help__convert_commands] )) || -_ca-certs__certdata__help__convert_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata help convert commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__help__fetch_commands] )) || -_ca-certs__certdata__help__fetch_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata help fetch commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__help__help_commands] )) || -_ca-certs__certdata__help__help_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata help help commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__help__parse_commands] )) || -_ca-certs__certdata__help__parse_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata help parse commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__help__sync_commands] )) || -_ca-certs__certdata__help__sync_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata help sync commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__parse_commands] )) || -_ca-certs__certdata__parse_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata parse commands' commands "$@" -} -(( $+functions[_ca-certs__certdata__sync_commands] )) || -_ca-certs__certdata__sync_commands() { - local commands; commands=() - _describe -t commands 'ca-certs certdata sync commands' commands "$@" -} -(( $+functions[_ca-certs__extract_commands] )) || -_ca-certs__extract_commands() { - local commands; commands=() - _describe -t commands 'ca-certs extract commands' commands "$@" -} -(( $+functions[_ca-certs__gen-artifacts_commands] )) || -_ca-certs__gen-artifacts_commands() { - local commands; commands=() - _describe -t commands 'ca-certs gen-artifacts commands' commands "$@" -} -(( $+functions[_ca-certs__help_commands] )) || -_ca-certs__help_commands() { - local commands; commands=( -'add:Add a PEM CA certificate to trust-source/anchors and refresh extracted outputs' \ -'extract:Regenerate extracted trust bundles (like update-ca-trust extract)' \ -'certdata:Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)' \ -'gen-artifacts:' \ -'help:Print this message or the help of the given subcommand(s)' \ - ) - _describe -t commands 'ca-certs help commands' commands "$@" -} -(( $+functions[_ca-certs__help__add_commands] )) || -_ca-certs__help__add_commands() { - local commands; commands=() - _describe -t commands 'ca-certs help add commands' commands "$@" -} -(( $+functions[_ca-certs__help__certdata_commands] )) || -_ca-certs__help__certdata_commands() { - local commands; commands=( -'fetch:Fetch certdata.txt from an NSS source URL' \ -'parse:Parse a local certdata.txt file and print a summary' \ -'convert:Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' \ -'sync:Run the full pipeline\: fetch certdata.txt, convert to p11-kit source, then extract outputs' \ - ) - _describe -t commands 'ca-certs help certdata commands' commands "$@" -} -(( $+functions[_ca-certs__help__certdata__convert_commands] )) || -_ca-certs__help__certdata__convert_commands() { - local commands; commands=() - _describe -t commands 'ca-certs help certdata convert commands' commands "$@" -} -(( $+functions[_ca-certs__help__certdata__fetch_commands] )) || -_ca-certs__help__certdata__fetch_commands() { - local commands; commands=() - _describe -t commands 'ca-certs help certdata fetch commands' commands "$@" -} -(( $+functions[_ca-certs__help__certdata__parse_commands] )) || -_ca-certs__help__certdata__parse_commands() { - local commands; commands=() - _describe -t commands 'ca-certs help certdata parse commands' commands "$@" -} -(( $+functions[_ca-certs__help__certdata__sync_commands] )) || -_ca-certs__help__certdata__sync_commands() { - local commands; commands=() - _describe -t commands 'ca-certs help certdata sync commands' commands "$@" -} -(( $+functions[_ca-certs__help__extract_commands] )) || -_ca-certs__help__extract_commands() { - local commands; commands=() - _describe -t commands 'ca-certs help extract commands' commands "$@" -} -(( $+functions[_ca-certs__help__gen-artifacts_commands] )) || -_ca-certs__help__gen-artifacts_commands() { - local commands; commands=() - _describe -t commands 'ca-certs help gen-artifacts commands' commands "$@" -} -(( $+functions[_ca-certs__help__help_commands] )) || -_ca-certs__help__help_commands() { - local commands; commands=() - _describe -t commands 'ca-certs help help commands' commands "$@" -} - -if [ "$funcstack[1]" = "_ca-certs" ]; then - _ca-certs "$@" -else - compdef _ca-certs ca-certs -fi diff --git a/contrib/completions/ca-certs.bash b/contrib/completions/ca-certs.bash deleted file mode 100644 index 24e6e5d..0000000 --- a/contrib/completions/ca-certs.bash +++ /dev/null @@ -1,558 +0,0 @@ -_ca-certs() { - local i cur prev opts cmd - COMPREPLY=() - if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then - cur="$2" - else - cur="${COMP_WORDS[COMP_CWORD]}" - fi - prev="$3" - cmd="" - opts="" - - for i in "${COMP_WORDS[@]:0:COMP_CWORD}" - do - case "${cmd},${i}" in - ",$1") - cmd="ca__certs" - ;; - ca__certs,add) - cmd="ca__certs__add" - ;; - ca__certs,certdata) - cmd="ca__certs__certdata" - ;; - ca__certs,extract) - cmd="ca__certs__extract" - ;; - ca__certs,gen-artifacts) - cmd="ca__certs__gen__artifacts" - ;; - ca__certs,help) - cmd="ca__certs__help" - ;; - ca__certs__certdata,convert) - cmd="ca__certs__certdata__convert" - ;; - ca__certs__certdata,fetch) - cmd="ca__certs__certdata__fetch" - ;; - ca__certs__certdata,help) - cmd="ca__certs__certdata__help" - ;; - ca__certs__certdata,parse) - cmd="ca__certs__certdata__parse" - ;; - ca__certs__certdata,sync) - cmd="ca__certs__certdata__sync" - ;; - ca__certs__certdata__help,convert) - cmd="ca__certs__certdata__help__convert" - ;; - ca__certs__certdata__help,fetch) - cmd="ca__certs__certdata__help__fetch" - ;; - ca__certs__certdata__help,help) - cmd="ca__certs__certdata__help__help" - ;; - ca__certs__certdata__help,parse) - cmd="ca__certs__certdata__help__parse" - ;; - ca__certs__certdata__help,sync) - cmd="ca__certs__certdata__help__sync" - ;; - ca__certs__help,add) - cmd="ca__certs__help__add" - ;; - ca__certs__help,certdata) - cmd="ca__certs__help__certdata" - ;; - ca__certs__help,extract) - cmd="ca__certs__help__extract" - ;; - ca__certs__help,gen-artifacts) - cmd="ca__certs__help__gen__artifacts" - ;; - ca__certs__help,help) - cmd="ca__certs__help__help" - ;; - ca__certs__help__certdata,convert) - cmd="ca__certs__help__certdata__convert" - ;; - ca__certs__help__certdata,fetch) - cmd="ca__certs__help__certdata__fetch" - ;; - ca__certs__help__certdata,parse) - cmd="ca__certs__help__certdata__parse" - ;; - ca__certs__help__certdata,sync) - cmd="ca__certs__help__certdata__sync" - ;; - *) - ;; - esac - done - - case "${cmd}" in - ca__certs) - opts="-h -V --help --version add extract certdata gen-artifacts help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__add) - opts="-n -o -h --name --force --no-extract --output --root --dry-run --help " - if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --name) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -n) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --output) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -o) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --root) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata) - opts="-h --help fetch parse convert sync help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__convert) - opts="-o -h --root --output --force --no-extract --extract-output --dry-run --help [INPUT]" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --root) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --output) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --extract-output) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -o) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__fetch) - opts="-o -h --url --log-url --output --force --no-revision-check --no-parse --dry-run --help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --url) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --log-url) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --output) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -o) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__help) - opts="fetch parse convert sync help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__help__convert) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__help__fetch) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__help__help) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__help__parse) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__help__sync) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__parse) - opts="-h --limit --help [INPUT]" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --limit) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__certdata__sync) - opts="-o -h --url --log-url --certdata-output --root --mozilla-output --force --no-revision-check --no-parse --no-extract --extract-output --dry-run --help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --url) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --log-url) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --certdata-output) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --root) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --mozilla-output) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --extract-output) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -o) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__extract) - opts="-o -h --output --root --dry-run --help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --output) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -o) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --root) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__gen__artifacts) - opts="-h --out-dir --shell --dry-run --help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --out-dir) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --shell) - COMPREPLY=($(compgen -W "bash fish zsh" -- "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help) - opts="add extract certdata gen-artifacts help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__add) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__certdata) - opts="fetch parse convert sync" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__certdata__convert) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__certdata__fetch) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__certdata__parse) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__certdata__sync) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__extract) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__gen__artifacts) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - ca__certs__help__help) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - esac -} - -if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then - complete -F _ca-certs -o nosort -o bashdefault -o default ca-certs -else - complete -F _ca-certs -o bashdefault -o default ca-certs -fi diff --git a/contrib/completions/ca-certs.fish b/contrib/completions/ca-certs.fish deleted file mode 100644 index 8f7189a..0000000 --- a/contrib/completions/ca-certs.fish +++ /dev/null @@ -1,99 +0,0 @@ -# Print an optspec for argparse to handle cmd's options that are independent of any subcommand. -function __fish_ca_certs_global_optspecs - string join \n h/help V/version -end - -function __fish_ca_certs_needs_command - # Figure out if the current invocation already has a command. - set -l cmd (commandline -opc) - set -e cmd[1] - argparse -s (__fish_ca_certs_global_optspecs) -- $cmd 2>/dev/null - or return - if set -q argv[1] - # Also print the command, so this can be used to figure out what it is. - echo $argv[1] - return 1 - end - return 0 -end - -function __fish_ca_certs_using_subcommand - set -l cmd (__fish_ca_certs_needs_command) - test -z "$cmd" - and return 1 - contains -- $cmd[1] $argv -end - -complete -c ca-certs -n "__fish_ca_certs_needs_command" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_needs_command" -s V -l version -d 'Print version' -complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "add" -d 'Add a PEM CA certificate to trust-source/anchors and refresh extracted outputs' -complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "extract" -d 'Regenerate extracted trust bundles (like update-ca-trust extract)' -complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "certdata" -d 'Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)' -complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "gen-artifacts" -complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -s n -l name -d 'Output certificate name (without extension). Defaults to the input filename stem' -r -complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -s o -l output -d 'Extracted output directory inside the target root (default: /etc/ca-certificates/extracted)' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -l root -d 'Target root filesystem (for chroot/image builds)' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -l force -d 'Overwrite an existing anchor if contents differ' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -l no-extract -d 'Add certificate but do not run extraction' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -l dry-run -d 'Print actions without modifying files' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand extract" -s o -l output -d 'Extracted output directory inside the target root (default: /etc/ca-certificates/extracted)' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand extract" -l root -d 'Target root filesystem (for chroot/image builds)' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand extract" -l dry-run -d 'Print actions without modifying files' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand extract" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "fetch" -d 'Fetch certdata.txt from an NSS source URL' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "parse" -d 'Parse a local certdata.txt file and print a summary' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "convert" -d 'Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "sync" -d 'Run the full pipeline: fetch certdata.txt, convert to p11-kit source, then extract outputs' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l url -d 'Source URL for certdata.txt' -r -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l log-url -d 'Override log URL used to determine the latest revision (optional)' -r -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -s o -l output -d 'Output file path' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l force -d 'Overwrite an existing file if contents differ' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l no-revision-check -d 'Always download even if the local file revision matches the remote revision' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l no-parse -d 'Skip parsing/summary after download' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l dry-run -d 'Print actions without modifying files' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from parse" -l limit -d 'Print up to N object summaries after the aggregate stats' -r -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from parse" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l root -d 'Target root filesystem (for chroot/image builds)' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l output -d 'Destination p11-kit trust source file inside the target root' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -s o -l extract-output -d 'Extracted output directory inside the target root (default: /etc/ca-certificates/extracted)' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l force -d 'Overwrite an existing output file if contents differ' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l no-extract -d 'Skip running trust extraction after writing the Mozilla source bundle' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l dry-run -d 'Print actions without modifying files' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l url -d 'Source URL for certdata.txt' -r -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l log-url -d 'Override log URL used to determine the latest revision (optional)' -r -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l certdata-output -d 'Local certdata.txt path used for fetch and convert' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l root -d 'Target root filesystem (for chroot/image builds)' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l mozilla-output -d 'Destination p11-kit trust source file inside the target root' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -s o -l extract-output -d 'Extracted output directory inside the target root (default: /etc/ca-certificates/extracted)' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l force -d 'Overwrite existing files if contents differ' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l no-revision-check -d 'Always download even if the local certdata revision matches the remote revision' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l no-parse -d 'Skip parsing/summary after download' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l no-extract -d 'Skip running trust extraction after writing the Mozilla source bundle' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l dry-run -d 'Print actions without modifying files' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "fetch" -d 'Fetch certdata.txt from an NSS source URL' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "parse" -d 'Parse a local certdata.txt file and print a summary' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "convert" -d 'Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "sync" -d 'Run the full pipeline: fetch certdata.txt, convert to p11-kit source, then extract outputs' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand gen-artifacts" -l out-dir -d 'Directory to write generated packaging artifacts into' -r -F -complete -c ca-certs -n "__fish_ca_certs_using_subcommand gen-artifacts" -l shell -d 'Shell completions to generate' -r -f -a "bash\t'' -fish\t'' -zsh\t''" -complete -c ca-certs -n "__fish_ca_certs_using_subcommand gen-artifacts" -l dry-run -d 'Print actions without modifying files' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand gen-artifacts" -s h -l help -d 'Print help' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "add" -d 'Add a PEM CA certificate to trust-source/anchors and refresh extracted outputs' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "extract" -d 'Regenerate extracted trust bundles (like update-ca-trust extract)' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "certdata" -d 'Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "gen-artifacts" -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and __fish_seen_subcommand_from certdata" -f -a "fetch" -d 'Fetch certdata.txt from an NSS source URL' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and __fish_seen_subcommand_from certdata" -f -a "parse" -d 'Parse a local certdata.txt file and print a summary' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and __fish_seen_subcommand_from certdata" -f -a "convert" -d 'Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' -complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and __fish_seen_subcommand_from certdata" -f -a "sync" -d 'Run the full pipeline: fetch certdata.txt, convert to p11-kit source, then extract outputs' diff --git a/meson.build b/meson.build index b1f3991..9d8218f 100644 --- a/meson.build +++ b/meson.build @@ -14,15 +14,6 @@ tracked_rust_inputs = files( 'src/main.rs', ) -# Track packaging assets in one place so install rules stay auditable. -tracked_packaging_assets = files( - 'LICENSE', - 'contrib/ca-certs.8', - 'contrib/completions/ca-certs.bash', - 'contrib/completions/ca-certs.fish', - 'contrib/completions/_ca-certs', -) - tracked_install_paths = [ join_paths(get_option('bindir'), 'ca-certs'), join_paths(get_option('datadir'), 'licenses', meson.project_name(), 'LICENSE'), @@ -54,31 +45,18 @@ ca_certs_bin = custom_target( install_dir: get_option('bindir'), ) -install_data( - 'contrib/ca-certs.8', - install_dir: join_paths(get_option('mandir'), 'man8'), -) - -install_data( - 'contrib/completions/ca-certs.bash', - install_dir: join_paths(get_option('datadir'), 'bash-completion', 'completions'), - rename: 'ca-certs', -) - -install_data( - 'contrib/completions/ca-certs.fish', - install_dir: join_paths(get_option('datadir'), 'fish', 'vendor_completions.d'), -) - -install_data( - 'contrib/completions/_ca-certs', - install_dir: join_paths(get_option('datadir'), 'zsh', 'site-functions'), +meson.add_install_script( + sh, + files('scripts/install-generated-docs.sh'), + ca_certs_bin_path, + get_option('mandir'), + get_option('datadir'), ) summary( { 'tracked rust inputs': tracked_rust_inputs.length(), - 'tracked packaging assets': tracked_packaging_assets.length(), + 'generated docs on install': true, 'tracked install paths': '\n ' + '\n '.join(tracked_install_paths), }, section: 'Packaging', diff --git a/scripts/install-generated-docs.sh b/scripts/install-generated-docs.sh new file mode 100644 index 0000000..a7db634 --- /dev/null +++ b/scripts/install-generated-docs.sh @@ -0,0 +1,33 @@ +#!/bin/sh +set -eu + +bin="$1" +mandir_opt="$2" +datadir_opt="$3" + +resolve_dir() { + case "$1" in + /*) printf '%s%s\n' "${DESTDIR:-}" "$1" ;; + *) printf '%s/%s\n' "${MESON_INSTALL_DESTDIR_PREFIX:?}" "$1" ;; + esac +} + +man_root="$(resolve_dir "$mandir_opt")" +data_root="$(resolve_dir "$datadir_opt")" +tmp_dir="$(mktemp -d "${TMPDIR:-/tmp}/ca-certs-docs.XXXXXX")" + +cleanup() { + rm -rf "$tmp_dir" +} +trap cleanup EXIT INT TERM + +"$bin" gen-artifacts --out-dir "$tmp_dir" + +install -Dm644 "$tmp_dir/ca-certs.8" \ + "$man_root/man8/ca-certs.8" +install -Dm644 "$tmp_dir/completions/ca-certs.bash" \ + "$data_root/bash-completion/completions/ca-certs" +install -Dm644 "$tmp_dir/completions/ca-certs.fish" \ + "$data_root/fish/vendor_completions.d/ca-certs.fish" +install -Dm644 "$tmp_dir/completions/_ca-certs" \ + "$data_root/zsh/site-functions/_ca-certs" diff --git a/src/main.rs b/src/main.rs index f9c9259..85c52f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,19 +9,84 @@ use std::io::Write; use std::os::unix::fs::{PermissionsExt, symlink}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; +use std::time::{SystemTime, UNIX_EPOCH}; // Default paths mirror the p11-kit / update-ca-trust layout used by this system. const DEFAULT_ROOT: &str = "/"; const DEFAULT_ANCHORS_DIR: &str = "/etc/ca-certificates/trust-source/anchors"; const DEFAULT_EXTRACTED_DIR: &str = "/etc/ca-certificates/extracted"; const DEFAULT_SSL_CERTS_DIR: &str = "/etc/ssl/certs"; +const DEFAULT_SSL_CERT_PEM_LINK: &str = "/etc/ssl/cert.pem"; const DEFAULT_SSL_CA_CERTIFICATES_BUNDLE_LINK: &str = "/etc/ssl/certs/ca-certificates.crt"; +const DEFAULT_SSL_CA_BUNDLE_CRT_LINK: &str = "/etc/ssl/certs/ca-bundle.crt"; const DEFAULT_JAVA_CACERTS_LINK: &str = "/etc/ssl/certs/java/cacerts"; +// BLFS make-ca compatibility paths (commonly used by OpenSSL/libgit2 builds on LFS). +const DEFAULT_PKI_TLS_CA_BUNDLE_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.crt"; +const DEFAULT_PKI_TLS_CA_BUNDLE_TRUST_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.trust.crt"; +const DEFAULT_PKI_TLS_JAVA_CACERTS_LINK: &str = "/etc/pki/tls/java/cacerts"; const DEFAULT_CERTDATA_URL: &str = "https://hg.mozilla.org/projects/nss/raw-file/tip/lib/ckfw/builtins/certdata.txt"; const DEFAULT_CERTDATA_OUTPUT: &str = "certdata.txt"; const DEFAULT_MOZILLA_TRUST_P11KIT: &str = "/usr/share/ca-certificates/trust-source/mozilla.trust.p11-kit"; +const DEFAULT_PKI_MOZILLA_TRUST_P11KIT: &str = "/etc/pki/anchors/mozilla.trust.p11-kit"; +// make-ca uses a pinned ISRG Root X1 to bootstrap downloads from hg.mozilla.org +// before the local trust store exists. We do the same for certdata fetch/sync. +const MOZILLA_HG_BOOTSTRAP_ROOT_PEM: &str = r#"-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +"#; +const MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM: &str = r#"-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- +"#; // Top-level CLI entrypoint with `add` and `extract` subcommands. #[derive(Parser, Debug)] @@ -194,9 +259,10 @@ struct CertdataSyncArgs { #[arg(long)] log_url: Option, - /// Local certdata.txt path used for fetch and convert - #[arg(long, default_value = DEFAULT_CERTDATA_OUTPUT)] - certdata_output: PathBuf, + /// Local certdata.txt path used for fetch and convert. + /// Defaults to a temporary `/tmp/.../certdata.txt` path that is cleaned up. + #[arg(long)] + certdata_output: Option, /// Target root filesystem (for chroot/image builds) #[arg(long, default_value = DEFAULT_ROOT)] @@ -263,6 +329,24 @@ struct ExtractJob { relative_dest: &'static str, } +#[derive(Debug)] +struct TempWorkspaceCleanup { + path: PathBuf, +} + +impl Drop for TempWorkspaceCleanup { + fn drop(&mut self) { + if let Err(err) = fs::remove_dir_all(&self.path) { + if err.kind() != std::io::ErrorKind::NotFound { + eprintln!( + "warning: failed to remove temporary directory {}: {err}", + self.path.display() + ); + } + } + } +} + // Output set mirrors `/bin/update-ca-trust extract`. const EXTRACT_JOBS: &[ExtractJob] = &[ ExtractJob { @@ -308,10 +392,10 @@ const EXTRACT_JOBS: &[ExtractJob] = &[ relative_dest: "java-cacerts.jks", }, ExtractJob { - format: "pem-directory-hash", + format: "openssl-directory", filter: "ca-anchors", - purpose: Some("server-auth"), - comment: false, + purpose: None, + comment: true, relative_dest: "cadir", }, ]; @@ -399,11 +483,14 @@ fn run_certdata(command: CertdataCommands) -> Result<()> { } fn run_certdata_sync(args: CertdataSyncArgs) -> Result<()> { + let (certdata_output, _temp_workspace_guard) = + resolve_sync_certdata_output(args.certdata_output, args.dry_run)?; + // Reuse the existing subcommand implementations so behavior stays aligned. run_certdata_fetch(CertdataFetchArgs { url: args.url, log_url: args.log_url, - output: args.certdata_output.clone(), + output: certdata_output.clone(), force: args.force, no_revision_check: args.no_revision_check, parse: args.parse, @@ -411,7 +498,7 @@ fn run_certdata_sync(args: CertdataSyncArgs) -> Result<()> { })?; run_certdata_convert(CertdataConvertArgs { - input: args.certdata_output, + input: certdata_output, root: args.root, output: args.mozilla_output, force: args.force, @@ -421,6 +508,60 @@ fn run_certdata_sync(args: CertdataSyncArgs) -> Result<()> { }) } +fn resolve_sync_certdata_output( + requested: Option, + dry_run: bool, +) -> Result<(PathBuf, Option)> { + if let Some(path) = requested { + return Ok((path, None)); + } + + if dry_run { + return Ok((build_sync_tmp_workspace_path(0).join(DEFAULT_CERTDATA_OUTPUT), None)); + } + + let workspace = create_sync_tmp_workspace_dir()?; + println!("Using temporary certdata workspace: {}", workspace.display()); + Ok(( + workspace.join(DEFAULT_CERTDATA_OUTPUT), + Some(TempWorkspaceCleanup { path: workspace }), + )) +} + +fn create_sync_tmp_workspace_dir() -> Result { + let tmp_root = Path::new("/tmp"); + for attempt in 0..32_u32 { + let candidate = build_sync_tmp_workspace_path(attempt); + match fs::create_dir(&candidate) { + Ok(()) => return Ok(candidate), + Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => continue, + Err(err) => { + return Err(err).with_context(|| { + format!( + "failed to create temporary certdata directory {}", + candidate.display() + ) + }); + } + } + } + bail!( + "failed to create a unique temporary certdata directory under {}", + tmp_root.display() + ); +} + +fn build_sync_tmp_workspace_path(attempt: u32) -> PathBuf { + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_nanos(); + Path::new("/tmp").join(format!( + "ca-certs-certdata-{}-{nonce:032x}-{attempt:02x}", + std::process::id() + )) +} + fn run_gen_artifacts(args: GenArtifactsArgs) -> Result<()> { let man_path = args.out_dir.join("ca-certs.8"); let completions_dir = args.out_dir.join("completions"); @@ -563,9 +704,7 @@ fn run_certdata_fetch(args: CertdataFetchArgs) -> Result<()> { return Ok(()); } - let client = reqwest::blocking::Client::builder() - .build() - .context("failed to build HTTP client")?; + let client = build_http_client().context("failed to build HTTP client")?; let existing_revision = read_certdata_revision_from_path(&args.output) .ok() .flatten(); @@ -613,15 +752,217 @@ fn run_certdata_fetch(args: CertdataFetchArgs) -> Result<()> { } fn http_get_text(client: &reqwest::blocking::Client, url: &str) -> Result { - let response = client - .get(url) - .send() - .with_context(|| format!("failed to request {url}"))? + let response = match client.get(url).send() { + Ok(response) => response, + Err(err) if is_mozilla_hg_url(url) => { + println!("reqwest fetch failed for Mozilla hg URL; retrying via openssl."); + return openssl_https_get_text(url); + } + Err(err) => return Err(err).with_context(|| format!("failed to request {url}")), + } .error_for_status() .with_context(|| format!("server returned an error for {url}"))?; response.text().context("failed to read response body") } +fn is_mozilla_hg_url(url: &str) -> bool { + reqwest::Url::parse(url) + .ok() + .and_then(|parsed| parsed.host_str().map(str::to_ascii_lowercase)) + .map(|host| host == "hg.mozilla.org" || host == "hg-edge.mozilla.org") + .unwrap_or(false) +} + +fn openssl_https_get_text(url: &str) -> Result { + let parsed = reqwest::Url::parse(url).with_context(|| format!("invalid URL: {url}"))?; + if parsed.scheme() != "https" { + bail!("openssl fallback only supports https URLs: {url}"); + } + + let host = parsed.host_str().context("URL missing host")?; + let port = parsed.port_or_known_default().context("URL missing port")?; + let mut path = parsed.path().to_string(); + if path.is_empty() { + path.push('/'); + } + if let Some(query) = parsed.query() { + path.push('?'); + path.push_str(query); + } + let host_header = match parsed.port() { + Some(p) if p != 443 => format!("{host}:{p}"), + _ => host.to_string(), + }; + + let bootstrap_ca_path = write_bootstrap_ca_tempfile()?; + let result = (|| { + let mut cmd = Command::new("openssl"); + cmd.args([ + "s_client", + "-quiet", + "-verify_return_error", + "-verifyCAfile", + bootstrap_ca_path.to_str().unwrap_or(""), + "-connect", + &format!("{host}:{port}"), + "-servername", + host, + ]); + if Path::new(DEFAULT_SSL_CERTS_DIR).is_dir() { + cmd.args(["-verifyCApath", DEFAULT_SSL_CERTS_DIR]); + } + cmd.stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let mut child = cmd.spawn().context("failed to spawn `openssl s_client`")?; + { + let stdin = child + .stdin + .as_mut() + .context("failed to open openssl stdin")?; + write!( + stdin, + "GET {path} HTTP/1.1\r\nHost: {host_header}\r\nConnection: close\r\n\r\n" + ) + .context("failed to write HTTP request to openssl stdin")?; + } + + let output = child + .wait_with_output() + .context("failed to wait for `openssl s_client`")?; + if !output.status.success() { + bail!( + "openssl s_client failed: {}", + String::from_utf8_lossy(&output.stderr).trim() + ); + } + parse_http_response_text(&output.stdout) + .with_context(|| format!("failed to parse HTTP response from openssl for {url}")) + })(); + + let _ = fs::remove_file(&bootstrap_ca_path); + result +} + +fn write_bootstrap_ca_tempfile() -> Result { + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "ca-certs-hg-bootstrap-{}-{nonce}.pem", + std::process::id() + )); + let bundle = format!( + "{}{}", + MOZILLA_HG_BOOTSTRAP_ROOT_PEM, MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM + ); + fs::write(&path, bundle) + .with_context(|| format!("failed to write {}", path.display()))?; + #[cfg(unix)] + { + fs::set_permissions(&path, fs::Permissions::from_mode(0o600)) + .with_context(|| format!("failed to set permissions on {}", path.display()))?; + } + Ok(path) +} + +fn parse_http_response_text(raw: &[u8]) -> Result { + let (header_bytes, body_bytes) = split_http_response(raw)?; + let header_text = String::from_utf8_lossy(header_bytes); + let mut header_lines = header_text.lines(); + + let status_line = header_lines.next().context("missing HTTP status line")?; + let status_code = status_line + .split_whitespace() + .nth(1) + .context("missing HTTP status code")? + .parse::() + .context("invalid HTTP status code")?; + if !(200..300).contains(&status_code) { + bail!("HTTP request failed with status {status_code}"); + } + + let chunked = header_lines.any(|line| { + line.split_once(':') + .map(|(name, value)| { + name.eq_ignore_ascii_case("transfer-encoding") + && value.to_ascii_lowercase().contains("chunked") + }) + .unwrap_or(false) + }); + + let body = if chunked { + decode_http_chunked_body(body_bytes)? + } else { + body_bytes.to_vec() + }; + String::from_utf8(body).context("HTTP response body was not UTF-8") +} + +fn split_http_response(raw: &[u8]) -> Result<(&[u8], &[u8])> { + if let Some(idx) = raw.windows(4).position(|w| w == b"\r\n\r\n") { + return Ok((&raw[..idx], &raw[idx + 4..])); + } + if let Some(idx) = raw.windows(2).position(|w| w == b"\n\n") { + return Ok((&raw[..idx], &raw[idx + 2..])); + } + bail!("HTTP response missing header/body separator") +} + +fn decode_http_chunked_body(mut input: &[u8]) -> Result> { + let mut out = Vec::new(); + loop { + let (line, rest) = take_http_line(input).context("truncated chunk header")?; + let size_hex = line.split(';').next().unwrap_or("").trim(); + let size = usize::from_str_radix(size_hex, 16) + .with_context(|| format!("invalid chunk size `{size_hex}`"))?; + input = rest; + if size == 0 { + break; + } + if input.len() < size { + bail!("truncated chunk body"); + } + out.extend_from_slice(&input[..size]); + input = &input[size..]; + if input.starts_with(b"\r\n") { + input = &input[2..]; + } else if input.starts_with(b"\n") { + input = &input[1..]; + } else { + bail!("missing chunk terminator"); + } + } + Ok(out) +} + +fn take_http_line(input: &[u8]) -> Option<(String, &[u8])> { + if let Some(idx) = input.windows(2).position(|w| w == b"\r\n") { + let line = String::from_utf8(input[..idx].to_vec()).ok()?; + return Some((line, &input[idx + 2..])); + } + if let Some(idx) = input.iter().position(|b| *b == b'\n') { + let line = String::from_utf8(input[..idx].to_vec()).ok()?; + return Some((line, &input[idx + 1..])); + } + None +} + +fn build_http_client() -> Result { + let bootstrap_ca = reqwest::Certificate::from_pem(MOZILLA_HG_BOOTSTRAP_ROOT_PEM.as_bytes()) + .context("failed to parse bundled Mozilla hg bootstrap root certificate")?; + let digicert_g2_ca = + reqwest::Certificate::from_pem(MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM.as_bytes()) + .context("failed to parse bundled DigiCert Global Root G2 bootstrap certificate")?; + reqwest::blocking::Client::builder() + .add_root_certificate(bootstrap_ca) + .add_root_certificate(digicert_g2_ca) + .build() + .context("failed to construct reqwest client") +} + fn derive_hg_log_url(raw_url: &str) -> Option { if raw_url.contains("/raw-file/") { Some(raw_url.replacen("/raw-file/", "/log/", 1)) @@ -759,10 +1100,17 @@ fn run_certdata_convert(args: CertdataConvertArgs) -> Result<()> { ); } println!("[dry-run] Would write {}", output_host.display()); + if should_write_make_ca_mirror(&args.root, &output_target) { + println!( + "[dry-run] Would also write BLFS make-ca compatibility source {}", + path_in_root(&args.root, Path::new(DEFAULT_PKI_MOZILLA_TRUST_P11KIT)).display() + ); + } } else { let parsed = parsed.context("certdata parse result missing")?; let p11kit = convert_certdata_to_p11kit_bundle(&parsed)?; install_file(&output_host, p11kit.as_bytes(), args.force, false)?; + maybe_install_make_ca_mozilla_mirror(&args.root, &output_target, &p11kit, args.force)?; } if args.no_extract { @@ -774,10 +1122,57 @@ fn run_certdata_convert(args: CertdataConvertArgs) -> Result<()> { .extract_output .unwrap_or_else(|| PathBuf::from(DEFAULT_EXTRACTED_DIR)); extract_trust(&args.root, &extract_output, args.dry_run)?; + if !args.dry_run { + ensure_nonempty_extraction_outputs(&args.root, &extract_output)?; + } println!("certdata conversion and extraction complete."); Ok(()) } +fn should_write_make_ca_mirror(root: &Path, output_target: &Path) -> bool { + output_target == Path::new(DEFAULT_MOZILLA_TRUST_P11KIT) + && path_in_root(root, Path::new("/etc/pki")).exists() +} + +fn maybe_install_make_ca_mozilla_mirror( + root: &Path, + output_target: &Path, + p11kit: &str, + force: bool, +) -> Result<()> { + if !should_write_make_ca_mirror(root, output_target) { + return Ok(()); + } + + let mirror_host = path_in_root(root, Path::new(DEFAULT_PKI_MOZILLA_TRUST_P11KIT)); + println!( + "Installing BLFS make-ca compatibility trust source: {}", + mirror_host.display() + ); + install_file(&mirror_host, p11kit.as_bytes(), force, false) +} + +fn ensure_nonempty_extraction_outputs(root: &Path, extract_output: &Path) -> Result<()> { + let tls_bundle = path_in_root(root, &extract_output.join("tls-ca-bundle.pem")); + let trust_bundle = path_in_root(root, &extract_output.join("ca-bundle.trust.crt")); + let cadir = path_in_root(root, &extract_output.join("cadir")); + + let tls_size = fs::metadata(&tls_bundle).map(|m| m.len()).unwrap_or(0); + let trust_size = fs::metadata(&trust_bundle).map(|m| m.len()).unwrap_or(0); + let cadir_count = fs::read_dir(&cadir).map(|it| it.count()).unwrap_or(0); + + if tls_size == 0 || trust_size == 0 || cadir_count == 0 { + bail!( + "trust extract produced empty outputs (tls-ca-bundle.pem={} bytes, ca-bundle.trust.crt={} bytes, cadir entries={}); p11-kit may be reading a different trust source layout (e.g. BLFS make-ca uses /etc/pki/anchors)", + tls_size, + trust_size, + cadir_count + ); + } + + Ok(()) +} + fn count_cert_and_trust_objects(doc: &CertDataDocument) -> (usize, usize) { let mut certs = 0usize; let mut trusts = 0usize; @@ -1439,10 +1834,18 @@ fn run_trust_extract_job( fn sync_system_ssl_symlinks(root: &Path, extracted_target: &Path, dry_run: bool) -> Result<()> { let ssl_certs_host = path_in_root(root, Path::new(DEFAULT_SSL_CERTS_DIR)); + let cert_pem_link_host = path_in_root(root, Path::new(DEFAULT_SSL_CERT_PEM_LINK)); let ca_bundle_link_host = path_in_root(root, Path::new(DEFAULT_SSL_CA_CERTIFICATES_BUNDLE_LINK)); + let ca_bundle_crt_link_host = path_in_root(root, Path::new(DEFAULT_SSL_CA_BUNDLE_CRT_LINK)); let java_link_host = path_in_root(root, Path::new(DEFAULT_JAVA_CACERTS_LINK)); + let pki_ca_bundle_crt_link_host = + path_in_root(root, Path::new(DEFAULT_PKI_TLS_CA_BUNDLE_CRT_LINK)); + let pki_ca_bundle_trust_crt_link_host = + path_in_root(root, Path::new(DEFAULT_PKI_TLS_CA_BUNDLE_TRUST_CRT_LINK)); + let pki_java_link_host = path_in_root(root, Path::new(DEFAULT_PKI_TLS_JAVA_CACERTS_LINK)); let tls_bundle_src_host = path_in_root(root, &extracted_target.join("tls-ca-bundle.pem")); + let trust_bundle_src_host = path_in_root(root, &extracted_target.join("ca-bundle.trust.crt")); let cadir_host = path_in_root(root, &extracted_target.join("cadir")); let java_src_host = path_in_root(root, &extracted_target.join("java-cacerts.jks")); @@ -1459,16 +1862,41 @@ fn sync_system_ssl_symlinks(root: &Path, extracted_target: &Path, dry_run: bool) "[dry-run] Would delete broken symlinks in {}", ssl_certs_host.display() ); + println!( + "[dry-run] Would link {} -> {}", + cert_pem_link_host.display(), + tls_bundle_src_host.display() + ); println!( "[dry-run] Would link {} -> {}", ca_bundle_link_host.display(), tls_bundle_src_host.display() ); + println!( + "[dry-run] Would link {} -> {}", + ca_bundle_crt_link_host.display(), + tls_bundle_src_host.display() + ); println!( "[dry-run] Would link {} -> {}", java_link_host.display(), java_src_host.display() ); + println!( + "[dry-run] Would link {} -> {}", + pki_ca_bundle_crt_link_host.display(), + tls_bundle_src_host.display() + ); + println!( + "[dry-run] Would link {} -> {}", + pki_ca_bundle_trust_crt_link_host.display(), + trust_bundle_src_host.display() + ); + println!( + "[dry-run] Would link {} -> {}", + pki_java_link_host.display(), + java_src_host.display() + ); return Ok(()); } @@ -1479,6 +1907,21 @@ fn sync_system_ssl_symlinks(root: &Path, extracted_target: &Path, dry_run: bool) .context("java cacerts link path has no parent")?; fs::create_dir_all(java_parent) .with_context(|| format!("failed to create {}", java_parent.display()))?; + let pki_ca_bundle_parent = pki_ca_bundle_crt_link_host + .parent() + .context("pki ca-bundle.crt link path has no parent")?; + fs::create_dir_all(pki_ca_bundle_parent) + .with_context(|| format!("failed to create {}", pki_ca_bundle_parent.display()))?; + let pki_ca_bundle_trust_parent = pki_ca_bundle_trust_crt_link_host + .parent() + .context("pki ca-bundle.trust.crt link path has no parent")?; + fs::create_dir_all(pki_ca_bundle_trust_parent) + .with_context(|| format!("failed to create {}", pki_ca_bundle_trust_parent.display()))?; + let pki_java_parent = pki_java_link_host + .parent() + .context("pki java cacerts link path has no parent")?; + fs::create_dir_all(pki_java_parent) + .with_context(|| format!("failed to create {}", pki_java_parent.display()))?; // Link every extracted hash file into `/etc/ssl/certs`, then prune stale links. for entry in fs::read_dir(&cadir_host) @@ -1491,8 +1934,13 @@ fn sync_system_ssl_symlinks(root: &Path, extracted_target: &Path, dry_run: bool) } remove_broken_symlinks(&ssl_certs_host)?; + replace_symlink(&tls_bundle_src_host, &cert_pem_link_host)?; replace_symlink(&tls_bundle_src_host, &ca_bundle_link_host)?; + replace_symlink(&tls_bundle_src_host, &ca_bundle_crt_link_host)?; replace_symlink(&java_src_host, &java_link_host)?; + replace_symlink(&tls_bundle_src_host, &pki_ca_bundle_crt_link_host)?; + replace_symlink(&trust_bundle_src_host, &pki_ca_bundle_trust_crt_link_host)?; + replace_symlink(&java_src_host, &pki_java_link_host)?; Ok(()) } @@ -1866,6 +2314,34 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR assert_eq!(bytes, b"ABC"); } + #[test] + fn sync_certdata_output_respects_explicit_path() { + let requested = PathBuf::from("/var/tmp/custom-certdata.txt"); + let (resolved, cleanup) = resolve_sync_certdata_output(Some(requested.clone()), false).unwrap(); + assert_eq!(resolved, requested); + assert!(cleanup.is_none()); + } + + #[test] + fn sync_certdata_output_uses_tmp_workspace_and_cleans_it() { + let (resolved, cleanup) = resolve_sync_certdata_output(None, false).unwrap(); + assert!(resolved.starts_with(Path::new("/tmp"))); + assert_eq!(resolved.file_name(), Some(OsStr::new(DEFAULT_CERTDATA_OUTPUT))); + let workspace = resolved.parent().unwrap().to_path_buf(); + assert!(workspace.exists()); + + drop(cleanup); + assert!(!workspace.exists()); + } + + #[test] + fn sync_certdata_output_dry_run_uses_tmp_path_without_creating_workspace() { + let (resolved, cleanup) = resolve_sync_certdata_output(None, true).unwrap(); + assert!(resolved.starts_with(Path::new("/tmp"))); + assert_eq!(resolved.file_name(), Some(OsStr::new(DEFAULT_CERTDATA_OUTPUT))); + assert!(cleanup.is_none()); + } + #[test] fn percent_encode_bytes_encodes_lower_hex() { assert_eq!(percent_encode_bytes(&[0x00, 0x2a, 0xff]), "%00%2a%ff");