rust_rcs_client/messaging/cpm/
mod.rs

1// Copyright 2023 宋昊文
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub mod session;
16pub mod session_invitation;
17pub mod sip;
18pub mod standalone_messaging;
19
20use std::sync::Arc;
21
22use base64::{engine::general_purpose, Engine as _};
23use chrono::{DateTime, Local, SecondsFormat};
24use rust_rcs_core::{
25    internet::{body::message_body::MessageBody, Body, Header},
26    sip::sip_session::SipSession,
27};
28use uuid::Uuid;
29
30use self::session::CPMSession;
31
32use super::ffi::RecipientType;
33
34pub struct MessagingSessionHandle {
35    pub(crate) inner: Arc<SipSession<CPMSession>>,
36}
37
38pub struct CPMMessageParam {
39    message_type: String,
40    message_content: String,
41    recipient: String,
42    recipient_type: RecipientType,
43    recipient_uri: String,
44    message_result_callback: Arc<dyn Fn(u16, String) + Send + Sync>, // to-do: use FnOnce in CPMSession ?
45}
46
47impl CPMMessageParam {
48    pub fn new<F>(
49        message_type: String,
50        message_content: String,
51        recipient: &str,
52        recipient_type: &RecipientType,
53        recipient_uri: &str,
54        message_result_callback: F,
55    ) -> CPMMessageParam
56    where
57        F: Fn(u16, String) + Send + Sync + 'static,
58    {
59        CPMMessageParam {
60            message_type,
61            message_content,
62            recipient: String::from(recipient),
63            recipient_type: RecipientType::from(recipient_type),
64            recipient_uri: String::from(recipient_uri),
65            message_result_callback: Arc::new(message_result_callback),
66        }
67    }
68}
69
70pub fn make_cpim_message_content_body(
71    message_type: &str,
72    message_content: &str,
73    recipient_type: &RecipientType,
74    recipient_uri: &str,
75    message_imdn_id: Uuid,
76    public_user_identity: &str,
77) -> Body {
78    let encoded = general_purpose::STANDARD.encode(message_content);
79
80    let content_body = Vec::from(encoded);
81    let content_body_length = content_body.len();
82
83    let mut content_headers = Vec::new();
84
85    content_headers.push(Header::new("Content-Type", String::from(message_type)));
86
87    content_headers.push(Header::new("Content-Transfer-Encoding", "base64"));
88
89    content_headers.push(Header::new(
90        "Content-Length",
91        format!("{}", content_body_length),
92    ));
93
94    let cpim_content_body = Body::Message(MessageBody {
95        headers: content_headers,
96        body: Arc::new(Body::Raw(content_body)),
97    });
98
99    let mut cpim_headers = Vec::new();
100
101    if message_type.eq("message/imdn") {
102        cpim_headers.push(Header::new("From", "<sip:anonymous@anonymous.invalid>"));
103        cpim_headers.push(Header::new("To", "<sip:anonymous@anonymous.invalid>"));
104    } else {
105        cpim_headers.push(Header::new("From", format!("<{}>", public_user_identity)));
106        cpim_headers.push(Header::new("To", format!("<{}>", recipient_uri)));
107    }
108
109    let utc: DateTime<Local> = Local::now();
110
111    cpim_headers.push(Header::new(
112        "DateTime",
113        utc.to_rfc3339_opts(SecondsFormat::Millis, false),
114    ));
115
116    cpim_headers.push(Header::new("NS", "imdn <urn:ietf:params:imdn>"));
117
118    cpim_headers.push(Header::new(
119        "imdn.Message-ID",
120        String::from(
121            message_imdn_id
122                .as_hyphenated()
123                .encode_lower(&mut Uuid::encode_buffer()),
124        ),
125    ));
126
127    // if !message_type.eq("message/imdn") {
128    //     cpim_headers.push(Header::new(
129    //         "imdn.Disposition-Notification",
130    //         "positive-delivery",
131    //     ));
132    // }
133
134    cpim_headers.push(Header::new(
135        "NS",
136        "cpm <http://www.openmobilealliance.org/cpm/>",
137    ));
138
139    cpim_headers.push(Header::new(b"cpm.Payload-Type", String::from(message_type)));
140
141    // cpim_headers.push(Header {
142    //     name: b"NS".to_vec(),
143    //     value: b"cpim <http://www.openmobilealliance.org/cpm/cpim>".to_vec(),
144    // });
145
146    if let RecipientType::Chatbot = recipient_type {
147        cpim_headers.push(Header::new("NS", "maap <http://www.gsma.com/rcs/maap/>"));
148    }
149
150    Body::Message(MessageBody {
151        headers: cpim_headers,
152        body: Arc::new(cpim_content_body),
153    })
154}