commit f3815e2b12df45f6e54c7d4f603a6a490da4718f Author: Dustin C. Hatch Date: Thu Dec 29 19:01:37 2022 -0600 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..383338e --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +Cargo.lock -diff diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6de7c88 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,920 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cmake" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +dependencies = [ + "cc", +] + +[[package]] +name = "concurrent-queue" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf888f9575c290197b2c948dc9e9ff10bd1a39ad1ea8585f734585fa6b9d3f9" +dependencies = [ + "bitflags", + "futures-core", + "inotify-sys", + "libc", + "tokio", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "mozprofile" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd320dce3c165cf4757a56f9cff929b747e003e80fcb9210a474dbb6e44d21c1" +dependencies = [ + "tempfile", +] + +[[package]] +name = "mozrunner" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69c22f9d5ab44661b7b767de8fd77e47a5b8b1738a60528e36d13cccc7398e72" +dependencies = [ + "dirs", + "log", + "mozprofile", + "plist", + "winreg", +] + +[[package]] +name = "mqttmarionette" +version = "0.1.0" +dependencies = [ + "inotify", + "mozprofile", + "mozrunner", + "paho-mqtt", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "paho-mqtt" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fac58bae33ba9679bb4908ffa7c3950114345860d3f9b98340c4943f18ff324" +dependencies = [ + "async-channel", + "crossbeam-channel", + "futures", + "futures-timer", + "libc", + "log", + "paho-mqtt-sys", + "thiserror", +] + +[[package]] +name = "paho-mqtt-sys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f970989ae16d333229db266f3a8376f301666d2b2c53328e9ba9522be6d66a8d" +dependencies = [ + "cmake", + "openssl-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "plist" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +dependencies = [ + "base64", + "indexmap", + "line-wrap", + "serde", + "time", + "xml-rs", +] + +[[package]] +name = "proc-macro2" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "serde" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +dependencies = [ + "itoa", + "libc", + "num_threads", +] + +[[package]] +name = "tokio" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "winreg" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" +dependencies = [ + "winapi", +] + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..cb4ac25 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "mqttmarionette" +version = "0.1.0" +edition = "2021" + +[dependencies] +inotify = "0.10.0" +mozprofile = "0.9.0" +mozrunner = "0.15.0" +paho-mqtt = "0.11.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "1.23.0", features = ["io-util", "macros", "net", "rt", "signal", "sync", "time"] } +tokio-stream = "0.1.11" +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.16", features = ["env-filter", "fmt"] } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..a1ffd27 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +max_width = 79 diff --git a/src/browser.rs b/src/browser.rs new file mode 100644 index 0000000..b8f7d99 --- /dev/null +++ b/src/browser.rs @@ -0,0 +1,135 @@ +use std::path::{Path, PathBuf}; + +use inotify::{Inotify, WatchMask}; +use mozprofile::preferences::Pref; +use mozprofile::profile::Profile; +use mozrunner::runner::{ + FirefoxProcess, FirefoxRunner, Runner, RunnerError, RunnerProcess, +}; +use tokio_stream::StreamExt; +use tracing::{debug, error, trace, warn}; + +#[derive(Debug)] +pub enum BrowserError { + Io(std::io::Error), + Runner(RunnerError), +} + +impl std::fmt::Display for BrowserError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Io(e) => write!(f, "{}", e), + Self::Runner(e) => write!(f, "{}", e), + } + } +} + +impl std::error::Error for BrowserError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + Self::Runner(e) => Some(e), + } + } +} + +impl From for BrowserError { + fn from(e: std::io::Error) -> Self { + Self::Io(e) + } +} + +impl From for BrowserError { + fn from(e: RunnerError) -> Self { + Self::Runner(e) + } +} + +pub struct Browser { + profile: PathBuf, + process: FirefoxProcess, +} + +impl Browser { + pub fn launch() -> Result { + Self::launch_with_binary(None) + } + + pub fn launch_with_binary( + binary: Option<&Path>, + ) -> Result { + let mut profile = Profile::new(None)?; + match profile.user_prefs() { + Ok(prefs) => { + prefs.insert("marionette.port", Pref::new(0)); + } + Err(e) => { + error!("Could not get profile user prefs: {}", e); + } + } + let binary = match binary { + Some(b) => b, + None => Path::new("firefox"), + }; + let profile_path = profile.path.clone(); + let mut runner = FirefoxRunner::new(binary, Some(profile)); + runner.arg("--marionette"); + let process = runner.start()?; + Ok(Self { + profile: profile_path, + process, + }) + } + + pub fn marionette_port(&self) -> Option { + let mut path = self.profile.clone(); + path.push("MarionetteActivePort"); + let port = match std::fs::read_to_string(path) { + Ok(v) => match v.parse() { + Ok(v) => v, + Err(e) => { + warn!("Failed to parse Marionette port: {}", e); + return None; + } + }, + Err(e) => { + warn!("Could not read active Marionette port: {}", e); + return None; + } + }; + Some(port) + } + + pub async fn wait_ready(&self) -> Result<(), std::io::Error> { + if self.marionette_port().is_some() { + debug!("Marionette port has already been assigned"); + return Ok(()); + } + let mut inotify = Inotify::init()?; + debug!("Starting inotify watch for {}", self.profile.display()); + inotify.add_watch(self.profile.clone(), WatchMask::CREATE)?; + let mut buffer = [0; 1024]; + let mut stream = inotify.event_stream(&mut buffer)?; + while let Some(evt) = stream.next().await { + trace!("inotify event: {:?}", evt); + if let Some(name) = evt?.name { + if name == "MarionetteActivePort" { + debug!("Marionette port has been assigned"); + break; + } + } + } + Ok(()) + } +} + +impl Drop for Browser { + fn drop(&mut self) { + debug!("Stopping browser process"); + if let Err(e) = + self.process.wait(std::time::Duration::from_millis(500)) + { + error!("Error stopping browser process: {}", e); + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..634b78d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,27 @@ +mod browser; +mod marionette; +mod session; + +use tokio::signal::unix::{self, SignalKind}; +use tracing::info; +use tracing_subscriber::filter::EnvFilter; + +use session::Session; + +#[tokio::main(flavor = "current_thread")] +async fn main() { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .with_writer(std::io::stderr) + .init(); + + let mut sig_term = unix::signal(SignalKind::terminate()).unwrap(); + let mut sig_int = unix::signal(SignalKind::interrupt()).unwrap(); + + let session = Session::begin().await.unwrap(); + + tokio::select! { + _ = sig_term.recv() => info!("Received SIGTERM"), + _ = sig_int.recv() => info!("Received SIGINT"), + }; +} diff --git a/src/marionette/error.rs b/src/marionette/error.rs new file mode 100644 index 0000000..c089467 --- /dev/null +++ b/src/marionette/error.rs @@ -0,0 +1,58 @@ +use std::str::Utf8Error; +use std::num::ParseIntError; + +#[derive(Debug)] +pub enum MessageError { + Io(std::io::Error), + Parse(ParseIntError), + Utf8(Utf8Error), +} + +impl From for MessageError { + fn from(e: std::io::Error) -> Self { + Self::Io(e) + } +} + +impl From for MessageError { + fn from(e: ParseIntError) -> Self { + Self::Parse(e) + } +} + +impl From for MessageError { + fn from(e: Utf8Error) -> Self { + Self::Utf8(e) + } +} + +#[derive(Debug)] +pub enum HandshakeError { + Io(std::io::Error), + Parse(ParseIntError), + Utf8(Utf8Error), + Json(serde_json::Error), +} + +impl From for HandshakeError { + fn from(e: MessageError) -> Self { + match e { + MessageError::Io(e) => Self::Io(e), + MessageError::Parse(e) => Self::Parse(e), + MessageError::Utf8(e) => Self::Utf8(e), + } + } +} + +impl From for HandshakeError { + fn from(e: std::io::Error) -> Self { + Self::Io(e) + } +} + +impl From for HandshakeError { + fn from(e: serde_json::Error) -> Self { + Self::Json(e) + } +} + diff --git a/src/marionette/message.rs b/src/marionette/message.rs new file mode 100644 index 0000000..4fb3a98 --- /dev/null +++ b/src/marionette/message.rs @@ -0,0 +1,70 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Hello { + pub application_type: String, + pub marionette_protocol: u16, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct Timeouts { + implicit: u32, + page_load: u32, + script: u32, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct Capabilities { + pub accept_insecure_certs: bool, + pub browser_name: String, + pub browser_version: String, + #[serde(rename = "moz:accessibilityChecks")] + pub accessibility_checks: bool, + #[serde(rename = "moz:buildID")] + pub build_id: String, + #[serde(rename = "moz:headless")] + pub headless: bool, + #[serde(rename = "moz:processID")] + pub process_id: u128, + #[serde(rename = "moz:profile")] + pub profile: String, + #[serde(rename = "moz:shutdownTimeout")] + pub shutdown_timeout: u32, + #[serde(rename = "moz:useNonSpecCompliantPointerOrigin")] + pub use_non_spec_compliant_pointer_origin: bool, + #[serde(rename = "moz:webdriverClick")] + pub webdriver_click: bool, + pub page_load_strategy: String, + pub platform_name: String, + pub platform_version: String, + // proxy: + pub set_window_rect: bool, + pub timeouts: Timeouts, + pub unhandled_prompt_behavior: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct NewSessionResponse { + pub capabilities: Capabilities, + pub session_id: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NewSessionParams { + pub strict_file_interactability: bool, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "command", content = "params")] +pub enum Command { + #[serde(rename = "WebDriver:NewSession")] + NewSession(NewSessionParams), +} diff --git a/src/marionette/mod.rs b/src/marionette/mod.rs new file mode 100644 index 0000000..1ce8e04 --- /dev/null +++ b/src/marionette/mod.rs @@ -0,0 +1,78 @@ +pub mod error; +pub mod message; + +use std::time::Instant; + +use serde::{Deserialize, Serialize}; +use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufStream}; +use tokio::net::{TcpStream, ToSocketAddrs}; +use tracing::{debug, trace}; + +pub use error::{HandshakeError, MessageError}; +use message::{Command, Hello, NewSessionParams, NewSessionResponse}; + +#[derive(Debug, Deserialize, Serialize)] +struct Message(u8, u32, Option, serde_json::Value); + +pub struct Marionette { + ts: Instant, + stream: BufStream, +} + +impl Marionette { + pub async fn connect(addr: A) -> Result + where + A: ToSocketAddrs, + { + let conn = TcpStream::connect(addr).await?; + let stream = BufStream::new(conn); + let ts = Instant::now(); + Ok(Self { ts, stream }) + } + + pub async fn handshake(&mut self) -> Result<(), HandshakeError> { + let buf = self.next_message().await?; + let hello: Hello = serde_json::from_slice(&buf)?; + debug!("Received hello: {:?}", hello); + self.send_message(Command::NewSession(NewSessionParams { + strict_file_interactability: true, + })) + .await?; + let buf = self.next_message().await?; + let msg: Message = serde_json::from_slice(&buf)?; + let res: NewSessionResponse = serde_json::from_value(msg.3)?; + debug!("Received message: {:?}", res); + Ok(()) + } + + async fn next_message(&mut self) -> Result, MessageError> { + let mut buf = vec![]; + self.stream.read_until(b':', &mut buf).await?; + let length: usize = + std::str::from_utf8(&buf[..buf.len() - 1])?.parse()?; + trace!("Message length: {:?}", length); + let mut buf = vec![0; length]; + self.stream.read_exact(&mut buf[..]).await?; + trace!("Received message: {:?}", buf); + Ok(buf) + } + + async fn send_message( + &mut self, + command: Command, + ) -> Result<(), std::io::Error> { + let value = serde_json::to_value(command)?; + let (command, params) = ( + value.get("command").unwrap().as_str().unwrap().into(), + value.get("params").unwrap().clone(), + ); + let msgid = (self.ts.elapsed().as_millis() % u32::MAX as u128) as u32; + let message = Message(0, msgid, Some(command), params); + let message = serde_json::to_string(&message)?; + let message = format!("{}:{}", message.len(), message); + trace!("Sending message: {}", message); + self.stream.write_all(message.as_bytes()).await?; + self.stream.flush().await?; + Ok(()) + } +} diff --git a/src/session.rs b/src/session.rs new file mode 100644 index 0000000..1bada9f --- /dev/null +++ b/src/session.rs @@ -0,0 +1,57 @@ +use tracing::{debug, info}; + +use crate::browser::{Browser, BrowserError}; +use crate::marionette::Marionette; +use crate::marionette::error::HandshakeError; + +#[derive(Debug)] +pub enum SessionError { + Browser(BrowserError), + Io(std::io::Error), + Handshake(HandshakeError), + InvalidState(String), +} + +impl From for SessionError { + fn from(e: BrowserError) -> Self { + Self::Browser(e) + } +} + +impl From for SessionError { + fn from(e: std::io::Error) -> Self { + Self::Io(e) + } +} + +impl From for SessionError { + fn from(e: HandshakeError) -> Self { + Self::Handshake(e) + } +} + + +pub struct Session { + browser: Browser, + marionette: Marionette, +} + +impl Session { + pub async fn begin() -> Result { + debug!("Launching Firefox"); + let browser = Browser::launch()?; + browser.wait_ready().await?; + debug!("Firefox Marionette is now ready"); + let Some(port) = browser.marionette_port() else { + return Err(SessionError::InvalidState("No active Marionette port".into())); + }; + debug!("Connecting to Firefox Marionette on port {}", port); + let mut marionette = Marionette::connect(("127.0.0.1", port)).await?; + info!("Successfully connected to Firefox Marionette"); + marionette.handshake().await?; + Ok(Self { + browser, + marionette, + }) + } +}