瀏覽代碼

Allow switching user accounts at runtime

Fixes #130
Paul Lietar 7 年之前
父節點
當前提交
d940ed161a
共有 12 個文件被更改,包括 431 次插入188 次删除
  1. 100 61
      Cargo.lock
  2. 1 1
      Cargo.toml
  3. 40 42
      src/authentication/discovery.rs
  4. 9 14
      src/authentication/mod.rs
  5. 8 0
      src/component.rs
  6. 161 39
      src/main.rs
  7. 6 2
      src/mercury/mod.rs
  8. 8 3
      src/mixer/mod.rs
  9. 4 6
      src/mixer/softmixer.rs
  10. 8 0
      src/player.rs
  11. 74 18
      src/session.rs
  12. 12 2
      src/spirc.rs

+ 100 - 61
Cargo.lock

@@ -6,7 +6,6 @@ dependencies = [
  "base64 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "ctrlc 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -27,13 +26,14 @@ dependencies = [
  "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "rpassword 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "shannon 0.1.1 (git+https://github.com/plietar/rust-shannon)",
  "tempfile 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "tremor 0.1.0 (git+https://github.com/plietar/rust-tremor)",
  "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -61,7 +61,7 @@ name = "aster"
 version = "0.41.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -72,6 +72,14 @@ dependencies = [
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "base64"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "bit-set"
 version = "0.4.0"
@@ -100,6 +108,11 @@ name = "bitflags"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "bitflags"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "byteorder"
 version = "0.5.3"
@@ -120,16 +133,6 @@ name = "crossbeam"
 version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "ctrlc"
-version = "2.0.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)",
- "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "dns-parser"
 version = "0.3.2"
@@ -169,7 +172,7 @@ dependencies = [
 
 [[package]]
 name = "futures-cpupool"
-version = "0.1.2"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -195,11 +198,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 [[package]]
 name = "hyper"
 version = "0.11.0-a.0"
-source = "git+https://github.com/hyperium/hyper#2f18ce20348392893f6815519f8c0ad2b5649e66"
+source = "git+https://github.com/hyperium/hyper#435fe84bf52fbbf819068402d6c8e902aa7a6685"
 dependencies = [
- "base64 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "base64 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures-cpupool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -220,7 +223,7 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-bidi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -336,6 +339,15 @@ dependencies = [
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "mio-uds"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "miow"
 version = "0.2.0"
@@ -454,8 +466,8 @@ version = "0.6.0"
 source = "git+https://github.com/plietar/rust-protobuf-macros#f186dc5a16c0d79f14c319ac8ce30b06de0cefee"
 dependencies = [
  "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -465,7 +477,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "quote"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -489,7 +501,7 @@ dependencies = [
  "aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -554,36 +566,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "serde"
-version = "0.9.6"
+version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "serde_codegen_internals"
-version = "0.13.0"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "0.9.6"
+version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_codegen_internals 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "serde_json"
-version = "0.9.5"
+version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -617,37 +629,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "syn"
-version = "0.11.4"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synom 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "synom"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "syntex"
-version = "0.58.0"
+version = "0.58.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "syntex_errors"
-version = "0.58.0"
+version = "0.58.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "syntex_pos"
-version = "0.58.0"
+version = "0.58.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -655,14 +676,14 @@ dependencies = [
 
 [[package]]
 name = "syntex_syntax"
-version = "0.58.0"
+version = "0.58.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -711,7 +732,7 @@ dependencies = [
 
 [[package]]
 name = "thread_local"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -765,6 +786,20 @@ dependencies = [
  "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "tokio-signal"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.10 (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.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio-uds 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "tremor"
 version = "0.1.0"
@@ -795,7 +830,7 @@ dependencies = [
 
 [[package]]
 name = "unicode-bidi"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -919,22 +954,23 @@ dependencies = [
 "checksum alsa 0.0.1 (git+https://github.com/plietar/rust-alsa)" = "<none>"
 "checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
 "checksum base64 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d156a04ec694d726e92ea3c13e4a62949b4f0488a9344f04341d679ec6b127b"
+"checksum base64 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "065a0ce220ab84d0b6d5ae3e7bb77232209519c366f51f946fe28c19e84989d0"
 "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
 "checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
 "checksum bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32866f4d103c4e438b1db1158aa1b1a80ee078e5d77a59a2f906fd62a577389c"
 "checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
 "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
+"checksum bitflags 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e1ab483fc81a8143faa7203c4a3c02888ebd1a782e37e41fa34753ba9a162"
 "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
 "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
 "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
 "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
-"checksum ctrlc 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77f98bb69e3fefadcc5ca80a1368a55251f70295168203e01165bcaecb270891"
 "checksum dns-parser 0.3.2 (git+https://github.com/plietar/dns-parser)" = "<none>"
 "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
 "checksum env_logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "99971fb1b635fe7a0ee3c4d065845bb93cca80a23b5613b5613391ece5de4144"
 "checksum error-chain 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e92ecf0a508c8e074c0e6fa8fe0fa38414848ad4dfc4db6f74c5e9753330b248"
 "checksum futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c1913eb7083840b1bbcbf9631b7fda55eaf35fe7ead13cca034e8946f9e2bc41"
-"checksum futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bb982bb25cd8fa5da6a8eb3a460354c984ff1113da82bcb4f0b0862b5795db82"
+"checksum futures-cpupool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9e48a3fff6a58fe9df1eed13d2599650416a987386c43a19aec656c3e6a2c229"
 "checksum gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)" = "c07c758b972368e703a562686adb39125707cc1ef3399da8c019fc6c2498a75d"
 "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
 "checksum httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e7a63e511f9edffbab707141fbb8707d1a3098615fb2adbd5769cdfcc9b17d"
@@ -954,6 +990,7 @@ dependencies = [
 "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
 "checksum mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5c93a4bd787ddc6e7833c519b73a50883deb5863d76d9b71eb8216fb7f94e66"
 "checksum mio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "eecdbdd49a849336e77b453f021c89972a2cfb5b51931a0026ae0ac4602de681"
+"checksum mio-uds 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "78437f00d9615c366932cbfe79790b5c2945706ba67cf78378ffacc0069ed9de"
 "checksum miow 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a78d2605eb97302c10cf944b8d96b0a2a890c52957caf92fcd1f24f69049579"
 "checksum multimap 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9223f4774d08e06185e44e555b9a7561243d387bac49c78a6205c42d6975fbf2"
 "checksum net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "5edf9cb6be97212423aed9413dd4729d62b370b5e1c571750e882cebbbc1e3e2"
@@ -969,7 +1006,7 @@ dependencies = [
 "checksum protobuf 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "22eaac7d4be49a479dbd875f6f84ab79eef282aa51ba36ce884ec10efd91d355"
 "checksum protobuf_macros 0.6.0 (git+https://github.com/plietar/rust-protobuf-macros)" = "<none>"
 "checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c"
-"checksum quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b44fd83db28b83c1c58187159934906e5e955c812e211df413b76b03c909a5"
+"checksum quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "08de3f12e670f83f61e450443cbae34496a35b665691fd8e99b24ec662f75865"
 "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"
 "checksum redox_syscall 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd35cc9a8bdec562c757e3d43c1526b5c6d2653e23e2315065bc25556550753"
 "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01"
@@ -981,33 +1018,35 @@ dependencies = [
 "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
 "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
 "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
-"checksum serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0ae9a3c8b07c09dbe43022486d55a18c629a0618d2241e49829aaef9b6d862f9"
-"checksum serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3172bf2940b975c0e4f6ab42a511c0a4407d4f46ccef87a9d3615db5c26fa96"
-"checksum serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ecc6e0379ca933ece58302d2d3034443f06fbf38fd535857c1dc516195cbc3bf"
-"checksum serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cf37ce931677e98b4fa5e6469aaa3ab4b6228309ea33b1b22d3ec055adfc4515"
+"checksum serde 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)" = "204db0f2a5335be7313fd4453132fd56d2085aed081c673140a256772903e116"
+"checksum serde_codegen_internals 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a5113d5bd16471b183803b374f0fe4877ad9658b95e33b11f4a004d73aacc74a"
+"checksum serde_derive 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)" = "e88ec062a02cbebfd6276044a305d665a9919b497aa6acb2e12c070d1a50d32d"
+"checksum serde_json 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6501ac6f8b74f9b1033f7ddf79a08edfa0f58d6f8e3190cb8dc97736afa257a8"
 "checksum shannon 0.1.1 (git+https://github.com/plietar/rust-shannon)" = "<none>"
 "checksum shannon-sys 0.1.0 (git+https://github.com/plietar/rust-shannon)" = "<none>"
 "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
 "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
-"checksum syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f94368aae82bb29656c98443a7026ca931a659e8d19dcdc41d6e273054e820"
-"checksum syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35f3cc9d446323ef8fefad933b65cd6de271d29fa14a2e9d036a084770c6d6d5"
-"checksum syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3af03823ea45d420dd2c1a44bb074e13ea55f9b99afe960fd58eb4069b7f6cad"
-"checksum syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e502a4a904d9f37cf975dbdbb0b08f2d111322f6792bda6eb095b4112c9a24b"
-"checksum syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cf936464c3863952ea3fab848860ea891eba8647b6008b04c36f0bb007192a3"
+"checksum syn 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e28da8d02d75d1e58b89258e0741128f0b0d8a8309fb5c627be0fbd37a76c67"
+"checksum synom 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8fece1853fb872b0acdc3ff88f37c474018e125ef81cd4cb8c0ca515746b62ed"
+"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e"
+"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c"
+"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
+"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
 "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
 "checksum tempfile 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3213fd2b7ed87e39306737ccfac04b1233b57a33ca64cfbf52f2ffaa2b765e2f"
 "checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989"
 "checksum termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a"
 "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a"
-"checksum thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7793b722f0f77ce716e7f1acf416359ca32ff24d04ffbac4269f44a4a83be05d"
+"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7"
 "checksum time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade"
 "checksum tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3d1be481b55126f02ef88ff86748086473cb537a949fc4a8f4be403a530ae54b"
 "checksum tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c0d6031f94d78d7b4d509d4a7c5e1cdf524a17e7b08d1c188a83cf720e69808"
 "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
+"checksum tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d121715f6917878a0df69f39365d01dd66c4463e4ba19efdcddcdfeb1bcb2bc"
 "checksum tremor 0.1.0 (git+https://github.com/plietar/rust-tremor)" = "<none>"
 "checksum tremor-sys 0.1.0 (git+https://github.com/plietar/rust-tremor)" = "<none>"
 "checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764"
-"checksum unicode-bidi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b61814f3e7fd0e0f15370f767c7c943e08bc2e3214233ae8f88522b334ceb778"
+"checksum unicode-bidi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a078ebdd62c0e71a709c3d53d2af693fe09fe93fbff8344aebe289b78f9032"
 "checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff"
 "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
 "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"

+ 1 - 1
Cargo.toml

@@ -25,7 +25,6 @@ path = "protocol"
 base64          = "0.3.0"
 bit-set         = "0.4.0"
 byteorder       = "1.0"
-ctrlc           = { version = "2.0", features = ["termination"] }
 env_logger      = "0.4.0"
 getopts         = "0.2.14"
 hyper           = { git = "https://github.com/hyperium/hyper" }
@@ -59,6 +58,7 @@ error-chain = { version = "0.9.0", default_features = false }
 futures = "0.1.8"
 tokio-core = "0.1.2"
 tokio-proto = "0.1.0"
+tokio-signal = "0.1"
 
 [build-dependencies]
 vergen          = "0.1.0"

+ 40 - 42
src/authentication/discovery.rs

@@ -4,7 +4,7 @@ use crypto::mac::Mac;
 use crypto;
 use diffie_hellman::{DH_GENERATOR, DH_PRIME};
 use futures::sync::mpsc;
-use futures::{Future, Stream, BoxFuture};
+use futures::{Future, Stream, BoxFuture, Poll, Async};
 use hyper::server::{Service, NewService, Request, Response, Http};
 use hyper::{self, Get, Post, StatusCode};
 use mdns;
@@ -12,7 +12,6 @@ use num_bigint::BigUint;
 use rand;
 use std::collections::BTreeMap;
 use std::io;
-use std::net::SocketAddr;
 use std::sync::Arc;
 use tokio_core::net::TcpListener;
 use tokio_core::reactor::Handle;
@@ -51,26 +50,6 @@ impl Discovery {
 
         (discovery, rx)
     }
-
-    pub fn serve(&self, addr: &SocketAddr, handle: &Handle)
-        -> hyper::Result<SocketAddr>
-    {
-        let listener = TcpListener::bind(addr, handle)?;
-        let addr = listener.local_addr()?;
-
-        let http = Http::new();
-        let svc = self.clone();
-        let handle_ = handle.clone();
-
-        let task = listener.incoming().for_each(move |(socket, addr)| {
-            http.bind_connection(&handle_, socket, addr, svc.clone());
-            Ok(())
-        });
-
-        handle.spawn(task.map_err(|e| panic!(e)));
-
-        Ok(addr)
-    }
 }
 
 impl Discovery {
@@ -189,7 +168,9 @@ impl Service for Discovery {
             params.extend(url::form_urlencoded::parse(query.as_bytes()).into_owned());
         }
 
-        debug!("{:?} {:?} {:?}", method, uri.path(), params);
+        if method != Get {
+            debug!("{:?} {:?} {:?}", method, uri.path(), params);
+        }
 
         let this = self.clone();
         body.fold(Vec::new(), |mut acc, chunk| {
@@ -219,34 +200,51 @@ impl NewService for Discovery {
     }
 }
 
-use tokio_core::reactor::Core;
+pub struct DiscoveryStream {
+    credentials: mpsc::UnboundedReceiver<Credentials>,
+    _svc: mdns::Service,
+    task: Box<Future<Item=(), Error=io::Error>>,
+}
 
-pub fn discovery_login<A,B>(device_name: A, device_id: B) -> Result<Credentials, ()>
-    where A: Into<String>,
-          B: Into<String>
+pub fn discovery(handle: &Handle, device_name: String, device_id: String)
+    -> io::Result<DiscoveryStream>
 {
-    let device_name = device_name.into();
-    let device_id = device_id.into();
-
     let (discovery, creds_rx) = Discovery::new(device_name.clone(), device_id);
 
-    let creds_rx = creds_rx.into_future()
-        .map(move |(creds, _)| creds.unwrap()).map_err(|(e, _)| e);
+    let listener = TcpListener::bind(&"0.0.0.0:0".parse().unwrap(), handle)?;
+    let addr = listener.local_addr()?;
 
-    let addr = "0.0.0.0:0".parse().unwrap();
+    let http = Http::new();
+    let handle_ = handle.clone();
+    let task = Box::new(listener.incoming().for_each(move |(socket, addr)| {
+        http.bind_connection(&handle_, socket, addr, discovery.clone());
+        Ok(())
+    }));
 
-    let mut core = Core::new().unwrap();
-    let handle = core.handle();
-    let listening_addr = discovery.serve(&addr, &handle).unwrap();
-
-    let responder = mdns::Responder::spawn(&handle).unwrap();
-    let _svc = responder.register(
+    let responder = mdns::Responder::spawn(&handle)?;
+    let svc = responder.register(
         "_spotify-connect._tcp".to_owned(),
         device_name,
-        listening_addr.port(),
+        addr.port(),
         &["VERSION=1.0", "CPath=/"]);
 
-    let creds = core.run(creds_rx).unwrap();
+    Ok(DiscoveryStream {
+        credentials: creds_rx,
+        _svc: svc,
+        task: task,
+    })
+}
+
+impl Stream for DiscoveryStream {
+    type Item = Credentials;
+    type Error = io::Error;
+
+    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
+        match self.task.poll()? {
+            Async::Ready(()) => unreachable!(),
+            Async::NotReady => (),
+        }
 
-    Ok(creds)
+        Ok(self.credentials.poll().unwrap())
+    }
 }

+ 9 - 14
src/authentication/mod.rs

@@ -16,6 +16,8 @@ use std::path::Path;
 
 use protocol::authentication::AuthenticationType;
 
+pub mod discovery;
+
 #[derive(Debug, Clone)]
 #[derive(Serialize, Deserialize)]
 pub struct Credentials {
@@ -169,35 +171,28 @@ fn deserialize_base64<D>(de: D) -> Result<Vec<u8>, D::Error>
     base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string()))
 }
 
-mod discovery;
-pub use self::discovery::discovery_login;
-
-pub fn get_credentials(device_name: &str, device_id: &str,
-                       username: Option<String>, password: Option<String>,
+pub fn get_credentials(username: Option<String>, password: Option<String>,
                        cached_credentials: Option<Credentials>)
-    -> Credentials
+    -> Option<Credentials>
 {
     match (username, password, cached_credentials) {
 
         (Some(username), Some(password), _)
-            => Credentials::with_password(username, password),
+            => Some(Credentials::with_password(username, password)),
 
         (Some(ref username), _, Some(ref credentials))
-            if *username == credentials.username => credentials.clone(),
+            if *username == credentials.username => Some(credentials.clone()),
 
         (Some(username), None, _) => {
             write!(stderr(), "Password for {}: ", username).unwrap();
             stderr().flush().unwrap();
             let password = rpassword::read_password().unwrap();
-            Credentials::with_password(username.clone(), password)
+            Some(Credentials::with_password(username.clone(), password))
         }
 
         (None, _, Some(credentials))
-            => credentials,
+            => Some(credentials),
 
-        (None, _, None) => {
-            info!("No username provided and no stored credentials, starting discovery ...");
-            discovery_login(device_name, device_id).unwrap()
-        }
+        (None, _, None) => None,
     }
 }

+ 8 - 0
src/component.rs

@@ -5,6 +5,8 @@ macro_rules! component {
         impl $name {
             #[allow(dead_code)]
             pub fn new(session: $crate::session::SessionWeak) -> $name {
+                debug!(target:"librespot::component", "new {}", stringify!($name));
+
                 $name(::std::sync::Arc::new((session, ::std::sync::Mutex::new($inner {
                     $($key : $value,)*
                 }))))
@@ -25,6 +27,12 @@ macro_rules! component {
         struct $inner {
             $($key : $ty,)*
         }
+
+        impl Drop for $inner {
+            fn drop(&mut self) {
+                debug!(target:"librespot::component", "drop {}", stringify!($name));
+            }
+        }
     }
 }
 

+ 161 - 39
src/main.rs

@@ -1,23 +1,25 @@
 #[macro_use] extern crate log;
-extern crate ctrlc;
 extern crate env_logger;
 extern crate futures;
 extern crate getopts;
 extern crate librespot;
 extern crate tokio_core;
+extern crate tokio_signal;
 
 use env_logger::LogBuilder;
-use futures::Future;
-use std::cell::{RefCell, Cell};
+use futures::{Future, Async, Poll, Stream};
 use std::env;
-use std::io::{stderr, Write};
+use std::io::{self, stderr, Write};
 use std::path::PathBuf;
 use std::process::exit;
 use std::str::FromStr;
-use tokio_core::reactor::Core;
+use tokio_core::reactor::{Handle, Core};
+use tokio_core::io::IoStream;
+use std::mem;
 
-use librespot::spirc::Spirc;
+use librespot::spirc::{Spirc, SpircTask};
 use librespot::authentication::{get_credentials, Credentials};
+use librespot::authentication::discovery::{discovery, DiscoveryStream};
 use librespot::audio_backend::{self, Sink, BACKENDS};
 use librespot::cache::Cache;
 use librespot::player::Player;
@@ -33,7 +35,6 @@ fn usage(program: &str, opts: &getopts::Options) -> String {
 
 fn setup_logging(verbose: bool) {
     let mut builder = LogBuilder::new();
-
     match env::var("RUST_LOG") {
         Ok(config) => {
             builder.parse(&config);
@@ -65,13 +66,17 @@ fn list_backends() {
     }
 }
 
+#[derive(Clone)]
 struct Setup {
     backend: fn(Option<String>) -> Box<Sink>,
-    mixer: Box<Mixer + Send>,
+    device: Option<String>,
+
+    mixer: fn() -> Box<Mixer>,
+
     cache: Option<Cache>,
     config: Config,
-    credentials: Credentials,
-    device: Option<String>,
+    credentials: Option<Credentials>,
+    enable_discovery: bool,
 }
 
 fn setup(args: &[String]) -> Setup {
@@ -84,6 +89,7 @@ fn setup(args: &[String]) -> Setup {
         .optflag("v", "verbose", "Enable verbose output")
         .optopt("u", "username", "Username to sign in with", "USERNAME")
         .optopt("p", "password", "Password", "PASSWORD")
+        .optflag("", "disable-discovery", "Disable discovery mode")
         .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND")
         .optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE")
         .optopt("", "mixer", "Mixer to use", "MIXER");
@@ -130,11 +136,12 @@ fn setup(args: &[String]) -> Setup {
 
     let cached_credentials = cache.as_ref().and_then(Cache::credentials);
 
-    let credentials = get_credentials(&name, &device_id,
-                                      matches.opt_str("username"),
+    let credentials = get_credentials(matches.opt_str("username"),
                                       matches.opt_str("password"),
                                       cached_credentials);
 
+    let enable_discovery = !matches.opt_present("disable-discovery");
+
     let config = Config {
         user_agent: version::version_string(),
         name: name,
@@ -148,48 +155,163 @@ fn setup(args: &[String]) -> Setup {
 
     Setup {
         backend: backend,
-        mixer: mixer,
         cache: cache,
         config: config,
         credentials: credentials,
         device: device,
+        enable_discovery: enable_discovery,
+        mixer: mixer,
     }
 }
 
-fn main() {
-    let mut core = Core::new().unwrap();
-    let handle = core.handle();
+struct Main {
+    cache: Option<Cache>,
+    config: Config,
+    backend: fn(Option<String>) -> Box<Sink>,
+    device: Option<String>,
+    mixer: fn() -> Box<Mixer>,
+    handle: Handle,
 
-    let args: Vec<String> = std::env::args().collect();
+    discovery: Option<DiscoveryStream>,
+    signal: IoStream<()>,
 
-    let Setup { backend, mixer, cache, config, credentials, device }
-        = setup(&args);
+    spirc: Option<Spirc>,
+    spirc_task: Option<SpircTask>,
+    connect: Box<Future<Item=Session, Error=io::Error>>,
 
-    let connection = Session::connect(config, credentials, cache, handle);
+    shutdown: bool,
+}
 
-    let task = connection.and_then(move |session| {
-        let audio_filter = mixer.get_audio_filter();
-        let player = Player::new(session.clone(), audio_filter, move || {
-            (backend)(device)
-        });
+impl Main {
+    fn new(handle: Handle,
+           config: Config,
+           backend: fn(Option<String>) -> Box<Sink>,
+           device: Option<String>,
+           mixer: fn() -> Box<Mixer>) -> Main
+    {
+        Main {
+            handle: handle.clone(),
+            config: config,
+            backend: backend,
+            device: device,
+            mixer: mixer,
+
+            cache: None,
+            connect: Box::new(futures::future::empty()),
+            discovery: None,
+            spirc: None,
+            spirc_task: None,
+            shutdown: false,
+            signal: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(),
+        }
+    }
 
-        let (spirc, task) = Spirc::new(session.clone(), player, mixer);
-        let spirc = RefCell::new(spirc);
+    fn discovery(&mut self) {
+        let name = self.config.name.clone();
+        let device_id = self.config.device_id.clone();
+        self.discovery = Some(discovery(&self.handle, name, device_id).unwrap());
+    }
 
-        let shutting_down = Cell::new(false);
-        ctrlc::set_handler(move || {
-            if shutting_down.get() {
-                warn!("Forced shutdown");
-                exit(1);
-            } else {
-                info!("Shutting down");
-                spirc.borrow_mut().shutdown();
-                shutting_down.set(true);
+    fn credentials(&mut self, credentials: Credentials) {
+        let config = self.config.clone();
+        let handle = self.handle.clone();
+        let connection = Session::connect(config, credentials, self.cache.clone(), handle);
+
+        self.connect = connection;
+        self.spirc = None;
+        let task = mem::replace(&mut self.spirc_task, None);
+        if let Some(task) = task {
+            self.handle.spawn(task);
+        }
+    }
+
+    fn cache(&mut self, cache: Cache) {
+        self.cache = Some(cache);
+    }
+}
+
+impl Future for Main {
+    type Item = ();
+    type Error = ();
+
+    fn poll(&mut self) -> Poll<(), ()> {
+        loop {
+            let mut progress = false;
+
+            if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
+                if let Some(ref spirc) = self.spirc {
+                    spirc.shutdown();
+                }
+                self.credentials(creds);
+
+                progress = true;
             }
-        });
 
-        task.map_err(|()| panic!("spirc error"))
-    });
+            if let Async::Ready(session) = self.connect.poll().unwrap() {
+                self.connect = Box::new(futures::future::empty());
+                let device = self.device.clone();
+                let mixer = (self.mixer)();
+
+                let audio_filter = mixer.get_audio_filter();
+                let backend = self.backend;
+                let player = Player::new(session.clone(), audio_filter, move || {
+                    (backend)(device)
+                });
+
+                let (spirc, spirc_task) = Spirc::new(session, player, mixer);
+                self.spirc = Some(spirc);
+                self.spirc_task = Some(spirc_task);
+
+                progress = true;
+            }
+
+            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
+                if !self.shutdown {
+                    if let Some(ref spirc) = self.spirc {
+                        spirc.shutdown();
+                    }
+                    self.shutdown = true;
+                } else {
+                    return Ok(Async::Ready(()));
+                }
+
+                progress = true;
+            }
+
+            if let Some(ref mut spirc_task) = self.spirc_task {
+                if let Async::Ready(()) = spirc_task.poll().unwrap() {
+                    if self.shutdown {
+                        return Ok(Async::Ready(()));
+                    } else {
+                        panic!("Spirc shut down unexpectedly");
+                    }
+                }
+            }
+
+            if !progress {
+                return Ok(Async::NotReady);
+            }
+        }
+    }
+}
+
+fn main() {
+    let mut core = Core::new().unwrap();
+    let handle = core.handle();
+
+    let args: Vec<String> = std::env::args().collect();
+    let Setup { backend, config, device, cache, enable_discovery, credentials, mixer } = setup(&args);
+
+    let mut task = Main::new(handle, config.clone(), backend, device, mixer);
+    if enable_discovery {
+        task.discovery();
+    }
+    if let Some(credentials) = credentials {
+        task.credentials(credentials);
+    }
+    if let Some(cache) = cache {
+        task.cache(cache);
+    }
 
     core.run(task).unwrap()
 }

+ 6 - 2
src/mercury/mod.rs

@@ -186,8 +186,12 @@ impl MercuryManager {
 
         if cmd == 0xb5 {
             self.lock(|inner| {
-                if let Some(cb) = inner.subscriptions.get(&response.uri) {
-                    cb.send(response).unwrap();
+                use std::collections::hash_map::Entry;
+                if let Entry::Occupied(entry) = inner.subscriptions.entry(response.uri.clone()) {
+                    // TODO: send unsub message
+                    if entry.get().send(response).is_err() {
+                        entry.remove();
+                    }
                 }
             })
         } else if let Some(cb) = pending.callback {

+ 8 - 3
src/mixer/mod.rs

@@ -1,4 +1,5 @@
-pub trait Mixer {
+pub trait Mixer : Send {
+    fn open() -> Self where Self: Sized;
     fn start(&self);
     fn stop(&self);
     fn set_volume(&self, volume: u16);
@@ -15,9 +16,13 @@ pub trait AudioFilter {
 pub mod softmixer;
 use self::softmixer::SoftMixer;
 
-pub fn find<T: AsRef<str>>(name: Option<T>) -> Option<Box<Mixer + Send>> {
+fn mk_sink<M: Mixer + 'static>() -> Box<Mixer> {
+    Box::new(M::open())
+}
+
+pub fn find<T: AsRef<str>>(name: Option<T>) -> Option<fn() -> Box<Mixer>> {
     match name.as_ref().map(AsRef::as_ref) {
-        None | Some("softvol") => Some(Box::new(SoftMixer::new())),
+        None | Some("softvol") => Some(mk_sink::<SoftMixer>),
         _ => None,
     }
 }

+ 4 - 6
src/mixer/softmixer.rs

@@ -4,19 +4,17 @@ use std::sync::atomic::{AtomicUsize, Ordering};
 use super::Mixer;
 use super::AudioFilter;
 
+#[derive(Clone)]
 pub struct SoftMixer {
   volume: Arc<AtomicUsize>
 }
 
-impl SoftMixer {
-    pub fn new() -> SoftMixer {
+impl Mixer for SoftMixer {
+    fn open() -> SoftMixer {
         SoftMixer {
             volume: Arc::new(AtomicUsize::new(0xFFFF))
         }
     }
-}
-
-impl Mixer for SoftMixer {
     fn start(&self) {
     }
     fn stop(&self) {
@@ -45,4 +43,4 @@ impl AudioFilter for SoftVolumeApplier {
             }
         }
     }
-}
+}

+ 8 - 0
src/player.rs

@@ -44,6 +44,8 @@ impl Player {
         let (cmd_tx, cmd_rx) = std::sync::mpsc::channel();
 
         thread::spawn(move || {
+            debug!("new Player[{}]", session.session_id());
+
             let internal = PlayerInternal {
                 session: session,
                 commands: cmd_rx,
@@ -383,6 +385,12 @@ impl PlayerInternal {
     }
 }
 
+impl Drop for PlayerInternal {
+    fn drop(&mut self) {
+        debug!("drop Player[{}]", self.session.session_id());
+    }
+}
+
 #[cfg(not(feature = "with-tremor"))]
 fn vorbis_time_seek_ms<R>(decoder: &mut vorbis::Decoder<R>, ms: i64) -> Result<(), vorbis::VorbisError> where R: Read + Seek {
     decoder.time_seek(ms as f64 / 1000f64)

+ 74 - 18
src/session.rs

@@ -1,14 +1,14 @@
 use crypto::digest::Digest;
 use crypto::sha1::Sha1;
-use futures::Future;
 use futures::sync::mpsc;
-use futures::{Stream, BoxFuture, IntoFuture};
+use futures::{Future, Stream, BoxFuture, IntoFuture, Poll, Async};
 use std::io;
 use std::result::Result;
 use std::str::FromStr;
 use std::sync::{RwLock, Arc, Weak};
 use tokio_core::io::EasyBuf;
 use tokio_core::reactor::{Handle, Remote};
+use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
 
 use apresolve::apresolve_or_fallback;
 use authentication::Credentials;
@@ -69,13 +69,14 @@ pub struct SessionInternal {
     cache: Option<Arc<Cache>>,
 
     handle: Remote,
+
+    session_id: usize,
 }
 
-#[derive(Clone)]
-pub struct Session(pub Arc<SessionInternal>);
+static SESSION_COUNTER : AtomicUsize = ATOMIC_USIZE_INIT;
 
 #[derive(Clone)]
-pub struct SessionWeak(pub Weak<SessionInternal>);
+pub struct Session(pub Arc<SessionInternal>);
 
 pub fn device_id(name: &str) -> String {
     let mut h = Sha1::new();
@@ -102,7 +103,7 @@ impl Session {
         });
 
         let result = authentication.map(move |(transport, reusable_credentials)| {
-            info!("Authenticated !");
+            info!("Authenticated as \"{}\" !", reusable_credentials.username);
             if let Some(ref cache) = cache {
                 cache.save_credentials(&reusable_credentials);
             }
@@ -126,10 +127,9 @@ impl Session {
         let (sink, stream) = transport.split();
 
         let (sender_tx, sender_rx) = mpsc::unbounded();
+        let session_id = SESSION_COUNTER.fetch_add(1, Ordering::Relaxed);
 
-        let sender_task = sender_rx
-            .map_err(|e| -> io::Error { panic!(e) })
-            .forward(sink).map(|_| ());
+        debug!("new Session[{}]", session_id);
 
         let session = Session(Arc::new(SessionInternal {
             config: config,
@@ -149,15 +149,14 @@ impl Session {
             metadata: Lazy::new(),
 
             handle: handle.remote().clone(),
+
+            session_id: session_id,
         }));
 
-        let receiver_task = {
-            let session = session.clone();
-            stream.for_each(move |(cmd, data)| {
-                session.dispatch(cmd, data);
-                Ok(())
-            })
-        };
+        let sender_task = sender_rx
+            .map_err(|e| -> io::Error { panic!(e) })
+            .forward(sink).map(|_| ());
+        let receiver_task = DispatchTask(stream, session.weak());
 
         let task = (receiver_task, sender_task).into_future()
             .map(|((), ())| ()).boxed();
@@ -193,10 +192,18 @@ impl Session {
         self.0.handle.spawn(f)
     }
 
+    fn debug_info(&self) {
+        debug!("Session[{}] strong={} weak={}",
+               self.0.session_id, Arc::strong_count(&self.0), Arc::weak_count(&self.0));
+    }
+
     #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
     fn dispatch(&self, cmd: u8, data: EasyBuf) {
         match cmd {
-            0x4 => self.send_packet(0x49, data.as_ref().to_owned()),
+            0x4 => {
+                self.debug_info();
+                self.send_packet(0x49, data.as_ref().to_owned());
+            },
             0x4a => (),
             0x1b => {
                 let country = String::from_utf8(data.as_ref().to_owned()).unwrap();
@@ -237,10 +244,59 @@ impl Session {
     pub fn weak(&self) -> SessionWeak {
         SessionWeak(Arc::downgrade(&self.0))
     }
+
+    pub fn session_id(&self) -> usize {
+        self.0.session_id
+    }
 }
 
+#[derive(Clone)]
+pub struct SessionWeak(pub Weak<SessionInternal>);
+
 impl SessionWeak {
+    pub fn try_upgrade(&self) -> Option<Session> {
+        self.0.upgrade().map(Session)
+    }
+
     pub fn upgrade(&self) -> Session {
-        Session(self.0.upgrade().expect("Session died"))
+        self.try_upgrade().expect("Session died")
+    }
+}
+
+impl Drop for SessionInternal {
+    fn drop(&mut self) {
+        debug!("drop Session[{}]", self.session_id);
+    }
+}
+
+struct DispatchTask<S>(S, SessionWeak)
+    where S: Stream<Item = (u8, EasyBuf)>;
+
+impl <S> Future for DispatchTask<S>
+    where S: Stream<Item = (u8, EasyBuf)>
+{
+    type Item = ();
+    type Error = S::Error;
+
+    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
+        let session = match self.1.try_upgrade() {
+            Some(session) => session,
+            None => {
+                return Ok(Async::Ready(()))
+            },
+        };
+
+        loop {
+            let (cmd, data) = try_ready!(self.0.poll()).expect("connection closed");
+            session.dispatch(cmd, data);
+        }
+    }
+}
+
+impl <S> Drop for DispatchTask<S>
+    where S: Stream<Item = (u8, EasyBuf)>
+{
+    fn drop(&mut self) {
+        debug!("drop Dispatch");
     }
 }

+ 12 - 2
src/spirc.rs

@@ -17,7 +17,7 @@ use protocol::spirc::{PlayStatus, State, MessageType, Frame, DeviceState};
 
 pub struct SpircTask {
     player: Player,
-    mixer: Box<Mixer + Send>,
+    mixer: Box<Mixer>,
 
     sequence: SeqGenerator<u32>,
 
@@ -31,6 +31,7 @@ pub struct SpircTask {
     end_of_track: BoxFuture<(), oneshot::Canceled>,
 
     shutdown: bool,
+    session: Session,
 }
 
 pub enum SpircCommand {
@@ -110,9 +111,11 @@ fn initial_device_state(name: String, volume: u16) -> DeviceState {
 }
 
 impl Spirc {
-    pub fn new(session: Session, player: Player, mixer: Box<Mixer + Send>)
+    pub fn new(session: Session, player: Player, mixer: Box<Mixer>)
         -> (Spirc, SpircTask)
     {
+        debug!("new Spirc[{}]", session.session_id());
+
         let ident = session.device_id().to_owned();
         let name = session.config().name.clone();
 
@@ -152,6 +155,7 @@ impl Spirc {
             end_of_track: future::empty().boxed(),
 
             shutdown: false,
+            session: session.clone(),
         };
 
         let spirc = Spirc {
@@ -431,6 +435,12 @@ impl SpircTask {
     }
 }
 
+impl Drop for SpircTask {
+    fn drop(&mut self) {
+        debug!("drop Spirc[{}]", self.session.session_id());
+    }
+}
+
 struct CommandSender<'a> {
     spirc: &'a mut SpircTask,
     frame: protocol::spirc::Frame,