sendxmpp-rs/src/main.rs

156 lines
4.2 KiB
Rust
Raw Normal View History

2019-01-25 21:56:50 -05:00
use std::env::args;
2017-08-11 00:37:50 -04:00
use std::fs::File;
2019-01-25 21:56:50 -05:00
use std::io::{stdin, Read};
use std::iter::Iterator;
2017-08-12 23:59:43 -04:00
use std::path::Path;
2019-01-25 21:56:50 -05:00
use std::thread;
use std::time::Duration;
2017-08-11 00:37:50 -04:00
use gumdrop::Options;
2019-01-25 21:56:50 -05:00
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};
2017-08-11 00:37:50 -04:00
#[derive(Deserialize)]
struct Config {
jid: String,
password: String,
}
2017-08-12 23:59:43 -04:00
fn parse_cfg<P: AsRef<Path>>(path: P) -> Option<Config> {
match File::open(path) {
Ok(mut f) => {
let mut input = String::new();
match f.read_to_string(&mut input) {
Ok(_) => match toml::from_str(&input) {
Ok(toml) => Some(toml),
2019-01-25 21:56:50 -05:00
Err(_) => None,
2017-08-12 23:59:43 -04:00
},
2019-01-25 21:56:50 -05:00
Err(_) => None,
2017-08-12 23:59:43 -04:00
}
2017-08-11 00:37:50 -04:00
}
2019-01-25 21:56:50 -05:00
Err(_) => None,
2017-08-12 23:59:43 -04:00
}
2017-08-11 00:37:50 -04:00
}
#[derive(Default, Options)]
struct MyOptions {
#[options(free)]
recipients: Vec<String>,
#[options(help = "show this help message and exit")]
help: bool,
2019-01-25 21:56:50 -05:00
#[options(
help = "path to config file. default: ~/.config/sendxmpp.toml with fallback to /etc/sendxmpp/sendxmpp.toml"
)]
2017-08-11 00:37:50 -04:00
config: Option<String>,
#[options(help = "Force OpenPGP encryption for all recipients", short = "e")]
force_pgp: bool,
#[options(help = "Attempt OpenPGP encryption for all recipients")]
attempt_pgp: bool,
}
fn main() {
let args: Vec<String> = args().collect();
// Remember to skip the first argument. That's the program name.
let opts = match MyOptions::parse_args_default(&args[1..]) {
Ok(opts) => opts,
Err(e) => {
println!("{}: {}", args[0], e);
println!("Usage: {} [OPTIONS] [ARGUMENTS]", args[0]);
println!();
println!("{}", MyOptions::usage());
return;
}
};
if opts.help {
println!("Usage: {} [OPTIONS] [ARGUMENTS]", args[0]);
println!();
println!("{}", MyOptions::usage());
return;
}
2019-01-25 21:56:50 -05:00
let recipients: Vec<Jid> = opts
.recipients
.iter()
.map(|s| s.parse::<Jid>().expect("invalid recipient jid"))
.collect();
let recipients = &recipients;
2017-08-11 00:37:50 -04:00
let cfg = match opts.config {
2017-08-12 23:59:43 -04:00
Some(config) => parse_cfg(&config).expect("provided config cannot be found/parsed"),
2019-01-25 21:56:50 -05:00
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"),
2017-08-11 00:37:50 -04:00
};
let mut data = String::new();
2019-01-25 21:56:50 -05:00
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);
}
}
2017-08-11 00:37:50 -04:00
2019-01-25 21:56:50 -05:00
Box::new(future::ok(()))
});
2017-08-11 00:37:50 -04:00
thread::spawn(|| {
thread::sleep(Duration::from_millis(4000));
std::process::exit(0);
});
2019-01-25 21:56:50 -05:00
// 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()
2017-08-11 00:37:50 -04:00
}