~nicohman/signal-rs

0f83f0bb6f4636a8f7ef3ced0ea9b7aef2f07a15 — nicohman 2 years ago 1e1259e presage
Beginnings of using presage as backend
35 files changed, 2452 insertions(+), 2008 deletions(-)

M Cargo.lock
M Cargo.toml
M README.md
D clickable.json
D manifest.json
D pallet
M qml/CreateChat.qml
M qml/CreateContact.qml
M qml/EditContact.qml
M qml/Settings.qml
M qml/SignalState.qml
M qml/main.cpp
M qml/main.qml
A qml/registration/Registration.qml
A qml/registration/RegistrationLink.qml
A qml/registration/RegistrationNew.qml
D signal-rs.apparmor
D signal-rs.desktop
M src/config.rs
D src/icon.png
M src/main.rs
A src/presage_manager.rs
M src/qrc.rs
D src/scli.rs
M src/signal.rs
D src/widgets/chat_history.rs
D src/widgets/chat_list.rs
D src/widgets/create_chat.rs
D src/widgets/create_contact.rs
D src/widgets/edit_contact.rs
D src/widgets/header.rs
D src/widgets/message.rs
D src/widgets/mod.rs
D src/widgets/password.rs
D src/widgets/settings.rs
M Cargo.lock => Cargo.lock +1531 -107
@@ 1,5 1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "addr2line"
version = "0.14.1"


@@ 16,6 18,139 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"

[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"

[[package]]
name = "aead"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf01b9b56e767bb57b94ebf91a58b338002963785cdd7013e21c0d4679471e4"
dependencies = [
 "generic-array 0.12.4",
]

[[package]]
name = "aead"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331"
dependencies = [
 "generic-array 0.14.4",
]

[[package]]
name = "aes"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9"
dependencies = [
 "aes-soft 0.3.3",
 "aesni 0.6.0",
 "block-cipher-trait",
]

[[package]]
name = "aes"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
dependencies = [
 "aes-soft 0.6.4",
 "aesni 0.10.0",
 "cipher",
]

[[package]]
name = "aes-ctr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7729c3cde54d67063be556aeac75a81330d802f0259500ca40cb52967f975763"
dependencies = [
 "aes-soft 0.6.4",
 "aesni 0.10.0",
 "cipher",
 "ctr",
]

[[package]]
name = "aes-gcm"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da"
dependencies = [
 "aead 0.3.2",
 "aes 0.6.0",
 "cipher",
 "ctr",
 "ghash",
 "subtle 2.4.0",
]

[[package]]
name = "aes-gcm-siv"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed8c21a21a0afb20aeb41328e980939e99fa02f136ae7317665e892d2760912a"
dependencies = [
 "aead 0.2.0",
 "aes 0.3.2",
 "block-cipher-trait",
 "polyval 0.3.3",
 "subtle 2.4.0",
 "zeroize",
]

[[package]]
name = "aes-soft"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
dependencies = [
 "block-cipher-trait",
 "byteorder",
 "opaque-debug 0.2.3",
]

[[package]]
name = "aes-soft"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
dependencies = [
 "cipher",
 "opaque-debug 0.3.0",
]

[[package]]
name = "aesni"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
dependencies = [
 "block-cipher-trait",
 "opaque-debug 0.2.3",
]

[[package]]
name = "aesni"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
dependencies = [
 "cipher",
 "opaque-debug 0.3.0",
]

[[package]]
name = "ahash"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"

[[package]]
name = "aho-corasick"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 34,6 169,15 @@ dependencies = [
]

[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
 "winapi 0.3.9",
]

[[package]]
name = "anyhow"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 52,6 196,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"

[[package]]
name = "ascii"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e"

[[package]]
name = "async-trait"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"
dependencies = [
 "proc-macro2",
 "quote 1.0.8",
 "syn 1.0.58",
]

[[package]]
name = "async-tungstenite"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07b30ef0ea5c20caaa54baea49514a206308989c68be7ecd86c7f956e4da6378"
dependencies = [
 "futures-io",
 "futures-util",
 "log",
 "pin-project-lite 0.2.4",
 "tokio 1.5.0",
 "tokio-rustls",
 "tungstenite",
 "webpki-roots",
]

[[package]]
name = "atomicwrites"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 63,6 240,17 @@ dependencies = [
]

[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
 "hermit-abi",
 "libc",
 "winapi 0.3.9",
]

[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 77,7 265,7 @@ dependencies = [
 "addr2line",
 "cfg-if 1.0.0",
 "libc",
 "miniz_oxide",
 "miniz_oxide 0.4.3",
 "object",
 "rustc-demangle",
]


@@ 144,20 332,78 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"

[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
 "block-padding 0.1.5",
 "byte-tools",
 "byteorder",
 "generic-array 0.12.4",
]

[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
 "generic-array",
 "generic-array 0.14.4",
]

[[package]]
name = "block-cipher-trait"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
dependencies = [
 "generic-array 0.12.4",
]

[[package]]
name = "block-modes"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0"
dependencies = [
 "block-padding 0.2.1",
 "cipher",
]

[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
 "byte-tools",
]

[[package]]
name = "block-padding"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"

[[package]]
name = "bumpalo"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"

[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"

[[package]]
name = "bytemuck"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58"

[[package]]
name = "byteorder"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 188,6 434,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5927edd8345aef08578bcbb4aea7314f340d80c7f4931f99fbeb40b99d8f5060"

[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"

[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 200,6 452,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "checked_int_cast"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cc5e6b5ab06331c33589842070416baa137e8b0eb912b008cfd4a78ada7919"

[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 208,11 466,36 @@ dependencies = [
 "libc",
 "num-integer",
 "num-traits",
 "serde",
 "time",
 "winapi 0.3.9",
]

[[package]]
name = "cipher"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
dependencies = [
 "generic-array 0.14.4",
]

[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
 "ansi_term",
 "atty",
 "bitflags 1.2.1",
 "strsim",
 "textwrap",
 "unicode-width",
 "vec_map",
]

[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 222,6 505,34 @@ dependencies = [
]

[[package]]
name = "cmake"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
dependencies = [
 "cc",
]

[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"

[[package]]
name = "combine"
version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680"
dependencies = [
 "ascii",
 "byteorder",
 "either",
 "memchr",
 "unreachable",
]

[[package]]
name = "combine"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 236,7 547,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2913470204e9e8498a0f31f17f90a0de801ae92c8c5ac18c49af4819e6786697"
dependencies = [
 "directories",
 "directories 2.0.2",
 "serde",
 "toml",
]


@@ 374,6 685,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"

[[package]]
name = "cpuid-bool"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"

[[package]]
name = "crc32fast"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 392,11 709,25 @@ dependencies = [
 "crossbeam-channel 0.4.4",
 "crossbeam-deque 0.7.3",
 "crossbeam-epoch 0.8.2",
 "crossbeam-queue",
 "crossbeam-queue 0.2.3",
 "crossbeam-utils 0.7.2",
]

[[package]]
name = "crossbeam"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd01a6eb3daaafa260f6fc94c3a6c36390abc2080e38e3e34ced87393fb77d80"
dependencies = [
 "cfg-if 1.0.0",
 "crossbeam-channel 0.5.0",
 "crossbeam-deque 0.8.0",
 "crossbeam-epoch 0.9.1",
 "crossbeam-queue 0.3.1",
 "crossbeam-utils 0.8.1",
]

[[package]]
name = "crossbeam-channel"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 479,6 810,16 @@ dependencies = [
]

[[package]]
name = "crossbeam-queue"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f6cb3c7f5b8e51bc3ebb73a2327ad4abdbd119dc13223f14f961d2f38486756"
dependencies = [
 "cfg-if 1.0.0",
 "crossbeam-utils 0.8.1",
]

[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 507,6 848,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"

[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
dependencies = [
 "generic-array 0.12.4",
 "subtle 1.0.0",
]

[[package]]
name = "crypto-mac"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
dependencies = [
 "generic-array 0.14.4",
 "subtle 2.4.0",
]

[[package]]
name = "cstr"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 527,27 888,63 @@ dependencies = [
]

[[package]]
name = "ct-logs"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8"
dependencies = [
 "sct",
]

[[package]]
name = "ctr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f"
dependencies = [
 "cipher",
]

[[package]]
name = "curve25519-dalek"
version = "2.0.0"
source = "git+https://github.com/signalapp/curve25519-dalek.git?branch=lizard2#477356e017c7cc2aa168f956786b34690870768f"
dependencies = [
 "byteorder",
 "digest 0.8.1",
 "rand_core 0.5.1",
 "serde",
 "subtle 2.4.0",
 "zeroize",
]

[[package]]
name = "dbus"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b1334c0161ddfccd239ac81b188d62015b049c986c5cd0b7f9447cf2c54f4a3"
dependencies = [
 "futures-channel",
 "futures-util",
 "libc",
 "libdbus-sys",
]

[[package]]
name = "dbus-tokio"
version = "0.6.0"
name = "deflate"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6840421e249bf51f6ad1873b53f7423f14e712208792dfe4b3a8ff99f713309"
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [
 "dbus",
 "libc",
 "mio 0.6.23",
 "tokio",
 "adler32",
 "byteorder",
]

[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
 "generic-array 0.12.4",
]

[[package]]


@@ 556,7 953,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
 "generic-array",
 "generic-array 0.14.4",
]

[[package]]


@@ 565,7 962,16 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
dependencies = [
 "cfg-if 0.1.10",
 "cfg-if 0.1.10",
 "dirs-sys",
]

[[package]]
name = "directories"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fed639d60b58d0f53498ab13d26f621fd77569cc6edb031f4cc36a2ad9da0f"
dependencies = [
 "dirs-sys",
]



@@ 625,6 1031,28 @@ dependencies = [
]

[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
 "atty",
 "humantime",
 "log",
 "regex",
 "termcolor",
]

[[package]]
name = "error-chain"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
 "version_check",
]

[[package]]
name = "fail"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 658,6 1086,12 @@ dependencies = [
]

[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"

[[package]]
name = "filetime"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 670,6 1104,12 @@ dependencies = [
]

[[package]]
name = "fixedbitset"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"

[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 855,6 1295,15 @@ dependencies = [

[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
 "typenum",
]

[[package]]
name = "generic-array"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"


@@ 905,6 1354,16 @@ dependencies = [
]

[[package]]
name = "ghash"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375"
dependencies = [
 "opaque-debug 0.3.0",
 "polyval 0.4.5",
]

[[package]]
name = "gimli"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 980,8 1439,8 @@ dependencies = [
 "http",
 "indexmap",
 "slab",
 "tokio",
 "tokio-util",
 "tokio 0.2.24",
 "tokio-util 0.3.1",
 "tracing",
 "tracing-futures",
]


@@ 991,6 1450,34 @@ name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
dependencies = [
 "ahash",
]

[[package]]
name = "headers"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0b7591fb62902706ae8e7aaff416b1b0fa2c0fd0878b46dc13baa3712d8a855"
dependencies = [
 "base64 0.13.0",
 "bitflags 1.2.1",
 "bytes 1.0.1",
 "headers-core",
 "http",
 "mime",
 "sha-1",
 "time",
]

[[package]]
name = "headers-core"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
dependencies = [
 "http",
]

[[package]]
name = "heck"


@@ 1011,6 1498,32 @@ dependencies = [
]

[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"

[[package]]
name = "hmac"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
dependencies = [
 "crypto-mac 0.7.0",
 "digest 0.8.1",
]

[[package]]
name = "hmac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
dependencies = [
 "crypto-mac 0.10.0",
 "digest 0.9.0",
]

[[package]]
name = "home"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1047,6 1560,17 @@ dependencies = [
]

[[package]]
name = "http-body"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737"
dependencies = [
 "bytes 1.0.1",
 "http",
 "pin-project-lite 0.2.4",
]

[[package]]
name = "httparse"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1059,6 1583,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"

[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
 "quick-error",
]

[[package]]
name = "hyper"
version = "0.13.9"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1070,28 1603,80 @@ dependencies = [
 "futures-util",
 "h2",
 "http",
 "http-body",
 "http-body 0.3.1",
 "httparse",
 "httpdate",
 "itoa",
 "pin-project 1.0.4",
 "socket2 0.3.19",
 "tokio 0.2.24",
 "tower-service",
 "tracing",
 "want",
]

[[package]]
name = "hyper"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1"
dependencies = [
 "bytes 1.0.1",
 "futures-channel",
 "futures-core",
 "futures-util",
 "http",
 "http-body 0.4.1",
 "httparse",
 "httpdate",
 "itoa",
 "pin-project 1.0.4",
 "socket2",
 "tokio",
 "socket2 0.4.0",
 "tokio 1.5.0",
 "tower-service",
 "tracing",
 "want",
]

[[package]]
name = "hyper-rustls"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64"
dependencies = [
 "ct-logs",
 "futures-util",
 "hyper 0.14.5",
 "log",
 "rustls",
 "rustls-native-certs",
 "tokio 1.5.0",
 "tokio-rustls",
 "webpki",
]

[[package]]
name = "hyper-timeout"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
dependencies = [
 "hyper 0.14.5",
 "pin-project-lite 0.2.4",
 "tokio 1.5.0",
 "tokio-io-timeout",
]

[[package]]
name = "hyper-tls"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
dependencies = [
 "bytes 0.5.6",
 "hyper",
 "hyper 0.13.9",
 "native-tls",
 "tokio",
 "tokio 0.2.24",
 "tokio-tls",
]



@@ 1113,6 1698,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46dbcb333e86939721589d25a3557e180b52778cb33c7fdfe9e0158ff790d5ec"

[[package]]
name = "image"
version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [
 "bytemuck",
 "byteorder",
 "color_quant",
 "num-iter",
 "num-rational",
 "num-traits",
 "png",
]

[[package]]
name = "indexmap"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1144,11 1744,20 @@ dependencies = [

[[package]]
name = "input_buffer"
version = "0.3.1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754"
checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413"
dependencies = [
 "bytes 0.5.6",
 "bytes 1.0.1",
]

[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]


@@ 1182,6 1791,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"

[[package]]
name = "jni"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22bbdc25b49340bc4fc3d9c96dd84d878c4beeca35e3651efa53db51a68d7d4d"
dependencies = [
 "cesu8",
 "combine 3.8.1",
 "error-chain",
 "jni-sys",
 "log",
 "walkdir",
]

[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"

[[package]]
name = "js-sys"
version = "0.3.46"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1219,6 1848,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f44db4199cdb049b494a92d105acbfa43c25b3925e33803923ba9580b7bc9e1a"

[[package]]
name = "lexical-core"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
dependencies = [
 "arrayvec",
 "bitflags 1.2.1",
 "cfg-if 1.0.0",
 "ryu",
 "static_assertions",
]

[[package]]
name = "libc"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1234,6 1876,99 @@ dependencies = [
]

[[package]]
name = "libsignal-protocol"
version = "0.1.1-alpha.0"
source = "git+https://github.com/Michael-F-Bryan/libsignal-protocol-rs.git#0fdf7c30ff0e1f207ac219252e7e961b5e14b937"
dependencies = [
 "aes 0.6.0",
 "aes-ctr",
 "backtrace",
 "base64 0.13.0",
 "block-modes",
 "hmac 0.10.1",
 "libc",
 "libsignal-protocol-sys",
 "log",
 "rand 0.8.2",
 "sha2 0.9.3",
 "static_assertions",
 "thiserror",
]

[[package]]
name = "libsignal-protocol-sys"
version = "0.1.1-alpha.0"
source = "git+https://github.com/Michael-F-Bryan/libsignal-protocol-rs.git#0fdf7c30ff0e1f207ac219252e7e961b5e14b937"
dependencies = [
 "cmake",
]

[[package]]
name = "libsignal-service"
version = "0.1.0"
source = "git+https://github.com/gferon/libsignal-service-rs.git?branch=presage#6f53b81b63feef19b226068593e1315fbb735c75"
dependencies = [
 "aes 0.6.0",
 "aes-ctr",
 "aes-gcm",
 "async-trait",
 "base64 0.13.0",
 "bincode",
 "block-modes",
 "bytes 1.0.1",
 "chrono",
 "futures",
 "hex",
 "hmac 0.10.1",
 "http",
 "libsignal-protocol",
 "log",
 "phonenumber",
 "pin-project 1.0.4",
 "prost",
 "prost-build",
 "rand 0.8.2",
 "serde",
 "sha2 0.9.3",
 "thiserror",
 "url",
 "uuid",
 "zkgroup",
]

[[package]]
name = "libsignal-service-hyper"
version = "0.1.0"
source = "git+https://github.com/gferon/libsignal-service-rs.git?branch=presage#6f53b81b63feef19b226068593e1315fbb735c75"
dependencies = [
 "async-trait",
 "async-tungstenite",
 "base64 0.13.0",
 "bytes 1.0.1",
 "futures",
 "headers",
 "hyper 0.14.5",
 "hyper-rustls",
 "hyper-timeout",
 "libsignal-protocol",
 "libsignal-service",
 "log",
 "mpart-async",
 "serde",
 "serde_json",
 "thiserror",
 "tokio 1.5.0",
 "tokio-rustls",
 "url",
]

[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"

[[package]]
name = "locale_config"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1254,6 1989,15 @@ dependencies = [
]

[[package]]
name = "lock_api"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
dependencies = [
 "scopeguard",
]

[[package]]
name = "log"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1263,6 2007,24 @@ dependencies = [
]

[[package]]
name = "lru"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f374d42cdfc1d7dbf3d3dec28afab2eb97ffbf43a3234d795b5986dbf4b90ba"
dependencies = [
 "hashbrown",
]

[[package]]
name = "lru-cache"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
dependencies = [
 "linked-hash-map",
]

[[package]]
name = "mac-notification-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1347,6 2109,15 @@ dependencies = [

[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
 "adler32",
]

[[package]]
name = "miniz_oxide"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"


@@ 1396,30 2167,7 @@ dependencies = [
 "lazycell",
 "log",
 "mio 0.6.23",
 "slab",
]

[[package]]
name = "mio-named-pipes"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656"
dependencies = [
 "log",
 "mio 0.6.23",
 "miow 0.3.6",
 "winapi 0.3.9",
]

[[package]]
name = "mio-uds"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
dependencies = [
 "iovec",
 "libc",
 "mio 0.6.23",
 "slab",
]

[[package]]


@@ 1440,11 2188,38 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
dependencies = [
 "socket2",
 "socket2 0.3.19",
 "winapi 0.3.9",
]

[[package]]
name = "mpart-async"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77db2fea7d8439c7e6a621618902635cb1841e62d5e3b17f7fc48e2ecca6b467"
dependencies = [
 "bytes 1.0.1",
 "futures-core",
 "futures-util",
 "http",
 "httparse",
 "log",
 "mime_guess",
 "pin-project 1.0.4",
 "rand 0.7.3",
 "thiserror",
 "tokio 1.5.0",
 "tokio-util 0.6.6",
 "twoway",
]

[[package]]
name = "multimap"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"

[[package]]
name = "murmurhash32"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1496,6 2271,17 @@ dependencies = [
]

[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
 "lexical-core",
 "memchr",
 "version_check",
]

[[package]]
name = "notify"
version = "4.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1544,6 2330,28 @@ dependencies = [
]

[[package]]
name = "num-iter"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
dependencies = [
 "autocfg",
 "num-integer",
 "num-traits",
]

[[package]]
name = "num-rational"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
 "autocfg",
 "num-integer",
 "num-traits",
]

[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1613,12 2421,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"

[[package]]
name = "oncemutex"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2"

[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"

[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"

[[package]]
name = "opener"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13117407ca9d0caf3a0e74f97b490a7e64c0ae3aa90a8b7085544d0c37b6f3ae"
dependencies = [
 "winapi 0.3.9",
]

[[package]]
name = "openssl"
version = "0.10.32"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1671,21 2500,24 @@ dependencies = [

[[package]]
name = "pallet"
version = "0.6.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c145d1ed9dd7874fdb74989a8f9270b9845530bdcc4486bd9218da8b31f8793f"
dependencies = [
 "bincode",
 "pallet-macros",
 "rayon",
 "serde",
 "sled",
 "tantivy",
 "tempfile",
 "sled 0.34.6",
 "tantivy 0.14.0",
 "thiserror",
]

[[package]]
name = "pallet-macros"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7090911079c230fbbaec3ebbd21b80e0c900e8fe82dd7a1c10dc811cc639b07"
dependencies = [
 "proc-macro2",
 "quote 1.0.8",


@@ 1698,7 2530,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
dependencies = [
 "lock_api",
 "lock_api 0.3.4",
 "parking_lot_core 0.6.2",
 "rustc_version",
]


@@ 1709,11 2541,22 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
dependencies = [
 "lock_api",
 "lock_api 0.3.4",
 "parking_lot_core 0.7.2",
]

[[package]]
name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
 "instant",
 "lock_api 0.4.3",
 "parking_lot_core 0.8.3",
]

[[package]]
name = "parking_lot_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1743,12 2586,56 @@ dependencies = [
]

[[package]]
name = "parking_lot_core"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
dependencies = [
 "cfg-if 1.0.0",
 "instant",
 "libc",
 "redox_syscall 0.2.4",
 "smallvec 1.6.1",
 "winapi 0.3.9",
]

[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"

[[package]]
name = "petgraph"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
dependencies = [
 "fixedbitset",
 "indexmap",
]

[[package]]
name = "phonenumber"
version = "0.3.1+8.12.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261a014e5f5e048bf2c6f1a72fa5e4c223009dc5f296a385b95fe19b464608f"
dependencies = [
 "bincode",
 "either",
 "fnv",
 "itertools",
 "lazy_static",
 "nom",
 "quick-xml",
 "regex",
 "regex-cache",
 "serde",
 "serde_derive",
 "thiserror",
]

[[package]]
name = "pin-project"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1813,12 2700,83 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"

[[package]]
name = "png"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [
 "bitflags 1.2.1",
 "crc32fast",
 "deflate",
 "miniz_oxide 0.3.7",
]

[[package]]
name = "poksho"
version = "0.7.0"
source = "git+https://github.com/signalapp/poksho.git?tag=v0.7.0#8bb8c61c18e7bbe93c094ed91be52b9f96c1c5cd"
dependencies = [
 "curve25519-dalek",
 "hmac 0.7.1",
 "sha2 0.8.2",
]

[[package]]
name = "polyval"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ec3341498978de3bfd12d1b22f1af1de22818f5473a11e8a6ef997989e3a212"
dependencies = [
 "cfg-if 0.1.10",
 "universal-hash 0.3.0",
]

[[package]]
name = "polyval"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd"
dependencies = [
 "cpuid-bool 0.2.0",
 "opaque-debug 0.3.0",
 "universal-hash 0.4.0",
]

[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"

[[package]]
name = "presage"
version = "0.1.0"
source = "git+https://github.com/whisperfish/presage?rev=54f93cd#54f93cd40f5a12974762490d182400ff228329e8"
dependencies = [
 "anyhow",
 "base64 0.12.3",
 "directories 3.0.1",
 "env_logger",
 "futures",
 "hex",
 "image",
 "libsignal-protocol",
 "libsignal-service",
 "libsignal-service-hyper",
 "log",
 "opener",
 "prost",
 "qrcode",
 "rand 0.7.3",
 "serde",
 "serde_json",
 "sled 0.31.0",
 "structopt",
 "thiserror",
 "tokio 1.5.0",
]

[[package]]
name = "proc-macro-crate"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1890,6 2848,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1383dff4092fe903ac180e391a8d4121cc48f08ccf850614b0290c6673b69d"

[[package]]
name = "prost"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2"
dependencies = [
 "bytes 1.0.1",
 "prost-derive",
]

[[package]]
name = "prost-build"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d3ebd75ac2679c2af3a92246639f9fcc8a442ee420719cc4fe195b98dd5fa3"
dependencies = [
 "bytes 1.0.1",
 "heck",
 "itertools",
 "log",
 "multimap",
 "petgraph",
 "prost",
 "prost-types",
 "tempfile",
 "which",
]

[[package]]
name = "prost-derive"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4"
dependencies = [
 "anyhow",
 "itertools",
 "proc-macro2",
 "quote 1.0.8",
 "syn 1.0.58",
]

[[package]]
name = "prost-types"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb"
dependencies = [
 "bytes 1.0.1",
 "prost",
]

[[package]]
name = "qmetaobject"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1913,6 2922,31 @@ dependencies = [
]

[[package]]
name = "qrcode"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16d2f1455f3630c6e5107b4f2b94e74d76dea80736de0981fd27644216cff57f"
dependencies = [
 "checked_int_cast",
 "image",
]

[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"

[[package]]
name = "quick-xml"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cc440ee4802a86e357165021e3e255a9143724da31db1e2ea540214c96a0f82"
dependencies = [
 "memchr",
]

[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2109,6 3143,18 @@ dependencies = [
]

[[package]]
name = "regex-cache"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7b62d69743b8b94f353b6b7c3deb4c5582828328bcb8d5fedf214373808793"
dependencies = [
 "lru-cache",
 "oncemutex",
 "regex",
 "regex-syntax 0.6.22",
]

[[package]]
name = "regex-syntax"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2141,8 3187,8 @@ dependencies = [
 "futures-core",
 "futures-util",
 "http",
 "http-body",
 "hyper",
 "http-body 0.3.1",
 "hyper 0.13.9",
 "hyper-tls",
 "ipnet",
 "js-sys",


@@ 2155,7 3201,7 @@ dependencies = [
 "pin-project-lite 0.2.4",
 "serde",
 "serde_urlencoded",
 "tokio",
 "tokio 0.2.24",
 "tokio-tls",
 "url",
 "wasm-bindgen",


@@ 2165,6 3211,21 @@ dependencies = [
]

[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
 "cc",
 "libc",
 "once_cell 1.5.2",
 "spin",
 "untrusted",
 "web-sys",
 "winapi 0.3.9",
]

[[package]]
name = "rust-argon2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2202,6 3263,31 @@ dependencies = [
]

[[package]]
name = "rustls"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
dependencies = [
 "base64 0.13.0",
 "log",
 "ring",
 "sct",
 "webpki",
]

[[package]]
name = "rustls-native-certs"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092"
dependencies = [
 "openssl-probe",
 "rustls",
 "schannel",
 "security-framework",
]

[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2233,6 3319,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"

[[package]]
name = "sct"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
dependencies = [
 "ring",
 "untrusted",
]

[[package]]
name = "security-framework"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2313,28 3409,53 @@ dependencies = [
]

[[package]]
name = "serde_urlencoded"
version = "0.7.0"
name = "serde_urlencoded"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9"
dependencies = [
 "form_urlencoded",
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "sha-1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c"
dependencies = [
 "block-buffer 0.9.0",
 "cfg-if 1.0.0",
 "cpuid-bool 0.1.2",
 "digest 0.9.0",
 "opaque-debug 0.3.0",
]

[[package]]
name = "sha2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9"
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
dependencies = [
 "form_urlencoded",
 "itoa",
 "ryu",
 "serde",
 "block-buffer 0.7.3",
 "digest 0.8.1",
 "fake-simd",
 "opaque-debug 0.2.3",
]

[[package]]
name = "sha-1"
version = "0.9.2"
name = "sha2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c"
checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
dependencies = [
 "block-buffer",
 "block-buffer 0.9.0",
 "cfg-if 1.0.0",
 "cpuid-bool",
 "digest",
 "opaque-debug",
 "cpuid-bool 0.1.2",
 "digest 0.9.0",
 "opaque-debug 0.3.0",
]

[[package]]


@@ 2352,6 3473,7 @@ version = "1.0.0"
dependencies = [
 "anyhow",
 "base64 0.13.0",
 "chrono",
 "confy",
 "cpp",
 "cpp_build",


@@ 2359,8 3481,6 @@ dependencies = [
 "cpp_macros 0.5.6",
 "crossbeam-channel 0.4.4",
 "cstr",
 "dbus",
 "dbus-tokio",
 "enum_variant_type",
 "futures",
 "futures-util",


@@ 2368,21 3488,23 @@ dependencies = [
 "glib",
 "home",
 "lazy_static",
 "libsignal-service",
 "mio 0.7.7",
 "notify-rust",
 "once_cell 0.2.4",
 "pallet",
 "presage",
 "qmetaobject",
 "regex",
 "reqwest",
 "serde",
 "serde_json",
 "serde_repr",
 "tantivy 0.13.3",
 "tempfile",
 "tokio",
 "tokio-tungstenite",
 "tungstenite",
 "tokio 1.5.0",
 "url",
 "zkgroup",
]

[[package]]


@@ 2408,6 3530,22 @@ dependencies = [
]

[[package]]
name = "sled"
version = "0.34.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0132f3e393bcb7390c60bb45769498cf4550bcb7a21d7f95c02b69f6362cdc"
dependencies = [
 "crc32fast",
 "crossbeam-epoch 0.9.1",
 "crossbeam-utils 0.8.1",
 "fs2",
 "fxhash",
 "libc",
 "log",
 "parking_lot 0.11.1",
]

[[package]]
name = "smallvec"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2440,12 3578,64 @@ dependencies = [
]

[[package]]
name = "socket2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2"
dependencies = [
 "libc",
 "winapi 0.3.9",
]

[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"

[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"

[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"

[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"

[[package]]
name = "structopt"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c"
dependencies = [
 "clap",
 "lazy_static",
 "structopt-derive",
]

[[package]]
name = "structopt-derive"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90"
dependencies = [
 "heck",
 "proc-macro-error",
 "proc-macro2",
 "quote 1.0.8",
 "syn 1.0.58",
]

[[package]]
name = "strum"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2480,6 3670,18 @@ dependencies = [
]

[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"

[[package]]
name = "subtle"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"

[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2550,7 3752,7 @@ dependencies = [
 "census",
 "chrono",
 "crc32fast",
 "crossbeam",
 "crossbeam 0.7.3",
 "downcast-rs",
 "fail",
 "failure",


@@ 2576,8 3778,50 @@ dependencies = [
 "snap",
 "stable_deref_trait",
 "tantivy-fst",
 "tantivy-query-grammar",
 "tantivy-query-grammar 0.13.0",
 "tempfile",
 "uuid",
 "winapi 0.3.9",
]

[[package]]
name = "tantivy"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edca90bddda472f39fdc74a031d61d52b08b1de97f2a704afae726a8004abb0d"
dependencies = [
 "base64 0.13.0",
 "bitpacking",
 "byteorder",
 "census",
 "chrono",
 "crc32fast",
 "crossbeam 0.8.0",
 "downcast-rs",
 "fail",
 "fnv",
 "fs2",
 "futures",
 "htmlescape",
 "levenshtein_automata",
 "log",
 "lru",
 "memmap",
 "murmurhash32",
 "num_cpus",
 "once_cell 1.5.2",
 "rayon",
 "regex",
 "rust-stemmers",
 "serde",
 "serde_json",
 "smallvec 1.6.1",
 "snap",
 "stable_deref_trait",
 "tantivy-fst",
 "tantivy-query-grammar 0.14.0",
 "tempfile",
 "thiserror",
 "uuid",
 "winapi 0.3.9",
]


@@ 2599,7 3843,16 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea03b8224ca9ff4ccfc7dfab790527c8a9d8edbc53f4677bdf6ba0fd8000c75"
dependencies = [
 "combine",
 "combine 4.5.2",
]

[[package]]
name = "tantivy-query-grammar"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70864085b31ecd5af8f53a76506440ece1c426d187f3d72f4b722e238d2ce19a"
dependencies = [
 "combine 4.5.2",
]

[[package]]


@@ 2627,6 3880,24 @@ dependencies = [
]

[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
 "winapi-util",
]

[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
 "unicode-width",
]

[[package]]
name = "thiserror"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2691,24 3962,47 @@ dependencies = [
 "futures-core",
 "iovec",
 "lazy_static",
 "libc",
 "memchr",
 "mio 0.6.23",
 "mio-named-pipes",
 "mio-uds",
 "num_cpus",
 "pin-project-lite 0.1.11",
 "signal-hook-registry",
 "slab",
]

[[package]]
name = "tokio"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
dependencies = [
 "autocfg",
 "bytes 1.0.1",
 "libc",
 "memchr",
 "mio 0.7.7",
 "num_cpus",
 "once_cell 1.5.2",
 "parking_lot 0.11.1",
 "pin-project-lite 0.2.4",
 "signal-hook-registry",
 "tokio-macros",
 "winapi 0.3.9",
]

[[package]]
name = "tokio-io-timeout"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90c49f106be240de154571dd31fbe48acb10ba6c6dd6f6517ad603abffa42de9"
dependencies = [
 "pin-project-lite 0.2.4",
 "tokio 1.5.0",
]

[[package]]
name = "tokio-macros"
version = "0.2.6"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
dependencies = [
 "proc-macro2",
 "quote 1.0.8",


@@ 2716,40 4010,52 @@ dependencies = [
]

[[package]]
name = "tokio-rustls"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
dependencies = [
 "rustls",
 "tokio 1.5.0",
 "webpki",
]

[[package]]
name = "tokio-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
dependencies = [
 "native-tls",
 "tokio",
 "tokio 0.2.24",
]

[[package]]
name = "tokio-tungstenite"
version = "0.11.0"
name = "tokio-util"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c"
checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
dependencies = [
 "futures-util",
 "bytes 0.5.6",
 "futures-core",
 "futures-sink",
 "log",
 "pin-project 0.4.27",
 "tokio",
 "tungstenite",
 "pin-project-lite 0.1.11",
 "tokio 0.2.24",
]

[[package]]
name = "tokio-util"
version = "0.3.1"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e"
dependencies = [
 "bytes 0.5.6",
 "bytes 1.0.1",
 "futures-core",
 "futures-sink",
 "log",
 "pin-project-lite 0.1.11",
 "tokio",
 "pin-project-lite 0.2.4",
 "tokio 1.5.0",
]

[[package]]


@@ 2806,22 4112,35 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"

[[package]]
name = "tungstenite"
version = "0.11.1"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23"
checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093"
dependencies = [
 "base64 0.12.3",
 "base64 0.13.0",
 "byteorder",
 "bytes 0.5.6",
 "bytes 1.0.1",
 "http",
 "httparse",
 "input_buffer",
 "log",
 "native-tls",
 "rand 0.7.3",
 "rand 0.8.2",
 "rustls",
 "sha-1",
 "thiserror",
 "url",
 "utf-8",
 "webpki",
 "webpki-roots",
]

[[package]]
name = "twoway"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b40075910de3a912adbd80b5d8bad6ad10a23eeb1f5bf9d4006839e899ba5bc"
dependencies = [
 "memchr",
 "unchecked-index",
]

[[package]]


@@ 2831,6 4150,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"

[[package]]
name = "unchecked-index"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c"

[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2864,6 4189,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"

[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"

[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2876,6 4207,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"

[[package]]
name = "universal-hash"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0c900f2f9b4116803415878ff48b63da9edb268668e08cf9292d7503114a01"
dependencies = [
 "generic-array 0.12.4",
 "subtle 2.4.0",
]

[[package]]
name = "universal-hash"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
dependencies = [
 "generic-array 0.14.4",
 "subtle 2.4.0",
]

[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
dependencies = [
 "void",
]

[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"

[[package]]
name = "url"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2885,6 4251,7 @@ dependencies = [
 "idna",
 "matches",
 "percent-encoding",
 "serde",
]

[[package]]


@@ 2916,6 4283,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"

[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"

[[package]]
name = "version-compare"
version = "0.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3045,6 4418,35 @@ dependencies = [
]

[[package]]
name = "webpki"
version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
dependencies = [
 "ring",
 "untrusted",
]

[[package]]
name = "webpki-roots"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
dependencies = [
 "webpki",
]

[[package]]
name = "which"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe"
dependencies = [
 "either",
 "libc",
]

[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3136,3 4538,25 @@ checksum = "e1945e12e16b951721d7976520b0832496ef79c31602c7a29d950de79ba74621"
dependencies = [
 "bitflags 0.9.1",
]

[[package]]
name = "zeroize"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"

[[package]]
name = "zkgroup"
version = "0.7.2"
source = "git+https://github.com/signalapp/zkgroup#d3cb5dbd3098f1e8f82864974eda20c25f5d7d24"
dependencies = [
 "aead 0.2.0",
 "aes-gcm-siv",
 "bincode",
 "curve25519-dalek",
 "hex",
 "jni",
 "poksho",
 "serde",
 "sha2 0.8.2",
]

M Cargo.toml => Cargo.toml +10 -8
@@ 10,35 10,37 @@ qmetaobject = "0.1.4"
gettext-rs = "0.4"
cstr = "0.1.0"
cpp = "0.5"
tungstenite = "0.11.0"
url = "2.1.0"
notify-rust = "4.2.2"
serde = { version = "1.0.115", features = ["derive"]}
serde_json = "1.0.57"
mio = { version = "0.7", features = ["tcp", "os-poll"]}
futures-util = { version = "0.3", default-features = true, features = ["async-await", "sink", "std"] }
tokio = { version = "0.2.22",features = ["io-util", "full", "stream"] }
tokio-tungstenite = "0.11.0"
tokio = { version = "1",features = ["full"] }
enum_variant_type = "0.2.0"
libsignal-service = { git = "https://github.com/gferon/libsignal-service-rs.git", branch = "presage" }
zkgroup = { git = "https://github.com/signalapp/zkgroup" }
chrono = "0.4.6"
crossbeam-channel = "0.4.4"
glib = "0.10"
once_cell = "^0"
presage = { git = "https://github.com/whisperfish/presage", rev = "54f93cd"}
lazy_static = "1.4.0"
regex = "1.4.1"
reqwest = "0.10.8"
confy = "0.4.0"
serde_repr = "0.1"
dbus-tokio = "0.6.0"
dbus = "0.9.0"
tantivy = "0.13.3"
home = "0.5.3"	
pallet = "0.6.1"
pallet = "0.7.0"
anyhow  = "1.0"
futures = "0.3.13"
tempfile = "3.1.0"
base64 = "0.13.0"
cpp_macros = "0.4"
[patch.crates-io]
pallet = { path = "./pallet" }
[build-dependencies]
cpp_build = "0.5"
cpp_macros = "0.5"
[patch."https://github.com/Michael-F-Bryan/libsignal-service-rs.git"]
libsignal-service = { git = "https://github.com/gferon/libsignal-service-rs.git", branch = "presage" }
libsignal-service-hyper = { git = "https://github.com/gferon/libsignal-service-rs.git", branch = "presage" }
\ No newline at end of file

M README.md => README.md +1 -1
@@ 1,6 1,6 @@
# signal-rs

A Rust-based signal app designed for Ubuntu Touch devices, using QML
A Rust-based signal app with a QML/Kirigami frontend. Uses presage as a backend. Better name pending.

## License


D clickable.json => clickable.json +0 -16
@@ 1,16 0,0 @@
{
  "clickable_minimum_required": "6.22.0",
  "builder": "rust",
  "kill": "signal-rs",
  "install_data": {
    "${ROOT}/manifest.json": "${INSTALL_DIR}",
    "${ROOT}/signal-rs.apparmor": "${INSTALL_DIR}",
    "${ROOT}/signal-rs.desktop": "${INSTALL_DIR}",
    "${ROOT}/assets": "${INSTALL_DIR}"
  },
  "dependencies_host": ["qml-module-qtquick-controls"],
  "image_setup": {
  	"run": ["rustup default nightly","rustup install nightly"]
  },
  "qt_version":"5.12"
}

D manifest.json => manifest.json +0 -15
@@ 1,15 0,0 @@
{
    "name": "signal-rs.nicohman",
    "description": "A short description of your app",
    "architecture": "@CLICK_ARCH@",
    "title": "signal-rs",
    "hooks": {
        "signal-rs-ut": {
            "apparmor": "signal-rs.apparmor",
            "desktop":  "signal-rs.desktop"
        }
    },
    "version": "1.0.0",
    "maintainer": "Nico Hickman <nicohickman@protonmail.com>",
    "framework" : "@CLICK_FRAMEWORK@"
}

D pallet => pallet +0 -1
@@ 1,1 0,0 @@
Subproject commit f3a52b95c04453e75e2d9e0b15e0b9b55fca53fd

M qml/CreateChat.qml => qml/CreateChat.qml +4 -2
@@ 12,16 12,18 @@ Kirigami.ScrollablePage {
			//width: parent.width
			width: crLa.width //+ crAv.width
			height: 40
			/*onClicked: {
			onClicked: {
				// Dumb hack to get around fact that we don't have an official Chat yet
                signalState.currentChat = {
                	is_group: false
                };
                console.log(model.tel);
                signalState.currentName = model.name;
                signalState.setCurrent(model.tel);
                signal.show_chat(model.tel);
                signalState.openView("chatHistory");
			}*/
                window.pageStack.layers.pop();
			}
			Row {
				/*Avatar {
					id: crAv

M qml/CreateContact.qml => qml/CreateContact.qml +1 -1
@@ 47,7 47,7 @@ Kirigami.Page {
        	text: "Add Contact"
        	onClicked: {
        		signal.add_contact(contactNameInput.text, contactPhoneInput.text);
        		stackView.pop();
        		window.pageStack.layers.pop();
        	}
        }
    

M qml/EditContact.qml => qml/EditContact.qml +5 -3
@@ 9,9 9,11 @@ Kirigami.Page {
	property string contactName: ""
	property string contactTel: ""
	property string currentTel: ""
	property string uuid: ""
	Component.onCompleted: {
		editContact.contactTel = signalState.current;
        editContact.currentTel = signalState.current;
		editContact.contactTel = signalState.currentTel;
		editContact.uuid = signalState.current;
        editContact.currentTel = signalState.currentTel;
        editContact.contactName = signalState.currentName;
	}
	ColumnLayout {


@@ 60,7 62,7 @@ Kirigami.Page {
				text: "Save changes"
				onClicked: {
					console.log("Sending edit contact");
					signal.edit_contact(editContactName.text, editContactTel.text, editContact.currentTel)
					signal.edit_contact(editContactName.text, editContactTel.text, editContact.uuid)
					window.pageStack.layers.pop();
				}
			}

M qml/Settings.qml => qml/Settings.qml +6 -30
@@ 4,53 4,29 @@ import QtQuick.Layouts 1.15
import org.kde.kirigami 2.13 as Kirigami

Kirigami.Page {
    title: qsTr("Settings")
    title: "Settings"
    id: settingsPage
    anchors.fill: parent
    ColumnLayout {
        anchors.fill: parent
    	Layout.fillWidth: true
    	Row {
    		Layout.leftMargin: 10
    		Layout.topMargin: 20
    		Layout.alignment: Qt.AlignTop
    		Label {
    			font.pixelSize: Qt.application.font.pixelSize * 1.3
    			text: "Theme Selection"
                color: theme.text
    		}
    	}
    	Row {
    		Layout.leftMargin: 15
    		Layout.alignment: Qt.AlignTop
		    ComboBox {
		    	Component.onCompleted: {
		    		currentIndex = indexOfValue(theme.current);
		    	}
		    	onActivated: {
		    		theme.setTheme(textAt(index));
		    	}
		    	model: ['Signal', 'System']
		    }
    	}
        Row {
            Layout.leftMargin: 10
            Layout.topMargin: 20
            Layout.alignment: Qt.AlignTop
            Label {
                font.pixelSize: Qt.application.font.pixelSize * 1.3
                text: "Desktop View"
                color: theme.text
                text: "Enable Notifications"
            }
        }
        Row {
            Layout.leftMargin: 15
            Layout.alignment: Qt.AlignTop
            CheckBox {
                id: desktopCheckBox
                id: notifCheckBox
                Component.onCompleted: {
                    desktopCheckBox.checked = signalState.desktopView;
                    // TODO: Make actually work
                }
                onClicked: {
                    signalState.desktopView = desktopCheckBox.checked;
                }
            }
        }

M qml/SignalState.qml => qml/SignalState.qml +5 -0
@@ 7,6 7,7 @@ Item {
    property string current: ""
    property string currentName: ""
    property string currentView: ""
    property string currentTel: ""
    property var currentChat : {}
    property var headerItems: []
    property var editGroupMembersModel: ListModel {}


@@ 27,10 28,14 @@ Item {
                break;
            case "chatHistory":
                if (!signalState.desktopView) {
                                console.log("show");

                    window.pageStack.push(messagesViewStack);
                }
                break;
            case "editContact":
                let contact = signalState.contact(signalState.current);
                signalState.currentTel = contact.Tel;
                window.pageStack.layers.push(Qt.resolvedUrl("EditContact.qml"));
                break;
            case "editGroup":

M qml/main.cpp => qml/main.cpp +0 -1
@@ 17,6 17,5 @@ int main(int argc, char *argv[])
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

M qml/main.qml => qml/main.qml +1 -127
@@ 9,7 9,7 @@ Kirigami.ApplicationWindow {
    width: 640
    height: 470
    visible: true
    title: signal.name
    title: "Signal-rs"
    color: theme.background
    Theme {
        id: theme


@@ 34,14 34,11 @@ Kirigami.ApplicationWindow {
        }
        name: "signal"
        onReady: {
            //Kirigami.Settings.setIsMobile(true);
            signalState.currentView = "chats";
            window.pageStack.replace(chatViewStack);
            //toolButton.enabled = true;
        }
    }
    pageStack.initialPage: Kirigami.Page {
    //anchors.fill: stackView
    objectName: "loadingpage"
    title: ""
    background:Rectangle {


@@ 78,129 75,6 @@ Kirigami.ApplicationWindow {
            }
        ]
    }
    /*header: ToolBar {
        id: headerToolBar
        background: Rectangle {
            color: theme.background
        }
        contentHeight: toolButton.implicitHeight
        ToolButton {
            enabled: false
            id: toolButton
            text: stackView.depth > 1 ? "\u25C0" : "\u2630"
            font.pixelSize: Qt.application.font.pixelSize * 1.6
            onClicked: {
                if (stackView.depth > 1) {
                    stackView.pop()
                    signal.show_view(stackView.currentItem.objectName);
                    signalState.currentView = stackView.currentItem.objectName;
                } else {
                    drawer.open()
                }
            }
        }
        Label {
            text: stackView.currentItem.title
            anchors.centerIn: parent
            font.pixelSize: Qt.application.font.pixelSize * 1.3
            color: theme.text
        }
        Item {
            anchors.right: parent.right
            anchors.top: parent.top
            id: headerBarDynamic
            ToolButton {
                visible: signalState.currentView === "chatHistory"
                id: editContactButton
                text: "Edit"
                anchors.right: parent.right
                anchors.rightMargin: 5
                anchors.top: parent.top
                anchors.topMargin: 5
                font.pixelSize: Qt.application.font.pixelSize * 1.1
                onClicked: {
                    signalState.openView("editContact");
                }
            }
            ToolButton {
                visible: signalState.currentView === "chats" 
                id: createChatButton
                text: "Create Chat"
                anchors.right: parent.right
                anchors.rightMargin: 5
                font.pixelSize: Qt.application.font.pixelSize * 1.1
                anchors.top: parent.top
                anchors.topMargin: 5
                onClicked: {
                    signalState.openView("createChat");
                }
            }
        }
    }
    Drawer {
        id: drawer
        width: window.width * 0.66
        height: window.height
        background: Rectangle {
            color: theme.background
        }
        Column {
            anchors.fill: parent
            ItemDelegate {
                width: parent.width
                onClicked: {
                    signalState.openView("chats");
                    drawer.close()
                }
                contentItem: Text {
                    text: "Chats"
                    color: theme.text
                }
            }
            ItemDelegate {
                width: parent.width
                onClicked: {
                    signalState.openView("createContact");
                    drawer.close()
                }
                contentItem: Text {
                    text: "Create Contact"
                    color: theme.text
                }
            }
            ItemDelegate {
                width: parent.width
                onClicked: {
                    signalState.openView("settings");
                    drawer.close()
                }
                contentItem : Text {
                    text: "Settings"
                    color: theme.text
                }
            }
        }
    }
    Row {
        anchors.fill: parent
        StackView {
            id: stackView
            initialItem: "loading.qml"
            anchors.fill: signalState.desktopView ? parent : undefined
            x: 0
            width: signalState.desktopView ? window.width * 0.33 : window.width
            height: window.height - headerToolBar.height
        }
        Loader {
            x: window.width * 0.33
            id: msgDesktopViewLoader
            sourceComponent: signalState.desktopView ? messagesViewStack : undefined
            height:  signalState.desktopView ? window.height - headerToolBar.height : 0
            width:  signalState.desktopView ? window.width * 0.66 : 0
            enabled: signalState.desktopView
            active: signalState.desktopView
        }
    }*/
    Component {
        id: chatViewStack
        ChatList {}

A qml/registration/Registration.qml => qml/registration/Registration.qml +33 -0
@@ 0,0 1,33 @@
import QtQuick 2.12
import QtQuick.Controls 2.5
import SignalUI 0.1
import QtQuick.Layouts 1.15
import QtQuick.Controls.Material 2.12
import org.kde.kirigami 2.13 as Kirigami
Kirigami.ApplicationWindow {
	id: window
	title: "Register for Signal"
	pageStack.initalPage: Kirigami.Page {
	title: "Signal Registration"
	ColumnLayout {
		Row {
			Button {
				id: regNewBut
				text: "Register as a new device"
				onClicked: {
					window.pageStack.push(Qt.resolveUrl("registration/RegistrationNew.qml"));
				}
			}
		}
		Row {
			Button {
				id: regLinkBut
				text: "Link as a secondary device"
				onClicked: {
					window.pageStack.push(Qt.resolveUrl("registration/RegistrationLink.qml"));
				}
			}
		}
	}
}
}
\ No newline at end of file

A qml/registration/RegistrationLink.qml => qml/registration/RegistrationLink.qml +13 -0
@@ 0,0 1,13 @@
import QtQuick 2.12
import QtQuick.Controls 2.5
import SignalUI 0.1
import QtQuick.Layouts 1.15
import QtQuick.Controls.Material 2.12
import org.kde.kirigami 2.13 as Kirigami
Kirigami.Page {
	id: regLinkPage
	title: "Register as a new device"
	ColumnLayout {
		
	}
}
\ No newline at end of file

A qml/registration/RegistrationNew.qml => qml/registration/RegistrationNew.qml +22 -0
@@ 0,0 1,22 @@
import QtQuick 2.12
import QtQuick.Controls 2.5
import SignalUI 0.1
import QtQuick.Layouts 1.15
import QtQuick.Controls.Material 2.12
import org.kde.kirigami 2.13 as Kirigami
Kirigami.Page {
	id: regLinkPage
	title: "Register as a secondary device"
	ColumnLayout {
		Label {
			text: "When you press request, a QR code will open in your OS's default image viewer. Scan it to continue with the registration process"
		}
		Label {
			text: "Device Name"
		}
		TextField {
			id: linkDevName

		}
	}
}
\ No newline at end of file

D signal-rs.apparmor => signal-rs.apparmor +0 -4
@@ 1,4 0,0 @@
{
    "policy_groups": [],
    "policy_version": 16.04
}

D signal-rs.desktop => signal-rs.desktop +0 -7
@@ 1,7 0,0 @@
[Desktop Entry]
Name=signal-rs
Exec=signal-rs %U
Icon=assets/logo.svg
Terminal=false
Type=Application
X-Ubuntu-Touch=true

M src/config.rs => src/config.rs +5 -9
@@ 1,12 1,13 @@
use serde::*;
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
pub struct Config {
    pub number: Option<String>,
    #[serde(default)]
    pub theme: Theme,
    pub scli: Option<SignalCLIConfig>,
    #[serde(default)]
    pub notifications: NotificationPolicy,
    #[serde(default = "default_data")]
    pub data_dir: String,
}
fn default_data() -> String {
    return "~/.config/signal-rs-data".to_string();
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
pub enum NotificationPolicy {


@@ 28,8 29,3 @@ impl Default for Theme {
        Theme::System
    }
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SignalCLIConfig {
    pub username: String,
    pub data_dir: String,
}

D src/icon.png => src/icon.png +0 -0
M src/main.rs => src/main.rs +126 -244
@@ 16,62 16,34 @@
#![feature(type_alias_impl_trait)]
#![feature(async_closure)]
#[macro_use]
extern crate cstr;
#[macro_use]
extern crate cpp;
#[macro_use]
extern crate qmetaobject;
extern crate confy;
extern crate crossbeam_channel;
extern crate dbus;
extern crate enum_variant_type;
extern crate futures_util;
extern crate glib;
extern crate mio;
extern crate notify_rust;
extern crate reqwest;
extern crate serde;
#[macro_use]
extern crate pallet;
extern crate dbus_tokio;
extern crate home;
extern crate serde_json;

extern crate tempfile;
use std::io::Read;
use std::sync::Arc;
extern crate tokio;
use std::ffi::CStr;
extern crate tungstenite;
#[macro_use]
extern crate lazy_static;
use dbus::*;
use dbus_tokio::connection;
use cpp::cpp;
use presage::config::*;
use presage::prelude::*;
use presage::*;
use qmetaobject::QAbstractListModel;
use qmetaobject::*;
use std::ffi::CStr;
use std::io::Read;
use tokio::sync::mpsc::*;

#[allow(non_snake_case)]
mod signal;
use signal::*;
use std::collections::*;

mod config;
use config::*;
use gettextrs::{bindtextdomain, textdomain};
use notify_rust::Notification;
use signal::signal_cli::*;
use std::cell::*;
use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::rc::*;
use std::result::Result;
mod scli;
mod presage_manager;
use presage_manager::*;
#[derive(QObject, Default)]
pub struct SignalUI {
    base: qt_base_class!(trait QObject),
    state: Option<SignalState>,
    state: Option<Presage>,
    name: qt_property!(QString; NOTIFY name_changed),
    name_changed: qt_signal!(),
    init_state: qt_method!(fn(&mut self)),


@@ 106,22 78,12 @@ impl SignalUI {
        });
    }
    fn group(&self, chat_id: String) -> String {
        serde_json::to_string(
            &self
                .state
                .as_ref()
                .unwrap()
                .sbackend
                .group(&chat_id)
                .unwrap(),
        )
        .unwrap()
        serde_json::to_string(&self.state.as_ref().unwrap().group(&chat_id).unwrap()).unwrap()
    }
    fn send_res(&self, resp: SignalResponse) {
        self.state
            .as_ref()
            .unwrap()
            .sbackend
            .res_sender
            .send(resp)
            .expect("Couldn't send SignalResponse");


@@ 130,7 92,6 @@ impl SignalUI {
        self.state
            .as_ref()
            .unwrap()
            .sbackend
            .req_sender
            .send(reqp)
            .expect("Couldn't send SignalRequest");


@@ 139,14 100,7 @@ impl SignalUI {
        self.send_res(SignalResponse::RmMessage { chat_id, sent_at });
    }
    fn avatar(&self, id: String) -> QString {
        QString::from(
            self.state
                .as_ref()
                .unwrap()
                .sbackend
                .avatar(&id)
                .unwrap_or_default(),
        )
        QString::from(self.state.as_ref().unwrap().avatar(&id).unwrap_or_default())
    }
    fn set_avatar(&self, id: String, path: String) -> String {
        let mut data: Vec<u8> = vec![];


@@ 154,28 108,20 @@ impl SignalUI {
            .unwrap()
            .read_to_end(&mut data)
            .unwrap();
        self.state.as_ref().unwrap().sbackend.set_avatar(id, &data)
        self.state.as_ref().unwrap().set_avatar(id, &data)
    }
    fn contact(&self, tel: String) -> String {
        let fetched = self
            .state
            .as_ref()
            .unwrap()
            .sbackend
            .contact(&tel)
            .unwrap_or_default();
        serde_json::to_string(&fetched).unwrap()
    }
    fn update_chat(&mut self, chat_id: String) {
        let name = self.resolve_name(chat_id.clone());
        if let Some(msgs) = self
            .state
            .as_ref()
            .unwrap()
            .sbackend
            .messages_id(&chat_id)
            .ok()
        {
        if let Some(msgs) = self.state.as_ref().unwrap().messages_id(&chat_id).ok() {
            if msgs.len() > 0 {
                let latest = msgs.values().max_by_key(|x| x.sent_at).unwrap();
                let mut chats = self.chats.borrow_mut();


@@ 186,7 132,6 @@ impl SignalUI {
                        chat.last = latest.message.clone();
                        chat.timestamp = latest.sent_at;
                        chat.messages = vec![latest.clone()];
                        //chats.change_line(i, chat);
                        chats.remove(i);
                        chats.insert(0, chat);
                        break;


@@ 213,32 158,32 @@ impl SignalUI {
            attachment,
        });
    }
    fn resolve_name(&mut self, source: String) -> String {
        self.state.as_mut().unwrap().resolve_name(source)
    }
    fn send_group_message(&self, chat_id: String, message: String, attachment: String) {
        let attachment = match attachment.len() {
            0 => None,
            _ => Some(vec![attachment]),
        };
        self.send_req(SignalRequest::SendGroupMessage {
            chat_id,
        let group = self.state.as_ref().unwrap().group(&chat_id).unwrap();
        self.send_req(SignalRequest::SendMessageToGroup {
            members: group.members,
            message,
            attachment,
            id: chat_id,
            master_key: group.master_key,
        });
    }
    fn add_contact(&self, name: String, tel: String) {
        self.send_req(SignalRequest::AddContact { name, phone: tel });
    }
    fn show_view(&mut self, view: String) {
        self.state.as_mut().unwrap().show_view(view);
        self.send_req(SignalRequest::EditContact {
            name,
            phone: tel,
            id: "".to_string(),
        });
    }
    fn show_chat(&mut self, view: String) {
        self.messages.replace(
            self.state
                .as_ref()
                .unwrap()
                .sbackend
                .messages_id(view.as_str())
                .unwrap()
                .values()


@@ 249,19 194,20 @@ impl SignalUI {
        );
        self.msgs_changed();
        self.current_messages = view.clone();
        self.state.as_ref().unwrap().show_chat(view);
        if let Some(map) = self.state.as_ref().unwrap().messages_id(&view).ok() {
            self.state
                .as_ref()
                .unwrap()
                .res_sender
                .send(SignalResponse::HistoryMessage {
                    phone: view,
                    messages: map.values().cloned().collect(),
                })
                .expect("Couldn't send HistoryMessage");
        }
    }
    fn init_state(&mut self) {
        //let config: Config = confy::load("signal-rs").expect("Couldn't handle config file");
        let config = Config {
            number: Some("+14254920949".to_string()),
            theme: Theme::System,
            scli: Some(SignalCLIConfig {
                username: "+14254920949".to_string(),
                data_dir: "data".to_string(),
            }),
            notifications: NotificationPolicy::Enabled,
        };
        let config: Config = confy::load("signal-rs").expect("Couldn't handle config file");
        let qptr = QPointer::from(&*self);
        let cfg = config.clone();
        let process_res = queued_callback(move |res: SignalResponse| {


@@ 274,8 220,13 @@ impl SignalUI {
                        slf.chats.borrow_mut().reset_data(chats.clone());
                        slf.chats_changed();
                    }
                    SignalResponse::ContactList(_) => {
                        slf.update_chats();
                    }
                    SignalResponse::ShowView(to_show) => {
                        slf.show_view(to_show);
                    }
                    SignalResponse::ModelContactList(contacts) => {
                        println!("Rec up contacts");
                        slf.contacts.borrow_mut().reset_data(contacts);
                        slf.contacts_changed();
                    }


@@ 311,13 262,13 @@ impl SignalUI {
                                }
                            }
                        }
                        println!("Ge");
                    }
                    SignalResponse::Groups(_) => {
                        let ids = slf
                            .state
                            .as_mut()
                            .unwrap()
                            .sbackend
                            .msg_store
                            .all()
                            .unwrap()


@@ 330,16 281,11 @@ impl SignalUI {
                        for id in ids {
                            if !done.contains_key(id.as_str()) {
                                done.insert(id.clone(), true);
                                slf.state
                                    .as_mut()
                                    .unwrap()
                                    .sbackend
                                    .resolve_name(&id)
                                    .unwrap();
                                slf.state.as_mut().unwrap().resolve_name(&id).unwrap();
                            }
                        }
                        println!("Finished initialization");
                        slf.state.as_ref().unwrap().update_contacts();
                        slf.update_contacts();
                        slf.show_view("chats".to_string());
                        slf.ready();
                        slf.signalResponse(serde_json::to_string(&res).unwrap());


@@ 352,21 298,12 @@ impl SignalUI {
            glib::MainContext::channel::<SignalResponse>(glib::PRIORITY_DEFAULT);
        let l_res_sender = res_sender.clone();
        let (req_sender, req_receiver) = unbounded_channel::<SignalRequest>();
        let clconf = config.clone();
        let (resource, conn) = connection::new_session_sync().expect("Couldn't connect to DBus");
        qmetaobject::future::execute_async(async {
            let err = resource.await;
            println!("Lost connection to DBus: {}", err);
        });
        let mut state: SignalState = SignalState::new(
            req_sender.clone(),
            l_res_sender.clone(),
            config.clone(),
            conn.clone(),
        );
        state.user_tel = config.number.clone();

        self.state = Some(state);
        self.state = Some(Presage::new(
            l_res_sender,
            config.clone(),
            req_sender.clone(),
        ));
        res_receiver.attach(None, move |value| {
            process_res(value);
            glib::Continue(true)


@@ 377,56 314,26 @@ impl SignalUI {
        req_sender
            .send(SignalRequest::GetGroups)
            .expect("Couldn't send GetGroups");
        res_sender
            .send(SignalResponse::Groups(vec![]))
            .expect("Couldn't send Groups");
        let cstore_path = PathBuf::from(
            config
                .clone()
                .data_dir
                .clone()
                .replace("~", home::home_dir().unwrap().to_str().unwrap()),
        );
        qmetaobject::future::execute_async(async move {
            scli::SCLISession::run(res_sender, req_receiver, clconf.scli.unwrap(), conn).await;
            Presage::run(
                presage::config::SledConfigStore::new(cstore_path.join(PathBuf::from("cstore")))
                    .unwrap(),
                res_sender,
                req_receiver,
            )
            .await;
        });
    }
}
/// The main state manager/UI controller
pub struct SignalState {
    pub current: Option<String>,
    pub req_sender: UnboundedSender<SignalRequest>,
    pub last_id: Rc<RefCell<Option<String>>>,
    pub res_sender: glib::Sender<SignalResponse>,
    pub current_chat: Rc<RefCell<Option<String>>>,
    pub user_tel: Option<String>,
    pub sbackend: std::boxed::Box<scli::SCLISession>,
    pub config: Rc<RefCell<Config>>,
}
/// An enum that represents a view that can be displayed.
#[derive(Clone, Debug)]
pub enum View {
    Chats,
    /// Contains the telephone number of the chat to show
    Messages(String),
    Password,
    CreateChat,
    CreateContact,
    /// Contains the ID of the contact to edit
    EditContact(String),
    Settings,
}
impl SignalState {
    /// Build a new signal state using a websocket connection
    pub fn new(
        req_sender: UnboundedSender<SignalRequest>,
        res_sender: glib::Sender<SignalResponse>,
        config: Config,
        conn: Arc<nonblock::SyncConnection>,
    ) -> SignalState {
        SignalState {
            current: None,
            req_sender: req_sender.clone(),
            last_id: Rc::new(RefCell::new(None)),
            res_sender: res_sender.clone(),
            current_chat: Rc::new(RefCell::new(None)),
            user_tel: None,
            config: Rc::new(RefCell::new(config.clone())),
            sbackend: std::boxed::Box::new(scli::SCLISession::new(
                res_sender, config, conn, req_sender,
            )),
        }
    }
    /// Shows the given view in the app_box
    pub fn show_view(&mut self, view: String) {
        match view.as_ref() {


@@ 442,12 349,14 @@ impl SignalState {
    pub fn update_chats(&mut self) {
        let mut constructed: Vec<String> = vec![];
        let mut chats: Vec<Chat> = self
            .sbackend
            .state
            .as_ref()
            .unwrap()
            .contacts()
            .values()
            .enumerate()
            .filter_map(|(i, contact)| {
                if let Some(msgs) = self.sbackend.messages_id(&contact.tel).ok() {
                if let Some(msgs) = self.state.as_ref().unwrap().messages_id(&contact.tel).ok() {
                    if msgs.len() > 0 {
                        let latest = msgs.values().max_by_key(|x| x.sent_at).unwrap();
                        constructed.push(contact.tel.clone());


@@ 465,9 374,16 @@ impl SignalState {
                None
            })
            .collect();
        for (i, (gid, group)) in self.sbackend.groups().into_iter().enumerate() {
            if let Some(msgs) = self.sbackend.messages_id(&gid).ok() {
                //println!("mgsgsgs{:?}", msgs);
        for (i, (gid, group)) in self
            .state
            .as_ref()
            .unwrap()
            .groups()
            .unwrap()
            .into_iter()
            .enumerate()
        {
            if let Some(msgs) = self.state.as_ref().unwrap().messages_id(&gid).ok() {
                if msgs.len() > 0 {
                    let latest = msgs.values().max_by_key(|x| x.sent_at).unwrap();
                    constructed.push(gid.clone());


@@ 485,7 401,9 @@ impl SignalState {
            }
        }
        for (i, (id, msgs)) in self
            .sbackend
            .state
            .as_ref()
            .unwrap()
            .messages()
            .unwrap()
            .into_iter()


@@ 509,86 427,74 @@ impl SignalState {
            }
        }
        chats.sort_by_key(|c| -1 * c.timestamp);
        self.res_sender
            .send(SignalResponse::ChatList(chats))
            .expect("Couldn't send ChatList");
        self.send_res(SignalResponse::ChatList(chats));
    }
    pub fn resolve_name(&mut self, source: String) -> String {
        return self.sbackend.resolve_name(source.as_str()).unwrap();
        return self
            .state
            .as_mut()
            .unwrap()
            .resolve_name(source.as_str())
            .unwrap();
    }
    pub fn update_contacts(&self) {
        let mut contacts: Vec<Contact> = self
            .sbackend
            .state
            .as_ref()
            .unwrap()
            .contacts()
            .into_iter()
            .map(|(_k, v)| v)
            .collect();
        contacts.sort_by_key(|x| x.name.clone());
        self.res_sender
            .send(SignalResponse::ModelContactList(contacts))
            .expect("Couldn't send ContactList");
    }
    /// Shows a given chat by tel
    pub fn show_chat(&self, chat_id: String) {
        println!("{}", chat_id);
        if let Some(map) = self.sbackend.messages_id(&chat_id).ok() {
            println!("{:?}", map);
            self.res_sender
                .send(SignalResponse::HistoryMessage {
                    phone: chat_id,
                    messages: map.values().cloned().collect(),
                })
                .expect("Couldn't send HistoryMessage");
        }
    }
    /// Processes an incoming SignalResponse and modifies state/UI accordingly
    pub fn process_response(&mut self, response: SignalResponse) {
        self.sbackend.process_response(response.clone());
        match response {
            SignalResponse::Quit => {}
            SignalResponse::ShowTheme(_theme) => {}
            SignalResponse::ShowView(to_show) => {
                self.show_view(to_show);
            }
            SignalResponse::ContactList(_) => {
                self.update_chats();
            }
            SignalResponse::Type(typ) => match typ.as_ref() {
                "getEncryptionPw" => {}
                "registrationDone" => {}
                _ => {
                    println!("Type: {}", typ);
                }
            },
            _ => {}
        }
        self.send_res(SignalResponse::ModelContactList(contacts));
    }
}
/// An enum that represents a view that can be displayed.
#[derive(Clone, Debug)]
pub enum View {
    Chats,
    /// Contains the telephone number of the chat to show
    Messages(String),
    Password,
    CreateChat,
    CreateContact,
    /// Contains the ID of the contact to edit
    EditContact(String),
    Settings,
}

mod qrc;

#[tokio::main]
async fn main() -> Result<(), std::boxed::Box<dyn std::error::Error>> {
    std::process::Command::new("pkill")
        .arg("-f")
        .arg("-SIGKILL")
        .arg("signal-cli")
        .spawn()
        .unwrap()
        .wait()
        .unwrap();
    init_gettext();
    /*let conf: Config = confy::load("signal-rs").unwrap();
    let data_dir = conf
        .data_dir
        .replace("~", home::home_dir().unwrap().to_str().unwrap());
    let config_store = presage::config::SledConfigStore::new(
        PathBuf::from(data_dir).join(PathBuf::from("cstore")),
    )
    .unwrap();*/
    /*let mut manager = Manager::with_config_store(config_store, ProtocolContext::default()).unwrap();
    manager
        .link_secondary_device(
            libsignal_service::configuration::SignalServers::Production,
            "pre".to_string(),
        )
        .await
        .unwrap();*/
    unsafe {
        cpp! { {
            #include <QtCore/QCoreApplication>
            #include <QtCore/QString>
        }}
        cpp! ([] {
            QCoreApplication::setApplicationName(QStringLiteral("signal-rs-ut.nicohman"));
            QCoreApplication::setApplicationName(QStringLiteral("signal-rs.nicohman"));
            QCoreApplication::setOrganizationDomain(QStringLiteral("nicohman.com"));
            QCoreApplication::setOrganizationName(QStringLiteral("signal-rs-ut.nicohman"));
            QCoreApplication::setOrganizationName(QStringLiteral("signal-rs.nicohman"));
        });
    }
    //QQuickStyle::set_style("Suru");
    qrc::load();
    qml_register_type::<SignalUI>(
        CStr::from_bytes_with_nul(b"SignalUI\0").unwrap(),


@@ 600,29 506,5 @@ async fn main() -> Result<(), std::boxed::Box<dyn std::error::Error>> {
    engine.load_file("qrc:/qml/main.qml".into());
    engine.exec();
    println!("Shutting down");
    std::process::Command::new("pkill")
        .arg("-f")
        .arg("-SIGKILL")
        .arg("signal-cli")
        .spawn()
        .unwrap()
        .wait()
        .unwrap();
    Ok(())
}

fn init_gettext() {
    let domain = "signal-rs-ut.nicohman";
    textdomain(domain);

    let app_dir = env::var("APP_DIR").expect("Failed to read the APP_DIR environment variable");

    let mut app_dir_path = PathBuf::from(app_dir);
    if !app_dir_path.is_absolute() {
        app_dir_path = PathBuf::from("/usr");
    }

    let path = app_dir_path.join("share/locale");

    bindtextdomain(domain, path.to_str().unwrap());
}

A src/presage_manager.rs => src/presage_manager.rs +628 -0
@@ 0,0 1,628 @@
use crate::*;
use anyhow::{Context, Result};
use futures_util::StreamExt;
use pallet::Document;
use pallet::Store;
use std::fs;
use std::path::Path;
use zkgroup::api::groups::group_params::*;
pub struct Presage {
    pub msg_store: Store<SignalMessage>,
    pub contact_store: Store<Contact>,
    pub group_store: Store<Group>,
    pub req_sender: UnboundedSender<SignalRequest>,
    pub whoami: Option<Uuid>,
    pub res_sender: glib::Sender<SignalResponse>,
}
impl Presage {
    pub async fn run(
        config_store: SledConfigStore,
        res_sender: glib::Sender<SignalResponse>,
        mut req_receiver: UnboundedReceiver<SignalRequest>,
    ) {
        let (tx, mut rx) = futures::channel::mpsc::channel::<(Metadata, ContentBody)>(100);
        let mut manager = Manager::with_config_store(config_store, ProtocolContext::default())
            .expect("Couldn't create Manager");
        let whoami = manager.whoami().await.unwrap().uuid.to_string();
        let cloned_man = manager.clone();
        let x = cloned_man.receive_messages(tx);
        let mut fut = Box::pin(x);
        loop {
            tokio::select! {
                    inc = rx.next() => {
                        if let Some(response) = inc {
                            println!("{:?}", response);
                            res_sender.send(SignalResponse::Metadata(response.0.clone())).expect("Couldn't send Metadata");

                            match response.1.clone() {
                ContentBody::DataMessage(dm) => {
                    if let Some(react) = dm.reaction {
                        if react.target_author_uuid.is_some() && react.emoji.is_some() && react.target_sent_timestamp.is_some() {
                            if let Some(remove) = react.remove {
                                if remove {
                                    //Remove reaction
                                    continue;
                                }
                            res_sender.send(SignalResponse::AddReaction{
                                target: react.target_author_uuid.unwrap().clone(),
                                emoji: react.emoji.unwrap(),
                                target_timestamp: react.target_sent_timestamp.unwrap() as i64,
                                timestamp: response.0.timestamp as i64,
                                from: response.0.sender.identifier()
                            }).expect("Couldn't send AddReaction");
                            }
                        }
                    }
                    if let Some(body) = dm.body {
                        let mut chat_id = response
                            .0
                            .sender
                            .identifier();
                        if let Some(gc) = dm.group {
                            chat_id = Presage::get_gid(gc.id.unwrap());
                            let g = Group {
                                name: gc.name.unwrap_or_default(),
                                members: gc.members_e164,
                                id: chat_id.clone(),
                                master_key: None
                            };
                            res_sender.send(SignalResponse::SGroup(g)).expect("Couldn't send SGroup");
                        }
                        if let Some(gc2) = dm.group_v2 {
                            let gid = Presage::get_gid(gc2.master_key.clone().unwrap());
                            chat_id = gid;
                            res_sender.send(SignalResponse::GroupV2Context(gc2)).expect("Couldn't send GroupV2Context");
                        }
                        let msg = SignalMessage {
                            source: response
                                .0
                                .sender
                                .identifier(),
                            chat_id: chat_id,
                            sent_at: dm.timestamp.unwrap() as i64,
                            ID: 0,
                            message: body,
                            reactions: vec![],
                            attachment: Some(vec![]),
                            outgoing: response.0.sender.identifier() == whoami,
                        };
                        res_sender.send(SignalResponse::IDLessMessage(msg)).expect("Couldn't send IDLessMessage");
                    } else {
                    }
                }
                ContentBody::SynchronizeMessage(sm) => {
                    if let Some(sent) = sm.sent {
                        if let Some(dm) = sent.message {
                                                if let Some(react) = dm.reaction {
                        if react.target_author_uuid.is_some() && react.emoji.is_some() && react.target_sent_timestamp.is_some() {
                            if let Some(remove) = react.remove {
                                if remove {
                                    //Remove reaction
                                    continue;
                                }
                            res_sender.send(SignalResponse::AddReaction{
                                target: react.target_author_uuid.unwrap().clone(),
                                emoji: react.emoji.unwrap(),
                                target_timestamp: react.target_sent_timestamp.unwrap() as i64,
                                timestamp: response.0.timestamp as i64,
                                from: response.0.sender.identifier()
                            }).expect("Couldn't send AddReaction");
                            }


                        }
                    }
                            if let Some(body) = dm.body {
                                let mut chat_id = sent
                                    .destination_uuid.
                                    unwrap_or(sent.destination_e164.unwrap_or_default());
                                if let Some(gc) = dm.group {
                                    chat_id = Presage::get_gid(gc.id.unwrap());
                                                                let g = Group {
                                name: gc.name.unwrap_or_default(),
                                members: gc.members_e164,
                                id: chat_id.clone(),
                                master_key: None
                            };
                            res_sender.send(SignalResponse::SGroup(g)).expect("Couldn't send SGroup");
                                }
                                if let Some(gc2) = dm.group_v2 {
                                    let gid = Presage::get_gid_v2(gc2.master_key.clone().unwrap());
                                    chat_id = gid;
                                    res_sender.send(SignalResponse::GroupV2Context(gc2)).expect("Couldn't send GroupV2Context");
                                }
                                let msg = SignalMessage {
                                    source: response
                                        .0
                                        .sender.identifier(),
                                    chat_id: chat_id,
                                    sent_at: dm.timestamp.unwrap() as i64,
                                    ID: 0,
                                    message: body,
                                    reactions: vec![],
                                    attachment: Some(vec![]),
                                    outgoing: response.0.sender.identifier() == whoami,
                                };
                                res_sender.send(SignalResponse::IDLessMessage(msg)).expect("Couldn't send IDLessMessage");
                            }
                        }
                    }
                }
                _ => {}
            }

                    }
                    },
                    req = req_receiver.recv() => {
                        println!("{:?}", req);
                        if let Some(req) = req {
                        match req {
                            SignalRequest::SendMessageToGroup { members, message, attachment: _, id, master_key} => {
                                let now = chrono::Utc::now().timestamp_millis() as u64;
                                let msg = DataMessage {
                                    body: Some(message.clone()),
                                    timestamp:Some(now.clone()),
                                    group_v2: Some(GroupContextV2 {
                                        master_key: master_key,
                                        revision: Some(1),
                                        group_change: None
                                    }),
                                    ..Default::default()
                                };
                                manager.send_message_to_group(members.into_iter().map(|x| {
                                    return ServiceAddress::parse(None, Some(&x)).unwrap();
                                }).collect::<Vec<ServiceAddress>>(),  msg, now).await.unwrap();
                                let sm = SignalResponse::IDLessMessage(SignalMessage {
                                    message: message,
                                    source: whoami.clone(),
                                    chat_id: id,
                                    outgoing: true,
                                    attachment: None,
                                    sent_at: now as i64,
                                    ID: 0,
                                    reactions: vec![]
                                });
                                res_sender.send(sm).expect("Couldn't send IDLessMessage");
                            },
                            SignalRequest::SendMessage { to, message, attachment: _ } => {
                                let tos = ServiceAddress::parse(None, Some(to.as_str())).unwrap_or_else(|_| {ServiceAddress::parse(Some(to.as_str()), None).unwrap()});
                                let now = chrono::Utc::now().timestamp_millis() as u64;
                                let msg = DataMessage {
                                    body: Some(message.clone()),
                                    timestamp:Some(now.clone()),
                                    ..Default::default()
                                };
                                manager.send_message(tos.clone(), msg, now).await.unwrap();
                                let sm = SignalResponse::IDLessMessage(SignalMessage {
                                    message: message,
                                    source: to.clone(),
                                    chat_id: to.clone(),
                                    outgoing: true,
                                    attachment: None,
                                    sent_at: now as i64,
                                    ID: 0,
                                    reactions: vec![]
                                });
                                res_sender.send(sm).expect("Couldn't send IDLessMessage");
                            },
                            SignalRequest::EditContact{ name, phone, id } => {
                                res_sender.send(SignalResponse::RmContact(id.clone())).expect("Couldn't send RmContact");
                                res_sender.send(SignalResponse::ContactList(vec![Contact {name, tel: phone, uuid: id}])).expect("Couldn't send ContactList");
                            },
                            SignalRequest::GetGroupCtxV2(ctx) => {
                                let group = manager.get_group_v2(Presage::make_gkey(ctx.master_key.clone().unwrap())).await.unwrap();
                                println!("{:?}", group);
                                let gid = Presage::get_gid_v2(ctx.master_key.clone().unwrap());
                                res_sender.send(SignalResponse::SGroup(Group {
                                    name: group.title,
                                    id: gid,
                                    master_key: ctx.master_key.clone(),
                                    members: group.members.clone().into_iter().map(|x| {
                                        return Uuid::from_slice(&x.uuid).unwrap().to_string();
                                    }).collect()
                                })).expect("Couldn't send SGroup");
                            },
                            _ => {

                            }
                        }
                    }
                    }
                    _ = &mut fut => {

                    }
                }
        }
    }
    pub fn make_gkey(bytes: Vec<u8>) -> GroupMasterKey {
        let mut slice: [u8; 32] = Default::default();
        slice.copy_from_slice(bytes.as_slice());
        GroupMasterKey::new(slice)
    }
    pub fn new(
        res_sender: glib::Sender<SignalResponse>,
        config: Config,
        req_sender: UnboundedSender<SignalRequest>,
    ) -> Presage {
        let data_dir = config
            .data_dir
            .replace("~", home::home_dir().unwrap().to_str().unwrap());
        let m_db = pallet::ext::sled::open(Path::new(&data_dir).join("messages").to_str().unwrap())
            .unwrap();
        let c_db = pallet::ext::sled::open(Path::new(&data_dir).join("contacts").to_str().unwrap())
            .unwrap();
        let g_db =
            pallet::ext::sled::open(Path::new(&data_dir).join("groups").to_str().unwrap()).unwrap();
        fs::create_dir("/tmp/mdb");
        fs::create_dir("/tmp/cdb");
        fs::create_dir("/tmp/gdb");
        let msg_store = pallet::Store::builder()
            .with_db(m_db)
            .with_index_dir("/tmp/mdb")
            .finish()
            .unwrap();
        msg_store.index_all().expect("Couldn't index message store");
        let contact_store = pallet::Store::builder()
            .with_db(c_db)
            .with_index_dir("/tmp/cdb")
            .finish()
            .unwrap();
        contact_store
            .index_all()
            .expect("Couldn't index contact store");

        let group_store = pallet::Store::builder()
            .with_db(g_db)
            .with_index_dir("/tmp/gdb")
            .finish()
            .unwrap();
        group_store.index_all().expect("Couldn't index group store");

        //let uuid = manager.whoami().await.unwrap().uuid;
        let pre = Presage {
            msg_store,
            contact_store,
            group_store,
            whoami: None,
            res_sender,
            req_sender,
        };
        pre.contact_store.all().unwrap().into_iter().for_each(|c| {
            if c.uuid == String::new() || c.tel == String::new() {
                return;
            }
            if let Ok(msgs) = pre.messages_id_doc(c.tel.as_str()) {
                let msgs = msgs
                    .into_iter()
                    .map(|(i, mut d)| {
                        d.chat_id = c.uuid.clone();
                        return d;
                    })
                    .collect::<Vec<Document<SignalMessage>>>();
                pre.msg_store.update_multi(&msgs).unwrap();
            }
        });
        return pre;
    }
    pub fn avatar(&self, id: &str) -> Option<String> {
        None
    }
    pub fn set_avatar(&self, id: String, data: &[u8]) -> String {
        return String::new();
    }
    pub fn get_gid_v2(master_key: Vec<u8>) -> String {
        let mut ar: [u8; 32] = Default::default();
        ar.copy_from_slice(&master_key);
        let secret_params = GroupSecretParams::derive_from_master_key(GroupMasterKey::new(ar));
        let gid = secret_params.get_group_identifier();
        let dec = base64::encode(gid);
        return dec;
    }
    pub fn get_gid(id: Vec<u8>) -> String {
        return base64::encode(id);
    }
    pub fn add_message(&mut self, message: SignalMessage, new: bool) {
        if self.message(&message.chat_id, message.sent_at).is_err() {
            println!("creating");
            self.msg_store
                .create(&message)
                .expect("Couldn't add message to store");
            self.res_sender
                .send(SignalResponse::AddHist(message.clone(), new))
                .expect("Couldn't send AddHist");
        }
    }
    pub fn process_response(&mut self, response: SignalResponse) {
        match response {
            SignalResponse::RmMessage { chat_id, sent_at } => {
                if let Some(doc) = self.message_doc(&chat_id, sent_at).ok() {
                    self.msg_store
                        .delete(doc.id)
                        .expect("Couldn't delete message");
                }
            }
            SignalResponse::Metadata(meta) => {
                if let Ok(mut cont) = self.contact_doc(meta.sender.identifier().as_str()) {
                    if let Some(uuid) = meta.sender.uuid {
                        if uuid.to_string() != cont.inner.uuid {
                            cont.inner.uuid = uuid.to_string();
                            self.contact_store
                                .update(&cont)
                                .expect("Couldn't update contact");
                        }
                    }
                    if let Some(e164) = meta.sender.e164() {
                        if e164.to_string() != cont.inner.tel {
                            cont.inner.tel = e164.to_string();
                            self.contact_store
                                .update(&cont)
                                .expect("Couldn't update contact");
                        }
                    }
                } else {
                    let name = meta
                        .sender
                        .e164()
                        .map(|x| x.to_string())
                        .unwrap_or(meta.sender.uuid.unwrap().to_string());
                    let contact = Contact {
                        name,
                        tel: meta.sender.e164().unwrap_or_default().to_string(),
                        uuid: meta.sender.uuid.unwrap_or_default().to_string(),
                    };
                    self.contact_store.create(&contact).unwrap();
                }
            }
            SignalResponse::IDLessMessage(mut msg) => {
                let mut id = 0;
                if let Some(v) = self.messages_id(&msg.chat_id).ok() {
                    if v.len() > 0 {
                        id = v.values().map(|msg| msg.ID).max().unwrap() + 1;
                    }
                }
                msg.ID = id;
                self.add_message(msg, true);
            }
            SignalResponse::RmContact(tel) => {
                if let Some(contact) = self.contact_doc(&tel).ok() {
                    self.contact_store
                        .delete(contact.id)
                        .expect("Couldn't delete contact");
                }
            }
            /*SignalResponse::Groups(groups) => {
                for group in groups.into_iter() {
                    if let Some(group_p) = self.group(group.id.as_str()) {
                        *group_p = group;
                    } else {
                        self.groups.insert(group.id.clone(), group);
                    }
                }
            }*/
            SignalResponse::GroupV2Context(ctx) => {
                let id = Presage::get_gid_v2(ctx.master_key.clone().unwrap());
                if ctx.group_change.is_some() || self.group_doc(id.as_str()).is_err() {
                    self.req_sender
                        .send(SignalRequest::GetGroupCtxV2(ctx))
                        .expect("Couldn't send GetGroupCtxV2");
                }
            }
            SignalResponse::SGroup(group) => {
                if let Ok(mut g) = self.group_doc(group.id.as_str()) {
                    g.inner = group;
                    self.group_store.update(&g).expect("Couldn't update group");
                } else {
                    self.group_store
                        .create(&group)
                        .expect("Couldn't create group");
                }
            }
            SignalResponse::ContactList(contacts) => {
                for contact in contacts.into_iter() {
                    if let Some(mut contact_p) = self.contact_doc(&contact.tel).ok() {
                        contact_p.inner = contact;
                        self.contact_store
                            .update(&contact_p)
                            .expect("Couldn't update contact");
                    } else {
                        self.contact_store
                            .create(&contact)
                            .expect("Couldn't create contact");
                    }
                }
            }
            SignalResponse::AddReaction {
                target,
                target_timestamp,
                timestamp,
                from,
                emoji,
            } => {
                let reaction = Reaction {
                    emoji,
                    source: from,
                    timestamp,
                };
                if let Some(fetched) = self
                    .msg_store
                    .search(format!("source:{} AND sent_at:{}", target, target_timestamp).as_str())
                    .expect("Couldn't search for reaction message")
                    .hits
                    .into_iter()
                    .next()
                {
                    let mut fetched = fetched.doc;
                    fetched.inner.reactions.push(reaction);
                    self.msg_store
                        .update(&fetched)
                        .expect("Couldn't update reaction message");
                }
            }
            _ => {}
        }
    }
    pub fn messages_id<'a>(&'a self, chat_id: &str) -> Result<BTreeMap<i64, SignalMessage>> {
        let messages = self
            .msg_store
            .search(format!("+chat_id:\"{}\"", chat_id).as_str())?
            .hits;
        Ok(messages
            .into_iter()
            .map(|m| {
                let m = m.doc.inner;
                (m.sent_at, m)
            })
            .collect())
    }
    pub fn messages_id_doc<'a>(
        &'a self,
        chat_id: &str,
    ) -> Result<BTreeMap<i64, Document<SignalMessage>>> {
        let messages = self
            .msg_store
            .search(format!("+chat_id:\"{}\"", chat_id).as_str())?
            .hits;
        Ok(messages
            .into_iter()
            .map(|m| {
                let m = m.doc;
                (m.sent_at, m)
            })
            .collect())
    }
    fn message_doc<'a>(
        &'a self,
        chat_id: &str,
        sent_at: i64,
    ) -> Result<Document<SignalMessage>, anyhow::Error> {
        Ok(self
            .msg_store
            .search(format!("chat_id:{} AND sent_at:{}", chat_id, sent_at).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .clone())
    }
    pub fn group_doc(&self, chat_id: &str) -> Result<Document<Group>> {
        Ok(self
            .group_store
            .search(format!("id:{}", chat_id).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .clone())
    }
    fn contact_doc<'a>(&'a self, tel: &str) -> Result<Document<Contact>, anyhow::Error> {
        Ok(self
            .contact_store
            .search(format!("tel:{} OR uuid:{}", tel, tel).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .clone())
    }
    pub fn resolve_name(&mut self, chat_id: &str) -> Result<String> {
        if let Some(contact) = self.contact(chat_id.clone()).ok() {
            return Ok(contact.name);
        } else if let Ok(group) = self.group(chat_id) {
            return Ok(group.name.clone());
        } else {
            return Ok(chat_id.to_string());
        }
    }
    pub fn messages<'a>(
        &'a self,
    ) -> Result<BTreeMap<String, BTreeMap<i64, SignalMessage>>, anyhow::Error> {
        let mut map: BTreeMap<String, BTreeMap<i64, SignalMessage>> = BTreeMap::new();
        for msg in self.msg_store.all()? {
            let msg = msg.inner;
            if let Some(ind) = map.get_mut(&msg.chat_id) {
                ind.insert(msg.sent_at, msg);
            } else {
                let mut new_map: BTreeMap<i64, SignalMessage> = BTreeMap::new();
                new_map.insert(msg.sent_at, msg.clone());
                map.insert(msg.chat_id.clone(), new_map);
            }
        }
        Ok(map)
    }
    pub fn message<'a>(&'a self, chat_id: &str, sent_at: i64) -> Result<SignalMessage> {
        Ok(self
            .msg_store
            .search(format!("chat_id:{} AND sent_at:{}", chat_id, sent_at).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .inner
            .clone())
    }
    pub fn contact<'a>(&'a self, tel: &str) -> Result<Contact> {
        Ok(self
            .contact_store
            .search(format!("uuid:{} OR tel:{}", tel, tel).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .inner
            .clone())
    }
    pub fn contact_tel<'a>(&'a self, tel: &str) -> Result<Contact> {
        Ok(self
            .contact_store
            .search(format!("tel:{}", tel).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .inner
            .clone())
    }
    pub fn groups<'a>(&'a self) -> Result<HashMap<String, Group>> {
        Ok(self
            .group_store
            .all()?
            .into_iter()
            .map(|x| {
                let x = x.inner;
                (x.id.clone(), x)
            })
            .collect())
    }
    pub fn group(&self, chat_id: &str) -> Result<Group> {
        Ok(self
            .group_store
            .search(format!("id:{}", chat_id).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .inner
            .clone())
    }
    pub fn contacts<'a>(&'a self) -> HashMap<String, Contact> {
        let map: HashMap<String, Contact> = self
            .contact_store
            .all()
            .unwrap()
            .into_iter()
            .map(|x| {
                let x = x.inner;
                (x.tel.clone(), x)
            })
            .collect();
        println!("{}", map.len());
        map
    }
}

M src/qrc.rs => src/qrc.rs +4 -1
@@ 14,7 14,10 @@ qrc!(qml_resources,
        "qml/CreateChat.qml",
        "qml/PictureOverlay.qml",
        "qml/MessageImage.qml",
        "qml/EditGroup.qml"
        "qml/EditGroup.qml",
        "qml/registration/Registration.qml",
        "qml/registration/RegistrationLink.qml",
        "qml/registration/RegistrationNew.qml"
    },
);


D src/scli.rs => src/scli.rs +0 -683
@@ 1,683 0,0 @@
use crate::*;
use anyhow::{Context, Result};
use dbus::message::MatchRule;
use dbus::nonblock;
use futures_util::StreamExt;
use pallet::Document;
use pallet::Store;
use std::fs;
use std::io::BufRead;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
lazy_static! {
    static ref SCLI_ATTACHMENT_DIRECTORY: String = home::home_dir()
        .unwrap()
        .join(".local/share/signal-cli/attachments/")
        .into_os_string()
        .into_string()
        .unwrap();
    static ref SCLI_AVATAR_DIRECTORY: String = home::home_dir()
        .unwrap()
        .join(".local/share/signal-cli/avatars/")
        .into_os_string()
        .into_string()
        .unwrap();
}
pub struct SCLISession {
    pub msg_store: Store<SignalMessage>,
    pub contact_store: Store<Contact>,
    pub config: SignalCLIConfig,
    pub res_sender: glib::Sender<SignalResponse>,
    pub groups: HashMap<String, Group>,
    pub proxy: nonblock::Proxy<'static, Arc<nonblock::SyncConnection>>,
    pub resolved_names: HashMap<String, Option<String>>,
    pub req_sender: UnboundedSender<SignalRequest>,
}
impl SCLISession {
    pub fn merge_ids(&self, from: String, to: String) {
        self.msg_store
            .update_multi(
                self.msg_store
                    .search(format!("chat_id:{}", from).as_str())
                    .unwrap()
                    .hits
                    .into_iter()
                    .map(|hit| {
                        let mut doc = hit.doc;
                        doc.inner.chat_id = to.clone();
                        doc
                    })
                    .collect::<Vec<Document<SignalMessage>>>()
                    .as_slice(),
            )
            .expect("Couldn't merge chatids");
    }
    pub fn delete_all(&self, all: String) {
        let ids: Vec<u64> = self
            .msg_store
            .search(format!("chat_id:{}", all).as_str())
            .unwrap()
            .hits
            .into_iter()
            .map(|x| {
                return x.doc.id;
            })
            .collect();
        self.msg_store.delete_multi(&ids);
    }
    pub fn fetch_contacts_scli(username: &str) -> Vec<Contact> {
        let output = Command::new("signal-cli")
            .arg("--username")
            .arg(username)
            .arg("listContacts")
            .output()
            .expect("Couldn't start listContacts");
        let res = BufRead::lines(output.stdout.as_slice())
            .into_iter()
            .filter_map(|s| SignalCLIContactInfo::from_str(s.unwrap()))
            .map(|ci| Contact {
                tel: ci.number,
                name: ci.name,
            })
            .collect();
        res
    }
    pub async fn run(
        res_sender: glib::Sender<SignalResponse>,
        mut req_receiver: UnboundedReceiver<SignalRequest>,
        config: SignalCLIConfig,
        conn: Arc<nonblock::SyncConnection>,
    ) {
        let (line_sender, mut line_receiver) =
            tokio::sync::mpsc::unbounded_channel::<SignalCLIContainer>();
        let clconf = config.clone();
        std::thread::spawn(move || {
            let mut cmd = std::process::Command::new("signal-cli");
            cmd.arg("-u")
                .arg(&clconf.username)
                .arg("daemon")
                .arg("--json")
                .stdout(Stdio::piped());
            let mut child = cmd.spawn().expect("Couldn't run signal-cli");
            let stdout = child.stdout.take().unwrap();
            let mut reader = std::io::BufReader::new(stdout).lines();
            let mut log_file = fs::OpenOptions::new()
                .read(true)
                .append(true)
                .open("/home/nicohman/scli.log")
                .unwrap();
            loop {
                let msg_line = reader.next().unwrap().unwrap();
                println!("{}", msg_line);
                write!(log_file, "{}\n", msg_line).unwrap();
                log_file.flush();
                if let Some(parsed) = serde_json::from_str::<SignalCLIContainer>(&msg_line).ok() {
                    line_sender
                        .send(parsed)
                        .expect("Couldn't send parsed line from scli daemon");
                } else {
                    println!("Could not parsed SCLI message {}", msg_line);
                }
            }
        });
        let proxy = nonblock::Proxy::new(
            "org.asamk.Signal",
            "/org/asamk/Signal",
            Duration::from_secs(10),
            conn.clone(),
        );
        let mr_mr = MatchRule::new_signal("org.asamk.Signal", "MessageReceived");
        let sy_mr = MatchRule::new_signal("org.asamk.Signal", "SyncMessageReceived");
        let _inc = conn.add_match(mr_mr).await.unwrap().msg_stream();
        let _inc_sync = conn.add_match(sy_mr).await.unwrap().msg_stream();
        // Wait to start main select loop until signal-cli's dbus service is up and working
        loop {
            let opts: Result<(Vec<Vec<u8>>,), dbus::Error> = proxy
                .method_call("org.asamk.Signal", "getGroupIds", ())
                .await;
            if opts.is_ok() {
                break;
            }
            std::thread::sleep(Duration::from_millis(500));
        }
        // NOTE: HOLY SHIT GO DBUS
        loop {
            tokio::select! {
                parsed = line_receiver.next() => {
                    let parsed = parsed.unwrap();
                    if let Some(ref data_msg) = parsed.envelope.data_message {
                        if let Some(react) = &data_msg.reaction {
                            res_sender.send(SignalResponse::AddReaction {
                                emoji: react.emoji.clone(),
                                target: react.target_author.clone(),
                                from: parsed.envelope.source.clone(),
                                target_timestamp: react.target_sent_timestamp,
                                timestamp: parsed.envelope.timestamp
                            }).expect("Couldn't send AddReaction");
                        } else {
                            let mut attachment = vec![];
                            if let Some(ats) = &data_msg.attachments {
                                attachment = parse_attachments(ats);
                            }
                            let mut chat_id = parsed.envelope.source.clone();
                            if let Some(group_info) = &data_msg.group_info {
                                chat_id = group_info.group_id.clone();
                            }
                            let outgoing = config.username == parsed.envelope.source;
                            let con_msg = SignalMessage {
                                message: data_msg.message.clone().unwrap_or_default(),
                                ID: 0,
                                outgoing,
                                attachment: Some(attachment),
                                source: parsed.envelope.source.clone(),
                                sent_at: parsed.envelope.timestamp,
                                chat_id,
                                reactions: vec![]
                            };
                            res_sender.send(SignalResponse::IDLessMessage(con_msg)).expect("Couldn't send IDLessMessage");
                        }
                    } else if let Some(ref sync_msg) = parsed.envelope.sync_message {
                        if let Some(sm) = &sync_msg.sent_message {
                            if let Some(react) = &sm.reaction {
                                res_sender.send(SignalResponse::AddReaction {
                                    emoji: react.emoji.clone(),
                                    target: react.target_author.clone(),
                                    from: parsed.envelope.source.clone(),
                                    target_timestamp: react.target_sent_timestamp,
                                    timestamp: parsed.envelope.timestamp
                                }).expect("Couldn't send AddReaction");
                            } else {
                            let mut outgoing = false;
                            let mut source = parsed.envelope.source.clone();
                            if parsed.envelope.source == config.username {
                                outgoing = true;
                                if let Some(ref _group_info) = sm.group_info {
                                    //source = group_info.group_id.clone();
                                } else {
                                    source = sm.destination.clone().unwrap();
                                }
                            }
                            let mut attachment = vec![];
                            if let Some(ats) = &sm.attachments {
                                attachment = parse_attachments(ats);
                            }
                            let mut chat_id = source.clone();
                            if let Some(group_info) = &sm.group_info {
                                chat_id = group_info.group_id.clone();
                            }
                            let con_msg = SignalMessage {
                                message: sm.message.clone().unwrap_or_default(),
                                ID: 0,
                                outgoing,
                                attachment: Some(attachment),
                                source,
                                sent_at: parsed.envelope.timestamp,
                                chat_id,
                                reactions: vec![]
                            };
                            println!("{:?}",con_msg );
                            res_sender.send(SignalResponse::IDLessMessage(con_msg)).expect("Couldn't send IDLessMessage");
                        }
                    }
                    }
                }
                inc = req_receiver.recv() => {
                    if let Some(v) = inc {
                        match v {
                            SignalRequest::GetContacts => {
                                println!("Getting contacts");
                                //let contacts = Self::fetch_contacts_scli(config.username.as_str());
                                //res_sender.send(SignalResponse::ContactList(contacts)).expect("Can't send ContactList");
                            }
                            SignalRequest::GetGroups => {
                                let (ids, ) : (Vec<Vec<u8>>, ) = proxy.method_call("org.asamk.Signal", "getGroupIds", ()).await.unwrap();
                                let mut groups: Vec<Group> = vec![];
                                for id in ids.into_iter() {
                                    let parsed_id = base64::encode(&id);
                                    let (group_members, ) : (Vec<String>, ) = proxy.method_call("org.asamk.Signal", "getGroupMembers", (id.clone(),)).await.unwrap();
                                    let (group_name, ) : (String, ) =  proxy.method_call("org.asamk.Signal", "getGroupName", (id,)).await.unwrap();
                                    groups.push(Group {
                                        name: group_name,
                                        members: group_members,
                                        id: parsed_id
                                    });

                                }
                                println!("{:?}", groups);
                                println!("Got groups");
                                res_sender.send(SignalResponse::Groups(groups)).expect("Couldn't send Groups");
                            }
                            SignalRequest::SendGroupMessage {chat_id, message, attachment} => {
                                let (timestamp, ) : (i64,) = proxy.method_call("org.asamk.Signal", "sendGroupMessage", (&message, attachment.clone().unwrap_or_default(), base64::decode(&chat_id).expect("Couldn't decode group id"))).await.unwrap();
                                res_sender.send(SignalResponse::IDLessMessage(SignalMessage {
                                    message: message,
                                    source: config.username.clone(),
                                    outgoing: true,
                                    attachment: attachment.map(|x| {
                                        x.into_iter().map(|x| {
                                        let path = PathBuf::from(x.clone());
                                        let mut ctype = AttachmentType::File;
                                        if x.contains("png") || x.contains("jpg") || x.contains("jpeg") {
                                            ctype = AttachmentType::Image;
                                        }

                                         Attachment {
                                            file_name: path.file_name().unwrap().to_str().unwrap().to_string(),
                                            file: x,
                                            c_type: ctype
                                        }
                                        }).collect()
                                    }),
                                    sent_at: timestamp,
                                    chat_id: chat_id,
                                    ID: 0,
                                    reactions: vec![]
                                })).expect("Couldnt send IDLessMessage");
                            }
                            SignalRequest::SendMessage { to, message, attachment } => {
                                let (timestamp,) : (i64,) = proxy.method_call("org.asamk.Signal", "sendMessage", (&message,attachment.clone().unwrap_or_default(), vec![&to] )).await.unwrap();
                                res_sender.send(SignalResponse::IDLessMessage(SignalMessage {
                                    message: message,
                                    source: to.clone(),
                                    outgoing: true,
                                    attachment: attachment.map(|x| {
                                        x.into_iter().map(|x| {


                                        let path = PathBuf::from(x.clone());
                                        let mut ctype = AttachmentType::File;
                                        if x.contains("png") || x.contains("jpg") || x.contains("jpeg") {
                                            ctype = AttachmentType::Image;
                                        }

                                         Attachment {
                                            file_name: path.file_name().unwrap().to_str().unwrap().to_string(),
                                            file: x,
                                            c_type: ctype
                                        }
                                        }).collect()
                                    }),
                                    sent_at: timestamp,
                                    chat_id: to,
                                    ID: 0,
                                    reactions: vec![]
                                })).expect("Couldnt send IDLessMessage");
                            },
                            SignalRequest::GetContactName { chat_id } => {
                                println!("{}", chat_id);
                                let result = proxy.method_call("org.asamk.Signal", "getContactName", (chat_id.clone(), )).await;
                                if result.is_ok() {
                                    let (name,): (String,) = result.unwrap();
                                    res_sender.send(SignalResponse::ContactList(vec![Contact {name, tel: chat_id}])).expect("Couldn't send ContactList");

                                }
                            },
                            SignalRequest::AddContact{ name, phone} => {
                                res_sender.send(SignalResponse::ContactList(vec![Contact {name, tel: phone}])).expect("Couldn't send ContactList");
                            },
                            SignalRequest::EditContact{ name, phone, id } => {
                                res_sender.send(SignalResponse::RmContact(id)).expect("Couldn't send RmContact");
                                res_sender.send(SignalResponse::ContactList(vec![Contact {name, tel: phone}])).expect("Couldn't send ContactList");
                            },
                            _ => {

                            }
                        }
                    }
                }
            }
        }
        //unreachable!();
        //conn.remove_match(_inc.0.token()).await.unwrap();
    }
    pub fn add_message(&mut self, message: SignalMessage, new: bool) {
        if self.message(&message.chat_id, message.sent_at).is_err() {
            println!("creating");
            self.msg_store
                .create(&message)
                .expect("Couldn't add message to store");
            self.res_sender
                .send(SignalResponse::AddHist(message.clone(), new))
                .expect("Couldn't send AddHist");
        }
    }

    pub fn process_scli_msg(&mut self, mut msg: SignalCLIMessage) {
        let message: Option<String>;
        let mut outgoing = false;
        let chat_id = msg.source.clone();
        if let Some(m) = msg.message {
            println!("text");
            message = Some(m);
        } else if let Some(data) = msg.data_message {
            message = data.message;
            if let Some(dest) = data.destination {
                if msg.source == self.config.username {
                    outgoing = true;
                    msg.source = dest;
                }
            }
        } else {
            println!("Return early");
            return;
        }
        if let Some(v) = self.messages_id(&chat_id).ok() {
            let last_id = v.values().map(|msg| msg.ID).max().unwrap();
            let made_msg = SignalMessage {
                ID: last_id + 1,
                source: msg.source,
                message: message.unwrap(),
                outgoing,
                sent_at: msg.timestamp,
                attachment: None,
                chat_id,
                reactions: vec![],
            };
            self.add_message(made_msg, true);
        } else {
            let made_msg = SignalMessage {
                ID: 0,
                source: msg.source,
                message: message.unwrap(),
                outgoing,
                sent_at: msg.timestamp,
                attachment: None,
                chat_id,
                reactions: vec![],
            };
            self.add_message(made_msg, true);
        }
    }
    fn message_doc<'a>(
        &'a self,
        chat_id: &str,
        sent_at: i64,
    ) -> Result<Document<SignalMessage>, anyhow::Error> {
        Ok(self
            .msg_store
            .search(format!("chat_id:{} AND sent_at:{}", chat_id, sent_at).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .clone())
    }
    fn contact_doc<'a>(&'a self, tel: &str) -> Result<Document<Contact>, anyhow::Error> {
        Ok(self
            .contact_store
            .search(format!("tel:{}", tel).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .clone())
    }
    pub fn avatar(&self, id: &str) -> Option<String> {
        let opts = vec![
            "profile".to_string(),
            "contact".to_string(),
            "group".to_string(),
        ];
        let av_dir = PathBuf::from(SCLI_AVATAR_DIRECTORY.to_owned());
        for opt in opts {
            let now_path = av_dir.join(format!("{}-{}", opt, id));
            if now_path.exists() {
                return Some(now_path.to_str().unwrap().to_string());
            }
        }
        None
    }
    pub fn set_avatar(&self, id: String, data: &[u8]) -> String {
        let path = PathBuf::from(SCLI_AVATAR_DIRECTORY.to_owned()).join(format!("contact-{}", id));
        let mut fd = fs::File::create(&path).expect("Couldn't create avatar file");
        fd.write_all(data).expect("Couldn't write to avatar file");
        return path.into_os_string().into_string().unwrap();
    }
    pub fn new(
        res_sender: glib::Sender<SignalResponse>,
        config: Config,
        conn: Arc<nonblock::SyncConnection>,
        req_sender: UnboundedSender<SignalRequest>,
    ) -> SCLISession {
        let scli_config = config.scli.expect("No configured SCLI settings");
        let data_dir = scli_config.data_dir.clone().replace(
            "~",
            home::home_dir()
                .expect("Couldn't get home dir")
                .to_str()
                .unwrap(),
        );
        let m_db = pallet::ext::sled::open(Path::new(&data_dir).join("messages").to_str().unwrap())
            .unwrap();
        let c_db = pallet::ext::sled::open(Path::new(&data_dir).join("contacts").to_str().unwrap())
            .unwrap();
        fs::create_dir("/tmp/sclim");
        fs::create_dir("/tmp/sclic");
        let msg_store = pallet::Store::builder()
            .with_db(m_db)
            .with_index_dir("/tmp/sclim")
            .finish()
            .unwrap();
        msg_store.index_all().expect("Couldn't index message store");
        let contact_store = pallet::Store::builder()
            .with_db(c_db)
            .with_index_dir("/tmp/sclic")
            .finish()
            .unwrap();
        contact_store
            .index_all()
            .expect("Couldn't index contact store");
        println!("{:?}", msg_store.all());
        let proxy = nonblock::Proxy::new(
            "org.asamk.Signal",
            "/org/asamk/Signal",
            Duration::from_secs(10),
            conn.clone(),
        );
        let session = SCLISession {
            res_sender,
            config: scli_config,
            msg_store,
            contact_store,
            groups: HashMap::new(),
            proxy,
            resolved_names: HashMap::new(),
            req_sender,
        };
        session.delete_all("EUSVmOeIH96m3CNb47OIx1rRp0jWpkHYeTciI28cuN0=".to_string());
        session
    }
    pub fn messages_id<'a>(&'a self, chat_id: &str) -> Result<BTreeMap<i64, SignalMessage>> {
        let messages = self
            .msg_store
            .search(format!("+chat_id:\"{}\"", chat_id).as_str())?
            .hits;
        Ok(messages
            .into_iter()
            .map(|m| {
                let m = m.doc.inner;
                (m.sent_at, m)
            })
            .collect())
    }
    pub fn resolve_name(&mut self, chat_id: &str) -> Result<String> {
        if let Some(contact) = self.contact(chat_id.clone()).ok() {
            return Ok(contact.name);
        } else if let Some(group) = self.groups().get(chat_id) {
            return Ok(group.name.clone());
        } else if let Some(name) = self.resolved_names.get(chat_id) {
            if let Some(name) = name {
                return Ok(name.to_string());
            } else {
                return Ok(chat_id.to_string());
            }
        } else {
            self.req_sender
                .send(SignalRequest::GetContactName {
                    chat_id: chat_id.to_string(),
                })
                .expect("Couldn't send GetContactName");
            self.resolved_names.insert(chat_id.to_string(), None);
            println!("{}", chat_id);
            return Ok(chat_id.to_string());
        }
    }
    pub fn messages<'a>(
        &'a self,
    ) -> Result<BTreeMap<String, BTreeMap<i64, SignalMessage>>, anyhow::Error> {
        let mut map: BTreeMap<String, BTreeMap<i64, SignalMessage>> = BTreeMap::new();
        for msg in self.msg_store.all()? {
            let msg = msg.inner;
            if let Some(ind) = map.get_mut(&msg.chat_id) {
                ind.insert(msg.sent_at, msg);
            } else {
                let mut new_map: BTreeMap<i64, SignalMessage> = BTreeMap::new();
                new_map.insert(msg.sent_at, msg.clone());
                map.insert(msg.chat_id.clone(), new_map);
            }
        }
        Ok(map)
    }
    pub fn message<'a>(&'a self, chat_id: &str, sent_at: i64) -> Result<SignalMessage> {
        Ok(self
            .msg_store
            .search(format!("chat_id:{} AND sent_at:{}", chat_id, sent_at).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .inner
            .clone())
    }
    pub fn contact<'a>(&'a self, tel: &str) -> Result<Contact> {
        Ok(self
            .contact_store
            .search(format!("tel:{}", tel).as_str())?
            .hits
            .into_iter()
            .next()
            .context("none")?
            .doc
            .inner
            .clone())
    }
    pub fn groups<'a>(&'a self) -> HashMap<String, Group> {
        self.groups.clone()
    }
    pub fn group(&self, chat_id: &str) -> Option<Group> {
        self.groups().get(chat_id).cloned()
    }
    pub fn contacts<'a>(&'a self) -> HashMap<String, Contact> {
        let map: HashMap<String, Contact> = self
            .contact_store
            .all()
            .unwrap()
            .into_iter()
            .map(|x| {
                let x = x.inner;
                (x.tel.clone(), x)
            })
            .collect();
        println!("{}", map.len());
        map
    }
    pub fn process_response(&mut self, response: SignalResponse) {
        match response {
            SignalResponse::RmMessage { chat_id, sent_at } => {
                if let Some(doc) = self.message_doc(&chat_id, sent_at).ok() {
                    self.msg_store
                        .delete(doc.id)
                        .expect("Couldn't delete message");
                }
            }
            SignalResponse::SignalCLIEnvelope(msg) => {
                println!("Processing");
                self.process_scli_msg(msg);
            }
            SignalResponse::IDLessMessage(mut msg) => {
                let mut id = 0;
                if let Some(v) = self.messages_id(&msg.chat_id).ok() {
                    if v.len() > 0 {
                        id = v.values().map(|msg| msg.ID).max().unwrap() + 1;
                    }
                }
                msg.ID = id;
                self.add_message(msg, true);
            }
            SignalResponse::RmContact(tel) => {
                if let Some(contact) = self.contact_doc(&tel).ok() {
                    self.contact_store
                        .delete(contact.id)
                        .expect("Couldn't delete contact");
                }
            }
            SignalResponse::Groups(groups) => {
                for group in groups.into_iter() {
                    if let Some(group_p) = self.groups.get_mut(&group.id) {
                        *group_p = group;
                    } else {
                        self.groups.insert(group.id.clone(), group);
                    }
                }
            }
            SignalResponse::ContactList(contacts) => {
                for contact in contacts.into_iter() {
                    if let Some(mut contact_p) = self.contact_doc(&contact.tel).ok() {
                        contact_p.inner = contact;
                        self.contact_store
                            .update(&contact_p)
                            .expect("Couldn't update contact");
                    } else {
                        self.contact_store
                            .create(&contact)
                            .expect("Couldn't create contact");
                    }
                }
            }
            SignalResponse::AddReaction {
                target,
                target_timestamp,
                timestamp,
                from,
                emoji,
            } => {
                let reaction = Reaction {
                    emoji,
                    source: from,
                    timestamp,
                };
                if let Some(fetched) = self
                    .msg_store
                    .search(format!("source:{} AND sent_at:{}", target, target_timestamp).as_str())
                    .expect("Couldn't search for reaction message")
                    .hits
                    .into_iter()
                    .next()
                {
                    let mut fetched = fetched.doc;
                    fetched.inner.reactions.push(reaction);
                    self.msg_store
                        .update(&fetched)
                        .expect("Couldn't update reaction message");
                }
            }
            _ => {}
        }
    }
}
/// Turns a vec of SignalCLIAttachments into their Attachment counterparts
pub fn parse_attachments(catt: &Vec<SignalCLIAttachment>) -> Vec<Attachment> {
    catt.into_iter()
        .map(|x| Attachment {
            file: SCLI_ATTACHMENT_DIRECTORY.to_owned() + &x.id,
            file_name: x.filename.clone().unwrap_or(x.id.to_string()),
            c_type: AttachmentType::from_mime(&x.content_type),
        })
        .collect()
}

M src/signal.rs => src/signal.rs +57 -34
@@ 1,5 1,8 @@
use enum_variant_type::EnumVariantType;
use pallet::DocumentLike;

use presage::prelude::*;

use qmetaobject::*;
use regex::Regex;
use serde::{Deserialize, Deserializer, Serialize, Serializer};


@@ 15,8 18,8 @@ pub mod signal_cli {
    pub struct SignalCLIContainer {
        pub envelope: SignalCLIMessage,
    }
    #[serde(rename_all = "camelCase")]
    #[derive(Serialize, Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SignalCLIMessage {
        pub source: String,
        pub source_device: u8,


@@ 28,14 31,16 @@ pub mod signal_cli {
        pub sync_message: Option<SyncMessage>,
        pub typing_message: Option<TypingMessage>,
    }
    #[serde(rename_all = "UPPERCASE")]

    #[derive(Serialize, Deserialize, Clone, Debug)]
    #[serde(rename_all = "UPPERCASE")]
    pub enum TypingType {
        Started,
        Stopped,
    }
    #[serde(rename_all = "camelCase")]

    #[derive(Serialize, Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct TypingMessage {
        pub action: TypingType,
        pub timestamp: i64,


@@ 56,13 61,15 @@ pub mod signal_cli {
            None
        }
    }
    #[serde(rename_all = "camelCase")]

    #[derive(Serialize, Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SyncMessage {
        pub sent_message: Option<SentMessage>,
    }
    #[serde(rename_all = "camelCase")]

    #[derive(Serialize, Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SentMessage {
        pub timestamp: u64,
        pub message: Option<String>,


@@ 72,16 79,18 @@ pub mod signal_cli {
        pub group_info: Option<GroupInfo>,
        pub destination: Option<String>,
    }
    #[serde(rename_all = "camelCase")]

    #[derive(Serialize, Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SCLIReaction {
        pub emoji: String,
        pub target_author: String,
        pub is_remove: bool,
        pub target_sent_timestamp: i64,
    }
    #[serde(rename_all = "camelCase")]

    #[derive(Serialize, Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct DataMessage {
        pub timestamp: u64,
        pub message: Option<String>,


@@ 91,8 100,9 @@ pub mod signal_cli {
        pub destination: Option<String>,
        pub reaction: Option<SCLIReaction>,
    }
    #[serde(rename_all = "camelCase")]

    #[derive(Serialize, Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SignalCLIAttachment {
        pub content_type: String,
        pub filename: Option<String>,


@@ 117,6 127,13 @@ pub enum SignalRequest {
        message: String,
        attachment: Option<Vec<String>>,
    },
    SendMessageToGroup {
        members: Vec<String>,
        message: String,
        attachment: Option<Vec<String>>,
        id: String,
        master_key: Option<Vec<u8>>,
    },
    GetMessageList {
        id: String,
    },


@@ 146,6 163,7 @@ pub enum SignalRequest {
        name: String,
        members: Vec<String>,
    },

    GetConfig,
    AddContact {
        name: String,


@@ 154,6 172,8 @@ pub enum SignalRequest {
    SendPassword {
        pw: String,
    },
    #[serde(skip)]
    GetGroupCtxV2(GroupContextV2),
    EditContact {
        name: String,
        phone: String,


@@ 173,6 193,11 @@ pub struct Reaction {
}
#[derive(Serialize, Deserialize, Clone, EnumVariantType, Debug)]
pub enum SignalResponse {
    #[serde(skip)]
    Whoami(String),
    SGroup(Group),
    #[serde(skip)]
    Metadata(presage::prelude::Metadata),
    #[evt(derive(Clone, Serialize, Deserialize, Debug))]
    ContactList(Vec<Contact>),
    #[evt(derive(Clone, Serialize, Deserialize, Debug))]


@@ 198,6 223,8 @@ pub enum SignalResponse {
    RmContact(String),
    Quit,
    DefaultOp,
    #[serde(skip)]
    GroupV2Context(GroupContextV2),
    HistoryMessage {
        phone: String,
        messages: Vec<SignalMessage>,


@@ 254,15 281,21 @@ where
    Deserialize::deserialize(d).map(|x: Option<_>| x.unwrap_or(vec![]))
}

#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[derive(Serialize, Deserialize, Clone, Debug, Default, DocumentLike)]
#[pallet(tree_name = "groups")]
pub struct Group {
    #[pallet(skip_indexing)]
    pub master_key: Option<Vec<u8>>,
    pub name: String,
    #[pallet(skip_indexing)]
    pub members: Vec<String>,
    #[pallet(default_search_field, index_field_options = "SignalMessage::id_opts()")]
    pub id: String,
}
impl QMetaType for Group {}
#[serde(rename_all = "camelCase")]

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct GroupInfo {
    pub group_id: String,
}


@@ 277,6 310,7 @@ pub struct SignalMessage {
    /*#[serde(rename = "SID")]
    pub SID: i32,*/
    #[serde(rename = "ChatID")]
    #[pallet(default_search_field, index_field_options = "SignalMessage::id_opts()")]
    pub chat_id: String,
    pub source: String,
    pub message: String,


@@ 292,24 326,21 @@ pub struct SignalMessage {
    #[serde(default)]
    #[pallet(skip_indexing)]
    pub reactions: Vec<Reaction>,
    //pub received_at: i64,
    /*pub h_time: String,
    pub c_type: i32,
    pub is_sent: bool,
    pub is_read: bool,
    pub flags: i32,
    pub expire_timer: i32,
    pub sending_error: bool,
    pub receipt: bool,
    pub status_message: bool,*/
}
impl SignalMessage {
    pub fn id_opts() -> pallet::ext::tantivy::schema::TextOptions {
        let mut to = pallet::ext::tantivy::schema::TextOptions::default();
        let mut indexing = pallet::ext::tantivy::schema::TextFieldIndexing::default();
        indexing = indexing.set_tokenizer("raw");
        to = to.set_indexing_options(indexing);
        to
    }
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, QGadget, SimpleListItem)]
#[serde(rename_all = "PascalCase")]
pub struct QMLSignalMessage {
    #[serde(rename = "ID")]
    pub ID: i32,
    /*#[serde(rename = "SID")]
    pub SID: i32,*/
    #[serde(rename = "ChatID")]
    pub chat_id: String,
    pub source: String,


@@ 318,16 349,6 @@ pub struct QMLSignalMessage {
    pub sent_at: i64,
    pub attachment: String,
    pub reactions: String,
    //pub received_at: i64,
    /*pub h_time: String,
    pub c_type: i32,
    pub is_sent: bool,
    pub is_read: bool,
    pub flags: i32,
    pub expire_timer: i32,
    pub sending_error: bool,
    pub receipt: bool,
    pub status_message: bool,*/
}
impl QMLSignalMessage {
    pub fn from_msg(msg: SignalMessage) -> QMLSignalMessage {


@@ 394,13 415,15 @@ impl PartialEq for SignalMessage {
}
trait SignalResponseTrait {}
impl SignalResponseTrait for SignalResponse {}

#[derive(Serialize, Deserialize, Clone, Debug, DocumentLike, Default, SimpleListItem)]
#[serde(rename_all = "PascalCase")]
#[pallet(tree_name = "contacts")]
#[derive(Serialize, Deserialize, Clone, Debug, DocumentLike, Default, SimpleListItem)]
pub struct Contact {
    pub tel: String,
    //pub uuid: String,
    pub name: String,
    #[pallet(default_search_field, index_field_options = "SignalMessage::id_opts()")]
    pub uuid: String,
    //pub color: String,
    //pub avatar: String,
    //pub blocked: bool,

D src/widgets/chat_history.rs => src/widgets/chat_history.rs +0 -82
@@ 1,82 0,0 @@
use crate::*;
use std::collections::VecDeque;

/// A widget that displays and tracks
pub struct ChatHistory {
    pub list: VecDeque<Element>,
    /// The GTK Listbox displaying the elements
    pub listbox: ListBox,
    /// The ID of the last message sent
    pub last_id: Rc<Option<String>>,
    /// The telephone number of the chat
    pub tel: String,
}
impl ChatHistory {
    pub fn new(tel: String) -> ChatHistory {
        let listbox = ListBoxBuilder::new()
            .selection_mode(SelectionMode::None)
            .activate_on_single_click(false)
            .build();
        listbox.set_sort_func(Some(std::boxed::Box::new(
            |a: &ListBoxRow, b: &ListBoxRow| {
                let a: String = a.get_property("name").unwrap().get().unwrap().unwrap();
                let b: String = b.get_property("name").unwrap().get().unwrap().unwrap();
                let diff = a.parse::<i64>().unwrap() - b.parse::<i64>().unwrap();
                if diff < 0 {
                    -1
                } else if diff > 0 {
                    1
                } else {
                    0
                }
            },
        )));
        listbox.get_style_context().add_class("history-listbox");
        ChatHistory {
            tel,
            list: VecDeque::new(),
            listbox,

            last_id: Rc::new(None),
        }
    }
    /// Inserts an element into the history at the top
    pub fn insert_top(&mut self, element: Element) {
        self.listbox.add(element.get_lbr());
        self.list.push_back(element);
    }
    /// Inserts an element into the history at the bottom
    pub fn insert_bottom(&mut self, element: Element) {
        self.listbox.add(element.get_lbr());
        self.list.push_front(element);
    }
    /// Get the ID of the latest message
    pub fn get_last_id(&mut self) -> Option<String> {
        self.list
            .iter()
            .rev()
            .filter_map(|x| match x {
                Element::Message(m) => Some(m.msg.ID.to_string()),
            })
            .next()
    }
    /// Erase all messages from the history
    pub fn wipe(&mut self) {
        self.list = VecDeque::new();
        for c in self.listbox.get_children().iter() {
            self.listbox.remove(c);
        }
    }
}
/// An element that can be displayed in a chat history
pub enum Element {
    Message(crate::Message),
}
impl Element {
    /// Returns the Ui of the element, inside a gtk::ListBoxRow
    pub fn get_lbr(&self) -> &ListBoxRow {
        match self {
            Element::Message(msg) => &msg.ui.row,
        }
    }
}

D src/widgets/chat_list.rs => src/widgets/chat_list.rs +0 -175
@@ 1,175 0,0 @@
use crate::util::parse_time;
use crate::*;
use gdk_pixbuf::*;
use qmetaobject::*;
use std::time::SystemTime;
/// The list of chats
#[derive(QObject, Default)]
pub struct ChatList {
    base: qt_base_class!(trait QAbstractListModel), //pub listbox: ListBox,
    pub chats: HashMap<i32, Chat>,
    /// Whether the ChatList is enabled
    pub enabled: bool,
    /// Holds the fetched avatars
    pub avatars: HashMap<String, Pixbuf>,
    add_chat: qt_method!(fn(&mut self, chat: Chat))
}
impl StaticUpcast<qt_core::QObject> for ChatList {
    unsafe fn static_upcast(ptr: Ptr<Self>) -> Ptr<qt_core::QObject> {
        ptr.static_upcast()
    }
}
impl DynamicCast<ChatList> for qt_core::QObject {
    unsafe fn dynamic_cast(ptr: Ptr<Self>) -> Ptr<ChatList> {
        ptr.dynamic_cast()
    }
}

// Use Contacts instead of Chats
impl ChatList {
    /*pub fn new() -> ChatList {
        let listbox = ListBoxBuilder::new().build();
        let style = listbox.get_style_context();
        style.add_class("chat-list");
        ChatList {
            //listbox,
            chats: HashMap::new(),
            enabled: false,
            avatars: HashMap::new(),
        }
    }*/
    /// Add a chat to the ChatList
    pub fn add_chat(&mut self, chat: Chat) {
        println!("add {}", chat.name);
        (self as &mut dyn QAbstractListModel).begin_reset_model();

        if let Some(c) = self.chats.get_mut(&chat.ID) {
            *c = chat;
        } else {
            self.chats.insert(chat.ID, chat);
        }
        (self as &mut dyn QAbstractListModel).end_reset_model();
    }
    pub fn add_chats(&mut self, chats: Vec<Chat>) {
        (self as &mut dyn QAbstractListModel).begin_reset_model();
        for chat in chats.into_iter() {
            self.add_chat(chat);
        }
        (self as &mut dyn QAbstractListModel).end_reset_model();
    }
    // Update the rendered ChatList
    /*pub fn update_chats(&mut self) {
        for child in self.listbox.get_children() {
            self.listbox.remove(&child);
        }
        for chat in self.chats.values() {
            let ui = build_chat_ui(&chat);
            self.listbox.add(&ui);
            if self.enabled && chat.messages.len() > 0 {
                ui.show();
            }
        }
    }
    /// Enable or disable the ChatList
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
        println!("{:?}", self.chats);
        if enabled{
            /*for (i, child) in self.listbox.get_children().iter().enumerate() {
                if self.chats[&((i) as i32)].messages.len() > 0 {
                    child.show();
                }
            }*/
            self.listbox.show();
        } else {
            self.listbox.hide();
        }
        self.update_chats();
    }
    pub fn connect(&self, state: &SignalState<impl SignalBackend>) {
        self.listbox.connect_row_activated(clone!(@strong state.res_sender as res_sender, @strong state.req_sender as req_sender  => move |_, chat| {
            let tel : String = chat.get_property("name").unwrap().get().unwrap().unwrap();
            req_sender.send(SignalRequest::GetMessageList{ id: tel.clone()}).expect("Couldn't send GetMessageList");
            res_sender.send(SignalResponse::ShowView(View::Messages(tel))).expect("Couldn't send ShowView");
        }));
    }*/
}


impl QAbstractListModel for ChatList {
    fn row_count(&self) -> i32 {
        self.chats.len() as i32
    }
    fn data(&self, index: QModelIndex, role: i32) -> QVariant {
        println!("printing");
        let idx = index.row() as i32;
        if let Some(chat) = self.chats.get(&idx) {
            //if role == USER_ROLE {
            return QString::from(chat.name.clone()).into();
            //  }
        }
        return QVariant::default();
    }

    fn role_names(&self) -> std::collections::HashMap<i32, QByteArray> {
        let mut map = std::collections::HashMap::new();
        map.insert(USER_ROLE, "chatName".into());
        map
    }
}
// Builds the row for a single chat
/*fn build_chat_ui(chat: &Chat) -> ListBoxRow {
    let row = ListBoxRow::new();
    let b = Box::new(Orientation::Horizontal, 6);
    add_class(&row, "chat-entry");
    row.set_property("name", &chat.tel.clone())
        .expect("Couldn't set chat list name");
    let name_label = Label::new(Some(&chat.name));
    add_class(&name_label, "chat-name");
    let last_label = Label::new(Some(&chat.last.trim()));
    add_class(&last_label, "chat-last");
    add_class(&last_label, "small-text");
    let now = SystemTime::now();
    let when_label = Label::new(Some(&parse_time(
        now.duration_since(SystemTime::UNIX_EPOCH)
            .expect("Couldn't get time since unix epoch")
            .as_millis()
            - chat.timestamp as u128,
    )));
    /*let avatar = Image::new();
        if let Some(buf) = self.avatars.get(&chat.tel) {
            avatar.set_from_pixbuf(Some(&buf));
        } else {
            let av_clone = avatar.clone();
            let tel_clone = chat.tel.clone();
            let (tx, rx) = glib::MainContext::channel::<glib::Bytes>(glib::PRIORITY_DEFAULT);
            rx.attach(None, move |b| {
                let pixbuf_loader = PixbufLoader::new();
                pixbuf_loader.write_bytes(&b).unwrap();
                pixbuf_loader.close().unwrap();
                av_clone.set_from_pixbuf(Some(&pixbuf_loader.get_pixbuf().unwrap()));

                glib::Continue(true)
            });
            tokio::spawn(async move {
                let res = reqwest::get(&format!("http://localhost:9080/avatars?file={}", tel_clone)).await.expect("Couldn't make avatar request");
                if res.status().is_success() {
                    let res_byes = res.bytes().await.unwrap();
                    let bytes = glib::Bytes::from(&*res_byes);
                    tx.send(bytes).unwrap();
                }
                            });
    }*/
    let avatar = libhandy::Avatar::new(32, Some(&chat.name), true);
    b.pack_start(&avatar, false, false, 0);
    let b2 = Box::new(Orientation::Vertical, 4);
    b.pack_start(&b2, true, true, 0);
    b2.pack_start(&name_label, true, true, 0);
    b2.pack_start(&last_label, true, true, 0);
    b.pack_end(&when_label, false, false, 0);
    row.add(&b);
    row.show_all();
    row.hide();
    row
}
*/

D src/widgets/create_chat.rs => src/widgets/create_chat.rs +0 -36
@@ 1,36 0,0 @@
use crate::*;
/// The widget displaying when creating a new chat
pub struct CreateChat {
    pub container: Box,
    pub picker: ComboBoxText,
    pub button: Button,
}
impl CreateChat {
    pub fn new() -> CreateChat {
        let container = Box::new(Orientation::Vertical, 6);
        let entry = ComboBoxText::new();
        let button = Button::with_label("Create chat");
        container.add(&entry);
        container.add(&button);
        CreateChat {
            container,
            picker: entry,
            button,
        }
    }
    pub fn set_choices(&mut self, chats: Vec<Contact>) {
        self.picker.remove_all();
        for chat in chats {
            self.picker.append_text(&chat.tel);
        }
    }
    pub fn connect(&self, state: &SignalState<impl SignalBackend>) {
        self.button.connect_clicked(
            clone!(@weak self.picker as entry, @strong state.req_sender as req_sender, @strong state.res_sender as res_sender => move |_| {
                let name = entry.get_active_text().unwrap().as_str().to_string();
                req_sender.send(SignalRequest::CreateChat {tel: name.clone()}).expect("Couldn't send CreateChat");
                res_sender.send(SignalResponse::ShowView(View::Messages(name))).expect("Couldn't send ShowView");
            }),
        );
    }
}

D src/widgets/create_contact.rs => src/widgets/create_contact.rs +0 -34
@@ 1,34 0,0 @@
use crate::*;
pub struct CreateContact {
    pub container: Box,
    pub name_entry: Entry,
    pub phone_entry: Entry,
    pub submit_button: Button,
}
impl CreateContact {
    pub fn new() -> CreateContact {
        let container = Box::new(Orientation::Vertical, 6);
        let name_entry = Entry::new();
        let phone_entry = Entry::new();
        let submit_button = Button::with_label("Submit");
        container.add(&Label::new(Some("Name:")));
        container.add(&name_entry);
        container.add(&Label::new(Some("Phone:")));
        container.add(&phone_entry);
        container.add(&submit_button);
        CreateContact {
            container,
            name_entry,
            phone_entry,
            submit_button,
        }
    }
    pub fn connect(&self, state: &SignalState<impl SignalBackend>) {
        self.submit_button.connect_clicked(clone!(@weak self.name_entry as name, @weak self.phone_entry as phone, @strong state.req_sender as req_sender, @strong state.res_sender as res_sender => move |_| {
            let name = name.get_buffer().get_text().to_string();
            let phone = phone.get_buffer().get_text();
            req_sender.send(SignalRequest::AddContact {name, phone}).expect("Couldn't send AddContact");
            res_sender.send(SignalResponse::ShowView(View::Chats)).expect("Couldn't send ShowView");
        }));
    }
}

D src/widgets/edit_contact.rs => src/widgets/edit_contact.rs +0 -52
@@ 1,52 0,0 @@
use crate::*;
pub struct EditContact {
    pub container: Box,
    pub name_entry: Entry,
    pub phone_entry: Entry,
    pub submit_button: Button,
    pub delete_button: Button,
    pub current_tel: Rc<RefCell<String>>,
}
impl EditContact {
    pub fn new() -> EditContact {
        let container = Box::new(Orientation::Vertical, 6);
        let name_entry = Entry::new();
        let phone_entry = Entry::new();
        let submit_button = Button::with_label("Submit Changes");
        let current_tel = Rc::new(RefCell::new(String::new()));
        let delete_button = Button::with_label("Delete Contact");
        container.add(&Label::new(Some("Name:")));
        container.add(&name_entry);
        container.add(&Label::new(Some("Phone:")));
        container.add(&phone_entry);
        container.add(&submit_button);
        container.add(&delete_button);
        EditContact {
            container,
            name_entry,
            phone_entry,
            submit_button,
            current_tel,
            delete_button,
        }
    }
    pub fn edit(&self, contact: Contact) {
        self.current_tel.replace(contact.tel.clone());
        self.name_entry.get_buffer().set_text(&contact.name);
        self.phone_entry.get_buffer().set_text(&contact.tel);
    }
    pub fn connect(&self, state: &SignalState<impl SignalBackend>) {
        self.submit_button.connect_clicked(clone!(@weak self.current_tel as ctel, @weak self.name_entry as name, @weak self.phone_entry as phone, @strong state.req_sender as req_sender, @strong state.res_sender as res_sender => move |_| {
            let name = name.get_buffer().get_text();
            let phone = phone.get_buffer().get_text();
            let req = SignalRequest::EditContact {name, phone, id: (*ctel).replace(String::new())};
            req_sender.send(req).expect("Couldn't send EditContact");
            res_sender.send(SignalResponse::ShowView(View::Chats)).expect("Couldn't send ShowView");
        }));
        self.delete_button.connect_clicked(clone!(@weak self.current_tel as ctel, @strong state.req_sender as req_sender, @strong state.res_sender as res_sender => move |_| {
            let ctel = ctel.replace(String::new());
            req_sender.send(SignalRequest::DelContact{id: ctel}).expect("Couldn't send DelContact");
            res_sender.send(SignalResponse::ShowView(View::Chats)).expect("Couldn't send ShowView");
        }));
    }
}

D src/widgets/header.rs => src/widgets/header.rs +0 -110
@@ 1,110 0,0 @@
use crate::*;
pub struct Header {
    pub headerbar: HeaderBar,
    pub view_history: Vec<View>,
    pub label: Label,
    pub menu_button: MenuButton,
}
impl Header {
    pub fn new(res_sender: &glib::Sender<SignalResponse>) -> Header {
        let headerbar = gtk::HeaderBarBuilder::new()
            .show_close_button(false)
            .build();
        add_class(&headerbar, "header");
        let label = Label::new(Some("Signal"));
        let view_history = vec![];
        let menu_button = MenuButton::new();
        let menu = Menu::new();
        let settings = MenuItem::with_label("Settings");
        let quit = MenuItem::with_label("Quit");
        settings.connect_activate(clone!(@strong res_sender => move |_| {
        	res_sender.send(SignalResponse::ShowView(View::Settings)).expect("Couldn't send ShowView");
        }));
        quit.connect_activate(clone!(@strong res_sender => move |_| {
            res_sender.send(SignalResponse::Quit).expect("Couldn't send quit");
        }));
        menu.append(&settings);
        menu.append(&quit);
        menu.show_all();
        menu_button.set_popup(Some(&menu));
        Header {
            headerbar,
            view_history,
            label,
            menu_button,
        }
    }
    pub fn set_view(
        &mut self,
        view: View,
        req_sender: &UnboundedSender<SignalRequest>,
        res_sender: &glib::Sender<SignalResponse>,
    ) {
        for child in self.headerbar.get_children() {
            self.headerbar.remove(&child);
        }
        self.headerbar.set_title(None);
        if let View::Chats = view {
            self.view_history = vec![];
        } else {
            let back_button = ButtonBuilder::new()
                .label("gtk-go-back")
                .receives_default(true)
                .build();
            back_button
                .set_property("use_stock", &true)
                .expect("Couldn't set stock property");
            back_button.connect_clicked(clone!(@strong res_sender => move |_| {
                res_sender.send(SignalResponse::ShowView(View::Chats)).expect("Couldn't send ShowView");
            }));
            self.headerbar.pack_start(&back_button);
        }
        match view {
            View::Chats => {
                self.headerbar.set_title(Some("Signal"));
                req_sender
                    .send(SignalRequest::GetContacts)
                    .expect("Couldn't send GetContacts");
                let create_button = ButtonBuilder::new()
                    .label("gtk-add")
                    .receives_default(true)
                    .build();
                create_button
                    .set_property("use_stock", &true)
                    .expect("Couldn't set create button's stock property");
                create_button.set_halign(Align::End);
                create_button.connect_clicked(
                    clone!(@strong res_sender => move |_| {
                        res_sender.send(SignalResponse::ShowView(View::CreateChat)).expect("Couldn't send ShowView");
                    }),
                );
                self.headerbar.pack_end(&create_button);
            }
            View::Messages(tel) => {
                let edit_button = Button::with_label("Edit");
                edit_button.connect_clicked(clone!(@strong res_sender => move |_| {
                    res_sender.send(SignalResponse::ShowView(View::EditContact(tel.clone()))).expect("Couldn't send ShowView");
                }));
                self.headerbar.pack_end(&edit_button);
            }
            View::CreateChat => {
                let create_contact_button = ButtonBuilder::new()
                    .label("gtk-add")
                    .receives_default(true)
                    .build();
                create_contact_button
                    .set_property("use_stock", &true)
                    .expect("Couldn't set create button's stock property");
                create_contact_button.connect_clicked(
                    clone!(@strong res_sender => move |_| {
                        res_sender.send(SignalResponse::ShowView(View::CreateContact)).expect("Couldn't send ShowView");
                    }),
                );
                self.headerbar.add(&create_contact_button);
            }
            _ => {}
        }
        self.headerbar.pack_end(&self.menu_button);
        self.headerbar.show_all();
    }
}

D src/widgets/message.rs => src/widgets/message.rs +0 -104
@@ 1,104 0,0 @@
use crate::util::parse_time;
use crate::*;
use core::fmt::Debug;
use regex::Regex;
use std::fmt;
use std::fs;
use std::time::*;
lazy_static! {
    static ref LINK_REG: Regex = Regex::new(
        r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+"
    )
    .unwrap();
}
/// A UI widget to display a message
#[derive(Clone)]
pub struct MessageUi {
    pub row: ListBoxRow,
    pub ui: Box,