use crate::errors::{Result, SessionError};
use rvoip_infra_common::events::{
coordinator::GlobalEventCoordinator,
cross_crate::{DialogToSessionEvent, RvoipCrossCrateEvent, SessionToDialogEvent},
};
use rvoip_sip_registrar::{
AddressOfRecord, ContactInfo, ContactReachability, RegistrarService, Transport,
};
use std::sync::Arc;
use tracing::{debug, info, warn};
pub struct RegistrationAdapter {
registrar: Arc<RegistrarService>,
global_coordinator: Arc<GlobalEventCoordinator>,
}
impl RegistrationAdapter {
pub fn new(
registrar: Arc<RegistrarService>,
global_coordinator: Arc<GlobalEventCoordinator>,
) -> Self {
Self {
registrar,
global_coordinator,
}
}
async fn handle_incoming_register(
&self,
transaction_id: String,
from_uri: String,
contact_uri: String,
expires: u32,
authorization: Option<String>,
) -> Result<()> {
info!("🔐 Handling incoming REGISTER from {}", from_uri);
let aor = Self::extract_aor(&from_uri)?;
let username = aor.user().to_string();
debug!("Extracted username: {}", username);
let (should_register, www_auth_challenge) = self
.registrar
.authenticate_register(&username, authorization.as_deref(), "REGISTER", &from_uri)
.await
.map_err(|e| SessionError::RegistrationFailed(e.to_string()))?;
if should_register {
info!("✅ Authentication successful for {}", username);
let contact = ContactInfo {
uri: contact_uri.clone(),
instance_id: uuid::Uuid::new_v4().to_string(),
transport: Transport::UDP,
user_agent: "rvoip-sip".to_string(),
expires: chrono::Utc::now()
+ chrono::Duration::try_seconds(expires as i64)
.unwrap_or_else(|| chrono::Duration::seconds(3600)),
q_value: 1.0,
received: None,
path: Vec::new(),
methods: vec!["INVITE".to_string(), "ACK".to_string(), "BYE".to_string()],
reg_id: None,
flow_id: None,
reachability: ContactReachability::Unknown,
};
self.registrar
.register_aor(&aor, contact, Some(expires))
.await
.map_err(|e| SessionError::RegistrationFailed(e.to_string()))?;
let response_event =
RvoipCrossCrateEvent::SessionToDialog(SessionToDialogEvent::SendRegisterResponse {
transaction_id,
status_code: 200,
reason: "OK".to_string(),
www_authenticate: None,
contact: Some(contact_uri),
expires: Some(expires),
min_expires: None,
service_route: Vec::new(),
path_echo: false,
associated_uri: Vec::new(),
extra_headers: Vec::new(),
});
self.global_coordinator
.publish(Arc::new(response_event))
.await
.map_err(|e| {
SessionError::InternalError(format!("Failed to publish 200 OK: {}", e))
})?;
info!("✅ User {} registered, sent 200 OK", username);
} else {
info!("🔐 Sending 401 challenge for {}", username);
let response_event =
RvoipCrossCrateEvent::SessionToDialog(SessionToDialogEvent::SendRegisterResponse {
transaction_id,
status_code: 401,
reason: "Unauthorized".to_string(),
www_authenticate: www_auth_challenge,
contact: None,
expires: None,
min_expires: None,
service_route: Vec::new(),
path_echo: false,
associated_uri: Vec::new(),
extra_headers: Vec::new(),
});
self.global_coordinator
.publish(Arc::new(response_event))
.await
.map_err(|e| {
SessionError::InternalError(format!("Failed to publish 401: {}", e))
})?;
info!("✅ Sent 401 challenge for {}", username);
}
Ok(())
}
pub async fn start(self: Arc<Self>) -> Result<()> {
info!("🎬 Starting RegistrationAdapter - subscribing to dialog_to_session events");
let mut receiver = self
.global_coordinator
.subscribe("dialog_to_session")
.await
.map_err(|e| SessionError::InternalError(format!("Subscribe failed: {}", e)))?;
let handler = self.clone();
tokio::spawn(async move {
info!("🔔 RegistrationAdapter event loop started");
loop {
match receiver.recv().await {
Some(event_arc) => {
if let Some(concrete) =
event_arc.as_any().downcast_ref::<RvoipCrossCrateEvent>()
{
if let RvoipCrossCrateEvent::DialogToSession(
DialogToSessionEvent::IncomingRegister {
transaction_id,
from_uri,
contact_uri,
expires,
authorization,
..
},
) = concrete
{
debug!("📩 Received IncomingRegister for {}", from_uri);
if let Err(e) = handler
.handle_incoming_register(
transaction_id.clone(),
from_uri.clone(),
contact_uri.clone(),
*expires,
authorization.clone(),
)
.await
{
warn!("Failed to handle REGISTER: {}", e);
}
}
}
}
None => {
debug!("RegistrationAdapter event channel closed");
break;
}
}
}
info!("🛑 RegistrationAdapter event loop stopped");
});
info!("✅ RegistrationAdapter started and subscribed to dialog_to_session events");
Ok(())
}
fn extract_aor(uri: &str) -> Result<AddressOfRecord> {
AddressOfRecord::parse(uri)
.map_err(|error| SessionError::InvalidInput(format!("Invalid AOR: {error}")))
}
}