rust_rcs_client/provisioning/
device_configuration.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
15extern 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    // RequireOtp(u16, String, String), // sms port, https host, cookie b
66    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::RequireOtp(port, https_host, cookie_b) => {
79            //     write!(f, "RequireOtp on port {}, pending https session with host {}, cookie {}", port, https_host, cookie_b)
80            // }
81            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}