U
    iWZ                  	   @   s  d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlmZ d dl	m
Z
 d dlmZ d dlmZ d dlmZ d dlmZmZmZ e  ed	Zd
ZddddddddhZdZe de de dddZe
eeeeeeedddZe
edddZeedd d!Z eed"d#d$Z!eed"d%d&Z"e
ee dd'd(Z#eeeed)d*d+Z$ej%edd,d-d.Z&eeeed/d0d1Z'eeeeeed2d3d4Z(ed5d6d7Z)e
eeeeed8d9d:Z*dS );    N)Path)secure_filename)FileStorage)load_dotenv)Optional)datetime)crear_docs_headcrear_docs_detailobtener_siguiente_doc_lineZdocs_managementi    z.pdfz.docz.docxz.jpgz.jpegz.pngz.xlsxz.xlszhttps://file.sycelephant.comFTP_HOSTFTP_USERFTP_PASSz/public_html/file/)hostuserpassword	base_path)file_objorigenuser_id
created_bytitledescriptionruta_completareturnc                 C   s.  d}zzPt	d| d|  t
| }	|	d sJd|	d	 d
dW W nS t| }|sjddddW W NS t|}
|
d sd|
d	 ddW W &S t|||}|d s|W W S t|||}|d s|W W S |d }t||||d |	d }|d s|W W S dd| d||d |d |d |	d |	d | jdd}t	d|d   |W W \S  tk
r } z6dt| }tj|dd ddd|d  W Y W S d}~X Y nX W 5 |r(t j|r(zt | td|  W n> tk
r& } ztd| dt|  W 5 d}~X Y nX X dS )!ux  
    Función REUTILIZABLE para subir UN documento con validación completa
    Puede usarse para: Formularios, Cotizaciones, Oportunidades, etc.
    
    Args:
        file_obj (FileStorage): Objeto archivo de Flask
        origen (str): Módulo origen (ej: "Q_SpQ_FormsHead", "Q_SpQ_Quotations")
        user_id (int): ID del usuario
        created_by (str): Nombre completo del usuario
        title (str): Título del archivo
        description (str): Descripción del archivo
        ruta_completa (str): Ruta completa (ej: "Ventas/Formularios/1001-2/FormPEmx")
    
    Returns:
        dict: Resultado de la operación
    NArchivo temporal eliminado: %No se pudo eliminar archivo temporal : zIniciando upload de documento: z para validFmessageVALIDATION_ERRORsuccessr   codeError al crear archivo temporalTEMP_FILE_ERRORSECURITY_ERRORr!   docs_idremote_path	extensionTDocumento "" subido exitosamentedoc_line_iddownload_url	file_sizer&   r+   r,   r'   r-   	file_typeoriginal_namer!   r   datazDocumento subido exitosamente: z%Error inesperado en upload_document: exc_info&Error interno al procesar el documentoINTERNAL_ERRORr!   r   r"   technical_error)ospathexistsremoveloggerdebug	Exceptionwarningstrinfovalidar_archivo_basicocrear_archivo_temporalvalidar_seguridad_archivosubir_archivo_ftpgestionar_docs_headcrear_registro_documentofilenameerror)r   r   r   r   r   r   r   	temp_pathevalidacion_resultseguridad_resultupload_resultdocs_head_resultr&   docs_detail_resultresultado_final	error_msg rT   bC:\Users\victor.barrera\Documents\proyectos\elepV3\Elep\src\App\Utilities_module\DocsManagement.pyupload_document8   s    
   



$	
rV   )r   r   c              
   C   s  z| r| j sdddW S t| j }t|j }|tkrPdddt dW S | dtj	 | 
 }| d |tkrtd }dd|d	d
dW S |dkrdddW S d|||dW S  tk
 r } z&tdt|  ddd W Y S d}~X Y nX dS )uo   
    Validaciones básicas del archivo (tamaño, extensión, etc.)
    REUTILIZABLE para cualquier módulo
    Fu*   No se proporcionó ningún archivo válidor   r   u4   Tipo de archivo no permitido. Extensiones válidas: z, r      u2   El archivo excede el tamaño máximo permitido de z.1fMBu   El archivo está vacíoT)r   rI   r(   r-   u   Error en validación básica: zError al validar el archivoN)rI   r   r   suffixlowerEXTENSIONES_PERMITIDASjoinseekr9   SEEK_ENDtell   TAMAÑO_MAXIMOr?   r=   rJ   rA   )r   rI   r(   r-   t
   tamaño_mbrL   rT   rT   rU   rC      sB    


rC   )rK   r   c              
   C   s   zHt | }|d s|W S t| }|d s>dd|d  dW S dddW S  tk
r } z&td	t|  dd
d W Y S d}~X Y nX dS )uX   
    Validación de seguridad usando ClamAV
    REUTILIZABLE para cualquier módulo
    r   cleanFz Archivo infectado o sospechoso: threatrW   TzArchivo segurou#   Error en validación de seguridad: z)Error al validar la seguridad del archivoN)validar_headers_archivoescanear_con_clamavr?   r=   rJ   rA   )rK   Zheader_resultZclamav_resultrL   rT   rT   rU   rE      s"    rE   )	file_pathr   c              
   C   s   z^t | d}|d}W 5 Q R X ddddg}|D ] }|| kr2ddd	  W S q2d
dd	W S  tk
r } z&tdt|  ddd	 W Y S d}~X Y nX dS )uW   
    Validación básica de headers de archivo para detección rápida de amenazas
    rbi   s!   X5O!P%@AP[4\PZX54(P^)7CC)7}$EICARs   <scripts   javascript:s	   vbscript:Fz%Archivo contiene patrones sospechososrW   Tu   Headers válidoszError al validar headers: z'Error al validar estructura del archivoN)openreadr[   r?   r=   rJ   rA   )rf   fheaderZpatrones_sospechososZpatronrL   rT   rT   rU   rd      s*    rd   c              
   C   s  zrt jdd| gdddd}|jdkr@td|   ddd	W S |jrP|j nd
}td|  d|d	W S W n t jk
r   t	d|   ddd	 Y S  t
k
r   t	d ddd	 Y S  tk
r } z0t	dt|  ddt| d	 W Y S d}~X Y nX dS )z$
    Escanea archivo con ClamAV
    clamscanz--no-summaryTd   )capture_outputtexttimeoutr   zClamAV: Archivo limpio - N)rb   rc   zAmenaza desconocidazClamAV: Amenaza detectada - FzClamAV timeout para archivo: zTimeout en escaneo de virusu3   ClamAV no está instalado o no se encuentra en PATHzSistema antivirus no disponiblezError ejecutando ClamAV: zError en escaneo: )
subprocessrun
returncoder=   r>   stdoutstripr@   TimeoutExpiredrJ   FileNotFoundErrorr?   rA   )rf   resultZthreat_inforL   rT   rT   rU   re   "  s@    



re   c              
   C   s   zjt | j}t|j}tj|d\}}t|d}| d |	| 
  W 5 Q R X td|  |W S  tk
r } ztdt|  W Y dS d}~X Y nX dS )z4
    Crea un archivo temporal desde FileStorage
    )rZ   wbr   zArchivo temporal creado: z Error creando archivo temporal: N)r   rI   r   rZ   tempfilemkstempr9   fdopenr^   writeri   r=   r>   r?   rJ   rA   )r   rI   r(   Ztemp_fdrK   	temp_filerL   rT   rT   rU   rD   T  s    


rD   )rK   r   r   r   c                 C   s  d}zzt|}|s,dtt   }td |dd }| d| }t d| d| }t	
td }|td td  t|| t| d	}|d
| | W 5 Q R X td|  td|  d||||dW W S  t	jk
r> }	 z2dt|	 }
t|
 ddd|
d W Y W dS d}	~	X Y nR tk
r }	 z2dt|	 }
t|
 ddd|
d W Y W S d}	~	X Y nX W 5 |rz|   W n   Y nX X dS )ub   
    Sube archivo al servidor FTP
    🆕 CORREGIDA: URL de descarga correcta para navegador
    Narchivo_r   \/r   r   r   rg   zSTOR z#Archivo subido exitosamente a FTP: u   URL pública accesible: T)r!   r'   r,   rI   Zftp_pathzError de permisos FTP: Fz"Error de permisos al subir archivoZFTP_PERMISSION_ERRORr7   zError subiendo archivo a FTP: z"Error al subir archivo al servidorZ	FTP_ERROR)quitr   intr   now	timestamp
FTP_CONFIGreplaceBASE_URLftplibFTPlogincrear_directorios_ftprh   
storbinaryr=   rB   
error_permrA   rJ   r?   )rK   r   r   Zconn_ftpnombre_archivoZftp_directoryZremote_file_pathZ
public_urlarchivorL   rS   rT   rT   rU   rF   j  sT    


$rF   )ftp_conndirectory_pathr   c                 C   s@   zz"|  | td|  W W dS  tjk
r<   Y nX |dd}d}|D ]}|s`qV|d| 7 }z|  | W qV tjk
r   z| | td|  W n8 tjk
r } zdt|	 kr|W 5 d}~X Y nX Y qVX qVt
d|  W n< tk
r: } ztdt|  |W 5 d}~X Y nX dS )	z4
    Crea directorios en FTP de forma recursiva
    zDirectorio FTP existe: Nr    zDirectorio FTP creado: r;   z*Estructura de directorios FTP verificada: zError creando directorios FTP: )cwdr=   r>   r   r   ru   splitmkdrA   r[   rB   r?   rJ   )r   r   
path_partsZcurrent_pathpartrL   rT   rT   rU   r     s4    

r   )r   r   r   r   c              
   C   sZ   zt || |W S  tk
rT } z(tdt|  dddd W Y S d}~X Y nX dS )ux   
    Crea un nuevo DocsHead (cada upload individual genera su propio grupo)
    REUTILIZABLE para cualquier módulo
    zError gestionando DocsHead: Fz1Error interno al gestionar registro de documentosDOCS_HEAD_ERRORr    N)r   r?   r=   rJ   rA   )r   r   r   rL   rT   rT   rU   rG     s    rG   )r&   r   r   rutar/   r   c              
   C   sh   zt | }t| |||||W S  tk
rb } z(tdt|  dddd W Y S d}~X Y nX dS )uM   
    Crea registro en DocsDetail
    REUTILIZABLE para cualquier módulo
    z%Error creando registro de documento: Fz$Error interno al registrar documentoZDOCS_DETAIL_ERRORr    N)r
   r	   r?   r=   rJ   rA   )r&   r   r   r   r/   Zdoc_linerL   rT   rT   rU   rH     s    rH   )r   c               	   C   s  g } t d s| d t d s(| d t d s:| d z,tjddgd	d
d}|jdkrd| d W n$ ttjfk
r   | d Y nX z6t }t	|d}|
d W 5 Q R X t| W n tk
r   | d Y nX t| dk| td tttdS )ui   
    Verifica que la configuración del módulo sea correcta
    REUTILIZABLE para cualquier módulo
    r   zFTP_HOST no configurador   zFTP_USER no configurador   zFTP_PASS no configuradorl   z	--versionT   )rn   rp   r   z ClamAV no responde correctamenteu(   ClamAV no está instalado o no accesiblewtestz&No se pueden crear archivos temporalesrX   )Zconfiguracion_validaerroresu   tamaño_maximo_mbextensiones_permitidasbase_url)r   appendrq   rr   rs   rw   rv   rz   mktemprh   r}   r9   r<   r?   lenra   listr\   r   )r   rx   Z	temp_testrj   rT   rT   rU   verificar_configuracion  s4    




r   )r   r&   r   r   r   r   c                 C   s
  d}zz,t	d| d|  t
| }|d sJd|d d	d
W W JS t| }|sjdddd
W W *S t|}|d sd|d dd
W W S t|||}	|	d s|	W W S t||||	d |d }
|
d s|
W W S dd| d||
d |	d |	d |d |d | jdd}t	d| d|
d   |W W \S  tk
r } z6dt| }tj|dd ddd|d W Y W S d}~X Y nX W 5 |rt j|rzt | td|  W n> tk
r } ztd| dt|  W 5 d}~X Y nX X dS )u  
    Sube un documento a un DocsHead existente (no crea nuevo DocsHead)
    
    Args:
        file_obj (FileStorage): Objeto archivo de Flask
        docs_id (int): DocsID existente donde agregar el archivo
        title (str): Título del archivo
        description (str): Descripción del archivo
        ruta_completa (str): Ruta completa (ej: "Ventas/Formularios/1001-2/FormPEmx")
    
    Returns:
        dict: Resultado de la operación
    Nr   r   r   z&Subiendo documento a DocsID existente r   Fr   r   r    r#   r$   r%   r!   r'   r(   Tr)   r*   r+   r,   r-   r.   r1   zDocumento subido a DocsID z6Error inesperado en upload_document_to_existing_docs: r3   r5   r6   r7   )r9   r:   r;   r<   r=   r>   r?   r@   rA   rB   rC   rD   rE   rF   rH   rI   rJ   )r   r&   r   r   r   rK   rL   rM   rN   rO   rQ   rR   rS   rT   rT   rU    upload_document_to_existing_docs.  sx    
   


$	
r   )+r9   rz   r   rq   loggingpathlibr   Zwerkzeug.utilsr   Zwerkzeug.datastructuresr   dotenvr   typingr   r   )Consultas_SQL.Utilities.DocsManagementSQLr   r	   r
   	getLoggerr=   ra   r\   r   getenvr   rA   r   dictrV   rC   rE   rd   re   rD   rF   r   r   rG   rH   r   r   rT   rT   rT   rU   <module>   s\   
   p8#2F-  (  