Compare commits
3 Commits
master
...
raw_newlib
Author | SHA1 | Date |
---|---|---|
Travis Burtrum | 2f27dc9464 | |
Travis Burtrum | b4ab9bf2f4 | |
Travis Burtrum | 88e39e3771 |
|
@ -9,6 +9,9 @@ SUFFIX=""
|
||||||
|
|
||||||
echo "$TARGET" | grep -E '^x86_64-pc-windows-gnu$' >/dev/null && SUFFIX=".exe"
|
echo "$TARGET" | grep -E '^x86_64-pc-windows-gnu$' >/dev/null && SUFFIX=".exe"
|
||||||
|
|
||||||
|
# ring fails to compile here
|
||||||
|
echo "$TARGET" | grep -E '^(s390x|powerpc|mips|riscv64gc|.*solaris$)' >/dev/null && echo "$TARGET not supported in rustls" && exit 0
|
||||||
|
|
||||||
# build binary
|
# build binary
|
||||||
cross build --target $TARGET --release
|
cross build --target $TARGET --release
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "sendxmpp"
|
name = "sendxmpp"
|
||||||
version = "3.0.1"
|
version = "2.0.0"
|
||||||
authors = ["moparisthebest <admin@moparisthebest.com>"]
|
authors = ["moparisthebest <admin@moparisthebest.com>"]
|
||||||
|
|
||||||
description = "Send XMPP messages from the command line."
|
description = "Send XMPP messages from the command line."
|
||||||
|
@ -25,10 +25,14 @@ serde_derive = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
gumdrop = "0.8.0"
|
gumdrop = "0.8.0"
|
||||||
gumdrop_derive = "0.8.0"
|
gumdrop_derive = "0.8.0"
|
||||||
dirs = "4.0.0"
|
dirs = "3.0.1"
|
||||||
tokio-xmpp = { version = "3.2.0", default-features = false, features = ["tls-rust"] }
|
tokio-xmpp = { git = "https://gitlab.com/moparisthebest/xmpp-rs.git", branch = "main", default-features = false }
|
||||||
tokio = { version = "1", features = ["net", "rt", "rt-multi-thread", "macros", "io-util", "io-std"] }
|
tokio = { version = "1", features = ["net", "rt", "rt-multi-thread", "macros", "io-util", "io-std"] }
|
||||||
xmpp-parsers = "0.19"
|
xmpp-parsers = "0.18"
|
||||||
die = "0.2.0"
|
die = "0.2.0"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
env_logger = "0.9"
|
|
||||||
|
[features]
|
||||||
|
default = ["tls-rust"]
|
||||||
|
tls-rust = ["tokio-xmpp/tls-rust"]
|
||||||
|
tls-native = ["tokio-xmpp/tls-native"]
|
||||||
|
|
|
@ -18,10 +18,6 @@ Optional arguments:
|
||||||
-c, --config CONFIG path to config file. default: ~/.config/sendxmpp.toml with fallback to /etc/sendxmpp/sendxmpp.toml
|
-c, --config CONFIG path to config file. default: ~/.config/sendxmpp.toml with fallback to /etc/sendxmpp/sendxmpp.toml
|
||||||
-e, --force-pgp Force OpenPGP encryption for all recipients
|
-e, --force-pgp Force OpenPGP encryption for all recipients
|
||||||
-a, --attempt-pgp Attempt OpenPGP encryption for all recipients
|
-a, --attempt-pgp Attempt OpenPGP encryption for all recipients
|
||||||
-r, --raw Send raw XML stream, cannot be used with recipients or PGP
|
|
||||||
-p, --presence Send a <presence/> after connecting before sending messages, required for receiving for --raw
|
|
||||||
-m, --muc Recipients are Multi-User Chats
|
|
||||||
-n, --nick NICK Nickname to use in Multi-User Chats
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage examples:
|
Usage examples:
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# jid and password exactly like this
|
# jid and password exactly like this, nothing else
|
||||||
|
|
||||||
jid = "jid@example.org"
|
jid = "jid@example.org"
|
||||||
password = "sOmePa55W0rD"
|
password = "sOmePa55W0rD"
|
||||||
# nick = "foobar" # optional nick for Multi-User Chat
|
|
||||||
|
|
98
src/main.rs
98
src/main.rs
|
@ -3,18 +3,16 @@ use std::fs::File;
|
||||||
use std::io::{stdin, Read, Write};
|
use std::io::{stdin, Read, Write};
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use die::{die, Die};
|
use die::{die, Die};
|
||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use tokio_xmpp::{SimpleClient as Client};
|
use tokio_xmpp::SimpleClient as Client;
|
||||||
use xmpp_parsers::message::{Body, Message, MessageType};
|
use xmpp_parsers::message::{Body, Message};
|
||||||
use xmpp_parsers::muc::Muc;
|
|
||||||
use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType};
|
use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType};
|
||||||
use xmpp_parsers::{BareJid, Element, FullJid, Jid};
|
use xmpp_parsers::{Element, Jid};
|
||||||
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
|
@ -24,7 +22,6 @@ use anyhow::{anyhow, bail, Result};
|
||||||
struct Config {
|
struct Config {
|
||||||
jid: String,
|
jid: String,
|
||||||
password: String,
|
password: String,
|
||||||
nick: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cfg<P: AsRef<Path>>(path: P) -> Result<Config> {
|
fn parse_cfg<P: AsRef<Path>>(path: P) -> Result<Config> {
|
||||||
|
@ -56,18 +53,10 @@ struct MyOptions {
|
||||||
|
|
||||||
#[options(help = "Send a <presence/> after connecting before sending messages, required for receiving for --raw")]
|
#[options(help = "Send a <presence/> after connecting before sending messages, required for receiving for --raw")]
|
||||||
presence: bool,
|
presence: bool,
|
||||||
|
|
||||||
#[options(help = "Recipients are Multi-User Chats")]
|
|
||||||
muc: bool,
|
|
||||||
|
|
||||||
#[options(help = "Nickname to use in Multi-User Chats")]
|
|
||||||
nick: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
env_logger::init();
|
|
||||||
|
|
||||||
let args: Vec<String> = args().collect();
|
let args: Vec<String> = args().collect();
|
||||||
|
|
||||||
// Remember to skip the first argument. That's the program name.
|
// Remember to skip the first argument. That's the program name.
|
||||||
|
@ -89,19 +78,10 @@ async fn main() {
|
||||||
if !recipients.is_empty() {
|
if !recipients.is_empty() {
|
||||||
die!("--raw is incompatible with recipients");
|
die!("--raw is incompatible with recipients");
|
||||||
}
|
}
|
||||||
if opts.muc {
|
|
||||||
die!("--raw is incompatible with --muc");
|
|
||||||
}
|
|
||||||
} else if recipients.is_empty() {
|
} else if recipients.is_empty() {
|
||||||
die!("no recipients specified!");
|
die!("no recipients specified!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.muc {
|
|
||||||
if opts.force_pgp || opts.attempt_pgp {
|
|
||||||
die!("--force-pgp and --attempt-pgp isn't implemented with --muc");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let recipients = &recipients;
|
let recipients = &recipients;
|
||||||
|
|
||||||
let cfg = match opts.config {
|
let cfg = match opts.config {
|
||||||
|
@ -120,13 +100,16 @@ async fn main() {
|
||||||
|
|
||||||
// can paste this to test: <message xmlns="jabber:client" to="travis@burtrum.org" type="chat"><body>woot</body></message>
|
// can paste this to test: <message xmlns="jabber:client" to="travis@burtrum.org" type="chat"><body>woot</body></message>
|
||||||
|
|
||||||
let mut open_client = client.into_inner().into_inner();
|
let client = client.into_inner();
|
||||||
|
let mut open_client = client.stream.try_lock().die("could not lock client stream");
|
||||||
|
let open_client = open_client.get_mut();
|
||||||
|
|
||||||
let mut rd_buf = [0u8; 256]; // todo: proper buffer size?
|
let mut rd_buf = [0u8; 256]; // todo: proper buffer size?
|
||||||
let mut stdin_buf = rd_buf.clone();
|
let mut stdin_buf = rd_buf.clone();
|
||||||
|
|
||||||
let mut stdin = tokio::io::stdin();
|
let mut stdin = tokio::io::stdin();
|
||||||
let mut stdout = tokio::io::stdout();
|
let mut stdout = tokio::io::stdout();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
n = open_client.read(&mut rd_buf) => {
|
n = open_client.read(&mut rd_buf) => {
|
||||||
|
@ -156,10 +139,6 @@ async fn main() {
|
||||||
let mut data = String::new();
|
let mut data = String::new();
|
||||||
stdin().lock().read_to_string(&mut data).die("error reading from stdin");
|
stdin().lock().read_to_string(&mut data).die("error reading from stdin");
|
||||||
let data = data.trim();
|
let data = data.trim();
|
||||||
if data.is_empty() {
|
|
||||||
// don't send empty stanzas
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut client = Client::new(&cfg.jid, &cfg.password).await.die("could not connect to xmpp server");
|
let mut client = Client::new(&cfg.jid, &cfg.password).await.die("could not connect to xmpp server");
|
||||||
|
|
||||||
|
@ -168,45 +147,27 @@ async fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for recipient in recipients {
|
for recipient in recipients {
|
||||||
if opts.muc {
|
let reply = if opts.force_pgp || opts.attempt_pgp {
|
||||||
let nick = opts
|
let encrypted = gpg_encrypt(recipient.clone(), &data);
|
||||||
.nick
|
if encrypted.is_err() {
|
||||||
.clone()
|
if opts.force_pgp {
|
||||||
.or(cfg.nick.clone())
|
die!("pgp encryption to jid '{}' failed!", recipient);
|
||||||
.or_else(|| BareJid::from_str(cfg.jid.as_str()).unwrap().node)
|
|
||||||
.die("couldn't find a nick to use");
|
|
||||||
let participant = match recipient.clone() {
|
|
||||||
Jid::Full(_) => die!("Invalid room address"),
|
|
||||||
Jid::Bare(bare) => bare.with_resource(nick.clone()),
|
|
||||||
};
|
|
||||||
let join = make_join(participant.clone());
|
|
||||||
client.send_stanza(join).await.die("failed to join MUC");
|
|
||||||
|
|
||||||
let reply = make_reply(recipient.clone(), &data, opts.muc);
|
|
||||||
client.send_stanza(reply).await.die("sending message failed");
|
|
||||||
} else {
|
|
||||||
let reply = if opts.force_pgp || opts.attempt_pgp {
|
|
||||||
let encrypted = gpg_encrypt(recipient.clone(), &data);
|
|
||||||
if encrypted.is_err() {
|
|
||||||
if opts.force_pgp {
|
|
||||||
die!("pgp encryption to jid '{}' failed!", recipient);
|
|
||||||
} else {
|
|
||||||
make_reply(recipient.clone(), &data, opts.muc)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let encrypted = encrypted.unwrap();
|
make_reply(recipient.clone(), &data)
|
||||||
let encrypted = encrypted.trim();
|
|
||||||
let mut reply = make_reply(recipient.clone(), "pgp", opts.muc);
|
|
||||||
let mut x = Element::bare("x", "jabber:x:encrypted");
|
|
||||||
x.append_text_node(encrypted);
|
|
||||||
reply.append_child(x);
|
|
||||||
reply
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
make_reply(recipient.clone(), &data, opts.muc)
|
let encrypted = encrypted.unwrap();
|
||||||
};
|
let encrypted = encrypted.trim();
|
||||||
client.send_stanza(reply).await.die("sending message failed");
|
let mut reply = make_reply(recipient.clone(), "pgp");
|
||||||
}
|
let mut x = Element::bare("x", "jabber:x:encrypted");
|
||||||
|
x.append_text_node(encrypted);
|
||||||
|
reply.append_child(x);
|
||||||
|
reply
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
make_reply(recipient.clone(), &data)
|
||||||
|
};
|
||||||
|
client.send_stanza(reply).await.die("sending message failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close client connection
|
// Close client connection
|
||||||
|
@ -221,16 +182,9 @@ fn make_presence() -> Element {
|
||||||
presence.into()
|
presence.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_join(to: FullJid) -> Element {
|
|
||||||
Presence::new(PresenceType::None).with_to(Jid::Full(to)).with_payloads(vec![Muc::new().into()]).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a chat <message/>
|
// Construct a chat <message/>
|
||||||
fn make_reply(to: Jid, body: &str, groupchat: bool) -> Element {
|
fn make_reply(to: Jid, body: &str) -> Element {
|
||||||
let mut message = Message::new(Some(to));
|
let mut message = Message::new(Some(to));
|
||||||
if groupchat {
|
|
||||||
message.type_ = MessageType::Groupchat;
|
|
||||||
}
|
|
||||||
message.bodies.insert(String::new(), Body(body.to_owned()));
|
message.bodies.insert(String::new(), Body(body.to_owned()));
|
||||||
message.into()
|
message.into()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue