• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
17 
18 pub use ffi::{Action, Mode};
19 
20 cfg_oh! {
21     use ipc::parcel::Serialize;
22 }
23 
24 use super::reason::Reason;
25 use crate::manage::network::{NetworkState, NetworkType};
26 use crate::utils::c_wrapper::{CFileSpec, CFormItem, CStringWrapper};
27 use crate::utils::form_item::{FileSpec, FormItem};
28 use crate::utils::hashmap_to_string;
29 #[cxx::bridge(namespace = "OHOS::Request")]
30 mod ffi {
31     /// Action
32     #[derive(Clone, Copy, PartialEq, Debug)]
33     #[repr(u8)]
34     pub enum Action {
35         /// Download
36         Download = 0,
37         /// Upload
38         Upload,
39         /// Any
40         Any,
41     }
42 
43     /// Mode
44     #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
45     #[repr(u8)]
46     pub enum Mode {
47         /// BackGround
48         BackGround = 0,
49         /// ForeGround
50         FrontEnd,
51         /// Any
52         Any,
53     }
54 }
55 
56 #[derive(Clone, Copy, PartialEq, Debug)]
57 #[repr(u8)]
58 pub(crate) enum Version {
59     API9 = 1,
60     API10,
61 }
62 
63 /// NetworkConfig
64 #[derive(Clone, Copy, PartialEq, Debug)]
65 #[repr(u8)]
66 pub enum NetworkConfig {
67     /// Any
68     Any = 0,
69     /// Wifi
70     Wifi,
71     /// Cellular
72     Cellular,
73 }
74 
75 #[repr(C)]
76 #[derive(Copy, Clone, Debug)]
77 pub(crate) struct CommonTaskConfig {
78     pub(crate) task_id: u32,
79     pub(crate) uid: u64,
80     pub(crate) token_id: u64,
81     pub(crate) action: Action,
82     pub(crate) mode: Mode,
83     pub(crate) cover: bool,
84     pub(crate) network_config: NetworkConfig,
85     pub(crate) metered: bool,
86     pub(crate) roaming: bool,
87     pub(crate) retry: bool,
88     pub(crate) redirect: bool,
89     pub(crate) index: u32,
90     pub(crate) begins: u64,
91     pub(crate) ends: i64,
92     pub(crate) gauge: bool,
93     pub(crate) precise: bool,
94     pub(crate) priority: u32,
95     pub(crate) background: bool,
96     pub(crate) multipart: bool,
97 }
98 
99 /// task config
100 #[derive(Clone, Debug)]
101 pub struct TaskConfig {
102     pub(crate) bundle: String,
103     pub(crate) bundle_type: u32,
104     pub(crate) atomic_account: String,
105     pub(crate) url: String,
106     pub(crate) title: String,
107     pub(crate) description: String,
108     pub(crate) method: String,
109     pub(crate) headers: HashMap<String, String>,
110     pub(crate) data: String,
111     pub(crate) token: String,
112     pub(crate) proxy: String,
113     pub(crate) certificate_pins: String,
114     pub(crate) extras: HashMap<String, String>,
115     pub(crate) version: Version,
116     pub(crate) form_items: Vec<FormItem>,
117     pub(crate) file_specs: Vec<FileSpec>,
118     pub(crate) body_file_paths: Vec<String>,
119     pub(crate) certs_path: Vec<String>,
120     pub(crate) common_data: CommonTaskConfig,
121 }
122 
123 impl TaskConfig {
satisfy_network(&self, network: &NetworkState) -> Result<(), Reason>124     pub(crate) fn satisfy_network(&self, network: &NetworkState) -> Result<(), Reason> {
125         // NetworkConfig::Cellular with NetworkType::Wifi is allowed
126         match network {
127             NetworkState::Offline => Err(Reason::NetworkOffline),
128             NetworkState::Online(info) => match self.common_data.network_config {
129                 NetworkConfig::Any => Ok(()),
130                 NetworkConfig::Wifi if info.network_type == NetworkType::Cellular => {
131                     Err(Reason::UnsupportedNetworkType)
132                 }
133                 _ => {
134                     if (self.common_data.roaming || !info.is_roaming)
135                         && (self.common_data.metered || !info.is_metered)
136                     {
137                         Ok(())
138                     } else {
139                         Err(Reason::UnsupportedNetworkType)
140                     }
141                 }
142             },
143         }
144     }
145 
satisfy_foreground(&self, foreground_abilities: &HashSet<u64>) -> bool146     pub(crate) fn satisfy_foreground(&self, foreground_abilities: &HashSet<u64>) -> bool {
147         self.common_data.mode == Mode::BackGround
148             || foreground_abilities.contains(&self.common_data.uid)
149     }
150 }
151 
152 pub(crate) struct ConfigSet {
153     pub(crate) headers: String,
154     pub(crate) extras: String,
155     pub(crate) form_items: Vec<CFormItem>,
156     pub(crate) file_specs: Vec<CFileSpec>,
157     pub(crate) body_file_names: Vec<CStringWrapper>,
158     pub(crate) certs_path: Vec<CStringWrapper>,
159 }
160 
161 impl PartialOrd for Mode {
partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>162     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
163         Some(self.cmp(other))
164     }
165 }
166 
167 impl Ord for Mode {
cmp(&self, other: &Self) -> std::cmp::Ordering168     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
169         let me = match *self {
170             Mode::FrontEnd => 0,
171             Mode::Any => 1,
172             Mode::BackGround => 2,
173             _ => unreachable!(),
174         };
175         let other = match *other {
176             Mode::FrontEnd => 0,
177             Mode::Any => 1,
178             Mode::BackGround => 2,
179             _ => unreachable!(),
180         };
181         me.cmp(&other)
182     }
183 }
184 
185 impl From<u8> for Mode {
from(value: u8) -> Self186     fn from(value: u8) -> Self {
187         match value {
188             0 => Mode::BackGround,
189             1 => Mode::FrontEnd,
190             _ => Mode::Any,
191         }
192     }
193 }
194 
195 impl From<u8> for Action {
from(value: u8) -> Self196     fn from(value: u8) -> Self {
197         match value {
198             0 => Action::Download,
199             1 => Action::Upload,
200             _ => Action::Any,
201         }
202     }
203 }
204 
205 impl From<u8> for Version {
from(value: u8) -> Self206     fn from(value: u8) -> Self {
207         match value {
208             2 => Version::API10,
209             _ => Version::API9,
210         }
211     }
212 }
213 
214 impl From<u8> for NetworkConfig {
from(value: u8) -> Self215     fn from(value: u8) -> Self {
216         match value {
217             0 => NetworkConfig::Any,
218             2 => NetworkConfig::Cellular,
219             _ => NetworkConfig::Wifi,
220         }
221     }
222 }
223 
224 impl TaskConfig {
build_config_set(&self) -> ConfigSet225     pub(crate) fn build_config_set(&self) -> ConfigSet {
226         ConfigSet {
227             headers: hashmap_to_string(&self.headers),
228             extras: hashmap_to_string(&self.extras),
229             form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(),
230             file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(),
231             body_file_names: self
232                 .body_file_paths
233                 .iter()
234                 .map(CStringWrapper::from)
235                 .collect(),
236             certs_path: self.certs_path.iter().map(CStringWrapper::from).collect(),
237         }
238     }
239 
contains_user_file(&self) -> bool240     pub(crate) fn contains_user_file(&self) -> bool {
241         for specs in self.file_specs.iter() {
242             if specs.is_user_file {
243                 return true;
244             }
245         }
246         false
247     }
248 }
249 
250 impl Default for TaskConfig {
default() -> Self251     fn default() -> Self {
252         Self {
253             bundle_type: 0,
254             atomic_account: "ohosAnonymousUid".to_string(),
255             bundle: "xxx".to_string(),
256             url: "".to_string(),
257             title: "xxx".to_string(),
258             description: "xxx".to_string(),
259             method: "GET".to_string(),
260             headers: Default::default(),
261             data: "".to_string(),
262             token: "xxx".to_string(),
263             proxy: "".to_string(),
264             extras: Default::default(),
265             version: Version::API10,
266             form_items: vec![],
267             file_specs: vec![],
268             body_file_paths: vec![],
269             certs_path: vec![],
270             certificate_pins: "".to_string(),
271             common_data: CommonTaskConfig {
272                 task_id: 0,
273                 uid: 0,
274                 token_id: 0,
275                 action: Action::Download,
276                 mode: Mode::BackGround,
277                 cover: false,
278                 network_config: NetworkConfig::Any,
279                 metered: false,
280                 roaming: false,
281                 retry: false,
282                 redirect: true,
283                 index: 0,
284                 begins: 0,
285                 ends: -1,
286                 gauge: false,
287                 precise: false,
288                 priority: 0,
289                 background: false,
290                 multipart: false,
291             },
292         }
293     }
294 }
295 
296 /// ConfigBuilder
297 pub struct ConfigBuilder {
298     inner: TaskConfig,
299 }
300 
301 impl ConfigBuilder {
302     /// Create a new ConfigBuilder
new() -> Self303     pub fn new() -> Self {
304         Self {
305             inner: TaskConfig::default(),
306         }
307     }
308     /// Set url
url(&mut self, url: &str) -> &mut Self309     pub fn url(&mut self, url: &str) -> &mut Self {
310         self.inner.url = url.to_string();
311         self
312     }
313 
314     /// set version
version(&mut self, version: u8) -> &mut Self315     pub fn version(&mut self, version: u8) -> &mut Self {
316         self.inner.version = version.into();
317         self
318     }
319 
320     /// Set title
file_spec(&mut self, file: File) -> &mut Self321     pub fn file_spec(&mut self, file: File) -> &mut Self {
322         self.inner.file_specs.push(FileSpec::user_file(file));
323         self
324     }
325     /// Set action
action(&mut self, action: Action) -> &mut Self326     pub fn action(&mut self, action: Action) -> &mut Self {
327         self.inner.common_data.action = action;
328         self
329     }
330 
331     /// Set mode
mode(&mut self, mode: Mode) -> &mut Self332     pub fn mode(&mut self, mode: Mode) -> &mut Self {
333         self.inner.common_data.mode = mode;
334         self
335     }
336 
337     /// Set bundle name
bundle_name(&mut self, bundle_name: &str) -> &mut Self338     pub fn bundle_name(&mut self, bundle_name: &str) -> &mut Self {
339         self.inner.bundle = bundle_name.to_string();
340         self
341     }
342 
343     /// Set uid
uid(&mut self, uid: u64) -> &mut Self344     pub fn uid(&mut self, uid: u64) -> &mut Self {
345         self.inner.common_data.uid = uid;
346         self
347     }
348 
349     /// set network
network(&mut self, network: NetworkConfig) -> &mut Self350     pub fn network(&mut self, network: NetworkConfig) -> &mut Self {
351         self.inner.common_data.network_config = network;
352         self
353     }
354 
355     /// Set metered
roaming(&mut self, roaming: bool) -> &mut Self356     pub fn roaming(&mut self, roaming: bool) -> &mut Self {
357         self.inner.common_data.roaming = roaming;
358         self
359     }
360 
361     /// set metered
metered(&mut self, metered: bool) -> &mut Self362     pub fn metered(&mut self, metered: bool) -> &mut Self {
363         self.inner.common_data.metered = metered;
364         self
365     }
366 
367     /// build
build(&mut self) -> TaskConfig368     pub fn build(&mut self) -> TaskConfig {
369         self.inner.clone()
370     }
371 
372     /// redirect
redirect(&mut self, redirect: bool) -> &mut Self373     pub fn redirect(&mut self, redirect: bool) -> &mut Self {
374         self.inner.common_data.redirect = redirect;
375         self
376     }
377 
378     /// begins
begins(&mut self, begins: u64) -> &mut Self379     pub fn begins(&mut self, begins: u64) -> &mut Self {
380         self.inner.common_data.begins = begins;
381         self
382     }
383 
384     /// ends
ends(&mut self, ends: u64) -> &mut Self385     pub fn ends(&mut self, ends: u64) -> &mut Self {
386         self.inner.common_data.ends = ends as i64;
387         self
388     }
389 
390     /// method
method(&mut self, metered: &str) -> &mut Self391     pub fn method(&mut self, metered: &str) -> &mut Self {
392         self.inner.method = metered.to_string();
393         self
394     }
395 
396     /// retry
retry(&mut self, retry: bool) -> &mut Self397     pub fn retry(&mut self, retry: bool) -> &mut Self {
398         self.inner.common_data.retry = retry;
399         self
400     }
401 }
402 
403 #[cfg(feature = "oh")]
404 impl Serialize for TaskConfig {
serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()>405     fn serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()> {
406         parcel.write(&(self.common_data.action.repr as u32))?;
407         parcel.write(&(self.version as u32))?;
408         parcel.write(&(self.common_data.mode.repr as u32))?;
409         parcel.write(&self.bundle_type)?;
410         parcel.write(&self.common_data.cover)?;
411         parcel.write(&(self.common_data.network_config as u32))?;
412         parcel.write(&(self.common_data.metered))?;
413         parcel.write(&self.common_data.roaming)?;
414         parcel.write(&(self.common_data.retry))?;
415         parcel.write(&(self.common_data.redirect))?;
416         parcel.write(&(self.common_data.background))?;
417         parcel.write(&(self.common_data.multipart))?;
418         parcel.write(&self.common_data.index)?;
419         parcel.write(&(self.common_data.begins as i64))?;
420         parcel.write(&self.common_data.ends)?;
421         parcel.write(&self.common_data.gauge)?;
422         parcel.write(&self.common_data.precise)?;
423         parcel.write(&self.common_data.priority)?;
424         parcel.write(&self.url)?;
425         parcel.write(&self.title)?;
426         parcel.write(&self.method)?;
427         parcel.write(&self.token)?;
428         parcel.write(&self.description)?;
429         parcel.write(&self.data)?;
430         parcel.write(&self.proxy)?;
431         parcel.write(&self.certificate_pins)?;
432 
433         parcel.write(&(self.certs_path.len() as u32))?;
434         for cert_path in &self.certs_path {
435             parcel.write(cert_path)?;
436         }
437 
438         parcel.write(&(self.form_items.len() as u32))?;
439         for form_item in &self.form_items {
440             parcel.write(&form_item.name)?;
441             parcel.write(&form_item.value)?;
442         }
443         parcel.write(&(self.file_specs.len() as u32))?;
444         for file_spec in &self.file_specs {
445             parcel.write(&file_spec.name)?;
446             parcel.write(&file_spec.path)?;
447             parcel.write(&file_spec.file_name)?;
448             parcel.write(&file_spec.mime_type)?;
449             parcel.write(&file_spec.is_user_file)?;
450             if file_spec.is_user_file {
451                 let file = unsafe { File::from_raw_fd(file_spec.fd.unwrap()) };
452                 parcel.write_file(file)?;
453             }
454         }
455 
456         parcel.write(&(self.body_file_paths.len() as u32))?;
457         for body_file_paths in self.body_file_paths.iter() {
458             parcel.write(body_file_paths)?;
459         }
460         parcel.write(&(self.headers.len() as u32))?;
461         for header in self.headers.iter() {
462             parcel.write(header.0)?;
463             parcel.write(header.1)?;
464         }
465 
466         parcel.write(&(self.extras.len() as u32))?;
467         for extra in self.extras.iter() {
468             parcel.write(extra.0)?;
469             parcel.write(extra.1)?;
470         }
471 
472         Ok(())
473     }
474 }
475 
476 #[cfg(test)]
477 mod test {
478     use super::*;
479     #[test]
ut_enum_action()480     fn ut_enum_action() {
481         assert_eq!(Action::Download.repr, 0);
482         assert_eq!(Action::Upload.repr, 1);
483         assert_eq!(Action::Any.repr, 2);
484     }
485 
486     #[test]
ut_enum_mode()487     fn ut_enum_mode() {
488         assert_eq!(Mode::BackGround.repr, 0);
489         assert_eq!(Mode::FrontEnd.repr, 1);
490         assert_eq!(Mode::Any.repr, 2);
491     }
492 
493     #[test]
ut_enum_version()494     fn ut_enum_version() {
495         assert_eq!(Version::API9 as u32, 1);
496         assert_eq!(Version::API10 as u32, 2);
497     }
498 
499     #[test]
ut_enum_network_config()500     fn ut_enum_network_config() {
501         assert_eq!(NetworkConfig::Any as u32, 0);
502         assert_eq!(NetworkConfig::Wifi as u32, 1);
503         assert_eq!(NetworkConfig::Cellular as u32, 2);
504     }
505 }
506