B
    îq\Ú  ã               @   s  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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 ddlmZmZmZ dd	lmZmZ dd
lmZ ddl m!Z!m"Z" ddgZ#ddgZ$G dd„ dƒZ%G dd„ dƒZ&dS )é    N)Ú
urlunparse)Úlogé   )ÚSAMP_STATUS_OK)Ú__profile_version__)ÚSAMPWarningÚSAMPHubErrorÚSAMPProxyError)Úinternet_onÚServerProxyPoolÚ_HubAsClient)Úread_lockfileÚcreate_lock_file)ÚThreadingXMLRPCServer)ÚWebProfileXMLRPCServerÚweb_profile_text_dialogÚSAMPHubServerÚWebProfileDialogÚ.zSAMPHubServer.*c               @   s  e Zd ZdZdˆd	d
„Ze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dd„ Zdd„ Zdd „ Zd‹d"d#„Zed$d%„ ƒZd&d'„ Zd(d)„ Zd*d+„ ZdŒd,d-„Zed.d/„ ƒZd0d1„ Zd2d3„ Zd4d5„ Zd6d7„ Zd8d9„ Zd:d;„ Zd<d=„ Zd>d?„ Zd@dA„ Z dBdC„ Z!dDdE„ Z"dFdG„ Z#dHdI„ Z$dJdK„ Z%dLdM„ Z&dNdO„ Z'dPdQ„ Z(dRdS„ Z)dTdU„ Z*dVdW„ Z+e,dXdY„ ƒZ-dZd[„ Z.d\d]„ Z/d^d_„ Z0d`da„ Z1dbdc„ Z2ddde„ Z3dfdg„ Z4dhdi„ Z5djdk„ Z6dldm„ Z7dndo„ Z8dpdq„ Z9drds„ Z:dtdu„ Z;dvdw„ Z<ddxdy„Z=dzd{„ Z>d|d}„ Z?d~d„ Z@dŽd‚dƒ„ZAd„d…„ ZBd†d‡„ ZCdS )r   aÇ
  
    SAMP Hub Server.

    Parameters
    ----------
    secret : str, optional
        The secret code to use for the SAMP lockfile. If none is is specified,
        the :func:`uuid.uuid1` function is used to generate one.

    addr : str, optional
        Listening address (or IP). This defaults to 127.0.0.1 if the internet
        is not reachable, otherwise it defaults to the host name.

    port : int, optional
        Listening XML-RPC server socket port. If left set to 0 (the default),
        the operating system will select a free port.

    lockfile : str, optional
        Custom lockfile name.

    timeout : int, optional
        Hub inactivity timeout. If ``timeout > 0`` then the Hub automatically
        stops after an inactivity period longer than ``timeout`` seconds. By
        default ``timeout`` is set to 0 (Hub never expires).

    client_timeout : int, optional
        Client inactivity timeout. If ``client_timeout > 0`` then the Hub
        automatically unregisters the clients which result inactive for a
        period longer than ``client_timeout`` seconds. By default
        ``client_timeout`` is set to 0 (clients never expire).

    mode : str, optional
        Defines the Hub running mode. If ``mode`` is ``'single'`` then the Hub
        runs using the standard ``.samp`` lock-file, having a single instance
        for user desktop session. Otherwise, if ``mode`` is ``'multiple'``,
        then the Hub runs using a non-standard lock-file, placed in
        ``.samp-1`` directory, of the form ``samp-hub-<UUID>``, where
        ``<UUID>`` is a unique UUID assigned to the hub.

    label : str, optional
        A string used to label the Hub with a human readable name. This string
        is written in the lock-file assigned to the ``hub.label`` token.

    web_profile : bool, optional
        Enables or disables the Web Profile support.

    web_profile_dialog : class, optional
        Allows a class instance to be specified using ``web_profile_dialog``
        to replace the terminal-based message with e.g. a GUI pop-up. Two
        `queue.Queue` instances will be added to the instance as attributes
        ``queue_request`` and ``queue_result``. When a request is received via
        the ``queue_request`` queue, the pop-up should be displayed, and a
        value of `True` or `False` should be added to ``queue_result``
        depending on whether the user accepted or refused the connection.

    web_port : int, optional
        The port to use for web SAMP. This should not be changed except for
        testing purposes, since web SAMP should always use port 21012.

    pool_size : int, optional
        The number of socket connections opened to communicate with the
        clients.
    Nr   ÚsingleÚ TéR  é   c             C   sD  t t ¡ ƒ| _d| _|| _d | _|| _|| _|| _	|| _
|| _|| _|| _|	| _|
| _|| _d | _i | _d | _d | _d | _d| _tƒ rÌy(t ¡ | _t | jp | j| jp¨d¡ W n tjk
rÊ   d| _Y nX t ¡ | _d | _d | _ d | _!g | _"d | _#i | _$d| _%|| _&|  '¡ | _(d| _)i | _*i | _+i | _,i | _-i | _.i | _/d| _0d S )NFz	127.0.0.1r   r   éÿÿÿÿ)1ÚstrÚuuidÚuuid1Ú_idÚ_is_runningÚ_customlockfilenameÚ	_lockfileÚ_addrÚ_portÚ_modeÚ_labelÚ_timeoutÚ_client_timeoutÚ
_pool_sizeÚ_web_profileÚ_web_profile_dialogÚ	_web_portÚ_web_profile_serverÚ_web_profile_callbacksÚ_web_profile_requests_queueÚ_web_profile_requests_resultÚ_web_profile_requests_semaphoreÚ
_host_namer
   ÚsocketZgetfqdnZgetaddrinfoÚerrorÚ	threadingZLockÚ_thread_lockÚ_thread_runÚ_thread_hub_timeoutÚ_thread_client_timeoutÚ_launched_threadsÚ_last_activity_timeÚ_client_activity_timeÚ_hub_msg_id_counterÚ_hub_secret_code_customizedÚ_create_secret_codeÚ_hub_secretÚ_hub_public_idÚ_private_keysÚ	_metadataÚ
_mtype2idsÚ
_id2mtypesÚ_xmlrpc_endpointsÚ_sync_msg_ids_heapÚ_client_id_counter)ÚselfÚsecretÚaddrZportZlockfileÚtimeoutZclient_timeoutÚmodeZlabelÚweb_profileZweb_profile_dialogZweb_portZ	pool_size© rM   ú/lib/python3.7/site-packages/astropy/samp/hub.pyÚ__init__b   sZ    


zSAMPHubServer.__init__c             C   s   | j S )z$
        The unique hub ID.
        )r   )rG   rM   rM   rN   Úidº   s    zSAMPHubServer.idc             C   sä   |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | j	d	¡ |  | j
d
¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ d S )Nzsamp.hub.pingzsamp.hub.setXmlrpcCallbackzsamp.hub.registerzsamp.hub.unregisterzsamp.hub.declareMetadatazsamp.hub.getMetadatazsamp.hub.declareSubscriptionszsamp.hub.getSubscriptionszsamp.hub.getRegisteredClientszsamp.hub.getSubscribedClientszsamp.hub.notifyzsamp.hub.notifyAllzsamp.hub.callzsamp.hub.callAllzsamp.hub.callAndWaitzsamp.hub.reply)Úregister_functionÚ_pingÚ_set_xmlrpc_callbackÚ	_registerÚ_unregisterÚ_declare_metadataÚ_get_metadataÚ_declare_subscriptionsÚ_get_subscriptionsÚ_get_registered_clientsÚ_get_subscribed_clientsÚ_notifyÚ_notify_allÚ_callÚ	_call_allÚ_call_and_waitÚ_reply)rG   ÚserverrM   rM   rN   Ú_register_standard_apiÁ   s     z$SAMPHubServer._register_standard_apic             C   sò   |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | j	d	¡ |  | j
d
¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ |  | jd¡ d S )Nzsamp.webhub.pingzsamp.webhub.unregisterzsamp.webhub.declareMetadatazsamp.webhub.getMetadataz samp.webhub.declareSubscriptionszsamp.webhub.getSubscriptionsz samp.webhub.getRegisteredClientsz samp.webhub.getSubscribedClientszsamp.webhub.notifyzsamp.webhub.notifyAllzsamp.webhub.callzsamp.webhub.callAllzsamp.webhub.callAndWaitzsamp.webhub.replyzsamp.webhub.registerz!samp.webhub.allowReverseCallbackszsamp.webhub.pullCallbacks)rQ   rR   rU   rV   rW   rX   rY   rZ   r[   r\   r]   r^   r_   r`   ra   Ú_web_profile_registerÚ"_web_profile_allowReverseCallbacksÚ_web_profile_pullCallbacks)rG   rb   rM   rM   rN   Ú_register_web_profile_apiÖ   s"    z'SAMPHubServer._register_web_profile_apic             C   s€   t | jp| j| jpdftddd| _d}| jj ¡ d | _d | jpH| j| j¡}t	||ddddfƒ| _
| j ¡  |  | j¡ d S )	Nr   FT)ÚlogRequestsÚ
allow_noneZhttpr   z{0}:{1}r   )r   r!   r0   r"   r   Ú_serverr1   ÚgetsocknameÚformatr   Ú_urlÚ register_introspection_functionsrc   )rG   ZprotrI   rM   rM   rN   Ú_start_standard_serverì   s    
z$SAMPHubServer._start_standard_serverc             C   sØ   t  d¡| _t  d¡| _t  d¡| _| jd k	rB| j| j_| j| j_yNtd| j	ft
ddd| _| jj ¡ d | _	| j ¡  |  | j¡ t
 d¡ W nB tjk
rÒ   t
 d | j	¡t¡ d| _d | _d | _d | _Y nX d S )Nr   Ú	localhostFT)rh   ri   z0Hub set to run with Web Profile support enabled.zLPort {0} already in use. Impossible to run the Hub with Web Profile support.)ÚqueueÚQueuer-   r.   r/   r)   Úqueue_requestÚqueue_resultr   r*   r   r+   r1   rk   rn   rg   Úinfor2   Zwarningrl   r   r(   )rG   rM   rM   rN   Ú_start_web_profile_serverù   s,    




z'SAMPHubServer._start_web_profile_serverc             C   sj   g }x | j D ]}| ¡ s| |¡ qW x|D ]}| j  |¡ q,W tj||||d}| ¡  | j  |¡ d S )N)ÚgroupÚtargetÚnameÚargs)r8   Úis_aliveÚappendÚremover3   ÚThreadÚstart)rG   rw   rx   ry   rz   r}   ÚtrM   rM   rN   Ú_launch_thread  s    
zSAMPHubServer._launch_threadc             C   s    x| j D ]}|j|d qW d S )N)rJ   )r8   Újoin)rG   rJ   r€   rM   rM   rN   Ú_join_launched_threads(  s    z$SAMPHubServer._join_launched_threadsc          	   C   sŒ   | j dkrd S t ¡ }xp| jr†t d¡ t ¡ }|| dkr| j8 | jd k	rv|| j | j krvt dt¡ |  	¡  d S W d Q R X |}qW d S )Nr   gš™™™™™©?g      ð?z&Timeout expired, Hub is shutting down!)
r%   Útimer   Úsleepr4   r9   ÚwarningsÚwarnr   Ústop)rG   ÚlastÚnowrM   rM   rN   Ú_timeout_test_hub,  s    


zSAMPHubServer._timeout_test_hubc             C   s    | j dkrd S t ¡ }x„| jršt d¡ t ¡ }|| dkrxV| j ¡ D ]H}|| j|  | j krH|| jkrHt d 	|¡t
¡ |  |¡ |  |¡ qHW |}qW d S )Nr   gš™™™™™©?g      ð?zClient {} timeout expired!)r&   r„   r   r…   r:   ÚkeysÚ_hub_private_keyr†   r‡   rl   r   Ú_notify_disconnectionrU   )rG   r‰   rŠ   Úprivate_keyrM   rM   rN   Ú_timeout_test_client?  s     



z"SAMPHubServer._timeout_test_clientc             C   sL   |dkr| j |Ž S |dkr$| j|Ž S |dkr6| j|Ž S |dkrH| j|Ž S d S )Nzsamp.client.receiveCallzsamp.client.receiveNotificationzsamp.client.receiveResponsezsamp.app.ping)Ú_receive_callÚ_receive_notificationÚ_receive_responserR   )rG   Úmethodrz   rM   rM   rN   Ú_hub_as_client_request_handlerS  s    


z,SAMPHubServer._hub_as_client_request_handlerc             C   sn   d| j dd| jd dœ}|  | j¡}|d | _|d | _|  | j| j¡ |  | j|¡ |  | ji i dœ¡ d S )	NzAstropy SAMP HubzThe Astropy Collaborationz&http://docs.astropy.org/en/stable/sampz
/samp/icon)z	samp.namezsamp.description.textzauthor.namezsamp.documentation.urlzsamp.icon.urlzsamp.self-idzsamp.private-key)zsamp.app.pingzx-samp.query.by-meta)	r$   rm   rT   r>   r?   r   rS   rV   rX   )rG   Zhub_metadataÚresultrM   rM   rN   Ú_setup_hub_as_client]  s    

z"SAMPHubServer._setup_hub_as_clientFc             C   s   | j rtdƒ‚| jdk	r tdƒ‚| jr.|  ¡  |  ¡  t| j| j| j	| j
d| _|  ¡  |  ¡  |  ¡  t d¡ |rŒ| j rŒ| j ¡  d| _dS )a<  
        Start the current SAMP Hub instance and create the lock file. Hub
        start-up can be blocking or non blocking depending on the ``wait``
        parameter.

        Parameters
        ----------
        wait : bool
            If `True` then the Hub process is joined with the caller, blocking
            the code flow. Usually `True` option is used to run a stand-alone
            Hub in an executable script. If `False` (default), then the Hub
            process runs in a separated thread. `False` is usually used in a
            Python shell.
        zHub is already runningNz&Hub is not running but lockfile is set)ZlockfilenamerK   Zhub_idZ
hub_paramszHub started)r   r   r    r(   rv   ro   r   r   r#   rP   ÚparamsÚ_update_last_activity_timer—   Ú_start_threadsr   ru   r5   r‚   )rG   ÚwaitrM   rM   rN   r   n  s"    



zSAMPHubServer.startc             C   sD   i }| j |d< | j|d< t|d< | j|d< | jp:d | j¡|d< |S )zG
        The hub parameters (which are written to the logfile)
        zsamp.secretzsamp.hub.xmlrpc.urlzsamp.profile.versionzhub.idzHub {0}z	hub.label)r>   rm   r   rP   r$   rl   )rG   r˜   rM   rM   rN   r˜   ˜  s    


zSAMPHubServer.paramsc             C   s¬   t j| jd| _d| j_| jdkr>t j| jdd| _d| j_nd | _| jdkrjt j| j	dd| _
d| j
_nd | _
d| _| j ¡  | jd k	r”| j ¡  | j
d k	r¨| j
 ¡  d S )N)rx   Tr   zHub timeout test)rx   ry   zClient timeout test)r3   r~   Ú_serve_foreverr5   Zdaemonr%   r‹   r6   r&   r   r7   r   r   )rG   rM   rM   rN   rš   ­  s(    









zSAMPHubServer._start_threadsc             C   s    | j d k	r| j S tt ¡ ƒS d S )N)r<   r   r   r   )rG   rM   rM   rN   r=   É  s    
z!SAMPHubServer._create_secret_codec             C   s´   | j s
dS t d¡ |  ¡  d| _ | jrZtj | j¡rZt| jƒ}|d | j	krZt 
| j¡ d| _| jdd d| _|  ¡ | _	d| _i | _i | _i | _i | _i | _d| _t d	¡ dS )
zN
        Stop the current SAMP Hub instance and delete the lock file.
        NzHub is stopping...Fzsamp.secretg      $@)rJ   r   r   zHub stopped.)r   r   ru   Ú_notify_shutdownr    ÚosÚpathÚisfiler   r>   r}   Ú_join_all_threadsr;   r=   r?   rA   r@   rB   rC   rD   r9   )rG   ZlockfiledictrM   rM   rN   rˆ   Ï  s*    


zSAMPHubServer.stopc             C   s¤   t  ¡ }| j|k	r0| jj|d | j ¡ s0d | _| jd k	rb| j|k	rb| jj|d | j ¡ sbd | _| jd k	r”| j|k	r”| jj|d | j ¡ s”d | _| j|d d S )N)rJ   )r3   Úcurrent_threadr5   r‚   r{   r6   r7   rƒ   )rG   rJ   r¢   rM   rM   rN   r¡   ó  s    



zSAMPHubServer._join_all_threadsc             C   s   | j S )z™Return an information concerning the Hub running status.

        Returns
        -------
        running : bool
            Is the hub running?
        )r   )rG   rM   rM   rN   Ú
is_running  s    	zSAMPHubServer.is_runningc          
   C   s<  x| j ryt | jjgg g d¡d }W n4 tk
r^ } zt d |¡t¡ W d d }~X Y nX |rn| j 	¡  | j
r| jd kr°y| j ¡ }W n tjk
r¢   Y nX t|| jƒ yt | jjgg g d¡d }W n6 tk
r } zt d |¡t¡ W d d }~X Y qX |r| j 	¡  qW | j ¡  | jd k	r8| j ¡  d S )Ng{®Gáz„?r   z-Call to select() in SAMPHubServer failed: {0})r   Úselectrj   r1   ÚOSErrorr†   r‡   rl   r   Zhandle_requestr(   r)   r-   Ú
get_nowaitrq   ÚEmptyr   r.   r+   Zserver_close)rG   Z
read_readyÚexcÚrequestrM   rM   rN   rœ     s2    


zSAMPHubServer._serve_foreverc          	   C   sZ   t  d¡}xJ|D ]B}|| jkrx2| j| D ]$}|  | j| j| d di dœ¡ q*W qW d S )Nzsamp.hub.event.shutdownr   )z
samp.mtypezsamp.params)r   Úget_mtype_subtypesrB   Ú_notify_r   r@   )rG   ÚmsubsÚmtypeÚkeyrM   rM   rN   r   :  s    


zSAMPHubServer._notify_shutdownc          	   C   sl   t  d¡}x\|D ]T}|| jkr| j| d }x6| j| D ](}|  | j| j| d dd|idœ¡ q8W qW d S )Nzsamp.hub.event.registerr   rP   )z
samp.mtypezsamp.params)r   rª   rB   r@   r\   r   )rG   r   r¬   r­   Ú	public_idr®   rM   rM   rN   Ú_notify_registerD  s    


zSAMPHubServer._notify_registerc          	   C   st   t  d¡}xd|D ]\}|| jkr| j| d }x>| j| D ]0}||kr8|  | j| j| d dd|idœ¡ q8W qW d S )Nzsamp.hub.event.unregisterr   rP   )z
samp.mtypezsamp.params)r   rª   rB   r@   r\   r   )rG   r   r¬   r­   r¯   r®   rM   rM   rN   Ú_notify_unregisterP  s    


z SAMPHubServer._notify_unregisterc          
   C   st   t  d¡}xd|D ]\}|| jkr| j| d }x>| j| D ]0}|  | j| j| d d|| j| dœdœ¡ q8W qW d S )Nzsamp.hub.event.metadatar   )rP   Úmetadata)z
samp.mtypezsamp.params)r   rª   rB   r@   r\   r   rA   )rG   r   r¬   r­   r¯   r®   rM   rM   rN   Ú_notify_metadata\  s    


zSAMPHubServer._notify_metadatac          
   C   st   t  d¡}xd|D ]\}|| jkr| j| d }x>| j| D ]0}|  | j| j| d d|| j| dœdœ¡ q8W qW d S )Nzsamp.hub.event.subscriptionsr   )rP   Zsubscriptions)z
samp.mtypezsamp.params)r   rª   rB   r@   r\   r   rC   )rG   r   r¬   r­   r¯   r®   rM   rM   rN   Ú_notify_subscriptionsj  s    


z#SAMPHubServer._notify_subscriptionsc          	   C   sŠ   dd„ }t  d¡}| j| d }| j| d }xV|D ]N}|| jkr4|| j| kr4t d |¡¡ | j|||| j	dddid	œfd
 q4W d S )Nc             S   s   | j j |||¡ d S )N)ÚsampÚclientÚreceiveNotification)Úendpointr   Úhub_public_idÚmessagerM   rM   rN   Ú_xmlrpc_call_disconnecty  s    zDSAMPHubServer._notify_disconnection.<locals>._xmlrpc_call_disconnectzsamp.hub.disconnectr   r   znotify disconnection to {}ÚreasonzTimeout expired!)z
samp.mtypezsamp.params)rx   rz   )
r   rª   r@   rD   rB   r   Údebugrl   r   r?   )rG   r   r»   r¬   r¯   r¸   r­   rM   rM   rN   rŽ   w  s    

z#SAMPHubServer._notify_disconnectionc             C   s   |   ¡  t d¡ dS )NZpingÚ1)r™   r   r½   )rG   rM   rM   rN   rR   ‰  s    
zSAMPHubServer._pingc             C   sL   g }xB| j D ]8}|| j | kr| j | | |kr| | j| d ¡ qW |S )Nr   )rA   r|   r@   )rG   r®   ÚvalueZpublic_id_listZ
private_idrM   rM   rN   Ú_query_by_metadataŽ  s    z SAMPHubServer._query_by_metadatac             C   s    |   |¡ || jkrŒ|| jkrD| j| d }|t| jƒf| j|< dS t d ||¡¡ d }t	| j
tj|dd}| j| d }||f| j|< ntdd |¡ƒ‚dS )Nr   r   zset_xmlrpc_callback: {} {}r   )ri   é   z"Private-key {} expired or invalid.)r™   r@   r   r   r•   rD   r   r½   rl   r   r'   ÚxmlrpcZServerProxyr	   )rG   r   Zxmlrpc_addrr¯   Zserver_proxy_poolrM   rM   rN   rS   —  s$    




z"SAMPHubServer._set_xmlrpc_callbackc          	   C   sd   | j  |  ¡ \}}W d Q R X |t ¡ f| j|< |  |¡ |  |¡ t d ||¡¡ ||| j	dœS )Nz+register: private-key = {} and self-id = {})zsamp.self-idzsamp.private-keyzsamp.hub-id)
r4   Ú_get_new_idsr„   r@   r™   r°   r   r½   rl   r?   )rG   r   r¯   rM   rM   rN   Ú_perform_standard_register´  s    


z(SAMPHubServer._perform_standard_registerc             C   s(   |   ¡  || jkr|  ¡ S tddƒ‚d S )Né   zBad secret code)r™   r>   rÄ   r	   )rG   rH   rM   rM   rN   rT   Á  s    
zSAMPHubServer._registerc             C   s<   t t ¡ ƒ}|  jd7  _d}| jdkr4d | j¡}||fS )Nr   zcli#hubr   zcli#{})r   r   r   rF   rl   )rG   r   r¯   rM   rM   rN   rÃ   É  s    
zSAMPHubServer._get_new_idsc          	   C   s  |   ¡  d}|  |¡ | jÊ || jkr@| j| d }| j|= ndS || jkrV| j|= || jkrh| j|= x0| j ¡ D ]"}|| j| krt| j|  |¡ qtW || j	kr¬| j	|= || j
kr¾| j
|= | jrâ|| jkrÖ| j|= | j |¡ W d Q R X t d ||¡¡ dS )Nr   r   zunregister {} ({}))r™   r±   r4   r@   rA   rC   rB   rŒ   r}   rD   r:   r(   r,   r+   Zremove_clientr   r½   rl   )rG   r   Z
public_keyr­   rM   rM   rN   rU   Ò  s2    







zSAMPHubServer._unregisterc             C   sT   |   |¡ || jkr@t d |t|ƒ¡¡ || j|< |  |¡ ntdd |¡ƒ‚dS )Nz0declare_metadata: private-key = {} metadata = {}rÁ   z"Private-key {} expired or invalid.r   )	r™   r@   r   r½   rl   r   rA   r³   r	   )rG   r   r²   rM   rM   rN   rV   û  s    


zSAMPHubServer._declare_metadatac             C   sˆ   |   |¡ || jkrt|  |¡}t d ||¡¡ |d k	rh|| jkrbt d | j| ¡¡ | j| S i S q„tddƒ‚ntdd |¡ƒ‚d S )Nz-get_metadata: private-key = {} client-id = {}z--> metadata = {}é   zInvalid client IDrÁ   z"Private-key {} expired or invalid.)r™   r@   Ú_public_id_to_private_keyr   r½   rl   rA   r	   )rG   r   Ú	client_idÚclient_private_keyrM   rM   rN   rW     s    





zSAMPHubServer._get_metadatac          	   C   s^  |   |¡ || jkrJt d |t|ƒ¡¡ || jkrx| j| }x6|D ].}y| j|  |¡ W qF t	k
rr   Y qFX qFW t
 |¡| j|< t
 |¡}xL|D ]D}| d¡r˜x4|D ],}| |d d… ¡r¬||kr¬||kr¬||= q¬W q˜W t d |t|ƒ¡¡ xF|D ]>}|| jkr.|| j| kr:| j|  |¡ qü|g| j|< qüW |  |¡ ntdd |¡ƒ‚dS )Nz3declare_subscriptions: private-key = {} mtypes = {}Ú*r   z;declare_subscriptions: subscriptions accepted from {} => {}rÁ   z"Private-key {} expired or invalid.r   )r™   r@   r   r½   rl   r   rC   rB   r}   Ú
ValueErrorÚcopyÚdeepcopyÚendswithÚ
startswithr|   r´   r	   )rG   r   ZmtypesZprev_mtypesr­   Zoriginal_mtypesZmtype2rM   rM   rN   rX     s>    









z$SAMPHubServer._declare_subscriptionsc             C   sŒ   |   |¡ || jkrx|  |¡}|d k	rl|| jkrVt d |t| j| ƒ¡¡ | j| S t d |¡¡ i S qˆtddƒ‚ntdd |¡ƒ‚d S )Nz-get_subscriptions: client-id = {} mtypes = {}z2get_subscriptions: client-id = {} mtypes = missingrÆ   zInvalid client IDrÁ   z"Private-key {} expired or invalid.)	r™   r@   rÇ   rC   r   r½   rl   r   r	   )rG   r   rÈ   rÉ   rM   rM   rN   rY   N  s    




z SAMPHubServer._get_subscriptionsc             C   sr   |   |¡ || jkr^g }x.| j ¡ D ] }||kr$| | j| d ¡ q$W t d ||¡¡ |S tdd |¡ƒ‚d S )Nr   z5get_registered_clients: private_key = {} clients = {}rÁ   z"Private-key {} expired or invalid.)r™   r@   rŒ   r|   r   r½   rl   r	   )rG   r   Zreg_clientsÚpkeyrM   rM   rN   rZ   d  s    


z%SAMPHubServer._get_registered_clientsc             C   s~   |   |¡ || jkrji }x8| j ¡ D ]*}||kr$|  ||¡r$i || j| d < q$W t d |||¡¡ |S tdd |¡ƒ‚d S )Nr   z@get_subscribed_clients: private_key = {} mtype = {} clients = {}rÁ   z"Private-key {} expired or invalid.)r™   r@   rŒ   Ú_is_subscribedr   r½   rl   r	   )rG   r   r­   Zsub_clientsrÐ   rM   rM   rN   r[   t  s    

z%SAMPHubServer._get_subscribed_clientsc             C   s€   g }|   d¡}ttt|ƒƒƒ}| ¡  | d¡ xJ|D ]B}d |d|d … ¡}|| krn|dkrj|d }nd}| |¡ q6W |S )aÒ  
        Return a list containing all the possible wildcarded subtypes of MType.

        Parameters
        ----------
        mtype : str
            MType to be parsed.

        Returns
        -------
        types : list
            List of subtypes

        Examples
        --------
        >>> from astropy.samp import SAMPHubServer
        >>> SAMPHubServer.get_mtype_subtypes("samp.app.ping")
        ['samp.app.ping', 'samp.app.*', 'samp.*', '*']
        r   r   Nr   r   z.*rÊ   )ÚsplitÚlistÚrangeÚlenÚreverser|   r‚   )r­   Zsubtypesr¬   ZindexesÚiZ	tmp_mtyperM   rM   rN   rª   †  s    



z SAMPHubServer.get_mtype_subtypesc             C   s<   d}t  |¡}x(|D ] }|| jkr|| j| krd}qW |S )NFT)r   rª   rB   )rG   r   r­   Z
subscribedr¬   ZmsubrM   rM   rN   rÑ   ®  s    


zSAMPHubServer._is_subscribedc             C   sr   |   |¡ || jkr^|  |  |¡|d ¡dkrDtdd ||d ¡ƒ‚| j| j|||fd i S tdd |¡ƒ‚d S )Nz
samp.mtypeFé   z$Client {} not subscribed to MType {})rx   rz   rÁ   z"Private-key {} expired or invalid.)r™   r@   rÑ   rÇ   r	   rl   r   r«   )rG   r   Úrecipient_idrº   rM   rM   rN   r\   »  s    



zSAMPHubServer._notifyc       	      C   s¢   || j krd S | j | d }yBt d |d ||¡¡ |  |¡}||f}d}|  ||||¡ W n> tk
rœ } z t d |d |||¡t	¡ W d d }~X Y nX d S )Nr   znotify {} from {} to {}z
samp.mtyper·   z7{} notification from client {} to client {} failed [{}])
r@   r   r½   rl   rÇ   Ú_retry_methodÚ	Exceptionr†   r‡   r   )	rG   Úsender_private_keyÚrecipient_public_idrº   Úsender_public_idÚrecipient_private_keyÚ
arg_paramsÚsamp_method_namer¨   rM   rM   rN   r«   Ì  s"    

zSAMPHubServer._notify_c             C   sJ   |   |¡ || jkr6d|kr&tddƒ‚|  ||¡}|S tdd |¡ƒ‚d S )Nz
samp.mtypeé   zsamp.mtype keyword is missingrÁ   z"Private-key {} expired or invalid.)r™   r@   r	   Ú_notify_all_rl   )rG   r   rº   Úrecipient_idsrM   rM   rN   r]   æ  s    


zSAMPHubServer._notify_allc             C   sx   g }t  |d ¡}x`|D ]X}|| jkrxH| j| D ]:}||kr2| j| d }| |¡ | j| j|||fd q2W qW |S )Nz
samp.mtyper   )rx   rz   )r   rª   rB   r@   r|   r   r\   )rG   rÜ   rº   rä   r¬   r­   r®   Z_recipient_idrM   rM   rN   rã   ò  s    


zSAMPHubServer._notify_all_c             C   s   |   |¡ || jkr||  |  |¡|d ¡dkrDtdd ||d ¡ƒ‚| j| d }|  ||¡}| j| j|||||fd |S tdd |¡ƒ‚d S )	Nz
samp.mtypeFrØ   z$Client {} not subscribed to MType {}r   )rx   rz   rÁ   z"Private-key {} expired or invalid.)	r™   r@   rÑ   rÇ   r	   rl   Ú_get_new_hub_msg_idr   Ú_call_)rG   r   rÙ   Úmsg_tagrº   r¯   Úmsg_idrM   rM   rN   r^     s    


zSAMPHubServer._callc       
      C   s´   || j krd S yPt d | d¡d |||d ¡¡ |  |¡}|||f}d}|  ||||¡ W nP tk
r® }	 z2t 	d |d | d¡d ||t
|	ƒ|	¡t¡ W d d }	~	X Y nX d S )Nzcall {} from {} to {} ({})z;;r   z
samp.mtypeZreceiveCallz5{} call {} from client {} to client {} failed [{},{}])r@   r   r½   rl   rÒ   rÇ   rÚ   rÛ   r†   r‡   Útyper   )
rG   rÜ   rÞ   rÝ   rè   rº   rß   rà   Zsamp_methodNamer¨   rM   rM   rN   ræ     s"    


zSAMPHubServer._call_c             C   sb   |   |¡ || jkrNd|kr,tdd |¡ƒ‚| j| d }|  ||||¡}|S tdd |¡ƒ‚d S )Nz
samp.mtyperâ   z5samp.mtype keyword is missing in message tagged as {}r   rÁ   z"Private-key {} expired or invalid.)r™   r@   r	   rl   Ú
_call_all_)rG   r   rç   rº   r¯   rè   rM   rM   rN   r_   0  s    

zSAMPHubServer._call_allc          	   C   s†   i }t  |d ¡}xn|D ]f}|| jkrxV| j| D ]H}||kr2|  ||¡}	| j| d }
|	||
< | j| j|||
|	|fd q2W qW |S )Nz
samp.mtyper   )rx   rz   )r   rª   rB   rå   r@   r   ræ   )rG   rÜ   rÞ   rç   rº   rè   r¬   r­   r®   Z_msg_idZreceiver_public_idrM   rM   rN   rê   ?  s     

zSAMPHubServer._call_all_c             C   sÊ   |   |¡ || jkr¶t|ƒ}t ¡ }i }|  ||d|¡}d | j|< xn| jr°d|  k rft ¡ | kr|n n| j|= tddƒ‚| j| d k	r¤t 	| j| ¡}| j|= P t 
d¡ qDW |S tdd |¡ƒ‚d S )Nzsamp::sync::callr   r   zTimeout expired!g{®Gáz„?rÁ   z"Private-key {} expired or invalid.)r™   r@   Úintr„   r^   rE   r   r	   rÌ   rÍ   r…   rl   )rG   r   rÙ   rº   rJ   rŠ   Úresponserè   rM   rM   rN   r`   T  s(    



 
zSAMPHubServer._call_and_waitc             C   s@   |   |¡ || jkr,| j| j|||fd ntdd |¡ƒ‚i S )z
        The main method that gets called for replying. This starts up an
        asynchronous reply thread and returns.
        )rx   rz   rÁ   z"Private-key {} expired or invalid.)r™   r@   r   Ú_reply_r	   rl   )rG   r   rè   rì   rM   rM   rN   ra   q  s    

zSAMPHubServer._replyc             C   sÖ   || j ks|sd S | j | d }| dd¡\}}}}ybt d |||¡¡ |dkrl|| j ¡ kr”|| j|< n(|  |¡}	|||f}
d}|  |	|||
¡ W n: t	k
rÐ } zt
 d ||||¡t¡ W d d }~X Y nX d S )Nr   z;;râ   zreply {} from {} to {}zsamp::sync::callZreceiveResponsez0{} reply from client {} to client {} failed [{}])r@   rÒ   r   r½   rl   rE   rŒ   rÇ   rÚ   rÛ   r†   r‡   r   )rG   Zresponder_private_keyrè   rì   Zresponder_public_idZcounterr¹   rÝ   Zrecipient_msg_tagrß   rà   rá   r¨   rM   rM   rN   rí   €  s&    

zSAMPHubServer._reply_c             C   s  |dkrt dƒ‚ddlm} xÈt|jƒD ]º}| js>t d¡ q(yV| jrl|| j	krl||dœ}| j	|  
|¡ n&| j| d }t|jj|ƒ|f|žŽ  W nH tjk
rÜ }	 z(t d ||d |	j¡¡ t d¡ W dd}	~	X Y q(X dS q(W |d |j d	 }
t |
ƒ‚dS )
aÉ  
        This method is used to retry a SAMP call several times.

        Parameters
        ----------
        recipient_private_key
            The private key of the receiver of the call
        recipient_public_key
            The public key of the receiver of the call
        samp_method_name : str
            The name of the SAMP method to call
        arg_params : tuple
            Any additional arguments to be passed to the SAMP method
        NzInvalid client IDr   )Úconfg{®Gáz„?)zsamp.methodNamezsamp.paramsz*{} XML-RPC endpoint error (attempt {}): {}z failed after z	 attempts)r   r   rî   rÔ   Z	n_retriesr   r„   r…   r(   r,   ÚputrD   Úgetattrrµ   r¶   rÂ   ZFaultr   r½   rl   ZfaultString)rG   rß   rÝ   rá   rà   rî   ZattemptÚcallbackZhubr¨   Zerror_messagerM   rM   rN   rÚ      s.    


zSAMPHubServer._retry_methodc             C   s.   x(| j  ¡ D ]}| j | d |kr|S qW d S )Nr   )r@   rŒ   )rG   r¯   r   rM   rM   rN   rÇ   ×  s    z'SAMPHubServer._public_id_to_private_keyc          	   C   s4   | j  |  jd7  _W d Q R X d | j| j||¡S )Nr   zmsg#{};;{};;{};;{})r4   r;   rl   r?   )rG   rÞ   Zsender_msg_idrM   rM   rN   rå   Þ  s
    z!SAMPHubServer._get_new_hub_msg_idc          	   C   s6   | j & t ¡ | _|d k	r(t ¡ | j|< W d Q R X d S )N)r4   r„   r9   r:   )rG   r   rM   rM   rN   r™   å  s    
z(SAMPHubServer._update_last_activity_timec             C   s   dS )Nr   rM   )rG   r   Ú	sender_idrº   rM   rM   rN   r’   ë  s    z#SAMPHubServer._receive_notificationc             C   s˜   || j krd|kr6|d dkr6|  | j |ti dœ¡ nVd|krŒ|d dksV|d dkrŒ|  |d d |d d ¡}|  | j |td	|idœ¡ d
S d
S d S )Nz
samp.mtypezsamp.app.ping)zsamp.statuszsamp.resultzx-samp.query.by-metazsamp.query.by-metazsamp.paramsr®   r¿   Zidsr   )r   ra   r   rÀ   )rG   r   rò   rè   rº   Zids_listrM   rM   rN   r‘   î  s    


zSAMPHubServer._receive_callc             C   s   dS )Nr   rM   )rG   r   Zresponder_idrç   rì   rM   rM   rN   r“     s    zSAMPHubServer._receive_response©Úunknownr   rô   c             C   s¼   |   ¡  |d dkrtddƒ‚|s&d}t|tƒrBd|krBtddƒ‚| j d¡ | j |||f¡ | j ¡ }| j ¡  |r®|  	¡ }d	 
| j|d
 ¡}||d< | j |d
 ¡ |S tddƒ‚d S )Nr   )rp   z	127.0.0.1i“  z,Request of registration rejected by the Hub.rô   z	samp.namezLRequest of registration rejected by the Hub (application name not provided).r›   z&http://localhost:{}/translator/{}?ref=zsamp.private-keyzsamp.url-translatorz-Request of registration rejected by the user.)r™   r	   Ú
isinstanceÚdictr/   rï   r-   r.   ÚgetrÄ   rl   r*   r+   Z
add_client)rG   Zidentity_infoZclient_addressÚoriginrì   Zregister_mapZtranslator_urlrM   rM   rN   rd     s*    





z#SAMPHubServer._web_profile_registerc             C   sR   |   ¡  || jkr>|dkr.|| jkr<| j|= qNt ¡ | j|< ntdd |¡ƒ‚dS )NÚ0rÁ   z"Private-key {} expired or invalid.r   )r™   r@   r,   rq   rr   r	   rl   )rG   r   ZallowrM   rM   rN   re   /  s    


z0SAMPHubServer._web_profile_allowReverseCallbacksc             C   sr   |   ¡  || jkr^g }| j| }y"x| jr>| ¡ }| |¡ q$W W n tjk
rX   Y nX |S tdd 	|¡ƒ‚d S )NrÁ   z"Private-key {} expired or invalid.)
r™   r@   r,   r   r¦   r|   rq   r§   r	   rl   )rG   r   Ztimeout_secsrñ   Zcallback_queueZitem_queuedrM   rM   rN   rf   <  s    

z(SAMPHubServer._web_profile_pullCallbacks)NNr   Nr   r   r   r   TNr   r   )NNNN)N)F)N)N)ró   rô   )DÚ__name__Ú
__module__Ú__qualname__Ú__doc__rO   ÚpropertyrP   rc   rg   ro   rv   r   rƒ   r‹   r   r•   r—   r   r˜   rš   r=   rˆ   r¡   r£   rœ   r   r°   r±   r³   r´   rŽ   rR   rÀ   rS   rÄ   rT   rÃ   rU   rV   rW   rX   rY   rZ   r[   Ústaticmethodrª   rÑ   r\   r«   r]   rã   r^   ræ   r_   rê   r`   ra   rí   rÚ   rÇ   rå   r™   r’   r‘   r“   rd   re   rf   rM   rM   rM   rN   r   !   s†   ?   
U



*$
(
		)4( 7
 
'c               @   s(   e Zd ZdZdd„ Zdd„ Zdd„ ZdS )	r   aU  
    A base class to make writing Web Profile GUI consent dialogs
    easier.

    The concrete class must:

        1) Poll ``handle_queue`` periodically, using the timer services
           of the GUI's event loop.  This function will call
           ``self.show_dialog`` when a request requires authorization.
           ``self.show_dialog`` will be given the arguments:

              - ``samp_name``: The name of the application making the request.

              - ``details``: A dictionary of details about the client
                making the request.

              - ``client``: A hostname, port pair containing the client
                address.

              - ``origin``: A string containing the origin of the
                request.

        2) Call ``consent`` or ``reject`` based on the user's response to
           the dialog.
    c             C   s|   y| j  ¡ }W n( tjk
r$   Y nT tk
r6   Y nBX t|d tƒrP|d }n|d d }|  ||d |d |d ¡ d S )Nr   z	samp.namer   rØ   )rs   r¦   rq   r§   ÚAttributeErrorrõ   r   Zshow_dialog)rG   r©   Z	samp_namerM   rM   rN   Úhandle_queueh  s    
zWebProfileDialog.handle_queuec             C   s   | j  d¡ d S )NT)rt   rï   )rG   rM   rM   rN   Úconsentw  s    zWebProfileDialog.consentc             C   s   | j  d¡ d S )NF)rt   rï   )rG   rM   rM   rN   Úrejectz  s    zWebProfileDialog.rejectN)rú   rû   rü   rý   r  r  r  rM   rM   rM   rN   r   M  s   )'rÌ   rž   r¤   r1   r3   r„   r   r†   rq   Zxmlrpc.clientr¶   rÂ   Zurllib.parser   Zastropyr   Z	constantsr   r   Úerrorsr   r   r	   Zutilsr
   r   r   Zlockfile_helpersr   r   Zstandard_profiler   rL   r   r   Ú__all__Z__doctest_skip__r   r   rM   rM   rM   rN   Ú<module>   s@             6