U
    7h                     @   s   d Z ddlm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ZdZdZd	Zed
ZG dd deZedddZdd Zdd Zdd Zdd Zdd ZG dd dZdS )zModule for interacting with GLPI using the REST API. It just wraps endpoints
provided by the API and manage HTTP return codes.
    )unicode_literalsNwraps)	b64encode)contextmanagerzG{{ "input": {{ "name": "{name:s}", "_filename" : ["{filename:s}"] }} }}zgThe file could not be uploaded but a document with id '{:d}' was created, this document will be purged.zQThe created document could not be purged, you may need to cealn it manually: {:s}z^filename="(.+)";c                   @   s   e Zd ZdZdS )	GLPIErrorz Exception raised by this module.N)__name__
__module____qualname____doc__ r   r   B/var/www/formularioweb/env/lib/python3.8/site-packages/glpi_api.pyr       s   r   Tc                 c   s,   t | ||||d}z
|V  W 5 |  X dS )a  Context manager that authenticate to GLPI when enter and kill application
    session in GLPI when leaving:

    .. code::

        >>> import glpi_api
        >>>
        >>> URL = 'https://glpi.exemple.com/apirest.php'
        >>> APPTOKEN = 'YOURAPPTOKEN'
        >>> USERTOKEN = 'YOURUSERTOKEN'
        >>>
        >>> try:
        >>>     with glpi_api.connect(URL, APPTOKEN, USERTOKEN) as glpi:
        >>>         print(glpi.get_config())
        >>> except glpi_api.GLPIError as err:
        >>>     print(str(err))

    You can set ``verify_certs`` to *False* to ignore invalid SSL certificates.

    ``use_headers`` indicates whether authentication parameters are sent through HTTP
    headers or as GET parameters (in the URL). The default is to use headers but
    some environments (cf `this GLPI issue
    <https://github.com/glpi-project/glpi/issues/5116#issuecomment-496166674>`_ and
    the following Stack Overflow post) may require to use GET parameters.
    use_headersN)GLPIkill_session)urlapptokenauthverify_certsr   
user_agentZglpir   r   r   connect#   s    
r   c                 C   s"   t jjdk r| d} t| dS )a  Raise ``GLPIError`` exception with ``msg`` message.

    In Python 2, exceptions expect ``str`` by default. ``requests`` module
    returns unicode strings and ``__future__.unicode_literals`` is used for
    ensuring all strings are ``unicode`` (prevent the use of ``u''`` and
    make strings manipulations easier). So for Python 2 we need to encode
    to ``str`` the message.
       zutf-8N)sysversion_infomajorencoder   )msgr   r   r   _raiseD   s    	
r   c                 C   s   t dj|    dS )zGLPI errors message are returned in a list of two elements. The first
    element is the key of the error and the second the message.z({}) {}N)r   formatjsonresponser   r   r   _glpi_errorQ   s    r#   c                 C   s   t d| j| j| j dS )zMHelper for returning a HTTP code and response on non managed status
    code.zunknown error: [{:d}/{:s}] {:s}N)r   r   status_codereasontextr!   r   r   r   _unknown_errorV   s
      r'   c                 C   s   dd |   D S )Nc                 S   s,   i | ]$\}}|t |tr$t| n|qS r   )
isinstanceboolstrlower).0keyvalr   r   r   
<dictcomp>]   s    z"_convert_bools.<locals>.<dictcomp>items)kwargsr   r   r   _convert_bools\   s    r3   c                    s   t   fdd}|S )zQDecorator function for catching communication error
    and raising an exception.c              
      sP   z | f||W S  t jjk
rJ } ztdt|W 5 d }~X Y nX d S )Nzcommunication error: {:s})requests
exceptionsRequestExceptionr   r   r*   )selfargsr2   errfuncr   r   wrapperc   s    z_catch_errors.<locals>.wrapperr   )r;   r<   r   r:   r   _catch_errors`   s    r=   c                   @   s  e Zd ZdZdEddZdd ZedFdd	Zed
d Zedd Z	edd Z
edd Zedd Zedd ZedGddZedd Zedd Zedd Zdd  Zed!d" Zed#d$ Zed%d& ZedHd'd(Zd)d* ZdId+d,ZdJd-d.Zd/d0 ZdKd1d2Zed3d4 Zed5d6 Zed7d8 Zed9d: Zed;d< Z ed=d> Z!ed?d@ Z"edAdB Z#edLdCdDZ$dS )Mr   a  Class for interacting with GLPI using the REST API.

    The constructor authenticate to the GLPI platform at ``url`` using an
    application token ``apptoken`` (see API clients configuration) and either a
    string containing the user token or a couple of username/password as ``auth``
    parameter:

    .. code::

       # Authentication using user API token.
       glpi = GLPI(url='https://glpi.exemple.com/apirest.php',
                   apptoken='YOURAPPTOKEN',
                   auth='YOURUSERTOKEN')
       # Authentication using username/password.
       glpi = GLPI(url='https://glpi.exemple.com/apirest.php',
                   apptoken='YOURAPPTOKEN',
                   auth=('USERNAME', 'PASSWORD'))

    `verify_certs` and `use_headers` can be unset to respectively not checking
    SSL certificates and passing authentication parameters as GET parameters
    (instead of headers).
    TNc           
      C   sr   || _ t | _|s6ddlm} tjj| d| j_	| j
||||d}d||d}	|r`||	d< |	| j_i | _dS )	zpConnect to GLPI and retrieve session token which is put in a
        ``requests`` session as attribute.
        r   )InsecureRequestWarningFr   application/json)Content-TypeSession-Token	App-Token
User-AgentN)r   r4   SessionsessionZ$requests.packages.urllib3.exceptionsr>   packagesurllib3disable_warningsverify_init_sessionheaders_fields)
r7   r   r   r   r   r   r   r>   session_tokenrK   r   r   r   __init__   s"    

zGLPI.__init__c                 G   s"   d dd | jdf|D S )z$Generate the URL from ``endpoints``./c                 s   s   | ]}t |V  qd S N)r*   )r,   partr   r   r   	<genexpr>   s     z#GLPI._set_method.<locals>.<genexpr>)joinr   strip)r7   Z	endpointsr   r   r   _set_method   s    zGLPI._set_methodc           	      C   s   d|d}|r||d< i }t |ttfrt|dkr<td|rjdtd| 	 }|j
|d q|j
|d	 |d
 d n$|r|j
d|d n|j
|d | jj| d||d}dd ttd|jt|S )aU  API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#init-session>`__

        Request a session token (will be sent alongside `apptoken` for next API calls).
        ``auth`` can either be a string containing the user token of a list/tuple
        of two elements containing username and password.
        r?   )r@   rB   rC      z@invalid 'auth' parameter (should contains username and password)z
Basic {:s}:)Authorizationr      )loginpasswordzuser_token {:s})Z
user_tokenZinitSession)r   rK   paramsc                 S   s   |   d S )NrM   r    rr   r   r   <lambda>       z$GLPI._init_session.<locals>.<lambda>       )r(   listtuplelenr   r   r   rS   r   decodeupdaterE   getrU   r#   r$   r'   )	r7   r   r   r   r   Zinit_headersr\   authorizationr"   r   r   r   rJ      s:    
 zGLPI._init_sessionc                 C   s4   | j | d}dd ttd|jt| dS )a  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#kill-session>`__

        Destroy a session identified by a session token. Note that this
        method is automatically called by the context manager ``connect``.

        .. code::

            >>> glpi.kill_session()
            # Doing another actions will raise this error.
            >>> glpi.list_search_options('Computer')
            ...
            GLPIError: (ERROR_SESSION_TOKEN_INVALID) session_token semble incorrect
        ZkillSessionc                 S   s   | j S rP   )r&   r^   r   r   r   r`      ra   z#GLPI.kill_session.<locals>.<lambda>rb   NrE   rk   rU   r#   r$   r'   r7   r"   r   r   r   r      s     zGLPI.kill_sessionc                 C   s0   | j | d}dd ttd|jt|S )a  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-my-profiles>`__

        Return all the profiles associated to logged user.

        .. code::

            >>> glpi.get_my_profiles()
            [{'id': 2,
              'name': 'Observer',
              'entities': [{'id': 0, 'name': 'Root entity', 'is_recursive': 1}]},
             {'id': 8,
              'name': 'Read-Only',
              'entities': [{'id': 0, 'name': 'Root entity', 'is_recursive': 1}]}]
        ZgetMyProfilesc                 S   s   |   d S )NZ
myprofilesr]   r^   r   r   r   r`      ra   z&GLPI.get_my_profiles.<locals>.<lambda>rb   rm   rn   r   r   r   get_my_profiles   s     zGLPI.get_my_profilesc                 C   s0   | j | d}dd ttd|jt|S )al  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-active-profile>`__

        Return the current active profile.

        .. code::

            >>> glpi.get_active_profile()
            {'id': 2,
             'name': 'Observer',
             'interface': 'central',
             'is_default': 0,
             ...
        ZgetActiveProfilec                 S   s   |   d S )NZactive_profiler]   r^   r   r   r   r`     ra   z)GLPI.get_active_profile.<locals>.<lambda>rb   rm   rn   r   r   r   get_active_profile   s     zGLPI.get_active_profilec                    sB   | j j| dd|id  fddtttd jt  dS )u  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#change-active-profile>`__

        Change active profile to the ``profile_id`` one.

        .. code::

            >>> glpi.get_active_profile()['name']
            'Observer'
            >>> glpi.set_active_profile(8)
            >>> glpi.get_active_profile()['name']
            'Read-Only'
            >>> glpi.set_active_profile(4) # Invalid profile for user
            GLPIError: (ERROR_ITEM_NOT_FOUND) Élément introuvable
        ZchangeActiveProfileZprofiles_idr]   c                    s
   t  jS rP   r)   r&   r^   r!   r   r   r`   )  ra   z)GLPI.set_active_profile.<locals>.<lambda>rc   rd   re   i  NrE   postrU   r#   rk   r$   r'   )r7   Z
profile_idr   r!   r   set_active_profile  s    
 zGLPI.set_active_profilec                 C   s0   | j | d}dd ttd|jt|S )aM  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-my-entities>`__

        Return all the possible entities of the current logged user (and for
        current active profile).

        .. code::

            >>> glpi.get_my_entities()
            [{'id': 0, 'name': 'Root entity'}]
        ZgetMyEntitiesc                 S   s   |   d S )NZ
myentitiesr]   r^   r   r   r   r`   >  ra   z&GLPI.get_my_entities.<locals>.<lambda>rb   rm   rn   r   r   r   get_my_entities/  s     zGLPI.get_my_entitiesc                 C   s0   | j | d}dd ttd|jt|S )a  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-active-entities>`_

        Return active entities of current logged user.

        .. code::

            >>> glpi.get_active_entity()
            {'id': 0,
             'active_entity_recursive': False,
             'active_entities': [{'id': 0}, {'id': 3}, {'id': 2}, {'id': 1}]}
        ZgetActiveEntitiesc                 S   s   |   d S )NZactive_entityr]   r^   r   r   r   r`   S  ra   z*GLPI.get_active_entities.<locals>.<lambda>rb   rm   rn   r   r   r   get_active_entitiesC  s     zGLPI.get_active_entitiesFc                    sB   ||d}| j j| d|d  fddttd jt S )a  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#change-active-entities>`__

        Change active entity to the ``entitie_id``.

        .. code::

            >>> glpi.set_active_entity(0, is_recursive=True)
        )Zentities_idis_recursiveZchangeActiveEntitiesr]   c                    s
   t  jS rP   rq   r^   r!   r   r   r`   g  ra   z*GLPI.set_active_entities.<locals>.<lambda>rb   rs   )r7   Z	entity_idrx   datar   r!   r   set_active_entitiesX  s    

 zGLPI.set_active_entitiesc                 C   s0   | j | d}dd ttd|jt|S )a  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-full-session>`__

        Return the current php $_SESSION.

        .. code::

            >>> glpi.get_full_session()
            {'glpi_plugins': {'1': 'fusioninventory', '2': 'racks', '3': 'fields'},
             'valid_id': '1ak1oms81ie61vhndhgp20b12a',
             'glpi_currenttime': '2018-09-06 14:52:31',
             ...
        ZgetFullSessionc                 S   s   |   d S )NrE   r]   r^   r   r   r   r`   }  ra   z'GLPI.get_full_session.<locals>.<lambda>rb   rm   rn   r   r   r   get_full_sessionl  s     zGLPI.get_full_sessionc                 C   s.   | j | d}dd td|jt|S )uc  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-glpi-config>`__

        Return the current $CFG_GLPI.

        .. code::

            >>> glpi.get_config()
            {'cfg_glpi': {'languages': {'ar_SA': ['العَرَبِيَّةُ',
                'ar_SA.mo',
                'ar',
            ...
        ZgetGlpiConfigc                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   z!GLPI.get_config.<locals>.<lambda>)rc   rd   rm   rn   r   r   r   
get_config  s     zGLPI.get_configc                 K   s@   | j j| ||t|d}dd ttdd d|jt|S )a0  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-an-item)>`__

        Return the instance fields of ``itemtype`` identified by ``item_id``.
        ``kwargs`` contains additional parameters allowed by the API.

        .. code::

            >>> glpi.get_item('Computer', 1)
            {'id': 1,
             'entities_id': 0,
             'name': 'test',
             ...
            # Using with_logs extra request parameters.
            >>> glpi.get_item('Computer', 1, with_logs=True)
            {'id': 1,
             'entities_id': 0,
             'name': 'test',
             ...,
             '_logs': {
               '261': {
                 'id': 261,
                  'itemtype': 'Computer',
                  'items_id': 1,
                  ...
        r\   c                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   zGLPI.get_item.<locals>.<lambda>c                 S   s   d S rP   r   r^   r   r   r   r`     ra   rr   )rE   rk   rU   r3   r#   r$   r'   )r7   itemtypeitem_idr2   r"   r   r   r   get_item  s     zGLPI.get_itemc                 C   s2   t |ts tdtt|dd | D S )z0
        Generate searchText parameter.
        z)search text should be a dict, found: {:s}c                 S   s   i | ]\}}d  ||qS )zsearchText[{:s}])r   )r,   kvr   r   r   r/     s      z(GLPI._add_searchtext.<locals>.<dictcomp>)r(   dictr   r   r*   typer1   )r7   
searchTextr   r   r   _add_searchtext  s
    
zGLPI._add_searchtextc                 K   sV   | | |di  | jj| |t|d}dd dd ttd|jt	|S )u  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-all-items>`__

        Return a collection of rows of the ``itemtype``. ``kwargs`` contains
        additional parameters allowed by the API.

        .. code::

            # Retrieve (non deleted) computers.
            >>> glpi.get_all_items('Computer')
            [{'id': 1,
             'entities_id': 0,
             'name': 'test',
            ...
            # Retrieve deleted computers.
            >>> glpi.get_all_items('Computer', is_deleted=True)
            []
            # Using searchText.
            >>> glpi.get_all_items('Computer', searchText={'name':'server'})
            []
        r   r}   c                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   z$GLPI.get_all_items.<locals>.<lambda>c                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   rc      rd   re   )
rj   r   poprE   rk   rU   r3   r#   r$   r'   )r7   r~   r2   r"   r   r   r   get_all_items  s     zGLPI.get_all_itemsc                 K   s@   |  |||}| jj|t|d}dd ttd|jt|S )a,  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-sub-items>`__

        Return a collection of rows of the ``sub_itemtype`` for the identified
        item of type ``itemtype`` and id ``item_id``. ``kwargs`` contains
        additional parameters allowed by the API.

        .. code::

            # Retrieve logs of a computer.
            >>> In [241]: glpi.get_sub_items('Computer', 1, 'Log')
            [{'id': 261,
              'itemtype': 'Computer',
              'items_id': 1,
            ...
        r}   c                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   z$GLPI.get_sub_items.<locals>.<lambda>rb   )rU   rE   rk   r3   r#   r$   r'   )r7   r~   r   sub_itemtyper2   r   r"   r   r   r   get_sub_items  s     zGLPI.get_sub_itemsc                 O   sR   dd }t |}||| | jj| d|d}dd ttd|jt|S )aU  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-multiple-items>`__

        Virtually call Get an item for each line in input. So, you can have a
        ticket, a user in the same query.

        .. code::

            >>> glpi.get_multiple_items({'itemtype': 'User', 'items_id': 2},
                                        {'itemtype': 'Computer', 'items_id': 1})
            [{'id': 2,
              'name': 'glpi',
              ...},
             {'id': 1,
              'entities_id': 0,
              'name': 'test',
               ...}]
        c                 S   s   dd t | D S )Nc                 S   s0   i | ](\}}|  D ]\}}d |||qqS )zitems[{:d}][{:s}])r1   r   )r,   idxitemr-   valuer   r   r   r/     s
   
 
 zAGLPI.get_multiple_items.<locals>.format_items.<locals>.<dictcomp>	enumerater0   r   r   r   format_items  s    z-GLPI.get_multiple_items.<locals>.format_itemsZgetMultipleItemsr}   c                 S   s   |   S rP   r]   r^   r   r   r   r`   "  ra   z)GLPI.get_multiple_items.<locals>.<lambda>rb   )r3   rj   rE   rk   rU   r#   r$   r'   )r7   r1   r2   r   r\   r"   r   r   r   get_multiple_items  s     zGLPI.get_multiple_itemsc                 C   s>   | j j| d||rdndd}dd ttd|jt|S )u&  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#list-searchoptions>`__

        List the searchoptions of provided ``itemtype``. ``raw`` return searchoption
        uncleaned (as provided by core).

        .. code::

            >>> glpi.list_search_options('Computer')
            {'common': {'name': 'Caractéristiques'},
             '1': {
              'name': 'Nom',
              'table': 'glpi_computers',
              'field': 'name',
              'datatype': 'itemlink',
              ...
        ZlistSearchOptionsrawNr}   c                 S   s   |   S rP   r]   r^   r   r   r   r`   =  ra   z*GLPI.list_search_options.<locals>.<lambda>rb   rm   )r7   r~   r   r"   r   r   r   list_search_options'  s    
 zGLPI.list_search_optionsc                    s    fdd|    D S )zOPrivate method that returns a mapping between fields uid and fields
        id.c                    s2   i | ]*\}}d |krt d d|d  |qS )uidz^{:s}. )resubr   )r,   field_idfieldr~   r   r   r/   E  s    z$GLPI._map_fields.<locals>.<dictcomp>)r   r1   )r7   r~   r   r   r   _map_fieldsB  s    
zGLPI._map_fieldsc                 C   sL   t dt|rt|S || jks&|r6| || j|< t| j| t| S )a  Return ``itemtype`` field id from ``field_uid``. Each ``itemtype``
        are "cached" (in *_fields* attribute) and will be retrieve once except
        if ``refresh`` is set.

        .. code::

            >>> glpi.field_id('Computer', 'Entity.completename')
            80
        z^\d+$)r   matchr*   rL   r   )r7   r~   	field_uidrefreshr   r   r   r   I  s
    zGLPI.field_idc                 C   s>   || j ks|r| || j |< dd | j |  D t| S )a  Return ``itemtype`` field uid from ``field_id``. Each ``itemtype``
        are "cached" (in *_fields* attribute) and will be retrieve once except
        if ``refresh`` is set.

        .. code::

            >>> glpi.field_id('Computer', 80)
            'Entity.completename'
        c                 S   s   i | ]\}}||qS r   r   )r,   r-   r   r   r   r   r/   k  s    z"GLPI.field_uid.<locals>.<dictcomp>)rL   r   r1   r*   )r7   r~   r   r   r   r   r   r   ]  s    zGLPI.field_uidc                    s    fddt |D S )Nc                    s$   i | ]\}}d  | |qS )zforcedisplay[{:d}])r   r   )r,   r   r   r~   r7   r   r   r/   p  s    z*GLPI._add_forcedisplay.<locals>.<dictcomp>r   )r7   r~   r   r   r   r   _add_forcedisplayo  s    zGLPI._add_forcedisplayc                    s   t  fddtttfD s2tdtt i }t D ]f\}}|dkrXd|n|d| |	j
|dg d |	fd	d
| D  q>|S )zH
        Recursively generate criteria/metacriteria parameters.
        c                 3   s   | ]}t  |V  qd S rP   )r(   )r,   t)criteriar   r   rR   y  s     z%GLPI._add_criteria.<locals>.<genexpr>z-search criteria should be a list, found: {:s}Nzcriteria[{:d}]z[criteria][{:d}]r   )parentc                    sP   i | ]H\}}|d krd  ||dkr2|nt|trH|ddn|qS )r   z
{:s}[{:s}]r   'z'')r   r   r(   r*   replace)r,   pr   )criterion_keyr~   r7   r   r   r/     s   
z&GLPI._add_criteria.<locals>.<dictcomp>)anyrf   rg   setr   r   r*   r   r   rj   _add_criteriark   r1   )r7   r   r~   r   r\   r   	criterionr   )r   r   r~   r7   r   r   u  s.    
	zGLPI._add_criteriac              	   K   s   i }| | ||dg  |dg }|dg D ]}d|d< || q6| | || | | | jj| d||d}dd	 d
d	 ttd|j	t
|S )a  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#search-items>`__

        Expose the GLPI searchEngine and combine criteria to retrieve a list of
        elements of specified ``itemtype``.

        .. code::

            # Retrieve
            >>> criteria = [{'field': 45, 'searchtype': 'contains', 'value': '^Ubuntu$'}]
            >>> forcedisplay = [1, 80, 45, 46] # name, entity, os name, os version
            >>> glpi.search('Computer', criteria=criteria, forcedisplay=forcedisplay)
            [{'1': 'test', '80': 'Root entity', '45': 'Ubuntu', '46': 16.04}]

            # You can use fields uid instead of fields id.
            >>> criteria = [{'field': 'Item_OperatingSystem.OperatingSystem.name',
                             'searchtype': 'contains',
                             'value': '^Ubuntu$'}]
            >>> forcedisplay = [
                    'name',
                    'Entity.completename',
                    'Item_OperatingSystem.OperatingSystem.name',
                    'Item_OperatingSystem.OperatingSystemVersion.name']
            >>> glpi.search('Computer', criteria=criteria, forcedisplay=forcedisplay)
            [{'1': 'test', '80': 'Root entity', '45': 'Ubuntu', '46': 16.04}]
        Zforcedisplayr   ZmetacriteriaTmetasearchr}   c                 S   s   |   dg S Nry   r    rk   r^   r   r   r   r`     ra   zGLPI.search.<locals>.<lambda>c                 S   s   |   dg S r   r   r^   r   r   r   r`     ra   r   )rj   r   r   appendr   rE   rk   rU   r#   r$   r'   )r7   r~   r2   r\   r   r   r"   r   r   r   r     s&    
 zGLPI.searchc                 G   s>   | j j| |d|id}dd dd ttd|jt|S )a  `API documentation <https://github.com
        /glpi-project/glpi/blob/master/apirest.md#add-items>`__

        Add an object (or multiple objects) of type ``itemtype`` into GLPI.

        .. code::

            >>> glpi.add('Computer',
                         {'name': 'computer1', 'serial': '123456', 'entities_id': 0},
                         {'name': 'computer2', 'serial': '234567', 'entities_id': 1})
            [{'id': 5, 'message': ''}, {'id': 6, 'message': ''}]
        inputr]   c                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   zGLPI.add.<locals>.<lambda>c                 S   s   |   d S NrY   r]   r^   r   r   r   r`     ra         rd   re   rs   r7   r~   r1   r"   r   r   r   add  s     zGLPI.addc                 G   sF   |  |||}| jj|d|id}dd dd ttd|jt|S )a  `API documentation
        Same method used as get-sub-items, same parameter
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-sub-items>`__

        Add a collection of rows of the ``sub_itemtype`` for the identified
        item of type ``itemtype`` and id ``item_id``. ``kwargs`` contains
        additional parameters allowed by the API.

        .. code::

            # Add a operatingsystem of a computer.
            >>> In [241]: glpi.add_sub_items('Computer',1,'Item_OperatingSystem',{'items_id': 1 ,'itemtype':'Computer','operatingsystems_id':1 })
            [{'id': 261,
              'itemtype': 'Computer',
              'items_id': 1,
            ...
        r   r]   c                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   z$GLPI.add_sub_items.<locals>.<lambda>c                 S   s   |   d S r   r]   r^   r   r   r   r`     ra   r   )rU   rE   rt   r#   rk   r$   r'   r7   r~   r   r   r1   r   r"   r   r   r   add_sub_items  s     zGLPI.add_sub_itemsc                 G   sD   | j j| |d|id}dd dd dd ttd|jt|S )a  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#update-items>`__

        Update an object (or multiple objects) existing in GLPI.

        .. code::

            >>> glpi.update('Computer',
                            {'id': 5, 'otherserial': 'abcdef'})
            >>> glpi.update('Computer',
                            {'id': 5, 'otherserial': 'abcdef'},
                            {'id': 6, 'otherserial': 'bcdefg'})
            [{'5': True, 'message': ''}, {'6': True, 'message': ''}]
        r   r]   c                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   zGLPI.update.<locals>.<lambda>c                 S   s   |   S rP   r]   r^   r   r   r   r`     ra   c                 S   s   |   d S r   r]   r^   r   r   r   r`     ra   rc   r   r   rd   re   )rE   putrU   r#   rk   r$   r'   r   r   r   r   rj     s     zGLPI.updatec                 G   sL   |  |||}| jj|d|id}dd dd dd ttd|jt|S )a  `API documentation
        Same method used as get-sub-items, same parameters
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-sub-items>`__

        Updates a collection of rows of the ``sub_itemtype`` for the identified
        item of type ``itemtype`` and id ``item_id``. ``kwargs`` contains
        additional parameters allowed by the API.

        .. code::

            # update the operatingsystem a computer.
            >>> In [241]: glpi.update_sub_items('Computer',1,'Item_OperatingSystem' {'id': 1 ,'itemtype':'Computer','operatingsystem_id':1 })
            [{'id': 261,
              'itemtype': 'Computer',
              'items_id': 1,
            ...
        r   r]   c                 S   s   |   S rP   r]   r^   r   r   r   r`   3  ra   z'GLPI.update_sub_items.<locals>.<lambda>c                 S   s   |   S rP   r]   r^   r   r   r   r`   4  ra   c                 S   s   |   S rP   r]   r^   r   r   r   r`   5  ra   r   )rU   rE   r   r#   rk   r$   r'   r   r   r   r   update_sub_items  s     zGLPI.update_sub_itemsc                 O   sN   | j j| |t|d|id}dd dd dd dd td|jt|S )	a  `API documentation <https://github.com
        /glpi-project/glpi/blob/master/apirest.md#delete-items>`__

        Delete an object existing in GLPI.

        .. code::

            # Move some computers to the trash.
            >>> glpi.delete('Computer', {'id': 5}, {'id': 6})
            [{'5': True, 'message': ''}, {'6': True, 'message': ''}]
            # Purge computers.
            >>> glpi.delete('Computer', {'id': 2}, {'id': 5}, force_purge=True)
            [{'2': True, 'message': ''}, {'5': True, 'message': ''}]
            # With non existing items
            >>> glpi.delete('Computer', {'id': 2}, {'id': 101}, force_purge=True)
            [{'2': True, 'message': ''}, {'101': False, 'message': 'Item not found'}]
        r   )r\   r    c                 S   s   |   S rP   r]   r^   r   r   r   r`   Q  ra   zGLPI.delete.<locals>.<lambda>c                 S   s   |   S rP   r]   r^   r   r   r   r`   R  ra   c                 S   s   |   d S r   r]   r^   r   r   r   r`   S  ra   c                 S   s$   |   d dkrt| S |   d S Nr   ZERROR_GLPI_DELETErY   r    r#   r^   r   r   r   r`   T  ra   rc      r   rd   re   )rE   deleterU   r3   r#   rk   r$   r'   )r7   r~   r1   r2   r"   r   r   r   r   :  s     zGLPI.deletec                 G   sP   |  |||}| jj|d|id}dd dd dd dd td|jt|S )	a  `API documentation
        Same method used as get-sub-items, same parameters
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#get-sub-items>`__

        deletes a collection of rows of the ``sub_itemtype`` for the identified
        item of type ``itemtype`` and id ``item_id``. ``kwargs`` contains
        additional parameters allowed by the API.

        .. code::

            # delete the operatingsystem a computer.
            >>> In [241]: glpi.delete_sub_items('Computer',1,'Item_OperatingSystem' {'id': 1 ,'itemtype':'Computer','operatingsystem_id':1 })
            [{'id': 261,
              'itemtype': 'Computer',
              'items_id': 1,
            ...
        r   r]   c                 S   s   |   S rP   r]   r^   r   r   r   r`   o  ra   z'GLPI.delete_sub_items.<locals>.<lambda>c                 S   s   |   S rP   r]   r^   r   r   r   r`   p  ra   c                 S   s   |   d S r   r]   r^   r   r   r   r`   q  ra   c                 S   s$   |   d dkrt| S |   d S r   r   r^   r   r   r   r`   r  ra   r   )rU   rE   r   r#   rk   r$   r'   r   r   r   r   delete_sub_itemsX  s     zGLPI.delete_sub_itemsc              
   C   s  t |dD}| jj| dddidtj|tj|ddf||fdd}W 5 Q R X |j	d	krft
| | d
 }| d d d dd}|dk	rtt|t z| jdd
|idd W n: tk
r } ztt|t|t W 5 d}~X Y nX td|| S )a  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#upload-a-document-file>`__

        Upload the file at ``filepath`` as a document named ``name``.

        .. code::

            glpi.upload_document("My test document", '/path/to/file/locally')
            {'id': 55,
             'message': 'Item successfully added: My test document',
             'upload_result': {'filename': [{'name': ...}]}}

        There may be errors while uploading the file (like a non managed file type).
        In this case, the API create a document but without a file attached to it.
        This method raise a warning (and another warning if the document could not
        be deleted for some reasons) and purge the created but incomplete document.
        rbDocumentr@   N)namefilenamer?   )ZuploadManifestzfilename[0])r   rK   filesr   idZupload_resultr   r   errorT)Zforce_purgez"(ERROR_GLPI_INVALID_DOCUMENT) {:s})openrE   rt   rU   _UPLOAD_MANIFESTr   ospathbasenamer$   r#   r    rk   warningswarn_WARN_DEL_DOCUserWarningr   r   _WARN_DEL_ERRr*   )r7   r   filepathfhandlerr"   doc_idr   r9   r   r   r   upload_documentw  s.    

*zGLPI.upload_documentc              	   C   s   t j|std||| jj| d|| jjd | jjd ddd}|j	dkr^t
| |ptt|jd	 d
 }t j||}t|d}||j W 5 Q R X |S )aY  `API documentation
        <https://github.com/glpi-project/glpi/blob/master/apirest.md#download-a-document-file>`__

        Download the file of the document with id ``doc_id`` in the directory
        ``dirpath``. If ``filename`` is not set, the name of the file is retrieved
        from the server otherwise the given value is used. The local path of the file
        is returned by the method.

        .. code::

            glpi.download_file(1, '/tmp')
            /tmp/test.txt
            glpi.download_file(1, '/tmp', filename='thenameiwant.txt')
            /tmp/thenameiwant.txt
        zLunable to download file of document '{:d}': directory '{:s}' does not existsr   rA   rB   zapplication/octet-stream)rA   rB   Accept)r   rK   rc   zContent-dispositionr   wb)r   r   existsr   r   rE   rk   rU   rK   r$   r#   _FILENAME_REfindallrS   r   writecontent)r7   r   dirpathr   r"   r   r   r   r   r   download_document  s&     



zGLPI.download_document)TTN)T)F)F)F)F)N)N)%r   r	   r
   r   rN   rU   r=   rJ   r   ro   rp   ru   rv   rw   rz   r{   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rj   r   r   r   r   r   r   r   r   r   r   k   sv     
)








%
 

"


)
/






/r   )TTN)r   
__future__r   r   r   r   r   	functoolsr   base64r   
contextlibr   r4   r   r   r   compiler   	Exceptionr   r   r   r#   r'   r3   r=   r   r   r   r   r   <module>   s0   
 