use crate::ast::*;
use crate::builders::WireContext;
use alloc::vec::Vec;
#[doc(alias = "SIP")]
#[doc(alias = "VOIP")]
#[doc(alias = "RFC3261")]
#[doc(alias = "INVITE")]
#[doc(alias = "CALL_ID")]
#[doc(alias = "CSEQ")]
pub fn build_sip_payload(
ctx: &WireContext,
spec: &SipSpec,
payload_bytes: &[u8],
) -> Vec<u8> {
let mut out = String::new();
let call_id = spec.call_id.unwrap_or("call123@pbx");
let cseq = spec.cseq.unwrap_or(1);
let cseq_method = spec.method.unwrap_or("INVITE");
if let Some(status) = spec.status {
let reason = spec.reason.unwrap_or("OK");
out.push_str(&format!("SIP/2.0 {} {}\r\n", status, reason));
if let Some(via) = spec.via {
out.push_str(&format!("Via: {}\r\n", via));
}
if let Some(from) = spec.from {
out.push_str(&format!("From: {}\r\n", from));
}
if let Some(to) = spec.to {
out.push_str(&format!("To: {}\r\n", to));
}
out.push_str(&format!(
"Call-ID: {}\r\nCSeq: {} {}\r\n",
call_id, cseq, cseq_method
));
if let Some(contact) = spec.contact {
if contact.starts_with('<') {
out.push_str(&format!("Contact: {}\r\n", contact));
} else {
out.push_str(&format!("Contact: <{}>\r\n", contact));
}
}
if let Some(ct) = spec.content_type {
out.push_str(&format!("Content-Type: {}\r\n", ct));
} else if !payload_bytes.is_empty() {
out.push_str("Content-Type: application/sdp\r\n");
}
if let Some(body) = spec.body {
out.push_str(&format!("Content-Length: {}\r\n\r\n{}", body.len(), body));
} else if !payload_bytes.is_empty() {
out.push_str(&format!("Content-Length: {}\r\n\r\n", payload_bytes.len()));
let mut bytes = out.into_bytes();
bytes.extend_from_slice(payload_bytes);
return bytes;
} else {
out.push_str("Content-Length: 0\r\n\r\n");
}
} else {
let method = spec.method.unwrap_or("INVITE");
let uri = spec.uri.unwrap_or("sip:service@pbx.local");
let from = spec.from.unwrap_or("sip:alice@pbx");
let to = spec.to.unwrap_or("sip:bob@pbx");
let via = spec
.via
.map(|v| v.to_string())
.unwrap_or_else(|| format!("SIP/2.0/UDP {}:5060;branch=z9hG4bK-1", ctx.src_ip));
out.push_str(&format!(
"{} {} SIP/2.0\r\nVia: {}\r\nFrom: <{}>;tag=1\r\nTo: <{}>\r\nCall-ID: {}\r\nCSeq: {} {}\r\n",
method, uri, via, from, to, call_id, cseq, method
));
if let Some(contact) = spec.contact {
if contact.starts_with('<') {
out.push_str(&format!("Contact: {}\r\n", contact));
} else {
out.push_str(&format!("Contact: <{}>\r\n", contact));
}
}
if let Some(ct) = spec.content_type {
out.push_str(&format!("Content-Type: {}\r\n", ct));
} else if !payload_bytes.is_empty() {
out.push_str("Content-Type: application/sdp\r\n");
}
if let Some(body) = spec.body {
out.push_str(&format!("Content-Length: {}\r\n\r\n{}", body.len(), body));
} else if !payload_bytes.is_empty() {
out.push_str(&format!("Content-Length: {}\r\n\r\n", payload_bytes.len()));
let mut bytes = out.into_bytes();
bytes.extend_from_slice(payload_bytes);
return bytes;
} else {
out.push_str("Content-Length: 0\r\n\r\n");
}
}
out.into_bytes()
}