diff --git a/tokio-xmpp/.gitignore b/tokio-xmpp/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..eb5a316cbd195d26e3f768c7dd8e1b47299e17f8 --- /dev/null +++ b/tokio-xmpp/.gitignore @@ -0,0 +1 @@ +target diff --git a/tokio-xmpp/.gitlab-ci.yml b/tokio-xmpp/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..ae087bde44b0e6eb8078985f13099bf76e212345 --- /dev/null +++ b/tokio-xmpp/.gitlab-ci.yml @@ -0,0 +1,14 @@ +stages: + - build +rust-latest: + stage: build + image: rust:latest + script: + - cargo build --verbose + - cargo test --verbose +rust-nightly: + stage: build + image: rustlang/rust:nightly + script: + - cargo build --verbose + - cargo test --verbose diff --git a/tokio-xmpp/Cargo.lock b/tokio-xmpp/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..de1c70ae4c095007dbad31a9e699e2d1b6c07919 --- /dev/null +++ b/tokio-xmpp/Cargo.lock @@ -0,0 +1,1731 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "c2-chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chrono" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crossbeam-deque" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "enum-as-inner" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hostname" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ipconfig" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "jid" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "linked-hash-map" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lock_api" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "markup5ever" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memoffset" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "minidom" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl" +version = "0.10.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pbkdf2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_codegen" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ppv-lite86" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quick-xml" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-xml" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "resolv-conf" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "sasl" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "schannel" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "security-framework" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha-1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha3" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "socket2" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache_codegen" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache_shared" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tendril" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-codec" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-sync" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tls" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-uds" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-xmpp" +version = "1.0.1" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-xml 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-resolver 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xml5ever 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xmpp-parsers 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trust-dns-proto" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "url" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf-8" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vcpkg" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "widestring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winreg" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winutil" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "xml5ever" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "markup5ever 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "xmpp-parsers" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" +"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" +"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" +"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" +"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d58266c97445680766be408285e798d3401c6d4c378ec5552e78737e681e37d" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" +"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" +"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" +"checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" +"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +"checksum ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum jid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "32b4cfc8edfd18c386be7b4e7307e59471aed5e21cd8b80e1aaf070b42de163d" +"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)" = "74dfca3d9957906e8d1e6a0b641dc9a59848e793f1da2165889fd4f62d10d79c" +"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +"checksum markup5ever 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "65381d9d47506b8592b97c4efd936afcf673b09b059f2bef39c7211ee78b9d03" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" +"checksum minidom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df1f29c97815c4328d6b533d2b8976e80cbed02d97063970aa4a80c100ecf29a" +"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" +"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)" = "ba24190c8f0805d3bd2ce028f439fe5af1d55882bbe6261bed1dbc93b50dd6b1" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +"checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" +"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" +"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quick-xml 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd45021132c1cb5540995e93fcc2cf5a874ef84f9639168fb6819caa023d4be" +"checksum quick-xml 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcdba8c8d71275493d966ef052a88726ac8590c15a09968b32158205c672ef" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +"checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +"checksum sasl 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e457758c85b736bbad56dc099406cd2a9c19554cf81880dba7a51d092929e600" +"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" +"checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" +"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" +"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" +"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" +"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" +"checksum string_cache 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "96ccb3a75a3caf2d7f2eb9ada86ec1fbbd4c74ad2bd8dc00a96a0c2f93509ef0" +"checksum string_cache_codegen 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6" +"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" +"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" +"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +"checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" +"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" +"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" +"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" +"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" +"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +"checksum tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c56391be9805bc80163151c0b9e5164ee64f4b0200962c346fea12773158f22d" +"checksum tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" +"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +"checksum tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" +"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" +"checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" +"checksum tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f02298505547f73e60f568359ef0d016d5acd6e830ab9bc7c4a5b3403440121b" +"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" +"checksum trust-dns-proto 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05457ece29839d056d8cb66ec080209d34492b3d2e7e00641b486977be973db9" +"checksum trust-dns-resolver 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb1b3a41ee784f8da051cd342c6f42a3a75ee45818164acad867eac8f2f85332" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" +"checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" +"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +"checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum xml5ever 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b93ca59bfbbc3c0f807a61faea54e6a4c753f82857d3730e9afb5523b6149c7" +"checksum xmpp-parsers 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57991f57c0011b66caeed8ed4380489b42c39c41364e31e4bbe011649cb79002" diff --git a/tokio-xmpp/Cargo.toml b/tokio-xmpp/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..7bee03f8fb0cb463381d1b677488da7aedc75860 --- /dev/null +++ b/tokio-xmpp/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "tokio-xmpp" +version = "1.0.1" +authors = ["Astro ", "Emmanuel Gil Peyrot ", "pep ", "O01eg "] +description = "Asynchronous XMPP for Rust with tokio" +license = "MPL-2.0" +homepage = "https://gitlab.com/xmpp-rs/tokio-xmpp" +repository = "https://gitlab.com/xmpp-rs/tokio-xmpp" +documentation = "https://docs.rs/tokio-xmpp" +categories = ["asynchronous", "network-programming"] +keywords = ["xmpp", "tokio"] +edition = "2018" + +[dependencies] +bytes = "0.4" +futures = "0.1" +idna = "0.2" +native-tls = "0.2" +sasl = "0.4" +tokio = "0.1" +tokio-codec = "0.1" +trust-dns-resolver = "0.12" +trust-dns-proto = "0.8" +tokio-io = "0.1" +tokio-tls = "0.2" +quick-xml = "0.17" +xml5ever = "0.15" +xmpp-parsers = "0.15" diff --git a/tokio-xmpp/README.md b/tokio-xmpp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..eccc836f75a474b8eba96906767dfb7cdc5af3e4 --- /dev/null +++ b/tokio-xmpp/README.md @@ -0,0 +1,6 @@ +# TODO + +- [ ] minidom ns +- [ ] replace debug output with log crate +- [ ] customize tls verify? +- [ ] more tests diff --git a/tokio-xmpp/examples/contact_addr.rs b/tokio-xmpp/examples/contact_addr.rs new file mode 100644 index 0000000000000000000000000000000000000000..f60601a8cdacad09d2b0a5577e89e8d6591b0b4b --- /dev/null +++ b/tokio-xmpp/examples/contact_addr.rs @@ -0,0 +1,130 @@ +use futures::{future, Sink, Stream}; +use std::convert::TryFrom; +use std::env::args; +use std::process::exit; +use tokio::runtime::current_thread::Runtime; +use tokio_xmpp::{Client, xmpp_codec::Packet}; +use xmpp_parsers::{ + Element, + Jid, + ns, + iq::{ + Iq, + IqType, + }, + disco::{ + DiscoInfoResult, + DiscoInfoQuery, + }, + server_info::ServerInfo, +}; + +fn main() { + let args: Vec = args().collect(); + if args.len() != 4 { + println!("Usage: {} ", args[0]); + exit(1); + } + let jid = &args[1]; + let password = &args[2]; + let target = &args[3]; + + // tokio_core context + let mut rt = Runtime::new().unwrap(); + // Client instance + let client = Client::new(jid, password).unwrap(); + + // Make the two interfaces for sending and receiving independent + // of each other so we can move one into a closure. + let (mut sink, stream) = client.split(); + // Wrap sink in Option so that we can take() it for the send(self) + // to consume and return it back when ready. + let mut send = move |packet| { + sink.start_send(packet).expect("start_send"); + }; + // Main loop, processes events + let mut wait_for_stream_end = false; + let done = stream.for_each(|event| { + if wait_for_stream_end { + /* Do Nothing. */ + } else if event.is_online() { + println!("Online!"); + + let target_jid: Jid = target.clone().parse().unwrap(); + let iq = make_disco_iq(target_jid); + println!("Sending disco#info request to {}", target.clone()); + println!(">> {}", String::from(&iq)); + send(Packet::Stanza(iq)); + } else if let Some(stanza) = event.into_stanza() { + if stanza.is("iq", "jabber:client") { + let iq = Iq::try_from(stanza).unwrap(); + if let IqType::Result(Some(payload)) = iq.payload { + if payload.is("query", ns::DISCO_INFO) { + if let Ok(disco_info) = DiscoInfoResult::try_from(payload) { + for ext in disco_info.extensions { + if let Ok(server_info) = ServerInfo::try_from(ext) { + print_server_info(server_info); + wait_for_stream_end = true; + send(Packet::StreamEnd); + } + } + } + } + } + } + } + + Box::new(future::ok(())) + }); + + // Start polling `done` + match rt.block_on(done) { + Ok(_) => (), + Err(e) => { + println!("Fatal: {}", e); + () + } + } +} + +fn make_disco_iq(target: Jid) -> Element { + Iq::from_get("disco", DiscoInfoQuery { node: None }) + .with_id(String::from("contact")) + .with_to(target) + .into() +} + +fn convert_field(field: Vec) -> String { + field.iter() + .fold((field.len(), String::new()), |(l, mut acc), s| { + acc.push('<'); + acc.push_str(&s); + acc.push('>'); + if l > 1 { + acc.push(','); + acc.push(' '); + } + (0, acc) + }).1 +} + +fn print_server_info(server_info: ServerInfo) { + if server_info.abuse.len() != 0 { + println!("abuse: {}", convert_field(server_info.abuse)); + } + if server_info.admin.len() != 0 { + println!("admin: {}", convert_field(server_info.admin)); + } + if server_info.feedback.len() != 0 { + println!("feedback: {}", convert_field(server_info.feedback)); + } + if server_info.sales.len() != 0 { + println!("sales: {}", convert_field(server_info.sales)); + } + if server_info.security.len() != 0 { + println!("security: {}", convert_field(server_info.security)); + } + if server_info.support.len() != 0 { + println!("support: {}", convert_field(server_info.support)); + } +} diff --git a/tokio-xmpp/examples/download_avatars.rs b/tokio-xmpp/examples/download_avatars.rs new file mode 100644 index 0000000000000000000000000000000000000000..ec27b6e5a9ad97285735a4409179aea22d50d2a6 --- /dev/null +++ b/tokio-xmpp/examples/download_avatars.rs @@ -0,0 +1,232 @@ +use futures::{future, Future, Sink, Stream}; +use std::convert::TryFrom; +use std::env::args; +use std::fs::{create_dir_all, File}; +use std::io::{self, Write}; +use std::process::exit; +use std::str::FromStr; +use tokio::runtime::current_thread::Runtime; +use tokio_xmpp::{Client, Packet}; +use xmpp_parsers::{ + avatar::{Data as AvatarData, Metadata as AvatarMetadata}, + caps::{compute_disco, hash_caps, Caps}, + disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}, + hashes::Algo, + iq::{Iq, IqType}, + message::Message, + ns, + presence::{Presence, Type as PresenceType}, + pubsub::{ + event::PubSubEvent, + pubsub::{Items, PubSub}, + NodeName, + }, + stanza_error::{StanzaError, ErrorType, DefinedCondition}, + Jid, +}; + +fn main() { + let args: Vec = args().collect(); + if args.len() != 3 { + println!("Usage: {} ", args[0]); + exit(1); + } + let jid = &args[1]; + let password = &args[2]; + + // tokio_core context + let mut rt = Runtime::new().unwrap(); + // Client instance + let client = Client::new(jid, password).unwrap(); + + // Make the two interfaces for sending and receiving independent + // of each other so we can move one into a closure. + let (sink, stream) = client.split(); + + // Create outgoing pipe + let (mut tx, rx) = futures::unsync::mpsc::unbounded(); + rt.spawn( + rx.forward( + sink.sink_map_err(|_| panic!("Pipe")) + ) + .map(|(rx, mut sink)| { + drop(rx); + let _ = sink.close(); + }) + .map_err(|e| { + panic!("Send error: {:?}", e); + }) + ); + + let disco_info = make_disco(); + + // Main loop, processes events + let mut wait_for_stream_end = false; + let done = stream.for_each(move |event| { + // Helper function to send an iq error. + let mut send_error = |to, id, type_, condition, text: &str| { + let error = StanzaError::new(type_, condition, "en", text); + let iq = Iq::from_error(id, error) + .with_to(to); + tx.start_send(Packet::Stanza(iq.into())).unwrap(); + }; + + if wait_for_stream_end { + /* Do nothing */ + } else if event.is_online() { + println!("Online!"); + + let caps = get_disco_caps(&disco_info, "https://gitlab.com/xmpp-rs/tokio-xmpp"); + let presence = make_presence(caps); + tx.start_send(Packet::Stanza(presence.into())).unwrap(); + } else if let Some(stanza) = event.into_stanza() { + if stanza.is("iq", "jabber:client") { + let iq = Iq::try_from(stanza).unwrap(); + if let IqType::Get(payload) = iq.payload { + if payload.is("query", ns::DISCO_INFO) { + let query = DiscoInfoQuery::try_from(payload); + match query { + Ok(query) => { + let mut disco = disco_info.clone(); + disco.node = query.node; + let iq = Iq::from_result(iq.id, Some(disco)) + .with_to(iq.from.unwrap()); + tx.start_send(Packet::Stanza(iq.into())).unwrap(); + }, + Err(err) => { + send_error(iq.from.unwrap(), iq.id, ErrorType::Modify, DefinedCondition::BadRequest, &format!("{}", err)); + }, + } + } else { + // We MUST answer unhandled get iqs with a service-unavailable error. + send_error(iq.from.unwrap(), iq.id, ErrorType::Cancel, DefinedCondition::ServiceUnavailable, "No handler defined for this kind of iq."); + } + } else if let IqType::Result(Some(payload)) = iq.payload { + if payload.is("pubsub", ns::PUBSUB) { + let pubsub = PubSub::try_from(payload).unwrap(); + let from = + iq.from.clone().unwrap_or(Jid::from_str(jid).unwrap()); + handle_iq_result(pubsub, &from); + } + } else if let IqType::Set(_) = iq.payload { + // We MUST answer unhandled set iqs with a service-unavailable error. + send_error(iq.from.unwrap(), iq.id, ErrorType::Cancel, DefinedCondition::ServiceUnavailable, "No handler defined for this kind of iq."); + } + } else if stanza.is("message", "jabber:client") { + let message = Message::try_from(stanza).unwrap(); + let from = message.from.clone().unwrap(); + if let Some(body) = message.get_best_body(vec!["en"]) { + if body.1 .0 == "die" { + println!("Secret die command triggered by {}", from); + wait_for_stream_end = true; + tx.start_send(Packet::StreamEnd).unwrap(); + } + } + for child in message.payloads { + if child.is("event", ns::PUBSUB_EVENT) { + let event = PubSubEvent::try_from(child).unwrap(); + if let PubSubEvent::PublishedItems { node, items } = event { + if node.0 == ns::AVATAR_METADATA { + for item in items.into_iter() { + let payload = item.payload.clone().unwrap(); + if payload.is("metadata", ns::AVATAR_METADATA) { + // TODO: do something with these metadata. + let _metadata = AvatarMetadata::try_from(payload).unwrap(); + println!( + "{} has published an avatar, downloading...", + from.clone() + ); + let iq = download_avatar(from.clone()); + tx.start_send(Packet::Stanza(iq.into())).unwrap(); + } + } + } + } + } + } + } else if stanza.is("presence", "jabber:client") { + // Nothing to do here. + } else { + panic!("Unknown stanza: {}", String::from(&stanza)); + } + } + + future::ok(()) + }); + + // Start polling `done` + match rt.block_on(done) { + Ok(_) => (), + Err(e) => { + println!("Fatal: {}", e); + () + } + } +} + +fn make_disco() -> DiscoInfoResult { + let identities = vec![Identity::new("client", "bot", "en", "tokio-xmpp")]; + let features = vec![ + Feature::new(ns::DISCO_INFO), + Feature::new(format!("{}+notify", ns::AVATAR_METADATA)), + ]; + DiscoInfoResult { + node: None, + identities, + features, + extensions: vec![], + } +} + +fn get_disco_caps(disco: &DiscoInfoResult, node: &str) -> Caps { + let caps_data = compute_disco(disco); + let hash = hash_caps(&caps_data, Algo::Sha_1).unwrap(); + Caps::new(node, hash) +} + +// Construct a +fn make_presence(caps: Caps) -> Presence { + let mut presence = Presence::new(PresenceType::None) + .with_priority(-1); + presence.set_status("en", "Downloading avatars."); + presence.add_payload(caps); + presence +} + +fn download_avatar(from: Jid) -> Iq { + Iq::from_get("coucou", PubSub::Items(Items { + max_items: None, + node: NodeName(String::from(ns::AVATAR_DATA)), + subid: None, + items: Vec::new(), + })) + .with_to(from) +} + +fn handle_iq_result(pubsub: PubSub, from: &Jid) { + if let PubSub::Items(items) = pubsub { + if items.node.0 == ns::AVATAR_DATA { + for item in items.items { + match (item.id.clone(), item.payload.clone()) { + (Some(id), Some(payload)) => { + let data = AvatarData::try_from(payload).unwrap(); + save_avatar(from, id.0, &data.data).unwrap(); + } + _ => {} + } + } + } + } +} + +fn save_avatar(from: &Jid, id: String, data: &[u8]) -> io::Result<()> { + let directory = format!("data/{}", from); + let filename = format!("data/{}/{}", from, id); + println!( + "Saving avatar from {} to {}.", + from, filename + ); + create_dir_all(directory)?; + let mut file = File::create(filename)?; + file.write_all(data) +} diff --git a/tokio-xmpp/examples/echo_bot.rs b/tokio-xmpp/examples/echo_bot.rs new file mode 100644 index 0000000000000000000000000000000000000000..a4144bf4f695475fc174bdc735867fd9a19a41c0 --- /dev/null +++ b/tokio-xmpp/examples/echo_bot.rs @@ -0,0 +1,106 @@ +use futures::{future, Future, Sink, Stream}; +use std::convert::TryFrom; +use std::env::args; +use std::process::exit; +use tokio::runtime::current_thread::Runtime; +use tokio_xmpp::{Client, Packet}; +use xmpp_parsers::{Jid, Element}; +use xmpp_parsers::message::{Body, Message, MessageType}; +use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; + +fn main() { + let args: Vec = args().collect(); + if args.len() != 3 { + println!("Usage: {} ", args[0]); + exit(1); + } + let jid = &args[1]; + let password = &args[2]; + + // tokio_core context + let mut rt = Runtime::new().unwrap(); + // Client instance + let client = Client::new(jid, password).unwrap(); + + // Make the two interfaces for sending and receiving independent + // of each other so we can move one into a closure. + let (sink, stream) = client.split(); + + // Create outgoing pipe + let (mut tx, rx) = futures::unsync::mpsc::unbounded(); + rt.spawn( + rx.forward( + sink.sink_map_err(|_| panic!("Pipe")) + ) + .map(|(rx, mut sink)| { + drop(rx); + let _ = sink.close(); + }) + .map_err(|e| { + panic!("Send error: {:?}", e); + }) + ); + + // Main loop, processes events + let mut wait_for_stream_end = false; + let done = stream.for_each(move |event| { + if wait_for_stream_end { + /* Do nothing */ + } else if event.is_online() { + let jid = event.get_jid() + .map(|jid| format!("{}", jid)) + .unwrap_or("unknown".to_owned()); + println!("Online at {}", jid); + + let presence = make_presence(); + tx.start_send(Packet::Stanza(presence)).unwrap(); + } else if let Some(message) = event + .into_stanza() + .and_then(|stanza| Message::try_from(stanza).ok()) + { + match (message.from, message.bodies.get("")) { + (Some(ref from), Some(ref body)) if body.0 == "die" => { + println!("Secret die command triggered by {}", from); + wait_for_stream_end = true; + tx.start_send(Packet::StreamEnd).unwrap(); + } + (Some(ref from), Some(ref body)) => { + if message.type_ != MessageType::Error { + // This is a message we'll echo + let reply = make_reply(from.clone(), &body.0); + tx.start_send(Packet::Stanza(reply)).unwrap(); + } + } + _ => {} + } + } + + future::ok(()) + }); + + // Start polling `done` + match rt.block_on(done) { + Ok(_) => (), + Err(e) => { + println!("Fatal: {}", e); + () + } + } +} + +// Construct a +fn make_presence() -> Element { + let mut presence = Presence::new(PresenceType::None); + presence.show = Some(PresenceShow::Chat); + presence + .statuses + .insert(String::from("en"), String::from("Echoing messages.")); + presence.into() +} + +// Construct a chat +fn make_reply(to: Jid, body: &str) -> Element { + let mut message = Message::new(Some(to)); + message.bodies.insert(String::new(), Body(body.to_owned())); + message.into() +} diff --git a/tokio-xmpp/examples/echo_component.rs b/tokio-xmpp/examples/echo_component.rs new file mode 100644 index 0000000000000000000000000000000000000000..6a8bc33c8fbd09ca93e0bac20516de5eda72975c --- /dev/null +++ b/tokio-xmpp/examples/echo_component.rs @@ -0,0 +1,99 @@ +use futures::{future, Sink, Stream}; +use std::convert::TryFrom; +use std::env::args; +use std::process::exit; +use std::str::FromStr; +use tokio::runtime::current_thread::Runtime; +use tokio_xmpp::Component; +use xmpp_parsers::{Jid, Element}; +use xmpp_parsers::message::{Body, Message, MessageType}; +use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; + +fn main() { + let args: Vec = args().collect(); + if args.len() < 3 || args.len() > 5 { + println!("Usage: {} [server] [port]", args[0]); + exit(1); + } + let jid = &args[1]; + let password = &args[2]; + let server = &args + .get(3) + .unwrap() + .parse() + .unwrap_or("127.0.0.1".to_owned()); + let port: u16 = args.get(4).unwrap().parse().unwrap_or(5347u16); + + // tokio_core context + let mut rt = Runtime::new().unwrap(); + // Component instance + println!("{} {} {} {}", jid, password, server, port); + let component = Component::new(jid, password, server, port).unwrap(); + + // Make the two interfaces for sending and receiving independent + // of each other so we can move one into a closure. + println!("Got it: {}", component.jid.clone()); + let (mut sink, stream) = component.split(); + // Wrap sink in Option so that we can take() it for the send(self) + // to consume and return it back when ready. + let mut send = move |stanza| { + sink.start_send(stanza).expect("start_send"); + }; + // Main loop, processes events + let done = stream.for_each(|event| { + if event.is_online() { + println!("Online!"); + + // TODO: replace these hardcoded JIDs + let presence = make_presence( + Jid::from_str("test@component.linkmauve.fr/coucou").unwrap(), + Jid::from_str("linkmauve@linkmauve.fr").unwrap(), + ); + send(presence); + } else if let Some(message) = event + .into_stanza() + .and_then(|stanza| Message::try_from(stanza).ok()) + { + // This is a message we'll echo + match (message.from, message.bodies.get("")) { + (Some(from), Some(body)) => { + if message.type_ != MessageType::Error { + let reply = make_reply(from, &body.0); + send(reply); + } + } + _ => (), + } + } + + Box::new(future::ok(())) + }); + + // Start polling `done` + match rt.block_on(done) { + Ok(_) => (), + Err(e) => { + println!("Fatal: {}", e); + () + } + } +} + +// Construct a +fn make_presence(from: Jid, to: Jid) -> Element { + let mut presence = Presence::new(PresenceType::None); + presence.from = Some(from); + presence.to = Some(to); + presence.show = Some(PresenceShow::Chat); + presence + .statuses + .insert(String::from("en"), String::from("Echoing messages.")); + presence.into() +} + +// Construct a chat +fn make_reply(to: Jid, body: &str) -> Element { + let mut message = Message::new(Some(to)); + message.bodies.insert(String::new(), Body(body.to_owned())); + message.into() +} diff --git a/tokio-xmpp/logo.svg b/tokio-xmpp/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..2e15a475a809525bbe1b0492a7e17ee840587287 --- /dev/null +++ b/tokio-xmpp/logo.svg @@ -0,0 +1,172 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tokio-xmpp/src/client/auth.rs b/tokio-xmpp/src/client/auth.rs new file mode 100644 index 0000000000000000000000000000000000000000..096a0bfbca3070dea65c31942c55fc5d8dce2e99 --- /dev/null +++ b/tokio-xmpp/src/client/auth.rs @@ -0,0 +1,116 @@ +use std::str::FromStr; +use std::collections::HashSet; +use std::convert::TryFrom; +use futures::{Future, Poll, Stream, future::{ok, err, IntoFuture}}; +use sasl::client::mechanisms::{Anonymous, Plain, Scram}; +use sasl::client::Mechanism; +use sasl::common::scram::{Sha1, Sha256}; +use sasl::common::Credentials; +use tokio_io::{AsyncRead, AsyncWrite}; +use xmpp_parsers::sasl::{Auth, Challenge, Failure, Mechanism as XMPPMechanism, Response, Success}; + +use crate::xmpp_codec::Packet; +use crate::xmpp_stream::XMPPStream; +use crate::{AuthError, Error, ProtocolError}; + +const NS_XMPP_SASL: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; + +pub struct ClientAuth { + future: Box, Error = Error>>, +} + +impl ClientAuth { + pub fn new(stream: XMPPStream, creds: Credentials) -> Result { + let local_mechs: Vec Box>> = vec![ + Box::new(|| Box::new(Scram::::from_credentials(creds.clone()).unwrap())), + Box::new(|| Box::new(Scram::::from_credentials(creds.clone()).unwrap())), + Box::new(|| Box::new(Plain::from_credentials(creds.clone()).unwrap())), + Box::new(|| Box::new(Anonymous::new())), + ]; + + let remote_mechs: HashSet = stream + .stream_features + .get_child("mechanisms", NS_XMPP_SASL) + .ok_or(AuthError::NoMechanism)? + .children() + .filter(|child| child.is("mechanism", NS_XMPP_SASL)) + .map(|mech_el| mech_el.text()) + .collect(); + + for local_mech in local_mechs { + let mut mechanism = local_mech(); + if remote_mechs.contains(mechanism.name()) { + let initial = mechanism.initial().map_err(AuthError::Sasl)?; + let mechanism_name = XMPPMechanism::from_str(mechanism.name()).map_err(ProtocolError::Parsers)?; + + let send_initial = Box::new(stream.send_stanza(Auth { + mechanism: mechanism_name, + data: initial, + })) + .map_err(Error::Io); + let future = Box::new(send_initial.and_then( + |stream| Self::handle_challenge(stream, mechanism) + ).and_then( + |stream| stream.restart() + )); + return Ok(ClientAuth { + future, + }); + } + } + + Err(AuthError::NoMechanism)? + } + + fn handle_challenge(stream: XMPPStream, mut mechanism: Box) -> Box, Error = Error>> { + Box::new( + stream.into_future() + .map_err(|(e, _stream)| e.into()) + .and_then(|(stanza, stream)| { + match stanza { + Some(Packet::Stanza(stanza)) => { + if let Ok(challenge) = Challenge::try_from(stanza.clone()) { + let response = mechanism + .response(&challenge.data); + Box::new( + response + .map_err(|e| AuthError::Sasl(e).into()) + .into_future() + .and_then(|response| { + // Send response and loop + stream.send_stanza(Response { data: response }) + .map_err(Error::Io) + .and_then(|stream| Self::handle_challenge(stream, mechanism)) + }) + ) + } else if let Ok(_) = Success::try_from(stanza.clone()) { + Box::new(ok(stream)) + } else if let Ok(failure) = Failure::try_from(stanza.clone()) { + Box::new(err(Error::Auth(AuthError::Fail(failure.defined_condition)))) + } else if stanza.name() == "failure" { + // Workaround for https://gitlab.com/xmpp-rs/xmpp-parsers/merge_requests/1 + Box::new(err(Error::Auth(AuthError::Sasl("failure".to_string())))) + } else { + // ignore and loop + Self::handle_challenge(stream, mechanism) + } + } + Some(_) => { + // ignore and loop + Self::handle_challenge(stream, mechanism) + } + None => Box::new(err(Error::Disconnected)) + } + }) + ) + } +} + +impl Future for ClientAuth { + type Item = XMPPStream; + type Error = Error; + + fn poll(&mut self) -> Poll { + self.future.poll() + } +} diff --git a/tokio-xmpp/src/client/bind.rs b/tokio-xmpp/src/client/bind.rs new file mode 100644 index 0000000000000000000000000000000000000000..f7d1828740dff5bfde15e22c01a4cba3ecfbdd6d --- /dev/null +++ b/tokio-xmpp/src/client/bind.rs @@ -0,0 +1,102 @@ +use futures::{sink, Async, Future, Poll, Stream}; +use std::convert::TryFrom; +use std::mem::replace; +use tokio_io::{AsyncRead, AsyncWrite}; +use xmpp_parsers::Jid; +use xmpp_parsers::bind::{BindQuery, BindResponse}; +use xmpp_parsers::iq::{Iq, IqType}; + +use crate::xmpp_codec::Packet; +use crate::xmpp_stream::XMPPStream; +use crate::{Error, ProtocolError}; + +const NS_XMPP_BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind"; +const BIND_REQ_ID: &str = "resource-bind"; + +pub enum ClientBind { + Unsupported(XMPPStream), + WaitSend(sink::Send>), + WaitRecv(XMPPStream), + Invalid, +} + +impl ClientBind { + /// Consumes and returns the stream to express that you cannot use + /// the stream for anything else until the resource binding + /// req/resp are done. + pub fn new(stream: XMPPStream) -> Self { + match stream.stream_features.get_child("bind", NS_XMPP_BIND) { + None => + // No resource binding available, + // return the (probably // usable) stream immediately + { + ClientBind::Unsupported(stream) + } + Some(_) => { + let resource; + if let Jid::Full(jid) = stream.jid.clone() { + resource = Some(jid.resource); + } else { + resource = None; + } + let iq = Iq::from_set(BIND_REQ_ID, BindQuery::new(resource)); + let send = stream.send_stanza(iq); + ClientBind::WaitSend(send) + } + } + } +} + +impl Future for ClientBind { + type Item = XMPPStream; + type Error = Error; + + fn poll(&mut self) -> Poll { + let state = replace(self, ClientBind::Invalid); + + match state { + ClientBind::Unsupported(stream) => Ok(Async::Ready(stream)), + ClientBind::WaitSend(mut send) => match send.poll() { + Ok(Async::Ready(stream)) => { + replace(self, ClientBind::WaitRecv(stream)); + self.poll() + } + Ok(Async::NotReady) => { + replace(self, ClientBind::WaitSend(send)); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + }, + ClientBind::WaitRecv(mut stream) => match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => match Iq::try_from(stanza) { + Ok(iq) => { + if iq.id == BIND_REQ_ID { + match iq.payload { + IqType::Result(payload) => { + payload + .and_then(|payload| BindResponse::try_from(payload).ok()) + .map(|bind| stream.jid = bind.into()); + Ok(Async::Ready(stream)) + } + _ => Err(ProtocolError::InvalidBindResponse)?, + } + } else { + Ok(Async::NotReady) + } + } + _ => Ok(Async::NotReady), + }, + Ok(Async::Ready(_)) => { + replace(self, ClientBind::WaitRecv(stream)); + self.poll() + } + Ok(Async::NotReady) => { + replace(self, ClientBind::WaitRecv(stream)); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + }, + ClientBind::Invalid => unreachable!(), + } + } +} diff --git a/tokio-xmpp/src/client/mod.rs b/tokio-xmpp/src/client/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..bcd07c954cac2f53b4571544fcefed19b6ce1c73 --- /dev/null +++ b/tokio-xmpp/src/client/mod.rs @@ -0,0 +1,236 @@ +use futures::{done, Async, AsyncSink, Future, Poll, Sink, StartSend, Stream}; +use idna; +use xmpp_parsers::{Jid, JidParseError}; +use sasl::common::{ChannelBinding, Credentials}; +use std::mem::replace; +use std::str::FromStr; +use tokio::net::TcpStream; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_tls::TlsStream; + +use super::event::Event; +use super::happy_eyeballs::Connecter; +use super::starttls::{StartTlsClient, NS_XMPP_TLS}; +use super::xmpp_codec::Packet; +use super::xmpp_stream; +use super::{Error, ProtocolError}; + +mod auth; +use self::auth::ClientAuth; +mod bind; +use self::bind::ClientBind; + +/// XMPP client connection and state +pub struct Client { + state: ClientState, +} + +type XMPPStream = xmpp_stream::XMPPStream>; +const NS_JABBER_CLIENT: &str = "jabber:client"; + +enum ClientState { + Invalid, + Disconnected, + Connecting(Box>), + Connected(XMPPStream), +} + +impl Client { + /// Start a new XMPP client + /// + /// Start polling the returned instance so that it will connect + /// and yield events. + pub fn new(jid: &str, password: &str) -> Result { + let jid = Jid::from_str(jid)?; + let client = Self::new_with_jid(jid, password); + Ok(client) + } + + /// Start a new client given that the JID is already parsed. + pub fn new_with_jid(jid: Jid, password: &str) -> Self { + let password = password.to_owned(); + let connect = Self::make_connect(jid, password.clone()); + let client = Client { + state: ClientState::Connecting(Box::new(connect)), + }; + client + } + + fn make_connect(jid: Jid, password: String) -> impl Future { + let username = jid.clone().node().unwrap(); + let jid1 = jid.clone(); + let jid2 = jid.clone(); + let password = password; + done(idna::domain_to_ascii(&jid.domain())) + .map_err(|_| Error::Idna) + .and_then(|domain| { + done(Connecter::from_lookup( + &domain, + Some("_xmpp-client._tcp"), + 5222, + )) + }) + .flatten() + .and_then(move |tcp_stream| { + xmpp_stream::XMPPStream::start(tcp_stream, jid1, NS_JABBER_CLIENT.to_owned()) + }) + .and_then(|xmpp_stream| { + if Self::can_starttls(&xmpp_stream) { + Ok(Self::starttls(xmpp_stream)) + } else { + Err(Error::Protocol(ProtocolError::NoTls)) + } + }) + .flatten() + .and_then(|tls_stream| XMPPStream::start(tls_stream, jid2, NS_JABBER_CLIENT.to_owned())) + .and_then( + move |xmpp_stream| done(Self::auth(xmpp_stream, username, password)), // TODO: flatten? + ) + .and_then(|auth| auth) + .and_then(|xmpp_stream| Self::bind(xmpp_stream)) + .and_then(|xmpp_stream| { + // println!("Bound to {}", xmpp_stream.jid); + Ok(xmpp_stream) + }) + } + + fn can_starttls(stream: &xmpp_stream::XMPPStream) -> bool { + stream + .stream_features + .get_child("starttls", NS_XMPP_TLS) + .is_some() + } + + fn starttls( + stream: xmpp_stream::XMPPStream, + ) -> StartTlsClient { + StartTlsClient::from_stream(stream) + } + + fn auth( + stream: xmpp_stream::XMPPStream, + username: String, + password: String, + ) -> Result, Error> { + let creds = Credentials::default() + .with_username(username) + .with_password(password) + .with_channel_binding(ChannelBinding::None); + ClientAuth::new(stream, creds) + } + + fn bind(stream: xmpp_stream::XMPPStream) -> ClientBind { + ClientBind::new(stream) + } + + /// Get the client's bound JID (the one reported by the XMPP + /// server). + pub fn bound_jid(&self) -> Option<&Jid> { + match self.state { + ClientState::Connected(ref stream) => Some(&stream.jid), + _ => None, + } + } +} + +impl Stream for Client { + type Item = Event; + type Error = Error; + + fn poll(&mut self) -> Poll, Self::Error> { + let state = replace(&mut self.state, ClientState::Invalid); + + match state { + ClientState::Invalid => Err(Error::InvalidState), + ClientState::Disconnected => Ok(Async::Ready(None)), + ClientState::Connecting(mut connect) => match connect.poll() { + Ok(Async::Ready(stream)) => { + let jid = stream.jid.clone(); + self.state = ClientState::Connected(stream); + Ok(Async::Ready(Some(Event::Online(jid)))) + } + Ok(Async::NotReady) => { + self.state = ClientState::Connecting(connect); + Ok(Async::NotReady) + } + Err(e) => Err(e), + }, + ClientState::Connected(mut stream) => { + // Poll sink + match stream.poll_complete() { + Ok(Async::NotReady) => (), + Ok(Async::Ready(())) => (), + Err(e) => return Err(e)?, + }; + + // Poll stream + match stream.poll() { + Ok(Async::Ready(None)) => { + // EOF + self.state = ClientState::Disconnected; + Ok(Async::Ready(Some(Event::Disconnected))) + } + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { + // Receive stanza + self.state = ClientState::Connected(stream); + Ok(Async::Ready(Some(Event::Stanza(stanza)))) + } + Ok(Async::Ready(Some(Packet::Text(_)))) => { + // Ignore text between stanzas + Ok(Async::NotReady) + } + Ok(Async::Ready(Some(Packet::StreamStart(_)))) => { + // + Err(ProtocolError::InvalidStreamStart.into()) + } + Ok(Async::Ready(Some(Packet::StreamEnd))) => { + // End of stream: + Ok(Async::Ready(None)) + } + Ok(Async::NotReady) => { + // Try again later + self.state = ClientState::Connected(stream); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + } + } + } + } +} + +impl Sink for Client { + type SinkItem = Packet; + type SinkError = Error; + + fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + match self.state { + ClientState::Connected(ref mut stream) => + Ok(stream.start_send(item)?), + _ => + Ok(AsyncSink::NotReady(item)), + } + } + + fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + match self.state { + ClientState::Connected(ref mut stream) => stream.poll_complete().map_err(|e| e.into()), + _ => Ok(Async::Ready(())), + } + } + + /// This closes the inner TCP stream. + /// + /// To synchronize your shutdown with the server side, you should + /// first send `Packet::StreamEnd` and wait for the end of the + /// incoming stream before closing the connection. + fn close(&mut self) -> Poll<(), Self::SinkError> { + match self.state { + ClientState::Connected(ref mut stream) => + stream.close() + .map_err(|e| e.into()), + _ => + Ok(Async::Ready(())), + } + } +} diff --git a/tokio-xmpp/src/component/auth.rs b/tokio-xmpp/src/component/auth.rs new file mode 100644 index 0000000000000000000000000000000000000000..44104f4c19052fe1db703563fab894cf32c2d7ca --- /dev/null +++ b/tokio-xmpp/src/component/auth.rs @@ -0,0 +1,89 @@ +use futures::{sink, Async, Future, Poll, Stream}; +use std::mem::replace; +use tokio_io::{AsyncRead, AsyncWrite}; +use xmpp_parsers::component::Handshake; + +use crate::xmpp_codec::Packet; +use crate::xmpp_stream::XMPPStream; +use crate::{AuthError, Error}; + +const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; + +pub struct ComponentAuth { + state: ComponentAuthState, +} + +enum ComponentAuthState { + WaitSend(sink::Send>), + WaitRecv(XMPPStream), + Invalid, +} + +impl ComponentAuth { + // TODO: doesn't have to be a Result<> actually + pub fn new(stream: XMPPStream, password: String) -> Result { + // FIXME: huge hack, shouldn’t be an element! + let sid = stream.stream_features.name().to_owned(); + let mut this = ComponentAuth { + state: ComponentAuthState::Invalid, + }; + this.send( + stream, + Handshake::from_password_and_stream_id(&password, &sid), + ); + Ok(this) + } + + fn send(&mut self, stream: XMPPStream, handshake: Handshake) { + let nonza = handshake; + let send = stream.send_stanza(nonza); + + self.state = ComponentAuthState::WaitSend(send); + } +} + +impl Future for ComponentAuth { + type Item = XMPPStream; + type Error = Error; + + fn poll(&mut self) -> Poll { + let state = replace(&mut self.state, ComponentAuthState::Invalid); + + match state { + ComponentAuthState::WaitSend(mut send) => match send.poll() { + Ok(Async::Ready(stream)) => { + self.state = ComponentAuthState::WaitRecv(stream); + self.poll() + } + Ok(Async::NotReady) => { + self.state = ComponentAuthState::WaitSend(send); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + }, + ComponentAuthState::WaitRecv(mut stream) => match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.is("handshake", NS_JABBER_COMPONENT_ACCEPT) => + { + self.state = ComponentAuthState::Invalid; + Ok(Async::Ready(stream)) + } + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.is("error", "http://etherx.jabber.org/streams") => + { + Err(AuthError::ComponentFail.into()) + } + Ok(Async::Ready(_event)) => { + // println!("ComponentAuth ignore {:?}", _event); + Ok(Async::NotReady) + } + Ok(_) => { + self.state = ComponentAuthState::WaitRecv(stream); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + }, + ComponentAuthState::Invalid => unreachable!(), + } + } +} diff --git a/tokio-xmpp/src/component/mod.rs b/tokio-xmpp/src/component/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..d822e8839ae24993521f430191a62ac6fcd876d2 --- /dev/null +++ b/tokio-xmpp/src/component/mod.rs @@ -0,0 +1,163 @@ +//! Components in XMPP are services/gateways that are logged into an +//! XMPP server under a JID consisting of just a domain name. They are +//! allowed to use any user and resource identifiers in their stanzas. +use futures::{done, Async, AsyncSink, Future, Poll, Sink, StartSend, Stream}; +use xmpp_parsers::{Jid, JidParseError, Element}; +use std::mem::replace; +use std::str::FromStr; +use tokio::net::TcpStream; +use tokio_io::{AsyncRead, AsyncWrite}; + +use super::event::Event; +use super::happy_eyeballs::Connecter; +use super::xmpp_codec::Packet; +use super::xmpp_stream; +use super::Error; + +mod auth; +use self::auth::ComponentAuth; + +/// Component connection to an XMPP server +pub struct Component { + /// The component's Jabber-Id + pub jid: Jid, + state: ComponentState, +} + +type XMPPStream = xmpp_stream::XMPPStream; +const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; + +enum ComponentState { + Invalid, + Disconnected, + Connecting(Box>), + Connected(XMPPStream), +} + +impl Component { + /// Start a new XMPP component + /// + /// Start polling the returned instance so that it will connect + /// and yield events. + pub fn new(jid: &str, password: &str, server: &str, port: u16) -> Result { + let jid = Jid::from_str(jid)?; + let password = password.to_owned(); + let connect = Self::make_connect(jid.clone(), password, server, port); + Ok(Component { + jid, + state: ComponentState::Connecting(Box::new(connect)), + }) + } + + fn make_connect( + jid: Jid, + password: String, + server: &str, + port: u16, + ) -> impl Future { + let jid1 = jid.clone(); + let password = password; + done(Connecter::from_lookup(server, None, port)) + .flatten() + .and_then(move |tcp_stream| { + xmpp_stream::XMPPStream::start( + tcp_stream, + jid1, + NS_JABBER_COMPONENT_ACCEPT.to_owned(), + ) + }) + .and_then(move |xmpp_stream| Self::auth(xmpp_stream, password).expect("auth")) + } + + fn auth( + stream: xmpp_stream::XMPPStream, + password: String, + ) -> Result, Error> { + ComponentAuth::new(stream, password) + } +} + +impl Stream for Component { + type Item = Event; + type Error = Error; + + fn poll(&mut self) -> Poll, Self::Error> { + let state = replace(&mut self.state, ComponentState::Invalid); + + match state { + ComponentState::Invalid => Err(Error::InvalidState), + ComponentState::Disconnected => Ok(Async::Ready(None)), + ComponentState::Connecting(mut connect) => match connect.poll() { + Ok(Async::Ready(stream)) => { + self.state = ComponentState::Connected(stream); + Ok(Async::Ready(Some(Event::Online(self.jid.clone())))) + } + Ok(Async::NotReady) => { + self.state = ComponentState::Connecting(connect); + Ok(Async::NotReady) + } + Err(e) => Err(e), + }, + ComponentState::Connected(mut stream) => { + // Poll sink + match stream.poll_complete() { + Ok(Async::NotReady) => (), + Ok(Async::Ready(())) => (), + Err(e) => return Err(e)?, + }; + + // Poll stream + match stream.poll() { + Ok(Async::NotReady) => { + self.state = ComponentState::Connected(stream); + Ok(Async::NotReady) + } + Ok(Async::Ready(None)) => { + // EOF + self.state = ComponentState::Disconnected; + Ok(Async::Ready(Some(Event::Disconnected))) + } + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { + self.state = ComponentState::Connected(stream); + Ok(Async::Ready(Some(Event::Stanza(stanza)))) + } + Ok(Async::Ready(_)) => { + self.state = ComponentState::Connected(stream); + Ok(Async::NotReady) + } + Err(e) => Err(e)?, + } + } + } + } +} + +impl Sink for Component { + type SinkItem = Element; + type SinkError = Error; + + fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + match self.state { + ComponentState::Connected(ref mut stream) => match stream + .start_send(Packet::Stanza(item)) + { + Ok(AsyncSink::NotReady(Packet::Stanza(stanza))) => Ok(AsyncSink::NotReady(stanza)), + Ok(AsyncSink::NotReady(_)) => { + panic!("Component.start_send with stanza but got something else back") + } + Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), + Err(e) => Err(e)?, + }, + _ => Ok(AsyncSink::NotReady(item)), + } + } + + fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + match &mut self.state { + &mut ComponentState::Connected(ref mut stream) => { + stream.poll_complete().map_err(|e| e.into()) + } + _ => Ok(Async::Ready(())), + } + } +} diff --git a/tokio-xmpp/src/error.rs b/tokio-xmpp/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..68a1412e9f4d2b87c91c392514e2ec5f7b1217c6 --- /dev/null +++ b/tokio-xmpp/src/error.rs @@ -0,0 +1,224 @@ +use native_tls::Error as TlsError; +use std::borrow::Cow; +use std::error::Error as StdError; +use std::fmt; +use std::io::Error as IoError; +use std::str::Utf8Error; +use trust_dns_proto::error::ProtoError; +use trust_dns_resolver::error::ResolveError; + +use xmpp_parsers::Error as ParsersError; +use xmpp_parsers::sasl::DefinedCondition as SaslDefinedCondition; + +/// Top-level error type +#[derive(Debug)] +pub enum Error { + /// I/O error + Io(IoError), + /// Error resolving DNS and establishing a connection + Connection(ConnecterError), + /// DNS label conversion error, no details available from module + /// `idna` + Idna, + /// Protocol-level error + Protocol(ProtocolError), + /// Authentication error + Auth(AuthError), + /// TLS error + Tls(TlsError), + /// Connection closed + Disconnected, + /// Shoud never happen + InvalidState, +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Io(e) => write!(fmt, "IO error: {}", e), + Error::Connection(e) => write!(fmt, "connection error: {}", e), + Error::Idna => write!(fmt, "IDNA error"), + Error::Protocol(e) => write!(fmt, "protocol error: {}", e), + Error::Auth(e) => write!(fmt, "authentication error: {}", e), + Error::Tls(e) => write!(fmt, "TLS error: {}", e), + Error::Disconnected => write!(fmt, "disconnected"), + Error::InvalidState => write!(fmt, "invalid state"), + } + } +} + +impl From for Error { + fn from(e: IoError) -> Self { + Error::Io(e) + } +} + +impl From for Error { + fn from(e: ConnecterError) -> Self { + Error::Connection(e) + } +} + +impl From for Error { + fn from(e: ProtocolError) -> Self { + Error::Protocol(e) + } +} + +impl From for Error { + fn from(e: AuthError) -> Self { + Error::Auth(e) + } +} + +impl From for Error { + fn from(e: TlsError) -> Self { + Error::Tls(e) + } +} + +/// Causes for stream parsing errors +#[derive(Debug)] +pub enum ParserError { + /// Encoding error + Utf8(Utf8Error), + /// XML parse error + Parse(ParseError), + /// Illegal `` + ShortTag, + /// Required by `impl Decoder` + Io(IoError), +} + +impl fmt::Display for ParserError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + ParserError::Utf8(e) => write!(fmt, "UTF-8 error: {}", e), + ParserError::Parse(e) => write!(fmt, "parse error: {}", e), + ParserError::ShortTag => write!(fmt, "short tag"), + ParserError::Io(e) => write!(fmt, "IO error: {}", e), + } + } +} + +impl From for ParserError { + fn from(e: IoError) -> Self { + ParserError::Io(e) + } +} + +impl From for Error { + fn from(e: ParserError) -> Self { + ProtocolError::Parser(e).into() + } +} + +/// XML parse error wrapper type +#[derive(Debug)] +pub struct ParseError(pub Cow<'static, str>); + +impl StdError for ParseError { + fn description(&self) -> &str { + self.0.as_ref() + } + fn cause(&self) -> Option<&dyn StdError> { + None + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// XMPP protocol-level error +#[derive(Debug)] +pub enum ProtocolError { + /// XML parser error + Parser(ParserError), + /// Error with expected stanza schema + Parsers(ParsersError), + /// No TLS available + NoTls, + /// Invalid response to resource binding + InvalidBindResponse, + /// No xmlns attribute in + NoStreamNamespace, + /// No id attribute in + NoStreamId, + /// Encountered an unexpected XML token + InvalidToken, + /// Unexpected (shouldn't occur) + InvalidStreamStart, +} + +impl fmt::Display for ProtocolError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + ProtocolError::Parser(e) => write!(fmt, "XML parser error: {}", e), + ProtocolError::Parsers(e) => write!(fmt, "error with expected stanza schema: {}", e), + ProtocolError::NoTls => write!(fmt, "no TLS available"), + ProtocolError::InvalidBindResponse => write!(fmt, "invalid response to resource binding"), + ProtocolError::NoStreamNamespace => write!(fmt, "no xmlns attribute in "), + ProtocolError::NoStreamId => write!(fmt, "no id attribute in "), + ProtocolError::InvalidToken => write!(fmt, "encountered an unexpected XML token"), + ProtocolError::InvalidStreamStart => write!(fmt, "unexpected "), + } + } +} + +impl From for ProtocolError { + fn from(e: ParserError) -> Self { + ProtocolError::Parser(e) + } +} + +impl From for ProtocolError { + fn from(e: ParsersError) -> Self { + ProtocolError::Parsers(e) + } +} + +/// Authentication error +#[derive(Debug)] +pub enum AuthError { + /// No matching SASL mechanism available + NoMechanism, + /// Local SASL implementation error + Sasl(String), + /// Failure from server + Fail(SaslDefinedCondition), + /// Component authentication failure + ComponentFail, +} + +impl fmt::Display for AuthError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + AuthError::NoMechanism => write!(fmt, "no matching SASL mechanism available"), + AuthError::Sasl(s) => write!(fmt, "local SASL implementation error: {}", s), + AuthError::Fail(c) => write!(fmt, "failure from the server: {:?}", c), + AuthError::ComponentFail => write!(fmt, "component authentication failure"), + } + } +} + +/// Error establishing connection +#[derive(Debug)] +pub enum ConnecterError { + /// All attempts failed, no error available + AllFailed, + /// DNS protocol error + Dns(ProtoError), + /// DNS resolution error + Resolve(ResolveError), +} + +impl std::error::Error for ConnecterError {} + +impl std::fmt::Display for ConnecterError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(fmt, "{:?}", self) + } +} diff --git a/tokio-xmpp/src/event.rs b/tokio-xmpp/src/event.rs new file mode 100644 index 0000000000000000000000000000000000000000..bd3dc402a2c2ad2f6b9eee29414e034db14406a2 --- /dev/null +++ b/tokio-xmpp/src/event.rs @@ -0,0 +1,54 @@ +use xmpp_parsers::{Element, Jid}; + +/// High-level event on the Stream implemented by Client and Component +#[derive(Debug)] +pub enum Event { + /// Stream is connected and initialized + Online(Jid), + /// Stream end + Disconnected, + /// Received stanza/nonza + Stanza(Element), +} + +impl Event { + /// `Online` event? + pub fn is_online(&self) -> bool { + match *self { + Event::Online(_) => true, + _ => false, + } + } + + /// Get the server-assigned JID for the `Online` event + pub fn get_jid(&self) -> Option<&Jid> { + match *self { + Event::Online(ref jid) => Some(jid), + _ => None, + } + } + + /// `Stanza` event? + pub fn is_stanza(&self, name: &str) -> bool { + match *self { + Event::Stanza(ref stanza) => stanza.name() == name, + _ => false, + } + } + + /// If this is a `Stanza` event, get its data + pub fn as_stanza(&self) -> Option<&Element> { + match *self { + Event::Stanza(ref stanza) => Some(stanza), + _ => None, + } + } + + /// If this is a `Stanza` event, unwrap into its data + pub fn into_stanza(self) -> Option { + match self { + Event::Stanza(stanza) => Some(stanza), + _ => None, + } + } +} diff --git a/tokio-xmpp/src/happy_eyeballs.rs b/tokio-xmpp/src/happy_eyeballs.rs new file mode 100644 index 0000000000000000000000000000000000000000..7696f65e2cfa42f8903259b341719f381cc3b9b3 --- /dev/null +++ b/tokio-xmpp/src/happy_eyeballs.rs @@ -0,0 +1,196 @@ +use crate::{ConnecterError, Error}; +use futures::{Async, Future, Poll}; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::collections::VecDeque; +use std::io::Error as IoError; +use std::mem; +use std::net::SocketAddr; +use tokio::net::tcp::ConnectFuture; +use tokio::net::TcpStream; +use trust_dns_resolver::{AsyncResolver, Name, IntoName, Background, BackgroundLookup}; +use trust_dns_resolver::config::LookupIpStrategy; +use trust_dns_resolver::lookup::SrvLookupFuture; +use trust_dns_resolver::lookup_ip::LookupIpFuture; + + +enum State { + ResolveSrv(AsyncResolver, BackgroundLookup), + ResolveTarget(AsyncResolver, Background, u16), + Connecting(Option, Vec>), + Invalid, +} + +pub struct Connecter { + fallback_port: u16, + srv_domain: Option, + domain: Name, + state: State, + targets: VecDeque<(Name, u16)>, + error: Option, +} + +fn resolver() -> Result { + let (config, mut opts) = trust_dns_resolver::system_conf::read_system_conf()?; + opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6; + let (resolver, resolver_background) = AsyncResolver::new(config, opts); + tokio::runtime::current_thread::spawn(resolver_background); + Ok(resolver) +} + +impl Connecter { + pub fn from_lookup( + domain: &str, + srv: Option<&str>, + fallback_port: u16, + ) -> Result { + if let Ok(ip) = domain.parse() { + // use specified IP address, not domain name, skip the whole dns part + let connect = RefCell::new(TcpStream::connect(&SocketAddr::new(ip, fallback_port))); + return Ok(Connecter { + fallback_port, + srv_domain: None, + domain: "nohost".into_name().map_err(ConnecterError::Dns)?, + state: State::Connecting(None, vec![connect]), + targets: VecDeque::new(), + error: None, + }); + } + + let srv_domain = match srv { + Some(srv) => Some( + format!("{}.{}.", srv, domain) + .into_name() + .map_err(ConnecterError::Dns)?, + ), + None => None, + }; + + let mut self_ = Connecter { + fallback_port, + srv_domain, + domain: domain.into_name().map_err(ConnecterError::Dns)?, + state: State::Invalid, + targets: VecDeque::new(), + error: None, + }; + + let resolver = resolver()?; + // Initialize state + match &self_.srv_domain { + &Some(ref srv_domain) => { + let srv_lookup = resolver.lookup_srv(srv_domain.clone()); + self_.state = State::ResolveSrv(resolver, srv_lookup); + } + None => { + self_.targets = [(self_.domain.clone(), self_.fallback_port)] + .into_iter() + .cloned() + .collect(); + self_.state = State::Connecting(Some(resolver), vec![]); + } + } + + Ok(self_) + } +} + +impl Future for Connecter { + type Item = TcpStream; + type Error = Error; + + fn poll(&mut self) -> Poll { + let state = mem::replace(&mut self.state, State::Invalid); + match state { + State::ResolveSrv(resolver, mut srv_lookup) => { + match srv_lookup.poll() { + Ok(Async::NotReady) => { + self.state = State::ResolveSrv(resolver, srv_lookup); + Ok(Async::NotReady) + } + Ok(Async::Ready(srv_result)) => { + let srv_map: BTreeMap<_, _> = srv_result + .iter() + .map(|srv| (srv.priority(), (srv.target().clone(), srv.port()))) + .collect(); + let targets = srv_map.into_iter().map(|(_, tp)| tp).collect(); + self.targets = targets; + self.state = State::Connecting(Some(resolver), vec![]); + self.poll() + } + Err(_) => { + // ignore, fallback + self.targets = [(self.domain.clone(), self.fallback_port)] + .into_iter() + .cloned() + .collect(); + self.state = State::Connecting(Some(resolver), vec![]); + self.poll() + } + } + } + State::Connecting(resolver, mut connects) => { + if resolver.is_some() && connects.len() == 0 && self.targets.len() > 0 { + let resolver = resolver.unwrap(); + let (host, port) = self.targets.pop_front().unwrap(); + let ip_lookup = resolver.lookup_ip(host); + self.state = State::ResolveTarget(resolver, ip_lookup, port); + self.poll() + } else if connects.len() > 0 { + let mut success = None; + connects.retain(|connect| match connect.borrow_mut().poll() { + Ok(Async::NotReady) => true, + Ok(Async::Ready(connection)) => { + success = Some(connection); + false + } + Err(e) => { + if self.error.is_none() { + self.error = Some(e.into()); + } + false + } + }); + match success { + Some(connection) => Ok(Async::Ready(connection)), + None => { + self.state = State::Connecting(resolver, connects); + Ok(Async::NotReady) + } + } + } else { + // All targets tried + match self.error.take() { + None => Err(ConnecterError::AllFailed.into()), + Some(e) => Err(e), + } + } + } + State::ResolveTarget(resolver, mut ip_lookup, port) => { + match ip_lookup.poll() { + Ok(Async::NotReady) => { + self.state = State::ResolveTarget(resolver, ip_lookup, port); + Ok(Async::NotReady) + } + Ok(Async::Ready(ip_result)) => { + let connects = ip_result + .iter() + .map(|ip| RefCell::new(TcpStream::connect(&SocketAddr::new(ip, port)))) + .collect(); + self.state = State::Connecting(Some(resolver), connects); + self.poll() + } + Err(e) => { + if self.error.is_none() { + self.error = Some(ConnecterError::Resolve(e).into()); + } + // ignore, next… + self.state = State::Connecting(Some(resolver), vec![]); + self.poll() + } + } + } + _ => panic!(""), + } + } +} diff --git a/tokio-xmpp/src/lib.rs b/tokio-xmpp/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..2bd823eef822a644becfa69b81c3d3b45782317f --- /dev/null +++ b/tokio-xmpp/src/lib.rs @@ -0,0 +1,19 @@ +#![deny(unsafe_code, unused, missing_docs, bare_trait_objects)] + +//! XMPP implementation with asynchronous I/O using Tokio. + +mod starttls; +mod stream_start; +pub mod xmpp_codec; +pub use crate::xmpp_codec::Packet; +pub mod xmpp_stream; +pub use crate::starttls::StartTlsClient; +mod event; +mod happy_eyeballs; +pub use crate::event::Event; +mod client; +pub use crate::client::Client; +mod component; +pub use crate::component::Component; +mod error; +pub use crate::error::{AuthError, ConnecterError, Error, ParseError, ParserError, ProtocolError}; diff --git a/tokio-xmpp/src/starttls.rs b/tokio-xmpp/src/starttls.rs new file mode 100644 index 0000000000000000000000000000000000000000..e23402792b96ac6f858c8ead10df9a154414d966 --- /dev/null +++ b/tokio-xmpp/src/starttls.rs @@ -0,0 +1,114 @@ +use futures::sink; +use futures::stream::Stream; +use futures::{Async, Future, Poll, Sink}; +use xmpp_parsers::{Jid, Element}; +use native_tls::TlsConnector as NativeTlsConnector; +use std::mem::replace; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_tls::{Connect, TlsConnector, TlsStream}; + +use crate::xmpp_codec::Packet; +use crate::xmpp_stream::XMPPStream; +use crate::Error; + +/// XMPP TLS XML namespace +pub const NS_XMPP_TLS: &str = "urn:ietf:params:xml:ns:xmpp-tls"; + +/// XMPP stream that switches to TLS if available in received features +pub struct StartTlsClient { + state: StartTlsClientState, + jid: Jid, +} + +enum StartTlsClientState { + Invalid, + SendStartTls(sink::Send>), + AwaitProceed(XMPPStream), + StartingTls(Connect), +} + +impl StartTlsClient { + /// Waits for + pub fn from_stream(xmpp_stream: XMPPStream) -> Self { + let jid = xmpp_stream.jid.clone(); + + let nonza = Element::builder("starttls").ns(NS_XMPP_TLS).build(); + let packet = Packet::Stanza(nonza); + let send = xmpp_stream.send(packet); + + StartTlsClient { + state: StartTlsClientState::SendStartTls(send), + jid, + } + } +} + +impl Future for StartTlsClient { + type Item = TlsStream; + type Error = Error; + + fn poll(&mut self) -> Poll { + let old_state = replace(&mut self.state, StartTlsClientState::Invalid); + let mut retry = false; + + let (new_state, result) = match old_state { + StartTlsClientState::SendStartTls(mut send) => match send.poll() { + Ok(Async::Ready(xmpp_stream)) => { + let new_state = StartTlsClientState::AwaitProceed(xmpp_stream); + retry = true; + (new_state, Ok(Async::NotReady)) + } + Ok(Async::NotReady) => { + (StartTlsClientState::SendStartTls(send), Ok(Async::NotReady)) + } + Err(e) => (StartTlsClientState::SendStartTls(send), Err(e.into())), + }, + StartTlsClientState::AwaitProceed(mut xmpp_stream) => match xmpp_stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) + if stanza.name() == "proceed" => + { + let stream = xmpp_stream.stream.into_inner(); + let connect = + TlsConnector::from(NativeTlsConnector::builder().build().unwrap()) + .connect(&self.jid.clone().domain(), stream); + let new_state = StartTlsClientState::StartingTls(connect); + retry = true; + (new_state, Ok(Async::NotReady)) + } + Ok(Async::Ready(_value)) => { + // println!("StartTlsClient ignore {:?}", _value); + ( + StartTlsClientState::AwaitProceed(xmpp_stream), + Ok(Async::NotReady), + ) + } + Ok(_) => ( + StartTlsClientState::AwaitProceed(xmpp_stream), + Ok(Async::NotReady), + ), + Err(e) => ( + StartTlsClientState::AwaitProceed(xmpp_stream), + Err(Error::Protocol(e.into())), + ), + }, + StartTlsClientState::StartingTls(mut connect) => match connect.poll() { + Ok(Async::Ready(tls_stream)) => { + (StartTlsClientState::Invalid, Ok(Async::Ready(tls_stream))) + } + Ok(Async::NotReady) => ( + StartTlsClientState::StartingTls(connect), + Ok(Async::NotReady), + ), + Err(e) => (StartTlsClientState::Invalid, Err(e.into())), + }, + StartTlsClientState::Invalid => unreachable!(), + }; + + self.state = new_state; + if retry { + self.poll() + } else { + result + } + } +} diff --git a/tokio-xmpp/src/stream_start.rs b/tokio-xmpp/src/stream_start.rs new file mode 100644 index 0000000000000000000000000000000000000000..f82c3901e0aaf8f4d58adf2fd75af38621800b95 --- /dev/null +++ b/tokio-xmpp/src/stream_start.rs @@ -0,0 +1,125 @@ +use futures::{sink, Async, Future, Poll, Sink, Stream}; +use xmpp_parsers::{Jid, Element}; +use std::mem::replace; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; + +use crate::xmpp_codec::{Packet, XMPPCodec}; +use crate::xmpp_stream::XMPPStream; +use crate::{Error, ProtocolError}; + +const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; + +pub struct StreamStart { + state: StreamStartState, + jid: Jid, + ns: String, +} + +enum StreamStartState { + SendStart(sink::Send>), + RecvStart(Framed), + RecvFeatures(Framed, String), + Invalid, +} + +impl StreamStart { + pub fn from_stream(stream: Framed, jid: Jid, ns: String) -> Self { + let attrs = [ + ("to".to_owned(), jid.clone().domain()), + ("version".to_owned(), "1.0".to_owned()), + ("xmlns".to_owned(), ns.clone()), + ("xmlns:stream".to_owned(), NS_XMPP_STREAM.to_owned()), + ] + .iter() + .cloned() + .collect(); + let send = stream.send(Packet::StreamStart(attrs)); + + StreamStart { + state: StreamStartState::SendStart(send), + jid, + ns, + } + } +} + +impl Future for StreamStart { + type Item = XMPPStream; + type Error = Error; + + fn poll(&mut self) -> Poll { + let old_state = replace(&mut self.state, StreamStartState::Invalid); + let mut retry = false; + + let (new_state, result) = match old_state { + StreamStartState::SendStart(mut send) => match send.poll() { + Ok(Async::Ready(stream)) => { + retry = true; + (StreamStartState::RecvStart(stream), Ok(Async::NotReady)) + } + Ok(Async::NotReady) => (StreamStartState::SendStart(send), Ok(Async::NotReady)), + Err(e) => (StreamStartState::Invalid, Err(e.into())), + }, + StreamStartState::RecvStart(mut stream) => match stream.poll() { + Ok(Async::Ready(Some(Packet::StreamStart(stream_attrs)))) => { + let stream_ns = stream_attrs + .get("xmlns") + .ok_or(ProtocolError::NoStreamNamespace)? + .clone(); + if self.ns == "jabber:client" { + retry = true; + // TODO: skip RecvFeatures for version < 1.0 + ( + StreamStartState::RecvFeatures(stream, stream_ns), + Ok(Async::NotReady), + ) + } else { + let id = stream_attrs + .get("id") + .ok_or(ProtocolError::NoStreamId)? + .clone(); + // FIXME: huge hack, shouldn’t be an element! + let stream = XMPPStream::new( + self.jid.clone(), + stream, + self.ns.clone(), + Element::builder(id).build(), + ); + (StreamStartState::Invalid, Ok(Async::Ready(stream))) + } + } + Ok(Async::Ready(_)) => return Err(ProtocolError::InvalidToken.into()), + Ok(Async::NotReady) => (StreamStartState::RecvStart(stream), Ok(Async::NotReady)), + Err(e) => return Err(ProtocolError::from(e).into()), + }, + StreamStartState::RecvFeatures(mut stream, stream_ns) => match stream.poll() { + Ok(Async::Ready(Some(Packet::Stanza(stanza)))) => { + if stanza.is("features", NS_XMPP_STREAM) { + let stream = + XMPPStream::new(self.jid.clone(), stream, self.ns.clone(), stanza); + (StreamStartState::Invalid, Ok(Async::Ready(stream))) + } else { + ( + StreamStartState::RecvFeatures(stream, stream_ns), + Ok(Async::NotReady), + ) + } + } + Ok(Async::Ready(_)) | Ok(Async::NotReady) => ( + StreamStartState::RecvFeatures(stream, stream_ns), + Ok(Async::NotReady), + ), + Err(e) => return Err(ProtocolError::from(e).into()), + }, + StreamStartState::Invalid => unreachable!(), + }; + + self.state = new_state; + if retry { + self.poll() + } else { + result + } + } +} diff --git a/tokio-xmpp/src/xmpp_codec.rs b/tokio-xmpp/src/xmpp_codec.rs new file mode 100644 index 0000000000000000000000000000000000000000..588d947d5c09898da7e6bbba36f3e6ff618fc218 --- /dev/null +++ b/tokio-xmpp/src/xmpp_codec.rs @@ -0,0 +1,532 @@ +//! XML stream parser for XMPP + +use crate::{ParseError, ParserError}; +use bytes::{BufMut, BytesMut}; +use xmpp_parsers::Element; +use quick_xml::Writer as EventWriter; +use std; +use std::cell::RefCell; +use std::collections::vec_deque::VecDeque; +use std::collections::HashMap; +use std::default::Default; +use std::fmt::Write; +use std::io; +use std::iter::FromIterator; +use std::rc::Rc; +use std::str::from_utf8; +use std::borrow::Cow; +use tokio_codec::{Decoder, Encoder}; +use xml5ever::interface::Attribute; +use xml5ever::tokenizer::{Tag, TagKind, Token, TokenSink, XmlTokenizer}; +use xml5ever::buffer_queue::BufferQueue; + +/// Anything that can be sent or received on an XMPP/XML stream +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Packet { + /// `` start tag + StreamStart(HashMap), + /// A complete stanza or nonza + Stanza(Element), + /// Plain text (think whitespace keep-alive) + Text(String), + /// `` closing tag + StreamEnd, +} + +type QueueItem = Result; + +/// Parser state +struct ParserSink { + // Ready stanzas, shared with XMPPCodec + queue: Rc>>, + // Parsing stack + stack: Vec, + ns_stack: Vec, String>>, +} + +impl ParserSink { + pub fn new(queue: Rc>>) -> Self { + ParserSink { + queue, + stack: vec![], + ns_stack: vec![], + } + } + + fn push_queue(&self, pkt: Packet) { + self.queue.borrow_mut().push_back(Ok(pkt)); + } + + fn push_queue_error(&self, e: ParserError) { + self.queue.borrow_mut().push_back(Err(e)); + } + + /// Lookup XML namespace declaration for given prefix (or no prefix) + fn lookup_ns(&self, prefix: &Option) -> Option<&str> { + for nss in self.ns_stack.iter().rev() { + if let Some(ns) = nss.get(prefix) { + return Some(ns); + } + } + + None + } + + fn handle_start_tag(&mut self, tag: Tag) { + let mut nss = HashMap::new(); + let is_prefix_xmlns = |attr: &Attribute| { + attr.name + .prefix + .as_ref() + .map(|prefix| prefix.eq_str_ignore_ascii_case("xmlns")) + .unwrap_or(false) + }; + for attr in &tag.attrs { + match attr.name.local.as_ref() { + "xmlns" => { + nss.insert(None, attr.value.as_ref().to_owned()); + } + prefix if is_prefix_xmlns(attr) => { + nss.insert(Some(prefix.to_owned()), attr.value.as_ref().to_owned()); + } + _ => (), + } + } + self.ns_stack.push(nss); + + let el = { + let mut el_builder = Element::builder(tag.name.local.as_ref()); + if let Some(el_ns) = + self.lookup_ns(&tag.name.prefix.map(|prefix| prefix.as_ref().to_owned())) + { + el_builder = el_builder.ns(el_ns); + } + for attr in &tag.attrs { + match attr.name.local.as_ref() { + "xmlns" => (), + _ if is_prefix_xmlns(attr) => (), + _ => { + let attr_name = if let Some(ref prefix) = attr.name.prefix { + Cow::Owned(format!("{}:{}", prefix, attr.name.local)) + } else { + Cow::Borrowed(attr.name.local.as_ref()) + }; + el_builder = el_builder.attr(attr_name, attr.value.as_ref()); + } + } + } + el_builder.build() + }; + + if self.stack.is_empty() { + let attrs = HashMap::from_iter(tag.attrs.iter().map(|attr| { + ( + attr.name.local.as_ref().to_owned(), + attr.value.as_ref().to_owned(), + ) + })); + self.push_queue(Packet::StreamStart(attrs)); + } + + self.stack.push(el); + } + + fn handle_end_tag(&mut self) { + let el = self.stack.pop().unwrap(); + self.ns_stack.pop(); + + match self.stack.len() { + // + 0 => self.push_queue(Packet::StreamEnd), + // + 1 => self.push_queue(Packet::Stanza(el)), + len => { + let parent = &mut self.stack[len - 1]; + parent.append_child(el); + } + } + } +} + +impl TokenSink for ParserSink { + fn process_token(&mut self, token: Token) { + match token { + Token::TagToken(tag) => match tag.kind { + TagKind::StartTag => self.handle_start_tag(tag), + TagKind::EndTag => self.handle_end_tag(), + TagKind::EmptyTag => { + self.handle_start_tag(tag); + self.handle_end_tag(); + } + TagKind::ShortTag => self.push_queue_error(ParserError::ShortTag), + }, + Token::CharacterTokens(tendril) => match self.stack.len() { + 0 | 1 => self.push_queue(Packet::Text(tendril.into())), + len => { + let el = &mut self.stack[len - 1]; + el.append_text_node(tendril); + } + }, + Token::EOFToken => self.push_queue(Packet::StreamEnd), + Token::ParseError(s) => { + // println!("ParseError: {:?}", s); + self.push_queue_error(ParserError::Parse(ParseError(s))); + } + _ => (), + } + } + + // fn end(&mut self) { + // } +} + +/// Stateful encoder/decoder for a bytestream from/to XMPP `Packet` +pub struct XMPPCodec { + /// Outgoing + ns: Option, + /// Incoming + parser: XmlTokenizer, + /// For handling incoming truncated utf8 + // TODO: optimize using tendrils? + buf: Vec, + /// Shared with ParserSink + queue: Rc>>, +} + +impl XMPPCodec { + /// Constructor + pub fn new() -> Self { + let queue = Rc::new(RefCell::new(VecDeque::new())); + let sink = ParserSink::new(queue.clone()); + // TODO: configure parser? + let parser = XmlTokenizer::new(sink, Default::default()); + XMPPCodec { + ns: None, + parser, + queue, + buf: vec![], + } + } +} + +impl Default for XMPPCodec { + fn default() -> Self { + Self::new() + } +} + +impl Decoder for XMPPCodec { + type Item = Packet; + type Error = ParserError; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { + let buf1: Box> = if !self.buf.is_empty() && !buf.is_empty() { + let mut prefix = std::mem::replace(&mut self.buf, vec![]); + prefix.extend_from_slice(buf.take().as_ref()); + Box::new(prefix) + } else { + Box::new(buf.take()) + }; + let buf1 = buf1.as_ref().as_ref(); + match from_utf8(buf1) { + Ok(mut s) => { + s = s.trim(); + if !s.is_empty() { + // println!("<< {}", s); + let mut buffer_queue = BufferQueue::new(); + let tendril = FromIterator::from_iter(s.chars()); + buffer_queue.push_back(tendril); + self.parser.feed(&mut buffer_queue); + } + } + // Remedies for truncated utf8 + Err(e) if e.valid_up_to() >= buf1.len() - 3 => { + // Prepare all the valid data + let mut b = BytesMut::with_capacity(e.valid_up_to()); + b.put(&buf1[0..e.valid_up_to()]); + + // Retry + let result = self.decode(&mut b); + + // Keep the tail back in + self.buf.extend_from_slice(&buf1[e.valid_up_to()..]); + + return result; + } + Err(e) => { + // println!("error {} at {}/{} in {:?}", e, e.valid_up_to(), buf1.len(), buf1); + return Err(ParserError::Utf8(e)); + } + } + + match self.queue.borrow_mut().pop_front() { + None => Ok(None), + Some(result) => result.map(|pkt| Some(pkt)), + } + } + + fn decode_eof(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { + self.decode(buf) + } +} + +impl Encoder for XMPPCodec { + type Item = Packet; + type Error = io::Error; + + fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { + let remaining = dst.capacity() - dst.len(); + let max_stanza_size: usize = 2usize.pow(16); + if remaining < max_stanza_size { + dst.reserve(max_stanza_size - remaining); + } + + fn to_io_err>>(e: E) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, e) + } + + match item { + Packet::StreamStart(start_attrs) => { + let mut buf = String::new(); + write!(buf, "\n") + .map_err(to_io_err)?; + + // print!(">> {}", buf); + write!(dst, "{}", buf) + .map_err(to_io_err) + } + Packet::Stanza(stanza) => { + stanza + .write_to_inner(&mut EventWriter::new(WriteBytes::new(dst))) + .and_then(|_| { + // println!(">> {:?}", dst); + Ok(()) + }) + .map_err(|e| to_io_err(format!("{}", e))) + } + Packet::Text(text) => { + write_text(&text, dst) + .and_then(|_| { + // println!(">> {:?}", dst); + Ok(()) + }) + .map_err(to_io_err) + } + Packet::StreamEnd => { + write!(dst, "\n") + .map_err(to_io_err) + } + } + } +} + +/// Write XML-escaped text string +pub fn write_text(text: &str, writer: &mut W) -> Result<(), std::fmt::Error> { + write!(writer, "{}", escape(text)) +} + +/// Copied from `RustyXML` for now +pub fn escape(input: &str) -> String { + let mut result = String::with_capacity(input.len()); + + for c in input.chars() { + match c { + '&' => result.push_str("&"), + '<' => result.push_str("<"), + '>' => result.push_str(">"), + '\'' => result.push_str("'"), + '"' => result.push_str("""), + o => result.push(o), + } + } + result +} + +/// BytesMut impl only std::fmt::Write but not std::io::Write. The +/// latter trait is required for minidom's +/// `Element::write_to_inner()`. +struct WriteBytes<'a> { + dst: &'a mut BytesMut, +} + +impl<'a> WriteBytes<'a> { + fn new(dst: &'a mut BytesMut) -> Self { + WriteBytes { dst } + } +} + +impl<'a> std::io::Write for WriteBytes<'a> { + fn write(&mut self, buf: &[u8]) -> std::result::Result { + self.dst.put_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::result::Result<(), std::io::Error> { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bytes::BytesMut; + + #[test] + fn test_stream_start() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + } + + #[test] + fn test_stream_end() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + b.clear(); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamEnd)) => true, + _ => false, + }); + } + + #[test] + fn test_truncated_stanza() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + + b.clear(); + b.put(r"ß true, + _ => false, + }); + + b.clear(); + b.put(r">"); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::Stanza(ref el))) if el.name() == "test" && el.text() == "ß" => true, + _ => false, + }); + } + + #[test] + fn test_truncated_utf8() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + + b.clear(); + b.put(&b"\xc3"[..]); + let r = c.decode(&mut b); + assert!(match r { + Ok(None) => true, + _ => false, + }); + + b.clear(); + b.put(&b"\x9f"[..]); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::Stanza(ref el))) if el.name() == "test" && el.text() == "ß" => true, + _ => false, + }); + } + + /// test case for https://gitlab.com/xmpp-rs/tokio-xmpp/issues/3 + #[test] + fn test_atrribute_prefix() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + + b.clear(); + b.put(r"Test status"); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::Stanza(ref el))) if el.name() == "status" && el.text() == "Test status" && el.attr("xml:lang").map_or(false, |a| a == "en") => true, + _ => false, + }); + + } + + /// By default, encode() only get's a BytesMut that has 8kb space reserved. + #[test] + fn test_large_stanza() { + use futures::{Future, Sink}; + use std::io::Cursor; + use tokio_codec::FramedWrite; + let framed = FramedWrite::new(Cursor::new(vec![]), XMPPCodec::new()); + let mut text = "".to_owned(); + for _ in 0..2usize.pow(15) { + text = text + "A"; + } + let stanza = Element::builder("message") + .append(Element::builder("body").append(text.as_ref()).build()) + .build(); + let framed = framed.send(Packet::Stanza(stanza)).wait().expect("send"); + assert_eq!( + framed.get_ref().get_ref(), + &("".to_owned() + &text + "").as_bytes() + ); + } + + #[test] + fn test_lone_whitespace() { + let mut c = XMPPCodec::new(); + let mut b = BytesMut::with_capacity(1024); + b.put(r""); + let r = c.decode(&mut b); + assert!(match r { + Ok(Some(Packet::StreamStart(_))) => true, + _ => false, + }); + + b.clear(); + b.put(r" "); + let r = c.decode(&mut b); + assert!(match r { + Ok(None) => true, + _ => false, + }); + } +} diff --git a/tokio-xmpp/src/xmpp_stream.rs b/tokio-xmpp/src/xmpp_stream.rs new file mode 100644 index 0000000000000000000000000000000000000000..129dd1d4fe8825b06409cf52fac2f145f6678969 --- /dev/null +++ b/tokio-xmpp/src/xmpp_stream.rs @@ -0,0 +1,92 @@ +//! `XMPPStream` is the common container for all XMPP network connections + +use futures::sink::Send; +use futures::{Poll, Sink, StartSend, Stream}; +use xmpp_parsers::{Jid, Element}; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; + +use crate::stream_start::StreamStart; +use crate::xmpp_codec::{Packet, XMPPCodec}; + +/// namespace +pub const NS_XMPP_STREAM: &str = "http://etherx.jabber.org/streams"; + +/// Wraps a `stream` +pub struct XMPPStream { + /// The local Jabber-Id + pub jid: Jid, + /// Codec instance + pub stream: Framed, + /// `` for XMPP version 1.0 + pub stream_features: Element, + /// Root namespace + /// + /// This is different for either c2s, s2s, or component + /// connections. + pub ns: String, +} + +impl XMPPStream { + /// Constructor + pub fn new( + jid: Jid, + stream: Framed, + ns: String, + stream_features: Element, + ) -> Self { + XMPPStream { + jid, + stream, + stream_features, + ns, + } + } + + /// Send a `` start tag + pub fn start(stream: S, jid: Jid, ns: String) -> StreamStart { + let xmpp_stream = Framed::new(stream, XMPPCodec::new()); + StreamStart::from_stream(xmpp_stream, jid, ns) + } + + /// Unwraps the inner stream + pub fn into_inner(self) -> S { + self.stream.into_inner() + } + + /// Re-run `start()` + pub fn restart(self) -> StreamStart { + Self::start(self.stream.into_inner(), self.jid, self.ns) + } +} + +impl XMPPStream { + /// Convenience method + pub fn send_stanza>(self, e: E) -> Send { + self.send(Packet::Stanza(e.into())) + } +} + +/// Proxy to self.stream +impl Sink for XMPPStream { + type SinkItem = as Sink>::SinkItem; + type SinkError = as Sink>::SinkError; + + fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + self.stream.start_send(item) + } + + fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + self.stream.poll_complete() + } +} + +/// Proxy to self.stream +impl Stream for XMPPStream { + type Item = as Stream>::Item; + type Error = as Stream>::Error; + + fn poll(&mut self) -> Poll, Self::Error> { + self.stream.poll() + } +}