|
|
|
@ -1,25 +1,19 @@
@@ -1,25 +1,19 @@
|
|
|
|
|
#[macro_use] |
|
|
|
|
extern crate serde_derive; |
|
|
|
|
extern crate toml; |
|
|
|
|
extern crate gumdrop; |
|
|
|
|
#[macro_use] |
|
|
|
|
extern crate gumdrop_derive; |
|
|
|
|
extern crate xmpp; |
|
|
|
|
|
|
|
|
|
use xmpp::jid::Jid; |
|
|
|
|
use xmpp::client::ClientBuilder; |
|
|
|
|
use xmpp::plugins::messaging::MessagingPlugin; |
|
|
|
|
|
|
|
|
|
use std::time::Duration; |
|
|
|
|
use std::thread; |
|
|
|
|
use std::env; |
|
|
|
|
use std::iter::Iterator; |
|
|
|
|
use std::io::{Read, stdin}; |
|
|
|
|
use std::env::args; |
|
|
|
|
use std::fs::File; |
|
|
|
|
use std::io::{stdin, Read}; |
|
|
|
|
use std::iter::Iterator; |
|
|
|
|
use std::path::Path; |
|
|
|
|
use std::thread; |
|
|
|
|
use std::time::Duration; |
|
|
|
|
|
|
|
|
|
use std::env::args; |
|
|
|
|
use gumdrop::Options; |
|
|
|
|
use serde_derive::Deserialize; |
|
|
|
|
|
|
|
|
|
use futures::{future, Sink, Stream}; |
|
|
|
|
use tokio::runtime::current_thread::Runtime; |
|
|
|
|
use tokio_xmpp::Client; |
|
|
|
|
use xmpp_parsers::message::{Body, Message}; |
|
|
|
|
use xmpp_parsers::{Element, Jid}; |
|
|
|
|
|
|
|
|
|
#[derive(Deserialize)] |
|
|
|
|
struct Config { |
|
|
|
@ -34,12 +28,12 @@ fn parse_cfg<P: AsRef<Path>>(path: P) -> Option<Config> {
@@ -34,12 +28,12 @@ fn parse_cfg<P: AsRef<Path>>(path: P) -> Option<Config> {
|
|
|
|
|
match f.read_to_string(&mut input) { |
|
|
|
|
Ok(_) => match toml::from_str(&input) { |
|
|
|
|
Ok(toml) => Some(toml), |
|
|
|
|
Err(_) => None |
|
|
|
|
Err(_) => None, |
|
|
|
|
}, |
|
|
|
|
Err(_) => None |
|
|
|
|
Err(_) => None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Err(_) => None |
|
|
|
|
Err(_) => None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -51,7 +45,9 @@ struct MyOptions {
@@ -51,7 +45,9 @@ struct MyOptions {
|
|
|
|
|
#[options(help = "show this help message and exit")] |
|
|
|
|
help: bool, |
|
|
|
|
|
|
|
|
|
#[options(help = "path to config file. default: ~/.config/sendxmpp.toml with fallback to /etc/sendxmpp/sendxmpp.toml")] |
|
|
|
|
#[options(
|
|
|
|
|
help = "path to config file. default: ~/.config/sendxmpp.toml with fallback to /etc/sendxmpp/sendxmpp.toml" |
|
|
|
|
)] |
|
|
|
|
config: Option<String>, |
|
|
|
|
|
|
|
|
|
#[options(help = "Force OpenPGP encryption for all recipients", short = "e")] |
|
|
|
@ -83,33 +79,77 @@ fn main() {
@@ -83,33 +79,77 @@ fn main() {
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let recipients: Vec<Jid> = opts.recipients.iter().map(|s| s.parse::<Jid>().expect("invalid recipient jid")).collect(); |
|
|
|
|
let recipients: Vec<Jid> = opts |
|
|
|
|
.recipients |
|
|
|
|
.iter() |
|
|
|
|
.map(|s| s.parse::<Jid>().expect("invalid recipient jid")) |
|
|
|
|
.collect(); |
|
|
|
|
let recipients = &recipients; |
|
|
|
|
|
|
|
|
|
let cfg = match opts.config { |
|
|
|
|
Some(config) => parse_cfg(&config).expect("provided config cannot be found/parsed"), |
|
|
|
|
None => parse_cfg(env::home_dir().expect("cannot find home directory").join(".config/sendxmpp.toml")) |
|
|
|
|
.or_else(|| parse_cfg("/etc/sendxmpp/sendxmpp.toml")).expect("valid config file not found") |
|
|
|
|
None => parse_cfg( |
|
|
|
|
dirs::config_dir() |
|
|
|
|
.expect("cannot find home directory") |
|
|
|
|
.join("sendxmpp.toml"), |
|
|
|
|
) |
|
|
|
|
.or_else(|| parse_cfg("/etc/sendxmpp/sendxmpp.toml")) |
|
|
|
|
.expect("valid config file not found"), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let jid: Jid = cfg.jid.parse().expect("invalid jid in config file"); |
|
|
|
|
|
|
|
|
|
let mut data = String::new(); |
|
|
|
|
stdin().read_to_string(&mut data).expect("error reading from stdin"); |
|
|
|
|
|
|
|
|
|
let mut client = ClientBuilder::new(jid) |
|
|
|
|
.password(cfg.password) |
|
|
|
|
.connect() |
|
|
|
|
.expect("client cannot connect"); |
|
|
|
|
|
|
|
|
|
client.register_plugin(MessagingPlugin::new()); |
|
|
|
|
stdin() |
|
|
|
|
.read_to_string(&mut data) |
|
|
|
|
.expect("error reading from stdin"); |
|
|
|
|
let data = data.trim(); |
|
|
|
|
|
|
|
|
|
// tokio_core context
|
|
|
|
|
let mut rt = Runtime::new().unwrap(); |
|
|
|
|
// Client instance
|
|
|
|
|
let client = Client::new(&cfg.jid, &cfg.password).expect("could not connect to xmpp server"); |
|
|
|
|
|
|
|
|
|
// Make the two interfaces for sending and receiving independent
|
|
|
|
|
// of each other so we can move one into a closure.
|
|
|
|
|
let (mut sink, stream) = client.split(); |
|
|
|
|
// Wrap sink in Option so that we can take() it for the send(self)
|
|
|
|
|
// to consume and return it back when ready.
|
|
|
|
|
let mut send = move |stanza| { |
|
|
|
|
sink.start_send(stanza).expect("start_send"); |
|
|
|
|
}; |
|
|
|
|
// Main loop, processes events
|
|
|
|
|
let done = stream.for_each(|event| { |
|
|
|
|
if event.is_online() { |
|
|
|
|
for recipient in recipients { |
|
|
|
|
let reply = make_reply(recipient.clone(), &data); |
|
|
|
|
send(reply); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for recipient in recipients { |
|
|
|
|
client.plugin::<MessagingPlugin>().send_message(&recipient, &data).expect("error sending message"); |
|
|
|
|
} |
|
|
|
|
Box::new(future::ok(())) |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
thread::spawn(|| { |
|
|
|
|
thread::sleep(Duration::from_millis(4000)); |
|
|
|
|
std::process::exit(0); |
|
|
|
|
}); |
|
|
|
|
client.main().expect("error during client main") |
|
|
|
|
|
|
|
|
|
// Start polling `done`
|
|
|
|
|
match rt.block_on(done) { |
|
|
|
|
Ok(_) => { |
|
|
|
|
println!("successful exiting"); |
|
|
|
|
std::process::exit(0); |
|
|
|
|
//()
|
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
println!("Fatal: {}", e); |
|
|
|
|
() |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Construct a chat <message/>
|
|
|
|
|
fn make_reply(to: Jid, body: &str) -> Element { |
|
|
|
|
let mut message = Message::new(Some(to)); |
|
|
|
|
message.bodies.insert(String::new(), Body(body.to_owned())); |
|
|
|
|
message.into() |
|
|
|
|
} |
|
|
|
|