freeswitch_log_parser/fields/
collect.rs1use std::net::IpAddr;
5use std::ops::Range;
6
7use crate::message::{
8 classify_message, dialplan_parts, execute_parts, hangup_channel, new_channel_name,
9 paren_channel, parse_bracketed_value, set_export_parts, sip_invite_direction,
10 strip_channel_prefix, MessageKind, SipInviteDirection,
11};
12use crate::uuid::find_uuids;
13
14use super::kind::{kind_rank, Field, FieldKind, FieldLocation};
15use super::processing::processing_parts;
16use super::subslice_range;
17
18fn bracketed_ip(token: &str) -> Option<&str> {
22 let inner = token.strip_prefix('[')?;
23 let close = inner.find(']')?;
24 let addr = &inner[..close];
25 addr.parse::<IpAddr>().ok().map(|_| addr)
26}
27
28fn channel_host_ip(channel: &str) -> Option<&str> {
31 let host = channel.rsplit_once('@')?.1;
32
33 if host.starts_with('[') {
34 return bracketed_ip(host);
35 }
36
37 if host.parse::<IpAddr>().is_ok() {
38 return Some(host);
39 }
40
41 let addr = host.rsplit_once(':')?.0;
44 addr.parse::<std::net::Ipv4Addr>().ok().map(|_| addr)
45}
46
47fn invite_source_addr(rest: &str) -> Option<&str> {
53 let after = rest.split_once("receiving invite from ")?.1;
54 let token = after.split_whitespace().next()?;
55
56 if token.starts_with('[') {
57 return bracketed_ip(token);
58 }
59
60 let addr = token.rsplit_once(':').map(|(a, _)| a).unwrap_or(token);
61 addr.parse::<IpAddr>().ok().map(|_| addr)
62}
63pub fn message_fields(msg: &str) -> Vec<Field> {
73 let mut out = Vec::new();
74 collect_typed(msg, &mut out);
75
76 for (start, uuid) in find_uuids(msg) {
79 let range = start..start + uuid.len();
80 if !out.iter().any(|f: &Field| intersects(&f.range, &range)) {
81 push(&mut out, FieldKind::Uuid, range);
82 }
83 }
84
85 sort_spans(&mut out);
86 out
87}
88
89fn sort_spans(fields: &mut [Field]) {
92 fields.sort_by(|a, b| {
93 (
94 a.range.start,
95 std::cmp::Reverse(a.range.end),
96 kind_rank(a.kind),
97 )
98 .cmp(&(
99 b.range.start,
100 std::cmp::Reverse(b.range.end),
101 kind_rank(b.kind),
102 ))
103 });
104}
105
106fn intersects(a: &Range<usize>, b: &Range<usize>) -> bool {
107 a.start < b.end && b.start < a.end
108}
109
110pub(super) fn raw_line_fields(line: &str, at: FieldLocation) -> Vec<Field> {
117 let message = crate::line::parse_line(line).message;
118 debug_assert!(line.ends_with(message), "message is a suffix of its line");
119 let offset = line.len() - message.len();
120
121 let mut out: Vec<Field> = message_fields(message)
122 .into_iter()
123 .map(|f| Field {
124 kind: f.kind,
125 at,
126 range: f.range.start + offset..f.range.end + offset,
127 })
128 .collect();
129
130 for (start, uuid) in find_uuids(&line[..offset]) {
132 out.push(Field {
133 kind: FieldKind::Uuid,
134 at,
135 range: start..start + uuid.len(),
136 });
137 }
138
139 sort_spans(&mut out);
140 out
141}
142fn push(out: &mut Vec<Field>, kind: FieldKind, range: Range<usize>) {
143 if !range.is_empty() {
144 out.push(Field {
145 kind,
146 at: FieldLocation::Message,
147 range,
148 });
149 }
150}
151
152fn push_channel(out: &mut Vec<Field>, msg: &str, channel: &str) {
154 let Some(range) = subslice_range(msg, channel) else {
155 return;
156 };
157 if let Some(host) = channel_host_ip(channel).and_then(|h| subslice_range(msg, h)) {
158 push(out, FieldKind::IpAddr, host);
159 }
160 push(out, FieldKind::ChannelName, range);
161}
162
163fn collect_typed(msg: &str, out: &mut Vec<Field>) {
164 match classify_message(msg) {
167 MessageKind::Execute { .. } => push_channel(out, msg, execute_parts(msg).channel),
168 MessageKind::Dialplan { .. } => collect_dialplan(msg, out),
169 MessageKind::Variable { .. } => {
170 if let Some(channel) = set_export_parts(msg).and_then(|p| p.channel) {
171 push_channel(out, msg, channel);
172 }
173 }
174 MessageKind::ChannelField { name, .. } => collect_channel_field(msg, &name, out),
175 MessageKind::SipInvite { direction, .. } => collect_invite(msg, direction, out),
176 MessageKind::StateChange { .. } | MessageKind::Media { .. } => {
177 collect_channel_prefixed(msg, out);
178 }
179 MessageKind::ChannelLifecycle { .. } if !collect_channel_prefixed(msg, out) => {
180 if let Some(channel) = hangup_channel(msg).or_else(|| new_channel_name(msg)) {
181 push_channel(out, msg, channel);
182 }
183 }
184 _ => {}
185 }
186}
187
188fn collect_channel_prefixed(msg: &str, out: &mut Vec<Field>) -> bool {
190 match strip_channel_prefix(msg) {
191 Some((channel, _)) => {
192 push_channel(out, msg, channel);
193 true
194 }
195 None => match paren_channel(msg) {
196 Some(channel) => {
197 push_channel(out, msg, channel);
198 true
199 }
200 None => false,
201 },
202 }
203}
204
205fn collect_dialplan(msg: &str, out: &mut Vec<Field>) {
206 if msg.starts_with("Dialplan: ") || msg.starts_with("Chatplan: ") {
207 push_channel(out, msg, dialplan_parts(msg).0);
208 return;
209 }
210 if let Some(parts) = processing_parts(msg) {
213 if let Some(name) = parts.name {
214 push(out, FieldKind::CallerIdName, name);
215 }
216 if let Some(number) = parts.number {
217 push(out, FieldKind::CallerIdNumber, number);
218 }
219 push(out, FieldKind::DestinationNumber, parts.dest);
220 }
221}
222
223fn collect_channel_field(msg: &str, name: &str, out: &mut Vec<Field>) {
224 let kind = match name {
225 "Channel-Name" => FieldKind::ChannelName,
226 "Caller-Caller-ID-Name" => FieldKind::CallerIdName,
227 "Caller-Caller-ID-Number" => FieldKind::CallerIdNumber,
228 "Caller-Destination-Number" => FieldKind::DestinationNumber,
229 _ => return,
230 };
231 let Some((_, value)) = parse_bracketed_value(msg, 0) else {
232 return;
233 };
234 if kind == FieldKind::ChannelName {
235 push_channel(out, msg, value);
236 } else if let Some(range) = subslice_range(msg, value) {
237 push(out, kind, range);
238 }
239}
240
241fn collect_invite(msg: &str, direction: SipInviteDirection, out: &mut Vec<Field>) {
242 let Some((channel, rest)) = strip_channel_prefix(msg) else {
243 return;
244 };
245 push_channel(out, msg, channel);
246
247 if sip_invite_direction(rest).is_none() {
248 return;
249 }
250 if let Some(range) = crate::message::call_id_token(rest).and_then(|t| subslice_range(msg, t)) {
251 push(out, FieldKind::CallId, range);
252 }
253 if direction == SipInviteDirection::Receiving {
254 if let Some(range) = invite_source_addr(rest).and_then(|a| subslice_range(msg, a)) {
255 push(out, FieldKind::IpAddr, range);
256 }
257 }
258}