1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
15use std::num::NonZeroU16;
16
17use binrw::{BinRead, BinWrite};
18
19use super::capabilities::{CapabilityUpdate, CapabilityUpdateVariant};
20use super::catalog::{CodecSupport, MessageRoute};
21use super::values::{
22 AddParticipantResult, AlarmSeverity, AnnouncementPlayMode, AnnouncementPlayStatus,
23 AuditParticipantResult, BusyLampFieldState, ButtonType, CallHistoryDisposition, CallState,
24 Codec, ConferenceResourceType, CreateConferenceResult, DeleteConferenceResult, DeviceType,
25 Digit, DynamicCallInfoLayout, EchoCancellation, EncryptionMethod, EndOfAnnouncementAck,
26 G723BitRate, IpAddressType, KeyMode, LampMode, MediaPathCapability, MediaPathEvent,
27 MediaPathId, MediaStatus, MediaTransport, MediaType, MessageWaitingResult, MicrophoneMode,
28 ModifyConferenceResult, NotificationPriority, PartyInformationRestrictions, PhoneFeatures,
29 ProtocolVersion, QosDirection, QosErrorCode, QosReservationStyle, ResetType, RingDuration,
30 RingerMode, RsvpErrorCode, SilenceSuppression, SoftKey, SpeakerMode, StationSessionContext,
31 StatisticsProcessing, Stimulus, SubscriptionCause, Tone, ToneDirection,
32};
33use super::wire::{CodecError, Frame};
34use super::*;
35use crate::types::{
36 CallInfo, DateTemplate, DeviceId, LegacyCodePage, MediaEndpoint, MediaTrafficClass,
37 SoftKeyProfile,
38};
39
40mod fixed_text;
41mod io;
42use fixed_text::{WireFixedText, station_text_bytes};
43use io::{
44 decode, decode_prefix, decode_zero_padded, encode, usize_from_wire, validate_exact_payload,
45 validate_payload_bounds, validate_zero_payload, wire_count,
46};
47
48fn ensure_station_route(
49 frame: &Frame,
50 expected: MessageRoute,
51 expected_name: &'static str,
52) -> Result<(), CodecError> {
53 let Some(actual) = frame.message_type().route() else {
54 return Ok(());
55 };
56 if actual == expected {
57 Ok(())
58 } else {
59 Err(CodecError::UnexpectedRoute {
60 message_id: frame.message_id,
61 actual,
62 expected: expected_name,
63 })
64 }
65}
66
67fn preserve_known_message(frame: Frame, id: MessageId) -> Result<KnownOpaqueMessage, CodecError> {
68 ensure_preserve_only(id)?;
69 let payload = BoundedBytes::try_from(frame.payload).map_err(|error| {
70 CodecError::FrameTooLarge(error.actual.saturating_add(super::wire::HEADER_SIZE))
71 })?;
72 Ok(KnownOpaqueMessage {
73 id,
74 protocol_version: frame.protocol_version,
75 payload,
76 })
77}
78
79fn ensure_preserve_only(id: MessageId) -> Result<(), CodecError> {
80 if id
81 .contract()
82 .is_some_and(|contract| contract.codec == CodecSupport::OpaqueOnly)
83 {
84 Ok(())
85 } else {
86 Err(CodecError::InvalidValue {
87 message_id: id.wire_value(),
88 field: "opaque preservation requires an opaque-only contract",
89 value: u64::from(id.wire_value()),
90 })
91 }
92}
93
94fn pad_typed_payload(message_id: u32, payload: &mut Vec<u8>) {
95 use super::catalog::PayloadLayout;
96
97 let Some(contract) = MessageId::from(message_id).contract() else {
98 return;
99 };
100 if !matches!(
101 contract.payload_layout,
102 PayloadLayout::Opaque
103 | PayloadLayout::BoundedOpaque
104 | PayloadLayout::BoundedPreserved
105 | PayloadLayout::VersionAndLengthSelected
106 | PayloadLayout::MinimumLengthPreserved
107 ) {
108 pad_dynamic_payload(payload);
109 }
110}
111
112fn canonical_open_receive_wire(
113 call_reference: u32,
114 source_address: IpAddr,
115 protocol: ProtocolVersion,
116) -> OpenReceiveChannelWire {
117 OpenReceiveChannelWire {
118 conference_id: call_reference,
119 g723_bitrate: 0,
120 stream_passthrough_id: 0,
121 associated_stream_id: 0,
122 dtmf_type: 10,
123 mixing_mode: 0,
124 direction: u32::from(protocol.wire() >= 12),
125 requested_address_type: u32::from(
126 protocol.wire() >= 17 && matches!(source_address, IpAddr::V6(_)),
127 ),
128 audio_level_adjustment: 0,
129 latent_capabilities: [0; 36],
130 }
131}
132
133fn canonical_start_media_wire(
134 call_reference: u32,
135 protocol: ProtocolVersion,
136) -> StartMediaTransmissionWire {
137 StartMediaTransmissionWire {
138 conference_id: call_reference,
139 g723_bitrate: 0,
140 stream_passthrough_id: 0,
141 associated_stream_id: 0,
142 dtmf_type: 10,
143 mixing_mode: 0,
144 direction: u32::from(protocol.wire() >= 12),
145 latent_capabilities: [0; 36],
146 }
147}
148
149#[derive(BinRead, BinWrite, Clone, Copy, Default, Eq, PartialEq)]
150#[brw(little)]
151struct WireEncryptionInfo {
152 algorithm: u32,
153 key_length: u16,
154 salt_length: u16,
155 key: [u8; 16],
156 salt: [u8; 16],
157 mki_present: u32,
158 key_derivation_rate: u32,
159}
160
161impl std::fmt::Debug for WireEncryptionInfo {
162 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 formatter
164 .debug_struct("WireEncryptionInfo")
165 .field("algorithm", &EncryptionMethod::from(self.algorithm))
166 .field("key", &"<redacted>")
167 .field("key_length", &self.key_length)
168 .field("salt", &"<redacted>")
169 .field("salt_length", &self.salt_length)
170 .field("mki_present", &self.mki_present)
171 .field("key_derivation_rate", &self.key_derivation_rate)
172 .finish()
173 }
174}
175
176impl WireEncryptionInfo {
177 fn from_public(encryption: Option<&MediaEncryption>) -> Self {
178 let Some(encryption) = encryption else {
179 return Self::default();
180 };
181 Self {
182 algorithm: encryption.algorithm.wire_value(),
183 key_length: u16::from(encryption.key_length),
184 salt_length: u16::from(encryption.salt_length),
185 key: encryption.key,
186 salt: encryption.salt,
187 mki_present: encryption.mki_present,
188 key_derivation_rate: encryption.key_derivation_rate,
189 }
190 }
191
192 fn to_public(self, _message_id: u32) -> Result<Option<MediaEncryption>, CodecError> {
193 if usize::from(self.key_length) > self.key.len() {
194 return Err(CodecError::SecretTooLong {
195 field: "media encryption key",
196 actual: usize::from(self.key_length),
197 maximum: self.key.len(),
198 });
199 }
200 if usize::from(self.salt_length) > self.salt.len() {
201 return Err(CodecError::SecretTooLong {
202 field: "media encryption salt",
203 actual: usize::from(self.salt_length),
204 maximum: self.salt.len(),
205 });
206 }
207 if self.algorithm == 0
208 && self.key_length == 0
209 && self.salt_length == 0
210 && self.mki_present == 0
211 && self.key_derivation_rate == 0
212 {
213 return Ok(None);
214 }
215 Ok(Some(MediaEncryption::from_wire(
216 EncryptionMethod::from(self.algorithm),
217 self.key,
218 self.key_length as u8,
219 self.salt,
220 self.salt_length as u8,
221 self.mki_present,
222 self.key_derivation_rate,
223 )))
224 }
225}
226
227#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
228#[brw(little)]
229struct WireLatentCapabilities {
230 bytes: [u8; 36],
231}
232
233impl Default for WireLatentCapabilities {
234 fn default() -> Self {
235 Self { bytes: [0; 36] }
236 }
237}
238
239#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
240#[brw(little)]
241struct WireExtendedAddress {
242 family: u32,
243 bytes: [u8; 16],
244}
245
246impl WireExtendedAddress {
247 fn from_ip(address: IpAddr) -> Self {
248 match address {
249 IpAddr::V4(address) => {
250 let mut bytes = [0; 16];
251 bytes[..4].copy_from_slice(&address.octets());
252 Self { family: 0, bytes }
253 }
254 IpAddr::V6(address) => Self {
255 family: 1,
256 bytes: address.octets(),
257 },
258 }
259 }
260
261 fn to_ip(self, message_id: u32) -> Result<IpAddr, CodecError> {
262 match self.family {
263 0 => Ok(IpAddr::V4(Ipv4Addr::new(
264 self.bytes[0],
265 self.bytes[1],
266 self.bytes[2],
267 self.bytes[3],
268 ))),
269 1 => Ok(IpAddr::V6(Ipv6Addr::from(self.bytes))),
270 value => Err(CodecError::InvalidValue {
271 message_id,
272 field: "IP address family",
273 value: u64::from(value),
274 }),
275 }
276 }
277}
278
279#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
280#[brw(little)]
281struct WireStartMulticastReceptionV3 {
282 conference_id: u32,
283 passthrough_party_id: u32,
284 address: [u8; 4],
285 port: u32,
286 packet_millis: u32,
287 codec: u32,
288 echo_cancellation: u32,
289 g723_bitrate: u32,
290 call_reference: u32,
291}
292
293#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
294#[brw(little)]
295struct WireStartMulticastReceptionV17 {
296 conference_id: u32,
297 passthrough_party_id: u32,
298 address: WireExtendedAddress,
299 port: u32,
300 packet_millis: u32,
301 codec: u32,
302 echo_cancellation: u32,
303 g723_bitrate: u32,
304 call_reference: u32,
305}
306
307#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
308#[brw(little)]
309struct WireStartMulticastTransmissionV3 {
310 conference_id: u32,
311 passthrough_party_id: u32,
312 address: [u8; 4],
313 port: u32,
314 packet_millis: u32,
315 codec: u32,
316 precedence: u32,
317 silence_suppression: u32,
318 max_frames_per_packet: u32,
319 g723_bitrate: u32,
320 call_reference: u32,
321}
322
323#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
324#[brw(little)]
325struct WireStartMulticastTransmissionV17 {
326 conference_id: u32,
327 passthrough_party_id: u32,
328 address: WireExtendedAddress,
329 port: u32,
330 packet_millis: u32,
331 codec: u32,
332 precedence: u32,
333 silence_suppression: u32,
334 max_frames_per_packet: u32,
335 g723_bitrate: u32,
336 call_reference: u32,
337}
338
339#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
340#[brw(little)]
341struct WireOpenReceiveV11 {
342 conference_id: u32,
343 passthrough_party_id: u32,
344 packet_millis: u32,
345 codec: u32,
346 vad: u32,
347 g723_bitrate: u32,
348 call_reference: u32,
349 encryption: WireEncryptionInfo,
350 stream_passthrough_id: u32,
351 associated_stream_id: u32,
352 rfc2833_payload: u32,
353 dtmf_type: u32,
354}
355
356#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
357#[brw(little)]
358struct WireOpenReceiveV12 {
359 conference_id: u32,
360 passthrough_party_id: u32,
361 packet_millis: u32,
362 codec: u32,
363 vad: u32,
364 g723_bitrate: u32,
365 call_reference: u32,
366 encryption: WireEncryptionInfo,
367 stream_passthrough_id: u32,
368 associated_stream_id: u32,
369 rfc2833_payload: u32,
370 dtmf_type: u32,
371 mixing_mode: u32,
372 direction: u32,
373 remote_ipv4: [u8; 4],
374 remote_port: u32,
375}
376
377#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
378#[brw(little)]
379struct WireOpenReceiveV17 {
380 conference_id: u32,
381 passthrough_party_id: u32,
382 packet_millis: u32,
383 codec: u32,
384 vad: u32,
385 g723_bitrate: u32,
386 call_reference: u32,
387 encryption: WireEncryptionInfo,
388 stream_passthrough_id: u32,
389 associated_stream_id: u32,
390 rfc2833_payload: u32,
391 dtmf_type: u32,
392 mixing_mode: u32,
393 direction: u32,
394 remote: WireExtendedAddress,
395 remote_port: u32,
396 requested_address_type: u32,
397}
398
399#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
400#[brw(little)]
401struct WireOpenReceiveV18 {
402 base: WireOpenReceiveV17,
403 audio_level_adjustment: u32,
404}
405
406#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
407#[brw(little)]
408struct WireOpenReceiveV21 {
409 base: WireOpenReceiveV18,
410 latent_capabilities: WireLatentCapabilities,
411}
412
413#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
414#[brw(little)]
415struct WireStartMediaV11 {
416 conference_id: u32,
417 passthrough_party_id: u32,
418 remote_ipv4: [u8; 4],
419 remote_port: u32,
420 packet_millis: u32,
421 codec: u32,
422 precedence: u32,
423 silence_suppression: u32,
424 max_frames_per_packet: u32,
425 g723_bitrate: u32,
426 call_reference: u32,
427 encryption: WireEncryptionInfo,
428 stream_passthrough_id: u32,
429 associated_stream_id: u32,
430 rfc2833_payload: u32,
431 dtmf_type: u32,
432}
433
434#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
435#[brw(little)]
436struct WireStartMediaV12 {
437 conference_id: u32,
438 passthrough_party_id: u32,
439 remote_ipv4: [u8; 4],
440 remote_port: u32,
441 packet_millis: u32,
442 codec: u32,
443 precedence: u32,
444 silence_suppression: u32,
445 max_frames_per_packet: u32,
446 g723_bitrate: u32,
447 call_reference: u32,
448 encryption: WireEncryptionInfo,
449 stream_passthrough_id: u32,
450 associated_stream_id: u32,
451 rfc2833_payload: u32,
452 dtmf_type: u32,
453 mixing_mode: u32,
454 direction: u32,
455}
456
457#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
458#[brw(little)]
459struct WireStartMediaV17 {
460 conference_id: u32,
461 passthrough_party_id: u32,
462 remote: WireExtendedAddress,
463 remote_port: u32,
464 packet_millis: u32,
465 codec: u32,
466 precedence: u32,
467 silence_suppression: u32,
468 max_frames_per_packet: u32,
469 g723_bitrate: u32,
470 call_reference: u32,
471 encryption: WireEncryptionInfo,
472 stream_passthrough_id: u32,
473 associated_stream_id: u32,
474 rfc2833_payload: u32,
475 dtmf_type: u32,
476 mixing_mode: u32,
477 direction: u32,
478}
479
480#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
481#[brw(little)]
482struct WireStartMediaV21 {
483 base: WireStartMediaV17,
484 latent_capabilities: WireLatentCapabilities,
485}
486
487#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
488#[brw(little)]
489struct WireStartMediaAckV3 {
490 conference_id: u32,
491 passthrough_party_id: u32,
492 call_reference: u32,
493 address: [u8; 4],
494 port: u32,
495 status: u32,
496}
497
498#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
499#[brw(little)]
500struct WireStartMediaAckV17 {
501 conference_id: u32,
502 passthrough_party_id: u32,
503 call_reference: u32,
504 address: WireExtendedAddress,
505 port: u32,
506 status: u32,
507}
508
509#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
510#[brw(little)]
511struct WireStartMediaAckV20 {
512 base: WireStartMediaAckV17,
513 extension: [u8; 8],
514}
515
516macro_rules! words {
517 ($name:ident { $($field:ident),+ $(,)? }) => {
518 #[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
519 #[brw(little)]
520 struct $name {
521 $($field: u32),+
522 }
523 };
524}
525
526words!(WireOneWord { value });
527words!(WireMulticastReceptionAck {
528 status,
529 passthrough_party_id,
530 call_reference
531});
532words!(WireLineCall {
533 line_instance,
534 call_reference
535});
536words!(WireCallParty {
537 call_reference,
538 passthrough_party_id
539});
540words!(WireAudioStreamControl {
541 conference_id,
542 passthrough_party_id,
543 call_reference,
544 port_handling_flag
545});
546words!(WireSelectSoftKeys {
547 line_instance,
548 call_reference,
549 set,
550 valid_mask
551});
552words!(WireCallState {
553 state,
554 line_instance,
555 call_reference,
556 visibility,
557 precedence,
558 domain
559});
560words!(WireCallInfoDynamicHeader {
561 line_instance,
562 call_reference,
563 call_type,
564 original_redirect_reason,
565 last_redirect_reason,
566 call_instance,
567 security_status,
568 party_restrictions
569});
570words!(WireDynamicPromptHeader {
571 timeout_seconds,
572 line_instance,
573 call_reference
574});
575words!(WireModeLineCall {
576 mode,
577 duration,
578 line_instance,
579 call_reference
580});
581words!(WireToneLineCall {
582 tone,
583 direction,
584 line_instance,
585 call_reference
586});
587words!(WireLampState {
588 stimulus,
589 instance,
590 mode
591});
592words!(WirePortRequestPre20 {
593 conference_id,
594 call_reference,
595 passthrough_party_id,
596 transport
597});
598words!(WirePortRequestFrom20 {
599 conference_id,
600 call_reference,
601 passthrough_party_id,
602 transport,
603 address_type,
604 media_type
605});
606words!(WirePortClosePre20 {
607 conference_id,
608 call_reference,
609 passthrough_party_id
610});
611words!(WirePortCloseFrom20 {
612 conference_id,
613 call_reference,
614 passthrough_party_id,
615 media_type
616});
617words!(WireSubscriptionStatus {
618 transaction_id,
619 feature_id,
620 timer_seconds,
621 cause
622});
623words!(WireCallSelectStatus {
624 status,
625 call_reference,
626 line_instance
627});
628words!(WireRecordingStatus {
629 call_reference,
630 active
631});
632words!(WireFeatureStatusRequest {
633 index,
634 capabilities
635});
636words!(WireLineStatusDynamicHeader {
637 line_instance,
638 line_type
639});
640words!(WireStopToneV12 {
641 line_instance,
642 call_reference,
643 tone
644});
645words!(WireCallHistoryDisposition {
646 disposition,
647 line_instance,
648 call_reference
649});
650words!(WireAnnouncementFinish {
651 conference_id,
652 play_status
653});
654words!(WireStopMulticast {
655 conference_id,
656 passthrough_party_id,
657 call_reference
658});
659words!(WireAddParticipantResponseHeader {
660 conference_id,
661 call_reference,
662 result
663});
664words!(WireAuditParticipantResponseHeader {
665 result,
666 last,
667 conference_id,
668 number_of_entries
669});
670
671#[derive(BinRead, BinWrite, Clone, Copy, Debug, Default, Eq, PartialEq)]
672#[brw(little)]
673struct WireAnnouncementEntry {
674 locale: u32,
675 country: u32,
676 tone: u32,
677}
678
679#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
680#[brw(little)]
681struct WireStartAnnouncement {
682 announcements: [WireAnnouncementEntry; 32],
683 end_of_ack: u32,
684 conference_id: u32,
685 matrix_conference_party_ids: [u32; 16],
686 hearing_conference_party_mask: u32,
687 play_mode: u32,
688}
689
690#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
691#[brw(little)]
692struct WireCreateConferenceRequest {
693 conference_id: u32,
694 reserved_participants: u32,
695 resource_type: u32,
696 application_id: u32,
697 application_conference_id: WireFixedText<32>,
698 application_data: WireFixedText<24>,
699 data_length: u32,
700 #[br(count = data_length)]
701 passthrough_data: Vec<u8>,
702}
703
704#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
705#[brw(little)]
706struct WireModifyConferenceRequest {
707 conference_id: u32,
708 reserved_participants: u32,
709 application_id: u32,
710 application_conference_id: WireFixedText<32>,
711 application_data: WireFixedText<24>,
712 data_length: u32,
713 #[br(count = data_length)]
714 passthrough_data: Vec<u8>,
715}
716
717#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
718#[brw(little)]
719struct WireConferenceResponse {
720 conference_id: u32,
721 result: u32,
722 data_length: u32,
723 #[br(count = data_length)]
724 passthrough_data: Vec<u8>,
725}
726
727#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
728#[brw(little)]
729struct WireAuditConferenceEntry {
730 conference_id: u32,
731 resource_type: u32,
732 reserved_participants: u32,
733 active_participants: u32,
734 application_id: u32,
735 application_conference_id: WireFixedText<32>,
736 application_data: WireFixedText<24>,
737}
738
739#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
740#[brw(little)]
741struct WireAuditConferenceResponse {
742 last: u32,
743 number_of_entries: u32,
744 #[br(count = number_of_entries)]
745 entries: Vec<WireAuditConferenceEntry>,
746}
747
748#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
749#[brw(little)]
750struct WireParticipantRequest {
751 conference_id: u32,
752 call_reference: u32,
753 presentation_restrictions: u32,
754 participant_name: WireFixedText<40>,
755 participant_number: WireFixedText<24>,
756 conference_name: WireFixedText<32>,
757}
758
759#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
760#[brw(little)]
761struct WireQosFlow {
762 conference_id: u32,
763 call_reference: u32,
764 passthrough_party_id: u32,
765 address: [u8; 4],
766 port: u32,
767}
768
769#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
770#[brw(little)]
771struct WireQosApplicationIdentifier {
772 vendor_id: WireFixedText<32>,
773 version: WireFixedText<16>,
774 application_name: WireFixedText<32>,
775 sub_application_id: WireFixedText<32>,
776}
777
778#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
779#[brw(little)]
780struct WireQosReservationNotify {
781 flow: WireQosFlow,
782 direction: u32,
783}
784
785#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
786#[brw(little)]
787struct WireUpdateDscp {
788 flow: WireQosFlow,
789 dscp: u32,
790}
791
792#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
793#[brw(little)]
794struct WireQosErrorNotify {
795 flow: WireQosFlow,
796 direction: u32,
797 error_code: u32,
798 failure_node: u32,
799 rsvp_error_code: u32,
800 rsvp_error_subcode: u32,
801 rsvp_error_flags: u32,
802}
803
804#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
805#[brw(little)]
806struct WireQosListen {
807 flow: WireQosFlow,
808 reservation_style: u32,
809 maximum_retries: u32,
810 retry_timer: u32,
811 confirmation_required: u32,
812 preemption_priority: u32,
813 defending_priority: u32,
814 compression_type: u32,
815 average_bit_rate: u32,
816 burst_size: u32,
817 peak_rate: u32,
818 application: WireQosApplicationIdentifier,
819}
820
821#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
822#[brw(little)]
823struct WireQosPath {
824 flow: WireQosFlow,
825 reservation_style: u32,
826 maximum_retries: u32,
827 retry_timer: u32,
828 preemption_priority: u32,
829 defending_priority: u32,
830 compression_type: u32,
831 average_bit_rate: u32,
832 burst_size: u32,
833 peak_rate: u32,
834 application: WireQosApplicationIdentifier,
835}
836
837#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
838#[brw(little)]
839struct WireQosModify {
840 flow: WireQosFlow,
841 direction: u32,
842 compression_type: u32,
843 average_bit_rate: u32,
844 burst_size: u32,
845 peak_rate: u32,
846 application: WireQosApplicationIdentifier,
847}
848
849#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
850#[brw(little)]
851struct WireMessageWaitingNotification {
852 target_number: WireFixedText<25>,
853 control_number: WireFixedText<25>,
854 alignment: [u8; 2],
855 messages_waiting: u32,
856 total_voicemail_new: u32,
857 total_voicemail_old: u32,
858 priority_voicemail_new: u32,
859 priority_voicemail_old: u32,
860 total_fax_new: u32,
861 total_fax_old: u32,
862 priority_fax_new: u32,
863 priority_fax_old: u32,
864}
865
866#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
867#[brw(little)]
868struct WireMessageWaitingResponse {
869 target_number: WireFixedText<25>,
870 alignment: [u8; 3],
871 result: u32,
872}
873
874#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
875#[brw(little)]
876struct WireRegisterAck {
877 keepalive_seconds: u32,
878 date_template: [u8; 6],
879 alignment: [u8; 2],
880 secondary_keepalive_seconds: u32,
881 protocol_features: [u8; 4],
882}
883
884#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
885#[brw(little)]
886struct WireConfigStatus {
887 device_id: WireFixedText<16>,
888 station_user_id: u32,
889 station_instance: u32,
890 user_name: WireFixedText<40>,
891 server_name: WireFixedText<40>,
892 line_count: u32,
893 speed_dial_count: u32,
894}
895
896#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
897#[brw(little)]
898struct WireLineStatus {
899 line_instance: u32,
900 directory_number: WireFixedText<24>,
901 display_name: WireFixedText<40>,
902 display_label: WireFixedText<40>,
903 reserved: u32,
904}
905
906#[derive(BinRead, BinWrite, Clone, Copy, Debug, Default, Eq, PartialEq)]
907struct WireButtonDefinition {
908 instance: u8,
909 button_type: u8,
910}
911
912#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
913#[brw(little)]
914struct WireButtonTemplate {
915 offset: u32,
916 count: u32,
917 total: u32,
918 definitions: [WireButtonDefinition; 42],
919}
920
921#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
922#[brw(little)]
923struct WireServerResponseV3 {
924 names: [WireFixedText<48>; 5],
925 ports: [u32; 5],
926 addresses: [[u8; 4]; 5],
927}
928
929#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
930#[brw(little)]
931struct WireServerResponseV17 {
932 names: [WireFixedText<48>; 5],
933 ports: [u32; 5],
934 addresses: [WireExtendedAddress; 5],
935}
936
937words!(WireTimeDate {
938 year,
939 month,
940 weekday,
941 day,
942 hour,
943 minute,
944 second,
945 milliseconds,
946 unix_seconds
947});
948
949#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
950#[brw(little)]
951struct WireSoftKeyDefinition {
952 label: [u8; 16],
953 event: u32,
954}
955
956#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
957#[brw(little)]
958struct WireSoftKeyTemplate {
959 offset: u32,
960 count: u32,
961 total: u32,
962 definitions: [WireSoftKeyDefinition; 32],
963}
964
965#[derive(BinRead, BinWrite, Clone, Copy, Debug, Default, Eq, PartialEq)]
966#[brw(little)]
967struct WireSoftKeySetDefinition {
968 template_indexes: [u8; 16],
969 info: [u16; 16],
970}
971
972#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
973#[brw(little)]
974struct WireSoftKeySet {
975 offset: u32,
976 count: u32,
977 total: u32,
978 #[br(count = 16)]
979 sets: Vec<WireSoftKeySetDefinition>,
980}
981
982#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
983#[brw(little)]
984struct WireCallInfo {
985 calling_name: WireFixedText<40>,
986 calling_number: WireFixedText<24>,
987 called_name: WireFixedText<40>,
988 called_number: WireFixedText<24>,
989 line_instance: u32,
990 call_reference: u32,
991 call_type: u32,
992 original_called_name: WireFixedText<40>,
993 original_called_number: WireFixedText<24>,
994 last_redirecting_name: WireFixedText<40>,
995 last_redirecting_number: WireFixedText<24>,
996 original_redirect_reason: u32,
997 last_redirect_reason: u32,
998 voice_mailboxes: [WireFixedText<24>; 4],
999 call_instance: u32,
1000 security_status: u32,
1001 party_restrictions: u32,
1002}
1003
1004#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1005#[brw(little)]
1006struct WirePromptStatus {
1007 timeout_seconds: u32,
1008 text: WireFixedText<32>,
1009 line_instance: u32,
1010 call_reference: u32,
1011}
1012
1013#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1014#[brw(little)]
1015struct WireNotify {
1016 timeout_seconds: u32,
1017 text: WireFixedText<32>,
1018}
1019
1020#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1021#[brw(little)]
1022struct WireDynamicNotifyHeader {
1023 timeout_seconds: u32,
1024}
1025
1026#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1027#[brw(little)]
1028struct WirePriorityNotify {
1029 timeout_seconds: u32,
1030 priority: u32,
1031 text: WireFixedText<32>,
1032}
1033
1034#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1035#[brw(little)]
1036struct WireDynamicPriorityNotifyHeader {
1037 timeout_seconds: u32,
1038 priority: u32,
1039}
1040
1041#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1042#[brw(little)]
1043struct WireConnectionStatisticsRequestV3 {
1044 directory_number: WireFixedText<24>,
1045 call_reference: u32,
1046 processing: u32,
1047}
1048
1049#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1050#[brw(little)]
1051struct WireConnectionStatisticsRequestV19 {
1052 directory_number: WireFixedText<25>,
1053 alignment: [u8; 3],
1054 call_reference: u32,
1055 processing: u32,
1056}
1057
1058#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1059#[brw(little)]
1060struct WireForwardStatusV3 {
1061 active: u32,
1062 line_instance: u32,
1063 all_active: u32,
1064 all_number: WireFixedText<24>,
1065 busy_active: u32,
1066 busy_number: WireFixedText<24>,
1067 no_answer_active: u32,
1068 no_answer_number: WireFixedText<24>,
1069}
1070
1071#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1072#[brw(little)]
1073struct WireForwardStatusV19 {
1074 active: u32,
1075 line_instance: u32,
1076 all_active: u32,
1077 all_number: WireFixedText<25>,
1078 all_alignment: [u8; 3],
1079 busy_active: u32,
1080 busy_number: WireFixedText<25>,
1081 busy_alignment: [u8; 3],
1082 no_answer_active: u32,
1083 no_answer_number: WireFixedText<25>,
1084 no_answer_alignment: [u8; 3],
1085}
1086
1087#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1088#[brw(little)]
1089struct WireSpeedDialStatus {
1090 instance: u32,
1091 number: WireFixedText<24>,
1092 display_name: WireFixedText<40>,
1093}
1094
1095#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1096#[brw(little)]
1097struct WireDialedNumberV3 {
1098 number: WireFixedText<24>,
1099 line_instance: u32,
1100 call_reference: u32,
1101}
1102
1103#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1104#[brw(little)]
1105struct WireDialedNumberV19 {
1106 number: WireFixedText<25>,
1107 alignment: [u8; 3],
1108 line_instance: u32,
1109 call_reference: u32,
1110}
1111
1112#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1113#[brw(little)]
1114struct WireFeatureStatus {
1115 instance: u32,
1116 button_type: u32,
1117 label: WireFixedText<40>,
1118 state: u32,
1119}
1120
1121#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1122#[brw(little)]
1123struct WireFeatureStatusDynamic {
1124 instance: u32,
1125 button_type: u32,
1126 state: u32,
1127 label: WireFixedText<121>,
1128 padding: [u8; 3],
1129}
1130
1131#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1132#[brw(little)]
1133struct WireServiceUrlStatus {
1134 index: u32,
1135 url: WireFixedText<256>,
1136 label: WireFixedText<40>,
1137}
1138
1139#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1140#[brw(little)]
1141struct WireNotification {
1142 transaction_id: u32,
1143 feature_id: u32,
1144 status: u32,
1145 text: WireFixedText<100>,
1146}
1147
1148#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1149#[brw(little)]
1150struct WireRegister {
1151 device_id: WireFixedText<16>,
1152 station_user_id: u32,
1153 station_instance: u32,
1154 reported_address: [u8; 4],
1155 device_type: u32,
1156 max_streams: u32,
1157 active_streams: u32,
1158 protocol_features: [u8; 4],
1159 max_conferences: u32,
1160 active_conferences: u32,
1161 mac_address: [u8; 12],
1162 ipv4_address_scope: u32,
1163 max_lines: u32,
1164 ipv6_address: [u8; 16],
1165 ipv6_address_scope: u32,
1166 firmware: WireFixedText<32>,
1167}
1168
1169words!(WireKeypadButton {
1170 button,
1171 line_instance,
1172 call_reference,
1173 keypad_union,
1174 reserved
1175});
1176
1177words!(WireKeypadButtonWithCall {
1178 button,
1179 line_instance,
1180 call_reference
1181});
1182
1183words!(WireKeypadButtonLegacy { button });
1184
1185#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1186#[brw(little)]
1187struct WireEnblocBefore19 {
1188 called_party: WireFixedText<24>,
1189 line_instance: u32,
1190}
1191
1192#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1193#[brw(little)]
1194struct WireEnblocFrom19 {
1195 called_party: WireFixedText<25>,
1196 alignment: [u8; 3],
1197 line_instance: u32,
1198}
1199
1200#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1201#[brw(little)]
1202struct WireOffHookWithCallingPartyBefore19 {
1203 calling_party_number: WireFixedText<24>,
1204 voice_mailbox: WireFixedText<24>,
1205 line_instance: u32,
1206}
1207
1208#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1209#[brw(little)]
1210struct WireOffHookWithCallingPartyFrom19 {
1211 calling_party_number: WireFixedText<25>,
1212 voice_mailbox: WireFixedText<25>,
1213 alignment: [u8; 2],
1214 line_instance: u32,
1215}
1216
1217words!(WireStimulus {
1218 stimulus,
1219 instance,
1220 call_reference,
1221 status
1222});
1223
1224#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1225#[brw(little)]
1226struct WireMediaCapability {
1227 codec: u32,
1228 max_frames_per_packet: u32,
1229 codec_parameters: [u8; 8],
1230}
1231
1232#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
1233#[brw(little)]
1234struct WireCapabilitiesResponse {
1235 count: u32,
1236 #[br(count = count)]
1237 capabilities: Vec<WireMediaCapability>,
1238}
1239
1240#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1241#[brw(little)]
1242struct WireAlarmLegacy {
1243 severity: u32,
1244 text: WireFixedText<80>,
1245}
1246
1247#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1248#[brw(little)]
1249struct WireAlarm {
1250 severity: u32,
1251 text: WireFixedText<80>,
1252 parameter_1: u32,
1253 parameter_2: u32,
1254}
1255
1256#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1257#[brw(little)]
1258struct WireLocationInfo {
1259 xml: WireFixedText<2401>,
1260 alignment: [u8; 3],
1261}
1262
1263#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1264#[brw(little)]
1265struct WireOpenReceiveAckV3 {
1266 status: u32,
1267 address: [u8; 4],
1268 port: u32,
1269 passthrough_party_id: u32,
1270 call_reference: u32,
1271}
1272
1273#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1274#[brw(little)]
1275struct WireOpenReceiveAckV17 {
1276 status: u32,
1277 address: WireExtendedAddress,
1278 port: u32,
1279 passthrough_party_id: u32,
1280 call_reference: u32,
1281}
1282
1283words!(WireSoftKeyEvent {
1284 event,
1285 line_instance,
1286 call_reference
1287});
1288
1289#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1290#[brw(little)]
1291struct WireRegisterToken {
1292 device_id: WireFixedText<16>,
1293 device_instance: u32,
1294 ipv4_address: [u8; 4],
1295 device_type: u32,
1296 ipv6_address: [u8; 16],
1297 flags: u32,
1298}
1299
1300words!(WireMediaResourceNotification {
1301 device_type,
1302 in_service_streams,
1303 max_streams_per_conference,
1304 out_of_service_streams
1305});
1306words!(WireAccessoryStatus { accessory, state });
1307words!(WireDtmfToneControl {
1308 tone,
1309 conference_id,
1310 passthrough_party_id
1311});
1312words!(WireDtmfPayloadIdentity {
1313 payload_type,
1314 conference_id,
1315 passthrough_party_id
1316});
1317words!(WireDtmfPayloadRequest {
1318 payload_type,
1319 conference_id,
1320 passthrough_party_id,
1321 dtmf_type
1322});
1323#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1324#[brw(little)]
1325struct WireMediaFailureDetection {
1326 conference_id: u32,
1327 passthrough_party_id: u32,
1328 packet_millis: u32,
1329 codec: u32,
1330 echo_cancellation: u32,
1331 codec_qualifier: [u8; 4],
1332 call_reference: u32,
1333}
1334words!(WireMultimediaStreamControl {
1335 conference_id,
1336 passthrough_party_id,
1337 call_reference,
1338 port_handling_flag
1339});
1340words!(WireVideoFlowControl {
1341 conference_id,
1342 passthrough_party_id,
1343 call_reference,
1344 maximum_bit_rate
1345});
1346words!(WireVideoDisplayCommand {
1347 conference_id,
1348 call_reference,
1349 layout_id
1350});
1351
1352#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1353#[brw(little)]
1354struct WireOpenMultimediaAckPre17 {
1355 status: u32,
1356 address: [u8; 4],
1357 port: u32,
1358 passthrough_party_id: u32,
1359 call_reference: u32,
1360}
1361
1362#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1363#[brw(little)]
1364struct WireOpenMultimediaAckFrom17 {
1365 status: u32,
1366 address: WireExtendedAddress,
1367 port: u32,
1368 passthrough_party_id: u32,
1369 call_reference: u32,
1370}
1371
1372#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1373#[brw(little)]
1374struct WireStartMultimediaAckPre17 {
1375 conference_id: u32,
1376 passthrough_party_id: u32,
1377 call_reference: u32,
1378 address: [u8; 4],
1379 port: u32,
1380 status: u32,
1381}
1382
1383#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1384#[brw(little)]
1385struct WireStartMultimediaAckFrom17 {
1386 conference_id: u32,
1387 passthrough_party_id: u32,
1388 call_reference: u32,
1389 address: WireExtendedAddress,
1390 port: u32,
1391 status: u32,
1392}
1393
1394#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1395#[brw(little)]
1396struct WireSessionTransmissionPre17 {
1397 remote_address: [u8; 4],
1398 session_type: u32,
1399}
1400
1401#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1402#[brw(little)]
1403struct WireSessionTransmissionFrom17 {
1404 remote_address: WireExtendedAddress,
1405 session_type: u32,
1406}
1407
1408#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1409#[brw(little)]
1410struct WireMultimediaPayloadDescriptor {
1411 payload_rfc_number: u32,
1412 payload_type: u32,
1413}
1414
1415impl From<MultimediaPayloadDescriptor> for WireMultimediaPayloadDescriptor {
1416 fn from(value: MultimediaPayloadDescriptor) -> Self {
1417 Self {
1418 payload_rfc_number: value.rfc_number(),
1419 payload_type: value.payload_number().into(),
1420 }
1421 }
1422}
1423
1424#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1425#[brw(little)]
1426struct WireOpenMultimediaV11 {
1427 conference_id: u32,
1428 passthrough_party_id: u32,
1429 compression_type: u32,
1430 line_instance: u32,
1431 call_reference: u32,
1432 payload_type: WireMultimediaPayloadDescriptor,
1433 conference_creator: u32,
1434 capability: [u8; MULTIMEDIA_CAPABILITY_BYTES],
1435 encryption: WireEncryptionInfo,
1436 stream_passthrough_id: u32,
1437 associated_stream_id: u32,
1438}
1439
1440#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1441#[brw(little)]
1442struct WireOpenMultimediaV12 {
1443 base: WireOpenMultimediaV11,
1444 source_address: [u8; 4],
1445 source_port: u32,
1446}
1447
1448#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1449#[brw(little)]
1450struct WireOpenMultimediaV17 {
1451 conference_id: u32,
1452 passthrough_party_id: u32,
1453 compression_type: u32,
1454 line_instance: u32,
1455 call_reference: u32,
1456 payload_type: WireMultimediaPayloadDescriptor,
1457 conference_creator: u32,
1458 capability: [u8; MULTIMEDIA_CAPABILITY_BYTES],
1459 encryption: WireEncryptionInfo,
1460 stream_passthrough_id: u32,
1461 associated_stream_id: u32,
1462 source_address: WireExtendedAddress,
1463 source_port: u32,
1464 requested_address_type: u32,
1465}
1466
1467#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1468#[brw(little)]
1469struct WireStartMultimediaPre17 {
1470 conference_id: u32,
1471 passthrough_party_id: u32,
1472 compression_type: u32,
1473 remote_address: [u8; 4],
1474 remote_port: u32,
1475 call_reference: u32,
1476 payload_type: WireMultimediaPayloadDescriptor,
1477 dscp: u32,
1478 capability: [u8; MULTIMEDIA_CAPABILITY_BYTES],
1479 encryption: WireEncryptionInfo,
1480 stream_passthrough_id: u32,
1481 associated_stream_id: u32,
1482}
1483
1484#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1485#[brw(little)]
1486struct WireStartMultimediaFrom17 {
1487 conference_id: u32,
1488 passthrough_party_id: u32,
1489 compression_type: u32,
1490 remote_address: WireExtendedAddress,
1491 remote_port: u32,
1492 call_reference: u32,
1493 payload_type: WireMultimediaPayloadDescriptor,
1494 dscp: u32,
1495 capability: [u8; MULTIMEDIA_CAPABILITY_BYTES],
1496 encryption: WireEncryptionInfo,
1497 stream_passthrough_id: u32,
1498 associated_stream_id: u32,
1499}
1500
1501#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1502#[brw(little)]
1503struct WireMiscellaneousCommand {
1504 conference_id: u32,
1505 passthrough_party_id: u32,
1506 call_reference: u32,
1507 command: u32,
1508 data: [u8; 36],
1509}
1510
1511#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1512#[brw(little)]
1513struct WireExtensionDeviceCapabilities {
1514 unknown_1: u32,
1515 unknown_2: u32,
1516 unknown_3: u32,
1517 description: WireFixedText<152>,
1518}
1519
1520#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1521#[brw(little)]
1522struct WireMediaFailureV3 {
1523 conference_id: u32,
1524 passthrough_party_id: u32,
1525 address: [u8; 4],
1526 port: u32,
1527 call_reference: u32,
1528}
1529
1530#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1531#[brw(little)]
1532struct WireMediaFailureV17 {
1533 conference_id: u32,
1534 passthrough_party_id: u32,
1535 address: WireExtendedAddress,
1536 port: u32,
1537 call_reference: u32,
1538}
1539
1540#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
1541#[brw(little)]
1542struct WireUserData {
1543 application_id: u32,
1544 line_instance: u32,
1545 call_reference: u32,
1546 transaction_id: u32,
1547 data_length: u32,
1548 #[br(count = data_length)]
1549 data: Vec<u8>,
1550}
1551
1552#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
1553#[brw(little)]
1554struct WireUserDataV1 {
1555 application_id: u32,
1556 line_instance: u32,
1557 call_reference: u32,
1558 transaction_id: u32,
1559 data_length: u32,
1560 sequence_flag: u32,
1561 display_priority: u32,
1562 conference_id: u32,
1563 application_instance_id: u32,
1564 routing: u32,
1565 #[br(count = data_length)]
1566 data: Vec<u8>,
1567}
1568
1569#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1570#[brw(little)]
1571struct WirePortResponseV3 {
1572 conference_id: u32,
1573 call_reference: u32,
1574 passthrough_party_id: u32,
1575 address: [u8; 4],
1576 rtp_port: u32,
1577 rtcp_port: u32,
1578}
1579
1580#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1581#[brw(little)]
1582struct WirePortResponseV20 {
1583 conference_id: u32,
1584 call_reference: u32,
1585 passthrough_party_id: u32,
1586 address: WireExtendedAddress,
1587 rtp_port: u32,
1588 rtcp_port: u32,
1589 media_type: u32,
1590}
1591
1592#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1593#[brw(little)]
1594struct WireSubscriptionRequest {
1595 transaction_id: u32,
1596 feature_id: u32,
1597 timer_seconds: u32,
1598 subscription_id: WireFixedText<256>,
1599}
1600
1601#[derive(BinRead, BinWrite, Clone, Copy, Debug, Eq, PartialEq)]
1602#[brw(little)]
1603struct WireConnectionStatisticsTail {
1604 packets_sent: u32,
1605 octets_sent: u32,
1606 packets_received: u32,
1607 octets_received: u32,
1608 packets_lost: u32,
1609 jitter_millis: u32,
1610 latency_millis: u32,
1611 quality_size: u32,
1612}
1613
1614#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
1615#[brw(little)]
1616struct WireConnectionStatisticsV3 {
1617 directory_number: WireFixedText<24>,
1618 call_reference: u32,
1619 processing: u32,
1620 statistics: WireConnectionStatisticsTail,
1621 #[br(count = statistics.quality_size)]
1622 quality: Vec<u8>,
1623}
1624
1625#[derive(BinRead, BinWrite, Clone, Debug, Eq, PartialEq)]
1626#[brw(little)]
1627struct WireConnectionStatisticsV19 {
1628 directory_number: WireFixedText<25>,
1629 alignment: [u8; 3],
1630 call_reference: u32,
1631 processing: u32,
1632 statistics: WireConnectionStatisticsTail,
1633 #[br(count = statistics.quality_size)]
1634 quality: Vec<u8>,
1635}
1636
1637impl ClientMessage {
1638 pub fn decode_with_version(
1643 frame: Frame,
1644 protocol: ProtocolVersion,
1645 ) -> Result<Self, CodecError> {
1646 ensure_station_route(&frame, MessageRoute::StationToControl, "station-to-control")?;
1647 Self::decode_using_protocol(frame, protocol.wire())
1648 }
1649
1650 pub fn decode(frame: Frame) -> Result<Self, CodecError> {
1655 ensure_station_route(&frame, MessageRoute::StationToControl, "station-to-control")?;
1656 let protocol_version = frame.protocol_version;
1657 Self::decode_using_protocol(frame, protocol_version)
1658 }
1659
1660 fn decode_using_protocol(frame: Frame, protocol_version: u32) -> Result<Self, CodecError> {
1661 let p = &frame.payload;
1662 match frame.message_id {
1663 id::KEEP_ALIVE => Ok(Self::KeepAlive),
1664 id::REGISTER => {
1665 const REQUIRED_BYTES: usize = 124;
1666 const MAXIMUM_BYTES: usize = REQUIRED_BYTES + 48;
1667 if p.len() < REQUIRED_BYTES {
1668 return Err(CodecError::Truncated {
1669 message_id: frame.message_id,
1670 needed: REQUIRED_BYTES,
1671 actual: p.len(),
1672 });
1673 }
1674 if p.len() > MAXIMUM_BYTES {
1675 return Err(CodecError::TrailingBytes {
1676 message_id: frame.message_id,
1677 count: p.len() - MAXIMUM_BYTES,
1678 });
1679 }
1680 let value: WireRegister = decode(frame.message_id, &p[..REQUIRED_BYTES])?;
1681 let reported_address = if value.reported_address.iter().any(|byte| *byte != 0) {
1682 Some(Ipv4Addr::from(value.reported_address))
1683 } else {
1684 None
1685 };
1686 let reported_ipv6_address = if value.ipv6_address.iter().any(|byte| *byte != 0) {
1687 Some(Ipv6Addr::from(value.ipv6_address))
1688 } else {
1689 None
1690 };
1691 let advertised_protocol = u32::from(value.protocol_features[0]);
1692 Ok(Self::Register(RegistrationMessage {
1693 device_id: DeviceId::new(value.device_id.text()?)?,
1694 reported_address,
1695 reported_ipv6_address,
1696 device_type: DeviceType::from(value.device_type),
1697 advertised_protocol,
1698 features: PhoneFeatures::from_bits_retain(
1699 u32::from_le_bytes(value.protocol_features) & !0xff,
1700 ),
1701 firmware: value.firmware.text()?,
1702 configuration_version_stamp: BoundedBytes::try_from(
1703 p[REQUIRED_BYTES..].to_vec(),
1704 )
1705 .expect("registration suffix length was bounded before allocation"),
1706 wire: Some(RegistrationWireDetails {
1707 station_user_id: value.station_user_id,
1708 station_instance: value.station_instance,
1709 max_streams: value.max_streams,
1710 active_streams: value.active_streams,
1711 mac_address_and_padding: value.mac_address,
1712 max_conferences: value.max_conferences,
1713 active_conferences: value.active_conferences,
1714 ipv4_address_scope: value.ipv4_address_scope,
1715 max_lines: value.max_lines,
1716 ipv6_address_scope: value.ipv6_address_scope,
1717 }),
1718 }))
1719 }
1720 id::IP_PORT => {
1721 let value: WireOneWord = decode(frame.message_id, p)?;
1722 Ok(Self::IpPort {
1723 rtp_port: decode_port(value.value, frame.message_id, "RTP port")?,
1724 })
1725 }
1726 id::KEYPAD_BUTTON => {
1727 let (button, line_instance, call_reference, wire_layout) = match p.len() {
1728 4 => {
1729 let value: WireKeypadButtonLegacy = decode(frame.message_id, p)?;
1730 (
1731 value.button,
1732 0,
1733 0,
1734 Some(KeypadButtonWireLayout::LegacyButtonOnly),
1735 )
1736 }
1737 12 => {
1738 let value: WireKeypadButtonWithCall = decode(frame.message_id, p)?;
1739 (
1740 value.button,
1741 value.line_instance,
1742 value.call_reference,
1743 Some(KeypadButtonWireLayout::WithCallIdentity),
1744 )
1745 }
1746 20 => {
1747 let value: WireKeypadButton = decode(frame.message_id, p)?;
1748 if value.keypad_union != 0 || value.reserved != 0 {
1749 return Err(CodecError::InvalidValue {
1750 message_id: frame.message_id,
1751 field: "keypad reserved fields",
1752 value: 1,
1753 });
1754 }
1755 (
1756 value.button,
1757 value.line_instance,
1758 value.call_reference,
1759 None,
1760 )
1761 }
1762 _ => return Err(CodecError::InvalidLength(frame.message_id)),
1763 };
1764 Ok(Self::KeypadButton {
1765 button: Digit::from_keypad(button),
1766 line_instance,
1767 call_reference,
1768 wire_layout,
1769 })
1770 }
1771 id::ENBLOC_CALL => {
1772 let (called_party, line_instance) = if protocol_version >= 19 {
1773 let value: WireEnblocFrom19 = decode(frame.message_id, p)?;
1774 validate_zero_payload(&value.alignment, frame.message_id, 3)?;
1775 (value.called_party.text()?, value.line_instance)
1776 } else {
1777 let value: WireEnblocBefore19 = decode(frame.message_id, p)?;
1778 (value.called_party.text()?, value.line_instance)
1779 };
1780 Ok(Self::EnblocCall {
1781 called_party,
1782 line_instance,
1783 })
1784 }
1785 id::STIMULUS => {
1786 let value: WireStimulus = decode(frame.message_id, p)?;
1787 Ok(Self::Stimulus {
1788 stimulus: Stimulus::from(value.stimulus),
1789 instance: value.instance,
1790 call_reference: value.call_reference,
1791 status: value.status,
1792 })
1793 }
1794 id::OFF_HOOK => {
1795 let value: WireLineCall = decode(frame.message_id, p)?;
1796 Ok(Self::OffHook {
1797 line_instance: value.line_instance,
1798 call_reference: value.call_reference,
1799 })
1800 }
1801 id::ON_HOOK => {
1802 let value: WireLineCall = decode(frame.message_id, p)?;
1803 Ok(Self::OnHook {
1804 line_instance: value.line_instance,
1805 call_reference: value.call_reference,
1806 })
1807 }
1808 id::OFF_HOOK_WITH_CALLING_PARTY => {
1809 let (calling_party_number, voice_mailbox, line_instance) = if protocol_version >= 19
1810 {
1811 let value: WireOffHookWithCallingPartyFrom19 = decode(frame.message_id, p)?;
1812 validate_zero_payload(&value.alignment, frame.message_id, 2)?;
1813 (
1814 value.calling_party_number.text()?,
1815 value.voice_mailbox.text()?,
1816 value.line_instance,
1817 )
1818 } else {
1819 let value: WireOffHookWithCallingPartyBefore19 = decode(frame.message_id, p)?;
1820 (
1821 value.calling_party_number.text()?,
1822 value.voice_mailbox.text()?,
1823 value.line_instance,
1824 )
1825 };
1826 Ok(Self::OffHookWithCallingParty {
1827 calling_party_number,
1828 voice_mailbox,
1829 line_instance,
1830 })
1831 }
1832 id::LINE_STAT_REQ => {
1833 let value: WireOneWord = decode(frame.message_id, p)?;
1834 Ok(Self::LineStatRequest {
1835 line_instance: value.value,
1836 })
1837 }
1838 id::CONFIG_STAT_REQ => Ok(Self::ConfigStatRequest),
1839 id::TIME_DATE_REQ => Ok(Self::TimeDateRequest),
1840 id::BUTTON_TEMPLATE_REQ => Ok(Self::ButtonTemplateRequest),
1841 id::VERSION_REQ => Ok(Self::VersionRequest),
1842 id::CAPABILITIES_RES => {
1843 let count = usize_from_wire(
1844 frame.message_id,
1845 "audio capabilities",
1846 decode_prefix::<WireOneWord>(frame.message_id, p)?.value,
1847 )?;
1848 if count > 18 {
1849 return Err(CodecError::CountTooLarge {
1850 message_id: frame.message_id,
1851 field: "audio capabilities",
1852 count,
1853 maximum: 18,
1854 });
1855 }
1856 let value: WireCapabilitiesResponse = decode(frame.message_id, p)?;
1857 let caps = value
1858 .capabilities
1859 .into_iter()
1860 .map(|capability| MediaCapability {
1861 codec: Codec::from(capability.codec),
1862 max_frames_per_packet: capability.max_frames_per_packet,
1863 codec_parameters: capability.codec_parameters,
1864 })
1865 .collect();
1866 Ok(Self::CapabilitiesResponse(caps))
1867 }
1868 id::UPDATE_CAPABILITIES => {
1869 let expanded_layout = CapabilityUpdateVariant::Version1ExpandedVideo;
1870 let variant = if protocol_version >= 16
1871 && p.len() >= expanded_layout.minimum_payload_bytes(protocol_version)
1872 {
1873 expanded_layout
1874 } else {
1875 CapabilityUpdateVariant::Version1
1876 };
1877 CapabilityUpdate::decode(variant, protocol_version, p).map(Self::CapabilitiesUpdate)
1878 }
1879 id::UPDATE_CAPABILITIES_V2 => {
1880 CapabilityUpdate::decode(CapabilityUpdateVariant::Version2, protocol_version, p)
1881 .map(Self::CapabilitiesUpdate)
1882 }
1883 id::UPDATE_CAPABILITIES_V3 => {
1884 CapabilityUpdate::decode(CapabilityUpdateVariant::Version3, protocol_version, p)
1885 .map(Self::CapabilitiesUpdate)
1886 }
1887 id::OPEN_MULTIMEDIA_RECEIVE_CHANNEL_ACK => {
1888 decode_open_multimedia_ack(p, protocol_version, frame.message_id)
1889 .map(Self::OpenMultimediaReceiveChannelAck)
1890 }
1891 id::SERVER_REQ => Ok(Self::ServerRequest),
1892 id::ALARM => match p.len() {
1893 84 => {
1894 let value: WireAlarmLegacy = decode(frame.message_id, p)?;
1895 Ok(Self::Alarm {
1896 severity: AlarmSeverity::from(value.severity),
1897 text: value.text.text()?,
1898 parameters: None,
1899 })
1900 }
1901 92 => {
1902 let value: WireAlarm = decode(frame.message_id, p)?;
1903 Ok(Self::Alarm {
1904 severity: AlarmSeverity::from(value.severity),
1905 text: value.text.text()?,
1906 parameters: Some([value.parameter_1, value.parameter_2]),
1907 })
1908 }
1909 _ => Err(CodecError::InvalidLength(frame.message_id)),
1910 },
1911 id::MULTICAST_MEDIA_RECEPTION_ACK => {
1912 validate_exact_payload(p, frame.message_id, 12)?;
1913 let value: WireMulticastReceptionAck = decode(frame.message_id, p)?;
1914 Ok(Self::MulticastMediaReceptionAck {
1915 status: MediaStatus::from(value.status),
1916 passthrough_party_id: value.passthrough_party_id.into(),
1917 call_reference: value.call_reference.into(),
1918 })
1919 }
1920 id::OPEN_RECEIVE_CHANNEL_ACK => {
1921 if protocol_version >= 17 {
1922 let value: WireOpenReceiveAckV17 = decode(frame.message_id, p)?;
1923 Ok(Self::OpenReceiveChannelAck {
1924 status: MediaStatus::from(value.status),
1925 address: value.address.to_ip(frame.message_id)?,
1926 port: decode_port(value.port, frame.message_id, "RTP port")?,
1927 passthrough_party_id: value.passthrough_party_id,
1928 call_reference: value.call_reference,
1929 })
1930 } else {
1931 let value: WireOpenReceiveAckV3 = decode(frame.message_id, p)?;
1932 Ok(Self::OpenReceiveChannelAck {
1933 status: MediaStatus::from(value.status),
1934 address: IpAddr::V4(Ipv4Addr::from(value.address)),
1935 port: decode_port(value.port, frame.message_id, "RTP port")?,
1936 passthrough_party_id: value.passthrough_party_id,
1937 call_reference: value.call_reference,
1938 })
1939 }
1940 }
1941 id::SOFT_KEY_SET_REQ => Ok(Self::SoftKeySetRequest),
1942 id::SOFT_KEY_TEMPLATE_REQ => Ok(Self::SoftKeyTemplateRequest),
1943 id::SOFT_KEY_EVENT => {
1944 let value: WireSoftKeyEvent = decode(frame.message_id, p)?;
1945 Ok(Self::SoftKeyEvent {
1946 event: value.event,
1947 line_instance: value.line_instance,
1948 call_reference: value.call_reference,
1949 })
1950 }
1951 id::UNREGISTER => {
1952 let reason = if p.is_empty() {
1953 0
1954 } else {
1955 decode::<WireOneWord>(frame.message_id, p)?.value
1956 };
1957 Ok(Self::Unregister { reason })
1958 }
1959 id::REGISTER_TOKEN_REQ => {
1960 let value: WireRegisterToken = decode(frame.message_id, p)?;
1961 let address = if value.ipv6_address.iter().any(|byte| *byte != 0) {
1962 IpAddr::V6(Ipv6Addr::from(value.ipv6_address))
1963 } else {
1964 IpAddr::V4(Ipv4Addr::from(value.ipv4_address))
1965 };
1966 Ok(Self::RegisterToken(RegisterTokenMessage {
1967 device_id: DeviceId::new(value.device_id.text()?)?,
1968 device_instance: value.device_instance,
1969 address,
1970 device_type: DeviceType::from(value.device_type),
1971 flags: value.flags,
1972 }))
1973 }
1974 id::HOOK_FLASH => {
1975 let value: WireLineCall = decode(frame.message_id, p)?;
1976 Ok(Self::HookFlash {
1977 line_instance: value.line_instance,
1978 call_reference: value.call_reference,
1979 })
1980 }
1981 id::FORWARD_STAT_REQ => {
1982 let value: WireOneWord = decode(frame.message_id, p)?;
1983 Ok(Self::ForwardStatusRequest {
1984 line_instance: value.value,
1985 })
1986 }
1987 id::SPEED_DIAL_STAT_REQ => {
1988 let value: WireOneWord = decode(frame.message_id, p)?;
1989 Ok(Self::SpeedDialStatusRequest {
1990 speed_dial_instance: value.value,
1991 })
1992 }
1993 id::HEADSET_STATUS => {
1994 let value: WireOneWord = decode(frame.message_id, p)?;
1995 Ok(Self::HeadsetStatus {
1996 enabled: value.value == 1,
1997 })
1998 }
1999 id::MEDIA_RESOURCE_NOTIFICATION => {
2000 let value: WireMediaResourceNotification = decode(frame.message_id, p)?;
2001 Ok(Self::MediaResourceNotification(MediaResourceNotification {
2002 device_type: DeviceType::from(value.device_type),
2003 in_service_streams: value.in_service_streams,
2004 max_streams_per_conference: value.max_streams_per_conference,
2005 out_of_service_streams: value.out_of_service_streams,
2006 }))
2007 }
2008 id::ACCESSORY_STATUS => {
2009 let value: WireAccessoryStatus = decode(frame.message_id, p)?;
2010 Ok(Self::MediaPathEvent {
2011 path: MediaPathId::from(value.accessory),
2012 event: MediaPathEvent::from(value.state),
2013 })
2014 }
2015 id::MEDIA_PATH_CAPABILITY => {
2016 let value: WireAccessoryStatus = decode(frame.message_id, p)?;
2017 Ok(Self::MediaPathCapability {
2018 path: MediaPathId::from(value.accessory),
2019 capability: MediaPathCapability::from(value.state),
2020 })
2021 }
2022 id::REGISTER_AVAILABLE_LINES => {
2023 let lines = if p.len() >= std::mem::size_of::<u32>() {
2024 decode::<WireOneWord>(frame.message_id, p)?.value
2025 } else {
2026 0
2027 };
2028 Ok(Self::RegisterAvailableLines { lines })
2029 }
2030 id::DEVICE_TO_USER_DATA => {
2031 decode_user_data(p, frame.message_id).map(Self::DeviceToUserData)
2032 }
2033 id::DEVICE_TO_USER_DATA_RESPONSE => {
2034 decode_user_data(p, frame.message_id).map(Self::DeviceToUserDataResponse)
2035 }
2036 id::DEVICE_TO_USER_DATA_V1 => {
2037 decode_user_data_v1(p, frame.message_id).map(Self::DeviceToUserDataV1)
2038 }
2039 id::DEVICE_TO_USER_DATA_RESPONSE_V1 => {
2040 decode_user_data_v1(p, frame.message_id).map(Self::DeviceToUserDataResponseV1)
2041 }
2042 id::PORT_RESPONSE => {
2043 decode_port_response(p, protocol_version, frame.message_id).map(Self::PortResponse)
2044 }
2045 id::SUBSCRIPTION_STAT_REQ => {
2046 let value: WireSubscriptionRequest = decode(frame.message_id, p)?;
2047 Ok(Self::SubscriptionStatusRequest(SubscriptionRequest {
2048 transaction_id: value.transaction_id,
2049 feature_id: value.feature_id,
2050 timer_seconds: value.timer_seconds,
2051 subscription_id: value.subscription_id.text()?,
2052 }))
2053 }
2054 id::SUBSCRIBE_DTMF_PAYLOAD_RES => {
2055 let value: WireDtmfPayloadIdentity = decode(frame.message_id, p)?;
2056 Ok(Self::SubscribeDtmfPayloadResponse(
2057 dtmf_payload_identity_from_wire(value),
2058 ))
2059 }
2060 id::UNSUBSCRIBE_DTMF_PAYLOAD_RES => {
2061 let value: WireDtmfPayloadIdentity = decode(frame.message_id, p)?;
2062 Ok(Self::UnsubscribeDtmfPayloadResponse(
2063 dtmf_payload_identity_from_wire(value),
2064 ))
2065 }
2066 id::SERVICE_URL_STAT_REQ => {
2067 let value: WireOneWord = decode(frame.message_id, p)?;
2068 Ok(Self::ServiceUrlStatusRequest { index: value.value })
2069 }
2070 id::FEATURE_STAT_REQ => {
2071 let value: WireFeatureStatusRequest = decode(frame.message_id, p)?;
2072 Ok(Self::FeatureStatusRequest {
2073 index: value.index,
2074 capabilities: value.capabilities,
2075 })
2076 }
2077 id::MEDIA_TRANSMISSION_FAILURE => {
2078 if protocol_version >= 17 {
2079 let value: WireMediaFailureV17 = decode(frame.message_id, p)?;
2080 Ok(Self::MediaTransmissionFailure {
2081 conference_id: value.conference_id,
2082 passthrough_party_id: value.passthrough_party_id,
2083 address: value.address.to_ip(frame.message_id)?,
2084 port: decode_port(value.port, frame.message_id, "RTP port")?,
2085 call_reference: value.call_reference,
2086 status: MediaStatus::UnspecifiedError,
2087 })
2088 } else {
2089 let value: WireMediaFailureV3 = decode(frame.message_id, p)?;
2090 Ok(Self::MediaTransmissionFailure {
2091 conference_id: value.conference_id,
2092 passthrough_party_id: value.passthrough_party_id,
2093 address: IpAddr::V4(Ipv4Addr::from(value.address)),
2094 port: decode_port(value.port, frame.message_id, "RTP port")?,
2095 call_reference: value.call_reference,
2096 status: MediaStatus::UnspecifiedError,
2097 })
2098 }
2099 }
2100 id::CONNECTION_STATISTICS_RES => {
2101 decode_connection_statistics(p, protocol_version, frame.message_id)
2102 .map(Self::ConnectionStatisticsResponse)
2103 }
2104 id::START_MEDIA_TRANSMISSION_ACK => {
2105 decode_start_media_ack(p, protocol_version, frame.message_id)
2106 .map(Self::StartMediaTransmissionAck)
2107 }
2108 id::START_MULTIMEDIA_TRANSMISSION_ACK => {
2109 decode_start_multimedia_ack(p, protocol_version, frame.message_id)
2110 .map(Self::StartMultimediaTransmissionAck)
2111 }
2112 id::EXTENSION_DEVICE_CAPABILITIES => {
2113 let value: WireExtensionDeviceCapabilities = decode(frame.message_id, p)?;
2114 Ok(Self::ExtensionDeviceCapabilities(
2115 ExtensionDeviceCapabilities {
2116 unknown_1: value.unknown_1,
2117 unknown_2: value.unknown_2,
2118 unknown_3: value.unknown_3,
2119 description: value.description.text()?,
2120 },
2121 ))
2122 }
2123 id::LOCATION_INFO => {
2124 let value: WireLocationInfo = decode(frame.message_id, p)?;
2125 validate_zero_payload(&value.alignment, frame.message_id, 3)?;
2126 Ok(Self::LocationInfo {
2127 xml: value.xml.text()?,
2128 })
2129 }
2130 id::XML_ALARM => XmlAlarmMessage::from_wire_payload(p.to_vec()).map(Self::XmlAlarm),
2131 id::CALL_COUNT_REQ => {
2132 let value: WireOneWord = decode(frame.message_id, p)?;
2133 Ok(Self::CallCountRequest { value: value.value })
2134 }
2135 id::CREATE_CONFERENCE_RES => {
2136 validate_conference_data_length(p, frame.message_id, 12, 8)?;
2137 let value: WireConferenceResponse = decode_zero_padded(frame.message_id, p)?;
2138 Ok(Self::CreateConferenceResponse(CreateConferenceResponse {
2139 conference_id: value.conference_id.into(),
2140 result: CreateConferenceResult::from(value.result),
2141 passthrough_data: value.passthrough_data,
2142 }))
2143 }
2144 id::DELETE_CONFERENCE_RES => {
2145 validate_exact_payload(p, frame.message_id, 8)?;
2146 let value: WireCallParty = decode(frame.message_id, p)?;
2147 Ok(Self::DeleteConferenceResponse {
2148 conference_id: value.call_reference.into(),
2149 result: DeleteConferenceResult::from(value.passthrough_party_id),
2150 })
2151 }
2152 id::MODIFY_CONFERENCE_RES => {
2153 validate_conference_data_length(p, frame.message_id, 12, 8)?;
2154 let value: WireConferenceResponse = decode_zero_padded(frame.message_id, p)?;
2155 Ok(Self::ModifyConferenceResponse(ModifyConferenceResponse {
2156 conference_id: value.conference_id.into(),
2157 result: ModifyConferenceResult::from(value.result),
2158 passthrough_data: value.passthrough_data,
2159 }))
2160 }
2161 id::AUDIT_CONFERENCE_RES => {
2162 if p.len() < 8 {
2163 return Err(CodecError::Truncated {
2164 message_id: frame.message_id,
2165 needed: 8,
2166 actual: p.len(),
2167 });
2168 }
2169 let number_of_entries = usize_from_wire(
2170 frame.message_id,
2171 "conference audit entries",
2172 u32::from_le_bytes(p[4..8].try_into().expect("validated audit header")),
2173 )?;
2174 if number_of_entries > MAX_AUDIT_CONFERENCE_ENTRIES {
2175 return Err(CodecError::CountTooLarge {
2176 message_id: frame.message_id,
2177 field: "conference audit entries",
2178 count: number_of_entries,
2179 maximum: MAX_AUDIT_CONFERENCE_ENTRIES,
2180 });
2181 }
2182 validate_exact_payload(p, frame.message_id, 8 + number_of_entries * 76)?;
2183 let value: WireAuditConferenceResponse = decode(frame.message_id, p)?;
2184 Ok(Self::AuditConferenceResponse(AuditConferenceResponse {
2185 last: value.last,
2186 entries: value
2187 .entries
2188 .into_iter()
2189 .map(|entry| {
2190 Ok(AuditConferenceEntry {
2191 conference_id: entry.conference_id.into(),
2192 resource_type: ConferenceResourceType::from(entry.resource_type),
2193 reserved_participants: entry.reserved_participants,
2194 active_participants: entry.active_participants,
2195 application_id: entry.application_id.into(),
2196 application_conference_id: entry
2197 .application_conference_id
2198 .text()?,
2199 application_data: entry.application_data.text()?,
2200 })
2201 })
2202 .collect::<Result<Vec<_>, CodecError>>()?,
2203 }))
2204 }
2205 id::ADD_PARTICIPANT_RES => {
2206 validate_payload_bounds(p, frame.message_id, 12, 272)?;
2207 let value: WireAddParticipantResponseHeader = decode_prefix(frame.message_id, p)?;
2208 let identifier_end = if p.len() == 272 {
2209 if p[269..].iter().any(|byte| *byte != 0) {
2210 return Err(CodecError::InvalidValue {
2211 message_id: frame.message_id,
2212 field: "AddParticipantResponse alignment",
2213 value: 1,
2214 });
2215 }
2216 269
2217 } else {
2218 p.len()
2219 };
2220 let identifier = &p[12..identifier_end];
2221 let bridge_participant_id =
2222 BoundedBytes::try_from(identifier).map_err(|error| {
2223 CodecError::CountTooLarge {
2224 message_id: frame.message_id,
2225 field: "bridge participant identifier",
2226 count: error.actual,
2227 maximum: error.maximum,
2228 }
2229 })?;
2230 Ok(Self::AddParticipantResponse(AddParticipantResponse {
2231 conference_id: value.conference_id.into(),
2232 call_reference: value.call_reference.into(),
2233 result: AddParticipantResult::from(value.result),
2234 bridge_participant_id,
2235 }))
2236 }
2237 id::AUDIT_PARTICIPANT_RES => {
2238 if p.len() < 16 {
2239 return Err(CodecError::Truncated {
2240 message_id: frame.message_id,
2241 needed: 16,
2242 actual: p.len(),
2243 });
2244 }
2245 let participant_entries = &p[16..];
2246 if participant_entries.len() > MAX_AUDIT_PARTICIPANT_DATA {
2247 return Err(CodecError::CountTooLarge {
2248 message_id: frame.message_id,
2249 field: "participant audit data",
2250 count: participant_entries.len(),
2251 maximum: MAX_AUDIT_PARTICIPANT_DATA,
2252 });
2253 }
2254 let value: WireAuditParticipantResponseHeader = decode(frame.message_id, &p[..16])?;
2255 Ok(Self::AuditParticipantResponse(AuditParticipantResponse {
2256 result: AuditParticipantResult::from(value.result),
2257 last: value.last,
2258 conference_id: value.conference_id.into(),
2259 number_of_entries: value.number_of_entries,
2260 participant_entries: participant_entries.to_vec(),
2261 }))
2262 }
2263 _ => {
2264 let id = frame.message_type();
2265 if id.is_known() {
2266 preserve_known_message(frame, id).map(Self::KnownOpaque)
2267 } else {
2268 Ok(Self::Unknown(RawMessage {
2269 message_id: frame.message_id,
2270 protocol_version: frame.protocol_version,
2271 payload: frame.payload,
2272 }))
2273 }
2274 }
2275 }
2276 }
2277
2278 pub fn encode(&self, protocol: ProtocolVersion) -> Result<Vec<u8>, CodecError> {
2280 let (message_id, payload, header_protocol) = self.payload(protocol)?;
2281 reject_non_station_route(
2282 message_id,
2283 MessageRoute::StationToControl,
2284 "station-to-control",
2285 )?;
2286 Frame::new(header_protocol, message_id, payload).encode()
2287 }
2288
2289 fn encode_unchecked(&self, protocol: ProtocolVersion) -> Result<Vec<u8>, CodecError> {
2290 let (message_id, payload, header_protocol) = self.payload(protocol)?;
2291 Frame::new(header_protocol, message_id, payload).encode()
2292 }
2293
2294 fn payload(&self, protocol: ProtocolVersion) -> Result<(u32, Vec<u8>, u32), CodecError> {
2295 let mut payload = Vec::new();
2296 let mut header_protocol = protocol.wire();
2297 let message_id = match self {
2298 Self::KeepAlive => {
2299 header_protocol = 0;
2300 id::KEEP_ALIVE
2301 }
2302 Self::Register(registration) => {
2303 header_protocol = 0;
2304 let feature_bytes = registration.features.bits().to_le_bytes();
2305 let wire = registration.wire.unwrap_or(RegistrationWireDetails {
2306 station_user_id: 0,
2307 station_instance: 1,
2308 max_streams: 0,
2309 active_streams: 0,
2310 mac_address_and_padding: [0; 12],
2311 max_conferences: 0,
2312 active_conferences: 0,
2313 ipv4_address_scope: 0,
2314 max_lines: 0,
2315 ipv6_address_scope: 0,
2316 });
2317 payload = encode(
2318 id::REGISTER,
2319 &WireRegister {
2320 device_id: WireFixedText::new(
2321 id::REGISTER,
2322 "device ID",
2323 registration.device_id.as_str(),
2324 )?,
2325 station_user_id: wire.station_user_id,
2326 station_instance: wire.station_instance,
2327 reported_address: registration
2328 .reported_address
2329 .unwrap_or(Ipv4Addr::UNSPECIFIED)
2330 .octets(),
2331 device_type: registration.device_type.wire_value(),
2332 max_streams: wire.max_streams,
2333 active_streams: wire.active_streams,
2334 protocol_features: [
2335 registration.advertised_protocol.min(u32::from(u8::MAX)) as u8,
2336 feature_bytes[1],
2337 feature_bytes[2],
2338 feature_bytes[3],
2339 ],
2340 max_conferences: wire.max_conferences,
2341 active_conferences: wire.active_conferences,
2342 mac_address: wire.mac_address_and_padding,
2343 ipv4_address_scope: wire.ipv4_address_scope,
2344 max_lines: wire.max_lines,
2345 ipv6_address: registration
2346 .reported_ipv6_address
2347 .unwrap_or(Ipv6Addr::UNSPECIFIED)
2348 .octets(),
2349 ipv6_address_scope: wire.ipv6_address_scope,
2350 firmware: WireFixedText::new(
2351 id::REGISTER,
2352 "firmware",
2353 ®istration.firmware,
2354 )?,
2355 },
2356 )?;
2357 payload.extend_from_slice(registration.configuration_version_stamp.as_bytes());
2358 id::REGISTER
2359 }
2360 Self::IpPort { rtp_port } => {
2361 payload = encode(
2362 id::IP_PORT,
2363 &WireOneWord {
2364 value: u32::from(*rtp_port),
2365 },
2366 )?;
2367 id::IP_PORT
2368 }
2369 Self::KeypadButton {
2370 button,
2371 line_instance,
2372 call_reference,
2373 wire_layout,
2374 } => {
2375 payload = match wire_layout {
2376 Some(KeypadButtonWireLayout::LegacyButtonOnly) => encode(
2377 id::KEYPAD_BUTTON,
2378 &WireKeypadButtonLegacy {
2379 button: button.keypad_value(),
2380 },
2381 )?,
2382 Some(KeypadButtonWireLayout::WithCallIdentity) => encode(
2383 id::KEYPAD_BUTTON,
2384 &WireKeypadButtonWithCall {
2385 button: button.keypad_value(),
2386 line_instance: *line_instance,
2387 call_reference: *call_reference,
2388 },
2389 )?,
2390 None => encode(
2391 id::KEYPAD_BUTTON,
2392 &WireKeypadButton {
2393 button: button.keypad_value(),
2394 line_instance: *line_instance,
2395 call_reference: *call_reference,
2396 keypad_union: 0,
2397 reserved: 0,
2398 },
2399 )?,
2400 };
2401 id::KEYPAD_BUTTON
2402 }
2403 Self::EnblocCall {
2404 called_party,
2405 line_instance,
2406 } => {
2407 if protocol.wire() >= 19 {
2408 payload = encode(
2409 id::ENBLOC_CALL,
2410 &WireEnblocFrom19 {
2411 called_party: WireFixedText::new(
2412 id::ENBLOC_CALL,
2413 "called party",
2414 called_party,
2415 )?,
2416 alignment: [0; 3],
2417 line_instance: *line_instance,
2418 },
2419 )?;
2420 } else {
2421 payload = encode(
2422 id::ENBLOC_CALL,
2423 &WireEnblocBefore19 {
2424 called_party: WireFixedText::new(
2425 id::ENBLOC_CALL,
2426 "called party",
2427 called_party,
2428 )?,
2429 line_instance: *line_instance,
2430 },
2431 )?;
2432 }
2433 id::ENBLOC_CALL
2434 }
2435 Self::Stimulus {
2436 stimulus,
2437 instance,
2438 call_reference,
2439 status,
2440 } => {
2441 payload = encode(
2442 id::STIMULUS,
2443 &WireStimulus {
2444 stimulus: stimulus.wire_value(),
2445 instance: *instance,
2446 call_reference: *call_reference,
2447 status: *status,
2448 },
2449 )?;
2450 id::STIMULUS
2451 }
2452 Self::OffHook {
2453 line_instance,
2454 call_reference,
2455 } => {
2456 payload = encode(
2457 id::OFF_HOOK,
2458 &WireLineCall {
2459 line_instance: *line_instance,
2460 call_reference: *call_reference,
2461 },
2462 )?;
2463 id::OFF_HOOK
2464 }
2465 Self::OnHook {
2466 line_instance,
2467 call_reference,
2468 } => {
2469 payload = encode(
2470 id::ON_HOOK,
2471 &WireLineCall {
2472 line_instance: *line_instance,
2473 call_reference: *call_reference,
2474 },
2475 )?;
2476 id::ON_HOOK
2477 }
2478 Self::OffHookWithCallingParty {
2479 calling_party_number,
2480 voice_mailbox,
2481 line_instance,
2482 } => {
2483 payload = if protocol.wire() >= 19 {
2484 encode(
2485 id::OFF_HOOK_WITH_CALLING_PARTY,
2486 &WireOffHookWithCallingPartyFrom19 {
2487 calling_party_number: WireFixedText::new(
2488 id::OFF_HOOK_WITH_CALLING_PARTY,
2489 "calling party number",
2490 calling_party_number,
2491 )?,
2492 voice_mailbox: WireFixedText::new(
2493 id::OFF_HOOK_WITH_CALLING_PARTY,
2494 "voice mailbox",
2495 voice_mailbox,
2496 )?,
2497 alignment: [0; 2],
2498 line_instance: *line_instance,
2499 },
2500 )?
2501 } else {
2502 encode(
2503 id::OFF_HOOK_WITH_CALLING_PARTY,
2504 &WireOffHookWithCallingPartyBefore19 {
2505 calling_party_number: WireFixedText::new(
2506 id::OFF_HOOK_WITH_CALLING_PARTY,
2507 "calling party number",
2508 calling_party_number,
2509 )?,
2510 voice_mailbox: WireFixedText::new(
2511 id::OFF_HOOK_WITH_CALLING_PARTY,
2512 "voice mailbox",
2513 voice_mailbox,
2514 )?,
2515 line_instance: *line_instance,
2516 },
2517 )?
2518 };
2519 id::OFF_HOOK_WITH_CALLING_PARTY
2520 }
2521 Self::HookFlash {
2522 line_instance,
2523 call_reference,
2524 } => {
2525 payload = encode(
2526 id::HOOK_FLASH,
2527 &WireLineCall {
2528 line_instance: *line_instance,
2529 call_reference: *call_reference,
2530 },
2531 )?;
2532 id::HOOK_FLASH
2533 }
2534 Self::ForwardStatusRequest { line_instance } => {
2535 payload = encode(
2536 id::FORWARD_STAT_REQ,
2537 &WireOneWord {
2538 value: *line_instance,
2539 },
2540 )?;
2541 id::FORWARD_STAT_REQ
2542 }
2543 Self::SpeedDialStatusRequest {
2544 speed_dial_instance,
2545 } => {
2546 payload = encode(
2547 id::SPEED_DIAL_STAT_REQ,
2548 &WireOneWord {
2549 value: *speed_dial_instance,
2550 },
2551 )?;
2552 id::SPEED_DIAL_STAT_REQ
2553 }
2554 Self::LineStatRequest { line_instance } => {
2555 payload = encode(
2556 id::LINE_STAT_REQ,
2557 &WireOneWord {
2558 value: *line_instance,
2559 },
2560 )?;
2561 id::LINE_STAT_REQ
2562 }
2563 Self::ConfigStatRequest => id::CONFIG_STAT_REQ,
2564 Self::TimeDateRequest => id::TIME_DATE_REQ,
2565 Self::ButtonTemplateRequest => id::BUTTON_TEMPLATE_REQ,
2566 Self::VersionRequest => id::VERSION_REQ,
2567 Self::CapabilitiesResponse(capabilities) => {
2568 if capabilities.len() > 18 {
2569 return Err(CodecError::CountTooLarge {
2570 message_id: id::CAPABILITIES_RES,
2571 field: "audio capabilities",
2572 count: capabilities.len(),
2573 maximum: 18,
2574 });
2575 }
2576 payload = encode(
2577 id::CAPABILITIES_RES,
2578 &WireCapabilitiesResponse {
2579 count: wire_count(
2580 id::CAPABILITIES_RES,
2581 "audio capabilities",
2582 capabilities.len(),
2583 )?,
2584 capabilities: capabilities
2585 .iter()
2586 .map(|capability| WireMediaCapability {
2587 codec: capability.codec.wire_value(),
2588 max_frames_per_packet: capability.max_frames_per_packet,
2589 codec_parameters: capability.codec_parameters,
2590 })
2591 .collect(),
2592 },
2593 )?;
2594 id::CAPABILITIES_RES
2595 }
2596 Self::CapabilitiesUpdate(update) => {
2597 payload.extend_from_slice(update.raw_payload());
2598 update.variant().message_id()
2599 }
2600 Self::OpenMultimediaReceiveChannelAck(ack) => {
2601 payload = encode_open_multimedia_ack(*ack, protocol)?;
2602 id::OPEN_MULTIMEDIA_RECEIVE_CHANNEL_ACK
2603 }
2604 Self::ServerRequest => id::SERVER_REQ,
2605 Self::Alarm {
2606 severity,
2607 text,
2608 parameters,
2609 } => {
2610 let text = WireFixedText::new(id::ALARM, "alarm text", text)?;
2611 payload = if let Some([parameter_1, parameter_2]) = parameters {
2612 encode(
2613 id::ALARM,
2614 &WireAlarm {
2615 severity: severity.wire_value(),
2616 text,
2617 parameter_1: *parameter_1,
2618 parameter_2: *parameter_2,
2619 },
2620 )?
2621 } else {
2622 encode(
2623 id::ALARM,
2624 &WireAlarmLegacy {
2625 severity: severity.wire_value(),
2626 text,
2627 },
2628 )?
2629 };
2630 id::ALARM
2631 }
2632 Self::MulticastMediaReceptionAck {
2633 status,
2634 passthrough_party_id,
2635 call_reference,
2636 } => {
2637 payload = encode(
2638 id::MULTICAST_MEDIA_RECEPTION_ACK,
2639 &WireMulticastReceptionAck {
2640 status: status.wire_value(),
2641 passthrough_party_id: passthrough_party_id.get(),
2642 call_reference: call_reference.get(),
2643 },
2644 )?;
2645 id::MULTICAST_MEDIA_RECEPTION_ACK
2646 }
2647 Self::OpenReceiveChannelAck {
2648 status,
2649 address,
2650 port,
2651 passthrough_party_id,
2652 call_reference,
2653 } => {
2654 if protocol.wire() >= 17 {
2655 payload = encode(
2656 id::OPEN_RECEIVE_CHANNEL_ACK,
2657 &WireOpenReceiveAckV17 {
2658 status: status.wire_value(),
2659 address: WireExtendedAddress::from_ip(*address),
2660 port: u32::from(*port),
2661 passthrough_party_id: *passthrough_party_id,
2662 call_reference: *call_reference,
2663 },
2664 )?;
2665 } else {
2666 let IpAddr::V4(address) = address else {
2667 return Err(CodecError::InvalidValue {
2668 message_id: id::OPEN_RECEIVE_CHANNEL_ACK,
2669 field: "IP address family for this protocol version",
2670 value: 1,
2671 });
2672 };
2673 payload = encode(
2674 id::OPEN_RECEIVE_CHANNEL_ACK,
2675 &WireOpenReceiveAckV3 {
2676 status: status.wire_value(),
2677 address: address.octets(),
2678 port: u32::from(*port),
2679 passthrough_party_id: *passthrough_party_id,
2680 call_reference: *call_reference,
2681 },
2682 )?;
2683 }
2684 id::OPEN_RECEIVE_CHANNEL_ACK
2685 }
2686 Self::SoftKeySetRequest => id::SOFT_KEY_SET_REQ,
2687 Self::SoftKeyTemplateRequest => id::SOFT_KEY_TEMPLATE_REQ,
2688 Self::SoftKeyEvent {
2689 event,
2690 line_instance,
2691 call_reference,
2692 } => {
2693 payload = encode(
2694 id::SOFT_KEY_EVENT,
2695 &WireSoftKeyEvent {
2696 event: *event,
2697 line_instance: *line_instance,
2698 call_reference: *call_reference,
2699 },
2700 )?;
2701 id::SOFT_KEY_EVENT
2702 }
2703 Self::Unregister { reason } => {
2704 payload = encode(id::UNREGISTER, &WireOneWord { value: *reason })?;
2705 id::UNREGISTER
2706 }
2707 Self::RegisterToken(token) => {
2708 let (ipv4_address, ipv6_address) = match token.address {
2709 IpAddr::V4(address) => (address.octets(), [0; 16]),
2710 IpAddr::V6(address) => ([0; 4], address.octets()),
2711 };
2712 payload = encode(
2713 id::REGISTER_TOKEN_REQ,
2714 &WireRegisterToken {
2715 device_id: WireFixedText::new(
2716 id::REGISTER_TOKEN_REQ,
2717 "device ID",
2718 token.device_id.as_str(),
2719 )?,
2720 device_instance: token.device_instance,
2721 ipv4_address,
2722 device_type: token.device_type.wire_value(),
2723 ipv6_address,
2724 flags: token.flags,
2725 },
2726 )?;
2727 id::REGISTER_TOKEN_REQ
2728 }
2729 Self::ConnectionStatisticsResponse(statistics) => {
2730 payload = encode_connection_statistics(statistics, protocol)?;
2731 id::CONNECTION_STATISTICS_RES
2732 }
2733 Self::HeadsetStatus { enabled } => {
2734 payload = encode(
2735 id::HEADSET_STATUS,
2736 &WireOneWord {
2737 value: u32::from(*enabled),
2738 },
2739 )?;
2740 id::HEADSET_STATUS
2741 }
2742 Self::MediaResourceNotification(notification) => {
2743 payload = encode(
2744 id::MEDIA_RESOURCE_NOTIFICATION,
2745 &WireMediaResourceNotification {
2746 device_type: notification.device_type.wire_value(),
2747 in_service_streams: notification.in_service_streams,
2748 max_streams_per_conference: notification.max_streams_per_conference,
2749 out_of_service_streams: notification.out_of_service_streams,
2750 },
2751 )?;
2752 id::MEDIA_RESOURCE_NOTIFICATION
2753 }
2754 Self::MediaPathEvent { path, event } => {
2755 payload = encode(
2756 id::ACCESSORY_STATUS,
2757 &WireAccessoryStatus {
2758 accessory: path.wire_value(),
2759 state: event.wire_value(),
2760 },
2761 )?;
2762 id::ACCESSORY_STATUS
2763 }
2764 Self::MediaPathCapability { path, capability } => {
2765 payload = encode(
2766 id::MEDIA_PATH_CAPABILITY,
2767 &WireAccessoryStatus {
2768 accessory: path.wire_value(),
2769 state: capability.wire_value(),
2770 },
2771 )?;
2772 id::MEDIA_PATH_CAPABILITY
2773 }
2774 Self::MediaTransmissionFailure {
2775 conference_id,
2776 passthrough_party_id,
2777 address,
2778 port,
2779 call_reference,
2780 ..
2781 } => {
2782 if protocol.wire() >= 17 {
2783 payload = encode(
2784 id::MEDIA_TRANSMISSION_FAILURE,
2785 &WireMediaFailureV17 {
2786 conference_id: *conference_id,
2787 passthrough_party_id: *passthrough_party_id,
2788 address: WireExtendedAddress::from_ip(*address),
2789 port: u32::from(*port),
2790 call_reference: *call_reference,
2791 },
2792 )?;
2793 } else {
2794 let IpAddr::V4(address) = address else {
2795 return Err(CodecError::InvalidValue {
2796 message_id: id::MEDIA_TRANSMISSION_FAILURE,
2797 field: "IP address family for this protocol version",
2798 value: 1,
2799 });
2800 };
2801 payload = encode(
2802 id::MEDIA_TRANSMISSION_FAILURE,
2803 &WireMediaFailureV3 {
2804 conference_id: *conference_id,
2805 passthrough_party_id: *passthrough_party_id,
2806 address: address.octets(),
2807 port: u32::from(*port),
2808 call_reference: *call_reference,
2809 },
2810 )?;
2811 }
2812 id::MEDIA_TRANSMISSION_FAILURE
2813 }
2814 Self::RegisterAvailableLines { lines } => {
2815 payload = encode(id::REGISTER_AVAILABLE_LINES, &WireOneWord { value: *lines })?;
2816 id::REGISTER_AVAILABLE_LINES
2817 }
2818 Self::ServiceUrlStatusRequest { index } => {
2819 payload = encode(id::SERVICE_URL_STAT_REQ, &WireOneWord { value: *index })?;
2820 id::SERVICE_URL_STAT_REQ
2821 }
2822 Self::FeatureStatusRequest {
2823 index,
2824 capabilities,
2825 } => {
2826 payload = encode(
2827 id::FEATURE_STAT_REQ,
2828 &WireFeatureStatusRequest {
2829 index: *index,
2830 capabilities: *capabilities,
2831 },
2832 )?;
2833 id::FEATURE_STAT_REQ
2834 }
2835 Self::StartMediaTransmissionAck(ack) => {
2836 payload = encode_start_media_ack(ack, protocol)?;
2837 id::START_MEDIA_TRANSMISSION_ACK
2838 }
2839 Self::StartMultimediaTransmissionAck(ack) => {
2840 payload = encode_start_multimedia_ack(*ack, protocol)?;
2841 id::START_MULTIMEDIA_TRANSMISSION_ACK
2842 }
2843 Self::ExtensionDeviceCapabilities(capabilities) => {
2844 payload = encode(
2845 id::EXTENSION_DEVICE_CAPABILITIES,
2846 &WireExtensionDeviceCapabilities {
2847 unknown_1: capabilities.unknown_1,
2848 unknown_2: capabilities.unknown_2,
2849 unknown_3: capabilities.unknown_3,
2850 description: WireFixedText::new(
2851 id::EXTENSION_DEVICE_CAPABILITIES,
2852 "extension-device capability description",
2853 &capabilities.description,
2854 )?,
2855 },
2856 )?;
2857 id::EXTENSION_DEVICE_CAPABILITIES
2858 }
2859 Self::DeviceToUserData(data) => {
2860 payload = encode_user_data(data, id::DEVICE_TO_USER_DATA)?;
2861 id::DEVICE_TO_USER_DATA
2862 }
2863 Self::DeviceToUserDataResponse(data) => {
2864 payload = encode_user_data(data, id::DEVICE_TO_USER_DATA_RESPONSE)?;
2865 id::DEVICE_TO_USER_DATA_RESPONSE
2866 }
2867 Self::DeviceToUserDataV1(data) => {
2868 payload = encode_user_data_v1(data, id::DEVICE_TO_USER_DATA_V1)?;
2869 id::DEVICE_TO_USER_DATA_V1
2870 }
2871 Self::DeviceToUserDataResponseV1(data) => {
2872 payload = encode_user_data_v1(data, id::DEVICE_TO_USER_DATA_RESPONSE_V1)?;
2873 id::DEVICE_TO_USER_DATA_RESPONSE_V1
2874 }
2875 Self::PortResponse(endpoint) => {
2876 payload = encode_port_response(endpoint, protocol)?;
2877 id::PORT_RESPONSE
2878 }
2879 Self::SubscriptionStatusRequest(subscription) => {
2880 payload = encode(
2881 id::SUBSCRIPTION_STAT_REQ,
2882 &WireSubscriptionRequest {
2883 transaction_id: subscription.transaction_id,
2884 feature_id: subscription.feature_id,
2885 timer_seconds: subscription.timer_seconds,
2886 subscription_id: WireFixedText::new(
2887 id::SUBSCRIPTION_STAT_REQ,
2888 "subscription ID",
2889 &subscription.subscription_id,
2890 )?,
2891 },
2892 )?;
2893 id::SUBSCRIPTION_STAT_REQ
2894 }
2895 Self::SubscribeDtmfPayloadResponse(identity) => {
2896 payload = encode(
2897 id::SUBSCRIBE_DTMF_PAYLOAD_RES,
2898 &dtmf_payload_identity_to_wire(*identity),
2899 )?;
2900 id::SUBSCRIBE_DTMF_PAYLOAD_RES
2901 }
2902 Self::UnsubscribeDtmfPayloadResponse(identity) => {
2903 payload = encode(
2904 id::UNSUBSCRIBE_DTMF_PAYLOAD_RES,
2905 &dtmf_payload_identity_to_wire(*identity),
2906 )?;
2907 id::UNSUBSCRIBE_DTMF_PAYLOAD_RES
2908 }
2909 Self::LocationInfo { xml } => {
2910 payload = encode(
2911 id::LOCATION_INFO,
2912 &WireLocationInfo {
2913 xml: WireFixedText::new(id::LOCATION_INFO, "location XML", xml)?,
2914 alignment: [0; 3],
2915 },
2916 )?;
2917 id::LOCATION_INFO
2918 }
2919 Self::XmlAlarm(message) => {
2920 payload = message.wire_payload().to_vec();
2921 id::XML_ALARM
2922 }
2923 Self::CallCountRequest { value } => {
2924 payload = encode(id::CALL_COUNT_REQ, &WireOneWord { value: *value })?;
2925 id::CALL_COUNT_REQ
2926 }
2927 Self::CreateConferenceResponse(response) => {
2928 payload = encode(
2929 id::CREATE_CONFERENCE_RES,
2930 &WireConferenceResponse {
2931 conference_id: response.conference_id.get(),
2932 result: response.result.wire_value(),
2933 data_length: validate_conference_data_for_encode(
2934 id::CREATE_CONFERENCE_RES,
2935 &response.passthrough_data,
2936 )?,
2937 passthrough_data: response.passthrough_data.clone(),
2938 },
2939 )?;
2940 id::CREATE_CONFERENCE_RES
2941 }
2942 Self::DeleteConferenceResponse {
2943 conference_id,
2944 result,
2945 } => {
2946 payload = encode(
2947 id::DELETE_CONFERENCE_RES,
2948 &WireCallParty {
2949 call_reference: conference_id.get(),
2950 passthrough_party_id: result.wire_value(),
2951 },
2952 )?;
2953 id::DELETE_CONFERENCE_RES
2954 }
2955 Self::ModifyConferenceResponse(response) => {
2956 payload = encode(
2957 id::MODIFY_CONFERENCE_RES,
2958 &WireConferenceResponse {
2959 conference_id: response.conference_id.get(),
2960 result: response.result.wire_value(),
2961 data_length: validate_conference_data_for_encode(
2962 id::MODIFY_CONFERENCE_RES,
2963 &response.passthrough_data,
2964 )?,
2965 passthrough_data: response.passthrough_data.clone(),
2966 },
2967 )?;
2968 id::MODIFY_CONFERENCE_RES
2969 }
2970 Self::AuditConferenceResponse(response) => {
2971 if response.entries.len() > MAX_AUDIT_CONFERENCE_ENTRIES {
2972 return Err(CodecError::CountTooLarge {
2973 message_id: id::AUDIT_CONFERENCE_RES,
2974 field: "conference audit entries",
2975 count: response.entries.len(),
2976 maximum: MAX_AUDIT_CONFERENCE_ENTRIES,
2977 });
2978 }
2979 payload = encode(
2980 id::AUDIT_CONFERENCE_RES,
2981 &WireAuditConferenceResponse {
2982 last: response.last,
2983 number_of_entries: wire_count(
2984 id::AUDIT_CONFERENCE_RES,
2985 "conference audit entries",
2986 response.entries.len(),
2987 )?,
2988 entries: response
2989 .entries
2990 .iter()
2991 .map(|entry| {
2992 Ok(WireAuditConferenceEntry {
2993 conference_id: entry.conference_id.get(),
2994 resource_type: entry.resource_type.wire_value(),
2995 reserved_participants: entry.reserved_participants,
2996 active_participants: entry.active_participants,
2997 application_id: entry.application_id.get(),
2998 application_conference_id: WireFixedText::new(
2999 id::AUDIT_CONFERENCE_RES,
3000 "application conference ID",
3001 &entry.application_conference_id,
3002 )?,
3003 application_data: WireFixedText::new(
3004 id::AUDIT_CONFERENCE_RES,
3005 "application data",
3006 &entry.application_data,
3007 )?,
3008 })
3009 })
3010 .collect::<Result<Vec<_>, CodecError>>()?,
3011 },
3012 )?;
3013 id::AUDIT_CONFERENCE_RES
3014 }
3015 Self::AddParticipantResponse(response) => {
3016 payload = encode(
3017 id::ADD_PARTICIPANT_RES,
3018 &WireAddParticipantResponseHeader {
3019 conference_id: response.conference_id.get(),
3020 call_reference: response.call_reference.get(),
3021 result: response.result.wire_value(),
3022 },
3023 )?;
3024 payload.extend_from_slice(response.bridge_participant_id.as_bytes());
3025 payload.resize(269, 0);
3026 payload.extend_from_slice(&[0; 3]);
3027 id::ADD_PARTICIPANT_RES
3028 }
3029 Self::AuditParticipantResponse(response) => {
3030 if response.participant_entries.len() > MAX_AUDIT_PARTICIPANT_DATA {
3031 return Err(CodecError::CountTooLarge {
3032 message_id: id::AUDIT_PARTICIPANT_RES,
3033 field: "participant audit data",
3034 count: response.participant_entries.len(),
3035 maximum: MAX_AUDIT_PARTICIPANT_DATA,
3036 });
3037 }
3038 payload = encode(
3039 id::AUDIT_PARTICIPANT_RES,
3040 &WireAuditParticipantResponseHeader {
3041 result: response.result.wire_value(),
3042 last: response.last,
3043 conference_id: response.conference_id.get(),
3044 number_of_entries: response.number_of_entries,
3045 },
3046 )?;
3047 payload.extend_from_slice(&response.participant_entries);
3048 id::AUDIT_PARTICIPANT_RES
3049 }
3050 Self::KnownOpaque(message) => {
3051 ensure_preserve_only(message.id)?;
3052 return Ok((
3053 message.id.wire_value(),
3054 message.payload.as_bytes().to_vec(),
3055 message.protocol_version,
3056 ));
3057 }
3058 Self::Unknown(message) => {
3059 return Ok((
3060 message.message_id,
3061 message.payload.clone(),
3062 message.protocol_version,
3063 ));
3064 }
3065 };
3066 pad_typed_payload(message_id, &mut payload);
3067 Ok((message_id, payload, header_protocol))
3068 }
3069}
3070
3071fn decode_connection_statistics(
3072 payload: &[u8],
3073 protocol: u32,
3074 message_id: u32,
3075) -> Result<ConnectionStatistics, CodecError> {
3076 let (directory_number, call_reference, processing, statistics, quality) = if protocol >= 19 {
3077 let value: WireConnectionStatisticsV19 = decode_zero_padded(message_id, payload)?;
3078 validate_zero_payload(&value.alignment, message_id, 3)?;
3079 (
3080 value.directory_number.text()?,
3081 value.call_reference,
3082 StatisticsProcessing::from(value.processing),
3083 value.statistics,
3084 value.quality,
3085 )
3086 } else {
3087 let value: WireConnectionStatisticsV3 = decode_zero_padded(message_id, payload)?;
3088 (
3089 value.directory_number.text()?,
3090 value.call_reference,
3091 StatisticsProcessing::from(value.processing),
3092 value.statistics,
3093 value.quality,
3094 )
3095 };
3096 Ok(ConnectionStatistics {
3097 directory_number,
3098 call_reference,
3099 processing,
3100 packets_sent: statistics.packets_sent,
3101 octets_sent: statistics.octets_sent,
3102 packets_received: statistics.packets_received,
3103 octets_received: statistics.octets_received,
3104 packets_lost: statistics.packets_lost,
3105 jitter_millis: statistics.jitter_millis,
3106 latency_millis: statistics.latency_millis,
3107 quality: ConnectionQualityStatistics::new(quality)?,
3108 })
3109}
3110
3111fn decode_start_media_ack(
3112 payload: &[u8],
3113 protocol: u32,
3114 message_id: u32,
3115) -> Result<MediaTransmissionAck, CodecError> {
3116 if protocol >= 17 {
3117 let (value, v20_extension) = match payload.len() {
3118 40 => (decode::<WireStartMediaAckV17>(message_id, payload)?, None),
3119 48 => {
3120 let value = decode::<WireStartMediaAckV20>(message_id, payload)?;
3121 (value.base, Some(value.extension))
3122 }
3123 actual => {
3124 return Err(if actual < 40 {
3125 CodecError::Truncated {
3126 message_id,
3127 needed: 40,
3128 actual,
3129 }
3130 } else {
3131 CodecError::TrailingBytes {
3132 message_id,
3133 count: actual - 40,
3134 }
3135 });
3136 }
3137 };
3138 let wire = MediaTransmissionAckWire {
3139 extension: v20_extension,
3140 };
3141 Ok(MediaTransmissionAck {
3142 conference_id: value.conference_id,
3143 passthrough_party_id: value.passthrough_party_id,
3144 call_reference: value.call_reference,
3145 status: MediaStatus::from(value.status),
3146 address: value.address.to_ip(message_id)?,
3147 port: decode_port(value.port, message_id, "RTP port")?,
3148 wire: wire.extension.is_some().then_some(wire),
3149 })
3150 } else {
3151 let value: WireStartMediaAckV3 = decode(message_id, payload)?;
3152 Ok(MediaTransmissionAck {
3153 conference_id: value.conference_id,
3154 passthrough_party_id: value.passthrough_party_id,
3155 call_reference: value.call_reference,
3156 status: MediaStatus::from(value.status),
3157 address: IpAddr::V4(Ipv4Addr::from(value.address)),
3158 port: decode_port(value.port, message_id, "RTP port")?,
3159 wire: None,
3160 })
3161 }
3162}
3163
3164fn decode_user_data(payload: &[u8], message_id: u32) -> Result<UserDataMessage, CodecError> {
3165 let value: WireUserData = decode_zero_padded(message_id, payload)?;
3166 if value.data.len() > 2000 {
3167 return Err(CodecError::CountTooLarge {
3168 message_id,
3169 field: "user data",
3170 count: value.data.len(),
3171 maximum: 2000,
3172 });
3173 }
3174 Ok(UserDataMessage {
3175 application_id: value.application_id,
3176 line_instance: value.line_instance,
3177 call_reference: value.call_reference,
3178 transaction_id: value.transaction_id,
3179 data: value.data,
3180 })
3181}
3182
3183fn decode_open_multimedia_ack(
3184 payload: &[u8],
3185 protocol: u32,
3186 message_id: u32,
3187) -> Result<OpenMultimediaReceiveChannelAck, CodecError> {
3188 let (status, address, port, passthrough_party_id, call_reference) = if protocol >= 17 {
3189 let value: WireOpenMultimediaAckFrom17 = decode(message_id, payload)?;
3190 (
3191 value.status,
3192 value.address.to_ip(message_id)?,
3193 value.port,
3194 value.passthrough_party_id,
3195 value.call_reference,
3196 )
3197 } else {
3198 let value: WireOpenMultimediaAckPre17 = decode(message_id, payload)?;
3199 (
3200 value.status,
3201 IpAddr::V4(Ipv4Addr::from(value.address)),
3202 value.port,
3203 value.passthrough_party_id,
3204 value.call_reference,
3205 )
3206 };
3207 Ok(OpenMultimediaReceiveChannelAck {
3208 status: MediaStatus::from(status),
3209 endpoint: MediaEndpointAddress {
3210 address,
3211 port: decode_port(port, message_id, "multimedia port")?,
3212 },
3213 passthrough_party_id: passthrough_party_id.into(),
3214 call_reference: call_reference.into(),
3215 })
3216}
3217
3218fn encode_open_multimedia_ack(
3219 value: OpenMultimediaReceiveChannelAck,
3220 protocol: ProtocolVersion,
3221) -> Result<Vec<u8>, CodecError> {
3222 if protocol.wire() >= 17 {
3223 encode(
3224 id::OPEN_MULTIMEDIA_RECEIVE_CHANNEL_ACK,
3225 &WireOpenMultimediaAckFrom17 {
3226 status: value.status.wire_value(),
3227 address: WireExtendedAddress::from_ip(value.endpoint.address),
3228 port: u32::from(value.endpoint.port),
3229 passthrough_party_id: value.passthrough_party_id.get(),
3230 call_reference: value.call_reference.get(),
3231 },
3232 )
3233 } else {
3234 let IpAddr::V4(address) = value.endpoint.address else {
3235 return Err(CodecError::InvalidValue {
3236 message_id: id::OPEN_MULTIMEDIA_RECEIVE_CHANNEL_ACK,
3237 field: "IP address family for this protocol version",
3238 value: 1,
3239 });
3240 };
3241 encode(
3242 id::OPEN_MULTIMEDIA_RECEIVE_CHANNEL_ACK,
3243 &WireOpenMultimediaAckPre17 {
3244 status: value.status.wire_value(),
3245 address: address.octets(),
3246 port: u32::from(value.endpoint.port),
3247 passthrough_party_id: value.passthrough_party_id.get(),
3248 call_reference: value.call_reference.get(),
3249 },
3250 )
3251 }
3252}
3253
3254fn decode_start_multimedia_ack(
3255 payload: &[u8],
3256 protocol: u32,
3257 message_id: u32,
3258) -> Result<StartMultimediaTransmissionAck, CodecError> {
3259 let (conference_id, party_id, call_reference, address, port, status) = if protocol >= 17 {
3260 let value: WireStartMultimediaAckFrom17 = decode(message_id, payload)?;
3261 (
3262 value.conference_id,
3263 value.passthrough_party_id,
3264 value.call_reference,
3265 value.address.to_ip(message_id)?,
3266 value.port,
3267 value.status,
3268 )
3269 } else {
3270 let value: WireStartMultimediaAckPre17 = decode(message_id, payload)?;
3271 (
3272 value.conference_id,
3273 value.passthrough_party_id,
3274 value.call_reference,
3275 IpAddr::V4(Ipv4Addr::from(value.address)),
3276 value.port,
3277 value.status,
3278 )
3279 };
3280 Ok(StartMultimediaTransmissionAck {
3281 conference_id: conference_id.into(),
3282 passthrough_party_id: party_id.into(),
3283 call_reference: call_reference.into(),
3284 endpoint: MediaEndpointAddress {
3285 address,
3286 port: decode_port(port, message_id, "multimedia port")?,
3287 },
3288 status: MediaStatus::from(status),
3289 })
3290}
3291
3292fn encode_start_multimedia_ack(
3293 value: StartMultimediaTransmissionAck,
3294 protocol: ProtocolVersion,
3295) -> Result<Vec<u8>, CodecError> {
3296 if protocol.wire() >= 17 {
3297 encode(
3298 id::START_MULTIMEDIA_TRANSMISSION_ACK,
3299 &WireStartMultimediaAckFrom17 {
3300 conference_id: value.conference_id.get(),
3301 passthrough_party_id: value.passthrough_party_id.get(),
3302 call_reference: value.call_reference.get(),
3303 address: WireExtendedAddress::from_ip(value.endpoint.address),
3304 port: u32::from(value.endpoint.port),
3305 status: value.status.wire_value(),
3306 },
3307 )
3308 } else {
3309 let IpAddr::V4(address) = value.endpoint.address else {
3310 return Err(CodecError::InvalidValue {
3311 message_id: id::START_MULTIMEDIA_TRANSMISSION_ACK,
3312 field: "IP address family for this protocol version",
3313 value: 1,
3314 });
3315 };
3316 encode(
3317 id::START_MULTIMEDIA_TRANSMISSION_ACK,
3318 &WireStartMultimediaAckPre17 {
3319 conference_id: value.conference_id.get(),
3320 passthrough_party_id: value.passthrough_party_id.get(),
3321 call_reference: value.call_reference.get(),
3322 address: address.octets(),
3323 port: u32::from(value.endpoint.port),
3324 status: value.status.wire_value(),
3325 },
3326 )
3327 }
3328}
3329
3330fn decode_session_transmission(
3331 payload: &[u8],
3332 protocol: ProtocolVersion,
3333 message_id: u32,
3334) -> Result<SessionTransmission, CodecError> {
3335 if protocol.wire() >= 17 {
3336 let value: WireSessionTransmissionFrom17 = decode(message_id, payload)?;
3337 Ok(SessionTransmission {
3338 remote_address: value.remote_address.to_ip(message_id)?,
3339 session_type: value.session_type,
3340 })
3341 } else {
3342 let value: WireSessionTransmissionPre17 = decode(message_id, payload)?;
3343 Ok(SessionTransmission {
3344 remote_address: IpAddr::V4(Ipv4Addr::from(value.remote_address)),
3345 session_type: value.session_type,
3346 })
3347 }
3348}
3349
3350fn encode_session_transmission(
3351 value: SessionTransmission,
3352 protocol: ProtocolVersion,
3353 message_id: u32,
3354) -> Result<Vec<u8>, CodecError> {
3355 if protocol.wire() >= 17 {
3356 encode(
3357 message_id,
3358 &WireSessionTransmissionFrom17 {
3359 remote_address: WireExtendedAddress::from_ip(value.remote_address),
3360 session_type: value.session_type,
3361 },
3362 )
3363 } else {
3364 let IpAddr::V4(address) = value.remote_address else {
3365 return Err(CodecError::InvalidValue {
3366 message_id,
3367 field: "IP address family for this protocol version",
3368 value: 1,
3369 });
3370 };
3371 encode(
3372 message_id,
3373 &WireSessionTransmissionPre17 {
3374 remote_address: address.octets(),
3375 session_type: value.session_type,
3376 },
3377 )
3378 }
3379}
3380
3381fn fixed_bounded_bytes<const MAX: usize>(value: &BoundedBytes<MAX>) -> [u8; MAX] {
3382 let mut bytes = [0; MAX];
3383 bytes[..value.len()].copy_from_slice(value.as_bytes());
3384 bytes
3385}
3386
3387fn bounded_from_fixed<const MAX: usize>(value: [u8; MAX]) -> BoundedBytes<MAX> {
3388 BoundedBytes::try_from(value.as_slice()).expect("fixed wire field fits its public bound")
3389}
3390
3391fn multimedia_capability_words(
3392 value: [u8; MULTIMEDIA_CAPABILITY_BYTES],
3393) -> [u32; MULTIMEDIA_CAPABILITY_BYTES / 4] {
3394 std::array::from_fn(|index| {
3395 let offset = index * 4;
3396 u32::from_le_bytes([
3397 value[offset],
3398 value[offset + 1],
3399 value[offset + 2],
3400 value[offset + 3],
3401 ])
3402 })
3403}
3404
3405fn multimedia_capability_bytes(
3406 words: [u32; MULTIMEDIA_CAPABILITY_BYTES / 4],
3407) -> [u8; MULTIMEDIA_CAPABILITY_BYTES] {
3408 let mut bytes = [0; MULTIMEDIA_CAPABILITY_BYTES];
3409 for (word, target) in words.iter().zip(bytes.as_chunks_mut::<4>().0) {
3410 target.copy_from_slice(&word.to_le_bytes());
3411 }
3412 bytes
3413}
3414
3415fn decode_multimedia_descriptor(
3416 value: WireMultimediaPayloadDescriptor,
3417 message_id: u32,
3418) -> Result<MultimediaPayloadDescriptor, CodecError> {
3419 let payload_number =
3420 RtpPayloadNumber::new(value.payload_type).map_err(|error| CodecError::InvalidValue {
3421 message_id,
3422 field: "RTP payload number",
3423 value: u64::from(error.actual),
3424 })?;
3425 Ok(MultimediaPayloadDescriptor::new(
3426 value.payload_rfc_number,
3427 payload_number,
3428 ))
3429}
3430
3431fn decoded_multimedia_capability(
3432 value: [u8; MULTIMEDIA_CAPABILITY_BYTES],
3433 codec: Codec,
3434) -> MultimediaCapabilityState {
3435 let words = multimedia_capability_words(value);
3436 let Ok(picture_count) = usize::try_from(words[1]) else {
3437 return MultimediaCapabilityState::Preserved(value);
3438 };
3439 if picture_count > MAX_MULTIMEDIA_PICTURE_FORMATS {
3440 return MultimediaCapabilityState::Preserved(value);
3441 }
3442 let arm = match codec {
3443 Codec::H261 if words[15..].iter().all(|word| *word == 0) => {
3444 MultimediaVideoCapabilityArm::H261 {
3445 temporal_spatial_trade_off_capability: words[13],
3446 still_image_transmission: words[14],
3447 }
3448 }
3449 Codec::H263 if words[15..].iter().all(|word| *word == 0) => {
3450 MultimediaVideoCapabilityArm::H263 {
3451 capability_bitfield: words[13],
3452 annex_n_and_w_future_use: words[14],
3453 }
3454 }
3455 Codec::H263Plus if words[15..].iter().all(|word| *word == 0) => {
3456 MultimediaVideoCapabilityArm::H263Plus {
3457 model_number: words[13],
3458 bandwidth: words[14],
3459 }
3460 }
3461 Codec::H264 => MultimediaVideoCapabilityArm::H264 {
3462 profile: words[13],
3463 level: words[14],
3464 custom_max_mbps: words[15],
3465 custom_max_fs: words[16],
3466 custom_max_dpb: words[17],
3467 custom_max_br_and_cpb: words[18],
3468 },
3469 _ => {
3470 return MultimediaCapabilityState::Preserved(value);
3471 }
3472 };
3473 let picture_formats = (0..picture_count)
3474 .map(|index| MultimediaPictureFormat {
3475 format: VideoFormat::from(words[2 + index * 2]),
3476 minimum_picture_interval: words[3 + index * 2],
3477 })
3478 .collect();
3479 MultimediaCapabilityState::Video(MultimediaVideoCapability {
3480 bit_rate: words[0],
3481 picture_formats,
3482 conference_service_number: words[12],
3483 arm,
3484 preserved_wire: Some(value),
3485 })
3486}
3487
3488fn multimedia_capability_to_wire(payload: &MultimediaPayload) -> [u8; MULTIMEDIA_CAPABILITY_BYTES] {
3489 let capability = match &payload.capability {
3490 MultimediaCapabilityState::Preserved(bytes) => return *bytes,
3491 MultimediaCapabilityState::Video(capability) => capability,
3492 };
3493 if let Some(value) = capability.preserved_wire {
3494 return value;
3495 }
3496
3497 let mut words = [0; MULTIMEDIA_CAPABILITY_BYTES / 4];
3498 words[0] = capability.bit_rate;
3499 words[1] = capability.picture_formats.len() as u32;
3500 for (index, format) in capability.picture_formats.iter().enumerate() {
3501 words[2 + index * 2] = format.format.wire_value();
3502 words[3 + index * 2] = format.minimum_picture_interval;
3503 }
3504 words[12] = capability.conference_service_number;
3505 match capability.arm {
3506 MultimediaVideoCapabilityArm::H261 {
3507 temporal_spatial_trade_off_capability,
3508 still_image_transmission,
3509 } => {
3510 words[13] = temporal_spatial_trade_off_capability;
3511 words[14] = still_image_transmission;
3512 }
3513 MultimediaVideoCapabilityArm::H263 {
3514 capability_bitfield,
3515 annex_n_and_w_future_use,
3516 } => {
3517 words[13] = capability_bitfield;
3518 words[14] = annex_n_and_w_future_use;
3519 }
3520 MultimediaVideoCapabilityArm::H263Plus {
3521 model_number,
3522 bandwidth,
3523 } => {
3524 words[13] = model_number;
3525 words[14] = bandwidth;
3526 }
3527 MultimediaVideoCapabilityArm::H264 {
3528 profile,
3529 level,
3530 custom_max_mbps,
3531 custom_max_fs,
3532 custom_max_dpb,
3533 custom_max_br_and_cpb,
3534 } => {
3535 words[13] = profile;
3536 words[14] = level;
3537 words[15] = custom_max_mbps;
3538 words[16] = custom_max_fs;
3539 words[17] = custom_max_dpb;
3540 words[18] = custom_max_br_and_cpb;
3541 }
3542 }
3543 multimedia_capability_bytes(words)
3544}
3545
3546fn decode_multimedia_payload(
3547 descriptor: WireMultimediaPayloadDescriptor,
3548 capability: [u8; MULTIMEDIA_CAPABILITY_BYTES],
3549 compression_codec: Codec,
3550 direction: MultimediaPayloadDirection,
3551 protocol: ProtocolVersion,
3552 message_id: u32,
3553) -> Result<MultimediaPayload, CodecError> {
3554 let descriptor = decode_multimedia_descriptor(descriptor, message_id)?;
3555 let capability = decoded_multimedia_capability(capability, compression_codec);
3556 Ok(MultimediaPayload::from_decoded(
3557 descriptor,
3558 capability,
3559 direction,
3560 protocol,
3561 compression_codec,
3562 ))
3563}
3564
3565fn validate_multimedia_payload(
3566 payload: &MultimediaPayload,
3567 direction: MultimediaPayloadDirection,
3568 protocol: ProtocolVersion,
3569 message_id: u32,
3570) -> Result<(), CodecError> {
3571 if payload.is_valid_for(direction, protocol) {
3572 Ok(())
3573 } else {
3574 Err(CodecError::InvalidValue {
3575 message_id,
3576 field: "multimedia payload provenance",
3577 value: 1,
3578 })
3579 }
3580}
3581
3582fn open_multimedia_from_common(
3583 value: WireOpenMultimediaV11,
3584 source: MediaEndpointAddress,
3585 requested_address_type: IpAddressType,
3586 protocol: ProtocolVersion,
3587 message_id: u32,
3588) -> Result<OpenMultimediaChannel, CodecError> {
3589 let codec = Codec::from(value.compression_type);
3590 Ok(OpenMultimediaChannel {
3591 conference_id: value.conference_id.into(),
3592 passthrough_party_id: value.passthrough_party_id.into(),
3593 line_instance: value.line_instance,
3594 call_reference: value.call_reference.into(),
3595 payload: decode_multimedia_payload(
3596 value.payload_type,
3597 value.capability,
3598 codec,
3599 MultimediaPayloadDirection::Receive,
3600 protocol,
3601 message_id,
3602 )?,
3603 conference_creator: decode_bool_word(
3604 value.conference_creator,
3605 message_id,
3606 "conference creator",
3607 )?,
3608 encryption: value.encryption.to_public(message_id)?,
3609 stream_passthrough_id: value.stream_passthrough_id,
3610 associated_stream_id: value.associated_stream_id,
3611 source,
3612 requested_address_type,
3613 })
3614}
3615
3616fn decode_open_multimedia(
3617 payload: &[u8],
3618 protocol: ProtocolVersion,
3619 message_id: u32,
3620) -> Result<OpenMultimediaChannel, CodecError> {
3621 match protocol.wire() {
3622 17.. => {
3623 let value: WireOpenMultimediaV17 = decode(message_id, payload)?;
3624 let common = WireOpenMultimediaV11 {
3625 conference_id: value.conference_id,
3626 passthrough_party_id: value.passthrough_party_id,
3627 compression_type: value.compression_type,
3628 line_instance: value.line_instance,
3629 call_reference: value.call_reference,
3630 payload_type: value.payload_type,
3631 conference_creator: value.conference_creator,
3632 capability: value.capability,
3633 encryption: value.encryption,
3634 stream_passthrough_id: value.stream_passthrough_id,
3635 associated_stream_id: value.associated_stream_id,
3636 };
3637 open_multimedia_from_common(
3638 common,
3639 MediaEndpointAddress {
3640 address: value.source_address.to_ip(message_id)?,
3641 port: decode_port(value.source_port, message_id, "multimedia source port")?,
3642 },
3643 IpAddressType::from(value.requested_address_type),
3644 protocol,
3645 message_id,
3646 )
3647 }
3648 12..=16 => {
3649 let value: WireOpenMultimediaV12 = decode(message_id, payload)?;
3650 open_multimedia_from_common(
3651 value.base,
3652 MediaEndpointAddress {
3653 address: IpAddr::V4(Ipv4Addr::from(value.source_address)),
3654 port: decode_port(value.source_port, message_id, "multimedia source port")?,
3655 },
3656 IpAddressType::Ipv4,
3657 protocol,
3658 message_id,
3659 )
3660 }
3661 _ => {
3662 let value: WireOpenMultimediaV11 = decode(message_id, payload)?;
3663 open_multimedia_from_common(
3664 value,
3665 MediaEndpointAddress {
3666 address: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
3667 port: 0,
3668 },
3669 IpAddressType::Ipv4,
3670 protocol,
3671 message_id,
3672 )
3673 }
3674 }
3675}
3676
3677fn encode_open_multimedia(
3678 value: &OpenMultimediaChannel,
3679 protocol: ProtocolVersion,
3680) -> Result<Vec<u8>, CodecError> {
3681 validate_multimedia_payload(
3682 &value.payload,
3683 MultimediaPayloadDirection::Receive,
3684 protocol,
3685 id::OPEN_MULTIMEDIA_CHANNEL,
3686 )?;
3687 let common = WireOpenMultimediaV11 {
3688 conference_id: value.conference_id.get(),
3689 passthrough_party_id: value.passthrough_party_id.get(),
3690 compression_type: value.payload.compression_codec().wire_value(),
3691 line_instance: value.line_instance,
3692 call_reference: value.call_reference.get(),
3693 payload_type: value.payload.descriptor().into(),
3694 conference_creator: u32::from(value.conference_creator),
3695 capability: multimedia_capability_to_wire(&value.payload),
3696 encryption: WireEncryptionInfo::from_public(value.encryption.as_ref()),
3697 stream_passthrough_id: value.stream_passthrough_id,
3698 associated_stream_id: value.associated_stream_id,
3699 };
3700 match protocol.wire() {
3701 17.. => encode(
3702 id::OPEN_MULTIMEDIA_CHANNEL,
3703 &WireOpenMultimediaV17 {
3704 conference_id: common.conference_id,
3705 passthrough_party_id: common.passthrough_party_id,
3706 compression_type: common.compression_type,
3707 line_instance: common.line_instance,
3708 call_reference: common.call_reference,
3709 payload_type: common.payload_type,
3710 conference_creator: common.conference_creator,
3711 capability: common.capability,
3712 encryption: common.encryption,
3713 stream_passthrough_id: common.stream_passthrough_id,
3714 associated_stream_id: common.associated_stream_id,
3715 source_address: WireExtendedAddress::from_ip(value.source.address),
3716 source_port: u32::from(value.source.port),
3717 requested_address_type: value.requested_address_type.wire_value(),
3718 },
3719 ),
3720 12..=16 => {
3721 let IpAddr::V4(address) = value.source.address else {
3722 return Err(CodecError::InvalidValue {
3723 message_id: id::OPEN_MULTIMEDIA_CHANNEL,
3724 field: "IP address family for this protocol version",
3725 value: 1,
3726 });
3727 };
3728 encode(
3729 id::OPEN_MULTIMEDIA_CHANNEL,
3730 &WireOpenMultimediaV12 {
3731 base: common,
3732 source_address: address.octets(),
3733 source_port: u32::from(value.source.port),
3734 },
3735 )
3736 }
3737 _ => encode(id::OPEN_MULTIMEDIA_CHANNEL, &common),
3738 }
3739}
3740
3741fn decode_start_multimedia(
3742 payload: &[u8],
3743 protocol: ProtocolVersion,
3744 message_id: u32,
3745) -> Result<StartMultimediaTransmission, CodecError> {
3746 let (
3747 conference_id,
3748 party_id,
3749 compression_type,
3750 address,
3751 port,
3752 call_reference,
3753 payload_type,
3754 dscp,
3755 capability,
3756 encryption,
3757 stream_passthrough_id,
3758 associated_stream_id,
3759 ) = if protocol.wire() >= 17 {
3760 let value: WireStartMultimediaFrom17 = decode(message_id, payload)?;
3761 (
3762 value.conference_id,
3763 value.passthrough_party_id,
3764 value.compression_type,
3765 value.remote_address.to_ip(message_id)?,
3766 value.remote_port,
3767 value.call_reference,
3768 value.payload_type,
3769 value.dscp,
3770 value.capability,
3771 value.encryption,
3772 value.stream_passthrough_id,
3773 value.associated_stream_id,
3774 )
3775 } else {
3776 let value: WireStartMultimediaPre17 = decode(message_id, payload)?;
3777 (
3778 value.conference_id,
3779 value.passthrough_party_id,
3780 value.compression_type,
3781 IpAddr::V4(Ipv4Addr::from(value.remote_address)),
3782 value.remote_port,
3783 value.call_reference,
3784 value.payload_type,
3785 value.dscp,
3786 value.capability,
3787 value.encryption,
3788 value.stream_passthrough_id,
3789 value.associated_stream_id,
3790 )
3791 };
3792 Ok(StartMultimediaTransmission {
3793 conference_id: conference_id.into(),
3794 passthrough_party_id: party_id.into(),
3795 endpoint: MediaEndpointAddress {
3796 address,
3797 port: decode_port(port, message_id, "multimedia port")?,
3798 },
3799 call_reference: call_reference.into(),
3800 payload: decode_multimedia_payload(
3801 payload_type,
3802 capability,
3803 Codec::from(compression_type),
3804 MultimediaPayloadDirection::Transmit,
3805 protocol,
3806 message_id,
3807 )?,
3808 traffic_class: MediaTrafficClass::from_wire(u8::try_from(dscp).map_err(|_| {
3809 CodecError::InvalidValue {
3810 message_id,
3811 field: "multimedia traffic class",
3812 value: u64::from(dscp),
3813 }
3814 })?),
3815 encryption: encryption.to_public(message_id)?,
3816 stream_passthrough_id,
3817 associated_stream_id,
3818 })
3819}
3820
3821fn encode_start_multimedia(
3822 value: &StartMultimediaTransmission,
3823 protocol: ProtocolVersion,
3824) -> Result<Vec<u8>, CodecError> {
3825 validate_multimedia_payload(
3826 &value.payload,
3827 MultimediaPayloadDirection::Transmit,
3828 protocol,
3829 id::START_MULTIMEDIA_TRANSMISSION,
3830 )?;
3831 if protocol.wire() >= 17 {
3832 encode(
3833 id::START_MULTIMEDIA_TRANSMISSION,
3834 &WireStartMultimediaFrom17 {
3835 conference_id: value.conference_id.get(),
3836 passthrough_party_id: value.passthrough_party_id.get(),
3837 compression_type: value.payload.compression_codec().wire_value(),
3838 remote_address: WireExtendedAddress::from_ip(value.endpoint.address),
3839 remote_port: u32::from(value.endpoint.port),
3840 call_reference: value.call_reference.get(),
3841 payload_type: value.payload.descriptor().into(),
3842 dscp: u32::from(value.traffic_class),
3843 capability: multimedia_capability_to_wire(&value.payload),
3844 encryption: WireEncryptionInfo::from_public(value.encryption.as_ref()),
3845 stream_passthrough_id: value.stream_passthrough_id,
3846 associated_stream_id: value.associated_stream_id,
3847 },
3848 )
3849 } else {
3850 let IpAddr::V4(address) = value.endpoint.address else {
3851 return Err(CodecError::InvalidValue {
3852 message_id: id::START_MULTIMEDIA_TRANSMISSION,
3853 field: "IP address family for this protocol version",
3854 value: 1,
3855 });
3856 };
3857 encode(
3858 id::START_MULTIMEDIA_TRANSMISSION,
3859 &WireStartMultimediaPre17 {
3860 conference_id: value.conference_id.get(),
3861 passthrough_party_id: value.passthrough_party_id.get(),
3862 compression_type: value.payload.compression_codec().wire_value(),
3863 remote_address: address.octets(),
3864 remote_port: u32::from(value.endpoint.port),
3865 call_reference: value.call_reference.get(),
3866 payload_type: value.payload.descriptor().into(),
3867 dscp: u32::from(value.traffic_class),
3868 capability: multimedia_capability_to_wire(&value.payload),
3869 encryption: WireEncryptionInfo::from_public(value.encryption.as_ref()),
3870 stream_passthrough_id: value.stream_passthrough_id,
3871 associated_stream_id: value.associated_stream_id,
3872 },
3873 )
3874 }
3875}
3876
3877fn decode_miscellaneous_command(
3878 payload: &[u8],
3879 message_id: u32,
3880) -> Result<MiscellaneousCommand, CodecError> {
3881 let value: WireMiscellaneousCommand = decode(message_id, payload)?;
3882 Ok(MiscellaneousCommand {
3883 conference_id: value.conference_id.into(),
3884 passthrough_party_id: value.passthrough_party_id.into(),
3885 call_reference: value.call_reference.into(),
3886 command: values::MiscCommandType::from(value.command),
3887 data: bounded_from_fixed(value.data),
3888 })
3889}
3890
3891fn encode_miscellaneous_command(value: &MiscellaneousCommand) -> Result<Vec<u8>, CodecError> {
3892 encode(
3893 id::MISCELLANEOUS_COMMAND,
3894 &WireMiscellaneousCommand {
3895 conference_id: value.conference_id.get(),
3896 passthrough_party_id: value.passthrough_party_id.get(),
3897 call_reference: value.call_reference.get(),
3898 command: value.command.wire_value(),
3899 data: fixed_bounded_bytes(&value.data),
3900 },
3901 )
3902}
3903
3904fn dtmf_payload_identity_from_wire(value: WireDtmfPayloadIdentity) -> DtmfPayloadIdentity {
3905 DtmfPayloadIdentity {
3906 payload_type: value.payload_type,
3907 conference_id: value.conference_id,
3908 passthrough_party_id: value.passthrough_party_id,
3909 }
3910}
3911
3912fn dtmf_payload_identity_to_wire(value: DtmfPayloadIdentity) -> WireDtmfPayloadIdentity {
3913 WireDtmfPayloadIdentity {
3914 payload_type: value.payload_type,
3915 conference_id: value.conference_id,
3916 passthrough_party_id: value.passthrough_party_id,
3917 }
3918}
3919
3920fn dtmf_payload_request_from_wire(value: WireDtmfPayloadRequest) -> DtmfPayloadRequest {
3921 DtmfPayloadRequest {
3922 payload_type: value.payload_type,
3923 conference_id: value.conference_id,
3924 passthrough_party_id: value.passthrough_party_id,
3925 dtmf_type: value.dtmf_type,
3926 }
3927}
3928
3929fn dtmf_payload_request_to_wire(value: DtmfPayloadRequest) -> WireDtmfPayloadRequest {
3930 WireDtmfPayloadRequest {
3931 payload_type: value.payload_type,
3932 conference_id: value.conference_id,
3933 passthrough_party_id: value.passthrough_party_id,
3934 dtmf_type: value.dtmf_type,
3935 }
3936}
3937
3938const MAX_CONFERENCE_PASSTHROUGH_DATA: usize = 2000;
3939const MAX_AUDIT_CONFERENCE_ENTRIES: usize = 32;
3940const MAX_AUDIT_PARTICIPANT_DATA: usize = 256;
3941
3942fn encode_participant_request(
3943 message_id: u32,
3944 conference_id: crate::types::ConferenceId,
3945 participant: &ConferenceParticipant,
3946) -> Result<WireParticipantRequest, CodecError> {
3947 Ok(WireParticipantRequest {
3948 conference_id: conference_id.get(),
3949 call_reference: participant.call_reference.get(),
3950 presentation_restrictions: participant.presentation_restrictions.bits(),
3951 participant_name: WireFixedText::new(message_id, "participant name", &participant.name)?,
3952 participant_number: WireFixedText::new(
3953 message_id,
3954 "participant number",
3955 &participant.number,
3956 )?,
3957 conference_name: WireFixedText::new(
3958 message_id,
3959 "conference name",
3960 &participant.conference_name,
3961 )?,
3962 })
3963}
3964
3965fn decode_participant_request(
3966 payload: &[u8],
3967 message_id: u32,
3968) -> Result<(crate::types::ConferenceId, ConferenceParticipant), CodecError> {
3969 validate_exact_payload(payload, message_id, 108)?;
3970 let value: WireParticipantRequest = decode(message_id, payload)?;
3971 Ok((
3972 value.conference_id.into(),
3973 ConferenceParticipant {
3974 call_reference: value.call_reference.into(),
3975 presentation_restrictions: PartyInformationRestrictions::from_bits_retain(
3976 value.presentation_restrictions,
3977 ),
3978 name: value.participant_name.text()?,
3979 number: value.participant_number.text()?,
3980 conference_name: value.conference_name.text()?,
3981 },
3982 ))
3983}
3984
3985impl ConferenceParticipantChange {
3986 pub fn to_user_data_v1(
3989 &self,
3990 routing: ParticipantChangeRouting,
3991 ) -> Result<UserDataV1Message, CodecError> {
3992 let data = encode(
3993 id::USER_TO_DEVICE_DATA_V1,
3994 &encode_participant_request(
3995 id::USER_TO_DEVICE_DATA_V1,
3996 self.conference_id,
3997 &self.participant,
3998 )?,
3999 )?;
4000 Ok(UserDataV1Message {
4001 application_id: routing.application_id.get(),
4002 line_instance: routing.line_instance,
4003 call_reference: self.participant.call_reference.get(),
4004 transaction_id: routing.transaction_id.get(),
4005 sequence_flag: routing.sequence_flag,
4006 display_priority: routing.display_priority,
4007 conference_id: self.conference_id.get(),
4008 application_instance_id: routing.application_instance_id.get(),
4009 routing: routing.routing,
4010 data,
4011 })
4012 }
4013
4014 pub fn from_user_data_v1(message: &UserDataV1Message) -> Result<Self, CodecError> {
4017 let (conference_id, participant) =
4018 decode_participant_request(&message.data, id::USER_TO_DEVICE_DATA_V1)?;
4019 if message.conference_id != conference_id.get() {
4020 return Err(CodecError::InvalidValue {
4021 message_id: id::USER_TO_DEVICE_DATA_V1,
4022 field: "participant change conference ID",
4023 value: u64::from(message.conference_id),
4024 });
4025 }
4026 if message.call_reference != participant.call_reference.get() {
4027 return Err(CodecError::InvalidValue {
4028 message_id: id::USER_TO_DEVICE_DATA_V1,
4029 field: "participant change call reference",
4030 value: u64::from(message.call_reference),
4031 });
4032 }
4033 Ok(Self {
4034 conference_id,
4035 participant,
4036 })
4037 }
4038}
4039
4040fn validate_conference_data_length(
4041 payload: &[u8],
4042 message_id: u32,
4043 header_size: usize,
4044 length_offset: usize,
4045) -> Result<(), CodecError> {
4046 if payload.len() < header_size {
4047 return Err(CodecError::Truncated {
4048 message_id,
4049 needed: header_size,
4050 actual: payload.len(),
4051 });
4052 }
4053 let data_length = usize_from_wire(
4054 message_id,
4055 "conference passthrough data",
4056 u32::from_le_bytes(
4057 payload[length_offset..length_offset + 4]
4058 .try_into()
4059 .expect("validated conference header contains length word"),
4060 ),
4061 )?;
4062 if data_length > MAX_CONFERENCE_PASSTHROUGH_DATA {
4063 return Err(CodecError::CountTooLarge {
4064 message_id,
4065 field: "conference passthrough data",
4066 count: data_length,
4067 maximum: MAX_CONFERENCE_PASSTHROUGH_DATA,
4068 });
4069 }
4070 let needed = header_size + data_length;
4071 if payload.len() < needed {
4072 return Err(CodecError::Truncated {
4073 message_id,
4074 needed,
4075 actual: payload.len(),
4076 });
4077 }
4078 Ok(())
4079}
4080
4081fn validate_conference_data_for_encode(
4082 message_id: u32,
4083 passthrough_data: &[u8],
4084) -> Result<u32, CodecError> {
4085 if passthrough_data.len() > MAX_CONFERENCE_PASSTHROUGH_DATA {
4086 return Err(CodecError::CountTooLarge {
4087 message_id,
4088 field: "conference passthrough data",
4089 count: passthrough_data.len(),
4090 maximum: MAX_CONFERENCE_PASSTHROUGH_DATA,
4091 });
4092 }
4093 wire_count(
4094 message_id,
4095 "conference passthrough data",
4096 passthrough_data.len(),
4097 )
4098}
4099
4100fn encode_user_data(message: &UserDataMessage, message_id: u32) -> Result<Vec<u8>, CodecError> {
4101 if message.data.len() > 2000 {
4102 return Err(CodecError::CountTooLarge {
4103 message_id,
4104 field: "user data",
4105 count: message.data.len(),
4106 maximum: 2000,
4107 });
4108 }
4109 encode(
4110 message_id,
4111 &WireUserData {
4112 application_id: message.application_id,
4113 line_instance: message.line_instance,
4114 call_reference: message.call_reference,
4115 transaction_id: message.transaction_id,
4116 data_length: wire_count(message_id, "user data", message.data.len())?,
4117 data: message.data.clone(),
4118 },
4119 )
4120}
4121
4122fn decode_user_data_v1(payload: &[u8], message_id: u32) -> Result<UserDataV1Message, CodecError> {
4123 let value: WireUserDataV1 = decode_zero_padded(message_id, payload)?;
4124 if value.data.len() > 2000 {
4125 return Err(CodecError::CountTooLarge {
4126 message_id,
4127 field: "user data",
4128 count: value.data.len(),
4129 maximum: 2000,
4130 });
4131 }
4132 Ok(UserDataV1Message {
4133 application_id: value.application_id,
4134 line_instance: value.line_instance,
4135 call_reference: value.call_reference,
4136 transaction_id: value.transaction_id,
4137 sequence_flag: value.sequence_flag,
4138 display_priority: value.display_priority,
4139 conference_id: value.conference_id,
4140 application_instance_id: value.application_instance_id,
4141 routing: value.routing,
4142 data: value.data,
4143 })
4144}
4145
4146fn encode_user_data_v1(
4147 message: &UserDataV1Message,
4148 message_id: u32,
4149) -> Result<Vec<u8>, CodecError> {
4150 if message.data.len() > 2000 {
4151 return Err(CodecError::CountTooLarge {
4152 message_id,
4153 field: "user data",
4154 count: message.data.len(),
4155 maximum: 2000,
4156 });
4157 }
4158 encode(
4159 message_id,
4160 &WireUserDataV1 {
4161 application_id: message.application_id,
4162 line_instance: message.line_instance,
4163 call_reference: message.call_reference,
4164 transaction_id: message.transaction_id,
4165 data_length: wire_count(message_id, "user data", message.data.len())?,
4166 sequence_flag: message.sequence_flag,
4167 display_priority: message.display_priority,
4168 conference_id: message.conference_id,
4169 application_instance_id: message.application_instance_id,
4170 routing: message.routing,
4171 data: message.data.clone(),
4172 },
4173 )
4174}
4175
4176fn decode_port_response(
4177 payload: &[u8],
4178 protocol: u32,
4179 message_id: u32,
4180) -> Result<PortEndpoint, CodecError> {
4181 let (conference_id, call_reference, passthrough_party_id, address, rtp, rtcp, media_type) =
4182 if protocol >= 20 {
4183 let value: WirePortResponseV20 = decode(message_id, payload)?;
4184 (
4185 value.conference_id,
4186 value.call_reference,
4187 value.passthrough_party_id,
4188 value.address.to_ip(message_id)?,
4189 value.rtp_port,
4190 value.rtcp_port,
4191 Some(MediaType::from(value.media_type)),
4192 )
4193 } else {
4194 let value: WirePortResponseV3 = decode(message_id, payload)?;
4195 (
4196 value.conference_id,
4197 value.call_reference,
4198 value.passthrough_party_id,
4199 IpAddr::V4(Ipv4Addr::from(value.address)),
4200 value.rtp_port,
4201 value.rtcp_port,
4202 None,
4203 )
4204 };
4205 Ok(PortEndpoint {
4206 conference_id,
4207 call_reference,
4208 passthrough_party_id,
4209 address,
4210 rtp_port: decode_port(rtp, message_id, "RTP port")?,
4211 rtcp_port: decode_port(rtcp, message_id, "RTCP port")?,
4212 media_type,
4213 })
4214}
4215
4216fn encode_port_response(
4217 endpoint: &PortEndpoint,
4218 protocol: ProtocolVersion,
4219) -> Result<Vec<u8>, CodecError> {
4220 if protocol.wire() >= 20 {
4221 encode(
4222 id::PORT_RESPONSE,
4223 &WirePortResponseV20 {
4224 conference_id: endpoint.conference_id,
4225 call_reference: endpoint.call_reference,
4226 passthrough_party_id: endpoint.passthrough_party_id,
4227 address: WireExtendedAddress::from_ip(endpoint.address),
4228 rtp_port: u32::from(endpoint.rtp_port),
4229 rtcp_port: u32::from(endpoint.rtcp_port),
4230 media_type: endpoint
4231 .media_type
4232 .ok_or(CodecError::InvalidValue {
4233 message_id: id::PORT_RESPONSE,
4234 field: "media type required from protocol 20",
4235 value: 0,
4236 })?
4237 .wire_value(),
4238 },
4239 )
4240 } else {
4241 let IpAddr::V4(address) = endpoint.address else {
4242 return Err(CodecError::InvalidValue {
4243 message_id: id::PORT_RESPONSE,
4244 field: "IP address family for this protocol version",
4245 value: 1,
4246 });
4247 };
4248 encode(
4249 id::PORT_RESPONSE,
4250 &WirePortResponseV3 {
4251 conference_id: endpoint.conference_id,
4252 call_reference: endpoint.call_reference,
4253 passthrough_party_id: endpoint.passthrough_party_id,
4254 address: address.octets(),
4255 rtp_port: u32::from(endpoint.rtp_port),
4256 rtcp_port: u32::from(endpoint.rtcp_port),
4257 },
4258 )
4259 }
4260}
4261
4262fn push_dynamic_text(
4263 output: &mut Vec<u8>,
4264 message_id: u32,
4265 field: &'static str,
4266 text: &str,
4267 maximum: usize,
4268) -> Result<(), CodecError> {
4269 if text.as_bytes().contains(&0) {
4270 return Err(CodecError::InvalidText);
4271 }
4272 if text.len() > maximum {
4273 return Err(CodecError::TextTooLong {
4274 message_id,
4275 field,
4276 actual: text.len(),
4277 maximum,
4278 });
4279 }
4280 output.extend_from_slice(text.as_bytes());
4281 output.push(0);
4282 Ok(())
4283}
4284
4285fn push_dynamic_station_text(
4286 output: &mut Vec<u8>,
4287 message_id: u32,
4288 field: &'static str,
4289 text: &str,
4290 maximum: usize,
4291 code_page: Option<LegacyCodePage>,
4292) -> Result<(), CodecError> {
4293 let bytes = station_text_bytes(text, code_page)?;
4294 if bytes.len() > maximum {
4295 return Err(CodecError::TextTooLong {
4296 message_id,
4297 field,
4298 actual: bytes.len(),
4299 maximum,
4300 });
4301 }
4302 output.extend_from_slice(&bytes);
4303 output.push(0);
4304 Ok(())
4305}
4306
4307fn pad_dynamic_payload(output: &mut Vec<u8>) {
4309 let padding = (4 - output.len() % 4) % 4;
4310 output.resize(output.len() + padding, 0);
4311}
4312
4313fn decode_dynamic_texts(
4314 message_id: u32,
4315 payload: &[u8],
4316 offset: usize,
4317 count: usize,
4318) -> Result<Vec<String>, CodecError> {
4319 if !payload.len().is_multiple_of(4) {
4320 return Err(CodecError::InvalidAlignment {
4321 message_id,
4322 actual: payload.len(),
4323 });
4324 }
4325 let Some(mut remaining) = payload.get(offset..) else {
4326 return Err(CodecError::Truncated {
4327 message_id,
4328 needed: offset,
4329 actual: payload.len(),
4330 });
4331 };
4332 let mut fields = Vec::with_capacity(count);
4333 for _ in 0..count {
4334 let Some(end) = remaining.iter().position(|byte| *byte == 0) else {
4335 return Err(CodecError::Truncated {
4336 message_id,
4337 needed: payload.len() + 1,
4338 actual: payload.len(),
4339 });
4340 };
4341 fields.push(
4342 std::str::from_utf8(&remaining[..end])
4343 .map_err(|_| CodecError::InvalidText)?
4344 .to_owned(),
4345 );
4346 remaining = &remaining[end + 1..];
4347 }
4348 validate_dynamic_padding(message_id, remaining)?;
4349 Ok(fields)
4350}
4351
4352fn decode_dynamic_text(
4353 message_id: u32,
4354 payload: &[u8],
4355 offset: usize,
4356) -> Result<String, CodecError> {
4357 Ok(decode_dynamic_texts(message_id, payload, offset, 1)?
4358 .pop()
4359 .expect("one requested dynamic field is returned"))
4360}
4361
4362fn take_dynamic_text(
4363 message_id: u32,
4364 remaining: &mut &[u8],
4365 field: &'static str,
4366 maximum: usize,
4367) -> Result<String, CodecError> {
4368 let Some(end) = remaining.iter().position(|byte| *byte == 0) else {
4369 return Err(CodecError::Truncated {
4370 message_id,
4371 needed: remaining.len() + 1,
4372 actual: remaining.len(),
4373 });
4374 };
4375 if end > maximum {
4376 return Err(CodecError::TextTooLong {
4377 message_id,
4378 field,
4379 actual: end,
4380 maximum,
4381 });
4382 }
4383 let value = std::str::from_utf8(&remaining[..end])
4384 .map_err(|_| CodecError::InvalidText)?
4385 .to_owned();
4386 *remaining = &remaining[end + 1..];
4387 Ok(value)
4388}
4389
4390fn take_dynamic_word(message_id: u32, remaining: &mut &[u8]) -> Result<u32, CodecError> {
4391 let Some((word, tail)) = remaining.split_first_chunk::<4>() else {
4392 return Err(CodecError::Truncated {
4393 message_id,
4394 needed: 4,
4395 actual: remaining.len(),
4396 });
4397 };
4398 *remaining = tail;
4399 Ok(u32::from_le_bytes(*word))
4400}
4401
4402fn decode_dynamic_config_status(payload: &[u8]) -> Result<ServerMessage, CodecError> {
4403 const MESSAGE_ID: u32 = id::CONFIG_STAT_DYNAMIC;
4404 if !payload.len().is_multiple_of(4) {
4405 return Err(CodecError::InvalidAlignment {
4406 message_id: MESSAGE_ID,
4407 actual: payload.len(),
4408 });
4409 }
4410 let mut remaining = payload;
4411 let device_name = take_dynamic_text(MESSAGE_ID, &mut remaining, "device name", 64)?;
4412 let reserved = take_dynamic_word(MESSAGE_ID, &mut remaining)?;
4413 let instance = take_dynamic_word(MESSAGE_ID, &mut remaining)?;
4414 let line_count = take_dynamic_word(MESSAGE_ID, &mut remaining)?;
4415 let speed_dial_count = take_dynamic_word(MESSAGE_ID, &mut remaining)?;
4416 let user_name = take_dynamic_text(MESSAGE_ID, &mut remaining, "user name", 64)?;
4417 let server_name = take_dynamic_text(MESSAGE_ID, &mut remaining, "server name", 256)?;
4418 validate_dynamic_padding(MESSAGE_ID, remaining)?;
4419 let status = ConfigurationStatus {
4420 device_name,
4421 station_user_id: reserved,
4422 station_instance: instance,
4423 line_count,
4424 speed_dial_count,
4425 user_name,
4426 server_name,
4427 };
4428 Ok(ServerMessage::ConfigStatus(status))
4429}
4430
4431fn encode_dynamic_config_status(value: &ConfigurationStatus) -> Result<Vec<u8>, CodecError> {
4432 const MESSAGE_ID: u32 = id::CONFIG_STAT_DYNAMIC;
4433 let mut payload = Vec::new();
4434 push_dynamic_text(
4435 &mut payload,
4436 MESSAGE_ID,
4437 "device name",
4438 &value.device_name,
4439 64,
4440 )?;
4441 payload.extend_from_slice(&value.station_user_id.to_le_bytes());
4442 payload.extend_from_slice(&value.station_instance.to_le_bytes());
4443 payload.extend_from_slice(&value.line_count.to_le_bytes());
4444 payload.extend_from_slice(&value.speed_dial_count.to_le_bytes());
4445 push_dynamic_text(&mut payload, MESSAGE_ID, "user name", &value.user_name, 64)?;
4446 push_dynamic_text(
4447 &mut payload,
4448 MESSAGE_ID,
4449 "server name",
4450 &value.server_name,
4451 256,
4452 )?;
4453 pad_dynamic_payload(&mut payload);
4454 Ok(payload)
4455}
4456
4457fn validate_dynamic_padding(message_id: u32, padding: &[u8]) -> Result<(), CodecError> {
4458 if padding.len() > 3 || padding.iter().any(|byte| *byte != 0) {
4459 return Err(CodecError::TrailingBytes {
4460 message_id,
4461 count: padding.len(),
4462 });
4463 }
4464 Ok(())
4465}
4466
4467fn encode_dynamic_line_status(
4468 instance: u32,
4469 number: &str,
4470 display_name: &str,
4471 code_page: Option<LegacyCodePage>,
4472) -> Result<Vec<u8>, CodecError> {
4473 let mut payload = encode(
4474 id::LINE_STAT_DYNAMIC,
4475 &WireLineStatusDynamicHeader {
4476 line_instance: instance,
4477 line_type: 15,
4478 },
4479 )?;
4480 push_dynamic_text(
4481 &mut payload,
4482 id::LINE_STAT_DYNAMIC,
4483 "line number",
4484 number,
4485 24,
4486 )?;
4487 push_dynamic_station_text(
4488 &mut payload,
4489 id::LINE_STAT_DYNAMIC,
4490 "fully qualified display name",
4491 display_name,
4492 120,
4493 code_page,
4494 )?;
4495 push_dynamic_station_text(
4496 &mut payload,
4497 id::LINE_STAT_DYNAMIC,
4498 "line label",
4499 display_name,
4500 120,
4501 code_page,
4502 )?;
4503 pad_dynamic_payload(&mut payload);
4504 Ok(payload)
4505}
4506
4507fn decode_dynamic_line_status(payload: &[u8]) -> Result<ServerMessage, CodecError> {
4508 const HEADER_SIZE: usize = 8;
4509 if payload.len() < HEADER_SIZE {
4510 return Err(CodecError::Truncated {
4511 message_id: id::LINE_STAT_DYNAMIC,
4512 needed: HEADER_SIZE,
4513 actual: payload.len(),
4514 });
4515 }
4516 let header: WireLineStatusDynamicHeader =
4517 decode(id::LINE_STAT_DYNAMIC, &payload[..HEADER_SIZE])?;
4518 let fields = decode_dynamic_texts(id::LINE_STAT_DYNAMIC, payload, HEADER_SIZE, 3)?;
4519 Ok(ServerMessage::LineStatus {
4520 instance: header.line_instance,
4521 number: fields[0].clone(),
4522 display_name: fields[2].clone(),
4523 })
4524}
4525
4526fn encode_dynamic_service_url_status(
4527 index: u32,
4528 url: &str,
4529 label: &str,
4530 extension_text: &str,
4531 protocol: ProtocolVersion,
4532 code_page: Option<LegacyCodePage>,
4533) -> Result<Vec<u8>, CodecError> {
4534 let mut payload = encode(id::SERVICE_URL_STAT_DYNAMIC, &WireOneWord { value: index })?;
4535 push_dynamic_text(
4536 &mut payload,
4537 id::SERVICE_URL_STAT_DYNAMIC,
4538 "service URL",
4539 url,
4540 255,
4541 )?;
4542 push_dynamic_station_text(
4543 &mut payload,
4544 id::SERVICE_URL_STAT_DYNAMIC,
4545 "service label",
4546 label,
4547 120,
4548 code_page,
4549 )?;
4550 if protocol >= ProtocolVersion::V19 {
4551 push_dynamic_station_text(
4552 &mut payload,
4553 id::SERVICE_URL_STAT_DYNAMIC,
4554 "service extension text",
4555 extension_text,
4556 120,
4557 code_page,
4558 )?;
4559 }
4560 pad_dynamic_payload(&mut payload);
4561 Ok(payload)
4562}
4563
4564fn decode_dynamic_service_url_status(
4565 payload: &[u8],
4566 protocol: ProtocolVersion,
4567) -> Result<ServerMessage, CodecError> {
4568 const HEADER_SIZE: usize = 4;
4569 if payload.len() < HEADER_SIZE {
4570 return Err(CodecError::Truncated {
4571 message_id: id::SERVICE_URL_STAT_DYNAMIC,
4572 needed: HEADER_SIZE,
4573 actual: payload.len(),
4574 });
4575 }
4576 let header: WireOneWord = decode(id::SERVICE_URL_STAT_DYNAMIC, &payload[..HEADER_SIZE])?;
4577 let fields = decode_dynamic_texts(
4578 id::SERVICE_URL_STAT_DYNAMIC,
4579 payload,
4580 HEADER_SIZE,
4581 if protocol >= ProtocolVersion::V19 {
4582 3
4583 } else {
4584 2
4585 },
4586 )?;
4587 Ok(ServerMessage::ServiceUrlStatus {
4588 index: header.value,
4589 url: fields[0].clone(),
4590 label: fields[1].clone(),
4591 extension_text: fields.get(2).cloned().unwrap_or_default(),
4592 })
4593}
4594
4595fn encode_dynamic_call_info(
4596 info: &CallInfo,
4597 line_instance: u32,
4598 call_reference: u32,
4599 protocol: ProtocolVersion,
4600) -> Result<Vec<u8>, CodecError> {
4601 let mut payload = encode(
4602 id::CALL_INFO_DYNAMIC,
4603 &WireCallInfoDynamicHeader {
4604 line_instance,
4605 call_reference,
4606 call_type: match info.direction {
4607 crate::types::CallDirection::Inbound => 1,
4608 crate::types::CallDirection::Outbound => 2,
4609 },
4610 original_redirect_reason: info.original_redirect_reason,
4611 last_redirect_reason: info.last_redirect_reason,
4612 call_instance: 1,
4613 security_status: 0,
4614 party_restrictions: info.party_restrictions,
4615 },
4616 )?;
4617
4618 let fields: &[(&'static str, &str, usize)] = match protocol.dynamic_call_info_layout() {
4619 DynamicCallInfoLayout::Fields15 => &[
4620 ("calling number", &info.calling_number, 24),
4621 ("original calling number", "", 24),
4622 ("called number", &info.called_number, 24),
4623 ("original called number", &info.original_called_number, 24),
4624 ("last redirecting number", &info.last_redirecting_number, 24),
4625 ("calling voicemail", "", 24),
4626 ("called voicemail", "", 24),
4627 ("original called voicemail", "", 24),
4628 ("last redirecting voicemail", "", 24),
4629 ("calling name", &info.calling_name, 120),
4630 ("called name", &info.called_name, 120),
4631 ("original called name", &info.original_called_name, 120),
4632 ("last redirecting name", &info.last_redirecting_name, 120),
4633 ("hunt pilot number", "", 24),
4634 ("hunt pilot name", "", 120),
4635 ],
4636 DynamicCallInfoLayout::Fields13 => &[
4637 ("calling number", &info.calling_number, 24),
4638 ("original calling number", "", 24),
4639 ("called number", &info.called_number, 24),
4640 ("original called number", &info.original_called_number, 24),
4641 ("last redirecting number", &info.last_redirecting_number, 24),
4642 ("calling voicemail", "", 24),
4643 ("called voicemail", "", 24),
4644 ("original called voicemail", "", 24),
4645 ("last redirecting voicemail", "", 24),
4646 ("calling name", &info.calling_name, 120),
4647 ("called name", &info.called_name, 120),
4648 ("original called name", &info.original_called_name, 120),
4649 ("last redirecting name", &info.last_redirecting_name, 120),
4650 ],
4651 DynamicCallInfoLayout::Fields12 => &[
4652 ("calling number", &info.calling_number, 24),
4653 ("called number", &info.called_number, 24),
4654 ("original called number", &info.original_called_number, 24),
4655 ("last redirecting number", &info.last_redirecting_number, 24),
4656 ("calling voicemail", "", 24),
4657 ("called voicemail", "", 24),
4658 ("original called voicemail", "", 24),
4659 ("last redirecting voicemail", "", 24),
4660 ("calling name", &info.calling_name, 120),
4661 ("called name", &info.called_name, 120),
4662 ("original called name", &info.original_called_name, 120),
4663 ("last redirecting name", &info.last_redirecting_name, 120),
4664 ],
4665 };
4666 for &(field, text, maximum) in fields {
4667 push_dynamic_text(&mut payload, id::CALL_INFO_DYNAMIC, field, text, maximum)?;
4668 }
4669 pad_dynamic_payload(&mut payload);
4670 Ok(payload)
4671}
4672
4673fn decode_dynamic_call_info(
4674 payload: &[u8],
4675 protocol: ProtocolVersion,
4676) -> Result<ServerMessage, CodecError> {
4677 const HEADER_SIZE: usize = 32;
4678 if payload.len() < HEADER_SIZE {
4679 return Err(CodecError::Truncated {
4680 message_id: id::CALL_INFO_DYNAMIC,
4681 needed: HEADER_SIZE,
4682 actual: payload.len(),
4683 });
4684 }
4685 let header: WireCallInfoDynamicHeader = decode(id::CALL_INFO_DYNAMIC, &payload[..HEADER_SIZE])?;
4686 let fields = decode_dynamic_texts(
4687 id::CALL_INFO_DYNAMIC,
4688 payload,
4689 HEADER_SIZE,
4690 protocol.dynamic_call_info_layout().string_count(),
4691 )?;
4692 let (
4693 calling_number,
4694 called_number,
4695 original_called_number,
4696 last_redirecting_number,
4697 calling_name,
4698 called_name,
4699 original_called_name,
4700 last_redirecting_name,
4701 ) = if matches!(
4702 protocol.dynamic_call_info_layout(),
4703 DynamicCallInfoLayout::Fields13 | DynamicCallInfoLayout::Fields15
4704 ) {
4705 (
4706 &fields[0],
4707 &fields[2],
4708 &fields[3],
4709 &fields[4],
4710 &fields[9],
4711 &fields[10],
4712 &fields[11],
4713 &fields[12],
4714 )
4715 } else {
4716 (
4717 &fields[0],
4718 &fields[1],
4719 &fields[2],
4720 &fields[3],
4721 &fields[8],
4722 &fields[9],
4723 &fields[10],
4724 &fields[11],
4725 )
4726 };
4727 Ok(ServerMessage::CallInfo {
4728 info: CallInfo {
4729 direction: if header.call_type == 1 {
4730 crate::types::CallDirection::Inbound
4731 } else {
4732 crate::types::CallDirection::Outbound
4733 },
4734 calling_name: calling_name.clone(),
4735 calling_number: calling_number.clone(),
4736 called_name: called_name.clone(),
4737 called_number: called_number.clone(),
4738 original_called_name: original_called_name.clone(),
4739 original_called_number: original_called_number.clone(),
4740 last_redirecting_name: last_redirecting_name.clone(),
4741 last_redirecting_number: last_redirecting_number.clone(),
4742 original_redirect_reason: header.original_redirect_reason,
4743 last_redirect_reason: header.last_redirect_reason,
4744 party_restrictions: header.party_restrictions,
4745 },
4746 line_instance: header.line_instance,
4747 call_reference: header.call_reference,
4748 })
4749}
4750
4751fn decode_port(value: u32, message_id: u32, field: &'static str) -> Result<u16, CodecError> {
4752 u16::try_from(value).map_err(|_| CodecError::InvalidValue {
4753 message_id,
4754 field,
4755 value: u64::from(value),
4756 })
4757}
4758
4759fn decode_server_endpoints(
4760 message_id: u32,
4761 names: [WireFixedText<48>; MAX_SIGNALING_SERVERS],
4762 ports: [u32; MAX_SIGNALING_SERVERS],
4763 addresses: Vec<IpAddr>,
4764) -> Result<Vec<SignalingServerEndpoint>, CodecError> {
4765 let mut servers = Vec::with_capacity(MAX_SIGNALING_SERVERS);
4766 for ((name, port), address) in names.into_iter().zip(ports).zip(addresses) {
4767 let name = name.text()?;
4768 let empty = name.is_empty() && port == 0 && address.is_unspecified();
4769 if empty {
4770 continue;
4771 }
4772 if port == 0 || address.is_unspecified() || address.is_multicast() {
4773 return Err(CodecError::InvalidValue {
4774 message_id,
4775 field: "server endpoint",
4776 value: u64::from(port),
4777 });
4778 }
4779 servers.push(SignalingServerEndpoint {
4780 name,
4781 address,
4782 port: NonZeroU16::new(decode_port(port, message_id, "server port")?).ok_or(
4783 CodecError::InvalidValue {
4784 message_id,
4785 field: "server port",
4786 value: 0,
4787 },
4788 )?,
4789 });
4790 }
4791 Ok(servers)
4792}
4793
4794fn decode_bool_word(value: u32, message_id: u32, field: &'static str) -> Result<bool, CodecError> {
4795 match value {
4796 0 => Ok(false),
4797 1 => Ok(true),
4798 value => Err(CodecError::InvalidValue {
4799 message_id,
4800 field,
4801 value: u64::from(value),
4802 }),
4803 }
4804}
4805
4806fn encode_connection_statistics(
4807 statistics: &ConnectionStatistics,
4808 protocol: ProtocolVersion,
4809) -> Result<Vec<u8>, CodecError> {
4810 let tail = WireConnectionStatisticsTail {
4811 packets_sent: statistics.packets_sent,
4812 octets_sent: statistics.octets_sent,
4813 packets_received: statistics.packets_received,
4814 octets_received: statistics.octets_received,
4815 packets_lost: statistics.packets_lost,
4816 jitter_millis: statistics.jitter_millis,
4817 latency_millis: statistics.latency_millis,
4818 quality_size: u32::try_from(statistics.quality.as_bytes().len()).map_err(|_| {
4819 CodecError::CountTooLarge {
4820 message_id: id::CONNECTION_STATISTICS_RES,
4821 field: "quality statistics",
4822 count: statistics.quality.as_bytes().len(),
4823 maximum: CONNECTION_QUALITY_MAX_BYTES,
4824 }
4825 })?,
4826 };
4827 if protocol.wire() >= 19 {
4828 encode(
4829 id::CONNECTION_STATISTICS_RES,
4830 &WireConnectionStatisticsV19 {
4831 directory_number: WireFixedText::new(
4832 id::CONNECTION_STATISTICS_RES,
4833 "directory number",
4834 &statistics.directory_number,
4835 )?,
4836 alignment: [0; 3],
4837 call_reference: statistics.call_reference,
4838 processing: statistics.processing.wire_value(),
4839 statistics: tail,
4840 quality: statistics.quality.as_bytes().to_vec(),
4841 },
4842 )
4843 } else {
4844 encode(
4845 id::CONNECTION_STATISTICS_RES,
4846 &WireConnectionStatisticsV3 {
4847 directory_number: WireFixedText::new(
4848 id::CONNECTION_STATISTICS_RES,
4849 "directory number",
4850 &statistics.directory_number,
4851 )?,
4852 call_reference: statistics.call_reference,
4853 processing: statistics.processing.wire_value(),
4854 statistics: tail,
4855 quality: statistics.quality.as_bytes().to_vec(),
4856 },
4857 )
4858 }
4859}
4860
4861fn encode_start_media_ack(
4862 ack: &MediaTransmissionAck,
4863 protocol: ProtocolVersion,
4864) -> Result<Vec<u8>, CodecError> {
4865 if protocol.wire() >= 17 {
4866 let base = WireStartMediaAckV17 {
4867 conference_id: ack.conference_id,
4868 passthrough_party_id: ack.passthrough_party_id,
4869 call_reference: ack.call_reference,
4870 address: WireExtendedAddress::from_ip(ack.address),
4871 port: u32::from(ack.port),
4872 status: ack.status.wire_value(),
4873 };
4874 if let Some(extension) = ack.wire.as_ref().and_then(|wire| wire.extension) {
4875 encode(
4876 id::START_MEDIA_TRANSMISSION_ACK,
4877 &WireStartMediaAckV20 { base, extension },
4878 )
4879 } else {
4880 encode(id::START_MEDIA_TRANSMISSION_ACK, &base)
4881 }
4882 } else {
4883 let IpAddr::V4(address) = ack.address else {
4884 return Err(CodecError::InvalidValue {
4885 message_id: id::START_MEDIA_TRANSMISSION_ACK,
4886 field: "IP address family for this protocol version",
4887 value: 1,
4888 });
4889 };
4890 encode(
4891 id::START_MEDIA_TRANSMISSION_ACK,
4892 &WireStartMediaAckV3 {
4893 conference_id: ack.conference_id,
4894 passthrough_party_id: ack.passthrough_party_id,
4895 call_reference: ack.call_reference,
4896 address: address.octets(),
4897 port: u32::from(ack.port),
4898 status: ack.status.wire_value(),
4899 },
4900 )
4901 }
4902}
4903
4904impl ServerMessage {
4905 pub fn decode(frame: Frame, protocol: ProtocolVersion) -> Result<Self, CodecError> {
4908 ensure_station_route(&frame, MessageRoute::ControlToStation, "control-to-station")?;
4909 Self::decode_unchecked(frame, protocol)
4910 }
4911
4912 fn decode_unchecked(frame: Frame, protocol: ProtocolVersion) -> Result<Self, CodecError> {
4913 let p = &frame.payload;
4914 match frame.message_id {
4915 id::REGISTER_ACK => {
4916 let value: WireRegisterAck = decode(frame.message_id, p)?;
4917 validate_zero_payload(&value.alignment, frame.message_id, 2)?;
4918 let protocol_features = u32::from_le_bytes(value.protocol_features);
4919 Ok(Self::RegisterAck {
4920 keepalive_seconds: value.keepalive_seconds,
4921 secondary_keepalive_seconds: value.secondary_keepalive_seconds,
4922 protocol: ProtocolVersion::negotiate(u32::from(value.protocol_features[0]))?,
4923 features: PhoneFeatures::from_bits_retain(protocol_features & !0xff),
4924 date_template: DateTemplate::new(
4925 std::str::from_utf8(
4926 &value.date_template[..value
4927 .date_template
4928 .iter()
4929 .position(|byte| *byte == 0)
4930 .unwrap_or(6)],
4931 )
4932 .map_err(|_| CodecError::InvalidText)?,
4933 )?,
4934 })
4935 }
4936 id::REGISTER_REJECT => {
4937 let value: WireFixedText<33> = decode_zero_padded(frame.message_id, p)?;
4938 Ok(Self::RegisterReject {
4939 reason: value.text()?,
4940 })
4941 }
4942 id::KEEP_ALIVE_ACK => Ok(Self::KeepAliveAck),
4943 id::UNREGISTER_ACK => {
4944 let _: WireOneWord = decode(frame.message_id, p)?;
4945 Ok(Self::UnregisterAck)
4946 }
4947 id::CAPABILITIES_REQ => Ok(Self::CapabilitiesRequest),
4948 id::CONFIG_STAT => {
4949 let value: WireConfigStatus = decode(frame.message_id, p)?;
4950 Ok(Self::ConfigStatus(ConfigurationStatus {
4951 device_name: value.device_id.text()?,
4952 station_user_id: value.station_user_id,
4953 station_instance: value.station_instance,
4954 user_name: value.user_name.text()?,
4955 server_name: value.server_name.text()?,
4956 line_count: value.line_count,
4957 speed_dial_count: value.speed_dial_count,
4958 }))
4959 }
4960 id::CONFIG_STAT_DYNAMIC => decode_dynamic_config_status(p),
4961 id::LINE_STAT => {
4962 let value: WireLineStatus = decode(frame.message_id, p)?;
4963 Ok(Self::LineStatus {
4964 instance: value.line_instance,
4965 number: value.directory_number.text()?,
4966 display_name: value.display_name.text()?,
4967 })
4968 }
4969 id::LINE_STAT_DYNAMIC => decode_dynamic_line_status(p),
4970 id::BUTTON_TEMPLATE => {
4971 let value: WireButtonTemplate = decode(frame.message_id, p)?;
4972 if value.count > 42 {
4973 return Err(CodecError::CountTooLarge {
4974 message_id: frame.message_id,
4975 field: "button definitions in message",
4976 count: usize_from_wire(
4977 frame.message_id,
4978 "button definitions in message",
4979 value.count,
4980 )?,
4981 maximum: 42,
4982 });
4983 }
4984 let total = usize_from_wire(frame.message_id, "button definitions", value.total)?;
4985 if total > 42 {
4986 return Err(CodecError::CountTooLarge {
4987 message_id: frame.message_id,
4988 field: "button definitions",
4989 count: total,
4990 maximum: 42,
4991 });
4992 }
4993 let offset = usize_from_wire(frame.message_id, "button offset", value.offset)?;
4994 let count = usize_from_wire(frame.message_id, "button definitions", value.count)?;
4995 if offset.checked_add(count).is_none_or(|end| end > total) {
4996 return Err(CodecError::InvalidValue {
4997 message_id: frame.message_id,
4998 field: "button template range",
4999 value: u64::from(value.offset) + u64::from(value.count),
5000 });
5001 }
5002 let mut buttons = value.definitions[..count]
5003 .iter()
5004 .map(|definition| ButtonTemplateEntry {
5005 instance: u32::from(definition.instance),
5006 button_type: ButtonType::from(u32::from(definition.button_type)),
5007 })
5008 .collect::<Vec<_>>();
5009 while buttons.last().is_some_and(|button| {
5010 button.instance == 0 && button.button_type == ButtonType::Unused
5011 }) {
5012 buttons.pop();
5013 }
5014 Ok(Self::ButtonTemplate { buttons })
5015 }
5016 id::VERSION => {
5017 let value: WireFixedText<16> = decode(frame.message_id, p)?;
5018 Ok(Self::Version {
5019 firmware: value.text()?,
5020 })
5021 }
5022 id::SERVER_RES => {
5023 let servers = if protocol.wire() >= 17 {
5024 let value: WireServerResponseV17 = decode(frame.message_id, p)?;
5025 decode_server_endpoints(
5026 frame.message_id,
5027 value.names,
5028 value.ports,
5029 value
5030 .addresses
5031 .map(|address| address.to_ip(frame.message_id))
5032 .into_iter()
5033 .collect::<Result<Vec<_>, _>>()?,
5034 )?
5035 } else {
5036 let value: WireServerResponseV3 = decode(frame.message_id, p)?;
5037 decode_server_endpoints(
5038 frame.message_id,
5039 value.names,
5040 value.ports,
5041 value
5042 .addresses
5043 .map(|address| IpAddr::V4(Ipv4Addr::from(address)))
5044 .to_vec(),
5045 )?
5046 };
5047 Ok(Self::ServerResponse { servers })
5048 }
5049 id::DEFINE_TIME_DATE => {
5050 let value: WireTimeDate = decode(frame.message_id, p)?;
5051 Ok(Self::TimeDate {
5052 year: value.year,
5053 month: value.month,
5054 weekday: value.weekday,
5055 day: value.day,
5056 hour: value.hour,
5057 minute: value.minute,
5058 second: value.second,
5059 milliseconds: value.milliseconds,
5060 unix_seconds: value.unix_seconds,
5061 })
5062 }
5063 id::SOFT_KEY_TEMPLATE_RES => {
5064 let value: WireSoftKeyTemplate = decode(frame.message_id, p)?;
5065 let actions = value
5066 .definitions
5067 .iter()
5068 .filter(|definition| definition.event != 0)
5069 .map(|definition| SoftKey::from(definition.event))
5070 .collect();
5071 Ok(Self::SoftKeyTemplate { actions })
5072 }
5073 id::SOFT_KEY_SET_RES => {
5074 let value: WireSoftKeySet = decode(frame.message_id, p)?;
5075 let profile =
5076 SoftKeyProfile::new(KeyMode::ALL_KNOWN.iter().copied().map(|mode| {
5077 let actions = value
5078 .sets
5079 .get(mode.wire_value() as usize)
5080 .map(|set| {
5081 set.template_indexes
5082 .iter()
5083 .copied()
5084 .take_while(|index| *index != 0)
5085 .map(|index| SoftKey::from(u32::from(index)))
5086 .collect()
5087 })
5088 .unwrap_or_default();
5089 (mode, actions)
5090 }))?;
5091 Ok(Self::SoftKeySet { profile })
5092 }
5093 id::SELECT_SOFT_KEYS => {
5094 let value: WireSelectSoftKeys = decode(frame.message_id, p)?;
5095 Ok(Self::SelectSoftKeys {
5096 line_instance: value.line_instance,
5097 call_reference: value.call_reference,
5098 set: KeyMode::from(value.set),
5099 valid_mask: value.valid_mask,
5100 })
5101 }
5102 id::CALL_STATE => {
5103 let value: WireCallState = decode(frame.message_id, p)?;
5104 Ok(Self::CallState {
5105 state: CallState::from(value.state),
5106 line_instance: value.line_instance,
5107 call_reference: value.call_reference,
5108 })
5109 }
5110 id::CALL_INFO => {
5111 let value: WireCallInfo = decode(frame.message_id, p)?;
5112 let call_type = super::values::CallType::from(value.call_type);
5113 Ok(Self::CallInfo {
5114 info: CallInfo {
5115 direction: match call_type {
5116 super::values::CallType::Inbound => {
5117 crate::types::CallDirection::Inbound
5118 }
5119 _ => crate::types::CallDirection::Outbound,
5120 },
5121 calling_name: value.calling_name.text()?,
5122 calling_number: value.calling_number.text()?,
5123 called_name: value.called_name.text()?,
5124 called_number: value.called_number.text()?,
5125 original_called_name: value.original_called_name.text()?,
5126 original_called_number: value.original_called_number.text()?,
5127 last_redirecting_name: value.last_redirecting_name.text()?,
5128 last_redirecting_number: value.last_redirecting_number.text()?,
5129 original_redirect_reason: value.original_redirect_reason,
5130 last_redirect_reason: value.last_redirect_reason,
5131 party_restrictions: value.party_restrictions,
5132 },
5133 line_instance: value.line_instance,
5134 call_reference: value.call_reference,
5135 })
5136 }
5137 id::CALL_INFO_DYNAMIC => decode_dynamic_call_info(p, protocol),
5138 id::DISPLAY_PROMPT_STATUS => {
5139 let value: WirePromptStatus = decode(frame.message_id, p)?;
5140 Ok(Self::DisplayPrompt {
5141 timeout_seconds: value.timeout_seconds,
5142 text: value.text.text()?,
5143 line_instance: value.line_instance,
5144 call_reference: value.call_reference,
5145 })
5146 }
5147 id::DISPLAY_DYNAMIC_PROMPT_STATUS => {
5148 const HEADER_SIZE: usize = 12;
5149 if p.len() < HEADER_SIZE {
5150 return Err(CodecError::Truncated {
5151 message_id: frame.message_id,
5152 needed: HEADER_SIZE,
5153 actual: p.len(),
5154 });
5155 }
5156 let value: WireDynamicPromptHeader = decode(frame.message_id, &p[..HEADER_SIZE])?;
5157 Ok(Self::DisplayPrompt {
5158 timeout_seconds: value.timeout_seconds,
5159 text: decode_dynamic_text(frame.message_id, p, HEADER_SIZE)?,
5160 line_instance: value.line_instance,
5161 call_reference: value.call_reference,
5162 })
5163 }
5164 id::CLEAR_PROMPT_STATUS => {
5165 let value: WireLineCall = decode(frame.message_id, p)?;
5166 Ok(Self::ClearPrompt {
5167 line_instance: value.line_instance,
5168 call_reference: value.call_reference,
5169 })
5170 }
5171 id::DISPLAY_NOTIFY => {
5172 let value: WireNotify = decode(frame.message_id, p)?;
5173 Ok(Self::DisplayNotify {
5174 timeout_seconds: value.timeout_seconds,
5175 text: value.text.text()?,
5176 })
5177 }
5178 id::DISPLAY_DYNAMIC_NOTIFY => {
5179 const HEADER_SIZE: usize = 4;
5180 if p.len() < HEADER_SIZE {
5181 return Err(CodecError::Truncated {
5182 message_id: frame.message_id,
5183 needed: HEADER_SIZE,
5184 actual: p.len(),
5185 });
5186 }
5187 let value: WireDynamicNotifyHeader = decode(frame.message_id, &p[..HEADER_SIZE])?;
5188 Ok(Self::DisplayNotify {
5189 timeout_seconds: value.timeout_seconds,
5190 text: decode_dynamic_text(frame.message_id, p, HEADER_SIZE)?,
5191 })
5192 }
5193 id::CLEAR_NOTIFY => Ok(Self::ClearNotify),
5194 id::DISPLAY_PRIORITY_NOTIFY => {
5195 let value: WirePriorityNotify = decode(frame.message_id, p)?;
5196 Ok(Self::DisplayPriorityNotify {
5197 timeout_seconds: value.timeout_seconds,
5198 priority: NotificationPriority::from(value.priority),
5199 text: value.text.text()?,
5200 })
5201 }
5202 id::DISPLAY_DYNAMIC_PRIORITY_NOTIFY => {
5203 const HEADER_SIZE: usize = 8;
5204 if p.len() < HEADER_SIZE {
5205 return Err(CodecError::Truncated {
5206 message_id: frame.message_id,
5207 needed: HEADER_SIZE,
5208 actual: p.len(),
5209 });
5210 }
5211 let value: WireDynamicPriorityNotifyHeader =
5212 decode(frame.message_id, &p[..HEADER_SIZE])?;
5213 Ok(Self::DisplayPriorityNotify {
5214 timeout_seconds: value.timeout_seconds,
5215 priority: NotificationPriority::from(value.priority),
5216 text: decode_dynamic_text(frame.message_id, p, HEADER_SIZE)?,
5217 })
5218 }
5219 id::CLEAR_PRIORITY_NOTIFY => {
5220 let value: WireOneWord = decode(frame.message_id, p)?;
5221 Ok(Self::ClearPriorityNotify {
5222 priority: NotificationPriority::from(value.value),
5223 })
5224 }
5225 id::NOTIFY_DTMF_TONE | id::SEND_DTMF_TONE => {
5226 let value: WireDtmfToneControl = decode(frame.message_id, p)?;
5227 let message = DtmfToneControl {
5228 tone: Tone::from(value.tone),
5229 conference_id: value.conference_id.into(),
5230 passthrough_party_id: value.passthrough_party_id,
5231 };
5232 if frame.message_id == id::NOTIFY_DTMF_TONE {
5233 Ok(Self::NotifyDtmfTone(message))
5234 } else {
5235 Ok(Self::SendDtmfTone(message))
5236 }
5237 }
5238 id::START_ANNOUNCEMENT => {
5239 const PAYLOAD_SIZE: usize = 464;
5240 validate_exact_payload(p, frame.message_id, PAYLOAD_SIZE)?;
5241 let value: WireStartAnnouncement = decode(frame.message_id, p)?;
5242 let mut announcements = value
5243 .announcements
5244 .into_iter()
5245 .map(|entry| AnnouncementEntry {
5246 locale: entry.locale,
5247 country: entry.country,
5248 tone: Tone::from(entry.tone),
5249 })
5250 .collect::<Vec<_>>();
5251 while announcements.last().is_some_and(|entry| {
5252 entry.locale == 0 && entry.country == 0 && entry.tone.wire_value() == 0
5253 }) {
5254 announcements.pop();
5255 }
5256 let mut matrix_conference_party_ids = value.matrix_conference_party_ids.to_vec();
5257 while matrix_conference_party_ids.last() == Some(&0) {
5258 matrix_conference_party_ids.pop();
5259 }
5260 Ok(Self::StartAnnouncement {
5261 announcements,
5262 end_of_ack: value.end_of_ack,
5263 conference_id: value.conference_id,
5264 matrix_conference_party_ids,
5265 hearing_conference_party_mask: value.hearing_conference_party_mask,
5266 play_mode: value.play_mode,
5267 })
5268 }
5269 id::STOP_ANNOUNCEMENT => {
5270 validate_exact_payload(p, frame.message_id, 4)?;
5271 let value: WireOneWord = decode(frame.message_id, p)?;
5272 Ok(Self::StopAnnouncement {
5273 conference_id: value.value,
5274 })
5275 }
5276 id::ANNOUNCEMENT_FINISH => {
5277 validate_exact_payload(p, frame.message_id, 8)?;
5278 let value: WireAnnouncementFinish = decode(frame.message_id, p)?;
5279 Ok(Self::AnnouncementFinish {
5280 conference_id: value.conference_id,
5281 play_status: value.play_status,
5282 })
5283 }
5284 id::CLEAR_CONFERENCE => {
5285 validate_exact_payload(p, frame.message_id, 8)?;
5286 let value: WireCallParty = decode(frame.message_id, p)?;
5287 Ok(Self::ClearConference {
5288 conference_id: value.call_reference.into(),
5289 service_number: value.passthrough_party_id,
5290 })
5291 }
5292 id::CREATE_CONFERENCE_REQ => {
5293 validate_conference_data_length(p, frame.message_id, 76, 72)?;
5294 let value: WireCreateConferenceRequest = decode_zero_padded(frame.message_id, p)?;
5295 Ok(Self::CreateConferenceRequest(CreateConferenceRequest {
5296 conference_id: value.conference_id.into(),
5297 reserved_participants: value.reserved_participants,
5298 resource_type: ConferenceResourceType::from(value.resource_type),
5299 application_id: value.application_id.into(),
5300 application_conference_id: value.application_conference_id.text()?,
5301 application_data: value.application_data.text()?,
5302 passthrough_data: value.passthrough_data,
5303 }))
5304 }
5305 id::DELETE_CONFERENCE_REQ => {
5306 validate_exact_payload(p, frame.message_id, 4)?;
5307 let value: WireOneWord = decode(frame.message_id, p)?;
5308 Ok(Self::DeleteConferenceRequest {
5309 conference_id: value.value.into(),
5310 })
5311 }
5312 id::MODIFY_CONFERENCE_REQ => {
5313 validate_conference_data_length(p, frame.message_id, 72, 68)?;
5314 let value: WireModifyConferenceRequest = decode_zero_padded(frame.message_id, p)?;
5315 Ok(Self::ModifyConferenceRequest(ModifyConferenceRequest {
5316 conference_id: value.conference_id.into(),
5317 reserved_participants: value.reserved_participants,
5318 application_id: value.application_id.into(),
5319 application_conference_id: value.application_conference_id.text()?,
5320 application_data: value.application_data.text()?,
5321 passthrough_data: value.passthrough_data,
5322 }))
5323 }
5324 id::AUDIT_CONFERENCE_REQ => {
5325 validate_exact_payload(p, frame.message_id, 0)?;
5326 Ok(Self::AuditConferenceRequest)
5327 }
5328 id::ADD_PARTICIPANT_REQ => {
5329 let (conference_id, participant) = decode_participant_request(p, frame.message_id)?;
5330 Ok(Self::AddParticipantRequest(AddParticipantRequest {
5331 conference_id,
5332 participant,
5333 }))
5334 }
5335 id::DROP_PARTICIPANT_REQ => {
5336 validate_exact_payload(p, frame.message_id, 8)?;
5337 let value: WireCallParty = decode(frame.message_id, p)?;
5338 Ok(Self::DropParticipantRequest {
5339 conference_id: value.call_reference.into(),
5340 call_reference: value.passthrough_party_id.into(),
5341 })
5342 }
5343 id::AUDIT_PARTICIPANT_REQ => {
5344 validate_exact_payload(p, frame.message_id, 4)?;
5345 let value: WireOneWord = decode(frame.message_id, p)?;
5346 Ok(Self::AuditParticipantRequest {
5347 conference_id: value.value.into(),
5348 })
5349 }
5350 id::CHANGE_PARTICIPANT_REQ => {
5351 let (conference_id, participant) = decode_participant_request(p, frame.message_id)?;
5352 Ok(Self::ChangeParticipantRequest(ChangeParticipantRequest {
5353 conference_id,
5354 participant,
5355 }))
5356 }
5357 id::STOP_MULTIMEDIA_TRANSMISSION | id::CLOSE_MULTIMEDIA_RECEIVE_CHANNEL => {
5358 let value: WireMultimediaStreamControl = decode(frame.message_id, p)?;
5359 let message = MultimediaStreamControl {
5360 conference_id: value.conference_id.into(),
5361 passthrough_party_id: value.passthrough_party_id.into(),
5362 call_reference: value.call_reference.into(),
5363 port_handling_flag: value.port_handling_flag,
5364 };
5365 if frame.message_id == id::STOP_MULTIMEDIA_TRANSMISSION {
5366 Ok(Self::StopMultimediaTransmission(message))
5367 } else {
5368 Ok(Self::CloseMultimediaReceiveChannel(message))
5369 }
5370 }
5371 id::FLOW_CONTROL_COMMAND | id::FLOW_CONTROL_NOTIFY => {
5372 let value: WireVideoFlowControl = decode(frame.message_id, p)?;
5373 let message = VideoFlowControl {
5374 conference_id: value.conference_id.into(),
5375 passthrough_party_id: value.passthrough_party_id.into(),
5376 call_reference: value.call_reference.into(),
5377 maximum_bit_rate: value.maximum_bit_rate,
5378 };
5379 if frame.message_id == id::FLOW_CONTROL_COMMAND {
5380 Ok(Self::FlowControlCommand(message))
5381 } else {
5382 Ok(Self::FlowControlNotify(message))
5383 }
5384 }
5385 id::VIDEO_DISPLAY_COMMAND => {
5386 let value: WireVideoDisplayCommand = decode(frame.message_id, p)?;
5387 Ok(Self::VideoDisplayCommand {
5388 conference_id: value.conference_id.into(),
5389 call_reference: value.call_reference.into(),
5390 layout_id: value.layout_id,
5391 })
5392 }
5393 id::ACTIVATE_CALL_PLANE => {
5394 let value: WireOneWord = decode(frame.message_id, p)?;
5395 Ok(Self::ActivateCallPlane {
5396 line_instance: value.value,
5397 })
5398 }
5399 id::DEACTIVATE_CALL_PLANE => Ok(Self::DeactivateCallPlane),
5400 id::BACKSPACE_RESPONSE => {
5401 let value: WireLineCall = decode(frame.message_id, p)?;
5402 Ok(Self::BackspaceResponse {
5403 line_instance: value.line_instance,
5404 call_reference: value.call_reference,
5405 })
5406 }
5407 id::REGISTER_TOKEN_ACK => Ok(Self::RegisterTokenAck),
5408 id::REGISTER_TOKEN_REJECT => {
5409 let value: WireOneWord = decode(frame.message_id, p)?;
5410 Ok(Self::RegisterTokenReject {
5411 backoff_seconds: value.value,
5412 })
5413 }
5414 id::SET_RINGER => {
5415 let value: WireModeLineCall = decode(frame.message_id, p)?;
5416 Ok(Self::SetRinger {
5417 mode: RingerMode::from(value.mode),
5418 duration: RingDuration::from(value.duration),
5419 line_instance: value.line_instance,
5420 call_reference: value.call_reference,
5421 })
5422 }
5423 id::SET_LAMP => {
5424 let value: WireLampState = decode(frame.message_id, p)?;
5425 Ok(Self::SetLamp {
5426 stimulus: ButtonType::from(value.stimulus),
5427 instance: value.instance,
5428 mode: LampMode::from(value.mode),
5429 })
5430 }
5431 id::START_TONE => {
5432 let value: WireToneLineCall = decode(frame.message_id, p)?;
5433 Ok(Self::StartTone {
5434 tone: Tone::from(value.tone),
5435 direction: ToneDirection::from(value.direction),
5436 line_instance: value.line_instance,
5437 call_reference: value.call_reference,
5438 })
5439 }
5440 id::STOP_TONE => {
5441 let (line_instance, call_reference) = if protocol.wire() >= 12 {
5442 let value: WireStopToneV12 = decode(frame.message_id, p)?;
5443 (value.line_instance, value.call_reference)
5444 } else {
5445 let value: WireLineCall = decode(frame.message_id, p)?;
5446 (value.line_instance, value.call_reference)
5447 };
5448 Ok(Self::StopTone {
5449 line_instance,
5450 call_reference,
5451 })
5452 }
5453 id::START_MULTICAST_MEDIA_RECEPTION => {
5454 decode_start_multicast_reception(p, protocol, frame.message_id)
5455 }
5456 id::START_MULTICAST_MEDIA_TRANSMISSION => {
5457 decode_start_multicast_transmission(p, protocol, frame.message_id)
5458 }
5459 id::STOP_MULTICAST_MEDIA_RECEPTION | id::STOP_MULTICAST_MEDIA_TRANSMISSION => {
5460 validate_exact_payload(p, frame.message_id, 12)?;
5461 let value: WireStopMulticast = decode(frame.message_id, p)?;
5462 if frame.message_id == id::STOP_MULTICAST_MEDIA_RECEPTION {
5463 Ok(Self::StopMulticastMediaReception {
5464 conference_id: value.conference_id.into(),
5465 passthrough_party_id: value.passthrough_party_id.into(),
5466 call_reference: value.call_reference.into(),
5467 })
5468 } else {
5469 Ok(Self::StopMulticastMediaTransmission {
5470 conference_id: value.conference_id.into(),
5471 passthrough_party_id: value.passthrough_party_id.into(),
5472 call_reference: value.call_reference.into(),
5473 })
5474 }
5475 }
5476 id::OPEN_RECEIVE_CHANNEL => decode_open_receive(p, protocol, frame.message_id),
5477 id::CLOSE_RECEIVE_CHANNEL => {
5478 validate_exact_payload(p, frame.message_id, 16)?;
5479 let value: WireAudioStreamControl = decode(frame.message_id, p)?;
5480 Ok(Self::CloseReceiveChannel(AudioStreamControl {
5481 conference_id: value.conference_id.into(),
5482 passthrough_party_id: value.passthrough_party_id.into(),
5483 call_reference: value.call_reference.into(),
5484 port_handling_flag: value.port_handling_flag,
5485 }))
5486 }
5487 id::CONNECTION_STATISTICS_REQ => {
5488 if protocol.wire() >= 19 {
5489 let value: WireConnectionStatisticsRequestV19 = decode(frame.message_id, p)?;
5490 Ok(Self::ConnectionStatisticsRequest {
5491 directory_number: value.directory_number.text()?,
5492 call_reference: value.call_reference,
5493 processing: StatisticsProcessing::from(value.processing),
5494 })
5495 } else {
5496 let value: WireConnectionStatisticsRequestV3 = decode(frame.message_id, p)?;
5497 Ok(Self::ConnectionStatisticsRequest {
5498 directory_number: value.directory_number.text()?,
5499 call_reference: value.call_reference,
5500 processing: StatisticsProcessing::from(value.processing),
5501 })
5502 }
5503 }
5504 id::START_MEDIA_TRANSMISSION => decode_start_media(p, protocol, frame.message_id),
5505 id::STOP_MEDIA_TRANSMISSION => {
5506 validate_exact_payload(p, frame.message_id, 16)?;
5507 let value: WireAudioStreamControl = decode(frame.message_id, p)?;
5508 Ok(Self::StopMediaTransmission(AudioStreamControl {
5509 conference_id: value.conference_id.into(),
5510 passthrough_party_id: value.passthrough_party_id.into(),
5511 call_reference: value.call_reference.into(),
5512 port_handling_flag: value.port_handling_flag,
5513 }))
5514 }
5515 id::SET_SPEAKER_MODE => {
5516 let value: WireOneWord = decode(frame.message_id, p)?;
5517 Ok(Self::SetSpeakerMode(SpeakerMode::from(value.value)))
5518 }
5519 id::SET_MICROPHONE_MODE => {
5520 let value: WireOneWord = decode(frame.message_id, p)?;
5521 Ok(Self::SetMicrophoneMode(MicrophoneMode::from(value.value)))
5522 }
5523 id::RESET => {
5524 let value: WireOneWord = decode(frame.message_id, p)?;
5525 Ok(Self::Reset(ResetType::from(value.value)))
5526 }
5527 id::DISPLAY_TEXT => {
5528 let value: WireFixedText<32> = decode(frame.message_id, p)?;
5529 Ok(Self::DisplayText {
5530 text: value.text()?,
5531 })
5532 }
5533 id::CLEAR_DISPLAY => Ok(Self::ClearDisplay),
5534 id::FORWARD_STAT => {
5535 if protocol.wire() >= 19 {
5536 let value: WireForwardStatusV19 = decode(frame.message_id, p)?;
5537 validate_zero_payload(&value.all_alignment, frame.message_id, 3)?;
5538 validate_zero_payload(&value.busy_alignment, frame.message_id, 3)?;
5539 validate_zero_payload(&value.no_answer_alignment, frame.message_id, 3)?;
5540 Ok(Self::ForwardStatus {
5541 forward_all: (value.all_active != 0)
5542 .then(|| value.all_number.text())
5543 .transpose()?,
5544 forward_busy: (value.busy_active != 0)
5545 .then(|| value.busy_number.text())
5546 .transpose()?,
5547 forward_no_answer: (value.no_answer_active != 0)
5548 .then(|| value.no_answer_number.text())
5549 .transpose()?,
5550 line_instance: value.line_instance,
5551 })
5552 } else {
5553 let value: WireForwardStatusV3 = decode_zero_padded(frame.message_id, p)?;
5554 Ok(Self::ForwardStatus {
5555 forward_all: (value.all_active != 0)
5556 .then(|| value.all_number.text())
5557 .transpose()?,
5558 forward_busy: (value.busy_active != 0)
5559 .then(|| value.busy_number.text())
5560 .transpose()?,
5561 forward_no_answer: (value.no_answer_active != 0)
5562 .then(|| value.no_answer_number.text())
5563 .transpose()?,
5564 line_instance: value.line_instance,
5565 })
5566 }
5567 }
5568 id::SPEED_DIAL_STAT => {
5569 let value: WireSpeedDialStatus = decode(frame.message_id, p)?;
5570 Ok(Self::SpeedDialStatus {
5571 instance: value.instance,
5572 number: value.number.text()?,
5573 display_name: value.display_name.text()?,
5574 })
5575 }
5576 id::SPEED_DIAL_STAT_DYNAMIC => {
5577 let value: WireSpeedDialStatus = decode(frame.message_id, p)?;
5578 Ok(Self::SpeedDialStatus {
5579 instance: value.instance,
5580 number: value.number.text()?,
5581 display_name: value.display_name.text()?,
5582 })
5583 }
5584 id::START_MEDIA_FAILURE_DETECTION => {
5585 let value: WireMediaFailureDetection = decode(frame.message_id, p)?;
5586 Ok(Self::StartMediaFailureDetection(MediaFailureDetection {
5587 conference_id: value.conference_id.into(),
5588 passthrough_party_id: value.passthrough_party_id,
5589 packet_millis: value.packet_millis,
5590 codec: Codec::from(value.codec),
5591 echo_cancellation: EchoCancellation::from(value.echo_cancellation),
5592 codec_qualifier: value.codec_qualifier,
5593 call_reference: value.call_reference.into(),
5594 }))
5595 }
5596 id::OPEN_MULTIMEDIA_CHANNEL => decode_open_multimedia(p, protocol, frame.message_id)
5597 .map(Self::OpenMultimediaChannel),
5598 id::START_MULTIMEDIA_TRANSMISSION => {
5599 decode_start_multimedia(p, protocol, frame.message_id)
5600 .map(Self::StartMultimediaTransmission)
5601 }
5602 id::MISCELLANEOUS_COMMAND => {
5603 decode_miscellaneous_command(p, frame.message_id).map(Self::MiscellaneousCommand)
5604 }
5605 id::DIALED_NUMBER => {
5606 if protocol.wire() >= 19 {
5607 let value: WireDialedNumberV19 = decode(frame.message_id, p)?;
5608 validate_zero_payload(&value.alignment, frame.message_id, 3)?;
5609 Ok(Self::DialedNumber {
5610 number: value.number.text()?,
5611 line_instance: value.line_instance,
5612 call_reference: value.call_reference,
5613 })
5614 } else {
5615 let value: WireDialedNumberV3 = decode_zero_padded(frame.message_id, p)?;
5616 Ok(Self::DialedNumber {
5617 number: value.number.text()?,
5618 line_instance: value.line_instance,
5619 call_reference: value.call_reference,
5620 })
5621 }
5622 }
5623 id::SUBSCRIBE_DTMF_PAYLOAD_REQ => {
5624 let value: WireDtmfPayloadRequest = decode(frame.message_id, p)?;
5625 Ok(Self::SubscribeDtmfPayloadRequest(
5626 dtmf_payload_request_from_wire(value),
5627 ))
5628 }
5629 id::SUBSCRIBE_DTMF_PAYLOAD_ERR => {
5630 let value: WireDtmfPayloadIdentity = decode(frame.message_id, p)?;
5631 Ok(Self::SubscribeDtmfPayloadError(
5632 dtmf_payload_identity_from_wire(value),
5633 ))
5634 }
5635 id::UNSUBSCRIBE_DTMF_PAYLOAD_REQ => {
5636 let value: WireDtmfPayloadRequest = decode(frame.message_id, p)?;
5637 Ok(Self::UnsubscribeDtmfPayloadRequest(
5638 dtmf_payload_request_from_wire(value),
5639 ))
5640 }
5641 id::UNSUBSCRIBE_DTMF_PAYLOAD_ERR => {
5642 let value: WireDtmfPayloadIdentity = decode(frame.message_id, p)?;
5643 Ok(Self::UnsubscribeDtmfPayloadError(
5644 dtmf_payload_identity_from_wire(value),
5645 ))
5646 }
5647 id::USER_TO_DEVICE_DATA => {
5648 decode_user_data(p, frame.message_id).map(Self::UserToDeviceData)
5649 }
5650 id::USER_TO_DEVICE_DATA_V1 => {
5651 decode_user_data_v1(p, frame.message_id).map(Self::UserToDeviceDataV1)
5652 }
5653 id::FEATURE_STAT => {
5654 let value: WireFeatureStatus = decode(frame.message_id, p)?;
5655 Ok(Self::FeatureStatus {
5656 instance: value.instance,
5657 button_type: ButtonType::from(value.button_type),
5658 label: value.label.text()?,
5659 state: value.state,
5660 })
5661 }
5662 id::FEATURE_STAT_DYNAMIC => {
5663 let value: WireFeatureStatusDynamic = decode(frame.message_id, p)?;
5664 Ok(Self::FeatureStatus {
5665 instance: value.instance,
5666 button_type: ButtonType::from(value.button_type),
5667 label: value.label.text()?,
5668 state: value.state,
5669 })
5670 }
5671 id::SERVICE_URL_STAT => {
5672 let value: WireServiceUrlStatus = decode(frame.message_id, p)?;
5673 Ok(Self::ServiceUrlStatus {
5674 index: value.index,
5675 url: value.url.text()?,
5676 label: value.label.text()?,
5677 extension_text: String::new(),
5678 })
5679 }
5680 id::SERVICE_URL_STAT_DYNAMIC => decode_dynamic_service_url_status(p, protocol),
5681 id::CALL_SELECT_STAT => {
5682 let value: WireCallSelectStatus = decode(frame.message_id, p)?;
5683 Ok(Self::CallSelectStatus {
5684 status: value.status,
5685 call_reference: value.call_reference,
5686 line_instance: value.line_instance,
5687 })
5688 }
5689 id::PORT_REQUEST => {
5690 let request = if protocol.wire() >= 20 {
5691 let value: WirePortRequestFrom20 = decode(frame.message_id, p)?;
5692 PortRequest {
5693 conference_id: value.conference_id.into(),
5694 call_reference: value.call_reference.into(),
5695 passthrough_party_id: value.passthrough_party_id.into(),
5696 transport: MediaTransport::from(value.transport),
5697 address_type: Some(IpAddressType::from(value.address_type)),
5698 media_type: Some(MediaType::from(value.media_type)),
5699 }
5700 } else {
5701 let value: WirePortRequestPre20 = decode(frame.message_id, p)?;
5702 PortRequest {
5703 conference_id: value.conference_id.into(),
5704 call_reference: value.call_reference.into(),
5705 passthrough_party_id: value.passthrough_party_id.into(),
5706 transport: MediaTransport::from(value.transport),
5707 address_type: None,
5708 media_type: None,
5709 }
5710 };
5711 Ok(Self::PortRequest(request))
5712 }
5713 id::PORT_CLOSE => {
5714 let close = if protocol.wire() >= 20 {
5715 let value: WirePortCloseFrom20 = decode(frame.message_id, p)?;
5716 PortClose {
5717 conference_id: value.conference_id.into(),
5718 call_reference: value.call_reference.into(),
5719 passthrough_party_id: value.passthrough_party_id.into(),
5720 media_type: Some(MediaType::from(value.media_type)),
5721 }
5722 } else {
5723 let value: WirePortClosePre20 = decode(frame.message_id, p)?;
5724 PortClose {
5725 conference_id: value.conference_id.into(),
5726 call_reference: value.call_reference.into(),
5727 passthrough_party_id: value.passthrough_party_id.into(),
5728 media_type: None,
5729 }
5730 };
5731 Ok(Self::PortClose(close))
5732 }
5733 id::SUBSCRIPTION_STAT => {
5734 let value: WireSubscriptionStatus = decode(frame.message_id, p)?;
5735 Ok(Self::SubscriptionStatus {
5736 transaction_id: value.transaction_id,
5737 feature_id: value.feature_id,
5738 timer_seconds: value.timer_seconds,
5739 cause: SubscriptionCause::from(value.cause),
5740 })
5741 }
5742 id::NOTIFICATION => {
5743 let value: WireNotification = decode(frame.message_id, p)?;
5744 Ok(Self::Notification {
5745 transaction_id: value.transaction_id,
5746 feature_id: value.feature_id,
5747 status: BusyLampFieldState::from(value.status),
5748 text: value.text.text()?,
5749 })
5750 }
5751 id::CALL_HISTORY_DISPOSITION => {
5752 let value: WireCallHistoryDisposition = decode(frame.message_id, p)?;
5753 Ok(Self::CallHistoryDisposition {
5754 disposition: CallHistoryDisposition::from(value.disposition),
5755 line_instance: value.line_instance,
5756 call_reference: value.call_reference,
5757 })
5758 }
5759 id::CALL_COUNT_RES => Ok(Self::CallCountResponse),
5760 id::RECORDING_STATUS => {
5761 let value: WireRecordingStatus = decode(frame.message_id, p)?;
5762 Ok(Self::RecordingStatus {
5763 call_reference: value.call_reference,
5764 active: decode_bool_word(value.active, frame.message_id, "recording active")?,
5765 })
5766 }
5767 _ => {
5768 let message_type = frame.message_type();
5769 if message_type.is_known() {
5770 preserve_known_message(frame, message_type).map(Self::KnownOpaque)
5771 } else {
5772 Ok(Self::Unknown(RawMessage {
5773 message_id: frame.message_id,
5774 protocol_version: frame.protocol_version,
5775 payload: frame.payload,
5776 }))
5777 }
5778 }
5779 }
5780 }
5781
5782 pub fn encode(&self, protocol: ProtocolVersion) -> Result<Vec<u8>, CodecError> {
5787 self.encode_for_session(protocol.into())
5788 }
5789
5790 pub fn encode_for_session(
5792 &self,
5793 session: StationSessionContext,
5794 ) -> Result<Vec<u8>, CodecError> {
5795 let (message_id, payload, header_protocol) = self.payload(session, None)?;
5796 reject_non_station_route(
5797 message_id,
5798 MessageRoute::ControlToStation,
5799 "control-to-station",
5800 )?;
5801 Frame::new(header_protocol, message_id, payload).encode()
5802 }
5803
5804 fn encode_unchecked(&self, protocol: ProtocolVersion) -> Result<Vec<u8>, CodecError> {
5805 let (message_id, payload, header_protocol) = self.payload(protocol.into(), None)?;
5806 Frame::new(header_protocol, message_id, payload).encode()
5807 }
5808
5809 pub fn encode_for_legacy_station(
5814 &self,
5815 protocol: ProtocolVersion,
5816 code_page: LegacyCodePage,
5817 ) -> Result<Vec<u8>, CodecError> {
5818 self.encode_for_legacy_session(protocol.into(), code_page)
5819 }
5820
5821 pub fn encode_for_legacy_session(
5824 &self,
5825 session: StationSessionContext,
5826 code_page: LegacyCodePage,
5827 ) -> Result<Vec<u8>, CodecError> {
5828 let (message_id, payload, header_protocol) = self.payload(session, Some(code_page))?;
5829 reject_non_station_route(
5830 message_id,
5831 MessageRoute::ControlToStation,
5832 "control-to-station",
5833 )?;
5834 Frame::new(header_protocol, message_id, payload).encode()
5835 }
5836
5837 fn payload(
5838 &self,
5839 session: StationSessionContext,
5840 legacy_code_page: Option<LegacyCodePage>,
5841 ) -> Result<(u32, Vec<u8>, u32), CodecError> {
5842 let protocol = session.protocol;
5843 let mut p = Vec::new();
5844 let id = match self {
5845 Self::RegisterAck {
5846 keepalive_seconds,
5847 secondary_keepalive_seconds,
5848 protocol,
5849 features,
5850 date_template,
5851 } => {
5852 if date_template.as_str().len() > 6 {
5853 return Err(CodecError::TextTooLong {
5854 message_id: id::REGISTER_ACK,
5855 field: "date template",
5856 actual: date_template.as_str().len(),
5857 maximum: 6,
5858 });
5859 }
5860 let mut wire_date_template = [0_u8; 6];
5861 wire_date_template[..date_template.as_str().len()]
5862 .copy_from_slice(date_template.as_str().as_bytes());
5863 p = encode(
5864 id::REGISTER_ACK,
5865 &WireRegisterAck {
5866 keepalive_seconds: *keepalive_seconds,
5867 date_template: wire_date_template,
5868 alignment: [0; 2],
5869 secondary_keepalive_seconds: *secondary_keepalive_seconds,
5870 protocol_features: {
5871 let mut bytes = features.bits().to_le_bytes();
5872 bytes[0] = protocol.wire() as u8;
5873 bytes
5874 },
5875 },
5876 )?;
5877 return Ok((id::REGISTER_ACK, p, 0));
5878 }
5879 Self::RegisterReject { reason } => {
5880 p = encode(
5881 id::REGISTER_REJECT,
5882 &WireFixedText::<33>::new(id::REGISTER_REJECT, "reject reason", reason)?,
5883 )?;
5884 pad_dynamic_payload(&mut p);
5885 id::REGISTER_REJECT
5886 }
5887 Self::KeepAliveAck => return Ok((id::KEEP_ALIVE_ACK, p, 0)),
5888 Self::UnregisterAck => {
5889 p = encode(id::UNREGISTER_ACK, &WireOneWord { value: 0 })?;
5890 return Ok((id::UNREGISTER_ACK, p, 0));
5891 }
5892 Self::CapabilitiesRequest => id::CAPABILITIES_REQ,
5893 Self::ConfigStatus(status) => {
5894 if session.uses_dynamic_general_ui() {
5895 p = encode_dynamic_config_status(status)?;
5896 id::CONFIG_STAT_DYNAMIC
5897 } else {
5898 p = encode(
5899 id::CONFIG_STAT,
5900 &WireConfigStatus {
5901 device_id: WireFixedText::new(
5902 id::CONFIG_STAT,
5903 "device ID",
5904 &status.device_name,
5905 )?,
5906 station_user_id: status.station_user_id,
5907 station_instance: status.station_instance,
5908 user_name: WireFixedText::new_station(
5909 id::CONFIG_STAT,
5910 "user name",
5911 &status.user_name,
5912 legacy_code_page,
5913 )?,
5914 server_name: WireFixedText::new_station(
5915 id::CONFIG_STAT,
5916 "server name",
5917 &status.server_name,
5918 legacy_code_page,
5919 )?,
5920 line_count: status.line_count,
5921 speed_dial_count: status.speed_dial_count,
5922 },
5923 )?;
5924 id::CONFIG_STAT
5925 }
5926 }
5927 Self::LineStatus {
5928 instance,
5929 number,
5930 display_name,
5931 } => {
5932 if session.uses_dynamic_general_ui() {
5933 p = encode_dynamic_line_status(
5934 *instance,
5935 number,
5936 display_name,
5937 legacy_code_page,
5938 )?;
5939 id::LINE_STAT_DYNAMIC
5940 } else {
5941 p = encode(
5942 id::LINE_STAT,
5943 &WireLineStatus {
5944 line_instance: *instance,
5945 directory_number: WireFixedText::new(
5946 id::LINE_STAT,
5947 "line number",
5948 number,
5949 )?,
5950 display_name: WireFixedText::new_station(
5951 id::LINE_STAT,
5952 "display name",
5953 display_name,
5954 legacy_code_page,
5955 )?,
5956 display_label: WireFixedText::new_station(
5957 id::LINE_STAT,
5958 "line label",
5959 display_name,
5960 legacy_code_page,
5961 )?,
5962 reserved: 0,
5963 },
5964 )?;
5965 id::LINE_STAT
5966 }
5967 }
5968 Self::ButtonTemplate { buttons } => {
5969 if buttons.len() > 42 {
5970 return Err(CodecError::CountTooLarge {
5971 message_id: id::BUTTON_TEMPLATE,
5972 field: "button definitions",
5973 count: buttons.len(),
5974 maximum: 42,
5975 });
5976 }
5977 let mut definitions = [WireButtonDefinition::default(); 42];
5978 for (index, button) in buttons.iter().enumerate() {
5979 definitions[index] = WireButtonDefinition {
5980 instance: u8::try_from(button.instance).map_err(|_| {
5981 CodecError::InvalidValue {
5982 message_id: id::BUTTON_TEMPLATE,
5983 field: "button instance",
5984 value: u64::from(button.instance),
5985 }
5986 })?,
5987 button_type: u8::try_from(button.button_type.wire_value()).map_err(
5988 |_| CodecError::InvalidValue {
5989 message_id: id::BUTTON_TEMPLATE,
5990 field: "button type",
5991 value: u64::from(button.button_type.wire_value()),
5992 },
5993 )?,
5994 };
5995 }
5996 p = encode(
5997 id::BUTTON_TEMPLATE,
5998 &WireButtonTemplate {
5999 offset: 0,
6000 count: 42,
6001 total: 42,
6002 definitions,
6003 },
6004 )?;
6005 id::BUTTON_TEMPLATE
6006 }
6007 Self::Version { firmware } => {
6008 p = encode(
6009 id::VERSION,
6010 &WireFixedText::<16>::new(id::VERSION, "firmware", firmware)?,
6011 )?;
6012 id::VERSION
6013 }
6014 Self::ServerResponse { servers } => {
6015 if servers.is_empty() {
6016 return Err(CodecError::InvalidValue {
6017 message_id: id::SERVER_RES,
6018 field: "server endpoints",
6019 value: 0,
6020 });
6021 }
6022 if servers.len() > MAX_SIGNALING_SERVERS {
6023 return Err(CodecError::CountTooLarge {
6024 message_id: id::SERVER_RES,
6025 field: "server endpoints",
6026 count: servers.len(),
6027 maximum: MAX_SIGNALING_SERVERS,
6028 });
6029 }
6030 if servers
6031 .iter()
6032 .any(|server| server.address.is_unspecified() || server.address.is_multicast())
6033 {
6034 return Err(CodecError::InvalidValue {
6035 message_id: id::SERVER_RES,
6036 field: "server address",
6037 value: 0,
6038 });
6039 }
6040 let names: [WireFixedText<48>; MAX_SIGNALING_SERVERS] = (0..MAX_SIGNALING_SERVERS)
6041 .map(|index| {
6042 WireFixedText::new(
6043 id::SERVER_RES,
6044 "server name",
6045 servers.get(index).map_or("", |server| server.name.as_str()),
6046 )
6047 })
6048 .collect::<Result<Vec<_>, _>>()?
6049 .try_into()
6050 .map_err(|_| CodecError::InvalidValue {
6051 message_id: id::SERVER_RES,
6052 field: "server endpoint array",
6053 value: servers.len() as u64,
6054 })?;
6055 let ports = std::array::from_fn(|index| {
6056 servers
6057 .get(index)
6058 .map_or(0, |server| u32::from(server.port.get()))
6059 });
6060 if protocol.wire() >= 17 {
6061 p = encode(
6062 id::SERVER_RES,
6063 &WireServerResponseV17 {
6064 names,
6065 ports,
6066 addresses: std::array::from_fn(|index| {
6067 WireExtendedAddress::from_ip(
6068 servers
6069 .get(index)
6070 .map_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED), |server| {
6071 server.address
6072 }),
6073 )
6074 }),
6075 },
6076 )?;
6077 } else {
6078 let addresses: [[u8; 4]; MAX_SIGNALING_SERVERS] = (0..MAX_SIGNALING_SERVERS)
6079 .map(|index| match servers.get(index) {
6080 Some(server) => match server.address {
6081 IpAddr::V4(address) => Ok(address.octets()),
6082 IpAddr::V6(_) => Err(CodecError::InvalidValue {
6083 message_id: id::SERVER_RES,
6084 field: "IP address family for pre-v17 protocol",
6085 value: 1,
6086 }),
6087 },
6088 None => Ok([0; 4]),
6089 })
6090 .collect::<Result<Vec<_>, _>>()?
6091 .try_into()
6092 .map_err(|_| CodecError::InvalidValue {
6093 message_id: id::SERVER_RES,
6094 field: "server address array",
6095 value: servers.len() as u64,
6096 })?;
6097 p = encode(
6098 id::SERVER_RES,
6099 &WireServerResponseV3 {
6100 names,
6101 ports,
6102 addresses,
6103 },
6104 )?;
6105 }
6106 id::SERVER_RES
6107 }
6108 Self::TimeDate {
6109 year,
6110 month,
6111 weekday,
6112 day,
6113 hour,
6114 minute,
6115 second,
6116 milliseconds,
6117 unix_seconds,
6118 } => {
6119 p = encode(
6120 id::DEFINE_TIME_DATE,
6121 &WireTimeDate {
6122 year: *year,
6123 month: *month,
6124 weekday: *weekday,
6125 day: *day,
6126 hour: *hour,
6127 minute: *minute,
6128 second: *second,
6129 milliseconds: *milliseconds,
6130 unix_seconds: *unix_seconds,
6131 },
6132 )?;
6133 id::DEFINE_TIME_DATE
6134 }
6135 Self::SoftKeyTemplate { actions } => {
6136 const LABELS: [u16; 32] = [
6140 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 202, 65, 67, 63,
6141 79, 78, 54, 62, 77, 80, 88, 60, 0, 201,
6142 ];
6143 let mut available = [false; 32];
6144 for action in actions {
6145 let value = action.wire_value();
6146 if !action.is_known() || value == 0 || value > available.len() as u32 {
6147 return Err(CodecError::InvalidDefinition(format!(
6148 "soft-key template contains unknown action {value}"
6149 )));
6150 }
6151 let slot = value as usize - 1;
6152 if std::mem::replace(&mut available[slot], true) {
6153 return Err(CodecError::InvalidDefinition(format!(
6154 "soft-key template repeats action {value}"
6155 )));
6156 }
6157 }
6158 p = encode(
6159 id::SOFT_KEY_TEMPLATE_RES,
6160 &WireSoftKeyTemplate {
6161 offset: 0,
6162 count: 32,
6163 total: 32,
6164 definitions: std::array::from_fn(|index| {
6165 if !available[index] {
6166 return WireSoftKeyDefinition {
6167 label: [0; 16],
6168 event: 0,
6169 };
6170 }
6171 let label = LABELS[index];
6172 let mut encoded = [0; 16];
6173 if label == 201 {
6174 encoded[..4].copy_from_slice(b"Dial");
6175 } else if label != 0 {
6176 encoded[0] = 0x80;
6177 encoded[1] = label as u8;
6178 }
6179 WireSoftKeyDefinition {
6180 label: encoded,
6181 event: index as u32 + 1,
6182 }
6183 }),
6184 },
6185 )?;
6186 id::SOFT_KEY_TEMPLATE_RES
6187 }
6188 Self::SoftKeySet { profile } => {
6189 let definitions = (0_u32..16)
6190 .map(KeyMode::from)
6191 .map(|mode| profile.actions(mode))
6192 .map(|actions| {
6193 let mut indexes = [0_u8; 16];
6194 let mut info = [0_u16; 16];
6195 for (slot, action) in actions.iter().copied().enumerate() {
6196 let template = action.wire_value() as u8;
6197 indexes[slot] = template;
6198 info[slot] = u16::from(template) + 300;
6199 }
6200 WireSoftKeySetDefinition {
6201 template_indexes: indexes,
6202 info,
6203 }
6204 })
6205 .collect();
6206 p = encode(
6207 id::SOFT_KEY_SET_RES,
6208 &WireSoftKeySet {
6209 offset: 0,
6210 count: 16,
6211 total: 16,
6212 sets: definitions,
6213 },
6214 )?;
6215 id::SOFT_KEY_SET_RES
6216 }
6217 Self::SelectSoftKeys {
6218 line_instance,
6219 call_reference,
6220 set,
6221 valid_mask,
6222 } => {
6223 p = encode(
6224 id::SELECT_SOFT_KEYS,
6225 &WireSelectSoftKeys {
6226 line_instance: *line_instance,
6227 call_reference: *call_reference,
6228 set: set.wire_value(),
6229 valid_mask: *valid_mask,
6230 },
6231 )?;
6232 id::SELECT_SOFT_KEYS
6233 }
6234 Self::CallState {
6235 state,
6236 line_instance,
6237 call_reference,
6238 } => {
6239 p = encode(
6240 id::CALL_STATE,
6241 &WireCallState {
6242 state: state.wire_value(),
6243 line_instance: *line_instance,
6244 call_reference: *call_reference,
6245 visibility: 0,
6246 precedence: call_state_precedence(*state),
6247 domain: 0,
6248 },
6249 )?;
6250 id::CALL_STATE
6251 }
6252 Self::CallInfo {
6253 info,
6254 line_instance,
6255 call_reference,
6256 } => {
6257 if session.uses_dynamic_general_ui() {
6258 p = encode_dynamic_call_info(info, *line_instance, *call_reference, protocol)?;
6259 id::CALL_INFO_DYNAMIC
6260 } else {
6261 p = encode(
6262 id::CALL_INFO,
6263 &WireCallInfo {
6264 calling_name: WireFixedText::new(
6265 id::CALL_INFO,
6266 "calling name",
6267 &info.calling_name,
6268 )?,
6269 calling_number: WireFixedText::new(
6270 id::CALL_INFO,
6271 "calling number",
6272 &info.calling_number,
6273 )?,
6274 called_name: WireFixedText::new(
6275 id::CALL_INFO,
6276 "called name",
6277 &info.called_name,
6278 )?,
6279 called_number: WireFixedText::new(
6280 id::CALL_INFO,
6281 "called number",
6282 &info.called_number,
6283 )?,
6284 line_instance: *line_instance,
6285 call_reference: *call_reference,
6286 call_type: match info.direction {
6287 crate::types::CallDirection::Inbound => 1,
6288 crate::types::CallDirection::Outbound => 2,
6289 },
6290 original_called_name: WireFixedText::new(
6291 id::CALL_INFO,
6292 "original called name",
6293 &info.original_called_name,
6294 )?,
6295 original_called_number: WireFixedText::new(
6296 id::CALL_INFO,
6297 "original called number",
6298 &info.original_called_number,
6299 )?,
6300 last_redirecting_name: WireFixedText::new(
6301 id::CALL_INFO,
6302 "last redirecting name",
6303 &info.last_redirecting_name,
6304 )?,
6305 last_redirecting_number: WireFixedText::new(
6306 id::CALL_INFO,
6307 "last redirecting number",
6308 &info.last_redirecting_number,
6309 )?,
6310 original_redirect_reason: info.original_redirect_reason,
6311 last_redirect_reason: info.last_redirect_reason,
6312 voice_mailboxes: std::array::from_fn(|_| {
6313 WireFixedText::new(id::CALL_INFO, "voice mailbox", "").unwrap()
6314 }),
6315 call_instance: 1,
6316 security_status: 0,
6317 party_restrictions: info.party_restrictions,
6318 },
6319 )?;
6320 id::CALL_INFO
6321 }
6322 }
6323 Self::DisplayPrompt {
6324 timeout_seconds,
6325 text,
6326 line_instance,
6327 call_reference,
6328 } => {
6329 if session.uses_dynamic_general_ui() {
6330 p = encode(
6331 id::DISPLAY_DYNAMIC_PROMPT_STATUS,
6332 &WireDynamicPromptHeader {
6333 timeout_seconds: *timeout_seconds,
6334 line_instance: *line_instance,
6335 call_reference: *call_reference,
6336 },
6337 )?;
6338 push_dynamic_text(
6339 &mut p,
6340 id::DISPLAY_DYNAMIC_PROMPT_STATUS,
6341 "prompt",
6342 text,
6343 96,
6344 )?;
6345 pad_dynamic_payload(&mut p);
6346 id::DISPLAY_DYNAMIC_PROMPT_STATUS
6347 } else {
6348 p = encode(
6349 id::DISPLAY_PROMPT_STATUS,
6350 &WirePromptStatus {
6351 timeout_seconds: *timeout_seconds,
6352 text: WireFixedText::new(id::DISPLAY_PROMPT_STATUS, "prompt", text)?,
6353 line_instance: *line_instance,
6354 call_reference: *call_reference,
6355 },
6356 )?;
6357 id::DISPLAY_PROMPT_STATUS
6358 }
6359 }
6360 Self::ClearPrompt {
6361 line_instance,
6362 call_reference,
6363 } => {
6364 p = encode(
6365 id::CLEAR_PROMPT_STATUS,
6366 &WireLineCall {
6367 line_instance: *line_instance,
6368 call_reference: *call_reference,
6369 },
6370 )?;
6371 id::CLEAR_PROMPT_STATUS
6372 }
6373 Self::DisplayNotify {
6374 timeout_seconds,
6375 text,
6376 } => {
6377 if session.uses_dynamic_general_ui() {
6378 p = encode(
6379 id::DISPLAY_DYNAMIC_NOTIFY,
6380 &WireDynamicNotifyHeader {
6381 timeout_seconds: *timeout_seconds,
6382 },
6383 )?;
6384 push_dynamic_text(
6385 &mut p,
6386 id::DISPLAY_DYNAMIC_NOTIFY,
6387 "notification",
6388 text,
6389 96,
6390 )?;
6391 pad_dynamic_payload(&mut p);
6392 id::DISPLAY_DYNAMIC_NOTIFY
6393 } else {
6394 p = encode(
6395 id::DISPLAY_NOTIFY,
6396 &WireNotify {
6397 timeout_seconds: *timeout_seconds,
6398 text: WireFixedText::new(id::DISPLAY_NOTIFY, "notification", text)?,
6399 },
6400 )?;
6401 id::DISPLAY_NOTIFY
6402 }
6403 }
6404 Self::ClearNotify => id::CLEAR_NOTIFY,
6405 Self::DisplayPriorityNotify {
6406 timeout_seconds,
6407 priority,
6408 text,
6409 } => {
6410 if session.uses_dynamic_general_ui() {
6411 p = encode(
6412 id::DISPLAY_DYNAMIC_PRIORITY_NOTIFY,
6413 &WireDynamicPriorityNotifyHeader {
6414 timeout_seconds: *timeout_seconds,
6415 priority: priority.wire_value(),
6416 },
6417 )?;
6418 push_dynamic_text(
6419 &mut p,
6420 id::DISPLAY_DYNAMIC_PRIORITY_NOTIFY,
6421 "notification",
6422 text,
6423 96,
6424 )?;
6425 pad_dynamic_payload(&mut p);
6426 id::DISPLAY_DYNAMIC_PRIORITY_NOTIFY
6427 } else {
6428 p = encode(
6429 id::DISPLAY_PRIORITY_NOTIFY,
6430 &WirePriorityNotify {
6431 timeout_seconds: *timeout_seconds,
6432 priority: priority.wire_value(),
6433 text: WireFixedText::new(
6434 id::DISPLAY_PRIORITY_NOTIFY,
6435 "notification",
6436 text,
6437 )?,
6438 },
6439 )?;
6440 id::DISPLAY_PRIORITY_NOTIFY
6441 }
6442 }
6443 Self::ClearPriorityNotify { priority } => {
6444 p = encode(
6445 id::CLEAR_PRIORITY_NOTIFY,
6446 &WireOneWord {
6447 value: priority.wire_value(),
6448 },
6449 )?;
6450 id::CLEAR_PRIORITY_NOTIFY
6451 }
6452 Self::NotifyDtmfTone(message) | Self::SendDtmfTone(message) => {
6453 let message_id = if matches!(self, Self::NotifyDtmfTone(_)) {
6454 id::NOTIFY_DTMF_TONE
6455 } else {
6456 id::SEND_DTMF_TONE
6457 };
6458 p = encode(
6459 message_id,
6460 &WireDtmfToneControl {
6461 tone: message.tone.wire_value(),
6462 conference_id: message.conference_id.get(),
6463 passthrough_party_id: message.passthrough_party_id,
6464 },
6465 )?;
6466 message_id
6467 }
6468 Self::StartAnnouncement {
6469 announcements,
6470 end_of_ack,
6471 conference_id,
6472 matrix_conference_party_ids,
6473 hearing_conference_party_mask,
6474 play_mode,
6475 } => {
6476 if announcements.len() > 32 {
6477 return Err(CodecError::CountTooLarge {
6478 message_id: id::START_ANNOUNCEMENT,
6479 field: "announcements",
6480 count: announcements.len(),
6481 maximum: 32,
6482 });
6483 }
6484 if matrix_conference_party_ids.len() > 16 {
6485 return Err(CodecError::CountTooLarge {
6486 message_id: id::START_ANNOUNCEMENT,
6487 field: "matrix conference party identifiers",
6488 count: matrix_conference_party_ids.len(),
6489 maximum: 16,
6490 });
6491 }
6492 let mut wire_announcements = [WireAnnouncementEntry::default(); 32];
6493 for (wire, entry) in wire_announcements.iter_mut().zip(announcements) {
6494 *wire = WireAnnouncementEntry {
6495 locale: entry.locale,
6496 country: entry.country,
6497 tone: entry.tone.wire_value(),
6498 };
6499 }
6500 let mut wire_party_ids = [0; 16];
6501 wire_party_ids[..matrix_conference_party_ids.len()]
6502 .copy_from_slice(matrix_conference_party_ids);
6503 p = encode(
6504 id::START_ANNOUNCEMENT,
6505 &WireStartAnnouncement {
6506 announcements: wire_announcements,
6507 end_of_ack: *end_of_ack,
6508 conference_id: *conference_id,
6509 matrix_conference_party_ids: wire_party_ids,
6510 hearing_conference_party_mask: *hearing_conference_party_mask,
6511 play_mode: *play_mode,
6512 },
6513 )?;
6514 id::START_ANNOUNCEMENT
6515 }
6516 Self::StopAnnouncement { conference_id } => {
6517 p = encode(
6518 id::STOP_ANNOUNCEMENT,
6519 &WireOneWord {
6520 value: *conference_id,
6521 },
6522 )?;
6523 id::STOP_ANNOUNCEMENT
6524 }
6525 Self::AnnouncementFinish {
6526 conference_id,
6527 play_status,
6528 } => {
6529 p = encode(
6530 id::ANNOUNCEMENT_FINISH,
6531 &WireAnnouncementFinish {
6532 conference_id: *conference_id,
6533 play_status: *play_status,
6534 },
6535 )?;
6536 id::ANNOUNCEMENT_FINISH
6537 }
6538 Self::ClearConference {
6539 conference_id,
6540 service_number,
6541 } => {
6542 p = encode(
6543 id::CLEAR_CONFERENCE,
6544 &WireCallParty {
6545 call_reference: conference_id.get(),
6546 passthrough_party_id: *service_number,
6547 },
6548 )?;
6549 id::CLEAR_CONFERENCE
6550 }
6551 Self::CreateConferenceRequest(request) => {
6552 p = encode(
6553 id::CREATE_CONFERENCE_REQ,
6554 &WireCreateConferenceRequest {
6555 conference_id: request.conference_id.get(),
6556 reserved_participants: request.reserved_participants,
6557 resource_type: request.resource_type.wire_value(),
6558 application_id: request.application_id.get(),
6559 application_conference_id: WireFixedText::new(
6560 id::CREATE_CONFERENCE_REQ,
6561 "application conference ID",
6562 &request.application_conference_id,
6563 )?,
6564 application_data: WireFixedText::new(
6565 id::CREATE_CONFERENCE_REQ,
6566 "application data",
6567 &request.application_data,
6568 )?,
6569 data_length: validate_conference_data_for_encode(
6570 id::CREATE_CONFERENCE_REQ,
6571 &request.passthrough_data,
6572 )?,
6573 passthrough_data: request.passthrough_data.clone(),
6574 },
6575 )?;
6576 id::CREATE_CONFERENCE_REQ
6577 }
6578 Self::DeleteConferenceRequest { conference_id } => {
6579 p = encode(
6580 id::DELETE_CONFERENCE_REQ,
6581 &WireOneWord {
6582 value: conference_id.get(),
6583 },
6584 )?;
6585 id::DELETE_CONFERENCE_REQ
6586 }
6587 Self::ModifyConferenceRequest(request) => {
6588 p = encode(
6589 id::MODIFY_CONFERENCE_REQ,
6590 &WireModifyConferenceRequest {
6591 conference_id: request.conference_id.get(),
6592 reserved_participants: request.reserved_participants,
6593 application_id: request.application_id.get(),
6594 application_conference_id: WireFixedText::new(
6595 id::MODIFY_CONFERENCE_REQ,
6596 "application conference ID",
6597 &request.application_conference_id,
6598 )?,
6599 application_data: WireFixedText::new(
6600 id::MODIFY_CONFERENCE_REQ,
6601 "application data",
6602 &request.application_data,
6603 )?,
6604 data_length: validate_conference_data_for_encode(
6605 id::MODIFY_CONFERENCE_REQ,
6606 &request.passthrough_data,
6607 )?,
6608 passthrough_data: request.passthrough_data.clone(),
6609 },
6610 )?;
6611 id::MODIFY_CONFERENCE_REQ
6612 }
6613 Self::AuditConferenceRequest => id::AUDIT_CONFERENCE_REQ,
6614 Self::AddParticipantRequest(request) => {
6615 p = encode(
6616 id::ADD_PARTICIPANT_REQ,
6617 &encode_participant_request(
6618 id::ADD_PARTICIPANT_REQ,
6619 request.conference_id,
6620 &request.participant,
6621 )?,
6622 )?;
6623 id::ADD_PARTICIPANT_REQ
6624 }
6625 Self::DropParticipantRequest {
6626 conference_id,
6627 call_reference,
6628 } => {
6629 p = encode(
6630 id::DROP_PARTICIPANT_REQ,
6631 &WireCallParty {
6632 call_reference: conference_id.get(),
6633 passthrough_party_id: call_reference.get(),
6634 },
6635 )?;
6636 id::DROP_PARTICIPANT_REQ
6637 }
6638 Self::AuditParticipantRequest { conference_id } => {
6639 p = encode(
6640 id::AUDIT_PARTICIPANT_REQ,
6641 &WireOneWord {
6642 value: conference_id.get(),
6643 },
6644 )?;
6645 id::AUDIT_PARTICIPANT_REQ
6646 }
6647 Self::ChangeParticipantRequest(request) => {
6648 p = encode(
6649 id::CHANGE_PARTICIPANT_REQ,
6650 &encode_participant_request(
6651 id::CHANGE_PARTICIPANT_REQ,
6652 request.conference_id,
6653 &request.participant,
6654 )?,
6655 )?;
6656 id::CHANGE_PARTICIPANT_REQ
6657 }
6658 Self::StopMultimediaTransmission(message)
6659 | Self::CloseMultimediaReceiveChannel(message) => {
6660 let message_id = if matches!(self, Self::StopMultimediaTransmission(_)) {
6661 id::STOP_MULTIMEDIA_TRANSMISSION
6662 } else {
6663 id::CLOSE_MULTIMEDIA_RECEIVE_CHANNEL
6664 };
6665 p = encode(
6666 message_id,
6667 &WireMultimediaStreamControl {
6668 conference_id: message.conference_id.get(),
6669 passthrough_party_id: message.passthrough_party_id.get(),
6670 call_reference: message.call_reference.get(),
6671 port_handling_flag: message.port_handling_flag,
6672 },
6673 )?;
6674 message_id
6675 }
6676 Self::FlowControlCommand(message) | Self::FlowControlNotify(message) => {
6677 let message_id = if matches!(self, Self::FlowControlCommand(_)) {
6678 id::FLOW_CONTROL_COMMAND
6679 } else {
6680 id::FLOW_CONTROL_NOTIFY
6681 };
6682 p = encode(
6683 message_id,
6684 &WireVideoFlowControl {
6685 conference_id: message.conference_id.get(),
6686 passthrough_party_id: message.passthrough_party_id.get(),
6687 call_reference: message.call_reference.get(),
6688 maximum_bit_rate: message.maximum_bit_rate,
6689 },
6690 )?;
6691 message_id
6692 }
6693 Self::VideoDisplayCommand {
6694 conference_id,
6695 call_reference,
6696 layout_id,
6697 } => {
6698 p = encode(
6699 id::VIDEO_DISPLAY_COMMAND,
6700 &WireVideoDisplayCommand {
6701 conference_id: conference_id.get(),
6702 call_reference: call_reference.get(),
6703 layout_id: *layout_id,
6704 },
6705 )?;
6706 id::VIDEO_DISPLAY_COMMAND
6707 }
6708 Self::ActivateCallPlane { line_instance } => {
6709 p = encode(
6710 id::ACTIVATE_CALL_PLANE,
6711 &WireOneWord {
6712 value: *line_instance,
6713 },
6714 )?;
6715 id::ACTIVATE_CALL_PLANE
6716 }
6717 Self::DeactivateCallPlane => id::DEACTIVATE_CALL_PLANE,
6718 Self::BackspaceResponse {
6719 line_instance,
6720 call_reference,
6721 } => {
6722 p = encode(
6723 id::BACKSPACE_RESPONSE,
6724 &WireLineCall {
6725 line_instance: *line_instance,
6726 call_reference: *call_reference,
6727 },
6728 )?;
6729 id::BACKSPACE_RESPONSE
6730 }
6731 Self::RegisterTokenAck => id::REGISTER_TOKEN_ACK,
6732 Self::RegisterTokenReject { backoff_seconds } => {
6733 p = encode(
6734 id::REGISTER_TOKEN_REJECT,
6735 &WireOneWord {
6736 value: *backoff_seconds,
6737 },
6738 )?;
6739 id::REGISTER_TOKEN_REJECT
6740 }
6741 Self::SetRinger {
6742 mode,
6743 duration,
6744 line_instance,
6745 call_reference,
6746 } => {
6747 p = encode(
6748 id::SET_RINGER,
6749 &WireModeLineCall {
6750 mode: mode.wire_value(),
6751 duration: duration.wire_value(),
6752 line_instance: *line_instance,
6753 call_reference: *call_reference,
6754 },
6755 )?;
6756 id::SET_RINGER
6757 }
6758 Self::SetLamp {
6759 stimulus,
6760 instance,
6761 mode,
6762 } => {
6763 p = encode(
6764 id::SET_LAMP,
6765 &WireLampState {
6766 stimulus: stimulus.wire_value(),
6767 instance: *instance,
6768 mode: mode.wire_value(),
6769 },
6770 )?;
6771 id::SET_LAMP
6772 }
6773 Self::StartTone {
6774 tone,
6775 direction,
6776 line_instance,
6777 call_reference,
6778 } => {
6779 p = encode(
6780 id::START_TONE,
6781 &WireToneLineCall {
6782 tone: tone.wire_value(),
6783 direction: direction.wire_value(),
6784 line_instance: *line_instance,
6785 call_reference: *call_reference,
6786 },
6787 )?;
6788 id::START_TONE
6789 }
6790 Self::StopTone {
6791 line_instance,
6792 call_reference,
6793 } => {
6794 p = if protocol.wire() > 11 {
6795 encode(
6796 id::STOP_TONE,
6797 &WireStopToneV12 {
6798 line_instance: *line_instance,
6799 call_reference: *call_reference,
6800 tone: 0,
6801 },
6802 )?
6803 } else {
6804 encode(
6805 id::STOP_TONE,
6806 &WireLineCall {
6807 line_instance: *line_instance,
6808 call_reference: *call_reference,
6809 },
6810 )?
6811 };
6812 id::STOP_TONE
6813 }
6814 Self::StartMulticastMediaReception(message) => {
6815 p = encode_start_multicast_reception(message, protocol)?;
6816 id::START_MULTICAST_MEDIA_RECEPTION
6817 }
6818 Self::StartMulticastMediaTransmission(message) => {
6819 p = encode_start_multicast_transmission(message, protocol)?;
6820 id::START_MULTICAST_MEDIA_TRANSMISSION
6821 }
6822 Self::StopMulticastMediaReception {
6823 conference_id,
6824 passthrough_party_id,
6825 call_reference,
6826 }
6827 | Self::StopMulticastMediaTransmission {
6828 conference_id,
6829 passthrough_party_id,
6830 call_reference,
6831 } => {
6832 let message_id = if matches!(self, Self::StopMulticastMediaReception { .. }) {
6833 id::STOP_MULTICAST_MEDIA_RECEPTION
6834 } else {
6835 id::STOP_MULTICAST_MEDIA_TRANSMISSION
6836 };
6837 p = encode(
6838 message_id,
6839 &WireStopMulticast {
6840 conference_id: conference_id.get(),
6841 passthrough_party_id: passthrough_party_id.get(),
6842 call_reference: call_reference.get(),
6843 },
6844 )?;
6845 message_id
6846 }
6847 Self::OpenReceiveChannel {
6848 call_reference,
6849 passthrough_party_id,
6850 packet_ms,
6851 codec,
6852 echo_cancellation,
6853 telephone_event_payload,
6854 source_address,
6855 source_port,
6856 encryption,
6857 wire,
6858 } => {
6859 p = encode_open_receive(
6860 *call_reference,
6861 *passthrough_party_id,
6862 OpenReceiveParameters {
6863 packet_ms: *packet_ms,
6864 codec: *codec,
6865 echo_cancellation: *echo_cancellation,
6866 telephone_event_payload: *telephone_event_payload,
6867 source_address: *source_address,
6868 source_port: *source_port,
6869 },
6870 encryption.as_ref(),
6871 wire.as_ref(),
6872 protocol,
6873 )?;
6874 id::OPEN_RECEIVE_CHANNEL
6875 }
6876 Self::CloseReceiveChannel(control) => {
6877 p = encode(
6878 id::CLOSE_RECEIVE_CHANNEL,
6879 &WireAudioStreamControl {
6880 conference_id: control.conference_id.get(),
6881 passthrough_party_id: control.passthrough_party_id.get(),
6882 call_reference: control.call_reference.get(),
6883 port_handling_flag: control.port_handling_flag,
6884 },
6885 )?;
6886 id::CLOSE_RECEIVE_CHANNEL
6887 }
6888 Self::ConnectionStatisticsRequest {
6889 directory_number,
6890 call_reference,
6891 processing,
6892 } => {
6893 if protocol.wire() >= 19 {
6894 p = encode(
6895 id::CONNECTION_STATISTICS_REQ,
6896 &WireConnectionStatisticsRequestV19 {
6897 directory_number: WireFixedText::new(
6898 id::CONNECTION_STATISTICS_REQ,
6899 "directory number",
6900 directory_number,
6901 )?,
6902 alignment: [0; 3],
6903 call_reference: *call_reference,
6904 processing: processing.wire_value(),
6905 },
6906 )?;
6907 } else {
6908 p = encode(
6909 id::CONNECTION_STATISTICS_REQ,
6910 &WireConnectionStatisticsRequestV3 {
6911 directory_number: WireFixedText::new(
6912 id::CONNECTION_STATISTICS_REQ,
6913 "directory number",
6914 directory_number,
6915 )?,
6916 call_reference: *call_reference,
6917 processing: processing.wire_value(),
6918 },
6919 )?;
6920 }
6921 id::CONNECTION_STATISTICS_REQ
6922 }
6923 Self::StartMediaTransmission {
6924 call_reference,
6925 passthrough_party_id,
6926 endpoint,
6927 silence_suppression,
6928 traffic_class,
6929 encryption,
6930 wire,
6931 } => {
6932 p = encode_start_media(
6933 *call_reference,
6934 *passthrough_party_id,
6935 StartMediaParameters {
6936 endpoint: *endpoint,
6937 silence_suppression: *silence_suppression,
6938 traffic_class: *traffic_class,
6939 },
6940 encryption.as_ref(),
6941 wire.as_ref(),
6942 protocol,
6943 )?;
6944 id::START_MEDIA_TRANSMISSION
6945 }
6946 Self::StopMediaTransmission(control) => {
6947 p = encode(
6948 id::STOP_MEDIA_TRANSMISSION,
6949 &WireAudioStreamControl {
6950 conference_id: control.conference_id.get(),
6951 passthrough_party_id: control.passthrough_party_id.get(),
6952 call_reference: control.call_reference.get(),
6953 port_handling_flag: control.port_handling_flag,
6954 },
6955 )?;
6956 id::STOP_MEDIA_TRANSMISSION
6957 }
6958 Self::SetSpeakerMode(mode) => {
6959 p = encode(
6960 id::SET_SPEAKER_MODE,
6961 &WireOneWord {
6962 value: mode.wire_value(),
6963 },
6964 )?;
6965 id::SET_SPEAKER_MODE
6966 }
6967 Self::SetMicrophoneMode(mode) => {
6968 p = encode(
6969 id::SET_MICROPHONE_MODE,
6970 &WireOneWord {
6971 value: mode.wire_value(),
6972 },
6973 )?;
6974 id::SET_MICROPHONE_MODE
6975 }
6976 Self::Reset(reset) => {
6977 p = encode(
6978 id::RESET,
6979 &WireOneWord {
6980 value: reset.wire_value(),
6981 },
6982 )?;
6983 id::RESET
6984 }
6985 Self::DisplayText { text } => {
6986 p = encode(
6987 id::DISPLAY_TEXT,
6988 &WireFixedText::<32>::new(id::DISPLAY_TEXT, "display text", text)?,
6989 )?;
6990 id::DISPLAY_TEXT
6991 }
6992 Self::ClearDisplay => id::CLEAR_DISPLAY,
6993 Self::ForwardStatus {
6994 line_instance,
6995 forward_all,
6996 forward_busy,
6997 forward_no_answer,
6998 } => {
6999 let active = u32::from(
7000 forward_all.is_some() || forward_busy.is_some() || forward_no_answer.is_some(),
7001 );
7002 if protocol.wire() >= 19 {
7003 p = encode(
7004 id::FORWARD_STAT,
7005 &WireForwardStatusV19 {
7006 active,
7007 line_instance: *line_instance,
7008 all_active: u32::from(forward_all.is_some()),
7009 all_number: WireFixedText::new(
7010 id::FORWARD_STAT,
7011 "forward number",
7012 forward_all.as_deref().unwrap_or(""),
7013 )?,
7014 all_alignment: [0; 3],
7015 busy_active: u32::from(forward_busy.is_some()),
7016 busy_number: WireFixedText::new(
7017 id::FORWARD_STAT,
7018 "forward number",
7019 forward_busy.as_deref().unwrap_or(""),
7020 )?,
7021 busy_alignment: [0; 3],
7022 no_answer_active: u32::from(forward_no_answer.is_some()),
7023 no_answer_number: WireFixedText::new(
7024 id::FORWARD_STAT,
7025 "forward number",
7026 forward_no_answer.as_deref().unwrap_or(""),
7027 )?,
7028 no_answer_alignment: [0; 3],
7029 },
7030 )?;
7031 } else {
7032 p = encode(
7033 id::FORWARD_STAT,
7034 &WireForwardStatusV3 {
7035 active,
7036 line_instance: *line_instance,
7037 all_active: u32::from(forward_all.is_some()),
7038 all_number: WireFixedText::new(
7039 id::FORWARD_STAT,
7040 "forward number",
7041 forward_all.as_deref().unwrap_or(""),
7042 )?,
7043 busy_active: u32::from(forward_busy.is_some()),
7044 busy_number: WireFixedText::new(
7045 id::FORWARD_STAT,
7046 "forward number",
7047 forward_busy.as_deref().unwrap_or(""),
7048 )?,
7049 no_answer_active: u32::from(forward_no_answer.is_some()),
7050 no_answer_number: WireFixedText::new(
7051 id::FORWARD_STAT,
7052 "forward number",
7053 forward_no_answer.as_deref().unwrap_or(""),
7054 )?,
7055 },
7056 )?;
7057 }
7058 id::FORWARD_STAT
7059 }
7060 Self::SpeedDialStatus {
7061 instance,
7062 number,
7063 display_name,
7064 } => {
7065 let message_id = if protocol.uses_dynamic_speed_dial_status() {
7066 id::SPEED_DIAL_STAT_DYNAMIC
7067 } else {
7068 id::SPEED_DIAL_STAT
7069 };
7070 p = encode(
7071 message_id,
7072 &WireSpeedDialStatus {
7073 instance: *instance,
7074 number: WireFixedText::new(message_id, "number", number)?,
7075 display_name: WireFixedText::new_station(
7076 message_id,
7077 "display name",
7078 display_name,
7079 legacy_code_page,
7080 )?,
7081 },
7082 )?;
7083 message_id
7084 }
7085 Self::DialedNumber {
7086 number,
7087 line_instance,
7088 call_reference,
7089 } => {
7090 p = if protocol.wire() >= 19 {
7091 encode(
7092 id::DIALED_NUMBER,
7093 &WireDialedNumberV19 {
7094 number: WireFixedText::new(id::DIALED_NUMBER, "dialed number", number)?,
7095 alignment: [0; 3],
7096 line_instance: *line_instance,
7097 call_reference: *call_reference,
7098 },
7099 )?
7100 } else {
7101 encode(
7102 id::DIALED_NUMBER,
7103 &WireDialedNumberV3 {
7104 number: WireFixedText::new(id::DIALED_NUMBER, "dialed number", number)?,
7105 line_instance: *line_instance,
7106 call_reference: *call_reference,
7107 },
7108 )?
7109 };
7110 id::DIALED_NUMBER
7111 }
7112 Self::StartMediaFailureDetection(detection) => {
7113 p = encode(
7114 id::START_MEDIA_FAILURE_DETECTION,
7115 &WireMediaFailureDetection {
7116 conference_id: detection.conference_id.get(),
7117 passthrough_party_id: detection.passthrough_party_id,
7118 packet_millis: detection.packet_millis,
7119 codec: detection.codec.wire_value(),
7120 echo_cancellation: detection.echo_cancellation.wire_value(),
7121 codec_qualifier: detection.codec_qualifier,
7122 call_reference: detection.call_reference.get(),
7123 },
7124 )?;
7125 id::START_MEDIA_FAILURE_DETECTION
7126 }
7127 Self::OpenMultimediaChannel(message) => {
7128 p = encode_open_multimedia(message, protocol)?;
7129 id::OPEN_MULTIMEDIA_CHANNEL
7130 }
7131 Self::StartMultimediaTransmission(message) => {
7132 p = encode_start_multimedia(message, protocol)?;
7133 id::START_MULTIMEDIA_TRANSMISSION
7134 }
7135 Self::MiscellaneousCommand(message) => {
7136 p = encode_miscellaneous_command(message)?;
7137 id::MISCELLANEOUS_COMMAND
7138 }
7139 Self::UserToDeviceData(data) => {
7140 p = encode_user_data(data, id::USER_TO_DEVICE_DATA)?;
7141 id::USER_TO_DEVICE_DATA
7142 }
7143 Self::UserToDeviceDataV1(data) => {
7144 p = encode_user_data_v1(data, id::USER_TO_DEVICE_DATA_V1)?;
7145 id::USER_TO_DEVICE_DATA_V1
7146 }
7147 Self::SubscribeDtmfPayloadRequest(request) => {
7148 p = encode(
7149 id::SUBSCRIBE_DTMF_PAYLOAD_REQ,
7150 &dtmf_payload_request_to_wire(*request),
7151 )?;
7152 id::SUBSCRIBE_DTMF_PAYLOAD_REQ
7153 }
7154 Self::SubscribeDtmfPayloadError(identity) => {
7155 p = encode(
7156 id::SUBSCRIBE_DTMF_PAYLOAD_ERR,
7157 &dtmf_payload_identity_to_wire(*identity),
7158 )?;
7159 id::SUBSCRIBE_DTMF_PAYLOAD_ERR
7160 }
7161 Self::UnsubscribeDtmfPayloadRequest(request) => {
7162 p = encode(
7163 id::UNSUBSCRIBE_DTMF_PAYLOAD_REQ,
7164 &dtmf_payload_request_to_wire(*request),
7165 )?;
7166 id::UNSUBSCRIBE_DTMF_PAYLOAD_REQ
7167 }
7168 Self::UnsubscribeDtmfPayloadError(identity) => {
7169 p = encode(
7170 id::UNSUBSCRIBE_DTMF_PAYLOAD_ERR,
7171 &dtmf_payload_identity_to_wire(*identity),
7172 )?;
7173 id::UNSUBSCRIBE_DTMF_PAYLOAD_ERR
7174 }
7175 Self::FeatureStatus {
7176 instance,
7177 button_type,
7178 label,
7179 state,
7180 } => {
7181 if session.uses_dynamic_feature_status() {
7182 p = encode(
7183 id::FEATURE_STAT_DYNAMIC,
7184 &WireFeatureStatusDynamic {
7185 instance: *instance,
7186 button_type: button_type.wire_value(),
7187 state: *state,
7188 label: WireFixedText::new_station(
7189 id::FEATURE_STAT_DYNAMIC,
7190 "feature label",
7191 label,
7192 legacy_code_page,
7193 )?,
7194 padding: [0; 3],
7195 },
7196 )?;
7197 id::FEATURE_STAT_DYNAMIC
7198 } else {
7199 p = encode(
7200 id::FEATURE_STAT,
7201 &WireFeatureStatus {
7202 instance: *instance,
7203 button_type: button_type.wire_value(),
7204 label: WireFixedText::new_station(
7205 id::FEATURE_STAT,
7206 "feature label",
7207 label,
7208 legacy_code_page,
7209 )?,
7210 state: *state,
7211 },
7212 )?;
7213 id::FEATURE_STAT
7214 }
7215 }
7216 Self::ServiceUrlStatus {
7217 index,
7218 url,
7219 label,
7220 extension_text,
7221 } => {
7222 if protocol < ProtocolVersion::V19 && !extension_text.is_empty() {
7223 return Err(CodecError::InvalidValue {
7224 message_id: id::SERVICE_URL_STAT_DYNAMIC,
7225 field: "service URL extension for this protocol version",
7226 value: extension_text.len() as u64,
7227 });
7228 }
7229 if session.uses_dynamic_general_ui() {
7230 p = encode_dynamic_service_url_status(
7231 *index,
7232 url,
7233 label,
7234 extension_text,
7235 protocol,
7236 legacy_code_page,
7237 )?;
7238 id::SERVICE_URL_STAT_DYNAMIC
7239 } else {
7240 p = encode(
7241 id::SERVICE_URL_STAT,
7242 &WireServiceUrlStatus {
7243 index: *index,
7244 url: WireFixedText::new(id::SERVICE_URL_STAT, "service URL", url)?,
7245 label: WireFixedText::new_station(
7246 id::SERVICE_URL_STAT,
7247 "service label",
7248 label,
7249 legacy_code_page,
7250 )?,
7251 },
7252 )?;
7253 id::SERVICE_URL_STAT
7254 }
7255 }
7256 Self::CallSelectStatus {
7257 status,
7258 call_reference,
7259 line_instance,
7260 } => {
7261 p = encode(
7262 id::CALL_SELECT_STAT,
7263 &WireCallSelectStatus {
7264 status: *status,
7265 call_reference: *call_reference,
7266 line_instance: *line_instance,
7267 },
7268 )?;
7269 id::CALL_SELECT_STAT
7270 }
7271 Self::PortRequest(request) => {
7272 p = if protocol.wire() >= 20 {
7273 encode(
7274 id::PORT_REQUEST,
7275 &WirePortRequestFrom20 {
7276 conference_id: request.conference_id.get(),
7277 call_reference: request.call_reference.get(),
7278 passthrough_party_id: request.passthrough_party_id.get(),
7279 transport: request.transport.wire_value(),
7280 address_type: request
7281 .address_type
7282 .ok_or(CodecError::InvalidValue {
7283 message_id: id::PORT_REQUEST,
7284 field: "address type required from protocol 20",
7285 value: 0,
7286 })?
7287 .wire_value(),
7288 media_type: request
7289 .media_type
7290 .ok_or(CodecError::InvalidValue {
7291 message_id: id::PORT_REQUEST,
7292 field: "media type required from protocol 20",
7293 value: 0,
7294 })?
7295 .wire_value(),
7296 },
7297 )?
7298 } else {
7299 encode(
7300 id::PORT_REQUEST,
7301 &WirePortRequestPre20 {
7302 conference_id: request.conference_id.get(),
7303 call_reference: request.call_reference.get(),
7304 passthrough_party_id: request.passthrough_party_id.get(),
7305 transport: request.transport.wire_value(),
7306 },
7307 )?
7308 };
7309 id::PORT_REQUEST
7310 }
7311 Self::PortClose(close) => {
7312 p = if protocol.wire() >= 20 {
7313 encode(
7314 id::PORT_CLOSE,
7315 &WirePortCloseFrom20 {
7316 conference_id: close.conference_id.get(),
7317 call_reference: close.call_reference.get(),
7318 passthrough_party_id: close.passthrough_party_id.get(),
7319 media_type: close
7320 .media_type
7321 .ok_or(CodecError::InvalidValue {
7322 message_id: id::PORT_CLOSE,
7323 field: "media type required from protocol 20",
7324 value: 0,
7325 })?
7326 .wire_value(),
7327 },
7328 )?
7329 } else {
7330 encode(
7331 id::PORT_CLOSE,
7332 &WirePortClosePre20 {
7333 conference_id: close.conference_id.get(),
7334 call_reference: close.call_reference.get(),
7335 passthrough_party_id: close.passthrough_party_id.get(),
7336 },
7337 )?
7338 };
7339 id::PORT_CLOSE
7340 }
7341 Self::SubscriptionStatus {
7342 transaction_id,
7343 feature_id,
7344 timer_seconds,
7345 cause,
7346 } => {
7347 p = encode(
7348 id::SUBSCRIPTION_STAT,
7349 &WireSubscriptionStatus {
7350 transaction_id: *transaction_id,
7351 feature_id: *feature_id,
7352 timer_seconds: *timer_seconds,
7353 cause: cause.wire_value(),
7354 },
7355 )?;
7356 id::SUBSCRIPTION_STAT
7357 }
7358 Self::Notification {
7359 transaction_id,
7360 feature_id,
7361 status,
7362 text,
7363 } => {
7364 p = encode(
7365 id::NOTIFICATION,
7366 &WireNotification {
7367 transaction_id: *transaction_id,
7368 feature_id: *feature_id,
7369 status: status.wire_value(),
7370 text: WireFixedText::new(id::NOTIFICATION, "notification", text)?,
7371 },
7372 )?;
7373 id::NOTIFICATION
7374 }
7375 Self::CallHistoryDisposition {
7376 disposition,
7377 line_instance,
7378 call_reference,
7379 } => {
7380 p = encode(
7381 id::CALL_HISTORY_DISPOSITION,
7382 &WireCallHistoryDisposition {
7383 disposition: disposition.wire_value(),
7384 line_instance: *line_instance,
7385 call_reference: *call_reference,
7386 },
7387 )?;
7388 id::CALL_HISTORY_DISPOSITION
7389 }
7390 Self::CallCountResponse => id::CALL_COUNT_RES,
7391 Self::RecordingStatus {
7392 call_reference,
7393 active,
7394 } => {
7395 p = encode(
7396 id::RECORDING_STATUS,
7397 &WireRecordingStatus {
7398 call_reference: *call_reference,
7399 active: u32::from(*active),
7400 },
7401 )?;
7402 id::RECORDING_STATUS
7403 }
7404 Self::KnownOpaque(message) => {
7405 ensure_preserve_only(message.id)?;
7406 return Ok((
7407 message.id.wire_value(),
7408 message.payload.as_bytes().to_vec(),
7409 message.protocol_version,
7410 ));
7411 }
7412 Self::Unknown(message) => {
7413 return Ok((
7414 message.message_id,
7415 message.payload.clone(),
7416 message.protocol_version,
7417 ));
7418 }
7419 };
7420 pad_typed_payload(id, &mut p);
7421 Ok((id, p, protocol.wire()))
7422 }
7423}
7424
7425fn qos_flow_from_wire(flow: WireQosFlow, message_id: u32) -> Result<QosFlow, CodecError> {
7426 Ok(QosFlow {
7427 conference_id: flow.conference_id.into(),
7428 call_reference: flow.call_reference.into(),
7429 passthrough_party_id: flow.passthrough_party_id.into(),
7430 address: Ipv4Addr::from(flow.address),
7431 port: decode_port(flow.port, message_id, "QoS media port")?,
7432 })
7433}
7434
7435fn qos_flow_to_wire(flow: QosFlow) -> WireQosFlow {
7436 WireQosFlow {
7437 conference_id: flow.conference_id.get(),
7438 call_reference: flow.call_reference.get(),
7439 passthrough_party_id: flow.passthrough_party_id.get(),
7440 address: flow.address.octets(),
7441 port: u32::from(flow.port),
7442 }
7443}
7444
7445fn qos_application_from_wire(
7446 value: WireQosApplicationIdentifier,
7447) -> Result<QosApplicationIdentifier, CodecError> {
7448 Ok(QosApplicationIdentifier {
7449 vendor_id: value.vendor_id.text()?,
7450 version: value.version.text()?,
7451 application_name: value.application_name.text()?,
7452 sub_application_id: value.sub_application_id.text()?,
7453 })
7454}
7455
7456fn qos_application_to_wire(
7457 message_id: u32,
7458 value: &QosApplicationIdentifier,
7459) -> Result<WireQosApplicationIdentifier, CodecError> {
7460 Ok(WireQosApplicationIdentifier {
7461 vendor_id: WireFixedText::new(message_id, "QoS vendor ID", &value.vendor_id)?,
7462 version: WireFixedText::new(message_id, "QoS application version", &value.version)?,
7463 application_name: WireFixedText::new(
7464 message_id,
7465 "QoS application name",
7466 &value.application_name,
7467 )?,
7468 sub_application_id: WireFixedText::new(
7469 message_id,
7470 "QoS sub-application ID",
7471 &value.sub_application_id,
7472 )?,
7473 })
7474}
7475
7476fn qos_traffic(
7477 compression_type: u32,
7478 average_bit_rate: u32,
7479 burst_size: u32,
7480 peak_rate: u32,
7481) -> QosTrafficSpecification {
7482 QosTrafficSpecification {
7483 codec: Codec::from(compression_type),
7484 average_bit_rate,
7485 burst_size,
7486 peak_rate,
7487 }
7488}
7489
7490fn reject_non_station_route(
7491 message_id: u32,
7492 expected_route: MessageRoute,
7493 expected: &'static str,
7494) -> Result<(), CodecError> {
7495 if let Some(actual) = MessageId::from(message_id).route()
7496 && actual != expected_route
7497 {
7498 return Err(CodecError::UnexpectedRoute {
7499 message_id,
7500 actual,
7501 expected,
7502 });
7503 }
7504 Ok(())
7505}
7506
7507impl ControlMessage {
7508 pub fn decode(frame: Frame, protocol: ProtocolVersion) -> Result<Self, CodecError> {
7512 let message_id = MessageId::from(frame.message_id);
7513 let route = message_id.route().ok_or(CodecError::InvalidValue {
7514 message_id: frame.message_id,
7515 field: "known control message identifier",
7516 value: u64::from(frame.message_id),
7517 })?;
7518 if matches!(
7519 route,
7520 MessageRoute::StationToControl | MessageRoute::ControlToStation
7521 ) {
7522 return Err(CodecError::UnexpectedRoute {
7523 message_id: frame.message_id,
7524 actual: route,
7525 expected: "control/service-node or intra-control route",
7526 });
7527 }
7528
7529 let p = &frame.payload;
7530 match frame.message_id {
7531 id::START_SESSION_TRANSMISSION | id::STOP_SESSION_TRANSMISSION => {
7532 let message = decode_session_transmission(p, protocol, frame.message_id)?;
7533 if frame.message_id == id::START_SESSION_TRANSMISSION {
7534 Ok(Self::StartSessionTransmission(message))
7535 } else {
7536 Ok(Self::StopSessionTransmission(message))
7537 }
7538 }
7539 id::QOS_RESERVATION_NOTIFY => {
7540 let value: WireQosReservationNotify = decode(frame.message_id, p)?;
7541 Ok(Self::QosReservationNotify {
7542 flow: qos_flow_from_wire(value.flow, frame.message_id)?,
7543 direction: QosDirection::from(value.direction),
7544 })
7545 }
7546 id::QOS_ERROR_NOTIFY => {
7547 let value: WireQosErrorNotify = decode(frame.message_id, p)?;
7548 Ok(Self::QosErrorNotify {
7549 flow: qos_flow_from_wire(value.flow, frame.message_id)?,
7550 direction: QosDirection::from(value.direction),
7551 error_code: QosErrorCode::from(value.error_code),
7552 failure_node: Ipv4Addr::from(value.failure_node),
7553 rsvp_error_code: RsvpErrorCode::from(value.rsvp_error_code),
7554 rsvp_error_subcode: value.rsvp_error_subcode,
7555 rsvp_error_flags: value.rsvp_error_flags,
7556 })
7557 }
7558 id::QOS_LISTEN => {
7559 let value: WireQosListen = decode(frame.message_id, p)?;
7560 Ok(Self::QosListen {
7561 flow: qos_flow_from_wire(value.flow, frame.message_id)?,
7562 reservation_style: QosReservationStyle::from(value.reservation_style),
7563 maximum_retries: value.maximum_retries,
7564 retry_timer: value.retry_timer,
7565 confirmation_required: decode_bool_word(
7566 value.confirmation_required,
7567 frame.message_id,
7568 "QoS confirmation required",
7569 )?,
7570 preemption_priority: value.preemption_priority,
7571 defending_priority: value.defending_priority,
7572 traffic: qos_traffic(
7573 value.compression_type,
7574 value.average_bit_rate,
7575 value.burst_size,
7576 value.peak_rate,
7577 ),
7578 application: qos_application_from_wire(value.application)?,
7579 })
7580 }
7581 id::QOS_PATH => {
7582 let value: WireQosPath = decode(frame.message_id, p)?;
7583 Ok(Self::QosPath {
7584 flow: qos_flow_from_wire(value.flow, frame.message_id)?,
7585 reservation_style: QosReservationStyle::from(value.reservation_style),
7586 maximum_retries: value.maximum_retries,
7587 retry_timer: value.retry_timer,
7588 preemption_priority: value.preemption_priority,
7589 defending_priority: value.defending_priority,
7590 traffic: qos_traffic(
7591 value.compression_type,
7592 value.average_bit_rate,
7593 value.burst_size,
7594 value.peak_rate,
7595 ),
7596 application: qos_application_from_wire(value.application)?,
7597 })
7598 }
7599 id::QOS_TEARDOWN => {
7600 let value: WireQosReservationNotify = decode(frame.message_id, p)?;
7601 Ok(Self::QosTeardown {
7602 flow: qos_flow_from_wire(value.flow, frame.message_id)?,
7603 direction: QosDirection::from(value.direction),
7604 })
7605 }
7606 id::UPDATE_DSCP => {
7607 let value: WireUpdateDscp = decode(frame.message_id, p)?;
7608 let dscp = u8::try_from(value.dscp).map_err(|_| CodecError::InvalidValue {
7609 message_id: frame.message_id,
7610 field: "DSCP",
7611 value: u64::from(value.dscp),
7612 })?;
7613 if dscp > 63 {
7614 return Err(CodecError::InvalidValue {
7615 message_id: frame.message_id,
7616 field: "DSCP",
7617 value: u64::from(dscp),
7618 });
7619 }
7620 Ok(Self::UpdateDscp {
7621 flow: qos_flow_from_wire(value.flow, frame.message_id)?,
7622 dscp,
7623 })
7624 }
7625 id::QOS_MODIFY => {
7626 let value: WireQosModify = decode(frame.message_id, p)?;
7627 Ok(Self::QosModify {
7628 flow: qos_flow_from_wire(value.flow, frame.message_id)?,
7629 direction: QosDirection::from(value.direction),
7630 traffic: qos_traffic(
7631 value.compression_type,
7632 value.average_bit_rate,
7633 value.burst_size,
7634 value.peak_rate,
7635 ),
7636 application: qos_application_from_wire(value.application)?,
7637 })
7638 }
7639 id::MWI_NOTIFICATION => {
7640 let value: WireMessageWaitingNotification = decode(frame.message_id, p)?;
7641 validate_zero_payload(&value.alignment, frame.message_id, 2)?;
7642 Ok(Self::MessageWaitingNotification(
7643 MessageWaitingNotification {
7644 target_number: value.target_number.text()?,
7645 control_number: value.control_number.text()?,
7646 messages_waiting: decode_bool_word(
7647 value.messages_waiting,
7648 frame.message_id,
7649 "messages waiting",
7650 )?,
7651 total_voicemail: MessageWaitingCounts {
7652 new: value.total_voicemail_new,
7653 old: value.total_voicemail_old,
7654 },
7655 priority_voicemail: MessageWaitingCounts {
7656 new: value.priority_voicemail_new,
7657 old: value.priority_voicemail_old,
7658 },
7659 total_fax: MessageWaitingCounts {
7660 new: value.total_fax_new,
7661 old: value.total_fax_old,
7662 },
7663 priority_fax: MessageWaitingCounts {
7664 new: value.priority_fax_new,
7665 old: value.priority_fax_old,
7666 },
7667 },
7668 ))
7669 }
7670 id::MWI_RESPONSE => {
7671 let value: WireMessageWaitingResponse = decode(frame.message_id, p)?;
7672 validate_zero_payload(&value.alignment, frame.message_id, 3)?;
7673 Ok(Self::MessageWaitingResponse {
7674 target_number: value.target_number.text()?,
7675 result: MessageWaitingResult::from(value.result),
7676 })
7677 }
7678 id::MEDIA_RESOURCE_NOTIFICATION
7679 | id::PORT_RESPONSE
7680 | id::CREATE_CONFERENCE_RES
7681 | id::DELETE_CONFERENCE_RES
7682 | id::MODIFY_CONFERENCE_RES
7683 | id::ADD_PARTICIPANT_RES
7684 | id::AUDIT_CONFERENCE_RES
7685 | id::AUDIT_PARTICIPANT_RES => Self::from_client_message(
7686 ClientMessage::decode_using_protocol(frame, protocol.wire())?,
7687 ),
7688 id::CLEAR_CONFERENCE
7689 | id::START_ANNOUNCEMENT
7690 | id::STOP_ANNOUNCEMENT
7691 | id::ANNOUNCEMENT_FINISH
7692 | id::CREATE_CONFERENCE_REQ
7693 | id::DELETE_CONFERENCE_REQ
7694 | id::MODIFY_CONFERENCE_REQ
7695 | id::ADD_PARTICIPANT_REQ
7696 | id::DROP_PARTICIPANT_REQ
7697 | id::AUDIT_CONFERENCE_REQ
7698 | id::AUDIT_PARTICIPANT_REQ
7699 | id::CHANGE_PARTICIPANT_REQ => {
7700 Self::from_server_message(ServerMessage::decode_unchecked(frame, protocol)?)
7701 }
7702 _ => preserve_known_message(frame, message_id).map(Self::KnownOpaque),
7703 }
7704 }
7705
7706 fn from_client_message(message: ClientMessage) -> Result<Self, CodecError> {
7707 Ok(match message {
7708 ClientMessage::MediaResourceNotification(value) => {
7709 Self::MediaResourceNotification(value)
7710 }
7711 ClientMessage::PortResponse(value) => Self::PortResponse(value),
7712 ClientMessage::CreateConferenceResponse(value) => Self::CreateConferenceResponse(value),
7713 ClientMessage::DeleteConferenceResponse {
7714 conference_id,
7715 result,
7716 } => Self::DeleteConferenceResponse {
7717 conference_id,
7718 result,
7719 },
7720 ClientMessage::ModifyConferenceResponse(value) => Self::ModifyConferenceResponse(value),
7721 ClientMessage::AddParticipantResponse(value) => Self::AddParticipantResponse(value),
7722 ClientMessage::AuditConferenceResponse(value) => Self::AuditConferenceResponse(value),
7723 ClientMessage::AuditParticipantResponse(value) => Self::AuditParticipantResponse(value),
7724 _ => {
7725 return Err(CodecError::InvalidValue {
7726 message_id: 0,
7727 field: "control message decoded through station codec",
7728 value: 0,
7729 });
7730 }
7731 })
7732 }
7733
7734 fn from_server_message(message: ServerMessage) -> Result<Self, CodecError> {
7735 Ok(match message {
7736 ServerMessage::ClearConference {
7737 conference_id,
7738 service_number,
7739 } => Self::ClearConference {
7740 conference_id,
7741 service_number,
7742 },
7743 ServerMessage::CreateConferenceRequest(value) => Self::CreateConferenceRequest(value),
7744 ServerMessage::DeleteConferenceRequest { conference_id } => {
7745 Self::DeleteConferenceRequest { conference_id }
7746 }
7747 ServerMessage::ModifyConferenceRequest(value) => Self::ModifyConferenceRequest(value),
7748 ServerMessage::AddParticipantRequest(value) => Self::AddParticipantRequest(value),
7749 ServerMessage::DropParticipantRequest {
7750 conference_id,
7751 call_reference,
7752 } => Self::DropParticipantRequest {
7753 conference_id,
7754 call_reference,
7755 },
7756 ServerMessage::AuditConferenceRequest => Self::AuditConferenceRequest,
7757 ServerMessage::AuditParticipantRequest { conference_id } => {
7758 Self::AuditParticipantRequest { conference_id }
7759 }
7760 ServerMessage::ChangeParticipantRequest(value) => Self::ChangeParticipantRequest(value),
7761 ServerMessage::StartAnnouncement {
7762 announcements,
7763 end_of_ack,
7764 conference_id,
7765 matrix_conference_party_ids,
7766 hearing_conference_party_mask,
7767 play_mode,
7768 } => Self::StartAnnouncement {
7769 announcements,
7770 end_of_ack: EndOfAnnouncementAck::from(end_of_ack),
7771 conference_id,
7772 matrix_conference_party_ids,
7773 hearing_conference_party_mask,
7774 play_mode: AnnouncementPlayMode::from(play_mode),
7775 },
7776 ServerMessage::StopAnnouncement { conference_id } => {
7777 Self::StopAnnouncement { conference_id }
7778 }
7779 ServerMessage::AnnouncementFinish {
7780 conference_id,
7781 play_status,
7782 } => Self::AnnouncementFinish {
7783 conference_id,
7784 play_status: AnnouncementPlayStatus::from(play_status),
7785 },
7786 _ => {
7787 return Err(CodecError::InvalidValue {
7788 message_id: 0,
7789 field: "control message decoded through station codec",
7790 value: 0,
7791 });
7792 }
7793 })
7794 }
7795
7796 pub fn encode(&self, protocol: ProtocolVersion) -> Result<Vec<u8>, CodecError> {
7801 let (message_id, payload, protocol_version) = match self {
7802 Self::StartSessionTransmission(message) | Self::StopSessionTransmission(message) => {
7803 let message_id = if matches!(self, Self::StartSessionTransmission(_)) {
7804 id::START_SESSION_TRANSMISSION
7805 } else {
7806 id::STOP_SESSION_TRANSMISSION
7807 };
7808 (
7809 message_id,
7810 encode_session_transmission(*message, protocol, message_id)?,
7811 protocol.wire(),
7812 )
7813 }
7814 Self::QosReservationNotify { flow, direction } => (
7815 id::QOS_RESERVATION_NOTIFY,
7816 encode(
7817 id::QOS_RESERVATION_NOTIFY,
7818 &WireQosReservationNotify {
7819 flow: qos_flow_to_wire(*flow),
7820 direction: direction.wire_value(),
7821 },
7822 )?,
7823 protocol.wire(),
7824 ),
7825 Self::QosErrorNotify {
7826 flow,
7827 direction,
7828 error_code,
7829 failure_node,
7830 rsvp_error_code,
7831 rsvp_error_subcode,
7832 rsvp_error_flags,
7833 } => (
7834 id::QOS_ERROR_NOTIFY,
7835 encode(
7836 id::QOS_ERROR_NOTIFY,
7837 &WireQosErrorNotify {
7838 flow: qos_flow_to_wire(*flow),
7839 direction: direction.wire_value(),
7840 error_code: error_code.wire_value(),
7841 failure_node: u32::from(*failure_node),
7842 rsvp_error_code: rsvp_error_code.wire_value(),
7843 rsvp_error_subcode: *rsvp_error_subcode,
7844 rsvp_error_flags: *rsvp_error_flags,
7845 },
7846 )?,
7847 protocol.wire(),
7848 ),
7849 Self::QosListen {
7850 flow,
7851 reservation_style,
7852 maximum_retries,
7853 retry_timer,
7854 confirmation_required,
7855 preemption_priority,
7856 defending_priority,
7857 traffic,
7858 application,
7859 } => (
7860 id::QOS_LISTEN,
7861 encode(
7862 id::QOS_LISTEN,
7863 &WireQosListen {
7864 flow: qos_flow_to_wire(*flow),
7865 reservation_style: reservation_style.wire_value(),
7866 maximum_retries: *maximum_retries,
7867 retry_timer: *retry_timer,
7868 confirmation_required: u32::from(*confirmation_required),
7869 preemption_priority: *preemption_priority,
7870 defending_priority: *defending_priority,
7871 compression_type: traffic.codec.wire_value(),
7872 average_bit_rate: traffic.average_bit_rate,
7873 burst_size: traffic.burst_size,
7874 peak_rate: traffic.peak_rate,
7875 application: qos_application_to_wire(id::QOS_LISTEN, application)?,
7876 },
7877 )?,
7878 protocol.wire(),
7879 ),
7880 Self::QosPath {
7881 flow,
7882 reservation_style,
7883 maximum_retries,
7884 retry_timer,
7885 preemption_priority,
7886 defending_priority,
7887 traffic,
7888 application,
7889 } => (
7890 id::QOS_PATH,
7891 encode(
7892 id::QOS_PATH,
7893 &WireQosPath {
7894 flow: qos_flow_to_wire(*flow),
7895 reservation_style: reservation_style.wire_value(),
7896 maximum_retries: *maximum_retries,
7897 retry_timer: *retry_timer,
7898 preemption_priority: *preemption_priority,
7899 defending_priority: *defending_priority,
7900 compression_type: traffic.codec.wire_value(),
7901 average_bit_rate: traffic.average_bit_rate,
7902 burst_size: traffic.burst_size,
7903 peak_rate: traffic.peak_rate,
7904 application: qos_application_to_wire(id::QOS_PATH, application)?,
7905 },
7906 )?,
7907 protocol.wire(),
7908 ),
7909 Self::QosTeardown { flow, direction } => (
7910 id::QOS_TEARDOWN,
7911 encode(
7912 id::QOS_TEARDOWN,
7913 &WireQosReservationNotify {
7914 flow: qos_flow_to_wire(*flow),
7915 direction: direction.wire_value(),
7916 },
7917 )?,
7918 protocol.wire(),
7919 ),
7920 Self::UpdateDscp { flow, dscp } => {
7921 if *dscp > 63 {
7922 return Err(CodecError::InvalidValue {
7923 message_id: id::UPDATE_DSCP,
7924 field: "DSCP",
7925 value: u64::from(*dscp),
7926 });
7927 }
7928 (
7929 id::UPDATE_DSCP,
7930 encode(
7931 id::UPDATE_DSCP,
7932 &WireUpdateDscp {
7933 flow: qos_flow_to_wire(*flow),
7934 dscp: u32::from(*dscp),
7935 },
7936 )?,
7937 protocol.wire(),
7938 )
7939 }
7940 Self::QosModify {
7941 flow,
7942 direction,
7943 traffic,
7944 application,
7945 } => (
7946 id::QOS_MODIFY,
7947 encode(
7948 id::QOS_MODIFY,
7949 &WireQosModify {
7950 flow: qos_flow_to_wire(*flow),
7951 direction: direction.wire_value(),
7952 compression_type: traffic.codec.wire_value(),
7953 average_bit_rate: traffic.average_bit_rate,
7954 burst_size: traffic.burst_size,
7955 peak_rate: traffic.peak_rate,
7956 application: qos_application_to_wire(id::QOS_MODIFY, application)?,
7957 },
7958 )?,
7959 protocol.wire(),
7960 ),
7961 Self::MessageWaitingNotification(value) => (
7962 id::MWI_NOTIFICATION,
7963 encode(
7964 id::MWI_NOTIFICATION,
7965 &WireMessageWaitingNotification {
7966 target_number: WireFixedText::new(
7967 id::MWI_NOTIFICATION,
7968 "MWI target number",
7969 &value.target_number,
7970 )?,
7971 control_number: WireFixedText::new(
7972 id::MWI_NOTIFICATION,
7973 "MWI control number",
7974 &value.control_number,
7975 )?,
7976 alignment: [0; 2],
7977 messages_waiting: u32::from(value.messages_waiting),
7978 total_voicemail_new: value.total_voicemail.new,
7979 total_voicemail_old: value.total_voicemail.old,
7980 priority_voicemail_new: value.priority_voicemail.new,
7981 priority_voicemail_old: value.priority_voicemail.old,
7982 total_fax_new: value.total_fax.new,
7983 total_fax_old: value.total_fax.old,
7984 priority_fax_new: value.priority_fax.new,
7985 priority_fax_old: value.priority_fax.old,
7986 },
7987 )?,
7988 protocol.wire(),
7989 ),
7990 Self::MessageWaitingResponse {
7991 target_number,
7992 result,
7993 } => (
7994 id::MWI_RESPONSE,
7995 encode(
7996 id::MWI_RESPONSE,
7997 &WireMessageWaitingResponse {
7998 target_number: WireFixedText::new(
7999 id::MWI_RESPONSE,
8000 "MWI target number",
8001 target_number,
8002 )?,
8003 alignment: [0; 3],
8004 result: result.wire_value(),
8005 },
8006 )?,
8007 protocol.wire(),
8008 ),
8009 Self::KnownOpaque(message) => {
8010 ensure_preserve_only(message.id)?;
8011 return Frame::new(
8012 message.protocol_version,
8013 message.id.wire_value(),
8014 message.payload.as_bytes().to_vec(),
8015 )
8016 .encode();
8017 }
8018 other => return other.encode_via_existing(protocol),
8019 };
8020 Frame::new(protocol_version, message_id, payload).encode()
8021 }
8022
8023 fn encode_via_existing(&self, protocol: ProtocolVersion) -> Result<Vec<u8>, CodecError> {
8024 match self {
8025 Self::MediaResourceNotification(value) => {
8026 ClientMessage::MediaResourceNotification(value.clone()).encode_unchecked(protocol)
8027 }
8028 Self::PortResponse(value) => {
8029 ClientMessage::PortResponse(value.clone()).encode_unchecked(protocol)
8030 }
8031 Self::CreateConferenceResponse(value) => {
8032 ClientMessage::CreateConferenceResponse(value.clone()).encode_unchecked(protocol)
8033 }
8034 Self::DeleteConferenceResponse {
8035 conference_id,
8036 result,
8037 } => ClientMessage::DeleteConferenceResponse {
8038 conference_id: *conference_id,
8039 result: *result,
8040 }
8041 .encode_unchecked(protocol),
8042 Self::ModifyConferenceResponse(value) => {
8043 ClientMessage::ModifyConferenceResponse(value.clone()).encode_unchecked(protocol)
8044 }
8045 Self::AddParticipantResponse(value) => {
8046 ClientMessage::AddParticipantResponse(value.clone()).encode_unchecked(protocol)
8047 }
8048 Self::AuditConferenceResponse(value) => {
8049 ClientMessage::AuditConferenceResponse(value.clone()).encode_unchecked(protocol)
8050 }
8051 Self::AuditParticipantResponse(value) => {
8052 ClientMessage::AuditParticipantResponse(value.clone()).encode_unchecked(protocol)
8053 }
8054 Self::ClearConference {
8055 conference_id,
8056 service_number,
8057 } => ServerMessage::ClearConference {
8058 conference_id: *conference_id,
8059 service_number: *service_number,
8060 }
8061 .encode_unchecked(protocol),
8062 Self::CreateConferenceRequest(value) => {
8063 ServerMessage::CreateConferenceRequest(value.clone()).encode_unchecked(protocol)
8064 }
8065 Self::DeleteConferenceRequest { conference_id } => {
8066 ServerMessage::DeleteConferenceRequest {
8067 conference_id: *conference_id,
8068 }
8069 .encode_unchecked(protocol)
8070 }
8071 Self::ModifyConferenceRequest(value) => {
8072 ServerMessage::ModifyConferenceRequest(value.clone()).encode_unchecked(protocol)
8073 }
8074 Self::AddParticipantRequest(value) => {
8075 ServerMessage::AddParticipantRequest(value.clone()).encode_unchecked(protocol)
8076 }
8077 Self::DropParticipantRequest {
8078 conference_id,
8079 call_reference,
8080 } => ServerMessage::DropParticipantRequest {
8081 conference_id: *conference_id,
8082 call_reference: *call_reference,
8083 }
8084 .encode_unchecked(protocol),
8085 Self::AuditConferenceRequest => {
8086 ServerMessage::AuditConferenceRequest.encode_unchecked(protocol)
8087 }
8088 Self::AuditParticipantRequest { conference_id } => {
8089 ServerMessage::AuditParticipantRequest {
8090 conference_id: *conference_id,
8091 }
8092 .encode_unchecked(protocol)
8093 }
8094 Self::ChangeParticipantRequest(value) => {
8095 ServerMessage::ChangeParticipantRequest(value.clone()).encode_unchecked(protocol)
8096 }
8097 Self::StartAnnouncement {
8098 announcements,
8099 end_of_ack,
8100 conference_id,
8101 matrix_conference_party_ids,
8102 hearing_conference_party_mask,
8103 play_mode,
8104 } => ServerMessage::StartAnnouncement {
8105 announcements: announcements.clone(),
8106 end_of_ack: end_of_ack.wire_value(),
8107 conference_id: *conference_id,
8108 matrix_conference_party_ids: matrix_conference_party_ids.clone(),
8109 hearing_conference_party_mask: *hearing_conference_party_mask,
8110 play_mode: play_mode.wire_value(),
8111 }
8112 .encode_unchecked(protocol),
8113 Self::StopAnnouncement { conference_id } => ServerMessage::StopAnnouncement {
8114 conference_id: *conference_id,
8115 }
8116 .encode_unchecked(protocol),
8117 Self::AnnouncementFinish {
8118 conference_id,
8119 play_status,
8120 } => ServerMessage::AnnouncementFinish {
8121 conference_id: *conference_id,
8122 play_status: play_status.wire_value(),
8123 }
8124 .encode_unchecked(protocol),
8125 _ => unreachable!("directly encoded control message"),
8126 }
8127 }
8128}
8129
8130const fn call_state_precedence(state: CallState) -> u32 {
8131 match state {
8132 CallState::OffHook | CallState::Proceed | CallState::Connected | CallState::Transfer => 3,
8133 CallState::RingOut => 4,
8134 _ => 2,
8135 }
8136}
8137
8138#[derive(Clone, Copy, Debug)]
8139struct OpenReceiveParameters {
8140 packet_ms: u32,
8141 codec: Codec,
8142 echo_cancellation: EchoCancellation,
8143 telephone_event_payload: u8,
8144 source_address: IpAddr,
8145 source_port: u16,
8146}
8147
8148fn encode_open_receive(
8149 call: u32,
8150 party: u32,
8151 parameters: OpenReceiveParameters,
8152 encryption: Option<&MediaEncryption>,
8153 wire: Option<&OpenReceiveChannelWire>,
8154 protocol: ProtocolVersion,
8155) -> Result<Vec<u8>, CodecError> {
8156 let OpenReceiveParameters {
8157 packet_ms,
8158 codec,
8159 echo_cancellation,
8160 telephone_event_payload,
8161 source_address,
8162 source_port,
8163 } = parameters;
8164 let conference_id = wire.map_or(call, |value| value.conference_id);
8165 let g723_bitrate = wire.map_or(0, |value| value.g723_bitrate);
8166 let stream_passthrough_id = wire.map_or(0, |value| value.stream_passthrough_id);
8167 let associated_stream_id = wire.map_or(0, |value| value.associated_stream_id);
8168 let dtmf_type = wire.map_or(10, |value| value.dtmf_type);
8169 let mixing_mode = wire.map_or(0, |value| value.mixing_mode);
8170 let direction = wire.map_or(1, |value| value.direction);
8171 let requested_address_type = wire.map_or_else(
8172 || u32::from(matches!(source_address, IpAddr::V6(_))),
8173 |value| value.requested_address_type,
8174 );
8175 let encryption = WireEncryptionInfo::from_public(encryption);
8176 let base_v17 = WireOpenReceiveV17 {
8177 conference_id,
8178 passthrough_party_id: party,
8179 packet_millis: packet_ms,
8180 codec: codec.skinny(),
8181 vad: echo_cancellation.wire_value(),
8182 g723_bitrate,
8183 call_reference: call,
8184 encryption,
8185 stream_passthrough_id,
8186 associated_stream_id,
8187 rfc2833_payload: u32::from(telephone_event_payload),
8188 dtmf_type,
8189 mixing_mode,
8190 direction,
8191 remote: WireExtendedAddress::from_ip(source_address),
8192 remote_port: u32::from(source_port),
8193 requested_address_type,
8194 };
8195 match protocol.wire() {
8196 21.. => encode(
8197 id::OPEN_RECEIVE_CHANNEL,
8198 &WireOpenReceiveV21 {
8199 base: WireOpenReceiveV18 {
8200 base: base_v17,
8201 audio_level_adjustment: wire.map_or(0, |value| value.audio_level_adjustment),
8202 },
8203 latent_capabilities: WireLatentCapabilities {
8204 bytes: wire.map_or([0; 36], |value| value.latent_capabilities),
8205 },
8206 },
8207 ),
8208 18..=20 => encode(
8209 id::OPEN_RECEIVE_CHANNEL,
8210 &WireOpenReceiveV18 {
8211 base: base_v17,
8212 audio_level_adjustment: wire.map_or(0, |value| value.audio_level_adjustment),
8213 },
8214 ),
8215 17 => encode(id::OPEN_RECEIVE_CHANNEL, &base_v17),
8216 version => {
8217 let IpAddr::V4(source_address) = source_address else {
8218 return Err(CodecError::InvalidValue {
8219 message_id: id::OPEN_RECEIVE_CHANNEL,
8220 field: "IP address family for pre-v17 protocol",
8221 value: 1,
8222 });
8223 };
8224 let base = WireOpenReceiveV11 {
8225 conference_id,
8226 passthrough_party_id: party,
8227 packet_millis: packet_ms,
8228 codec: codec.skinny(),
8229 vad: echo_cancellation.wire_value(),
8230 g723_bitrate,
8231 call_reference: call,
8232 encryption,
8233 stream_passthrough_id,
8234 associated_stream_id,
8235 rfc2833_payload: u32::from(telephone_event_payload),
8236 dtmf_type,
8237 };
8238 if version >= 12 {
8239 encode(
8240 id::OPEN_RECEIVE_CHANNEL,
8241 &WireOpenReceiveV12 {
8242 conference_id: base.conference_id,
8243 passthrough_party_id: base.passthrough_party_id,
8244 packet_millis: base.packet_millis,
8245 codec: base.codec,
8246 vad: base.vad,
8247 g723_bitrate: base.g723_bitrate,
8248 call_reference: base.call_reference,
8249 encryption: base.encryption,
8250 stream_passthrough_id: base.stream_passthrough_id,
8251 associated_stream_id: base.associated_stream_id,
8252 rfc2833_payload: base.rfc2833_payload,
8253 dtmf_type: base.dtmf_type,
8254 mixing_mode,
8255 direction,
8256 remote_ipv4: source_address.octets(),
8257 remote_port: u32::from(source_port),
8258 },
8259 )
8260 } else {
8261 encode(id::OPEN_RECEIVE_CHANNEL, &base)
8262 }
8263 }
8264 }
8265}
8266
8267struct StartMediaParameters {
8268 endpoint: MediaEndpoint,
8269 silence_suppression: SilenceSuppression,
8270 traffic_class: MediaTrafficClass,
8271}
8272
8273fn encode_start_media(
8274 call: u32,
8275 party: u32,
8276 parameters: StartMediaParameters,
8277 encryption: Option<&MediaEncryption>,
8278 wire: Option<&StartMediaTransmissionWire>,
8279 protocol: ProtocolVersion,
8280) -> Result<Vec<u8>, CodecError> {
8281 let StartMediaParameters {
8282 endpoint,
8283 silence_suppression,
8284 traffic_class,
8285 } = parameters;
8286 let conference_id = wire.map_or(call, |value| value.conference_id);
8287 let precedence = u32::from(traffic_class);
8288 let g723_bitrate = wire.map_or(0, |value| value.g723_bitrate);
8289 let stream_passthrough_id = wire.map_or(0, |value| value.stream_passthrough_id);
8290 let associated_stream_id = wire.map_or(0, |value| value.associated_stream_id);
8291 let dtmf_type = wire.map_or(10, |value| value.dtmf_type);
8292 let mixing_mode = wire.map_or(0, |value| value.mixing_mode);
8293 let direction = wire.map_or(1, |value| value.direction);
8294 let encryption = WireEncryptionInfo::from_public(encryption);
8295 if protocol.wire() >= 17 {
8296 let base = WireStartMediaV17 {
8297 conference_id,
8298 passthrough_party_id: party,
8299 remote: WireExtendedAddress::from_ip(endpoint.address),
8300 remote_port: u32::from(endpoint.rtp_port),
8301 packet_millis: endpoint.packet_ms,
8302 codec: endpoint.codec.skinny(),
8303 precedence,
8304 silence_suppression: silence_suppression.wire_value(),
8305 max_frames_per_packet: endpoint.max_frames_per_packet,
8306 g723_bitrate,
8307 call_reference: call,
8308 encryption,
8309 stream_passthrough_id,
8310 associated_stream_id,
8311 rfc2833_payload: u32::from(endpoint.telephone_event_payload),
8312 dtmf_type,
8313 mixing_mode,
8314 direction,
8315 };
8316 if protocol.wire() >= 21 {
8317 encode(
8318 id::START_MEDIA_TRANSMISSION,
8319 &WireStartMediaV21 {
8320 base,
8321 latent_capabilities: WireLatentCapabilities {
8322 bytes: wire.map_or([0; 36], |value| value.latent_capabilities),
8323 },
8324 },
8325 )
8326 } else {
8327 encode(id::START_MEDIA_TRANSMISSION, &base)
8328 }
8329 } else {
8330 let IpAddr::V4(address) = endpoint.address else {
8331 return Err(CodecError::InvalidValue {
8332 message_id: id::START_MEDIA_TRANSMISSION,
8333 field: "IP address family for pre-v17 protocol",
8334 value: 1,
8335 });
8336 };
8337 let base = WireStartMediaV11 {
8338 conference_id,
8339 passthrough_party_id: party,
8340 remote_ipv4: address.octets(),
8341 remote_port: u32::from(endpoint.rtp_port),
8342 packet_millis: endpoint.packet_ms,
8343 codec: endpoint.codec.skinny(),
8344 precedence,
8345 silence_suppression: silence_suppression.wire_value(),
8346 max_frames_per_packet: endpoint.max_frames_per_packet,
8347 g723_bitrate,
8348 call_reference: call,
8349 encryption,
8350 stream_passthrough_id,
8351 associated_stream_id,
8352 rfc2833_payload: u32::from(endpoint.telephone_event_payload),
8353 dtmf_type,
8354 };
8355 if protocol.wire() >= 12 {
8356 encode(
8357 id::START_MEDIA_TRANSMISSION,
8358 &WireStartMediaV12 {
8359 conference_id: base.conference_id,
8360 passthrough_party_id: base.passthrough_party_id,
8361 remote_ipv4: base.remote_ipv4,
8362 remote_port: base.remote_port,
8363 packet_millis: base.packet_millis,
8364 codec: base.codec,
8365 precedence: base.precedence,
8366 silence_suppression: base.silence_suppression,
8367 max_frames_per_packet: base.max_frames_per_packet,
8368 g723_bitrate: base.g723_bitrate,
8369 call_reference: base.call_reference,
8370 encryption: base.encryption,
8371 stream_passthrough_id: base.stream_passthrough_id,
8372 associated_stream_id: base.associated_stream_id,
8373 rfc2833_payload: base.rfc2833_payload,
8374 dtmf_type: base.dtmf_type,
8375 mixing_mode,
8376 direction,
8377 },
8378 )
8379 } else {
8380 encode(id::START_MEDIA_TRANSMISSION, &base)
8381 }
8382 }
8383}
8384
8385fn encode_start_multicast_reception(
8386 message: &MulticastMediaReception,
8387 protocol: ProtocolVersion,
8388) -> Result<Vec<u8>, CodecError> {
8389 if protocol.wire() >= 17 {
8390 encode(
8391 id::START_MULTICAST_MEDIA_RECEPTION,
8392 &WireStartMulticastReceptionV17 {
8393 conference_id: message.conference_id.get(),
8394 passthrough_party_id: message.passthrough_party_id.get(),
8395 address: WireExtendedAddress::from_ip(message.address),
8396 port: u32::from(message.port),
8397 packet_millis: message.packet_millis,
8398 codec: message.codec.wire_value(),
8399 echo_cancellation: message.echo_cancellation.wire_value(),
8400 g723_bitrate: message.g723_bitrate.wire_value(),
8401 call_reference: message.call_reference.get(),
8402 },
8403 )
8404 } else {
8405 let IpAddr::V4(address) = message.address else {
8406 return Err(CodecError::InvalidValue {
8407 message_id: id::START_MULTICAST_MEDIA_RECEPTION,
8408 field: "IP address family for pre-v17 protocol",
8409 value: 1,
8410 });
8411 };
8412 encode(
8413 id::START_MULTICAST_MEDIA_RECEPTION,
8414 &WireStartMulticastReceptionV3 {
8415 conference_id: message.conference_id.get(),
8416 passthrough_party_id: message.passthrough_party_id.get(),
8417 address: address.octets(),
8418 port: u32::from(message.port),
8419 packet_millis: message.packet_millis,
8420 codec: message.codec.wire_value(),
8421 echo_cancellation: message.echo_cancellation.wire_value(),
8422 g723_bitrate: message.g723_bitrate.wire_value(),
8423 call_reference: message.call_reference.get(),
8424 },
8425 )
8426 }
8427}
8428
8429fn decode_start_multicast_reception(
8430 payload: &[u8],
8431 protocol: ProtocolVersion,
8432 message_id: u32,
8433) -> Result<ServerMessage, CodecError> {
8434 let (conference_id, party_id, address, port, packet_millis, codec, echo, g723, call_reference) =
8435 if protocol.wire() >= 17 {
8436 validate_exact_payload(payload, message_id, 52)?;
8437 let value: WireStartMulticastReceptionV17 = decode(message_id, payload)?;
8438 (
8439 value.conference_id,
8440 value.passthrough_party_id,
8441 value.address.to_ip(message_id)?,
8442 value.port,
8443 value.packet_millis,
8444 value.codec,
8445 value.echo_cancellation,
8446 value.g723_bitrate,
8447 value.call_reference,
8448 )
8449 } else {
8450 validate_exact_payload(payload, message_id, 36)?;
8451 let value: WireStartMulticastReceptionV3 = decode(message_id, payload)?;
8452 (
8453 value.conference_id,
8454 value.passthrough_party_id,
8455 IpAddr::V4(Ipv4Addr::from(value.address)),
8456 value.port,
8457 value.packet_millis,
8458 value.codec,
8459 value.echo_cancellation,
8460 value.g723_bitrate,
8461 value.call_reference,
8462 )
8463 };
8464 Ok(ServerMessage::StartMulticastMediaReception(
8465 MulticastMediaReception {
8466 conference_id: conference_id.into(),
8467 passthrough_party_id: party_id.into(),
8468 call_reference: call_reference.into(),
8469 address,
8470 port: decode_port(port, message_id, "multicast port")?,
8471 packet_millis,
8472 codec: Codec::from(codec),
8473 echo_cancellation: EchoCancellation::from(echo),
8474 g723_bitrate: G723BitRate::from(g723),
8475 },
8476 ))
8477}
8478
8479fn encode_start_multicast_transmission(
8480 message: &MulticastMediaTransmission,
8481 protocol: ProtocolVersion,
8482) -> Result<Vec<u8>, CodecError> {
8483 if protocol.wire() >= 17 {
8484 encode(
8485 id::START_MULTICAST_MEDIA_TRANSMISSION,
8486 &WireStartMulticastTransmissionV17 {
8487 conference_id: message.conference_id.get(),
8488 passthrough_party_id: message.passthrough_party_id.get(),
8489 address: WireExtendedAddress::from_ip(message.address),
8490 port: u32::from(message.port),
8491 packet_millis: message.packet_millis,
8492 codec: message.codec.wire_value(),
8493 precedence: message.precedence,
8494 silence_suppression: message.silence_suppression,
8495 max_frames_per_packet: message.max_frames_per_packet,
8496 g723_bitrate: message.g723_bitrate.wire_value(),
8497 call_reference: message.call_reference.get(),
8498 },
8499 )
8500 } else {
8501 let IpAddr::V4(address) = message.address else {
8502 return Err(CodecError::InvalidValue {
8503 message_id: id::START_MULTICAST_MEDIA_TRANSMISSION,
8504 field: "IP address family for pre-v17 protocol",
8505 value: 1,
8506 });
8507 };
8508 encode(
8509 id::START_MULTICAST_MEDIA_TRANSMISSION,
8510 &WireStartMulticastTransmissionV3 {
8511 conference_id: message.conference_id.get(),
8512 passthrough_party_id: message.passthrough_party_id.get(),
8513 address: address.octets(),
8514 port: u32::from(message.port),
8515 packet_millis: message.packet_millis,
8516 codec: message.codec.wire_value(),
8517 precedence: message.precedence,
8518 silence_suppression: message.silence_suppression,
8519 max_frames_per_packet: message.max_frames_per_packet,
8520 g723_bitrate: message.g723_bitrate.wire_value(),
8521 call_reference: message.call_reference.get(),
8522 },
8523 )
8524 }
8525}
8526
8527fn decode_start_multicast_transmission(
8528 payload: &[u8],
8529 protocol: ProtocolVersion,
8530 message_id: u32,
8531) -> Result<ServerMessage, CodecError> {
8532 let (
8533 conference_id,
8534 party_id,
8535 address,
8536 port,
8537 packet_millis,
8538 codec,
8539 precedence,
8540 silence,
8541 max_frames,
8542 g723,
8543 call_reference,
8544 ) = if protocol.wire() >= 17 {
8545 validate_exact_payload(payload, message_id, 60)?;
8546 let value: WireStartMulticastTransmissionV17 = decode(message_id, payload)?;
8547 (
8548 value.conference_id,
8549 value.passthrough_party_id,
8550 value.address.to_ip(message_id)?,
8551 value.port,
8552 value.packet_millis,
8553 value.codec,
8554 value.precedence,
8555 value.silence_suppression,
8556 value.max_frames_per_packet,
8557 value.g723_bitrate,
8558 value.call_reference,
8559 )
8560 } else {
8561 validate_exact_payload(payload, message_id, 44)?;
8562 let value: WireStartMulticastTransmissionV3 = decode(message_id, payload)?;
8563 (
8564 value.conference_id,
8565 value.passthrough_party_id,
8566 IpAddr::V4(Ipv4Addr::from(value.address)),
8567 value.port,
8568 value.packet_millis,
8569 value.codec,
8570 value.precedence,
8571 value.silence_suppression,
8572 value.max_frames_per_packet,
8573 value.g723_bitrate,
8574 value.call_reference,
8575 )
8576 };
8577 Ok(ServerMessage::StartMulticastMediaTransmission(
8578 MulticastMediaTransmission {
8579 conference_id: conference_id.into(),
8580 passthrough_party_id: party_id.into(),
8581 call_reference: call_reference.into(),
8582 address,
8583 port: decode_port(port, message_id, "multicast port")?,
8584 packet_millis,
8585 codec: Codec::from(codec),
8586 precedence,
8587 silence_suppression: silence,
8588 max_frames_per_packet: max_frames,
8589 g723_bitrate: G723BitRate::from(g723),
8590 },
8591 ))
8592}
8593
8594fn decode_open_receive(
8595 payload: &[u8],
8596 protocol: ProtocolVersion,
8597 message_id: u32,
8598) -> Result<ServerMessage, CodecError> {
8599 let (
8600 call_reference,
8601 passthrough_party_id,
8602 packet_ms,
8603 codec,
8604 echo,
8605 rfc2833,
8606 source_address,
8607 source_port,
8608 encryption,
8609 wire,
8610 ) = match protocol.wire() {
8611 21.. => {
8612 let value: WireOpenReceiveV21 = decode(message_id, payload)?;
8613 (
8614 value.base.base.call_reference,
8615 value.base.base.passthrough_party_id,
8616 value.base.base.packet_millis,
8617 value.base.base.codec,
8618 value.base.base.vad,
8619 value.base.base.rfc2833_payload,
8620 value.base.base.remote.to_ip(message_id)?,
8621 decode_port(value.base.base.remote_port, message_id, "source RTP port")?,
8622 value.base.base.encryption,
8623 OpenReceiveChannelWire {
8624 conference_id: value.base.base.conference_id,
8625 g723_bitrate: value.base.base.g723_bitrate,
8626 stream_passthrough_id: value.base.base.stream_passthrough_id,
8627 associated_stream_id: value.base.base.associated_stream_id,
8628 dtmf_type: value.base.base.dtmf_type,
8629 mixing_mode: value.base.base.mixing_mode,
8630 direction: value.base.base.direction,
8631 requested_address_type: value.base.base.requested_address_type,
8632 audio_level_adjustment: value.base.audio_level_adjustment,
8633 latent_capabilities: value.latent_capabilities.bytes,
8634 },
8635 )
8636 }
8637 18..=20 => {
8638 let value: WireOpenReceiveV18 = decode(message_id, payload)?;
8639 (
8640 value.base.call_reference,
8641 value.base.passthrough_party_id,
8642 value.base.packet_millis,
8643 value.base.codec,
8644 value.base.vad,
8645 value.base.rfc2833_payload,
8646 value.base.remote.to_ip(message_id)?,
8647 decode_port(value.base.remote_port, message_id, "source RTP port")?,
8648 value.base.encryption,
8649 OpenReceiveChannelWire {
8650 conference_id: value.base.conference_id,
8651 g723_bitrate: value.base.g723_bitrate,
8652 stream_passthrough_id: value.base.stream_passthrough_id,
8653 associated_stream_id: value.base.associated_stream_id,
8654 dtmf_type: value.base.dtmf_type,
8655 mixing_mode: value.base.mixing_mode,
8656 direction: value.base.direction,
8657 requested_address_type: value.base.requested_address_type,
8658 audio_level_adjustment: value.audio_level_adjustment,
8659 latent_capabilities: [0; 36],
8660 },
8661 )
8662 }
8663 17 => {
8664 let value: WireOpenReceiveV17 = decode(message_id, payload)?;
8665 (
8666 value.call_reference,
8667 value.passthrough_party_id,
8668 value.packet_millis,
8669 value.codec,
8670 value.vad,
8671 value.rfc2833_payload,
8672 value.remote.to_ip(message_id)?,
8673 decode_port(value.remote_port, message_id, "source RTP port")?,
8674 value.encryption,
8675 OpenReceiveChannelWire {
8676 conference_id: value.conference_id,
8677 g723_bitrate: value.g723_bitrate,
8678 stream_passthrough_id: value.stream_passthrough_id,
8679 associated_stream_id: value.associated_stream_id,
8680 dtmf_type: value.dtmf_type,
8681 mixing_mode: value.mixing_mode,
8682 direction: value.direction,
8683 requested_address_type: value.requested_address_type,
8684 audio_level_adjustment: 0,
8685 latent_capabilities: [0; 36],
8686 },
8687 )
8688 }
8689 12..=16 => {
8690 let value: WireOpenReceiveV12 = decode(message_id, payload)?;
8691 (
8692 value.call_reference,
8693 value.passthrough_party_id,
8694 value.packet_millis,
8695 value.codec,
8696 value.vad,
8697 value.rfc2833_payload,
8698 IpAddr::V4(Ipv4Addr::from(value.remote_ipv4)),
8699 decode_port(value.remote_port, message_id, "source RTP port")?,
8700 value.encryption,
8701 OpenReceiveChannelWire {
8702 conference_id: value.conference_id,
8703 g723_bitrate: value.g723_bitrate,
8704 stream_passthrough_id: value.stream_passthrough_id,
8705 associated_stream_id: value.associated_stream_id,
8706 dtmf_type: value.dtmf_type,
8707 mixing_mode: value.mixing_mode,
8708 direction: value.direction,
8709 requested_address_type: 0,
8710 audio_level_adjustment: 0,
8711 latent_capabilities: [0; 36],
8712 },
8713 )
8714 }
8715 _ => {
8716 let value: WireOpenReceiveV11 = decode(message_id, payload)?;
8717 (
8718 value.call_reference,
8719 value.passthrough_party_id,
8720 value.packet_millis,
8721 value.codec,
8722 value.vad,
8723 value.rfc2833_payload,
8724 IpAddr::V4(Ipv4Addr::UNSPECIFIED),
8725 0,
8726 value.encryption,
8727 OpenReceiveChannelWire {
8728 conference_id: value.conference_id,
8729 g723_bitrate: value.g723_bitrate,
8730 stream_passthrough_id: value.stream_passthrough_id,
8731 associated_stream_id: value.associated_stream_id,
8732 dtmf_type: value.dtmf_type,
8733 mixing_mode: 0,
8734 direction: 0,
8735 requested_address_type: 0,
8736 audio_level_adjustment: 0,
8737 latent_capabilities: [0; 36],
8738 },
8739 )
8740 }
8741 };
8742 let telephone_event_payload = u8::try_from(rfc2833).map_err(|_| CodecError::InvalidValue {
8743 message_id,
8744 field: "RFC2833 payload",
8745 value: u64::from(rfc2833),
8746 })?;
8747 Ok(ServerMessage::OpenReceiveChannel {
8748 call_reference,
8749 passthrough_party_id,
8750 packet_ms,
8751 codec: Codec::from(codec),
8752 echo_cancellation: EchoCancellation::from(echo),
8753 telephone_event_payload,
8754 source_address,
8755 source_port,
8756 encryption: encryption.to_public(message_id)?,
8757 wire: (wire != canonical_open_receive_wire(call_reference, source_address, protocol))
8758 .then_some(wire),
8759 })
8760}
8761
8762fn decode_start_media(
8763 payload: &[u8],
8764 protocol: ProtocolVersion,
8765 message_id: u32,
8766) -> Result<ServerMessage, CodecError> {
8767 let (
8768 call_reference,
8769 passthrough_party_id,
8770 address,
8771 port,
8772 packet_ms,
8773 codec,
8774 precedence,
8775 silence_suppression,
8776 max_frames_per_packet,
8777 rfc2833,
8778 encryption,
8779 wire,
8780 ) = match protocol.wire() {
8781 21.. => {
8782 let value: WireStartMediaV21 = decode(message_id, payload)?;
8783 (
8784 value.base.call_reference,
8785 value.base.passthrough_party_id,
8786 value.base.remote.to_ip(message_id)?,
8787 value.base.remote_port,
8788 value.base.packet_millis,
8789 value.base.codec,
8790 value.base.precedence,
8791 value.base.silence_suppression,
8792 value.base.max_frames_per_packet,
8793 value.base.rfc2833_payload,
8794 value.base.encryption,
8795 StartMediaTransmissionWire {
8796 conference_id: value.base.conference_id,
8797 g723_bitrate: value.base.g723_bitrate,
8798 stream_passthrough_id: value.base.stream_passthrough_id,
8799 associated_stream_id: value.base.associated_stream_id,
8800 dtmf_type: value.base.dtmf_type,
8801 mixing_mode: value.base.mixing_mode,
8802 direction: value.base.direction,
8803 latent_capabilities: value.latent_capabilities.bytes,
8804 },
8805 )
8806 }
8807 17..=20 => {
8808 let value: WireStartMediaV17 = decode(message_id, payload)?;
8809 (
8810 value.call_reference,
8811 value.passthrough_party_id,
8812 value.remote.to_ip(message_id)?,
8813 value.remote_port,
8814 value.packet_millis,
8815 value.codec,
8816 value.precedence,
8817 value.silence_suppression,
8818 value.max_frames_per_packet,
8819 value.rfc2833_payload,
8820 value.encryption,
8821 StartMediaTransmissionWire {
8822 conference_id: value.conference_id,
8823 g723_bitrate: value.g723_bitrate,
8824 stream_passthrough_id: value.stream_passthrough_id,
8825 associated_stream_id: value.associated_stream_id,
8826 dtmf_type: value.dtmf_type,
8827 mixing_mode: value.mixing_mode,
8828 direction: value.direction,
8829 latent_capabilities: [0; 36],
8830 },
8831 )
8832 }
8833 12..=16 => {
8834 let value: WireStartMediaV12 = decode(message_id, payload)?;
8835 (
8836 value.call_reference,
8837 value.passthrough_party_id,
8838 IpAddr::V4(Ipv4Addr::from(value.remote_ipv4)),
8839 value.remote_port,
8840 value.packet_millis,
8841 value.codec,
8842 value.precedence,
8843 value.silence_suppression,
8844 value.max_frames_per_packet,
8845 value.rfc2833_payload,
8846 value.encryption,
8847 StartMediaTransmissionWire {
8848 conference_id: value.conference_id,
8849 g723_bitrate: value.g723_bitrate,
8850 stream_passthrough_id: value.stream_passthrough_id,
8851 associated_stream_id: value.associated_stream_id,
8852 dtmf_type: value.dtmf_type,
8853 mixing_mode: value.mixing_mode,
8854 direction: value.direction,
8855 latent_capabilities: [0; 36],
8856 },
8857 )
8858 }
8859 _ => {
8860 let value: WireStartMediaV11 = decode(message_id, payload)?;
8861 (
8862 value.call_reference,
8863 value.passthrough_party_id,
8864 IpAddr::V4(Ipv4Addr::from(value.remote_ipv4)),
8865 value.remote_port,
8866 value.packet_millis,
8867 value.codec,
8868 value.precedence,
8869 value.silence_suppression,
8870 value.max_frames_per_packet,
8871 value.rfc2833_payload,
8872 value.encryption,
8873 StartMediaTransmissionWire {
8874 conference_id: value.conference_id,
8875 g723_bitrate: value.g723_bitrate,
8876 stream_passthrough_id: value.stream_passthrough_id,
8877 associated_stream_id: value.associated_stream_id,
8878 dtmf_type: value.dtmf_type,
8879 mixing_mode: 0,
8880 direction: 0,
8881 latent_capabilities: [0; 36],
8882 },
8883 )
8884 }
8885 };
8886 let rtp_port = decode_port(port, message_id, "RTP port")?;
8887 let telephone_event_payload = u8::try_from(rfc2833).map_err(|_| CodecError::InvalidValue {
8888 message_id,
8889 field: "RFC2833 payload",
8890 value: u64::from(rfc2833),
8891 })?;
8892 Ok(ServerMessage::StartMediaTransmission {
8893 call_reference,
8894 passthrough_party_id,
8895 endpoint: MediaEndpoint {
8896 address,
8897 rtp_port,
8898 rtcp_port: rtp_port.saturating_add(1),
8899 codec: Codec::from(codec),
8900 packet_ms,
8901 max_frames_per_packet,
8902 telephone_event_payload,
8903 },
8904 silence_suppression: SilenceSuppression::from(silence_suppression),
8905 traffic_class: MediaTrafficClass::from_wire(u8::try_from(precedence).map_err(|_| {
8906 CodecError::InvalidValue {
8907 message_id,
8908 field: "media traffic class",
8909 value: u64::from(precedence),
8910 }
8911 })?),
8912 encryption: encryption.to_public(message_id)?,
8913 wire: (wire != canonical_start_media_wire(call_reference, protocol)).then_some(wire),
8914 })
8915}
8916
8917#[cfg(test)]
8918mod tests {
8919 use super::catalog::MessageDirection;
8920 use super::values::SoftKey;
8921 use super::wire::{FrameDecoder, MAX_FRAME_SIZE};
8922 use super::*;
8923
8924 fn fixture(source: &str) -> Vec<u8> {
8925 source
8926 .split_whitespace()
8927 .map(|byte| u8::from_str_radix(byte, 16).expect("valid fixture byte"))
8928 .collect()
8929 }
8930
8931 fn deterministic_payload(message_id: u32, protocol: u32, length: usize) -> Vec<u8> {
8932 let mut state = u64::from(message_id)
8933 ^ (u64::from(protocol) << 32)
8934 ^ (length as u64).wrapping_mul(0x9e37_79b9_7f4a_7c15);
8935 (0..length)
8936 .map(|_| {
8937 state ^= state << 13;
8938 state ^= state >> 7;
8939 state ^= state << 17;
8940 state as u8
8941 })
8942 .collect()
8943 }
8944
8945 fn fuzz_lengths() -> impl Iterator<Item = usize> {
8946 (0..=96).chain([127, 255, 511, 1024, MAX_FRAME_SIZE - 12])
8947 }
8948
8949 const fn test_rtp_payload_number(value: u32) -> RtpPayloadNumber {
8950 match RtpPayloadNumber::new(value) {
8951 Ok(value) => value,
8952 Err(_) => panic!("test RTP payload number is out of range"),
8953 }
8954 }
8955
8956 fn typed_video_payload(arm: MultimediaVideoCapabilityArm) -> MultimediaPayload {
8957 let payload_number = match arm.codec() {
8958 Codec::H261 => 31,
8959 Codec::H263 => 34,
8960 Codec::H263Plus => 96,
8961 Codec::H264 => 97,
8962 _ => unreachable!("typed video arms always have a modeled codec"),
8963 };
8964 MultimediaPayload::new(
8965 test_rtp_payload_number(payload_number),
8966 MultimediaVideoCapability::new(
8967 1_024,
8968 [
8969 MultimediaPictureFormat {
8970 format: VideoFormat::Cif4,
8971 minimum_picture_interval: 1,
8972 },
8973 MultimediaPictureFormat {
8974 format: VideoFormat::Cif,
8975 minimum_picture_interval: 2,
8976 },
8977 ],
8978 7,
8979 arm,
8980 )
8981 .unwrap(),
8982 )
8983 }
8984
8985 #[test]
8986 fn every_catalogued_client_decoder_is_panic_free_for_bounded_property_corpus() {
8987 let protocols = [
8988 ProtocolVersion::V3,
8989 ProtocolVersion::V8,
8990 ProtocolVersion::V17,
8991 ProtocolVersion::V22,
8992 ];
8993 let mut cases = 0_usize;
8994 for message_id in MessageId::ALL_KNOWN
8995 .iter()
8996 .copied()
8997 .filter(|id| id.direction() == Some(MessageDirection::DeviceToServer))
8998 {
8999 for protocol in protocols {
9000 for length in fuzz_lengths() {
9001 let frame = Frame::new(
9002 protocol.wire(),
9003 message_id.wire_value(),
9004 deterministic_payload(message_id.wire_value(), protocol.wire(), length),
9005 );
9006 let _ = ClientMessage::decode_with_version(frame, protocol);
9007 cases += 1;
9008 }
9009 }
9010 }
9011 assert!(
9012 cases > 20_000,
9013 "property corpus unexpectedly shrank: {cases}"
9014 );
9015 }
9016
9017 #[test]
9018 fn every_catalogued_server_encoder_round_trips_all_decodable_bounded_inputs() {
9019 let protocols = [
9020 ProtocolVersion::V3,
9021 ProtocolVersion::V8,
9022 ProtocolVersion::V17,
9023 ProtocolVersion::V22,
9024 ];
9025 let mut decoded = 0_usize;
9026 let mut encoded = 0_usize;
9027 for message_id in MessageId::ALL_KNOWN
9028 .iter()
9029 .copied()
9030 .filter(|id| id.direction() == Some(MessageDirection::ServerToDevice))
9031 {
9032 for protocol in protocols {
9033 for length in fuzz_lengths() {
9034 let frame = Frame::new(
9035 protocol.wire(),
9036 message_id.wire_value(),
9037 deterministic_payload(message_id.wire_value(), protocol.wire(), length),
9038 );
9039 let Ok(message) = ServerMessage::decode(frame, protocol) else {
9040 continue;
9041 };
9042 decoded += 1;
9043 let Ok(bytes) = message.encode(protocol) else {
9044 continue;
9045 };
9046 assert!(bytes.len() <= MAX_FRAME_SIZE);
9047 let frames = FrameDecoder::new().push(&bytes).unwrap();
9048 assert_eq!(frames.len(), 1);
9049 assert_eq!(
9050 ServerMessage::decode(frames.into_iter().next().unwrap(), protocol)
9051 .unwrap(),
9052 message
9053 );
9054 encoded += 1;
9055 }
9056 }
9057 }
9058 assert!(
9059 decoded > 1_000,
9060 "decodable encoder corpus unexpectedly shrank: {decoded}"
9061 );
9062 assert!(
9063 encoded > 1_000,
9064 "encodable property corpus unexpectedly shrank: {encoded}"
9065 );
9066 }
9067
9068 #[test]
9069 fn registration_preserves_both_reported_address_families() {
9070 let message = ClientMessage::Register(RegistrationMessage {
9071 device_id: DeviceId::new("SEP001122334455").unwrap(),
9072 reported_address: Some(Ipv4Addr::new(192, 0, 2, 10)),
9073 reported_ipv6_address: Some("2001:db8::10".parse().unwrap()),
9074 device_type: DeviceType::Cisco7962,
9075 advertised_protocol: ProtocolVersion::V22.wire(),
9076 features: PhoneFeatures::empty(),
9077 firmware: "test-load".into(),
9078 configuration_version_stamp: BoundedBytes::default(),
9079 wire: Some(RegistrationWireDetails {
9080 station_user_id: 17,
9081 station_instance: 2,
9082 max_streams: 5,
9083 active_streams: 1,
9084 mac_address_and_padding: [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0, 0, 0, 0, 0, 0],
9085 max_conferences: 3,
9086 active_conferences: 1,
9087 ipv4_address_scope: 3,
9088 max_lines: 6,
9089 ipv6_address_scope: 2,
9090 }),
9091 });
9092 let bytes = message.encode(ProtocolVersion::V22).unwrap();
9093 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
9094 assert_eq!(
9095 ClientMessage::decode_with_version(frame, ProtocolVersion::V22).unwrap(),
9096 message
9097 );
9098 }
9099
9100 #[test]
9101 fn registration_preserves_every_bounded_configuration_suffix() {
9102 let base = ClientMessage::Register(RegistrationMessage {
9103 device_id: DeviceId::new("SEP001122334455").unwrap(),
9104 reported_address: None,
9105 reported_ipv6_address: None,
9106 device_type: DeviceType::Cisco7962,
9107 advertised_protocol: ProtocolVersion::V22.wire(),
9108 features: PhoneFeatures::empty(),
9109 firmware: "test-load".into(),
9110 configuration_version_stamp: BoundedBytes::default(),
9111 wire: Some(RegistrationWireDetails {
9112 station_user_id: 0,
9113 station_instance: 1,
9114 max_streams: 0,
9115 active_streams: 0,
9116 mac_address_and_padding: [0; 12],
9117 max_conferences: 0,
9118 active_conferences: 0,
9119 ipv4_address_scope: 0,
9120 max_lines: 0,
9121 ipv6_address_scope: 0,
9122 }),
9123 });
9124
9125 for length in 0..=48 {
9126 let mut message = base.clone();
9127 let ClientMessage::Register(registration) = &mut message else {
9128 unreachable!("test message is registration")
9129 };
9130 registration.configuration_version_stamp =
9131 BoundedBytes::try_from(vec![0xa5; length]).unwrap();
9132 let bytes = message.encode(ProtocolVersion::V22).unwrap();
9133 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
9134 assert_eq!(frame.payload.len(), 124 + length);
9135 assert_eq!(
9136 ClientMessage::decode_with_version(frame, ProtocolVersion::V22).unwrap(),
9137 message
9138 );
9139 }
9140
9141 assert!(matches!(
9142 ClientMessage::decode_with_version(
9143 Frame::new(0, id::REGISTER, vec![0; 123]),
9144 ProtocolVersion::V22,
9145 ),
9146 Err(CodecError::Truncated { .. })
9147 ));
9148 assert!(matches!(
9149 ClientMessage::decode_with_version(
9150 Frame::new(0, id::REGISTER, vec![0; 173]),
9151 ProtocolVersion::V22,
9152 ),
9153 Err(CodecError::TrailingBytes { .. })
9154 ));
9155 }
9156
9157 #[test]
9158 fn alarm_preserves_both_supported_wire_lengths() {
9159 for parameters in [None, Some([0x1122_3344, 0xaabb_ccdd])] {
9160 let message = ClientMessage::Alarm {
9161 severity: AlarmSeverity::Warning,
9162 text: "TFTP load failed".into(),
9163 parameters,
9164 };
9165 let bytes = message.encode(ProtocolVersion::V17).unwrap();
9166 assert_eq!(bytes.len(), if parameters.is_some() { 104 } else { 96 });
9167 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
9168 assert_eq!(ClientMessage::decode(frame).unwrap(), message);
9169 }
9170 }
9171
9172 #[test]
9173 fn connection_statistics_reject_oversized_quality_payloads() {
9174 let payload = WireConnectionStatisticsV19 {
9175 directory_number: WireFixedText::new(
9176 id::CONNECTION_STATISTICS_RES,
9177 "directory number",
9178 "2002",
9179 )
9180 .unwrap(),
9181 alignment: [0; 3],
9182 call_reference: 42,
9183 processing: StatisticsProcessing::Clear.wire_value(),
9184 statistics: WireConnectionStatisticsTail {
9185 packets_sent: 1,
9186 octets_sent: 2,
9187 packets_received: 3,
9188 octets_received: 4,
9189 packets_lost: 5,
9190 jitter_millis: 6,
9191 latency_millis: 7,
9192 quality_size: (CONNECTION_QUALITY_MAX_BYTES + 1) as u32,
9193 },
9194 quality: vec![0; CONNECTION_QUALITY_MAX_BYTES + 1],
9195 };
9196 let mut encoded = encode(id::CONNECTION_STATISTICS_RES, &payload).unwrap();
9197 pad_dynamic_payload(&mut encoded);
9198 let frame = Frame::new(
9199 ProtocolVersion::V22.wire(),
9200 id::CONNECTION_STATISTICS_RES,
9201 encoded,
9202 );
9203 assert!(matches!(
9204 ClientMessage::decode_with_version(frame, ProtocolVersion::V22),
9205 Err(CodecError::CountTooLarge {
9206 field: "quality statistics",
9207 maximum: CONNECTION_QUALITY_MAX_BYTES,
9208 ..
9209 })
9210 ));
9211 }
9212
9213 #[test]
9214 fn media_wire_schemas_round_trip_byte_for_byte() {
9215 let start = fixture(include_str!(
9216 "../../tests/fixtures/golden/start_media_transmission_v17.hex"
9217 ));
9218 let start_value: WireStartMediaV17 = decode(0x008a, &start[12..]).unwrap();
9219 assert_eq!(encode(0x008a, &start_value).unwrap(), &start[12..]);
9220 let start_frame = FrameDecoder::new().push(&start).unwrap().remove(0);
9221 let start_message = ServerMessage::decode(start_frame, ProtocolVersion::V17).unwrap();
9222 assert_eq!(start_message.encode(ProtocolVersion::V17).unwrap(), start);
9223
9224 let open = fixture(include_str!(
9225 "../../tests/fixtures/golden/open_receive_channel_v17.hex"
9226 ));
9227 let open_value: WireOpenReceiveV17 = decode(0x0105, &open[12..]).unwrap();
9228 assert_eq!(encode(0x0105, &open_value).unwrap(), &open[12..]);
9229 let open_frame = FrameDecoder::new().push(&open).unwrap().remove(0);
9230 let open_message = ServerMessage::decode(open_frame, ProtocolVersion::V17).unwrap();
9231 assert_eq!(open_message.encode(ProtocolVersion::V17).unwrap(), open);
9232
9233 let ack = fixture(include_str!(
9234 "../../tests/fixtures/golden/start_media_transmission_ack_v20.hex"
9235 ));
9236 let ack_value: WireStartMediaAckV20 = decode(0x0154, &ack[12..]).unwrap();
9237 assert_eq!(encode(0x0154, &ack_value).unwrap(), &ack[12..]);
9238 let ack_frame = FrameDecoder::new().push(&ack).unwrap().remove(0);
9239 let ack_message =
9240 ClientMessage::decode_with_version(ack_frame, ProtocolVersion::V20).unwrap();
9241 assert_eq!(ack_message.encode(ProtocolVersion::V20).unwrap(), ack);
9242 }
9243
9244 #[test]
9245 fn audio_media_version_boundaries_have_exact_payload_sizes() {
9246 let endpoint = MediaEndpoint {
9247 address: "192.0.2.20".parse().unwrap(),
9248 rtp_port: 16_000,
9249 rtcp_port: 16_001,
9250 codec: Codec::Pcmu,
9251 packet_ms: 20,
9252 max_frames_per_packet: 2,
9253 telephone_event_payload: 101,
9254 };
9255 for (version, open_size, start_size) in [
9256 (11, 92, 108),
9257 (12, 108, 116),
9258 (16, 108, 116),
9259 (17, 128, 132),
9260 (18, 132, 132),
9261 (20, 132, 132),
9262 (21, 168, 168),
9263 (22, 168, 168),
9264 ] {
9265 let protocol = ProtocolVersion::new(version).unwrap();
9266 let open = ServerMessage::OpenReceiveChannel {
9267 call_reference: 7,
9268 passthrough_party_id: 9,
9269 packet_ms: 20,
9270 codec: Codec::Pcmu,
9271 echo_cancellation: EchoCancellation::On,
9272 telephone_event_payload: 101,
9273 source_address: endpoint.address,
9274 source_port: endpoint.rtp_port,
9275 encryption: None,
9276 wire: None,
9277 };
9278 let open_bytes = open.encode(protocol).unwrap();
9279 assert_eq!(open_bytes.len() - 12, open_size, "protocol {version}");
9280 let decoded = ServerMessage::decode(
9281 FrameDecoder::new().push(&open_bytes).unwrap().remove(0),
9282 protocol,
9283 )
9284 .unwrap();
9285 assert_eq!(decoded.encode(protocol).unwrap(), open_bytes);
9286
9287 let start = ServerMessage::StartMediaTransmission {
9288 call_reference: 7,
9289 passthrough_party_id: 9,
9290 endpoint,
9291 silence_suppression: SilenceSuppression::Off,
9292 traffic_class: MediaTrafficClass::default(),
9293 encryption: None,
9294 wire: None,
9295 };
9296 let start_bytes = start.encode(protocol).unwrap();
9297 assert_eq!(start_bytes.len() - 12, start_size, "protocol {version}");
9298 let decoded = ServerMessage::decode(
9299 FrameDecoder::new().push(&start_bytes).unwrap().remove(0),
9300 protocol,
9301 )
9302 .unwrap();
9303 assert_eq!(decoded.encode(protocol).unwrap(), start_bytes);
9304 }
9305 }
9306
9307 #[test]
9308 fn audio_acknowledgements_keep_conference_and_call_references_distinct() {
9309 for (protocol, address, open_size, start_size, failure_size) in [
9310 (
9311 ProtocolVersion::V16,
9312 "192.0.2.21".parse().unwrap(),
9313 20,
9314 24,
9315 20,
9316 ),
9317 (
9318 ProtocolVersion::V17,
9319 "2001:db8::21".parse().unwrap(),
9320 36,
9321 40,
9322 36,
9323 ),
9324 ] {
9325 let open = ClientMessage::OpenReceiveChannelAck {
9326 status: MediaStatus::Ok,
9327 address,
9328 port: 16_000,
9329 passthrough_party_id: 9,
9330 call_reference: 7,
9331 };
9332 assert_eq!(open.encode(protocol).unwrap().len() - 12, open_size);
9333
9334 let start = ClientMessage::StartMediaTransmissionAck(MediaTransmissionAck {
9335 conference_id: 6,
9336 passthrough_party_id: 9,
9337 call_reference: 7,
9338 status: MediaStatus::Ok,
9339 address,
9340 port: 16_000,
9341 wire: None,
9342 });
9343 let start_bytes = start.encode(protocol).unwrap();
9344 assert_eq!(start_bytes.len() - 12, start_size);
9345 let decoded = ClientMessage::decode_with_version(
9346 FrameDecoder::new().push(&start_bytes).unwrap().remove(0),
9347 protocol,
9348 )
9349 .unwrap();
9350 assert_eq!(decoded, start);
9351
9352 let failure = ClientMessage::MediaTransmissionFailure {
9353 conference_id: 6,
9354 passthrough_party_id: 9,
9355 address,
9356 port: 16_000,
9357 call_reference: 7,
9358 status: MediaStatus::UnspecifiedError,
9359 };
9360 assert_eq!(failure.encode(protocol).unwrap().len() - 12, failure_size);
9361 }
9362
9363 for message_id in [id::CLOSE_RECEIVE_CHANNEL, id::STOP_MEDIA_TRANSMISSION] {
9364 let payload = [0_u8; 16];
9365 let frame = Frame::new(ProtocolVersion::V22.wire(), message_id, payload.to_vec());
9366 let message = ServerMessage::decode(frame, ProtocolVersion::V22).unwrap();
9367 assert_eq!(message.encode(ProtocolVersion::V22).unwrap().len() - 12, 16);
9368
9369 let truncated = Frame::new(
9370 ProtocolVersion::V22.wire(),
9371 message_id,
9372 payload[..12].to_vec(),
9373 );
9374 assert!(matches!(
9375 ServerMessage::decode(truncated, ProtocolVersion::V22),
9376 Err(CodecError::Truncated { .. })
9377 ));
9378 }
9379 }
9380
9381 #[test]
9382 fn session_and_video_envelopes_preserve_every_wire_byte() {
9383 for (protocol, address, expected_size) in [
9384 (ProtocolVersion::V16, "192.0.2.30".parse().unwrap(), 8),
9385 (ProtocolVersion::V17, "2001:db8::30".parse().unwrap(), 24),
9386 ] {
9387 for message in [
9388 ControlMessage::StartSessionTransmission(SessionTransmission {
9389 remote_address: address,
9390 session_type: 0x1122_3344,
9391 }),
9392 ControlMessage::StopSessionTransmission(SessionTransmission {
9393 remote_address: address,
9394 session_type: 0x5566_7788,
9395 }),
9396 ] {
9397 let bytes = message.encode(protocol).unwrap();
9398 assert_eq!(bytes.len() - 12, expected_size);
9399 let decoded = ControlMessage::decode(
9400 FrameDecoder::new().push(&bytes).unwrap().remove(0),
9401 protocol,
9402 )
9403 .unwrap();
9404 assert_eq!(decoded.encode(protocol).unwrap(), bytes);
9405 }
9406 }
9407
9408 for (version, address, expected_size) in [
9409 (11, "0.0.0.0".parse().unwrap(), 164),
9410 (12, "192.0.2.31".parse().unwrap(), 172),
9411 (16, "192.0.2.31".parse().unwrap(), 172),
9412 (17, "2001:db8::31".parse().unwrap(), 192),
9413 ] {
9414 let protocol = ProtocolVersion::new(version).unwrap();
9415 let message = ServerMessage::OpenMultimediaChannel(OpenMultimediaChannel {
9416 conference_id: 42.into(),
9417 passthrough_party_id: 9.into(),
9418 line_instance: 1,
9419 call_reference: 7.into(),
9420 payload: typed_video_payload(MultimediaVideoCapabilityArm::H264 {
9421 profile: 100,
9422 level: 42,
9423 custom_max_mbps: 40_500,
9424 custom_max_fs: 1_620,
9425 custom_max_dpb: 8_100,
9426 custom_max_br_and_cpb: 10_000,
9427 }),
9428 conference_creator: true,
9429 encryption: None,
9430 stream_passthrough_id: 10,
9431 associated_stream_id: 11,
9432 source: MediaEndpointAddress {
9433 address,
9434 port: if version < 12 { 0 } else { 16_000 },
9435 },
9436 requested_address_type: if version >= 17 {
9437 IpAddressType::Ipv6
9438 } else {
9439 IpAddressType::Ipv4
9440 },
9441 });
9442 let bytes = message.encode(protocol).unwrap();
9443 assert_eq!(bytes.len() - 12, expected_size, "protocol {version}");
9444 let decoded = ServerMessage::decode(
9445 FrameDecoder::new().push(&bytes).unwrap().remove(0),
9446 protocol,
9447 )
9448 .unwrap();
9449 assert_eq!(decoded.encode(protocol).unwrap(), bytes);
9450 }
9451
9452 for (protocol, address, expected_size) in [
9453 (ProtocolVersion::V16, "192.0.2.32".parse().unwrap(), 168),
9454 (ProtocolVersion::V17, "2001:db8::32".parse().unwrap(), 184),
9455 ] {
9456 let message = ServerMessage::StartMultimediaTransmission(StartMultimediaTransmission {
9457 conference_id: 42.into(),
9458 passthrough_party_id: 9.into(),
9459 endpoint: MediaEndpointAddress {
9460 address,
9461 port: 16_002,
9462 },
9463 call_reference: 7.into(),
9464 payload: typed_video_payload(MultimediaVideoCapabilityArm::H264 {
9465 profile: 100,
9466 level: 42,
9467 custom_max_mbps: 40_500,
9468 custom_max_fs: 1_620,
9469 custom_max_dpb: 8_100,
9470 custom_max_br_and_cpb: 10_000,
9471 }),
9472 traffic_class: MediaTrafficClass::from_wire(184),
9473 encryption: None,
9474 stream_passthrough_id: 10,
9475 associated_stream_id: 11,
9476 });
9477 let bytes = message.encode(protocol).unwrap();
9478 assert_eq!(bytes.len() - 12, expected_size);
9479 let traffic_class_offset = if protocol >= ProtocolVersion::V17 {
9480 60
9481 } else {
9482 44
9483 };
9484 assert_eq!(
9485 &bytes[traffic_class_offset..traffic_class_offset + 4],
9486 &184_u32.to_le_bytes()
9487 );
9488 let decoded = ServerMessage::decode(
9489 FrameDecoder::new().push(&bytes).unwrap().remove(0),
9490 protocol,
9491 )
9492 .unwrap();
9493 assert_eq!(decoded.encode(protocol).unwrap(), bytes);
9494 }
9495
9496 let miscellaneous = ServerMessage::MiscellaneousCommand(MiscellaneousCommand {
9497 conference_id: 42.into(),
9498 passthrough_party_id: 9.into(),
9499 call_reference: 7.into(),
9500 command: values::MiscCommandType::LostPartialPicture,
9501 data: BoundedBytes::try_from((0_u8..36).collect::<Vec<_>>()).unwrap(),
9502 });
9503 let bytes = miscellaneous.encode(ProtocolVersion::V22).unwrap();
9504 assert_eq!(bytes.len() - 12, 52);
9505 let decoded = ServerMessage::decode(
9506 FrameDecoder::new().push(&bytes).unwrap().remove(0),
9507 ProtocolVersion::V22,
9508 )
9509 .unwrap();
9510 assert_eq!(decoded, miscellaneous);
9511
9512 for (message_id, protocol, expected) in [
9513 (id::OPEN_MULTIMEDIA_CHANNEL, ProtocolVersion::V17, 192),
9514 (id::START_MULTIMEDIA_TRANSMISSION, ProtocolVersion::V17, 184),
9515 (id::MISCELLANEOUS_COMMAND, ProtocolVersion::V22, 52),
9516 ] {
9517 for actual in [expected - 1, expected + 1] {
9518 let frame = Frame::new(protocol.wire(), message_id, vec![0; actual]);
9519 assert!(ServerMessage::decode(frame, protocol).is_err());
9520 }
9521 }
9522 for (protocol, expected) in [(ProtocolVersion::V16, 8), (ProtocolVersion::V17, 24)] {
9523 for actual in [expected - 1, expected + 1] {
9524 let frame = Frame::new(
9525 protocol.wire(),
9526 id::START_SESSION_TRANSMISSION,
9527 vec![0; actual],
9528 );
9529 assert!(ControlMessage::decode(frame, protocol).is_err());
9530 }
9531 }
9532 }
9533
9534 #[test]
9535 fn unsupported_decoded_multimedia_payloads_are_lossless_and_provenance_bound() {
9536 let mut words = [0; MULTIMEDIA_CAPABILITY_BYTES / 4];
9537 words[0] = 2_048;
9538 words[1] = 1;
9539 words[2] = VideoFormat::Cif.wire_value();
9540 words[3] = 2;
9541 words[12] = 7;
9542 words[13..].copy_from_slice(&[61, 62, 63, 64, 65, 66]);
9543 let capability = multimedia_capability_bytes(words);
9544 let payload = MultimediaPayload::from_wire(
9545 0,
9546 test_rtp_payload_number(97),
9547 capability,
9548 Codec::H265,
9549 MultimediaPayloadDirection::Receive,
9550 ProtocolVersion::V17,
9551 );
9552 assert_eq!(payload.codec(), Codec::H265);
9553 assert_eq!(payload.video_capability(), None);
9554 let open = ServerMessage::OpenMultimediaChannel(OpenMultimediaChannel {
9555 conference_id: 42.into(),
9556 passthrough_party_id: 9.into(),
9557 line_instance: 1,
9558 call_reference: 7.into(),
9559 payload: payload.clone(),
9560 conference_creator: false,
9561 encryption: None,
9562 stream_passthrough_id: 10,
9563 associated_stream_id: 0,
9564 source: MediaEndpointAddress {
9565 address: "192.0.2.31".parse().unwrap(),
9566 port: 16_000,
9567 },
9568 requested_address_type: IpAddressType::Ipv4,
9569 });
9570 let encoded = open.encode(ProtocolVersion::V17).unwrap();
9571 let decoded = ServerMessage::decode(
9572 FrameDecoder::new().push(&encoded).unwrap().remove(0),
9573 ProtocolVersion::V17,
9574 )
9575 .unwrap();
9576 assert_eq!(decoded.encode(ProtocolVersion::V17).unwrap(), encoded);
9577 assert!(matches!(
9578 open.encode(ProtocolVersion::V16),
9579 Err(CodecError::InvalidValue {
9580 message_id: id::OPEN_MULTIMEDIA_CHANNEL,
9581 field: "multimedia payload provenance",
9582 ..
9583 })
9584 ));
9585
9586 let start = ServerMessage::StartMultimediaTransmission(StartMultimediaTransmission {
9587 conference_id: 42.into(),
9588 passthrough_party_id: 9.into(),
9589 endpoint: MediaEndpointAddress {
9590 address: "192.0.2.32".parse().unwrap(),
9591 port: 16_002,
9592 },
9593 call_reference: 7.into(),
9594 payload,
9595 traffic_class: MediaTrafficClass::from_wire(136),
9596 encryption: None,
9597 stream_passthrough_id: 11,
9598 associated_stream_id: 0,
9599 });
9600 assert!(matches!(
9601 start.encode(ProtocolVersion::V17),
9602 Err(CodecError::InvalidValue {
9603 message_id: id::START_MULTIMEDIA_TRANSMISSION,
9604 field: "multimedia payload provenance",
9605 ..
9606 })
9607 ));
9608 }
9609
9610 #[test]
9611 fn capabilities_incompatible_with_outer_compression_remain_opaque_and_lossless() {
9612 let message = ServerMessage::OpenMultimediaChannel(OpenMultimediaChannel {
9613 conference_id: 42.into(),
9614 passthrough_party_id: 9.into(),
9615 line_instance: 1,
9616 call_reference: 7.into(),
9617 payload: typed_video_payload(MultimediaVideoCapabilityArm::H264 {
9618 profile: 100,
9619 level: 42,
9620 custom_max_mbps: 40_500,
9621 custom_max_fs: 1_620,
9622 custom_max_dpb: 8_100,
9623 custom_max_br_and_cpb: 10_000,
9624 }),
9625 conference_creator: false,
9626 encryption: None,
9627 stream_passthrough_id: 10,
9628 associated_stream_id: 0,
9629 source: MediaEndpointAddress {
9630 address: "192.0.2.31".parse().unwrap(),
9631 port: 16_000,
9632 },
9633 requested_address_type: IpAddressType::Ipv4,
9634 });
9635 let mut mismatched = message.encode(ProtocolVersion::V17).unwrap();
9636 let compression_offset = super::wire::HEADER_SIZE + 8;
9637 mismatched[compression_offset..compression_offset + 4]
9638 .copy_from_slice(&Codec::H263.wire_value().to_le_bytes());
9639
9640 let decoded = ServerMessage::decode(
9641 FrameDecoder::new().push(&mismatched).unwrap().remove(0),
9642 ProtocolVersion::V17,
9643 )
9644 .unwrap();
9645 let ServerMessage::OpenMultimediaChannel(open) = &decoded else {
9646 panic!("multimedia message decoded as a different command");
9647 };
9648 assert_eq!(open.payload.codec(), Codec::H263);
9649 assert_eq!(open.payload.compression_codec(), Codec::H263);
9650 assert_eq!(open.payload.video_capability(), None);
9651 assert_eq!(decoded.encode(ProtocolVersion::V17).unwrap(), mismatched);
9652 assert_ne!(decoded, message);
9653
9654 let mut invalid_payload_number = mismatched;
9655 let descriptor_offset = super::wire::HEADER_SIZE + 20;
9656 invalid_payload_number[descriptor_offset + 4..descriptor_offset + 8]
9657 .copy_from_slice(&128_u32.to_le_bytes());
9658 assert!(matches!(
9659 ServerMessage::decode(
9660 FrameDecoder::new()
9661 .push(&invalid_payload_number)
9662 .unwrap()
9663 .remove(0),
9664 ProtocolVersion::V17,
9665 ),
9666 Err(CodecError::InvalidValue {
9667 field: "RTP payload number",
9668 value: 128,
9669 ..
9670 })
9671 ));
9672 }
9673
9674 #[test]
9675 fn typed_multimedia_video_arms_encode_at_the_evidenced_offsets() {
9676 let arms = [
9677 (
9678 MultimediaVideoCapabilityArm::H261 {
9679 temporal_spatial_trade_off_capability: 11,
9680 still_image_transmission: 12,
9681 },
9682 [11, 12, 0, 0, 0, 0],
9683 ),
9684 (
9685 MultimediaVideoCapabilityArm::H263 {
9686 capability_bitfield: 21,
9687 annex_n_and_w_future_use: 22,
9688 },
9689 [21, 22, 0, 0, 0, 0],
9690 ),
9691 (
9692 MultimediaVideoCapabilityArm::H263Plus {
9693 model_number: 31,
9694 bandwidth: 32,
9695 },
9696 [31, 32, 0, 0, 0, 0],
9697 ),
9698 (
9699 MultimediaVideoCapabilityArm::H264 {
9700 profile: 41,
9701 level: 42,
9702 custom_max_mbps: 43,
9703 custom_max_fs: 44,
9704 custom_max_dpb: 45,
9705 custom_max_br_and_cpb: 46,
9706 },
9707 [41, 42, 43, 44, 45, 46],
9708 ),
9709 ];
9710
9711 for (arm, expected_arm) in arms {
9712 let payload = typed_video_payload(arm);
9713 let bytes = multimedia_capability_to_wire(&payload);
9714 let words = multimedia_capability_words(bytes);
9715 assert_eq!(words[0], 1_024);
9716 assert_eq!(words[1], 2);
9717 assert_eq!(
9718 &words[2..6],
9719 &[
9720 VideoFormat::Cif4.wire_value(),
9721 1,
9722 VideoFormat::Cif.wire_value(),
9723 2,
9724 ]
9725 );
9726 assert_eq!(&words[6..12], &[0; 6]);
9727 assert_eq!(words[12], 7);
9728 assert_eq!(&words[13..], &expected_arm);
9729
9730 let decoded = decoded_multimedia_capability(bytes, arm.codec());
9731 let MultimediaCapabilityState::Video(decoded) = decoded else {
9732 panic!("typed codec arm was not decoded");
9733 };
9734 assert_eq!(decoded.arm(), arm);
9735 assert_eq!(decoded.picture_formats().len(), 2);
9736 let decoded_payload = MultimediaPayload::from_decoded(
9737 payload.descriptor(),
9738 MultimediaCapabilityState::Video(decoded),
9739 MultimediaPayloadDirection::Transmit,
9740 ProtocolVersion::V17,
9741 arm.codec(),
9742 );
9743 assert_eq!(multimedia_capability_to_wire(&decoded_payload), bytes);
9744 }
9745 }
9746
9747 #[test]
9748 fn multimedia_descriptor_carries_the_negotiated_rtp_mapping() {
9749 for (arm, expected_payload_number) in [
9750 (
9751 MultimediaVideoCapabilityArm::H261 {
9752 temporal_spatial_trade_off_capability: 0,
9753 still_image_transmission: 0,
9754 },
9755 31,
9756 ),
9757 (
9758 MultimediaVideoCapabilityArm::H263 {
9759 capability_bitfield: 0,
9760 annex_n_and_w_future_use: 0,
9761 },
9762 34,
9763 ),
9764 (
9765 MultimediaVideoCapabilityArm::H263Plus {
9766 model_number: 0,
9767 bandwidth: 0,
9768 },
9769 96,
9770 ),
9771 (
9772 MultimediaVideoCapabilityArm::H264 {
9773 profile: 0,
9774 level: 0,
9775 custom_max_mbps: 0,
9776 custom_max_fs: 0,
9777 custom_max_dpb: 0,
9778 custom_max_br_and_cpb: 0,
9779 },
9780 97,
9781 ),
9782 ] {
9783 let payload = typed_video_payload(arm);
9784 let descriptor = payload.descriptor();
9785 assert_eq!(descriptor.rfc_number(), 0);
9786 assert_eq!(descriptor.payload_number().get(), expected_payload_number);
9787 assert_eq!(payload.codec(), arm.codec());
9788 assert_eq!(
9789 encode(
9790 id::OPEN_MULTIMEDIA_CHANNEL,
9791 &WireMultimediaPayloadDescriptor::from(descriptor)
9792 )
9793 .unwrap(),
9794 [0, 0, 0, 0, expected_payload_number, 0, 0, 0,]
9795 );
9796 }
9797
9798 let payload = typed_video_payload(MultimediaVideoCapabilityArm::H263 {
9799 capability_bitfield: 0,
9800 annex_n_and_w_future_use: 0,
9801 });
9802 let descriptor = MultimediaPayloadDescriptor::new(4, payload.payload_number());
9803 assert_eq!(
9804 encode(
9805 id::OPEN_MULTIMEDIA_CHANNEL,
9806 &WireMultimediaPayloadDescriptor::from(descriptor),
9807 )
9808 .unwrap(),
9809 [4, 0, 0, 0, 34, 0, 0, 0]
9810 );
9811 }
9812
9813 #[test]
9814 fn multimedia_acknowledgements_use_distinct_versioned_layouts() {
9815 for (protocol, address, open_size, start_size) in [
9816 (ProtocolVersion::V16, "192.0.2.33".parse().unwrap(), 20, 24),
9817 (
9818 ProtocolVersion::V17,
9819 "2001:db8::33".parse().unwrap(),
9820 36,
9821 40,
9822 ),
9823 ] {
9824 let open =
9825 ClientMessage::OpenMultimediaReceiveChannelAck(OpenMultimediaReceiveChannelAck {
9826 status: MediaStatus::Ok,
9827 endpoint: MediaEndpointAddress {
9828 address,
9829 port: 16_000,
9830 },
9831 passthrough_party_id: 9.into(),
9832 call_reference: 7.into(),
9833 });
9834 let open_bytes = open.encode(protocol).unwrap();
9835 assert_eq!(open_bytes.len() - 12, open_size);
9836 assert_eq!(
9837 ClientMessage::decode_with_version(
9838 FrameDecoder::new().push(&open_bytes).unwrap().remove(0),
9839 protocol,
9840 )
9841 .unwrap(),
9842 open
9843 );
9844
9845 let start =
9846 ClientMessage::StartMultimediaTransmissionAck(StartMultimediaTransmissionAck {
9847 conference_id: 42.into(),
9848 passthrough_party_id: 9.into(),
9849 call_reference: 7.into(),
9850 endpoint: MediaEndpointAddress {
9851 address,
9852 port: 16_002,
9853 },
9854 status: MediaStatus::Ok,
9855 });
9856 let start_bytes = start.encode(protocol).unwrap();
9857 assert_eq!(start_bytes.len() - 12, start_size);
9858 assert_eq!(
9859 ClientMessage::decode_with_version(
9860 FrameDecoder::new().push(&start_bytes).unwrap().remove(0),
9861 protocol,
9862 )
9863 .unwrap(),
9864 start
9865 );
9866 }
9867 }
9868
9869 #[test]
9870 fn port_messages_switch_layouts_at_protocol_twenty() {
9871 for (protocol, request_size, close_size, response_size) in [
9872 (ProtocolVersion::V19, 16, 12, 24),
9873 (ProtocolVersion::V20, 24, 16, 44),
9874 ] {
9875 let extended = protocol.wire() >= 20;
9876 let request = ServerMessage::PortRequest(PortRequest {
9877 conference_id: 42.into(),
9878 call_reference: 7.into(),
9879 passthrough_party_id: 9.into(),
9880 transport: MediaTransport::Rtp,
9881 address_type: extended.then_some(IpAddressType::Ipv4AndIpv6),
9882 media_type: extended.then_some(MediaType::Audio),
9883 });
9884 let request_bytes = request.encode(protocol).unwrap();
9885 assert_eq!(request_bytes.len() - 12, request_size);
9886 assert_eq!(
9887 ServerMessage::decode(
9888 FrameDecoder::new().push(&request_bytes).unwrap().remove(0),
9889 protocol,
9890 )
9891 .unwrap(),
9892 request
9893 );
9894
9895 let close = ServerMessage::PortClose(PortClose {
9896 conference_id: 42.into(),
9897 call_reference: 7.into(),
9898 passthrough_party_id: 9.into(),
9899 media_type: extended.then_some(MediaType::Audio),
9900 });
9901 let close_bytes = close.encode(protocol).unwrap();
9902 assert_eq!(close_bytes.len() - 12, close_size);
9903 assert_eq!(
9904 ServerMessage::decode(
9905 FrameDecoder::new().push(&close_bytes).unwrap().remove(0),
9906 protocol,
9907 )
9908 .unwrap(),
9909 close
9910 );
9911
9912 let response = ControlMessage::PortResponse(PortEndpoint {
9913 conference_id: 42,
9914 call_reference: 7,
9915 passthrough_party_id: 9,
9916 address: if extended {
9917 "2001:db8::34".parse().unwrap()
9918 } else {
9919 "192.0.2.34".parse().unwrap()
9920 },
9921 rtp_port: 16_000,
9922 rtcp_port: 16_001,
9923 media_type: extended.then_some(MediaType::Audio),
9924 });
9925 let response_bytes = response.encode(protocol).unwrap();
9926 assert_eq!(response_bytes.len() - 12, response_size);
9927 assert_eq!(
9928 ControlMessage::decode(
9929 FrameDecoder::new().push(&response_bytes).unwrap().remove(0),
9930 protocol,
9931 )
9932 .unwrap(),
9933 response
9934 );
9935 }
9936 }
9937
9938 #[test]
9939 fn wire_encryption_rejects_invalid_lengths_without_debugging_secrets() {
9940 let encryption = WireEncryptionInfo {
9941 algorithm: EncryptionMethod::Aes128HmacSha1_80.wire_value(),
9942 key_length: 17,
9943 salt_length: 16,
9944 key: [0xa5; 16],
9945 salt: [0x5a; 16],
9946 mki_present: 1,
9947 key_derivation_rate: 64,
9948 };
9949 let debug = format!("{encryption:?}");
9950 assert!(debug.contains("<redacted>"));
9951 assert!(!debug.contains("165"));
9952 assert!(!debug.contains("90"));
9953
9954 let error = encryption
9955 .to_public(id::OPEN_MULTIMEDIA_CHANNEL)
9956 .unwrap_err();
9957 assert!(matches!(
9958 error,
9959 CodecError::SecretTooLong {
9960 field: "media encryption key",
9961 actual: 17,
9962 maximum: 16,
9963 }
9964 ));
9965 assert!(!error.to_string().contains("165"));
9966 }
9967
9968 #[test]
9969 fn wire_encryption_preserves_bytes_after_declared_lengths() {
9970 let wire = WireEncryptionInfo {
9971 algorithm: EncryptionMethod::Aes128HmacSha1_80.wire_value(),
9972 key_length: 1,
9973 salt_length: 1,
9974 key: [0xa5, 0x7f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
9975 salt: [0x5a, 0x6f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
9976 mki_present: 0,
9977 key_derivation_rate: 0,
9978 };
9979 let encryption = wire.to_public(id::OPEN_MULTIMEDIA_CHANNEL).unwrap();
9980
9981 assert_eq!(WireEncryptionInfo::from_public(encryption.as_ref()), wire);
9982 assert_eq!(encryption.as_ref().unwrap().key(), &[0xa5]);
9983 assert_eq!(encryption.as_ref().unwrap().salt(), &[0x5a]);
9984 }
9985
9986 #[test]
9987 fn announcement_messages_use_bounded_fixed_wire_layouts() {
9988 let message = ControlMessage::StartAnnouncement {
9989 announcements: vec![AnnouncementEntry {
9990 locale: 1,
9991 country: 46,
9992 tone: Tone::Zip,
9993 }],
9994 end_of_ack: EndOfAnnouncementAck::Required,
9995 conference_id: 42,
9996 matrix_conference_party_ids: vec![7, 9],
9997 hearing_conference_party_mask: 0b11,
9998 play_mode: AnnouncementPlayMode::Continuous,
9999 };
10000 let bytes = message.encode(ProtocolVersion::V22).unwrap();
10001 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10002 assert_eq!(frame.message_id, id::START_ANNOUNCEMENT);
10003 assert_eq!(frame.payload.len(), 464);
10004 assert_eq!(
10005 ControlMessage::decode(frame.clone(), ProtocolVersion::V22).unwrap(),
10006 message
10007 );
10008
10009 let mut truncated = frame;
10010 truncated.payload.pop();
10011 assert!(matches!(
10012 ControlMessage::decode(truncated, ProtocolVersion::V22),
10013 Err(CodecError::Truncated {
10014 message_id: id::START_ANNOUNCEMENT,
10015 needed: 464,
10016 actual: 463,
10017 })
10018 ));
10019
10020 for (message, expected_payload_len) in [
10021 (ControlMessage::StopAnnouncement { conference_id: 42 }, 4),
10022 (
10023 ControlMessage::AnnouncementFinish {
10024 conference_id: 42,
10025 play_status: AnnouncementPlayStatus::Unknown(3),
10026 },
10027 8,
10028 ),
10029 ] {
10030 let bytes = message.encode(ProtocolVersion::V22).unwrap();
10031 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10032 assert_eq!(frame.payload.len(), expected_payload_len);
10033 assert_eq!(
10034 ControlMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10035 message
10036 );
10037 }
10038 }
10039
10040 #[test]
10041 fn conference_lifecycle_messages_use_documented_wire_sizes() {
10042 let server_messages = [
10043 (
10044 ControlMessage::ClearConference {
10045 conference_id: 42.into(),
10046 service_number: 3,
10047 },
10048 id::CLEAR_CONFERENCE,
10049 8,
10050 ),
10051 (
10052 ControlMessage::CreateConferenceRequest(CreateConferenceRequest {
10053 conference_id: 42.into(),
10054 reserved_participants: 8,
10055 resource_type: ConferenceResourceType::Conference,
10056 application_id: 7.into(),
10057 application_conference_id: "festival-42".into(),
10058 application_data: "main-stage".into(),
10059 passthrough_data: vec![1, 2, 3],
10060 }),
10061 id::CREATE_CONFERENCE_REQ,
10062 80,
10063 ),
10064 (
10065 ControlMessage::DeleteConferenceRequest {
10066 conference_id: 42.into(),
10067 },
10068 id::DELETE_CONFERENCE_REQ,
10069 4,
10070 ),
10071 (
10072 ControlMessage::ModifyConferenceRequest(ModifyConferenceRequest {
10073 conference_id: 42.into(),
10074 reserved_participants: 12,
10075 application_id: 7.into(),
10076 application_conference_id: "festival-42".into(),
10077 application_data: "main-stage".into(),
10078 passthrough_data: vec![4, 5],
10079 }),
10080 id::MODIFY_CONFERENCE_REQ,
10081 76,
10082 ),
10083 (
10084 ControlMessage::AuditConferenceRequest,
10085 id::AUDIT_CONFERENCE_REQ,
10086 0,
10087 ),
10088 ];
10089 for (message, expected_id, expected_payload_len) in server_messages {
10090 let frame = FrameDecoder::new()
10091 .push(&message.encode(ProtocolVersion::V22).unwrap())
10092 .unwrap()
10093 .remove(0);
10094 assert_eq!(frame.message_id, expected_id);
10095 assert_eq!(frame.payload.len(), expected_payload_len);
10096 assert_eq!(
10097 ControlMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10098 message
10099 );
10100 }
10101
10102 let audit = AuditConferenceResponse {
10103 last: 1,
10104 entries: vec![AuditConferenceEntry {
10105 conference_id: 42.into(),
10106 resource_type: ConferenceResourceType::Conference,
10107 reserved_participants: 8,
10108 active_participants: 3,
10109 application_id: 7.into(),
10110 application_conference_id: "festival-42".into(),
10111 application_data: "main-stage".into(),
10112 }],
10113 };
10114 let client_messages = [
10115 (
10116 ControlMessage::CreateConferenceResponse(CreateConferenceResponse {
10117 conference_id: 42.into(),
10118 result: CreateConferenceResult::Ok,
10119 passthrough_data: vec![1, 2, 3],
10120 }),
10121 id::CREATE_CONFERENCE_RES,
10122 16,
10123 ),
10124 (
10125 ControlMessage::DeleteConferenceResponse {
10126 conference_id: 42.into(),
10127 result: DeleteConferenceResult::Ok,
10128 },
10129 id::DELETE_CONFERENCE_RES,
10130 8,
10131 ),
10132 (
10133 ControlMessage::ModifyConferenceResponse(ModifyConferenceResponse {
10134 conference_id: 42.into(),
10135 result: ModifyConferenceResult::Ok,
10136 passthrough_data: vec![4, 5],
10137 }),
10138 id::MODIFY_CONFERENCE_RES,
10139 16,
10140 ),
10141 (
10142 ControlMessage::AuditConferenceResponse(audit),
10143 id::AUDIT_CONFERENCE_RES,
10144 84,
10145 ),
10146 ];
10147 for (message, expected_id, expected_payload_len) in client_messages {
10148 let frame = FrameDecoder::new()
10149 .push(&message.encode(ProtocolVersion::V22).unwrap())
10150 .unwrap()
10151 .remove(0);
10152 assert_eq!(frame.message_id, expected_id);
10153 assert_eq!(frame.payload.len(), expected_payload_len);
10154 assert_eq!(
10155 ControlMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10156 message
10157 );
10158 }
10159 }
10160
10161 #[test]
10162 fn conference_participant_messages_and_application_changes_round_trip() {
10163 let participant = ConferenceParticipant {
10164 call_reference: 100.into(),
10165 presentation_restrictions: PartyInformationRestrictions::CALLING_NUMBER
10166 | PartyInformationRestrictions::LAST_REDIRECT_NAME,
10167 name: "Festival Caller".into(),
10168 number: "1001".into(),
10169 conference_name: "Main Stage".into(),
10170 };
10171 let server_messages = [
10172 (
10173 ControlMessage::AddParticipantRequest(AddParticipantRequest {
10174 conference_id: 42.into(),
10175 participant: participant.clone(),
10176 }),
10177 id::ADD_PARTICIPANT_REQ,
10178 108,
10179 ),
10180 (
10181 ControlMessage::DropParticipantRequest {
10182 conference_id: 42.into(),
10183 call_reference: 100.into(),
10184 },
10185 id::DROP_PARTICIPANT_REQ,
10186 8,
10187 ),
10188 (
10189 ControlMessage::AuditParticipantRequest {
10190 conference_id: 42.into(),
10191 },
10192 id::AUDIT_PARTICIPANT_REQ,
10193 4,
10194 ),
10195 ];
10196 for (message, expected_id, expected_payload_len) in server_messages {
10197 let frame = FrameDecoder::new()
10198 .push(&message.encode(ProtocolVersion::V22).unwrap())
10199 .unwrap()
10200 .remove(0);
10201 assert_eq!(frame.message_id, expected_id);
10202 assert_eq!(frame.payload.len(), expected_payload_len);
10203 assert_eq!(
10204 ControlMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10205 message
10206 );
10207 }
10208
10209 let client_messages = [
10210 (
10211 ControlMessage::AddParticipantResponse(AddParticipantResponse {
10212 conference_id: 42.into(),
10213 call_reference: 100.into(),
10214 result: AddParticipantResult::Ok,
10215 bridge_participant_id: BoundedBytes::try_from(vec![3; 257]).unwrap(),
10216 }),
10217 id::ADD_PARTICIPANT_RES,
10218 272,
10219 ),
10220 (
10221 ControlMessage::AuditParticipantResponse(AuditParticipantResponse {
10222 result: AuditParticipantResult::Ok,
10223 last: 1,
10224 conference_id: 42.into(),
10225 number_of_entries: 2,
10226 participant_entries: vec![1, 2, 3, 4],
10227 }),
10228 id::AUDIT_PARTICIPANT_RES,
10229 20,
10230 ),
10231 ];
10232 for (message, expected_id, expected_payload_len) in client_messages {
10233 let frame = FrameDecoder::new()
10234 .push(&message.encode(ProtocolVersion::V22).unwrap())
10235 .unwrap()
10236 .remove(0);
10237 assert_eq!(frame.message_id, expected_id);
10238 assert_eq!(frame.payload.len(), expected_payload_len);
10239 assert_eq!(
10240 ControlMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10241 message
10242 );
10243 }
10244
10245 let change = ConferenceParticipantChange {
10246 conference_id: 42.into(),
10247 participant,
10248 };
10249 let routing = ParticipantChangeRouting {
10250 application_id: 7.into(),
10251 line_instance: 1,
10252 transaction_id: 9.into(),
10253 sequence_flag: 1,
10254 display_priority: 2,
10255 application_instance_id: 3.into(),
10256 routing: 4,
10257 };
10258 let envelope = change.to_user_data_v1(routing).unwrap();
10259 assert_eq!(envelope.data.len(), 108);
10260 assert_eq!(
10261 ConferenceParticipantChange::from_user_data_v1(&envelope).unwrap(),
10262 change
10263 );
10264
10265 let mut mismatched = envelope;
10266 mismatched.conference_id += 1;
10267 assert!(matches!(
10268 ConferenceParticipantChange::from_user_data_v1(&mismatched),
10269 Err(CodecError::InvalidValue {
10270 field: "participant change conference ID",
10271 ..
10272 })
10273 ));
10274 }
10275
10276 #[test]
10277 fn participant_messages_enforce_text_and_audit_bounds() {
10278 let oversized = ControlMessage::AuditParticipantResponse(AuditParticipantResponse {
10279 result: AuditParticipantResult::Ok,
10280 last: 1,
10281 conference_id: 42.into(),
10282 number_of_entries: 1,
10283 participant_entries: vec![0; 257],
10284 });
10285 assert!(matches!(
10286 oversized.encode(ProtocolVersion::V22),
10287 Err(CodecError::CountTooLarge {
10288 field: "participant audit data",
10289 count: 257,
10290 maximum: 256,
10291 ..
10292 })
10293 ));
10294
10295 let mut oversized_payload = vec![0; 16 + 257];
10296 oversized_payload[8..12].copy_from_slice(&42_u32.to_le_bytes());
10297 assert!(matches!(
10298 ControlMessage::decode(
10299 Frame::new(22, id::AUDIT_PARTICIPANT_RES, oversized_payload),
10300 ProtocolVersion::V22,
10301 ),
10302 Err(CodecError::CountTooLarge {
10303 field: "participant audit data",
10304 count: 257,
10305 maximum: 256,
10306 ..
10307 })
10308 ));
10309
10310 let long_name = ControlMessage::AddParticipantRequest(AddParticipantRequest {
10311 conference_id: 42.into(),
10312 participant: ConferenceParticipant {
10313 call_reference: 100.into(),
10314 presentation_restrictions: PartyInformationRestrictions::empty(),
10315 name: "x".repeat(40),
10316 number: "1001".into(),
10317 conference_name: "Main Stage".into(),
10318 },
10319 });
10320 assert!(matches!(
10321 long_name.encode(ProtocolVersion::V22),
10322 Err(CodecError::TextTooLong {
10323 field: "participant name",
10324 actual: 40,
10325 maximum: 39,
10326 ..
10327 })
10328 ));
10329 }
10330
10331 #[test]
10332 fn multicast_media_layouts_cover_legacy_and_extended_addresses() {
10333 let acknowledgement = ClientMessage::MulticastMediaReceptionAck {
10334 status: MediaStatus::Ok,
10335 passthrough_party_id: 9.into(),
10336 call_reference: 7.into(),
10337 };
10338 let frame = FrameDecoder::new()
10339 .push(&acknowledgement.encode(ProtocolVersion::V3).unwrap())
10340 .unwrap()
10341 .remove(0);
10342 assert_eq!(frame.message_id, id::MULTICAST_MEDIA_RECEPTION_ACK);
10343 assert_eq!(frame.payload.len(), 12);
10344 assert_eq!(ClientMessage::decode(frame).unwrap(), acknowledgement);
10345
10346 let reception_v3 = ServerMessage::StartMulticastMediaReception(MulticastMediaReception {
10347 conference_id: 42.into(),
10348 passthrough_party_id: 9.into(),
10349 call_reference: 7.into(),
10350 address: "239.1.2.3".parse().unwrap(),
10351 port: 16_000,
10352 packet_millis: 20,
10353 codec: Codec::Pcmu,
10354 echo_cancellation: EchoCancellation::On,
10355 g723_bitrate: G723BitRate::Rate6_3,
10356 });
10357 let transmission_v3 =
10358 ServerMessage::StartMulticastMediaTransmission(MulticastMediaTransmission {
10359 conference_id: 42.into(),
10360 passthrough_party_id: 9.into(),
10361 call_reference: 7.into(),
10362 address: "239.1.2.3".parse().unwrap(),
10363 port: 16_002,
10364 packet_millis: 20,
10365 codec: Codec::Pcmu,
10366 precedence: 5,
10367 silence_suppression: 1,
10368 max_frames_per_packet: 2,
10369 g723_bitrate: G723BitRate::Rate5_3,
10370 });
10371 for (message, expected_id, expected_payload_len) in [
10372 (reception_v3, id::START_MULTICAST_MEDIA_RECEPTION, 36),
10373 (transmission_v3, id::START_MULTICAST_MEDIA_TRANSMISSION, 44),
10374 ] {
10375 let frame = FrameDecoder::new()
10376 .push(&message.encode(ProtocolVersion::V3).unwrap())
10377 .unwrap()
10378 .remove(0);
10379 assert_eq!(frame.message_id, expected_id);
10380 assert_eq!(frame.payload.len(), expected_payload_len);
10381 assert_eq!(
10382 ServerMessage::decode(frame, ProtocolVersion::V3).unwrap(),
10383 message
10384 );
10385 }
10386
10387 let reception_v17 = ServerMessage::StartMulticastMediaReception(MulticastMediaReception {
10388 conference_id: 43.into(),
10389 passthrough_party_id: 10.into(),
10390 call_reference: 8.into(),
10391 address: "ff3e::1234".parse().unwrap(),
10392 port: 17_000,
10393 packet_millis: 30,
10394 codec: Codec::Pcma,
10395 echo_cancellation: EchoCancellation::Unknown(7),
10396 g723_bitrate: G723BitRate::Unknown(9),
10397 });
10398 let transmission_v17 =
10399 ServerMessage::StartMulticastMediaTransmission(MulticastMediaTransmission {
10400 conference_id: 43.into(),
10401 passthrough_party_id: 10.into(),
10402 call_reference: 8.into(),
10403 address: "ff3e::1234".parse().unwrap(),
10404 port: 17_002,
10405 packet_millis: 30,
10406 codec: Codec::Pcma,
10407 precedence: 6,
10408 silence_suppression: 2,
10409 max_frames_per_packet: 3,
10410 g723_bitrate: G723BitRate::Unknown(9),
10411 });
10412 for (message, expected_id, expected_payload_len) in [
10413 (reception_v17, id::START_MULTICAST_MEDIA_RECEPTION, 52),
10414 (transmission_v17, id::START_MULTICAST_MEDIA_TRANSMISSION, 60),
10415 ] {
10416 let frame = FrameDecoder::new()
10417 .push(&message.encode(ProtocolVersion::V17).unwrap())
10418 .unwrap()
10419 .remove(0);
10420 assert_eq!(frame.message_id, expected_id);
10421 assert_eq!(frame.payload.len(), expected_payload_len);
10422 assert_eq!(
10423 ServerMessage::decode(frame, ProtocolVersion::V17).unwrap(),
10424 message
10425 );
10426 }
10427
10428 for (message, expected_id) in [
10429 (
10430 ServerMessage::StopMulticastMediaReception {
10431 conference_id: 42.into(),
10432 passthrough_party_id: 9.into(),
10433 call_reference: 100.into(),
10434 },
10435 id::STOP_MULTICAST_MEDIA_RECEPTION,
10436 ),
10437 (
10438 ServerMessage::StopMulticastMediaTransmission {
10439 conference_id: 42.into(),
10440 passthrough_party_id: 9.into(),
10441 call_reference: 100.into(),
10442 },
10443 id::STOP_MULTICAST_MEDIA_TRANSMISSION,
10444 ),
10445 ] {
10446 let frame = FrameDecoder::new()
10447 .push(&message.encode(ProtocolVersion::V22).unwrap())
10448 .unwrap()
10449 .remove(0);
10450 assert_eq!(frame.message_id, expected_id);
10451 assert_eq!(frame.payload.len(), 12);
10452 assert_eq!(
10453 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10454 message
10455 );
10456 }
10457 }
10458
10459 #[test]
10460 fn legacy_multicast_rejects_ipv6_and_invalid_ports() {
10461 let ipv6 = ServerMessage::StartMulticastMediaReception(MulticastMediaReception {
10462 conference_id: 42.into(),
10463 passthrough_party_id: 9.into(),
10464 call_reference: 7.into(),
10465 address: "ff3e::1234".parse().unwrap(),
10466 port: 16_000,
10467 packet_millis: 20,
10468 codec: Codec::Pcmu,
10469 echo_cancellation: EchoCancellation::On,
10470 g723_bitrate: G723BitRate::Rate6_3,
10471 });
10472 assert!(matches!(
10473 ipv6.encode(ProtocolVersion::V15),
10474 Err(CodecError::InvalidValue {
10475 field: "IP address family for pre-v17 protocol",
10476 ..
10477 })
10478 ));
10479
10480 let mut payload = vec![0; 52];
10481 payload[8..12].copy_from_slice(&1_u32.to_le_bytes());
10482 payload[28..32].copy_from_slice(&70_000_u32.to_le_bytes());
10483 assert!(matches!(
10484 ServerMessage::decode(
10485 Frame::new(17, id::START_MULTICAST_MEDIA_RECEPTION, payload),
10486 ProtocolVersion::V17,
10487 ),
10488 Err(CodecError::InvalidValue {
10489 field: "multicast port",
10490 value: 70_000,
10491 ..
10492 })
10493 ));
10494 }
10495
10496 #[test]
10497 fn qos_control_messages_use_field_typed_service_layouts() {
10498 let flow = QosFlow {
10499 conference_id: 42.into(),
10500 call_reference: 7.into(),
10501 passthrough_party_id: 9.into(),
10502 address: "192.0.2.20".parse().unwrap(),
10503 port: 16_000,
10504 };
10505 let traffic = QosTrafficSpecification {
10506 codec: Codec::Pcmu,
10507 average_bit_rate: 64_000,
10508 burst_size: 1_200,
10509 peak_rate: 128_000,
10510 };
10511 let application = QosApplicationIdentifier {
10512 vendor_id: "Cisco".into(),
10513 version: "1".into(),
10514 application_name: "SCCP audio".into(),
10515 sub_application_id: "primary".into(),
10516 };
10517 let messages = [
10518 ControlMessage::QosReservationNotify {
10519 flow,
10520 direction: QosDirection::Send,
10521 },
10522 ControlMessage::QosErrorNotify {
10523 flow,
10524 direction: QosDirection::Send,
10525 error_code: QosErrorCode::ListenFailed,
10526 failure_node: "198.51.100.9".parse().unwrap(),
10527 rsvp_error_code: RsvpErrorCode::NoSenderInformation,
10528 rsvp_error_subcode: 5,
10529 rsvp_error_flags: 6,
10530 },
10531 ControlMessage::QosListen {
10532 flow,
10533 reservation_style: QosReservationStyle::SharedExplicit,
10534 maximum_retries: 3,
10535 retry_timer: 4,
10536 confirmation_required: true,
10537 preemption_priority: 5,
10538 defending_priority: 6,
10539 traffic,
10540 application: application.clone(),
10541 },
10542 ControlMessage::QosPath {
10543 flow,
10544 reservation_style: QosReservationStyle::SharedExplicit,
10545 maximum_retries: 3,
10546 retry_timer: 4,
10547 preemption_priority: 5,
10548 defending_priority: 6,
10549 traffic,
10550 application: application.clone(),
10551 },
10552 ControlMessage::QosTeardown {
10553 flow,
10554 direction: QosDirection::Send,
10555 },
10556 ControlMessage::UpdateDscp { flow, dscp: 46 },
10557 ControlMessage::QosModify {
10558 flow,
10559 direction: QosDirection::Send,
10560 traffic,
10561 application,
10562 },
10563 ];
10564 for (message, expected_size) in messages.into_iter().zip([24, 44, 172, 168, 24, 24, 152]) {
10565 let bytes = message.encode(ProtocolVersion::V22).unwrap();
10566 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10567 assert_eq!(frame.payload.len(), expected_size);
10568 assert_eq!(
10569 ControlMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10570 message
10571 );
10572 }
10573 }
10574
10575 #[test]
10576 fn fixed_layout_alignment_bytes_must_be_zero() {
10577 let register_ack = WireRegisterAck {
10578 keepalive_seconds: 30,
10579 date_template: *b"D/M/Y\0",
10580 alignment: [1, 0],
10581 secondary_keepalive_seconds: 30,
10582 protocol_features: [22, 0, 0, 0],
10583 };
10584 assert!(
10585 ServerMessage::decode(
10586 Frame::new(
10587 ProtocolVersion::V22.wire(),
10588 id::REGISTER_ACK,
10589 encode(id::REGISTER_ACK, ®ister_ack).unwrap(),
10590 ),
10591 ProtocolVersion::V22,
10592 )
10593 .is_err()
10594 );
10595
10596 let notification = WireMessageWaitingNotification {
10597 target_number: WireFixedText::new(id::MWI_NOTIFICATION, "target", "1001").unwrap(),
10598 control_number: WireFixedText::new(id::MWI_NOTIFICATION, "control", "5000").unwrap(),
10599 alignment: [1, 0],
10600 messages_waiting: 1,
10601 total_voicemail_new: 0,
10602 total_voicemail_old: 0,
10603 priority_voicemail_new: 0,
10604 priority_voicemail_old: 0,
10605 total_fax_new: 0,
10606 total_fax_old: 0,
10607 priority_fax_new: 0,
10608 priority_fax_old: 0,
10609 };
10610 let notification = encode(id::MWI_NOTIFICATION, ¬ification).unwrap();
10611 assert_eq!(notification.len(), 88);
10612 assert!(
10613 ControlMessage::decode(
10614 Frame::new(
10615 ProtocolVersion::V22.wire(),
10616 id::MWI_NOTIFICATION,
10617 notification,
10618 ),
10619 ProtocolVersion::V22,
10620 )
10621 .is_err()
10622 );
10623
10624 let response = WireMessageWaitingResponse {
10625 target_number: WireFixedText::new(id::MWI_RESPONSE, "target", "1001").unwrap(),
10626 alignment: [0, 1, 0],
10627 result: MessageWaitingResult::Ok.wire_value(),
10628 };
10629 let response = encode(id::MWI_RESPONSE, &response).unwrap();
10630 assert_eq!(response.len(), 32);
10631 assert!(
10632 ControlMessage::decode(
10633 Frame::new(ProtocolVersion::V22.wire(), id::MWI_RESPONSE, response,),
10634 ProtocolVersion::V22,
10635 )
10636 .is_err()
10637 );
10638
10639 let connection_statistics = WireConnectionStatisticsV19 {
10640 directory_number: WireFixedText::new(
10641 id::CONNECTION_STATISTICS_RES,
10642 "directory number",
10643 "2002",
10644 )
10645 .unwrap(),
10646 alignment: [1, 0, 0],
10647 call_reference: 42,
10648 processing: StatisticsProcessing::Clear.wire_value(),
10649 statistics: WireConnectionStatisticsTail {
10650 packets_sent: 0,
10651 octets_sent: 0,
10652 packets_received: 0,
10653 octets_received: 0,
10654 packets_lost: 0,
10655 jitter_millis: 0,
10656 latency_millis: 0,
10657 quality_size: 0,
10658 },
10659 quality: Vec::new(),
10660 };
10661 assert!(
10662 ClientMessage::decode_with_version(
10663 Frame::new(
10664 ProtocolVersion::V22.wire(),
10665 id::CONNECTION_STATISTICS_RES,
10666 encode(id::CONNECTION_STATISTICS_RES, &connection_statistics).unwrap(),
10667 ),
10668 ProtocolVersion::V22,
10669 )
10670 .is_err()
10671 );
10672
10673 let mut enbloc = FrameDecoder::new()
10674 .push(
10675 &ClientMessage::EnblocCall {
10676 called_party: "2001".into(),
10677 line_instance: 2,
10678 }
10679 .encode(ProtocolVersion::V19)
10680 .unwrap(),
10681 )
10682 .unwrap()
10683 .remove(0);
10684 enbloc.payload[25] = 1;
10685 assert!(ClientMessage::decode_with_version(enbloc, ProtocolVersion::V19).is_err());
10686
10687 let mut off_hook = FrameDecoder::new()
10688 .push(
10689 &ClientMessage::OffHookWithCallingParty {
10690 calling_party_number: "2001".into(),
10691 voice_mailbox: "5000".into(),
10692 line_instance: 2,
10693 }
10694 .encode(ProtocolVersion::V19)
10695 .unwrap(),
10696 )
10697 .unwrap()
10698 .remove(0);
10699 off_hook.payload[50] = 1;
10700 assert!(ClientMessage::decode_with_version(off_hook, ProtocolVersion::V19).is_err());
10701
10702 let mut dialed = FrameDecoder::new()
10703 .push(
10704 &ServerMessage::DialedNumber {
10705 number: "2001".into(),
10706 line_instance: 2,
10707 call_reference: 42,
10708 }
10709 .encode(ProtocolVersion::V19)
10710 .unwrap(),
10711 )
10712 .unwrap()
10713 .remove(0);
10714 dialed.payload[25] = 1;
10715 assert!(ServerMessage::decode(dialed, ProtocolVersion::V19).is_err());
10716
10717 let mut forwarding = FrameDecoder::new()
10718 .push(
10719 &ServerMessage::ForwardStatus {
10720 line_instance: 2,
10721 forward_all: Some("2001".into()),
10722 forward_busy: None,
10723 forward_no_answer: None,
10724 }
10725 .encode(ProtocolVersion::V19)
10726 .unwrap(),
10727 )
10728 .unwrap()
10729 .remove(0);
10730 forwarding.payload[37] = 1;
10731 assert!(ServerMessage::decode(forwarding, ProtocolVersion::V19).is_err());
10732 }
10733
10734 #[test]
10735 fn boolean_control_words_reject_non_boolean_values() {
10736 let recording = encode(
10737 id::RECORDING_STATUS,
10738 &WireRecordingStatus {
10739 call_reference: 7,
10740 active: 2,
10741 },
10742 )
10743 .unwrap();
10744 assert!(matches!(
10745 ServerMessage::decode(
10746 Frame::new(ProtocolVersion::V22.wire(), id::RECORDING_STATUS, recording),
10747 ProtocolVersion::V22,
10748 ),
10749 Err(CodecError::InvalidValue {
10750 field: "recording active",
10751 value: 2,
10752 ..
10753 })
10754 ));
10755
10756 let notification = WireMessageWaitingNotification {
10757 target_number: WireFixedText::new(id::MWI_NOTIFICATION, "target", "1001").unwrap(),
10758 control_number: WireFixedText::new(id::MWI_NOTIFICATION, "control", "5000").unwrap(),
10759 alignment: [0; 2],
10760 messages_waiting: 2,
10761 total_voicemail_new: 0,
10762 total_voicemail_old: 0,
10763 priority_voicemail_new: 0,
10764 priority_voicemail_old: 0,
10765 total_fax_new: 0,
10766 total_fax_old: 0,
10767 priority_fax_new: 0,
10768 priority_fax_old: 0,
10769 };
10770 let payload = encode(id::MWI_NOTIFICATION, ¬ification).unwrap();
10771 assert!(matches!(
10772 ControlMessage::decode(
10773 Frame::new(ProtocolVersion::V22.wire(), id::MWI_NOTIFICATION, payload),
10774 ProtocolVersion::V22,
10775 ),
10776 Err(CodecError::InvalidValue {
10777 field: "messages waiting",
10778 value: 2,
10779 ..
10780 })
10781 ));
10782
10783 let flow = QosFlow {
10784 conference_id: 1.into(),
10785 call_reference: 2.into(),
10786 passthrough_party_id: 3.into(),
10787 address: "192.0.2.1".parse().unwrap(),
10788 port: 16_000,
10789 };
10790 let traffic = QosTrafficSpecification {
10791 codec: Codec::Pcmu,
10792 average_bit_rate: 64_000,
10793 burst_size: 1_200,
10794 peak_rate: 128_000,
10795 };
10796 let application = QosApplicationIdentifier {
10797 vendor_id: "Cisco".into(),
10798 version: "1".into(),
10799 application_name: "SCCP audio".into(),
10800 sub_application_id: "primary".into(),
10801 };
10802 let mut qos_listen = FrameDecoder::new()
10803 .push(
10804 &ControlMessage::QosListen {
10805 flow,
10806 reservation_style: QosReservationStyle::SharedExplicit,
10807 maximum_retries: 3,
10808 retry_timer: 4,
10809 confirmation_required: true,
10810 preemption_priority: 5,
10811 defending_priority: 6,
10812 traffic,
10813 application,
10814 }
10815 .encode(ProtocolVersion::V22)
10816 .unwrap(),
10817 )
10818 .unwrap()
10819 .remove(0);
10820 qos_listen.payload[32..36].copy_from_slice(&2_u32.to_le_bytes());
10821 assert!(matches!(
10822 ControlMessage::decode(qos_listen, ProtocolVersion::V22),
10823 Err(CodecError::InvalidValue {
10824 field: "QoS confirmation required",
10825 value: 2,
10826 ..
10827 })
10828 ));
10829
10830 assert!(matches!(
10831 ControlMessage::UpdateDscp { flow, dscp: 64 }.encode(ProtocolVersion::V22),
10832 Err(CodecError::InvalidValue {
10833 field: "DSCP",
10834 value: 64,
10835 ..
10836 })
10837 ));
10838
10839 let invalid_dscp = encode(
10840 id::UPDATE_DSCP,
10841 &WireUpdateDscp {
10842 flow: qos_flow_to_wire(flow),
10843 dscp: 64,
10844 },
10845 )
10846 .unwrap();
10847 assert!(matches!(
10848 ControlMessage::decode(
10849 Frame::new(ProtocolVersion::V22.wire(), id::UPDATE_DSCP, invalid_dscp),
10850 ProtocolVersion::V22,
10851 ),
10852 Err(CodecError::InvalidValue {
10853 field: "DSCP",
10854 value: 64,
10855 ..
10856 })
10857 ));
10858 }
10859
10860 #[test]
10861 fn compact_multimedia_dtmf_and_addon_layouts_round_trip_exactly() {
10862 let open_ack = OpenMultimediaReceiveChannelAck {
10863 status: MediaStatus::Ok,
10864 endpoint: MediaEndpointAddress {
10865 address: "2001:db8::20".parse().unwrap(),
10866 port: 16_000,
10867 },
10868 passthrough_party_id: 9.into(),
10869 call_reference: 7.into(),
10870 };
10871 let start_ack = StartMultimediaTransmissionAck {
10872 conference_id: 42.into(),
10873 passthrough_party_id: 9.into(),
10874 call_reference: 7.into(),
10875 endpoint: MediaEndpointAddress {
10876 address: "2001:db8::20".parse().unwrap(),
10877 port: 16_000,
10878 },
10879 status: MediaStatus::Ok,
10880 };
10881 for (message, expected_len) in [
10882 (ClientMessage::OpenMultimediaReceiveChannelAck(open_ack), 48),
10883 (ClientMessage::StartMultimediaTransmissionAck(start_ack), 52),
10884 ] {
10885 let bytes = message.encode(ProtocolVersion::V22).unwrap();
10886 assert_eq!(bytes.len(), expected_len);
10887 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10888 assert_eq!(ClientMessage::decode(frame).unwrap(), message);
10889 }
10890
10891 let addon = ClientMessage::ExtensionDeviceCapabilities(ExtensionDeviceCapabilities {
10892 unknown_1: 1,
10893 unknown_2: 2,
10894 unknown_3: 3,
10895 description: "7914 sidecar".into(),
10896 });
10897 let bytes = addon.encode(ProtocolVersion::V22).unwrap();
10898 assert_eq!(bytes.len(), 176);
10899 assert_eq!(
10900 ClientMessage::decode(FrameDecoder::new().push(&bytes).unwrap().remove(0)).unwrap(),
10901 addon
10902 );
10903
10904 let dtmf = DtmfToneControl {
10905 tone: Tone::Dtmf5,
10906 conference_id: 42.into(),
10907 passthrough_party_id: 9,
10908 };
10909 for message in [
10910 ServerMessage::NotifyDtmfTone(dtmf),
10911 ServerMessage::SendDtmfTone(dtmf),
10912 ] {
10913 let bytes = message.encode(ProtocolVersion::V22).unwrap();
10914 assert_eq!(bytes.len(), 24);
10915 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10916 assert_eq!(
10917 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10918 message
10919 );
10920 }
10921
10922 let lifecycle = MultimediaStreamControl {
10923 conference_id: 42.into(),
10924 passthrough_party_id: 9.into(),
10925 call_reference: 7.into(),
10926 port_handling_flag: 1,
10927 };
10928 let flow = VideoFlowControl {
10929 conference_id: 42.into(),
10930 passthrough_party_id: 9.into(),
10931 call_reference: 7.into(),
10932 maximum_bit_rate: 512_000,
10933 };
10934 for message in [
10935 ServerMessage::StopMultimediaTransmission(lifecycle),
10936 ServerMessage::CloseMultimediaReceiveChannel(lifecycle),
10937 ServerMessage::FlowControlCommand(flow),
10938 ServerMessage::FlowControlNotify(flow),
10939 ] {
10940 let bytes = message.encode(ProtocolVersion::V22).unwrap();
10941 assert_eq!(bytes.len(), 28);
10942 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10943 assert_eq!(
10944 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10945 message
10946 );
10947 }
10948 let display = ServerMessage::VideoDisplayCommand {
10949 conference_id: 42.into(),
10950 call_reference: 7.into(),
10951 layout_id: 2,
10952 };
10953 let bytes = display.encode(ProtocolVersion::V22).unwrap();
10954 assert_eq!(bytes.len(), 24);
10955 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10956 assert_eq!(
10957 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10958 display
10959 );
10960
10961 let failure_detection = ServerMessage::StartMediaFailureDetection(MediaFailureDetection {
10962 conference_id: 42.into(),
10963 passthrough_party_id: 9,
10964 packet_millis: 20,
10965 codec: Codec::Pcmu,
10966 echo_cancellation: EchoCancellation::On,
10967 codec_qualifier: [1, 2, 3, 4],
10968 call_reference: 7.into(),
10969 });
10970 let bytes = failure_detection.encode(ProtocolVersion::V22).unwrap();
10971 assert_eq!(bytes.len(), 40);
10972 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10973 assert_eq!(
10974 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10975 failure_detection
10976 );
10977
10978 let dynamic = ServerMessage::ConfigStatus(ConfigurationStatus {
10979 device_name: "SEP001122334455".into(),
10980 station_user_id: 0xfeed,
10981 station_instance: 2,
10982 line_count: 6,
10983 speed_dial_count: 12,
10984 user_name: "festival".into(),
10985 server_name: "sccp.example.test".into(),
10986 });
10987 let bytes = dynamic.encode(ProtocolVersion::V22).unwrap();
10988 assert_eq!(bytes.len() % 4, 0);
10989 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
10990 assert_eq!(
10991 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
10992 dynamic
10993 );
10994 }
10995
10996 #[test]
10997 fn soft_key_template_keeps_cisco_event_positions() {
10998 let bytes = ServerMessage::SoftKeyTemplate {
10999 actions: SoftKeyProfile::default().template_actions(),
11000 }
11001 .encode(ProtocolVersion::V22)
11002 .unwrap();
11003 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11004 let payload: WireSoftKeyTemplate = decode(frame.message_id, &frame.payload).unwrap();
11005 assert_eq!(payload.count, 32);
11006 assert_eq!(payload.definitions[31].event, 32);
11007 assert_eq!(
11008 payload
11009 .definitions
11010 .iter()
11011 .map(|definition| definition.event)
11012 .collect::<Vec<_>>(),
11013 (1..=32).collect::<Vec<_>>()
11014 );
11015 }
11016
11017 #[test]
11018 fn line_only_button_template_preserves_the_fixed_wire_layout() {
11019 let message = ServerMessage::ButtonTemplate {
11020 buttons: vec![ButtonTemplateEntry {
11021 instance: 1,
11022 button_type: ButtonType::Line,
11023 }],
11024 };
11025 let bytes = message.encode(ProtocolVersion::V22).unwrap();
11026 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11027 let payload: WireButtonTemplate = decode(frame.message_id, &frame.payload).unwrap();
11028
11029 assert_eq!(payload.offset, 0);
11030 assert_eq!(payload.count, 42);
11031 assert_eq!(payload.total, 42);
11032 assert_eq!(
11033 payload.definitions[0],
11034 WireButtonDefinition {
11035 instance: 1,
11036 button_type: ButtonType::Line.wire_value() as u8,
11037 }
11038 );
11039 assert!(
11040 payload.definitions[1..]
11041 .iter()
11042 .all(|definition| *definition == WireButtonDefinition::default())
11043 );
11044 assert_eq!(
11045 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
11046 message
11047 );
11048 }
11049
11050 #[test]
11051 fn mixed_button_template_round_trips_ordered_semantic_entries() {
11052 let message = ServerMessage::ButtonTemplate {
11053 buttons: vec![
11054 ButtonTemplateEntry {
11055 instance: 1,
11056 button_type: ButtonType::Line,
11057 },
11058 ButtonTemplateEntry {
11059 instance: 2,
11060 button_type: ButtonType::SpeedDial,
11061 },
11062 ButtonTemplateEntry {
11063 instance: 3,
11064 button_type: ButtonType::DoNotDisturb,
11065 },
11066 ButtonTemplateEntry {
11067 instance: 4,
11068 button_type: ButtonType::ServiceUrl,
11069 },
11070 ButtonTemplateEntry {
11071 instance: 0,
11072 button_type: ButtonType::Unused,
11073 },
11074 ButtonTemplateEntry {
11075 instance: 5,
11076 button_type: ButtonType::BlfSpeedDial,
11077 },
11078 ],
11079 };
11080
11081 let bytes = message.encode(ProtocolVersion::V22).unwrap();
11082 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11083 assert_eq!(
11084 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
11085 message
11086 );
11087 }
11088
11089 #[test]
11090 fn button_template_rejects_unrepresentable_entries_and_counts() {
11091 let too_many = ServerMessage::ButtonTemplate {
11092 buttons: vec![
11093 ButtonTemplateEntry {
11094 instance: 1,
11095 button_type: ButtonType::Line,
11096 };
11097 43
11098 ],
11099 };
11100 assert!(matches!(
11101 too_many.encode(ProtocolVersion::V22),
11102 Err(CodecError::CountTooLarge { .. })
11103 ));
11104
11105 let large_instance = ServerMessage::ButtonTemplate {
11106 buttons: vec![ButtonTemplateEntry {
11107 instance: 256,
11108 button_type: ButtonType::Line,
11109 }],
11110 };
11111 assert!(matches!(
11112 large_instance.encode(ProtocolVersion::V22),
11113 Err(CodecError::InvalidValue {
11114 field: "button instance",
11115 ..
11116 })
11117 ));
11118
11119 let payload = WireButtonTemplate {
11120 offset: 0,
11121 count: 43,
11122 total: 42,
11123 definitions: [WireButtonDefinition::default(); 42],
11124 };
11125 let frame = Frame::new(
11126 ProtocolVersion::V22.wire(),
11127 id::BUTTON_TEMPLATE,
11128 encode(id::BUTTON_TEMPLATE, &payload).unwrap(),
11129 );
11130 assert!(matches!(
11131 ServerMessage::decode(frame, ProtocolVersion::V22),
11132 Err(CodecError::CountTooLarge {
11133 field: "button definitions in message",
11134 ..
11135 })
11136 ));
11137
11138 let payload = WireButtonTemplate {
11139 offset: 1,
11140 count: 42,
11141 total: 42,
11142 definitions: [WireButtonDefinition::default(); 42],
11143 };
11144 let frame = Frame::new(
11145 ProtocolVersion::V22.wire(),
11146 id::BUTTON_TEMPLATE,
11147 encode(id::BUTTON_TEMPLATE, &payload).unwrap(),
11148 );
11149 assert!(matches!(
11150 ServerMessage::decode(frame, ProtocolVersion::V22),
11151 Err(CodecError::InvalidValue {
11152 field: "button template range",
11153 ..
11154 })
11155 ));
11156 }
11157
11158 #[test]
11159 fn station_statuses_select_and_round_trip_dynamic_layouts() {
11160 let cases = [
11161 (
11162 ServerMessage::LineStatus {
11163 instance: 3,
11164 number: "1003".into(),
11165 display_name: "A dynamic line label".into(),
11166 },
11167 ProtocolVersion::V8,
11168 id::LINE_STAT,
11169 ),
11170 (
11171 ServerMessage::LineStatus {
11172 instance: 3,
11173 number: "1003".into(),
11174 display_name: "A dynamic line label".into(),
11175 },
11176 ProtocolVersion::V9,
11177 id::LINE_STAT_DYNAMIC,
11178 ),
11179 (
11180 ServerMessage::SpeedDialStatus {
11181 instance: 4,
11182 number: "2004".into(),
11183 display_name: "Warehouse".into(),
11184 },
11185 ProtocolVersion::new(14).unwrap(),
11186 id::SPEED_DIAL_STAT,
11187 ),
11188 (
11189 ServerMessage::SpeedDialStatus {
11190 instance: 4,
11191 number: "2004".into(),
11192 display_name: "Warehouse".into(),
11193 },
11194 ProtocolVersion::V15,
11195 id::SPEED_DIAL_STAT_DYNAMIC,
11196 ),
11197 (
11198 ServerMessage::FeatureStatus {
11199 instance: 5,
11200 button_type: ButtonType::DoNotDisturb,
11201 label: "Do not disturb".into(),
11202 state: 0x0002_0101,
11203 },
11204 ProtocolVersion::V22,
11205 id::FEATURE_STAT,
11206 ),
11207 (
11208 ServerMessage::ServiceUrlStatus {
11209 index: 6,
11210 url: "http://services.invalid/directory".into(),
11211 label: "Directory".into(),
11212 extension_text: String::new(),
11213 },
11214 ProtocolVersion::V8,
11215 id::SERVICE_URL_STAT,
11216 ),
11217 (
11218 ServerMessage::ServiceUrlStatus {
11219 index: 6,
11220 url: "http://services.invalid/directory".into(),
11221 label: "Directory".into(),
11222 extension_text: String::new(),
11223 },
11224 ProtocolVersion::V9,
11225 id::SERVICE_URL_STAT_DYNAMIC,
11226 ),
11227 ];
11228
11229 for (message, protocol, expected_id) in cases {
11230 let bytes = message.encode(protocol).unwrap();
11231 assert_eq!(bytes.len() % 4, 0, "message 0x{expected_id:04x}");
11232 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11233 assert_eq!(frame.message_id, expected_id);
11234 assert_eq!(ServerMessage::decode(frame, protocol).unwrap(), message);
11235 }
11236
11237 let message = ServerMessage::FeatureStatus {
11238 instance: 5,
11239 button_type: ButtonType::DoNotDisturb,
11240 label: "Do not disturb".into(),
11241 state: 0x0002_0101,
11242 };
11243 let session =
11244 StationSessionContext::new(ProtocolVersion::V8, PhoneFeatures::DYNAMIC_MESSAGES);
11245 let bytes = message.encode_for_session(session).unwrap();
11246 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11247 assert_eq!(frame.message_id, id::FEATURE_STAT_DYNAMIC);
11248 assert_eq!(
11249 ServerMessage::decode(frame, ProtocolVersion::V8).unwrap(),
11250 message
11251 );
11252 }
11253
11254 #[test]
11255 fn configuration_status_is_lossless_across_session_selected_layouts() {
11256 let message = ServerMessage::ConfigStatus(ConfigurationStatus {
11257 device_name: "SEP001122334455".into(),
11258 station_user_id: 17,
11259 station_instance: 2,
11260 line_count: 6,
11261 speed_dial_count: 12,
11262 user_name: "festival".into(),
11263 server_name: "sccp.example.test".into(),
11264 });
11265 for (session, expected_id) in [
11266 (
11267 StationSessionContext::from(ProtocolVersion::V8),
11268 id::CONFIG_STAT,
11269 ),
11270 (
11271 StationSessionContext::new(ProtocolVersion::V8, PhoneFeatures::DYNAMIC_MESSAGES),
11272 id::CONFIG_STAT_DYNAMIC,
11273 ),
11274 (
11275 StationSessionContext::from(ProtocolVersion::V9),
11276 id::CONFIG_STAT_DYNAMIC,
11277 ),
11278 ] {
11279 let bytes = message.encode_for_session(session).unwrap();
11280 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11281 assert_eq!(frame.message_id, expected_id);
11282 assert_eq!(
11283 ServerMessage::decode(frame, session.protocol).unwrap(),
11284 message
11285 );
11286 }
11287 }
11288
11289 #[test]
11290 fn dynamic_call_information_uses_the_versioned_string_count() {
11291 let message = ServerMessage::CallInfo {
11292 info: CallInfo {
11293 direction: crate::types::CallDirection::Inbound,
11294 calling_name: "Alice".into(),
11295 calling_number: "1001".into(),
11296 called_name: "Bob".into(),
11297 called_number: "2001".into(),
11298 original_called_name: "Carol".into(),
11299 original_called_number: "3001".into(),
11300 last_redirecting_name: "Dave".into(),
11301 last_redirecting_number: "4001".into(),
11302 original_redirect_reason: 2,
11303 last_redirect_reason: 4,
11304 party_restrictions: 0,
11305 },
11306 line_instance: 2,
11307 call_reference: 42,
11308 };
11309
11310 for (protocol, count) in [
11311 (ProtocolVersion::V15, 12),
11312 (ProtocolVersion::V16, 13),
11313 (ProtocolVersion::V18, 13),
11314 (ProtocolVersion::V19, 15),
11315 ] {
11316 let bytes = message.encode(protocol).unwrap();
11317 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11318 assert_eq!(frame.message_id, id::CALL_INFO_DYNAMIC);
11319 assert_eq!(
11320 decode_dynamic_texts(frame.message_id, &frame.payload, 32, count)
11321 .unwrap()
11322 .len(),
11323 count
11324 );
11325 assert_eq!(ServerMessage::decode(frame, protocol).unwrap(), message);
11326 }
11327 }
11328
11329 #[test]
11330 fn dynamic_service_status_adds_the_extension_field_from_version_nineteen() {
11331 let unsupported = ServerMessage::ServiceUrlStatus {
11332 index: 3,
11333 url: "http://services.invalid/directory".into(),
11334 label: "Directory".into(),
11335 extension_text: "extension".into(),
11336 };
11337 assert!(matches!(
11338 unsupported.encode(ProtocolVersion::V18),
11339 Err(CodecError::InvalidValue {
11340 field: "service URL extension for this protocol version",
11341 ..
11342 })
11343 ));
11344
11345 let before = ServerMessage::ServiceUrlStatus {
11346 index: 3,
11347 url: "http://services.invalid/directory".into(),
11348 label: "Directory".into(),
11349 extension_text: String::new(),
11350 };
11351 let bytes = before.encode(ProtocolVersion::V18).unwrap();
11352 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11353 assert_eq!(
11354 decode_dynamic_texts(frame.message_id, &frame.payload, 4, 2).unwrap(),
11355 ["http://services.invalid/directory", "Directory"]
11356 );
11357 assert_eq!(
11358 ServerMessage::decode(frame, ProtocolVersion::V18).unwrap(),
11359 before
11360 );
11361
11362 let from = ServerMessage::ServiceUrlStatus {
11363 index: 3,
11364 url: "http://services.invalid/directory".into(),
11365 label: "Directory".into(),
11366 extension_text: "extension".into(),
11367 };
11368 let bytes = from.encode(ProtocolVersion::V19).unwrap();
11369 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11370 assert_eq!(
11371 decode_dynamic_texts(frame.message_id, &frame.payload, 4, 3).unwrap(),
11372 [
11373 "http://services.invalid/directory",
11374 "Directory",
11375 "extension"
11376 ]
11377 );
11378 assert_eq!(
11379 ServerMessage::decode(frame, ProtocolVersion::V19).unwrap(),
11380 from
11381 );
11382 }
11383
11384 #[test]
11385 fn dynamic_7961_line_status_has_cisco_word_padding() {
11386 let bytes = ServerMessage::LineStatus {
11387 instance: 1,
11388 number: "1006".into(),
11389 display_name: "1006".into(),
11390 }
11391 .encode(ProtocolVersion::V22)
11392 .unwrap();
11393 assert_eq!(bytes.len(), 36);
11394 assert_eq!(&bytes[..4], &28_u32.to_le_bytes());
11395 assert_eq!(
11396 &bytes[12..],
11397 b"\x01\0\0\0\x0f\0\0\x001006\x001006\x001006\0\0"
11398 );
11399 }
11400
11401 #[test]
11402 fn dynamic_station_decoders_reject_missing_or_nonzero_word_padding() {
11403 let bytes = ServerMessage::LineStatus {
11404 instance: 1,
11405 number: "1006".into(),
11406 display_name: "1006".into(),
11407 }
11408 .encode(ProtocolVersion::V22)
11409 .unwrap();
11410 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11411
11412 let mut missing_padding = frame.clone();
11413 missing_padding.payload.pop();
11414 assert!(matches!(
11415 ServerMessage::decode(missing_padding, ProtocolVersion::V22),
11416 Err(CodecError::InvalidAlignment { actual: 23, .. })
11417 ));
11418
11419 let mut nonzero_padding = frame.clone();
11420 *nonzero_padding.payload.last_mut().unwrap() = 0x7f;
11421 assert!(matches!(
11422 ServerMessage::decode(nonzero_padding, ProtocolVersion::V22),
11423 Err(CodecError::TrailingBytes { count: 1, .. })
11424 ));
11425
11426 let mut extension = frame;
11427 extension.payload.extend_from_slice(&[0; 4]);
11428 assert!(matches!(
11429 ServerMessage::decode(extension, ProtocolVersion::V22),
11430 Err(CodecError::TrailingBytes { count: 5, .. })
11431 ));
11432 }
11433
11434 #[test]
11435 fn dynamic_display_decoders_validate_the_same_padding_contract() {
11436 let bytes = ServerMessage::DisplayNotify {
11437 timeout_seconds: 4,
11438 text: "status".into(),
11439 }
11440 .encode(ProtocolVersion::V22)
11441 .unwrap();
11442 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11443 assert_eq!(frame.payload.len() % 4, 0);
11444
11445 let mut bad = frame;
11446 bad.payload.extend_from_slice(&[0, 0, 0, 1]);
11447 assert!(matches!(
11448 ServerMessage::decode(bad, ProtocolVersion::V22),
11449 Err(CodecError::TrailingBytes { .. })
11450 ));
11451 }
11452
11453 #[test]
11454 fn legacy_station_labels_use_the_configured_single_byte_code_page() {
11455 let message = ServerMessage::LineStatus {
11456 instance: 1,
11457 number: "1001".into(),
11458 display_name: "Räksmörgås".into(),
11459 };
11460 let latin1 = message
11461 .encode_for_legacy_station(ProtocolVersion::V3, LegacyCodePage::Iso8859_1)
11462 .unwrap();
11463 let latin1 = FrameDecoder::new().push(&latin1).unwrap().remove(0);
11464 let expected = b"R\xe4ksm\xf6rg\xe5s";
11465 assert!(
11466 latin1
11467 .payload
11468 .windows(expected.len())
11469 .any(|bytes| bytes == expected)
11470 );
11471
11472 let ascii = message
11473 .encode_for_legacy_station(ProtocolVersion::V3, LegacyCodePage::Ascii)
11474 .unwrap();
11475 let ascii = FrameDecoder::new().push(&ascii).unwrap().remove(0);
11476 assert!(
11477 ascii
11478 .payload
11479 .windows(10)
11480 .any(|bytes| bytes == b"R?ksm?rg?s")
11481 );
11482
11483 let utf8 = message.encode(ProtocolVersion::V3).unwrap();
11484 let utf8 = FrameDecoder::new().push(&utf8).unwrap().remove(0);
11485 assert!(
11486 utf8.payload
11487 .windows(13)
11488 .any(|bytes| bytes == "Räksmörgås".as_bytes())
11489 );
11490 }
11491
11492 #[test]
11493 fn dynamic_station_statuses_support_extended_labels_and_require_terminators() {
11494 let label = "A label that is intentionally longer than the static forty-byte field";
11495 for message in [
11496 ServerMessage::LineStatus {
11497 instance: 1,
11498 number: "1001".into(),
11499 display_name: label.into(),
11500 },
11501 ServerMessage::ServiceUrlStatus {
11502 index: 3,
11503 url: "http://services.invalid/directory".into(),
11504 label: label.into(),
11505 extension_text: String::new(),
11506 },
11507 ] {
11508 let bytes = message.encode(ProtocolVersion::V17).unwrap();
11509 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11510 assert_eq!(
11511 ServerMessage::decode(frame, ProtocolVersion::V17).unwrap(),
11512 message
11513 );
11514 }
11515
11516 let feature = ServerMessage::FeatureStatus {
11517 instance: 2,
11518 button_type: ButtonType::DoNotDisturb,
11519 label: label.into(),
11520 state: 1,
11521 };
11522 let session =
11523 StationSessionContext::new(ProtocolVersion::V8, PhoneFeatures::DYNAMIC_MESSAGES);
11524 let bytes = feature.encode_for_session(session).unwrap();
11525 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11526 assert_eq!(frame.message_id, id::FEATURE_STAT_DYNAMIC);
11527 assert_eq!(
11528 ServerMessage::decode(frame, ProtocolVersion::V8).unwrap(),
11529 feature
11530 );
11531
11532 let unterminated_service = Frame::new(
11533 ProtocolVersion::V17.wire(),
11534 id::SERVICE_URL_STAT_DYNAMIC,
11535 [3_u32.to_le_bytes().as_slice(), b"x\0YZ"].concat(),
11536 );
11537 assert!(matches!(
11538 ServerMessage::decode(unterminated_service, ProtocolVersion::V17),
11539 Err(CodecError::Truncated { .. })
11540 ));
11541 }
11542
11543 #[test]
11544 fn call_info_layouts_preserve_redirecting_and_presentation_fields() {
11545 let info = CallInfo {
11546 direction: crate::types::CallDirection::Inbound,
11547 calling_name: "Festival Caller".into(),
11548 calling_number: "1001".into(),
11549 called_name: "Festival Phone".into(),
11550 called_number: "1006".into(),
11551 original_called_name: "Reception".into(),
11552 original_called_number: "1000".into(),
11553 last_redirecting_name: "Front Desk".into(),
11554 last_redirecting_number: "1002".into(),
11555 original_redirect_reason: 4,
11556 last_redirect_reason: 2,
11557 party_restrictions: 0xf,
11558 };
11559 for (protocol, expected_id) in [
11560 (ProtocolVersion::V3, id::CALL_INFO),
11561 (ProtocolVersion::V8, id::CALL_INFO),
11562 (ProtocolVersion::V16, id::CALL_INFO_DYNAMIC),
11563 (ProtocolVersion::V22, id::CALL_INFO_DYNAMIC),
11564 ] {
11565 let message = ServerMessage::CallInfo {
11566 info: info.clone(),
11567 line_instance: 1,
11568 call_reference: 42,
11569 };
11570 let bytes = message.encode(protocol).unwrap();
11571 assert_eq!(bytes.len() % 4, 0, "message 0x{expected_id:04x}");
11572 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11573 assert_eq!(frame.message_id, expected_id);
11574 assert_eq!(ServerMessage::decode(frame, protocol).unwrap(), message);
11575 }
11576
11577 let bytes = ServerMessage::DisplayPrompt {
11578 timeout_seconds: 0,
11579 text: "From Festival Caller (1001)".into(),
11580 line_instance: 1,
11581 call_reference: 42,
11582 }
11583 .encode(ProtocolVersion::V22)
11584 .unwrap();
11585 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11586 assert_eq!(frame.message_id, id::DISPLAY_DYNAMIC_PROMPT_STATUS);
11587 assert_eq!(
11588 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
11589 ServerMessage::DisplayPrompt {
11590 timeout_seconds: 0,
11591 text: "From Festival Caller (1001)".into(),
11592 line_instance: 1,
11593 call_reference: 42,
11594 }
11595 );
11596 }
11597
11598 #[test]
11599 fn notification_frames_select_static_or_dynamic_layout_and_keep_priority_six() {
11600 for (protocol, expected_id) in [
11601 (ProtocolVersion::V3, id::DISPLAY_PRIORITY_NOTIFY),
11602 (ProtocolVersion::V22, id::DISPLAY_DYNAMIC_PRIORITY_NOTIFY),
11603 ] {
11604 let message = ServerMessage::DisplayPriorityNotify {
11605 timeout_seconds: 10,
11606 priority: NotificationPriority::Timed,
11607 text: "Status line".into(),
11608 };
11609 let bytes = message.encode(protocol).unwrap();
11610 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11611 assert_eq!(frame.message_id, expected_id);
11612 assert_eq!(ServerMessage::decode(frame, protocol).unwrap(), message);
11613 }
11614
11615 let message = ServerMessage::DisplayNotify {
11616 timeout_seconds: 3,
11617 text: "Dynamic notification text longer than thirty-one bytes".into(),
11618 };
11619 let bytes = message.encode(ProtocolVersion::V22).unwrap();
11620 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11621 assert_eq!(frame.message_id, id::DISPLAY_DYNAMIC_NOTIFY);
11622 assert_eq!(
11623 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
11624 message
11625 );
11626 }
11627
11628 #[test]
11629 fn call_state_uses_cisco_visibility_then_precedence_layout() {
11630 let bytes = ServerMessage::CallState {
11631 state: CallState::RingIn,
11632 line_instance: 1,
11633 call_reference: 42,
11634 }
11635 .encode(ProtocolVersion::V22)
11636 .unwrap();
11637 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11638 let words = (0..frame.payload.len() / 4)
11639 .map(|index| {
11640 let offset = index * 4;
11641 u32::from_le_bytes([
11642 frame.payload[offset],
11643 frame.payload[offset + 1],
11644 frame.payload[offset + 2],
11645 frame.payload[offset + 3],
11646 ])
11647 })
11648 .collect::<Vec<_>>();
11649
11650 assert_eq!(
11651 words,
11652 vec![CallState::RingIn.wire_value(), 1, 42, 0, 2, 0],
11653 "CallState is state, line, call, visibility, priority, domain"
11654 );
11655
11656 for (state, expected) in [
11657 (CallState::OffHook, 3),
11658 (CallState::Proceed, 3),
11659 (CallState::Connected, 3),
11660 (CallState::RingOut, 4),
11661 ] {
11662 let bytes = ServerMessage::CallState {
11663 state,
11664 line_instance: 1,
11665 call_reference: 42,
11666 }
11667 .encode(ProtocolVersion::V22)
11668 .unwrap();
11669 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11670 assert_eq!(
11671 u32::from_le_bytes(frame.payload[16..20].try_into().unwrap()),
11672 expected,
11673 "wrong precedence for {state:?}"
11674 );
11675 }
11676 }
11677
11678 #[test]
11679 fn soft_key_sets_and_masks_only_advertise_implemented_actions() {
11680 let profile = SoftKeyProfile::default();
11681 let bytes = ServerMessage::SoftKeySet {
11682 profile: profile.clone(),
11683 }
11684 .encode(ProtocolVersion::V22)
11685 .unwrap();
11686 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11687 let payload: WireSoftKeySet = decode(frame.message_id, &frame.payload).unwrap();
11688
11689 assert_eq!(
11690 &payload.sets[KeyMode::RingIn.wire_value() as usize].template_indexes[..2],
11691 &[
11692 SoftKey::Answer.wire_value() as u8,
11693 SoftKey::EndCall.wire_value() as u8
11694 ]
11695 );
11696 assert_eq!(profile.valid_mask(KeyMode::RingIn), 0b11);
11697 assert_eq!(profile.valid_mask(KeyMode::Connected), 0b111);
11698 assert_eq!(profile.valid_mask(KeyMode::Empty), 0);
11699 }
11700
11701 #[test]
11702 fn configured_soft_key_set_round_trips_order_and_empty_modes() {
11703 let profile = SoftKeyProfile::new(KeyMode::ALL_KNOWN.iter().copied().map(|mode| {
11704 let actions = match mode {
11705 KeyMode::OnHook => vec![SoftKey::Redial, SoftKey::NewCall],
11706 KeyMode::Connected => vec![SoftKey::EndCall, SoftKey::Hold],
11707 _ => Vec::new(),
11708 };
11709 (mode, actions)
11710 }))
11711 .unwrap();
11712 let message = ServerMessage::SoftKeySet {
11713 profile: profile.clone(),
11714 };
11715 let bytes = message.encode(ProtocolVersion::V22).unwrap();
11716 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11717 let payload: WireSoftKeySet = decode(frame.message_id, &frame.payload).unwrap();
11718
11719 assert_eq!(
11720 &payload.sets[KeyMode::OnHook.wire_value() as usize].template_indexes[..3],
11721 &[
11722 SoftKey::Redial.wire_value() as u8,
11723 SoftKey::NewCall.wire_value() as u8,
11724 0,
11725 ]
11726 );
11727 assert_eq!(
11728 &payload.sets[KeyMode::Connected.wire_value() as usize].template_indexes[..3],
11729 &[
11730 SoftKey::EndCall.wire_value() as u8,
11731 SoftKey::Hold.wire_value() as u8,
11732 0,
11733 ]
11734 );
11735 assert_eq!(
11736 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
11737 message
11738 );
11739 assert_eq!(profile.valid_mask(KeyMode::OnHook), 0b11);
11740 assert_eq!(profile.valid_mask(KeyMode::RingIn), 0);
11741
11742 let template = ServerMessage::SoftKeyTemplate {
11743 actions: profile.template_actions(),
11744 };
11745 let bytes = template.encode(ProtocolVersion::V22).unwrap();
11746 let frame = FrameDecoder::new().push(&bytes).unwrap().remove(0);
11747 let payload: WireSoftKeyTemplate = decode(frame.message_id, &frame.payload).unwrap();
11748 assert_eq!(payload.definitions[0].event, SoftKey::Redial.wire_value());
11749 assert_eq!(payload.definitions[2].event, SoftKey::Hold.wire_value());
11750 assert_eq!(payload.definitions[3].event, 0);
11751 assert_eq!(
11752 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
11753 template
11754 );
11755 }
11756
11757 #[test]
11758 fn nominally_empty_requests_accept_bounded_extensions() {
11759 for message_id in [
11760 id::CONFIG_STAT_REQ,
11761 id::TIME_DATE_REQ,
11762 id::VERSION_REQ,
11763 id::SERVER_REQ,
11764 id::SOFT_KEY_SET_REQ,
11765 id::SOFT_KEY_TEMPLATE_REQ,
11766 ] {
11767 ClientMessage::decode(Frame::new(22, message_id, 34_u32.to_le_bytes().to_vec()))
11768 .unwrap();
11769 }
11770 }
11771
11772 #[test]
11773 fn dtmf_payload_messages_use_their_structural_word_layouts() {
11774 let identity = DtmfPayloadIdentity {
11775 payload_type: 101,
11776 conference_id: 0x1122_3344,
11777 passthrough_party_id: 0x5566_7788,
11778 };
11779 let request = DtmfPayloadRequest {
11780 payload_type: identity.payload_type,
11781 conference_id: identity.conference_id,
11782 passthrough_party_id: identity.passthrough_party_id,
11783 dtmf_type: 2,
11784 };
11785 let identity_payload = [
11786 identity.payload_type.to_le_bytes(),
11787 identity.conference_id.to_le_bytes(),
11788 identity.passthrough_party_id.to_le_bytes(),
11789 ]
11790 .concat();
11791 let request_payload = [
11792 request.payload_type.to_le_bytes(),
11793 request.conference_id.to_le_bytes(),
11794 request.passthrough_party_id.to_le_bytes(),
11795 request.dtmf_type.to_le_bytes(),
11796 ]
11797 .concat();
11798
11799 for message in [
11800 ClientMessage::SubscribeDtmfPayloadResponse(identity),
11801 ClientMessage::UnsubscribeDtmfPayloadResponse(identity),
11802 ] {
11803 let frame = FrameDecoder::new()
11804 .push(&message.encode(ProtocolVersion::V22).unwrap())
11805 .unwrap()
11806 .remove(0);
11807 assert_eq!(frame.payload, identity_payload);
11808 assert_eq!(
11809 ClientMessage::decode_with_version(frame, ProtocolVersion::V22).unwrap(),
11810 message
11811 );
11812 }
11813
11814 for (message, expected_payload) in [
11815 (
11816 ServerMessage::SubscribeDtmfPayloadRequest(request),
11817 request_payload.as_slice(),
11818 ),
11819 (
11820 ServerMessage::SubscribeDtmfPayloadError(identity),
11821 identity_payload.as_slice(),
11822 ),
11823 (
11824 ServerMessage::UnsubscribeDtmfPayloadRequest(request),
11825 request_payload.as_slice(),
11826 ),
11827 (
11828 ServerMessage::UnsubscribeDtmfPayloadError(identity),
11829 identity_payload.as_slice(),
11830 ),
11831 ] {
11832 let frame = FrameDecoder::new()
11833 .push(&message.encode(ProtocolVersion::V22).unwrap())
11834 .unwrap()
11835 .remove(0);
11836 assert_eq!(frame.payload.as_slice(), expected_payload);
11837 assert_eq!(
11838 ServerMessage::decode(frame, ProtocolVersion::V22).unwrap(),
11839 message
11840 );
11841 }
11842
11843 assert!(
11844 ClientMessage::decode_with_version(
11845 Frame::new(22, id::SUBSCRIBE_DTMF_PAYLOAD_RES, vec![0; 13]),
11846 ProtocolVersion::V22,
11847 )
11848 .is_err()
11849 );
11850 assert!(
11851 ServerMessage::decode(
11852 Frame::new(22, id::SUBSCRIBE_DTMF_PAYLOAD_REQ, vec![0; 17]),
11853 ProtocolVersion::V22,
11854 )
11855 .is_err()
11856 );
11857 }
11858
11859 #[test]
11860 fn add_participant_response_preserves_progressive_identifier_bytes() {
11861 for identifier_len in [0, 1, 64, 256] {
11862 let identifier = (0..identifier_len)
11863 .map(|index| (index as u8).wrapping_mul(17).wrapping_add(3))
11864 .collect::<Vec<_>>();
11865 let mut payload = [
11866 42_u32.to_le_bytes(),
11867 100_u32.to_le_bytes(),
11868 0_u32.to_le_bytes(),
11869 ]
11870 .concat();
11871 payload.extend_from_slice(&identifier);
11872 let decoded = ControlMessage::decode(
11873 Frame::new(22, id::ADD_PARTICIPANT_RES, payload),
11874 ProtocolVersion::V22,
11875 )
11876 .unwrap();
11877 let ControlMessage::AddParticipantResponse(response) = &decoded else {
11878 panic!("expected add-participant response");
11879 };
11880 assert_eq!(response.bridge_participant_id.as_bytes(), identifier);
11881 let frame = FrameDecoder::new()
11882 .push(&decoded.encode(ProtocolVersion::V22).unwrap())
11883 .unwrap()
11884 .remove(0);
11885 assert_eq!(frame.payload.len(), 272);
11886 assert_eq!(&frame.payload[12..12 + identifier_len], identifier);
11887 assert!(
11888 frame.payload[12 + identifier_len..]
11889 .iter()
11890 .all(|byte| *byte == 0)
11891 );
11892 }
11893
11894 let identifier = (0..257)
11895 .map(|index| (index as u8).wrapping_mul(17).wrapping_add(3))
11896 .collect::<Vec<_>>();
11897 let canonical = ControlMessage::AddParticipantResponse(AddParticipantResponse {
11898 conference_id: 42.into(),
11899 call_reference: 100.into(),
11900 result: AddParticipantResult::Ok,
11901 bridge_participant_id: BoundedBytes::try_from(identifier).unwrap(),
11902 });
11903 let frame = FrameDecoder::new()
11904 .push(&canonical.encode(ProtocolVersion::V22).unwrap())
11905 .unwrap()
11906 .remove(0);
11907 assert_eq!(frame.payload.len(), 272);
11908 assert_eq!(
11909 ControlMessage::decode(frame, ProtocolVersion::V22).unwrap(),
11910 canonical
11911 );
11912
11913 for invalid_len in [270, 271, 273] {
11914 assert!(
11915 ControlMessage::decode(
11916 Frame::new(22, id::ADD_PARTICIPANT_RES, vec![0; invalid_len]),
11917 ProtocolVersion::V22,
11918 )
11919 .is_err()
11920 );
11921 }
11922 let mut invalid_alignment = vec![0; 272];
11923 invalid_alignment[271] = 1;
11924 assert!(
11925 ControlMessage::decode(
11926 Frame::new(22, id::ADD_PARTICIPANT_RES, invalid_alignment),
11927 ProtocolVersion::V22,
11928 )
11929 .is_err()
11930 );
11931 }
11932
11933 #[test]
11934 fn xml_alarm_accepts_and_preserves_every_bounded_frame_form() {
11935 for payload_len in [0, 1, 2_000, 2_004, 2_048] {
11936 let payload = (0..payload_len)
11937 .map(|index| (index as u8).wrapping_mul(29).wrapping_add(1))
11938 .collect::<Vec<_>>();
11939 let decoded = ClientMessage::decode_with_version(
11940 Frame::new(22, id::XML_ALARM, payload.clone()),
11941 ProtocolVersion::V22,
11942 )
11943 .unwrap();
11944 let ClientMessage::XmlAlarm(message) = &decoded else {
11945 panic!("expected XML alarm");
11946 };
11947 assert_eq!(message.wire_payload(), payload.as_slice());
11948 let frame = FrameDecoder::new()
11949 .push(&decoded.encode(ProtocolVersion::V22).unwrap())
11950 .unwrap()
11951 .remove(0);
11952 assert_eq!(frame.payload, payload);
11953 }
11954
11955 assert!(matches!(
11956 ClientMessage::decode_with_version(
11957 Frame::new(22, id::XML_ALARM, vec![0; 2_049]),
11958 ProtocolVersion::V22,
11959 ),
11960 Err(CodecError::CountTooLarge {
11961 message_id: id::XML_ALARM,
11962 count: 2_049,
11963 maximum: 2_048,
11964 ..
11965 })
11966 ));
11967
11968 let with_suffix =
11969 XmlAlarmMessage::from_wire_payload(b"<alarm/>\0ignored".to_vec()).unwrap();
11970 assert_eq!(with_suffix.xml_bytes(), b"<alarm/>");
11971 assert_eq!(with_suffix.wire_payload(), b"<alarm/>\0ignored");
11972
11973 let canonical = XmlAlarmMessage::from_xml(vec![b'x'; 2_000]).unwrap();
11974 assert_eq!(canonical.xml_bytes().len(), 2_000);
11975 assert_eq!(canonical.wire_payload().len(), 2_004);
11976 assert!(XmlAlarmMessage::from_xml(vec![b'x'; 2_001]).is_err());
11977 }
11978
11979 #[test]
11980 fn xml_alarm_preserves_bounded_wire_payload() {
11981 let xml = "<?xml version=\"1.0\"?><x-cisco-alarm></x-cisco-alarm>";
11982 let mut payload = vec![0; 2_000];
11983 payload[..xml.len()].copy_from_slice(xml.as_bytes());
11984
11985 let decoded = ClientMessage::decode(Frame::new(0, id::XML_ALARM, payload.clone())).unwrap();
11986 let ClientMessage::XmlAlarm(message) = &decoded else {
11987 panic!("expected XML alarm");
11988 };
11989 assert_eq!(message.xml_bytes(), xml.as_bytes());
11990 assert_eq!(message.wire_payload(), payload);
11991 let frame = FrameDecoder::new()
11992 .push(&decoded.encode(ProtocolVersion::V22).unwrap())
11993 .unwrap()
11994 .remove(0);
11995 assert_eq!(frame.payload, payload);
11996 }
11997
11998 #[test]
11999 fn location_information_uses_text_storage_followed_by_zero_alignment() {
12000 let maximum = "x".repeat(2_400);
12001 let encoded = ClientMessage::LocationInfo {
12002 xml: maximum.clone(),
12003 }
12004 .encode(ProtocolVersion::V22)
12005 .unwrap();
12006 let frame = FrameDecoder::new().push(&encoded).unwrap().remove(0);
12007 assert_eq!(frame.payload.len(), 2_404);
12008 assert_eq!(&frame.payload[2_400..], &[0, 0, 0, 0]);
12009 assert_eq!(
12010 ClientMessage::decode_with_version(frame, ProtocolVersion::V22).unwrap(),
12011 ClientMessage::LocationInfo { xml: maximum }
12012 );
12013
12014 assert!(matches!(
12015 ClientMessage::LocationInfo {
12016 xml: "x".repeat(2_401),
12017 }
12018 .encode(ProtocolVersion::V22),
12019 Err(CodecError::TextTooLong {
12020 message_id: id::LOCATION_INFO,
12021 maximum: 2_400,
12022 ..
12023 })
12024 ));
12025
12026 let mut nonzero_alignment = vec![0; 2_404];
12027 nonzero_alignment[2_401] = 1;
12028 assert!(matches!(
12029 ClientMessage::decode_with_version(
12030 Frame::new(22, id::LOCATION_INFO, nonzero_alignment),
12031 ProtocolVersion::V22,
12032 ),
12033 Err(CodecError::InvalidValue {
12034 message_id: id::LOCATION_INFO,
12035 field: "reserved payload byte",
12036 ..
12037 })
12038 ));
12039 }
12040
12041 #[test]
12042 fn decodes_7961_button_template_request_with_payload() {
12043 assert_eq!(
12044 ClientMessage::decode(Frame::new(
12045 22,
12046 id::BUTTON_TEMPLATE_REQ,
12047 34_u32.to_le_bytes().to_vec(),
12048 ))
12049 .unwrap(),
12050 ClientMessage::ButtonTemplateRequest
12051 );
12052 }
12053}