1extern crate chrono;
16extern crate cookie;
17extern crate futures;
18extern crate url;
19
20use std::fmt;
21use std::str;
22use std::sync::Arc;
23
24use chrono::{DateTime, Duration, FixedOffset, Utc};
25
26use cookie::{Cookie, CookieJar};
27
28use futures::future::{BoxFuture, FutureExt};
29use futures::io::AsyncReadExt;
30
31use rust_rcs_core::security::authentication::digest::DigestAnswerParams;
32use tokio::time::timeout;
33use url::Url;
34
35use rust_rcs_core::ffi::log::platform_log;
36
37use rust_rcs_core::http::request::{Request, GET};
38
39use rust_rcs_core::internet::header::{self, Header, HeaderSearch};
40
41use rust_rcs_core::security::gba::{self, GbaContext};
42
43use rust_rcs_core::third_gen_pp::{CountryCode, NetworkCode, ThreeDigit};
44
45use rust_rcs_core::util::raw_string::ToInt;
46
47use crate::context::Context;
48
49use super::characteristic::Characteristic;
50use super::ims_application::GetImsApplication;
51use super::local_provisioning_doc::LocalProvisioningDoc;
52use super::local_provisioning_doc::{
53 load_provisionging_doc, store_provisionging_doc, ProvisiongingInfo,
54};
55use super::rcs_application::GetRcsApplication;
56use super::wap_provisioning_doc::DEFAULT_APPLICATION_PORT;
57use super::wap_provisioning_doc::{AccessControlInfo, WapProvisioningDoc};
58
59const LOG_TAG: &str = "dm";
60
61const ShouldReceiveZeroPortSMS: i32 = 1;
62
63pub enum RetryReason {
64 RequireCellularNetworkOrMsisdn,
65 ServiceUnavailable,
67 IO,
68 NetworkIO,
69}
70
71impl fmt::Debug for RetryReason {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 RetryReason::RequireCellularNetworkOrMsisdn => {
75 write!(f, "RequireCellularNetworkOrMsisdn")
76 }
77
78 RetryReason::ServiceUnavailable => {
82 write!(f, "ServiceUnavailable")
83 }
84
85 RetryReason::IO => {
86 write!(f, "IO")
87 }
88
89 RetryReason::NetworkIO => {
90 write!(f, "NetworkIO")
91 }
92 }
93 }
94}
95
96pub enum DeviceConfigurationStatus {
97 InvalidArgument,
98 Forbidden,
99 Retry(RetryReason, u32),
100}
101
102impl fmt::Debug for DeviceConfigurationStatus {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match self {
105 DeviceConfigurationStatus::InvalidArgument => {
106 write!(f, "InvalidArgument")
107 }
108
109 DeviceConfigurationStatus::Forbidden => {
110 write!(f, "Forbidden")
111 }
112
113 DeviceConfigurationStatus::Retry(reason, timeout) => {
114 write!(f, "Retry after {} because of reason: {:?}", timeout, reason)
115 }
116 }
117 }
118}
119
120enum ConfigurationServer {
121 Default(CountryCode, NetworkCode),
122 PreConfiguredDefault(String, String, String),
123 Additional(String, bool),
124}
125
126impl ConfigurationServer {
127 fn identifier(&self) -> String {
128 match self {
129 ConfigurationServer::Default(mcc, mnc) => {
130 format!(
131 "config.rcs.mnc{}.mcc{}.pub.3gppnetwork.org",
132 mnc.string_repr(),
133 mcc.string_repr()
134 )
135 }
136 ConfigurationServer::PreConfiguredDefault(identifier, _, _) => identifier.clone(),
137 ConfigurationServer::Additional(fqdn, _) => fqdn.clone(),
138 }
139 }
140
141 fn http_uri(&self) -> String {
142 match self {
143 ConfigurationServer::Default(mcc, mnc) => {
144 format!(
145 "http://config.rcs.mnc{}.mcc{}.pub.3gppnetwork.org",
146 mnc.string_repr(),
147 mcc.string_repr()
148 )
149 }
150 ConfigurationServer::PreConfiguredDefault(_, http_uri, _) => http_uri.clone(),
151 ConfigurationServer::Additional(fqdn, _) => {
152 format!("http://{}", fqdn)
153 }
154 }
155 }
156
157 fn https_uri(&self) -> String {
158 match self {
159 ConfigurationServer::Default(mcc, mnc) => {
160 format!(
161 "https://config.rcs.mnc{}.mcc{}.pub.3gppnetwork.org",
162 mnc.string_repr(),
163 mcc.string_repr()
164 )
165 }
166 ConfigurationServer::PreConfiguredDefault(_, _, https_uri) => https_uri.clone(),
167 ConfigurationServer::Additional(fqdn, _) => {
168 format!("https://{}", fqdn)
169 }
170 }
171 }
172}
173
174fn try_otp_configuration_request<'a, 'b: 'a>(
175 server: ConfigurationServer,
176 access_control_info: Option<AccessControlInfo<'a>>,
177 imei: &'b str,
178 imsi: &'b str,
179 msisdn: Option<&'b str>,
180 vers_version: i64,
181 token_string: Option<&'b str>,
182 otp: &'b str,
183 cookie_jar: Option<&'b CookieJar>,
184 is_over_cellular: bool,
185 context: Arc<Context>,
186 gba_context: &'a GbaContext,
187 progress_callback: Arc<Box<dyn Fn(i32) + Send + Sync + 'b>>,
188) -> BoxFuture<'a, Result<Option<String>, DeviceConfigurationStatus>> {
189 async move {
190 try_otp_configuration_request_inner(
191 server,
192 access_control_info,
193 imei,
194 imsi,
195 msisdn,
196 vers_version,
197 token_string,
198 otp,
199 cookie_jar,
200 is_over_cellular,
201 context,
202 gba_context,
203 progress_callback,
204 )
205 .await
206 }
207 .boxed()
208}
209
210async fn try_otp_configuration_request_inner(
211 server: ConfigurationServer,
212 access_control_info: Option<AccessControlInfo<'_>>,
213 imei: &str,
214 imsi: &str,
215 msisdn: Option<&str>,
216 vers_version: i64,
217 token_string: Option<&str>,
218 otp: &str,
219 cookie_jar: Option<&CookieJar>,
220 is_over_cellular: bool,
221 context: Arc<Context>,
222 gba_context: &GbaContext,
223 progress_callback: Arc<Box<dyn Fn(i32) + Send + Sync + '_>>,
224) -> Result<Option<String>, DeviceConfigurationStatus> {
225 let https_uri = server.https_uri();
226
227 let uri = format!("{}?otp={}", https_uri, otp);
228
229 if let Ok(url) = Url::parse(&uri) {
230 match context.get_http_client().connect(&url, false).await {
231 Ok(connection) => {
232 let host = url.host_str().unwrap();
233
234 let mut req = Request::new_with_default_headers(GET, host, url.path(), url.query());
235
236 if let Some(msisdn) = msisdn {
237 req.headers.push(Header::new(
238 b"X-3GPP-Intended-Identity",
239 format!("tel:{}", msisdn),
240 ));
241 }
242
243 if let Some(cookie_jar) = cookie_jar {
244 for cookie in cookie_jar.iter() {
245 req.headers.push(Header::new(b"Cookie", cookie.to_string()));
246 }
247 }
248
249 match connection.send(req, |_| {}).await {
250 Ok((resp, resp_stream)) => {
251 platform_log(
252 LOG_TAG,
253 format!(
254 "try_otp_configuration_request_inner resp.status_code = {}",
255 resp.status_code
256 ),
257 );
258
259 if resp.status_code == 200 {
260 if let Some(mut resp_stream) = resp_stream {
261 let mut resp_data = Vec::new();
262 if let Ok(size) = resp_stream.read_to_end(&mut resp_data).await {
263 platform_log(
264 LOG_TAG,
265 format!(
266 "try_otp_configuration_request_inner resp.data read {} bytes",
267 &size
268 ),
269 );
270 if let Ok(resp_string) = String::from_utf8(resp_data) {
271 platform_log(
272 LOG_TAG,
273 format!(
274 "try_otp_configuration_request_inner resp.string = {}",
275 &resp_string
276 ),
277 );
278
279 match resp_string.parse::<WapProvisioningDoc>() {
280 Ok(wap_provisioning_doc) => {
281 return handle_config_for_server(
282 server,
283 access_control_info,
284 imei,
285 imsi,
286 msisdn,
287 &wap_provisioning_doc,
288 context,
289 gba_context,
290 progress_callback,
291 )
292 .await;
293 }
294
295 Err(e) => {
296 platform_log(
297 LOG_TAG,
298 format!("error parsing resp: {}", e),
299 );
300 }
301 }
302 }
303 }
304 }
305 } else if resp.status_code == 404 {
306 } else if resp.status_code == 408 {
307 } else if resp.status_code == 511 {
308 }
309 }
310
311 Err(e) => {
312 platform_log(
313 LOG_TAG,
314 format!(
315 "try_otp_configuration_request_inner send failed with error {:?}",
316 e
317 ),
318 );
319 }
320 }
321 }
322
323 Err(e) => {
324 platform_log(
325 LOG_TAG,
326 format!(
327 "try_otp_configuration_request_inner connect failed with error {:?}",
328 e
329 ),
330 );
331 }
332 }
333 }
334
335 Err(DeviceConfigurationStatus::Retry(RetryReason::NetworkIO, 0))
336}
337
338fn try_https_configuration_request<'a, 'b: 'a>(
339 server: ConfigurationServer,
340 access_control_info: Option<AccessControlInfo<'a>>,
341 imei: &'b str,
342 imsi: &'b str,
343 msisdn: Option<&'b str>,
344 vers_version: i64,
345 token_string: Option<&'b str>,
346 cookie_jar: Option<&'b CookieJar>,
347 digest_answer: Option<&'a DigestAnswerParams>,
348 is_over_cellular: bool,
349 context: Arc<Context>,
350 gba_context: &'b GbaContext,
351 progress_callback: Arc<Box<dyn Fn(i32) + Send + Sync + 'b>>,
352) -> BoxFuture<'a, Result<Option<String>, DeviceConfigurationStatus>> {
353 async move {
354 try_https_configuration_request_inner(
355 server,
356 access_control_info,
357 imei,
358 imsi,
359 msisdn,
360 vers_version,
361 token_string,
362 cookie_jar,
363 digest_answer,
364 is_over_cellular,
365 context,
366 gba_context,
367 progress_callback,
368 )
369 .await
370 }
371 .boxed()
372}
373
374async fn try_https_configuration_request_inner(
375 server: ConfigurationServer,
376 access_control_info: Option<AccessControlInfo<'_>>,
377 imei: &str,
378 imsi: &str,
379 msisdn: Option<&str>,
380 vers_version: i64,
381 token_string: Option<&str>,
382 cookie_jar: Option<&CookieJar>,
383 digest_answer: Option<&DigestAnswerParams>,
384 is_over_cellular: bool,
385 context: Arc<Context>,
386 gba_context: &GbaContext,
387 progress_callback: Arc<Box<dyn Fn(i32) + Send + Sync + '_>>,
388) -> Result<Option<String>, DeviceConfigurationStatus> {
389 let https_uri = server.https_uri();
390
391 let uri = match (msisdn, token_string) {
392 (Some(msisdn), Some(token_string)) => format!("{}?vers={}&IMSI={}&IMEI={}&msisdn={}&token={}&Channel_ID=&rcs_version=11.0&rcs_profile=UP_2.4&provisioning_version=5.0&app=ap2001&app=ap2002&app=urn%3Aoma%3Amo%3Aext-3gpp-ims%3A1.0", https_uri, vers_version, imsi, imei, msisdn, token_string),
393 (None, Some(token_string)) => format!("{}?vers={}&IMSI={}&IMEI={}&msisdn=&token={}&Channel_ID=&rcs_version=11.0&rcs_profile=UP_2.4&provisioning_version=5.0&app=ap2001&app=ap2002&app=urn%3Aoma%3Amo%3Aext-3gpp-ims%3A1.0", https_uri, vers_version, imsi, imei, token_string),
394 (Some(msisdn), None) => format!("{}?vers={}&IMSI={}&IMEI={}&msisdn={}&token=&SMS_port={}&Channel_ID=&rcs_version=11.0&rcs_profile=UP_2.4&provisioning_version=5.0&app=ap2001&app=ap2002&app=urn%3Aoma%3Amo%3Aext-3gpp-ims%3A1.0", https_uri, vers_version, imsi, imei, msisdn, DEFAULT_APPLICATION_PORT),
395 (None, None) => format!("{}?vers={}&IMSI={}&IMEI={}&msisdn=&token=&SMS_port={}&Channel_ID=&rcs_version=11.0&rcs_profile=UP_2.4&provisioning_version=5.0&app=ap2001&app=ap2002&app=urn%3Aoma%3Amo%3Aext-3gpp-ims%3A1.0", https_uri, vers_version, imsi, imei, DEFAULT_APPLICATION_PORT),
396 };
397
398 if let Ok(url) = Url::parse(&uri) {
399 match context.get_http_client().connect(&url, false).await {
400 Ok(connection) => {
401 let host = url.host_str().unwrap();
402
403 let mut req = Request::new_with_default_headers(GET, host, url.path(), url.query());
404
405 if let Some(msisdn) = msisdn {
406 req.headers.push(Header::new(
407 b"X-3GPP-Intended-Identity",
408 format!("tel:{}", msisdn),
409 ));
410 }
411
412 if let Some(digest_answer) = digest_answer {
413 if let Ok(authorization) = digest_answer.make_authorization_header(
414 match &digest_answer.challenge {
415 Some(challenge) => Some(&challenge.algorithm),
416 None => None,
417 },
418 false,
419 false,
420 ) {
421 if let Ok(authorization) = String::from_utf8(authorization) {
422 req.headers
423 .push(Header::new(b"Authorization", String::from(authorization)));
424 }
425 }
426 }
427
428 if let Some(cookie_jar) = cookie_jar {
429 for cookie in cookie_jar.iter() {
430 req.headers.push(Header::new(b"Cookie", cookie.to_string()));
431 }
432 }
433
434 match connection.send(req, |_| {}).await {
435 Ok((resp, resp_stream)) => {
436 platform_log(
437 LOG_TAG,
438 format!(
439 "try_https_configuration_request_inner resp.status_code = {}",
440 resp.status_code
441 ),
442 );
443
444 if resp.status_code == 200 {
445 if let Some(mut resp_stream) = resp_stream {
446 let mut resp_data = Vec::new();
447 if let Ok(size) = resp_stream.read_to_end(&mut resp_data).await {
448 platform_log(LOG_TAG, format!("try_https_configuration_request_inner resp.data read {}", &size));
449 if let Ok(resp_string) = String::from_utf8(resp_data) {
450 platform_log(
451 LOG_TAG,
452 format!(
453 "try_https_configuration_request_inner resp.string = {}",
454 &resp_string
455 ),
456 );
457
458 match resp_string.parse::<WapProvisioningDoc>() {
459 Ok(wap_provisioning_doc) => {
460 if let Some(port) =
461 wap_provisioning_doc.get_sms_policy()
462 {
463 if port == 0 {
464 progress_callback(ShouldReceiveZeroPortSMS);
465 }
466
467 let mut cookie_jar = CookieJar::new();
468 for header in HeaderSearch::new(
469 &resp.headers,
470 b"Set-Cookie",
471 false,
472 ) {
473 if let Ok(cookie_string) =
474 str::from_utf8(header.get_value())
475 {
476 if let Ok(cookie) =
477 cookie_string.parse::<Cookie>()
478 {
479 cookie_jar.add(cookie);
480 }
481 }
482 }
483
484 let mut otp_receiver = context.subscribe_otp();
485
486 match timeout(
487 std::time::Duration::from_secs(64),
488 otp_receiver.recv(),
489 )
490 .await
491 {
492 Ok(r) => match r {
493 Ok(otp) => {
494 return try_otp_configuration_request(server, access_control_info, imei, imsi, msisdn, vers_version, token_string, &otp, Some(&cookie_jar), is_over_cellular, context, gba_context, progress_callback).await;
495 }
496 Err(e) => {
497 platform_log(
498 LOG_TAG,
499 format!("failed to receive otp with error {}", e),
500 );
501 }
502 },
503 Err(e) => {
504 platform_log(
505 LOG_TAG,
506 format!(
507 "failed to receive otp in {}",
508 e
509 ),
510 );
511 }
512 }
513 } else {
514 return handle_config_for_server(
515 server,
516 access_control_info,
517 imei,
518 imsi,
519 msisdn,
520 &wap_provisioning_doc,
521 context,
522 gba_context,
523 progress_callback,
524 )
525 .await;
526 }
527 }
528
529 Err(e) => {
530 platform_log(
531 LOG_TAG,
532 format!("error parsing resp: {}", e),
533 );
534 }
535 }
536 }
537 }
538 } else {
539 platform_log(LOG_TAG, "try_https_configuration_request_inner resp has no readable stream")
540 }
541
542 let mut has_cookie_b = false;
543
544 let mut cookie_jar = CookieJar::new();
545 for header in HeaderSearch::new(&resp.headers, b"Set-Cookie", false) {
546 if let Ok(cookie_string) = str::from_utf8(header.get_value()) {
547 if let Ok(cookie) = cookie_string.parse::<Cookie>() {
548 cookie_jar.add(cookie);
549 has_cookie_b = true;
550 }
551 }
552 }
553
554 if has_cookie_b {
555 platform_log(LOG_TAG, "try_https_configuration_request_inner subscribe for otp before continue");
556
557 let mut otp_receiver = context.subscribe_otp();
558
559 match timeout(
560 std::time::Duration::from_secs(64),
561 otp_receiver.recv(),
562 )
563 .await
564 {
565 Ok(r) => match r {
566 Ok(otp) => {
567 return try_otp_configuration_request(
568 server,
569 access_control_info,
570 imei,
571 imsi,
572 msisdn,
573 vers_version,
574 token_string,
575 &otp,
576 Some(&cookie_jar),
577 is_over_cellular,
578 context,
579 gba_context,
580 progress_callback,
581 )
582 .await;
583 }
584 Err(e) => {
585 platform_log(
586 LOG_TAG,
587 format!("failed to receive otp with error {}", e),
588 );
589 }
590 },
591 Err(e) => {
592 platform_log(
593 LOG_TAG,
594 format!("failed to receive otp in {}", e),
595 );
596 }
597 }
598 } else {
599 platform_log(
600 LOG_TAG,
601 "received 200 OK without either wap-doc or Set-Cookie header",
602 );
603 }
604 } else if resp.status_code == 401 {
605 if digest_answer.is_none() {
606 if let Some(www_authenticate_header) =
607 header::search(&resp.headers, b"WWW-Authenticate", false)
608 {
609 if let Some(Ok(answer)) = gba::try_process_401_response(
610 gba_context,
611 host.as_bytes(),
612 connection.cipher_id(),
613 GET,
614 b"\"/\"",
615 None,
616 www_authenticate_header,
617 &context.get_http_client(),
618 &context.get_security_context(),
619 )
620 .await
621 {
622 return try_https_configuration_request(
623 server,
624 access_control_info,
625 imei,
626 imsi,
627 msisdn,
628 vers_version,
629 token_string,
630 cookie_jar,
631 Some(&answer),
632 is_over_cellular,
633 context,
634 gba_context,
635 progress_callback,
636 )
637 .await;
638 }
639 }
640 }
641 } else if resp.status_code == 403 {
642 if is_over_cellular {
643 return Err(DeviceConfigurationStatus::Forbidden);
644 } else {
645 return Err(DeviceConfigurationStatus::Retry(
646 RetryReason::RequireCellularNetworkOrMsisdn,
647 0,
648 ));
649 }
650 } else if resp.status_code == 404 {
651 } else if resp.status_code == 503 {
652 for header in HeaderSearch::new(&resp.headers, b"Retry-After", false) {
653 if let Ok(seconds) = header.get_value().to_int::<u32>() {
654 return Err(DeviceConfigurationStatus::Retry(
655 RetryReason::ServiceUnavailable,
656 seconds,
657 ));
658 }
659 }
660 } else if resp.status_code == 511 {
661 let path = format!("{}/{}", context.get_fs_root_dir(), imsi);
662 if let Some(provisioning_doc) = load_provisionging_doc(&path) {
663 let identifier = server.identifier();
664 let provisioning_doc = provisioning_doc.unlink_token(
665 match server {
666 ConfigurationServer::Default(_, _)
667 | ConfigurationServer::PreConfiguredDefault(_, _, _) => {
668 true
669 }
670 ConfigurationServer::Additional(_, _) => false,
671 },
672 &identifier,
673 );
674
675 if let Ok(_) = store_provisionging_doc(&provisioning_doc, &path) {
676 platform_log(
677 LOG_TAG,
678 format!("provisioning-doc written to {}", &path),
679 );
680 return start_auto_config_for_server(
681 server,
682 access_control_info,
683 imei,
684 imsi,
685 msisdn,
686 context,
687 gba_context,
688 progress_callback,
689 )
690 .await;
691 }
692
693 return Err(DeviceConfigurationStatus::Retry(RetryReason::IO, 0));
694 }
695 }
696 }
697
698 Err(e) => {
699 platform_log(
700 LOG_TAG,
701 format!(
702 "try_https_configuration_request_inner send failed with error {:?}",
703 e
704 ),
705 );
706 }
707 }
708 }
709
710 Err(e) => {
711 platform_log(
712 LOG_TAG,
713 format!(
714 "try_https_configuration_request_inner connect failed with error {:?}",
715 e
716 ),
717 );
718 }
719 }
720 }
721
722 Err(DeviceConfigurationStatus::Retry(RetryReason::NetworkIO, 0))
723}
724
725async fn try_initial_http_request(
726 server: ConfigurationServer,
727 access_control_info: Option<AccessControlInfo<'_>>,
728 imei: &str,
729 imsi: &str,
730 msisdn: Option<&str>,
731 vers_version: i64,
732 token: Option<(&str, DateTime<FixedOffset>)>,
733 context: Arc<Context>,
734 gba_context: &GbaContext,
735 progress_callback: Arc<Box<dyn Fn(i32) + Send + Sync + '_>>,
736) -> Result<Option<String>, DeviceConfigurationStatus> {
737 let http_uri = server.http_uri();
738
739 if let Ok(url) = Url::parse(&http_uri) {
740 if let Ok(connection) = context.get_http_client().connect(&url, false).await {
741 let req = Request::new_with_default_headers(
742 GET,
743 url.host_str().unwrap(),
744 url.path(),
745 url.query(),
746 );
747
748 if let Ok((resp, _)) = connection.send(req, |_| {}).await {
749 platform_log(
750 LOG_TAG,
751 format!(
752 "try_initial_http_request resp.status_code = {}",
753 resp.status_code
754 ),
755 );
756
757 if resp.status_code == 200 {
758 let mut cookie_jar = CookieJar::new();
759 for header in HeaderSearch::new(&resp.headers, b"Set-Cookie", false) {
760 if let Ok(cookie_string) = str::from_utf8(header.get_value()) {
761 if let Ok(cookie) = cookie_string.parse::<Cookie>() {
762 cookie_jar.add(cookie);
763 }
764 }
765 }
766 let is_over_cellular = cookie_jar.iter().count() > 0;
767 if let Some((token_string, token_validity_through)) = token {
768 if token_validity_through > Utc::now() {
769 return try_https_configuration_request(
770 server,
771 access_control_info,
772 imei,
773 imsi,
774 msisdn,
775 vers_version,
776 Some(token_string),
777 Some(&cookie_jar),
778 None,
779 is_over_cellular,
780 context,
781 gba_context,
782 progress_callback,
783 )
784 .await;
785 } else {
786 return try_https_configuration_request(
787 server,
788 access_control_info,
789 imei,
790 imsi,
791 msisdn,
792 vers_version,
793 None,
794 Some(&cookie_jar),
795 None,
796 is_over_cellular,
797 context,
798 gba_context,
799 progress_callback,
800 )
801 .await;
802 }
803 } else {
804 return try_https_configuration_request(
805 server,
806 access_control_info,
807 imei,
808 imsi,
809 msisdn,
810 vers_version,
811 None,
812 Some(&cookie_jar),
813 None,
814 is_over_cellular,
815 context,
816 gba_context,
817 progress_callback,
818 )
819 .await;
820 }
821 } else if resp.status_code == 403 {
822 return Err(DeviceConfigurationStatus::Forbidden);
823 } else if resp.status_code == 404 {
824 } else if resp.status_code == 408 || resp.status_code == 511 {
825 if let Some((token_string, token_validity_through)) = token {
826 if token_validity_through > Utc::now() {
827 return try_https_configuration_request(
828 server,
829 access_control_info,
830 imei,
831 imsi,
832 msisdn,
833 vers_version,
834 Some(token_string),
835 None,
836 None,
837 false,
838 context,
839 gba_context,
840 progress_callback,
841 )
842 .await;
843 } else {
844 return try_https_configuration_request(
845 server,
846 access_control_info,
847 imei,
848 imsi,
849 msisdn,
850 vers_version,
851 None,
852 None,
853 None,
854 false,
855 context,
856 gba_context,
857 progress_callback,
858 )
859 .await;
860 }
861 } else {
862 return try_https_configuration_request(
863 server,
864 access_control_info,
865 imei,
866 imsi,
867 msisdn,
868 vers_version,
869 None,
870 None,
871 None,
872 false,
873 context,
874 gba_context,
875 progress_callback,
876 )
877 .await;
878 }
879 }
880 } else {
881 platform_log(LOG_TAG, format!("try_initial_http_request failed"));
882 }
883 }
884 }
885
886 Err(DeviceConfigurationStatus::Retry(RetryReason::NetworkIO, 0))
887}
888
889async fn handle_config_for_server(
890 server: ConfigurationServer,
891 access_control_info: Option<AccessControlInfo<'_>>,
892 imei: &str,
893 imsi: &str,
894 msisdn: Option<&str>,
895 doc: &WapProvisioningDoc,
896 context: Arc<Context>,
897 gba_context: &GbaContext,
898 progress_callback: Arc<Box<dyn Fn(i32) + Send + Sync + '_>>,
899) -> Result<Option<String>, DeviceConfigurationStatus> {
900 let mut provided_msisdn: Option<String> = None;
901 match server {
902 ConfigurationServer::Default(_, _) | ConfigurationServer::PreConfiguredDefault(_, _, _) => {
903 platform_log(LOG_TAG, "finding access-control node for default server");
904 if let Some(iter) = doc.access_control() {
905 for access_control_info in iter {
906 if access_control_info.is_id_provider {
907 if access_control_info.app_ids().any(|app_id| {
908 app_id == "ap2001"
909 || app_id == "ap2002"
910 || app_id == "urn%3Aoma%3Amo%3Aext-3gpp-ims%3A1.0"
911 }) {
912 platform_log(
913 LOG_TAG,
914 format!("found additional server {}", access_control_info.fqdn),
915 );
916 let additional_server = ConfigurationServer::Additional(
917 String::from(access_control_info.fqdn),
918 true,
919 );
920 match start_auto_config_for_server(
921 additional_server,
922 Some(access_control_info),
923 imei,
924 imsi,
925 msisdn,
926 Arc::clone(&context),
927 gba_context,
928 Arc::clone(&progress_callback),
929 )
930 .await
931 {
932 Ok(possible_user_msisdn) => {
933 if let Some(user_msisdn) = possible_user_msisdn {
934 provided_msisdn.replace(user_msisdn);
935 }
936 }
937 Err(e) => {
938 return Err(e);
939 }
940 }
941 }
942 }
943 }
944 }
945
946 if let Some(iter) = doc.access_control() {
947 for access_control_info in iter {
948 if !access_control_info.is_id_provider {
949 if access_control_info.app_ids().any(|app_id| {
950 app_id == "ap2001"
951 || app_id == "ap2002"
952 || app_id == "urn%3Aoma%3Amo%3Aext-3gpp-ims%3A1.0"
953 }) {
954 platform_log(
955 LOG_TAG,
956 format!("found additional server {}", access_control_info.fqdn),
957 );
958 let additional_server = ConfigurationServer::Additional(
959 String::from(access_control_info.fqdn),
960 false,
961 );
962 if let Some(ref provided_msisdn) = provided_msisdn {
963 start_auto_config_for_server(
964 additional_server,
965 Some(access_control_info),
966 imei,
967 imsi,
968 Some(provided_msisdn),
969 Arc::clone(&context),
970 gba_context,
971 Arc::clone(&progress_callback),
972 )
973 .await?;
974 } else {
975 start_auto_config_for_server(
976 additional_server,
977 Some(access_control_info),
978 imei,
979 imsi,
980 msisdn,
981 Arc::clone(&context),
982 gba_context,
983 Arc::clone(&progress_callback),
984 )
985 .await?;
986 }
987 }
988 }
989 }
990 }
991 }
992
993 ConfigurationServer::Additional(_, is_id_provider) => {
994 if is_id_provider {
995 if let Some(user_msisdn) = doc.get_user_msisdn() {
996 provided_msisdn.replace(String::from(user_msisdn));
997 }
998 }
999 }
1000 }
1001
1002 let default: &str = match server {
1003 ConfigurationServer::Default(_, _) | ConfigurationServer::PreConfiguredDefault(_, _, _) => {
1004 "yes"
1005 }
1006 ConfigurationServer::Additional(_, _) => "no",
1007 };
1008
1009 let identifier = server.identifier();
1010
1011 platform_log(
1012 LOG_TAG,
1013 format!(
1014 "handle wap provisioning doc for server {} --is-default?:{}",
1015 &identifier, default
1016 ),
1017 );
1018
1019 if let Some((vers, version_validity, token)) = doc.get_vers_token() {
1020 let vers_validity_through;
1021 if vers > 0 {
1022 vers_validity_through = (Utc::now() + Duration::seconds(version_validity)).to_rfc3339();
1023 } else {
1024 vers_validity_through = (Utc::now() + Duration::days(365)).to_rfc3339();
1025 }
1026
1027 if vers > 0 {
1028 let path = format!("{}/{}", context.fs_root_dir, imsi);
1029 if let Some(mut provisioning_doc) = load_provisionging_doc(&path) {
1030 platform_log(LOG_TAG, "update existing provisioning-doc");
1031
1032 provisioning_doc.update_vers_token(
1033 default == "yes",
1034 &identifier,
1035 vers,
1036 vers_validity_through,
1037 token,
1038 );
1039
1040 for application in doc.applications() {
1041 platform_log(LOG_TAG, "processing application in wap provisioning doc");
1042 if let Some(app_id) = application.get_parameter("AppID") {
1043 platform_log(LOG_TAG, format!("trying to insert application {}", app_id));
1044 if let Some(ref access_control_info) = access_control_info {
1045 if !access_control_info
1046 .app_ids()
1047 .any(|app_id_| app_id_ == app_id)
1048 {
1049 platform_log(LOG_TAG, "forbidden by access_control");
1050 continue;
1051 }
1052 }
1053
1054 provisioning_doc = provisioning_doc.update_application(
1055 default == "yes",
1056 &identifier,
1057 app_id,
1058 &application,
1059 );
1060 }
1061 }
1062
1063 if default == "yes" {
1064 provisioning_doc.remove_non_application_characteristics_for_default_server();
1065 for characteristic in doc.children() {
1066 if characteristic.characteristic_type != "APPLICATION" {
1067 provisioning_doc
1068 .update_characteristics_for_default_server(characteristic);
1069 }
1070 }
1071 }
1072
1073 if let Ok(_) = store_provisionging_doc(&provisioning_doc, &path) {
1074 platform_log(LOG_TAG, format!("provisioning-doc written to {}", &path));
1075 return Ok(provided_msisdn);
1076 }
1077
1078 return Err(DeviceConfigurationStatus::Retry(RetryReason::IO, 0));
1079 } else {
1080 platform_log(LOG_TAG, "creating new provisioning-doc");
1081
1082 let mut v = Vec::new();
1083
1084 for characteristic in doc.children() {
1085 if characteristic.characteristic_type == "APPLICATION" {
1086 platform_log(LOG_TAG, "processing applications in wap provisioning doc");
1087 if let Some(app_id) = characteristic.get_parameter("AppID") {
1088 platform_log(
1089 LOG_TAG,
1090 format!("trying to insert application {}", app_id),
1091 );
1092 if let Some(ref access_control_info) = access_control_info {
1093 if !access_control_info
1094 .app_ids()
1095 .any(|app_id_| app_id_ == app_id)
1096 {
1097 platform_log(LOG_TAG, "forbidden by access_control");
1098 continue;
1099 }
1100 }
1101
1102 v.push(characteristic.clone());
1103 }
1104 } else {
1105 if default == "yes" {
1106 v.push(characteristic.clone())
1107 }
1108 }
1109 }
1110
1111 let wap_provisioning_doc = WapProvisioningDoc::new(v);
1112
1113 let mut provisioning_info = ProvisiongingInfo {
1114 default: String::from(default),
1115 id: identifier,
1116 VERS_version: format!("{}", vers),
1117 VERS_validity_through: vers_validity_through,
1118 TOKEN_token: None,
1119 TOKEN_validity_through: None,
1120 wap_doc: Some(wap_provisioning_doc),
1121 };
1122
1123 if let Some((token_string, token_validity)) = token {
1124 provisioning_info
1125 .TOKEN_token
1126 .replace(String::from(token_string));
1127
1128 if let Some(token_validity) = token_validity {
1129 if token_validity > 0 {
1130 let token_validity_through =
1131 (Utc::now() + Duration::seconds(token_validity)).to_rfc3339();
1132 provisioning_info
1133 .TOKEN_validity_through
1134 .replace(token_validity_through);
1135 } else {
1136 let token_validity_through =
1137 (Utc::now() + Duration::days(365)).to_rfc3339();
1138 provisioning_info
1139 .TOKEN_validity_through
1140 .replace(token_validity_through);
1141 }
1142 } else {
1143 let token_validity_through =
1144 (Utc::now() + Duration::days(365)).to_rfc3339();
1145 provisioning_info
1146 .TOKEN_validity_through
1147 .replace(token_validity_through);
1148 }
1149 }
1150
1151 let mut v = Vec::new();
1152
1153 v.push(provisioning_info);
1154
1155 let provisioning_doc = LocalProvisioningDoc::new(v);
1156
1157 if let Ok(_) = store_provisionging_doc(&provisioning_doc, &path) {
1158 platform_log(LOG_TAG, format!("provisioning-doc written to {}", &path));
1159 return Ok(provided_msisdn);
1160 }
1161
1162 return Err(DeviceConfigurationStatus::Retry(RetryReason::IO, 0));
1163 }
1164 }
1165
1166 if vers <= 0 {
1167 let path = format!("{}/{}", context.fs_root_dir, imsi);
1168 let mut provisioning_doc = load_provisionging_doc(&path);
1169 if let Some(mut doc) = provisioning_doc.take() {
1170 doc = doc.unlink(default == "yes", &identifier);
1171 provisioning_doc.replace(doc);
1172 }
1173
1174 if vers < 0 {
1175 if provisioning_doc.is_none() {
1176 let provisioning_info = ProvisiongingInfo {
1177 default: String::from(default),
1178 id: identifier,
1179 VERS_version: format!("{}", vers),
1180 VERS_validity_through: String::from(vers_validity_through),
1181 TOKEN_token: None,
1182 TOKEN_validity_through: None,
1183 wap_doc: None,
1184 };
1185
1186 let mut v = Vec::new();
1187
1188 v.push(provisioning_info);
1189
1190 provisioning_doc.replace(LocalProvisioningDoc::new(v));
1191 }
1192 }
1193
1194 if let Some(provisioning_doc) = provisioning_doc {
1195 if let Ok(_) = store_provisionging_doc(&provisioning_doc, &path) {
1196 platform_log(LOG_TAG, format!("provisioning-doc written to {}", &path));
1197 return Ok(provided_msisdn);
1198 }
1199
1200 return Err(DeviceConfigurationStatus::Retry(RetryReason::IO, 0));
1201 }
1202 }
1203 }
1204
1205 Ok(provided_msisdn)
1206}
1207
1208fn start_auto_config_for_server<'a, 'b: 'a>(
1209 server: ConfigurationServer,
1210 access_control_info: Option<AccessControlInfo<'a>>,
1211 imei: &'b str,
1212 imsi: &'b str,
1213 msisdn: Option<&'b str>,
1214 context: Arc<Context>,
1215 gba_context: &'a GbaContext,
1216 progress_callback: Arc<Box<dyn Fn(i32) + Send + Sync + 'b>>,
1217) -> BoxFuture<'a, Result<Option<String>, DeviceConfigurationStatus>> {
1218 async move {
1219 start_auto_config_for_server_inner(
1220 server,
1221 access_control_info,
1222 imei,
1223 imsi,
1224 msisdn,
1225 context,
1226 gba_context,
1227 progress_callback,
1228 )
1229 .await
1230 }
1231 .boxed()
1232}
1233
1234async fn start_auto_config_for_server_inner(
1235 server: ConfigurationServer,
1236 access_control_info: Option<AccessControlInfo<'_>>,
1237 imei: &str,
1238 imsi: &str,
1239 msisdn: Option<&str>,
1240 context: Arc<Context>,
1241 gba_context: &GbaContext,
1242 progress_callback: Arc<Box<dyn Fn(i32) + Send + Sync + '_>>,
1243) -> Result<Option<String>, DeviceConfigurationStatus> {
1244 let path = format!("{}/{}", context.fs_root_dir, imsi);
1245 platform_log(LOG_TAG, format!("loading provisioning doc from {}", &path));
1246 if let Some(provisioning_doc) = load_provisionging_doc(&path) {
1247 platform_log(LOG_TAG, "local provisioning doc loaded");
1248
1249 if let Some((vers_version, vers_validity_through, token, doc)) = match &server {
1250 ConfigurationServer::Default(_, _)
1251 | ConfigurationServer::PreConfiguredDefault(_, _, _) => {
1252 let identifier = server.identifier();
1253 platform_log(
1254 LOG_TAG,
1255 format!("get provisioning info with default server {}", &identifier),
1256 );
1257 provisioning_doc.get_provisioning_info(true, &identifier)
1258 }
1259 ConfigurationServer::Additional(fqdn, _) => {
1260 platform_log(LOG_TAG, format!("get provisioning info with fqdn {}", fqdn));
1261 provisioning_doc.get_provisioning_info(false, fqdn)
1262 }
1263 } {
1264 platform_log(
1265 LOG_TAG,
1266 format!(
1267 "VERS_version {:?} VERS_validity_through {:?}",
1268 vers_version, vers_validity_through
1269 ),
1270 );
1271
1272 if vers_version < 0 {
1273 return Err(DeviceConfigurationStatus::Forbidden);
1274 }
1275
1276 if vers_validity_through > Utc::now() {
1277 if let Some(doc) = doc {
1278 return handle_config_for_server(
1279 server,
1280 access_control_info,
1281 imei,
1282 imsi,
1283 msisdn,
1284 doc,
1285 context,
1286 gba_context,
1287 progress_callback,
1288 )
1289 .await;
1290 }
1291 }
1292
1293 return try_initial_http_request(
1294 server,
1295 access_control_info,
1296 imei,
1297 imsi,
1298 msisdn,
1299 0,
1300 token,
1301 context,
1302 gba_context,
1303 progress_callback,
1304 )
1305 .await;
1306 }
1307 }
1308
1309 try_initial_http_request(
1310 server,
1311 access_control_info,
1312 imei,
1313 imsi,
1314 msisdn,
1315 0,
1316 None,
1317 context,
1318 gba_context,
1319 progress_callback,
1320 )
1321 .await
1322}
1323
1324pub async fn start_auto_config<F>(
1325 subscription_id: i32,
1326 mcc: CountryCode,
1327 mnc: NetworkCode,
1328 imei: &str,
1329 imsi: &str,
1330 msisdn: Option<&str>,
1331 context: Arc<Context>,
1332 progress_callback: F,
1333) -> Result<(Option<String>, Characteristic, Characteristic), DeviceConfigurationStatus>
1334where
1335 F: Fn(i32) + Send + Sync + 'static,
1336{
1337 if mcc.is_valid_three_digits() && mnc.is_valid_three_digits() {
1338 let server = ConfigurationServer::Default(mcc, mnc);
1339 let gba_context = context.make_gba_context(imsi, mcc, mnc, subscription_id);
1340 let provided_msisdn = start_auto_config_for_server(
1341 server,
1342 None,
1343 imei,
1344 imsi,
1345 msisdn,
1346 Arc::clone(&context),
1347 &gba_context,
1348 Arc::new(Box::new(progress_callback)),
1349 )
1350 .await?;
1351 platform_log(
1352 LOG_TAG,
1353 format!("complete with provided_msisdn {:?}", provided_msisdn),
1354 );
1355 let path = format!("{}/{}", &context.fs_root_dir, imsi);
1356 if let Some(provisioning_doc) = load_provisionging_doc(&path) {
1357 for doc in provisioning_doc.provisioning_files() {
1358 if let Some(rcs_application) = doc.get_rcs_application() {
1359 let to_app_ref = rcs_application.get_to_app_ref();
1360 for doc in provisioning_doc.provisioning_files() {
1361 if let Some(ims_application) = doc.get_ims_application(to_app_ref) {
1362 return Ok((
1363 provided_msisdn,
1364 rcs_application.clone_characteristic(),
1365 ims_application.clone_characteristic(),
1366 ));
1367 }
1368 }
1369 }
1370 }
1371 }
1372 return Err(DeviceConfigurationStatus::Forbidden);
1373 } else {
1374 Err(DeviceConfigurationStatus::InvalidArgument)
1375 }
1376}