using System; using System.IO; using System.IO.Compression; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; namespace EFCDesk.Classes { public static class Helpers { // Lista de extensiones permitidas private static readonly HashSet ExtensionesPermitidas = new HashSet(StringComparer.OrdinalIgnoreCase) { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".pdf", ".txt", ".csv" }; // Lista de extensiones prohibidas (opcional, para mayor seguridad) private static readonly HashSet ExtensionesProhibidas = new HashSet(StringComparer.OrdinalIgnoreCase) { ".exe", ".bat", ".cmd", ".msi", ".sh", ".js", ".vbs", ".ps1", ".jar", ".com", ".msp", ".scr", ".docm", ".xlsm", ".pptm" }; public static bool ValidarArchivo(string rutaArchivo) { string extension = Path.GetExtension(rutaArchivo); // Revisar primero si está en la lista prohibida if (ExtensionesProhibidas.Contains(extension)) { return false; } //// Solo permitir si está en la lista de permitidas //return ExtensionesPermitidas.Contains(extension); return true; } /// /// Obtiene el tamaño de un archivo en bytes. /// /// Ruta completa del archivo. /// Tamaño en bytes, o -1 si el archivo no existe. public static long ObtenerTamanoEnBytes(string rutaArchivo) { if (!File.Exists(rutaArchivo)) return -1; FileInfo info = new FileInfo(rutaArchivo); return info.Length; } /// /// Obtiene el tamaño de un archivo en formato legible (KB, MB, GB). /// /// Ruta completa del archivo. /// Cadena con el tamaño formateado, o mensaje de error. public static string ObtenerTamanoFormateado(string rutaArchivo) { if (!File.Exists(rutaArchivo)) return "El archivo no existe"; long bytes = new FileInfo(rutaArchivo).Length; string[] sufijos = { "B", "KB", "MB", "GB", "TB" }; int indice = 0; double tamaño = bytes; while (tamaño >= 1024 && indice < sufijos.Length - 1) { tamaño /= 1024; indice++; } return $"{tamaño:0.##} {sufijos[indice]}"; } /// /// Obtiene la url del endpoint para obtener el pedimento. /// /// Cadena del endpoint. public static string ObtenerEndpointPedimentos() { return @"/api/v1/customs/pedimentos/?pedimento_app="; } /// /// Obtiene la url del endpoint para obtener el documento. /// /// Cadena del endpoint. public static string ObtenerEndpointDocumentos() { return @"/api/v1/record/documents/"; } public static string CrearEndpointpedimentoDesk() { // return @"/api/v1/customs/pedimentos/bulk-create-pedimento_desk/"; // return @"/api/v1/customs/pedimentos/bulk-upload-record-zip/"; return @"/api/v1/customs/pedimentos/bulk-upload-record-zip-async/"; } /// /// Espera hasta que todos los archivos dentro de la carpeta estén libres (no en uso) /// public static bool EsperarArchivosListos(string carpetaRaiz, int timeoutSegundos = 30) { var archivos = Directory.GetFiles(carpetaRaiz, "*.*", SearchOption.AllDirectories); var inicio = DateTime.Now; while ((DateTime.Now - inicio).TotalSeconds < timeoutSegundos) { bool todosListos = true; foreach (var archivo in archivos) { try { using (FileStream stream = new FileStream(archivo, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { // Archivo disponible } } catch (IOException) { todosListos = false; break; } } if (todosListos) return true; Thread.Sleep(500); } return false; } /// /// Obtiene la carpeta raíz según aplicaWinsaii /// public static (string RutaRaiz, string NombreRaiz) ObtenerCarpetaRaiz(string ruta) { SQLiteHelper sqliteHelper = new SQLiteHelper(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "historico.db")); ConfiguracionExpediente config = ConfiguracionExpediente.ObtenerDesdeBaseDeDatos(sqliteHelper); bool aplicaWinsaii = config.UsarEstructuraPedimentosWinsaai; string? carpetaActual = EsCarpeta(ruta) ? ruta : Path.GetDirectoryName(ruta); string carpetaBase = Path.GetFullPath(config.FolderParaGenerarExpediente); carpetaActual = Path.GetFullPath(carpetaActual); string[] segmentosBase = carpetaBase.Trim(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar); string[] segmentosActual = carpetaActual.Trim(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar); int nivelDeseado = aplicaWinsaii ? segmentosBase.Length + 2 : segmentosBase.Length + 1; if (segmentosActual.Length > nivelDeseado) segmentosActual = segmentosActual.Take(nivelDeseado).ToArray(); string carpetaRaiz = string.Join(Path.DirectorySeparatorChar.ToString(), segmentosActual); if (!Path.IsPathRooted(carpetaRaiz)) carpetaRaiz = Path.DirectorySeparatorChar + carpetaRaiz; string nombreRaiz = Path.GetFileName(carpetaRaiz); return (carpetaRaiz, nombreRaiz); } public static bool EsCarpeta(string ruta) { return Directory.Exists(ruta); } public static string DominioExpedienteElectronico() { string efc = Properties.Settings.Default.urlEFC as string; return efc; } public static bool EsValidaNomeclaturaCarpeta(string pathCarpeta) { var nombre = Path.GetFileName(pathCarpeta); string pattern = @"^\d{2}-\d{4}-\d{7}$|^\d{2}-\d{2}-\d{4}-\d{7}$"; bool esValido = System.Text.RegularExpressions.Regex.IsMatch(nombre, pattern); if (!esValido) { return false; } return true; } public static bool EsValidaNomeclaturaSAAIM3(string pathArchivo) { var nombre = Path.GetFileName(pathArchivo); string pattern = @"^[m|M]\d{7}\.\d{3}$"; bool esValido = System.Text.RegularExpressions.Regex.IsMatch(nombre, pattern); if (!esValido) { return false; } return true; } public static bool EsArchivoXMl(string rutaArchivo) { try { string extension = Path.GetExtension(rutaArchivo); if (!extension.Equals(".xml", StringComparison.OrdinalIgnoreCase)) return false; XDocument xdoc = XDocument.Load(rutaArchivo); } catch (XmlException exception) { Globales.logger.LogError("Helpers - EsArchivoXML", exception); return false; } catch (Exception ex) { Globales.logger.LogError("Helpers - EsArchivoXML", ex); return false; } return true; } public static byte[] CrearZipEnMemoria(string nombreArchivo, byte[] contenido) { try { using (var memoryStream = new MemoryStream()) { using (var zip = new System.IO.Compression.ZipArchive(memoryStream, System.IO.Compression.ZipArchiveMode.Create, leaveOpen: true)) { // Aquí puedes agregar archivos al archivo ZIP si es necesario var entry = zip.CreateEntry(nombreArchivo); using (var entryStream = entry.Open()) { entryStream.Write(contenido, 0, contenido.Length); } } return memoryStream.ToArray(); } } catch (Exception ex) { // Manejo de la excepción (puedes registrar el error o lanzar una excepción personalizada) Globales.logger.LogError("Helpers", ex); return Array.Empty(); } } public static string CalcularHashMD5(string rutaArchivo) { using (var md5 = MD5.Create()) { using (var stream = File.OpenRead(rutaArchivo)) { var hashBytes = md5.ComputeHash(stream); StringBuilder sb = new StringBuilder(); foreach (var b in hashBytes) { sb.Append(b.ToString("x2")); } return sb.ToString(); } } } /// /// Crea un ZIP de un solo archivo en una carpeta temporal única. /// /// Archivo a comprimir. /// Nombre del ZIP que se generará. /// Si es true, borra la carpeta temporal automáticamente después de crear el ZIP. /// Ruta completa del ZIP generado. public static string CrearZipArchivoEnTemp(string rutaArchivo, string nombreArchivoZip, bool autoLimpiar = false) { // Obtiene la ruta raíz de la carpeta temporal del sistema string tempRoot = Path.Combine(Path.GetTempPath(), "temp-efc-desk"); //string tempRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),"tempEFCDesk"); // Crea una subcarpeta temporal única string tempFolder = Path.Combine(tempRoot, Guid.NewGuid().ToString().ToLower().Replace("-","").Substring(0,8)); Directory.CreateDirectory(tempFolder); // Ruta completa del archivo ZIP dentro de la carpeta temporal string zipPath = Path.Combine(tempFolder, nombreArchivoZip); try { // Verifica que el archivo exista if (!File.Exists(rutaArchivo)) { throw new FileNotFoundException($"El archivo no existe: {rutaArchivo}"); } // Crea el ZIP y agrega el archivo using (var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create)) { zip.CreateEntryFromFile(rutaArchivo, Path.GetFileName(rutaArchivo)); } return zipPath; } catch (Exception ex) { Globales.logger.LogError("Helpers - CrearZipArchivoEnTemp", ex); return ""; } finally { if (autoLimpiar) { try { // Espera un momento para asegurarse de que el ZIP no esté bloqueado System.Threading.Thread.Sleep(3000); // Borra toda la carpeta temporal if (Directory.Exists(tempFolder)) { Directory.Delete(tempFolder, true); } } catch (Exception ex) { Globales.logger.LogError("Helpers - No se pudo limpiar la carpeta temporal", ex); } } } } public static string CrearZipMultipleArchivos(string[] rutasArchivos, string nombreArchivoZip, bool autoLimpiar = false) { string tempRoot = Path.Combine(Path.GetTempPath(), "temp-efc-desk"); string tempFolder = Path.Combine(tempRoot, Guid.NewGuid().ToString().ToLower().Replace("-", "").Substring(0, 8)); Directory.CreateDirectory(tempFolder); string zipPath = Path.Combine(tempFolder, nombreArchivoZip); try { using (var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create)) { foreach (var rutaArchivo in rutasArchivos) { if (File.Exists(rutaArchivo)) { zip.CreateEntryFromFile(rutaArchivo, Path.GetFileName(rutaArchivo)); } } } return zipPath; } catch (Exception ex) { Globales.logger.LogError("Helpers - CrearZipMultipleArchivos", ex); return ""; } finally { if (autoLimpiar) { try { System.Threading.Thread.Sleep(3000); if (Directory.Exists(tempFolder)) { Directory.Delete(tempFolder, true); } } catch (Exception ex) { Globales.logger.LogError("Helpers - No se pudo limpiar carpeta temporal en CrearZipMultipleArchivos", ex); } } } } } }