Compare commits
32 Commits
09afdf51a4
...
main
Author | SHA1 | Date | |
---|---|---|---|
7707242d10 | |||
bd76603c54 | |||
9e764d72a1 | |||
b9faad742b | |||
370f5076a1 | |||
373311e826 | |||
67a8715a25 | |||
c773146b26 | |||
7b1b8bdc83 | |||
d06badfc96 | |||
8dbd023718 | |||
7cee6348fd | |||
14aa639a4b | |||
efe97a33c4 | |||
d77d04e9b1 | |||
db7ecc6d98 | |||
15174ea03f | |||
aae7607c7f | |||
825f5d90ed | |||
a95be7ee4b | |||
72947bc99d | |||
b511dd873b | |||
667eb3b3f6 | |||
cb463336bc | |||
1e481d4c9a | |||
e5e06c55f9 | |||
576fec63cd | |||
782662b82f | |||
1eefce4f1c | |||
b814a9aab0 | |||
49362a6606 | |||
7003b56ce6 |
898
Cargo.lock
generated
898
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,9 @@ license = "MIT"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
async-std = "1.6"
|
||||||
dbus = "0.8"
|
dbus = "0.8"
|
||||||
deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="1.31.0" }
|
deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="1.34.0" }
|
||||||
directories = "2.0"
|
directories = "2.0"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
|
|
||||||
|
148
README.md
148
README.md
@@ -33,10 +33,10 @@ Here's where we're at right now:
|
|||||||
- [x] Appear as online in Empathy
|
- [x] Appear as online in Empathy
|
||||||
- [x] Disconnect!
|
- [x] Disconnect!
|
||||||
- [ ] Set up an account manually
|
- [ ] Set up an account manually
|
||||||
- [ ] Contacts handling
|
- [~] Contacts handling
|
||||||
- [~] Text messages
|
- [x] Text messages
|
||||||
- [ ] Multimedia messages
|
- [ ] Multimedia messages
|
||||||
- [ ] Setup messages
|
- [~] Setup messages
|
||||||
- [ ] Import/Export
|
- [ ] Import/Export
|
||||||
- [ ] Group chats
|
- [ ] Group chats
|
||||||
- [ ] Geolocation messages
|
- [ ] Geolocation messages
|
||||||
@@ -52,9 +52,143 @@ learning exercise!
|
|||||||
|
|
||||||
## How
|
## How
|
||||||
|
|
||||||
|
### Compiling
|
||||||
|
|
||||||
This project is written in Rust, so you'll need a rust compiler to build it.
|
This project is written in Rust, so you'll need a rust compiler to build it.
|
||||||
[Rustup]() comes highly recommended. Deltachat is also written in Rust and it
|
[Rustup](https://github.com/rust-lang/rustup) comes highly recommended.
|
||||||
needs the `nightly` version, so follow the instructions for that.
|
|
||||||
|
There is a [`rust-toolchain`](rust-toolchain) file that I try to keep synced
|
||||||
|
with the version of rust that
|
||||||
|
[`deltachat-core-rust`](https://github.com/deltachat/deltachat-core-rust)
|
||||||
|
uses.
|
||||||
|
|
||||||
|
|
||||||
|
Once you have a working rust compiler, just:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
to get a `telepathy-padfoot binary. Drop the release flag to make it build fast.
|
||||||
|
|
||||||
|
|
||||||
|
### Cross-compiling amd64 -> i386
|
||||||
|
|
||||||
|
If you need a 32-bit binary and you're on an am64 bit system, this seems to
|
||||||
|
work, as long as you have 32-bit versions of `libdbus-1` and `libssl` installed.
|
||||||
|
|
||||||
|
On Debian, the full sequence looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dpkg --print-architecture
|
||||||
|
amd64
|
||||||
|
# dpkg --add-architecture i386
|
||||||
|
$ dpkg --print-foreign-architectures
|
||||||
|
i386
|
||||||
|
# apt update
|
||||||
|
# apt install libdbus-1-dev:i386 libssl-dev:i386
|
||||||
|
$ rustup target install i686-unknown-linux-gnu
|
||||||
|
$ PKG_CONFIG_ALLOW_CROSS=1 cargo build --target=i686-unknown-linux-gnu --release
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a 32-bit executable at `target/i686-unknown-linux-gnu/release/telepathy-padfoot`
|
||||||
|
|
||||||
|
I don't have a 32-bit machine to test this on, but happy to take fixes for it.
|
||||||
|
|
||||||
|
### Cross-compiling amd64 -> aarch64
|
||||||
|
|
||||||
|
This is a handy thing to do for linux phones, most of which use telepathy. Rust
|
||||||
|
is quite heavy to compile - it's a pain even on a pinebook pro, which is the
|
||||||
|
same architecture. Setup on a Debian machine is quite simple:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dpkg --print-architecture
|
||||||
|
amd64
|
||||||
|
# dpkg --add-architecture arm64
|
||||||
|
$ dpkg --print-foreign-architectures
|
||||||
|
arm64
|
||||||
|
# apt update
|
||||||
|
# apt install libdbus-1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu
|
||||||
|
$ rustup target install aarch64-unknown-linux-gnu
|
||||||
|
$ RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc" PKG_CONFIG_ALLOW_CROSS=1 cargo build --target=aarch64-unknown-linux-gnu --release
|
||||||
|
```
|
||||||
|
|
||||||
|
We have to specify the linker because of [this bug](https://github.com/rust-lang/cargo/issues/4133).
|
||||||
|
|
||||||
|
Note that this doesn't create a static binary, so you'll need to match versions
|
||||||
|
for the shared libraries that are on the phone. In theory we can create static
|
||||||
|
binaries with musl, but openssl makes it hard. If you get it working, tell me
|
||||||
|
how!
|
||||||
|
|
||||||
|
UBTouch uses an ancient version of OpenSSL: 1.0.2g. KDE Neon does much better
|
||||||
|
with 1.1.1, so is easier to compile against.
|
||||||
|
|
||||||
|
An alternative approach to using multiarch (as above) is to use `debootstrap`
|
||||||
|
(or a similar tool) to get a sysroot containing libraries of all the right
|
||||||
|
versions. E.g. You can then add `-C link-args=--sysroot=/path/to/sysroot` to
|
||||||
|
`RUSTFLAGS` to use those libraries. Ufff. I've not got this working yet either.
|
||||||
|
|
||||||
|
...I'm compiling it directly on the phone. Not ideal. Add swap.
|
||||||
|
|
||||||
|
Compiling directly on the phone, using KDE Neon, I can get Padfoot running at
|
||||||
|
the same time as [Spacebar](https://invent.kde.org/plasma-mobile/spacebar),
|
||||||
|
which is a Telepathy client. I can see that Padfoot is checked for protocols,
|
||||||
|
but I don't see a way to start a connection with it yet. Next step for this is
|
||||||
|
to get Spacebar built and running locally, for a better debugging experience.
|
||||||
|
|
||||||
|
postmarketOS is more difficult. It's an `aarch64...musl` target. Rustup doesn't
|
||||||
|
support this, and the `rustc` included in the repositories is stable, not
|
||||||
|
nightly, so compiling directly on the phone is very difficult. Cross-compile is
|
||||||
|
likely the way to go here, in the end, but I need to get one of the two tries
|
||||||
|
above working first. Spacebar is available, but Empathy is not.
|
||||||
|
|
||||||
|
Phosh uses Chatty, which is based on libpurple, so doesn't work with Padfoot.
|
||||||
|
|
||||||
|
In the end, I tried Mobian. This is regular ordinary Debian Bullseye, plus a few
|
||||||
|
Phosh packages. Installing Empathy and Padfoot together (Chatty is bundled but
|
||||||
|
doesn't work), I have a working setup \o/ - although there are many warts, I can
|
||||||
|
use Deltachat on Linux Mobile in at least one configuration.
|
||||||
|
|
||||||
|
I'll probably keep Mobian for a while though, it's exactly what I want in a
|
||||||
|
mobile phone. Yes, I am peculiar.
|
||||||
|
|
||||||
|
### Installing
|
||||||
|
|
||||||
|
There is a `share/` directory in this project that contains a bunch of files.
|
||||||
|
They should be placed into `/usr/share`, following the same layout. Then put
|
||||||
|
the binary into `/usr/lib/telepathy/telepathy-padfoot`.
|
||||||
|
|
||||||
|
I should probably put this into the makefile.
|
||||||
|
|
||||||
|
### Running
|
||||||
|
|
||||||
|
D-Bus activation is not enabled yet, since it gets in the way of disaster-driven
|
||||||
|
development. Just run the `telepathy-padfoot` binary as the same user that your
|
||||||
|
chat client will be running as. It registers to the DBUS **session bus**, and
|
||||||
|
will be picked up next time your chat client scans (which may need a restart).
|
||||||
|
|
||||||
|
### Setup messages
|
||||||
|
|
||||||
|
If you send an autocrypt setup message while a padfoot connection is up, it will
|
||||||
|
notice it and open a channel asking you to reply with a message like:
|
||||||
|
|
||||||
|
```
|
||||||
|
IMEX: <id> nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn
|
||||||
|
```
|
||||||
|
|
||||||
|
The ID is the delta-generated message ID, while the rest is the setup code. No
|
||||||
|
whitespace!
|
||||||
|
|
||||||
|
This bit is still extremely janky; it should instead be a magic channel type or
|
||||||
|
action button of some kind. It is, however, functional.
|
||||||
|
|
||||||
|
Delta wants us to enable the "Send copy to self" option in settings. That's
|
||||||
|
exposed as "Bcc self" in the advanced options in Empathy. Once enabled, messages
|
||||||
|
you send via Padfoot will appear in other clients. However, messages you send
|
||||||
|
from other clients don't appear in Padfoot yet because it mostly ignores self
|
||||||
|
messages as a dirty hack to avoid double-showing messages. Progess though.
|
||||||
|
|
||||||
|
### Autogenerated telepathy DBUS bindings
|
||||||
|
|
||||||
It makes use of the `dbus-codegen-rust` crate to convert the
|
It makes use of the `dbus-codegen-rust` crate to convert the
|
||||||
[telepathy interface specs](https://github.com/TelepathyIM/telepathy-spec) into
|
[telepathy interface specs](https://github.com/TelepathyIM/telepathy-spec) into
|
||||||
@@ -64,10 +198,10 @@ regenerated like so:
|
|||||||
```bash
|
```bash
|
||||||
$ git submodule init telepathy-spec
|
$ git submodule init telepathy-spec
|
||||||
$ git submodule update telepathy-spec
|
$ git submodule update telepathy-spec
|
||||||
$ cargo install dbus-codegen-rust
|
$ cargo install dbus-codegen
|
||||||
$ ./scripts/dbus-codegen
|
$ ./scripts/dbus-codegen
|
||||||
```
|
```
|
||||||
|
|
||||||
`dbus-codegen-rust` doesn't seem to handle namespaced attributes properly, so
|
`dbus-codegen-rust` doesn't seem to handle namespaced attributes properly, so
|
||||||
we modify the XML files in `telepathy-spec`... with `sed`. The `tp:type`
|
we modify the XML files in `telepathy-spec`... with `sed`. The `tp:type`
|
||||||
attribute is renamed to `tp:typehint`.
|
attribute is renamed to `tp:typehint`. This will be fixed in the next release.
|
||||||
|
70
about.hbs
Normal file
70
about.hbs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background: #333;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: skyblue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
font-family: sans-serif;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.intro {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.licenses-list {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.license-used-by {
|
||||||
|
margin-top: -10px;
|
||||||
|
}
|
||||||
|
.license-text {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<main class="container">
|
||||||
|
<div class="intro">
|
||||||
|
<h1>Third Party Licenses</h1>
|
||||||
|
<p>This page lists the licenses of the projects used in telepathy-padfoot.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Overview of licenses:</h2>
|
||||||
|
<ul class="licenses-overview">
|
||||||
|
{{#each overview}}
|
||||||
|
<li><a href="#{{id}}">{{name}}</a> ({{count}})</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>All license text:</h2>
|
||||||
|
<ul class="licenses-list">
|
||||||
|
{{#each licenses}}
|
||||||
|
<li class="license">
|
||||||
|
<h3 id="{{id}}">{{name}}</h3>
|
||||||
|
<h4>Used by:</h4>
|
||||||
|
<ul class="license-used-by">
|
||||||
|
{{#each used_by}}
|
||||||
|
<li><a href="{{#if crate.repository}} {{crate.repository}} {{else}} https://crates.io/crates/{{crate.name}} {{/if}}">{{crate.name}} {{crate.version}}</a></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
<pre class="license-text">{{text}}</pre>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
11
about.toml
Normal file
11
about.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
accepted = [
|
||||||
|
"0BSD",
|
||||||
|
"Apache-2.0",
|
||||||
|
"BSD-2-Clause",
|
||||||
|
"BSD-3-Clause",
|
||||||
|
"ISC",
|
||||||
|
"CC0-1.0",
|
||||||
|
"MIT",
|
||||||
|
"MPL-2.0",
|
||||||
|
"Zlib"
|
||||||
|
]
|
9555
license.html
Normal file
9555
license.html
Normal file
File diff suppressed because it is too large
Load Diff
1
rust-toolchain
Normal file
1
rust-toolchain
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nightly-2020-03-12
|
@@ -12,7 +12,7 @@ rm -f "$dest.rs"
|
|||||||
rm -rf "$dest"
|
rm -rf "$dest"
|
||||||
mkdir -p "$dest"
|
mkdir -p "$dest"
|
||||||
|
|
||||||
echo "#![allow(unused)]\n#![allow(clippy::all)]" > "$modfile"
|
echo "#![allow(unused)]" > "$modfile"
|
||||||
|
|
||||||
for file in $(ls -a $specs/*.xml); do
|
for file in $(ls -a $specs/*.xml); do
|
||||||
sed -i 's/tp:type=/tp:typehint=/g' "$file"
|
sed -i 's/tp:type=/tp:typehint=/g' "$file"
|
||||||
@@ -31,9 +31,9 @@ for file in $(ls -a $specs/*.xml); do
|
|||||||
-a AsRefClosure \
|
-a AsRefClosure \
|
||||||
-o "$out"
|
-o "$out"
|
||||||
|
|
||||||
rustfmt $out
|
rustfmt "$out"
|
||||||
|
|
||||||
echo "\nmod $name;\npub use self::$name::*;" >> "$modfile"
|
echo "\n#[allow(clippy::all)]\nmod $name;\npub use self::$name::*;" >> "$modfile"
|
||||||
done
|
done
|
||||||
|
|
||||||
git -C telepathy-spec checkout -- .
|
git -C telepathy-spec checkout -- .
|
||||||
|
@@ -5,21 +5,22 @@ BusName=org.freedesktop.Telepathy.ConnectionManager.padfoot
|
|||||||
ObjectPath=/org/freedesktop/Telepathy/ConnectionManager/padfoot
|
ObjectPath=/org/freedesktop/Telepathy/ConnectionManager/padfoot
|
||||||
|
|
||||||
[Protocol delta]
|
[Protocol delta]
|
||||||
Interfaces=org.freedesktop.Telepathy.Protocol.Interface.Presence;
|
|
||||||
param-account=s required
|
param-account=s required
|
||||||
param-password=s required secret
|
param-password=s required secret
|
||||||
|
param-bcc-self=b
|
||||||
status-available=2 settable
|
status-available=2 settable
|
||||||
status-offline = 1 settable
|
status-offline = 1 settable
|
||||||
|
|
||||||
AuthenticationTypes=org.freedesktop.Telepathy.Channel.Type.ServerTLSConnection;
|
AuthenticationTypes=org.freedesktop.Telepathy.Channel.Type.ServerTLSConnection;
|
||||||
ConnectionInterfaces=org.freedesktop.Telepathy.Connection.Interface.Avatars;org.freedesktop.Telepathy.Connection.Interface.Contacts;org.freedesktop.Telepathy.Connection.Interface.Requests;org.freedesktop.Telepathy.Connection.Interface.SimplePresence;
|
ConnectionInterfaces=org.freedesktop.Telepathy.Connection.Interface.Avatars;org.freedesktop.Telepathy.Connection.Interface.Contacts;org.freedesktop.Telepathy.Connection.Interface.ContactList;org.freedesktop.Telepathy.Connection.Interface.Requests;org.freedesktop.Telepathy.Connection.Interface.SimplePresence;
|
||||||
EnglishName=Delta Chat
|
EnglishName=Delta Chat
|
||||||
Icon=im-delta
|
Icon=im-delta
|
||||||
Interfaces=
|
Interfaces=org.freedesktop.Telepathy.Protocol;org.freedesktop.Telepathy.Protocol.Interface.Presence;
|
||||||
RequestableChannelClasses=text;
|
RequestableChannelClasses=text;
|
||||||
VCardField=email
|
VCardField=email
|
||||||
|
|
||||||
[text]
|
[text]
|
||||||
|
Interfaces=org.freedesktop.Telepathy.Channel.Interface.Messages;
|
||||||
org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Text
|
org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Text
|
||||||
org.freedesktop.Telepathy.Channel.TargetHandleType u=1
|
org.freedesktop.Telepathy.Channel.TargetHandleType u=1
|
||||||
allowed=org.freedesktop.Telepathy.Channel.TargetHandle;org.freedesktop.Telepathy.Channel.TargetID;
|
allowed=org.freedesktop.Telepathy.Channel.TargetHandle;org.freedesktop.Telepathy.Channel.TargetID;org.freedesktop.Telepathy.Channel.Interface.Messages;
|
||||||
|
@@ -7,6 +7,9 @@ pub use self::connection::*;
|
|||||||
mod connection_manager;
|
mod connection_manager;
|
||||||
pub use self::connection_manager::*;
|
pub use self::connection_manager::*;
|
||||||
|
|
||||||
|
mod message;
|
||||||
|
pub use self::message::*;
|
||||||
|
|
||||||
mod protocol;
|
mod protocol;
|
||||||
pub use self::protocol::*;
|
pub use self::protocol::*;
|
||||||
|
|
||||||
|
@@ -8,47 +8,190 @@ pub use messages::*;
|
|||||||
mod type_text;
|
mod type_text;
|
||||||
pub use type_text::*;
|
pub use type_text::*;
|
||||||
|
|
||||||
|
use crate::padfoot::{var_bool, var_str, var_str_vec, var_u32, DbusAction, VarArg};
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
|
||||||
|
use async_std::task::block_on;
|
||||||
|
use deltachat as dc;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{mpsc, Arc};
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, dbus::tree::MethodErr>;
|
||||||
|
|
||||||
|
pub type HandleType = u32;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const HANDLE_TYPE_NONE: HandleType = 0;
|
||||||
|
pub const HANDLE_TYPE_CONTACT: HandleType = 1;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const HANDLE_TYPE_ROOM: HandleType = 2;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const HANDLE_TYPE_LIST: HandleType = 3; // Deprecated
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const HANDLE_TYPE_GROUP: HandleType = 4; // Deprecated
|
||||||
|
|
||||||
// FIXME: I'm assuming that all channels will be of type text and 1-1 for now.
|
// FIXME: I'm assuming that all channels will be of type text and 1-1 for now.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
|
actq: mpsc::Sender<DbusAction>,
|
||||||
|
chat_id: dc::chat::ChatId,
|
||||||
|
ctx: Arc<dc::context::Context>,
|
||||||
|
initiator_handle: u32,
|
||||||
|
path: dbus::Path<'static>,
|
||||||
|
requested: bool,
|
||||||
|
target_handle: u32, // Who we're talking to
|
||||||
}
|
}
|
||||||
|
|
||||||
// "This SHOULD NOT include the channel type and channel interface itself"
|
// "This SHOULD NOT include the channel type and channel interface itself"
|
||||||
pub fn channel_interfaces() -> Vec<String> {
|
pub fn channel_interfaces() -> Vec<String> {
|
||||||
vec![
|
vec!["org.freedesktop.Telepathy.Channel.Interface.Messages".to_string()]
|
||||||
"org.freedesktop.Telepathy.Channel.Interface.Messages".to_string(),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, dbus::tree::MethodErr>;
|
|
||||||
|
|
||||||
impl Channel {
|
impl Channel {
|
||||||
fn build_tree(self) -> dbus::tree::Tree<dbus::tree::MTFn, ()> {
|
pub fn new(
|
||||||
let c_rc = std::rc::Rc::new(self);
|
actq: mpsc::Sender<DbusAction>,
|
||||||
let f = dbus::tree::Factory::new_fn::<()>();
|
chat_id: dc::chat::ChatId,
|
||||||
let mut tree = f.tree(());
|
ctx: Arc<dc::context::Context>,
|
||||||
|
initiator_handle: u32,
|
||||||
|
path: dbus::Path<'static>,
|
||||||
|
requested: bool,
|
||||||
|
target_handle: u32,
|
||||||
|
) -> Self {
|
||||||
|
Channel {
|
||||||
|
actq,
|
||||||
|
chat_id,
|
||||||
|
ctx,
|
||||||
|
initiator_handle,
|
||||||
|
path,
|
||||||
|
requested,
|
||||||
|
target_handle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let c_rc1 = c_rc.clone();
|
// FIXME: we should be able to introspect this already???
|
||||||
|
pub fn chan_props(&self) -> HashMap<String, VarArg> {
|
||||||
|
let mut out = HashMap::<String, VarArg>::new();
|
||||||
|
out.insert(
|
||||||
|
"org.freedesktop.Telepathy.Channel.ChannelType".to_string(),
|
||||||
|
var_str(self.chan_type()),
|
||||||
|
);
|
||||||
|
out.insert(
|
||||||
|
"org.freedesktop.Telepathy.Channel.TargetHandleType".to_string(),
|
||||||
|
var_u32(self.handle_type()),
|
||||||
|
);
|
||||||
|
out.insert(
|
||||||
|
"org.freedesktop.Telepathy.Channel.InitiatorHandle".to_string(),
|
||||||
|
var_u32(self.initiator_handle),
|
||||||
|
);
|
||||||
|
out.insert(
|
||||||
|
"org.freedesktop.Telepathy.Channel.InitiatorID".to_string(),
|
||||||
|
var_str(self.initiator_contact().unwrap().get_addr().to_string()),
|
||||||
|
);
|
||||||
|
out.insert(
|
||||||
|
"org.freedesktop.Telepathy.Channel.TargetHandle".to_string(),
|
||||||
|
var_u32(self.target_handle),
|
||||||
|
);
|
||||||
|
out.insert(
|
||||||
|
"org.freedesktop.Telepathy.Channel.TargetID".to_string(),
|
||||||
|
var_str(self.target_contact().unwrap().get_addr().to_string()),
|
||||||
|
);
|
||||||
|
out.insert(
|
||||||
|
"org.freedesktop.Telepathy.Channel.Requested".to_string(),
|
||||||
|
var_bool(self.requested),
|
||||||
|
);
|
||||||
|
out.insert(
|
||||||
|
"org.freedesktop.Telepathy.Channel.Interfaces".to_string(),
|
||||||
|
var_str_vec(vec![
|
||||||
|
"org.freedesktop.Telepathy.Channel.Interface.Messages".to_string()
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> dbus::Path<'static> {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chan_type(&self) -> String {
|
||||||
|
"org.freedesktop.Telepathy.Channel.Type.Text".to_string() // FIXME: this shouldn't be hardcoded
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_type(&self) -> HandleType {
|
||||||
|
HANDLE_TYPE_CONTACT // FIXME: this shouldn't be hardcoded
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(&self) -> u32 {
|
||||||
|
self.target_handle
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target_contact(&self) -> Option<dc::contact::Contact> {
|
||||||
|
block_on(dc::contact::Contact::get_by_id(&self.ctx, self.handle())).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initiator_contact(&self) -> Option<dc::contact::Contact> {
|
||||||
|
block_on(dc::contact::Contact::get_by_id(
|
||||||
|
&self.ctx,
|
||||||
|
self.initiator_handle,
|
||||||
|
))
|
||||||
|
.ok() // FIXME: this will be wrong for outbound channels
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn requested(&self) -> bool {
|
||||||
|
self.requested
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_object_path(
|
||||||
|
channel: Arc<Channel>,
|
||||||
|
) -> dbus::tree::ObjectPath<dbus::tree::MTFn, ()> {
|
||||||
|
let f = dbus::tree::Factory::new_fn::<()>();
|
||||||
|
|
||||||
|
let c_rc1 = channel.clone();
|
||||||
let chan_iface = telepathy::channel_server(&f, (), move |_| c_rc1.clone());
|
let chan_iface = telepathy::channel_server(&f, (), move |_| c_rc1.clone());
|
||||||
|
|
||||||
let c_rc2 = c_rc.clone();
|
let c_rc2 = channel.clone();
|
||||||
let messages_iface =
|
let messages_iface =
|
||||||
telepathy::channel_interface_messages_server(&f, (), move |_| c_rc2.clone());
|
telepathy::channel_interface_messages_server(&f, (), move |_| c_rc2.clone());
|
||||||
|
|
||||||
let type_text_iface =
|
let c_rc3 = channel.clone();
|
||||||
telepathy::channel_type_text_server(&f, (), move |_| c_rc.clone());
|
let type_text_iface = telepathy::channel_type_text_server(&f, (), move |_| c_rc3.clone());
|
||||||
|
|
||||||
tree = tree.add(
|
f.object_path(channel.path.clone(), ())
|
||||||
f.object_path("", ())
|
.introspectable()
|
||||||
.introspectable()
|
.add(chan_iface)
|
||||||
.add(chan_iface)
|
.add(messages_iface)
|
||||||
.add(messages_iface)
|
.add(type_text_iface)
|
||||||
.add(type_text_iface),
|
}
|
||||||
);
|
|
||||||
tree = tree.add(f.object_path("/", ()).introspectable());
|
|
||||||
|
|
||||||
tree
|
fn try_process_setupmsg(self: &Self, text: String) {
|
||||||
|
if !text.starts_with("IMEX: ") {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Expected form: "IMEX: <msg-id> <setupcode>
|
||||||
|
let mut iter = text.split_whitespace();
|
||||||
|
iter.next(); // Ignore the prefix
|
||||||
|
|
||||||
|
let msg_id = match iter.next() {
|
||||||
|
Some(txt) => match txt.parse::<u32>() {
|
||||||
|
Ok(id) => id,
|
||||||
|
_ => return,
|
||||||
|
},
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let setup_code = match iter.next() {
|
||||||
|
Some(txt) => txt,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = block_on(dc::imex::continue_key_transfer(
|
||||||
|
&self.ctx,
|
||||||
|
dc::message::MsgId::new(msg_id),
|
||||||
|
&setup_code,
|
||||||
|
)) {
|
||||||
|
println!("Failed to apply setup code {}: {}", msg_id, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
|
use crate::padfoot::DbusAction;
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
|
|
||||||
use super::{Channel, Result};
|
use super::{Channel, Result};
|
||||||
|
|
||||||
impl AsRef<dyn telepathy::Channel + 'static> for std::rc::Rc<Channel> {
|
impl AsRef<dyn telepathy::Channel + 'static> for std::sync::Arc<Channel> {
|
||||||
fn as_ref(&self) -> &(dyn telepathy::Channel + 'static) {
|
fn as_ref(&self) -> &(dyn telepathy::Channel + 'static) {
|
||||||
&**self
|
&**self
|
||||||
}
|
}
|
||||||
@@ -13,27 +14,37 @@ impl AsRef<dyn telepathy::Channel + 'static> for std::rc::Rc<Channel> {
|
|||||||
impl telepathy::Channel for Channel {
|
impl telepathy::Channel for Channel {
|
||||||
fn close(&self) -> Result<()> {
|
fn close(&self) -> Result<()> {
|
||||||
println!("Channel::close()");
|
println!("Channel::close()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
self.actq
|
||||||
|
.send(DbusAction::CloseChannel(self.path()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
fn get_channel_type(&self) -> Result<String> {
|
fn get_channel_type(&self) -> Result<String> {
|
||||||
self.channel_type()
|
self.channel_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_handle(&self) -> Result<(u32, u32)> {
|
|
||||||
println!("Channel::get_handle()");
|
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_interfaces(&self) -> Result<Vec<String>> {
|
|
||||||
println!("Channel::get_interfaces()");
|
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn channel_type(&self) -> Result<String> {
|
fn channel_type(&self) -> Result<String> {
|
||||||
println!("Channel::channel_type()");
|
println!("Channel::channel_type()");
|
||||||
|
|
||||||
Ok("org.freedesktop.Telepathy.Channel.Text".to_string())
|
Ok(self.chan_type())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
|
fn get_handle(&self) -> Result<(u32, u32)> {
|
||||||
|
println!("Channel::get_handle()");
|
||||||
|
|
||||||
|
Ok((self.handle_type(), self.handle()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
|
fn get_interfaces(&self) -> Result<Vec<String>> {
|
||||||
|
println!("Channel::get_interfaces()");
|
||||||
|
|
||||||
|
self.interfaces()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interfaces(&self) -> Result<Vec<String>> {
|
fn interfaces(&self) -> Result<Vec<String>> {
|
||||||
@@ -43,31 +54,45 @@ impl telepathy::Channel for Channel {
|
|||||||
|
|
||||||
fn target_handle(&self) -> Result<u32> {
|
fn target_handle(&self) -> Result<u32> {
|
||||||
println!("Channel::target_handle()");
|
println!("Channel::target_handle()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
Ok(self.handle())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_id(&self) -> Result<String> {
|
fn target_id(&self) -> Result<String> {
|
||||||
println!("Channel::target_id()");
|
println!("Channel::target_id()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
if let Some(contact) = self.target_contact() {
|
||||||
|
Ok(contact.get_addr().to_string())
|
||||||
|
} else {
|
||||||
|
Err(MethodErr::no_arg())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_handle_type(&self) -> Result<u32> {
|
fn target_handle_type(&self) -> Result<u32> {
|
||||||
println!("Channel::target_handle_type()");
|
println!("Channel::target_handle_type()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
Ok(self.handle_type())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn requested(&self) -> Result<bool> {
|
fn requested(&self) -> Result<bool> {
|
||||||
println!("Channel::requested()");
|
println!("Channel::requested()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
Ok(self.requested) // FIXME: channels initiated by ourselves *will* be requested
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initiator_handle(&self) -> Result<u32> {
|
fn initiator_handle(&self) -> Result<u32> {
|
||||||
println!("Channel::initiator_handle()");
|
println!("Channel::initiator_handle()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
self.target_handle() // FIXME: Not the case for channels initiated by ourselves
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initiator_id(&self) -> Result<String> {
|
fn initiator_id(&self) -> Result<String> {
|
||||||
println!("Channel::initiator_id()");
|
println!("Channel::initiator_id()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
if let Some(contact) = self.initiator_contact() {
|
||||||
|
Ok(contact.get_addr().to_string())
|
||||||
|
} else {
|
||||||
|
Err(MethodErr::no_arg())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,52 +1,139 @@
|
|||||||
use crate::padfoot::VarArg;
|
use crate::padfoot::{convert_msg, DbusAction, VarArg};
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
|
||||||
|
use async_std::task::block_on;
|
||||||
|
use dbus::message::SignalArgs;
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
|
use dc::constants::Viewtype;
|
||||||
|
use dc::message::{Message, MessageState};
|
||||||
|
use deltachat as dc;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::{Channel, Result};
|
use super::{Channel, Result};
|
||||||
|
|
||||||
impl AsRef<dyn telepathy::ChannelInterfaceMessages + 'static> for std::rc::Rc<Channel> {
|
impl AsRef<dyn telepathy::ChannelInterfaceMessages + 'static> for std::sync::Arc<Channel> {
|
||||||
fn as_ref(&self) -> &(dyn telepathy::ChannelInterfaceMessages + 'static) {
|
fn as_ref(&self) -> &(dyn telepathy::ChannelInterfaceMessages + 'static) {
|
||||||
&**self
|
&**self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl telepathy::ChannelInterfaceMessages for Channel {
|
impl telepathy::ChannelInterfaceMessages for Channel {
|
||||||
fn send_message(&self, message: Vec<HashMap<&str, VarArg>>, flags: u32) -> Result<String> {
|
fn send_message(&self, parts: Vec<HashMap<&str, VarArg>>, flags: u32) -> Result<String> {
|
||||||
println!("Channel::send_message({:?}, {})", message, flags);
|
println!("Channel::send_message({:?}, {})", parts, flags);
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
if parts.len() != 2 {
|
||||||
|
return Err(MethodErr::no_arg());
|
||||||
|
}
|
||||||
|
let _meta = &parts[0];
|
||||||
|
let content = &parts[1];
|
||||||
|
|
||||||
|
let content_type = content["content-type"].0.as_str().unwrap();
|
||||||
|
|
||||||
|
if content_type != "text/plain" {
|
||||||
|
println!("FIXME: can only send text/plain messages right now");
|
||||||
|
return Err(MethodErr::no_arg());
|
||||||
|
}
|
||||||
|
|
||||||
|
let text_opt = content["content"].0.as_str().map(|s| s.to_string());
|
||||||
|
|
||||||
|
let mut delta_msg = Message::new(Viewtype::Text); // FIXME: this won't always be plaintext
|
||||||
|
delta_msg.set_text(text_opt.clone());
|
||||||
|
|
||||||
|
if let Some(text) = text_opt.clone() {
|
||||||
|
self.try_process_setupmsg(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctx = &self.ctx;
|
||||||
|
let blobdir = ctx.get_blobdir();
|
||||||
|
|
||||||
|
let msg_id = match block_on(dc::chat::send_msg(&ctx, self.chat_id, &mut delta_msg)) {
|
||||||
|
Ok(msg_id) => msg_id,
|
||||||
|
Err(e) => {
|
||||||
|
println!(" Failed to send message: {}", e);
|
||||||
|
return Err(MethodErr::no_arg());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let token = format!("{}", msg_id.to_u32());
|
||||||
|
let dbus_parts = convert_msg(blobdir, &delta_msg).map_err(|_| MethodErr::no_arg())?;
|
||||||
|
|
||||||
|
let messages_sig = telepathy::ChannelInterfaceMessagesMessageSent {
|
||||||
|
content: dbus_parts,
|
||||||
|
flags: 0,
|
||||||
|
message_token: token.clone(),
|
||||||
|
}
|
||||||
|
.to_emit_message(&self.path());
|
||||||
|
|
||||||
|
let text_sig = telepathy::ChannelTypeTextSent {
|
||||||
|
timestamp: delta_msg.get_timestamp() as u32,
|
||||||
|
type_: 0,
|
||||||
|
text: text_opt.or_else(|| Some("".to_string())).unwrap(),
|
||||||
|
}
|
||||||
|
.to_emit_message(&self.path());
|
||||||
|
|
||||||
|
self.actq.send(DbusAction::Signal(messages_sig)).unwrap();
|
||||||
|
self.actq.send(DbusAction::Signal(text_sig)).unwrap();
|
||||||
|
|
||||||
|
Ok(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pending_message_content(&self, message_id: u32, parts: Vec<u32>) -> Result<HashMap<u32, VarArg>> {
|
fn get_pending_message_content(
|
||||||
println!("Channel::get_pending_message_content({}, {:?})", message_id, parts);
|
&self,
|
||||||
Err(MethodErr::no_arg())
|
message_id: u32,
|
||||||
|
parts: Vec<u32>,
|
||||||
|
) -> Result<HashMap<u32, VarArg>> {
|
||||||
|
println!(
|
||||||
|
"Channel::get_pending_message_content({}, {:?})",
|
||||||
|
message_id, parts
|
||||||
|
);
|
||||||
|
Err(MethodErr::no_arg())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_content_types(&self) -> Result<Vec<String>> {
|
fn supported_content_types(&self) -> Result<Vec<String>> {
|
||||||
println!("Channel::supported_content_types()");
|
println!("Channel::supported_content_types()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
Ok(vec!["*/*".to_string()])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_types(&self) -> Result<Vec<u32>> {
|
fn message_types(&self) -> Result<Vec<u32>> {
|
||||||
println!("Channel::message_types()");
|
println!("Channel::message_types()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
|
||||||
|
Ok(vec![0]) // Normal messages. FIXME: MDNs too
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_part_support_flags(&self) -> Result<u32> {
|
fn message_part_support_flags(&self) -> Result<u32> {
|
||||||
println!("Channel::message_part_support_flags()");
|
println!("Channel::message_part_support_flags()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
Ok(0) // FIXME: support multipart messages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return value is an array of array of message parts
|
||||||
fn pending_messages(&self) -> Result<Vec<Vec<HashMap<String, VarArg>>>> {
|
fn pending_messages(&self) -> Result<Vec<Vec<HashMap<String, VarArg>>>> {
|
||||||
println!("Channel::pending_messages()");
|
println!("Channel::pending_messages()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
|
||||||
|
let mut out = Vec::<Vec<HashMap<String, VarArg>>>::new();
|
||||||
|
let ctx = &self.ctx;
|
||||||
|
let blobdir = ctx.get_blobdir();
|
||||||
|
|
||||||
|
for msg_id in block_on(dc::chat::get_chat_msgs(ctx, self.chat_id, 0, None)) {
|
||||||
|
if let Ok(msg) = block_on(dc::message::Message::load_from_db(ctx, msg_id)) {
|
||||||
|
match msg.get_state() {
|
||||||
|
MessageState::InFresh | MessageState::InNoticed => {
|
||||||
|
println!(" A message: {:?}", msg);
|
||||||
|
let parts = convert_msg(blobdir, &msg).map_err(|_| MethodErr::no_arg())?;
|
||||||
|
out.push(parts);
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out) // FIXME: check for pending messages
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delivery_reporting_support(&self) -> Result<u32> {
|
fn delivery_reporting_support(&self) -> Result<u32> {
|
||||||
println!("Channel::delivery_reporting_support()");
|
println!("Channel::delivery_reporting_support()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
Ok(0) // FIXME: MDNs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,38 +1,69 @@
|
|||||||
|
use crate::padfoot::DbusAction;
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
use crate::telepathy::ChannelInterfaceMessages;
|
||||||
|
|
||||||
|
use async_std::task::block_on;
|
||||||
|
use dbus::message::SignalArgs;
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
|
use dc::message::MsgId;
|
||||||
|
use deltachat as dc;
|
||||||
|
|
||||||
use super::{Channel, Result};
|
use super::{Channel, Result};
|
||||||
|
|
||||||
impl AsRef<dyn telepathy::ChannelTypeText + 'static> for std::rc::Rc<Channel> {
|
impl AsRef<dyn telepathy::ChannelTypeText + 'static> for std::sync::Arc<Channel> {
|
||||||
fn as_ref(&self) -> &(dyn telepathy::ChannelTypeText + 'static) {
|
fn as_ref(&self) -> &(dyn telepathy::ChannelTypeText + 'static) {
|
||||||
&**self
|
&**self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PendingMessagesSpec = (
|
type PendingMessagesSpec = (
|
||||||
u32, // numeric identifier
|
u32, // numeric identifier
|
||||||
u32, // Unix timestamp indicating when the message was received
|
u32, // Unix timestamp indicating when the message was received
|
||||||
u32, // contact handle for the contact who sent the message
|
u32, // contact handle for the contact who sent the message
|
||||||
u32, // message type, taken from ChannelTextMessageType
|
u32, // message type, taken from ChannelTextMessageType
|
||||||
u32, // bitwise-OR of the message flags from ChannelTextMessageFlags
|
u32, // bitwise-OR of the message flags from ChannelTextMessageFlags
|
||||||
String, // text of the message
|
String, // text of the message
|
||||||
);
|
);
|
||||||
|
|
||||||
// Most of these methods are deprecated, so should be implemented in terms of
|
// Most of these methods are deprecated, so should be implemented in terms of
|
||||||
// the mandatory Messages interface.
|
// the mandatory Messages interface.
|
||||||
impl telepathy::ChannelTypeText for Channel {
|
impl telepathy::ChannelTypeText for Channel {
|
||||||
|
// ids is a list of deltachat msg_ids
|
||||||
fn acknowledge_pending_messages(&self, ids: Vec<u32>) -> Result<()> {
|
fn acknowledge_pending_messages(&self, ids: Vec<u32>) -> Result<()> {
|
||||||
println!("Channel::acknowledge_pending_messages({:?})", ids);
|
println!("Channel::acknowledge_pending_messages({:?})", ids);
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
let mut msg_ids = Vec::<MsgId>::new();
|
||||||
|
for msg_id in &ids {
|
||||||
|
msg_ids.push(MsgId::new(*msg_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
print!(" Marking messages as seen...");
|
||||||
|
let result = block_on(dc::message::markseen_msgs(&self.ctx, msg_ids));
|
||||||
|
if result {
|
||||||
|
println!("OK!");
|
||||||
|
|
||||||
|
// Emit a PendingMessagesRemoved signal only if all have been removed
|
||||||
|
let sig =
|
||||||
|
telepathy::ChannelInterfaceMessagesPendingMessagesRemoved { message_ids: ids }
|
||||||
|
.to_emit_message(&self.path());
|
||||||
|
|
||||||
|
self.actq.send(DbusAction::Signal(sig)).unwrap();
|
||||||
|
} else {
|
||||||
|
println!("FAILED!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_message_types(&self) -> Result<Vec<u32>> {
|
fn get_message_types(&self) -> Result<Vec<u32>> {
|
||||||
println!("Channel::get_message_types()");
|
println!("Channel::get_message_types()");
|
||||||
Err(MethodErr::no_arg())
|
|
||||||
|
self.message_types()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_pending_messages(&self, clear: bool) -> Result<Vec<PendingMessagesSpec>> {
|
fn list_pending_messages(&self, clear: bool) -> Result<Vec<PendingMessagesSpec>> {
|
||||||
println!("Channel::list_pending_messages({})", clear);
|
println!("Channel::list_pending_messages({})", clear);
|
||||||
|
|
||||||
Err(MethodErr::no_arg())
|
Err(MethodErr::no_arg())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,11 +20,13 @@ pub use self::requests::*;
|
|||||||
mod simple_presence;
|
mod simple_presence;
|
||||||
pub use self::simple_presence::*;
|
pub use self::simple_presence::*;
|
||||||
|
|
||||||
use crate::padfoot::VarArg;
|
use crate::padfoot::{convert_msg, Channel, VarArg};
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
|
||||||
|
use async_std::task::block_on;
|
||||||
use dbus::blocking::{stdintf::org_freedesktop_dbus::RequestNameReply, LocalConnection};
|
use dbus::blocking::{stdintf::org_freedesktop_dbus::RequestNameReply, LocalConnection};
|
||||||
use dbus::channel::Sender;
|
use dbus::channel::{MatchingReceiver, Sender};
|
||||||
|
use dbus::message::SignalArgs;
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
|
|
||||||
use dc::config::Config;
|
use dc::config::Config;
|
||||||
@@ -32,31 +34,50 @@ use dc::context::Context;
|
|||||||
use dc::Event;
|
use dc::Event;
|
||||||
use deltachat as dc;
|
use deltachat as dc;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::{mpsc, Arc, Mutex, RwLock};
|
use std::sync::{mpsc, Arc, Mutex, RwLock};
|
||||||
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub const CONN_BUS_NAME: &str = "org.freedesktop.Telepathy.Connection.padfoot.delta";
|
pub const CONN_BUS_NAME: &str = "org.freedesktop.Telepathy.Connection.padfoot.delta";
|
||||||
pub const CONN_OBJECT_PATH: &str = "/org/freedesktop/Telepathy/Connection/padfoot/delta";
|
pub const CONN_OBJECT_PATH: &str = "/org/freedesktop/Telepathy/Connection/padfoot/delta";
|
||||||
|
|
||||||
|
// Only the main loop has access to the DBUS connection. Interacting with DBUS
|
||||||
|
// outside of method return values requires one of these to be added to actq
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
// A Deltachast connection uses email addresses as handles, and delta's Db IDs
|
pub enum DbusAction {
|
||||||
pub struct Connection {
|
Signal(dbus::Message), // Generic signal to send
|
||||||
// Remove ourselves from this when done
|
|
||||||
conns: Arc<Mutex<HashSet<String>>>,
|
|
||||||
|
|
||||||
ctx: Arc<RwLock<Context>>,
|
NewChannel(Channel), // Add this channel
|
||||||
|
CloseChannel(dbus::Path<'static>), // Close this channel
|
||||||
|
|
||||||
|
IncomingMessage(dc::chat::ChatId, dc::message::MsgId), // Look at this \o/
|
||||||
|
FreshMessages, // Hint that some messages need looking at
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
// A connection uses delta database IDs as handles, and email addresses as IDs
|
||||||
|
pub struct Connection {
|
||||||
|
// Used for sending out messages
|
||||||
|
actq: mpsc::Sender<DbusAction>,
|
||||||
|
// actq: Arc<Mutex<VecDeque<DbusAction>>>,
|
||||||
|
|
||||||
|
// Channels we own
|
||||||
|
channels: Arc<RwLock<HashMap<dbus::Path<'static>, Arc<Channel>>>>,
|
||||||
|
|
||||||
|
// Owned by the CM. Remove ourselves from this when done
|
||||||
|
conns: Arc<Mutex<HashSet<dbus::Path<'static>>>>,
|
||||||
|
|
||||||
|
ctx: Arc<Context>, // Delta contexts are threadsafe
|
||||||
settings: ConnSettings,
|
settings: ConnSettings,
|
||||||
state: Arc<RwLock<ConnState>>,
|
state: Arc<RwLock<ConnState>>,
|
||||||
|
|
||||||
// Used for sending out messages
|
|
||||||
msgq: Arc<Mutex<VecDeque<dbus::Message>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ConnSettings {
|
pub struct ConnSettings {
|
||||||
account: String,
|
account: String,
|
||||||
password: String,
|
password: String,
|
||||||
|
bcc_self: bool,
|
||||||
id: String,
|
id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +103,24 @@ impl ConnSettings {
|
|||||||
None => return err,
|
None => return err,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let bcc_self = match params.get("bcc-self") {
|
||||||
|
Some(variant) => match variant.0.as_u64() {
|
||||||
|
Some(i) => i != 0,
|
||||||
|
None => {
|
||||||
|
println!("0!");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
println!("1!");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
account,
|
account,
|
||||||
password,
|
password,
|
||||||
|
bcc_self,
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -97,94 +133,130 @@ impl ConnSettings {
|
|||||||
CONN_BUS_NAME.to_owned() + "." + &self.id
|
CONN_BUS_NAME.to_owned() + "." + &self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self) -> String {
|
pub fn path(&self) -> dbus::Path<'static> {
|
||||||
CONN_OBJECT_PATH.to_owned() + "/" + &self.id
|
dbus::Path::new(format!("{}/{}", CONN_OBJECT_PATH, &self.id)).expect("Valid path")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
settings: ConnSettings,
|
settings: ConnSettings,
|
||||||
conns: Arc<Mutex<HashSet<String>>>,
|
conns: Arc<Mutex<HashSet<dbus::Path<'static>>>>,
|
||||||
) -> Result<Self, MethodErr> {
|
) -> Result<(Self, mpsc::Receiver<DbusAction>), MethodErr> {
|
||||||
let mut dbfile = directories::ProjectDirs::from("gs", "ur", "telepathy-padfoot")
|
let proj_dir = directories::ProjectDirs::from("gs", "ur", "telepathy-padfoot")
|
||||||
.ok_or_else(MethodErr::no_arg)
|
.ok_or_else(MethodErr::no_arg)?;
|
||||||
.and_then(|p| Ok(p.data_local_dir().to_path_buf()))?;
|
|
||||||
|
let mut dbfile = async_std::path::PathBuf::new();
|
||||||
|
dbfile.push(proj_dir.data_local_dir().to_str().unwrap());
|
||||||
|
|
||||||
dbfile.push(settings.id());
|
dbfile.push(settings.id());
|
||||||
dbfile.push("db.sqlite3");
|
dbfile.push("db.sqlite3");
|
||||||
|
|
||||||
// FIXME: how to give it access to the connection (initialized later)?
|
let (q_s, q_r) = mpsc::channel::<DbusAction>();
|
||||||
let msgq = Arc::new(Mutex::new(VecDeque::<dbus::Message>::new()));
|
|
||||||
|
|
||||||
let id = settings.id();
|
let id = settings.id();
|
||||||
// Use this if we need to send messages in response to DC events:
|
|
||||||
// let msgq2 = msgq.clone();
|
|
||||||
let f = move |_c: &Context, e: Event| {
|
|
||||||
match e {
|
|
||||||
Event::Info(msg) => println!("Connection<{}>: INFO: {}", id, msg),
|
|
||||||
Event::Warning(msg) => println!("Connection<{}>: WARN : {}", id, msg),
|
|
||||||
Event::Error(msg) | Event::ErrorNetwork(msg) | Event::ErrorSelfNotInGroup(msg) => {
|
|
||||||
println!("Connection<{}>: ERR : {}", id, msg)
|
|
||||||
}
|
|
||||||
Event::ConfigureProgress(progress) => {
|
|
||||||
println!("Connection<{}>: Configuration progress: {}", id, progress)
|
|
||||||
}
|
|
||||||
Event::ImapConnected(msg) | Event::SmtpConnected(msg) => {
|
|
||||||
println!("Connection<{}>: Network: {}", id, msg);
|
|
||||||
}
|
|
||||||
/* Unhandled messages:
|
|
||||||
SmtpMessageSent(String),
|
|
||||||
ImapMessageDeleted(String),
|
|
||||||
ImapMessageMoved(String),
|
|
||||||
ImapFolderEmptied(String),
|
|
||||||
NewBlobFile(String),
|
|
||||||
DeletedBlobFile(String),
|
|
||||||
MsgsChanged
|
|
||||||
IncomingMsg
|
|
||||||
MsgDelivered
|
|
||||||
MsgFailed
|
|
||||||
MsgRead
|
|
||||||
ChatModified(ChatId),
|
|
||||||
ContactsChanged(Option<u32>),
|
|
||||||
LocationChanged(Option<u32>),
|
|
||||||
ImexProgress(usize),
|
|
||||||
ImexFileWritten(PathBuf),
|
|
||||||
SecurejoinInviterProgress
|
|
||||||
SecurejoinJoinerProgress
|
|
||||||
*/
|
|
||||||
_ => println!("Connection<{}>: unhandled event received: {:?}", id, e),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
let ctx =
|
let ctx = Arc::new(
|
||||||
Context::new(Box::new(f), "telepathy-padfoot".to_string(), dbfile).map_err(|e| {
|
block_on(Context::new("telepathy-padfoot".to_string(), dbfile)).map_err(|e| {
|
||||||
println!(
|
println!(
|
||||||
"Connection<{}>::new(): couldn't get delta context: {}",
|
"Connection<{}>::new(): couldn't get delta context: {}",
|
||||||
settings.id(),
|
settings.id(),
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
MethodErr::no_arg() // FIXME: better error handling
|
MethodErr::no_arg() // FIXME: better error handling
|
||||||
})?;
|
})?,
|
||||||
|
);
|
||||||
|
|
||||||
ctx.set_config(Config::Addr, Some(&settings.account))
|
let e_ctx = ctx.clone();
|
||||||
|
let e_queue = q_s.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
let emitter = e_ctx.get_event_emitter();
|
||||||
|
while let Some(e) = emitter.recv_sync() {
|
||||||
|
match e {
|
||||||
|
Event::Info(msg) => println!("Connection<{}>: INFO: {}", id, msg),
|
||||||
|
Event::Warning(msg) => println!("Connection<{}>: WARN : {}", id, msg),
|
||||||
|
Event::Error(msg)
|
||||||
|
| Event::ErrorNetwork(msg)
|
||||||
|
| Event::ErrorSelfNotInGroup(msg) => {
|
||||||
|
println!("Connection<{}>: ERR : {}", id, msg)
|
||||||
|
}
|
||||||
|
Event::ConfigureProgress(progress) => {
|
||||||
|
println!("Connection<{}>: Configuration progress: {}", id, progress)
|
||||||
|
}
|
||||||
|
Event::ImapConnected(msg) | Event::SmtpConnected(msg) => {
|
||||||
|
println!("Connection<{}>: Network: {}", id, msg);
|
||||||
|
}
|
||||||
|
Event::MsgsChanged { chat_id, msg_id } => {
|
||||||
|
println!(
|
||||||
|
"Connection<{}>: Messages changed for {}: {}",
|
||||||
|
id, chat_id, msg_id
|
||||||
|
);
|
||||||
|
e_queue
|
||||||
|
.send(DbusAction::IncomingMessage(chat_id, msg_id))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Event::IncomingMsg { chat_id, msg_id } => {
|
||||||
|
println!(
|
||||||
|
"Connection<{}>: Incoming message for {}: {}",
|
||||||
|
id, chat_id, msg_id
|
||||||
|
);
|
||||||
|
e_queue
|
||||||
|
.send(DbusAction::IncomingMessage(chat_id, msg_id))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unhandled messages:
|
||||||
|
SmtpMessageSent(String),
|
||||||
|
ImapMessageDeleted(String),
|
||||||
|
ImapFolderEmptied(String),
|
||||||
|
NewBlobFile(String),
|
||||||
|
DeletedBlobFile(String),
|
||||||
|
MsgDelivered
|
||||||
|
MsgFailed
|
||||||
|
MsgRead
|
||||||
|
ChatModified(ChatId),
|
||||||
|
ContactsChanged(Option<u32>),
|
||||||
|
LocationChanged(Option<u32>),
|
||||||
|
ImexProgress(usize),
|
||||||
|
ImexFileWritten(PathBuf),
|
||||||
|
SecurejoinInviterProgress
|
||||||
|
SecurejoinJoinerProgress
|
||||||
|
*/
|
||||||
|
Event::ImapMessageMoved(_) | Event::ImapMessageDeleted(_) => {}
|
||||||
|
_ => println!("Connection<{}>: unhandled event received: {:?}", id, e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
block_on(ctx.set_config(Config::Addr, Some(&settings.account)))
|
||||||
.map_err(|_e| MethodErr::no_arg())?;
|
.map_err(|_e| MethodErr::no_arg())?;
|
||||||
ctx.set_config(Config::MailPw, Some(&settings.password))
|
block_on(ctx.set_config(Config::MailPw, Some(&settings.password)))
|
||||||
.map_err(|_e| MethodErr::no_arg())?;
|
|
||||||
ctx.set_config(Config::SentboxWatch, Some(&"Sent"))
|
|
||||||
.map_err(|_e| MethodErr::no_arg())?;
|
.map_err(|_e| MethodErr::no_arg())?;
|
||||||
|
|
||||||
if !ctx.is_configured() {
|
if settings.bcc_self {
|
||||||
ctx.configure();
|
block_on(ctx.set_config(Config::BccSelf, Some(&"1")))
|
||||||
|
.map_err(|_e| MethodErr::no_arg())?;
|
||||||
|
} else {
|
||||||
|
block_on(ctx.set_config(Config::BccSelf, Some(&"0")))
|
||||||
|
.map_err(|_e| MethodErr::no_arg())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !block_on(ctx.is_configured()) {
|
||||||
|
block_on(ctx.configure()).unwrap();
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Connection {
|
Ok((
|
||||||
conns,
|
Connection {
|
||||||
settings,
|
actq: q_s,
|
||||||
msgq,
|
channels: Arc::new(RwLock::new(
|
||||||
ctx: Arc::new(RwLock::new(ctx)),
|
HashMap::<dbus::Path<'static>, Arc<Channel>>::new(),
|
||||||
state: Arc::new(RwLock::new(ConnState::Initial)),
|
)),
|
||||||
})
|
conns,
|
||||||
|
ctx,
|
||||||
|
settings,
|
||||||
|
state: Arc::new(RwLock::new(ConnState::Initial)),
|
||||||
|
},
|
||||||
|
q_r,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should be run inside its own thread. It will signal via the channel
|
// This should be run inside its own thread. It will signal via the channel
|
||||||
@@ -192,13 +264,19 @@ impl Connection {
|
|||||||
//
|
//
|
||||||
// FIXME: running several +process+ loops sure is convenient, but it also
|
// FIXME: running several +process+ loops sure is convenient, but it also
|
||||||
// seems inefficient...
|
// seems inefficient...
|
||||||
pub fn run(self, signal: mpsc::Sender<Option<MethodErr>>) {
|
pub fn run(
|
||||||
|
self,
|
||||||
|
done_signal: mpsc::Sender<Option<MethodErr>>,
|
||||||
|
queue_receiver: mpsc::Receiver<DbusAction>,
|
||||||
|
) {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
let bus = self.bus();
|
let bus = self.bus();
|
||||||
let path = self.path();
|
let path = self.path();
|
||||||
|
|
||||||
let conns = self.conns.clone();
|
let conns = self.conns.clone();
|
||||||
let msgq = self.msgq.clone();
|
let chans = self.channels.clone();
|
||||||
|
let actq = self.actq.clone();
|
||||||
|
let ctx = self.ctx.clone();
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let tree = self.build_tree();
|
let tree = self.build_tree();
|
||||||
|
|
||||||
@@ -211,24 +289,36 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tree.start_receive(&c);
|
let tc = tree.clone();
|
||||||
|
c.start_receive(
|
||||||
|
dbus::message::MatchRule::new_method_call(),
|
||||||
|
Box::new(move |msg, c| {
|
||||||
|
let tree = tc.lock().unwrap();
|
||||||
|
if let Some(replies) = tree.handle(&msg) {
|
||||||
|
for r in replies {
|
||||||
|
let _ = c.send(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
match c.request_name(bus.clone(), false, false, true) {
|
match c.request_name(bus.clone(), false, false, true) {
|
||||||
Ok(RequestNameReply::Exists) => {
|
Ok(RequestNameReply::Exists) => {
|
||||||
println!("Another process is already registered on {}", bus);
|
println!("Another process is already registered on {}", bus);
|
||||||
signal.send(Some(MethodErr::no_arg())).unwrap();
|
done_signal.send(Some(MethodErr::no_arg())).unwrap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Failed to register {}: {}", bus, e);
|
println!("Failed to register {}: {}", bus, e);
|
||||||
signal.send(Some(MethodErr::no_arg())).unwrap();
|
done_signal.send(Some(MethodErr::no_arg())).unwrap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// All other responses we can get are a success. We are now on
|
// All other responses we can get are a success. We are now on
|
||||||
// the message bus, so the caller can proceed
|
// the message bus, so the caller can proceed
|
||||||
println!("{} listening on {}", c.unique_name(), bus);
|
println!("{} listening on {}", c.unique_name(), bus);
|
||||||
signal.send(None).unwrap();
|
done_signal.send(None).unwrap();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -242,12 +332,168 @@ impl Connection {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Spend a bit of time sending any outgoing messages - signals, mostly
|
// Spend a bit of time sending any outgoing messages - signals, mostly
|
||||||
while let Some(msg) = msgq.lock().unwrap().pop_front() {
|
while let Some(act) = queue_receiver.try_recv().ok() {
|
||||||
print!("Connection<{}>: Sending message...", id);
|
match act {
|
||||||
|
DbusAction::Signal(msg) => {
|
||||||
|
print!("*** Connection<{}>: Sending signal: {:?}...", id, msg);
|
||||||
|
|
||||||
match c.send(msg) {
|
match c.send(msg) {
|
||||||
Err(e) => println!("error! {:?}", e), // FIXME: handle error better?
|
Err(e) => println!("error! {:?}", e), // FIXME: handle error better?
|
||||||
_ => println!("OK!"),
|
_ => println!("OK!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DbusAction::NewChannel(channel) => {
|
||||||
|
let chan_type = channel.chan_type();
|
||||||
|
let handle_type = channel.handle_type();
|
||||||
|
let handle = channel.handle();
|
||||||
|
let chan_path = channel.path().clone();
|
||||||
|
let chan_props = channel.chan_props();
|
||||||
|
let rc_channel = Arc::new(channel);
|
||||||
|
|
||||||
|
println!("*** Creating channel {}", chan_path);
|
||||||
|
|
||||||
|
Arc::clone(&chans)
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(chan_path.clone(), rc_channel.clone());
|
||||||
|
|
||||||
|
let t2 = tree.clone();
|
||||||
|
let op = Channel::build_object_path(rc_channel);
|
||||||
|
|
||||||
|
t2.lock().unwrap().insert(op);
|
||||||
|
|
||||||
|
let requests_sig = telepathy::ConnectionInterfaceRequestsNewChannels {
|
||||||
|
channels: vec![(chan_path.clone(), chan_props)],
|
||||||
|
};
|
||||||
|
|
||||||
|
let legacy_sig = telepathy::ConnectionNewChannel {
|
||||||
|
object_path: chan_path.clone(),
|
||||||
|
channel_type: chan_type,
|
||||||
|
handle_type, // contact. FIXME: support other channel types
|
||||||
|
handle, // id of other contact
|
||||||
|
// TODO: initiator needs to be tracked
|
||||||
|
suppress_handler: false, // We'll need to start passing this
|
||||||
|
};
|
||||||
|
|
||||||
|
actq.send(DbusAction::Signal(requests_sig.to_emit_message(&path)))
|
||||||
|
.unwrap();
|
||||||
|
actq.send(DbusAction::Signal(legacy_sig.to_emit_message(&path)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
DbusAction::CloseChannel(chan_path) => {
|
||||||
|
println!("*** Closing channel {}", chan_path.clone());
|
||||||
|
let _chan = Arc::clone(&chans).write().unwrap().remove(&chan_path);
|
||||||
|
let t2 = tree.clone();
|
||||||
|
t2.lock().unwrap().remove(&chan_path);
|
||||||
|
|
||||||
|
let requests_sig = telepathy::ConnectionInterfaceRequestsChannelClosed {
|
||||||
|
removed: chan_path.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let legacy_sig = telepathy::ChannelClosed {};
|
||||||
|
|
||||||
|
actq.send(DbusAction::Signal(requests_sig.to_emit_message(&path)))
|
||||||
|
.unwrap();
|
||||||
|
actq.send(DbusAction::Signal(legacy_sig.to_emit_message(&chan_path)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
DbusAction::IncomingMessage(chat_id, msg_id) => {
|
||||||
|
println!("*** Incoming message: {} {}", chat_id, msg_id);
|
||||||
|
// TODO: check if we have a channel for the chat
|
||||||
|
let chan_path = Connection::build_channel_path(path.clone(), chat_id);
|
||||||
|
let c2 = Arc::clone(&chans);
|
||||||
|
let chans = c2.read().unwrap();
|
||||||
|
//let u_ctx = ctx.clone();
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
let blobdir = ctx.get_blobdir();
|
||||||
|
|
||||||
|
// Autocreate channel if it doesn't already exist
|
||||||
|
// FIXME: unknown contacts need more care than this
|
||||||
|
if !chans.contains_key(&chan_path) {
|
||||||
|
print!("Channel for {} doesn't exist yet, creating it...", chat_id);
|
||||||
|
|
||||||
|
let contacts = block_on(dc::chat::get_chat_contacts(&ctx, chat_id));
|
||||||
|
if contacts.len() > 1 {
|
||||||
|
println!("...{} contacts in chat, ignoring!", contacts.len());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: device-specific chat isn't really a self-chat
|
||||||
|
let handle = contacts
|
||||||
|
.first()
|
||||||
|
.unwrap_or(&dc::constants::DC_CONTACT_ID_SELF);
|
||||||
|
let chan = Channel::new(
|
||||||
|
actq.clone(),
|
||||||
|
chat_id,
|
||||||
|
ctx.clone(),
|
||||||
|
*handle, // initiator is the remote contact
|
||||||
|
chan_path,
|
||||||
|
false, // FIXME: this needs to handle requested channels
|
||||||
|
*handle, // target is always the other party
|
||||||
|
);
|
||||||
|
actq.send(DbusAction::NewChannel(chan)).unwrap();
|
||||||
|
actq.send(act).unwrap();
|
||||||
|
|
||||||
|
println!("OK");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the channel exists, emit new message signals
|
||||||
|
print!("Message {} received for {}...", msg_id, chan_path);
|
||||||
|
|
||||||
|
let msg = match block_on(dc::message::Message::load_from_db(&ctx, msg_id)) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Can't load from database, skipping: {}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ignore messages that are self-originated.
|
||||||
|
// FIXME: special-case self-chats
|
||||||
|
if msg.get_from_id() == dc::constants::DC_CONTACT_ID_SELF
|
||||||
|
&& !msg.is_setupmessage()
|
||||||
|
{
|
||||||
|
println!("from ourselves, skipping");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parts = convert_msg(blobdir, &msg);
|
||||||
|
if parts.is_err() {
|
||||||
|
println!("can't convert, skipping: {}", parts.unwrap_err());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sig = telepathy::ChannelInterfaceMessagesMessageReceived {
|
||||||
|
message: parts.unwrap(),
|
||||||
|
}
|
||||||
|
.to_emit_message(&chan_path);
|
||||||
|
|
||||||
|
actq.send(DbusAction::Signal(sig)).unwrap();
|
||||||
|
|
||||||
|
// FIXME: We MUST also send a Text.Received signal
|
||||||
|
|
||||||
|
println!("OK!");
|
||||||
|
}
|
||||||
|
|
||||||
|
DbusAction::FreshMessages => {
|
||||||
|
println!("*** FRESH MESSAGES");
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
|
||||||
|
for msg_id in block_on(dc::context::Context::get_fresh_msgs(&ctx)) {
|
||||||
|
println!(" FRESH MESSAGE: {}", msg_id);
|
||||||
|
match block_on(dc::message::Message::load_from_db(&ctx, msg_id)) {
|
||||||
|
Ok(msg) => {
|
||||||
|
actq.send(DbusAction::IncomingMessage(
|
||||||
|
msg.get_chat_id(),
|
||||||
|
msg_id,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Err(e) => println!("Couldn't load fresh message {}: {}", msg_id, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,11 +512,20 @@ impl Connection {
|
|||||||
self.settings.bus()
|
self.settings.bus()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self) -> String {
|
pub fn path(&self) -> dbus::Path<'static> {
|
||||||
self.settings.path()
|
self.settings.path()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_tree(self) -> dbus::tree::Tree<dbus::tree::MTFn, ()> {
|
fn build_channel_path(
|
||||||
|
path: dbus::Path<'static>,
|
||||||
|
chat_id: dc::chat::ChatId,
|
||||||
|
) -> dbus::strings::Path<'static> {
|
||||||
|
let path = format!("{}/{}", path, chat_id.to_u32());
|
||||||
|
|
||||||
|
dbus::strings::Path::new(path).expect("Must be valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_tree(self) -> Arc<Mutex<dbus::tree::Tree<dbus::tree::MTFn, ()>>> {
|
||||||
let path = self.path();
|
let path = self.path();
|
||||||
let c_rc = std::rc::Rc::new(self);
|
let c_rc = std::rc::Rc::new(self);
|
||||||
let f = dbus::tree::Factory::new_fn::<()>();
|
let f = dbus::tree::Factory::new_fn::<()>();
|
||||||
@@ -288,7 +543,7 @@ impl Connection {
|
|||||||
telepathy::connection_interface_contacts_server(&f, (), move |_| c_rc3.clone());
|
telepathy::connection_interface_contacts_server(&f, (), move |_| c_rc3.clone());
|
||||||
|
|
||||||
let _c_rc4 = c_rc.clone();
|
let _c_rc4 = c_rc.clone();
|
||||||
let _contact_list_iface =
|
let contact_list_iface =
|
||||||
telepathy::connection_interface_contact_list_server(&f, (), move |_| _c_rc4.clone());
|
telepathy::connection_interface_contact_list_server(&f, (), move |_| _c_rc4.clone());
|
||||||
|
|
||||||
let c_rc5 = c_rc.clone();
|
let c_rc5 = c_rc.clone();
|
||||||
@@ -304,12 +559,12 @@ impl Connection {
|
|||||||
.add(conn_iface)
|
.add(conn_iface)
|
||||||
.add(avatars_iface)
|
.add(avatars_iface)
|
||||||
.add(contacts_iface)
|
.add(contacts_iface)
|
||||||
// .add(contact_list_iface)
|
.add(contact_list_iface)
|
||||||
.add(requests_iface)
|
.add(requests_iface)
|
||||||
.add(simple_presence_iface),
|
.add(simple_presence_iface),
|
||||||
);
|
);
|
||||||
tree = tree.add(f.object_path("/", ()).introspectable());
|
tree = tree.add(f.object_path("/", ()).introspectable());
|
||||||
|
|
||||||
tree
|
Arc::new(Mutex::new(tree))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,17 @@
|
|||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
use crate::telepathy::{ConnectionInterfaceContacts, ConnectionInterfaceRequests}; // Non-deprecated channel methods
|
||||||
|
|
||||||
|
use async_std::task::block_on;
|
||||||
use dbus::message::SignalArgs;
|
use dbus::message::SignalArgs;
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
|
use dc::contact::Contact;
|
||||||
use deltachat as dc;
|
use deltachat as dc;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use super::Connection;
|
use super::{Connection, DbusAction};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum ConnState {
|
pub enum ConnState {
|
||||||
@@ -14,12 +20,28 @@ pub enum ConnState {
|
|||||||
Disconnected,
|
Disconnected,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, dbus::tree::MethodErr>;
|
||||||
|
|
||||||
|
// Deprecated channel information. Replaced by Requests.ChannelSpec
|
||||||
|
type ChannelInfo = (
|
||||||
|
dbus::Path<'static>, // Object path
|
||||||
|
String, // Channel type
|
||||||
|
u32, // Handle type
|
||||||
|
u32, // Handle
|
||||||
|
);
|
||||||
|
|
||||||
|
type ContactSubscription = (
|
||||||
|
u32, // Subscribe state
|
||||||
|
u32, // Publish state
|
||||||
|
String, // Publish-request message
|
||||||
|
);
|
||||||
|
|
||||||
pub fn connection_interfaces() -> Vec<String> {
|
pub fn connection_interfaces() -> Vec<String> {
|
||||||
vec![
|
vec![
|
||||||
"org.freedesktop.Telepathy.Connection".to_string(),
|
"org.freedesktop.Telepathy.Connection".to_string(),
|
||||||
"org.freedesktop.Telepathy.Connection.Interface.Avatars".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.Avatars".to_string(),
|
||||||
"org.freedesktop.Telepathy.Connection.Interface.Contacts".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.Contacts".to_string(),
|
||||||
// "org.freedesktop.Telepathy.Connection.Interface.ContactList".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.ContactList".to_string(),
|
||||||
"org.freedesktop.Telepathy.Connection.Interface.Requests".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.Requests".to_string(),
|
||||||
"org.freedesktop.Telepathy.Connection.Interface.SimplePresence".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.SimplePresence".to_string(),
|
||||||
]
|
]
|
||||||
@@ -33,67 +55,15 @@ impl AsRef<dyn telepathy::Connection + 'static> for std::rc::Rc<Connection> {
|
|||||||
|
|
||||||
impl telepathy::Connection for Connection {
|
impl telepathy::Connection for Connection {
|
||||||
// In connect(), we start the threads that drive the deltachat context
|
// In connect(), we start the threads that drive the deltachat context
|
||||||
fn connect(&self) -> Result<(), MethodErr> {
|
fn connect(&self) -> Result<()> {
|
||||||
println!("Connection<{}>::connect()", self.id());
|
println!("Connection<{}>::connect()", self.id());
|
||||||
|
|
||||||
let inbox_ctx = self.ctx.clone();
|
let io_ctx = self.ctx.clone();
|
||||||
let state = self.state.clone();
|
let io_id = self.id();
|
||||||
let id = self.id();
|
let _io_thread = thread::spawn(move || {
|
||||||
let _inbox_thread = thread::spawn(move || {
|
block_on(io_ctx.start_io());
|
||||||
while *state.read().unwrap() != ConnState::Disconnected {
|
|
||||||
dc::job::perform_inbox_jobs(&inbox_ctx.read().unwrap());
|
|
||||||
if *state.read().unwrap() != ConnState::Disconnected {
|
|
||||||
dc::job::perform_inbox_fetch(&inbox_ctx.read().unwrap());
|
|
||||||
|
|
||||||
if *state.read().unwrap() != ConnState::Disconnected {
|
println!("Connection<{}>::connect(): I/O thread exited", io_id);
|
||||||
dc::job::perform_inbox_idle(&inbox_ctx.read().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Connection<{}>::connect(): inbox thread exited", id);
|
|
||||||
});
|
|
||||||
|
|
||||||
let smtp_ctx = self.ctx.clone();
|
|
||||||
let state = self.state.clone();
|
|
||||||
let id = self.id();
|
|
||||||
let _smtp_thread = thread::spawn(move || {
|
|
||||||
while *state.read().unwrap() != ConnState::Disconnected {
|
|
||||||
dc::job::perform_smtp_jobs(&smtp_ctx.read().unwrap());
|
|
||||||
if *state.read().unwrap() != ConnState::Disconnected {
|
|
||||||
dc::job::perform_smtp_idle(&smtp_ctx.read().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Connection<{}>::connect(): smtp thread exited", id);
|
|
||||||
});
|
|
||||||
|
|
||||||
let mvbox_ctx = self.ctx.clone();
|
|
||||||
let state = self.state.clone();
|
|
||||||
let id = self.id();
|
|
||||||
let _mvbox_thread = thread::spawn(move || {
|
|
||||||
while *state.read().unwrap() != ConnState::Disconnected {
|
|
||||||
dc::job::perform_mvbox_fetch(&mvbox_ctx.read().unwrap());
|
|
||||||
if *state.read().unwrap() != ConnState::Disconnected {
|
|
||||||
dc::job::perform_mvbox_idle(&mvbox_ctx.read().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Connection<{}>::connect(): mvbox thread exited", id);
|
|
||||||
});
|
|
||||||
|
|
||||||
let sentbox_ctx = self.ctx.clone();
|
|
||||||
let state = self.state.clone();
|
|
||||||
let id = self.id();
|
|
||||||
let _sentbox_thread = thread::spawn(move || {
|
|
||||||
while *state.read().unwrap() != ConnState::Disconnected {
|
|
||||||
dc::job::perform_sentbox_fetch(&sentbox_ctx.read().unwrap());
|
|
||||||
if *state.read().unwrap() != ConnState::Disconnected {
|
|
||||||
dc::job::perform_sentbox_idle(&sentbox_ctx.read().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Connection<{}>::connect(): sentbox thread exited", id);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Just pretend to be connected all the time for now. Tracking IMAP+SMTP
|
// Just pretend to be connected all the time for now. Tracking IMAP+SMTP
|
||||||
@@ -101,88 +71,111 @@ impl telepathy::Connection for Connection {
|
|||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let mut w = state.write().unwrap();
|
let mut w = state.write().unwrap();
|
||||||
*w = ConnState::Connected;
|
*w = ConnState::Connected;
|
||||||
|
let ctx = self.ctx.clone();
|
||||||
|
|
||||||
// Emit a StatusChanged signal for the benefit of others, but the caller
|
// Emit a StatusChanged signal for the benefit of others, but the caller
|
||||||
// learns from our RPC response
|
// learns from our RPC response
|
||||||
let sig = telepathy::ConnectionStatusChanged {
|
let connected_sig = telepathy::ConnectionStatusChanged {
|
||||||
status: 0, // Connected
|
status: 0, // Connected
|
||||||
reason: 1, // Requested
|
reason: 1, // Requested
|
||||||
|
}
|
||||||
|
.to_emit_message(&self.path());
|
||||||
|
|
||||||
|
self.actq.send(DbusAction::Signal(connected_sig)).unwrap();
|
||||||
|
self.actq.send(DbusAction::FreshMessages).unwrap();
|
||||||
|
|
||||||
|
// If we can, emit signals on connect about the contact list
|
||||||
|
if let Ok(handles) = block_on(Contact::get_all(
|
||||||
|
&ctx,
|
||||||
|
(dc::constants::DC_GCL_ADD_SELF as usize)
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
None::<String>,
|
||||||
|
)) {
|
||||||
|
println!(" HANDLES: {:?}", handles);
|
||||||
|
let mut changes = HashMap::<u32, ContactSubscription>::new();
|
||||||
|
for handle in handles {
|
||||||
|
println!(" *** Handle: {}", handle);
|
||||||
|
changes.insert(handle, (4, 4, "".to_string())); // FIXME: hardcoded lies
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: the old signal is deprecated. The new signal requires us to
|
||||||
|
// send identifiers with it, which is a bit of a lookup here.
|
||||||
|
// let cl_sig_new = telepathy::ConnectionInterfaceContactsChangedWithID {
|
||||||
|
// }.to_emit_message(&self.path());
|
||||||
|
|
||||||
|
let cl_sig_old = telepathy::ConnectionInterfaceContactListContactsChanged {
|
||||||
|
changes,
|
||||||
|
removals: Vec::new(),
|
||||||
|
}
|
||||||
|
.to_emit_message(&self.path());
|
||||||
|
|
||||||
|
// self.actq.send(DbusAction::Signal(cl_sig_new)).unwrap();
|
||||||
|
self.actq.send(DbusAction::Signal(cl_sig_old)).unwrap();
|
||||||
};
|
};
|
||||||
|
|
||||||
let dbus_conn_path = dbus::strings::Path::new(self.path())
|
|
||||||
.expect("Object path should meet DBUS requirements");
|
|
||||||
|
|
||||||
self.msgq
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(sig.to_emit_message(&dbus_conn_path));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disconnect(&self) -> Result<(), MethodErr> {
|
fn disconnect(&self) -> Result<()> {
|
||||||
println!("Connection<{}>::disconnect()", self.id());
|
println!("Connection<{}>::disconnect()", self.id());
|
||||||
let ctx = self.ctx.read().unwrap();
|
block_on(self.ctx.stop_io());
|
||||||
|
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let mut w = state.write().unwrap();
|
let mut w = state.write().unwrap();
|
||||||
*w = ConnState::Disconnected;
|
*w = ConnState::Disconnected;
|
||||||
|
|
||||||
dc::job::interrupt_inbox_idle(&ctx);
|
|
||||||
dc::job::interrupt_smtp_idle(&ctx);
|
|
||||||
dc::job::interrupt_sentbox_idle(&ctx);
|
|
||||||
dc::job::interrupt_mvbox_idle(&ctx);
|
|
||||||
|
|
||||||
// FIXME: we need to signal to the CM that they should remove the
|
// FIXME: we need to signal to the CM that they should remove the
|
||||||
// connection from the active list
|
// connection from the active list
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interfaces(&self) -> Result<Vec<String>, MethodErr> {
|
fn interfaces(&self) -> Result<Vec<String>> {
|
||||||
println!("Connection<{}>::interfaces()", self.id());
|
println!("Connection<{}>::interfaces()", self.id());
|
||||||
|
|
||||||
self.get_interfaces()
|
self.get_interfaces()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_interfaces(&self) -> Result<Vec<String>, MethodErr> {
|
fn get_interfaces(&self) -> Result<Vec<String>> {
|
||||||
println!("Connection<{}>::get_interfaces()", self.id());
|
println!("Connection<{}>::get_interfaces()", self.id());
|
||||||
|
|
||||||
Ok(connection_interfaces())
|
Ok(connection_interfaces())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_protocol(&self) -> Result<String, MethodErr> {
|
fn get_protocol(&self) -> Result<String> {
|
||||||
println!("Connection<{}>::get_protocol()", self.id());
|
println!("Connection<{}>::get_protocol()", self.id());
|
||||||
|
|
||||||
Ok(crate::padfoot::PROTO_NAME.to_string())
|
Ok(crate::padfoot::PROTO_NAME.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_handle(&self) -> Result<u32, MethodErr> {
|
fn self_handle(&self) -> Result<u32> {
|
||||||
println!("Connection<{}>::self_handle()", self.id());
|
println!("Connection<{}>::self_handle()", self.id());
|
||||||
|
|
||||||
self.get_self_handle()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_self_handle(&self) -> Result<u32, MethodErr> {
|
|
||||||
println!("Connection<{}>::get_self_handle()", self.id());
|
|
||||||
|
|
||||||
Ok(dc::constants::DC_CONTACT_ID_SELF)
|
Ok(dc::constants::DC_CONTACT_ID_SELF)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn status(&self) -> Result<u32, MethodErr> {
|
// Deprecated in favour of the self_handle DBUS property
|
||||||
|
fn get_self_handle(&self) -> Result<u32> {
|
||||||
|
println!("Connection<{}>::get_self_handle()", self.id());
|
||||||
|
|
||||||
|
self.self_handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> Result<u32> {
|
||||||
println!("Connection<{}>::status()", self.id());
|
println!("Connection<{}>::status()", self.id());
|
||||||
|
|
||||||
self.get_status()
|
self.get_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_status(&self) -> Result<u32, MethodErr> {
|
fn get_status(&self) -> Result<u32> {
|
||||||
match *self.state.clone().read().unwrap() {
|
match *self.state.clone().read().unwrap() {
|
||||||
ConnState::Initial | ConnState::Disconnected => Ok(2),
|
ConnState::Initial | ConnState::Disconnected => Ok(2),
|
||||||
ConnState::Connected => Ok(0),
|
ConnState::Connected => Ok(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hold_handles(&self, handle_type: u32, handles: Vec<u32>) -> Result<(), MethodErr> {
|
fn hold_handles(&self, handle_type: u32, handles: Vec<u32>) -> Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"Connection<{}>::hold_handles({}, {:?})",
|
"Connection<{}>::hold_handles({}, {:?})",
|
||||||
self.id(),
|
self.id(),
|
||||||
@@ -194,26 +187,52 @@ impl telepathy::Connection for Connection {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inspect_handles(
|
fn inspect_handles(&self, handle_type: u32, handles: Vec<u32>) -> Result<Vec<String>> {
|
||||||
&self,
|
|
||||||
handle_type: u32,
|
|
||||||
handles: Vec<u32>,
|
|
||||||
) -> Result<Vec<String>, MethodErr> {
|
|
||||||
println!(
|
println!(
|
||||||
"Connection<{}>::inspect_handles({}, {:?})",
|
"Connection<{}>::inspect_handles({}, {:?})",
|
||||||
self.id(),
|
self.id(),
|
||||||
handle_type,
|
handle_type,
|
||||||
handles
|
handles
|
||||||
);
|
);
|
||||||
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
match handle_type {
|
||||||
|
crate::padfoot::HANDLE_TYPE_CONTACT => {
|
||||||
|
let mut out = Vec::<String>::new();
|
||||||
|
for (_handle, attrs) in self.get_contact_attributes(handles, vec![], true)? {
|
||||||
|
if let Some(contact_id) =
|
||||||
|
attrs.get("org.freedesktop.Telepathy.Connection/contact-id")
|
||||||
|
{
|
||||||
|
out.push(contact_id.0.as_str().unwrap().to_string());
|
||||||
|
} else {
|
||||||
|
return Err(MethodErr::no_arg()); // FIXME: should be InvalidHandle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
_ => Err(MethodErr::no_arg()), // FIXME: should be NotImplemented?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_channels(&self) -> Result<Vec<(dbus::Path<'static>, String, u32, u32)>, MethodErr> {
|
// Deprecated in favour of Requests.Channels
|
||||||
|
fn list_channels(&self) -> Result<Vec<ChannelInfo>> {
|
||||||
println!("Connection<{}>::list_channels()", self.id());
|
println!("Connection<{}>::list_channels()", self.id());
|
||||||
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
||||||
|
let data = self.channels()?;
|
||||||
|
let mut out = Vec::<ChannelInfo>::new();
|
||||||
|
|
||||||
|
for (path, _props) in data {
|
||||||
|
// FIXME: pull correct details from the properties hash
|
||||||
|
out.push((
|
||||||
|
path,
|
||||||
|
"org.freedesktop.Telepathy.Channel.Type.Text".to_string(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release_handles(&self, handle_type: u32, handles: Vec<u32>) -> Result<(), MethodErr> {
|
fn release_handles(&self, handle_type: u32, handles: Vec<u32>) -> Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"Connection<{}>::release_handles({}, {:?})",
|
"Connection<{}>::release_handles({}, {:?})",
|
||||||
self.id(),
|
self.id(),
|
||||||
@@ -225,39 +244,74 @@ impl telepathy::Connection for Connection {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestChannel is deprecated in favour of the Requests interface.
|
||||||
fn request_channel(
|
fn request_channel(
|
||||||
&self,
|
&self,
|
||||||
type_: &str,
|
channel_type: &str,
|
||||||
handle_type: u32,
|
handle_type: u32,
|
||||||
handle: u32,
|
handle: u32,
|
||||||
suppress_handler: bool,
|
suppress_handler: bool,
|
||||||
) -> Result<dbus::Path<'static>, MethodErr> {
|
) -> Result<dbus::Path<'static>> {
|
||||||
println!(
|
println!(
|
||||||
"Connection<{}>::request_channel({}, {}, {}, {})",
|
"Connection<{}>::request_channel({}, {}, {}, {})",
|
||||||
self.id(),
|
self.id(),
|
||||||
type_,
|
channel_type,
|
||||||
handle_type,
|
handle_type,
|
||||||
handle,
|
handle,
|
||||||
suppress_handler
|
suppress_handler
|
||||||
);
|
);
|
||||||
|
|
||||||
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_handles(
|
fn request_handles(&self, handle_type: u32, identifiers: Vec<&str>) -> Result<Vec<u32>> {
|
||||||
&self,
|
|
||||||
handle_type: u32,
|
|
||||||
identifiers: Vec<&str>,
|
|
||||||
) -> Result<Vec<u32>, MethodErr> {
|
|
||||||
println!(
|
println!(
|
||||||
"Connection<{}>::request_handles({}, {:?})",
|
"Connection<{}>::request_handles({}, {:?})",
|
||||||
self.id(),
|
self.id(),
|
||||||
handle_type,
|
handle_type,
|
||||||
identifiers
|
identifiers
|
||||||
);
|
);
|
||||||
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
||||||
|
match handle_type {
|
||||||
|
crate::padfoot::HANDLE_TYPE_CONTACT => {
|
||||||
|
let ctx = &self.ctx;
|
||||||
|
let mut out = Vec::<u32>::new();
|
||||||
|
|
||||||
|
// Identifiers is a list of email addresses. These can be
|
||||||
|
// contact IDs, although we may have to create contacts to get
|
||||||
|
// the ID.
|
||||||
|
//
|
||||||
|
// FIXME: will it be faster to get all and filter?
|
||||||
|
|
||||||
|
for addr in identifiers {
|
||||||
|
let id = block_on(Contact::lookup_id_by_addr(
|
||||||
|
ctx,
|
||||||
|
addr,
|
||||||
|
dc::contact::Origin::Unknown,
|
||||||
|
));
|
||||||
|
match id {
|
||||||
|
0 => {
|
||||||
|
// No contact exists for this address yet. Try to
|
||||||
|
// add one so we can have an ID.
|
||||||
|
match block_on(Contact::create(ctx, &addr, &addr)) {
|
||||||
|
Ok(new_id) => out.push(new_id),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to add contact {}: {}", addr, e);
|
||||||
|
return Err(MethodErr::no_arg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => out.push(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
_ => Err(MethodErr::no_arg()), // FIXME: should be NotImplemented?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_client_interest(&self, tokens: Vec<&str>) -> Result<(), MethodErr> {
|
fn add_client_interest(&self, tokens: Vec<&str>) -> Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"Connection<{}>::add_client_interest({:?})",
|
"Connection<{}>::add_client_interest({:?})",
|
||||||
self.id(),
|
self.id(),
|
||||||
@@ -266,7 +320,7 @@ impl telepathy::Connection for Connection {
|
|||||||
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_client_interest(&self, tokens: Vec<&str>) -> Result<(), MethodErr> {
|
fn remove_client_interest(&self, tokens: Vec<&str>) -> Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"Connection<{}>::remove_client_interest({:?})",
|
"Connection<{}>::remove_client_interest({:?})",
|
||||||
self.id(),
|
self.id(),
|
||||||
@@ -275,13 +329,13 @@ impl telepathy::Connection for Connection {
|
|||||||
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_id(&self) -> Result<String, MethodErr> {
|
fn self_id(&self) -> Result<String> {
|
||||||
println!("Connection<{}>::self_id()", self.id());
|
println!("Connection<{}>::self_id()", self.id());
|
||||||
|
|
||||||
let contact = match dc::contact::Contact::get_by_id(
|
let contact = match block_on(dc::contact::Contact::get_by_id(
|
||||||
&self.ctx.read().unwrap(),
|
&self.ctx,
|
||||||
dc::constants::DC_CONTACT_ID_SELF,
|
dc::constants::DC_CONTACT_ID_SELF,
|
||||||
) {
|
)) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!(" err: {}", e);
|
println!(" err: {}", e);
|
||||||
@@ -292,7 +346,7 @@ impl telepathy::Connection for Connection {
|
|||||||
Ok(contact.get_addr().to_string())
|
Ok(contact.get_addr().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_immortal_handles(&self) -> Result<bool, MethodErr> {
|
fn has_immortal_handles(&self) -> Result<bool> {
|
||||||
println!("Connection<{}>::has_immortal_handles()", self.id());
|
println!("Connection<{}>::has_immortal_handles()", self.id());
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
use crate::padfoot::VarArg;
|
use crate::padfoot::VarArg;
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
|
||||||
|
use async_std::task::block_on;
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
use deltachat::constants::DC_GCL_ADD_SELF;
|
use dc::constants::DC_GCL_ADD_SELF;
|
||||||
use deltachat::contact::Contact;
|
use dc::contact::Contact;
|
||||||
|
use deltachat as dc;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use telepathy::ConnectionInterfaceContacts; // for get_contact_attributes
|
use telepathy::ConnectionInterfaceContacts; // for get_contact_attributes
|
||||||
@@ -30,8 +32,11 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
|
|||||||
hold
|
hold
|
||||||
);
|
);
|
||||||
|
|
||||||
let ctx = &self.ctx.read().unwrap();
|
let ids = match block_on(Contact::get_all(
|
||||||
let ids = match Contact::get_all(ctx, DC_GCL_ADD_SELF.try_into().unwrap(), None::<String>) {
|
&self.ctx,
|
||||||
|
DC_GCL_ADD_SELF.try_into().unwrap(),
|
||||||
|
None::<String>,
|
||||||
|
)) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!(" err: {}", e);
|
println!(" err: {}", e);
|
||||||
@@ -39,6 +44,7 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME: swap implementations so Contacts depends on ContactList?
|
||||||
self.get_contact_attributes(ids, vec![], true)
|
self.get_contact_attributes(ids, vec![], true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +68,17 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
|
|||||||
}
|
}
|
||||||
fn remove_contacts(&self, contacts: Vec<u32>) -> Result<(), MethodErr> {
|
fn remove_contacts(&self, contacts: Vec<u32>) -> Result<(), MethodErr> {
|
||||||
println!("Connection<{}>::remove_contacts({:?})", self.id(), contacts);
|
println!("Connection<{}>::remove_contacts({:?})", self.id(), contacts);
|
||||||
|
|
||||||
|
for contact_id in contacts {
|
||||||
|
// FIXME: don't ignore errors
|
||||||
|
if let Err(e) = block_on(Contact::delete(&self.ctx, contact_id)) {
|
||||||
|
println!(" Deleting contact {} failed: {}", contact_id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: signals MUST be emitted before this method returns
|
||||||
|
// FIXME: emitting signals at all would be great
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn unsubscribe(&self, contacts: Vec<u32>) -> Result<(), MethodErr> {
|
fn unsubscribe(&self, contacts: Vec<u32>) -> Result<(), MethodErr> {
|
||||||
@@ -76,6 +93,8 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
|
|||||||
|
|
||||||
fn download(&self) -> Result<(), MethodErr> {
|
fn download(&self) -> Result<(), MethodErr> {
|
||||||
println!("Connection<{}>::download()", self.id());
|
println!("Connection<{}>::download()", self.id());
|
||||||
|
|
||||||
|
// This can be a no-op since we store the contacts list in delta's DB
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +110,7 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
|
|||||||
|
|
||||||
fn can_change_contact_list(&self) -> Result<bool, MethodErr> {
|
fn can_change_contact_list(&self) -> Result<bool, MethodErr> {
|
||||||
println!("Connection<{}>::can_change_contact_list()", self.id());
|
println!("Connection<{}>::can_change_contact_list()", self.id());
|
||||||
Ok(false)
|
Ok(true)
|
||||||
}
|
}
|
||||||
fn request_uses_message(&self) -> Result<bool, MethodErr> {
|
fn request_uses_message(&self) -> Result<bool, MethodErr> {
|
||||||
println!("Connection<{}>::request_uses_message()", self.id());
|
println!("Connection<{}>::request_uses_message()", self.id());
|
||||||
@@ -100,6 +119,11 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
|
|||||||
|
|
||||||
fn download_at_connection(&self) -> Result<bool, MethodErr> {
|
fn download_at_connection(&self) -> Result<bool, MethodErr> {
|
||||||
println!("Connection<{}>::download_at_connection()", self.id());
|
println!("Connection<{}>::download_at_connection()", self.id());
|
||||||
Ok(false)
|
|
||||||
|
// TODO: https://telepathy.freedesktop.org/spec/Connection_Interface_Contact_List.html#Property:DownloadAtConnection
|
||||||
|
// Connections ... SHOULD provide a corresponding parameter named
|
||||||
|
// org.freedesktop.Telepathy.Connection.Interface.ContactList.DownloadAtConnection
|
||||||
|
// with the DBus_Property flag
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
use crate::padfoot::{var_str, VarArg};
|
use crate::padfoot::{var_str, var_u32, VarArg};
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
|
||||||
|
use async_std::task::block_on;
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
use deltachat::contact::{Contact, Origin};
|
use deltachat::contact::{Contact, Origin};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -31,7 +32,7 @@ impl telepathy::ConnectionInterfaceContacts for Connection {
|
|||||||
let mut out = HashMap::<u32, HashMap<String, VarArg>>::new();
|
let mut out = HashMap::<u32, HashMap<String, VarArg>>::new();
|
||||||
for id in handles.iter() {
|
for id in handles.iter() {
|
||||||
// FIXME: work out how to use get_all
|
// FIXME: work out how to use get_all
|
||||||
let contact = match Contact::get_by_id(&self.ctx.read().unwrap(), *id) {
|
let contact = match block_on(Contact::get_by_id(&self.ctx, *id)) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_e) => continue, // Invalid IDs are silently ignored
|
Err(_e) => continue, // Invalid IDs are silently ignored
|
||||||
};
|
};
|
||||||
@@ -48,18 +49,17 @@ impl telepathy::ConnectionInterfaceContacts for Connection {
|
|||||||
"org.freedesktop.Telepathy.Connection.Interface.Avatars/token".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.Avatars/token".to_string(),
|
||||||
var_str("".to_string()),
|
var_str("".to_string()),
|
||||||
);
|
);
|
||||||
/*
|
|
||||||
// TODO: we need to publish DBUS services on these endpoints
|
|
||||||
props.insert(
|
|
||||||
"org.freedesktop.Telepathy.Connection.Interface.ContactList/publish".to_string(),
|
|
||||||
var_arg(Box::new(4)),
|
|
||||||
);
|
|
||||||
|
|
||||||
props.insert(
|
props.insert(
|
||||||
"org.freedesktop.Telepathy.Connection.Interface.ContactList/subscribe".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.ContactList/publish".to_string(),
|
||||||
var_arg(Box::new(4)),
|
var_u32(4), // YES (faking it for now)
|
||||||
);
|
);
|
||||||
*/
|
|
||||||
|
props.insert(
|
||||||
|
"org.freedesktop.Telepathy.Connection.Interface.ContactList/subscribe".to_string(),
|
||||||
|
var_u32(4), // YES (faking it for now)
|
||||||
|
);
|
||||||
|
|
||||||
out.insert(*id, props);
|
out.insert(*id, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,10 +78,11 @@ impl telepathy::ConnectionInterfaceContacts for Connection {
|
|||||||
interfaces
|
interfaces
|
||||||
);
|
);
|
||||||
|
|
||||||
let id = {
|
let id = block_on(Contact::lookup_id_by_addr(
|
||||||
let ctx = &self.ctx.read().unwrap();
|
&self.ctx,
|
||||||
Contact::lookup_id_by_addr(ctx, identifier, Origin::Unknown)
|
identifier,
|
||||||
};
|
Origin::Unknown,
|
||||||
|
));
|
||||||
|
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
return Err(MethodErr::no_arg()); // FIXME: should be InvalidHandle
|
return Err(MethodErr::no_arg()); // FIXME: should be InvalidHandle
|
||||||
@@ -101,7 +102,7 @@ impl telepathy::ConnectionInterfaceContacts for Connection {
|
|||||||
Ok(vec![
|
Ok(vec![
|
||||||
"org.freedesktop.Telepathy.Connection".to_string(),
|
"org.freedesktop.Telepathy.Connection".to_string(),
|
||||||
"org.freedesktop.Telepathy.Connection.Interface.Avatars".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.Avatars".to_string(),
|
||||||
// "org.freedesktop.Telepathy.Connection.Interface.ContactList".to_string(),
|
"org.freedesktop.Telepathy.Connection.Interface.ContactList".to_string(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,35 @@
|
|||||||
use crate::padfoot::{requestables, VarArg};
|
use crate::padfoot::{get_var_str, get_var_u32, requestables, Channel, DbusAction, VarArg};
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
|
||||||
|
use async_std::task::block_on;
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
|
use dc::contact::Contact;
|
||||||
|
use deltachat as dc;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::Connection;
|
use super::Connection;
|
||||||
|
|
||||||
|
type ChannelSpec = (
|
||||||
|
dbus::Path<'static>, // Object path on this connection
|
||||||
|
HashMap<String, VarArg>, // Map of channel property -> value
|
||||||
|
);
|
||||||
|
|
||||||
|
type RequestableChannelSpec = (HashMap<String, VarArg>, Vec<String>);
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, MethodErr>;
|
||||||
|
|
||||||
impl AsRef<dyn telepathy::ConnectionInterfaceRequests + 'static> for std::rc::Rc<Connection> {
|
impl AsRef<dyn telepathy::ConnectionInterfaceRequests + 'static> for std::rc::Rc<Connection> {
|
||||||
fn as_ref(&self) -> &(dyn telepathy::ConnectionInterfaceRequests + 'static) {
|
fn as_ref(&self) -> &(dyn telepathy::ConnectionInterfaceRequests + 'static) {
|
||||||
&**self
|
&**self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelSpec = (dbus::Path<'static>, HashMap<String, VarArg>);
|
|
||||||
|
|
||||||
type RequestableChannelSpec = (HashMap<String, VarArg>, Vec<String>);
|
|
||||||
|
|
||||||
impl telepathy::ConnectionInterfaceRequests for Connection {
|
impl telepathy::ConnectionInterfaceRequests for Connection {
|
||||||
fn create_channel(
|
fn create_channel(
|
||||||
&self,
|
&self,
|
||||||
request: HashMap<&str, VarArg>,
|
request: HashMap<&str, VarArg>,
|
||||||
) -> Result<(dbus::Path<'static>, HashMap<String, VarArg>), MethodErr> {
|
) -> Result<(dbus::Path<'static>, HashMap<String, VarArg>)> {
|
||||||
println!("Connection<{}>::create_channel({:?})", self.id(), request);
|
println!("Connection<{}>::create_channel({:?})", self.id(), request);
|
||||||
|
|
||||||
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
||||||
@@ -29,17 +38,99 @@ impl telepathy::ConnectionInterfaceRequests for Connection {
|
|||||||
fn ensure_channel(
|
fn ensure_channel(
|
||||||
&self,
|
&self,
|
||||||
request: HashMap<&str, VarArg>,
|
request: HashMap<&str, VarArg>,
|
||||||
) -> Result<(bool, dbus::Path<'static>, HashMap<String, VarArg>), MethodErr> {
|
) -> Result<(bool, dbus::Path<'static>, HashMap<String, VarArg>)> {
|
||||||
|
let path = self.path().clone();
|
||||||
println!("Connection<{}>::ensure_channel({:?})", self.id(), request);
|
println!("Connection<{}>::ensure_channel({:?})", self.id(), request);
|
||||||
Err(MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
||||||
|
// Empathy sends this for the request:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "org.freedesktop.Telepathy.Channel.TargetID": Variant("me@example.com"),
|
||||||
|
// "org.freedesktop.Telepathy.Channel.TargetHandleType": Variant(1),
|
||||||
|
// "org.freedesktop.Telepathy.Channel.ChannelType": Variant("org.freedesktop.Telepathy.Channel.Type.Text")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// FIXME: life would be easier with TargetHandle
|
||||||
|
let target_id = request
|
||||||
|
.get("org.freedesktop.Telepathy.Channel.TargetID")
|
||||||
|
.map(|va| get_var_str(va))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let target_handle_type = request
|
||||||
|
.get("org.freedesktop.Telepathy.Channel.TargetHandleType")
|
||||||
|
.map(|va| get_var_u32(va))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let channel_type = request
|
||||||
|
.get("org.freedesktop.Telepathy.Channel.ChannelType")
|
||||||
|
.map(|va| get_var_str(va))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Text only
|
||||||
|
if channel_type != "org.freedesktop.Telepathy.Channel.Type.Text" {
|
||||||
|
println!(" Wrong channel type: {:?}", channel_type);
|
||||||
|
return Err(MethodErr::no_arg());
|
||||||
|
};
|
||||||
|
|
||||||
|
// IMs only
|
||||||
|
if target_handle_type != 1 {
|
||||||
|
println!(" Wrong target handle type: {:?}", target_handle_type);
|
||||||
|
return Err(MethodErr::no_arg());
|
||||||
|
};
|
||||||
|
|
||||||
|
let target_handle = block_on(Contact::lookup_id_by_addr(
|
||||||
|
&self.ctx,
|
||||||
|
target_id.clone(),
|
||||||
|
dc::contact::Origin::Unknown,
|
||||||
|
));
|
||||||
|
if target_handle == 0 {
|
||||||
|
println!("Couldn't find target handle for {}", target_id);
|
||||||
|
return Err(MethodErr::no_arg());
|
||||||
|
};
|
||||||
|
|
||||||
|
let chat_id = dc::chat::ChatId::new(target_handle);
|
||||||
|
let channel_path = Connection::build_channel_path(path, chat_id);
|
||||||
|
|
||||||
|
// Return an existing channel if it already exists
|
||||||
|
let chans = self.channels.read().unwrap();
|
||||||
|
if let Some(channel) = chans.get(&channel_path) {
|
||||||
|
return Ok((false, channel_path, channel.chan_props()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we need to discover or create a chat id for the contact
|
||||||
|
let chat_id = block_on(dc::chat::create_by_contact_id(&self.ctx, target_handle)).unwrap();
|
||||||
|
let channel = Channel::new(
|
||||||
|
self.actq.clone(),
|
||||||
|
chat_id,
|
||||||
|
self.ctx.clone(),
|
||||||
|
dc::constants::DC_CONTACT_ID_SELF, // initiator is self in this case
|
||||||
|
channel_path.clone(),
|
||||||
|
true, // requested
|
||||||
|
target_handle,
|
||||||
|
);
|
||||||
|
let response = channel.chan_props(); // FIXME: fill with data about the channel
|
||||||
|
|
||||||
|
// Send signal
|
||||||
|
self.actq.send(DbusAction::NewChannel(channel)).unwrap();
|
||||||
|
|
||||||
|
Ok((true, channel_path, response))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn channels(&self) -> Result<Vec<ChannelSpec>, MethodErr> {
|
fn channels(&self) -> Result<Vec<ChannelSpec>> {
|
||||||
println!("Connection<{}>::channels()", self.id());
|
println!("Connection<{}>::channels()", self.id());
|
||||||
Ok(vec![])
|
|
||||||
|
let mut out = Vec::<ChannelSpec>::new();
|
||||||
|
for channel in self.channels.read().unwrap().values() {
|
||||||
|
out.push((
|
||||||
|
channel.path(),
|
||||||
|
HashMap::<String, VarArg>::new(), // FIXME: work out what props should be shown
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn requestable_channel_classes(&self) -> Result<Vec<RequestableChannelSpec>, MethodErr> {
|
fn requestable_channel_classes(&self) -> Result<Vec<RequestableChannelSpec>> {
|
||||||
println!("Connection<{}>::requestable_channel_classes()", self.id());
|
println!("Connection<{}>::requestable_channel_classes()", self.id());
|
||||||
Ok(requestables())
|
Ok(requestables())
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@ pub const CM_OBJECT_PATH: &str = "/org/freedesktop/Telepathy/ConnectionManager/p
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ConnectionManager {
|
pub struct ConnectionManager {
|
||||||
conns: Arc<Mutex<HashSet<String>>>,
|
conns: Arc<Mutex<HashSet<dbus::Path<'static>>>>,
|
||||||
sender: mpsc::Sender<dbus::Message>,
|
sender: mpsc::Sender<dbus::Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ impl ConnectionManager {
|
|||||||
|
|
||||||
(
|
(
|
||||||
ConnectionManager {
|
ConnectionManager {
|
||||||
conns: Arc::new(Mutex::new(HashSet::<String>::new())),
|
conns: Arc::new(Mutex::new(HashSet::<dbus::Path<'static>>::new())),
|
||||||
sender: msg_s,
|
sender: msg_s,
|
||||||
},
|
},
|
||||||
msg_r,
|
msg_r,
|
||||||
@@ -46,26 +46,25 @@ impl ConnectionManager {
|
|||||||
|
|
||||||
let mut conns = self.conns.lock().expect("Mutex access");
|
let mut conns = self.conns.lock().expect("Mutex access");
|
||||||
|
|
||||||
let dbus_conn_path = dbus::strings::Path::new(path.to_owned())
|
|
||||||
.expect("Object path should meet DBUS requirements");
|
|
||||||
|
|
||||||
// We can't call connect() multiple times on the connection yet
|
// We can't call connect() multiple times on the connection yet
|
||||||
if conns.contains(&path) {
|
if conns.contains(&path) {
|
||||||
return Err(MethodErr::no_arg());
|
return Err(MethodErr::no_arg());
|
||||||
}
|
}
|
||||||
|
|
||||||
let conn = Connection::new(settings, self.conns.clone())?;
|
|
||||||
|
|
||||||
// It would be nice to have a single main loop, but thread-per-conn is
|
// It would be nice to have a single main loop, but thread-per-conn is
|
||||||
// is easy enough for me to understand in Rust at the moment.
|
// is easy enough for me to understand in Rust at the moment.
|
||||||
|
let conns_clone = self.conns.clone();
|
||||||
let (ok_s, ok_r) = mpsc::channel();
|
let (ok_s, ok_r) = mpsc::channel();
|
||||||
std::thread::spawn(move || conn.run(ok_s));
|
std::thread::spawn(move || {
|
||||||
|
let (conn, receiver) = Connection::new(settings, conns_clone).unwrap();
|
||||||
|
conn.run(ok_s, receiver);
|
||||||
|
});
|
||||||
|
|
||||||
// Emit a NewConnection signal for the benefit of others, but the caller
|
// Emit a NewConnection signal for the benefit of others, but the caller
|
||||||
// learns from our RPC response
|
// learns from our RPC response
|
||||||
let sig = telepathy::ConnectionManagerNewConnection {
|
let sig = telepathy::ConnectionManagerNewConnection {
|
||||||
bus_name: bus.to_owned(),
|
bus_name: bus.to_owned(),
|
||||||
object_path: dbus_conn_path.clone(),
|
object_path: path.clone(),
|
||||||
protocol: super::PROTO_NAME.to_string(),
|
protocol: super::PROTO_NAME.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ impl ConnectionManager {
|
|||||||
return Err(MethodErr::no_arg());
|
return Err(MethodErr::no_arg());
|
||||||
}
|
}
|
||||||
|
|
||||||
conns.insert(path);
|
conns.insert(path.clone());
|
||||||
|
|
||||||
let dbus_cm_path = dbus::strings::Path::new(CM_OBJECT_PATH.to_string())
|
let dbus_cm_path = dbus::strings::Path::new(CM_OBJECT_PATH.to_string())
|
||||||
.expect("Object path should meet DBUS requirements");
|
.expect("Object path should meet DBUS requirements");
|
||||||
@@ -91,7 +90,7 @@ impl ConnectionManager {
|
|||||||
.expect("send signal");
|
.expect("send signal");
|
||||||
|
|
||||||
// The bus name *must* be org.freedesktop.Telepathy.Connection.padfoot.delta.<name>
|
// The bus name *must* be org.freedesktop.Telepathy.Connection.padfoot.delta.<name>
|
||||||
Ok((bus, dbus_conn_path))
|
Ok((bus, path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
138
src/padfoot/message.rs
Normal file
138
src/padfoot/message.rs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
use crate::padfoot::{var_bytearray, var_i64, var_str, var_u32, VarArg};
|
||||||
|
|
||||||
|
use async_std::path::{Path, PathBuf};
|
||||||
|
use dc::constants::Viewtype as Vt;
|
||||||
|
use dc::message::Message;
|
||||||
|
use deltachat as dc;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
type Part = HashMap<String, VarArg>;
|
||||||
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
|
// Turns a deltachat::message::Message into a Telepathy Message_Part_List
|
||||||
|
pub fn convert_msg(blobdir: &Path, msg: &Message) -> Result<Vec<Part>> {
|
||||||
|
if msg.is_setupmessage() {
|
||||||
|
return Ok(convert_setupmessage(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parts = vec![make_props(msg)];
|
||||||
|
|
||||||
|
// TODO: Check. Can a deltachat message have multiple parts? Can an image
|
||||||
|
// viewtype have text as well?
|
||||||
|
let result = match msg.get_viewtype() {
|
||||||
|
Vt::Text => build_txt(msg),
|
||||||
|
Vt::Unknown => build_unknown(msg),
|
||||||
|
Vt::Audio | Vt::Voice => build_snd(msg),
|
||||||
|
Vt::Video => build_vid(msg),
|
||||||
|
_ => build_attachment(blobdir, msg),
|
||||||
|
};
|
||||||
|
|
||||||
|
result.map(|mut more| {
|
||||||
|
parts.append(&mut more);
|
||||||
|
parts
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_setupmessage(msg: &Message) -> Vec<Part> {
|
||||||
|
let msg_id = msg.get_id();
|
||||||
|
vec![
|
||||||
|
make_props(msg),
|
||||||
|
make_plain(&format!("Setup message received. To apply it, reply with:\nIMEX: {} nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn\nNo whitespace in the setup-code!", msg_id.to_u32())),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_props(msg: &Message) -> Part {
|
||||||
|
let msg_id = msg.get_id();
|
||||||
|
let mut out = HashMap::new();
|
||||||
|
|
||||||
|
out.insert(
|
||||||
|
"message-token".to_string(),
|
||||||
|
var_str(format!("{}", msg_id.to_u32())),
|
||||||
|
);
|
||||||
|
out.insert("message-sent".to_string(), var_i64(msg.get_timestamp()));
|
||||||
|
out.insert(
|
||||||
|
"message-received".to_string(),
|
||||||
|
var_i64(msg.get_received_timestamp()),
|
||||||
|
);
|
||||||
|
out.insert("message-sender".to_string(), var_u32(msg.get_from_id()));
|
||||||
|
// props.insert("message-sender-id", var_str()); // This doesn't need to be sent
|
||||||
|
// props.insert("sender-nickname", var_str()); // Can we get away without this one?
|
||||||
|
out.insert("message-type".to_string(), var_u32(0)); // normal
|
||||||
|
|
||||||
|
// These relate to superseded messages
|
||||||
|
// props.insert("supersedes", var_str());
|
||||||
|
// props.insert("original-message-sent", var_i64());
|
||||||
|
// props.insert("original-message-received", var_i64());
|
||||||
|
|
||||||
|
out.insert("pending-message-id".to_string(), var_u32(msg_id.to_u32()));
|
||||||
|
|
||||||
|
// Don't need these
|
||||||
|
// props.insert("interface", var_str());
|
||||||
|
// props.insert("scrollback", var_vool());
|
||||||
|
// props.insert("silent", var_bool());
|
||||||
|
// props.insert("rescued", var_bool());
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_plain(text: &str) -> Part {
|
||||||
|
make_content("text/plain", None, var_str(text.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_content(type_: &str, id: Option<&str>, content: VarArg) -> Part {
|
||||||
|
let mut out = HashMap::new();
|
||||||
|
|
||||||
|
out.insert("content-type".to_string(), var_str(type_.to_string()));
|
||||||
|
out.insert("content".to_string(), content);
|
||||||
|
|
||||||
|
id.map(|txt| out.insert("identifier".to_string(), var_str(txt.to_string())));
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_snd(_msg: &Message) -> Result<Vec<Part>> {
|
||||||
|
Ok(vec![make_plain("(a sound file was received)")])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_txt(msg: &Message) -> Result<Vec<Part>> {
|
||||||
|
Ok(vec![make_plain(
|
||||||
|
&msg.get_text().unwrap_or_else(|| "".to_string()),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_unknown(_msg: &Message) -> Result<Vec<Part>> {
|
||||||
|
Ok(vec![make_plain("(a message of unknown type was received)")])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_vid(_msg: &Message) -> Result<Vec<Part>> {
|
||||||
|
Ok(vec![make_plain("(a video was received)")])
|
||||||
|
}
|
||||||
|
|
||||||
|
// The message contains a file. Detect the content-type and construct a part
|
||||||
|
// containing the data in full.
|
||||||
|
fn build_attachment(blobdir: &Path, msg: &Message) -> Result<Vec<Part>> {
|
||||||
|
let mime = msg
|
||||||
|
.get_filemime()
|
||||||
|
.unwrap_or_else(|| "application/octet-stream".to_string());
|
||||||
|
let filename = msg.get_filename().ok_or("Failed to get filename")?;
|
||||||
|
|
||||||
|
let path: PathBuf = [blobdir, &Path::new(&filename)].iter().collect();
|
||||||
|
|
||||||
|
let data =
|
||||||
|
std::fs::read(&path).map_err(|e| format!("Failed to read file {:?}: {}", path, e))?;
|
||||||
|
|
||||||
|
println!("MIME type for attachment: {}", mime);
|
||||||
|
|
||||||
|
let html = make_content(
|
||||||
|
"text/html",
|
||||||
|
None,
|
||||||
|
var_str("<img src=\"cid:picture\" />".to_string()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let txt = make_plain("(an image was sent but cannot be displayed)");
|
||||||
|
|
||||||
|
let blob = make_content(&mime, Some("picture"), var_bytearray(data));
|
||||||
|
|
||||||
|
Ok(vec![html, txt, blob])
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
use crate::padfoot::{var_str, var_u32, VarArg};
|
use crate::padfoot::{var_bool, var_str, var_u32, VarArg};
|
||||||
use crate::telepathy;
|
use crate::telepathy;
|
||||||
|
|
||||||
use dbus::tree::MethodErr;
|
use dbus::tree::MethodErr;
|
||||||
@@ -49,6 +49,7 @@ pub fn parameters() -> Vec<ParamSpec> {
|
|||||||
"s".to_string(),
|
"s".to_string(),
|
||||||
var_str("".to_string()),
|
var_str("".to_string()),
|
||||||
),
|
),
|
||||||
|
("bcc-self".to_string(), 0, "b".to_string(), var_bool(false)),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
use dbus::arg::{RefArg, Variant};
|
use dbus::arg::{RefArg, Variant};
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
pub type VarArg = Variant<Box<dyn RefArg + 'static>>;
|
pub type VarArg = Variant<Box<dyn RefArg + 'static>>;
|
||||||
|
|
||||||
pub fn var_arg(item: Box<dyn RefArg + 'static>) -> VarArg {
|
pub fn var_arg(item: Box<dyn RefArg + 'static>) -> VarArg {
|
||||||
@@ -14,6 +16,26 @@ pub fn var_str_vec(item: Vec<String>) -> VarArg {
|
|||||||
var_arg(Box::new(item))
|
var_arg(Box::new(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn var_bool(item: bool) -> VarArg {
|
||||||
|
var_arg(Box::new(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn var_bytearray(item: std::vec::Vec<u8>) -> VarArg {
|
||||||
|
var_arg(Box::new(item))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn var_u32(item: u32) -> VarArg {
|
pub fn var_u32(item: u32) -> VarArg {
|
||||||
var_arg(Box::new(item))
|
var_arg(Box::new(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn var_i64(item: i64) -> VarArg {
|
||||||
|
var_arg(Box::new(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_var_str(from: &VarArg) -> String {
|
||||||
|
from.0.as_str().unwrap().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_var_u32(from: &VarArg) -> u32 {
|
||||||
|
from.0.as_u64().unwrap().try_into().unwrap()
|
||||||
|
}
|
||||||
|
122
src/telepathy.rs
122
src/telepathy.rs
@@ -1,365 +1,485 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#![allow(clippy::all)]
|
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod account_interface_addressing;
|
mod account_interface_addressing;
|
||||||
pub use self::account_interface_addressing::*;
|
pub use self::account_interface_addressing::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod account_interface_avatar;
|
mod account_interface_avatar;
|
||||||
pub use self::account_interface_avatar::*;
|
pub use self::account_interface_avatar::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod account_interface_external_password_storage;
|
mod account_interface_external_password_storage;
|
||||||
pub use self::account_interface_external_password_storage::*;
|
pub use self::account_interface_external_password_storage::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod account_interface_hidden;
|
mod account_interface_hidden;
|
||||||
pub use self::account_interface_hidden::*;
|
pub use self::account_interface_hidden::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod account_interface_storage;
|
mod account_interface_storage;
|
||||||
pub use self::account_interface_storage::*;
|
pub use self::account_interface_storage::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod account_manager_interface_hidden;
|
mod account_manager_interface_hidden;
|
||||||
pub use self::account_manager_interface_hidden::*;
|
pub use self::account_manager_interface_hidden::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod account_manager;
|
mod account_manager;
|
||||||
pub use self::account_manager::*;
|
pub use self::account_manager::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod account;
|
mod account;
|
||||||
pub use self::account::*;
|
pub use self::account::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod all;
|
mod all;
|
||||||
pub use self::all::*;
|
pub use self::all::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod authentication_tls_certificate;
|
mod authentication_tls_certificate;
|
||||||
pub use self::authentication_tls_certificate::*;
|
pub use self::authentication_tls_certificate::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content_interface_audio_control;
|
mod call_content_interface_audio_control;
|
||||||
pub use self::call_content_interface_audio_control::*;
|
pub use self::call_content_interface_audio_control::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content_interface_dtmf;
|
mod call_content_interface_dtmf;
|
||||||
pub use self::call_content_interface_dtmf::*;
|
pub use self::call_content_interface_dtmf::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content_interface_media;
|
mod call_content_interface_media;
|
||||||
pub use self::call_content_interface_media::*;
|
pub use self::call_content_interface_media::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content_interface_video_control;
|
mod call_content_interface_video_control;
|
||||||
pub use self::call_content_interface_video_control::*;
|
pub use self::call_content_interface_video_control::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content_media_description_interface_rtcp_extended_reports;
|
mod call_content_media_description_interface_rtcp_extended_reports;
|
||||||
pub use self::call_content_media_description_interface_rtcp_extended_reports::*;
|
pub use self::call_content_media_description_interface_rtcp_extended_reports::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content_media_description_interface_rtcp_feedback;
|
mod call_content_media_description_interface_rtcp_feedback;
|
||||||
pub use self::call_content_media_description_interface_rtcp_feedback::*;
|
pub use self::call_content_media_description_interface_rtcp_feedback::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content_media_description_interface_rtp_header_extensions;
|
mod call_content_media_description_interface_rtp_header_extensions;
|
||||||
pub use self::call_content_media_description_interface_rtp_header_extensions::*;
|
pub use self::call_content_media_description_interface_rtp_header_extensions::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content_media_description;
|
mod call_content_media_description;
|
||||||
pub use self::call_content_media_description::*;
|
pub use self::call_content_media_description::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_content;
|
mod call_content;
|
||||||
pub use self::call_content::*;
|
pub use self::call_content::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_interface_mute;
|
mod call_interface_mute;
|
||||||
pub use self::call_interface_mute::*;
|
pub use self::call_interface_mute::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_stream_endpoint;
|
mod call_stream_endpoint;
|
||||||
pub use self::call_stream_endpoint::*;
|
pub use self::call_stream_endpoint::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_stream_interface_media;
|
mod call_stream_interface_media;
|
||||||
pub use self::call_stream_interface_media::*;
|
pub use self::call_stream_interface_media::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod call_stream;
|
mod call_stream;
|
||||||
pub use self::call_stream::*;
|
pub use self::call_stream::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_bundle;
|
mod channel_bundle;
|
||||||
pub use self::channel_bundle::*;
|
pub use self::channel_bundle::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_dispatcher_interface_messages1;
|
mod channel_dispatcher_interface_messages1;
|
||||||
pub use self::channel_dispatcher_interface_messages1::*;
|
pub use self::channel_dispatcher_interface_messages1::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_dispatcher_interface_operation_list;
|
mod channel_dispatcher_interface_operation_list;
|
||||||
pub use self::channel_dispatcher_interface_operation_list::*;
|
pub use self::channel_dispatcher_interface_operation_list::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_dispatcher;
|
mod channel_dispatcher;
|
||||||
pub use self::channel_dispatcher::*;
|
pub use self::channel_dispatcher::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_dispatch_operation;
|
mod channel_dispatch_operation;
|
||||||
pub use self::channel_dispatch_operation::*;
|
pub use self::channel_dispatch_operation::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_future;
|
mod channel_future;
|
||||||
pub use self::channel_future::*;
|
pub use self::channel_future::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_handler;
|
mod channel_handler;
|
||||||
pub use self::channel_handler::*;
|
pub use self::channel_handler::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_addressing;
|
mod channel_interface_addressing;
|
||||||
pub use self::channel_interface_addressing::*;
|
pub use self::channel_interface_addressing::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_anonymity;
|
mod channel_interface_anonymity;
|
||||||
pub use self::channel_interface_anonymity::*;
|
pub use self::channel_interface_anonymity::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_call_state;
|
mod channel_interface_call_state;
|
||||||
pub use self::channel_interface_call_state::*;
|
pub use self::channel_interface_call_state::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_captcha_authentication;
|
mod channel_interface_captcha_authentication;
|
||||||
pub use self::channel_interface_captcha_authentication::*;
|
pub use self::channel_interface_captcha_authentication::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_chat_state;
|
mod channel_interface_chat_state;
|
||||||
pub use self::channel_interface_chat_state::*;
|
pub use self::channel_interface_chat_state::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_conference;
|
mod channel_interface_conference;
|
||||||
pub use self::channel_interface_conference::*;
|
pub use self::channel_interface_conference::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_credentials_storage;
|
mod channel_interface_credentials_storage;
|
||||||
pub use self::channel_interface_credentials_storage::*;
|
pub use self::channel_interface_credentials_storage::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_destroyable;
|
mod channel_interface_destroyable;
|
||||||
pub use self::channel_interface_destroyable::*;
|
pub use self::channel_interface_destroyable::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_dtmf;
|
mod channel_interface_dtmf;
|
||||||
pub use self::channel_interface_dtmf::*;
|
pub use self::channel_interface_dtmf::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_file_transfer_metadata;
|
mod channel_interface_file_transfer_metadata;
|
||||||
pub use self::channel_interface_file_transfer_metadata::*;
|
pub use self::channel_interface_file_transfer_metadata::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_group;
|
mod channel_interface_group;
|
||||||
pub use self::channel_interface_group::*;
|
pub use self::channel_interface_group::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_hold;
|
mod channel_interface_hold;
|
||||||
pub use self::channel_interface_hold::*;
|
pub use self::channel_interface_hold::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_html;
|
mod channel_interface_html;
|
||||||
pub use self::channel_interface_html::*;
|
pub use self::channel_interface_html::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_media_signalling;
|
mod channel_interface_media_signalling;
|
||||||
pub use self::channel_interface_media_signalling::*;
|
pub use self::channel_interface_media_signalling::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_mergeable_conference;
|
mod channel_interface_mergeable_conference;
|
||||||
pub use self::channel_interface_mergeable_conference::*;
|
pub use self::channel_interface_mergeable_conference::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_messages;
|
mod channel_interface_messages;
|
||||||
pub use self::channel_interface_messages::*;
|
pub use self::channel_interface_messages::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_password;
|
mod channel_interface_password;
|
||||||
pub use self::channel_interface_password::*;
|
pub use self::channel_interface_password::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_picture;
|
mod channel_interface_picture;
|
||||||
pub use self::channel_interface_picture::*;
|
pub use self::channel_interface_picture::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_room_config;
|
mod channel_interface_room_config;
|
||||||
pub use self::channel_interface_room_config::*;
|
pub use self::channel_interface_room_config::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_room;
|
mod channel_interface_room;
|
||||||
pub use self::channel_interface_room::*;
|
pub use self::channel_interface_room::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_sasl_authentication;
|
mod channel_interface_sasl_authentication;
|
||||||
pub use self::channel_interface_sasl_authentication::*;
|
pub use self::channel_interface_sasl_authentication::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_securable;
|
mod channel_interface_securable;
|
||||||
pub use self::channel_interface_securable::*;
|
pub use self::channel_interface_securable::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_service_point;
|
mod channel_interface_service_point;
|
||||||
pub use self::channel_interface_service_point::*;
|
pub use self::channel_interface_service_point::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_sms;
|
mod channel_interface_sms;
|
||||||
pub use self::channel_interface_sms::*;
|
pub use self::channel_interface_sms::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_splittable;
|
mod channel_interface_splittable;
|
||||||
pub use self::channel_interface_splittable::*;
|
pub use self::channel_interface_splittable::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_subject;
|
mod channel_interface_subject;
|
||||||
pub use self::channel_interface_subject::*;
|
pub use self::channel_interface_subject::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_transfer;
|
mod channel_interface_transfer;
|
||||||
pub use self::channel_interface_transfer::*;
|
pub use self::channel_interface_transfer::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_interface_tube;
|
mod channel_interface_tube;
|
||||||
pub use self::channel_interface_tube::*;
|
pub use self::channel_interface_tube::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_request;
|
mod channel_request;
|
||||||
pub use self::channel_request::*;
|
pub use self::channel_request::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_call;
|
mod channel_type_call;
|
||||||
pub use self::channel_type_call::*;
|
pub use self::channel_type_call::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_contact_list;
|
mod channel_type_contact_list;
|
||||||
pub use self::channel_type_contact_list::*;
|
pub use self::channel_type_contact_list::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_contact_search;
|
mod channel_type_contact_search;
|
||||||
pub use self::channel_type_contact_search::*;
|
pub use self::channel_type_contact_search::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_dbus_tube;
|
mod channel_type_dbus_tube;
|
||||||
pub use self::channel_type_dbus_tube::*;
|
pub use self::channel_type_dbus_tube::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_file_transfer;
|
mod channel_type_file_transfer;
|
||||||
pub use self::channel_type_file_transfer::*;
|
pub use self::channel_type_file_transfer::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_room_list;
|
mod channel_type_room_list;
|
||||||
pub use self::channel_type_room_list::*;
|
pub use self::channel_type_room_list::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_server_authentication;
|
mod channel_type_server_authentication;
|
||||||
pub use self::channel_type_server_authentication::*;
|
pub use self::channel_type_server_authentication::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_server_tls_connection;
|
mod channel_type_server_tls_connection;
|
||||||
pub use self::channel_type_server_tls_connection::*;
|
pub use self::channel_type_server_tls_connection::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_streamed_media;
|
mod channel_type_streamed_media;
|
||||||
pub use self::channel_type_streamed_media::*;
|
pub use self::channel_type_streamed_media::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_stream_tube;
|
mod channel_type_stream_tube;
|
||||||
pub use self::channel_type_stream_tube::*;
|
pub use self::channel_type_stream_tube::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_text;
|
mod channel_type_text;
|
||||||
pub use self::channel_type_text::*;
|
pub use self::channel_type_text::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel_type_tubes;
|
mod channel_type_tubes;
|
||||||
pub use self::channel_type_tubes::*;
|
pub use self::channel_type_tubes::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod channel;
|
mod channel;
|
||||||
pub use self::channel::*;
|
pub use self::channel::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod client_approver;
|
mod client_approver;
|
||||||
pub use self::client_approver::*;
|
pub use self::client_approver::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod client_handler_future;
|
mod client_handler_future;
|
||||||
pub use self::client_handler_future::*;
|
pub use self::client_handler_future::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod client_handler;
|
mod client_handler;
|
||||||
pub use self::client_handler::*;
|
pub use self::client_handler::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod client_interface_requests;
|
mod client_interface_requests;
|
||||||
pub use self::client_interface_requests::*;
|
pub use self::client_interface_requests::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod client_observer;
|
mod client_observer;
|
||||||
pub use self::client_observer::*;
|
pub use self::client_observer::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod client;
|
mod client;
|
||||||
pub use self::client::*;
|
pub use self::client::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_addressing;
|
mod connection_interface_addressing;
|
||||||
pub use self::connection_interface_addressing::*;
|
pub use self::connection_interface_addressing::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_aliasing;
|
mod connection_interface_aliasing;
|
||||||
pub use self::connection_interface_aliasing::*;
|
pub use self::connection_interface_aliasing::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_anonymity;
|
mod connection_interface_anonymity;
|
||||||
pub use self::connection_interface_anonymity::*;
|
pub use self::connection_interface_anonymity::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_avatars;
|
mod connection_interface_avatars;
|
||||||
pub use self::connection_interface_avatars::*;
|
pub use self::connection_interface_avatars::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_balance;
|
mod connection_interface_balance;
|
||||||
pub use self::connection_interface_balance::*;
|
pub use self::connection_interface_balance::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_capabilities;
|
mod connection_interface_capabilities;
|
||||||
pub use self::connection_interface_capabilities::*;
|
pub use self::connection_interface_capabilities::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_cellular;
|
mod connection_interface_cellular;
|
||||||
pub use self::connection_interface_cellular::*;
|
pub use self::connection_interface_cellular::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_client_types;
|
mod connection_interface_client_types;
|
||||||
pub use self::connection_interface_client_types::*;
|
pub use self::connection_interface_client_types::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_communication_policy;
|
mod connection_interface_communication_policy;
|
||||||
pub use self::connection_interface_communication_policy::*;
|
pub use self::connection_interface_communication_policy::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_contact_blocking;
|
mod connection_interface_contact_blocking;
|
||||||
pub use self::connection_interface_contact_blocking::*;
|
pub use self::connection_interface_contact_blocking::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_contact_capabilities;
|
mod connection_interface_contact_capabilities;
|
||||||
pub use self::connection_interface_contact_capabilities::*;
|
pub use self::connection_interface_contact_capabilities::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_contact_groups;
|
mod connection_interface_contact_groups;
|
||||||
pub use self::connection_interface_contact_groups::*;
|
pub use self::connection_interface_contact_groups::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_contact_info;
|
mod connection_interface_contact_info;
|
||||||
pub use self::connection_interface_contact_info::*;
|
pub use self::connection_interface_contact_info::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_contact_list;
|
mod connection_interface_contact_list;
|
||||||
pub use self::connection_interface_contact_list::*;
|
pub use self::connection_interface_contact_list::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_contacts;
|
mod connection_interface_contacts;
|
||||||
pub use self::connection_interface_contacts::*;
|
pub use self::connection_interface_contacts::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_forwarding;
|
mod connection_interface_forwarding;
|
||||||
pub use self::connection_interface_forwarding::*;
|
pub use self::connection_interface_forwarding::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_irc_command1;
|
mod connection_interface_irc_command1;
|
||||||
pub use self::connection_interface_irc_command1::*;
|
pub use self::connection_interface_irc_command1::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_keepalive;
|
mod connection_interface_keepalive;
|
||||||
pub use self::connection_interface_keepalive::*;
|
pub use self::connection_interface_keepalive::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_location;
|
mod connection_interface_location;
|
||||||
pub use self::connection_interface_location::*;
|
pub use self::connection_interface_location::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_mail_notification;
|
mod connection_interface_mail_notification;
|
||||||
pub use self::connection_interface_mail_notification::*;
|
pub use self::connection_interface_mail_notification::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_power_saving;
|
mod connection_interface_power_saving;
|
||||||
pub use self::connection_interface_power_saving::*;
|
pub use self::connection_interface_power_saving::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_presence;
|
mod connection_interface_presence;
|
||||||
pub use self::connection_interface_presence::*;
|
pub use self::connection_interface_presence::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_privacy;
|
mod connection_interface_privacy;
|
||||||
pub use self::connection_interface_privacy::*;
|
pub use self::connection_interface_privacy::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_renaming;
|
mod connection_interface_renaming;
|
||||||
pub use self::connection_interface_renaming::*;
|
pub use self::connection_interface_renaming::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_requests;
|
mod connection_interface_requests;
|
||||||
pub use self::connection_interface_requests::*;
|
pub use self::connection_interface_requests::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_resources;
|
mod connection_interface_resources;
|
||||||
pub use self::connection_interface_resources::*;
|
pub use self::connection_interface_resources::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_service_point;
|
mod connection_interface_service_point;
|
||||||
pub use self::connection_interface_service_point::*;
|
pub use self::connection_interface_service_point::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_sidecars1;
|
mod connection_interface_sidecars1;
|
||||||
pub use self::connection_interface_sidecars1::*;
|
pub use self::connection_interface_sidecars1::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_interface_simple_presence;
|
mod connection_interface_simple_presence;
|
||||||
pub use self::connection_interface_simple_presence::*;
|
pub use self::connection_interface_simple_presence::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_manager_interface_account_storage;
|
mod connection_manager_interface_account_storage;
|
||||||
pub use self::connection_manager_interface_account_storage::*;
|
pub use self::connection_manager_interface_account_storage::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection_manager;
|
mod connection_manager;
|
||||||
pub use self::connection_manager::*;
|
pub use self::connection_manager::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod connection;
|
mod connection;
|
||||||
pub use self::connection::*;
|
pub use self::connection::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod debug;
|
mod debug;
|
||||||
pub use self::debug::*;
|
pub use self::debug::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod errors;
|
mod errors;
|
||||||
pub use self::errors::*;
|
pub use self::errors::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod generic_types;
|
mod generic_types;
|
||||||
pub use self::generic_types::*;
|
pub use self::generic_types::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod media_session_handler;
|
mod media_session_handler;
|
||||||
pub use self::media_session_handler::*;
|
pub use self::media_session_handler::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod media_stream_handler;
|
mod media_stream_handler;
|
||||||
pub use self::media_stream_handler::*;
|
pub use self::media_stream_handler::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod properties_interface;
|
mod properties_interface;
|
||||||
pub use self::properties_interface::*;
|
pub use self::properties_interface::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod protocol_interface_addressing;
|
mod protocol_interface_addressing;
|
||||||
pub use self::protocol_interface_addressing::*;
|
pub use self::protocol_interface_addressing::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod protocol_interface_avatars;
|
mod protocol_interface_avatars;
|
||||||
pub use self::protocol_interface_avatars::*;
|
pub use self::protocol_interface_avatars::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod protocol_interface_presence;
|
mod protocol_interface_presence;
|
||||||
pub use self::protocol_interface_presence::*;
|
pub use self::protocol_interface_presence::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod protocol;
|
mod protocol;
|
||||||
pub use self::protocol::*;
|
pub use self::protocol::*;
|
||||||
|
|
||||||
|
#[allow(clippy::all)]
|
||||||
mod template;
|
mod template;
|
||||||
pub use self::template::*;
|
pub use self::template::*;
|
||||||
|
8
telepathy-padfoot.service
Normal file
8
telepathy-padfoot.service
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Telepathy Delta Chat service
|
||||||
|
Documentation=man:telepathy-padfoot(8)
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=dbus
|
||||||
|
BusName=org.freedesktop.Telepathy.ConnectionManager.padfoot
|
||||||
|
ExecStart=/usr/lib/telepathy/telepathy-padfoot
|
Reference in New Issue
Block a user