1pub mod chat_bot;
16pub mod conference;
17pub mod connection;
18pub mod connectivity;
19pub mod contact;
20pub mod context;
21pub mod ffi;
22pub mod messaging;
23pub mod provisioning;
24pub mod rcs_engine;
25
26use std::ffi::{CStr, CString};
27use std::ptr::{self, null_mut, NonNull};
28use std::sync::{Arc, Mutex};
29
30use libc::c_char;
33use messaging::cpm::MessagingSessionHandle;
34use rust_rcs_core::security::gba::GbaContext;
35use rust_rcs_core::third_gen_pp::ThreeDigit;
36use tokio::runtime::Runtime;
37
38use rust_rcs_core::ffi::log::platform_log;
39
40use chat_bot::ffi::{
41 RetrieveChatbotInfoResultCallback, RetrieveChatbotInfoResultCallbackContext,
42 RetrieveChatbotInfoResultCallbackContextWrapper, RetrieveSpecificChatbotsResultCallback,
43 RetrieveSpecificChatbotsResultCallbackContext,
44 RetrieveSpecificChatbotsResultCallbackContextWrapper, SearchChatbotResultCallback,
45 SearchChatbotResultCallbackContext, SearchChatbotResultCallbackContextWrapper,
46};
47use conference::ffi::{
48 MultiConferenceCreateResultCallback, MultiConferenceCreateResultCallbackContext,
49 MultiConferenceCreateResultCallbackContextWrapper, MultiConferenceEventListener,
50 MultiConferenceEventListenerContext, MultiConferenceEventListenerContextWrapper,
51 MultiConferenceJoinResultCallback, MultiConferenceJoinResultCallbackContext,
52 MultiConferenceJoinResultCallbackContextWrapper, MultiConferenceV1InviteHandlerContext,
53 MultiConferenceV1InviteHandlerContextWrapper, MultiConferenceV1InviteHandlerFunction,
54};
55use conference::MultiConferenceV1;
56use context::Context;
57use ffi::{
58 MessageCallback, MessageCallbackContext, MessageCallbackContextWrapper, StateChangeCallback,
59 StateChangeCallbackContext, StateChangeCallbackContextWrapper,
60};
61use messaging::ffi::{
62 get_recipient_type, DownloadFileProgressCallback, DownloadFileProgressCallbackContext,
63 DownloadFileResultCallback, DownloadFileResultCallbackContext,
64 DownloadFileResultCallbackContextWrapper, DownloadileProgressCallbackContextWrapper,
65 MessageResultCallback, MessageResultCallbackContext, MessageResultCallbackContextWrapper,
66 SendImdnReportResultCallback, SendImdnReportResultCallbackContext,
67 SendImdnReportResultCallbackContextWrapper, UploadFileProgressCallback,
68 UploadFileProgressCallbackContext, UploadFileProgressCallbackContextWrapper,
69 UploadFileResultCallback, UploadFileResultCallbackContext,
70 UploadFileResultCallbackContextWrapper,
71};
72use provisioning::device_configuration::{
73 start_auto_config, DeviceConfigurationStatus, RetryReason,
74};
75
76use rcs_engine::RcsEngine;
77
78const LOG_TAG: &str = "librust_rcs_client";
79
80pub struct RcsRuntime {
81 pub rt: Arc<Runtime>,
82 }
84
85impl RcsRuntime {
86 pub fn new() -> Result<RcsRuntime, ()> {
87 std::panic::set_hook(Box::new(|info| {
88 platform_log(LOG_TAG, format!("panic! {}", info));
89
90 }));
94
95 if let Ok(rt) = Runtime::new() {
97 return Ok(RcsRuntime { rt: Arc::new(rt) });
99 }
100
101 Err(())
102 }
103}
104
105pub struct RcsClient {
106 subscription_id: i32,
107 mcc: u16,
108 mnc: u16,
109 imsi: String,
110 imei: String,
111 msisdn: Option<String>,
112
113 context: Arc<Context>,
114
115 engine: RcsEngine,
118 engine_gba_context: Option<Arc<GbaContext>>,
120}
121
122impl RcsClient {
123 pub fn new(
124 subscription_id: i32,
125 mcc: u16,
126 mnc: u16,
127 imsi: &str,
128 imei: &str,
129 msisdn: Option<&str>,
130 context: Arc<Context>,
131 state_cb: Option<StateChangeCallback>,
132 state_cb_context: *mut StateChangeCallbackContext,
133 message_cb: Option<MessageCallback>,
134 message_cb_context: *mut MessageCallbackContext,
135 multi_conference_v1_invite_handler: Option<MultiConferenceV1InviteHandlerFunction>,
136 multi_conference_v1_invite_handler_context: *mut MultiConferenceV1InviteHandlerContext,
137 rt: Arc<Runtime>,
138 ) -> RcsClient {
139 let state_cb_context = Arc::new(Mutex::new(StateChangeCallbackContextWrapper(
140 NonNull::new(state_cb_context).unwrap(),
141 )));
142 let message_cb_context = Arc::new(Mutex::new(MessageCallbackContextWrapper(
143 NonNull::new(message_cb_context).unwrap(),
144 )));
145 let multi_conference_v1_invite_handler_context =
146 Arc::new(Mutex::new(MultiConferenceV1InviteHandlerContextWrapper(
147 NonNull::new(multi_conference_v1_invite_handler_context).unwrap(),
148 )));
149 let context_ = Arc::clone(&context);
150 let engine = RcsEngine::new(
151 subscription_id,
152 rt,
153 context_,
154 move |state| {
155 if let Some(state_cb) = state_cb {
156 let state_cb_context = state_cb_context.lock().unwrap();
157 let state_cb_context_ptr = state_cb_context.0.as_ptr();
158 match state {
159 rcs_engine::RcsEngineRegistrationState::NONE => {
160 state_cb(0, state_cb_context_ptr)
161 }
162 rcs_engine::RcsEngineRegistrationState::AUTHENTICATED(_) => {
163 state_cb(1, state_cb_context_ptr)
164 }
165 rcs_engine::RcsEngineRegistrationState::MAINTAINED(_) => {
166 state_cb(2, state_cb_context_ptr)
167 }
168 }
169 }
170 },
171 move |service_type,
172 session_handle,
173 contact_uri,
174 content_type,
175 content_body,
176 imdn_message_id,
177 cpim_date,
178 cpim_from| {
179 if let Some(messag_cb) = message_cb {
180 let session_handle = match session_handle {
181 Some(session_handle) => Some(Box::new(session_handle)),
182 None => None,
183 };
184 let contact_uri = CString::new(contact_uri).unwrap();
185 let content_type = CString::new(content_type).unwrap();
186 let content_body = CString::new(content_body).unwrap();
187 let imdn_message_id = CString::new(imdn_message_id).unwrap();
188 let cpim_date = CString::new(cpim_date).unwrap();
189 if let Some(cpim_from) = cpim_from {
190 let cpim_from = CString::new(cpim_from).unwrap();
191 let message_cb_context = message_cb_context.lock().unwrap();
192 let message_cb_context_ptr = message_cb_context.0.as_ptr();
193 messag_cb(
194 service_type,
195 session_handle,
196 contact_uri.as_ptr(),
197 content_type.as_ptr(),
198 content_body.as_ptr(),
199 imdn_message_id.as_ptr(),
200 cpim_date.as_ptr(),
201 cpim_from.as_ptr(),
202 message_cb_context_ptr,
203 );
204 } else {
205 let message_cb_context = message_cb_context.lock().unwrap();
206 let message_cb_context_ptr = message_cb_context.0.as_ptr();
207 messag_cb(
208 service_type,
209 session_handle,
210 contact_uri.as_ptr(),
211 content_type.as_ptr(),
212 content_body.as_ptr(),
213 imdn_message_id.as_ptr(),
214 cpim_date.as_ptr(),
215 ptr::null(),
216 message_cb_context_ptr,
217 );
218 };
219 }
220 },
221 move |conference_v1, offer_sdp, response_receiver| {
222 if let Some(multi_conference_v1_invite_handler) = multi_conference_v1_invite_handler
223 {
224 let multi_conference_v1_invite_handler_context =
225 multi_conference_v1_invite_handler_context.lock().unwrap();
226 let multi_conference_v1_invite_handler_context_ptr =
227 multi_conference_v1_invite_handler_context.0.as_ptr();
228 let offer_sdp_ptr = offer_sdp.as_ptr();
229 let offer_sdp_len = offer_sdp.len();
230 multi_conference_v1_invite_handler(
231 Some(Box::new(conference_v1)),
232 offer_sdp_ptr,
233 offer_sdp_len,
234 Some(Box::new(response_receiver)),
235 multi_conference_v1_invite_handler_context_ptr,
236 );
237 }
238 },
239 );
240 RcsClient {
241 subscription_id,
242 mcc,
243 mnc,
244 imsi: imsi.to_string(),
245 imei: imei.to_string(),
246 msisdn: match msisdn {
247 Some(msisdn) => Some(String::from(msisdn)),
248 None => None,
249 },
250 context,
251 engine,
256 engine_gba_context: None,
258 }
259 }
260}
261
262#[no_mangle]
263pub unsafe extern "C" fn new_rcs_runtime() -> Option<Box<RcsRuntime>> {
264 if let Ok(rcs_runtime) = RcsRuntime::new() {
265 return Some(Box::new(rcs_runtime));
266 }
267 None
268}
269
270#[no_mangle]
271pub unsafe extern "C" fn new_rcs_client(
272 rcs_runtime: *mut RcsRuntime,
273 subscription_id: i32,
274 mcc: u16,
275 mnc: u16,
276 imsi: *const c_char,
277 imei: *const c_char,
278 msisdn: *const c_char,
279 fs_root_dir: *const c_char,
280 state_cb: Option<StateChangeCallback>,
281 state_cb_context: *mut StateChangeCallbackContext,
282 message_cb: Option<MessageCallback>,
283 message_cb_context: *mut MessageCallbackContext,
284 conference_v1_invite_handler: Option<MultiConferenceV1InviteHandlerFunction>,
285 conference_v1_invite_handler_context: *mut MultiConferenceV1InviteHandlerContext,
286) -> Option<Box<RcsClient>> {
287 let imsi = CStr::from_ptr(imsi).to_string_lossy().into_owned();
288 let imei = CStr::from_ptr(imei).to_string_lossy().into_owned();
289 let msisdn = if msisdn.is_null() {
290 None
291 } else {
292 Some(CStr::from_ptr(msisdn).to_string_lossy())
293 };
294 let fs_root_dir = CStr::from_ptr(fs_root_dir).to_string_lossy().into_owned();
295 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
296 platform_log(LOG_TAG, "creating context");
297 let rt = Arc::clone(&rcs_runtime.rt);
298 let context = Arc::new(Context::new(&fs_root_dir, rt));
299 return Some(Box::new(RcsClient::new(
300 subscription_id,
301 mcc,
302 mnc,
303 &imsi,
304 &imei,
305 msisdn.as_deref(),
306 context,
307 state_cb,
308 state_cb_context,
309 message_cb,
310 message_cb_context,
311 conference_v1_invite_handler,
312 conference_v1_invite_handler_context,
313 Arc::clone(&rcs_runtime.rt),
314 )));
315 }
316 None
317}
318
319type AutoConfigProgressCallback =
320 extern "C" fn(status_code: i32, context: *mut AutoConfigCallbackContext);
321
322type AutoConfigResultCallback = extern "C" fn(
323 status_code: i32,
324 ims_config: *const c_char,
325 rcs_config: *const c_char,
326 extra: *const c_char,
327 context: *mut AutoConfigCallbackContext,
330);
331
332#[repr(C)]
333pub struct AutoConfigCallbackContext {
334 _data: [u8; 0],
335 _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
336}
337
338#[cfg(any(
339 all(feature = "android", target_os = "android"),
340 all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
341))]
342extern "C" {
343 fn auto_config_callback_context_release(context: *mut AutoConfigCallbackContext);
344}
345
346struct AutoConfigCallbackContextWrapper(NonNull<AutoConfigCallbackContext>);
347
348impl Drop for AutoConfigCallbackContextWrapper {
349 fn drop(&mut self) {
350 #[cfg(any(
351 all(feature = "android", target_os = "android"),
352 all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
353 ))]
354 let cb_context = self.0.as_ptr();
355 #[cfg(any(
356 all(feature = "android", target_os = "android"),
357 all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
358 ))]
359 unsafe {
360 auto_config_callback_context_release(cb_context);
361 }
362 }
363}
364
365unsafe impl Send for AutoConfigCallbackContextWrapper {}
366
367#[no_mangle]
368pub unsafe extern "C" fn rcs_client_start_config(
369 rcs_runtime: *mut RcsRuntime,
370 client: *const RcsClient,
371 p_cb: Option<AutoConfigProgressCallback>,
372 r_cb: Option<AutoConfigResultCallback>,
373 cb_context: *mut AutoConfigCallbackContext,
374) {
375 platform_log(LOG_TAG, "calling rcs_client_start_config()");
376
377 let cb_context = AutoConfigCallbackContextWrapper(NonNull::new(cb_context).unwrap());
378
379 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
380 if let Some(client) = client.as_ref() {
381 let subscription_id = client.subscription_id;
382
383 let mcc = client.mcc;
384 let mnc = client.mnc;
385 let imsi = String::from(&client.imsi);
386 let imei = String::from(&client.imei);
387
388 let context = Arc::clone(&client.context);
389
390 let p_cb_context = Arc::new(Mutex::new(cb_context));
391 let r_cb_context: Arc<Mutex<AutoConfigCallbackContextWrapper>> =
392 Arc::clone(&p_cb_context);
393
394 rcs_runtime.rt.spawn(async move {
397 match start_auto_config(
398 subscription_id,
399 mcc,
400 mnc,
401 &imei,
402 &imsi,
403 None,
404 context,
405 move |status| {
406 if let Some(cb) = p_cb {
407 let cb_context = p_cb_context.lock().unwrap();
408 let cb_context_ptr = cb_context.0.as_ptr();
409 cb(status, cb_context_ptr);
410 }
411 },
412 )
413 .await
414 {
415 Ok((
416 msisdn,
417 rcs_application_characteristic,
418 ims_application_characteristic,
419 )) => {
420 platform_log(
421 LOG_TAG,
422 format!("on configuration success with provided msisdn {:?}", msisdn),
423 );
424
425 if let (
426 Ok(ims_application_characteristic_string),
427 Ok(rcs_application_characteristic_string),
428 ) = (
429 ims_application_characteristic.to_c_string(),
430 rcs_application_characteristic.to_c_string(),
431 ) {
432 platform_log(
433 LOG_TAG,
434 format!("with ims-app {:?}", ims_application_characteristic_string),
435 );
436
437 platform_log(
438 LOG_TAG,
439 format!("with rcs-app {:?}", rcs_application_characteristic_string),
440 );
441
442 if let Some(cb) = r_cb {
444 let ims_config = ims_application_characteristic_string.as_ptr();
445 let rcs_config = rcs_application_characteristic_string.as_ptr();
446 let extra = ptr::null();
447 let cb_context: std::sync::MutexGuard<
448 '_,
449 AutoConfigCallbackContextWrapper,
450 > = r_cb_context.lock().unwrap();
451 let cb_context_ptr = cb_context.0.as_ptr();
452 cb(0, ims_config, rcs_config, extra, cb_context_ptr);
453 }
454 } else {
455 if let Some(cb) = r_cb {
456 let ims_config = ptr::null();
457 let rcs_config = ptr::null();
458 let extra = ptr::null();
459 let cb_context = r_cb_context.lock().unwrap();
460 let cb_context_ptr = cb_context.0.as_ptr();
461 cb(-1, ims_config, rcs_config, extra, cb_context_ptr);
462 }
463 }
464 }
465
466 Err(status) => {
467 platform_log(LOG_TAG, format!("on configuration error {:?}", status));
468
469 match status {
470 DeviceConfigurationStatus::InvalidArgument => {
471 if let Some(cb) = r_cb {
472 let ims_config = ptr::null();
473 let rcs_config = ptr::null();
474 let extra = ptr::null();
475 let cb_context = r_cb_context.lock().unwrap();
476 let cb_context_ptr = cb_context.0.as_ptr();
477 cb(-1, ims_config, rcs_config, extra, cb_context_ptr);
478 }
479 }
480
481 DeviceConfigurationStatus::Forbidden => {
482 if let Some(cb) = r_cb {
483 let ims_config = ptr::null();
484 let rcs_config = ptr::null();
485 let extra = ptr::null();
486 let cb_context = r_cb_context.lock().unwrap();
487 let cb_context_ptr = cb_context.0.as_ptr();
488 cb(-1, ims_config, rcs_config, extra, cb_context_ptr);
489 }
490 }
491
492 DeviceConfigurationStatus::Retry(reason, timeout) => {
493 if let Some(cb) = r_cb {
494 let ims_config = ptr::null();
495 let rcs_config = ptr::null();
496 let extra = ptr::null();
497 let cb_context = r_cb_context.lock().unwrap();
498 let cb_context_ptr = cb_context.0.as_ptr();
499 cb(-1, ims_config, rcs_config, extra, cb_context_ptr);
500 }
501 } }
535 }
536 }
537 });
538 }
539 }
540}
541
542#[no_mangle]
543pub unsafe extern "C" fn rcs_client_input_otp(
544 rcs_runtime: *mut RcsRuntime,
545 client: *const RcsClient,
546 otp: *const c_char,
547) {
548 platform_log(LOG_TAG, "calling rcs_client_input_otp()");
549
550 if let Some(client) = client.as_ref() {
551 let otp = CStr::from_ptr(otp).to_string_lossy().into_owned();
552
553 client.context.broadcast_otp(&otp);
554 }
555}
556
557#[no_mangle]
558pub unsafe extern "C" fn rcs_client_setup(
559 rcs_runtime: *mut RcsRuntime,
560 client: *mut RcsClient,
561 ims_config: *const c_char,
562 rcs_config: *const c_char,
563) {
564 platform_log(LOG_TAG, "calling rcs_client_setup()");
565
566 if let Some(client) = client.as_mut() {
567 let ims_config = CStr::from_ptr(ims_config).to_string_lossy().into_owned();
568 let rcs_config = CStr::from_ptr(rcs_config).to_string_lossy().into_owned();
569
570 let gba_context = client.context.make_gba_context(
571 &client.imsi,
572 client.mcc,
573 client.mnc,
574 client.subscription_id,
575 );
576 let gba_context = Arc::new(gba_context);
577
578 client.engine.configure(ims_config, rcs_config);
579 client.engine_gba_context.replace(gba_context);
580 }
581}
582
583#[no_mangle]
584pub unsafe extern "C" fn rcs_client_connect(rcs_runtime: *mut RcsRuntime, client: *mut RcsClient) {
585 platform_log(LOG_TAG, "calling rcs_client_connect()");
586
587 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
588 let rt = Arc::clone(&rcs_runtime.rt);
589 if let Some(client) = client.as_mut() {
590 client.engine.connect(rt);
591 }
592 }
593}
594
595#[no_mangle]
596pub unsafe extern "C" fn rcs_client_disconnect(
597 rcs_runtime: *mut RcsRuntime,
598 client: *mut RcsClient,
599) {
600 platform_log(LOG_TAG, "calling rcs_client_disconnect()");
601
602 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
603 let rt = Arc::clone(&rcs_runtime.rt);
604 if let Some(client) = client.as_mut() {
605 client.engine.disconnect(rt);
606 }
607 }
608}
609
610#[no_mangle]
611pub unsafe extern "C" fn rcs_client_send_message(
612 rcs_runtime: *mut RcsRuntime,
613 client: *mut RcsClient,
614 message_type: *const c_char,
615 message_content: *const c_char,
616 recipient: *const c_char,
617 recipient_type: i8,
618 cb: Option<MessageResultCallback>,
619 cb_context: *mut MessageResultCallbackContext,
620) {
621 platform_log(LOG_TAG, "calling rcs_client_send_message()");
622
623 let cb_context = if cb_context.is_null() {
624 None
625 } else {
626 Some(MessageResultCallbackContextWrapper(
627 NonNull::new(cb_context).unwrap(),
628 ))
629 };
630
631 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
632 if let Some(client) = client.as_ref() {
633 let message_type = CStr::from_ptr(message_type).to_string_lossy();
634 let message_content = CStr::from_ptr(message_content).to_string_lossy();
635 let recipient = CStr::from_ptr(recipient).to_string_lossy();
636 let recipient_type = get_recipient_type(recipient_type);
637
638 if let Some(recipient_type) = recipient_type {
639 let cb_context_ = if let Some(cb_context) = cb_context {
640 Some(Arc::new(Mutex::new(cb_context)))
641 } else {
642 None
643 };
644
645 client.engine.send_message(
646 &message_type,
647 &message_content,
648 &recipient,
649 recipient_type,
650 move |status_code, reason_phrase| {
651 if let Some(cb) = cb {
652 let reason_phrase = CString::new(reason_phrase).unwrap();
653 if let Some(cb_context_) = cb_context_ {
654 let cb_context = cb_context_.lock().unwrap();
655 cb(status_code, reason_phrase.as_ptr(), cb_context.0.as_ptr());
656 } else {
657 cb(status_code, reason_phrase.as_ptr(), ptr::null_mut());
658 }
659 }
660 },
661 &rcs_runtime.rt,
662 );
663
664 return;
665 }
666 }
667 }
668
669 if let Some(cb) = cb {
670 let reason_phrase = CString::new("Forbidden").unwrap();
671 cb(
672 403,
673 reason_phrase.as_ptr(),
674 if let Some(cb_context) = cb_context {
675 cb_context.0.as_ptr()
676 } else {
677 ptr::null_mut()
678 },
679 );
680 }
681}
682
683#[no_mangle]
684pub unsafe extern "C" fn rcs_client_send_imdn_report(
685 rcs_runtime: *mut RcsRuntime,
686 client: *mut RcsClient,
687 imdn_content: *const c_char,
688 sender_uri: *const c_char,
689 sender_service_type: i32,
690 sender_session_handle: *mut MessagingSessionHandle,
691 cb: Option<SendImdnReportResultCallback>,
692 cb_context: *mut SendImdnReportResultCallbackContext,
693) {
694 platform_log(LOG_TAG, "calling rcs_client_send_imdn_report()");
695
696 let cb_context = if cb_context.is_null() {
697 None
698 } else {
699 Some(SendImdnReportResultCallbackContextWrapper(
700 NonNull::new(cb_context).unwrap(),
701 ))
702 };
703
704 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
705 let rt = Arc::clone(&rcs_runtime.rt);
706 if let Some(client) = client.as_ref() {
707 let cb_context_ = if let Some(cb_context) = cb_context {
708 Some(Arc::new(Mutex::new(cb_context)))
709 } else {
710 None
711 };
712
713 let imdn_content = CStr::from_ptr(imdn_content).to_string_lossy();
714 let sender_uri = CStr::from_ptr(sender_uri).to_string_lossy();
715
716 client.engine.send_imdn_report(
717 &imdn_content,
718 &sender_uri,
719 sender_service_type,
720 sender_session_handle,
721 rt,
722 move |status_code, reason_phrase| {
723 if let Some(cb) = cb {
724 let reason_phrase = CString::new(reason_phrase).unwrap();
725 if let Some(cb_context_) = cb_context_ {
726 let cb_context = cb_context_.lock().unwrap();
727 cb(status_code, reason_phrase.as_ptr(), cb_context.0.as_ptr());
728 } else {
729 cb(status_code, reason_phrase.as_ptr(), ptr::null_mut());
730 }
731 }
732 },
733 );
734
735 return;
736 }
737 }
738
739 if let Some(cb) = cb {
740 let reason_phrase = CString::new("Forbidden").unwrap();
741 cb(
742 403,
743 reason_phrase.as_ptr(),
744 if let Some(cb_context) = cb_context {
745 cb_context.0.as_ptr()
746 } else {
747 ptr::null_mut()
748 },
749 );
750 }
751}
752
753#[no_mangle]
754pub unsafe extern "C" fn rcs_client_upload_file(
755 rcs_runtime: *mut RcsRuntime,
756 client: *mut RcsClient,
757 tid: *const c_char,
758 file_path: *const c_char,
759 file_name: *const c_char,
760 file_mime: *const c_char,
761 file_hash: *const c_char,
762 thumbnail_path: *const c_char,
763 thumbnail_name: *const c_char,
764 thumbnail_mime: *const c_char,
765 thumbnail_hash: *const c_char,
766 progress_cb: Option<UploadFileProgressCallback>,
767 progress_cb_context: *mut UploadFileProgressCallbackContext,
768 result_cb: Option<UploadFileResultCallback>,
769 result_cb_context: *mut UploadFileResultCallbackContext,
770) {
771 platform_log(LOG_TAG, "calling rcs_client_upload_file()");
772
773 let progress_cb_context = if progress_cb_context.is_null() {
774 None
775 } else {
776 Some(UploadFileProgressCallbackContextWrapper(
777 NonNull::new(progress_cb_context).unwrap(),
778 ))
779 };
780
781 let result_cb_context = if result_cb_context.is_null() {
782 None
783 } else {
784 Some(UploadFileResultCallbackContextWrapper(
785 NonNull::new(result_cb_context).unwrap(),
786 ))
787 };
788
789 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
790 let rt = Arc::clone(&rcs_runtime.rt);
791 if let Some(client) = client.as_ref() {
792 let msisdn = client.msisdn.as_deref();
793
794 if let Some(gba_context) = &client.engine_gba_context {
795 let gba_context = Arc::clone(gba_context);
796 let http_client = client.context.get_http_client();
797 let security_context = client.context.get_security_context();
798
799 let tid = CStr::from_ptr(tid).to_string_lossy();
800
801 let file_path = CStr::from_ptr(file_path).to_string_lossy();
802 let file_name = CStr::from_ptr(file_name).to_string_lossy();
803 let file_mime = CStr::from_ptr(file_mime).to_string_lossy();
804 let file_hash = if file_hash.is_null() {
805 None
806 } else {
807 Some(CStr::from_ptr(file_hash).to_string_lossy())
808 };
809
810 let thumbnail_path = if thumbnail_path.is_null() {
811 None
812 } else {
813 Some(CStr::from_ptr(thumbnail_path).to_string_lossy())
814 };
815
816 let thumbnail_name = if thumbnail_name.is_null() {
817 None
818 } else {
819 Some(CStr::from_ptr(thumbnail_name).to_string_lossy())
820 };
821
822 let thumbnail_mime = if thumbnail_mime.is_null() {
823 None
824 } else {
825 Some(CStr::from_ptr(thumbnail_mime).to_string_lossy())
826 };
827
828 let thumbnail_hash = if thumbnail_hash.is_null() {
829 None
830 } else {
831 Some(CStr::from_ptr(thumbnail_hash).to_string_lossy())
832 };
833
834 let progress_cb_context_ = if let Some(progress_cb_context) = progress_cb_context {
835 Some(Arc::new(Mutex::new(progress_cb_context)))
836 } else {
837 None
838 };
839
840 let result_cb_context_ = if let Some(result_cb_context) = result_cb_context {
841 Some(Arc::new(Mutex::new(result_cb_context)))
842 } else {
843 None
844 };
845
846 client.engine.upload_file(
847 &tid,
848 &file_path,
849 &file_name,
850 &file_mime,
851 file_hash.as_deref(),
852 thumbnail_path.as_deref(),
853 thumbnail_name.as_deref(),
854 thumbnail_mime.as_deref(),
855 thumbnail_hash.as_deref(),
856 msisdn,
857 http_client,
858 gba_context,
859 security_context,
860 rt,
861 move |current, total| {
862 if let Some(progress_cb) = progress_cb {
863 if let Some(progress_cb_context_) = &progress_cb_context_ {
864 let progress_cb_context = progress_cb_context_.lock().unwrap();
865 progress_cb(current, total, progress_cb_context.0.as_ptr());
866 } else {
867 progress_cb(current, total, ptr::null_mut());
868 }
869 }
870 },
871 move |status_code, reason_phrase, result_xml| {
872 if let Some(result_cb) = result_cb {
873 let reason_phrase = CString::new(reason_phrase).unwrap();
874 let result_xml = if let Some(result_xml) = result_xml {
875 let result_xml = CString::new(result_xml).unwrap();
876 Some(result_xml)
877 } else {
878 None
879 };
880 if let Some(result_cb_context_) = result_cb_context_ {
881 let result_cb_context = result_cb_context_.lock().unwrap();
882 result_cb(
883 status_code,
884 reason_phrase.as_ptr(),
885 if let Some(result_xml) = result_xml {
886 result_xml.as_ptr()
887 } else {
888 ptr::null()
889 },
890 result_cb_context.0.as_ptr(),
891 );
892 } else {
893 result_cb(
894 status_code,
895 reason_phrase.as_ptr(),
896 if let Some(result_xml) = result_xml {
897 result_xml.as_ptr()
898 } else {
899 ptr::null()
900 },
901 ptr::null_mut(),
902 );
903 }
904 }
905 },
906 );
907
908 return;
909 }
910 }
911 }
912
913 if let Some(result_cb) = result_cb {
914 let reason_phrase = CString::new("Forbidden").unwrap();
915 let xml = ptr::null();
916 let result_cb_context_ptr = if let Some(result_cb_context) = result_cb_context {
917 result_cb_context.0.as_ptr()
918 } else {
919 ptr::null_mut()
920 };
921 result_cb(403, reason_phrase.as_ptr(), xml, result_cb_context_ptr);
922 }
923}
924
925#[no_mangle]
926pub unsafe extern "C" fn rcs_client_download_file(
927 rcs_runtime: *mut RcsRuntime,
928 client: *mut RcsClient,
929 file_uri: *const c_char,
930 download_path: *const c_char,
931 start: u32,
932 total: i32,
933 progress_cb: Option<DownloadFileProgressCallback>,
934 progress_cb_context: *mut DownloadFileProgressCallbackContext,
935 result_cb: Option<DownloadFileResultCallback>,
936 result_cb_context: *mut DownloadFileResultCallbackContext,
937) {
938 platform_log(LOG_TAG, "calling rcs_client_download_file()");
939
940 let progress_cb_context = if progress_cb_context.is_null() {
941 None
942 } else {
943 Some(DownloadileProgressCallbackContextWrapper(
944 NonNull::new(progress_cb_context).unwrap(),
945 ))
946 };
947
948 let result_cb_context = if result_cb_context.is_null() {
949 None
950 } else {
951 Some(DownloadFileResultCallbackContextWrapper(
952 NonNull::new(result_cb_context).unwrap(),
953 ))
954 };
955
956 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
957 let rt = Arc::clone(&rcs_runtime.rt);
958 if let Some(client) = client.as_ref() {
959 let msisdn = client.msisdn.as_deref();
960
961 if let Some(gba_context) = &client.engine_gba_context {
962 let gba_context = Arc::clone(gba_context);
963 let http_client = client.context.get_http_client();
964 let security_context = client.context.get_security_context();
965
966 let file_uri = CStr::from_ptr(file_uri).to_string_lossy();
967 let download_path = CStr::from_ptr(download_path).to_string_lossy();
968
969 if let Ok(start) = usize::try_from(start) {
970 let total = if total > 0 {
971 if let Ok(total) = usize::try_from(total) {
972 Some(total)
973 } else {
974 None
975 }
976 } else {
977 None
978 };
979
980 let progress_cb_context_ =
981 if let Some(progress_cb_context) = progress_cb_context {
982 Some(Arc::new(Mutex::new(progress_cb_context)))
983 } else {
984 None
985 };
986
987 let result_cb_context_ = if let Some(result_cb_context) = result_cb_context {
988 Some(Arc::new(Mutex::new(result_cb_context)))
989 } else {
990 None
991 };
992
993 client.engine.download_file(
994 &file_uri,
995 &download_path,
996 start,
997 total,
998 msisdn,
999 http_client,
1000 gba_context,
1001 security_context,
1002 rt,
1003 move |current, total| {
1004 if let Some(progress_cb) = progress_cb {
1005 if let Some(progress_cb_context_) = &progress_cb_context_ {
1006 let progress_cb_context = progress_cb_context_.lock().unwrap();
1007 progress_cb(current, total, progress_cb_context.0.as_ptr());
1008 } else {
1009 progress_cb(current, total, ptr::null_mut());
1010 }
1011 }
1012 },
1013 move |status_code, reason_phrase| {
1014 if let Some(result_cb) = result_cb {
1015 let reason_phrase = CString::new(reason_phrase).unwrap();
1016 if let Some(result_cb_context_) = result_cb_context_ {
1017 let result_cb_context = result_cb_context_.lock().unwrap();
1018 result_cb(
1019 status_code,
1020 reason_phrase.as_ptr(),
1021 result_cb_context.0.as_ptr(),
1022 );
1023 } else {
1024 result_cb(status_code, reason_phrase.as_ptr(), ptr::null_mut());
1025 }
1026 }
1027 },
1028 );
1029
1030 return;
1031 }
1032 }
1033 }
1034 }
1035
1036 if let Some(cb) = result_cb {
1037 let reason_phrase = CString::new("Forbidden").unwrap();
1038 let result_cb_context_ptr = if let Some(result_cb_context) = result_cb_context {
1039 result_cb_context.0.as_ptr()
1040 } else {
1041 ptr::null_mut()
1042 };
1043 cb(403, reason_phrase.as_ptr(), result_cb_context_ptr);
1044 }
1045}
1046
1047#[no_mangle]
1048pub unsafe extern "C" fn destroy_rcs_messaging_session(
1049 session_handle: *mut MessagingSessionHandle,
1050) {
1051 let _ = Box::from_raw(session_handle);
1052}
1053
1054#[no_mangle]
1055pub unsafe extern "C" fn rcs_client_create_multi_conference_v1(
1056 rcs_runtime: *mut RcsRuntime,
1057 client: *mut RcsClient,
1058 recipients: *const c_char,
1059 offer_sdp: *const c_char,
1060 event_cb: Option<MultiConferenceEventListener>,
1061 event_cb_context: *mut MultiConferenceEventListenerContext,
1062 result_cb: Option<MultiConferenceCreateResultCallback>,
1063 result_cb_context: *mut MultiConferenceCreateResultCallbackContext,
1064) {
1065 platform_log(LOG_TAG, "calling rcs_client_create_multi_conference_v1()");
1066
1067 let event_cb_context =
1068 MultiConferenceEventListenerContextWrapper(NonNull::new(event_cb_context).unwrap());
1069
1070 let result_cb_context =
1071 MultiConferenceCreateResultCallbackContextWrapper(NonNull::new(result_cb_context).unwrap());
1072
1073 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
1074 if let Some(client) = client.as_ref() {
1075 let recipients = CStr::from_ptr(recipients).to_string_lossy();
1076 let offer_sdp = CStr::from_ptr(offer_sdp).to_string_lossy();
1077
1078 let result_cb_context = Arc::new(Mutex::new(result_cb_context));
1079
1080 client.engine.create_conference_v1(
1081 &recipients,
1082 &offer_sdp,
1083 event_cb,
1084 event_cb_context,
1085 &rcs_runtime.rt,
1086 move |result| {
1087 if let Some(cb) = result_cb {
1088 let cb_context = result_cb_context.lock().unwrap();
1089 let cb_context_ptr = cb_context.0.as_ptr();
1090 if let Some((conf, sdp)) = result {
1091 let sdp_ptr = sdp.as_ptr();
1092 let sdp_len = sdp.len();
1093 cb(Some(Box::new(conf)), sdp_ptr, sdp_len, cb_context_ptr);
1094 } else {
1095 cb(None, ptr::null(), 0, cb_context_ptr);
1096 }
1097 }
1098 },
1099 );
1100
1101 return;
1102 }
1103 }
1104}
1105
1106#[no_mangle]
1107pub unsafe extern "C" fn rcs_client_join_multi_conference_v1(
1108 rcs_runtime: *mut RcsRuntime,
1109 client: *mut RcsClient,
1110 conference_id: *const c_char,
1111 offer_sdp: *const c_char,
1112 event_cb: Option<MultiConferenceEventListener>,
1113 event_cb_context: *mut MultiConferenceEventListenerContext,
1114 result_cb: Option<MultiConferenceJoinResultCallback>,
1115 result_cb_context: *mut MultiConferenceJoinResultCallbackContext,
1116) {
1117 platform_log(LOG_TAG, "calling rcs_client_join_multi_conference_v1()");
1118
1119 let _ = MultiConferenceEventListenerContextWrapper(NonNull::new(event_cb_context).unwrap());
1120 let _ =
1121 MultiConferenceJoinResultCallbackContextWrapper(NonNull::new(result_cb_context).unwrap());
1122}
1123
1124#[no_mangle]
1143pub unsafe extern "C" fn rcs_multi_conference_v1_keep_alive(
1144 rcs_runtime: *mut RcsRuntime,
1145 conference: *mut MultiConferenceV1,
1146) {
1147 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
1148 if let Some(conference) = conference.as_mut() {
1149 conference.keep_alive(&rcs_runtime.rt);
1150 }
1151 }
1152}
1153
1154#[no_mangle]
1155pub unsafe extern "C" fn rcs_client_retrieve_specific_chatbots(
1156 rcs_runtime: *mut RcsRuntime,
1157 client: *mut RcsClient,
1158 local_etag: *const c_char,
1159 cb: Option<RetrieveSpecificChatbotsResultCallback>,
1160 cb_context: *mut RetrieveSpecificChatbotsResultCallbackContext,
1161) {
1162 platform_log(LOG_TAG, "calling rcs_client_retrieve_specific_chatbots()");
1163
1164 let cb_context =
1165 RetrieveSpecificChatbotsResultCallbackContextWrapper(NonNull::new(cb_context).unwrap());
1166
1167 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
1168 let rt = Arc::clone(&rcs_runtime.rt);
1169 if let Some(client) = client.as_mut() {
1170 let local_etag = if local_etag.is_null() {
1171 None
1172 } else {
1173 Some(CStr::from_ptr(local_etag).to_string_lossy())
1174 };
1175
1176 let msisdn = client.msisdn.as_deref();
1177
1178 if let Some(gba_context) = &client.engine_gba_context {
1179 let gba_context = Arc::clone(gba_context);
1180 let http_client = client.context.get_http_client();
1181 let security_context = client.context.get_security_context();
1182
1183 let cb_context_ = Arc::new(Mutex::new(cb_context));
1184
1185 client.engine.retrieve_specific_chatbots(
1186 local_etag.as_deref(),
1187 msisdn,
1188 http_client,
1189 gba_context,
1190 security_context,
1191 rt,
1192 move |status_code, reason_phrase, specific_chatbots, response_etag, expiry| {
1193 if let Some(cb) = cb {
1194 let reason_phrase = CString::new(reason_phrase).unwrap();
1195
1196 let specific_chatbots = match specific_chatbots {
1197 Some(specific_chatbots) => {
1198 Some(CString::new(specific_chatbots).unwrap())
1199 }
1200 None => None,
1201 };
1202 let response_etag = match response_etag {
1203 Some(response_etag) => Some(CString::new(response_etag).unwrap()),
1204 None => None,
1205 };
1206
1207 let cb_context = cb_context_.lock().unwrap();
1208 let cb_context_ptr = cb_context.0.as_ptr();
1209 cb(
1210 status_code,
1211 reason_phrase.as_ptr(),
1212 if let Some(specific_chatbots) = &specific_chatbots {
1213 specific_chatbots.as_ptr()
1214 } else {
1215 std::ptr::null()
1216 },
1217 if let Some(response_etag) = &response_etag {
1218 response_etag.as_ptr()
1219 } else {
1220 std::ptr::null()
1221 },
1222 expiry,
1223 cb_context_ptr,
1224 );
1225 }
1226 },
1227 );
1228
1229 return;
1230 }
1231 }
1232 }
1233
1234 if let Some(cb) = cb {
1235 let reason_phrase = CString::new("Forbidden").unwrap();
1236 let cb_context_ptr = cb_context.0.as_ptr();
1237 cb(
1238 403,
1239 reason_phrase.as_ptr(),
1240 std::ptr::null(),
1241 std::ptr::null(),
1242 0,
1243 cb_context_ptr,
1244 );
1245 }
1246}
1247
1248#[no_mangle]
1249pub unsafe extern "C" fn rcs_client_search_chatbot(
1250 rcs_runtime: *mut RcsRuntime,
1251 client: *mut RcsClient,
1252 query: *const c_char,
1253 start: u32,
1254 num: u32,
1255 cb: Option<SearchChatbotResultCallback>,
1256 cb_context: *mut SearchChatbotResultCallbackContext,
1257) {
1258 platform_log(LOG_TAG, "calling rcs_client_search_chatbot()");
1259
1260 let cb_context = SearchChatbotResultCallbackContextWrapper(NonNull::new(cb_context).unwrap());
1261
1262 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
1263 let rt = Arc::clone(&rcs_runtime.rt);
1264 if let Some(client) = client.as_mut() {
1265 let query = CStr::from_ptr(query).to_string_lossy();
1266 let home_operator = format!("{}{}", client.mcc.string_repr(), client.mnc.string_repr());
1267 let msisdn = client.msisdn.as_deref();
1268
1269 if let Some(gba_context) = &client.engine_gba_context {
1270 let gba_context = Arc::clone(gba_context);
1271 let http_client = client.context.get_http_client();
1272 let security_context = client.context.get_security_context();
1273
1274 let cb_context_ = Arc::new(Mutex::new(cb_context));
1275
1276 client.engine.search_chatbot(
1277 &query,
1278 start,
1279 num,
1280 &home_operator,
1281 msisdn,
1282 http_client,
1283 gba_context,
1284 security_context,
1285 rt,
1286 move |status_code, reason_phrase, json| {
1287 if let Some(cb) = cb {
1288 let reason_phrase = CString::new(reason_phrase).unwrap();
1289 let json = match json {
1290 Some(json) => Some(CString::new(json).unwrap()),
1291 None => None,
1292 };
1293
1294 let cb_context = cb_context_.lock().unwrap();
1295 let cb_context_ptr = cb_context.0.as_ptr();
1296 cb(
1297 status_code,
1298 reason_phrase.as_ptr(),
1299 if let Some(json) = &json {
1300 json.as_ptr()
1301 } else {
1302 std::ptr::null()
1303 },
1304 cb_context_ptr,
1305 );
1306 }
1307 },
1308 );
1309
1310 return;
1311 }
1312 }
1313 }
1314
1315 if let Some(cb) = cb {
1316 let reason_phrase = CString::new("Forbidden").unwrap();
1317 let json = ptr::null();
1318 let cb_context_ptr = cb_context.0.as_ptr();
1319 cb(403, reason_phrase.as_ptr(), json, cb_context_ptr);
1320 }
1321}
1322
1323#[no_mangle]
1324pub unsafe extern "C" fn rcs_client_retrieve_chatbot_info(
1325 rcs_runtime: *mut RcsRuntime,
1326 client: *mut RcsClient,
1327 chatbot_sip_uri: *const c_char,
1328 local_etag: *const c_char,
1329 cb: Option<RetrieveChatbotInfoResultCallback>,
1330 cb_context: *mut RetrieveChatbotInfoResultCallbackContext,
1331) {
1332 platform_log(LOG_TAG, "calling rcs_client_retrieve_chatbot_info()");
1333
1334 let cb_context =
1335 RetrieveChatbotInfoResultCallbackContextWrapper(NonNull::new(cb_context).unwrap());
1336
1337 if let Some(rcs_runtime) = rcs_runtime.as_ref() {
1338 let rt = Arc::clone(&rcs_runtime.rt);
1339 if let Some(client) = client.as_mut() {
1340 let chatbot_sip_uri = CStr::from_ptr(chatbot_sip_uri).to_string_lossy();
1341 let local_etag = if local_etag.is_null() {
1342 None
1343 } else {
1344 Some(CStr::from_ptr(local_etag).to_string_lossy())
1345 };
1346 let home_operator = format!("{}{}", client.mcc.string_repr(), client.mnc.string_repr());
1347 let home_language = String::from("zh"); let msisdn = client.msisdn.as_deref();
1349
1350 if let Some(gba_context) = &client.engine_gba_context {
1351 let gba_context = Arc::clone(gba_context);
1352 let http_client = client.context.get_http_client();
1353 let security_context = client.context.get_security_context();
1354
1355 let cb_context_ = Arc::new(Mutex::new(cb_context));
1356
1357 client.engine.retrieve_chatbot_info(
1358 &chatbot_sip_uri,
1359 local_etag.as_deref(),
1360 &home_operator,
1361 &home_language,
1362 msisdn,
1363 http_client,
1364 gba_context,
1365 security_context,
1366 rt,
1367 move |status_code, reason_phrase, chatbot_info, response_etag, expiry| {
1368 if let Some(cb) = cb {
1369 let reason_phrase = CString::new(reason_phrase).unwrap();
1370
1371 let chatbot_info = match chatbot_info {
1372 Some(chatbot_info) => Some(CString::new(chatbot_info).unwrap()),
1373 None => None,
1374 };
1375 let response_etag = match response_etag {
1376 Some(response_etag) => Some(CString::new(response_etag).unwrap()),
1377 None => None,
1378 };
1379
1380 let cb_context = cb_context_.lock().unwrap();
1381 let cb_context_ptr = cb_context.0.as_ptr();
1382 cb(
1383 status_code,
1384 reason_phrase.as_ptr(),
1385 if let Some(chatbot_info) = &chatbot_info {
1386 chatbot_info.as_ptr()
1387 } else {
1388 std::ptr::null()
1389 },
1390 if let Some(response_etag) = &response_etag {
1391 response_etag.as_ptr()
1392 } else {
1393 std::ptr::null()
1394 },
1395 expiry,
1396 cb_context_ptr,
1397 );
1398 }
1399 },
1400 );
1401
1402 return;
1403 }
1404 }
1405 }
1406
1407 if let Some(cb) = cb {
1408 let reason_phrase = CString::new("Forbidden").unwrap();
1409 let cb_context_ptr = cb_context.0.as_ptr();
1410 cb(
1411 403,
1412 reason_phrase.as_ptr(),
1413 ptr::null(),
1414 ptr::null(),
1415 0,
1416 cb_context_ptr,
1417 );
1418 }
1419}
1420
1421#[no_mangle]
1422pub unsafe extern "C" fn destroy_rcs_client(client: *mut RcsClient) {
1423 let _ = Box::from_raw(client);
1424}