Compare commits
16 Commits
15174ea03f
...
main
Author | SHA1 | Date | |
---|---|---|---|
7707242d10 | |||
bd76603c54 | |||
9e764d72a1 | |||
b9faad742b | |||
370f5076a1 | |||
373311e826 | |||
67a8715a25 | |||
c773146b26 | |||
7b1b8bdc83 | |||
d06badfc96 | |||
8dbd023718 | |||
7cee6348fd | |||
14aa639a4b | |||
efe97a33c4 | |||
d77d04e9b1 | |||
db7ecc6d98 |
812
Cargo.lock
generated
812
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,9 @@ license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
async-std = "1.6"
|
||||
dbus = "0.8"
|
||||
deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="1.33.0" }
|
||||
deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="1.34.0" }
|
||||
directories = "2.0"
|
||||
rand = "0.7"
|
||||
|
||||
|
89
README.md
89
README.md
@@ -33,10 +33,10 @@ Here's where we're at right now:
|
||||
- [x] Appear as online in Empathy
|
||||
- [x] Disconnect!
|
||||
- [ ] Set up an account manually
|
||||
- [ ] Contacts handling
|
||||
- [~] Contacts handling
|
||||
- [x] Text messages
|
||||
- [ ] Multimedia messages
|
||||
- [ ] Setup messages
|
||||
- [~] Setup messages
|
||||
- [ ] Import/Export
|
||||
- [ ] Group chats
|
||||
- [ ] Geolocation messages
|
||||
@@ -95,6 +95,63 @@ This creates a 32-bit executable at `target/i686-unknown-linux-gnu/release/telep
|
||||
|
||||
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.
|
||||
@@ -103,12 +160,36 @@ 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
|
||||
[telepathy interface specs](https://github.com/TelepathyIM/telepathy-spec) into
|
||||
the executable code in `src/telepathy`. This is checked in, but can be
|
||||
@@ -123,4 +204,4 @@ $ ./scripts/dbus-codegen
|
||||
|
||||
`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`
|
||||
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
@@ -5,14 +5,14 @@ BusName=org.freedesktop.Telepathy.ConnectionManager.padfoot
|
||||
ObjectPath=/org/freedesktop/Telepathy/ConnectionManager/padfoot
|
||||
|
||||
[Protocol delta]
|
||||
Interfaces=org.freedesktop.Telepathy.Protocol.Interface.Presence;
|
||||
param-account=s required
|
||||
param-password=s required secret
|
||||
param-bcc-self=b
|
||||
status-available=2 settable
|
||||
status-offline = 1 settable
|
||||
|
||||
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
|
||||
Icon=im-delta
|
||||
Interfaces=org.freedesktop.Telepathy.Protocol;org.freedesktop.Telepathy.Protocol.Interface.Presence;
|
||||
|
@@ -11,10 +11,11 @@ pub use type_text::*;
|
||||
use crate::padfoot::{var_bool, var_str, var_str_vec, var_u32, DbusAction, VarArg};
|
||||
use crate::telepathy;
|
||||
|
||||
use async_std::task::block_on;
|
||||
use deltachat as dc;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{mpsc, Arc, RwLock};
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
type Result<T> = std::result::Result<T, dbus::tree::MethodErr>;
|
||||
|
||||
@@ -35,7 +36,7 @@ pub const HANDLE_TYPE_GROUP: HandleType = 4; // Deprecated
|
||||
pub struct Channel {
|
||||
actq: mpsc::Sender<DbusAction>,
|
||||
chat_id: dc::chat::ChatId,
|
||||
ctx: Arc<RwLock<dc::context::Context>>,
|
||||
ctx: Arc<dc::context::Context>,
|
||||
initiator_handle: u32,
|
||||
path: dbus::Path<'static>,
|
||||
requested: bool,
|
||||
@@ -51,7 +52,7 @@ impl Channel {
|
||||
pub fn new(
|
||||
actq: mpsc::Sender<DbusAction>,
|
||||
chat_id: dc::chat::ChatId,
|
||||
ctx: Arc<RwLock<dc::context::Context>>,
|
||||
ctx: Arc<dc::context::Context>,
|
||||
initiator_handle: u32,
|
||||
path: dbus::Path<'static>,
|
||||
requested: bool,
|
||||
@@ -79,6 +80,14 @@ impl Channel {
|
||||
"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),
|
||||
@@ -118,15 +127,15 @@ impl Channel {
|
||||
}
|
||||
|
||||
pub fn target_contact(&self) -> Option<dc::contact::Contact> {
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
|
||||
dc::contact::Contact::get_by_id(&ctx, self.handle()).ok()
|
||||
block_on(dc::contact::Contact::get_by_id(&self.ctx, self.handle())).ok()
|
||||
}
|
||||
|
||||
pub fn initiator_contact(&self) -> Option<dc::contact::Contact> {
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
|
||||
dc::contact::Contact::get_by_id(&ctx, self.initiator_handle).ok() // FIXME: this will be wrong for outbound channels
|
||||
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 {
|
||||
@@ -154,4 +163,35 @@ impl Channel {
|
||||
.add(messages_iface)
|
||||
.add(type_text_iface)
|
||||
}
|
||||
|
||||
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,6 +1,7 @@
|
||||
use crate::padfoot::{convert_msg, DbusAction, VarArg};
|
||||
use crate::telepathy;
|
||||
|
||||
use async_std::task::block_on;
|
||||
use dbus::message::SignalArgs;
|
||||
use dbus::tree::MethodErr;
|
||||
use dc::constants::Viewtype;
|
||||
@@ -34,11 +35,18 @@ impl telepathy::ChannelInterfaceMessages for Channel {
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
let msg_id = match dc::chat::send_msg(&ctx, self.chat_id, &mut delta_msg) {
|
||||
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);
|
||||
@@ -47,7 +55,7 @@ impl telepathy::ChannelInterfaceMessages for Channel {
|
||||
};
|
||||
|
||||
let token = format!("{}", msg_id.to_u32());
|
||||
let dbus_parts = convert_msg(&delta_msg);
|
||||
let dbus_parts = convert_msg(blobdir, &delta_msg).map_err(|_| MethodErr::no_arg())?;
|
||||
|
||||
let messages_sig = telepathy::ChannelInterfaceMessagesMessageSent {
|
||||
content: dbus_parts,
|
||||
@@ -84,7 +92,7 @@ impl telepathy::ChannelInterfaceMessages for Channel {
|
||||
fn supported_content_types(&self) -> Result<Vec<String>> {
|
||||
println!("Channel::supported_content_types()");
|
||||
|
||||
Ok(vec!["text/plain".to_string()]) // TODO: image support
|
||||
Ok(vec!["*/*".to_string()])
|
||||
}
|
||||
|
||||
fn message_types(&self) -> Result<Vec<u32>> {
|
||||
@@ -104,13 +112,17 @@ impl telepathy::ChannelInterfaceMessages for Channel {
|
||||
println!("Channel::pending_messages()");
|
||||
|
||||
let mut out = Vec::<Vec<HashMap<String, VarArg>>>::new();
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
let ctx = &self.ctx;
|
||||
let blobdir = ctx.get_blobdir();
|
||||
|
||||
for msg_id in dc::chat::get_chat_msgs(&ctx, self.chat_id, 0, None) {
|
||||
if let Ok(msg) = dc::message::Message::load_from_db(&ctx, msg_id) {
|
||||
println!(" A message: {:?}", msg);
|
||||
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 => out.push(convert_msg(&msg)),
|
||||
MessageState::InFresh | MessageState::InNoticed => {
|
||||
println!(" A message: {:?}", msg);
|
||||
let parts = convert_msg(blobdir, &msg).map_err(|_| MethodErr::no_arg())?;
|
||||
out.push(parts);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ use crate::padfoot::DbusAction;
|
||||
use crate::telepathy;
|
||||
use crate::telepathy::ChannelInterfaceMessages;
|
||||
|
||||
use async_std::task::block_on;
|
||||
use dbus::message::SignalArgs;
|
||||
use dbus::tree::MethodErr;
|
||||
use dc::message::MsgId;
|
||||
@@ -31,14 +32,13 @@ impl telepathy::ChannelTypeText for Channel {
|
||||
fn acknowledge_pending_messages(&self, ids: Vec<u32>) -> Result<()> {
|
||||
println!("Channel::acknowledge_pending_messages({:?})", ids);
|
||||
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
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 = dc::message::markseen_msgs(&ctx, &msg_ids);
|
||||
let result = block_on(dc::message::markseen_msgs(&self.ctx, msg_ids));
|
||||
if result {
|
||||
println!("OK!");
|
||||
|
||||
|
@@ -23,6 +23,7 @@ pub use self::simple_presence::*;
|
||||
use crate::padfoot::{convert_msg, Channel, VarArg};
|
||||
use crate::telepathy;
|
||||
|
||||
use async_std::task::block_on;
|
||||
use dbus::blocking::{stdintf::org_freedesktop_dbus::RequestNameReply, LocalConnection};
|
||||
use dbus::channel::{MatchingReceiver, Sender};
|
||||
use dbus::message::SignalArgs;
|
||||
@@ -35,6 +36,7 @@ use deltachat as dc;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::{mpsc, Arc, Mutex, RwLock};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
pub const CONN_BUS_NAME: &str = "org.freedesktop.Telepathy.Connection.padfoot.delta";
|
||||
@@ -66,7 +68,7 @@ pub struct Connection {
|
||||
// Owned by the CM. Remove ourselves from this when done
|
||||
conns: Arc<Mutex<HashSet<dbus::Path<'static>>>>,
|
||||
|
||||
ctx: Arc<RwLock<Context>>,
|
||||
ctx: Arc<Context>, // Delta contexts are threadsafe
|
||||
settings: ConnSettings,
|
||||
state: Arc<RwLock<ConnState>>,
|
||||
}
|
||||
@@ -75,6 +77,7 @@ pub struct Connection {
|
||||
pub struct ConnSettings {
|
||||
account: String,
|
||||
password: String,
|
||||
bcc_self: bool,
|
||||
id: String,
|
||||
}
|
||||
|
||||
@@ -100,9 +103,24 @@ impl ConnSettings {
|
||||
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 {
|
||||
account,
|
||||
password,
|
||||
bcc_self,
|
||||
id,
|
||||
})
|
||||
}
|
||||
@@ -125,9 +143,11 @@ impl Connection {
|
||||
settings: ConnSettings,
|
||||
conns: Arc<Mutex<HashSet<dbus::Path<'static>>>>,
|
||||
) -> Result<(Self, mpsc::Receiver<DbusAction>), MethodErr> {
|
||||
let mut dbfile = directories::ProjectDirs::from("gs", "ur", "telepathy-padfoot")
|
||||
.ok_or_else(MethodErr::no_arg)
|
||||
.and_then(|p| Ok(p.data_local_dir().to_path_buf()))?;
|
||||
let proj_dir = directories::ProjectDirs::from("gs", "ur", "telepathy-padfoot")
|
||||
.ok_or_else(MethodErr::no_arg)?;
|
||||
|
||||
let mut dbfile = async_std::path::PathBuf::new();
|
||||
dbfile.push(proj_dir.data_local_dir().to_str().unwrap());
|
||||
|
||||
dbfile.push(settings.id());
|
||||
dbfile.push("db.sqlite3");
|
||||
@@ -135,98 +155,104 @@ impl Connection {
|
||||
let (q_s, q_r) = mpsc::channel::<DbusAction>();
|
||||
let id = settings.id();
|
||||
|
||||
// The closure is shared between several different threads in delta, and
|
||||
// we can't Send *or* clone the mpsc sender across them, so just wrap it
|
||||
// in a mutex for now
|
||||
let queue = Mutex::new(q_s.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);
|
||||
}
|
||||
Event::MsgsChanged { chat_id, msg_id } => {
|
||||
println!(
|
||||
"Connection<{}>: Messages changed for {}: {}",
|
||||
id, chat_id, msg_id
|
||||
);
|
||||
queue
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send(DbusAction::IncomingMessage(chat_id, msg_id))
|
||||
.unwrap();
|
||||
}
|
||||
Event::IncomingMsg { chat_id, msg_id } => {
|
||||
println!(
|
||||
"Connection<{}>: Incoming message for {}: {}",
|
||||
id, chat_id, msg_id
|
||||
);
|
||||
queue
|
||||
.lock()
|
||||
.unwrap()
|
||||
.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),
|
||||
};
|
||||
};
|
||||
|
||||
let ctx =
|
||||
Context::new(Box::new(f), "telepathy-padfoot".to_string(), dbfile).map_err(|e| {
|
||||
let ctx = Arc::new(
|
||||
block_on(Context::new("telepathy-padfoot".to_string(), dbfile)).map_err(|e| {
|
||||
println!(
|
||||
"Connection<{}>::new(): couldn't get delta context: {}",
|
||||
settings.id(),
|
||||
e
|
||||
);
|
||||
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())?;
|
||||
ctx.set_config(Config::MailPw, Some(&settings.password))
|
||||
.map_err(|_e| MethodErr::no_arg())?;
|
||||
ctx.set_config(Config::SentboxWatch, Some(&"Sent"))
|
||||
block_on(ctx.set_config(Config::MailPw, Some(&settings.password)))
|
||||
.map_err(|_e| MethodErr::no_arg())?;
|
||||
|
||||
if !ctx.is_configured() {
|
||||
ctx.configure();
|
||||
if settings.bcc_self {
|
||||
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 {
|
||||
conns,
|
||||
settings,
|
||||
actq: q_s,
|
||||
channels: Arc::new(RwLock::new(
|
||||
HashMap::<dbus::Path<'static>, Arc<Channel>>::new(),
|
||||
)),
|
||||
ctx: Arc::new(RwLock::new(ctx)),
|
||||
conns,
|
||||
ctx,
|
||||
settings,
|
||||
state: Arc::new(RwLock::new(ConnState::Initial)),
|
||||
},
|
||||
q_r,
|
||||
@@ -377,73 +403,86 @@ impl Connection {
|
||||
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!("Message {} received for {}...", msg_id, chan_path);
|
||||
|
||||
// Emit new message signals for the channel
|
||||
if let Ok(msg) = dc::message::Message::load_from_db(
|
||||
&ctx.clone().read().unwrap(),
|
||||
msg_id,
|
||||
) {
|
||||
// Ignore messages that are self-originated.
|
||||
// FIXME: special-case self-chats
|
||||
if msg.get_from_id() == dc::constants::DC_CONTACT_ID_SELF {
|
||||
println!("from ourselves, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
let parts = convert_msg(&msg);
|
||||
|
||||
let sig = telepathy::ChannelInterfaceMessagesMessageReceived {
|
||||
message: parts,
|
||||
}
|
||||
.to_emit_message(&chan_path);
|
||||
|
||||
actq.send(DbusAction::Signal(sig)).unwrap();
|
||||
|
||||
println!("OK!");
|
||||
} else {
|
||||
println!(" couldn't find message, not sending signal");
|
||||
}
|
||||
// FIXME: We MUST also send a Text.Received signal
|
||||
} else {
|
||||
if !chans.contains_key(&chan_path) {
|
||||
print!("Channel for {} doesn't exist yet, creating it...", chat_id);
|
||||
|
||||
let contacts =
|
||||
dc::chat::get_chat_contacts(&ctx.clone().read().unwrap(), chat_id);
|
||||
if contacts.len() != 1 {
|
||||
let contacts = block_on(dc::chat::get_chat_contacts(&ctx, chat_id));
|
||||
if contacts.len() > 1 {
|
||||
println!("...{} contacts in chat, ignoring!", contacts.len());
|
||||
continue;
|
||||
}
|
||||
|
||||
let handle = contacts.first().unwrap();
|
||||
// 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
|
||||
*handle, // initiator is the remote contact
|
||||
chan_path,
|
||||
false, // FIXME: this needs to handle requested channels
|
||||
dc::constants::DC_CONTACT_ID_SELF, // target is us
|
||||
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_rc = ctx.clone();
|
||||
let ctx = ctx_rc.read().unwrap();
|
||||
let ctx = ctx.clone();
|
||||
|
||||
for msg_id in dc::context::Context::get_fresh_msgs(&ctx) {
|
||||
for msg_id in block_on(dc::context::Context::get_fresh_msgs(&ctx)) {
|
||||
println!(" FRESH MESSAGE: {}", msg_id);
|
||||
match dc::message::Message::load_from_db(&ctx, msg_id) {
|
||||
match block_on(dc::message::Message::load_from_db(&ctx, msg_id)) {
|
||||
Ok(msg) => {
|
||||
actq.send(DbusAction::IncomingMessage(
|
||||
msg.get_chat_id(),
|
||||
|
@@ -1,6 +1,7 @@
|
||||
use crate::telepathy;
|
||||
use crate::telepathy::{ConnectionInterfaceContacts, ConnectionInterfaceRequests}; // Non-deprecated channel methods
|
||||
|
||||
use async_std::task::block_on;
|
||||
use dbus::message::SignalArgs;
|
||||
use dbus::tree::MethodErr;
|
||||
use dc::contact::Contact;
|
||||
@@ -57,64 +58,12 @@ impl telepathy::Connection for Connection {
|
||||
fn connect(&self) -> Result<()> {
|
||||
println!("Connection<{}>::connect()", self.id());
|
||||
|
||||
let inbox_ctx = self.ctx.clone();
|
||||
let state = self.state.clone();
|
||||
let id = self.id();
|
||||
let _inbox_thread = thread::spawn(move || {
|
||||
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());
|
||||
let io_ctx = self.ctx.clone();
|
||||
let io_id = self.id();
|
||||
let _io_thread = thread::spawn(move || {
|
||||
block_on(io_ctx.start_io());
|
||||
|
||||
if *state.read().unwrap() != ConnState::Disconnected {
|
||||
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);
|
||||
println!("Connection<{}>::connect(): I/O thread exited", io_id);
|
||||
});
|
||||
|
||||
// Just pretend to be connected all the time for now. Tracking IMAP+SMTP
|
||||
@@ -122,7 +71,7 @@ impl telepathy::Connection for Connection {
|
||||
let state = self.state.clone();
|
||||
let mut w = state.write().unwrap();
|
||||
*w = ConnState::Connected;
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
let ctx = self.ctx.clone();
|
||||
|
||||
// Emit a StatusChanged signal for the benefit of others, but the caller
|
||||
// learns from our RPC response
|
||||
@@ -136,13 +85,13 @@ impl telepathy::Connection for Connection {
|
||||
self.actq.send(DbusAction::FreshMessages).unwrap();
|
||||
|
||||
// If we can, emit signals on connect about the contact list
|
||||
if let Ok(handles) = Contact::get_all(
|
||||
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 {
|
||||
@@ -170,17 +119,12 @@ impl telepathy::Connection for Connection {
|
||||
|
||||
fn disconnect(&self) -> Result<()> {
|
||||
println!("Connection<{}>::disconnect()", self.id());
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
block_on(self.ctx.stop_io());
|
||||
|
||||
let state = self.state.clone();
|
||||
let mut w = state.write().unwrap();
|
||||
*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
|
||||
// connection from the active list
|
||||
|
||||
@@ -330,7 +274,7 @@ impl telepathy::Connection for Connection {
|
||||
|
||||
match handle_type {
|
||||
crate::padfoot::HANDLE_TYPE_CONTACT => {
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
let ctx = &self.ctx;
|
||||
let mut out = Vec::<u32>::new();
|
||||
|
||||
// Identifiers is a list of email addresses. These can be
|
||||
@@ -340,12 +284,16 @@ impl telepathy::Connection for Connection {
|
||||
// FIXME: will it be faster to get all and filter?
|
||||
|
||||
for addr in identifiers {
|
||||
let id = Contact::lookup_id_by_addr(&ctx, addr, dc::contact::Origin::Unknown);
|
||||
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 Contact::create(&ctx, &addr, &addr) {
|
||||
match block_on(Contact::create(ctx, &addr, &addr)) {
|
||||
Ok(new_id) => out.push(new_id),
|
||||
Err(e) => {
|
||||
println!("Failed to add contact {}: {}", addr, e);
|
||||
@@ -384,10 +332,10 @@ impl telepathy::Connection for Connection {
|
||||
fn self_id(&self) -> Result<String> {
|
||||
println!("Connection<{}>::self_id()", self.id());
|
||||
|
||||
let contact = match dc::contact::Contact::get_by_id(
|
||||
&self.ctx.read().unwrap(),
|
||||
let contact = match block_on(dc::contact::Contact::get_by_id(
|
||||
&self.ctx,
|
||||
dc::constants::DC_CONTACT_ID_SELF,
|
||||
) {
|
||||
)) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
println!(" err: {}", e);
|
||||
|
@@ -1,9 +1,11 @@
|
||||
use crate::padfoot::VarArg;
|
||||
use crate::telepathy;
|
||||
|
||||
use async_std::task::block_on;
|
||||
use dbus::tree::MethodErr;
|
||||
use deltachat::constants::DC_GCL_ADD_SELF;
|
||||
use deltachat::contact::Contact;
|
||||
use dc::constants::DC_GCL_ADD_SELF;
|
||||
use dc::contact::Contact;
|
||||
use deltachat as dc;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use telepathy::ConnectionInterfaceContacts; // for get_contact_attributes
|
||||
@@ -30,8 +32,11 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
|
||||
hold
|
||||
);
|
||||
|
||||
let ctx = &self.ctx.read().unwrap();
|
||||
let ids = match Contact::get_all(ctx, DC_GCL_ADD_SELF.try_into().unwrap(), None::<String>) {
|
||||
let ids = match block_on(Contact::get_all(
|
||||
&self.ctx,
|
||||
DC_GCL_ADD_SELF.try_into().unwrap(),
|
||||
None::<String>,
|
||||
)) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
println!(" err: {}", e);
|
||||
@@ -63,6 +68,17 @@ impl telepathy::ConnectionInterfaceContactList for Connection {
|
||||
}
|
||||
fn remove_contacts(&self, contacts: Vec<u32>) -> Result<(), MethodErr> {
|
||||
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(())
|
||||
}
|
||||
fn unsubscribe(&self, contacts: Vec<u32>) -> Result<(), MethodErr> {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
use crate::padfoot::{var_str, var_u32, VarArg};
|
||||
use crate::telepathy;
|
||||
|
||||
use async_std::task::block_on;
|
||||
use dbus::tree::MethodErr;
|
||||
use deltachat::contact::{Contact, Origin};
|
||||
use std::collections::HashMap;
|
||||
@@ -31,7 +32,7 @@ impl telepathy::ConnectionInterfaceContacts for Connection {
|
||||
let mut out = HashMap::<u32, HashMap<String, VarArg>>::new();
|
||||
for id in handles.iter() {
|
||||
// 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,
|
||||
Err(_e) => continue, // Invalid IDs are silently ignored
|
||||
};
|
||||
@@ -77,10 +78,11 @@ impl telepathy::ConnectionInterfaceContacts for Connection {
|
||||
interfaces
|
||||
);
|
||||
|
||||
let id = {
|
||||
let ctx = &self.ctx.read().unwrap();
|
||||
Contact::lookup_id_by_addr(ctx, identifier, Origin::Unknown)
|
||||
};
|
||||
let id = block_on(Contact::lookup_id_by_addr(
|
||||
&self.ctx,
|
||||
identifier,
|
||||
Origin::Unknown,
|
||||
));
|
||||
|
||||
if id == 0 {
|
||||
return Err(MethodErr::no_arg()); // FIXME: should be InvalidHandle
|
||||
|
@@ -1,6 +1,7 @@
|
||||
use crate::padfoot::{get_var_str, get_var_u32, requestables, Channel, DbusAction, VarArg};
|
||||
use crate::telepathy;
|
||||
|
||||
use async_std::task::block_on;
|
||||
use dbus::tree::MethodErr;
|
||||
use dc::contact::Contact;
|
||||
use deltachat as dc;
|
||||
@@ -77,10 +78,11 @@ impl telepathy::ConnectionInterfaceRequests for Connection {
|
||||
return Err(MethodErr::no_arg());
|
||||
};
|
||||
|
||||
let ctx = self.ctx.read().unwrap();
|
||||
|
||||
let target_handle =
|
||||
Contact::lookup_id_by_addr(&ctx, target_id.clone(), dc::contact::Origin::Unknown);
|
||||
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());
|
||||
@@ -96,12 +98,12 @@ impl telepathy::ConnectionInterfaceRequests for Connection {
|
||||
}
|
||||
|
||||
// Now we need to discover or create a chat id for the contact
|
||||
let chat_id = dc::chat::create_by_contact_id(&ctx, target_handle).unwrap();
|
||||
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
|
||||
dc::constants::DC_CONTACT_ID_SELF, // initiator is self in this case
|
||||
channel_path.clone(),
|
||||
true, // requested
|
||||
target_handle,
|
||||
|
@@ -1,37 +1,71 @@
|
||||
use crate::padfoot::{var_i64, var_str, var_u32, VarArg};
|
||||
use crate::padfoot::{var_bytearray, var_i64, var_str, var_u32, VarArg};
|
||||
|
||||
use deltachat::message::Message;
|
||||
use async_std::path::{Path, PathBuf};
|
||||
use dc::constants::Viewtype as Vt;
|
||||
use dc::message::Message;
|
||||
use deltachat as dc;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Turns a deltachat::message::Message into a Telepathy Message_Part_List
|
||||
pub fn convert_msg(msg: &Message) -> Vec<HashMap<String, VarArg>> {
|
||||
let mut parts = Vec::new();
|
||||
let mut props = HashMap::new();
|
||||
let msg_id = msg.get_id();
|
||||
type Part = HashMap<String, VarArg>;
|
||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
props.insert(
|
||||
// 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())),
|
||||
);
|
||||
props.insert("message-sent".to_string(), var_i64(msg.get_timestamp()));
|
||||
props.insert(
|
||||
out.insert("message-sent".to_string(), var_i64(msg.get_timestamp()));
|
||||
out.insert(
|
||||
"message-received".to_string(),
|
||||
var_i64(msg.get_received_timestamp()),
|
||||
);
|
||||
props.insert("message-sender".to_string(), var_u32(msg.get_from_id()));
|
||||
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?
|
||||
props.insert("message-type".to_string(), var_u32(0)); // normal
|
||||
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());
|
||||
|
||||
props.insert("pending-message-id".to_string(), var_u32(msg_id.to_u32()));
|
||||
|
||||
parts.push(props);
|
||||
out.insert("pending-message-id".to_string(), var_u32(msg_id.to_u32()));
|
||||
|
||||
// Don't need these
|
||||
// props.insert("interface", var_str());
|
||||
@@ -39,17 +73,66 @@ pub fn convert_msg(msg: &Message) -> Vec<HashMap<String, VarArg>> {
|
||||
// props.insert("silent", var_bool());
|
||||
// props.insert("rescued", var_bool());
|
||||
|
||||
if let Some(text) = msg.get_text() {
|
||||
let mut part = HashMap::new();
|
||||
part.insert(
|
||||
"content-type".to_string(),
|
||||
var_str("text/plain".to_string()),
|
||||
);
|
||||
part.insert("content".to_string(), var_str(text));
|
||||
parts.push(part);
|
||||
}
|
||||
|
||||
// TODO: other parts. Can a deltachat message have multiple parts?
|
||||
|
||||
parts
|
||||
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 dbus::tree::MethodErr;
|
||||
@@ -49,6 +49,7 @@ pub fn parameters() -> Vec<ParamSpec> {
|
||||
"s".to_string(),
|
||||
var_str("".to_string()),
|
||||
),
|
||||
("bcc-self".to_string(), 0, "b".to_string(), var_bool(false)),
|
||||
]
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,10 @@ 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 {
|
||||
var_arg(Box::new(item))
|
||||
}
|
||||
|
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