freeswitch_log_parser/fields/
collect.rs1use std::net::IpAddr;
5use std::ops::Range;
6use std::str::FromStr;
7
8use freeswitch_types::ChannelVariable;
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::{kind_rank, 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]) {
98 fields.sort_by(|a, b| {
99 (
100 a.range.start,
101 std::cmp::Reverse(a.range.end),
102 kind_rank(a.kind),
103 )
104 .cmp(&(
105 b.range.start,
106 std::cmp::Reverse(b.range.end),
107 kind_rank(b.kind),
108 ))
109 });
110}
111
112fn intersects(a: &Range<usize>, b: &Range<usize>) -> bool {
113 a.start < b.end && b.start < a.end
114}
115
116pub(super) fn raw_line_fields(line: &str, at: FieldLocation) -> Vec<Field> {
123 let message = crate::line::parse_line(line).message;
124 debug_assert!(line.ends_with(message), "message is a suffix of its line");
125 let offset = line.len() - message.len();
126
127 let mut out: Vec<Field> = message_fields(message)
128 .into_iter()
129 .map(|f| Field {
130 kind: f.kind,
131 at,
132 range: f.range.start + offset..f.range.end + offset,
133 })
134 .collect();
135
136 for (start, uuid) in find_uuids(&line[..offset]) {
138 out.push(Field {
139 kind: FieldKind::Uuid,
140 at,
141 range: start..start + uuid.len(),
142 });
143 }
144
145 sort_spans(&mut out);
146 out
147}
148fn push(out: &mut Vec<Field>, kind: FieldKind, range: Range<usize>) {
149 if !range.is_empty() {
150 out.push(Field {
151 kind,
152 at: FieldLocation::Message,
153 range,
154 });
155 }
156}
157
158fn push_channel(out: &mut Vec<Field>, msg: &str, channel: &str) {
160 let Some(range) = subslice_range(msg, channel) else {
161 return;
162 };
163 if let Some(host) = channel_host_ip(channel).and_then(|h| subslice_range(msg, h)) {
164 push(out, FieldKind::IpAddr, host);
165 }
166 push(out, FieldKind::ChannelName, range);
167}
168
169fn variable_value_kind(name: &str) -> FieldKind {
172 let bare = name.strip_prefix("variable_").unwrap_or(name);
173 match ChannelVariable::from_str(bare) {
174 Ok(ChannelVariable::CallerIdName)
175 | Ok(ChannelVariable::EffectiveCallerIdName)
176 | Ok(ChannelVariable::OriginationCallerIdName) => FieldKind::CallerIdName,
177 Ok(ChannelVariable::CallerIdNumber)
178 | Ok(ChannelVariable::EffectiveCallerIdNumber)
179 | Ok(ChannelVariable::OriginationCallerIdNumber) => FieldKind::CallerIdNumber,
180 Ok(ChannelVariable::DestinationNumber) => FieldKind::DestinationNumber,
181 _ => FieldKind::VariableValue,
182 }
183}
184
185fn collect_variable(msg: &str, name: &str, out: &mut Vec<Field>) {
188 let push_value = |out: &mut Vec<Field>, value: &str| {
189 if let Some(range) = subslice_range(msg, value) {
190 push(out, variable_value_kind(name), range);
191 }
192 };
193 if msg.starts_with("variable_") {
194 if let Some((_, value)) = parse_bracketed_value(msg, 0) {
195 push_value(out, value);
196 }
197 return;
198 }
199 if let Some((channel, rest)) = strip_channel_prefix(msg) {
200 if is_channel_variable_narration(rest) {
201 if let Some(parts) = set_export_parts(rest) {
202 push_channel(out, msg, channel);
203 push_value(out, parts.value);
204 }
205 }
206 return;
207 }
208 if msg.starts_with("SET ")
209 || msg.starts_with("EXPORT ")
210 || msg.starts_with("PUSH ")
211 || msg.starts_with("UNSHIFT ")
212 {
213 if let Some(parts) = set_export_parts(msg) {
214 if let Some(channel) = parts.channel {
215 push_channel(out, msg, channel);
216 }
217 push_value(out, parts.value);
218 }
219 return;
220 }
221 if let Some(rest) = msg.strip_prefix("CoreSession::setVariable(") {
222 if let Some(inner) = rest.strip_suffix(')') {
223 if let Some(comma) = inner.find(", ") {
224 push_value(out, &inner[comma + 2..]);
225 }
226 }
227 return;
228 }
229 if let Some(rest) = msg.strip_prefix("set variable ") {
230 if let Some((_, value)) = rest.split_once('=') {
231 push_value(out, value);
232 }
233 }
234}
235
236fn collect_typed(msg: &str, out: &mut Vec<Field>) {
237 match classify_message(msg) {
240 MessageKind::Execute { .. } => push_channel(out, msg, execute_parts(msg).channel),
241 MessageKind::Dialplan { .. } => collect_dialplan(msg, out),
242 MessageKind::Variable { name, .. } => collect_variable(msg, &name, out),
243 MessageKind::ChannelField { name, .. } => collect_channel_field(msg, &name, out),
244 MessageKind::SipInvite { direction, .. } => collect_invite(msg, direction, out),
245 MessageKind::StateChange { .. } | MessageKind::Media { .. } => {
246 collect_channel_prefixed(msg, out);
247 }
248 MessageKind::ChannelLifecycle { .. } if !collect_channel_prefixed(msg, out) => {
249 if let Some(channel) = hangup_channel(msg).or_else(|| new_channel_name(msg)) {
250 push_channel(out, msg, channel);
251 }
252 }
253 _ => {}
254 }
255}
256
257fn collect_channel_prefixed(msg: &str, out: &mut Vec<Field>) -> bool {
259 match strip_channel_prefix(msg) {
260 Some((channel, _)) => {
261 push_channel(out, msg, channel);
262 true
263 }
264 None => match paren_channel(msg) {
265 Some(channel) => {
266 push_channel(out, msg, channel);
267 true
268 }
269 None => false,
270 },
271 }
272}
273
274fn collect_dialplan(msg: &str, out: &mut Vec<Field>) {
275 if msg.starts_with("Dialplan: ") || msg.starts_with("Chatplan: ") {
276 let (channel, detail) = dialplan_parts(msg);
277 push_channel(out, msg, channel);
278 if let Some(cond) = regex_condition_parts(detail) {
281 if let Some(range) = subslice_range(msg, cond.value) {
282 push(out, variable_value_kind(cond.field), range);
283 }
284 }
285 return;
286 }
287 if let Some(parts) = processing_parts(msg) {
290 if let Some(name) = parts.name {
291 push(out, FieldKind::CallerIdName, name);
292 }
293 if let Some(number) = parts.number {
294 push(out, FieldKind::CallerIdNumber, number);
295 }
296 push(out, FieldKind::DestinationNumber, parts.dest);
297 }
298}
299
300fn collect_channel_field(msg: &str, name: &str, out: &mut Vec<Field>) {
301 let kind = match name {
302 "Channel-Name" => FieldKind::ChannelName,
303 "Caller-Caller-ID-Name" => FieldKind::CallerIdName,
304 "Caller-Caller-ID-Number" => FieldKind::CallerIdNumber,
305 "Caller-Destination-Number" => FieldKind::DestinationNumber,
306 _ => FieldKind::VariableValue,
307 };
308 let Some((_, value)) = parse_bracketed_value(msg, 0) else {
309 return;
310 };
311 if kind == FieldKind::ChannelName {
312 push_channel(out, msg, value);
313 } else if let Some(range) = subslice_range(msg, value) {
314 push(out, kind, range);
315 }
316}
317
318fn collect_invite(msg: &str, direction: SipInviteDirection, out: &mut Vec<Field>) {
319 let Some((channel, rest)) = strip_channel_prefix(msg) else {
320 return;
321 };
322 push_channel(out, msg, channel);
323
324 if sip_invite_direction(rest).is_none() {
325 return;
326 }
327 if let Some(range) = crate::message::call_id_token(rest).and_then(|t| subslice_range(msg, t)) {
328 push(out, FieldKind::CallId, range);
329 }
330 if direction == SipInviteDirection::Receiving {
331 if let Some(range) = invite_source_addr(rest).and_then(|a| subslice_range(msg, a)) {
332 push(out, FieldKind::IpAddr, range);
333 }
334 }
335}