o
    �õ±jR7 ã                   @   sÌ   d Z ddlZddlZddlZ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mZ ddlmZmZ G dd„ dƒZG dd	„ d	eƒZ			ddd„Z			ddd„Zdd„ Zedkrdeƒ  dS dS )a“  
File Download Server Example

This example demonstrates how to create a secure file download server using the 
HTTPServerHandler class. The server allows:
- Listing files in a specified directory
- Downloading individual files
- Protection against directory traversal attacks

Usage:
    python fileserver.py [directory_path] [host] [port]

Example:
    python fileserver.py ./downloads 127.0.0.1 8080
é    N)ÚPath)Ú	UniTargetÚUniProto)ÚHTTPServerHandlerÚ
HTTPServerc                   @   sˆ   e Zd ZdZ		d#dd„Zd$d	d
„Zdd„ Zdd„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd „ Zd!d"„ ZdS )%ÚMultipartStreamProcessorzœ
    Memory-efficient multipart form data processor that handles boundary detection
    across chunk boundaries and streams file data directly to disk.
    é   é  @Néd   c                 C   sp   || _ || _|| _|| _|| _|| _d| _d| _d | _d | _	g | _
g | _d| _d| j  | _| j d | _|| _d S )Nó    Úlooking_for_boundaryr   ó   
s   --)Úboundary_bytesÚtarget_pathÚmax_buffer_sizeÚmax_file_sizeÚallowed_extensionsÚmax_files_per_requestÚbufferÚstateÚcurrent_file_infoÚcurrent_file_handleÚ
temp_filesÚcompleted_filesÚ
file_countÚboundary_with_crlfÚend_boundaryÚprint_cb)Úselfr   r   r   r   r   r   r   © r   ú�/root/aizidognhua/tmp/workspace/projects/ec89d86c-575f-41c9-af57-ac45cbdbf775/venv/lib/python3.10/site-packages/asysocks/examples/fileserver.pyÚ__init__%   s    
z!MultipartStreamProcessor.__init__Ú c                 Ã   ó$   �| j d u rd S |   |¡I d H  d S ©N©r   ©r   Úmsgr   r   r    Úprint<   ó   €
zMultipartStreamProcessor.printc                 Ã   sò   �|  j |7  _ g }t| j ƒ| jkr4| jdkr|  ¡ I dH  nt| j ƒ| jd kr4tdt| j ƒ› d�ƒ‚	 | jdkrM| |  ¡ I dH ¡ | jdkrL	 |S n+| jdkr]|  ¡ I dH s\	 |S n| jdkrx|  	¡ I dH }|rp| 
|¡ | jdkrx	 |S q5)	zç
        Process a chunk of data, returning any completed file info.
        
        Args:
            chunk (bytes): Raw chunk data
            
        Returns:
            list: List of completed file info dictionaries
        Úreading_file_dataNé   zBuffer size exceeded: ú bytesTr   Úreading_headers)r   Úlenr   r   Ú_flush_file_dataÚ
ValueErrorÚextendÚ_process_boundary_searchÚ_process_headersÚ_process_file_dataÚappend)r   Úchunkr   Ú	file_infor   r   r    Úprocess_chunkA   s8   €



ò
	ö


ïz&MultipartStreamProcessor.process_chunkc                 Ã   s  �g }| j  | j¡}|dkr,t| j ƒt| jƒd kr*t| jƒd }| j | d… | _ |S | j ||t| jƒ … | jkrP|  ¡ I dH  | jrN| | j¡ d| _|S |t| jƒ }| j |d… | _ | j  d¡rn| j dd… | _ n| j  d¡r|| j dd… | _ d| _	|S )	z*Search for boundary markers in the buffer.éÿÿÿÿr+   é
   Nr   ó   
é   r-   )
r   Úfindr   r.   r   Ú_finalize_current_filer   r5   Ú
startswithr   )r   r   Úboundary_posÚ	keep_sizeÚboundary_endr   r   r    r2   i   s,   €z1MultipartStreamProcessor._process_boundary_searchc              
   Ã   s0  �| j  d¡}|dkr4| j  d¡}|dkr"t| j ƒdkr tdƒ‚dS | j d|… }| j |d d… | _ n| j d|… }| j |d	 d… | _ t|ƒd
krOtdƒ‚z	|jddd�}W n tyk } ztd|› �ƒ‚d}~ww |  |¡}|r“| jr}|  ¡ I dH  |  	|¡}|s‹td|› �ƒ‚|  
|¡I dH  d| _dS )zProcess multipart headers.s   

r9   s   

i    zCMultipart headers too long or malformed (missing header terminator)FNr+   é   i   zMultipart part headers too longúutf-8Ústrict)ÚerrorszInvalid header encoding: zInvalid or unsafe filename: r*   T)r   r=   r.   r0   ÚdecodeÚUnicodeDecodeErrorÚ_extract_filename_from_headersr   r>   Ú_sanitize_upload_filenameÚ_start_new_filer   )r   Ú
header_endÚheader_sectionÚheaders_textÚeÚfilenameÚsafe_filenamer   r   r    r3   Ž   s<   €€ÿ

z)MultipartStreamProcessor._process_headersc                 Ã   sÐ   �| j s	d| _dS | j | j¡}|dkrEt| jƒt| jƒd krCt| jƒt| jƒ d }| jd|… }| j|d… | _|  |¡I dH  dS | jd|… }| j|d… | _|  |¡I dH  |  ¡ I dH }d| _|S )z1Process file data, looking for the next boundary.r   Nr9   r:   )r   r   r   r=   r   r.   Ú_write_file_datar>   )r   Únext_boundary_posÚ
write_sizeÚdata_to_writer7   r   r   r    r4   º   s$   €z+MultipartStreamProcessor._process_file_datac                 C   sn   |  d¡D ]/}| ¡ }| ¡  d¡r4t d|tj¡}|r#| d¡  S t d|tj¡}|r4| d¡  S qdS )z1Extract filename from Content-Disposition header.Ú
zcontent-disposition:zfilename="([^"]*)"r<   zfilename=([^;\s]+)N)ÚsplitÚstripÚlowerr?   ÚreÚsearchÚ
IGNORECASEÚgroup)r   rN   ÚlineÚfilename_matchr   r   r    rI   Ø   s   €z7MultipartStreamProcessor._extract_filename_from_headersc                 C   s�   |sdS t j |¡}t dd|¡}t dd|¡}t dd|¡}| d¡}|r*|dv r,dS t|ƒd	krFt j |¡\}}|dd
… |dd…  }|S )z^
        Sanitize uploaded filename to prevent directory traversal and other attacks.
        Nz	[<>:"|?*]Ú_z
[^\w\-_\.]z\.\.+Ú.z._)ra   ú..éÿ   éú   é   )ÚosÚpathÚbasenamerZ   ÚsubrX   r.   Úsplitext)r   rP   Ú	safe_nameÚ	name_partÚext_partr   r   r    rJ   ç   s   
z2MultipartStreamProcessor._sanitize_upload_filenamec              
   Ã   s  �| j | jkrtd| j› d�ƒ‚| jr0tj |¡d  ¡ }|| jvr0td|› dd | j¡› �ƒ‚tj | j	|¡}|d }zt
|dƒ| _| j |¡ W n+ ty` } z
td	|› d
|› �ƒ‚d}~w tyu } z
td|› d
|› �ƒ‚d}~ww |||ddœ| _|  j d7  _ dS )zStart writing a new file.zToo many files in upload (max: ú)r<   zFile extension 'z' not allowed. Allowed: ú, z
.uploadingÚwbzCannot create upload file 'ú': Nz(Permission denied creating upload file 'r   )rP   Ú	temp_pathÚ
final_pathÚsize)r   r   r0   r   rf   rg   rj   rY   Újoinr   Úopenr   r   r5   ÚOSErrorÚPermissionErrorr   )r   rP   Úfile_extÚ	file_pathÚtemp_file_pathrO   r   r   r    rK     s2   €
€€ÿüz(MultipartStreamProcessor._start_new_filec              
   Ã   sà   �| j rl|rn| jd t|ƒ }|| jkr&td| jd › d|› d| j› d�ƒ‚z| j  |¡ | jd  t|ƒ7  < W d	S  tyS } ztd| jd › d|› �ƒ‚d	}~w tyk } ztd
| jd › d|› �ƒ‚d	}~ww d	S d	S )zWrite data to current file.rt   zFile 'rP   z' exceeds size limit: ú bytes (max: rn   zError writing to file 'rq   NzUnexpected error writing file ')r   r   r.   r   r0   Úwriterw   Ú	Exception)r   ÚdataÚnew_sizerO   r   r   r    rR   "  s    €

$€€ÿõz)MultipartStreamProcessor._write_file_datac                 Ã   s   �| j r| j  ¡  dS dS )z'Flush file data to reduce memory usage.N)r   Úflush)r   r   r   r    r/   2  s   €ÿz)MultipartStreamProcessor._flush_file_datac              
   Ã   s*  �| j sdS | jr�| j ¡  d| _| j d }| j d }z=t ||¡ || jv r-| j |¡ | j d | j d |dœ}|  d|d › d|d d	›d
�¡I dH  | j 	|¡ d| _ |W S  t
y� } z+|  d|› d|› �¡I dH  zt |¡ || jv r| j |¡ W n   Y W Y d}~nd}~ww d| _ dS )z.Finalize the current file and return its info.Nrr   rs   rP   rt   )rP   rt   rg   z[UPLOAD-SUCCESS] Uploaded: z (ú,ú bytes)z%[UPLOAD-ERROR] Error finalizing file ú: )r   r   Úcloserf   Úrenamer   Úremover(   r   r5   r~   Úunlink)r   rr   rs   r7   rO   r   r   r    r>   7  sB   €



ý(

€€ø
z/MultipartStreamProcessor._finalize_current_filec                 Ã   sš   �g }| j rK| jdkrK| jdkr|  ¡ I dH s	 |S n(| jdkrC| jr2| j r2|  | j ¡I dH  d| _ |  ¡ I dH }|r@| |¡ 	 |S | j rK| jdks|S )zFinalize any remaining data.r   r-   Nr*   r   )r   r   r3   r   rR   r>   r5   )r   r   r7   r   r   r    Úfinalizeb  s&   €
ô

òz!MultipartStreamProcessor.finalizec                 Ã   s°   �| j rz| j  ¡  W n   Y d| _ | jD ]:}ztj |¡r/t |¡ |  d|› �¡I dH  W q tyP } z|  d|› d|› �¡I dH  W Y d}~qd}~ww | j 	¡  dS )zClean up any temporary files.Nz$[UPLOAD-CLEANUP] Removed temp file: z*[UPLOAD-CLEANUP] Error removing temp file r„   )
r   r…   r   rf   rg   Úexistsrˆ   r(   r~   Úclear)r   rr   rO   r   r   r    Úcleanupw  s$   €

€(€ÿz MultipartStreamProcessor.cleanup)r   r	   Nr
   N©r"   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r!   r(   r8   r2   r3   r4   rI   rJ   rK   rR   r/   r>   r‰   rŒ   r   r   r   r    r      s$    
ÿ
(%, +r   c                       s¨   e Zd ZdZ		d'‡ fdd„	Zd(d	d
„Zdd„ Zd)dd„Zdd„ Zdd„ Z	dd„ Z
dd„ Zd(dd„Zdd„ Zdd„ Zdd„ Zdd „ Zd(d!d"„Zd)d#d$„Zd%d&„ Z‡  ZS )*ÚFileDownloadHandlera0  
    HTTP server handler that provides secure file download functionality.
    
    Features:
    - File listing for GET requests without parameters
    - File download for GET requests with 'file' parameter
    - Directory traversal protection
    - MIME type detection
    - HTML directory listing
    Nì        r	   r
   c                    sˆ   t ƒ  ¡  || _|| _|| _|| _|| _|| _| jr@tj	 
| j¡| _tj	 | j¡s1td| j› �ƒ‚tj	 | j¡sBtd| j› �ƒ‚dS dS )a  
        Initialize the file download handler.
        
        Args:
            download_directory (str): Directory to serve files from
            max_upload_size (int): Maximum total upload size per request in bytes (default: 2GB)
            max_file_size (int): Maximum individual file size in bytes (default: 500MB)
            allowed_extensions (set): Set of allowed file extensions (None = allow all)
            max_files_per_request (int): Maximum number of files per upload request (default: 100)
        z#Download directory does not exist: z"Download path is not a directory: N)Úsuperr!   Údownload_directoryÚmax_upload_sizer   r   r   r   rf   rg   ÚabspathrŠ   r0   Úisdir)r   r•   r–   r   r   r   r   ©Ú	__class__r   r    r!   —  s   
úzFileDownloadHandler.__init__r"   c                 Ã   r#   r$   r%   r&   r   r   r    r(   ³  r)   zFileDownloadHandler.printc                    sì   | j sdS |s
| j S | dd¡}g }| d¡D ]%‰ ˆ  ¡ ‰ ˆ r#ˆ dkr$qˆ dkr)qt‡ fdd„dD ƒƒr7 dS | ˆ ¡ q|sB| j S | j }|D ]	‰ tj |ˆ ¡}qGtj 	|¡}ztj 
|| j g¡}|| j kriW dS W |S  tyu   Y dS w )	aW  
        Sanitize the requested path to prevent directory traversal attacks while allowing
        safe navigation within the allowed directory tree.
        
        Args:
            path_request (str): The requested file or directory path
            
        Returns:
            str or None: Safe absolute path or None if invalid
        Nú\ú/ra   rb   c                 3   s   � | ]}|ˆ v V  qd S r$   r   )Ú.0Úchar©Ú	componentr   r    Ú	<genexpr>Ö  s   € z5FileDownloadHandler._sanitize_path.<locals>.<genexpr>)ú:Ú*ú?ú"ú<ú>ú|)r•   ÚreplacerW   rX   Úanyr5   rf   rg   ru   r—   Ú
commonpathr0   )r   Úpath_requestÚnormalized_pathÚpath_componentsÚ	safe_pathÚcommon_pathr   rŸ   r    Ú_sanitize_path¸  s<   
ÿüþz"FileDownloadHandler._sanitize_pathc                  C   sæ  | j sdS |du r| j }�zÅ|| j krd}d}ndtj || j ¡ tjd¡ }|}g }g }t |¡D ](}tj ||¡}tj |¡rOtj 	|¡}| 
||f¡ q2tj |¡rZ| 
|¡ q2| ¡  | ¡  t |¡}	t |¡}
|  |¡}d| d }|| j krštj | d¡¡}|dkrŒd}tj |¡}|d| d	 7 }|dkr§tj | d¡¡nd}|r°d
|› �nd}d}|d|› d�7 }|D ]+}t |¡}|dkrÌ|}n	| d¡d | }tj |¡}|d|› d|› d|› d�7 }q¾|D ]t\}}t |¡}|dkrü|}n	| d¡d | }tj |¡}|  |¡}tj |¡d  ¡ }|dv �r"d}n*|dv �r*d}n"|dv �r2d}n|dv �r:d}n|dv �rBd}n
|dv �rJd}nd}|d|› d |› d!|› d"|› d#|› d$�7 }qìt|ƒt|ƒ }|d g d%‘t|ƒ› ‘d&‘t|ƒ› ‘d'‘|› ‘d(‘|
› ‘d)‘|› ‘d‘|› ‘d*‘|› ‘d‘|› ‘d+‘|› ‘d‘|› ‘d,‘|› ‘d‘|› ‘d-‘|› ‘d‘|› ‘d.‘|› ‘d‘|› ‘d/‘|› ‘d‘|› ‘d0‘¡7 }|W S  t�yò } zt|ƒ}d1t |¡› d2�W  Y d}~S d}~ww )3zÜ
        Generate an HTML page listing files and directories.
        
        Args:
            current_path (str): The current directory path to list
            
        Returns:
            str: HTML content
        zV<html><body><h1>File Server</h1><p>No download directory configured.</p></body></html>Nrœ   zRoot Directoryu‡$  <!DOCTYPE html>
<html>
<head>
    <title>ðŸ“� File Server - Directory Browser</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        :root {
            --primary: #2563eb;
            --primary-dark: #1d4ed8;
            --primary-light: #3b82f6;
            --secondary: #64748b;
            --secondary-light: #94a3b8;
            --success: #059669;
            --success-light: #10b981;
            --warning: #d97706;
            --warning-light: #f59e0b;
            --background: #f8fafc;
            --surface: #ffffff;
            --surface-2: #f1f5f9;
            --text: #1e293b;
            --text-muted: #64748b;
            --border: #e2e8f0;
            --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
        }
        
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
            background: var(--background);
            color: var(--text);
            min-height: 100vh;
            padding: 20px;
            line-height: 1.6;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: var(--surface);
            border-radius: 12px;
            box-shadow: var(--shadow);
            overflow: hidden;
            animation: slideIn 0.5s ease-out;
        }
        @keyframes slideIn {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }
        .header {
            background: var(--primary);
            color: white;
            padding: 32px;
            text-align: center;
        }
        .header h1 {
            font-size: 2.5rem;
            font-weight: 700;
            margin-bottom: 8px;
        }
        .header p {
            opacity: 0.9;
            font-size: 1.1rem;
        }
        .breadcrumb {
            background: var(--surface-2);
            padding: 16px 32px;
            border-bottom: 1px solid var(--border);
            font-size: 1rem;
            font-weight: 500;
        }
        .content {
            padding: 32px;
        }
        .section {
            margin-bottom: 32px;
            padding: 24px;
            border-radius: 8px;
            border: 1px solid var(--border);
            background: var(--surface);
        }
        .upload-section {
            background: #f0fdf4;
            border: 2px dashed var(--success);
            transition: all 0.3s ease;
        }
        .upload-section:hover {
            background: #ecfdf5;
            border-color: var(--success-light);
        }
        .commands-section {
            background: #fefce8;
            border: 1px solid var(--warning);
        }
        .section h3 {
            color: var(--text);
            margin-bottom: 16px;
            font-weight: 600;
            font-size: 1.25rem;
        }
        .btn {
            background: var(--primary);
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            transition: all 0.2s ease;
            text-decoration: none;
            display: inline-block;
        }
        .btn:hover {
            background: var(--primary-dark);
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
        }
        .btn-upload {
            background: var(--success);
            font-size: 16px;
            padding: 16px 32px;
            border-radius: 8px;
        }
        .btn-upload:hover {
            background: var(--success-light);
            box-shadow: 0 4px 12px rgba(5, 150, 105, 0.3);
        }
        .file-input {
            padding: 12px;
            border: 2px solid var(--border);
            border-radius: 6px;
            width: 100%;
            margin-bottom: 16px;
            transition: border-color 0.2s ease;
            font-size: 14px;
        }
        .file-input:focus {
            border-color: var(--primary);
            outline: none;
            box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
        }
        .progress-container {
            display: none;
            margin-top: 16px;
            background: var(--surface-2);
            border-radius: 8px;
            padding: 16px;
        }
        .progress-bar {
            width: 100%;
            height: 8px;
            background: var(--border);
            border-radius: 4px;
            overflow: hidden;
            margin-bottom: 8px;
        }
        .progress-fill {
            height: 100%;
            background: var(--primary);
            width: 0%;
            transition: width 0.3s ease;
            border-radius: 4px;
        }
        .command-box {
            background: #0f172a;
            color: #e2e8f0;
            padding: 16px;
            border-radius: 6px;
            font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
            font-size: 13px;
            margin: 12px 0;
            position: relative;
            overflow-x: auto;
            border: 1px solid #334155;
        }
        .command-box pre {
            margin: 0;
            padding: 0;
            background: transparent;
            color: inherit;
            font-family: inherit;
            font-size: inherit;
            line-height: 1.5;
            white-space: pre-wrap;
            word-wrap: break-word;
        }
        .copy-btn {
            position: absolute;
            top: 12px;
            right: 12px;
            background: var(--secondary);
            color: white;
            border: none;
            padding: 6px 12px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 11px;
            transition: background 0.2s;
        }
        .copy-btn:hover { 
            background: var(--secondary-light); 
        }
        .table-container {
            overflow-x: auto;
            border-radius: 8px;
            border: 1px solid var(--border);
        }
        table {
            width: 100%;
            border-collapse: collapse;
            background: var(--surface);
        }
        th {
            background: var(--surface-2);
            color: var(--text);
            padding: 16px;
            text-align: left;
            font-weight: 600;
            font-size: 14px;
            border-bottom: 1px solid var(--border);
        }
        td {
            padding: 12px 16px;
            border-bottom: 1px solid var(--border);
            transition: background-color 0.2s ease;
        }
        tr:hover td {
            background-color: var(--surface-2);
        }
        .file-link {
            color: var(--primary);
            text-decoration: none;
            font-weight: 500;
            transition: color 0.2s ease;
        }
        .file-link:hover {
            color: var(--primary-dark);
        }
        .stats {
            background: var(--surface-2);
            padding: 24px;
            border-radius: 8px;
            text-align: center;
            margin-top: 24px;
            border: 1px solid var(--border);
        }
        .stats h3 {
            color: var(--text);
            margin-bottom: 20px;
        }
        .stat-item {
            text-align: center;
            margin: 12px;
        }
        .stat-number {
            font-size: 2rem;
            font-weight: 700;
            margin-bottom: 4px;
        }
        .stat-label {
            font-size: 14px;
            color: var(--text-muted);
            font-weight: 500;
        }
        .tab-container {
            margin-bottom: 20px;
        }
        .tab-buttons {
            display: flex;
            background: var(--surface-2);
            border-radius: 6px;
            padding: 4px;
            margin-bottom: 16px;
        }
        .tab-btn {
            flex: 1;
            padding: 10px 16px;
            border: none;
            background: transparent;
            cursor: pointer;
            border-radius: 4px;
            transition: all 0.2s ease;
            font-weight: 500;
            font-size: 14px;
            color: var(--text-muted);
        }
        .tab-btn.active {
            background: var(--surface);
            color: var(--text);
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
        }
        .tab-content {
            display: none;
        }
        .tab-content.active {
            display: block;
        }
        .badge {
            padding: 4px 8px;
            border-radius: 12px;
            font-size: 12px;
            font-weight: 500;
        }
        .badge-directory {
            background: #dbeafe;
            color: #1e40af;
        }
        .badge-file {
            background: #f3e8ff;
            color: #7c3aed;
        }
        @media (max-width: 768px) {
            .container { margin: 10px; }
            .content { padding: 20px; }
            .header { padding: 24px; }
            .header h1 { font-size: 2rem; }
            .stats > div { flex-direction: column; }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="breadcrumb">
            <strong>ðŸ“� Current Location:</strong> z.
        </div>
        <div class="content">
r"   z@            <div class="section">
                <a href="?dir=u¡   " class="btn" style="text-decoration: none; display: inline-block;">
                    â¬†ï¸� Back to Parent Directory
                </a>
            </div>
z?dir=zhttp://YOUR_SERVER_IP:8080u·   
            <div class="section upload-section">
                <h3>ðŸ“¤ Upload Files</h3>
                <form id="uploadForm" method="post" enctype="multipart/form-data" action="u;  ">
                    <input type="file" name="files" multiple accept="*/*" class="file-input" id="fileInput">
                    <button type="submit" class="btn btn-upload">ðŸš€ Upload Files</button>
                    <div class="progress-container" id="progressContainer">
                        <div class="progress-bar">
                            <div class="progress-fill" id="progressFill"></div>
                        </div>
                        <div id="progressText">Uploading... 0%</div>
                    </div>
                </form>
                <div style="margin-top: 15px; font-size: 14px; color: #2d5a27;">
                    ðŸ’¡ Drag and drop files or click to browse. Multiple files supported. Large files streamed efficiently.
                </div>
            </div>

            <div class="section">
                <h3>ðŸ“‚ Directory Contents</h3>
                <div class="table-container">
                    <table>
                        <thead>
                            <tr>
                                <th>ðŸ“‹ Name</th>
                                <th>ðŸ“� Type</th>
                                <th>ðŸ“� Size</th>
                                <th>ðŸ•’ Actions</th>
                            </tr>
                        </thead>
                        <tbody>
zS                            <tr>
                                <td><a href="?dir=u   " class="file-link">ðŸ“� zà</a></td>
                                <td><span class="badge badge-directory">Directory</span></td>
                                <td style="text-align: right;">-</td>
                                <td><a href="?dir=zƒ" class="btn" style="padding: 6px 12px; font-size: 12px; text-decoration: none;">Browse</a></td>
                            </tr>
r<   )z.jpgz.jpegz.pngz.gifz.bmpu   ðŸ–¼ï¸�)z.mp4z.aviz.mkvz.movu   ðŸŽ¬)z.mp3z.wavz.flacz.oggu   ðŸŽµ)z.pdfu   ðŸ“•)z.txtz.mdu   ðŸ“„)z.zipz.rarz.7zz.tarz.gzu   ðŸ“¦zT                            <tr>
                                <td><a href="?file=z" class="file-link">ú z�</a></td>
                                <td><span class="badge badge-file">File</span></td>
                                <td style="text-align: right;">z9</td>
                                <td><a href="?file=z…" class="btn" style="padding: 6px 12px; font-size: 12px; text-decoration: none;">Download</a></td>
                            </tr>
u³                          </tbody>
                    </table>
                </div>
            </div>
            
            <div class="stats">
                <h3>ðŸ“Š Directory Statistics</h3>
                <div style="display: flex; justify-content: space-around; flex-wrap: wrap; margin-top: 16px;">
                    <div class="stat-item">
                        <div class="stat-number" style="color: var(--success);">uå   </div>
                        <div class="stat-label">ðŸ“� Directories</div>
                    </div>
                    <div class="stat-item">
                        <div class="stat-number" style="color: var(--primary);">uá   </div>
                        <div class="stat-label">ðŸ“„ Files</div>
                    </div>
                    <div class="stat-item">
                        <div class="stat-number" style="color: var(--secondary);">ue  </div>
                        <div class="stat-label">ðŸ“‹ Total Items</div>
                    </div>
                </div>
                <div style="margin-top: 16px; font-size: 14px; color: var(--text-muted);">
                    ðŸ’¾ Current path: <code style="background: var(--surface-2); padding: 2px 6px; border-radius: 4px; font-size: 13px;">u™  </code>
                </div>
            </div>

            <div class="section commands-section">
                <h3>ðŸ’» Command Line Upload Examples</h3>
                <p style="margin-bottom: 20px; color: var(--text-muted);">Use these commands to upload files from your terminal or scripts.</p>
                <div class="tab-container">
                    <div class="tab-buttons">
                        <button class="tab-btn active" onclick="showTab('curl')">ðŸŒ� cURL</button>
                        <button class="tab-btn" onclick="showTab('wget')">ðŸ“¥ wget</button>
                        <button class="tab-btn" onclick="showTab('powershell')">âš¡ PowerShell</button>
                        <button class="tab-btn" onclick="showTab('python')">ðŸ�� Python</button>
                    </div>
                    
                    <div id="curl" class="tab-content active">
                        <div class="command-box">
                            <button class="copy-btn" onclick="copyToClipboard('curlCmd')">ðŸ“‹ Copy</button>
                            <pre id="curlCmd"># Upload a single file
curl -X POST -F "files=@/path/to/your/file.txt" "z{"

# Upload multiple files
curl -X POST \
  -F "files=@file1.txt" \
  -F "files=@file2.jpg" \
  -F "files=@file3.pdf" \
  "uV  "</pre>
                        </div>
                        <p><strong>Usage:</strong> Replace <code>/path/to/your/file.txt</code> with your actual file path. Use <code>-F "files=@filename"</code> for each file.</p>
                    </div>
                    
                    <div id="wget" class="tab-content">
                        <div class="command-box">
                            <button class="copy-btn" onclick="copyToClipboard('wgetCmd')">ðŸ“‹ Copy</button>
                            <pre id="wgetCmd"># Create multipart form data (limited support)
echo -e "--boundary123\r\nContent-Disposition: form-data; name=\"files\"; filename=\"test.txt\"\r\nContent-Type: text/plain\r\n\r\nfile content here\r\n--boundary123--\r\n" | \
wget --post-data=@- \
     --header="Content-Type: multipart/form-data; boundary=boundary123" \
     "u’  " -O -</pre>
                        </div>
                        <p><strong>Note:</strong> wget has limited multipart support. <strong>Recommended:</strong> Use cURL for better compatibility and easier syntax.</p>
                    </div>
                    
                    <div id="powershell" class="tab-content">
                        <div class="command-box">
                            <button class="copy-btn" onclick="copyToClipboard('psCmd')">ðŸ“‹ Copy</button>
                            <pre id="psCmd"># Upload a single file
$file = Get-Item "C:\path\to\your\file.txt"
$form = @{'files' = $file}
$response = Invoke-RestMethod -Uri "zþ" -Method Post -Form $form
Write-Output $response

# Upload multiple files
$files = Get-ChildItem "C:\path\to\files\*" -Include *.txt,*.jpg,*.pdf
$form = @{}
foreach ($file in $files) {
    $form.Add("files", $file)
}
$response = Invoke-RestMethod -Uri "u˜  " -Method Post -Form $form
Write-Output $response</pre>
                        </div>
                        <p><strong>Usage:</strong> Replace the file paths with your actual Windows file paths. Works with PowerShell 3.0+.</p>
                    </div>
                    
                    <div id="python" class="tab-content">
                        <div class="command-box">
                            <button class="copy-btn" onclick="copyToClipboard('pythonCmd')">ðŸ“‹ Copy</button>
                            <pre id="pythonCmd">#!/usr/bin/env python3
import requests
import os

# Upload a single file
def upload_single_file(file_path):
    url = "a7  "
    with open(file_path, 'rb') as f:
        files = {'files': f}
        response = requests.post(url, files=files)
        print(f"Status: {response.status_code}")
        print(f"Response: {response.text}")
        return response

# Upload multiple files
def upload_multiple_files(file_paths):
    url = "u  "
    files = []
    try:
        for file_path in file_paths:
            files.append(('files', open(file_path, 'rb')))
        
        response = requests.post(url, files=files)
        print(f"Status: {response.status_code}")
        print(f"Response: {response.text}")
        return response
    finally:
        # Close all file handles
        for _, file_handle in files:
            file_handle.close()

# Example usage
if __name__ == "__main__":
    # Single file upload
    upload_single_file("/path/to/your/file.txt")
    
    # Multiple files upload
    file_list = [
        "/path/to/file1.txt",
        "/path/to/file2.jpg",
        "/path/to/file3.pdf"
    ]
    upload_multiple_files(file_list)</pre>
                        </div>
                        <p><strong>Installation:</strong> <code>pip install requests</code><br>
                        <strong>Usage:</strong> Replace file paths with your actual file paths. The script includes proper error handling and file cleanup.</p>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // Tab switching functionality
        function showTab(tabName) {
            const contents = document.querySelectorAll('.tab-content');
            contents.forEach(content => content.classList.remove('active'));
            
            const buttons = document.querySelectorAll('.tab-btn');
            buttons.forEach(btn => btn.classList.remove('active'));
            
            document.getElementById(tabName).classList.add('active');
            event.target.classList.add('active');
        }
        
        // Copy to clipboard functionality
        function copyToClipboard(elementId) {
            const element = document.getElementById(elementId);
            const text = element.textContent;
            
            if (navigator.clipboard) {
                navigator.clipboard.writeText(text).then(() => {
                    showCopyFeedback(event.target);
                });
            } else {
                const textArea = document.createElement('textarea');
                textArea.value = text;
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
                showCopyFeedback(event.target);
            }
        }
        
        function showCopyFeedback(button) {
            const originalText = button.textContent;
            button.textContent = 'âœ… Copied!';
            button.style.background = '#28a745';
            setTimeout(() => {
                button.textContent = originalText;
                button.style.background = '#4a5568';
            }, 2000);
        }
        
        // Upload progress functionality
        document.getElementById('uploadForm').addEventListener('submit', function(e) {
            e.preventDefault();
            
            const fileInput = document.getElementById('fileInput');
            const files = fileInput.files;
            
            if (files.length === 0) {
                alert('Please select files to upload');
                return;
            }
            
            const formData = new FormData();
            for (let i = 0; i < files.length; i++) {
                formData.append('files', files[i]);
            }
            
            const progressContainer = document.getElementById('progressContainer');
            const progressFill = document.getElementById('progressFill');
            const progressText = document.getElementById('progressText');
            
            progressContainer.style.display = 'block';
            
            const xhr = new XMLHttpRequest();
            
            xhr.upload.addEventListener('progress', function(e) {
                if (e.lengthComputable) {
                    const percentComplete = (e.loaded / e.total) * 100;
                    progressFill.style.width = percentComplete + '%';
                    progressText.textContent = `Uploading... ${Math.round(percentComplete)}% (${formatBytes(e.loaded)} / ${formatBytes(e.total)})`;
                }
            });
            
            xhr.addEventListener('load', function() {
                if (xhr.status === 200) {
                    progressText.textContent = 'Upload complete! Redirecting...';
                    setTimeout(() => {
                        location.reload();
                    }, 1500);
                } else {
                    progressText.textContent = 'Upload failed!';
                    progressFill.style.background = '#ef4444';
                }
            });
            
            xhr.addEventListener('error', function() {
                progressText.textContent = 'Upload error!';
                progressFill.style.background = '#ef4444';
            });
            
            xhr.open('POST', this.action);
            xhr.send(formData);
        });
        
        function formatBytes(bytes) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }
        
        // Drag and drop functionality
        const fileInput = document.getElementById('fileInput');
        const uploadSection = document.querySelector('.upload-section');
        
        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
            uploadSection.addEventListener(eventName, preventDefaults, false);
        });
        
        function preventDefaults(e) {
            e.preventDefault();
            e.stopPropagation();
        }
        
        ['dragenter', 'dragover'].forEach(eventName => {
            uploadSection.addEventListener(eventName, highlight, false);
        });
        
        ['dragleave', 'drop'].forEach(eventName => {
            uploadSection.addEventListener(eventName, unhighlight, false);
        });
        
        function highlight(e) {
            uploadSection.style.background = '#ecfdf5';
            uploadSection.style.borderColor = 'var(--success-light)';
            uploadSection.style.transform = 'scale(1.02)';
        }
        
        function unhighlight(e) {
            uploadSection.style.background = '#f0fdf4';
            uploadSection.style.borderColor = 'var(--success)';
            uploadSection.style.transform = 'scale(1)';
        }
        
        uploadSection.addEventListener('drop', handleDrop, false);
        
        function handleDrop(e) {
            const dt = e.dataTransfer;
            const files = dt.files;
            fileInput.files = files;
            
            if (files.length > 0) {
                const fileCount = files.length;
                const totalSize = Array.from(files).reduce((sum, file) => sum + file.size, 0);
                alert(`Ready to upload ${fileCount} file(s) (${formatBytes(totalSize)} total)`);
            }
        }
    </script>
</body>
</html>z7<html><body><h1>Error</h1><p>Failed to list directory: ú</p></body></html>)r•   rf   rg   Úrelpathr©   ÚsepÚlistdirru   ÚisfileÚgetsizer5   r˜   ÚsortÚhtmlÚescapeÚ_generate_breadcrumbsÚdirnameÚrstripÚurllibÚparseÚquoteÚlstripÚ_format_file_sizerj   rY   r.   r~   Ústr) r   Úcurrent_pathÚrelative_pathÚdisplay_pathÚfilesÚdirectoriesÚitemÚ	item_pathrt   Úescaped_directoryÚescaped_display_pathÚbreadcrumbsÚhtml_contentÚparent_relativeÚencoded_parentÚcurrent_dir_encodedÚcurrent_dir_paramÚ
server_urlr½   Úescaped_dirnameÚdir_pathÚencoded_dirnamerP   Úescaped_filenamerz   Úencoded_filenameÚsize_strry   ÚiconÚtotal_itemsrO   Ú	error_msgr   r   r    Ú_generate_directory_list_htmlï  s&  


€


  E � �»  E � �»  
Kÿÿ
ý"
ÿÿü	







ÿÿÿýü	÷óïê)×)×0Ð0Ð<Ä<ÄG¹G¹P°P°_¡_¡i—i—  *€þz1FileDownloadHandler._generate_directory_list_htmlc           	      C   s®   |dkrdS dg}|  d¡ d¡}d}t|ƒD ]:\}}|sq||r%d| n|7 }tj |¡}t |¡}|t|ƒd krE| 	d|› d�¡ q| 	d|› d|› d	�¡ qd
 
|¡S )zÒ
        Generate breadcrumb navigation HTML.
        
        Args:
            relative_path (str): The current relative path
            
        Returns:
            str: HTML breadcrumb navigation
        rœ   uH   <a href="?" style="text-decoration: none; color: #0066cc;">ðŸ�  Root</a>r"   r<   u    <span style="color: #333;">ðŸ“� z</span>z<a href="?dir=u6   " style="text-decoration: none; color: #0066cc;">ðŸ“� z</a>u'    <span style="color: #999;">â†’</span> )rX   rW   Ú	enumerater¿   rÀ   rÁ   rº   r»   r.   r5   ru   )	r   rÆ   rÎ   ÚpartsrÅ   ÚiÚpartÚencoded_pathÚescaped_partr   r   r    r¼     s   


z)FileDownloadHandler._generate_breadcrumbsc                 C   s   t  |¡\}}|p
dS )z¬
        Get the MIME type for a file.
        
        Args:
            filepath (str): Path to the file
            
        Returns:
            str: MIME type
        zapplication/octet-stream)Ú	mimetypesÚ
guess_type)r   ÚfilepathÚ	mime_typer`   r   r   r    Ú_get_mime_type%  s   
z"FileDownloadHandler._get_mime_typec              
   Ã   sî   �zTt j |j d¡¡}t j |j¡}d|v r.|d r.|d d }|  ||j¡I dH  W dS d|v rK|d d r>|d d nd}|  	|¡I dH  W dS |  	d¡I dH  W dS  t
yv } z|  ddt|ƒ› �¡I dH  W Y d}~dS d}~ww )	z›
        Handle GET requests for file listing, directory browsing, and downloads.
        
        Args:
            event: The HTTP request event
        ÚasciiÚfiler   NÚdirr"   éô  zInternal Server Error: )r¿   rÀ   ÚurlparseÚtargetrG   Úparse_qsÚqueryÚ_serve_fileÚheadersÚ_serve_directory_listr~   Ú_serve_errorrÄ   )r   ÚeventÚ	url_partsÚquery_paramsrz   rÖ   rO   r   r   r    Údo_GET2  s   €*€ÿzFileDownloadHandler.do_GETc              
   Ã   s’   �z&t j |j d¡¡}t j |j¡}| ddg¡d }|  ||¡I dH  W dS  t	yH } z|  
ddt|ƒ› �¡I dH  W Y d}~dS d}~ww )zy
        Handle POST requests for file uploads.
        
        Args:
            event: The HTTP request event
        rê   rì   r"   r   Nrí   zUpload Error: )r¿   rÀ   rî   rï   rG   rð   rñ   ÚgetÚ_handle_file_uploadr~   rõ   rÄ   )r   rö   r÷   rø   Ú
target_dirrO   r   r   r    Údo_POSTN  s   €*€ÿzFileDownloadHandler.do_POSTc              
   Ã   s�  �zš|   |¡}|rtj |¡s|  dd¡I dH  W dS d}d}|jD ]\}}| ¡ dkr2| d¡}q"| ¡ dkr?t| d¡ƒ}q"|rG| 	d¡sS|  dd¡I dH  W dS |sa|  dd	¡I dH  W dS t
 d
|¡}|su|  dd¡I dH  W dS | d¡ d¡}	d|	› � d¡}
d|	› d� d¡}|  ||
||¡I dH  W dS  tyÇ } z |  d|› �¡I dH  |  ddt|ƒ› �¡I dH  W Y d}~dS d}~ww )zë
        Handle multipart file upload with memory-efficient streaming.
        
        Args:
            event: The HTTP request event
            target_dir (str): Target directory for upload (relative to download_directory)
        é�  zInvalid target directoryNs   content-typerê   s   content-lengthzmultipart/form-dataz.Only multipart/form-data uploads are supportedzContent-Length header requiredzboundary=([^;]+)z Missing boundary in Content-Typer<   r¥   z--z[UPLOAD-ERROR] rí   zUpload processing error: )r±   rf   rg   r˜   rõ   ró   rY   rG   Úintr?   rZ   r[   r]   rX   ÚencodeÚ_process_multipart_uploadr~   r(   rÄ   )r   rö   rü   Úsafe_target_pathÚcontent_typeÚcontent_lengthÚnameÚvalueÚboundary_matchÚboundaryr   Úend_boundary_bytesrO   r   r   r    rû   b  sB   €
€*€þz'FileDownloadHandler._handle_file_uploadc              
   Ã   s¸  �g }d}d}d}|| j krtd|› d| j › d�ƒ‚t|||| j| j| j| jd�}	zl||k r~|| }
t||
ƒ}|  |¡I dH }|sBn<|t	|ƒ7 }|	 
|¡I dH }| |¡ |d	krz|| d
 }|d dkrz|  d|d›d|d›d|d›d�¡I dH  ||k s.|	 ¡ I dH }| |¡ |  ||¡I dH  W dS  tyÈ } z'|  d|› �¡I dH  |	 ¡ I dH  |  ddt|ƒ› �¡I dH  W Y d}~dS d}~w tyõ } z"|  d|› �¡I dH  |	 ¡ I dH  |  dd¡I dH  W Y d}~dS d}~w t�y( } z'|  d|› �¡I dH  |	 ¡ I dH  |  ddt|ƒ› �¡I dH  W Y d}~dS d}~w t�y[ } z'|  d|› �¡I dH  |	 ¡ I dH  |  ddt|ƒ› �¡I dH  W Y d}~dS d}~ww )aJ  
        Process multipart upload data with streaming to avoid memory issues.
        
        Args:
            target_path (str): Safe target directory path
            boundary_bytes (bytes): Multipart boundary
            end_boundary_bytes (bytes): End boundary
            content_length (int): Total content length
        r   i   r   zUpload too large: r|   rn   )r   r   r   r   Né    r
   z[UPLOAD-PROGRESS] ú.1fú% (r‚   rœ   rƒ   z[UPLOAD-VALIDATION] rþ   zUpload validation failed: z[UPLOAD-MEMORY] Out of memory: i�  z&Upload too large - insufficient memoryz[UPLOAD-DISK] Disk error: iû  zServer storage error: z![UPLOAD-ERROR] Unexpected error: rí   zUpload processing failed: )r–   r0   r   r   r   r   r   ÚminÚ_read_upload_chunkr.   r8   r1   r(   r‰   Ú_send_upload_success_responserŒ   rõ   rÄ   ÚMemoryErrorrw   r~   )r   r   r   r	  r  Úuploaded_filesÚbytes_processedÚ
chunk_sizer   Ú	processorÚ	remainingÚ	read_sizer6   Ú
file_infosÚprogressÚfinal_filesrO   r   r   r    r  “  sn   €

ù


*í
*€ €*€*€ýz-FileDownloadHandler._process_multipart_uploadc              
   Ã   s  �z:t j| j ¡ dd�I dH }t|tjƒr#|jr |jd|… W S dW S t|tjƒr,W dS |  	dt
|ƒ› �¡I dH  W dS  t jyO   |  	d¡I dH  tdƒ‚ tyl } z|  	d|› �¡I dH  td	|› �ƒ‚d}~w ty‰ } z|  	d
|› �¡I dH  td|› �ƒ‚d}~ww )zQ
        Read a chunk of upload data from the HTTP request with timeout.
        g      >@)ÚtimeoutNr   z&[UPLOAD-ERROR] Unexpected event type: z*[UPLOAD-ERROR] Timeout reading upload dataz$Upload timeout - connection too slowz![UPLOAD-ERROR] Connection error: zUpload connection lost: z$[UPLOAD-ERROR] Error reading chunk: zError reading upload data: )ÚasyncioÚwait_forÚ_wrapperÚ
next_eventÚ
isinstanceÚh11ÚDatar   ÚEndOfMessager(   ÚtypeÚTimeoutErrorrw   ÚConnectionErrorr~   )r   rt   rö   rO   r   r   r    r  â  s*   €€€þz&FileDownloadHandler._read_upload_chunkc              
   Ã   s^  �z•|| j kr
d}ntj || j ¡ tjd¡}dtj |¡› dt	|ƒ› d�}|D ]}|  
|d ¡}|dt |d ¡› d	|› d
�7 }q(|dtj |¡ d 7 }| d¡}|  ¡ }| dd d¡fdtt	|ƒƒ d¡fg¡ tjd|d�}	| j |	¡I dH  | j tj|d�¡I dH  | j t ¡ ¡I dH  W dS  ty® }
 z|  d|
› �¡I dH  ‚ d}
~
ww )zÆ
        Send a success response after file upload.
        
        Args:
            uploaded_files (list): List of uploaded file info
            target_path (str): Target directory path
        r"   rœ   zv<!DOCTYPE html>
<html>
<head>
    <title>Upload Successful</title>
    <meta http-equiv="refresh" content="3;url=?dir=u‰   ">
</head>
<body style="font-family: Arial, sans-serif; margin: 40px;">
    <h1>âœ… Upload Successful!</h1>
    <p>Successfully uploaded z file(s):</p>
    <ul>
rt   u           <li>ðŸ“„ <strong>rP   z</strong> (z)</li>
z`    </ul>
    <p>Redirecting back to directory listing in 3 seconds...</p>
    <p><a href="?dir=zD">Click here if not redirected automatically</a></p>
</body>
</html>rD   úContent-Typeútext/html; charset=utf-8rê   úContent-LengthéÈ   ©Ústatus_coderó   N©r   z/[UPLOAD-ERROR] Error sending success response: )r•   rf   rg   r´   r©   rµ   r¿   rÀ   rÁ   r.   rÃ   rº   r»   r   Úbasic_headersr1   rÄ   r   ÚResponser  Úsendr!  r"  r~   r(   )r   r  r   Úredirect_pathrÏ   r7   rÚ   Úbodyró   ÚresponserO   r   r   r    r  ý  sB   €

üø"
þþ
þ€þz1FileDownloadHandler._send_upload_success_responsec                 C   sR   |dkr|d d›d�S |dkr|d d›d�S |dkr$|d d›d�S |› d�S )	z*Format file size in human-readable format.i   @r  z GBr   ú MBé   z KBr,   r   )r   rt   r   r   r    rÃ   4  s   
z%FileDownloadHandler._format_file_sizec              
   Ã   sP  �z…|   |¡}|s|  dd¡I dH  W dS tj |¡s'|  dd¡I dH  W dS tj |¡s9|  dd¡I dH  W dS |  |¡}| d¡}|  ¡ }| 	dd	 d
¡fdt
t|ƒƒ d
¡fg¡ tjd|d�}| j |¡I dH  | j tj|d�¡I dH  | j t ¡ ¡I dH  W dS  ty§ } z|  ddt
|ƒ› �¡I dH  W Y d}~dS d}~ww )z§
        Serve the HTML directory listing page.
        
        Args:
            dir_path (str): The directory path to list (relative to download_directory)
        rþ   zInvalid directory pathNé”  zDirectory not foundzNot a directoryrD   r&  r'  rê   r(  r)  r*  r,  rí   z!Error generating directory list: )r±   rõ   rf   rg   rŠ   r˜   rÞ   r   r-  r1   rÄ   r.   r   r.  r  r/  r!  r"  r~   )r   rÖ   r¯   rÏ   r1  ró   r2  rO   r   r   r    rô   ?  s6   €


þ*€ÿz)FileDownloadHandler._serve_directory_listc                 Ã   sn  �|   |¡}|s|  dd¡I dH  dS tj |¡s$|  dd¡I dH  dS tj |¡s5|  dd¡I dH  dS �z^tj |¡}|  |¡}d}|rZ|D ]\}}| ¡ dkrY| 	d¡} nqHd	}	|d
 }
d}|r—| 
d¡r—z"|dd… }d|v rŠ| dd
¡\}}|r‚t|ƒ}	|rˆt|ƒ}
d}W n ttfy–   Y nw |
|	 d
 }|  ¡ }| d| d¡fdt|ƒ d¡fdddtj |¡› d� d¡fg¡ |dkrÚ| dd|	› d|
› d|› � d¡f¡ tj||d�}| j |¡I dH  d}d	}|}t|dƒ�‰}|	d	krÿ| |	¡ |d	k�rwt||ƒ}| |¡}|�snezB| j tj|d�¡I dH  |t|ƒ7 }|t|ƒ8 }|dk�rS|d d	k�rS|| d }|  d |d!›d"|d#›d|d#›d$�¡I dH  W n t �yq } z|  d%|d#›d&|› �¡I dH  ‚ d}~ww |d	k�sW d  ƒ n	1 �s‚w   Y  | j t !¡ ¡I dH  W dS  t �y¶ } z|  d'd(t|ƒ› �¡I dH  W Y d}~dS d}~ww ))a  
        Serve a specific file for download with support for large files.
        
        Args:
            file_path (str): The requested file path (can include directories)
            request_headers (dict): HTTP request headers for range support
        rþ   zInvalid filenameNr5  zFile not foundz
Not a files   rangerê   r   r<   r)  zbytes=é   ú-éÎ   r&  r(  )zAccept-Rangess   byteszContent-Dispositionzattachment; filename="r¥   zContent-Rangezbytes rœ   r*  i   Úrbr,  i  @r
  r
   z![FILE-SERVER] Download progress: r  r  r‚   rƒ   z%[FILE-SERVER] Error sending chunk at z bytes: rí   zError serving file: )"r±   rõ   rf   rg   rŠ   r·   r¸   ré   rY   rG   r?   rW   rÿ   r0   Ú
IndexErrorr-  r1   r   rÄ   rh   r5   r   r.  r  r/  rv   Úseekr  Úreadr!  r.   r(   r~   r"  )r   rz   Úrequest_headersr¯   Ú	file_sizerè   Úrange_headerr  r  Ú
start_byteÚend_byter+  Ú
range_specÚstartÚendr  ró   r2  r  Ú
bytes_sentÚbytes_remainingÚfr  r6   r  rO   r   r   r    rò   g  s¤   €
	

þ€þü&



*€€þ
î€û*€ÿzFileDownloadHandler._serve_filec              
   Ã   sæ   �zSd|› dt  |¡› d� d¡}|  ¡ }| dd d¡fdtt|ƒƒ d¡fg¡ tj||d	�}| j	 
|¡I d
H  | j	 
tj|d�¡I d
H  | j	 
t ¡ ¡I d
H  W d
S  tyr } z|  d|› �¡I d
H  W Y d
}~d
S d
}~ww )zš
        Serve an error response.
        
        Args:
            status_code (int): HTTP status code
            message (str): Error message
        z<html><body><h1>Error z</h1><p>r³   rD   r&  r'  rê   r(  r*  Nr,  zError serving error response: )rº   r»   r   r-  r1   rÄ   r.   r   r.  r  r/  r!  r"  r~   r(   )r   r+  Úmessager1  ró   r2  rO   r   r   r    rõ   Õ  s    €þ$€ÿz FileDownloadHandler._serve_error)Nr“   r	   Nr
   Nr�   r$   )rŽ   r�   r�   r‘   r!   r(   r±   rÞ   r¼   ré   rù   rý   rû   r  r  r  rÃ   rô   rò   rõ   Ú__classcell__r   r   r™   r    r’   ‹  s4    ÿ

7      $
1O7

(nr’   r“   r	   r
   c           
   
   ƒ   sb   �z‡ ‡‡‡‡fdd„}t || |d�}| ¡ I dH W S  ty0 }	 z
d|	fW  Y d}	~	S d}	~	ww )aD  
    Run the file download server from a target.
    
    Args:
        target: Server target configuration
        download_directory (str): Directory to serve files from
        log_callback: Logging callback function
        max_upload_size (int): Maximum total upload size per request in bytes (default: 2GB)
        max_file_size (int): Maximum individual file size in bytes (default: 500MB)
        allowed_extensions (set): Set of allowed file extensions (None = allow all)
        max_files_per_request (int): Maximum number of files per upload request (default: 100)
    c                      s   t ˆˆˆˆ ˆd�S )N©r–   r   r   r   )r’   r   ©r   r•   r   r   r–   r   r    Ú<lambda>þ  s    ûz-run_file_server_from_target.<locals>.<lambda>)Úlog_callbackN)r   Úserver~   )
rï   r•   rM  r–   r   r   r   Úhandler_factoryÚserverrO   r   rK  r    Úrun_file_server_from_targetî  s   €€ÿrQ  ú	127.0.0.1é�  Fc              
   Ã   s<  �z�zZd}|rdd„ }| r#t d|› d|› �ƒ t dtj | ¡› �ƒ nt d|› d|› d�ƒ |r4t dƒ t||tjƒ}	t|	| |||||d	�I dH \}
}|durVt d
|› �ƒ |
I dH  W n% tyi   t dƒ Y n t	y� } zt d
|› �ƒ W Y d}~nd}~ww W |
dur‹|
 
¡  t dƒ dS |
dur™|
 
¡  t dƒ w )aM  
    Run the file download server.
    
    Args:
        download_directory (str): Directory to serve files from
        host (str): Host to bind to
        port (int): Port to bind to
        debug (bool): Enable debug logging
        max_upload_size (int): Maximum total upload size per request in bytes (default: 2GB)
        max_file_size (int): Maximum individual file size in bytes (default: 500MB)
        allowed_extensions (set): Set of allowed file extensions (None = allow all)
        max_files_per_request (int): Maximum number of files per upload request (default: 100)
    Nc                 Ó   s   �t d| › �ƒ d S )Nz[FILE-SERVER] )r(   )r'   r   r   r    rM    s   €z%run_file_server.<locals>.log_callbackzStarting file server on r¢   zServing files from: z (no download directory)z+Debug mode enabled - verbose logging active)r•   rM  r–   r   r   r   zServer error: ú
Server stopped by userzServer stopped)r(   rf   rg   r—   r   r   Ú
SERVER_TCPrQ  ÚKeyboardInterruptr~   Úcancel)r•   ÚhostÚportÚdebugr–   r   r   r   rM  rï   Úserver_taskÚerrrO   r   r   r    Úrun_file_server
  sJ   €ù	€ÿ€þ
r]  c                  C   sl  ddl } ddl}| jd| jdd�}|jdddd	� |jd
dddd� |jddtddd� |jddddd� |jdtddd� |jdtddd� |jdtd d!d� |jd"td#d$� |jd%d&d'd(d)� | ¡ }|jr’t	j
 |j¡s}td*|j› d+�ƒ | d,¡ t	j
 |j¡s’td-|j› d.�ƒ | d,¡ |jd,k sœ|jd/kr©td0|j› �ƒ | d,¡ |jd1k r»td2|j› �ƒ | d,¡ |jd1k rÍtd3|j› �ƒ | d,¡ |jd,k rßtd4|j› �ƒ | d,¡ d}|j�rtƒ }|j d5¡D ]}| ¡ }| d6¡sýd6| }| | ¡ ¡ qîtd7ƒ td8ƒ |j�rtd9t	j
 |j¡› �ƒ ntd:ƒ td;|j› d<|j› �ƒ |j�r6td=ƒ td>|jd? d@›dA�ƒ tdB|jd? d@›dA�ƒ tdC|j› �ƒ |�rftdDdE t|ƒ¡› �ƒ ntdFƒ td8ƒ zt  t!|j|j|j|j|j|j||jdG�¡ W dS  t"�y–   tdHƒ Y dS  t#�yµ } ztdI|› �ƒ | d,¡ W Y d}~dS d}~ww )Jz/
    Main entry point for the file server.
    r   NzLAsysocks File Server - Secure file upload/download server with web interfaceu  
Examples:
  %(prog)s                                    # Start server on 127.0.0.1:8080 (no directory)
  %(prog)s /home/user/files                  # Serve files from directory
  %(prog)s /home/user/files --host 0.0.0.0  # Bind to all interfaces
  %(prog)s /home/user/files --port 9000      # Use custom port
  %(prog)s /home/user/files --debug          # Enable debug logging
  %(prog)s --help                           # Show this help message

Features:
  â€¢ Modern web interface with drag-and-drop uploads
  â€¢ Directory browsing with breadcrumb navigation
  â€¢ Real-time upload progress bars
  â€¢ Command-line examples (cURL, wget, PowerShell, Python)
  â€¢ Secure file handling with directory traversal protection
  â€¢ Large file streaming support
        )ÚdescriptionÚformatter_classÚepilogÚ	directoryr¤   z(Directory to serve files from (optional))ÚnargsÚhelpz--hostz-HrR  z$Host to bind to (default: 127.0.0.1))Údefaultrc  z--portz-prS  zPort to bind to (default: 8080))r#  rd  rc  z--debugz-dÚ
store_truez5Enable debug logging (shows detailed server activity))Úactionrc  z--max-upload-sizer“   z=Maximum total upload size per request in bytes (default: 2GB)z--max-file-sizer	   z6Maximum individual file size in bytes (default: 500MB)z--max-filesr
   z9Maximum number of files per upload request (default: 100)z--allowed-extensionszHComma-separated list of allowed file extensions (e.g., ".txt,.jpg,.pdf"))r#  rc  z	--versionz-vÚversionzAsysocks File Server 1.0.0)rf  rg  zError: Directory 'z' does not existr<   zError: 'z' is not a directoryiÿÿ  z-Error: Port must be between 1 and 65535, got r4  z8Error: max-upload-size must be at least 1024 bytes, got z6Error: max-file-size must be at least 1024 bytes, got z)Error: max-files must be at least 1, got r‚   ra   zAsysocks File Serverz2==================================================zDirectory: z"Directory: None (upload-only mode)zAddress: http://r¢   zDebug: EnabledzMax upload size: r   z.0fr3  zMax file size: zMax files per request: zAllowed extensions: ro   zAllowed extensions: AllrJ  rT  zFailed to start server: )$ÚargparseÚsysÚArgumentParserÚRawDescriptionHelpFormatterÚadd_argumentrÿ   rÄ   Ú
parse_argsra  rf   rg   rŠ   r(   Úexitr˜   rY  r–   r   Ú	max_filesr   ÚsetrW   rX   r?   ÚaddrY   r—   rX  rZ  ru   Úsortedr  Úrunr]  rV  r~   )rh  ri  ÚparserÚargsr   ÚextrO   r   r   r    ÚmainA  sê   ýýýüýüüüýý









ø
€þrw  Ú__main__)NNr“   r	   Nr
   )NrR  rS  Fr“   r	   Nr
   )r‘   r  rf   Úurllib.parser¿   rå   rº   r   ÚtempfilerZ   Úpathlibr   Úasysocks.unicomm.common.targetr   r   Ú0asysocks.unicomm.protocol.server.http.httpserverr   r   r   r’   rQ  r]  rw  rŽ   r   r   r   r    Ú<module>   sN     n          m
þ
þ7 
ÿ