first pass at xpc impl
This commit is contained in:
313
Cargo.lock
generated
313
Cargo.lock
generated
@@ -169,6 +169,29 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.58.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"clap 2.34.0",
|
||||||
|
"env_logger 0.8.4",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"log",
|
||||||
|
"peeking_take_while",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"which",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -181,6 +204,12 @@ version = "2.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@@ -208,6 +237,15 @@ version = "1.0.95"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b"
|
checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -229,6 +267,17 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.34.0"
|
version = "2.34.0"
|
||||||
@@ -612,6 +661,19 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.11.8"
|
version = "0.11.8"
|
||||||
@@ -668,6 +730,31 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-async-runtime-preview"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33c03035be1dae627b7e05c6984acb1f2086043fde5249ae51604f1ff20ed037"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core-preview",
|
||||||
|
"futures-stable-preview",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -675,6 +762,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel-preview"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6f8aec6b0eb1d281843ec666fba2b71a49610181e3078fbef7a8cbed481821e"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core-preview",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -683,6 +780,55 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core-preview"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "098785413db44e5dbf3b1fc23c24039a9091bea5acb3eb0d293f386f18aff97d"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor-preview"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28ff61425699ca85de5c63c1f135278403518c3398bd15cf4b6fd1d21c9846e4"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel-preview",
|
||||||
|
"futures-core-preview",
|
||||||
|
"futures-util-preview",
|
||||||
|
"lazy_static",
|
||||||
|
"num_cpus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io-preview"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aaa769a6ac904912c1557b4dcf85b93db2bc9ba57c349f9ce43870e49d67f8e1"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core-preview",
|
||||||
|
"iovec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -694,12 +840,49 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-preview"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4d575096a4e2cf458f309b5b7bce5c8aaad8e874b8d77f0aa26c08d7ac18f74"
|
||||||
|
dependencies = [
|
||||||
|
"futures-async-runtime-preview",
|
||||||
|
"futures-channel-preview",
|
||||||
|
"futures-core-preview",
|
||||||
|
"futures-executor-preview",
|
||||||
|
"futures-io-preview",
|
||||||
|
"futures-sink-preview",
|
||||||
|
"futures-stable-preview",
|
||||||
|
"futures-util-preview",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink-preview"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5dc4cdc628b934f18a11ba070d589655f68cfec031a16381b0e7784ff0e9cc18"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"futures-channel-preview",
|
||||||
|
"futures-core-preview",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-stable-preview"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a6ba960b8bbbc14a9a741cc8ad9c26aff44538ea14be021db905b43f33854da"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core-preview",
|
||||||
|
"futures-executor-preview",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -712,15 +895,31 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util-preview"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b29aa737dba9e2e47a5dcd4d58ec7c7c2d5f78e8460f609f857bcf04163235e"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"futures-channel-preview",
|
||||||
|
"futures-core-preview",
|
||||||
|
"futures-io-preview",
|
||||||
|
"futures-sink-preview",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@@ -760,6 +959,12 @@ version = "0.28.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.26"
|
version = "0.3.26"
|
||||||
@@ -857,6 +1062,12 @@ version = "1.0.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.28"
|
version = "0.14.28"
|
||||||
@@ -933,6 +1144,15 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iovec"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.16"
|
version = "0.4.16"
|
||||||
@@ -1008,7 +1228,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"ctor",
|
"ctor",
|
||||||
"env_logger",
|
"env_logger 0.11.8",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-tls",
|
"hyper-tls",
|
||||||
@@ -1057,18 +1277,22 @@ dependencies = [
|
|||||||
"dbus-tokio",
|
"dbus-tokio",
|
||||||
"dbus-tree",
|
"dbus-tree",
|
||||||
"directories",
|
"directories",
|
||||||
"env_logger",
|
"env_logger 0.11.8",
|
||||||
|
"futures-preview",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"keyring",
|
"keyring",
|
||||||
"kordophone",
|
"kordophone",
|
||||||
"kordophone-db",
|
"kordophone-db",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-condvar",
|
"tokio-condvar",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
"xpc-connection",
|
||||||
|
"xpc-connection-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1082,7 +1306,8 @@ dependencies = [
|
|||||||
"dbus-codegen",
|
"dbus-codegen",
|
||||||
"dbus-tree",
|
"dbus-tree",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"env_logger",
|
"env_logger 0.11.8",
|
||||||
|
"futures-preview",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"kordophone",
|
"kordophone",
|
||||||
"kordophone-db",
|
"kordophone-db",
|
||||||
@@ -1092,6 +1317,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"xpc-connection",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1100,6 +1326,12 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.172"
|
version = "0.2.172"
|
||||||
@@ -1115,6 +1347,16 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@@ -1223,6 +1465,16 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "5.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -1302,6 +1554,16 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.5.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.32.2"
|
version = "0.32.2"
|
||||||
@@ -1390,6 +1652,12 @@ dependencies = [
|
|||||||
"windows-targets 0.48.5",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peeking_take_while"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@@ -1610,6 +1878,12 @@ version = "0.1.23"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.34"
|
version = "0.38.34"
|
||||||
@@ -1733,6 +2007,12 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
@@ -2219,6 +2499,15 @@ version = "0.2.95"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "3.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@@ -2421,3 +2710,21 @@ name = "xml-rs"
|
|||||||
version = "0.8.25"
|
version = "0.8.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
|
checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xpc-connection"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "git+https://github.com/dfrankland/xpc-connection-rs.git?rev=cd4fb3d#cd4fb3d05edb4292ccb9566ae27cdeb874222d2a"
|
||||||
|
dependencies = [
|
||||||
|
"block",
|
||||||
|
"futures",
|
||||||
|
"xpc-connection-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xpc-connection-sys"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "git+https://github.com/dfrankland/xpc-connection-rs.git?rev=cd4fb3d#cd4fb3d05edb4292ccb9566ae27cdeb874222d2a"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
]
|
||||||
|
|||||||
@@ -50,12 +50,12 @@ impl Conversation {
|
|||||||
|
|
||||||
impl PartialEq for Conversation {
|
impl PartialEq for Conversation {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.guid == other.guid &&
|
self.guid == other.guid
|
||||||
self.unread_count == other.unread_count &&
|
&& self.unread_count == other.unread_count
|
||||||
self.display_name == other.display_name &&
|
&& self.display_name == other.display_name
|
||||||
self.last_message_preview == other.last_message_preview &&
|
&& self.last_message_preview == other.last_message_preview
|
||||||
self.date == other.date &&
|
&& self.date == other.date
|
||||||
self.participants == other.participants
|
&& self.participants == other.participants
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,10 @@ impl From<kordophone::model::Conversation> for Conversation {
|
|||||||
participants: value
|
participants: value
|
||||||
.participant_display_names
|
.participant_display_names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| Participant::Remote { handle: p, contact_id: None }) // todo: this is wrong
|
.map(|p| Participant::Remote {
|
||||||
|
handle: p,
|
||||||
|
contact_id: None,
|
||||||
|
}) // todo: this is wrong
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,10 @@ impl From<Record> for Message {
|
|||||||
.and_then(|json| serde_json::from_str(&json).ok());
|
.and_then(|json| serde_json::from_str(&json).ok());
|
||||||
|
|
||||||
let message_sender = match record.sender_participant_handle {
|
let message_sender = match record.sender_participant_handle {
|
||||||
Some(handle) => Participant::Remote { handle, contact_id: None },
|
Some(handle) => Participant::Remote {
|
||||||
|
handle,
|
||||||
|
contact_id: None,
|
||||||
|
},
|
||||||
None => Participant::Me,
|
None => Participant::Me,
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -22,16 +22,18 @@ pub struct InsertableRecord {
|
|||||||
impl From<Participant> for InsertableRecord {
|
impl From<Participant> for InsertableRecord {
|
||||||
fn from(participant: Participant) -> Self {
|
fn from(participant: Participant) -> Self {
|
||||||
match participant {
|
match participant {
|
||||||
Participant::Me => InsertableRecord {
|
Participant::Me => InsertableRecord {
|
||||||
handle: "me".to_string(),
|
handle: "me".to_string(),
|
||||||
is_me: true,
|
is_me: true,
|
||||||
contact_id: None,
|
contact_id: None,
|
||||||
},
|
},
|
||||||
Participant::Remote { handle, contact_id, .. } => InsertableRecord {
|
Participant::Remote {
|
||||||
handle,
|
handle, contact_id, ..
|
||||||
is_me: false,
|
} => InsertableRecord {
|
||||||
contact_id,
|
handle,
|
||||||
},
|
is_me: false,
|
||||||
|
contact_id,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,16 +64,18 @@ impl From<Record> for Participant {
|
|||||||
impl From<Participant> for Record {
|
impl From<Participant> for Record {
|
||||||
fn from(participant: Participant) -> Self {
|
fn from(participant: Participant) -> Self {
|
||||||
match participant {
|
match participant {
|
||||||
Participant::Me => Record {
|
Participant::Me => Record {
|
||||||
handle: "me".to_string(),
|
handle: "me".to_string(),
|
||||||
is_me: true,
|
is_me: true,
|
||||||
contact_id: None,
|
contact_id: None,
|
||||||
},
|
},
|
||||||
Participant::Remote { handle, contact_id, .. } => Record {
|
Participant::Remote {
|
||||||
handle,
|
handle, contact_id, ..
|
||||||
is_me: false,
|
} => Record {
|
||||||
contact_id,
|
handle,
|
||||||
},
|
is_me: false,
|
||||||
|
contact_id,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,13 @@ impl From<kordophone::model::Message> for Message {
|
|||||||
contact_id: None,
|
contact_id: None,
|
||||||
|
|
||||||
// Weird server quirk: some sender handles are encoded with control characters.
|
// Weird server quirk: some sender handles are encoded with control characters.
|
||||||
handle: sender.chars()
|
handle: sender
|
||||||
.filter(|c| !c.is_control() && !matches!(c,
|
.chars()
|
||||||
'\u{202A}' | // LRE
|
.filter(|c| {
|
||||||
|
!c.is_control()
|
||||||
|
&& !matches!(
|
||||||
|
c,
|
||||||
|
'\u{202A}' | // LRE
|
||||||
'\u{202B}' | // RLE
|
'\u{202B}' | // RLE
|
||||||
'\u{202C}' | // PDF
|
'\u{202C}' | // PDF
|
||||||
'\u{202D}' | // LRO
|
'\u{202D}' | // LRO
|
||||||
@@ -38,8 +42,9 @@ impl From<kordophone::model::Message> for Message {
|
|||||||
'\u{2066}' | // LRI
|
'\u{2066}' | // LRI
|
||||||
'\u{2067}' | // RLI
|
'\u{2067}' | // RLI
|
||||||
'\u{2068}' | // FSI
|
'\u{2068}' | // FSI
|
||||||
'\u{2069}' // PDI
|
'\u{2069}' // PDI
|
||||||
))
|
)
|
||||||
|
})
|
||||||
.collect::<String>(),
|
.collect::<String>(),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ pub mod participant;
|
|||||||
|
|
||||||
pub use conversation::Conversation;
|
pub use conversation::Conversation;
|
||||||
pub use message::Message;
|
pub use message::Message;
|
||||||
pub use participant::Participant;
|
pub use participant::Participant;
|
||||||
|
|||||||
@@ -377,7 +377,11 @@ impl<'a> Repository<'a> {
|
|||||||
fn get_or_create_participant(&mut self, participant: &Participant) -> Option<String> {
|
fn get_or_create_participant(&mut self, participant: &Participant) -> Option<String> {
|
||||||
match participant {
|
match participant {
|
||||||
Participant::Me => None,
|
Participant::Me => None,
|
||||||
Participant::Remote { handle: p_handle, contact_id: c_id, .. } => {
|
Participant::Remote {
|
||||||
|
handle: p_handle,
|
||||||
|
contact_id: c_id,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
use crate::schema::participants::dsl::*;
|
use crate::schema::participants::dsl::*;
|
||||||
|
|
||||||
let existing_participant = participants
|
let existing_participant = participants
|
||||||
|
|||||||
@@ -271,10 +271,14 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
|
|||||||
Ok(token)
|
Ok(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mark_conversation_as_read(&mut self, conversation_id: &ConversationID) -> Result<(), Self::Error> {
|
async fn mark_conversation_as_read(
|
||||||
|
&mut self,
|
||||||
|
conversation_id: &ConversationID,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
// SERVER JANK: This should be POST, but it's GET for some reason.
|
// SERVER JANK: This should be POST, but it's GET for some reason.
|
||||||
let endpoint = format!("markConversation?guid={}", conversation_id);
|
let endpoint = format!("markConversation?guid={}", conversation_id);
|
||||||
self.response_with_body_retry(&endpoint, Method::GET, Body::empty, true).await?;
|
self.response_with_body_retry(&endpoint, Method::GET, Body::empty, true)
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ pub trait APIInterface {
|
|||||||
async fn authenticate(&mut self, credentials: Credentials) -> Result<JwtToken, Self::Error>;
|
async fn authenticate(&mut self, credentials: Credentials) -> Result<JwtToken, Self::Error>;
|
||||||
|
|
||||||
// (GET) /markConversation
|
// (GET) /markConversation
|
||||||
async fn mark_conversation_as_read(&mut self, conversation_id: &ConversationID) -> Result<(), Self::Error>;
|
async fn mark_conversation_as_read(
|
||||||
|
&mut self,
|
||||||
|
conversation_id: &ConversationID,
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
// (WS) /updates
|
// (WS) /updates
|
||||||
async fn open_event_socket(
|
async fn open_event_socket(
|
||||||
|
|||||||
@@ -149,7 +149,10 @@ impl APIInterface for TestClient {
|
|||||||
Ok(String::from("test"))
|
Ok(String::from("test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mark_conversation_as_read(&mut self, conversation_id: &ConversationID) -> Result<(), Self::Error> {
|
async fn mark_conversation_as_read(
|
||||||
|
&mut self,
|
||||||
|
conversation_id: &ConversationID,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,21 @@ tokio-condvar = "0.3.0"
|
|||||||
uuid = "1.16.0"
|
uuid = "1.16.0"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
# D-Bus dependencies only on Linux
|
# D-Bus dependencies only on Linux
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
dbus = { version = "0.9.7", features = ["futures"] }
|
dbus = { version = "0.9.7", features = ["futures"] }
|
||||||
dbus-crossroads = "0.5.2"
|
dbus-crossroads = "0.5.2"
|
||||||
dbus-tokio = "0.7.6"
|
dbus-tokio = "0.7.6"
|
||||||
dbus-tree = "0.9.2"
|
dbus-tree = "0.9.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.build-dependencies]
|
|
||||||
# D-Bus codegen only on Linux
|
# D-Bus codegen only on Linux
|
||||||
|
[target.'cfg(target_os = "linux")'.build-dependencies]
|
||||||
dbus-codegen = "0.10.0"
|
dbus-codegen = "0.10.0"
|
||||||
dbus-crossroads = "0.5.1"
|
dbus-crossroads = "0.5.1"
|
||||||
|
|
||||||
|
# XPC (libxpc) interface for macOS IPC
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
futures-preview = "=0.2.2"
|
||||||
|
xpc-connection = { git = "https://github.com/dfrankland/xpc-connection-rs.git", rev = "cd4fb3d", package = "xpc-connection" }
|
||||||
|
xpc-connection-sys = { git = "https://github.com/dfrankland/xpc-connection-rs.git", rev = "cd4fb3d", package = "xpc-connection-sys" }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ fn main() {
|
|||||||
|
|
||||||
let xml = std::fs::read_to_string(KORDOPHONE_XML).expect("Error reading server dbus interface");
|
let xml = std::fs::read_to_string(KORDOPHONE_XML).expect("Error reading server dbus interface");
|
||||||
|
|
||||||
let output = dbus_codegen::generate(&xml, &opts)
|
let output =
|
||||||
.expect("Error generating server dbus interface");
|
dbus_codegen::generate(&xml, &opts).expect("Error generating server dbus interface");
|
||||||
|
|
||||||
std::fs::write(out_path, output).expect("Error writing server dbus code");
|
std::fs::write(out_path, output).expect("Error writing server dbus code");
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use super::ContactResolverBackend;
|
use super::ContactResolverBackend;
|
||||||
use dbus::blocking::Connection;
|
|
||||||
use dbus::arg::{RefArg, Variant};
|
use dbus::arg::{RefArg, Variant};
|
||||||
|
use dbus::blocking::Connection;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EDSContactResolverBackend;
|
pub struct EDSContactResolverBackend;
|
||||||
@@ -40,8 +40,13 @@ static ADDRESS_BOOK_HANDLE: OnceCell<Mutex<AddressBookHandle>> = OnceCell::new()
|
|||||||
|
|
||||||
/// Check whether a given well-known name currently has an owner on the bus.
|
/// Check whether a given well-known name currently has an owner on the bus.
|
||||||
fn name_has_owner(conn: &Connection, name: &str) -> bool {
|
fn name_has_owner(conn: &Connection, name: &str) -> bool {
|
||||||
let proxy = conn.with_proxy("org.freedesktop.DBus", "/org/freedesktop/DBus", Duration::from_secs(2));
|
let proxy = conn.with_proxy(
|
||||||
let result: Result<(bool,), _> = proxy.method_call("org.freedesktop.DBus", "NameHasOwner", (name.to_string(),));
|
"org.freedesktop.DBus",
|
||||||
|
"/org/freedesktop/DBus",
|
||||||
|
Duration::from_secs(2),
|
||||||
|
);
|
||||||
|
let result: Result<(bool,), _> =
|
||||||
|
proxy.method_call("org.freedesktop.DBus", "NameHasOwner", (name.to_string(),));
|
||||||
result.map(|(b,)| b).unwrap_or(false)
|
result.map(|(b,)| b).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,10 +104,7 @@ fn ensure_address_book_uid(conn: &Connection) -> anyhow::Result<String> {
|
|||||||
|
|
||||||
// The GetManagedObjects reply is the usual ObjectManager map.
|
// The GetManagedObjects reply is the usual ObjectManager map.
|
||||||
let (managed_objects,): (
|
let (managed_objects,): (
|
||||||
HashMap<
|
HashMap<dbus::Path<'static>, HashMap<String, HashMap<String, Variant<Box<dyn RefArg>>>>>,
|
||||||
dbus::Path<'static>,
|
|
||||||
HashMap<String, HashMap<String, Variant<Box<dyn RefArg>>>>,
|
|
||||||
>,
|
|
||||||
) = source_manager_proxy.method_call(
|
) = source_manager_proxy.method_call(
|
||||||
"org.freedesktop.DBus.ObjectManager",
|
"org.freedesktop.DBus.ObjectManager",
|
||||||
"GetManagedObjects",
|
"GetManagedObjects",
|
||||||
@@ -153,10 +155,7 @@ fn data_contains_address_book_backend(data: &str) -> bool {
|
|||||||
/// Open the Evolution address book referenced by `source_uid` and return the
|
/// Open the Evolution address book referenced by `source_uid` and return the
|
||||||
/// pair `(object_path, bus_name)` that identifies the newly created D-Bus
|
/// pair `(object_path, bus_name)` that identifies the newly created D-Bus
|
||||||
/// proxy.
|
/// proxy.
|
||||||
fn open_address_book(
|
fn open_address_book(conn: &Connection, source_uid: &str) -> anyhow::Result<(String, String)> {
|
||||||
conn: &Connection,
|
|
||||||
source_uid: &str,
|
|
||||||
) -> anyhow::Result<(String, String)> {
|
|
||||||
let factory_proxy = conn.with_proxy(
|
let factory_proxy = conn.with_proxy(
|
||||||
"org.gnome.evolution.dataserver.AddressBook10",
|
"org.gnome.evolution.dataserver.AddressBook10",
|
||||||
"/org/gnome/evolution/dataserver/AddressBookFactory",
|
"/org/gnome/evolution/dataserver/AddressBookFactory",
|
||||||
@@ -177,11 +176,8 @@ fn open_address_book(
|
|||||||
/// calls the `Open` method once per process. We ignore any error here
|
/// calls the `Open` method once per process. We ignore any error here
|
||||||
/// because the backend might already be open.
|
/// because the backend might already be open.
|
||||||
fn ensure_address_book_open(proxy: &dbus::blocking::Proxy<&Connection>) {
|
fn ensure_address_book_open(proxy: &dbus::blocking::Proxy<&Connection>) {
|
||||||
let _: Result<(), _> = proxy.method_call(
|
let _: Result<(), _> =
|
||||||
"org.gnome.evolution.dataserver.AddressBook",
|
proxy.method_call("org.gnome.evolution.dataserver.AddressBook", "Open", ());
|
||||||
"Open",
|
|
||||||
(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContactResolverBackend for EDSContactResolverBackend {
|
impl ContactResolverBackend for EDSContactResolverBackend {
|
||||||
@@ -295,4 +291,4 @@ impl Default for EDSContactResolverBackend {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ impl ContactResolverBackend for GenericContactResolverBackend {
|
|||||||
fn get_contact_display_name(&self, contact_id: &Self::ContactID) -> Option<String> {
|
fn get_contact_display_name(&self, contact_id: &Self::ContactID) -> Option<String> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ pub struct EDSContactResolverBackend;
|
|||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
impl Default for EDSContactResolverBackend {
|
impl Default for EDSContactResolverBackend {
|
||||||
fn default() -> Self { EDSContactResolverBackend }
|
fn default() -> Self {
|
||||||
|
EDSContactResolverBackend
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
@@ -56,7 +58,11 @@ where
|
|||||||
T: Default,
|
T: Default,
|
||||||
{
|
{
|
||||||
pub fn new(backend: T) -> Self {
|
pub fn new(backend: T) -> Self {
|
||||||
Self { backend, display_name_cache: HashMap::new(), contact_id_cache: HashMap::new() }
|
Self {
|
||||||
|
backend,
|
||||||
|
display_name_cache: HashMap::new(),
|
||||||
|
contact_id_cache: HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_contact_id(&mut self, address: &str) -> Option<AnyContactID> {
|
pub fn resolve_contact_id(&mut self, address: &str) -> Option<AnyContactID> {
|
||||||
@@ -66,7 +72,8 @@ where
|
|||||||
|
|
||||||
let id = self.backend.resolve_contact_id(address).map(|id| id.into());
|
let id = self.backend.resolve_contact_id(address).map(|id| id.into());
|
||||||
if let Some(ref id) = id {
|
if let Some(ref id) = id {
|
||||||
self.contact_id_cache.insert(address.to_string(), id.clone());
|
self.contact_id_cache
|
||||||
|
.insert(address.to_string(), id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
id
|
id
|
||||||
@@ -80,7 +87,8 @@ where
|
|||||||
let backend_contact_id: T::ContactID = T::ContactID::from((*contact_id).clone());
|
let backend_contact_id: T::ContactID = T::ContactID::from((*contact_id).clone());
|
||||||
let display_name = self.backend.get_contact_display_name(&backend_contact_id);
|
let display_name = self.backend.get_contact_display_name(&backend_contact_id);
|
||||||
if let Some(ref display_name) = display_name {
|
if let Some(ref display_name) = display_name {
|
||||||
self.display_name_cache.insert(contact_id.to_string(), display_name.clone());
|
self.display_name_cache
|
||||||
|
.insert(contact_id.to_string(), display_name.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
display_name
|
display_name
|
||||||
|
|||||||
@@ -522,12 +522,18 @@ impl Daemon {
|
|||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
for p in &saved.participants {
|
for p in &saved.participants {
|
||||||
if let DbParticipant::Remote { handle, contact_id: None } = p {
|
if let DbParticipant::Remote {
|
||||||
|
handle,
|
||||||
|
contact_id: None,
|
||||||
|
} = p
|
||||||
|
{
|
||||||
log::trace!(target: target::SYNC, "Resolving contact id for participant: {}", handle);
|
log::trace!(target: target::SYNC, "Resolving contact id for participant: {}", handle);
|
||||||
if let Some(contact) = contact_resolver.resolve_contact_id(handle) {
|
if let Some(contact) = contact_resolver.resolve_contact_id(handle) {
|
||||||
log::trace!(target: target::SYNC, "Resolved contact id for participant: {}", contact);
|
log::trace!(target: target::SYNC, "Resolved contact id for participant: {}", contact);
|
||||||
let _ = database
|
let _ = database
|
||||||
.with_repository(|r| r.update_participant_contact(&handle, &contact))
|
.with_repository(|r| {
|
||||||
|
r.update_participant_contact(&handle, &contact)
|
||||||
|
})
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
log::trace!(target: target::SYNC, "No contact id found for participant: {}", handle);
|
log::trace!(target: target::SYNC, "No contact id found for participant: {}", handle);
|
||||||
@@ -663,11 +669,11 @@ impl Daemon {
|
|||||||
signal_sender: &Sender<Signal>,
|
signal_sender: &Sender<Signal>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
log::debug!(target: target::DAEMON, "Updating conversation metadata: {}", conversation.guid);
|
log::debug!(target: target::DAEMON, "Updating conversation metadata: {}", conversation.guid);
|
||||||
let updated = database.with_repository(|r| r.merge_conversation_metadata(conversation)).await?;
|
let updated = database
|
||||||
|
.with_repository(|r| r.merge_conversation_metadata(conversation))
|
||||||
|
.await?;
|
||||||
if updated {
|
if updated {
|
||||||
signal_sender
|
signal_sender.send(Signal::ConversationsUpdated).await?;
|
||||||
.send(Signal::ConversationsUpdated)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -177,7 +177,10 @@ impl From<Participant> for DbParticipant {
|
|||||||
fn from(participant: Participant) -> Self {
|
fn from(participant: Participant) -> Self {
|
||||||
match participant {
|
match participant {
|
||||||
Participant::Me => DbParticipant::Me,
|
Participant::Me => DbParticipant::Me,
|
||||||
Participant::Remote { handle, contact_id } => DbParticipant::Remote { handle, contact_id: contact_id.clone() },
|
Participant::Remote { handle, contact_id } => DbParticipant::Remote {
|
||||||
|
handle,
|
||||||
|
contact_id: contact_id.clone(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,9 @@ impl UpdateMonitor {
|
|||||||
UpdateEventData::ConversationChanged(conversation) => {
|
UpdateEventData::ConversationChanged(conversation) => {
|
||||||
log::info!(target: target::UPDATES, "Conversation changed: {:?}", conversation);
|
log::info!(target: target::UPDATES, "Conversation changed: {:?}", conversation);
|
||||||
|
|
||||||
// Explicitly update the unread count, we assume this is fresh from the notification.
|
// Explicitly update the unread count, we assume this is fresh from the notification.
|
||||||
let db_conversation: kordophone_db::models::Conversation = conversation.clone().into();
|
let db_conversation: kordophone_db::models::Conversation =
|
||||||
|
conversation.clone().into();
|
||||||
self.send_event(|r| Event::UpdateConversationMetadata(db_conversation, r))
|
self.send_event(|r| Event::UpdateConversationMetadata(db_conversation, r))
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use dbus::arg;
|
use dbus::arg;
|
||||||
use dbus_tree::MethodErr;
|
use dbus_tree::MethodErr;
|
||||||
use std::{future::Future, thread};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::{future::Future, thread};
|
||||||
use tokio::sync::{mpsc, oneshot, Mutex};
|
use tokio::sync::{mpsc, oneshot, Mutex};
|
||||||
|
|
||||||
use crate::daemon::{
|
use crate::daemon::{
|
||||||
|
contact_resolver::{ContactResolver, DefaultContactResolverBackend},
|
||||||
events::{Event, Reply},
|
events::{Event, Reply},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
signals::Signal,
|
signals::Signal,
|
||||||
DaemonResult,
|
DaemonResult,
|
||||||
contact_resolver::{ContactResolver, DefaultContactResolverBackend},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use kordophone_db::models::participant::Participant;
|
use kordophone_db::models::participant::Participant;
|
||||||
@@ -37,7 +37,8 @@ impl DBusAgent {
|
|||||||
|
|
||||||
pub async fn run(self) {
|
pub async fn run(self) {
|
||||||
// Establish a session bus connection.
|
// Establish a session bus connection.
|
||||||
let (resource, connection) = connection::new_session_sync().expect("Failed to connect to session bus");
|
let (resource, connection) =
|
||||||
|
connection::new_session_sync().expect("Failed to connect to session bus");
|
||||||
|
|
||||||
// Ensure the D-Bus resource is polled.
|
// Ensure the D-Bus resource is polled.
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -79,7 +80,10 @@ impl DBusAgent {
|
|||||||
Signal::ConversationsUpdated => {
|
Signal::ConversationsUpdated => {
|
||||||
log::debug!("Sending signal: ConversationsUpdated");
|
log::debug!("Sending signal: ConversationsUpdated");
|
||||||
registry
|
registry
|
||||||
.send_signal(interface::OBJECT_PATH, DbusSignals::ConversationsUpdated {})
|
.send_signal(
|
||||||
|
interface::OBJECT_PATH,
|
||||||
|
DbusSignals::ConversationsUpdated {},
|
||||||
|
)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
log::error!("Failed to send signal");
|
log::error!("Failed to send signal");
|
||||||
0
|
0
|
||||||
@@ -118,7 +122,8 @@ impl DBusAgent {
|
|||||||
Signal::AttachmentUploaded(upload_guid, attachment_guid) => {
|
Signal::AttachmentUploaded(upload_guid, attachment_guid) => {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Sending signal: AttachmentUploaded for upload {}, attachment {}",
|
"Sending signal: AttachmentUploaded for upload {}, attachment {}",
|
||||||
upload_guid, attachment_guid
|
upload_guid,
|
||||||
|
attachment_guid
|
||||||
);
|
);
|
||||||
registry
|
registry
|
||||||
.send_signal(
|
.send_signal(
|
||||||
@@ -154,7 +159,10 @@ impl DBusAgent {
|
|||||||
std::future::pending::<()>().await;
|
std::future::pending::<()>().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_event<T>(&self, make_event: impl FnOnce(Reply<T>) -> Event) -> DaemonResult<T> {
|
pub async fn send_event<T>(
|
||||||
|
&self,
|
||||||
|
make_event: impl FnOnce(Reply<T>) -> Event,
|
||||||
|
) -> DaemonResult<T> {
|
||||||
let (reply_tx, reply_rx) = oneshot::channel();
|
let (reply_tx, reply_rx) = oneshot::channel();
|
||||||
self.event_sink
|
self.event_sink
|
||||||
.send(make_event(reply_tx))
|
.send(make_event(reply_tx))
|
||||||
@@ -172,26 +180,29 @@ impl DBusAgent {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.map_err(|e| MethodErr::failed(&format!("Daemon error: {}", e)))
|
.map_err(|e| MethodErr::failed(&format!("Daemon error: {}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_participant_display_name(&mut self, participant: &Participant) -> String {
|
fn resolve_participant_display_name(&mut self, participant: &Participant) -> String {
|
||||||
match participant {
|
match participant {
|
||||||
// Me (we should use a special string here...)
|
// Me (we should use a special string here...)
|
||||||
Participant::Me => "(Me)".to_string(),
|
Participant::Me => "(Me)".to_string(),
|
||||||
|
|
||||||
// Remote participant with a resolved contact_id
|
// Remote participant with a resolved contact_id
|
||||||
Participant::Remote { handle, contact_id: Some(contact_id), .. } => {
|
Participant::Remote {
|
||||||
self.contact_resolver.get_contact_display_name(contact_id).unwrap_or_else(|| handle.clone())
|
handle,
|
||||||
}
|
contact_id: Some(contact_id),
|
||||||
|
..
|
||||||
|
} => self
|
||||||
|
.contact_resolver
|
||||||
|
.get_contact_display_name(contact_id)
|
||||||
|
.unwrap_or_else(|| handle.clone()),
|
||||||
|
|
||||||
// Remote participant without a resolved contact_id
|
// Remote participant without a resolved contact_id
|
||||||
Participant::Remote { handle, .. } => {
|
Participant::Remote { handle, .. } => handle.clone(),
|
||||||
handle.clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// D-Bus repository interface implementation
|
// D-Bus repository interface implementation
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -203,9 +214,12 @@ impl DbusRepository for DBusAgent {
|
|||||||
self.send_event_sync(Event::GetVersion)
|
self.send_event_sync(Event::GetVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_conversations(&mut self, limit: i32, offset: i32) -> Result<Vec<arg::PropMap>, MethodErr> {
|
fn get_conversations(
|
||||||
self
|
&mut self,
|
||||||
.send_event_sync(|r| Event::GetAllConversations(limit, offset, r))
|
limit: i32,
|
||||||
|
offset: i32,
|
||||||
|
) -> Result<Vec<arg::PropMap>, MethodErr> {
|
||||||
|
self.send_event_sync(|r| Event::GetAllConversations(limit, offset, r))
|
||||||
.map(|conversations| {
|
.map(|conversations| {
|
||||||
conversations
|
conversations
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -243,7 +257,6 @@ impl DbusRepository for DBusAgent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn sync_conversation_list(&mut self) -> Result<(), MethodErr> {
|
fn sync_conversation_list(&mut self) -> Result<(), MethodErr> {
|
||||||
self.send_event_sync(Event::SyncConversationList)
|
self.send_event_sync(Event::SyncConversationList)
|
||||||
}
|
}
|
||||||
@@ -260,15 +273,18 @@ impl DbusRepository for DBusAgent {
|
|||||||
self.send_event_sync(|r| Event::MarkConversationAsRead(conversation_id, r))
|
self.send_event_sync(|r| Event::MarkConversationAsRead(conversation_id, r))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_messages(&mut self, conversation_id: String, last_message_id: String) -> Result<Vec<arg::PropMap>, MethodErr> {
|
fn get_messages(
|
||||||
|
&mut self,
|
||||||
|
conversation_id: String,
|
||||||
|
last_message_id: String,
|
||||||
|
) -> Result<Vec<arg::PropMap>, MethodErr> {
|
||||||
let last_message_id_opt = if last_message_id.is_empty() {
|
let last_message_id_opt = if last_message_id.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(last_message_id)
|
Some(last_message_id)
|
||||||
};
|
};
|
||||||
|
|
||||||
self
|
self.send_event_sync(|r| Event::GetMessages(conversation_id, last_message_id_opt, r))
|
||||||
.send_event_sync(|r| Event::GetMessages(conversation_id, last_message_id_opt, r))
|
|
||||||
.map(|messages| {
|
.map(|messages| {
|
||||||
messages
|
messages
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -286,7 +302,9 @@ impl DbusRepository for DBusAgent {
|
|||||||
);
|
);
|
||||||
map.insert(
|
map.insert(
|
||||||
"sender".into(),
|
"sender".into(),
|
||||||
arg::Variant(Box::new(self.resolve_participant_display_name(&msg.sender.into()))),
|
arg::Variant(Box::new(
|
||||||
|
self.resolve_participant_display_name(&msg.sender.into()),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Attachments array
|
// Attachments array
|
||||||
@@ -312,7 +330,9 @@ impl DbusRepository for DBusAgent {
|
|||||||
);
|
);
|
||||||
attachment_map.insert(
|
attachment_map.insert(
|
||||||
"preview_path".into(),
|
"preview_path".into(),
|
||||||
arg::Variant(Box::new(preview_path.to_string_lossy().to_string())),
|
arg::Variant(Box::new(
|
||||||
|
preview_path.to_string_lossy().to_string(),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
attachment_map.insert(
|
attachment_map.insert(
|
||||||
"downloaded".into(),
|
"downloaded".into(),
|
||||||
@@ -374,27 +394,34 @@ impl DbusRepository for DBusAgent {
|
|||||||
text: String,
|
text: String,
|
||||||
attachment_guids: Vec<String>,
|
attachment_guids: Vec<String>,
|
||||||
) -> Result<String, MethodErr> {
|
) -> Result<String, MethodErr> {
|
||||||
self
|
self.send_event_sync(|r| Event::SendMessage(conversation_id, text, attachment_guids, r))
|
||||||
.send_event_sync(|r| Event::SendMessage(conversation_id, text, attachment_guids, r))
|
|
||||||
.map(|uuid| uuid.to_string())
|
.map(|uuid| uuid.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_attachment_info(&mut self, attachment_id: String) -> Result<(String, String, bool, bool), MethodErr> {
|
fn get_attachment_info(
|
||||||
self.send_event_sync(|r| Event::GetAttachment(attachment_id, r)).map(|attachment| {
|
&mut self,
|
||||||
let path = attachment.get_path_for_preview(false);
|
attachment_id: String,
|
||||||
let downloaded = attachment.is_downloaded(false);
|
) -> Result<(String, String, bool, bool), MethodErr> {
|
||||||
let preview_path = attachment.get_path_for_preview(true);
|
self.send_event_sync(|r| Event::GetAttachment(attachment_id, r))
|
||||||
let preview_downloaded = attachment.is_downloaded(true);
|
.map(|attachment| {
|
||||||
(
|
let path = attachment.get_path_for_preview(false);
|
||||||
path.to_string_lossy().to_string(),
|
let downloaded = attachment.is_downloaded(false);
|
||||||
preview_path.to_string_lossy().to_string(),
|
let preview_path = attachment.get_path_for_preview(true);
|
||||||
downloaded,
|
let preview_downloaded = attachment.is_downloaded(true);
|
||||||
preview_downloaded,
|
(
|
||||||
)
|
path.to_string_lossy().to_string(),
|
||||||
})
|
preview_path.to_string_lossy().to_string(),
|
||||||
|
downloaded,
|
||||||
|
preview_downloaded,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn download_attachment(&mut self, attachment_id: String, preview: bool) -> Result<(), MethodErr> {
|
fn download_attachment(
|
||||||
|
&mut self,
|
||||||
|
attachment_id: String,
|
||||||
|
preview: bool,
|
||||||
|
) -> Result<(), MethodErr> {
|
||||||
self.send_event_sync(|r| Event::DownloadAttachment(attachment_id, preview, r))
|
self.send_event_sync(|r| Event::DownloadAttachment(attachment_id, preview, r))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,4 +509,4 @@ where
|
|||||||
.join()
|
.join()
|
||||||
})
|
})
|
||||||
.expect("Error joining runtime thread")
|
.expect("Error joining runtime thread")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
pub mod endpoint;
|
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
|
pub mod endpoint;
|
||||||
|
|
||||||
pub mod interface {
|
pub mod interface {
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ mod daemon;
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod dbus;
|
mod dbus;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
mod xpc;
|
||||||
|
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use std::future;
|
use std::future;
|
||||||
|
|
||||||
@@ -33,7 +36,16 @@ async fn start_ipc_agent(daemon: &mut Daemon) {
|
|||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
async fn start_ipc_agent(daemon: &mut Daemon) {
|
async fn start_ipc_agent(daemon: &mut Daemon) {
|
||||||
// TODO: Implement macOS IPC agent.
|
// Start the macOS XPC agent (events in, signals out) on a dedicated thread.
|
||||||
|
let agent = xpc::agent::XpcAgent::new(daemon.event_sender.clone(), daemon.obtain_signal_receiver());
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
// Use a single-threaded Tokio runtime for the XPC agent.
|
||||||
|
let rt = tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.expect("Unable to create tokio runtime for XPC agent");
|
||||||
|
rt.block_on(agent.run());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||||
|
|||||||
38
kordophoned/src/xpc/agent.rs
Normal file
38
kordophoned/src/xpc/agent.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use crate::daemon::{events::Event, signals::Signal, DaemonResult};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::{mpsc, oneshot, Mutex};
|
||||||
|
|
||||||
|
/// XPC IPC agent that forwards daemon events and signals over libxpc.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct XpcAgent {
|
||||||
|
event_sink: mpsc::Sender<Event>,
|
||||||
|
signal_receiver: Arc<Mutex<Option<mpsc::Receiver<Signal>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XpcAgent {
|
||||||
|
/// Create a new XPC agent with an event sink and signal receiver.
|
||||||
|
pub fn new(event_sink: mpsc::Sender<Event>, signal_receiver: mpsc::Receiver<Signal>) -> Self {
|
||||||
|
Self {
|
||||||
|
event_sink,
|
||||||
|
signal_receiver: Arc::new(Mutex::new(Some(signal_receiver))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the XPC agent: perform a basic GetVersion IPC call to the daemon and print the result.
|
||||||
|
pub async fn run(self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send an event to the daemon and await its reply.
|
||||||
|
pub async fn send_event<T>(
|
||||||
|
&self,
|
||||||
|
make_event: impl FnOnce(crate::daemon::events::Reply<T>) -> Event,
|
||||||
|
) -> DaemonResult<T> {
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
self.event_sink
|
||||||
|
.send(make_event(tx))
|
||||||
|
.await
|
||||||
|
.map_err(|_| "Failed to send event")?;
|
||||||
|
rx.await.map_err(|_| "Failed to receive reply".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
27
kordophoned/src/xpc/endpoint.rs
Normal file
27
kordophoned/src/xpc/endpoint.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#![cfg(target_os = "macos")]
|
||||||
|
//! XPC registry for registering handlers and emitting signals.
|
||||||
|
|
||||||
|
/// Registry for XPC message handlers and signal emission.
|
||||||
|
pub struct XpcRegistry;
|
||||||
|
|
||||||
|
impl XpcRegistry {
|
||||||
|
/// Create a new XPC registry for the service.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
XpcRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a handler for incoming messages at a given endpoint.
|
||||||
|
pub fn register_handler<F>(&self, _name: &str, _handler: F)
|
||||||
|
where
|
||||||
|
F: Fn(&[u8]) -> Vec<u8> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
// TODO: Implement handler registration over libxpc using SERVICE_NAME
|
||||||
|
let _ = (_name, _handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a signal (notification) to connected clients.
|
||||||
|
pub fn send_signal<T: serde::Serialize>(&self, _signal: &str, _data: &T) {
|
||||||
|
// TODO: Serialize and send signal over XPC
|
||||||
|
let _ = (_signal, _data);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
kordophoned/src/xpc/interface.rs
Normal file
8
kordophoned/src/xpc/interface.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#![cfg(target_os = "macos")]
|
||||||
|
//! XPC interface definitions for macOS IPC
|
||||||
|
|
||||||
|
/// Mach service name for the XPC interface (must include trailing NUL).
|
||||||
|
pub const SERVICE_NAME: &str = "net.buzzert.kordophonecd\0";
|
||||||
|
|
||||||
|
/// Method names for the XPC interface (must include trailing NUL).
|
||||||
|
pub const GET_VERSION_METHOD: &str = "GetVersion\0";
|
||||||
6
kordophoned/src/xpc/mod.rs
Normal file
6
kordophoned/src/xpc/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#![cfg(target_os = "macos")]
|
||||||
|
//! macOS XPC IPC interface modules.
|
||||||
|
|
||||||
|
pub mod agent;
|
||||||
|
pub mod endpoint;
|
||||||
|
pub mod interface;
|
||||||
@@ -29,3 +29,8 @@ dbus-tree = "0.9.2"
|
|||||||
# D-Bus codegen only on Linux
|
# D-Bus codegen only on Linux
|
||||||
[target.'cfg(target_os = "linux")'.build-dependencies]
|
[target.'cfg(target_os = "linux")'.build-dependencies]
|
||||||
dbus-codegen = "0.10.0"
|
dbus-codegen = "0.10.0"
|
||||||
|
|
||||||
|
# XPC (libxpc) interface only on macOS
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
futures-preview = "=0.2.2"
|
||||||
|
xpc-connection = { git = "https://github.com/dfrankland/xpc-connection-rs.git", rev = "cd4fb3d", package = "xpc-connection" }
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ fn main() {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let xml = std::fs::read_to_string(KORDOPHONE_XML)
|
let xml = std::fs::read_to_string(KORDOPHONE_XML).expect("Error reading server dbus interface");
|
||||||
.expect("Error reading server dbus interface");
|
|
||||||
|
|
||||||
let output = dbus_codegen::generate(&xml, &opts)
|
let output =
|
||||||
.expect("Error generating client dbus interface");
|
dbus_codegen::generate(&xml, &opts).expect("Error generating client dbus interface");
|
||||||
|
|
||||||
std::fs::write(out_path, output).expect("Error writing client dbus code");
|
std::fs::write(out_path, output).expect("Error writing client dbus code");
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ use clap::Subcommand;
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod dbus;
|
mod dbus;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
mod xpc;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait DaemonInterface {
|
pub trait DaemonInterface {
|
||||||
async fn print_version(&mut self) -> Result<()>;
|
async fn print_version(&mut self) -> Result<()>;
|
||||||
@@ -40,40 +43,72 @@ impl StubDaemonInterface {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl DaemonInterface for StubDaemonInterface {
|
impl DaemonInterface for StubDaemonInterface {
|
||||||
async fn print_version(&mut self) -> Result<()> {
|
async fn print_version(&mut self) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn print_conversations(&mut self) -> Result<()> {
|
async fn print_conversations(&mut self) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn sync_conversations(&mut self, _conversation_id: Option<String>) -> Result<()> {
|
async fn sync_conversations(&mut self, _conversation_id: Option<String>) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn sync_conversations_list(&mut self) -> Result<()> {
|
async fn sync_conversations_list(&mut self) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn print_messages(&mut self, _conversation_id: String, _last_message_id: Option<String>) -> Result<()> {
|
async fn print_messages(
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
&mut self,
|
||||||
|
_conversation_id: String,
|
||||||
|
_last_message_id: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn enqueue_outgoing_message(&mut self, _conversation_id: String, _text: String) -> Result<()> {
|
async fn enqueue_outgoing_message(
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
&mut self,
|
||||||
|
_conversation_id: String,
|
||||||
|
_text: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn wait_for_signals(&mut self) -> Result<()> {
|
async fn wait_for_signals(&mut self) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn config(&mut self, _cmd: ConfigCommands) -> Result<()> {
|
async fn config(&mut self, _cmd: ConfigCommands) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn delete_all_conversations(&mut self) -> Result<()> {
|
async fn delete_all_conversations(&mut self) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn download_attachment(&mut self, _attachment_id: String) -> Result<()> {
|
async fn download_attachment(&mut self, _attachment_id: String) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn upload_attachment(&mut self, _path: String) -> Result<()> {
|
async fn upload_attachment(&mut self, _path: String) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
async fn mark_conversation_as_read(&mut self, _conversation_id: String) -> Result<()> {
|
async fn mark_conversation_as_read(&mut self, _conversation_id: String) -> Result<()> {
|
||||||
Err(anyhow::anyhow!("Daemon interface not implemented on this platform"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Daemon interface not implemented on this platform"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +117,11 @@ pub fn new_daemon_interface() -> Result<Box<dyn DaemonInterface>> {
|
|||||||
{
|
{
|
||||||
Ok(Box::new(dbus::DBusDaemonInterface::new()?))
|
Ok(Box::new(dbus::DBusDaemonInterface::new()?))
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
Ok(Box::new(xpc::XpcDaemonInterface::new()?))
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||||
{
|
{
|
||||||
Ok(Box::new(StubDaemonInterface::new()?))
|
Ok(Box::new(StubDaemonInterface::new()?))
|
||||||
}
|
}
|
||||||
@@ -162,7 +201,9 @@ impl Commands {
|
|||||||
conversation_id,
|
conversation_id,
|
||||||
last_message_id,
|
last_message_id,
|
||||||
} => {
|
} => {
|
||||||
client.print_messages(conversation_id, last_message_id).await
|
client
|
||||||
|
.print_messages(conversation_id, last_message_id)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
Commands::DeleteAllConversations => client.delete_all_conversations().await,
|
Commands::DeleteAllConversations => client.delete_all_conversations().await,
|
||||||
Commands::SendMessage {
|
Commands::SendMessage {
|
||||||
|
|||||||
100
kpcli/src/daemon/xpc.rs
Normal file
100
kpcli/src/daemon/xpc.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#![cfg(target_os = "macos")]
|
||||||
|
//! macOS XPC implementation of the DaemonInterface for kpcli.
|
||||||
|
|
||||||
|
use super::{ConfigCommands, DaemonInterface};
|
||||||
|
use anyhow::Result;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures::stream::StreamExt;
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use xpc_connection::{Message, XpcConnection};
|
||||||
|
|
||||||
|
const SERVICE_NAME: &str = "net.buzzert.kordophonecd\0";
|
||||||
|
const GET_VERSION_METHOD: &str = "GetVersion\0";
|
||||||
|
|
||||||
|
/// XPC-based implementation of DaemonInterface that sends method calls to the daemon over libxpc.
|
||||||
|
pub struct XpcDaemonInterface;
|
||||||
|
|
||||||
|
impl XpcDaemonInterface {
|
||||||
|
/// Create a new XpcDaemonInterface. No state is held.
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl DaemonInterface for XpcDaemonInterface {
|
||||||
|
async fn print_version(&mut self) -> Result<()> {
|
||||||
|
// Open an XPC connection to the daemon service
|
||||||
|
let mut conn = XpcConnection::new(SERVICE_NAME);
|
||||||
|
let mut incoming = conn.connect();
|
||||||
|
|
||||||
|
// Send a GetVersion request as a dictionary message
|
||||||
|
let mut dict = HashMap::new();
|
||||||
|
dict.insert(
|
||||||
|
GET_VERSION_METHOD.to_string(),
|
||||||
|
Message::String(String::new()),
|
||||||
|
);
|
||||||
|
conn.send_message(Message::Dictionary(dict));
|
||||||
|
|
||||||
|
// Wait for a single string reply (futures-preview StreamFuture returns (Option<Item>, Stream))
|
||||||
|
let (opt_msg, _) = match block_on(incoming.next()) {
|
||||||
|
Ok(pair) => pair,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error reading XPC reply: {:?}", e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(Message::String(ver_raw)) = opt_msg {
|
||||||
|
// Trim the trailing NUL if present
|
||||||
|
let version = ver_raw.trim_end_matches('\0');
|
||||||
|
println!("Server version: {}", version);
|
||||||
|
} else {
|
||||||
|
eprintln!("Unexpected XPC reply for GetVersion");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remaining methods unimplemented on macOS
|
||||||
|
async fn print_conversations(&mut self) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn sync_conversations(&mut self, _conversation_id: Option<String>) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn sync_conversations_list(&mut self) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn print_messages(
|
||||||
|
&mut self,
|
||||||
|
_conversation_id: String,
|
||||||
|
_last_message_id: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn enqueue_outgoing_message(
|
||||||
|
&mut self,
|
||||||
|
_conversation_id: String,
|
||||||
|
_text: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn wait_for_signals(&mut self) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn config(&mut self, _cmd: ConfigCommands) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn delete_all_conversations(&mut self) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn download_attachment(&mut self, _attachment_id: String) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn upload_attachment(&mut self, _path: String) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
async fn mark_conversation_as_read(&mut self, _conversation_id: String) -> Result<()> {
|
||||||
|
Err(anyhow::anyhow!("Feature not implemented for XPC"))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user