iris-chat 0.1.48

Iris Chat command line client and shared encrypted chat core
Documentation
use super::*;

pub(super) fn configure_fips_lan(config: &mut Config, enabled: bool) {
    config.node.discovery.lan.enabled = enabled;
    config.transports.udp = if enabled {
        TransportInstances::Single(UdpConfig {
            bind_addr: Some("0.0.0.0:0".to_string()),
            advertise_on_nostr: Some(false),
            public: Some(false),
            outbound_only: Some(false),
            accept_connections: Some(true),
            ..UdpConfig::default()
        })
    } else {
        TransportInstances::default()
    };
}

pub(super) async fn run_recent_peer_observer(
    endpoint: Arc<FipsEndpoint>,
    recent_peers: Arc<RwLock<DeviceSyncRecentPeers>>,
) {
    loop {
        let peers = match peer_snapshot::query(|| endpoint.peers()).await {
            Ok(peers) => peers,
            Err(_) => return,
        };
        if let Ok(mut recent_peers) = recent_peers.write() {
            if let Err(error) =
                recent_peers.observe_and_flush_if_due(&peers, crate::perflog::now_ms())
            {
                crate::perflog!("fips_recent_peers.observe error={error}");
            }
        }
        sleep(RECENT_PEERS_OBSERVE_INTERVAL).await;
    }
}

pub(super) async fn shutdown_shared_fips(
    endpoint: Arc<FipsEndpoint>,
    pubsub: Option<Arc<FipsPubsubClient>>,
    recent_peers: Option<Arc<RwLock<DeviceSyncRecentPeers>>>,
) {
    if let Some(pubsub) = pubsub {
        pubsub.shutdown_shared().await;
    }
    if let (Some(recent_peers), Ok(peers)) = (recent_peers, endpoint.peers().await) {
        if let Ok(mut recent_peers) = recent_peers.write() {
            if let Err(error) = recent_peers.observe_and_flush(&peers, crate::perflog::now_ms()) {
                crate::perflog!("fips_recent_peers.shutdown error={error}");
            }
        }
    }
    let _ = endpoint.shutdown().await;
}

pub(super) fn same_host_hashtree_enabled() -> bool {
    same_host_hashtree_setting(std::env::var(SAME_HOST_HASHTREE_ENV).ok().as_deref())
}

pub(super) fn same_host_hashtree_setting(value: Option<&str>) -> bool {
    !value.is_some_and(|value| {
        matches!(
            value.trim().to_ascii_lowercase().as_str(),
            "0" | "false" | "no" | "off"
        )
    })
}

pub(super) fn configured_websocket_seeds() -> Option<WebSocketConfig> {
    let configured = std::env::var(WEBSOCKET_SEED_URLS_ENV).ok();
    let seed_urls = websocket_seed_urls(configured.as_deref());
    let bind_addr = std::env::var("IRIS_CHAT_FIPS_WEBSOCKET_BIND_ADDR")
        .ok()
        .filter(|value| !value.trim().is_empty());
    (!seed_urls.is_empty() || bind_addr.is_some()).then_some(WebSocketConfig {
        bind_addr,
        seed_urls,
        ..WebSocketConfig::default()
    })
}

pub(super) fn configured_local_rendezvous_addr() -> Result<Option<SocketAddrV4>, String> {
    let Some(value) = std::env::var(LOCAL_RENDEZVOUS_ADDR_ENV)
        .ok()
        .filter(|value| !value.trim().is_empty())
    else {
        return Ok(None);
    };
    parse_local_rendezvous_addr(&value).map(Some)
}

pub(super) fn parse_local_rendezvous_addr(value: &str) -> Result<SocketAddrV4, String> {
    let address = value.trim().parse::<SocketAddrV4>().map_err(|error| {
        format!("{LOCAL_RENDEZVOUS_ADDR_ENV} must be an IPv4 loopback address: {error}")
    })?;
    if !address.ip().is_loopback() || address.port() == 0 {
        return Err(format!(
            "{LOCAL_RENDEZVOUS_ADDR_ENV} must be a non-zero IPv4 loopback address"
        ));
    }
    Ok(address)
}