brum 1.2.1

Multi-Pane Web Environment (File Commander/Manager) - By Woofson
use super::{is_archive_file, DirectoryListing, FileEntry};
use reqwest::Client;
use std::time::Duration;

pub struct WebDavClient;

impl WebDavClient {
    pub async fn list_dir(
        url: &str,
        user: Option<&str>,
        pass: Option<&str>,
    ) -> Result<DirectoryListing, Box<dyn std::error::Error + Send + Sync>> {
        let client = Client::builder()
            .timeout(Duration::from_secs(15))
            .build()?;

        let mut req = client.request(reqwest::Method::from_bytes(b"PROPFIND")?, url)
            .header("Depth", "1")
            .header("Content-Type", "application/xml; charset=utf-8");

        if let (Some(u), Some(p)) = (user, pass) {
            req = req.basic_auth(u, Some(p));
        }

        let resp = req.send().await?;
        if !resp.status().is_success() && resp.status().as_u16() != 207 {
            return Err(format!("WebDAV PROPFIND error: HTTP {}", resp.status()).into());
        }

        let xml_body = resp.text().await?;
        let mut entries = Vec::new();
        let mut total_files = 0;
        let mut total_dirs = 0;
        let mut total_size = 0u64;

        for response_chunk in xml_body.split("<d:response>") {
            if !response_chunk.contains("</d:response>") {
                continue;
            }

            let href = response_chunk
                .split("<d:href>")
                .nth(1)
                .and_then(|s| s.split("</d:href>").next())
                .unwrap_or("")
                .trim();

            if href.is_empty() || href == url || href.trim_end_matches('/') == url.trim_end_matches('/') {
                continue;
            }

            let is_dir = response_chunk.contains("<d:collection/>") || response_chunk.contains("<d:collection />") || href.ends_with('/');
            let clean_name = href.trim_end_matches('/').split('/').last().unwrap_or("").to_string();

            if clean_name.is_empty() {
                continue;
            }

            let size = response_chunk
                .split("<d:getcontentlength>")
                .nth(1)
                .and_then(|s| s.split("</d:getcontentlength>").next())
                .and_then(|s| s.parse::<u64>().ok())
                .unwrap_or(0);

            if is_dir {
                total_dirs += 1;
            } else {
                total_files += 1;
                total_size += size;
            }

            let is_archive = !is_dir && is_archive_file(&clean_name);

            entries.push(FileEntry {
                name: clean_name.clone(),
                path: format!("webdav://{}{}", url.trim_start_matches("http://").trim_start_matches("https://"), href),
                is_dir,
                is_symlink: false,
                is_empty: None,
                size,
                modified: None,
                permissions: if is_dir { "drwxr-xr-x".to_string() } else { "-rw-r--r--".to_string() },
                mode_octal: if is_dir { "0755".to_string() } else { "0644".to_string() },
                owner: "webdav".to_string(),
                group: "webdav".to_string(),
                uid: 1000,
                gid: 1000,
                mime_type: if is_dir { None } else { Some(mime_guess::from_path(&clean_name).first_or_octet_stream().to_string()) },
                is_archive,
            });
        }

        entries.sort_by(|a, b| {
            if a.is_dir == b.is_dir {
                a.name.to_lowercase().cmp(&b.name.to_lowercase())
            } else if a.is_dir {
                std::cmp::Ordering::Less
            } else {
                std::cmp::Ordering::Greater
            }
        });

        Ok(DirectoryListing {
            current_path: url.to_string(),
            parent_path: None,
            entries,
            total_files,
            total_dirs,
            total_size,
            protocol: "webdav".to_string(),
            is_truncated: None,
            max_limit: None,
        })
    }
}