/***************************************************************************/
/*                                                                         */
/* Project:     OpenSLP - OpenSource implementation of Service Location    */
/*              Protocol Version 2                                         */
/*                                                                         */
/* File:        slplib_parse.c                                             */
/*                                                                         */
/* Abstract:    Implementation for SLPParseSrvUrl(), SLPEscape(),          */
/*              SLPUnescape() and SLPFree() calls.                         */
/*                                                                         */
/*-------------------------------------------------------------------------*/
/*                                                                         */
/*     Please submit patches to http://www.openslp.org                     */
/*                                                                         */
/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Copyright (C) 2000 Caldera Systems, Inc                                 */
/* All rights reserved.                                                    */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions are  */
/* met:                                                                    */ 
/*                                                                         */
/*      Redistributions of source code must retain the above copyright     */
/*      notice, this list of conditions and the following disclaimer.      */
/*                                                                         */
/*      Redistributions in binary form must reproduce the above copyright  */
/*      notice, this list of conditions and the following disclaimer in    */
/*      the documentation and/or other materials provided with the         */
/*      distribution.                                                      */
/*                                                                         */
/*      Neither the name of Caldera Systems nor the names of its           */
/*      contributors may be used to endorse or promote products derived    */
/*      from this software without specific prior written permission.      */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS     */
/* `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR   */
/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA      */
/* SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT        */
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  LOSS OF USE,  */
/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON       */
/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/*                                                                         */
/***************************************************************************/

#include "slp.h"
#include "libslp.h"

#ifdef _WIN32 /* on Win32 strncasecmp is named strnicmp, but behaves the same */
    #define strncasecmp(String1, String2, Num) strnicmp(String1, String2, Num)
#endif

/*=========================================================================*/
void SLPAPI SLPFree(void* pvMem)                                                  
/*                                                                         */
/* Frees memory returned from SLPParseSrvURL(), SLPFindScopes(),           */
/* SLPEscape(), and SLPUnescape().                                         */
/*                                                                         */
/* pvMem    A pointer to the storage allocated by the SLPParseSrvURL(),    */
/*          SLPEscape(), SLPUnescape(), or SLPFindScopes() function.       */
/*          Ignored if NULL.                                               */
/*=========================================================================*/
{
    if(pvMem)
    {
        xfree(pvMem);
    }
}

/*=========================================================================*/
SLPError SLPAPI SLPParseSrvURL(const char *pcSrvURL,
                        SLPSrvURL** ppSrvURL)
/*                                                                         */
/* Parses the URL passed in as the argument into a service URL structure   */
/* and returns it in the ppSrvURL pointer.  If a parse error occurs,       */
/* returns SLP_PARSE_ERROR. The input buffer pcSrvURL is destructively     */
/* modified during the parse and used to fill in the fields of the         */
/* return structure.  The structure returned in ppSrvURL should be freed   */
/* with SLPFreeURL().  If the URL has no service part, the s_pcSrvPart     */
/* string is the empty string, "", i.e.  not NULL. If pcSrvURL is not a    */
/* service:  URL, then the s_pcSrvType field in the returned data          */
/* structure is the URL's scheme, which might not be the same as the       */
/* service type under which the URL was registered.  If the transport is   */
/* IP, the s_pcTransport field is the empty string.  If the transport is   */
/* not IP or there is no port number, the s_iPort field is zero.           */
/*                                                                         */
/* pcSrvURL A pointer to a character buffer containing the null terminated */
/*          URL string to parse.                                           */
/*                                                                         */
/* ppSrvURL A pointer to a pointer for the SLPSrvURL structure to receive  */
/*          the parsed URL. The memory should be freed by a call to        */
/*          SLPFree() when no longer needed.                               */
/*                                                                         */
/* Returns: If no error occurs, the return value is SLP_OK. Otherwise, the */
/*          appropriate error code is returned.                            */
/*=========================================================================*/
{
    int result = SLPParseSrvUrl(strlen(pcSrvURL),
                                pcSrvURL,
                                (SLPParsedSrvUrl**) ppSrvURL);
    switch(result)
    {
    case ENOMEM:
        return SLP_MEMORY_ALLOC_FAILED;
    case EINVAL:
        return SLP_PARSE_ERROR;
    } 

    return SLP_OK;
}

#define ATTRIBUTE_RESERVE_STRING	"(),\\!<=>~"
#define ATTRIBUTE_BAD_TAG			"\r\n\t_"
#define ESCAPE_CHARACTER			'\\'
#define ESCAPE_CHARACTER_STRING		"\\"
/*=========================================================================*/
SLPError SLPAPI SLPEscape(const char* pcInbuf,
                   char** ppcOutBuf,
                   SLPBoolean isTag)
/*                                                                         */
/* Process the input string in pcInbuf and escape any SLP reserved         */
/* characters.  If the isTag parameter is SLPTrue, then look for bad tag   */
/* characters and signal an error if any are found by returning the        */
/* SLP_PARSE_ERROR code.  The results are put into a buffer allocated by   */
/* the API library and returned in the ppcOutBuf parameter.  This buffer   */
/* should be deallocated using SLPFree() when the memory is no longer      */
/* needed.                                                                 */
/*                                                                         */
/* pcInbuf      Pointer to he input buffer to process for escape           */
/*              characters.                                                */
/*                                                                         */
/* ppcOutBuf    Pointer to a pointer for the output buffer with the SLP    */
/*              reserved characters escaped.  Must be freed using          */
/*              SLPFree()when the memory is no longer needed.              */ 
/*                                                                         */
/* isTag        When true, the input buffer is checked for bad tag         */
/*              characters.                                                */
/*                                                                         */
/* Returns:     Return SLP_PARSE_ERROR if any characters are bad tag       */
/*              characters and the isTag flag is true, otherwise SLP_OK,   */
/*              or the appropriate error code if another error occurs.     */
/*=========================================================================*/
{
    char        *current_inbuf, *current_outBuf;
    int         amount_of_escape_characters;
    char        hex_digit;

    /* Ensure that the parameters are good. */
    if((pcInbuf == NULL) || ((isTag != SLP_TRUE) && (isTag != SLP_FALSE)))
        return(SLP_PARAMETER_BAD);

    /* 
     * Loop thru the string, counting the number of reserved characters 
     * and checking for bad tags when required.  This is also used to 
     * calculate the size of the new string to create.
     * ASSUME: that pcInbuf is a NULL terminated string. 
     */
    current_inbuf = (char *) pcInbuf;
    amount_of_escape_characters = 0;

    while(*current_inbuf != '\0')
    {
        /* Ensure that there are no bad tags when it is a tag. */
        if((isTag) && strchr(ATTRIBUTE_BAD_TAG, *current_inbuf))
            return(SLP_PARSE_ERROR);

        if((strchr(ATTRIBUTE_RESERVE_STRING, *current_inbuf)) || 
           ((*current_inbuf >= 0x00) && (*current_inbuf <= 0x1F)) ||
           (*current_inbuf == 0x7F)
          )
            amount_of_escape_characters++;

        current_inbuf++;
    } /* End While. */

    /* Allocate the string. */
    *ppcOutBuf = (char *) xmalloc(
                                sizeof(char) * 
                                (strlen(pcInbuf) + (amount_of_escape_characters * 2) + 1));

    if(ppcOutBuf == NULL)
        return(SLP_MEMORY_ALLOC_FAILED);

    /*
     * Go over it, again.  Replace each of the escape characters with their 
     * \hex equivalent.
     */
    current_inbuf = (char *) pcInbuf;
    current_outBuf = *ppcOutBuf;
    while(*current_inbuf != '\0')
    {
        /* Check to see if it is an escape character. */
        if((strchr(ATTRIBUTE_RESERVE_STRING, *current_inbuf)) || 
           ((*current_inbuf >= 0x00) && (*current_inbuf <= 0x1F)) ||
           (*current_inbuf == 0x7F)
          )
        {
            /* Insert the escape character. */
            *current_outBuf = ESCAPE_CHARACTER;
            current_outBuf++;

            /* Do the first digit. */
            hex_digit = (*current_inbuf & 0xF0)/0x0F;
            if((hex_digit >= 0) && (hex_digit <= 9))
                *current_outBuf = hex_digit + '0';
            else
                *current_outBuf = hex_digit + 'A' - 0x0A;

            current_outBuf++;

            /* Do the last digit. */
            hex_digit = *current_inbuf & 0x0F;
            if((hex_digit >= 0) && (hex_digit <= 9))
                *current_outBuf = hex_digit + '0';
            else
                *current_outBuf = hex_digit + 'A' - 0x0A;
        }
        else
        {
            *current_outBuf = *current_inbuf;
        } /* End If, Else. */

        current_outBuf += sizeof(char);
        current_inbuf += sizeof(char);
    } /* End While. */

    /* Make sure that the string is properly terminated. */
    *current_outBuf = '\0';

    return(SLP_OK);
}

/*=========================================================================*/
SLPError SLPAPI SLPUnescape(const char* pcInbuf,
                     char** ppcOutBuf,
                     SLPBoolean isTag)
/*                                                                         */
/* Process the input string in pcInbuf and unescape any SLP reserved       */
/* characters.  If the isTag parameter is SLPTrue, then look for bad tag   */
/* characters and signal an error if any are found with the                */
/* SLP_PARSE_ERROR code.  No transformation is performed if the input      */
/* string is an opaque.  The results are put into a buffer allocated by    */
/* the API library and returned in the ppcOutBuf parameter.  This buffer   */
/* should be deallocated using SLPFree() when the memory is no longer      */
/* needed.                                                                 */
/*                                                                         */
/* pcInbuf      Pointer to he input buffer to process for escape           */
/*              characters.                                                */
/*                                                                         */
/* ppcOutBuf    Pointer to a pointer for the output buffer with the SLP    */
/*              reserved characters escaped.  Must be freed using          */
/*              SLPFree() when the memory is no longer needed.             */
/*                                                                         */
/* isTag        When true, the input buffer is checked for bad tag         */
/*              characters.                                                */
/*                                                                         */
/* Returns:     Return SLP_PARSE_ERROR if any characters are bad tag       */
/*              characters and the isTag flag is true, otherwise SLP_OK,   */
/*              or the appropriate error code if another error occurs.     */
/*=========================================================================*/
{
    int     output_buffer_size;
    char    *current_Inbuf, *current_OutBuf;
    char    escaped_digit[2];

    /* Ensure that the parameters are good. */
    if((pcInbuf == NULL) || ((isTag != SLP_TRUE) && (isTag != SLP_FALSE)))
        return(SLP_PARAMETER_BAD);

    /* 
     * Loop thru the string, counting the number of escape characters 
     * and checking for bad tags when required.  This is also used to 
     * calculate the size of the new string to create.
     * ASSUME: that pcInbuf is a NULL terminated string. 
     */
    current_Inbuf = (char *) pcInbuf;
    output_buffer_size = strlen(pcInbuf);

    while(*current_Inbuf != '\0')
    {
        /* Ensure that there are no bad tags when it is a tag. */
        if((isTag) && strchr(ATTRIBUTE_BAD_TAG, *current_Inbuf))
            return(SLP_PARSE_ERROR);

        if(strchr(ESCAPE_CHARACTER_STRING, *current_Inbuf))
            output_buffer_size-=2;

        current_Inbuf++;
    } /* End While. */

    /* Allocate the string. */
    *ppcOutBuf = (char *) xmalloc((sizeof(char) * output_buffer_size) + 1);

    if(ppcOutBuf == NULL)
        return(SLP_MEMORY_ALLOC_FAILED);

    current_Inbuf = (char *) pcInbuf;
    current_OutBuf = *ppcOutBuf;

    while(*current_Inbuf != '\0')
    {
        /* Check to see if it is an escape character. */
        if(strchr(ESCAPE_CHARACTER_STRING, *current_Inbuf))
        {
            /* Insert the real character based on the escaped character. */
            escaped_digit[0] = *(current_Inbuf + sizeof(char));
            escaped_digit[1] = *(current_Inbuf + (sizeof(char) * 2));

            if((escaped_digit[0] >= 'A') && (escaped_digit[0] <= 'F'))
                escaped_digit[0] = escaped_digit[0] - 'A' + 0x0A;
            else if((escaped_digit[0] >= '0') && (escaped_digit[0] <= '9'))
                escaped_digit[0] = escaped_digit[0] - '0';
            else
                return(SLP_PARSE_ERROR);

            if((escaped_digit[1] >= 'A') && (escaped_digit[1] <= 'F'))
                escaped_digit[1] = escaped_digit[1] - 'A' + 0x0A;
            else if((escaped_digit[1] >= '0') && (escaped_digit[1] <= '9'))
                escaped_digit[1] = escaped_digit[1] - '0';
            else
                return(SLP_PARSE_ERROR);

            *current_OutBuf = escaped_digit[1] + (escaped_digit[0] * 0x10);
            current_Inbuf = (char *) current_Inbuf + (sizeof(char) * 2);
        }
        else
        {
            *current_OutBuf = *current_Inbuf;
        } /* End If, Else. */

        /* Move to the next character. */
        current_OutBuf++;
        current_Inbuf++;
    } /* End While. */

    /* Make sure we terminate the string properly. */
    *current_OutBuf = '\0';

    return(SLP_OK);
}


/*=========================================================================*/
SLPError SLPAPI SLPParseAttrs(const char* pcAttrList,
                       const char *pcAttrId,
                       char** ppcAttrVal)
/*                                                                         */
/* Used to get individual attribute values from an attribute string that   */
/* is passed to the SLPAttrCallback                                        */
/*                                                                         */
/* pcAttrList (IN) A character buffer containing a comma separated, null   */
/*                 terminated list of attribute id/value assignments, in   */
/*                 SLP wire format; i.e.  "(attr-id=attr-value-list)"      */
/*                                                                         */
/* pcAttrId (IN)   The string indicating which attribute value to return.  */
/*                 MUST not be null.  MUST not be the empty string ("").   */
/*                                                                         */
/* ppcAttrVal (IN) A pointer to a pointer to the buffer to receive         */
/*                 attribute value.  The memory should be freed by a call  */
/*                 to SLPFree() when no longer needed.                     */
/*                                                                         */
/* Returns: Returns SLP_PARSE_ERROR if an attribute of the specified id    */
/*          was not found otherwise SLP_OK                                 */
/*=========================================================================*/
{
    const char* slider1;
    const char* slider2;
    int			attridlen;

    /* Check for bad parameters */
    if(pcAttrList == 0 ||
       pcAttrId == 0   ||
       ppcAttrVal == 0)
    {
        return SLP_PARAMETER_BAD;
    }

    attridlen = strlen(pcAttrId);
    slider1 = pcAttrList;
    while(1)
    {
        while(*slider1 != '(')
        {
            if(*slider1 == 0)
            {
                return SLP_PARSE_ERROR;
            }
            slider1++;
        }
        slider1++;
        slider2=slider1;

        while(*slider2 && *slider2 != '=' && *slider2 !=')') slider2++;

        if(attridlen == slider2-slider1 && 
           strncasecmp(slider1, pcAttrId, slider2 - slider1) == 0)
        {
            /* found the attribute id */
            slider1 = slider2;
            if(*slider1 == '=') slider1++;
            while(*slider2 && *slider2 !=')') slider2++;
            
            *ppcAttrVal = (char*)xmalloc((slider2 - slider1) + 1);
            if(*ppcAttrVal == 0)
            {
                return SLP_MEMORY_ALLOC_FAILED;
            }

            memcpy(*ppcAttrVal,slider1,slider2-slider1);
            (*ppcAttrVal)[slider2-slider1] = 0;
                
            return SLP_OK;
        }
    }

    /* attrid does not exist */
    return SLP_PARSE_ERROR;
}
