freeswitch_log_parser/fields/
collect.rs1use std::net::IpAddr;
5use std::ops::Range;
6use std::str::FromStr;
7
8use freeswitch_types::{ChannelVariable, EventHeader, VARIABLE_PREFIX};
9
10use crate::message::{
11 classify_message, dialplan_parts, execute_parts, hangup_channel, is_channel_variable_narration,
12 new_channel_name, paren_channel, parse_bracketed_value, regex_condition_parts,
13 set_export_parts, sip_invite_direction, strip_channel_prefix, MessageKind, SipInviteDirection,
14};
15use crate::uuid::find_uuids;
16
17use super::kind::{field_order, Field, FieldKind, FieldLocation};
18use super::processing::processing_parts;
19use super::subslice_range;
20
21fn bracketed_ip(token: &str) -> Option<&str> {
25 let inner = token.strip_prefix('[')?;
26 let close = inner.find(']')?;
27 let addr = &inner[..close];
28 addr.parse::<IpAddr>().ok().map(|_| addr)
29}
30
31fn channel_host_ip(channel: &str) -> Option<&str> {
34 let host = channel.rsplit_once('@')?.1;
35
36 if host.starts_with('[') {
37 return bracketed_ip(host);
38 }
39
40 if host.parse::<IpAddr>().is_ok() {
41 return Some(host);
42 }
43
44 let addr = host.rsplit_once(':')?.0;
47 addr.parse::<std::net::Ipv4Addr>().ok().map(|_| addr)
48}
49
50fn invite_source_addr(rest: &str) -> Option<&str> {
56 let after = rest.split_once("receiving invite from ")?.1;
57 let token = after.split_whitespace().next()?;
58
59 if token.starts_with('[') {
60 return bracketed_ip(token);
61 }
62
63 let addr = token.rsplit_once(':').map(|(a, _)| a).unwrap_or(token);
64 addr.parse::<IpAddr>().ok().map(|_| addr)
65}
66pub fn message_fields(msg: &str) -> Vec<Field> {
76 let mut out = Vec::new();
77 collect_typed(msg, &mut out);
78
79 for (start, uuid) in find_uuids(msg) {
82 let range = start..start + uuid.len();
83 let covered = out
84 .iter()
85 .any(|f: &Field| f.kind != FieldKind::VariableValue && intersects(&f.range, &range));
86 if !covered {
87 push(&mut out, FieldKind::Uuid, range);
88 }
89 }
90
91 sort_spans(&mut out);
92 out
93}
94
95fn sort_spans(fields: &mut [Field]) {
96 fields.sort_by(field_order);
97}
98
99fn intersects(a: &Range<usize>, b: &Range<usize>) -> bool {
100 a.start < b.end && b.start < a.end
101}
102
103pub(super) fn raw_line_fields(line: &str, at: FieldLocation) -> Vec<Field> {
110 let message = crate::line::parse_line(line).message;
111 debug_assert!(line.ends_with(message), "message is a suffix of its line");
112 let offset = line.len() - message.len();
113
114 let mut out: Vec<Field> = message_fields(message)
115 .into_iter()
116 .map(|f| Field {
117 kind: f.kind,
118 at,
119 range: f.range.start + offset..f.range.end + offset,
120 })
121 .collect();
122
123 for (start, uuid) in find_uuids(&line[..offset]) {
125 out.push(Field {
126 kind: FieldKind::Uuid,
127 at,
128 range: start..start + uuid.len(),
129 });
130 }
131
132 sort_spans(&mut out);
133 out
134}
135fn push(out: &mut Vec<Field>, kind: FieldKind, range: Range<usize>) {
136 if !range.is_empty() {
137 out.push(Field {
138 kind,
139 at: FieldLocation::Message,
140 range,
141 });
142 }
143}
144
145fn push_channel(out: &mut Vec<Field>, msg: &str, channel: &str) {
147 let Some(range) = subslice_range(msg, channel) else {
148 return;
149 };
150 if let Some(host) = channel_host_ip(channel).and_then(|h| subslice_range(msg, h)) {
151 push(out, FieldKind::IpAddr, host);
152 }
153 push(out, FieldKind::ChannelName, range);
154}
155
156fn variable_value_kind(name: &str) -> FieldKind {
159 match ChannelVariable::from_str(name) {
160 Ok(ChannelVariable::CallerIdName)
161 | Ok(ChannelVariable::EffectiveCallerIdName)
162 | Ok(ChannelVariable::OriginationCallerIdName) => FieldKind::CallerIdName,
163 Ok(ChannelVariable::CallerIdNumber)
164 | Ok(ChannelVariable::EffectiveCallerIdNumber)
165 | Ok(ChannelVariable::OriginationCallerIdNumber) => FieldKind::CallerIdNumber,
166 Ok(ChannelVariable::DestinationNumber) => FieldKind::DestinationNumber,
167 _ => FieldKind::VariableValue,
168 }
169}
170
171fn collect_variable(msg: &str, name: &str, out: &mut Vec<Field>) {
174 let push_value = |out: &mut Vec<Field>, value: &str| {
175 if let Some(range) = subslice_range(msg, value) {
176 push(out, variable_value_kind(name), range);
177 }
178 };
179 if msg.starts_with(VARIABLE_PREFIX) {
180 if let Some((_, value)) = parse_bracketed_value(msg, 0) {
181 push_value(out, value);
182 }
183 return;
184 }
185 if let Some((channel, rest)) = strip_channel_prefix(msg) {
186 if is_channel_variable_narration(rest) {
187 if let Some(parts) = set_export_parts(rest) {
188 push_channel(out, msg, channel);
189 push_value(out, parts.value);
190 }
191 }
192 return;
193 }
194 if msg.starts_with("SET ")
195 || msg.starts_with("EXPORT ")
196 || msg.starts_with("PUSH ")
197 || msg.starts_with("UNSHIFT ")
198 {
199 if let Some(parts) = set_export_parts(msg) {
200 if let Some(channel) = parts.channel {
201 push_channel(out, msg, channel);
202 }
203 push_value(out, parts.value);
204 }
205 return;
206 }
207 if let Some(rest) = msg.strip_prefix("CoreSession::setVariable(") {
208 if let Some(inner) = rest.strip_suffix(')') {
209 if let Some(comma) = inner.find(", ") {
210 push_value(out, &inner[comma + 2..]);
211 }
212 }
213 return;
214 }
215 if let Some(rest) = msg.strip_prefix("set variable ") {
216 if let Some((_, value)) = rest.split_once('=') {
217 push_value(out, value);
218 }
219 }
220}
221
222fn collect_typed(msg: &str, out: &mut Vec<Field>) {
223 match classify_message(msg) {
226 MessageKind::Execute { .. } => push_channel(out, msg, execute_parts(msg).channel),
227 MessageKind::Dialplan { .. } => collect_dialplan(msg, out),
228 MessageKind::Variable { name, .. } => collect_variable(msg, name.bare(), out),
229 MessageKind::ChannelField { name, .. } => collect_channel_field(msg, &name, out),
230 MessageKind::SipInvite { direction, .. } => collect_invite(msg, direction, out),
231 MessageKind::StateChange { .. } | MessageKind::Media { .. } => {
232 collect_channel_prefixed(msg, out);
233 }
234 MessageKind::ChannelLifecycle { .. } if !collect_channel_prefixed(msg, out) => {
235 if let Some(channel) = hangup_channel(msg).or_else(|| new_channel_name(msg)) {
236 push_channel(out, msg, channel);
237 }
238 }
239 _ => {}
240 }
241}
242
243fn collect_channel_prefixed(msg: &str, out: &mut Vec<Field>) -> bool {
245 match strip_channel_prefix(msg) {
246 Some((channel, _)) => {
247 push_channel(out, msg, channel);
248 true
249 }
250 None => match paren_channel(msg) {
251 Some(channel) => {
252 push_channel(out, msg, channel);
253 true
254 }
255 None => false,
256 },
257 }
258}
259
260fn collect_dialplan(msg: &str, out: &mut Vec<Field>) {
261 if msg.starts_with("Dialplan: ") || msg.starts_with("Chatplan: ") {
262 let (channel, detail) = dialplan_parts(msg);
263 push_channel(out, msg, channel);
264 if let Some(cond) = regex_condition_parts(detail) {
267 if let Some(range) = subslice_range(msg, cond.value) {
268 push(out, variable_value_kind(cond.field), range);
269 }
270 }
271 return;
272 }
273 if let Some(parts) = processing_parts(msg) {
276 if let Some(name) = parts.name {
277 push(out, FieldKind::CallerIdName, name);
278 }
279 if let Some(number) = parts.number {
280 push(out, FieldKind::CallerIdNumber, number);
281 }
282 push(out, FieldKind::DestinationNumber, parts.dest);
283 }
284}
285
286fn collect_channel_field(msg: &str, name: &str, out: &mut Vec<Field>) {
287 let kind = match EventHeader::from_str(name) {
288 Ok(EventHeader::ChannelName) => FieldKind::ChannelName,
289 Ok(EventHeader::CallerCallerIdName) => FieldKind::CallerIdName,
290 Ok(EventHeader::CallerCallerIdNumber) => FieldKind::CallerIdNumber,
291 Ok(EventHeader::CallerDestinationNumber) => FieldKind::DestinationNumber,
292 _ => FieldKind::VariableValue,
293 };
294 let Some((_, value)) = parse_bracketed_value(msg, 0) else {
295 return;
296 };
297 if kind == FieldKind::ChannelName {
298 push_channel(out, msg, value);
299 } else if let Some(range) = subslice_range(msg, value) {
300 push(out, kind, range);
301 }
302}
303
304fn collect_invite(msg: &str, direction: SipInviteDirection, out: &mut Vec<Field>) {
305 let Some((channel, rest)) = strip_channel_prefix(msg) else {
306 return;
307 };
308 push_channel(out, msg, channel);
309
310 if sip_invite_direction(rest).is_none() {
311 return;
312 }
313 if let Some(range) = crate::message::call_id_token(rest).and_then(|t| subslice_range(msg, t)) {
314 push(out, FieldKind::CallId, range);
315 }
316 if direction == SipInviteDirection::Receiving {
317 if let Some(range) = invite_source_addr(rest).and_then(|a| subslice_range(msg, a)) {
318 push(out, FieldKind::IpAddr, range);
319 }
320 }
321}