use crate::Peer; use std::net::SocketAddr; use std::path::PathBuf; use std::process::Command; use std::thread; use std::time::SystemTime; pub trait EventListener { fn added<'a>(&self, peer: &'a Peer) { self.connected(peer); } fn connected<'a>(&self, peer: &'a Peer); fn disconnected<'a>(&self, peer: &'a Peer); fn removed<'a>(&self, peer: &'a Peer) { self.disconnected(peer) } fn roaming<'a>(&self, peer: &'a Peer, previous_addr: SocketAddr); } impl EventListener for Vec> { fn added<'a>(&self, peer: &'a Peer) { if cfg!(feature = "addrem") || cfg!(test) { self.iter().for_each(|l| l.added(&peer)); } } fn connected<'a>(&self, peer: &'a Peer) { self.iter().for_each(|l| l.connected(&peer)); } fn disconnected<'a>(&self, peer: &'a Peer) { self.iter().for_each(|l| l.disconnected(&peer)); } fn removed<'a>(&self, peer: &'a Peer) { if cfg!(feature = "addrem") || cfg!(test) { self.iter().for_each(|l| l.removed(&peer)); } } fn roaming<'a>(&self, peer: &'a Peer, previous_addr: SocketAddr) { self.iter().for_each(|l| l.roaming(&peer, previous_addr)); } } pub struct LogListener; impl EventListener for LogListener { fn connected<'a>(&self, peer: &'a Peer) { println!("{} connected!", peer.key); } fn disconnected<'a>(&self, peer: &'a Peer) { println!("{} disconnected!", peer.key); } fn added<'a>(&self, peer: &'a Peer) { println!("{} added!", peer.key); } fn removed<'a>(&self, peer: &'a Peer) { println!("{} removed!", peer.key); } fn roaming<'a>(&self, peer: &'a Peer, previous_addr: SocketAddr) { println!( "{} roamed {} -> {}!", peer.key, previous_addr, peer.endpoint.unwrap() ); } } pub struct ScriptListener { pub script: PathBuf, } impl ScriptListener { pub fn new(script: PathBuf) -> ScriptListener { ScriptListener { script } } fn peer_props<'a>(&self, peer: &'a Peer) -> String { format!( "{id} {allowed_ips} {endpoint} {last_handshake} {persistent_keepalive} {traffic}", id = peer.key, allowed_ips = peer .allowed_ips .iter() .map(|(addr, mask)| [addr.to_string(), mask.to_string()].join("/")) .collect::>() .join(","), endpoint = peer .endpoint .map(|e| e.to_string()) .unwrap_or("0".to_owned()), last_handshake = peer .last_handshake .map(|s| s.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as i64) .unwrap_or(-1), persistent_keepalive = peer .persistent_keepalive .map(|k| k.as_secs() as i64) .unwrap_or(-1), traffic = { let (rx, tx) = peer.traffic; [rx.to_string(), tx.to_string()].join(",") } ) } fn mkcmd<'a>(&self, args: Vec<&'a str>) -> Command { let mut cmd = Command::new("/bin/sh"); cmd.arg("-c"); cmd.arg(format!( "{} {}", (&self.script).to_str().unwrap(), args.join(" ") )); cmd } fn call_sub<'a>(&self, args: Vec<&'a str>) { let mut cmd = self.mkcmd(args); thread::spawn(move || { cmd.spawn().expect("Failed to call Script hooḱ!"); }); } } impl EventListener for ScriptListener { fn connected<'a>(&self, peer: &'a Peer) { self.call_sub(vec!["connected", &self.peer_props(peer)]); } fn disconnected<'a>(&self, peer: &'a Peer) { self.call_sub(vec!["disconnected", &self.peer_props(peer)]); } fn added<'a>(&self, peer: &'a Peer) { self.call_sub(vec!["added", &self.peer_props(peer)]); } fn removed<'a>(&self, peer: &'a Peer) { self.call_sub(vec!["removed", &self.peer_props(peer)]); } fn roaming<'a>(&self, peer: &'a Peer, previous_addr: SocketAddr) { self.call_sub(vec![ "roaming", &self.peer_props(peer), &previous_addr.to_string(), ]); } }