rusmpp_core/pdus/owned/
submit_sm.rs

1use rusmpp_macros::Rusmpp;
2
3use crate::{
4    encode::Length,
5    pdus::owned::Pdu,
6    tlvs::{
7        TlvTag,
8        owned::{MessageSubmissionRequestTlvValue, Tlv},
9    },
10    types::owned::{COctetString, EmptyOrFullCOctetString, OctetString},
11    values::{owned::*, *},
12};
13
14/// This operation is used by an ESME to submit a short message to the MC for onward
15/// transmission to a specified short message entity (SME).
16#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Rusmpp)]
17#[rusmpp(decode = owned, test = skip)]
18#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
19#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
20#[cfg_attr(feature = "serde-deserialize-unchecked", derive(::serde::Deserialize))]
21pub struct SubmitSm {
22    /// The service_type parameter can be used to
23    /// indicate the SMS Application service
24    /// associated with the message. Specifying the
25    /// service_type allows the ESME to avail of enhanced
26    /// messaging services such as “replace by service_type”
27    /// or to control the teleservice used on the
28    /// air interface.
29    ///
30    /// Set to NULL for default MC settings.
31    pub service_type: ServiceType,
32    /// Type of Number for source address.
33    pub source_addr_ton: Ton,
34    /// Numbering Plan Indicator for source address.
35    pub source_addr_npi: Npi,
36    /// Address of SME which originated this message.
37    pub source_addr: COctetString<1, 21>,
38    /// Type of Number for destination.
39    pub dest_addr_ton: Ton,
40    /// Numbering Plan Indicator for destination.
41    pub dest_addr_npi: Npi,
42    /// Destination address of this
43    /// short message For mobile
44    /// terminated messages, this
45    /// is the directory number of
46    /// the recipient MS
47    pub destination_addr: COctetString<1, 21>,
48    /// Indicates Message Mode
49    /// and Message Type.
50    pub esm_class: EsmClass,
51    /// Protocol Identifier.
52    /// Network specific field.
53    pub protocol_id: u8,
54    /// Designates the priority level of the message.
55    pub priority_flag: PriorityFlag,
56    /// The short message is to be
57    /// scheduled by the MC for delivery.
58    /// Set to NULL for immediate message delivery.
59    pub schedule_delivery_time: EmptyOrFullCOctetString<17>,
60    /// The validity period of this message.
61    /// Set to NULL to request the MC default validity period.
62    ///
63    /// Note: this is superseded by the qos_time_to_live TLV if
64    /// specified.
65    pub validity_period: EmptyOrFullCOctetString<17>,
66    /// Indicator to signify if a MC delivery receipt, manual
67    /// ACK, delivery ACK or an intermediate notification is required.
68    pub registered_delivery: RegisteredDelivery,
69    /// Flag indicating if the submitted message should replace an existing message.
70    pub replace_if_present_flag: ReplaceIfPresentFlag,
71    // Defines the encoding scheme of the short message user data.
72    pub data_coding: DataCoding,
73    /// Indicates the short message to send from a list of pre- defined (‘canned’)
74    /// short messages stored on the MC. If not using a MC canned message, set to NULL.
75    pub sm_default_msg_id: u8,
76    /// Length in octets of the short_message user data.
77    sm_length: u8,
78    /// Up to 255 octets of short message user data.
79    ///
80    /// The exact physical limit for short_message size may
81    /// vary according to the underlying network
82    ///
83    /// Note: this field is superceded by the message_payload TLV if
84    /// specified.
85    #[rusmpp(length = sm_length)]
86    short_message: OctetString<0, 255>,
87    /// Message submission request TLVs ([`MessageSubmissionRequestTlvValue`]).
88    #[rusmpp(length = "unchecked")]
89    tlvs: alloc::vec::Vec<Tlv>,
90}
91
92impl SubmitSm {
93    #[allow(clippy::too_many_arguments)]
94    pub fn new(
95        service_type: ServiceType,
96        source_addr_ton: Ton,
97        source_addr_npi: Npi,
98        source_addr: COctetString<1, 21>,
99        dest_addr_ton: Ton,
100        dest_addr_npi: Npi,
101        destination_addr: COctetString<1, 21>,
102        esm_class: EsmClass,
103        protocol_id: u8,
104        priority_flag: PriorityFlag,
105        schedule_delivery_time: EmptyOrFullCOctetString<17>,
106        validity_period: EmptyOrFullCOctetString<17>,
107        registered_delivery: RegisteredDelivery,
108        replace_if_present_flag: ReplaceIfPresentFlag,
109        data_coding: DataCoding,
110        sm_default_msg_id: u8,
111        short_message: OctetString<0, 255>,
112        tlvs: alloc::vec::Vec<impl Into<MessageSubmissionRequestTlvValue>>,
113    ) -> Self {
114        let tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
115
116        let sm_length = short_message.length() as u8;
117
118        let mut submit_sm = Self {
119            service_type,
120            source_addr_ton,
121            source_addr_npi,
122            source_addr,
123            dest_addr_ton,
124            dest_addr_npi,
125            destination_addr,
126            esm_class,
127            protocol_id,
128            priority_flag,
129            schedule_delivery_time,
130            validity_period,
131            registered_delivery,
132            replace_if_present_flag,
133            data_coding,
134            sm_default_msg_id,
135            sm_length,
136            short_message,
137            tlvs,
138        };
139
140        submit_sm.clear_short_message_if_message_payload_exists();
141
142        submit_sm
143    }
144
145    pub fn sm_length(&self) -> u8 {
146        self.sm_length
147    }
148
149    pub fn short_message(&self) -> &OctetString<0, 255> {
150        &self.short_message
151    }
152
153    /// Sets the short message and short message length.
154    /// Updates the short message and short message length accordingly.
155    /// Has no effect if the message payload is set.
156    /// Returns true if the short message and short message length were set.
157    pub fn set_short_message(&mut self, short_message: OctetString<0, 255>) -> bool {
158        self.short_message = short_message;
159        self.sm_length = self.short_message.length() as u8;
160
161        !self.clear_short_message_if_message_payload_exists()
162    }
163
164    pub fn tlvs(&self) -> &[Tlv] {
165        &self.tlvs
166    }
167
168    pub fn set_tlvs(&mut self, tlvs: alloc::vec::Vec<impl Into<MessageSubmissionRequestTlvValue>>) {
169        self.tlvs = tlvs.into_iter().map(Into::into).map(From::from).collect();
170
171        self.clear_short_message_if_message_payload_exists();
172    }
173
174    pub fn clear_tlvs(&mut self) {
175        self.tlvs.clear();
176    }
177
178    pub fn push_tlv(&mut self, tlv: impl Into<MessageSubmissionRequestTlvValue>) {
179        self.tlvs.push(Tlv::from(tlv.into()));
180
181        self.clear_short_message_if_message_payload_exists();
182    }
183
184    /// Clears the short message and short message length if the message payload is set.
185    /// Returns true if the short message and short message length were cleared.
186    fn clear_short_message_if_message_payload_exists(&mut self) -> bool {
187        let message_payload_exists = self
188            .tlvs
189            .iter()
190            .any(|value| matches!(value.tag(), TlvTag::MessagePayload));
191
192        if message_payload_exists {
193            self.short_message = OctetString::empty();
194            self.sm_length = 0;
195
196            return true;
197        };
198
199        false
200    }
201
202    pub fn builder() -> SubmitSmBuilder {
203        SubmitSmBuilder::new()
204    }
205}
206
207impl From<SubmitSm> for Pdu {
208    fn from(value: SubmitSm) -> Self {
209        Self::SubmitSm(value)
210    }
211}
212
213#[derive(Debug, Default)]
214pub struct SubmitSmBuilder {
215    inner: SubmitSm,
216}
217
218impl SubmitSmBuilder {
219    pub fn new() -> Self {
220        Self::default()
221    }
222
223    pub fn service_type(mut self, service_type: ServiceType) -> Self {
224        self.inner.service_type = service_type;
225        self
226    }
227
228    pub fn source_addr_ton(mut self, source_addr_ton: Ton) -> Self {
229        self.inner.source_addr_ton = source_addr_ton;
230        self
231    }
232
233    pub fn source_addr_npi(mut self, source_addr_npi: Npi) -> Self {
234        self.inner.source_addr_npi = source_addr_npi;
235        self
236    }
237
238    pub fn source_addr(mut self, source_addr: COctetString<1, 21>) -> Self {
239        self.inner.source_addr = source_addr;
240        self
241    }
242
243    pub fn dest_addr_ton(mut self, dest_addr_ton: Ton) -> Self {
244        self.inner.dest_addr_ton = dest_addr_ton;
245        self
246    }
247
248    pub fn dest_addr_npi(mut self, dest_addr_npi: Npi) -> Self {
249        self.inner.dest_addr_npi = dest_addr_npi;
250        self
251    }
252
253    pub fn destination_addr(mut self, destination_addr: COctetString<1, 21>) -> Self {
254        self.inner.destination_addr = destination_addr;
255        self
256    }
257
258    pub fn esm_class(mut self, esm_class: EsmClass) -> Self {
259        self.inner.esm_class = esm_class;
260        self
261    }
262
263    pub fn protocol_id(mut self, protocol_id: u8) -> Self {
264        self.inner.protocol_id = protocol_id;
265        self
266    }
267
268    pub fn priority_flag(mut self, priority_flag: PriorityFlag) -> Self {
269        self.inner.priority_flag = priority_flag;
270        self
271    }
272
273    pub fn schedule_delivery_time(
274        mut self,
275        schedule_delivery_time: EmptyOrFullCOctetString<17>,
276    ) -> Self {
277        self.inner.schedule_delivery_time = schedule_delivery_time;
278        self
279    }
280
281    pub fn validity_period(mut self, validity_period: EmptyOrFullCOctetString<17>) -> Self {
282        self.inner.validity_period = validity_period;
283        self
284    }
285
286    pub fn registered_delivery(mut self, registered_delivery: RegisteredDelivery) -> Self {
287        self.inner.registered_delivery = registered_delivery;
288        self
289    }
290
291    pub fn replace_if_present_flag(
292        mut self,
293        replace_if_present_flag: ReplaceIfPresentFlag,
294    ) -> Self {
295        self.inner.replace_if_present_flag = replace_if_present_flag;
296        self
297    }
298
299    pub fn data_coding(mut self, data_coding: DataCoding) -> Self {
300        self.inner.data_coding = data_coding;
301        self
302    }
303
304    pub fn sm_default_msg_id(mut self, sm_default_msg_id: u8) -> Self {
305        self.inner.sm_default_msg_id = sm_default_msg_id;
306        self
307    }
308
309    pub fn short_message(mut self, short_message: OctetString<0, 255>) -> Self {
310        self.inner.set_short_message(short_message);
311        self
312    }
313
314    pub fn tlvs(
315        mut self,
316        tlvs: alloc::vec::Vec<impl Into<MessageSubmissionRequestTlvValue>>,
317    ) -> Self {
318        self.inner.set_tlvs(tlvs);
319        self
320    }
321
322    pub fn clear_tlvs(mut self) -> Self {
323        self.inner.clear_tlvs();
324        self
325    }
326
327    pub fn push_tlv(mut self, tlv: impl Into<MessageSubmissionRequestTlvValue>) -> Self {
328        self.inner.push_tlv(tlv);
329        self
330    }
331
332    pub fn build(self) -> SubmitSm {
333        self.inner
334    }
335}
336
337#[cfg(test)]
338mod tests {
339    use std::str::FromStr;
340
341    use crate::{tests::TestInstance, types::owned::AnyOctetString};
342
343    use super::*;
344
345    impl TestInstance for SubmitSm {
346        fn instances() -> alloc::vec::Vec<Self> {
347            alloc::vec![
348                Self::default(),
349                Self::builder()
350                    .service_type(ServiceType::new(
351                        GenericServiceType::CellularMessaging.into(),
352                    ))
353                    .source_addr_ton(Ton::International)
354                    .source_addr_npi(Npi::Isdn)
355                    .source_addr(COctetString::from_str("Source Address").unwrap())
356                    .dest_addr_ton(Ton::International)
357                    .dest_addr_npi(Npi::Isdn)
358                    .destination_addr(COctetString::from_str("Destination Address").unwrap())
359                    .esm_class(EsmClass::new(
360                        MessagingMode::StoreAndForward,
361                        MessageType::ShortMessageContainsMCDeliveryReceipt,
362                        Ansi41Specific::ShortMessageContainsDeliveryAcknowledgement,
363                        GsmFeatures::SetUdhiAndReplyPath,
364                    ))
365                    .protocol_id(0)
366                    .priority_flag(PriorityFlag::from(PriorityFlagType::from(Ansi136::Bulk)))
367                    .schedule_delivery_time(
368                        EmptyOrFullCOctetString::new(b"2023-09-01T12:00\0").unwrap(),
369                    )
370                    .validity_period(EmptyOrFullCOctetString::from_str("2023-10-01T12:00").unwrap())
371                    .registered_delivery(RegisteredDelivery::request_all())
372                    .replace_if_present_flag(ReplaceIfPresentFlag::Replace)
373                    .data_coding(DataCoding::Ksc5601)
374                    .sm_default_msg_id(69)
375                    .short_message(OctetString::new(b"Short Message").unwrap())
376                    .build(),
377                Self::builder()
378                    .service_type(ServiceType::new(
379                        GenericServiceType::CellularMessaging.into(),
380                    ))
381                    .source_addr_ton(Ton::International)
382                    .source_addr_npi(Npi::Isdn)
383                    .source_addr(COctetString::new(b"Source Address\0").unwrap())
384                    .dest_addr_ton(Ton::International)
385                    .dest_addr_npi(Npi::Isdn)
386                    .destination_addr(COctetString::new(b"Destination Address\0").unwrap())
387                    .esm_class(EsmClass::new(
388                        MessagingMode::Default,
389                        MessageType::ShortMessageContainsIntermediateDeliveryNotification,
390                        Ansi41Specific::ShortMessageContainsUserAcknowledgment,
391                        GsmFeatures::SetUdhiAndReplyPath,
392                    ))
393                    .protocol_id(0)
394                    .priority_flag(PriorityFlag::from(PriorityFlagType::from(
395                        Ansi136::VeryUrgent,
396                    )))
397                    .schedule_delivery_time(
398                        EmptyOrFullCOctetString::new(b"2023-09-01T12:01\0").unwrap(),
399                    )
400                    .validity_period(EmptyOrFullCOctetString::from_str("2023-10-01T12:20").unwrap())
401                    .registered_delivery(RegisteredDelivery::request_all())
402                    .replace_if_present_flag(ReplaceIfPresentFlag::DoNotReplace)
403                    .data_coding(DataCoding::Jis)
404                    .sm_default_msg_id(96)
405                    .short_message(OctetString::new(b"Short Message").unwrap())
406                    .tlvs(alloc::vec![
407                        MessageSubmissionRequestTlvValue::MessagePayload(MessagePayload::new(
408                            AnyOctetString::new(b"Message Payload")
409                        ),)
410                    ])
411                    .build(),
412                Self::builder()
413                    .short_message(OctetString::new(b"Short Message").unwrap())
414                    .tlvs(alloc::vec![
415                        MessageSubmissionRequestTlvValue::MessagePayload(MessagePayload::new(
416                            AnyOctetString::new(b"Message Payload"),
417                        )),
418                        MessageSubmissionRequestTlvValue::UserResponseCode(3),
419                        MessageSubmissionRequestTlvValue::DestBearerType(BearerType::FlexReFlex),
420                        MessageSubmissionRequestTlvValue::SourceSubaddress(Subaddress::new(
421                            SubaddressTag::NsapOdd,
422                            OctetString::from_str("Subaddress :D").unwrap(),
423                        )),
424                    ])
425                    .build(),
426            ]
427        }
428    }
429
430    #[test]
431    fn encode_decode() {
432        crate::tests::owned::encode_decode_with_length_test_instances::<SubmitSm>();
433    }
434
435    #[test]
436    fn short_message_length() {
437        let short_message = OctetString::new(b"Short Message").unwrap();
438
439        let submit_sm = SubmitSm::builder()
440            .short_message(short_message.clone())
441            .build();
442
443        assert_eq!(submit_sm.short_message(), &short_message);
444        assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
445    }
446
447    #[test]
448    fn short_message_override() {
449        let short_message_1 = OctetString::new(b"Short Message 101").unwrap();
450        let short_message_2 = OctetString::new(b"Short Message 2").unwrap();
451
452        let submit_sm = SubmitSm::builder()
453            .short_message(short_message_1)
454            .short_message(short_message_2.clone())
455            .build();
456
457        assert_eq!(submit_sm.short_message(), &short_message_2);
458        assert_eq!(submit_sm.sm_length(), short_message_2.length() as u8);
459    }
460
461    #[test]
462    fn message_payload_suppresses_short_message() {
463        let short_message = OctetString::new(b"Short Message").unwrap();
464        let message_payload = MessagePayload::new(AnyOctetString::new(b"Message Payload"));
465
466        // Using push_tlv
467        let submit_sm = SubmitSm::builder()
468            .short_message(short_message.clone())
469            .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
470                message_payload.clone(),
471            ))
472            .build();
473
474        assert_eq!(submit_sm.short_message(), &OctetString::empty());
475        assert_eq!(submit_sm.sm_length(), 0);
476
477        // Using tlvs
478        let submit_sm = SubmitSm::builder()
479            .short_message(short_message.clone())
480            .tlvs(alloc::vec![
481                MessageSubmissionRequestTlvValue::MessagePayload(message_payload.clone(),)
482            ])
483            .build();
484
485        assert_eq!(submit_sm.short_message(), &OctetString::empty());
486        assert_eq!(submit_sm.sm_length(), 0);
487
488        // Even setting the short message after the message payload should not set the short message
489        // Using push_tlv
490        let submit_sm = SubmitSm::builder()
491            .short_message(short_message.clone())
492            .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
493                message_payload.clone(),
494            ))
495            .short_message(short_message.clone())
496            .build();
497
498        assert_eq!(submit_sm.short_message(), &OctetString::empty());
499        assert_eq!(submit_sm.sm_length(), 0);
500
501        // Using tlvs
502        let submit_sm = SubmitSm::builder()
503            .short_message(short_message.clone())
504            .tlvs(alloc::vec![
505                MessageSubmissionRequestTlvValue::MessagePayload(message_payload.clone(),)
506            ])
507            .short_message(short_message.clone())
508            .build();
509
510        assert_eq!(submit_sm.short_message(), &OctetString::empty());
511        assert_eq!(submit_sm.sm_length(), 0);
512
513        // Removing the message payload and then setting the short message should set the short message
514        let submit_sm = SubmitSm::builder()
515            .push_tlv(MessageSubmissionRequestTlvValue::MessagePayload(
516                message_payload.clone(),
517            ))
518            .clear_tlvs()
519            .short_message(short_message.clone())
520            .build();
521
522        assert_eq!(submit_sm.short_message(), &short_message);
523        assert_eq!(submit_sm.sm_length(), short_message.length() as u8);
524    }
525}