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