
    RSiV                        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 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gZdej        v Z G d de
          Z G d de          Z G d de          Zej        dk    r6	 ddlZ ej                     edgz  Z G d de          ZdS # e$ r Y dS w xY wdS )aq  
This module contains the main FTPServer class which listens on a
host:port and dispatches the incoming connections to a handler.
The concurrency is handled asynchronously by the main process thread,
meaning the handler cannot block otherwise the whole server will hang.

Other than that we have 2 subclasses changing the asynchronous concurrency
model using multiple threads or processes.

You might be interested in these in case your code contains blocking
parts which cannot be adapted to the base async model or if the
underlying filesystem is particularly slow, see:

https://github.com/giampaolo/pyftpdlib/issues/197
https://github.com/giampaolo/pyftpdlib/issues/212

Two classes are provided:

 - ThreadingFTPServer
 - MultiprocessFTPServer

...spawning a new thread or process every time a client connects.

The main thread will be async-based and be used only to accept new
connections.
Every time a new connection comes in that will be dispatched to a
separate thread/process which internally will run its own IO loop.
This way the handler handling that connections will be free to block
without hanging the whole FTP server.
    N   )Acceptor)PREFIX)PREFIX_MPROC)config_logging)debug)is_logging_configured)logger)fork_processes	FTPServerThreadedFTPServerbsdc                   t    e Zd ZdZdZdZddZd Zd Ze	d	             Z
d
 Zd ZddZ	 ddZd Zd Zd ZdS )r   a  Creates a socket listening on <address>, dispatching the requests
    to a <handler> (typically FTPHandler class).

    Depending on the type of address specified IPv4 or IPv6 connections
    (or both, depending from the underlying system) will be accepted.

    All relevant session information is stored in class attributes
    described below.

     - (int) max_cons:
        number of maximum simultaneous connections accepted (defaults
        to 512). Can be set to 0 for unlimited but it is recommended
        to always have a limit to avoid running out of file descriptors
        (DoS).

     - (int) max_cons_per_ip:
        number of maximum connections accepted for the same IP address
        (defaults to 0 == unlimited).
    i   r   Nd   c                    t          j        | |           || _        || _        g | _        t          |d          r|                                 t          t          |dd                    r-|}|	                    d           | 
                    |           n|                     |           |                     |           dS )ao  Creates a socket listening on 'address' dispatching
        connections to a 'handler'.

         - (tuple) address_or_socket: the (host, port) pair on which
           the command channel will listen for incoming connections or
           an existent socket object.

         - (instance) handler: the handler class to use.

         - (instance) ioloop: a pyftpdlib.ioloop.IOLoop instance

         - (int) backlog: the maximum number of queued connections
           passed to listen(). If a connection request arrives when
           the queue is full the client may raise ECONNRESET.
           Defaults to 5.
        ioloopget_ssl_contextlistenNr   )r   __init__handlerbacklogip_maphasattrr   callablegetattrsetblocking
set_socketbind_af_unspecifiedr   )selfaddress_or_socketr   r   r   socks         E/home/jrussi/.local/lib/python3.11/site-packages/pyftpdlib/servers.pyr   zFTPServer.__init__W   s    " 	$v.... 7-.. 	&##%%%G-x>>?? 	8$DQOOD!!!!$$%6777G    c                     | S N r    s    r#   	__enter__zFTPServer.__enter__x   s    r$   c                 .    |                                   d S r&   )	close_all)r    argss     r#   __exit__zFTPServer.__exit__{   s    r$   c                 D    | j                                         dd         S )z>The address this server is listening on as a (ip, port) tuple.N   )socketgetsocknamer(   s    r#   addresszFTPServer.address~   s!     {&&((!,,r$   c                 4    t          | j        j                  S r&   )lenr   
socket_mapr(   s    r#   _map_lenzFTPServer._map_len   s    4;)***r$   c                 N    | j         sdS |                                 | j         k    S )z?Return True if the server is willing to accept new connections.T)max_consr6   r(   s    r#   _accept_new_conszFTPServer._accept_new_cons   s&    } 	44==??dm33r$   Fc                    d }t                      st          |rt          nt                     | j        j        r(| j        j        d         d| j        j        d         }nd }|rdnd}dt          v r t          | j        t                    r|d	z  }nNd
t          v r t          | j        t                    r|dz  }n%t          | j        t                    r|dz  }n|dz  }t          j        d|z              t          j        d| j        j                   t          j        d|           t          j        d || j                             t          j        d || j        j                             t$          j        dk    rt          j        d| j        j                   t          j        d || j                             t          j        d| j        pd           t          j        d| j        pd           t          j        d| j        j        pd           t          j        d| j        j                   t          j        d| j        j                   t5          | j        dd           rt          j        d| j        j                   t5          | j        dd           r!t          j        d| j        j                   d S d S ) Nc                     	 | j         dz   | j        j        z   S # t          $ r7 	 | j         dz   | j        z   cY S # t          $ r t	          |           cY cY S w xY ww xY w)N.)
__module__	__class____name__AttributeErrorstr)objs    r#   
get_fqnamez(FTPServer._log_start.<locals>.get_fqname   s    $~+cm.DDD! $ $ $$>C/#,>>>>% $ $ $s88OOOOO$$s)    
A8AAAAA)prefixr   z->z
prefork +  r   zmulti-threadMultiprocessFTPServerzmulti-processasynczunknown (custom class)zconcurrency model: zmasquerade (NAT) address: %szpassive ports: %sz
poller: %rzauthorizer: %rposixzuse sendfile(): %szhandler: %rzmax connections: %s	unlimitedzmax connections per ip: %sztimeout: %sz
banner: %rzmax login attempts: %rcertfilezSSL certfile: %rkeyfilezSSL keyfile: %r)r	   r   r   r   r   passive_ports__all__
issubclassr>   r   rG   r   r
   infomasquerade_addressr   r   
authorizerosnameuse_sendfiler8   max_cons_per_iptimeoutbannermax_login_attemptsr   rK   rL   )r    preforkrC   
pasv_portsmodels        r#   
_log_startzFTPServer._log_start   s   	$ 	$ 	$ %&& 	G '"E,,vFFFF<% 	*1---*2..JJ
 J '/R'))jN-/
 /
) ^#EE$//JN15
 5
/ _$EE	22 	.WEE--E)E1222*DL,K	
 	
 	
 	'444\::dk#:#:;;;%zz$,2I'J'JKKK7gL-t|/HIII]JJt|$<$<===*DM,H[III($*>*M+	
 	
 	
 	]DL$8$GKHHH\4<#6777-t|/NOOO4<T22 	DL+T\-BCCC4<D11 	BL*DL,@AAAAA	B 	Br$   Tr   c                    |o|}|dk    rIt           j        dk    r9|st          d          |r|                     d           t	          |           n|r|                                  t          | j        d          rdnd}t          j        d	|| j	        d
         | j	        d         t          j
                    fz             |r	 | j                            ||           n+# t          t          f$ r t          j        d           Y nw xY w|rQ|r9t          j        dt          j
                    |                                            |                                  dS dS | j                            ||           dS )a  Start serving.

        - (float) timeout: the timeout passed to the underlying IO
          loop expressed in seconds.

        - (bool) blocking: if False loop once and then return the
          timeout of the next scheduled call next to expire soonest
          (if any).

        - (bool) handle_exit: when True catches KeyboardInterrupt and
          SystemExit exceptions (generally caused by SIGTERM / SIGINT
          signals) and gracefully exits after cleaning up resources.
          Also, logs server start and stop.

        - (int) worker_processes: pre-fork a certain number of child
          processes before starting.
          Each child process will keep using a 1-thread, async
          concurrency model, handling multiple concurrent connections.
          If the number is None or <= 0 the number of usable cores
          available on this machine is detected and used.
          It is a good idea to use this option in case the app risks
          blocking for too long on a single function call (e.g.
          hard-disk is slow, long DB query on auth etc.).
          By splitting the work load over multiple processes the delay
          introduced by a blocking function call is amortized and divided
          by the number of worker processes.
        r   rI   z8'worker_processes' and 'blocking' are mutually exclusiveT)rZ   ssl_protocolzFTPS (FTP over SSL)FTPz+>>> starting %s server on %s:%s, pid=%i <<<r   zreceived interrupt signalz;>>> shutting down FTP server, pid=%i, %s open socket(s) <<<N)rS   rT   
ValueErrorr]   r   r   r   r
   rP   r2   getpidr   loopKeyboardInterrupt
SystemExitr6   r+   )r    rW   blockinghandle_exitworker_processeslogprotos          r#   serve_foreverzFTPServer.serve_forever   s   < &hq  RW%7%7  N    .---+,,,, 	OO t|^44!! 	
 	9dl1ot|A	DE	
 	
 	

  	09  (3333%z2 9 9 97888889 ! K)		        ! ! KWh/////s   C$ $%DDc                    d}d}	 |                      || | j                  }|j        sdS |d         }| j                            |           |                                 s|                                 dS | j        r9| j                            |          | j        k    r|	                                 dS 	 |
                                 |S # t          $ r |                                 Y dS w xY w# t          $ rm t          j        t          j                               ||                                 Y dS |&|| j        v r | j                            |           Y dS Y dS Y dS w xY w)z1Called when remote client initiates a connection.Nr   r   )r   r   	connectedr   appendr9   handle_max_consrV   counthandle_max_cons_per_iphandle	Exceptionhandle_errorr
   error	traceback
format_exccloseremove)r    r"   addrr   ips        r#   handle_acceptedzFTPServer.handle_accepted  s   *	'll4dklBBG$ aBKr""" ((** ''))) # ;$$R((4+???22444F      ' ' '$$&&&&&&'  	' 	' 	' L-//000"B$+$5$5""2&&&&&&  $5$5$5	'sH   $C4 A
C4 8>C4 9C C4 C1-C4 0C11C4 4AE+<%E+*E+c                     	  # t           $ r( t          j        t          j                               Y nw xY w|                                  dS )z)Called to handle any uncaught exceptions.N)rs   r
   ru   rv   rw   rx   r(   s    r#   rt   zFTPServer.handle_error=  sU    	1 	1 	1 	1L-//00000	1

s    /55c                 4    | j                                         S )zSStop serving and also disconnects all currently connected
        clients.
        )r   rx   r(   s    r#   r+   zFTPServer.close_allE  s     {  """r$   Nr   )F)NTTr   )r?   r=   __qualname____doc__r8   rV   r   r)   r-   propertyr2   r6   r9   r]   rk   r|   rt   r+   r'   r$   r#   r   r   ?   s         ( HO   B     - - X-+ + +4 4 48B 8B 8B 8Bv OPD0 D0 D0 D0L.' .' .'`  # # # # #r$   c                   h    e Zd ZdZdZdZdZdZddZd Z	d Z
d Zd	 Zd
 Zd ZddZd Zd Zd ZdS )_SpawnerBasez[Base class shared by multiple threads/process dispatcher.
    Not supposed to be used.
       Nr   c                     t                               | ||||           g | _        | j                            | j        | j        | j                  | _        d S )N)r   r   )_errback)	r   r   _active_tasksr   
call_everyrefresh_interval_refresh_tasksrt   _active_tasks_idler)r    r!   r   r   r   s        r#   r   z_SpawnerBase.__init__^  sl    #WVW 	 	
 	
 	
  #';#9#9!& $: $
 $
   r$   c                      t          d          )Nzmust be implemented in subclass)NotImplementedErrorr    r,   kwargss      r#   _start_taskz_SpawnerBase._start_taski  s    !"CDDDr$   c                     t          | j                  | j        k    r|                                  t          | j                  S r&   )r4   r   r8   r   r(   s    r#   r6   z_SpawnerBase._map_lenl  s?    t!""dm33
 !!!4%&&&r$   c                 P   | j         rt          j        dt          | j                    d           | j        5  g }| j         D ]A}|                                s|                     |           ,|                    |           B|| _         ddd           dS # 1 swxY w Y   dS dS )zhjoin() terminated tasks and update internal _tasks list.
        This gets called every X secs.
        zrefreshing tasks (z join() potentials)N)r   r
   r   r4   _lockis_alive
_join_taskrn   )r    newts      r#   r   z_SpawnerBase._refresh_tasksu  s     	)LS);%<%<       ) )+ & &A::<< &****

1%(") ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )	) 	)s   ABB Bc           	         | j                                         5 }||_         	 |                                 nI# t          $ r<}|j        t          j        k    r!t          d|            Y d}~ddd           dS  d}~ww xY w|j        }|j        j        }t          | dd          }|}|j
        s|j        j        r| j                                        s	 |j
        r ||           |j        j        rY |            }|j
        sG|j                                          |            }|r"t          j        t#          |d                     nd}|r
|||k    r|}n# t$          t&          f$ r | j                                         Y nt          $ r}t*          j        dk    r|j        dk    rt1          |j
                                                  D ]e}	 t5          j        |gg g d           # t          $ r= 	 t7          j        d	|j
        |                    |j
        |= n# t:          $ r Y nw xY wY bw xY wn Y d}~nd}~ww xY w|j
        s|j        j        r| j                                        ddd           dS # 1 swxY w Y   dS )
z8Serve handler's IO loop in a separate thread or process.z.call: %s._loop(); add_channel() returned EBADFNpoll_timeout)rW   r   nti6'  r   zdiscarding broken socket %r)r   factoryadd_channelOSErrorerrnoEBADFr   pollschedr   r5   _tasks_exitis_set	reheapifytimesleepminrd   re   setrS   rT   winerrorlistkeysselectr
   rP   KeyError)	r    r   r   errr   
sched_pollr   soonest_timeoutfds	            r#   _loopz_SpawnerBase._loop  s   [  "" J	;f#GN##%%%% 	 	 	9++ H$   FFFJ	; J	; J	; J	; J	; J	; J	; J	; 	 ;D*J"4>>L*O !5;%+\%85;j''))5;2;( 6_5555|* /*4*,,  &0 D"L22444.8jllO. D $
3+B+B C C C*.2 $ ;+3.==.:O; *:6 % % % JNN$$$$$    w$3<5+@+@"&v'8'='='?'?"@"@ ) )B) &rdBA > > > >#* 	) 	) 	)!)$*K(E(.(9"(=%& %& %& )/(9"(=(='/ !) !) !)$(D!)	)) ) ) ) ) )	7 !5;%+\%85;j''))5;+J	; J	; J	; J	; J	; J	; J	; J	; J	; J	; J	; J	; J	; J	; J	; J	; J	; J	;s   J8J
A>%A9'J8A99A>>AJA:EJ*IJ		IAI G/.I /
H6	:(H#"H6	#
H0-H6	/H00H6	3I 5H6	6I ;J I0JJ	Jc                    t                               | ||          }|| j                            |j                   |                     | j        |fd          }t          |          |_        |	                                 t          |d          r|                                 | j        5  | j                            |           d d d            d S # 1 swxY w Y   d S d S )Nftpd)targetr,   rT   pid)r   r|   r   
unregister_filenor   r   reprrT   startr   rx   r   r   rn   )r    r"   rz   r   r   s        r#   r|   z_SpawnerBase.handle_accepted  s&   ++D$== K""7?333  z
 !  A $ZZAFGGIII q%     - -"))!,,,- - - - - - - - - - - - - - - - - - s   /CCCc                 :    t                               |            d S r&   )r   r]   r(   s    r#   r]   z_SpawnerBase._log_start  s    T"""""r$         ?Tc                    | j                                          |r|o|}|r|                                  	 | j                            ||           n# t
          t          f$ r Y nw xY w|r?|r't          j        d| 	                                           | 
                                 d S d S | j                            ||           d S )Nz4>>> shutting down FTP server (%s active workers) <<<)r   clearr]   r   rc   rd   re   r
   rP   r6   r+   )r    rW   rf   rg   ri   s        r#   rk   z_SpawnerBase.serve_forever  s    
 	0*(C "!!!  (3333%z2    ! KN        ! ! KWh/////s   A A'&A'c                     t          |d          rlt          j        d|           	 t          s|                                 d S t          j        |j        t          j	                   d S # t          $ r Y d S w xY wd S )N	terminatezterminate()ing task )r   r
   r   _BSDr   rS   killr   signalSIGKILLProcessLookupErrorr    r   s     r#   _terminate_taskz_SpawnerBase._terminate_task  s    1k"" 	L555666	 3KKMMMMM
 GAE6>22222%   	 	s   A, $A, ,
A:9A:c                     t          j        d|           |                    | j                   |                                rt          j        d|| j                   d S d S )Nzjoin()ing task z$task %r remained alive after %r secs)r
   r   joinjoin_timeoutr   warningr   s     r#   r   z_SpawnerBase._join_task  sr    ,q,,---	t !!!::<< 	N64;L    	 	r$   c                 l   | j                                          | j                                         | j        5  | j        D ]}|                     |           | j        D ]}|                     |           | j        d d = d d d            n# 1 swxY w Y   t          	                    |            d S r&   )
r   cancelr   r   r   r   r   r   r   r+   r   s     r#   r+   z_SpawnerBase.close_all  s    ''))) 	
Z 	& 	&' ( ($$Q''''' # #"""""111%	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	D!!!!!s   A	BBBr   )r   TT)r?   r=   r   r   r   r   r   r   r   r   r6   r   r   r|   r]   rk   r   r   r+   r'   r$   r#   r   r   Q  s          LEE	
 	
 	
 	
E E E' ' ') ) )&L; L; L;\- - -*# # #0 0 0 0(    " " " " "r$   r   c                   X    e Zd ZdZdZ ej                    Z ej                    Z	d Z
dS )r   zuA modified version of base FTPServer class which spawns a
    thread every time a new connection is established.
    r   c                 $    t          j        |i |S r&   )	threadingThreadr   s      r#   r   zThreadedFTPServer._start_task3  s    0000r$   N)r?   r=   r   r   r   r   Lockr   Eventr   r   r'   r$   r#   r   r   (  sR          LINEIOE1 1 1 1 1r$   rI   rG   c                       e Zd ZdZ ej                    Z ej                    Z ej	                    dk    r ej
        d          Zn ej
                    Zd ZdS )rG   zA modified version of base FTPServer class which spawns a
            process every time a new connection is established.
            
forkserverfork)methodc                 &     | j         j        |i |S r&   )_mp_contextProcessr   s      r#   r   z!MultiprocessFTPServer._start_taskQ  s    /t'/@@@@r$   N)r?   r=   r   r   multiprocessingr   r   r   r   get_start_methodget_contextr   r   r'   r$   r#   rG   rG   B  s          )O(**E)O)++E 0/11\AA9o9HHH9o9;;A A A A Ar$   )r   r   rS   r   r   sysr   r   rv   r   r   ri   r   r   r   r   r	   r
   rZ   r   rN   platformr   r   r   r   rT   r   r   rG   rs   r'   r$   r#   <module>r      sB  
 >  				   



                                        & & & & & &       # # # # # #+
,J# J# J# J# J# J# J# J#dT" T" T" T" T"9 T" T" T"n1 1 1 1 1 1 1 1 7gA
 	+,,	A 	A 	A 	A 	AL 	A 	A 	A 	A 	A     s   B? ?CC