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