1 // Copyright (C) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 use std::collections::{HashMap, HashSet}; 15 use std::fs::File; 16 use std::os::fd::{FromRawFd, IntoRawFd, RawFd}; 17 18 pub use ffi::{Action, Mode}; 19 use ipc::IpcStatusCode; 20 21 cfg_oh! { 22 use ipc::parcel::Serialize; 23 use ipc::parcel::Deserialize; 24 } 25 26 use super::reason::Reason; 27 use super::ATOMIC_SERVICE; 28 use crate::manage::account::GetOhosAccountUid; 29 use crate::manage::network::{NetworkState, NetworkType}; 30 use crate::utils::c_wrapper::{CFileSpec, CFormItem, CStringWrapper}; 31 use crate::utils::form_item::{FileSpec, FormItem}; 32 use crate::utils::{hashmap_to_string, query_calling_bundle}; 33 34 #[cxx::bridge(namespace = "OHOS::Request")] 35 mod ffi { 36 /// Action 37 #[derive(Clone, Copy, PartialEq, Debug)] 38 #[repr(u8)] 39 pub enum Action { 40 /// Download 41 Download = 0, 42 /// Upload 43 Upload, 44 /// Any 45 Any, 46 } 47 48 /// Mode 49 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] 50 #[repr(u8)] 51 pub enum Mode { 52 /// BackGround 53 BackGround = 0, 54 /// ForeGround 55 FrontEnd, 56 /// Any 57 Any, 58 } 59 } 60 61 #[derive(Clone, Copy, PartialEq, Debug)] 62 #[repr(u8)] 63 pub(crate) enum Version { 64 API9 = 1, 65 API10, 66 } 67 68 /// NetworkConfig 69 #[derive(Clone, Copy, PartialEq, Debug)] 70 #[repr(u8)] 71 pub enum NetworkConfig { 72 /// Any 73 Any = 0, 74 /// Wifi 75 Wifi, 76 /// Cellular 77 Cellular, 78 } 79 80 #[repr(C)] 81 #[derive(Copy, Clone, Debug)] 82 pub(crate) struct CommonTaskConfig { 83 pub(crate) task_id: u32, 84 pub(crate) uid: u64, 85 pub(crate) token_id: u64, 86 pub(crate) action: Action, 87 pub(crate) mode: Mode, 88 pub(crate) cover: bool, 89 pub(crate) network_config: NetworkConfig, 90 pub(crate) metered: bool, 91 pub(crate) roaming: bool, 92 pub(crate) retry: bool, 93 pub(crate) redirect: bool, 94 pub(crate) index: u32, 95 pub(crate) begins: u64, 96 pub(crate) ends: i64, 97 pub(crate) gauge: bool, 98 pub(crate) precise: bool, 99 pub(crate) priority: u32, 100 pub(crate) background: bool, 101 pub(crate) multipart: bool, 102 } 103 104 /// task config 105 #[derive(Clone, Debug)] 106 pub struct TaskConfig { 107 pub(crate) bundle: String, 108 pub(crate) bundle_type: u32, 109 pub(crate) atomic_account: String, 110 pub(crate) url: String, 111 pub(crate) title: String, 112 pub(crate) description: String, 113 pub(crate) method: String, 114 pub(crate) headers: HashMap<String, String>, 115 pub(crate) data: String, 116 pub(crate) token: String, 117 pub(crate) proxy: String, 118 pub(crate) certificate_pins: String, 119 pub(crate) extras: HashMap<String, String>, 120 pub(crate) version: Version, 121 pub(crate) form_items: Vec<FormItem>, 122 pub(crate) file_specs: Vec<FileSpec>, 123 pub(crate) body_file_paths: Vec<String>, 124 pub(crate) certs_path: Vec<String>, 125 pub(crate) common_data: CommonTaskConfig, 126 } 127 128 impl TaskConfig { satisfy_network(&self, network: &NetworkState) -> Result<(), Reason>129 pub(crate) fn satisfy_network(&self, network: &NetworkState) -> Result<(), Reason> { 130 // NetworkConfig::Cellular with NetworkType::Wifi is allowed 131 match network { 132 NetworkState::Offline => Err(Reason::NetworkOffline), 133 NetworkState::Online(info) => match self.common_data.network_config { 134 NetworkConfig::Any => Ok(()), 135 NetworkConfig::Wifi if info.network_type == NetworkType::Cellular => { 136 Err(Reason::UnsupportedNetworkType) 137 } 138 _ => { 139 if (self.common_data.roaming || !info.is_roaming) 140 && (self.common_data.metered || !info.is_metered) 141 { 142 Ok(()) 143 } else { 144 Err(Reason::UnsupportedNetworkType) 145 } 146 } 147 }, 148 } 149 } 150 satisfy_foreground(&self, foreground_abilities: &HashSet<u64>) -> bool151 pub(crate) fn satisfy_foreground(&self, foreground_abilities: &HashSet<u64>) -> bool { 152 self.common_data.mode == Mode::BackGround 153 || foreground_abilities.contains(&self.common_data.uid) 154 } 155 } 156 157 pub(crate) struct ConfigSet { 158 pub(crate) headers: String, 159 pub(crate) extras: String, 160 pub(crate) form_items: Vec<CFormItem>, 161 pub(crate) file_specs: Vec<CFileSpec>, 162 pub(crate) body_file_names: Vec<CStringWrapper>, 163 pub(crate) certs_path: Vec<CStringWrapper>, 164 } 165 166 impl PartialOrd for Mode { partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>167 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { 168 Some(self.cmp(other)) 169 } 170 } 171 172 impl Ord for Mode { cmp(&self, other: &Self) -> std::cmp::Ordering173 fn cmp(&self, other: &Self) -> std::cmp::Ordering { 174 self.to_usize().cmp(&other.to_usize()) 175 } 176 } 177 178 impl Mode { to_usize(self) -> usize179 fn to_usize(self) -> usize { 180 match self { 181 Mode::FrontEnd => 0, 182 Mode::Any => 1, 183 Mode::BackGround => 2, 184 _ => unreachable!(), 185 } 186 } 187 } 188 189 impl From<u8> for Mode { from(value: u8) -> Self190 fn from(value: u8) -> Self { 191 match value { 192 0 => Mode::BackGround, 193 1 => Mode::FrontEnd, 194 _ => Mode::Any, 195 } 196 } 197 } 198 199 impl From<u8> for Action { from(value: u8) -> Self200 fn from(value: u8) -> Self { 201 match value { 202 0 => Action::Download, 203 1 => Action::Upload, 204 _ => Action::Any, 205 } 206 } 207 } 208 209 impl From<u8> for Version { from(value: u8) -> Self210 fn from(value: u8) -> Self { 211 match value { 212 2 => Version::API10, 213 _ => Version::API9, 214 } 215 } 216 } 217 218 impl From<u8> for NetworkConfig { from(value: u8) -> Self219 fn from(value: u8) -> Self { 220 match value { 221 0 => NetworkConfig::Any, 222 2 => NetworkConfig::Cellular, 223 _ => NetworkConfig::Wifi, 224 } 225 } 226 } 227 228 impl TaskConfig { build_config_set(&self) -> ConfigSet229 pub(crate) fn build_config_set(&self) -> ConfigSet { 230 ConfigSet { 231 headers: hashmap_to_string(&self.headers), 232 extras: hashmap_to_string(&self.extras), 233 form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(), 234 file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(), 235 body_file_names: self 236 .body_file_paths 237 .iter() 238 .map(CStringWrapper::from) 239 .collect(), 240 certs_path: self.certs_path.iter().map(CStringWrapper::from).collect(), 241 } 242 } 243 contains_user_file(&self) -> bool244 pub(crate) fn contains_user_file(&self) -> bool { 245 for specs in self.file_specs.iter() { 246 if specs.is_user_file { 247 return true; 248 } 249 } 250 false 251 } 252 } 253 254 impl Default for TaskConfig { default() -> Self255 fn default() -> Self { 256 Self { 257 bundle_type: 0, 258 atomic_account: "ohosAnonymousUid".to_string(), 259 bundle: "xxx".to_string(), 260 url: "".to_string(), 261 title: "xxx".to_string(), 262 description: "xxx".to_string(), 263 method: "GET".to_string(), 264 headers: Default::default(), 265 data: "".to_string(), 266 token: "xxx".to_string(), 267 proxy: "".to_string(), 268 extras: Default::default(), 269 version: Version::API10, 270 form_items: vec![], 271 file_specs: vec![], 272 body_file_paths: vec![], 273 certs_path: vec![], 274 certificate_pins: "".to_string(), 275 common_data: CommonTaskConfig { 276 task_id: 0, 277 uid: 0, 278 token_id: 0, 279 action: Action::Download, 280 mode: Mode::BackGround, 281 cover: false, 282 network_config: NetworkConfig::Any, 283 metered: false, 284 roaming: false, 285 retry: false, 286 redirect: true, 287 index: 0, 288 begins: 0, 289 ends: -1, 290 gauge: false, 291 precise: false, 292 priority: 0, 293 background: false, 294 multipart: false, 295 }, 296 } 297 } 298 } 299 300 /// ConfigBuilder 301 pub struct ConfigBuilder { 302 inner: TaskConfig, 303 } 304 305 impl ConfigBuilder { 306 /// Create a new ConfigBuilder new() -> Self307 pub fn new() -> Self { 308 Self { 309 inner: TaskConfig::default(), 310 } 311 } 312 /// Set url url(&mut self, url: &str) -> &mut Self313 pub fn url(&mut self, url: &str) -> &mut Self { 314 self.inner.url = url.to_string(); 315 self 316 } 317 318 /// set version version(&mut self, version: u8) -> &mut Self319 pub fn version(&mut self, version: u8) -> &mut Self { 320 self.inner.version = version.into(); 321 self 322 } 323 324 /// Set title file_spec(&mut self, file: File) -> &mut Self325 pub fn file_spec(&mut self, file: File) -> &mut Self { 326 self.inner.file_specs.push(FileSpec::user_file(file)); 327 self 328 } 329 /// Set action action(&mut self, action: Action) -> &mut Self330 pub fn action(&mut self, action: Action) -> &mut Self { 331 self.inner.common_data.action = action; 332 self 333 } 334 335 /// Set mode mode(&mut self, mode: Mode) -> &mut Self336 pub fn mode(&mut self, mode: Mode) -> &mut Self { 337 self.inner.common_data.mode = mode; 338 self 339 } 340 341 /// Set bundle name bundle_name(&mut self, bundle_name: &str) -> &mut Self342 pub fn bundle_name(&mut self, bundle_name: &str) -> &mut Self { 343 self.inner.bundle = bundle_name.to_string(); 344 self 345 } 346 347 /// Set uid uid(&mut self, uid: u64) -> &mut Self348 pub fn uid(&mut self, uid: u64) -> &mut Self { 349 self.inner.common_data.uid = uid; 350 self 351 } 352 353 /// set network network(&mut self, network: NetworkConfig) -> &mut Self354 pub fn network(&mut self, network: NetworkConfig) -> &mut Self { 355 self.inner.common_data.network_config = network; 356 self 357 } 358 359 /// Set metered roaming(&mut self, roaming: bool) -> &mut Self360 pub fn roaming(&mut self, roaming: bool) -> &mut Self { 361 self.inner.common_data.roaming = roaming; 362 self 363 } 364 365 /// set metered metered(&mut self, metered: bool) -> &mut Self366 pub fn metered(&mut self, metered: bool) -> &mut Self { 367 self.inner.common_data.metered = metered; 368 self 369 } 370 371 /// build build(&mut self) -> TaskConfig372 pub fn build(&mut self) -> TaskConfig { 373 self.inner.clone() 374 } 375 376 /// redirect redirect(&mut self, redirect: bool) -> &mut Self377 pub fn redirect(&mut self, redirect: bool) -> &mut Self { 378 self.inner.common_data.redirect = redirect; 379 self 380 } 381 382 /// begins begins(&mut self, begins: u64) -> &mut Self383 pub fn begins(&mut self, begins: u64) -> &mut Self { 384 self.inner.common_data.begins = begins; 385 self 386 } 387 388 /// ends ends(&mut self, ends: u64) -> &mut Self389 pub fn ends(&mut self, ends: u64) -> &mut Self { 390 self.inner.common_data.ends = ends as i64; 391 self 392 } 393 394 /// method method(&mut self, metered: &str) -> &mut Self395 pub fn method(&mut self, metered: &str) -> &mut Self { 396 self.inner.method = metered.to_string(); 397 self 398 } 399 400 /// retry retry(&mut self, retry: bool) -> &mut Self401 pub fn retry(&mut self, retry: bool) -> &mut Self { 402 self.inner.common_data.retry = retry; 403 self 404 } 405 } 406 407 #[cfg(feature = "oh")] 408 impl Serialize for TaskConfig { serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()>409 fn serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()> { 410 parcel.write(&(self.common_data.action.repr as u32))?; 411 parcel.write(&(self.version as u32))?; 412 parcel.write(&(self.common_data.mode.repr as u32))?; 413 parcel.write(&self.bundle_type)?; 414 parcel.write(&self.common_data.cover)?; 415 parcel.write(&(self.common_data.network_config as u32))?; 416 parcel.write(&(self.common_data.metered))?; 417 parcel.write(&self.common_data.roaming)?; 418 parcel.write(&(self.common_data.retry))?; 419 parcel.write(&(self.common_data.redirect))?; 420 parcel.write(&(self.common_data.background))?; 421 parcel.write(&(self.common_data.multipart))?; 422 parcel.write(&self.common_data.index)?; 423 parcel.write(&(self.common_data.begins as i64))?; 424 parcel.write(&self.common_data.ends)?; 425 parcel.write(&self.common_data.gauge)?; 426 parcel.write(&self.common_data.precise)?; 427 parcel.write(&self.common_data.priority)?; 428 parcel.write(&self.url)?; 429 parcel.write(&self.title)?; 430 parcel.write(&self.method)?; 431 parcel.write(&self.token)?; 432 parcel.write(&self.description)?; 433 parcel.write(&self.data)?; 434 parcel.write(&self.proxy)?; 435 parcel.write(&self.certificate_pins)?; 436 437 parcel.write(&(self.certs_path.len() as u32))?; 438 for cert_path in &self.certs_path { 439 parcel.write(cert_path)?; 440 } 441 442 parcel.write(&(self.form_items.len() as u32))?; 443 for form_item in &self.form_items { 444 parcel.write(&form_item.name)?; 445 parcel.write(&form_item.value)?; 446 } 447 parcel.write(&(self.file_specs.len() as u32))?; 448 for file_spec in &self.file_specs { 449 parcel.write(&file_spec.name)?; 450 parcel.write(&file_spec.path)?; 451 parcel.write(&file_spec.file_name)?; 452 parcel.write(&file_spec.mime_type)?; 453 parcel.write(&file_spec.is_user_file)?; 454 if file_spec.is_user_file { 455 let file = unsafe { File::from_raw_fd(file_spec.fd.unwrap()) }; 456 parcel.write_file(file)?; 457 } 458 } 459 460 parcel.write(&(self.body_file_paths.len() as u32))?; 461 for body_file_paths in self.body_file_paths.iter() { 462 parcel.write(body_file_paths)?; 463 } 464 parcel.write(&(self.headers.len() as u32))?; 465 for header in self.headers.iter() { 466 parcel.write(header.0)?; 467 parcel.write(header.1)?; 468 } 469 470 parcel.write(&(self.extras.len() as u32))?; 471 for extra in self.extras.iter() { 472 parcel.write(extra.0)?; 473 parcel.write(extra.1)?; 474 } 475 476 Ok(()) 477 } 478 } 479 480 #[cfg(feature = "oh")] 481 impl Deserialize for TaskConfig { deserialize(parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<Self>482 fn deserialize(parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<Self> { 483 let action: u32 = parcel.read()?; 484 let action: Action = Action::from(action as u8); 485 let version: u32 = parcel.read()?; 486 let version: Version = Version::from(version as u8); 487 let mode: u32 = parcel.read()?; 488 let mode: Mode = Mode::from(mode as u8); 489 let bundle_type: u32 = parcel.read()?; 490 let cover: bool = parcel.read()?; 491 let network: u32 = parcel.read()?; 492 let network_config = NetworkConfig::from(network as u8); 493 let metered: bool = parcel.read()?; 494 let roaming: bool = parcel.read()?; 495 let retry: bool = parcel.read()?; 496 let redirect: bool = parcel.read()?; 497 let background: bool = parcel.read()?; 498 let multipart: bool = parcel.read()?; 499 let index: u32 = parcel.read()?; 500 let begins: i64 = parcel.read()?; 501 let ends: i64 = parcel.read()?; 502 let gauge: bool = parcel.read()?; 503 let precise: bool = parcel.read()?; 504 let priority: u32 = parcel.read()?; 505 let url: String = parcel.read()?; 506 let title: String = parcel.read()?; 507 let method: String = parcel.read()?; 508 let token: String = parcel.read()?; 509 let description: String = parcel.read()?; 510 let data_base: String = parcel.read()?; 511 let proxy: String = parcel.read()?; 512 let certificate_pins: String = parcel.read()?; 513 let bundle = query_calling_bundle(); 514 let uid = ipc::Skeleton::calling_uid(); 515 let token_id = ipc::Skeleton::calling_full_token_id(); 516 let certs_path_size: u32 = parcel.read()?; 517 if certs_path_size > parcel.readable() as u32 { 518 error!("deserialize failed: certs_path_size too large"); 519 sys_event!( 520 ExecFault, 521 DfxCode::INVALID_IPC_MESSAGE_A00, 522 "deserialize failed: certs_path_size too large" 523 ); 524 return Err(IpcStatusCode::Failed); 525 } 526 let mut certs_path = Vec::new(); 527 for _ in 0..certs_path_size { 528 let cert_path: String = parcel.read()?; 529 certs_path.push(cert_path); 530 } 531 532 let form_size: u32 = parcel.read()?; 533 if form_size > parcel.readable() as u32 { 534 error!("deserialize failed: form_size too large"); 535 sys_event!( 536 ExecFault, 537 DfxCode::INVALID_IPC_MESSAGE_A00, 538 "deserialize failed: form_size too large" 539 ); 540 return Err(IpcStatusCode::Failed); 541 } 542 let mut form_items = Vec::new(); 543 for _ in 0..form_size { 544 let name: String = parcel.read()?; 545 let value: String = parcel.read()?; 546 form_items.push(FormItem { name, value }); 547 } 548 549 let file_size: u32 = parcel.read()?; 550 if file_size > parcel.readable() as u32 { 551 error!("deserialize failed: file_specs size too large"); 552 sys_event!( 553 ExecFault, 554 DfxCode::INVALID_IPC_MESSAGE_A00, 555 "deserialize failed: file_specs size too large" 556 ); 557 return Err(IpcStatusCode::Failed); 558 } 559 let mut file_specs: Vec<FileSpec> = Vec::new(); 560 for _ in 0..file_size { 561 let name: String = parcel.read()?; 562 let path: String = parcel.read()?; 563 let file_name: String = parcel.read()?; 564 let mime_type: String = parcel.read()?; 565 let is_user_file: bool = parcel.read()?; 566 let mut fd: Option<RawFd> = None; 567 if is_user_file { 568 let ipc_fd: File = parcel.read_file()?; 569 fd = Some(ipc_fd.into_raw_fd()); 570 } 571 file_specs.push(FileSpec { 572 name, 573 path, 574 file_name, 575 mime_type, 576 is_user_file, 577 fd, 578 }); 579 } 580 581 // Response bodies fd. 582 let body_file_size: u32 = parcel.read()?; 583 if body_file_size > parcel.readable() as u32 { 584 error!("deserialize failed: body_file size too large"); 585 sys_event!( 586 ExecFault, 587 DfxCode::INVALID_IPC_MESSAGE_A00, 588 "deserialize failed: body_file size too large" 589 ); 590 return Err(IpcStatusCode::Failed); 591 } 592 593 let mut body_file_paths: Vec<String> = Vec::new(); 594 for _ in 0..body_file_size { 595 let file_name: String = parcel.read()?; 596 body_file_paths.push(file_name); 597 } 598 599 let header_size: u32 = parcel.read()?; 600 if header_size > parcel.readable() as u32 { 601 error!("deserialize failed: header size too large"); 602 sys_event!( 603 ExecFault, 604 DfxCode::INVALID_IPC_MESSAGE_A00, 605 "deserialize failed: header size too large" 606 ); 607 return Err(IpcStatusCode::Failed); 608 } 609 let mut headers: HashMap<String, String> = HashMap::new(); 610 for _ in 0..header_size { 611 let key: String = parcel.read()?; 612 let value: String = parcel.read()?; 613 headers.insert(key, value); 614 } 615 616 let extras_size: u32 = parcel.read()?; 617 if extras_size > parcel.readable() as u32 { 618 error!("deserialize failed: extras size too large"); 619 sys_event!( 620 ExecFault, 621 DfxCode::INVALID_IPC_MESSAGE_A00, 622 "deserialize failed: extras size too large" 623 ); 624 return Err(IpcStatusCode::Failed); 625 } 626 let mut extras: HashMap<String, String> = HashMap::new(); 627 for _ in 0..extras_size { 628 let key: String = parcel.read()?; 629 let value: String = parcel.read()?; 630 extras.insert(key, value); 631 } 632 633 let atomic_account = if bundle_type == ATOMIC_SERVICE { 634 GetOhosAccountUid() 635 } else { 636 "".to_string() 637 }; 638 639 let task_config = TaskConfig { 640 bundle, 641 bundle_type, 642 atomic_account, 643 url, 644 title, 645 description, 646 method, 647 headers, 648 data: data_base, 649 token, 650 proxy, 651 certificate_pins, 652 extras, 653 version, 654 form_items, 655 file_specs, 656 body_file_paths, 657 certs_path, 658 common_data: CommonTaskConfig { 659 task_id: 0, 660 uid, 661 token_id, 662 action, 663 mode, 664 cover, 665 network_config, 666 metered, 667 roaming, 668 retry, 669 redirect, 670 index, 671 begins: begins as u64, 672 ends, 673 gauge, 674 precise, 675 priority, 676 background, 677 multipart, 678 }, 679 }; 680 Ok(task_config) 681 } 682 } 683 684 #[cfg(test)] 685 mod test { 686 use super::*; 687 #[test] ut_enum_action()688 fn ut_enum_action() { 689 assert_eq!(Action::Download.repr, 0); 690 assert_eq!(Action::Upload.repr, 1); 691 assert_eq!(Action::Any.repr, 2); 692 } 693 694 #[test] ut_enum_mode()695 fn ut_enum_mode() { 696 assert_eq!(Mode::BackGround.repr, 0); 697 assert_eq!(Mode::FrontEnd.repr, 1); 698 assert_eq!(Mode::Any.repr, 2); 699 } 700 701 #[test] ut_enum_version()702 fn ut_enum_version() { 703 assert_eq!(Version::API9 as u32, 1); 704 assert_eq!(Version::API10 as u32, 2); 705 } 706 707 #[test] ut_enum_network_config()708 fn ut_enum_network_config() { 709 assert_eq!(NetworkConfig::Any as u32, 0); 710 assert_eq!(NetworkConfig::Wifi as u32, 1); 711 assert_eq!(NetworkConfig::Cellular as u32, 2); 712 } 713 } 714