/*
 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * mslpd_parse.c : Parses messages in and out of the mini slpv2 SA.
 *
 * Version: 1.7
 * Date:    10/05/99
 *
 * Licensee will, at its expense,  defend and indemnify Sun Microsystems,
 * Inc.  ("Sun")  and  its  licensors  from  and  against any third party
 * claims, including costs and reasonable attorneys' fees,  and be wholly
 * responsible for  any liabilities  arising  out  of  or  related to the
 * Licensee's use of the Software or Modifications.   The Software is not
 * designed  or intended for use in  on-line  control  of  aircraft,  air
 * traffic,  aircraft navigation,  or aircraft communications;  or in the
 * design, construction, operation or maintenance of any nuclear facility
 * and Sun disclaims any express or implied warranty of fitness  for such
 * uses.  THE SOFTWARE IS PROVIDED TO LICENSEE "AS IS" AND ALL EXPRESS OR
 * IMPLIED CONDITION AND WARRANTIES, INCLUDING  ANY  IMPLIED  WARRANTY OF
 * MERCHANTABILITY,   FITNESS  FOR  WARRANTIES,   INCLUDING  ANY  IMPLIED
 * WARRANTY  OF  MERCHANTABILITY,  FITNESS FOR PARTICULAR PURPOSE OR NON-
 * INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT WILL SUN BE LIABLE HEREUNDER
 * FOR ANY DIRECT DAMAGES OR ANY INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL
 * OR CONSEQUENTIAL DAMAGES OF ANY KIND.
 *
 * (c) Sun Microsystems, 1998, All Rights Reserved.
 * Author: Erik Guttman
 */
 /*
	Portions Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
 */

#include <stdio.h>
#include <string.h>
//#include <assert.h>

#include "mslp_sd.h"
#include "slp.h"
#include "mslp.h"
#include "mslp_dat.h"
#include "mslpd_store.h"
#include "mslpd.h"
#include "mslpd_mask.h"
#include "mslpd_parse.h"

/* ------------------------------------------------------------------------- */
/* 
* parse the request in
*/

SLPInternalError srvrqst_in(Slphdr *pslphdr, const char *pcInBuf, int iInSz,
                           char **ppcPRList, char **ppcSrvType, char **ppcScopes,
                           char **ppcPredicate) {
  
  SLPInternalError err;
  int offset  = HDRLEN + strlen(pslphdr->h_pcLangTag);
  *ppcPRList  = NULL;
  *ppcSrvType = NULL;
  *ppcScopes  = NULL;
  if ((err = get_string(pcInBuf, iInSz, &offset, ppcPRList)) < 0) 
    goto srvrqst_in_fail;
  if ((err = get_string(pcInBuf, iInSz, &offset, ppcSrvType)) < 0)
    goto srvrqst_in_fail;
  if ((err = get_string(pcInBuf, iInSz, &offset, ppcScopes)) < 0)  
    goto srvrqst_in_fail;
  if ((err = get_string(pcInBuf, iInSz, &offset, ppcPredicate)) < 0)
    goto srvrqst_in_fail;
  /* ignore SLP SPI */
  /* ignore options */
  return SLP_OK;
  
srvrqst_in_fail:
  if (*ppcPRList) SLPFree((void*)ppcPRList); *ppcPRList = NULL;
  if (*ppcSrvType) SLPFree((void*)ppcSrvType); *ppcSrvType = NULL;
  if (*ppcScopes) SLPFree((void*)ppcScopes); *ppcScopes = NULL;
  return err;
}


/*
* srvrply_out
*
*   Parse the reply out, into a SRVRPLY.  Use the result mask to determine
*   which services were matched by the request.  The request's header is
*   used to set the fields in the reply header.
*
*     ps          A pointer to the service store.
*     errcode     The error code of the reply.  If nonzero, just return this.
*     pm          The mask of results.
*     pslphdr     The header of the request.
*     ppcOutBuf   A pointer to the buffer to allocate.
*     piOutSz     A pointer to the size of the buffer that was allocated.
*     piGot       A pointer to the number of results serialized out.
*
* Return:
*   SLPInternalError result.  The ppcOutBuf is set to a new buffer allocated by
*   this function.  The piOutSz parameter is set to the size of this buffer.
*
* Side Effects:
*   The caller must SLPFree the ppcOutBuf.  It is wise to do mask_reset on
*   pm if it is used again as it might have been iterated upon here.
*
*/ 
SLPInternalError srvrply_out(SAStore *ps, SLPInternalError err, Mask *pm, 
		     Slphdr *pslphdr, char **ppcOutBuf, int *piOutSz,
		     int *piGot) {
  
  char*	endPtr	= NULL;
  int iMTU      = (SLPGetProperty("net.slp.MTU"))?strtol(SLPGetProperty("net.slp.MTU"),&endPtr,10):1400;
  int iOverflow = 0;
  int iIsMcast  = (pslphdr->h_usFlags & MCASTFLAG)?1:0;
  int hdrsz     = HDRLEN + strlen(pslphdr->h_pcLangTag);
  int offset    = 0;
  int count     = 0; /* number of urlentries to return*/
  *piOutSz      = hdrsz + 4; /* error code and number of results fields */
  *piGot        = 0;

  if (err == SLP_OK) {
    int item;
    mask_reset(pm);
    while((item = mask_next(pm,1)) >= 0) {
      count++;
      /* 6 is from reserved (1) + lifetime (2) + url len (2) + # auths (1) */
      *piOutSz +=  6 + strlen(ps->url[item]);
    }
  }
  
  /* in the case of overflow in a unicast request, send 0 results & flag */
  if (iIsMcast && *piOutSz >= iMTU) { 
    *piOutSz = hdrsz + 4;
    iOverflow = 1;
  } 

  *piGot     = count;
  *ppcOutBuf = safe_malloc(*piOutSz, 0, 0);  
  if( !*ppcOutBuf ) return SLP_PARSE_ERROR;

  /* parse header out */
  SETVER(*ppcOutBuf,2);
  SETFUN(*ppcOutBuf,SRVRPLY);
  SETLEN(*ppcOutBuf,*piOutSz);
  SETLANG(*ppcOutBuf,pslphdr->h_pcLangTag);
  SETXID(*ppcOutBuf,pslphdr->h_usXID);

  if (iOverflow) {
    SETFLAGS(*ppcOutBuf,OVERFLOWFLAG);
    return SLP_OK;
  }
  
  /* parse payload out */
  offset = hdrsz;  
  SETSHT(*ppcOutBuf,api2slp(err),offset);  offset += 2;
  
  if (err == SLP_OK) {
    SETSHT(*ppcOutBuf,count,offset); offset += 2;
    
    mask_reset(pm);
    while (count--) { /* if there are no URLs (ie. on error), skip this */
      int item = mask_next(pm,1);
      offset++; /* skip reserved */
      if ((err = add_sht(*ppcOutBuf,*piOutSz,
			 (0xFFFF & ps->life[item]),&offset)) < 0 ||
	  (err = add_string(*ppcOutBuf,*piOutSz,ps->url[item],&offset)) < 0) {
	return err;
      }
      offset++; /* skip # auths field, none will be supplied */
    }
  }
  return SLP_OK;
}  

#ifdef EXTRA_MSGS
int saadvert_out(SAState *ps, Slphdr *pslph, char **ppc, int *piOutSz){

  int offset = 0;
  const char *pcLang = (pslph->h_pcLangTag)?pslph->h_pcLangTag:"en";
  const char *pcScopeList = SLPGetProperty("net.slp.useScopes");
  const char *pcSrvTypeList = SLPGetProperty("com.sun.slp.saSrvTypes");
  char *pcSAAttrs;
  if (!pcScopeList) pcScopeList = "";
  if (!pcSrvTypeList) pcSrvTypeList = "";

  pcSAAttrs = safe_malloc(strlen(pcSrvTypeList)+strlen("(service-types=")+2,
			  "(service-types=",strlen("(service-types="));
  if( !pcSAAttrs ) return SLP_MEMORY_ALLOC_FAILED;
  
  strcat(pcSAAttrs,pcSrvTypeList);
  strcat(pcSAAttrs,")");
  
  *piOutSz = strlen(ps->pcSAURL) + 2 + strlen(pcScopeList) + 2 + HDRLEN +
    strlen(pcLang) + strlen(pcSAAttrs) + 1;

  *ppc = safe_malloc(*piOutSz,0,0);
  if( !*ppc ) return SLP_MEMORY_ALLOC_FAILED;
  
  /* parse header out */
  SETVER(*ppc,2);
  SETFUN(*ppc,SAADVERT);
  SETLEN(*ppc,*piOutSz);
  SETLANG(*ppc,pslph->h_pcLangTag);
  SETXID(*ppc,pslph->h_usXID);
  
  /* parse payload out */
  offset = HDRLEN + strlen(pcLang);  
  add_string(*ppc,*piOutSz,ps->pcSAURL, &offset);
  add_string(*ppc,*piOutSz,pcScopeList, &offset);
  add_string(*ppc,*piOutSz,pcSAAttrs, &offset);
  (*ppc)[offset] = '\0'; /* set the # of auth blocks to 0 */

  SLPFree(pcSAAttrs);

  return SLP_OK;

}
#endif /* EXTRA_MSGS */

#ifdef MAC_OS_X
int daadvert_out(SAState *ps, SLPBoolean viaTCP, Slphdr *pslph, char **ppc, int *piOutSz)
{
    const char *	pcAttributeList = "";
    char*			advertMessage = NULL;
    char*			scopeListToAdvertise = (char*)malloc( strlen(SLPGetProperty("com.apple.slp.daScopeList")) + 1 );	// start out with a copy
    SLPBoolean		needToSetOverflow = SLP_FALSE;
    
    SLP_LOG( SLP_LOG_DEBUG, "daadvert_out called");
    
    if ( SLPGetProperty("com.apple.slp.daAttributeList") )
        pcAttributeList = SLPGetProperty("com.apple.slp.daAttributeList");
        
    if ( !viaTCP && SLPGetProperty("com.apple.slp.daPrunedScopeList") && SLPGetProperty("com.apple.slp.daPrunedScopeList") != ""  )
    {
        strcpy( scopeListToAdvertise, SLPGetProperty("com.apple.slp.daPrunedScopeList") );
        needToSetOverflow = SLP_TRUE;
    }
    else if ( SLPGetProperty("com.apple.slp.daScopeList") )
        strcpy( scopeListToAdvertise, SLPGetProperty("com.apple.slp.daScopeList") );
    else
        return -1;		// no Scope list!
        
    advertMessage = MakeDAAdvertisementMessage( pslph, ps->pcDAURL, scopeListToAdvertise, pcAttributeList, GetStatelessBootTime(), piOutSz );            
    if ( needToSetOverflow )
        SETFLAGS(advertMessage,(unsigned char) OVERFLOWFLAG);	// we want clients to make a TCP connection

    *ppc = advertMessage;
    
    if ( strlen(scopeListToAdvertise) < strlen(SLPGetProperty("com.apple.slp.daScopeList")) )
    {
        SLP_LOG( SLP_LOG_DEBUG, "daadvert_out, advertising scopelist of size:%d out of original size:%d", strlen(scopeListToAdvertise), strlen(SLPGetProperty("com.apple.slp.daScopeList")) );
    }        

    if ( scopeListToAdvertise )
        free( scopeListToAdvertise );
        
    return SLP_OK;

}

char* MakeDAAdvertisementMessage(	Slphdr*	pslph,
									char*	url,
									const char*	scopeList,
									const char*	attributeList,
									long	timeStamp,
                                    int*	outSize )
{
	short	urlLength =strlen( url );
	short	scopeListLength = strlen(scopeList);
	short	attributeListLength = strlen(attributeList);
    const char*	pcLang = (pslph && pslph->h_pcLangTag)?pslph->h_pcLangTag:"en";
	char*	newRequest = NULL;
	char*	curPtr;
	short	sizeofNewMessage 	= HDRLEN + strlen(pcLang) 
								+ 2 													// error code
								+ 4 													// Time stamp
								+ urlLength + sizeof(urlLength) 
								+ scopeListLength + sizeof(scopeListLength) 
								+ attributeListLength + sizeof(attributeListLength)
								+ 2														// SPI List length
								+ 1;													// num Auth blocks
    
    if ( ServerScopeSponsoringEnabled() )
    {
        sizeofNewMessage 	+= 2 + SizeOfServerScopeSponsorData();	// including scope data
    }
    
 	newRequest = (char*)safe_malloc( sizeofNewMessage, 0, 0 );

    if( newRequest )
	{
		// first fill out the header
		SETVER(newRequest,2);
		SETFUN(newRequest, DAADVERT);
		SETLEN(newRequest, sizeofNewMessage);
		SETLANG(newRequest,pcLang);
		SETXID(newRequest, (pslph)?pslph->h_usXID:0);			// only is zero if this is an unsolicited DAAdvert
	
		curPtr = newRequest+GETHEADERLEN(newRequest);			// point to beyond the header
		
		*((short*)curPtr) = SLP_OK;	// set the error code
		curPtr += 2;
		
		*(long*)curPtr = timeStamp;	// if we are stateless then we need to remember last time all regs were lost
		curPtr += 4;
		
		*(short*)curPtr = urlLength;
		curPtr += sizeof(urlLength);			// advance past the length bytes
		strcpy( curPtr, url );				// now add the url
		curPtr += urlLength;
	
		*((short*)curPtr) = scopeListLength;	// set the scope list length
		curPtr += sizeof(scopeListLength);
		
		if ( scopeListLength > 0 )
			strcpy( curPtr, scopeList );
		curPtr += scopeListLength;
	
		*((short*)curPtr) = attributeListLength;	// set the attributeListLength
		curPtr += sizeof(attributeListLength);
		
		if ( attributeListLength > 0 )
			strcpy( curPtr, attributeList );
		curPtr += attributeListLength;
	
	//	 now we don't support SLP SPI yet
		*((short*)curPtr) = 0;
		curPtr += 2;
		
	//	 nor auth blocks so...
		*((char*)curPtr) = 0;
		curPtr += 2;
	
	//	 but perhaps we have it this configured to use SCOPE_SPONSER_EXTENSION_ID
		if ( ServerScopeSponsoringEnabled() )
		{
			memcpy( curPtr, GetServerScopeSponsorData(), SizeOfServerScopeSponsorData() );	// including scope data
			curPtr += SizeOfServerScopeSponsorData();
		}
	
		*outSize = sizeofNewMessage;
	}
    else
		*outSize = 0;
		
	return newRequest;
}

#endif /* MAC_OS_X */

/* ------------------------------------------------------------------------- */

unsigned char api2slp(SLPInternalError se) {
  switch(se) {
    case SLP_OK: return 0;
    case SLP_LANGUAGE_NOT_SUPPORTED:
      return LANGUAGE_NOT_SUPPORTED;
    case SLP_PARSE_ERROR:
      return PARSE_ERROR;
    case SLP_SCOPE_NOT_SUPPORTED:
      return SCOPE_NOT_SUPPORTED;
    default: return INTERNAL_ERROR;
  }
}

