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;
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 }
97 
98 /// task config
99 #[derive(Clone, Debug)]
100 pub struct TaskConfig {
101     pub(crate) bundle: String,
102     pub(crate) bundle_type: u32,
103     pub(crate) atomic_account: String,
104     pub(crate) url: String,
105     pub(crate) title: String,
106     pub(crate) description: String,
107     pub(crate) method: String,
108     pub(crate) headers: HashMap<String, String>,
109     pub(crate) data: String,
110     pub(crate) token: String,
111     pub(crate) proxy: String,
112     pub(crate) certificate_pins: String,
113     pub(crate) extras: HashMap<String, String>,
114     pub(crate) version: Version,
115     pub(crate) form_items: Vec<FormItem>,
116     pub(crate) file_specs: Vec<FileSpec>,
117     pub(crate) body_file_paths: Vec<String>,
118     pub(crate) certs_path: Vec<String>,
119     pub(crate) common_data: CommonTaskConfig,
120 }
121 
122 impl TaskConfig {
satisfy_network(&self, network: &NetworkState) -> Result<(), Reason>123     pub(crate) fn satisfy_network(&self, network: &NetworkState) -> Result<(), Reason> {
124         // NetworkConfig::Cellular with NetworkType::Wifi is allowed
125         match network {
126             NetworkState::Offline => Err(Reason::NetworkOffline),
127             NetworkState::Online(info) => match self.common_data.network_config {
128                 NetworkConfig::Any => Ok(()),
129                 NetworkConfig::Wifi if info.network_type == NetworkType::Cellular => {
130                     Err(Reason::UnsupportedNetworkType)
131                 }
132                 _ => {
133                     if (self.common_data.roaming || !info.is_roaming)
134                         && (self.common_data.metered || !info.is_metered)
135                     {
136                         Ok(())
137                     } else {
138                         Err(Reason::UnsupportedNetworkType)
139                     }
140                 }
141             },
142         }
143     }
144 
satisfy_foreground(&self, top_uid: Option<u64>) -> bool145     pub(crate) fn satisfy_foreground(&self, top_uid: Option<u64>) -> bool {
146         self.common_data.mode == Mode::BackGround || Some(self.common_data.uid) == top_uid
147     }
148 }
149 
150 pub(crate) struct ConfigSet {
151     pub(crate) headers: String,
152     pub(crate) extras: String,
153     pub(crate) form_items: Vec<CFormItem>,
154     pub(crate) file_specs: Vec<CFileSpec>,
155     pub(crate) body_file_names: Vec<CStringWrapper>,
156     pub(crate) certs_path: Vec<CStringWrapper>,
157 }
158 
159 impl PartialOrd for Mode {
partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>160     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
161         Some(self.cmp(other))
162     }
163 }
164 
165 impl Ord for Mode {
cmp(&self, other: &Self) -> std::cmp::Ordering166     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
167         let me = match *self {
168             Mode::FrontEnd => 0,
169             Mode::Any => 1,
170             Mode::BackGround => 2,
171             _ => unreachable!(),
172         };
173         let other = match *other {
174             Mode::FrontEnd => 0,
175             Mode::Any => 1,
176             Mode::BackGround => 2,
177             _ => unreachable!(),
178         };
179         me.cmp(&other)
180     }
181 }
182 
183 impl From<u8> for Mode {
from(value: u8) -> Self184     fn from(value: u8) -> Self {
185         match value {
186             0 => Mode::BackGround,
187             1 => Mode::FrontEnd,
188             _ => Mode::Any,
189         }
190     }
191 }
192 
193 impl From<u8> for Action {
from(value: u8) -> Self194     fn from(value: u8) -> Self {
195         match value {
196             0 => Action::Download,
197             1 => Action::Upload,
198             _ => Action::Any,
199         }
200     }
201 }
202 
203 impl From<u8> for Version {
from(value: u8) -> Self204     fn from(value: u8) -> Self {
205         match value {
206             2 => Version::API10,
207             _ => Version::API9,
208         }
209     }
210 }
211 
212 impl From<u8> for NetworkConfig {
from(value: u8) -> Self213     fn from(value: u8) -> Self {
214         match value {
215             0 => NetworkConfig::Any,
216             2 => NetworkConfig::Cellular,
217             _ => NetworkConfig::Wifi,
218         }
219     }
220 }
221 
222 impl TaskConfig {
build_config_set(&self) -> ConfigSet223     pub(crate) fn build_config_set(&self) -> ConfigSet {
224         ConfigSet {
225             headers: hashmap_to_string(&self.headers),
226             extras: hashmap_to_string(&self.extras),
227             form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(),
228             file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(),
229             body_file_names: self
230                 .body_file_paths
231                 .iter()
232                 .map(CStringWrapper::from)
233                 .collect(),
234             certs_path: self.certs_path.iter().map(CStringWrapper::from).collect(),
235         }
236     }
237 
contains_user_file(&self) -> bool238     pub(crate) fn contains_user_file(&self) -> bool {
239         for specs in self.file_specs.iter() {
240             if specs.is_user_file {
241                 return true;
242             }
243         }
244         false
245     }
246 }
247 
248 impl Default for TaskConfig {
default() -> Self249     fn default() -> Self {
250         Self {
251             bundle_type: 0,
252             atomic_account: "ohosAnonymousUid".to_string(),
253             bundle: "xxx".to_string(),
254             url: "".to_string(),
255             title: "xxx".to_string(),
256             description: "xxx".to_string(),
257             method: "GET".to_string(),
258             headers: Default::default(),
259             data: "".to_string(),
260             token: "xxx".to_string(),
261             proxy: "".to_string(),
262             extras: Default::default(),
263             version: Version::API10,
264             form_items: vec![],
265             file_specs: vec![],
266             body_file_paths: vec![],
267             certs_path: vec![],
268             certificate_pins: "".to_string(),
269             common_data: CommonTaskConfig {
270                 task_id: 0,
271                 uid: 0,
272                 token_id: 0,
273                 action: Action::Download,
274                 mode: Mode::BackGround,
275                 cover: false,
276                 network_config: NetworkConfig::Any,
277                 metered: false,
278                 roaming: false,
279                 retry: false,
280                 redirect: true,
281                 index: 0,
282                 begins: 0,
283                 ends: -1,
284                 gauge: false,
285                 precise: false,
286                 priority: 0,
287                 background: false,
288             },
289         }
290     }
291 }
292 
293 /// ConfigBuilder
294 pub struct ConfigBuilder {
295     inner: TaskConfig,
296 }
297 
298 impl ConfigBuilder {
299     /// Create a new ConfigBuilder
new() -> Self300     pub fn new() -> Self {
301         Self {
302             inner: TaskConfig::default(),
303         }
304     }
305     /// Set url
url(&mut self, url: &str) -> &mut Self306     pub fn url(&mut self, url: &str) -> &mut Self {
307         self.inner.url = url.to_string();
308         self
309     }
310 
311     /// set version
version(&mut self, version: u8) -> &mut Self312     pub fn version(&mut self, version: u8) -> &mut Self {
313         self.inner.version = version.into();
314         self
315     }
316 
317     /// Set title
file_spec(&mut self, file: File) -> &mut Self318     pub fn file_spec(&mut self, file: File) -> &mut Self {
319         self.inner.file_specs.push(FileSpec::user_file(file));
320         self
321     }
322     /// Set action
action(&mut self, action: Action) -> &mut Self323     pub fn action(&mut self, action: Action) -> &mut Self {
324         self.inner.common_data.action = action;
325         self
326     }
327 
328     /// Set mode
mode(&mut self, mode: Mode) -> &mut Self329     pub fn mode(&mut self, mode: Mode) -> &mut Self {
330         self.inner.common_data.mode = mode;
331         self
332     }
333 
334     /// Set bundle name
bundle_name(&mut self, bundle_name: &str) -> &mut Self335     pub fn bundle_name(&mut self, bundle_name: &str) -> &mut Self {
336         self.inner.bundle = bundle_name.to_string();
337         self
338     }
339 
340     /// Set uid
uid(&mut self, uid: u64) -> &mut Self341     pub fn uid(&mut self, uid: u64) -> &mut Self {
342         self.inner.common_data.uid = uid;
343         self
344     }
345 
346     /// set network
network(&mut self, network: NetworkConfig) -> &mut Self347     pub fn network(&mut self, network: NetworkConfig) -> &mut Self {
348         self.inner.common_data.network_config = network;
349         self
350     }
351 
352     /// Set metered
roaming(&mut self, roaming: bool) -> &mut Self353     pub fn roaming(&mut self, roaming: bool) -> &mut Self {
354         self.inner.common_data.roaming = roaming;
355         self
356     }
357 
358     /// set metered
metered(&mut self, metered: bool) -> &mut Self359     pub fn metered(&mut self, metered: bool) -> &mut Self {
360         self.inner.common_data.metered = metered;
361         self
362     }
363 
364     /// build
build(&mut self) -> TaskConfig365     pub fn build(&mut self) -> TaskConfig {
366         self.inner.clone()
367     }
368 
369     /// redirect
redirect(&mut self, redirect: bool) -> &mut Self370     pub fn redirect(&mut self, redirect: bool) -> &mut Self {
371         self.inner.common_data.redirect = redirect;
372         self
373     }
374 
375     /// begins
begins(&mut self, begins: u64) -> &mut Self376     pub fn begins(&mut self, begins: u64) -> &mut Self {
377         self.inner.common_data.begins = begins;
378         self
379     }
380 
381     /// ends
ends(&mut self, ends: u64) -> &mut Self382     pub fn ends(&mut self, ends: u64) -> &mut Self {
383         self.inner.common_data.ends = ends as i64;
384         self
385     }
386 
387     /// method
method(&mut self, metered: &str) -> &mut Self388     pub fn method(&mut self, metered: &str) -> &mut Self {
389         self.inner.method = metered.to_string();
390         self
391     }
392 
393     /// retry
retry(&mut self, retry: bool) -> &mut Self394     pub fn retry(&mut self, retry: bool) -> &mut Self {
395         self.inner.common_data.retry = retry;
396         self
397     }
398 }
399 
400 #[cfg(feature = "oh")]
401 impl Serialize for TaskConfig {
serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()>402     fn serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()> {
403         parcel.write(&(self.common_data.action.repr as u32))?;
404         parcel.write(&(self.version as u32))?;
405         parcel.write(&(self.common_data.mode.repr as u32))?;
406         parcel.write(&self.bundle_type)?;
407         parcel.write(&self.common_data.cover)?;
408         parcel.write(&(self.common_data.network_config as u32))?;
409         parcel.write(&(self.common_data.metered))?;
410         parcel.write(&self.common_data.roaming)?;
411         parcel.write(&(self.common_data.retry))?;
412         parcel.write(&(self.common_data.redirect))?;
413         parcel.write(&(self.common_data.background))?;
414         parcel.write(&self.common_data.index)?;
415         parcel.write(&(self.common_data.begins as i64))?;
416         parcel.write(&self.common_data.ends)?;
417         parcel.write(&self.common_data.gauge)?;
418         parcel.write(&self.common_data.precise)?;
419         parcel.write(&self.common_data.priority)?;
420         parcel.write(&self.url)?;
421         parcel.write(&self.title)?;
422         parcel.write(&self.method)?;
423         parcel.write(&self.token)?;
424         parcel.write(&self.description)?;
425         parcel.write(&self.data)?;
426         parcel.write(&self.proxy)?;
427         parcel.write(&self.certificate_pins)?;
428 
429         parcel.write(&(self.certs_path.len() as u32))?;
430         for cert_path in &self.certs_path {
431             parcel.write(cert_path)?;
432         }
433 
434         parcel.write(&(self.form_items.len() as u32))?;
435         for form_item in &self.form_items {
436             parcel.write(&form_item.name)?;
437             parcel.write(&form_item.value)?;
438         }
439         parcel.write(&(self.file_specs.len() as u32))?;
440         for file_spec in &self.file_specs {
441             parcel.write(&file_spec.name)?;
442             parcel.write(&file_spec.path)?;
443             parcel.write(&file_spec.file_name)?;
444             parcel.write(&file_spec.mime_type)?;
445             parcel.write(&file_spec.is_user_file)?;
446             if file_spec.is_user_file {
447                 let file = unsafe { File::from_raw_fd(file_spec.fd.unwrap()) };
448                 parcel.write_file(file)?;
449             }
450         }
451 
452         parcel.write(&(self.body_file_paths.len() as u32))?;
453         for body_file_paths in self.body_file_paths.iter() {
454             parcel.write(body_file_paths)?;
455         }
456         parcel.write(&(self.headers.len() as u32))?;
457         for header in self.headers.iter() {
458             parcel.write(header.0)?;
459             parcel.write(header.1)?;
460         }
461 
462         parcel.write(&(self.extras.len() as u32))?;
463         for extra in self.extras.iter() {
464             parcel.write(extra.0)?;
465             parcel.write(extra.1)?;
466         }
467 
468         Ok(())
469     }
470 }
471 
472 #[cfg(test)]
473 mod test {
474     use super::*;
475     #[test]
ut_enum_action()476     fn ut_enum_action() {
477         assert_eq!(Action::Download.repr, 0);
478         assert_eq!(Action::Upload.repr, 1);
479         assert_eq!(Action::Any.repr, 2);
480     }
481 
482     #[test]
ut_enum_mode()483     fn ut_enum_mode() {
484         assert_eq!(Mode::BackGround.repr, 0);
485         assert_eq!(Mode::FrontEnd.repr, 1);
486         assert_eq!(Mode::Any.repr, 2);
487     }
488 
489     #[test]
ut_enum_version()490     fn ut_enum_version() {
491         assert_eq!(Version::API9 as u32, 1);
492         assert_eq!(Version::API10 as u32, 2);
493     }
494 
495     #[test]
ut_enum_network_config()496     fn ut_enum_network_config() {
497         assert_eq!(NetworkConfig::Any as u32, 0);
498         assert_eq!(NetworkConfig::Wifi as u32, 1);
499         assert_eq!(NetworkConfig::Cellular as u32, 2);
500     }
501 }
502