/* Copyright information is at end of file */

#include "xmlrpc_config.h"

#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>

#include "bool.h"
#include "mallocvar.h"

#include "xmlrpc-c/lock.h"
#include "xmlrpc-c/lock_platform.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"


/*=============================================================================

  xmlrpc_value is designed to enable cheap copies by sharing pointers and
  maintaining reference counts.  Multiple threads can use an xmlrpc_value
  simultaneously because there is locking around the reference count
  manipulation (but only since Xmlrpc-c 1.33).  But there is no copy on
  write, so the scheme depends upon the user not modifying an xmlrpc_value
  after building it, and not copying it while building it.  Another reason
  to observe this sequence is that there is no locking around modifications,
  so a reader could see a half-updated xmlrpc_value.

  We could enforce a prohibition against modifying an xmlrpc_value that has
  references other than the one by the party doing the modifying, but we
  don't because we allowed it for a long time before we noticed the problem
  adn we're afraid of breaking an existing program that does these updates in
  such a way that it actually works.

=============================================================================*/



static void
destroyCptr(xmlrpc_value * const valueP) {

    if (valueP->_value.cptr.dtor)
        valueP->_value.cptr.dtor(valueP->_value.cptr.dtorContext,
                                 valueP->_value.cptr.objectP);
}



static void
destroyValue(xmlrpc_value * const valueP) {

    /* First, we need to destroy this value's contents, if any. */
    switch (valueP->_type) {
    case XMLRPC_TYPE_INT:
        break;
        
    case XMLRPC_TYPE_BOOL:
        break;

    case XMLRPC_TYPE_DOUBLE:
        break;

    case XMLRPC_TYPE_DATETIME:
        xmlrpc_destroyDatetime(valueP);
        break;

    case XMLRPC_TYPE_STRING:
        xmlrpc_destroyString(valueP);
        break;
        
    case XMLRPC_TYPE_BASE64:
        xmlrpc_mem_block_clean(&valueP->_block);
        break;

    case XMLRPC_TYPE_ARRAY:
        xmlrpc_destroyArrayContents(valueP);
        break;
        
    case XMLRPC_TYPE_STRUCT:
        xmlrpc_destroyStruct(valueP);
        break;

    case XMLRPC_TYPE_C_PTR:
        destroyCptr(valueP);
        break;

    case XMLRPC_TYPE_NIL:
        break;

    case XMLRPC_TYPE_I8:
        break;

    case XMLRPC_TYPE_DEAD:
        XMLRPC_ASSERT(false); /* Can't happen, per entry conditions */

    default:
        XMLRPC_ASSERT(false); /* There are no other possible values */
    }

    valueP->lockP->destroy(valueP->lockP);

    /* Next, we mark this value as invalid, to help catch refcount errors.
    */
    valueP->_type = XMLRPC_TYPE_DEAD;

    /* Finally, we destroy the value itself. */
    free(valueP);
}



/*===========================================================================
  Reference Counting
=============================================================================
  Some simple reference-counting code. The xmlrpc_DECREF routine is in
  charge of destroying values when their reference count reaches zero.
============================================================================*/

void 
xmlrpc_INCREF (xmlrpc_value * const valueP) {

    XMLRPC_ASSERT_VALUE_OK(valueP);

    valueP->lockP->acquire(valueP->lockP);

    XMLRPC_ASSERT(valueP->refcount > 0);
    
    ++valueP->refcount;

    valueP->lockP->release(valueP->lockP);
}



void 
xmlrpc_DECREF (xmlrpc_value * const valueP) {

    bool died;

    XMLRPC_ASSERT_VALUE_OK(valueP);

    valueP->lockP->acquire(valueP->lockP);

    XMLRPC_ASSERT(valueP->refcount > 0);
    XMLRPC_ASSERT(valueP->_type != XMLRPC_TYPE_DEAD);

    --valueP->refcount;

    died = (valueP->refcount == 0);

    valueP->lockP->release(valueP->lockP);

    if (died)
        destroyValue(valueP);
}



/*=========================================================================
    Utiltiies
=========================================================================*/

const char *
xmlrpc_type_name(xmlrpc_type const type) {

    switch (type) {

    case XMLRPC_TYPE_INT:      return "INT";
    case XMLRPC_TYPE_BOOL:     return "BOOL";
    case XMLRPC_TYPE_DOUBLE:   return "DOUBLE";
    case XMLRPC_TYPE_DATETIME: return "DATETIME";
    case XMLRPC_TYPE_STRING:   return "STRING";
    case XMLRPC_TYPE_BASE64:   return "BASE64";
    case XMLRPC_TYPE_ARRAY:    return "ARRAY";
    case XMLRPC_TYPE_STRUCT:   return "STRUCT";
    case XMLRPC_TYPE_C_PTR:    return "C_PTR";
    case XMLRPC_TYPE_NIL:      return "NIL";
    case XMLRPC_TYPE_I8:       return "I8";
    case XMLRPC_TYPE_DEAD:     return "DEAD";
    default:                   return "???";

    }
}



static void
validateType(xmlrpc_env *         const envP,
             const xmlrpc_value * const valueP,
             xmlrpc_type          const expectedType) {

    if (valueP->_type != expectedType) {
        xmlrpc_env_set_fault_formatted(
            envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where "
            "type %s was expected.", 
            xmlrpc_type_name(valueP->_type), xmlrpc_type_name(expectedType));
    }
}



/*=========================================================================
    Extracting XML-RPC value
===========================================================================
  These routines extract XML-RPC values into ordinary C data types.

  For array and struct values, see the separates files xmlrpc_array.c
  and xmlrpc_struct.c.
=========================================================================*/

void 
xmlrpc_read_int(xmlrpc_env *         const envP,
                const xmlrpc_value * const valueP,
                xmlrpc_int32 *       const intValueP) {

    validateType(envP, valueP, XMLRPC_TYPE_INT);
    if (!envP->fault_occurred)
        *intValueP = valueP->_value.i;
}



void
xmlrpc_read_bool(xmlrpc_env *         const envP,
                 const xmlrpc_value * const valueP,
                 xmlrpc_bool *        const boolValueP) {

    validateType(envP, valueP, XMLRPC_TYPE_BOOL);
    if (!envP->fault_occurred)
        *boolValueP = valueP->_value.b;
}



void
xmlrpc_read_double(xmlrpc_env *         const envP,
                   const xmlrpc_value * const valueP,
                   xmlrpc_double *      const doubleValueP) {
    
    validateType(envP, valueP, XMLRPC_TYPE_DOUBLE);
    if (!envP->fault_occurred)
        *doubleValueP = valueP->_value.d;

}



/* datetime stuff is in xmlrpc_datetime.c */

/* string stuff is in xmlrpc_string.c */



void
xmlrpc_read_base64(xmlrpc_env *           const envP,
                   const xmlrpc_value *   const valueP,
                   size_t *               const lengthP,
                   const unsigned char ** const byteStringValueP) {

    validateType(envP, valueP, XMLRPC_TYPE_BASE64);
    if (!envP->fault_occurred) {
        size_t const size = 
            XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
        const char * const contents = 
            XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);

        char * byteStringValue;

        byteStringValue = malloc(size);
        if (byteStringValue == NULL)
            xmlrpc_faultf(envP,
                          "Unable to allocate %u bytes for byte string.",
                          (unsigned)size);
        else {
            memcpy(byteStringValue, contents, size);
            *byteStringValueP = (const unsigned char *)byteStringValue;
            *lengthP = size;
        }
    }
}



void
xmlrpc_read_base64_old(xmlrpc_env *           const envP,
                       const xmlrpc_value *   const valueP,
                       size_t *               const lengthP,
                       const unsigned char ** const byteStringValueP) {

    validateType(envP, valueP, XMLRPC_TYPE_BASE64);
    if (!envP->fault_occurred) {
        *lengthP =
            XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
        *byteStringValueP = (const unsigned char *)
            XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
    }
}



void
xmlrpc_read_base64_size(xmlrpc_env *           const envP,
                        const xmlrpc_value *   const valueP,
                        size_t *               const lengthP) {

    validateType(envP, valueP, XMLRPC_TYPE_BASE64);
    if (!envP->fault_occurred)
        *lengthP = XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
}



void
xmlrpc_read_cptr(xmlrpc_env *         const envP,
                 const xmlrpc_value * const valueP,
                 void **              const ptrValueP) {

    validateType(envP, valueP, XMLRPC_TYPE_C_PTR);
    if (!envP->fault_occurred)
        *ptrValueP = valueP->_value.cptr.objectP;
}



void
xmlrpc_read_nil(xmlrpc_env *   const envP,
                xmlrpc_value * const valueP) {
/*----------------------------------------------------------------------------
   Read out the value of a nil value.  It doesn't have one, of course, so
   this is essentially a no-op.  But it does validate the type and is
   necessary to match all the other types.
-----------------------------------------------------------------------------*/
    validateType(envP, valueP, XMLRPC_TYPE_NIL);
}



void 
xmlrpc_read_i8(xmlrpc_env *         const envP,
               const xmlrpc_value * const valueP,
               xmlrpc_int64 *       const intValueP) {

    validateType(envP, valueP, XMLRPC_TYPE_I8);
    if (!envP->fault_occurred)
        *intValueP = valueP->_value.i8;
}



xmlrpc_type xmlrpc_value_type (xmlrpc_value* const value)
{
    XMLRPC_ASSERT_VALUE_OK(value);
    return value->_type;
}



void
xmlrpc_createXmlrpcValue(xmlrpc_env *    const envP,
                         xmlrpc_value ** const valPP) {
/*----------------------------------------------------------------------------
   Create a blank xmlrpc_value to be filled in.

   Set the reference count to 1.
-----------------------------------------------------------------------------*/
    xmlrpc_value * valP;

    MALLOCVAR(valP);
    if (!valP)
        xmlrpc_faultf(envP, "Could not allocate memory for xmlrpc_value");
    else {
        valP->lockP = xmlrpc_lock_create();

        if (!valP->lockP)
            xmlrpc_faultf(envP, "Could not allocate memory for lock for "
                          "xmlrpc_value");
        else
            valP->refcount = 1;
    }
    *valPP = valP;
}



xmlrpc_value *
xmlrpc_int_new(xmlrpc_env * const envP, 
               xmlrpc_int32 const value) {

    xmlrpc_value * valP;

    xmlrpc_createXmlrpcValue(envP, &valP);

    if (!envP->fault_occurred) {
        valP->_type    = XMLRPC_TYPE_INT;
        valP->_value.i = value;
    }
    return valP;
}



xmlrpc_value *
xmlrpc_i8_new(xmlrpc_env * const envP, 
              xmlrpc_int64 const value) {

    xmlrpc_value * valP;

    xmlrpc_createXmlrpcValue(envP, &valP);

    if (!envP->fault_occurred) {
        valP->_type     = XMLRPC_TYPE_I8;
        valP->_value.i8 = value;
    }
    return valP;
}



xmlrpc_value *
xmlrpc_bool_new(xmlrpc_env * const envP, 
                xmlrpc_bool  const value) {

    xmlrpc_value * valP;

    xmlrpc_createXmlrpcValue(envP, &valP);

    if (!envP->fault_occurred) {
        valP->_type = XMLRPC_TYPE_BOOL;
        valP->_value.b = value;
    }
    return valP;
}



xmlrpc_value *
xmlrpc_double_new(xmlrpc_env * const envP, 
                  double       const value) {

    xmlrpc_value * valP;

    if (!finite(value))
        xmlrpc_faultf(envP, "Value is not a finite number, "
                      "so cannot be represented in XML-RPC");
    else {
        xmlrpc_createXmlrpcValue(envP, &valP);

        if (!envP->fault_occurred) {
            valP->_type = XMLRPC_TYPE_DOUBLE;
            valP->_value.d = value;
        }
    }
    return valP;
}



xmlrpc_value *
xmlrpc_base64_new(xmlrpc_env *          const envP, 
                  size_t                const length,
                  const unsigned char * const value) {

    xmlrpc_value * valP;

    xmlrpc_createXmlrpcValue(envP, &valP);

    if (!envP->fault_occurred) {
        valP->_type = XMLRPC_TYPE_BASE64;

        xmlrpc_mem_block_init(envP, &valP->_block, length);
        if (!envP->fault_occurred) {
            char * const contents = 
                xmlrpc_mem_block_contents(&valP->_block);
            memcpy(contents, value, length);
        }
        if (envP->fault_occurred)
            free(valP);
    }
    return valP;
}



/* array stuff is in xmlrpc_array.c */



xmlrpc_value *
xmlrpc_cptr_new(xmlrpc_env *    const envP,
                void *          const value) {

    return xmlrpc_cptr_new_dtor(envP, value, NULL, NULL);
}



xmlrpc_value *
xmlrpc_cptr_new_dtor(xmlrpc_env *        const envP,
                     void *              const value,
                     xmlrpc_cptr_dtor_fn const dtor,
                     void *              const dtorContext) {

    xmlrpc_value * valP;

    xmlrpc_createXmlrpcValue(envP, &valP);

    if (!envP->fault_occurred) {
        valP->_type = XMLRPC_TYPE_C_PTR;
        valP->_value.cptr.objectP     = value;
        valP->_value.cptr.dtor        = dtor;
        valP->_value.cptr.dtorContext = dtorContext;
    }
    return valP;
}



xmlrpc_value *
xmlrpc_nil_new(xmlrpc_env *    const envP) {
    xmlrpc_value * valP;

    xmlrpc_createXmlrpcValue(envP, &valP);
    if (!envP->fault_occurred)
        valP->_type = XMLRPC_TYPE_NIL;

    return valP;
}



/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
** Copyright (C) 2001 by Eric Kidd. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
** 2. 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.
** 3. The name of the author may not be used to endorse or promote products
**    derived from this software without specific prior written permission. 
**  
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */
