From 0483588af50d0309a411420324e1cc92756d4814 Mon Sep 17 00:00:00 2001 From: Martin Yang Date: Sun, 29 Mar 2026 02:07:39 -0400 Subject: [PATCH] Support outbound call --- conf/autoload_configs/acl.conf.xml | 1 + did_router/src/database.rs | 29 ++++++++++ did_router/src/httapi.rs | 87 ++++++++++++++++++++---------- did_router/src/main.rs | 11 ++-- 4 files changed, 95 insertions(+), 33 deletions(-) diff --git a/conf/autoload_configs/acl.conf.xml b/conf/autoload_configs/acl.conf.xml index cd4a559..17424c1 100644 --- a/conf/autoload_configs/acl.conf.xml +++ b/conf/autoload_configs/acl.conf.xml @@ -29,6 +29,7 @@ + diff --git a/did_router/src/database.rs b/did_router/src/database.rs index 0f07c7e..7476988 100644 --- a/did_router/src/database.rs +++ b/did_router/src/database.rs @@ -127,3 +127,32 @@ pub fn list_did() ->Result, Error> { Ok(res) } + +pub fn from_pbx(ipaddr: &str) -> Result { + use crate::schema::dids::dsl::*; + + let pattern = format!("%{}%", ipaddr); + let mut conn = connect(); + let count = dids + .filter(target.is_not_null().and(target.ilike(&pattern))) + .count() + .get_result::(&mut conn)?; + + Ok(count > 0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validate_from_pbx() { + assert_eq!(Ok(true), from_pbx("172.16.0.213")); + assert_eq!(Ok(true), from_pbx("172.16.0.215")); + } + + #[test] + fn validate_not_from_pbx() { + assert_eq!(Ok(false), from_pbx("192.168.1.1")); + } +} diff --git a/did_router/src/httapi.rs b/did_router/src/httapi.rs index 60d05c4..e41761e 100644 --- a/did_router/src/httapi.rs +++ b/did_router/src/httapi.rs @@ -46,7 +46,7 @@ pub fn no_service(_did: Did) -> XMLElement { let no_service_path = format!("{}/no_service.wav", sound_path); let mut work = XMLElement::new("work"); - add_playback(&mut work, &no_service_path); + add_playback_element(&mut work, &no_service_path); work } @@ -58,7 +58,7 @@ pub fn night_mode(_did: Did) -> XMLElement { let mut work = XMLElement::new("work"); - add_playback(&mut work, &night_mode_path); + add_playback_element(&mut work, &night_mode_path); work } @@ -68,7 +68,19 @@ pub fn custom_message(_did: Did) -> XMLElement { work } -pub fn add_playback(work: &mut XMLElement ,file_path: &str) { +pub fn outbound(d: &str) -> XMLElement { + let mut work = XMLElement::new("work"); + let mut bridge = XMLElement::new("execute"); + + bridge.add_attribute("application", "bridge"); + bridge.add_attribute("data", format!("sofia/gateway/powernet_1/{}", d).as_str()); + + work.add_child(bridge).unwrap(); + + work +} + +pub fn add_playback_element(work: &mut XMLElement ,file_path: &str) { let mut answer = XMLElement::new("execute"); answer.add_attribute("application", "answer"); work.add_child(answer).unwrap(); @@ -78,9 +90,37 @@ pub fn add_playback(work: &mut XMLElement ,file_path: &str) { work.add_child(playback).unwrap(); } -pub fn route_call(did: String) -> Vec { - let d = db::get_did_by(&did).unwrap(); - let did_active = d.active; +pub fn build_work_element(did: &str, caller_ipaddr: &str) -> XMLElement { + if db::from_pbx(&caller_ipaddr).unwrap() == true { + outbound(did) + } else { + let d = db::get_did_by(&did).unwrap(); + let work = match d.target_type { + DidTargetType::Url => { + url(d) + }, + DidTargetType::Moh => { + moh(d) + }, + DidTargetType::ExternalNumber => { + external_number(d) + }, + DidTargetType::NoService => { + no_service(d) + }, + DidTargetType::NightMode => { + night_mode(d) + }, + DidTargetType::CustomMessage => { + custom_message(d) + } + }; + + work + } +} + +pub fn route_call(did: String, caller_ipaddr: String) -> Vec { let mut xml = XMLBuilder::new() .version(XMLVersion::XML1_1) .encoding("UTF-8".into()) @@ -90,35 +130,24 @@ pub fn route_call(did: String) -> Vec { doc.add_attribute("type", "xml/freeswitch-httapi"); let params = XMLElement::new("params"); - - let mut work = match d.target_type { - DidTargetType::Url => { - url(d) - }, - DidTargetType::Moh => { - moh(d) - }, - DidTargetType::ExternalNumber => { - external_number(d) - }, - DidTargetType::NoService => { - no_service(d) - }, - DidTargetType::NightMode => { - night_mode(d) - }, - DidTargetType::CustomMessage => { - custom_message(d) - } - }; + let mut work = build_work_element(&did, &caller_ipaddr); let mut hangup = XMLElement::new("execute"); hangup.add_attribute("application", "hangup"); work.add_child(hangup).unwrap(); doc.add_child(params).unwrap(); - if did_active == true { - doc.add_child(work).unwrap(); + + let d = db::get_did_by(&did); + match d { + Ok(d) => { + if d.active == true { + doc.add_child(work).unwrap(); + } + }, + Err(_) => { + doc.add_child(work).unwrap(); + } } xml.set_root_element(doc); diff --git a/did_router/src/main.rs b/did_router/src/main.rs index bce2a7a..4fbf1b8 100644 --- a/did_router/src/main.rs +++ b/did_router/src/main.rs @@ -17,7 +17,9 @@ use database::DidTargetType; #[derive(Debug, Deserialize)] struct RouteData { #[serde(rename = "Caller-Destination-Number")] - dest_did: String + dest_did: String, + #[serde(rename = "Caller-Network-Addr")] + caller_ipaddr: String } #[derive(Debug, Deserialize)] @@ -28,9 +30,10 @@ struct JsonDid { active: bool } -async fn route_did(data: web::Form) -> impl Responder { - let did = data.dest_did.clone(); - let xml = httapi::route_call(did); +async fn route_did(form_data: web::Form) -> impl Responder { + let did = form_data.dest_did.clone(); + let caller_ipaddr = form_data.caller_ipaddr.clone(); + let xml = httapi::route_call(did, caller_ipaddr); HttpResponse::Ok() .content_type(ContentType::xml()) .body(xml)