// Copyright (C) 2025 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! File path resolution and permission management for tasks.
//!
//! Exposes `FileManager` and `PermissionManager` which validate task file
//! paths, resolve them to absolute locations, and grant the associated
//! access permissions.
mod permission;
use std::collections::HashMap;
use std::fs::{self, File, OpenOptions};
use std::os::unix::fs::PermissionsExt;
use std::os::unix::io::IntoRawFd;
use std::path::PathBuf;
use std::sync::{Mutex, OnceLock};
use cxx::let_cxx_string;
/// Re-exports of the permission manager and its permission tokens.
pub use permission::{PermissionManager, PermissionToken};
use request_core::config::{Action, Mode, TaskConfig, Version};
use request_core::file::FileSpec;
use request_utils::context::Context;
use request_utils::storage;
const DOCS_PREFIX: &str = "file://docs/";
const MEDIA_PREFIX: &str = "file://media/";
const ABSOLUTE_PREFIX: &str = "/";
const INTERNAL_PATTERN: &str = "internal://cache/";
const MAX_FILE_PATH_LENGTH: usize = 4096;
const FILE_PREFIX: &str = "file://";
const INTERNAL_PREFIX: &str = "internal://";
const RELATIVE_PREFIX: &str = "./";
const AREA1: &str = "/data/storage/el1/base";
const AREA2: &str = "/data/storage/el2/base";
const AREA5: &str = "/data/storage/el5/base";
const CERTS_PATH: &str = "/data/storage/el2/base/.ohos/.request/.certs";
/// Manager that validates task file paths and grants the associated file
/// permissions.
pub struct FileManager {
/// Underlying permission manager used to issue and track permission tokens.
pub permission_manager: PermissionManager,
}
impl FileManager {
/// Returns the shared singleton file manager instance.
pub fn get_instance() -> &'static Self {
static INSTANCE: OnceLock<FileManager> = OnceLock::new();
INSTANCE.get_or_init(|| FileManager {
permission_manager: PermissionManager::new(),
})
}
/// Resolves and validates the file paths for a task, applying the required
/// access permissions and populating certificate settings.
///
/// For downloads it processes the save-as path; for uploads it processes the
/// file specifications. Certificate paths and pins are filled in as needed.
///
/// # Arguments
/// * `context` - Application context used for path resolution and ACL checks.
/// * `config` - Task configuration whose paths are resolved in place.
///
/// # Returns
/// The permission tokens granted for the resolved paths, or an error code.
pub fn apply(
&self,
context: Context,
config: &mut TaskConfig,
) -> Result<Vec<PermissionToken>, i32> {
let mut tokens = if matches!(config.common_data.action, Action::Download) {
let mut tokens = vec![];
if let Some(token) = self.apply_download_path(config, &context)? {
tokens.push(token);
}
tokens
} else {
self.apply_upload_path(config, &context)?
};
Self::get_cert_path(config);
// test
Self::get_certificate_pins(config);
self.apply_cert_path(&mut config.certs_path, &mut tokens)?;
Ok(tokens)
}
/// Resolves the download destination path and grants the associated
/// permission.
///
/// Parses the save-as path, distinguishes user-provided files from sandbox
/// files, converts relative/URI paths to an absolute sandbox path, pushes
/// the resulting `FileSpec`, sets file permissions, and issues a permission
/// token for the resolved path.
///
/// # Arguments
/// * `config` - Task configuration whose `saveas` and `file_specs` are
/// updated in place.
/// * `context` - Application context used for path resolution and ACL
/// checks.
///
/// # Returns
/// `Some(token)` when a permission token is granted for a sandbox file, or
/// `None` for a user-provided file that needs no token.
///
/// # Errors
/// Returns an `i32` error code if path parsing, conversion, or permission
/// setting fails.
fn apply_download_path(
&self,
config: &mut TaskConfig,
context: &Context,
) -> Result<Option<PermissionToken>, i32> {
Self::parse_saveas(config)?;
if Self::is_user_file(&config.saveas.clone()) {
Self::check_download_user_file(config)?;
return Ok(None);
}
let path = Self::convert_download_path(config, context)?;
let file_name = path
.file_name()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_else(|| path.to_string_lossy().to_string());
config.file_specs.push(FileSpec {
name: "file".to_string(),
path: path.to_string_lossy().to_string(),
mime_type: file_name
.rsplit_once('.')
.map(|(_, name)| name.to_string())
.unwrap_or_default(),
file_name,
// todo: check
is_user_file: false,
fd: None,
});
Self::chmod_download_file(&path, config)?;
Ok(Some(self.permission_manager.grant(&path)?))
}
/// Normalizes the `saveas` field of a download task configuration.
///
/// Derives the file name from the request URL when the save-as path is
/// empty or `"./"`, and rejects paths that end with a trailing slash.
///
/// # Arguments
/// * `config` - Task configuration whose `saveas` field is normalized in
/// place.
///
/// # Errors
/// Returns `401` if the save-as path cannot be derived or ends with `/`.
fn parse_saveas(config: &mut TaskConfig) -> Result<(), i32> {
config.saveas.trim();
if config.saveas.is_empty() || config.saveas == "./" {
config.saveas = if let Some(path) = config
.url
.rsplit_once('/')
.map(|(_, name)| name.to_string())
{
path
} else {
error!("ParseSaveas error");
return Err(401);
};
return Ok(());
}
if config.saveas.ends_with('/') {
error!("ParseSaveas error");
Err(401)
} else {
Ok(())
}
}
/// Resolves upload file paths and creates the temporary body files.
///
/// Validates each file spec, grants permission tokens for sandbox files,
/// and creates temporary multipart body files under the cache directory
/// with `0o644` permissions.
///
/// # Arguments
/// * `config` - Task configuration whose `file_specs` and `body_file_paths`
/// are updated in place.
/// * `context` - Application context used for path resolution and ACL
/// checks.
///
/// # Returns
/// The permission tokens granted for the resolved upload files and body
/// files.
///
/// # Errors
/// Returns an `i32` error code if the file count exceeds the limit, a path
/// is invalid, or a body file cannot be created.
fn apply_upload_path(
&self,
config: &mut TaskConfig,
context: &Context,
) -> Result<Vec<PermissionToken>, i32> {
// SDK version must be greater than 15
const MAX_UPLOAD_FILES: i32 = 100;
if config.file_specs.len() as i32 > MAX_UPLOAD_FILES {
return Err(401);
}
let mut tokens = Vec::new();
for file_spec in &mut config.file_specs {
if Self::is_user_file(&file_spec.path) {
if config.version == Version::API9 {
return Err(401);
}
if matches!(config.common_data.mode, Mode::BackGround) {
return Err(401);
}
file_spec.is_user_file = true;
Self::check_upload_user_file(file_spec, context)?;
} else {
tokens.push(self.check_upload_file(file_spec, context, &config.version)?);
}
}
let len = if config.common_data.multipart {
1
} else {
config.file_specs.len()
};
let file_path = context.get_cache_dir();
if file_path.is_empty() {
error!("internal to cache error");
return Err(401); // E_PARAMETER_CHECK
}
for i in 0..len {
// Generate timestamp
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let path = format!("{}/tmp_body_{}_{}", file_path, i, now);
// Validate path validity (simplified version; IsPathValid logic needs to be
// implemented)
if path.contains("..") || path.contains("//") {
error!("Upload IsPathValid error");
return Err(401); // E_PARAMETER_CHECK
}
// Create file
let body_file = match std::fs::OpenOptions::new()
.write(true)
.read(true)
.create(true)
.truncate(true)
.open(&path)
{
Ok(file) => file,
Err(e) => {
error!("UploadBodyFiles failed to open file: {}", e);
return Err(13400001); // E_FILE_IO
}
};
if let Err(e) = fs::set_permissions(&path, fs::Permissions::from_mode(0o644)) {
error!("body chmod fail: {}", e);
}
tokens.push(self.permission_manager.grant(&PathBuf::from(&path))?);
config.body_file_paths.push(path);
}
Ok(tokens)
}
// todo: check
/// Copies certificate files into the shared certs directory and grants
/// permission tokens for them.
///
/// Creates the shared certs directory if missing, copies each certificate
/// file from the supplied folders into it (skipping files that already
/// exist), sets `0o755` permissions, and issues permission tokens. The
/// `certs_path` vector is replaced with the shared certs directory on
/// success.
///
/// # Arguments
/// * `certs_path` - Caller-supplied certificate folders; replaced in place
/// with the shared certs directory path after copying.
/// * `tokens` - Permission token vector extended with tokens for each
/// copied certificate file.
///
/// # Errors
/// Returns `13400001` if the directory cannot be created or a folder path
/// is invalid.
fn apply_cert_path(
&self,
certs_path: &mut Vec<String>,
tokens: &mut Vec<PermissionToken>,
) -> Result<(), i32> {
let new_path = PathBuf::from(CERTS_PATH);
if !new_path.exists() {
fs::create_dir_all(new_path.as_path()).map_err(|e| {
error!("Failed to create directory {:?}: {}", new_path, e);
13400001
})?;
}
for folder_path in certs_path.as_slice() {
let folder = PathBuf::from(folder_path);
if !folder.exists() || !folder.is_dir() {
error!("bad certs_path");
return Err(13400001);
}
if let Ok(entries) = fs::read_dir(&folder) {
for entry in entries.flatten() {
let path = entry.path();
let exist_file_path = folder.join(path.file_name().unwrap_or_default());
let new_file_path = new_path.join(path.file_name().unwrap_or_default());
if !new_file_path.exists() {
if let Err(e) = fs::copy(&exist_file_path, &new_file_path) {
error!(
"Failed to copy file from {:?} to {:?}: {}",
exist_file_path, new_file_path, e
);
continue;
}
}
if let Err(e) =
fs::set_permissions(&new_file_path, fs::Permissions::from_mode(0o755))
{
error!("Failed to set permissions for {:?}: {}", new_file_path, e);
}
tokens.push(self.permission_manager.grant(&new_file_path)?);
}
}
}
if !certs_path.is_empty() {
certs_path.clear();
certs_path.push(CERTS_PATH.to_string());
}
Ok(())
}
/// Validates and prepares a user-provided download destination file.
///
/// Rejects API9, background mode, and non-overwrite configurations, then
/// resolves the file URI to a real path, opens/creates the target file,
/// and pushes a `FileSpec` with the resulting file descriptor.
///
/// # Arguments
/// * `config` - Task configuration whose `file_specs` is extended with the
/// resolved user file.
///
/// # Errors
/// Returns `401` for unsupported configurations, or `13400001` if the
/// target file cannot be opened.
fn check_download_user_file(config: &mut TaskConfig) -> Result<(), i32> {
if matches!(config.version, Version::API9) {
return Err(401);
}
if matches!(config.common_data.mode, Mode::BackGround) {
return Err(401);
}
if matches!(config.common_data.action, Action::Download) {
if !config.overwrite {
return Err(401);
}
let_cxx_string!(target_file = config.saveas.clone());
let file_uri = request_utils::wrapper::FileUriGetRealPath(&target_file);
// todo first_init, fdsan
let fd = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(file_uri)
.map_err(|e| {
error!("open fail: {}", e);
13400001
})?
.into_raw_fd();
let file_name = config
.saveas
.clone()
.rsplit_once('/')
.map(|(_, name)| name.to_string())
.unwrap_or_default();
let file = FileSpec {
name: "file".to_string(),
path: config.saveas.clone(),
file_name: file_name.clone(),
mime_type: file_name
.rsplit_once('.')
.map(|(_, name)| name.to_string())
.unwrap_or_default(),
is_user_file: true,
fd: Some(fd),
};
config.file_specs.push(file);
} else {
}
Ok(())
}
/// Opens a file through the data ability layer.
///
/// # Arguments
/// * `context` - Application context passed to the data ability.
/// * `target_file` - File URI or path to open.
///
/// # Returns
/// The raw file descriptor on success, or a negative value on failure.
fn data_ability_open_file(context: &Context, target_file: String) -> i32 {
let_cxx_string!(target_file = target_file);
request_data_ability::dataability::DataAbilityOpenFile(&context.inner, &target_file)
}
/// Validates a user-provided upload file and populates its descriptor.
///
/// Opens the file via the data ability layer, stores the descriptor on the
/// file spec, and fills in the file name and mime type when missing.
///
/// # Arguments
/// * `file_spec` - File specification updated with `fd`, `file_name`,
/// `mime_type`, and `name`.
/// * `context` - Application context used for data ability access.
///
/// # Errors
/// Returns `401` if the data ability fails to open the file.
fn check_upload_user_file(file_spec: &mut FileSpec, context: &Context) -> Result<(), i32> {
let fd = Self::data_ability_open_file(context, file_spec.path.clone());
if fd < 0 {
return Err(401);
}
file_spec.fd = Some(fd);
file_spec.file_name = file_spec
.path
.clone()
.rsplit_once('/')
.map(|(_, name)| name.to_string())
.unwrap_or_default();
if file_spec.mime_type.is_empty() {
file_spec.mime_type = file_spec
.file_name
.rsplit_once('.')
.map(|(_, name)| name.to_string())
.unwrap_or_default();
}
if file_spec.name.is_empty() {
file_spec.name.push_str("file");
}
Ok(())
}
/// Returns whether the path points to a user-managed file.
///
/// A path is considered user-managed when it starts with the `file://docs/`
/// or `file://media/` prefix.
fn is_user_file(path: &str) -> bool {
path.starts_with(DOCS_PREFIX) || path.starts_with(MEDIA_PREFIX)
}
/// Converts the download save-as path to an absolute sandbox path.
///
/// For API9, absolute paths are used as-is and `internal://cache/` paths
/// are resolved against the cache directory. For API10, the path is
/// converted to an absolute path and verified to reside under one of the
/// allowed application base areas, creating parent directories as needed.
///
/// # Arguments
/// * `config` - Task configuration whose `saveas` is updated with the
/// resolved absolute path.
/// * `context` - Application context used for path resolution.
///
/// # Returns
/// The resolved absolute `PathBuf`.
///
/// # Errors
/// Returns `401` if the path is empty, too long, or outside the allowed
/// application areas.
fn convert_download_path(config: &mut TaskConfig, context: &Context) -> Result<PathBuf, i32> {
let saveas = config.saveas.clone();
match config.version {
Version::API9 => {
if saveas.starts_with(ABSOLUTE_PREFIX) {
// if saveas.len() == ABSOLUTE_PREFIX.len() {
// return Err(401);
// }
return Ok(PathBuf::from(saveas));
} else {
let file_name = match saveas.find(INTERNAL_PATTERN) {
Some(0) => saveas.split_at(INTERNAL_PATTERN.len()).1,
_ => &saveas,
};
if file_name.is_empty() {
return Err(401);
}
let cache_dir = context.get_cache_dir();
if cache_dir.len() + file_name.len() + 1 > MAX_FILE_PATH_LENGTH {
return Err(401);
}
config.saveas = format!("{}/{}", cache_dir, file_name);
// todo: realpath?
// todo: check api9 path verify
Ok(PathBuf::from(config.saveas.clone()))
}
}
Version::API10 => {
let absolute_path = Self::convert_to_absolute_path(&context, &saveas)?;
if context.get_base_dir().is_empty() {
error!("base dir empty");
return Err(401);
}
if !absolute_path.starts_with(AREA1)
&& !absolute_path.starts_with(AREA2)
&& !absolute_path.starts_with(AREA5)
{
error!("not belong app");
return Err(401);
}
config.saveas = absolute_path.to_string_lossy().to_string();
if let Some(parent) = absolute_path.parent() {
if let Err(e) = fs::create_dir_all(parent) {
error!("dir create error: {}", e);
return Err(401);
}
}
Ok(absolute_path)
}
}
}
/// Converts an upload file spec path to an absolute sandbox path.
///
/// For API9, absolute paths are used as-is and `internal://cache/` paths
/// are resolved against the cache directory. For API10, the path is
/// converted to an absolute path and verified to reside under one of the
/// allowed application base areas.
///
/// # Arguments
/// * `file_spec` - File specification whose `path` is updated with the
/// resolved absolute path.
/// * `version` - API version governing path resolution rules.
/// * `context` - Application context used for path resolution.
///
/// # Returns
/// The resolved absolute `PathBuf`.
///
/// # Errors
/// Returns `401` or `13400001` if the path is empty, too long, or outside
/// the allowed application areas.
fn convert_upload_path(
file_spec: &mut FileSpec,
version: &Version,
context: &Context,
) -> Result<PathBuf, i32> {
let path = file_spec.path.clone();
match version {
Version::API9 => {
if path.starts_with(ABSOLUTE_PREFIX) {
if path.len() == ABSOLUTE_PREFIX.len() {
return Err(401);
}
return Ok(PathBuf::from(path));
} else {
let file_name = match path.find(INTERNAL_PATTERN) {
Some(0) => path.split_at(INTERNAL_PATTERN.len()).1,
_ => &path,
};
if file_name.is_empty() {
return Err(13400001);
}
let cache_dir = context.get_cache_dir();
if cache_dir.len() + file_name.len() + 1 > MAX_FILE_PATH_LENGTH {
return Err(401);
}
file_spec.path = format!("{}/{}", cache_dir, file_name);
// todo: realpath?
// todo: check api9 path verify
Ok(PathBuf::from(file_spec.path.clone()))
}
}
Version::API10 => {
let absolute_path = Self::convert_to_absolute_path(&context, &path)?;
if context.get_base_dir().is_empty() {
return Err(401);
}
if !absolute_path.starts_with(AREA1)
&& !absolute_path.starts_with(AREA2)
&& !absolute_path.starts_with(AREA5)
{
return Err(401);
}
file_spec.path = absolute_path.to_string_lossy().to_string();
Ok(absolute_path)
}
}
}
/// Normalizes a path by collapsing `.` and `..` segments.
///
/// Splits the path on `/`, discards empty and `.` segments, and resolves
/// `..` by popping the previous segment. The result is an absolute path
/// with redundant separators removed.
///
/// # Arguments
/// * `path` - Path to normalize.
///
/// # Returns
/// The normalized absolute path string.
///
/// # Errors
/// Returns `401` if a `..` segment would escape above the root.
fn normalize(path: &str) -> Result<String, i32> {
let mut stk = Vec::new();
for seg in path.split('/') {
match seg {
"" | "." => {}
".." => {
if stk.pop().is_none() {
error!("bad path with ..");
return Err(401);
}
}
_ => stk.push(seg),
}
}
Ok(format!("/{}", stk.join("/")))
}
/// Converts a user-supplied path to an absolute sandbox path.
///
/// Handles absolute paths, `file://` URIs (validating the bundle name
/// against the context), `internal://` URIs resolved against the base
/// directory, and relative paths resolved against the cache directory.
/// Each result is normalized to remove redundant segments.
///
/// # Arguments
/// * `context` - Application context providing bundle name and directory
/// roots.
/// * `path` - Path to convert.
///
/// # Returns
/// The resolved absolute `PathBuf`.
///
/// # Errors
/// Returns `401` or `13400001` if the path is empty, malformed, or
/// references a foreign bundle.
fn convert_to_absolute_path(context: &Context, path: &str) -> Result<PathBuf, i32> {
if let Some(0) = path.find(ABSOLUTE_PREFIX) {
// if path.len() == ABSOLUTE_PREFIX.len() {
// return Err(DownloadPathError::EmptyPath);
// }
return Ok(PathBuf::from(Self::normalize(path)?));
}
if path.starts_with(FILE_PREFIX) {
let path = path.split_at(FILE_PREFIX.len()).1;
if path.is_empty() {
error!("convert_to_absolute_path path empty");
return Err(401);
}
let Some(index) = path.find('/') else {
error!("convert_to_absolute_path / not found ");
return Err(401);
};
let (bundle_name, path) = path.split_at(index);
if bundle_name != context.get_bundle_name() {
error!("path bundlename error");
return Err(401);
}
return Ok(PathBuf::from(Self::normalize(path)?));
}
if let Some(0) = path.find(INTERNAL_PREFIX) {
let path = path.split_at(INTERNAL_PREFIX.len()).1;
if path.is_empty() {
return Err(13400001);
}
let base_dir = context.get_base_dir();
return Ok(PathBuf::from(Self::normalize(&format!(
"{}/{}",
base_dir, path
))?));
}
let path = if let Some(0) = path.find(RELATIVE_PREFIX) {
path.split_at(RELATIVE_PREFIX.len()).1
} else {
path
};
if path.is_empty() {
return Err(13400001);
}
let cache_dir = context.get_cache_dir();
Ok(PathBuf::from(Self::normalize(&format!(
"{}/{}",
cache_dir, path
))?))
}
/// Creates and sets permissions on the download destination file.
///
/// When `overwrite` is disabled and the file already exists, returns an
/// error. Otherwise creates the file if needed and applies `0o644`
/// permissions.
///
/// # Arguments
/// * `path` - Destination file path.
/// * `config` - Task configuration supplying the `overwrite` flag and API
/// version.
///
/// # Errors
/// Returns `13400002` (API9) or `13400001` (API10) when the file exists
/// without overwrite, and `13400001` if the file cannot be created or
/// chmodded.
fn chmod_download_file(path: &PathBuf, config: &TaskConfig) -> Result<(), i32> {
// todo first init
if !config.overwrite && path.exists() {
error!("file exists");
if config.version == Version::API9 {
return Err(13400002);
} else {
return Err(13400001);
}
}
let _ = OpenOptions::new()
.write(true)
.create(true)
.open(&path)
.map_err(|_| {
error!("open fail");
13400001
})?;
if let Err(_) = fs::set_permissions(&path, fs::Permissions::from_mode(0o644)) {
error!("permission fail");
return Err(13400001);
}
Ok(())
}
/// Validates an upload source file exists and sets its permissions.
///
/// Checks that the path points to an existing regular file that can be
/// opened for reading, then applies `0o644` permissions.
///
/// # Arguments
/// * `path` - Upload source file path.
/// * `version` - API version governing the error codes returned.
///
/// # Errors
/// Returns `13400001` (API10) or `13400002` (API9) if the file does not
/// exist or cannot be opened.
fn chmod_upload_file(path: &PathBuf, version: &Version) -> Result<(), i32> {
if !path.exists() || !path.is_file() {
error!("path error: path: {}", path.to_string_lossy().to_string());
return Err(if matches!(version, Version::API10) {
13400001
} else {
13400002
});
}
match File::open(&path) {
Ok(_) => {
info!("upload file open ok");
}
Err(e) => {
error!("GetFd failed to open file errno {}", e);
return Err(if matches!(version, Version::API10) {
13400001
} else {
13400002
});
}
}
if let Err(e) = fs::set_permissions(&path, fs::Permissions::from_mode(0o644)) {
error!("upload file chmod fail: {}", e);
}
Ok(())
}
/// Validates a single sandbox upload file and grants its permission token.
///
/// Converts the file spec path to an absolute path, validates and chmods
/// the file, issues a permission token, and fills in the file name, mime
/// type, and field name when missing.
///
/// # Arguments
/// * `file_spec` - File specification updated with the resolved path and
/// metadata.
/// * `context` - Application context used for path resolution.
/// * `version` - API version governing path resolution rules.
///
/// # Returns
/// The permission token granted for the resolved upload file.
///
/// # Errors
/// Returns an `i32` error code if path conversion, validation, or
/// permission granting fails.
fn check_upload_file(
&self,
file_spec: &mut FileSpec,
context: &Context,
version: &Version,
) -> Result<PermissionToken, i32> {
let path_buf = Self::convert_upload_path(file_spec, version, context)?;
Self::chmod_upload_file(&path_buf, version)?;
let token = self.permission_manager.grant(&path_buf)?;
file_spec.path = path_buf.to_string_lossy().to_string();
file_spec.file_name = file_spec
.path
.clone()
.rsplit_once('/')
.map(|(_, name)| name.to_string())
.unwrap_or_default();
if file_spec.mime_type.is_empty() {
file_spec.mime_type = file_spec
.file_name
.rsplit_once('.')
.map(|(_, name)| name.to_string())
.unwrap_or_default();
}
if file_spec.name.is_empty() {
file_spec.name.push_str("file");
}
Ok(token)
}
/// Populates trusted certificate paths for HTTPS URLs.
///
/// For HTTPS URLs, resolves the hostname and queries the trust anchors for
/// it, storing the resulting certificate paths on the config. Non-HTTPS
/// URLs are skipped.
///
/// # Arguments
/// * `config` - Task configuration whose `certs_path` is populated in
/// place.
fn get_cert_path(config: &mut TaskConfig) {
if !config.url.starts_with("https://") {
debug!("Using Http");
return;
}
let hostname = crate::verify::url::get_hostname_from_url(&config.url);
debug!("Hostname is {}", hostname);
let_cxx_string!(hostname_str = hostname);
let certs_path = request_utils::wrapper::GetTrustAnchorsForHostName(&hostname_str);
config.certs_path = certs_path;
}
/// Populates certificate pins for the task URL hostname.
///
/// Resolves the hostname from the task URL and queries the certificate
/// pins associated with it, storing them on the config.
///
/// # Arguments
/// * `config` - Task configuration whose `certificate_pins` is populated in
/// place.
fn get_certificate_pins(config: &mut TaskConfig) {
let hostname = crate::verify::url::get_hostname_from_url(&config.url);
debug!("Hostname is {}", hostname);
let_cxx_string!(hostname_str = hostname);
let certificate_pins = request_utils::wrapper::GetCertificatePinsForHostName(&hostname_str);
config.certificate_pins = certificate_pins;
}
/// Reads the full contents of a file into a byte vector.
///
/// # Arguments
/// * `file_path` - Path of the file to read.
///
/// # Returns
/// `Some(bytes)` on success, or `None` if the file cannot be read.
pub fn read_bytes_from_file(file_path: &str) -> Option<Vec<u8>> {
match fs::read(file_path) {
Ok(data) => Some(data),
Err(e) => {
error!("Failed to read file {}: {}", file_path, e);
None
}
}
}
}