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