1use super::ber::{self, Tag, Tlv};
12use super::gsm::{self, Address};
13use serde_json::{json, Map, Value};
14
15pub type CapResult<T> = Result<T, String>;
16
17pub const OP_INITIAL_DP: i64 = 0;
18pub const OP_CONNECT: i64 = 20;
19pub const OP_RELEASE_CALL: i64 = 22;
20pub const OP_REQUEST_REPORT_BCSM_EVENT: i64 = 23;
21pub const OP_EVENT_REPORT_BCSM: i64 = 24;
22pub const OP_CONTINUE: i64 = 31;
23pub const OP_ACTIVITY_TEST: i64 = 55;
24
25pub const CONTEXTS: &[(&str, u64, &[u64])] = &[
27 ("capV2-gsmSSF-to-gsmSCF", 2, &[0, 4, 0, 0, 1, 0, 50, 1]),
28 ("capV3-gsmSSF-scfGeneric", 3, &[0, 4, 0, 0, 1, 21, 3, 4]),
29 ("capV4-gsmSSF-scfGeneric", 4, &[0, 4, 0, 0, 1, 23, 3, 4]),
30];
31
32pub fn context_of(oid: &[u64]) -> Option<(&'static str, u64)> {
33 CONTEXTS
34 .iter()
35 .find(|(_, _, o)| *o == oid)
36 .map(|(n, v, _)| (*n, *v))
37}
38
39pub fn context_for_phase(phase: u64) -> Option<&'static [u64]> {
40 CONTEXTS
41 .iter()
42 .find(|(_, v, _)| *v == phase)
43 .map(|(_, _, o)| *o)
44}
45
46pub const OPERATIONS: &[(&str, i64)] = &[
47 ("initialDP", OP_INITIAL_DP),
48 ("connect", OP_CONNECT),
49 ("releaseCall", OP_RELEASE_CALL),
50 ("requestReportBCSMEvent", OP_REQUEST_REPORT_BCSM_EVENT),
51 ("eventReportBCSM", OP_EVENT_REPORT_BCSM),
52 ("continue", OP_CONTINUE),
53 ("activityTest", OP_ACTIVITY_TEST),
54];
55
56pub fn operation_name(code: i64) -> Option<&'static str> {
57 OPERATIONS.iter().find(|(_, c)| *c == code).map(|(n, _)| *n)
58}
59
60pub fn operation_code(name: &str) -> Option<i64> {
61 let norm = |s: &str| s.replace(['-', '_'], "").to_ascii_lowercase();
62 OPERATIONS
63 .iter()
64 .find(|(n, _)| norm(n) == norm(name))
65 .map(|(_, c)| *c)
66}
67
68pub const EVENT_TYPES: &[(&str, i64)] = &[
70 ("collectedInfo", 2),
71 ("analyzedInformation", 3),
72 ("routeSelectFailure", 4),
73 ("oCalledPartyBusy", 5),
74 ("oNoAnswer", 6),
75 ("oAnswer", 7),
76 ("oMidCall", 8),
77 ("oDisconnect", 9),
78 ("oAbandon", 10),
79 ("termAttemptAuthorized", 12),
80 ("tBusy", 13),
81 ("tNoAnswer", 14),
82 ("tAnswer", 15),
83 ("tMidCall", 16),
84 ("tDisconnect", 17),
85 ("tAbandon", 18),
86 ("oTermSeized", 19),
87 ("callAccepted", 27),
88 ("oChangeOfPosition", 28),
89 ("tChangeOfPosition", 29),
90 ("oServiceChange", 30),
91 ("tServiceChange", 31),
92];
93
94pub const MONITOR_MODES: &[&str] = &["interrupted", "notifyAndContinue", "transparent"];
95
96pub const CAP_ERRORS: &[(&str, i64)] = &[
97 ("canceled", 0),
98 ("cancelFailed", 1),
99 ("eTCFailed", 3),
100 ("improperCallerResponse", 4),
101 ("missingCustomerRecord", 6),
102 ("missingParameter", 7),
103 ("parameterOutOfRange", 8),
104 ("requestedInfoError", 10),
105 ("systemFailure", 11),
106 ("taskRefused", 12),
107 ("unavailableResource", 13),
108 ("unexpectedComponentSequence", 14),
109 ("unexpectedDataValue", 15),
110 ("unexpectedParameter", 16),
111 ("unknownLegID", 17),
112];
113
114pub fn error_name(code: i64) -> String {
115 CAP_ERRORS
116 .iter()
117 .find(|(_, c)| *c == code)
118 .map(|(n, _)| n.to_string())
119 .unwrap_or_else(|| format!("error{code}"))
120}
121
122fn event_name(v: i64) -> Value {
123 EVENT_TYPES
124 .iter()
125 .find(|(_, c)| *c == v)
126 .map(|(n, _)| json!(n))
127 .unwrap_or_else(|| json!(v))
128}
129
130fn event_value(v: &Value, field: &str) -> CapResult<i64> {
131 if let Some(n) = v.as_i64() {
132 if EVENT_TYPES.iter().any(|(_, c)| *c == n) {
133 return Ok(n);
134 }
135 }
136 let s = v.as_str().unwrap_or_default();
137 EVENT_TYPES
138 .iter()
139 .find(|(n, _)| n.eq_ignore_ascii_case(s))
140 .map(|(_, c)| *c)
141 .ok_or_else(|| {
142 format!(
143 "{field} must be one of {}",
144 EVENT_TYPES
145 .iter()
146 .map(|(n, _)| *n)
147 .collect::<Vec<_>>()
148 .join(", ")
149 )
150 })
151}
152
153fn ctx(n: u32) -> Tag {
154 Tag::context(n, false)
155}
156
157fn ctxc(n: u32) -> Tag {
158 Tag::context(n, true)
159}
160
161fn find(kids: &[Tlv], tag: Tag) -> Option<&Tlv> {
162 kids.iter().find(|c| c.tag == tag)
163}
164
165pub fn decode_argument(code: i64, p: Option<&Tlv>) -> CapResult<Value> {
167 match code {
168 OP_CONTINUE | OP_ACTIVITY_TEST => return Ok(json!({})),
169 OP_RELEASE_CALL => {
170 let p = p.ok_or("releaseCall without a cause")?;
171 return Ok(json!({"cause": gsm::cause_decode(&p.value)}));
172 }
173 _ => {}
174 }
175 let p = p.ok_or("the operation's argument is missing")?;
176 let kids = &p.children;
177 let mut out = Map::new();
178 let used: Vec<Tag> = match code {
179 OP_INITIAL_DP => {
180 out.insert(
181 "service_key".into(),
182 json!(find(kids, ctx(0))
183 .ok_or("serviceKey [0] missing")?
184 .as_i64()
185 .map_err(|e| e.0)?),
186 );
187 let num = |n: u32| find(kids, ctx(n)).map(|t| gsm::isup_number_decode(&t.value));
188 out.insert("called_party_number".into(), json!(num(2)));
189 out.insert("calling_party_number".into(), json!(num(3)));
190 out.insert(
191 "calling_partys_category".into(),
192 json!(find(kids, ctx(5)).and_then(|t| t.value.first().copied())),
193 );
194 out.insert("location_number".into(), json!(num(10)));
195 out.insert(
196 "event_type_bcsm".into(),
197 find(kids, ctx(28))
198 .and_then(|t| t.as_i64().ok())
199 .map(event_name)
200 .unwrap_or(Value::Null),
201 );
202 out.insert(
203 "imsi".into(),
204 json!(find(kids, ctx(50)).map(|t| gsm::tbcd_decode(&t.value))),
205 );
206 out.insert(
207 "call_reference_number".into(),
208 json!(find(kids, ctx(54)).map(|t| ber::hex(&t.value))),
209 );
210 let addr = |n: u32| {
211 find(kids, ctx(n))
212 .and_then(|t| Address::decode(&t.value).ok())
213 .map(|a| a.to_json())
214 };
215 out.insert("msc_address".into(), json!(addr(55)));
216 out.insert("called_party_bcd_number".into(), json!(addr(56)));
217 out.insert(
218 "location_information_present".into(),
219 json!(find(kids, ctxc(52)).is_some()),
220 );
221 vec![
222 ctx(0),
223 ctx(2),
224 ctx(3),
225 ctx(5),
226 ctx(10),
227 ctx(28),
228 ctx(50),
229 ctxc(52),
230 ctx(54),
231 ctx(55),
232 ctx(56),
233 ]
234 }
235 OP_CONNECT => {
236 let dra = find(kids, ctxc(0)).ok_or("destinationRoutingAddress [0] missing")?;
237 out.insert(
238 "destination_routing_address".into(),
239 json!(dra
240 .children
241 .iter()
242 .map(|n| gsm::isup_number_decode(&n.value))
243 .collect::<Vec<_>>()),
244 );
245 out.insert(
246 "suppression_of_announcement".into(),
247 json!(find(kids, ctx(55)).is_some()),
248 );
249 vec![ctxc(0), ctx(55)]
250 }
251 OP_REQUEST_REPORT_BCSM_EVENT => {
252 let events = find(kids, ctxc(0)).ok_or("bcsmEvents [0] missing")?;
253 if events.children.len() > 30 {
254 return Err("more than 30 BCSM events".into());
255 }
256 let mut list = Vec::new();
257 for e in &events.children {
258 let ev = find(&e.children, ctx(0)).and_then(|t| t.as_i64().ok());
259 let mode = find(&e.children, ctx(1)).and_then(|t| t.as_i64().ok());
260 let leg = find(&e.children, ctxc(2))
261 .and_then(|l| l.children.first())
262 .map(|c| {
263 json!({
264 "side": if c.tag.number == 0 { "sending" } else { "receiving" },
265 "leg_type": c.value.first().copied(),
266 })
267 });
268 list.push(json!({
269 "event_type_bcsm": ev.map(event_name),
270 "monitor_mode": mode.and_then(|m| MONITOR_MODES.get(m as usize).copied()),
271 "leg_id": leg,
272 }));
273 }
274 out.insert("bcsm_events".into(), json!(list));
275 vec![ctxc(0)]
276 }
277 OP_EVENT_REPORT_BCSM => {
278 out.insert(
279 "event_type_bcsm".into(),
280 find(kids, ctx(0))
281 .and_then(|t| t.as_i64().ok())
282 .map(event_name)
283 .ok_or("eventTypeBCSM [0] missing")?,
284 );
285 out.insert(
286 "leg_id".into(),
287 json!(find(kids, ctxc(3))
288 .and_then(|l| l.children.first())
289 .and_then(|c| c.value.first().copied())),
290 );
291 out.insert(
292 "message_type".into(),
293 json!(match find(kids, ctxc(4))
294 .and_then(|m| m.ctx(0))
295 .and_then(|t| t.as_i64().ok())
296 {
297 Some(1) => "notification",
298 _ => "request",
299 }),
300 );
301 out.insert(
302 "event_specific_information_present".into(),
303 json!(find(kids, ctxc(2)).is_some()),
304 );
305 vec![ctx(0), ctxc(2), ctxc(3), ctxc(4)]
306 }
307 other => return Err(format!("CAP operation {other} is not implemented")),
308 };
309 let rest: Vec<String> = kids
310 .iter()
311 .filter(|c| !used.contains(&c.tag))
312 .map(|c| c.tag.label())
313 .collect();
314 out.insert("uninterpreted".into(), json!(rest));
315 Ok(Value::Object(out))
316}
317
318pub fn encode_argument(code: i64, v: &Value) -> CapResult<Option<Vec<u8>>> {
320 Ok(Some(match code {
321 OP_CONTINUE | OP_ACTIVITY_TEST => return Ok(None),
322 OP_RELEASE_CALL => {
323 let cause = v
324 .get("cause")
325 .cloned()
326 .unwrap_or(json!("normal_unspecified"));
327 ber::tlv(Tag::OCTET_STRING, &gsm::cause_encode(&cause)?)
328 }
329 OP_CONNECT => {
330 let dra = v
331 .get("destination_routing_address")
332 .ok_or("destination_routing_address is required")?;
333 let first = match dra {
334 Value::Array(a) => {
335 if a.len() != 1 {
336 return Err(
337 "destination_routing_address holds exactly one number in CAP".into(),
338 );
339 }
340 a[0].clone()
341 }
342 other => other.clone(),
343 };
344 let mut parts = vec![ber::seq(
345 ctxc(0),
346 &[ber::tlv(
347 Tag::OCTET_STRING,
348 &gsm::isup_number_encode(&first, "destination_routing_address", false)?,
349 )],
350 )];
351 if v.get("suppression_of_announcement")
352 .and_then(Value::as_bool)
353 == Some(true)
354 {
355 parts.push(ber::tlv(ctx(55), &[]));
356 }
357 ber::seq(Tag::SEQUENCE, &parts)
358 }
359 OP_REQUEST_REPORT_BCSM_EVENT => {
360 let events = v
361 .get("bcsm_events")
362 .and_then(Value::as_array)
363 .ok_or("bcsm_events is required (a list)")?;
364 if events.is_empty() || events.len() > 30 {
365 return Err("bcsm_events must hold 1..30 events".into());
366 }
367 let mut list = Vec::new();
368 for (i, e) in events.iter().enumerate() {
369 let field = format!("bcsm_events[{i}]");
370 let ev = event_value(
371 e.get("event_type_bcsm").unwrap_or(&Value::Null),
372 &format!("{field}.event_type_bcsm"),
373 )?;
374 let mode = e
375 .get("monitor_mode")
376 .and_then(Value::as_str)
377 .unwrap_or("notifyAndContinue");
378 let m = MONITOR_MODES
379 .iter()
380 .position(|x| *x == mode)
381 .ok_or_else(|| {
382 format!(
383 "{field}.monitor_mode must be one of {}",
384 MONITOR_MODES.join(", ")
385 )
386 })?;
387 let mut parts = vec![ber::integer(ctx(0), ev), ber::integer(ctx(1), m as i64)];
388 let leg = e.get("leg").and_then(Value::as_u64).unwrap_or(match ev {
391 5..=7 | 9 => 2,
392 _ => 1,
393 });
394 if !(1..=2).contains(&leg) {
395 return Err(format!("{field}.leg must be 1 or 2"));
396 }
397 if ev != 2 && ev != 3 {
400 parts.push(ber::seq(ctxc(2), &[ber::tlv(ctx(0), &[leg as u8])]));
401 }
402 list.push(ber::seq(Tag::SEQUENCE, &parts));
403 }
404 ber::seq(Tag::SEQUENCE, &[ber::seq(ctxc(0), &list)])
405 }
406 OP_EVENT_REPORT_BCSM => {
407 let ev = event_value(
408 v.get("event_type_bcsm").unwrap_or(&Value::Null),
409 "event_type_bcsm",
410 )?;
411 let leg = v
412 .get("leg")
413 .and_then(Value::as_u64)
414 .unwrap_or(2)
415 .clamp(1, 2) as u8;
416 let notification =
417 v.get("message_type").and_then(Value::as_str) == Some("notification");
418 ber::seq(
419 Tag::SEQUENCE,
420 &[
421 ber::integer(ctx(0), ev),
422 ber::seq(ctxc(3), &[ber::tlv(ctx(1), &[leg])]),
423 ber::seq(ctxc(4), &[ber::integer(ctx(0), notification as i64)]),
424 ],
425 )
426 }
427 OP_INITIAL_DP => {
428 let sk = v
429 .get("service_key")
430 .and_then(Value::as_i64)
431 .filter(|k| (0..=2147483647).contains(k))
432 .ok_or("service_key is required (0..2147483647)")?;
433 let mut parts = vec![ber::integer(ctx(0), sk)];
434 if let Some(n) = v.get("called_party_number").filter(|x| !x.is_null()) {
435 parts.push(ber::tlv(
436 ctx(2),
437 &gsm::isup_number_encode(n, "called_party_number", false)?,
438 ));
439 }
440 if let Some(n) = v.get("calling_party_number").filter(|x| !x.is_null()) {
441 parts.push(ber::tlv(
442 ctx(3),
443 &gsm::isup_number_encode(n, "calling_party_number", true)?,
444 ));
445 }
446 parts.push(ber::tlv(
447 ctx(5),
448 &[v.get("calling_partys_category")
449 .and_then(Value::as_u64)
450 .unwrap_or(10)
451 .min(255) as u8],
452 ));
453 let ev = match v.get("event_type_bcsm") {
454 Some(e) => event_value(e, "event_type_bcsm")?,
455 None => 2,
456 };
457 parts.push(ber::integer(ctx(28), ev));
458 if let Some(i) = v.get("imsi").and_then(Value::as_str) {
459 parts.push(ber::tlv(ctx(50), &gsm::imsi_encode(i)?));
460 }
461 if let Some(a) = v.get("msc_address").filter(|x| !x.is_null()) {
462 let a = Address::from_json(a, "msc_address")?;
463 parts.push(ber::tlv(ctx(55), &a.encode(gsm::MAX_ISDN_ADDRESS_DIGITS)?));
464 }
465 if let Some(a) = v.get("called_party_bcd_number").filter(|x| !x.is_null()) {
466 let a = Address::from_json(a, "called_party_bcd_number")?;
467 parts.push(ber::tlv(ctx(56), &a.encode(40)?));
468 }
469 ber::seq(Tag::SEQUENCE, &parts)
470 }
471 other => return Err(format!("CAP operation {other} is not implemented")),
472 }))
473}