secure chat addon
This commit is contained in:
1 parent
0715f5d66d
commit
a6a4375281
4 files changed
+92
-25
No files matched your search
+15
-7
@@ -26,6 +26,8 @@ use crate::serviceclient::ServiceClient;
|
|||||||
|
|
||||||
const WINDOW_W: c_int = 960;
|
const WINDOW_W: c_int = 960;
|
||||||
const WINDOW_H: c_int = 640;
|
const WINDOW_H: c_int = 640;
|
||||||
|
const DEFAULT_WEBHUB_PORT: u16 = 9994;
|
||||||
|
const DEFAULT_BACKONE_PORT: u16 = 9993;
|
||||||
|
|
||||||
struct SecureChatCtx {
|
struct SecureChatCtx {
|
||||||
client: ServiceClient,
|
client: ServiceClient,
|
||||||
@@ -226,7 +228,7 @@ unsafe fn update_core_status() {
|
|||||||
if c.session.hub_connected {
|
if c.session.hub_connected {
|
||||||
"WebHub connected"
|
"WebHub connected"
|
||||||
} else {
|
} else {
|
||||||
"WebHub disconnected — messages may not send"
|
"WebHub disconnected — incoming sync may pause"
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -296,10 +298,7 @@ unsafe fn update_send_enabled() {
|
|||||||
if send_button.is_null() || compose_entry.is_null() {
|
if send_button.is_null() || compose_entry.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let c = ctx();
|
let ok = selected_network_id().is_some() && selected_contact().is_some();
|
||||||
let ok = c.session.hub_connected
|
|
||||||
&& selected_network_id().is_some()
|
|
||||||
&& selected_contact().is_some();
|
|
||||||
if ok {
|
if ok {
|
||||||
libui::uiControlEnable(send_button.cast());
|
libui::uiControlEnable(send_button.cast());
|
||||||
libui::uiControlEnable(compose_entry.cast());
|
libui::uiControlEnable(compose_entry.cast());
|
||||||
@@ -780,9 +779,16 @@ unsafe extern "C" fn on_send_clicked(_: *mut libui::uiButton, _: *mut c_void) {
|
|||||||
let name = self_display_name(&c.client);
|
let name = self_display_name(&c.client);
|
||||||
let sender_id = c.client.self_address();
|
let sender_id = c.client.self_address();
|
||||||
let now = crate::serviceclient::ms_since_epoch();
|
let now = crate::serviceclient::ms_since_epoch();
|
||||||
|
let mut ports = vec![cv.contact.port];
|
||||||
|
if !ports.contains(&DEFAULT_WEBHUB_PORT) {
|
||||||
|
ports.push(DEFAULT_WEBHUB_PORT);
|
||||||
|
}
|
||||||
|
if !ports.contains(&DEFAULT_BACKONE_PORT) {
|
||||||
|
ports.push(DEFAULT_BACKONE_PORT);
|
||||||
|
}
|
||||||
let _ = c.hub_cmd.send(HubCommand::SendChatRemote {
|
let _ = c.hub_cmd.send(HubCommand::SendChatRemote {
|
||||||
host: cv.contact.ip.clone(),
|
host: cv.contact.ip.clone(),
|
||||||
port: cv.contact.port,
|
ports,
|
||||||
network_id: network_id.to_lowercase(),
|
network_id: network_id.to_lowercase(),
|
||||||
sender_id: sender_id.to_lowercase(),
|
sender_id: sender_id.to_lowercase(),
|
||||||
target_id: cv.contact.peer_id.to_lowercase(),
|
target_id: cv.contact.peer_id.to_lowercase(),
|
||||||
@@ -887,6 +893,7 @@ unsafe extern "C" fn on_timer(_: *mut c_void) -> c_int {
|
|||||||
&c.client.self_address(),
|
&c.client.self_address(),
|
||||||
&name,
|
&name,
|
||||||
&cv.contact.peer_id,
|
&cv.contact.peer_id,
|
||||||
|
&cv.contact.ip,
|
||||||
&body,
|
&body,
|
||||||
);
|
);
|
||||||
let now = crate::serviceclient::ms_since_epoch();
|
let now = crate::serviceclient::ms_since_epoch();
|
||||||
@@ -1066,8 +1073,9 @@ pub fn secure_chat_main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let auth_token = client.auth_token().to_string();
|
let auth_token = client.auth_token().to_string();
|
||||||
|
let hub_host = client.webhub_host();
|
||||||
let hub_port = client.webhub_port();
|
let hub_port = client.webhub_port();
|
||||||
let (hub_cmd, hub_events) = spawn_webhub(auth_token, hub_port);
|
let (hub_cmd, hub_events) = spawn_webhub(auth_token, hub_host, hub_port);
|
||||||
|
|
||||||
let ctx_box = Box::new(SecureChatCtx {
|
let ctx_box = Box::new(SecureChatCtx {
|
||||||
client,
|
client,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ use serde_json::json;
|
|||||||
|
|
||||||
use crate::serviceclient::ServiceClient;
|
use crate::serviceclient::ServiceClient;
|
||||||
|
|
||||||
|
use super::state::load_member_ip_for_peer;
|
||||||
|
|
||||||
fn peer_path_hosts(client: &ServiceClient, target_id: &str) -> Vec<String> {
|
fn peer_path_hosts(client: &ServiceClient, target_id: &str) -> Vec<String> {
|
||||||
let target = target_id.to_lowercase();
|
let target = target_id.to_lowercase();
|
||||||
let Some(peers) = client.http_get_json("/peer").and_then(|v| v.as_array().cloned()) else {
|
let Some(peers) = client.http_get_json("/peer").and_then(|v| v.as_array().cloned()) else {
|
||||||
@@ -35,6 +37,14 @@ fn peer_path_hosts(client: &ServiceClient, target_id: &str) -> Vec<String> {
|
|||||||
hosts
|
hosts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_unique_host(hosts: &mut Vec<String>, host: &str) {
|
||||||
|
let host = host.split('/').next().unwrap_or(host).trim();
|
||||||
|
if host.is_empty() || hosts.iter().any(|candidate| candidate == host) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hosts.push(host.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
fn post_relay_url(
|
fn post_relay_url(
|
||||||
host: &str,
|
host: &str,
|
||||||
port: u16,
|
port: u16,
|
||||||
@@ -69,6 +79,7 @@ pub fn relay_chat_to_peer_paths(
|
|||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
sender_name: &str,
|
sender_name: &str,
|
||||||
target_id: &str,
|
target_id: &str,
|
||||||
|
preferred_host: &str,
|
||||||
body: &str,
|
body: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let port = client.http_port();
|
let port = client.http_port();
|
||||||
@@ -85,8 +96,17 @@ pub fn relay_chat_to_peer_paths(
|
|||||||
});
|
});
|
||||||
let payload_str = payload.to_string();
|
let payload_str = payload.to_string();
|
||||||
let token = client.auth_token();
|
let token = client.auth_token();
|
||||||
|
let mut hosts = Vec::new();
|
||||||
|
|
||||||
|
push_unique_host(&mut hosts, preferred_host);
|
||||||
|
if let Some(member_ip) = load_member_ip_for_peer(client, network_id, target_id) {
|
||||||
|
push_unique_host(&mut hosts, &member_ip);
|
||||||
|
}
|
||||||
for host in peer_path_hosts(client, target_id) {
|
for host in peer_path_hosts(client, target_id) {
|
||||||
|
push_unique_host(&mut hosts, &host);
|
||||||
|
}
|
||||||
|
|
||||||
|
for host in hosts {
|
||||||
for use_auth in [true, false] {
|
for use_auth in [true, false] {
|
||||||
let auth = if use_auth { Some(token) } else { None };
|
let auth = if use_auth { Some(token) } else { None };
|
||||||
let (status, resp_body) = post_relay_url(&host, port, auth, &payload_str);
|
let (status, resp_body) = post_relay_url(&host, port, auth, &payload_str);
|
||||||
|
|||||||
+28
-18
@@ -37,7 +37,7 @@ pub enum HubCommand {
|
|||||||
},
|
},
|
||||||
SendChatRemote {
|
SendChatRemote {
|
||||||
host: String,
|
host: String,
|
||||||
port: u16,
|
ports: Vec<u16>,
|
||||||
network_id: String,
|
network_id: String,
|
||||||
sender_id: String,
|
sender_id: String,
|
||||||
target_id: String,
|
target_id: String,
|
||||||
@@ -65,13 +65,23 @@ fn is_loopback_host(host: &str) -> bool {
|
|||||||
fn send_chat_remote(
|
fn send_chat_remote(
|
||||||
auth_token: &str,
|
auth_token: &str,
|
||||||
host: &str,
|
host: &str,
|
||||||
port: u16,
|
ports: &[u16],
|
||||||
network_id: &str,
|
network_id: &str,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
target_id: &str,
|
target_id: &str,
|
||||||
sender_name: &str,
|
sender_name: &str,
|
||||||
body: &str,
|
body: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
let payload = json!({
|
||||||
|
"type": "chat_message",
|
||||||
|
"networkId": network_id.to_lowercase(),
|
||||||
|
"senderId": sender_id.to_lowercase(),
|
||||||
|
"targetId": target_id.to_lowercase(),
|
||||||
|
"senderName": sender_name,
|
||||||
|
"body": body,
|
||||||
|
});
|
||||||
|
|
||||||
|
for port in ports {
|
||||||
let url = if is_loopback_host(host) && !auth_token.is_empty() {
|
let url = if is_loopback_host(host) && !auth_token.is_empty() {
|
||||||
format!(
|
format!(
|
||||||
"ws://{}:{}/?auth={}",
|
"ws://{}:{}/?auth={}",
|
||||||
@@ -84,7 +94,7 @@ fn send_chat_remote(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Ok((mut socket, _)) = connect(&url) else {
|
let Ok((mut socket, _)) = connect(&url) else {
|
||||||
return false;
|
continue;
|
||||||
};
|
};
|
||||||
if let MaybeTlsStream::Plain(tcp) = socket.get_ref() {
|
if let MaybeTlsStream::Plain(tcp) = socket.get_ref() {
|
||||||
let _ = tcp.set_read_timeout(Some(Duration::from_millis(3000)));
|
let _ = tcp.set_read_timeout(Some(Duration::from_millis(3000)));
|
||||||
@@ -103,16 +113,8 @@ fn send_chat_remote(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let payload = json!({
|
|
||||||
"type": "chat_message",
|
|
||||||
"networkId": network_id.to_lowercase(),
|
|
||||||
"senderId": sender_id.to_lowercase(),
|
|
||||||
"targetId": target_id.to_lowercase(),
|
|
||||||
"senderName": sender_name,
|
|
||||||
"body": body,
|
|
||||||
});
|
|
||||||
if socket.send(Message::Text(payload.to_string())).is_err() {
|
if socket.send(Message::Text(payload.to_string())).is_err() {
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let deadline = std::time::Instant::now() + Duration::from_secs(3);
|
let deadline = std::time::Instant::now() + Duration::from_secs(3);
|
||||||
@@ -121,17 +123,22 @@ fn send_chat_remote(
|
|||||||
Ok(Message::Text(text)) => {
|
Ok(Message::Text(text)) => {
|
||||||
if let Ok(msg) = serde_json::from_str::<Value>(&text) {
|
if let Ok(msg) = serde_json::from_str::<Value>(&text) {
|
||||||
if msg.get("type").and_then(|t| t.as_str()) == Some("chat_delivery") {
|
if msg.get("type").and_then(|t| t.as_str()) == Some("chat_delivery") {
|
||||||
return msg
|
if msg
|
||||||
.get("delivered")
|
.get("delivered")
|
||||||
.and_then(|v| v.as_bool())
|
.and_then(|v| v.as_bool())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Message::Close(_)) => break,
|
Ok(Message::Close(_)) => break,
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
tungstenite::Error::Io(ref io)
|
tungstenite::Error::Io(ref io)
|
||||||
if io.kind() == ErrorKind::WouldBlock || io.kind() == ErrorKind::TimedOut =>
|
if io.kind() == ErrorKind::WouldBlock
|
||||||
|
|| io.kind() == ErrorKind::TimedOut =>
|
||||||
{
|
{
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
continue;
|
continue;
|
||||||
@@ -141,11 +148,13 @@ fn send_chat_remote(
|
|||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_webhub(
|
pub fn spawn_webhub(
|
||||||
auth_token: String,
|
auth_token: String,
|
||||||
|
host: String,
|
||||||
port: u16,
|
port: u16,
|
||||||
) -> (Sender<HubCommand>, Receiver<HubEvent>) {
|
) -> (Sender<HubCommand>, Receiver<HubEvent>) {
|
||||||
let (cmd_tx, cmd_rx) = mpsc::channel::<HubCommand>();
|
let (cmd_tx, cmd_rx) = mpsc::channel::<HubCommand>();
|
||||||
@@ -159,7 +168,8 @@ pub fn spawn_webhub(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"ws://127.0.0.1:{}/?auth={}",
|
"ws://{}:{}/?auth={}",
|
||||||
|
host,
|
||||||
port,
|
port,
|
||||||
encode_query_component(auth_token.as_str())
|
encode_query_component(auth_token.as_str())
|
||||||
);
|
);
|
||||||
@@ -204,7 +214,7 @@ pub fn spawn_webhub(
|
|||||||
}
|
}
|
||||||
HubCommand::SendChatRemote {
|
HubCommand::SendChatRemote {
|
||||||
host,
|
host,
|
||||||
port: remote_port,
|
ports,
|
||||||
network_id,
|
network_id,
|
||||||
sender_id,
|
sender_id,
|
||||||
target_id,
|
target_id,
|
||||||
@@ -217,7 +227,7 @@ pub fn spawn_webhub(
|
|||||||
let delivered = send_chat_remote(
|
let delivered = send_chat_remote(
|
||||||
&auth,
|
&auth,
|
||||||
&host,
|
&host,
|
||||||
remote_port,
|
&ports,
|
||||||
&network_id,
|
&network_id,
|
||||||
&sender_id,
|
&sender_id,
|
||||||
&target_id,
|
&target_id,
|
||||||
|
|||||||
@@ -432,6 +432,10 @@ impl ServiceClient {
|
|||||||
Self::webhub_port_from_local_conf().unwrap_or(9994)
|
Self::webhub_port_from_local_conf().unwrap_or(9994)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn webhub_host(&self) -> String {
|
||||||
|
Self::webhub_host_from_local_conf().unwrap_or_else(|| String::from("127.0.0.1"))
|
||||||
|
}
|
||||||
|
|
||||||
fn webhub_port_from_local_conf() -> Option<u16> {
|
fn webhub_port_from_local_conf() -> Option<u16> {
|
||||||
for path in [crate::GLOBAL_SERVICE_HOME_V2, crate::GLOBAL_SERVICE_HOME_V1] {
|
for path in [crate::GLOBAL_SERVICE_HOME_V2, crate::GLOBAL_SERVICE_HOME_V1] {
|
||||||
let conf_path = Path::new(path).join("local.conf");
|
let conf_path = Path::new(path).join("local.conf");
|
||||||
@@ -452,6 +456,31 @@ impl ServiceClient {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn webhub_host_from_local_conf() -> Option<String> {
|
||||||
|
for path in [crate::GLOBAL_SERVICE_HOME_V2, crate::GLOBAL_SERVICE_HOME_V1] {
|
||||||
|
let conf_path = Path::new(path).join("local.conf");
|
||||||
|
let Ok(body) = std::fs::read_to_string(conf_path) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(cfg) = serde_json::from_str::<Value>(&body) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(bind) = cfg
|
||||||
|
.get("settings")
|
||||||
|
.and_then(|s| s.get("webhubBind"))
|
||||||
|
.and_then(|b| b.as_str())
|
||||||
|
.map(str::trim)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if bind.is_empty() || bind.eq_ignore_ascii_case("overlay") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return Some(bind.to_string());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn self_address(&self) -> String {
|
pub fn self_address(&self) -> String {
|
||||||
self.get_str(&["status", "address"]).to_lowercase()
|
self.get_str(&["status", "address"]).to_lowercase()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user