2020-05-10 23:09:58 +01:00
|
|
|
use crate::telepathy;
|
|
|
|
use dbus::blocking::{stdintf::org_freedesktop_dbus::RequestNameReply, LocalConnection};
|
2020-05-12 23:52:52 +01:00
|
|
|
use dbus::channel::Sender;
|
|
|
|
use dbus::message::SignalArgs;
|
2020-05-10 23:09:58 +01:00
|
|
|
use dbus::tree;
|
2020-05-12 01:25:48 +01:00
|
|
|
|
|
|
|
use dc::config::Config;
|
|
|
|
use dc::context::Context;
|
|
|
|
use dc::Event;
|
|
|
|
use deltachat as dc;
|
|
|
|
|
2020-05-12 23:52:52 +01:00
|
|
|
use std::collections::{HashMap, VecDeque};
|
|
|
|
use std::sync::{Arc, Mutex, RwLock};
|
2020-05-12 01:25:48 +01:00
|
|
|
use std::thread;
|
2020-05-10 23:09:58 +01:00
|
|
|
use std::time::Duration;
|
2020-05-10 19:04:14 +01:00
|
|
|
|
|
|
|
pub const CONN_BUS_NAME: &'static str = "org.freedesktop.Telepathy.Connection.padfoot.delta";
|
2020-05-10 23:09:58 +01:00
|
|
|
pub const CONN_OBJECT_PATH: &'static str = "/org/freedesktop/Telepathy/Connection/padfoot/delta";
|
2020-05-10 19:04:14 +01:00
|
|
|
|
2020-05-12 23:52:52 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
enum ConnState {
|
|
|
|
Initial,
|
|
|
|
Connected,
|
|
|
|
Disconnected,
|
|
|
|
}
|
|
|
|
|
2020-05-11 00:48:18 +01:00
|
|
|
#[derive(Debug)]
|
2020-05-10 19:04:14 +01:00
|
|
|
pub struct Connection {
|
2020-05-10 23:09:58 +01:00
|
|
|
id: String,
|
2020-05-12 01:25:48 +01:00
|
|
|
ctx: Arc<RwLock<Context>>,
|
2020-05-12 22:28:36 +01:00
|
|
|
|
2020-05-12 23:52:52 +01:00
|
|
|
state: Arc<RwLock<ConnState>>,
|
|
|
|
|
|
|
|
// Used for sending out messages
|
|
|
|
msgq: Arc<Mutex<VecDeque<dbus::Message>>>,
|
2020-05-10 19:04:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Connection {
|
2020-05-12 01:25:48 +01:00
|
|
|
pub fn new(params: HashMap<&str, super::Variant>) -> Result<Self, dbus::tree::MethodErr> {
|
|
|
|
let err = Err(dbus::tree::MethodErr::no_arg());
|
|
|
|
|
|
|
|
let acct = match params.get("account") {
|
|
|
|
Some(variant) => match variant.0.as_str() {
|
|
|
|
Some(str) => str.to_string(),
|
|
|
|
None => return err,
|
|
|
|
},
|
|
|
|
None => return err,
|
|
|
|
};
|
|
|
|
|
|
|
|
let id = escape(acct.to_owned());
|
|
|
|
|
|
|
|
let password = match params.get("password") {
|
|
|
|
Some(variant) => match variant.0.as_str() {
|
|
|
|
Some(str) => str.to_string(),
|
|
|
|
None => return err,
|
|
|
|
},
|
|
|
|
None => return err,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut dbfile = directories::ProjectDirs::from("gs", "ur", "telepathy-padfoot")
|
|
|
|
.ok_or_else(|| tree::MethodErr::no_arg())
|
|
|
|
.and_then(|p| Ok(p.data_local_dir().to_path_buf()))?;
|
|
|
|
|
|
|
|
dbfile.push(&id);
|
|
|
|
dbfile.push("db.sqlite3");
|
|
|
|
|
|
|
|
// FIXME: how to give it access to the connection (initialized later)?
|
2020-05-12 23:52:52 +01:00
|
|
|
let msgq = Arc::new(Mutex::new(VecDeque::<dbus::Message>::new()));
|
|
|
|
|
2020-05-12 01:25:48 +01:00
|
|
|
let id2 = id.clone();
|
2020-05-12 23:52:52 +01:00
|
|
|
let msgq2 = msgq.clone();
|
2020-05-12 01:25:48 +01:00
|
|
|
let f = move |_c: &Context, e: Event| {
|
|
|
|
match e {
|
|
|
|
Event::Info(msg) => println!("Connection<{}>: INFO: {}", id2, msg),
|
|
|
|
Event::Warning(msg) => println!("Connection<{}>: WARN : {}", id2, msg),
|
|
|
|
Event::Error(msg) | Event::ErrorNetwork(msg) | Event::ErrorSelfNotInGroup(msg) => {
|
|
|
|
println!("Connection<{}>: ERR : {}", id2, msg)
|
|
|
|
}
|
2020-05-12 23:52:52 +01:00
|
|
|
Event::ConfigureProgress(progress) => {
|
|
|
|
println!("Connection<{}>: Configuration progress: {}", id2, progress)
|
|
|
|
}
|
|
|
|
Event::ImapConnected(msg) | Event::SmtpConnected(msg) => {
|
|
|
|
println!("Connection<{}>: Network: {}", id2, 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
|
|
|
|
*/
|
2020-05-12 01:25:48 +01:00
|
|
|
_ => println!("Connection<{}>: unhandled event received: {:?}", id2, e),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
let ctx =
|
|
|
|
Context::new(Box::new(f), "telepathy-padfoot".to_string(), dbfile).map_err(|e| {
|
|
|
|
println!("Connection<{}>: couldn't get delta context: {}", id, e);
|
|
|
|
tree::MethodErr::no_arg() // FIXME: better error handling
|
|
|
|
})?;
|
|
|
|
|
|
|
|
ctx.set_config(Config::Addr, Some(&acct))
|
|
|
|
.map_err(|_e| tree::MethodErr::no_arg())?;
|
|
|
|
ctx.set_config(Config::MailPw, Some(&password))
|
|
|
|
.map_err(|_e| tree::MethodErr::no_arg())?;
|
|
|
|
ctx.set_config(Config::SentboxWatch, Some(&"Sent"))
|
|
|
|
.map_err(|_e| tree::MethodErr::no_arg())?;
|
|
|
|
|
|
|
|
if !ctx.is_configured() {
|
|
|
|
ctx.configure();
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Connection {
|
|
|
|
id: id,
|
|
|
|
ctx: Arc::new(RwLock::new(ctx)),
|
2020-05-12 23:52:52 +01:00
|
|
|
state: Arc::new(RwLock::new(ConnState::Initial)),
|
|
|
|
msgq: msgq.clone(),
|
2020-05-12 01:25:48 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-05-10 23:09:58 +01:00
|
|
|
// This is run inside its own thread
|
|
|
|
//
|
|
|
|
// FIXME: running several +process+ loops sure is convenient, but it also
|
|
|
|
// seems inefficient...
|
|
|
|
pub fn run(self) {
|
|
|
|
let bus = self.bus();
|
|
|
|
let path = self.path();
|
2020-05-12 23:52:52 +01:00
|
|
|
let state = self.state.clone();
|
|
|
|
let msgq = self.msgq.clone();
|
|
|
|
let id = self.id.clone();
|
2020-05-11 00:48:18 +01:00
|
|
|
let c_rc = std::rc::Rc::new(self);
|
|
|
|
|
|
|
|
let f = tree::Factory::new_fn::<()>();
|
|
|
|
let iface = telepathy::connection_server(&f, (), move |_| c_rc.clone());
|
2020-05-10 19:04:14 +01:00
|
|
|
|
2020-05-10 23:09:58 +01:00
|
|
|
let mut tree = f.tree(());
|
|
|
|
tree = tree.add(f.object_path(path, ()).introspectable().add(iface));
|
2020-05-10 19:04:14 +01:00
|
|
|
|
2020-05-10 23:09:58 +01:00
|
|
|
tree = tree.add(f.object_path("/", ()).introspectable());
|
|
|
|
|
|
|
|
// Setup DBus connection
|
|
|
|
let mut c = match LocalConnection::new_session() {
|
|
|
|
Ok(c) => c,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Failed to establish DBUS session for {}: {}", bus, e);
|
|
|
|
return; // Leave early
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
tree.start_receive(&c);
|
|
|
|
|
|
|
|
match c.request_name(bus.clone(), false, false, true) {
|
|
|
|
Ok(RequestNameReply::Exists) => {
|
|
|
|
println!("Another process is already registered on {}", bus);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
println!("Failed to register {}: {}", bus, e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_ => println!("{} listening on {}", c.unique_name(), bus), // All other responses we can get are a success
|
|
|
|
};
|
|
|
|
|
2020-05-12 01:25:48 +01:00
|
|
|
// Set up delta jobs last in case registering to DBUS fails
|
|
|
|
// "Borrowed" from https://github.com/deltachat/deltachat-core-rust/blob/master/examples/simple.rs
|
2020-05-12 23:52:52 +01:00
|
|
|
while *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 22:28:36 +01:00
|
|
|
match c.process(Duration::from_millis(100)) {
|
|
|
|
Err(e) => {
|
|
|
|
println!("Error processing: {}", e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2020-05-12 23:52:52 +01:00
|
|
|
|
|
|
|
// Spend a bit of time sending any outgoing messages - signals, mostly
|
|
|
|
loop {
|
|
|
|
let msg = match msgq.lock().unwrap().pop_front() {
|
|
|
|
Some(msg) => msg,
|
|
|
|
None => break,
|
|
|
|
};
|
|
|
|
|
|
|
|
print!("Connection<{}>: Sending message...", id);
|
|
|
|
match c.send(msg) {
|
|
|
|
Err(e) => println!("error! {:?}", e),
|
|
|
|
_ => println!("OK!"),
|
|
|
|
}
|
|
|
|
}
|
2020-05-12 22:28:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: clean up, emit disconnected signal. Join on threads started in
|
|
|
|
// connect() ?
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn bus(&self) -> String {
|
|
|
|
CONN_BUS_NAME.to_owned() + "." + &self.id
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn path(&self) -> String {
|
|
|
|
CONN_OBJECT_PATH.to_owned() + "/" + &self.id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsRef<dyn telepathy::Connection + 'static> for std::rc::Rc<Connection> {
|
|
|
|
fn as_ref(&self) -> &(dyn telepathy::Connection + 'static) {
|
|
|
|
&**self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl telepathy::Connection for Connection {
|
|
|
|
// In connect(), we start the threads that drive the deltachat context
|
|
|
|
fn connect(&self) -> Result<(), tree::MethodErr> {
|
|
|
|
println!("Connection<{}>::connect()", self.id);
|
2020-05-12 01:25:48 +01:00
|
|
|
|
2020-05-12 22:28:36 +01:00
|
|
|
let inbox_ctx = self.ctx.clone();
|
2020-05-12 23:52:52 +01:00
|
|
|
let state = self.state.clone();
|
2020-05-12 22:28:36 +01:00
|
|
|
let _inbox_thread = thread::spawn(move || {
|
2020-05-12 23:52:52 +01:00
|
|
|
while *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_inbox_jobs(&inbox_ctx.read().unwrap());
|
2020-05-12 23:52:52 +01:00
|
|
|
if *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_inbox_fetch(&inbox_ctx.read().unwrap());
|
|
|
|
|
2020-05-12 23:52:52 +01:00
|
|
|
if *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_inbox_idle(&inbox_ctx.read().unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-05-12 22:28:36 +01:00
|
|
|
let smtp_ctx = self.ctx.clone();
|
2020-05-12 23:52:52 +01:00
|
|
|
let state = self.state.clone();
|
2020-05-12 22:28:36 +01:00
|
|
|
let _smtp_thread = thread::spawn(move || {
|
2020-05-12 23:52:52 +01:00
|
|
|
while *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_smtp_jobs(&smtp_ctx.read().unwrap());
|
2020-05-12 23:52:52 +01:00
|
|
|
if *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_smtp_idle(&smtp_ctx.read().unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-05-12 22:28:36 +01:00
|
|
|
let mvbox_ctx = self.ctx.clone();
|
2020-05-12 23:52:52 +01:00
|
|
|
let state = self.state.clone();
|
2020-05-12 22:28:36 +01:00
|
|
|
let _mvbox_thread = thread::spawn(move || {
|
2020-05-12 23:52:52 +01:00
|
|
|
while *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_mvbox_fetch(&mvbox_ctx.read().unwrap());
|
2020-05-12 23:52:52 +01:00
|
|
|
if *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_mvbox_idle(&mvbox_ctx.read().unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-05-12 22:28:36 +01:00
|
|
|
let sentbox_ctx = self.ctx.clone();
|
2020-05-12 23:52:52 +01:00
|
|
|
let state = self.state.clone();
|
2020-05-12 22:28:36 +01:00
|
|
|
let _sentbox_thread = thread::spawn(move || {
|
2020-05-12 23:52:52 +01:00
|
|
|
while *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_sentbox_fetch(&sentbox_ctx.read().unwrap());
|
2020-05-12 23:52:52 +01:00
|
|
|
if *state.read().unwrap() != ConnState::Disconnected {
|
2020-05-12 01:25:48 +01:00
|
|
|
dc::job::perform_sentbox_idle(&sentbox_ctx.read().unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-05-12 23:52:52 +01:00
|
|
|
// Just pretend to be connected all the time for now. Tracking IMAP+SMTP
|
|
|
|
// state is a pain
|
|
|
|
let state = self.state.clone();
|
|
|
|
let mut w = state.write().unwrap();
|
|
|
|
*w = ConnState::Connected;
|
|
|
|
|
|
|
|
// Emit a NewConnection signal for the benefit of others, but the caller
|
|
|
|
// learns from our RPC response
|
|
|
|
let sig = telepathy::ConnectionStatusChanged {
|
|
|
|
status: 0, // Connected
|
|
|
|
reason: 1, // Requested
|
|
|
|
};
|
|
|
|
|
|
|
|
let dbus_conn_path = dbus::strings::Path::new(CONN_OBJECT_PATH.to_string())
|
|
|
|
.expect("Object path should meet DBUS requirements");
|
|
|
|
|
|
|
|
self.msgq
|
|
|
|
.clone()
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.push_back(sig.to_emit_message(&dbus_conn_path));
|
|
|
|
|
2020-05-12 22:28:36 +01:00
|
|
|
Ok(())
|
2020-05-10 23:09:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn disconnect(&self) -> Result<(), tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!("Connection<{}>::disconnect()", self.id);
|
2020-05-12 23:52:52 +01:00
|
|
|
|
|
|
|
let state = self.state.clone();
|
|
|
|
let mut w = state.write().unwrap();
|
|
|
|
*w = ConnState::Disconnected;
|
|
|
|
|
|
|
|
// FIXME: we need to signal to the CM that they should remove the
|
|
|
|
// connection from the active list
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn interfaces(&self) -> Result<Vec<String>, tree::MethodErr> {
|
|
|
|
println!("Connection<{}>::interfaces()", self.id);
|
|
|
|
|
|
|
|
self.get_interfaces()
|
2020-05-10 23:09:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_interfaces(&self) -> Result<Vec<String>, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!("Connection<{}>::get_interfaces()", self.id);
|
2020-05-12 23:52:52 +01:00
|
|
|
|
|
|
|
Ok(vec![
|
|
|
|
"org.freedesktop.Telepathy.Connection.Interface.Contacts".to_string(),
|
|
|
|
"org.freedesktop.Telepathy.Connection.Interface.Requests".to_string(),
|
|
|
|
])
|
2020-05-10 23:09:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_protocol(&self) -> Result<String, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!("Connection<{}>::get_protocol()", self.id);
|
2020-05-12 23:52:52 +01:00
|
|
|
|
2020-05-10 23:09:58 +01:00
|
|
|
Ok(super::PROTO_NAME.to_string())
|
|
|
|
}
|
|
|
|
|
2020-05-12 23:52:52 +01:00
|
|
|
fn self_handle(&self) -> Result<u32, tree::MethodErr> {
|
|
|
|
println!("Connection<{}>::self_handle()", self.id);
|
|
|
|
|
|
|
|
self.get_self_handle()
|
|
|
|
}
|
|
|
|
|
2020-05-10 23:09:58 +01:00
|
|
|
fn get_self_handle(&self) -> Result<u32, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!("Connection<{}>::get_self_handle()", self.id);
|
2020-05-12 23:52:52 +01:00
|
|
|
|
|
|
|
Ok(deltachat::constants::DC_CONTACT_ID_SELF)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn status(&self) -> Result<u32, tree::MethodErr> {
|
|
|
|
println!("Connection<{}>::status()", self.id);
|
|
|
|
|
|
|
|
self.get_status()
|
2020-05-10 23:09:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_status(&self) -> Result<u32, tree::MethodErr> {
|
2020-05-12 23:52:52 +01:00
|
|
|
match *self.state.clone().read().unwrap() {
|
|
|
|
ConnState::Initial | ConnState::Disconnected => Ok(2),
|
|
|
|
ConnState::Connected => Ok(0),
|
|
|
|
}
|
2020-05-10 23:09:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn hold_handles(&self, handle_type: u32, handles: Vec<u32>) -> Result<(), tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!(
|
|
|
|
"Connection<{}>::hold_handles({}, {:?})",
|
|
|
|
self.id, handle_type, handles
|
|
|
|
);
|
2020-05-10 23:09:58 +01:00
|
|
|
Err(tree::MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
|
|
}
|
|
|
|
|
|
|
|
fn inspect_handles(
|
|
|
|
&self,
|
|
|
|
handle_type: u32,
|
|
|
|
handles: Vec<u32>,
|
|
|
|
) -> Result<Vec<String>, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!(
|
|
|
|
"Connection<{}>::inspect_handles({}, {:?})",
|
|
|
|
self.id, handle_type, handles
|
|
|
|
);
|
2020-05-10 23:09:58 +01:00
|
|
|
Err(tree::MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
|
|
}
|
|
|
|
|
|
|
|
fn list_channels(
|
|
|
|
&self,
|
|
|
|
) -> Result<Vec<(dbus::Path<'static>, String, u32, u32)>, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!("Connection<{}>::list_channels()", self.id);
|
2020-05-10 23:09:58 +01:00
|
|
|
Err(tree::MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
|
|
}
|
|
|
|
|
|
|
|
fn release_handles(&self, handle_type: u32, handles: Vec<u32>) -> Result<(), tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!(
|
|
|
|
"Connection<{}>::release_handles({}, {:?})",
|
|
|
|
self.id, handle_type, handles
|
|
|
|
);
|
2020-05-10 23:09:58 +01:00
|
|
|
Err(tree::MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
|
|
}
|
|
|
|
|
|
|
|
fn request_channel(
|
|
|
|
&self,
|
|
|
|
type_: &str,
|
|
|
|
handle_type: u32,
|
|
|
|
handle: u32,
|
|
|
|
suppress_handler: bool,
|
|
|
|
) -> Result<dbus::Path<'static>, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!(
|
|
|
|
"Connection<{}>::request_channel({}, {}, {}, {})",
|
|
|
|
self.id, type_, handle_type, handle, suppress_handler
|
|
|
|
);
|
2020-05-10 23:09:58 +01:00
|
|
|
Err(tree::MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
|
|
}
|
|
|
|
|
|
|
|
fn request_handles(
|
|
|
|
&self,
|
|
|
|
handle_type: u32,
|
|
|
|
identifiers: Vec<&str>,
|
|
|
|
) -> Result<Vec<u32>, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!(
|
|
|
|
"Connection<{}>::request_handles({}, {:?})",
|
|
|
|
self.id, handle_type, identifiers
|
|
|
|
);
|
2020-05-10 23:09:58 +01:00
|
|
|
Err(tree::MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_client_interest(&self, tokens: Vec<&str>) -> Result<(), tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!("Connection<{}>::add_client_interest({:?})", self.id, tokens);
|
2020-05-10 23:09:58 +01:00
|
|
|
Err(tree::MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
|
|
}
|
|
|
|
|
|
|
|
fn remove_client_interest(&self, tokens: Vec<&str>) -> Result<(), tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!(
|
|
|
|
"Connection<{}>::remove_client_interest({:?})",
|
|
|
|
self.id, tokens
|
|
|
|
);
|
2020-05-10 23:09:58 +01:00
|
|
|
Err(tree::MethodErr::no_arg()) // FIXME: should be NotImplemented?
|
|
|
|
}
|
|
|
|
|
|
|
|
fn self_id(&self) -> Result<String, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!("Connection<{}>::self_id()", self.id);
|
2020-05-10 23:09:58 +01:00
|
|
|
|
2020-05-12 23:52:52 +01:00
|
|
|
Ok("Yourself".to_string()) // FIXME: this could be passed through config
|
2020-05-10 23:09:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn has_immortal_handles(&self) -> Result<bool, tree::MethodErr> {
|
2020-05-10 23:25:41 +01:00
|
|
|
println!("Connection<{}>::has_immortal_handles()", self.id);
|
2020-05-12 23:52:52 +01:00
|
|
|
|
2020-05-10 23:09:58 +01:00
|
|
|
Ok(true)
|
2020-05-10 19:04:14 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-12 22:12:08 +01:00
|
|
|
|
|
|
|
fn escape_one(b: u8) -> String {
|
|
|
|
format!("_{:0<2x}", b)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some non-empty sequence of ASCII letters, digits and underscores
|
|
|
|
fn escape(s: String) -> String {
|
|
|
|
// Special-case the empty string
|
|
|
|
if s.len() == 0 {
|
|
|
|
return "_".to_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
let bytes = s.into_bytes();
|
|
|
|
let mut iter = bytes.iter();
|
|
|
|
let mut out = String::new();
|
|
|
|
|
|
|
|
// Only alphanumeric in the first byte
|
|
|
|
let x = *iter.next().expect("Already checked len > 0");
|
|
|
|
let first = match x {
|
|
|
|
b'a'..=b'z' | b'A'..=b'Z' => unsafe { String::from_utf8_unchecked(vec![x]) },
|
|
|
|
_ => escape_one(x),
|
|
|
|
};
|
|
|
|
|
|
|
|
out.push_str(&first);
|
|
|
|
|
|
|
|
for x in iter {
|
|
|
|
let next = match x {
|
|
|
|
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' => unsafe {
|
|
|
|
String::from_utf8_unchecked(vec![*x])
|
|
|
|
},
|
|
|
|
_ => escape_one(*x),
|
|
|
|
};
|
|
|
|
|
|
|
|
out.push_str(&next);
|
|
|
|
}
|
|
|
|
|
|
|
|
out
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_escape() {
|
|
|
|
assert_eq!(escape("".to_string()), "_");
|
|
|
|
assert_eq!(escape("foo".to_string()), "foo");
|
|
|
|
assert_eq!(escape("foo@bar".to_string()), "foo_40bar");
|
|
|
|
assert_eq!(escape("foo_bar".to_string()), "foo_5fbar");
|
|
|
|
assert_eq!(escape("foo__@__bar".to_string()), "foo_5f_5f_40_5f_5fbar");
|
|
|
|
assert_eq!(escape("1foo".to_string()), "_31foo");
|
|
|
|
}
|
|
|
|
}
|