rusmpp_core/pdus/owned/
submit_sm.rs

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