| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- /*
- * file-utils.vala
- *
- * File system operations for valaq.
- * This class provides utility functions for file operations.
- */
- /**
- * Utility class for file system operations.
- *
- * Provides comprehensive file system functionality including VAPI file discovery,
- * path resolution, validation, and security checks. This class handles
- * the complex task of locating VAPI files across multiple standard directories
- * and provides safe file access with proper validation.
- */
- public class FileUtils : Object {
-
- /**
- * Array of VAPI search paths in order of preference.
- *
- * These paths are searched sequentially when resolving VAPI file names.
- * The order is important for precedence - earlier paths have priority.
- */
- private string[] vapi_search_paths;
-
- /**
- * Creates a new FileUtils instance.
- *
- * Initializes the VAPI search paths with standard system directories
- and any additional fallback paths that might exist on the system.
- */
- public FileUtils () {
- // Initialize VAPI search paths
- initialize_vapi_search_paths ();
- }
-
- /**
- * Initializes the VAPI search paths in order of preference.
- */
- private void initialize_vapi_search_paths () {
- vapi_search_paths = new string[0];
-
- // Primary search path
- vapi_search_paths += "/usr/share/vala-0.56/vapi";
-
- // Secondary search path
- vapi_search_paths += "/usr/share/vala/vapi";
-
- // Additional fallback paths (for backward compatibility)
- string[] fallback_paths = {
- "/usr/share/vala-0.54/vapi",
- "/usr/share/vala-0.52/vapi",
- "/usr/local/share/vala/vapi"
- };
-
- foreach (string path in fallback_paths) {
- if (file_exists (path)) {
- vapi_search_paths += path;
- }
- }
- }
-
- /**
- * Gets the VAPI search paths.
- *
- * @return Array of VAPI search paths in order of preference
- */
- public string[] get_vapi_search_paths () {
- return vapi_search_paths;
- }
-
- /**
- * Checks if a file exists.
- *
- * @param path Path to the file
- * @return True if file exists, false otherwise
- */
- public bool file_exists (string path) {
- return File.new_for_path (path).query_exists ();
- }
-
- /**
- * Checks if a path is a VAPI file.
- *
- * Determines if the given path points to a VAPI file by checking
- * the file extension. This is used for filtering and validation.
- *
- * @param path Path to check
- * @return True if path is a VAPI file, false otherwise
- */
- public bool is_vapi_file (string path) {
- return path.has_suffix (".vapi");
- }
-
- /**
- * Finds VAPI files in a directory.
- *
- * Scans the specified directory for files with .vapi extension.
- * Returns full paths to all found VAPI files. Handles directory
- * access errors gracefully and continues operation.
- *
- * @param directory Directory path to search for VAPI files
- * @return Array of VAPI file paths found in directory
- */
- public Gee.ArrayList<string> find_vapi_files (string directory) {
- var vapi_files = new Gee.ArrayList<string> ();
-
- try {
- var dir = File.new_for_path (directory);
- if (!dir.query_exists ()) {
- stderr.printf ("Warning: VAPI directory does not exist: %s\n", directory);
- return vapi_files;
- }
-
- var enumerator = dir.enumerate_children (
- FileAttribute.STANDARD_NAME + "," + FileAttribute.STANDARD_TYPE,
- FileQueryInfoFlags.NONE
- );
-
- FileInfo file_info;
- while ((file_info = enumerator.next_file ()) != null) {
- string filename = file_info.get_name ();
- if (is_vapi_file (filename)) {
- vapi_files.add (Path.build_filename (directory, filename));
- }
- }
- } catch (Error e) {
- stderr.printf ("Error scanning VAPI directory: %s\n", e.message);
- }
-
- return vapi_files;
- }
-
- /**
- * Finds VAPI files across all search paths.
- *
- * Scans all configured VAPI directories and returns a consolidated
- * list of available VAPI files. Removes duplicates based on basename
- * to avoid showing the same file from multiple directories.
- *
- * @return Array of VAPI file paths with duplicates removed
- */
- public Gee.ArrayList<string> find_all_vapi_files () {
- var all_vapi_files = new Gee.ArrayList<string> ();
- var seen_files = new Gee.HashSet<string> ();
-
- foreach (string search_path in vapi_search_paths) {
- var vapi_files = find_vapi_files (search_path);
-
- foreach (string file_path in vapi_files) {
- var file = File.new_for_path (file_path);
- string basename = file.get_basename ();
-
- // Avoid duplicates by checking basename
- if (!seen_files.contains (basename)) {
- seen_files.add (basename);
- all_vapi_files.add (file_path);
- }
- }
- }
-
- return all_vapi_files;
- }
-
- /**
- * Resolves a VAPI file name across all search paths.
- * Supports both basenames (e.g., "zlib.vapi") and names without extension (e.g., "zlib").
- * The first found file is used, with primary search path taking precedence.
- *
- * @param vapi_name Name of the VAPI file (with or without .vapi extension)
- * @return Full path to the VAPI file, or null if not found
- */
- public string? resolve_vapi_file (string vapi_name) {
- // Validate input
- if (vapi_name == "" || vapi_name.strip () == "") {
- return null;
- }
-
- // Check if this is a full path (contains path separators)
- if (vapi_name.contains ("/") || vapi_name.contains ("\\")) {
- // For full paths, we don't resolve through search paths
- // Just check if the file exists as-is
- return file_exists (vapi_name) ? vapi_name : null;
- }
-
- // This is a basename - ensure it has .vapi extension for searching
- string filename = vapi_name;
- if (!filename.has_suffix (".vapi")) {
- filename += ".vapi";
- }
-
- // First, check the current working directory
- string current_dir = Environment.get_current_dir ();
- string current_dir_path = Path.build_filename (current_dir, filename);
- if (file_exists (current_dir_path)) {
- return current_dir_path;
- }
-
- // Then search in all VAPI paths in order of preference
- foreach (string search_path in vapi_search_paths) {
- string full_path = Path.build_filename (search_path, filename);
- if (file_exists (full_path)) {
- return full_path;
- }
- }
-
- return null;
- }
-
- /**
- * Gets information about which search path contains a VAPI file.
- *
- * @param vapi_name Name of the VAPI file (with or without .vapi extension)
- * @return Array containing [search_path, full_path], or null if not found
- */
- public string[]? find_vapi_file_location (string vapi_name) {
- // Ensure the name has .vapi extension
- string filename = vapi_name;
- if (!filename.has_suffix (".vapi")) {
- filename += ".vapi";
- }
-
- // Search in all paths in order of preference
- foreach (string search_path in vapi_search_paths) {
- string full_path = Path.build_filename (search_path, filename);
- if (file_exists (full_path)) {
- return { search_path, full_path };
- }
- }
-
- return null;
- }
-
- /**
- * Gets the default VAPI directory.
- *
- * @return Path to the default VAPI directory (first in search paths)
- */
- public string get_default_vapi_directory () {
- // Return the first search path that exists
- foreach (string path in vapi_search_paths) {
- if (file_exists (path)) {
- return path;
- }
- }
-
- // Default fallback to the primary search path
- return vapi_search_paths.length > 0 ? vapi_search_paths[0] : "/usr/share/vala/vapi";
- }
-
- /**
- * Validates a file path for security and accessibility.
- *
- * Performs security checks to prevent directory traversal attacks
- * and ensures the path is within allowed directories. This is
- * crucial for preventing unauthorized file access.
- *
- * Security measures:
- * - Prevents "../" directory traversal
- * - Restricts access to VAPI directories and current working directory
- * - Allows absolute paths to existing VAPI files
- *
- * @param path Path to validate for security and accessibility
- * @return True if path is safe and allowed, false otherwise
- */
- public bool validate_path (string path) {
- // Check for directory traversal attempts
- if (path.contains ("../") || path.contains ("..\\")) {
- return false;
- }
-
- // Convert to absolute path and check if it's within allowed directories
- var file = File.new_for_path (path);
- string absolute_path;
-
- try {
- absolute_path = file.get_path ();
- } catch (Error e) {
- return false;
- }
-
- // Allow paths in any VAPI search directory or current working directory
- string[] allowed_prefixes = {};
-
- // Add all VAPI search paths
- foreach (string vapi_path in vapi_search_paths) {
- allowed_prefixes += vapi_path;
- }
-
- // Add current working directory
- allowed_prefixes += Environment.get_current_dir ();
-
- foreach (string prefix in allowed_prefixes) {
- if (absolute_path.has_prefix (prefix)) {
- return true;
- }
- }
-
- // Also allow absolute paths to VAPI files directly
- if (absolute_path.has_suffix (".vapi") && file_exists (absolute_path)) {
- return true;
- }
-
- return false;
- }
- }
|