/***************************************************************************/
/*                                                                         */
/* Project:     OpenSLP - OpenSource implementation of Service Location    */
/*              Protocol Version 2                                         */
/*                                                                         */
/* File:        slplib_findattrs.c                                         */
/*                                                                         */
/* Abstract:    Implementation for SLPFindAttrs() call.                    */
/*                                                                         */
/*-------------------------------------------------------------------------*/
/*                                                                         */
/*     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"


/* TODO: do we need to add anything for collation here? */

/*-------------------------------------------------------------------------*/
SLPBoolean ProcessAttrRplyCallback(SLPError errorcode, 
                                   struct sockaddr_in* peerinfo,
                                   SLPBuffer replybuf,
                                   void* cookie)
/*-------------------------------------------------------------------------*/
{
    SLPMessage      replymsg;
    SLPAttrRply*    attrrply;
    PSLPHandleInfo  handle      = (PSLPHandleInfo) cookie;
    SLPBoolean      result      = SLP_TRUE;

#ifdef ENABLE_SLPv2_SECURITY 
    int             securityenabled;
    securityenabled = SLPPropertyAsBoolean(SLPGetProperty("net.slp.securityEnabled"));
#endif

    /*-------------------------------------------*/
    /* Check the errorcode and bail if it is set */
    /*-------------------------------------------*/
    if(errorcode)
    {
        handle->params.findattrs.callback((SLPHandle)handle,
                                          0,
                                          errorcode,
                                          handle->params.findattrs.cookie);
        return SLP_FALSE;
    }

    /*--------------------*/
    /* Parse the replybuf */
    /*--------------------*/
    replymsg = SLPMessageAlloc();
    if(replymsg)
    {
        if(SLPMessageParseBuffer(peerinfo,replybuf,replymsg) == 0 &&
           replymsg->header.functionid == SLP_FUNCT_ATTRRPLY &&
           replymsg->body.attrrply.errorcode == 0)
        {
            attrrply = &(replymsg->body.attrrply);
            
            if(attrrply->attrlistlen)
            {
     
#ifdef ENABLE_SLPv2_SECURITY
                /*-------------------------------*/
                /* Validate the authblocks       */
                /*-------------------------------*/
                if(SLPPropertyAsBoolean(SLPGetProperty("net.slp.securityEnabled")) &&
                   SLPAuthVerifyString(handle->hspi,
                                       1,
                                       attrrply->attrlistlen,
                                       attrrply->attrlist,
                                       attrrply->authcount,
                                       attrrply->autharray))
                {
                    /* Could not verify the attr auth block */
                    SLPMessageFree(replymsg);
                    return result;
                }
#endif
                /*---------------------------------------*/
                /* Send the attribute list to the caller */
                /*---------------------------------------*/
                /* TRICKY: null terminate the attrlist by setting the authcount to 0 */
                ((char*)(attrrply->attrlist))[attrrply->attrlistlen] = 0;
                
                /* Call the callback function */
                result = handle->params.findattrs.callback((SLPHandle)handle,
                                                           attrrply->attrlist,
                                                           attrrply->errorcode * -1,
                                                           handle->params.findattrs.cookie);
            }
        }
        
        SLPMessageFree(replymsg);
    }
    
    return result;
}


/*-------------------------------------------------------------------------*/
SLPError ProcessAttrRqst(PSLPHandleInfo handle)
/*-------------------------------------------------------------------------*/
{
    int                 sock;
    struct sockaddr_in  peeraddr;
    int                 bufsize     = 0;
    char*               buf         = 0;
    char*               curpos      = 0;
    SLPError            result      = 0;

#ifdef ENABLE_SLPv2_SECURITY
    int                 spistrlen   = 0;
    char*               spistr      = 0;

    if(SLPPropertyAsBoolean(SLPGetProperty("net.slp.securityEnabled")))
    {
        SLPSpiGetDefaultSPI(handle->hspi,
                            SLPSPI_KEY_TYPE_PUBLIC,
                            &spistrlen,
                            &spistr);
    }
#endif

    /*-------------------------------------------------------------------*/
    /* determine the size of the fixed portion of the ATTRRQST           */
    /*-------------------------------------------------------------------*/
    bufsize  = handle->params.findattrs.urllen + 2;       /*  2 bytes for len field */
    bufsize += handle->params.findattrs.scopelistlen + 2; /*  2 bytes for len field */
    bufsize += handle->params.findattrs.taglistlen + 2;   /*  2 bytes for len field */
    bufsize += 2;    /*  2 bytes for spistr len*/
#ifdef ENABLE_SLPv2_SECURITY
    bufsize += spistrlen;
#endif

    buf = curpos = (char*)xmalloc(bufsize);
    if(buf == 0)
    {
        result = SLP_MEMORY_ALLOC_FAILED;
        goto FINISHED;
    }

    /*------------------------------------------------------------*/
    /* Build a buffer containing the fixed portion of the ATTRRQST*/
    /*------------------------------------------------------------*/
    /* url */
    ToUINT16(curpos,handle->params.findattrs.urllen);
    curpos = curpos + 2;
    memcpy(curpos,
           handle->params.findattrs.url,
           handle->params.findattrs.urllen);
    curpos = curpos + handle->params.findattrs.urllen;
    /* scope list */
    ToUINT16(curpos,handle->params.findattrs.scopelistlen);
    curpos = curpos + 2;
    memcpy(curpos,
           handle->params.findattrs.scopelist,
           handle->params.findattrs.scopelistlen);
    curpos = curpos + handle->params.findattrs.scopelistlen;
    /* taglist  */
    ToUINT16(curpos,handle->params.findattrs.taglistlen);
    curpos = curpos + 2;
    memcpy(curpos,
           handle->params.findattrs.taglist,
           handle->params.findattrs.taglistlen);
    curpos = curpos + handle->params.findattrs.taglistlen;
#ifdef ENABLE_SLPv2_SECURITY
    ToUINT16(curpos,spistrlen);
    curpos = curpos + 2;
    memcpy(curpos,spistr,spistrlen);
    curpos = curpos + spistrlen;
#else
    ToUINT16(curpos,0);
#endif


    /*--------------------------*/
    /* Call the RqstRply engine */
    /*--------------------------*/
    do
    {

        #ifndef UNICAST_NOT_SUPPORTED
	if ( handle->dounicast == 1 ) 
	{
	    void *cookie = (PSLPHandleInfo) handle;
	    result = NetworkUcastRqstRply(handle,
                                          buf,
                                          SLP_FUNCT_ATTRRQST,
					  bufsize,
                                          ProcessAttrRplyCallback,
					  cookie);
	    break;
	}
	else
	#endif
	sock = NetworkConnectToDA(handle,
                                  handle->params.findattrs.scopelist,
                                  handle->params.findattrs.scopelistlen,
                                  &peeraddr);
        if(sock == -1)
        {
            /* use multicast as a last resort */
            
            #ifndef MI_NOT_SUPPORTED
            result = NetworkMcastRqstRply(handle,
                                          buf,
                                          SLP_FUNCT_ATTRRQST,
                                          bufsize,
                                          ProcessAttrRplyCallback,
                                          NULL);
            #else	
	    result = NetworkMcastRqstRply(handle->langtag,
                                          buf,
                                          SLP_FUNCT_ATTRRQST,
                                          bufsize,
                                          ProcessAttrRplyCallback,
                                          handle);
            #endif /* MI_NOT_SUPPORTED */			
            break;
        }

        result = NetworkRqstRply(sock,
                                 &peeraddr,
                                 handle->langtag,
                                 0,
                                 buf,
                                 SLP_FUNCT_ATTRRQST,
                                 bufsize,
                                 ProcessAttrRplyCallback,
                                 handle);
        if(result)
        {
            NetworkDisconnectDA(handle);
        }

    }while(result == SLP_NETWORK_ERROR);


    FINISHED:
    if(buf) xfree(buf);
#ifdef ENABLE_SLPv2_SECURITY
    if(spistr) xfree(spistr);
#endif

    return result;
}

#ifdef ENABLE_ASYNC_API
/*-------------------------------------------------------------------------*/ 
SLPError AsyncProcessAttrRqst(PSLPHandleInfo handle)
/*-------------------------------------------------------------------------*/
{
    SLPError result = ProcessAttrRqst(handle);
    xfree((void*)handle->params.findattrs.url);
    xfree((void*)handle->params.findattrs.scopelist);
    xfree((void*)handle->params.findattrs.taglist);
    handle->inUse = SLP_FALSE;
    return result;
}
#endif


/*=========================================================================*/
SLPError SLPAPI SLPFindAttrs(SLPHandle   hSLP,
                      const char *pcURLOrServiceType,
                      const char *pcScopeList,
                      const char *pcAttrIds,
                      SLPAttrCallback callback,
                      void *pvCookie)
/*                                                                         */
/* This function returns service attributes matching the attribute ids     */
/* for the indicated service URL or service type.  If pcURLOrServiceType   */
/* is a service URL, the attribute information returned is for that        */
/* particular advertisement in the language locale of the SLPHandle.       */
/*                                                                         */
/* If pcURLOrServiceType is a service type name (including naming          */
/* authority if any), then the attributes for all advertisements of that   */
/* service type are returned regardless of the language of registration.   */
/* Results are returned through the callback.                              */
/*                                                                         */
/* The result is filtered with an SLP attribute request filter string      */
/* parameter, the syntax of which is described in RFC 2608. If the filter  */
/* string is the empty string, i.e.  "", all attributes are returned.      */
/*                                                                         */
/* hSLP                 The language specific SLPHandle on which to search */
/*                      for attributes.                                    */
/*                                                                         */
/* pcURLOrServiceType   The service URL or service type.  See RFC 2608 for */
/*                      URL and service type syntax.  May not be the empty */
/*                      string.                                            */
/*                                                                         */
/* pcScopeList          A pointer to a char containing a comma separated   */
/*                      list of scope names. Pass in NULL or the empty     */
/*                      string "" to find services in all the scopes the   */
/*                      local host is configured query.                    */
/*                                                                         */
/* pcAttrIds            A comma separated list of attribute ids to return. */
/*                      Use NULL or the empty string, "", to indicate all  */
/*                      values. Wildcards are not currently supported      */
/*                                                                         */
/* callback             A callback function through which the results of   */
/*                      the operation are reported.                        */
/*                                                                         */
/* pvCookie             Memory passed to the callback code from the client.*/  
/*                      May be NULL.                                       */
/*                                                                         */
/* Returns:             If an error occurs in starting the operation, one  */
/*                      of the SLPError codes is returned.                 */
/*=========================================================================*/
{
    PSLPHandleInfo      handle;
    SLPError            result;

    /*------------------------------*/
    /* check for invalid parameters */
    /*------------------------------*/
    if(hSLP == 0 ||
       *(unsigned int*)hSLP != SLP_HANDLE_SIG ||
       pcURLOrServiceType == 0 ||
       *pcURLOrServiceType == 0 || 
       callback == 0)
    {
        return SLP_PARAMETER_BAD;
    }

    /*-----------------------------------------*/
    /* cast the SLPHandle into a SLPHandleInfo */
    /*-----------------------------------------*/
    handle = (PSLPHandleInfo)hSLP; 

    /*-----------------------------------------*/
    /* Check to see if the handle is in use    */
    /*-----------------------------------------*/
    if(handle->inUse == SLP_TRUE)
    {
        return SLP_HANDLE_IN_USE;
    }
    handle->inUse = SLP_TRUE;


    /*-------------------------------------------*/
    /* Set the handle up to reference parameters */
    /*-------------------------------------------*/
    handle->params.findattrs.urllen   = strlen(pcURLOrServiceType);
    handle->params.findattrs.url      = pcURLOrServiceType;
    if(pcScopeList && *pcScopeList)
    {
        handle->params.findattrs.scopelistlen = strlen(pcScopeList);
        handle->params.findattrs.scopelist    = pcScopeList;
    }
    else
    {
        handle->params.findattrs.scopelist    = SLPGetProperty("net.slp.useScopes");
        handle->params.findattrs.scopelistlen = strlen(handle->params.findattrs.scopelist);
    }
    if(pcAttrIds && *pcAttrIds)
    {
        handle->params.findattrs.taglistlen = strlen(pcAttrIds);
        handle->params.findattrs.taglist    = pcAttrIds;
    }
    else
    {
        handle->params.findattrs.taglistlen = 0;
        handle->params.findattrs.taglist    = (char*)&handle->params.findattrs.taglistlen;
    }
    handle->params.findattrs.callback   = callback;
    handle->params.findattrs.cookie     = pvCookie; 


    /*----------------------------------------------*/
    /* Check to see if we should be async or sync   */
    /*----------------------------------------------*/
#ifdef ENABLE_ASYNC_API
    if(handle->isAsync)
    {
        /* COPY all the referenced parameters */
        handle->params.findattrs.url = xstrdup(handle->params.findattrs.url);
        handle->params.findattrs.scopelist = xstrdup(handle->params.findattrs.scopelist);
        handle->params.findattrs.taglist = xstrdup(handle->params.findattrs.taglist);

        /* make sure strdups did not fail */
        if(handle->params.findattrs.url &&
           handle->params.findattrs.scopelist &&
           handle->params.findattrs.taglist)
        {
            result = ThreadCreate((ThreadStartProc)AsyncProcessAttrRqst,handle);
        }
        else
        {
            result = SLP_MEMORY_ALLOC_FAILED;    
        }

        if(result)
        {
            if(handle->params.findattrs.url) xfree((void*)handle->params.findattrs.url);
            if(handle->params.findattrs.scopelist) xfree((void*)handle->params.findattrs.scopelist);
            if(handle->params.findattrs.taglist) xfree((void*)handle->params.findattrs.taglist);
            handle->inUse = SLP_FALSE;
        }
    }
    else
#endif /* ifdef ENABLE_ASYNC_API */
    {
        /* Leave all parameters REFERENCED */

        result = ProcessAttrRqst(handle);

        handle->inUse = SLP_FALSE;
    }

    return result;
}




