/* Copyright (C) 2001 by First Peer, 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:
** 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. */


/*============================================================================
                              xmlrpc_server_validatee
==============================================================================

  This program runs an XMLRPC server, using the Xmlrpc-c libraries.

  The server implements the methods that the Userland Validator1 test suite
  invokes, which are supposed to exercise a broad range of XMLRPC server
  function.

  Coments here used to say you could get information about Validator1
  from <http://validator.xmlrpc.com/>, but as of 2004.09.25, there's nothing
  there (there's a web server, but it is not configured to serve that
  particular URL).

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <xmlrpc-c/base.h>
#include <xmlrpc-c/server.h>
#include <xmlrpc-c/server_abyss.h>

#include "config.h"  /* information about this build environment */

#define RETURN_IF_FAULT(env) \
    do { \
        if ((env)->fault_occurred) \
            return NULL; \
    } while (0)
        

/*=========================================================================
**  validator1.arrayOfStructsTest
**=========================================================================
*/

static xmlrpc_value *
array_of_structs(xmlrpc_env *   const envP, 
                 xmlrpc_value * const param_array, 
                 void *         const user_data ATTR_UNUSED) {

    xmlrpc_value * array;
    xmlrpc_value * retval;

    /* Parse our argument array. */
    xmlrpc_decompose_value(envP, param_array, "(A)", &array);
    if (envP->fault_occurred)
        retval = NULL;
    else {
        /* Add up all the struct elements named "curly". */
        size_t size;
        size = xmlrpc_array_size(envP, array);
        if (envP->fault_occurred)
            retval = NULL;
        else {
            unsigned int sum;
            unsigned int i;
            sum = 0;
            for (i = 0; i < size && !envP->fault_occurred; ++i) {
                xmlrpc_value * strct;
                strct = xmlrpc_array_get_item(envP, array, i);
                if (!envP->fault_occurred) {
                    xmlrpc_int32 curly;
                    xmlrpc_decompose_value(envP, strct, "{s:i,*}", 
                                           "curly", &curly);
                    if (!envP->fault_occurred)
                        sum += curly;
                }
            }
            xmlrpc_DECREF(array);
            if (!envP->fault_occurred)
                retval = xmlrpc_build_value(envP, "i", sum);
        }
    }
    return retval;
}


/*=========================================================================
**  validator1.countTheEntities
**=========================================================================
*/

static xmlrpc_value *
count_entities(xmlrpc_env *   const env, 
               xmlrpc_value * const param_array, 
               void *         const user_data ATTR_UNUSED) {
    const char *str;
    size_t len, i;
    xmlrpc_int32 left, right, amp, apos, quote;

    xmlrpc_decompose_value(env, param_array, "(s#)", &str, &len);
    RETURN_IF_FAULT(env);

    left = right = amp = apos = quote = 0;
    for (i = 0; i < len; i++) {
        switch (str[i]) {
        case '<': left++; break;
        case '>': right++; break;
        case '&': amp++; break;
        case '\'': apos++; break;
        case '\"': quote++; break;
        default: break;
        }
    }
    free((void*)str);

    return xmlrpc_build_value(env, "{s:i,s:i,s:i,s:i,s:i}",
                              "ctLeftAngleBrackets", left,
                              "ctRightAngleBrackets", right,
                              "ctAmpersands", amp,
                              "ctApostrophes", apos,
                              "ctQuotes", quote);
}



/*=========================================================================
**  validator1.easyStructTest
**=========================================================================
*/

static xmlrpc_value *
easy_struct(xmlrpc_env *   const env, 
            xmlrpc_value * const param_array,
            void *         const user_data ATTR_UNUSED) {

    xmlrpc_int32 larry, moe, curly;

    /* Parse our argument array and get the stooges. */
    xmlrpc_decompose_value(env, param_array, "({s:i,s:i,s:i,*})",
                           "larry", &larry,
                           "moe", &moe,
                           "curly", &curly);
    RETURN_IF_FAULT(env);

    /* Return our result. */
    return xmlrpc_build_value(env, "i", larry + moe + curly);
}


/*=========================================================================
**  validator1.echoStructTest
**=========================================================================
*/

static xmlrpc_value *
echo_struct(xmlrpc_env *   const env, 
            xmlrpc_value * const param_array, 
            void *         const user_data ATTR_UNUSED) {
    xmlrpc_value *s;

    /* Parse our argument array. */
    xmlrpc_decompose_value(env, param_array, "(S)", &s);
    RETURN_IF_FAULT(env);
    
    return s;  /* We transfer our reference on 's' to Caller */
}


/*=========================================================================
**  validator1.manyTypesTest
**=========================================================================
*/

static xmlrpc_value *
many_types(xmlrpc_env *   const env ATTR_UNUSED, 
           xmlrpc_value * const param_array, 
           void *         const user_data ATTR_UNUSED) {

    /* Create another reference to our argument array and return it as is. */
    xmlrpc_INCREF(param_array);
    return param_array;
}


/*=========================================================================
**  validator1.moderateSizeArrayCheck
**=========================================================================
*/

static void
concatenate(xmlrpc_env *    const envP,
            const char *    const str1,
            size_t          const str1_len,
            const char *    const str2,
            size_t          const str2_len,
            xmlrpc_value ** const resultP) {

    /* Concatenate the two strings. */

    char * buffer;

    buffer = (char*) malloc(str1_len + str2_len);
    if (!buffer) {
        xmlrpc_env_set_fault(envP, 1, 
                             "Couldn't allocate concatenated string");
    } else {
        memcpy(buffer, str1, str1_len);
        memcpy(&buffer[str1_len], str2, str2_len);
        *resultP = xmlrpc_build_value(envP, "s#", 
                                      buffer, str1_len + str2_len);
        free(buffer);
    }
}



static xmlrpc_value *
moderate_array(xmlrpc_env *   const envP, 
               xmlrpc_value * const param_array, 
               void *         const user_data ATTR_UNUSED) {

    xmlrpc_value *array, *item, *result;
    int size;
    const char * str1;
    const char * str2;
    size_t str1_len, str2_len;
    xmlrpc_value * retval;

    /* Parse our argument array. */
    xmlrpc_decompose_value(envP, param_array, "(A)", &array);
    if (!envP->fault_occurred) {
        size = xmlrpc_array_size(envP, array);
        if (!envP->fault_occurred) {
            /* Get our first string. */
            item = xmlrpc_array_get_item(envP, array, 0);
            if (!envP->fault_occurred) {
                xmlrpc_read_string_lp(envP, item, &str1_len, &str1);
                if (!envP->fault_occurred) {
                    /* Get our last string. */
                    item = xmlrpc_array_get_item(envP, array, size - 1);
                    if (!envP->fault_occurred) {
                        xmlrpc_read_string_lp(envP, item, &str2_len, &str2);
                        if (!envP->fault_occurred) {
                            concatenate(envP, str1, str1_len, str2, str2_len,
                                        &retval);
                            free((char*)str2);
                        }
                    }
                    free((char*)str1);
                }
            }
        }
        xmlrpc_DECREF(array);
    }
    return result;
}


/*=========================================================================
**  validator1.nestedStructTest
**=========================================================================
*/

static xmlrpc_value *
nested_struct(xmlrpc_env *   const envP,
              xmlrpc_value * const param_array, 
              void *         const user_data ATTR_UNUSED) {

    xmlrpc_value * yearsP;
    xmlrpc_value * retval;

    /* Parse our argument array. */
    xmlrpc_decompose_value(envP, param_array, "(S)", &yearsP);
    if (!envP->fault_occurred) {
        /* Get values of larry, moe and curly for 2000-04-01. */
        xmlrpc_int32 larry, moe, curly;
        xmlrpc_decompose_value(envP, yearsP,
                               "{s:{s:{s:{s:i,s:i,s:i,*},*},*},*}",
                               "2000", "04", "01",
                               "larry", &larry,
                               "moe", &moe,
                               "curly", &curly);               
        if (!envP->fault_occurred)
            retval = xmlrpc_build_value(envP, "i", larry + moe + curly);
        xmlrpc_DECREF(yearsP);
    }
    return retval;
}


/*=========================================================================
**  validator1.simpleStructReturnTest
**=========================================================================
*/

static xmlrpc_value *
struct_return(xmlrpc_env *   const env, 
              xmlrpc_value * const param_array, 
              void *         const user_data ATTR_UNUSED) {

    xmlrpc_int32 i;

    xmlrpc_decompose_value(env, param_array, "(i)", &i);
    RETURN_IF_FAULT(env);

    return xmlrpc_build_value(env, "{s:i,s:i,s:i}",
                              "times10", (xmlrpc_int32) i * 10,
                              "times100", (xmlrpc_int32) i * 100,
                              "times1000", (xmlrpc_int32) i * 1000);
}


/*=========================================================================
**  main
**=========================================================================
*/

int main(int           const argc, 
         const char ** const argv) {

    xmlrpc_server_abyss_parms serverparm;
    xmlrpc_registry * registryP;
    xmlrpc_env env;

    if (argc-1 != 1) {
        fprintf(stderr, "You must specify 1 argument:  The TCP port "
                "number on which the server will accept connections "
                "for RPCs.  You specified %d arguments.\n",  argc-1);
        exit(1);
    }

    xmlrpc_env_init(&env);

    registryP = xmlrpc_registry_new(&env);

    xmlrpc_registry_add_method(
        &env, registryP, NULL, "validator1.arrayOfStructsTest", 
        &array_of_structs, NULL);
    xmlrpc_registry_add_method(
        &env, registryP, NULL, "validator1.countTheEntities", 
        &count_entities, NULL);
    xmlrpc_registry_add_method(
        &env, registryP, NULL, "validator1.easyStructTest", 
        &easy_struct, NULL);
    xmlrpc_registry_add_method(
        &env, registryP, NULL, "validator1.echoStructTest", 
        &echo_struct, NULL);
    xmlrpc_registry_add_method(
        &env, registryP, NULL, "validator1.manyTypesTest", 
        &many_types, NULL);
    xmlrpc_registry_add_method(
        &env, registryP, NULL, "validator1.moderateSizeArrayCheck", 
        &moderate_array, NULL);
    xmlrpc_registry_add_method(
        &env, registryP, NULL, "validator1.nestedStructTest", 
        &nested_struct, NULL);
    xmlrpc_registry_add_method(
        &env, registryP, NULL, "validator1.simpleStructReturnTest", 
        &struct_return, NULL);

    serverparm.config_file_name = NULL;
    serverparm.registryP = registryP;
    serverparm.port_number = atoi(argv[1]);
    serverparm.log_file_name = NULL;

    printf("Running XML-RPC server...\n");

    xmlrpc_server_abyss(&env, &serverparm, XMLRPC_APSIZE(log_file_name));

    /* This never gets executed. */
    return 0;
}
