joecalsend/src/lib.rs

227 lines
6.1 KiB
Rust
Raw Normal View History

pub mod config;
2025-07-04 00:00:11 +00:00
pub mod discovery;
pub mod error;
2025-07-06 21:15:08 +00:00
pub mod http_server;
2025-07-04 00:00:11 +00:00
pub mod models;
pub mod transfer;
2025-07-04 22:15:52 +00:00
use std::{
collections::BTreeMap,
2025-08-07 00:11:20 +00:00
fmt::Debug,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
2025-08-14 23:25:32 +00:00
path::PathBuf,
2025-07-07 22:55:40 +00:00
sync::{Arc, OnceLock},
time::Duration,
2025-07-04 22:15:52 +00:00
};
pub use config::Config;
use julid::Julid;
use log::error;
2025-08-01 16:16:05 +00:00
use models::{Device, FileMetadata};
2025-07-06 23:02:11 +00:00
use tokio::{
net::UdpSocket,
sync::{
Mutex,
2025-08-10 22:08:10 +00:00
mpsc::{self, UnboundedReceiver, UnboundedSender},
},
2025-07-08 23:31:00 +00:00
task::JoinSet,
2025-07-06 23:02:11 +00:00
};
2025-07-06 21:15:08 +00:00
use transfer::Session;
2025-07-04 22:15:52 +00:00
pub const DEFAULT_PORT: u16 = 53317;
pub const MULTICAST_IP: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 167);
pub const LISTENING_SOCKET_ADDR: SocketAddrV4 =
SocketAddrV4::new(Ipv4Addr::from_bits(0), DEFAULT_PORT);
2025-08-14 00:48:55 +00:00
pub const DEFAULT_INTERVAL: Duration = Duration::from_millis(200);
2025-08-14 04:05:30 +00:00
pub type Peers = Arc<Mutex<BTreeMap<String, (SocketAddr, Device)>>>;
pub type Sessions = Arc<Mutex<BTreeMap<String, Session>>>; // Session ID to Session
2025-07-08 23:31:00 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2025-08-14 00:48:55 +00:00
pub enum JocalTasks {
2025-07-08 23:31:00 +00:00
Udp,
Http,
Multicast,
2025-08-14 00:48:55 +00:00
Tick,
2025-07-08 23:31:00 +00:00
}
2025-07-07 22:55:40 +00:00
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReceiveDialog {
Approve,
Deny,
}
2025-08-14 23:25:32 +00:00
#[derive(Debug)]
pub enum SendingType {
File(PathBuf),
Text(String),
}
2025-08-14 04:05:30 +00:00
#[derive(Debug, Clone, PartialEq, Eq)]
2025-08-14 00:48:55 +00:00
pub enum JocalEvent {
ReceivedInbound(Julid),
2025-08-14 04:05:30 +00:00
SendApproved(String),
SendDenied,
2025-08-14 23:25:32 +00:00
SendSuccess { content: String, session: String },
SendFailed { error: String },
Cancelled { session_id: Julid },
ReceiveRequest { id: Julid, request: ReceiveRequest },
2025-08-14 00:48:55 +00:00
Tick,
2025-08-01 16:16:05 +00:00
}
2025-08-07 00:11:20 +00:00
#[derive(Clone)]
pub struct ReceiveRequest {
2025-08-01 16:16:05 +00:00
pub alias: String,
pub files: BTreeMap<String, FileMetadata>,
pub tx: UnboundedSender<ReceiveDialog>,
}
2025-08-14 04:05:30 +00:00
impl PartialEq for ReceiveRequest {
fn eq(&self, other: &Self) -> bool {
self.alias == other.alias
}
}
impl Eq for ReceiveRequest {}
2025-08-07 00:11:20 +00:00
impl Debug for ReceiveRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReceiveRequest")
.field("alias", &self.alias)
.field("files", &self.files)
.finish()
}
}
2025-07-08 00:48:25 +00:00
/// Contains the main network and backend state for an application session.
2025-07-04 22:15:52 +00:00
#[derive(Clone)]
2025-08-06 21:09:37 +00:00
pub struct JocalService {
2025-08-14 04:05:30 +00:00
pub peers: Peers,
pub sessions: Sessions,
pub running_state: Arc<Mutex<RunningState>>,
pub socket: Arc<UdpSocket>,
2025-07-06 23:02:11 +00:00
pub client: reqwest::Client,
pub config: Config,
2025-08-14 23:25:32 +00:00
pub http_handle: Arc<OnceLock<axum_server::Handle>>,
// the receiving end will be held by the application so it can update the UI based on backend
// events
2025-08-14 00:48:55 +00:00
transfer_event_tx: UnboundedSender<JocalEvent>,
2025-07-04 22:15:52 +00:00
}
2025-08-06 21:09:37 +00:00
impl JocalService {
pub async fn new(
config: Config,
2025-08-14 00:48:55 +00:00
) -> crate::error::Result<(Self, UnboundedReceiver<JocalEvent>)> {
2025-08-10 22:08:10 +00:00
let (tx, rx) = mpsc::unbounded_channel();
let socket = UdpSocket::bind(LISTENING_SOCKET_ADDR).await?;
2025-07-04 00:00:11 +00:00
socket.set_multicast_loop_v4(true)?;
socket.set_multicast_ttl_v4(8)?; // 8 hops out from localnet
socket.join_multicast_v4(MULTICAST_IP, Ipv4Addr::from_bits(0))?;
2025-07-04 00:00:11 +00:00
let client = reqwest::ClientBuilder::new()
2025-08-03 23:43:25 +00:00
// localsend certs are self-signed
.danger_accept_invalid_certs(true)
.build()?;
2025-08-10 22:08:10 +00:00
Ok((
Self {
config,
client,
socket: socket.into(),
transfer_event_tx: tx,
peers: Default::default(),
sessions: Default::default(),
running_state: Default::default(),
http_handle: Default::default(),
},
rx,
))
2025-07-04 00:00:11 +00:00
}
2025-08-14 00:48:55 +00:00
pub async fn start(&self, handles: &mut JoinSet<JocalTasks>) {
let service = self.clone();
2025-08-14 00:48:55 +00:00
handles.spawn(async move {
if let Err(e) = service.start_http_server().await {
error!("HTTP server error: {e}");
2025-07-08 23:31:00 +00:00
}
2025-08-14 00:48:55 +00:00
JocalTasks::Http
2025-07-08 23:31:00 +00:00
});
let service = self.clone();
2025-08-14 00:48:55 +00:00
handles.spawn(async move {
if let Err(e) = service.listen_multicast().await {
error!("UDP listener error: {e}");
}
JocalTasks::Multicast
});
let service = self.clone();
handles.spawn(async move {
let service = &service;
let mut tick = tokio::time::interval(DEFAULT_INTERVAL);
loop {
let rstate = service.running_state.lock().await;
if *rstate == RunningState::Stopping {
break;
2025-07-04 00:00:11 +00:00
}
2025-08-14 00:48:55 +00:00
tick.tick().await;
service
.transfer_event_tx
.send(JocalEvent::Tick)
.unwrap_or_else(|e| log::warn!("could not send tick event: {e:?}"));
2025-07-08 23:31:00 +00:00
}
2025-08-14 00:48:55 +00:00
JocalTasks::Tick
2025-07-08 23:31:00 +00:00
});
2025-07-04 00:00:11 +00:00
let service = self.clone();
2025-08-14 00:48:55 +00:00
handles.spawn(async move {
loop {
let rstate = service.running_state.lock().await;
if *rstate == RunningState::Stopping {
break;
}
if let Err(e) = service.announce(None).await {
error!("Announcement error: {e}");
2025-07-04 00:00:11 +00:00
}
2025-08-14 00:48:55 +00:00
tokio::time::sleep(Duration::from_secs(2)).await;
2025-07-08 23:31:00 +00:00
}
2025-08-14 00:48:55 +00:00
JocalTasks::Udp
2025-07-08 23:31:00 +00:00
});
2025-07-04 00:00:11 +00:00
}
2025-07-06 23:02:11 +00:00
pub async fn stop(&self) {
2025-07-15 22:46:13 +00:00
let mut rstate = self.running_state.lock().await;
*rstate = RunningState::Stopping;
log::info!("shutting down http server");
self.http_handle
2025-07-15 22:46:13 +00:00
.get()
.expect("missing http handle for shutdown")
.graceful_shutdown(Some(Duration::from_secs(5)));
2025-07-06 23:02:11 +00:00
}
2025-07-04 00:00:11 +00:00
pub async fn refresh_peers(&self) {
let mut peers = self.peers.lock().await;
peers.clear();
}
2025-07-28 20:46:50 +00:00
2025-08-14 00:48:55 +00:00
pub fn send_event(&self, event: JocalEvent) {
2025-08-01 23:59:31 +00:00
if let Err(e) = self.transfer_event_tx.send(event.clone()) {
error!("got error sending transfer event '{event:?}': {e:?}");
}
}
2025-07-04 00:00:11 +00:00
}
2025-07-06 23:02:11 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunningState {
Running,
Stopping,
}
impl Default for RunningState {
fn default() -> Self {
Self::Running
}
}