/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::fs::{DirEntry, Metadata, ReadDir}; use std::path::PathBuf; use chrono::{DateTime, Local}; use embedder_traits::resources::{read_string, Resource}; use headers::{ContentType, HeaderMapExt}; use net_traits::request::Request; use net_traits::response::{Response, ResponseBody}; use net_traits::{NetworkError, ResourceFetchTiming}; use servo_config::pref; use servo_url::ServoUrl; use url::Url; pub fn fetch(request: &mut Request, url: ServoUrl, path_buf: PathBuf) -> Response { if !pref!(network.local_directory_listing.enabled) { // If you want to be able to browse local directories, configure Servo prefs so that // "network.local_directory_listing.enabled" is set to true. return Response::network_error(NetworkError::Internal( "Local directory listing feature has not been enabled in preferences".into(), )); } if !request.origin.is_opaque() { // Checking for an opaque origin as a shorthand for user activation // as opposed to a request originating from a script. // TODO(32534): carefully consider security of this approach. return Response::network_error(NetworkError::Internal( "Cannot request local directory listing from non-local origin.".into(), )); } let directory_contents = match std::fs::read_dir(path_buf.clone()) { Ok(directory_contents) => directory_contents, Err(error) => { return Response::network_error(NetworkError::Internal(format!( "Unable to access directory: {error}" ))); }, }; let output = build_html_directory_listing(url.as_url(), path_buf, directory_contents); let mut response = Response::new(url, ResourceFetchTiming::new(request.timing_type())); response.headers.typed_insert(ContentType::html()); *response.body.lock().unwrap() = ResponseBody::Done(output.into_bytes()); response } /// Returns an the string of an JavaScript `\n"); page_html } fn write_directory_entry(entry: DirEntry, metadata: Metadata, url: &Url, output: &mut String) { let Ok(name) = entry.file_name().into_string() else { return; }; let mut file_url = url.clone(); { let Ok(mut path_segments) = file_url.path_segments_mut() else { return; }; path_segments.push(&name); } let class = if metadata.is_dir() { "directory" } else if metadata.is_symlink() { "symlink" } else { "file" }; let file_url_string = &file_url.to_string(); let file_size = metadata_to_file_size_string(&metadata); let last_modified = metadata .modified() .map(DateTime::::from) .map(|time| time.format("%F %r").to_string()) .unwrap_or_default(); output.push_str(&format!( "[{class:?}, {name:?}, {file_url_string:?}, {file_size:?}, {last_modified:?}]," )); } pub fn metadata_to_file_size_string(metadata: &Metadata) -> String { if !metadata.is_file() { return String::new(); } let mut float_size = metadata.len() as f64; let mut prefix_power = 0; while float_size > 1000.0 && prefix_power < 3 { float_size /= 1000.0; prefix_power += 1; } let prefix = match prefix_power { 0 => "B", 1 => "KB", 2 => "MB", _ => "GB", }; format!("{:.2} {prefix}", float_size) }