]> Untitled Git - lemmy.git/commitdiff
Merge branch 'federation' into dev_1
authorDessalines <tyhou13@gmx.com>
Wed, 5 Feb 2020 17:51:03 +0000 (12:51 -0500)
committerDessalines <tyhou13@gmx.com>
Wed, 5 Feb 2020 17:51:03 +0000 (12:51 -0500)
1  2 
docker/federation-test/docker-compose.yml
server/Cargo.lock
server/Cargo.toml
server/src/api/community.rs
server/src/apub/mod.rs
server/src/apub/puller.rs
server/src/routes/federation.rs
server/src/settings.rs
server/src/websocket/server.rs

index 0000000000000000000000000000000000000000,b7df6e20e83607bcad4512166bdce8f76df352e8..2a8b0fc3262f278c7927b0e7aef2a01ab762c3a6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,74 +1,74 @@@
 -      - "127.0.0.1:8536:8536"
+ version: '3.3'
+ services:
+   lemmy_alpha:
+     image: lemmy-federation-test:latest
+     ports:
 -      - LEMMY_HOSTNAME=localhost:8536
++      - "127.0.0.1:8540:8540"
+     environment:
 -      - LEMMY_FEDERATED_INSTANCE=lemmy_beta:8537
 -      - LEMMY_PORT=8536
++      - LEMMY_HOSTNAME=localhost:8540
+       - LEMMY_DATABASE_URL=postgres://lemmy:password@lemmy_db_alpha:5432/lemmy
+       - LEMMY_JWT_SECRET=changeme
+       - LEMMY_FRONT_END_DIR=/app/dist
+       - LEMMY_FEDERATION_ENABLED=true
 -  lemmy_pictshare_alpha:
 -    image: shtripok/pictshare:latest
 -    ports:
 -      - "127.0.0.1:8540:80"
 -    volumes:
 -      - lemmy_pictshare_alpha:/usr/share/nginx/html/data
 -    restart: always
++      - LEMMY_FEDERATED_INSTANCE=lemmy_beta:8541
++      - LEMMY_PORT=8540
+       - RUST_BACKTRACE=1
+     restart: always
+     depends_on:
+       - lemmy_db_alpha
+   lemmy_db_alpha:
+     image: postgres:12-alpine
+     environment:
+       - POSTGRES_USER=lemmy
+       - POSTGRES_PASSWORD=${LEMMY_DATABASE_PASSWORD}
+       - POSTGRES_DB=lemmy
+     volumes:
+       - lemmy_db_alpha:/var/lib/postgresql/data
+     restart: always
 -      - "127.0.0.1:8537:8537"
++  # lemmy_pictshare_alpha:
++  #   image: shtripok/pictshare:latest
++  #   ports:
++  #     - "127.0.0.1:8550:80"
++  #   volumes:
++  #     - lemmy_pictshare_alpha:/usr/share/nginx/html/data
++  #   restart: always
+   lemmy_beta:
+     image: lemmy-federation-test:latest
+     ports:
 -      - LEMMY_HOSTNAME=localhost:8537
++      - "127.0.0.1:8541:8541"
+     environment:
 -      - LEMMY_FEDERATED_INSTANCE=lemmy_alpha:8536
 -      - LEMMY_PORT=8537
++      - LEMMY_HOSTNAME=localhost:8541
+       - LEMMY_DATABASE_URL=postgres://lemmy:password@lemmy_db_beta:5432/lemmy
+       - LEMMY_JWT_SECRET=changeme
+       - LEMMY_FRONT_END_DIR=/app/dist
+       - LEMMY_FEDERATION_ENABLED=true
 -  lemmy_pictshare_beta:
 -    image: shtripok/pictshare:latest
 -    ports:
 -      - "127.0.0.1:8541:80"
 -    volumes:
 -      - lemmy_pictshare_beta:/usr/share/nginx/html/data
 -    restart: always
++      - LEMMY_FEDERATED_INSTANCE=lemmy_alpha:8540
++      - LEMMY_PORT=8541
+       - RUST_BACKTRACE=1
+     restart: always
+     depends_on:
+       - lemmy_db_beta
+   lemmy_db_beta:
+     image: postgres:12-alpine
+     environment:
+       - POSTGRES_USER=lemmy
+       - POSTGRES_PASSWORD=${LEMMY_DATABASE_PASSWORD}
+       - POSTGRES_DB=lemmy
+     volumes:
+       - lemmy_db_beta:/var/lib/postgresql/data
+     restart: always
 -  lemmy_pictshare_alpha:
++  # lemmy_pictshare_beta:
++  #   image: shtripok/pictshare:latest
++  #   ports:
++  #     - "127.0.0.1:8551:80"
++  #   volumes:
++  #     - lemmy_pictshare_beta:/usr/share/nginx/html/data
++  #   restart: always
+ volumes:
+   lemmy_db_alpha:
 -  lemmy_pictshare_beta:
++  # lemmy_pictshare_alpha:
+   lemmy_db_beta:
++  # lemmy_pictshare_beta:
diff --combined server/Cargo.lock
index dc583d14d757513604f348c7d8df11fe621243cf,3ddedd77eacbabbe2cf95aa5a11b824a0d19d878..125319206ec789f28e249ed9dca09390548154f8
@@@ -10,7 -10,7 +10,7 @@@ dependencies = 
   "activitystreams-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -29,7 -29,7 +29,7 @@@ version = "0.2.0
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
   "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
@@@ -44,7 -44,7 +44,7 @@@ dependencies = 
   "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -158,10 -158,10 +158,10 @@@ dependencies = 
   "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
   "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
   "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@@ -185,7 -185,7 +185,7 @@@ dependencies = 
   "bytestring 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
   "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
 - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
@@@ -315,9 -315,9 +315,9 @@@ dependencies = 
   "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
   "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
   "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
   "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
   "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@@ -378,11 -378,6 +378,11 @@@ dependencies = 
   "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 +[[package]]
 +name = "anyhow"
 +version = "1.0.26"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +
  [[package]]
  name = "arc-swap"
  version = "0.4.4"
@@@ -425,11 -420,6 +425,11 @@@ name = "autocfg
  version = "0.1.7"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
 +[[package]]
 +name = "autocfg"
 +version = "1.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +
  [[package]]
  name = "awc"
  version = "1.0.1"
@@@ -446,9 -436,9 +446,9 @@@ dependencies = 
   "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
   "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
   "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
@@@ -503,7 -493,7 +503,7 @@@ dependencies = 
   "blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "byteorder 1.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)",
 - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -571,11 -561,6 +571,11 @@@ name = "bufstream
  version = "0.1.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
 +[[package]]
 +name = "bumpalo"
 +version = "3.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +
  [[package]]
  name = "byte-tools"
  version = "0.3.1"
@@@ -586,6 -571,16 +586,16 @@@ 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)",
+  "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+  "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "bytes"
  version = "0.5.3"
@@@ -622,8 -617,8 +632,8 @@@ name = "chrono
  version = "0.4.10"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
 - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
   "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
@@@ -646,11 -641,37 +656,37 @@@ dependencies = 
   "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
   "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
   "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ [[package]]
+ name = "cookie"
+ version = "0.12.0"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
+ [[package]]
+ name = "cookie_store"
+ version = "0.7.0"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+  "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+  "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+  "publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
++ "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
+  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+  "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "copyless"
  version = "0.1.4"
@@@ -686,6 -707,45 +722,45 @@@ dependencies = 
   "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
+ [[package]]
+ name = "crossbeam-deque"
+ version = "0.7.2"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+  "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
+ [[package]]
+ name = "crossbeam-epoch"
+ version = "0.8.0"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "autocfg 0.1.7 (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.7.0 (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.3 (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 = "crossbeam-utils"
  version = "0.7.0"
@@@ -698,57 -758,68 +773,57 @@@ dependencies = 
  
  [[package]]
  name = "darling"
 -version = "0.9.0"
 +version = "0.10.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 - "darling_macro 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
  name = "darling_core"
 -version = "0.9.0"
 +version = "0.10.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
   "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
   "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 - "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)",
 - "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
 + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
 + "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
  name = "darling_macro"
 -version = "0.9.0"
 +version = "0.10.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "darling_core 0.9.0 (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)",
 + "darling_core 0.10.2 (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.13 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
  name = "derive_builder"
 -version = "0.7.2"
 +version = "0.9.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 - "derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
 - "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)",
 + "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "proc-macro2 1.0.7 (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.13 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
  name = "derive_builder_core"
 -version = "0.5.0"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -dependencies = [
 - "darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 - "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 = "derive_more"
 -version = "0.14.1"
 +version = "0.9.0"
  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)",
 - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
 - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "proc-macro2 1.0.7 (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.13 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -930,10 -1001,18 +1005,18 @@@ dependencies = 
   "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
   "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
 - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
   "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
+ [[package]]
+ name = "error-chain"
+ version = "0.12.1"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "failure"
  version = "0.1.6"
@@@ -1015,6 -1094,11 +1098,11 @@@ name = "fuchsia-zircon-sys
  version = "0.3.3"
  source = "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 = "futures"
  version = "0.3.1"
@@@ -1043,6 -1127,15 +1131,15 @@@ name = "futures-core
  version = "0.3.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
+ [[package]]
+ name = "futures-cpupool"
+ version = "0.1.8"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
+  "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "futures-executor"
  version = "0.3.1"
@@@ -1123,6 -1216,23 +1220,23 @@@ dependencies = 
   "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
+ [[package]]
+ name = "h2"
+ version = "0.1.26"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+  "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
+  "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)",
+  "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+  "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+  "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+  "string 0.2.1 (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 = "h2"
  version = "0.2.1"
@@@ -1182,6 -1292,16 +1296,16 @@@ name = "htmlescape
  version = "0.3.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
+ [[package]]
+ name = "http"
+ version = "0.1.21"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
+  "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+  "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "http"
  version = "0.2.0"
@@@ -1192,6 -1312,17 +1316,17 @@@ dependencies = 
   "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
+ [[package]]
+ name = "http-body"
+ version = "0.1.0"
+ 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)",
+  "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+  "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "httparse"
  version = "1.3.4"
@@@ -1205,11 -1336,62 +1340,62 @@@ dependencies = 
   "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
+ [[package]]
+ name = "hyper"
+ version = "0.12.35"
+ 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)",
+  "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+  "h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+  "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+  "http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+  "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+  "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
+  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+  "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
+  "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+  "tokio-executor 0.1.9 (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.11 (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.17 (registry+https://github.com/rust-lang/crates.io-index)",
+  "tokio-timer 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+  "want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
+ [[package]]
+ name = "hyper-tls"
+ version = "0.3.2"
+ 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)",
+  "hyper 0.12.35 (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 = "ident_case"
  version = "1.0.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
+ [[package]]
+ name = "idna"
+ version = "0.1.5"
+ 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.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "idna"
  version = "0.2.0"
@@@ -1257,25 -1439,18 +1443,25 @@@ name = "itoa
  version = "0.4.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
 +[[package]]
 +name = "js-sys"
 +version = "0.3.35"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
  [[package]]
  name = "jsonwebtoken"
 -version = "6.0.1"
 +version = "7.0.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
 - "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
 - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
 + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
 + "simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -1322,16 -1497,17 +1508,17 @@@ dependencies = 
   "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
   "hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
   "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 - "jsonwebtoken 6.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "jsonwebtoken 7.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
   "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
   "lettre_email 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
 - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)",
 - "rss 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "rss 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
 + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
   "strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
   "strum_macros 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
@@@ -1350,7 -1526,7 +1537,7 @@@ dependencies = 
   "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
 + "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -1426,6 -1602,11 +1613,11 @@@ name = "matches
  version = "0.1.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
+ [[package]]
+ name = "maybe-uninit"
+ version = "2.0.0"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
  [[package]]
  name = "memchr"
  version = "0.1.11"
@@@ -1439,6 -1620,14 +1631,14 @@@ name = "memchr
  version = "2.2.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
+ [[package]]
+ name = "memoffset"
+ version = "0.5.3"
+ 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 = "migrations_internals"
  version = "1.4.0"
@@@ -1570,23 -1759,13 +1770,23 @@@ dependencies = 
   "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 +[[package]]
 +name = "num-bigint"
 +version = "0.2.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
  [[package]]
  name = "num-integer"
 -version = "0.1.41"
 +version = "0.1.42"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
 - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
 + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -1594,15 -1773,15 +1794,15 @@@ name = "num-traits
  version = "0.1.43"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
  name = "num-traits"
 -version = "0.2.10"
 +version = "0.2.11"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
 + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -1649,6 -1828,16 +1849,16 @@@ dependencies = 
   "vcpkg 0.2.8 (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.2 (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"
  version = "0.10.0"
@@@ -1658,6 -1847,20 +1868,20 @@@ dependencies = 
   "parking_lot_core 0.7.0 (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.66 (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.13 (registry+https://github.com/rust-lang/crates.io-index)",
+  "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "parking_lot_core"
  version = "0.7.0"
@@@ -1671,16 -1874,11 +1895,21 @@@ dependencies = 
   "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 +[[package]]
 +name = "pem"
 +version = "0.7.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
+ [[package]]
+ name = "percent-encoding"
+ version = "1.0.1"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
  [[package]]
  name = "percent-encoding"
  version = "2.1.0"
@@@ -1747,6 -1945,14 +1976,6 @@@ name = "proc-macro-nested
  version = "0.1.3"
  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.7"
@@@ -1755,6 -1961,18 +1984,18 @@@ dependencies = 
   "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ [[package]]
+ name = "publicsuffix"
+ version = "1.5.4"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "error-chain 0.12.1 (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)",
++ "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "quick-error"
  version = "1.2.3"
@@@ -1762,13 -1980,24 +2003,13 @@@ source = "registry+https://github.com/r
  
  [[package]]
  name = "quick-xml"
 -version = "0.14.0"
 +version = "0.17.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
   "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)",
 - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
 - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
   "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"
@@@ -1819,7 -2048,7 +2060,7 @@@ dependencies = 
  
  [[package]]
  name = "rand"
 -version = "0.7.2"
 +version = "0.7.3"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
   "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@@ -1959,13 -2188,13 +2200,13 @@@ dependencies = 
  
  [[package]]
  name = "regex"
 -version = "1.3.1"
 +version = "1.3.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
   "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
   "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 - "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
 - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
 + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -1975,7 -2204,7 +2216,7 @@@ source = "registry+https://github.com/r
  
  [[package]]
  name = "regex-syntax"
 -version = "0.6.12"
 +version = "0.6.14"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
  [[package]]
@@@ -1986,6 -2215,39 +2227,39 @@@ dependencies = 
   "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ [[package]]
+ name = "reqwest"
+ version = "0.9.24"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
+  "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
+  "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+  "cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+  "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)",
+  "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+  "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
+  "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+  "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)",
+  "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+  "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
+  "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+  "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+  "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
++ "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
+  "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+  "time 0.1.42 (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.9 (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.17 (registry+https://github.com/rust-lang/crates.io-index)",
+  "tokio-timer 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+  "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "resolv-conf"
  version = "0.6.2"
@@@ -1997,25 -2259,25 +2271,25 @@@ dependencies = 
  
  [[package]]
  name = "ring"
 -version = "0.14.6"
 +version = "0.16.9"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
   "cc 1.0.48 (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.66 (registry+https://github.com/rust-lang/crates.io-index)",
   "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
 - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
   "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
  name = "rss"
 -version = "1.8.0"
 +version = "1.9.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
 - "derive_builder 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
 - "quick-xml 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "derive_builder 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 + "quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -2126,7 -2388,7 +2400,7 @@@ dependencies = 
   "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
   "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
 - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
@@@ -2138,7 -2400,7 +2412,7 @@@ dependencies = 
   "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
 - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
   "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
@@@ -2165,7 -2427,7 +2439,7 @@@ dependencies = 
  
  [[package]]
  name = "serde_json"
 -version = "1.0.44"
 +version = "1.0.45"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
   "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@@ -2182,6 -2444,17 +2456,17 @@@ dependencies = 
   "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
+ [[package]]
+ name = "serde_urlencoded"
+ version = "0.5.5"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
+  "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "serde_urlencoded"
  version = "0.6.1"
@@@ -2200,7 -2473,7 +2485,7 @@@ source = "registry+https://github.com/r
  
  [[package]]
  name = "sha2"
 -version = "0.8.0"
 +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)",
@@@ -2218,21 -2491,19 +2503,29 @@@ dependencies = 
   "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 +[[package]]
 +name = "simple_asn1"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num-traits 0.2.11 (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.13"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "smallvec"
  version = "1.1.0"
@@@ -2249,11 -2520,6 +2542,11 @@@ dependencies = 
   "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 +[[package]]
 +name = "sourcefile"
 +version = "0.1.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +
  [[package]]
  name = "spin"
  version = "0.5.2"
@@@ -2264,6 -2530,14 +2557,14 @@@ name = "static_assertions
  version = "0.3.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
+ [[package]]
+ name = "string"
+ version = "0.2.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)",
+ ]
  [[package]]
  name = "strsim"
  version = "0.5.2"
@@@ -2271,7 -2545,7 +2572,7 @@@ source = "registry+https://github.com/r
  
  [[package]]
  name = "strsim"
 -version = "0.7.0"
 +version = "0.9.3"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
  [[package]]
@@@ -2290,6 -2564,16 +2591,6 @@@ dependencies = 
   "syn 1.0.13 (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.13"
@@@ -2318,7 -2602,7 +2619,7 @@@ source = "registry+https://github.com/r
  dependencies = [
   "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
   "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
 - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "rand 0.7.3 (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)",
@@@ -2369,7 -2653,7 +2670,7 @@@ dependencies = 
  
  [[package]]
  name = "thread_local"
 -version = "0.3.6"
 +version = "1.0.1"
  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)",
@@@ -2393,6 -2677,24 +2694,24 @@@ dependencies = 
   "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.21 (registry+https://github.com/rust-lang/crates.io-index)",
+  "num_cpus 1.11.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.9 (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.11 (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.17 (registry+https://github.com/rust-lang/crates.io-index)",
+  "tokio-timer 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "tokio"
  version = "0.2.9"
@@@ -2413,6 -2715,111 +2732,111 @@@ dependencies = 
   "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
+ [[package]]
+ name = "tokio-buf"
+ 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)",
+  "either 1.5.3 (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-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.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
+ [[package]]
+ name = "tokio-executor"
+ version = "0.1.9"
+ 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-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.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)",
+  "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.21 (registry+https://github.com/rust-lang/crates.io-index)",
+  "num_cpus 1.11.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.9 (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.21 (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.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
+ [[package]]
+ name = "tokio-threadpool"
+ version = "0.1.17"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "crossbeam-deque 0.7.2 (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.11.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.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
+ [[package]]
+ name = "tokio-timer"
+ version = "0.2.12"
+ 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.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "tokio-util"
  version = "0.2.0"
@@@ -2446,7 -2853,7 +2870,7 @@@ dependencies = 
   "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)",
 + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
   "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
   "tokio 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@@ -2471,6 -2878,19 +2895,19 @@@ dependencies = 
   "trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
+ [[package]]
+ name = "try-lock"
+ version = "0.2.2"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ [[package]]
+ name = "try_from"
+ version = "0.3.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)",
+ ]
  [[package]]
  name = "typenum"
  version = "1.11.2"
@@@ -2505,6 -2925,11 +2942,6 @@@ name = "unicode-segmentation
  version = "1.6.0"
  source = "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"
@@@ -2512,9 -2937,19 +2949,19 @@@ source = "registry+https://github.com/r
  
  [[package]]
  name = "untrusted"
 -version = "0.6.2"
 +version = "0.7.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
+ [[package]]
+ name = "url"
+ version = "1.7.2"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+  "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+  "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ ]
  [[package]]
  name = "url"
  version = "2.1.0"
@@@ -2581,95 -3016,21 +3028,105 @@@ name = "version_check
  version = "0.9.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
+ [[package]]
+ name = "want"
+ version = "0.2.0"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ dependencies = [
+  "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)",
+  "try-lock 0.2.2 (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 = "wasm-bindgen"
 +version = "0.2.58"
 +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)",
 + "wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
 +[[package]]
 +name = "wasm-bindgen-backend"
 +version = "0.2.58"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "bumpalo 3.1.2 (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)",
 + "proc-macro2 1.0.7 (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.13 (registry+https://github.com/rust-lang/crates.io-index)",
 + "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
 +[[package]]
 +name = "wasm-bindgen-macro"
 +version = "0.2.58"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
 +[[package]]
 +name = "wasm-bindgen-macro-support"
 +version = "0.2.58"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "proc-macro2 1.0.7 (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.13 (registry+https://github.com/rust-lang/crates.io-index)",
 + "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 + "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
 +[[package]]
 +name = "wasm-bindgen-shared"
 +version = "0.2.58"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +
 +[[package]]
 +name = "wasm-bindgen-webidl"
 +version = "0.2.58"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
 + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
 + "proc-macro2 1.0.7 (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.13 (registry+https://github.com/rust-lang/crates.io-index)",
 + "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 + "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
 +[[package]]
 +name = "web-sys"
 +version = "0.3.35"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
 + "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
 + "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 + "wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
 +[[package]]
 +name = "weedle"
 +version = "0.10.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +dependencies = [
 + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
 +]
 +
  [[package]]
  name = "widestring"
  version = "0.4.0"
@@@ -2780,14 -3141,12 +3237,14 @@@ dependencies = 
  "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
  "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
  "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
 +"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c"
  "checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff"
  "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
  "checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
  "checksum async-trait 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c8df72488e87761e772f14ae0c2480396810e51b2c2ade912f97f0f7e5b95e3c"
  "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
  "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
 +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
  "checksum awc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7601d4d1d7ef2335d6597a41b5fe069f6ab799b85f53565ab390e7b7065aac5"
  "checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea"
  "checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
  "checksum brotli-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd"
  "checksum brotli2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e"
  "checksum bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
 +"checksum bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4"
  "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 bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10004c15deb332055f7a4a208190aed362cf9a7c2f6ab70a305fba50e1105f38"
  "checksum bytestring 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b24c107a4432e408d2caa58d3f5e763b219236221406ea58a4076b62343a039d"
  "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
  "checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
  "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
  "checksum config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3"
+ "checksum cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5"
+ "checksum cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46750b3f362965f197996c4448e4a0935e791bf7d6631bfce9ee0af3d24c919c"
  "checksum copyless 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff9c56c9fb2a49c05ef0e431485a22400af20d33226dc0764d891d09e724127"
  "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 crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
  "checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c"
+ "checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
+ "checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
+ "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 crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
 -"checksum darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfbcb0c5961907597a7d1148e3af036268f2b773886b8bb3eeb1e1281d3d3d6"
 -"checksum darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6afc018370c3bff3eb51f89256a6bdb18b4fdcda72d577982a14954a7a0b402c"
 -"checksum darling_macro 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6d8dac1c6f1d29a41c4712b4400f878cb4fcc4c7628f298dd75038e024998d1"
 -"checksum derive_builder 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ac53fa6a3cda160df823a9346442525dcaf1e171999a1cf23e67067e4fd64d4"
 -"checksum derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0288a23da9333c246bb18c143426074a6ae96747995c5819d2947b64cd942b37"
 -"checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839"
 +"checksum darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
 +"checksum darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
 +"checksum darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
 +"checksum derive_builder 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
 +"checksum derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
  "checksum derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8"
  "checksum diesel 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d7cc03b910de9935007861dce440881f69102aaaedfd4bc5a6f40340ca5840c"
  "checksum diesel_derives 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
  "checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28"
  "checksum enum-as-inner 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "900a6c7fbe523f4c2884eaf26b57b81bb69b6810a01a236390a7ac021d09492e"
  "checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+ "checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9"
  "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 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 futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
  "checksum futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987"
  "checksum futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86"
  "checksum futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866"
+ "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
  "checksum futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231"
  "checksum futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff"
  "checksum futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764"
  "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
  "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
  "checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
+ "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462"
  "checksum h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1"
  "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
  "checksum hermit-abi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f629dc602392d3ec14bfc8a09b5e644d7ffd725102b48b81e59f90f2633621d7"
  "checksum hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0849d73a64ec77d1c8354aff489cf31943c4b4d3716de1eabfba572c70fde530"
  "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e"
  "checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
+ "checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0"
  "checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b"
+ "checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d"
  "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
  "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
+ "checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6"
+ "checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f"
  "checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+ "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
  "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
  "checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2"
  "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.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1"
  "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
 -"checksum jsonwebtoken 6.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a81d1812d731546d2614737bee92aa071d37e9afa1409bc374da9e5e70e70b22"
 +"checksum js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9"
 +"checksum jsonwebtoken 7.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7577c6272114f9a75da574d2497509d7a73c25cd005a2df35e4a1845a6522ea4"
  "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
  "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
  "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
  "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 matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
+ "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
  "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
  "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
+ "checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
  "checksum migrations_internals 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8089920229070f914b9ce9b07ef60e175b2b9bc2d35c3edd8bf4433604e863b9"
  "checksum migrations_macros 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "719ef0bc7f531428764c9b70661c14abd50a7f3d21f355752d9985aa21251c9e"
  "checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf"
  "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
  "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
  "checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca"
 -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
 +"checksum num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
 +"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
  "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
 -"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
 +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
  "checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
  "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
  "checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585"
  "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
  "checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f"
  "checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
+ "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 parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1"
 +"checksum pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1581760c757a756a41f0ee3ff01256227bdf64cb752839779b95ffb01c59793"
+ "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
  "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
  "checksum pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "94b90146c7216e4cb534069fb91366de4ea0ea353105ee45ed297e2d1619e469"
  "checksum pin-project-internal 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "44ca92f893f0656d3cba8158dd0f2b99b94de256a4a54e870bd6922fcc6c8355"
  "checksum pq-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda"
  "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
  "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e"
 -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
  "checksum proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc"
+ "checksum publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b"
  "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 -"checksum quick-xml 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a8b2062cd4735d683121dbd525f5961226936229b0ac6bbbc40b34155744a41"
 -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
 +"checksum quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fe1e430bdcf30c9fdc25053b9c459bb1a4672af4617b6c783d7d91dc17c6bbb0"
  "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
  "checksum r2d2 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1497e40855348e4a8a40767d8e55174bce1e445a3ac9254ad44ad468ee0485af"
  "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
  "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 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
  "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 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 regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
 -"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
 +"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8"
  "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
 -"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
 +"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
  "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
+ "checksum reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab"
  "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb"
 -"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c"
 -"checksum rss 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0706a43e890fbaf1714d495d12f69a7b34b70c6e903586d70311c2ce15ffe67"
 +"checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac"
 +"checksum rss 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "99979205510c60f80a119dedbabd0b8426517384edf205322f8bcd51796bcef9"
  "checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
  "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
  "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
  "checksum serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8"
  "checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
  "checksum serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "67f7d2e9edc3523a9c8ec8cd6ec481b3a27810aafee3e625d311febd3e656b4c"
 -"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
 +"checksum serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b"
  "checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5"
+ "checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a"
  "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
  "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
 -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
 +"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0"
  "checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
 +"checksum simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618"
  "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
+ "checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
  "checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4"
  "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85"
 +"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
  "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
  "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
+ "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d"
  "checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c"
 -"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
 +"checksum strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
  "checksum strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "530efb820d53b712f4e347916c5e7ed20deb76a4f0457943b3182fb889b06d2c"
  "checksum strum_macros 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6e163a520367c465f59e0a61a23cfae3b10b6546d78b6f672a382be79f7110"
 -"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
  "checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8"
  "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
  "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
  "checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef"
  "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
  "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
 -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
 +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
  "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865"
  "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 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ffa2fdcfa937b20cb3c822a635ceecd5fc1a27a6a474527e5516aa24b8c8820a"
+ "checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46"
+ "checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443"
+ "checksum tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ca6df436c42b0c3330a82d855d2ef017cd793090ad550a6bc2184f4b933532ab"
+ "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926"
+ "checksum tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146"
+ "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.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c32ffea4827978e9aa392d2f743d973c1dfa3730a2ed3f22ce1e6984da848c"
+ "checksum tokio-timer 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1739638e364e558128461fc1ad84d997702c8e31c2e6b18fb99842268199e827"
  "checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930"
  "checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf"
  "checksum trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2a7f3a2ab8a919f5eca52a468866a67ed7d3efa265d48a652a9a3452272b413f"
  "checksum trust-dns-resolver 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6f90b1502b226f8b2514c6d5b37bafa8c200d7ca4102d57dc36ee0f3b7a04a2f"
+ "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
+ "checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b"
  "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
  "checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
  "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
  "checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf"
  "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
 -"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 untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
 +"checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece"
+ "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
  "checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
  "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
  "checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
  "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
  "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
  "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
+ "checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230"
  "checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
 +"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c"
 +"checksum wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45"
 +"checksum wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3"
 +"checksum wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668"
 +"checksum wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601"
 +"checksum wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d"
 +"checksum web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b"
 +"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164"
  "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"
diff --combined server/Cargo.toml
index 939d038ed59dce8594ed945432ea3559b0ff5466,3d198d19d94186059f102cac95107afa66eacd8e..828230572fba57264ed16cefc06bff5d30c668d8
@@@ -12,7 -12,7 +12,7 @@@ bcrypt = "0.6.1
  activitypub = "0.2.0"
  chrono = { version = "0.4.7", features = ["serde"] }
  failure = "0.1.5"
 -serde_json = { version = "1.0.40", features = ["preserve_order"]}
 +serde_json = { version = "1.0.45", features = ["preserve_order"]}
  serde = { version = "1.0.94", features = ["derive"] }
  actix = "0.9.0"
  actix-web = "2.0.0"
@@@ -20,16 -20,17 +20,17 @@@ actix-files = "0.2.1
  actix-web-actors = "2.0.0"
  actix-rt = "1.0.0"
  env_logger = "0.7.1"
 -rand = "0.7.0"
 +rand = "0.7.3"
  strum = "0.17.1"
  strum_macros = "0.17.1"
 -jsonwebtoken = "6.0.1"
 -regex = "1.1.9"
 +jsonwebtoken = "7.0.1"
 +regex = "1.3.4"
  lazy_static = "1.3.0"
  lettre = "0.9.2"
  lettre_email = "0.9.2"
 -sha2 = "0.8.0"
 -rss = "1.8.0"
 +sha2 = "0.8.1"
 +rss = "1.9.0"
  htmlescape = "0.3.1"
  config = "0.10.1"
  hjson = "0.8.2"
+ reqwest = "0.9.24"
index 936e54cda33212c9fbb7de0cdf6f20fc7db0caaf,1854dd14e6e4262ac33e5b3ffc72690135089e83..7d5f8d613dc9e095dbfc693c0f99bf565f02b95d
@@@ -5,16 -5,16 +5,16 @@@ use std::str::FromStr
  #[derive(Serialize, Deserialize)]
  pub struct GetCommunity {
    id: Option<i32>,
-   name: Option<String>,
+   pub name: Option<String>,
    auth: Option<String>,
  }
  
  #[derive(Serialize, Deserialize)]
  pub struct GetCommunityResponse {
 -  pub op: String,
    pub community: CommunityView,
-   moderators: Vec<CommunityModeratorView>,
-   admins: Vec<UserView>,
+   pub moderators: Vec<CommunityModeratorView>,
+   pub admins: Vec<UserView>,
 +  pub online: usize,
  }
  
  #[derive(Serialize, Deserialize)]
@@@ -29,6 -29,7 +29,6 @@@ pub struct CreateCommunity 
  
  #[derive(Serialize, Deserialize, Clone)]
  pub struct CommunityResponse {
 -  op: String,
    pub community: CommunityView,
  }
  
@@@ -40,9 -41,10 +40,9 @@@ pub struct ListCommunities 
    auth: Option<String>,
  }
  
- #[derive(Serialize, Deserialize)]
+ #[derive(Serialize, Deserialize, Debug)]
  pub struct ListCommunitiesResponse {
-   communities: Vec<CommunityView>,
 -  pub op: String,
+   pub communities: Vec<CommunityView>,
  }
  
  #[derive(Serialize, Deserialize, Clone)]
@@@ -57,6 -59,7 +57,6 @@@ pub struct BanFromCommunity 
  
  #[derive(Serialize, Deserialize)]
  pub struct BanFromCommunityResponse {
 -  op: String,
    user: UserView,
    banned: bool,
  }
@@@ -71,6 -74,7 +71,6 @@@ pub struct AddModToCommunity 
  
  #[derive(Serialize, Deserialize)]
  pub struct AddModToCommunityResponse {
 -  op: String,
    moderators: Vec<CommunityModeratorView>,
  }
  
@@@ -103,6 -107,7 +103,6 @@@ pub struct GetFollowedCommunities 
  
  #[derive(Serialize, Deserialize)]
  pub struct GetFollowedCommunitiesResponse {
 -  op: String,
    communities: Vec<CommunityFollowerView>,
  }
  
@@@ -136,19 -141,19 +136,19 @@@ impl Perform<GetCommunityResponse> for 
            data.name.to_owned().unwrap_or_else(|| "main".to_string()),
          ) {
            Ok(community) => community.id,
 -          Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
 +          Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
          }
        }
      };
  
      let community_view = match CommunityView::read(&conn, community_id, user_id) {
        Ok(community) => community,
 -      Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
 +      Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
      };
  
      let moderators = match CommunityModeratorView::for_community(&conn, community_id) {
        Ok(moderators) => moderators,
 -      Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
 +      Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
      };
  
      let site_creator_id = Site::read(&conn, 1)?.creator_id;
  
      // Return the jwt
      Ok(GetCommunityResponse {
 -      op: self.op.to_string(),
        community: community_view,
        moderators,
        admins,
 +      online: 0,
      })
    }
  }
@@@ -173,28 -178,21 +173,28 @@@ impl Perform<CommunityResponse> for Ope
  
      let claims = match Claims::decode(&data.auth) {
        Ok(claims) => claims.claims,
 -      Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
 +      Err(_e) => return Err(APIError::err("not_logged_in").into()),
      };
  
 -    if has_slurs(&data.name)
 -      || has_slurs(&data.title)
 -      || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap()))
 -    {
 -      return Err(APIError::err(&self.op, "no_slurs").into());
 +    if let Err(slurs) = slur_check(&data.name) {
 +      return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
 +    }
 +
 +    if let Err(slurs) = slur_check(&data.title) {
 +      return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
 +    }
 +
 +    if let Some(description) = &data.description {
 +      if let Err(slurs) = slur_check(description) {
 +        return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
 +      }
      }
  
      let user_id = claims.id;
  
      // Check for a site ban
      if UserView::read(&conn, user_id)?.banned {
 -      return Err(APIError::err(&self.op, "site_ban").into());
 +      return Err(APIError::err("site_ban").into());
      }
  
      // When you create a community, make sure the user becomes a moderator and a follower
  
      let inserted_community = match Community::create(&conn, &community_form) {
        Ok(community) => community,
 -      Err(_e) => return Err(APIError::err(&self.op, "community_already_exists").into()),
 +      Err(_e) => return Err(APIError::err("community_already_exists").into()),
      };
  
      let community_moderator_form = CommunityModeratorForm {
      let _inserted_community_moderator =
        match CommunityModerator::join(&conn, &community_moderator_form) {
          Ok(user) => user,
 -        Err(_e) => {
 -          return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
 -        }
 +        Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
        };
  
      let community_follower_form = CommunityFollowerForm {
      let _inserted_community_follower =
        match CommunityFollower::follow(&conn, &community_follower_form) {
          Ok(user) => user,
 -        Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()),
 +        Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
        };
  
      let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
  
      Ok(CommunityResponse {
 -      op: self.op.to_string(),
        community: community_view,
      })
    }
@@@ -249,30 -250,20 +249,30 @@@ impl Perform<CommunityResponse> for Ope
    fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
      let data: &EditCommunity = &self.data;
  
 -    if has_slurs(&data.name) || has_slurs(&data.title) {
 -      return Err(APIError::err(&self.op, "no_slurs").into());
 +    if let Err(slurs) = slur_check(&data.name) {
 +      return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
 +    }
 +
 +    if let Err(slurs) = slur_check(&data.title) {
 +      return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
 +    }
 +
 +    if let Some(description) = &data.description {
 +      if let Err(slurs) = slur_check(description) {
 +        return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
 +      }
      }
  
      let claims = match Claims::decode(&data.auth) {
        Ok(claims) => claims.claims,
 -      Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
 +      Err(_e) => return Err(APIError::err("not_logged_in").into()),
      };
  
      let user_id = claims.id;
  
      // Check for a site ban
      if UserView::read(&conn, user_id)?.banned {
 -      return Err(APIError::err(&self.op, "site_ban").into());
 +      return Err(APIError::err("site_ban").into());
      }
  
      // Verify its a mod
      );
      editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
      if !editors.contains(&user_id) {
 -      return Err(APIError::err(&self.op, "no_community_edit_allowed").into());
 +      return Err(APIError::err("no_community_edit_allowed").into());
      }
  
      let community_form = CommunityForm {
  
      let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
        Ok(community) => community,
 -      Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_community").into()),
 +      Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
      };
  
      // Mod tables
      let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
  
      Ok(CommunityResponse {
 -      op: self.op.to_string(),
        community: community_view,
      })
    }
@@@ -362,7 -354,10 +362,7 @@@ impl Perform<ListCommunitiesResponse> f
        .list()?;
  
      // Return the jwt
 -    Ok(ListCommunitiesResponse {
 -      op: self.op.to_string(),
 -      communities,
 -    })
 +    Ok(ListCommunitiesResponse { communities })
    }
  }
  
@@@ -372,7 -367,7 +372,7 @@@ impl Perform<CommunityResponse> for Ope
  
      let claims = match Claims::decode(&data.auth) {
        Ok(claims) => claims.claims,
 -      Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
 +      Err(_e) => return Err(APIError::err("not_logged_in").into()),
      };
  
      let user_id = claims.id;
      if data.follow {
        match CommunityFollower::follow(&conn, &community_follower_form) {
          Ok(user) => user,
 -        Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()),
 +        Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
        };
      } else {
        match CommunityFollower::ignore(&conn, &community_follower_form) {
          Ok(user) => user,
 -        Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()),
 +        Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
        };
      }
  
      let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
  
      Ok(CommunityResponse {
 -      op: self.op.to_string(),
        community: community_view,
      })
    }
@@@ -408,7 -404,7 +408,7 @@@ impl Perform<GetFollowedCommunitiesResp
  
      let claims = match Claims::decode(&data.auth) {
        Ok(claims) => claims.claims,
 -      Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
 +      Err(_e) => return Err(APIError::err("not_logged_in").into()),
      };
  
      let user_id = claims.id;
      let communities: Vec<CommunityFollowerView> =
        match CommunityFollowerView::for_user(&conn, user_id) {
          Ok(communities) => communities,
 -        Err(_e) => return Err(APIError::err(&self.op, "system_err_login").into()),
 +        Err(_e) => return Err(APIError::err("system_err_login").into()),
        };
  
      // Return the jwt
 -    Ok(GetFollowedCommunitiesResponse {
 -      op: self.op.to_string(),
 -      communities,
 -    })
 +    Ok(GetFollowedCommunitiesResponse { communities })
    }
  }
  
@@@ -430,7 -429,7 +430,7 @@@ impl Perform<BanFromCommunityResponse> 
  
      let claims = match Claims::decode(&data.auth) {
        Ok(claims) => claims.claims,
 -      Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
 +      Err(_e) => return Err(APIError::err("not_logged_in").into()),
      };
  
      let user_id = claims.id;
      if data.ban {
        match CommunityUserBan::ban(&conn, &community_user_ban_form) {
          Ok(user) => user,
 -        Err(_e) => return Err(APIError::err(&self.op, "community_user_already_banned").into()),
 +        Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
        };
      } else {
        match CommunityUserBan::unban(&conn, &community_user_ban_form) {
          Ok(user) => user,
 -        Err(_e) => return Err(APIError::err(&self.op, "community_user_already_banned").into()),
 +        Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
        };
      }
  
      let user_view = UserView::read(&conn, data.user_id)?;
  
      Ok(BanFromCommunityResponse {
 -      op: self.op.to_string(),
        user: user_view,
        banned: data.ban,
      })
@@@ -483,7 -483,7 +483,7 @@@ impl Perform<AddModToCommunityResponse
  
      let claims = match Claims::decode(&data.auth) {
        Ok(claims) => claims.claims,
 -      Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
 +      Err(_e) => return Err(APIError::err("not_logged_in").into()),
      };
  
      let user_id = claims.id;
      if data.added {
        match CommunityModerator::join(&conn, &community_moderator_form) {
          Ok(user) => user,
 -        Err(_e) => {
 -          return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
 -        }
 +        Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
        };
      } else {
        match CommunityModerator::leave(&conn, &community_moderator_form) {
          Ok(user) => user,
 -        Err(_e) => {
 -          return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
 -        }
 +        Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
        };
      }
  
  
      let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
  
 -    Ok(AddModToCommunityResponse {
 -      op: self.op.to_string(),
 -      moderators,
 -    })
 +    Ok(AddModToCommunityResponse { moderators })
    }
  }
  
@@@ -526,7 -533,7 +526,7 @@@ impl Perform<GetCommunityResponse> for 
  
      let claims = match Claims::decode(&data.auth) {
        Ok(claims) => claims.claims,
 -      Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
 +      Err(_e) => return Err(APIError::err("not_logged_in").into()),
      };
  
      let user_id = claims.id;
  
      // Make sure user is the creator, or an admin
      if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
 -      return Err(APIError::err(&self.op, "not_an_admin").into());
 +      return Err(APIError::err("not_an_admin").into());
      }
  
      let community_form = CommunityForm {
  
      let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
        Ok(community) => community,
 -      Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_community").into()),
 +      Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
      };
  
      // You also have to re-do the community_moderator table, reordering it.
        let _inserted_community_moderator =
          match CommunityModerator::join(&conn, &community_moderator_form) {
            Ok(user) => user,
 -          Err(_e) => {
 -            return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
 -          }
 +          Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
          };
      }
  
  
      let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
        Ok(community) => community,
 -      Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
 +      Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
      };
  
      let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
        Ok(moderators) => moderators,
 -      Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
 +      Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
      };
  
      // Return the jwt
      Ok(GetCommunityResponse {
 -      op: self.op.to_string(),
        community: community_view,
        moderators,
        admins,
 +      online: 0,
      })
    }
  }
diff --combined server/src/apub/mod.rs
index c5a0b2f029868155d5a36f1ce28f2eb390d26bdd,e28d7f1c41dcb79e2c78431c73498d8d9eb99962..7a8b74f289c19bc17e1b6b0aeeaf7125c400fa37
@@@ -1,5 -1,6 +1,6 @@@
  pub mod community;
  pub mod post;
+ pub mod puller;
  pub mod user;
  use crate::Settings;
  
@@@ -22,7 -23,6 +23,7 @@@ mod tests 
        preferred_username: None,
        password_encrypted: "here".into(),
        email: None,
 +      matrix_user_id: None,
        avatar: None,
        published: naive_now(),
        admin: false,
index 0000000000000000000000000000000000000000,8b5c4cc46cdbc9317b3453e75be12154d2640665..4b899a319ad77f8c752980692f6a599b9b47f000
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,98 +1,97 @@@
 -use crate::api::UserOperation;
+ extern crate reqwest;
+ use self::reqwest::Error;
+ use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse};
+ use crate::api::post::GetPosts;
 -    op: UserOperation::GetCommunity.to_string(),
+ use crate::db::community_view::CommunityView;
+ use crate::naive_now;
+ use crate::settings::Settings;
+ use activitypub::actor::Group;
+ // TODO: right now all of the data is requested on demand, for production we will need to store
+ //       things in the local database to not ruin the performance
+ fn fetch_communities_from_instance(domain: &str) -> Result<Vec<CommunityView>, Error> {
+   // TODO: check nodeinfo to make sure we are dealing with a lemmy instance
+   //       -> means we need proper nodeinfo json classes instead of inline generation
+   // TODO: follow pagination (seems like page count is missing?)
+   // TODO: see if there is any standard for discovering remote actors, so we dont have to rely on lemmy apis
+   let communities_uri = format!("http://{}/api/v1/communities/list?sort=Hot", domain);
+   let communities1: ListCommunitiesResponse = reqwest::get(&communities_uri)?.json()?;
+   let mut communities2 = communities1.communities;
+   for c in &mut communities2 {
+     c.name = format_community_name(&c.name, domain);
+   }
+   Ok(communities2)
+ }
+ pub fn get_remote_community_posts(name: String) -> Result<GetPosts, Error> {
+   // TODO: this is for urls like /c/!main@example.com, activitypub exposes it through the outbox
+   //       https://www.w3.org/TR/activitypub/#outbox
+   dbg!(name);
+   unimplemented!()
+ }
+ pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse, Error> {
+   let x: Vec<&str> = identifier.split('@').collect();
+   let name = x[0].replace("!", "");
+   let instance = x[1];
+   let community_uri = format!("http://{}/federation/c/{}", instance, name);
+   let community: Group = reqwest::get(&community_uri)?.json()?;
+   // TODO: looks like a bunch of data is missing from the activitypub response
+   // TODO: i dont think simple numeric ids are going to work, we probably need something like uuids
+   // TODO: why are the Group properties not typed?
+   Ok(GetCommunityResponse {
+     moderators: vec![],
+     admins: vec![],
+     community: CommunityView {
+       id: -1,
+       name: identifier.clone(),
+       title: identifier,
+       description: community.object_props.summary.map(|c| c.to_string()),
+       category_id: -1,
+       creator_id: -1,
+       removed: false,
+       published: naive_now(),     // TODO: community.object_props.published
+       updated: Some(naive_now()), // TODO: community.object_props.updated
+       deleted: false,
+       nsfw: false,
+       creator_name: "".to_string(),
+       creator_avatar: None,
+       category_name: "".to_string(),
+       number_of_subscribers: -1,
+       number_of_posts: -1,
+       number_of_comments: -1,
+       hot_rank: -1,
+       user_id: None,
+       subscribed: None,
+     },
++    online: 0,
+   })
+ }
+ pub fn get_following_instances() -> Result<Vec<String>, Error> {
+   let instance_list = match Settings::get().federated_instance.clone() {
+     Some(f) => vec![f, Settings::get().hostname.clone()],
+     None => vec![Settings::get().hostname.clone()],
+   };
+   Ok(instance_list)
+ }
+ pub fn get_all_communities() -> Result<Vec<CommunityView>, Error> {
+   let mut communities_list: Vec<CommunityView> = vec![];
+   for instance in &get_following_instances()? {
+     communities_list.append(fetch_communities_from_instance(instance)?.as_mut());
+   }
+   Ok(communities_list)
+ }
+ /// If community is on local instance, don't include the @instance part
+ pub fn format_community_name(name: &str, instance: &str) -> String {
+   if instance == Settings::get().hostname {
+     format!("!{}", name)
+   } else {
+     format!("!{}@{}", name, instance)
+   }
+ }
index ea6039d6bd34722d0cb306babe41fd3508562fbc,9667694a0c19dfa346d1d04b6311dbf1714bd1a6..6816f1bc036f622db5a0d603db5b371dbbf4c542
@@@ -1,18 -1,42 +1,42 @@@
 -use crate::api::{Oper, UserOperation};
+ use crate::api::community::ListCommunities;
++use crate::api::Oper;
+ use crate::api::Perform;
  use crate::apub;
- use actix_web::web;
+ use crate::settings::Settings;
+ use actix_web::web::Query;
+ use actix_web::{web, HttpResponse};
+ use diesel::r2d2::{ConnectionManager, Pool};
+ use diesel::PgConnection;
  
  pub fn config(cfg: &mut web::ServiceConfig) {
-   cfg
-     .route(
-       "/federation/c/{community_name}",
-       web::get().to(apub::community::get_apub_community),
-     )
-     .route(
-       "/federation/c/{community_name}/followers",
-       web::get().to(apub::community::get_apub_community_followers),
-     )
-     .route(
-       "/federation/u/{user_name}",
-       web::get().to(apub::user::get_apub_user),
-     );
+   if Settings::get().federation_enabled {
+     println!("federation enabled, host is {}", Settings::get().hostname);
+     cfg
+       .route(
+         "/federation/c/{community_name}",
+         web::get().to(apub::community::get_apub_community),
+       )
+       .route(
+         "/federation/c/{community_name}/followers",
+         web::get().to(apub::community::get_apub_community_followers),
+       )
+       .route(
+         "/federation/u/{user_name}",
+         web::get().to(apub::user::get_apub_user),
+       )
+       // TODO: this is a very quick and dirty implementation for http api calls
+       .route(
+         "/api/v1/communities/list",
+         web::get().to(
+           |query: Query<ListCommunities>, db: web::Data<Pool<ConnectionManager<PgConnection>>>| {
 -            let res = Oper::new(UserOperation::ListCommunities, query.into_inner())
++            let res = Oper::new(query.into_inner())
+               .perform(&db.get().unwrap())
+               .unwrap();
+             HttpResponse::Ok()
+               .content_type("application/json")
+               .body(serde_json::to_string(&res).unwrap())
+           },
+         ),
+       );
+   }
  }
diff --combined server/src/settings.rs
index ee3a3c0720036595362a98e93a2bc0122a474802,82d7e1428b22ae9a1c940888f3b74af44354c2a9..3d32f3f38d63d6d4dc611db902a925be930a77cb
@@@ -18,6 -18,7 +18,7 @@@ pub struct Settings 
    pub rate_limit: RateLimitConfig,
    pub email: Option<EmailConfig>,
    pub federation_enabled: bool,
+   pub federated_instance: Option<String>,
  }
  
  #[derive(Debug, Deserialize)]
@@@ -33,10 -34,9 +34,10 @@@ pub struct RateLimitConfig 
  #[derive(Debug, Deserialize)]
  pub struct EmailConfig {
    pub smtp_server: String,
 -  pub smtp_login: String,
 -  pub smtp_password: String,
 +  pub smtp_login: Option<String>,
 +  pub smtp_password: Option<String>,
    pub smtp_from_address: String,
 +  pub use_tls: bool,
  }
  
  #[derive(Debug, Deserialize)]
index a26c8144e0fd805df43c379180a5b9dd92234b4c,c444e11bb215e7e90795776326c817f953b3d0b2..0c606284537c4c6f3fa4de9b8edfca6d39a53a6d
@@@ -3,7 -3,7 +3,7 @@@
  //! room through `ChatServer`.
  
  use actix::prelude::*;
 -use diesel::r2d2::{ConnectionManager, Pool};
 +use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
  use diesel::PgConnection;
  use failure::Error;
  use rand::{rngs::ThreadRng, Rng};
@@@ -19,15 -19,9 +19,16 @@@ use crate::api::post::*
  use crate::api::site::*;
  use crate::api::user::*;
  use crate::api::*;
+ use crate::apub::puller::*;
 +use crate::websocket::UserOperation;
  use crate::Settings;
  
 +type ConnectionId = usize;
 +type PostId = i32;
 +type CommunityId = i32;
 +type UserId = i32;
 +type IPAddr = String;
 +
  /// Chat server sends this messages to session
  #[derive(Message)]
  #[rtype(result = "()")]
@@@ -40,22 -34,35 +41,22 @@@ pub struct WSMessage(pub String)
  #[rtype(usize)]
  pub struct Connect {
    pub addr: Recipient<WSMessage>,
 -  pub ip: String,
 +  pub ip: IPAddr,
  }
  
  /// Session is disconnected
  #[derive(Message)]
  #[rtype(result = "()")]
  pub struct Disconnect {
 -  pub id: usize,
 -  pub ip: String,
 -}
 -
 -// TODO this is unused rn
 -/// Send message to specific room
 -#[derive(Message)]
 -#[rtype(result = "()")]
 -pub struct ClientMessage {
 -  /// Id of the client session
 -  pub id: usize,
 -  /// Peer message
 -  pub msg: String,
 -  /// Room name
 -  pub room: String,
 +  pub id: ConnectionId,
 +  pub ip: IPAddr,
  }
  
  #[derive(Serialize, Deserialize, Message)]
  #[rtype(String)]
  pub struct StandardMessage {
    /// Id of the client session
 -  pub id: usize,
 +  pub id: ConnectionId,
    /// Peer message
    pub msg: String,
  }
@@@ -68,93 -75,51 +69,93 @@@ pub struct RateLimitBucket 
  
  pub struct SessionInfo {
    pub addr: Recipient<WSMessage>,
 -  pub ip: String,
 +  pub ip: IPAddr,
  }
  
  /// `ChatServer` manages chat rooms and responsible for coordinating chat
 -/// session. implementation is super primitive
 +/// session.
  pub struct ChatServer {
 -  sessions: HashMap<usize, SessionInfo>, // A map from generated random ID to session addr
 -  rate_limits: HashMap<String, RateLimitBucket>,
 -  rooms: HashMap<i32, HashSet<usize>>, // A map from room / post name to set of connectionIDs
 +  /// A map from generated random ID to session addr
 +  sessions: HashMap<ConnectionId, SessionInfo>,
 +
 +  /// A map from post_id to set of connectionIDs
 +  post_rooms: HashMap<PostId, HashSet<ConnectionId>>,
 +
 +  /// A map from community to set of connectionIDs
 +  community_rooms: HashMap<CommunityId, HashSet<ConnectionId>>,
 +
 +  /// A map from user id to its connection ID for joined users. Remember a user can have multiple
 +  /// sessions (IE clients)
 +  user_rooms: HashMap<UserId, HashSet<ConnectionId>>,
 +
 +  /// Rate limiting based on IP addr
 +  rate_limits: HashMap<IPAddr, RateLimitBucket>,
 +
    rng: ThreadRng,
    db: Pool<ConnectionManager<PgConnection>>,
  }
  
 -// impl Default for ChatServer {
 -//   fn default(nah: String) -> ChatServer {
 -//     // default room
 -//     let rooms = HashMap::new();
 -
 -//     ChatServer {
 -//       sessions: HashMap::new(),
 -//       rate_limits: HashMap::new(),
 -//       rooms,
 -//       rng: rand::thread_rng(),
 -//       nah: nah,
 -//     }
 -//   }
 -// }
 -
  impl ChatServer {
    pub fn startup(db: Pool<ConnectionManager<PgConnection>>) -> ChatServer {
 -    // default room
 -    let rooms = HashMap::new();
 -
      ChatServer {
        sessions: HashMap::new(),
        rate_limits: HashMap::new(),
 -      rooms,
 +      post_rooms: HashMap::new(),
 +      community_rooms: HashMap::new(),
 +      user_rooms: HashMap::new(),
        rng: rand::thread_rng(),
        db,
      }
    }
  
 -  /// Send message to all users in the room
 -  fn send_room_message(&self, room: i32, message: &str, skip_id: usize) {
 -    if let Some(sessions) = self.rooms.get(&room) {
 +  fn join_community_room(&mut self, community_id: CommunityId, id: ConnectionId) {
 +    // remove session from all rooms
 +    for sessions in self.community_rooms.values_mut() {
 +      sessions.remove(&id);
 +    }
 +
 +    // If the room doesn't exist yet
 +    if self.community_rooms.get_mut(&community_id).is_none() {
 +      self.community_rooms.insert(community_id, HashSet::new());
 +    }
 +
 +    self
 +      .community_rooms
 +      .get_mut(&community_id)
 +      .unwrap()
 +      .insert(id);
 +  }
 +
 +  fn join_post_room(&mut self, post_id: PostId, id: ConnectionId) {
 +    // remove session from all rooms
 +    for sessions in self.post_rooms.values_mut() {
 +      sessions.remove(&id);
 +    }
 +
 +    // If the room doesn't exist yet
 +    if self.post_rooms.get_mut(&post_id).is_none() {
 +      self.post_rooms.insert(post_id, HashSet::new());
 +    }
 +
 +    self.post_rooms.get_mut(&post_id).unwrap().insert(id);
 +  }
 +
 +  fn join_user_room(&mut self, user_id: UserId, id: ConnectionId) {
 +    // remove session from all rooms
 +    for sessions in self.user_rooms.values_mut() {
 +      sessions.remove(&id);
 +    }
 +
 +    // If the room doesn't exist yet
 +    if self.user_rooms.get_mut(&user_id).is_none() {
 +      self.user_rooms.insert(user_id, HashSet::new());
 +    }
 +
 +    self.user_rooms.get_mut(&user_id).unwrap().insert(id);
 +  }
 +
 +  fn send_post_room_message(&self, post_id: PostId, message: &str, skip_id: ConnectionId) {
 +    if let Some(sessions) = self.post_rooms.get(&post_id) {
        for id in sessions {
          if *id != skip_id {
            if let Some(info) = self.sessions.get(id) {
      }
    }
  
 -  fn join_room(&mut self, room_id: i32, id: usize) {
 -    // remove session from all rooms
 -    for sessions in self.rooms.values_mut() {
 -      sessions.remove(&id);
 +  fn send_community_room_message(
 +    &self,
 +    community_id: CommunityId,
 +    message: &str,
 +    skip_id: ConnectionId,
 +  ) {
 +    if let Some(sessions) = self.community_rooms.get(&community_id) {
 +      for id in sessions {
 +        if *id != skip_id {
 +          if let Some(info) = self.sessions.get(id) {
 +            let _ = info.addr.do_send(WSMessage(message.to_owned()));
 +          }
 +        }
 +      }
      }
 +  }
  
 -    // If the room doesn't exist yet
 -    if self.rooms.get_mut(&room_id).is_none() {
 -      self.rooms.insert(room_id, HashSet::new());
 +  fn send_user_room_message(&self, user_id: UserId, message: &str, skip_id: ConnectionId) {
 +    if let Some(sessions) = self.user_rooms.get(&user_id) {
 +      for id in sessions {
 +        if *id != skip_id {
 +          if let Some(info) = self.sessions.get(id) {
 +            let _ = info.addr.do_send(WSMessage(message.to_owned()));
 +          }
 +        }
 +      }
      }
 +  }
  
 -    self.rooms.get_mut(&room_id).unwrap().insert(id);
 +  fn send_all_message(&self, message: &str, skip_id: ConnectionId) {
 +    for id in self.sessions.keys() {
 +      if *id != skip_id {
 +        if let Some(info) = self.sessions.get(id) {
 +          let _ = info.addr.do_send(WSMessage(message.to_owned()));
 +        }
 +      }
 +    }
    }
  
 -  fn send_community_message(
 +  fn comment_sends(
      &self,
 -    community_id: i32,
 -    message: &str,
 -    skip_id: usize,
 -  ) -> Result<(), Error> {
 -    use crate::db::post_view::*;
 -    use crate::db::*;
 +    user_operation: UserOperation,
 +    comment: CommentResponse,
 +    id: ConnectionId,
 +  ) -> Result<String, Error> {
 +    let mut comment_reply_sent = comment.clone();
 +    comment_reply_sent.comment.my_vote = None;
 +    comment_reply_sent.comment.user_id = None;
 +
 +    // For the post room ones, and the directs back to the user
 +    // strip out the recipient_ids, so that
 +    // users don't get double notifs
 +    let mut comment_user_sent = comment.clone();
 +    comment_user_sent.recipient_ids = Vec::new();
  
 -    let conn = self.db.get()?;
 +    let mut comment_post_sent = comment_reply_sent.clone();
 +    comment_post_sent.recipient_ids = Vec::new();
  
 -    let posts = PostQueryBuilder::create(&conn)
 -      .listing_type(ListingType::Community)
 -      .sort(&SortType::New)
 -      .for_community_id(community_id)
 -      .limit(9999)
 -      .list()?;
 +    let comment_reply_sent_str = to_json_string(&user_operation, &comment_reply_sent)?;
 +    let comment_post_sent_str = to_json_string(&user_operation, &comment_post_sent)?;
 +    let comment_user_sent_str = to_json_string(&user_operation, &comment_user_sent)?;
  
 -    for post in posts {
 -      self.send_room_message(post.id, message, skip_id);
 +    // Send it to the post room
 +    self.send_post_room_message(comment.comment.post_id, &comment_post_sent_str, id);
 +
 +    // Send it to the recipient(s) including the mentioned users
 +    for recipient_id in comment_reply_sent.recipient_ids {
 +      self.send_user_room_message(recipient_id, &comment_reply_sent_str, id);
      }
  
 -    Ok(())
 +    Ok(comment_user_sent_str)
 +  }
 +
 +  fn post_sends(
 +    &self,
 +    user_operation: UserOperation,
 +    post: PostResponse,
 +    id: ConnectionId,
 +  ) -> Result<String, Error> {
 +    let community_id = post.post.community_id;
 +
 +    // Don't send my data with it
 +    let mut post_sent = post.clone();
 +    post_sent.post.my_vote = None;
 +    post_sent.post.user_id = None;
 +    let post_sent_str = to_json_string(&user_operation, &post_sent)?;
 +
 +    // Send it to /c/all and that community
 +    self.send_community_room_message(0, &post_sent_str, id);
 +    self.send_community_room_message(community_id, &post_sent_str, id);
 +
 +    to_json_string(&user_operation, post)
    }
  
    fn check_rate_limit_register(&mut self, id: usize) -> Result<(), Error> {
            );
            Err(
              APIError {
 -              op: "Rate Limit".to_string(),
                message: format!("Too many requests. {} per {} seconds", rate, per),
              }
              .into(),
@@@ -338,6 -249,9 +339,6 @@@ impl Handler<Connect> for ChatServer 
    type Result = usize;
  
    fn handle(&mut self, msg: Connect, _ctx: &mut Context<Self>) -> Self::Result {
 -    // notify all users in same room
 -    // self.send_room_message(&"Main".to_owned(), "Someone joined", 0);
 -
      // register session with random id
      let id = self.rng.gen::<usize>();
      println!("{} joined", &msg.ip);
@@@ -369,18 -283,15 +370,18 @@@ impl Handler<Disconnect> for ChatServe
    type Result = ();
  
    fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
 -    // let mut rooms: Vec<i32> = Vec::new();
 -
 -    // remove address
 +    // Remove connections from sessions and all 3 scopes
      if self.sessions.remove(&msg.id).is_some() {
 -      // remove session from all rooms
 -      for sessions in self.rooms.values_mut() {
 -        if sessions.remove(&msg.id) {
 -          // rooms.push(*id);
 -        }
 +      for sessions in self.user_rooms.values_mut() {
 +        sessions.remove(&msg.id);
 +      }
 +
 +      for sessions in self.post_rooms.values_mut() {
 +        sessions.remove(&msg.id);
 +      }
 +
 +      for sessions in self.community_rooms.values_mut() {
 +        sessions.remove(&msg.id);
        }
      }
    }
@@@ -396,47 -307,15 +397,47 @@@ impl Handler<StandardMessage> for ChatS
        Err(e) => e.to_string(),
      };
  
 +    println!("Message Sent: {}", msg_out);
      MessageResult(msg_out)
    }
  }
  
 +#[derive(Serialize)]
 +struct WebsocketResponse<T> {
 +  op: String,
 +  data: T,
 +}
 +
 +fn to_json_string<T>(op: &UserOperation, data: T) -> Result<String, Error>
 +where
 +  T: Serialize,
 +{
 +  let response = WebsocketResponse {
 +    op: op.to_string(),
 +    data,
 +  };
 +  Ok(serde_json::to_string(&response)?)
 +}
 +
 +fn do_user_operation<'a, Data, Response>(
 +  op: UserOperation,
 +  data: &str,
 +  conn: &PooledConnection<ConnectionManager<PgConnection>>,
 +) -> Result<String, Error>
 +where
 +  for<'de> Data: Deserialize<'de> + 'a,
 +  Response: Serialize,
 +  Oper<Data>: Perform<Response>,
 +{
 +  let parsed_data: Data = serde_json::from_str(data)?;
 +  let res = Oper::new(parsed_data).perform(&conn)?;
 +  to_json_string(&op, &res)
 +}
 +
  fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<String, Error> {
    let json: Value = serde_json::from_str(&msg.msg)?;
    let data = &json["data"].to_string();
    let op = &json["op"].as_str().ok_or(APIError {
 -    op: "Unknown op type".to_string(),
      message: "Unknown op type".to_string(),
    })?;
  
  
    let user_operation: UserOperation = UserOperation::from_str(&op)?;
  
 +  // TODO: none of the chat messages are going to work if stuff is submitted via http api,
 +  //       need to move that handling elsewhere
    match user_operation {
 -    UserOperation::Login => {
 -      let login: Login = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, login).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 -    }
 +    UserOperation::Login => do_user_operation::<Login, LoginResponse>(user_operation, data, &conn),
      UserOperation::Register => {
 -      let register: Register = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, register).perform(&conn);
 -      if res.is_ok() {
 -        chat.check_rate_limit_register(msg.id)?;
 -      }
 -      Ok(serde_json::to_string(&res?)?)
 +      chat.check_rate_limit_register(msg.id)?;
 +      do_user_operation::<Register, LoginResponse>(user_operation, data, &conn)
      }
      UserOperation::GetUserDetails => {
 -      let get_user_details: GetUserDetails = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, get_user_details).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<GetUserDetails, GetUserDetailsResponse>(user_operation, data, &conn)
      }
      UserOperation::SaveUserSettings => {
 -      let save_user_settings: SaveUserSettings = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, save_user_settings).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<SaveUserSettings, LoginResponse>(user_operation, data, &conn)
      }
      UserOperation::AddAdmin => {
        let add_admin: AddAdmin = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, add_admin).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      let res = Oper::new(add_admin).perform(&conn)?;
 +      let res_str = to_json_string(&user_operation, &res)?;
 +      chat.send_all_message(&res_str, msg.id);
 +      Ok(res_str)
      }
      UserOperation::BanUser => {
        let ban_user: BanUser = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, ban_user).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      let res = Oper::new(ban_user).perform(&conn)?;
 +      let res_str = to_json_string(&user_operation, &res)?;
 +      chat.send_all_message(&res_str, msg.id);
 +      Ok(res_str)
      }
      UserOperation::GetReplies => {
 -      let get_replies: GetReplies = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, get_replies).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<GetReplies, GetRepliesResponse>(user_operation, data, &conn)
      }
      UserOperation::GetUserMentions => {
 -      let get_user_mentions: GetUserMentions = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, get_user_mentions).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<GetUserMentions, GetUserMentionsResponse>(user_operation, data, &conn)
      }
      UserOperation::EditUserMention => {
 -      let edit_user_mention: EditUserMention = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, edit_user_mention).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<EditUserMention, UserMentionResponse>(user_operation, data, &conn)
      }
      UserOperation::MarkAllAsRead => {
 -      let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, mark_all_as_read).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<MarkAllAsRead, GetRepliesResponse>(user_operation, data, &conn)
      }
      UserOperation::GetCommunity => {
 -      let mut get_community: GetCommunity = serde_json::from_str(data)?;
 -      if Settings::get().federation_enabled && get_community.name.is_some() {
 -        let name = &get_community.name.unwrap();
 -        let remote_community = if name.contains('@') {
 -          // TODO: need to support sort, filter etc for remote communities
 -          get_remote_community(name.to_owned())?
 +      let get_community: GetCommunity = serde_json::from_str(data)?;
-       let mut res = Oper::new(get_community).perform(&conn)?;
++
++      let mut res = if Settings::get().federation_enabled {
++        if let Some(community_name) = get_community.name.to_owned() {
++          if community_name.contains('@') {
++            // TODO: need to support sort, filter etc for remote communities
++            get_remote_community(community_name)?
++          // TODO what is this about
++          // get_community.name = Some(name.replace("!", ""));
++          } else {
++            Oper::new(get_community).perform(&conn)?
++          }
+         } else {
 -          get_community.name = Some(name.replace("!", ""));
 -          Oper::new(user_operation, get_community).perform(&conn)?
 -        };
 -        Ok(serde_json::to_string(&remote_community)?)
++          Oper::new(get_community).perform(&conn)?
++        }
+       } else {
 -        let res = Oper::new(user_operation, get_community).perform(&conn)?;
 -        Ok(serde_json::to_string(&res)?)
 -      }
++        Oper::new(get_community).perform(&conn)?
++      };
++
 +      let community_id = res.community.id;
 +
 +      chat.join_community_room(community_id, msg.id);
 +
 +      res.online = if let Some(community_users) = chat.community_rooms.get(&community_id) {
 +        community_users.len()
 +      } else {
 +        0
 +      };
 +
 +      to_json_string(&user_operation, &res)
      }
      UserOperation::ListCommunities => {
-       do_user_operation::<ListCommunities, ListCommunitiesResponse>(user_operation, data, &conn)
+       if Settings::get().federation_enabled {
+         let res = get_all_communities()?;
 -        let val = ListCommunitiesResponse {
 -          op: UserOperation::ListCommunities.to_string(),
 -          communities: res,
 -        };
 -        Ok(serde_json::to_string(&val)?)
++        let val = ListCommunitiesResponse { communities: res };
++        to_json_string(&user_operation, &val)
+       } else {
 -        let list_communities: ListCommunities = serde_json::from_str(data)?;
 -        let res = Oper::new(user_operation, list_communities).perform(&conn)?;
 -        Ok(serde_json::to_string(&res)?)
++        do_user_operation::<ListCommunities, ListCommunitiesResponse>(user_operation, data, &conn)
+       }
      }
      UserOperation::CreateCommunity => {
        chat.check_rate_limit_register(msg.id)?;
 -      let create_community: CreateCommunity = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, create_community).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<CreateCommunity, CommunityResponse>(user_operation, data, &conn)
      }
      UserOperation::EditCommunity => {
        let edit_community: EditCommunity = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, edit_community).perform(&conn)?;
 +      let res = Oper::new(edit_community).perform(&conn)?;
        let mut community_sent: CommunityResponse = res.clone();
        community_sent.community.user_id = None;
        community_sent.community.subscribed = None;
 -      let community_sent_str = serde_json::to_string(&community_sent)?;
 -      chat.send_community_message(community_sent.community.id, &community_sent_str, msg.id)?;
 -      Ok(serde_json::to_string(&res)?)
 +      let community_sent_str = to_json_string(&user_operation, &community_sent)?;
 +      chat.send_community_room_message(community_sent.community.id, &community_sent_str, msg.id);
 +      to_json_string(&user_operation, &res)
      }
      UserOperation::FollowCommunity => {
 -      let follow_community: FollowCommunity = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, follow_community).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 -    }
 -    UserOperation::GetFollowedCommunities => {
 -      let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, followed_communities).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<FollowCommunity, CommunityResponse>(user_operation, data, &conn)
      }
 +    UserOperation::GetFollowedCommunities => do_user_operation::<
 +      GetFollowedCommunities,
 +      GetFollowedCommunitiesResponse,
 +    >(user_operation, data, &conn),
      UserOperation::BanFromCommunity => {
        let ban_from_community: BanFromCommunity = serde_json::from_str(data)?;
        let community_id = ban_from_community.community_id;
 -      let res = Oper::new(user_operation, ban_from_community).perform(&conn)?;
 -      let res_str = serde_json::to_string(&res)?;
 -      chat.send_community_message(community_id, &res_str, msg.id)?;
 +      let res = Oper::new(ban_from_community).perform(&conn)?;
 +      let res_str = to_json_string(&user_operation, &res)?;
 +      chat.send_community_room_message(community_id, &res_str, msg.id);
        Ok(res_str)
      }
      UserOperation::AddModToCommunity => {
        let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?;
        let community_id = mod_add_to_community.community_id;
 -      let res = Oper::new(user_operation, mod_add_to_community).perform(&conn)?;
 -      let res_str = serde_json::to_string(&res)?;
 -      chat.send_community_message(community_id, &res_str, msg.id)?;
 +      let res = Oper::new(mod_add_to_community).perform(&conn)?;
 +      let res_str = to_json_string(&user_operation, &res)?;
 +      chat.send_community_room_message(community_id, &res_str, msg.id);
        Ok(res_str)
      }
      UserOperation::ListCategories => {
 -      let list_categories: ListCategories = ListCategories;
 -      let res = Oper::new(user_operation, list_categories).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 -    }
 -    UserOperation::CreatePost => {
 -      chat.check_rate_limit_post(msg.id)?;
 -      let create_post: CreatePost = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, create_post).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<ListCategories, ListCategoriesResponse>(user_operation, data, &conn)
      }
      UserOperation::GetPost => {
        let get_post: GetPost = serde_json::from_str(data)?;
 -      chat.join_room(get_post.id, msg.id);
 -      let res = Oper::new(user_operation, get_post).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      let post_id = get_post.id;
 +      chat.join_post_room(post_id, msg.id);
 +      let mut res = Oper::new(get_post).perform(&conn)?;
 +
 +      res.online = if let Some(post_users) = chat.post_rooms.get(&post_id) {
 +        post_users.len()
 +      } else {
 +        0
 +      };
 +
 +      to_json_string(&user_operation, &res)
      }
      UserOperation::GetPosts => {
        let get_posts: GetPosts = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, get_posts).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      if get_posts.community_id.is_none() {
 +        // 0 is the "all" community
 +        chat.join_community_room(0, msg.id);
 +      }
 +      let res = Oper::new(get_posts).perform(&conn)?;
 +      to_json_string(&user_operation, &res)
 +    }
 +    UserOperation::CreatePost => {
 +      chat.check_rate_limit_post(msg.id)?;
 +      let create_post: CreatePost = serde_json::from_str(data)?;
 +      let res = Oper::new(create_post).perform(&conn)?;
 +
 +      chat.post_sends(UserOperation::CreatePost, res, msg.id)
      }
      UserOperation::CreatePostLike => {
        chat.check_rate_limit_message(msg.id)?;
        let create_post_like: CreatePostLike = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, create_post_like).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      let res = Oper::new(create_post_like).perform(&conn)?;
 +
 +      chat.post_sends(UserOperation::CreatePostLike, res, msg.id)
      }
      UserOperation::EditPost => {
        let edit_post: EditPost = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, edit_post).perform(&conn)?;
 -      let mut post_sent = res.clone();
 -      post_sent.post.my_vote = None;
 -      let post_sent_str = serde_json::to_string(&post_sent)?;
 -      chat.send_room_message(post_sent.post.id, &post_sent_str, msg.id);
 -      Ok(serde_json::to_string(&res)?)
 +      let res = Oper::new(edit_post).perform(&conn)?;
 +
 +      chat.post_sends(UserOperation::EditPost, res, msg.id)
      }
      UserOperation::SavePost => {
 -      let save_post: SavePost = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, save_post).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<SavePost, PostResponse>(user_operation, data, &conn)
      }
      UserOperation::CreateComment => {
        chat.check_rate_limit_message(msg.id)?;
        let create_comment: CreateComment = serde_json::from_str(data)?;
 -      let post_id = create_comment.post_id;
 -      let res = Oper::new(user_operation, create_comment).perform(&conn)?;
 -      let mut comment_sent = res.clone();
 -      comment_sent.comment.my_vote = None;
 -      comment_sent.comment.user_id = None;
 -      let comment_sent_str = serde_json::to_string(&comment_sent)?;
 -      chat.send_room_message(post_id, &comment_sent_str, msg.id);
 -      Ok(serde_json::to_string(&res)?)
 +      let res = Oper::new(create_comment).perform(&conn)?;
 +
 +      chat.comment_sends(UserOperation::CreateComment, res, msg.id)
      }
      UserOperation::EditComment => {
        let edit_comment: EditComment = serde_json::from_str(data)?;
 -      let post_id = edit_comment.post_id;
 -      let res = Oper::new(user_operation, edit_comment).perform(&conn)?;
 -      let mut comment_sent = res.clone();
 -      comment_sent.comment.my_vote = None;
 -      comment_sent.comment.user_id = None;
 -      let comment_sent_str = serde_json::to_string(&comment_sent)?;
 -      chat.send_room_message(post_id, &comment_sent_str, msg.id);
 -      Ok(serde_json::to_string(&res)?)
 +      let res = Oper::new(edit_comment).perform(&conn)?;
 +
 +      chat.comment_sends(UserOperation::EditComment, res, msg.id)
      }
      UserOperation::SaveComment => {
 -      let save_comment: SaveComment = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, save_comment).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<SaveComment, CommentResponse>(user_operation, data, &conn)
      }
      UserOperation::CreateCommentLike => {
        chat.check_rate_limit_message(msg.id)?;
        let create_comment_like: CreateCommentLike = serde_json::from_str(data)?;
 -      let post_id = create_comment_like.post_id;
 -      let res = Oper::new(user_operation, create_comment_like).perform(&conn)?;
 -      let mut comment_sent = res.clone();
 -      comment_sent.comment.my_vote = None;
 -      comment_sent.comment.user_id = None;
 -      let comment_sent_str = serde_json::to_string(&comment_sent)?;
 -      chat.send_room_message(post_id, &comment_sent_str, msg.id);
 -      Ok(serde_json::to_string(&res)?)
 +      let res = Oper::new(create_comment_like).perform(&conn)?;
 +
 +      chat.comment_sends(UserOperation::CreateCommentLike, res, msg.id)
      }
      UserOperation::GetModlog => {
 -      let get_modlog: GetModlog = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, get_modlog).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<GetModlog, GetModlogResponse>(user_operation, data, &conn)
      }
      UserOperation::CreateSite => {
 -      let create_site: CreateSite = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, create_site).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<CreateSite, SiteResponse>(user_operation, data, &conn)
      }
      UserOperation::EditSite => {
        let edit_site: EditSite = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, edit_site).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      let res = Oper::new(edit_site).perform(&conn)?;
 +      let res_str = to_json_string(&user_operation, &res)?;
 +      chat.send_all_message(&res_str, msg.id);
 +      Ok(res_str)
      }
      UserOperation::GetSite => {
 -      let online: usize = chat.sessions.len();
        let get_site: GetSite = serde_json::from_str(data)?;
 -      let mut res = Oper::new(user_operation, get_site).perform(&conn)?;
 -      res.online = online;
 -      Ok(serde_json::to_string(&res)?)
 +      let mut res = Oper::new(get_site).perform(&conn)?;
 +      res.online = chat.sessions.len();
 +      to_json_string(&user_operation, &res)
      }
      UserOperation::Search => {
 -      let search: Search = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, search).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<Search, SearchResponse>(user_operation, data, &conn)
      }
      UserOperation::TransferCommunity => {
 -      let transfer_community: TransferCommunity = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, transfer_community).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<TransferCommunity, GetCommunityResponse>(user_operation, data, &conn)
      }
      UserOperation::TransferSite => {
 -      let transfer_site: TransferSite = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, transfer_site).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<TransferSite, GetSiteResponse>(user_operation, data, &conn)
      }
      UserOperation::DeleteAccount => {
 -      let delete_account: DeleteAccount = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, delete_account).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<DeleteAccount, LoginResponse>(user_operation, data, &conn)
      }
      UserOperation::PasswordReset => {
 -      let password_reset: PasswordReset = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, password_reset).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<PasswordReset, PasswordResetResponse>(user_operation, data, &conn)
      }
      UserOperation::PasswordChange => {
 -      let password_change: PasswordChange = serde_json::from_str(data)?;
 -      let res = Oper::new(user_operation, password_change).perform(&conn)?;
 -      Ok(serde_json::to_string(&res)?)
 +      do_user_operation::<PasswordChange, LoginResponse>(user_operation, data, &conn)
 +    }
 +    UserOperation::CreatePrivateMessage => {
 +      chat.check_rate_limit_message(msg.id)?;
 +      let create_private_message: CreatePrivateMessage = serde_json::from_str(data)?;
 +      let recipient_id = create_private_message.recipient_id;
 +      let res = Oper::new(create_private_message).perform(&conn)?;
 +      let res_str = to_json_string(&user_operation, &res)?;
 +
 +      chat.send_user_room_message(recipient_id, &res_str, msg.id);
 +      Ok(res_str)
 +    }
 +    UserOperation::EditPrivateMessage => {
 +      do_user_operation::<EditPrivateMessage, PrivateMessageResponse>(user_operation, data, &conn)
 +    }
 +    UserOperation::GetPrivateMessages => {
 +      do_user_operation::<GetPrivateMessages, PrivateMessagesResponse>(user_operation, data, &conn)
 +    }
 +    UserOperation::UserJoin => {
 +      let user_join: UserJoin = serde_json::from_str(data)?;
 +      let res = Oper::new(user_join).perform(&conn)?;
 +      chat.join_user_room(res.user_id, msg.id);
 +      to_json_string(&user_operation, &res)
      }
    }
  }