/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <axiom_xml_reader.h>
#include <libxml/xmlreader.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <axutil_utils_defines.h>
#include <axutil_utils.h>
#include <string.h>
#include <axutil_string.h>

/**
 * Security limits for XML parsing to prevent DoS attacks.
 * These values are conservative defaults suitable for SOAP/XML web services.
 */

/* Maximum element nesting depth (prevents deeply nested XML bombs) */
#define AXIS2_XML_MAX_DEPTH 256

/* Maximum entity expansion (bytes) - prevents billion laughs attack */
#define AXIS2_XML_MAX_ENTITY_SIZE (1024 * 1024)  /* 1 MB */

/* Maximum number of attributes per element */
#define AXIS2_XML_MAX_ATTRIBUTES 256

/* Maximum number of namespace declarations per element */
#define AXIS2_XML_MAX_NAMESPACES 64

/**
 * Secure XML parsing options to prevent XXE (XML External Entity) attacks.
 *
 * Security flags explained:
 *
 * - XML_PARSE_RECOVER:    Recover from errors gracefully
 * - XML_PARSE_NONET:      Forbid network access (prevents SSRF via XXE)
 * - XML_PARSE_NOXINCNODE: Disable XInclude processing (prevents file inclusion)
 * - XML_PARSE_NOENT:      Substitute entities - libxml2 2.9+ has built-in
 *                         entity expansion limits that prevent billion laughs
 *
 * Flags intentionally NOT set (for security):
 * - XML_PARSE_HUGE:       Would allow unlimited document size (DoS risk)
 * - XML_PARSE_DTDLOAD:    Would load external DTD (XXE risk)
 * - XML_PARSE_DTDVALID:   Would validate against DTD (external resource risk)
 * - XML_PARSE_DTDATTR:    Would default attributes from DTD (external resource risk)
 *
 * Note: The guththila parser (default) is already safe as it drops all
 * unknown entities. This hardening applies only when libxml2 is used.
 */
#define AXIS2_LIBXML2_SECURE_PARSE_OPTIONS \
    (XML_PARSE_RECOVER | XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_NOXINCNODE)


int AXIS2_CALL
axis2_libxml2_reader_wrapper_next(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

void AXIS2_CALL
axis2_libxml2_reader_wrapper_free(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

int AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_count(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_name_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_prefix_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_value_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_namespace_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_value(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

int AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_count(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_uri_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_prefix_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_prefix(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_name(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_pi_target(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_pi_data(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_dtd(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

void AXIS2_CALL
axis2_libxml2_reader_wrapper_xml_free(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    void *data);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_char_set_encoding(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_uri(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_uri_by_prefix(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    axis2_char_t * prefix);

axis2_status_t
axis2_libxml2_reader_wrapper_fill_maps(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env);

void
axis2_libxml2_reader_wrapper_error_handler(
    void *arg,
    const char *msg,
    int severities,
    void *locator_ptr);

static int
axis2_libxml2_reader_wrapper_read_input_callback(
    void *ctx,
    char *buffer,
    int size);

static int
axis2_libxml2_reader_wrapper_close_input_callback(
    void *ctx);

typedef struct axis2_libxml2_reader_wrapper_impl_t
{
    axiom_xml_reader_t parser;

    xmlTextReaderPtr reader;

    int current_event;
    int current_attribute_count;
    int current_namespace_count;
    int event_map[18];

    void *ctx;
    /* assuming that max ns and attribute will be 20 */

    int *namespace_map;
    int *attribute_map;

    AXIS2_READ_INPUT_CALLBACK read_input_callback;

    AXIS2_CLOSE_INPUT_CALLBACK close_input_callback;

} axis2_libxml2_reader_wrapper_impl_t;

#define AXIS2_INTF_TO_IMPL(p) ((axis2_libxml2_reader_wrapper_impl_t*)p)
#define AXIS2_IMPL_TO_INTF(p) &(p->parser)

static const axiom_xml_reader_ops_t axiom_xml_reader_ops_var = {
    axis2_libxml2_reader_wrapper_next,
    axis2_libxml2_reader_wrapper_free,
    axis2_libxml2_reader_wrapper_get_attribute_count,
    axis2_libxml2_reader_wrapper_get_attribute_name_by_number,
    axis2_libxml2_reader_wrapper_get_attribute_prefix_by_number,
    axis2_libxml2_reader_wrapper_get_attribute_value_by_number,
    axis2_libxml2_reader_wrapper_get_attribute_namespace_by_number,
    axis2_libxml2_reader_wrapper_get_value,
    axis2_libxml2_reader_wrapper_get_namespace_count,
    axis2_libxml2_reader_wrapper_get_namespace_uri_by_number,
    axis2_libxml2_reader_wrapper_get_namespace_prefix_by_number,
    axis2_libxml2_reader_wrapper_get_prefix,
    axis2_libxml2_reader_wrapper_get_name,
    axis2_libxml2_reader_wrapper_get_pi_target,
    axis2_libxml2_reader_wrapper_get_pi_data,
    axis2_libxml2_reader_wrapper_get_dtd,
    axis2_libxml2_reader_wrapper_xml_free,
    axis2_libxml2_reader_wrapper_get_char_set_encoding,
    axis2_libxml2_reader_wrapper_get_namespace_uri,
    axis2_libxml2_reader_wrapper_get_namespace_uri_by_prefix };

static axis2_status_t
axis2_libxml2_reader_wrapper_init_map(
    axis2_libxml2_reader_wrapper_impl_t * parser)
{
    int i = 0;
    if(parser)
    {
        for(i = 0; i < 18; i++)
        {
            parser->event_map[i] = -1;
        }

        parser->event_map[XML_READER_TYPE_ELEMENT] = AXIOM_XML_READER_START_ELEMENT;
        parser->event_map[XML_READER_TYPE_DOCUMENT] = AXIOM_XML_READER_START_DOCUMENT;
        parser->event_map[XML_READER_TYPE_TEXT] = AXIOM_XML_READER_CHARACTER;
        parser->event_map[XML_READER_TYPE_CDATA] = AXIOM_XML_READER_CHARACTER;
        parser->event_map[XML_READER_TYPE_SIGNIFICANT_WHITESPACE] = AXIOM_XML_READER_SPACE;
        parser->event_map[XML_READER_TYPE_WHITESPACE] = AXIOM_XML_READER_SPACE;
        parser->event_map[XML_READER_TYPE_END_ELEMENT] = AXIOM_XML_READER_END_ELEMENT;
        parser->event_map[XML_READER_TYPE_ENTITY_REFERENCE] = AXIOM_XML_READER_ENTITY_REFERENCE;
        parser->event_map[XML_READER_TYPE_END_ENTITY] = AXIOM_XML_READER_SPACE;
        parser->event_map[XML_READER_TYPE_ENTITY] = AXIOM_XML_READER_SPACE;
        parser->event_map[XML_READER_TYPE_PROCESSING_INSTRUCTION]
            = AXIOM_XML_READER_PROCESSING_INSTRUCTION;
        parser->event_map[XML_READER_TYPE_COMMENT] = AXIOM_XML_READER_COMMENT;
        parser->event_map[XML_READER_TYPE_DOCUMENT_TYPE] = AXIOM_XML_READER_DOCUMENT_TYPE;
        return AXIS2_SUCCESS;
    }
    return AXIS2_FAILURE;
}

/**
 * Secure external entity loader that blocks all external entity resolution.
 * This is a defense-in-depth measure against XXE attacks.
 *
 * Even though XML_PARSE_NONET is set, this provides additional protection
 * if parsing options are incorrectly specified elsewhere in the codebase.
 */
static xmlParserInputPtr
axis2_secure_external_entity_loader(
    const char *URL,
    const char *ID,
    xmlParserCtxtPtr ctxt)
{
    /*
     * Security: Block ALL external entity resolution.
     * This prevents:
     * - XXE attacks (reading local files)
     * - SSRF attacks (making network requests)
     * - Denial of service (loading huge external resources)
     *
     * Legitimate SOAP/XML web services should not require external entities.
     * If external entity support is needed, it should be explicitly enabled
     * with appropriate whitelisting (not implemented - too risky).
     */
    if (ctxt != NULL && ctxt->sax != NULL && ctxt->sax->warning != NULL)
    {
        ctxt->sax->warning(ctxt->userData,
            "Security: External entity blocked (URL=%s, ID=%s)\n",
            URL ? URL : "NULL", ID ? ID : "NULL");
    }

    /* Return NULL to indicate entity could not be loaded */
    return NULL;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_xml_reader_init()
{
    xmlInitParser();

    /*
     * Configure global security limits for libxml2.
     * These protect against various XML-based DoS attacks.
     *
     * Note: libxml2 2.9+ has built-in entity expansion limits that
     * automatically protect against billion laughs / XML bomb attacks.
     * The default limit is approximately 10MB of expanded content.
     *
     * For additional protection, we install a custom entity loader
     * that blocks all external entity resolution (defense in depth).
     */

    /* Install secure entity loader that blocks external entities */
    xmlSetExternalEntityLoader(axis2_secure_external_entity_loader);

    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_xml_reader_cleanup()
{
    xmlCleanupParser();
    return AXIS2_SUCCESS;
}

static axis2_libxml2_reader_wrapper_impl_t*
libxml2_reader_wrapper_create(
    const axutil_env_t *env)
{
    axis2_libxml2_reader_wrapper_impl_t *wrapper_impl = NULL;
    wrapper_impl = (axis2_libxml2_reader_wrapper_impl_t *)AXIS2_MALLOC(env->allocator,
        sizeof(axis2_libxml2_reader_wrapper_impl_t));

    if(!wrapper_impl)
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "No memory. Cannot create libxml2 reader wrapper");
        return NULL;
    }
    memset(wrapper_impl, 0, sizeof(axis2_libxml2_reader_wrapper_impl_t));
    wrapper_impl->attribute_map = NULL;
    wrapper_impl->namespace_map = NULL;
    wrapper_impl->close_input_callback = NULL;
    wrapper_impl->read_input_callback = NULL;
    wrapper_impl->ctx = NULL;
    wrapper_impl->current_namespace_count = 0;
    wrapper_impl->current_attribute_count = 0;
    wrapper_impl->current_event = -1;
    return wrapper_impl;
}

AXIS2_EXTERN axiom_xml_reader_t *AXIS2_CALL
axiom_xml_reader_create_for_file(
    const axutil_env_t * env,
    char *filename,
    const axis2_char_t * encoding)
{

    axis2_libxml2_reader_wrapper_impl_t *wrapper_impl = NULL;
    AXIS2_ENV_CHECK(env, NULL);
    AXIS2_PARAM_CHECK(env->error, filename, NULL);

    wrapper_impl = libxml2_reader_wrapper_create(env);
    if(!wrapper_impl)
    {
        return NULL;
    }

    wrapper_impl->reader = xmlReaderForFile(filename, encoding, AXIS2_LIBXML2_SECURE_PARSE_OPTIONS);
    if(!(wrapper_impl->reader))
    {
        AXIS2_FREE(env->allocator, wrapper_impl);
        AXIS2_HANDLE_ERROR(env, AXIS2_ERROR_CREATING_XML_STREAM_READER, AXIS2_FAILURE);
        return NULL;
    }

    xmlTextReaderSetErrorHandler(wrapper_impl->reader,
        (xmlTextReaderErrorFunc)axis2_libxml2_reader_wrapper_error_handler, (void *)env);
    wrapper_impl->current_event = -1;
    wrapper_impl->ctx = NULL;
    axis2_libxml2_reader_wrapper_init_map(wrapper_impl);

    wrapper_impl->parser.ops = &axiom_xml_reader_ops_var;
    return &(wrapper_impl->parser);
}

AXIS2_EXTERN axiom_xml_reader_t *AXIS2_CALL
axiom_xml_reader_create_for_io(
    const axutil_env_t * env,
    AXIS2_READ_INPUT_CALLBACK read_input_callback,
    AXIS2_CLOSE_INPUT_CALLBACK close_input_callback,
    void *ctx,
    const axis2_char_t * encoding)
{
    axis2_libxml2_reader_wrapper_impl_t *wrapper_impl = NULL;

    AXIS2_ENV_CHECK(env, NULL);

    if(!read_input_callback)
    {
        return NULL;
    }

    wrapper_impl = libxml2_reader_wrapper_create(env);
    if(!wrapper_impl)
    {
        return NULL;
    }
    wrapper_impl->close_input_callback = NULL;
    wrapper_impl->read_input_callback = NULL;
    wrapper_impl->read_input_callback = read_input_callback;
    wrapper_impl->close_input_callback = close_input_callback;
    wrapper_impl->ctx = ctx;
    if(wrapper_impl->close_input_callback)
    {
        wrapper_impl->reader = xmlReaderForIO(axis2_libxml2_reader_wrapper_read_input_callback,
            axis2_libxml2_reader_wrapper_close_input_callback, wrapper_impl, NULL, encoding,
            AXIS2_LIBXML2_SECURE_PARSE_OPTIONS);
    }
    else
    {
        wrapper_impl->reader = xmlReaderForIO(axis2_libxml2_reader_wrapper_read_input_callback,
            NULL, wrapper_impl, NULL, encoding, AXIS2_LIBXML2_SECURE_PARSE_OPTIONS);
    }
    if(!(wrapper_impl->reader))
    {
        AXIS2_FREE(env->allocator, wrapper_impl);
        AXIS2_HANDLE_ERROR(env, AXIS2_ERROR_CREATING_XML_STREAM_READER, AXIS2_FAILURE);
        return NULL;
    }

    xmlTextReaderSetErrorHandler(wrapper_impl->reader,
        (xmlTextReaderErrorFunc)axis2_libxml2_reader_wrapper_error_handler, (void *)env);

    wrapper_impl->current_event = -1;

    axis2_libxml2_reader_wrapper_init_map(wrapper_impl);

    wrapper_impl->parser.ops = &axiom_xml_reader_ops_var;
    return &(wrapper_impl->parser);
}

AXIS2_EXTERN axiom_xml_reader_t *AXIS2_CALL
axiom_xml_reader_create_for_memory(
    const axutil_env_t * env,
    void *container,
    int size,
    const axis2_char_t * encoding,
    int type)
{
    axis2_libxml2_reader_wrapper_impl_t *wrapper_impl = NULL;

    AXIS2_PARAM_CHECK(env->error, container, NULL);

    wrapper_impl = libxml2_reader_wrapper_create(env);
    if(!wrapper_impl)
    {
        return NULL;
    }
    wrapper_impl->close_input_callback = NULL;
    wrapper_impl->read_input_callback = NULL;
    wrapper_impl->ctx = NULL;

    if(AXIS2_XML_PARSER_TYPE_BUFFER == type)
    {
        wrapper_impl->reader = xmlReaderForMemory((axis2_char_t *)container, size, NULL, encoding,
            AXIS2_LIBXML2_SECURE_PARSE_OPTIONS);
    }
    else if(AXIS2_XML_PARSER_TYPE_DOC == type)
    {
        wrapper_impl->reader = xmlReaderWalker((xmlDocPtr)container);
    }
    else
    {
        AXIS2_FREE(env->allocator, wrapper_impl);
        AXIS2_HANDLE_ERROR(env, AXIS2_ERROR_XML_PARSER_INVALID_MEM_TYPE, AXIS2_FAILURE);
        return NULL;
    }

    if(!(wrapper_impl->reader))
    {
        AXIS2_FREE(env->allocator, wrapper_impl);
        AXIS2_HANDLE_ERROR(env, AXIS2_ERROR_CREATING_XML_STREAM_READER, AXIS2_FAILURE);
        return NULL;
    }

    if(AXIS2_XML_PARSER_TYPE_BUFFER == type)
    {
        xmlTextReaderSetErrorHandler(wrapper_impl->reader,
            (xmlTextReaderErrorFunc)axis2_libxml2_reader_wrapper_error_handler, (void *)env);
    }

    wrapper_impl->current_event = -1;

    axis2_libxml2_reader_wrapper_init_map(wrapper_impl);

    wrapper_impl->parser.ops = &axiom_xml_reader_ops_var;
    return &(wrapper_impl->parser);
}

int AXIS2_CALL
axis2_libxml2_reader_wrapper_next(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    int ret_val = 0;
    int node = 2;
    int empty_check = 0;
    int depth = 0;
    axis2_libxml2_reader_wrapper_impl_t *parser_impl;
    AXIS2_ENV_CHECK(env, -1);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    ret_val = xmlTextReaderRead(parser_impl->reader);
    if(ret_val == 0)
    {
        AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "xml stream is over ");
    }
    if(ret_val == -1)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, " error occurred in reading xml stream ");
        return -1;
    }

    if(ret_val == 1)
    {
        /*
         * Security: Check element nesting depth to prevent stack exhaustion
         * from deeply nested XML (XML bomb variant).
         */
        depth = xmlTextReaderDepth(parser_impl->reader);
        if(depth < 0)
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                "Error: xmlTextReaderDepth returned error (%d). Aborting parsing.", depth);
            return -1;
        }
        if(depth >= AXIS2_XML_MAX_DEPTH)
        {
            const xmlChar *base_uri = xmlTextReaderConstBaseUri(parser_impl->reader);
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                "Security: XML nesting depth exceeded (%d >= %d) in %s. Possible DoS attack.",
                depth, AXIS2_XML_MAX_DEPTH, base_uri ? (const char*)base_uri : "unknown source");
            return -1;
        }

        node = xmlTextReaderNodeType(parser_impl->reader);
        parser_impl->current_event = parser_impl->event_map[node];
        parser_impl->current_attribute_count = 0;
        parser_impl->current_namespace_count = 0;

        if(node == XML_READER_TYPE_ELEMENT)
        {
            empty_check = xmlTextReaderIsEmptyElement(parser_impl->reader);
            if(axis2_libxml2_reader_wrapper_fill_maps(parser, env) != AXIS2_SUCCESS)
            {
                /* Security limit exceeded in fill_maps */
                return -1;
            }
        }
        if(empty_check == 1)
        {
            parser_impl->current_event = AXIOM_XML_READER_EMPTY_ELEMENT;
            return AXIOM_XML_READER_EMPTY_ELEMENT;
        }
        return parser_impl->event_map[node];
    }
    else
    {
        return -1;
    }
}

/**
 * If your application crashes here, it may be due to an earlier call to
 * xmlCleanupParser() function. In client API, op_client create function has a call
 * to axiom_xml_reader_init and op_client_free function has a call to axiom_xml_reader_cleanup
 * function. You can avoid the call to axiom_xml_reader_cleanup using
 * axis2_options_set_xml_parser_reset function in client API.
 * refer to jira issue:  https://issues.apache.org/jira/browse/AXIS2C-884
 */
void AXIS2_CALL
axis2_libxml2_reader_wrapper_free(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    parser_impl = AXIS2_INTF_TO_IMPL(parser);

    /* AXIS2C-1222: Close the reader BEFORE freeing ctx.
     * xmlTextReaderClose triggers the close_input_callback which needs
     * parser_impl->ctx to still be valid. */
    if(parser_impl->reader)
    {
        xmlTextReaderClose(parser_impl->reader);
        xmlFreeTextReader(parser_impl->reader);
    }

    if(parser_impl->ctx)
    {
        AXIS2_FREE(env->allocator, parser_impl->ctx);
    }
    if(parser_impl->namespace_map)
    {
        AXIS2_FREE(env->allocator, parser_impl->namespace_map);
        parser_impl->namespace_map = NULL;
    }
    if(parser_impl->attribute_map)
    {
        AXIS2_FREE(env->allocator, parser_impl->attribute_map);
        parser_impl->attribute_map = NULL;
    }
    AXIS2_FREE(env->allocator, AXIS2_INTF_TO_IMPL(parser));
    return;
}

int AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_count(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    if(parser_impl->current_event == AXIOM_XML_READER_START_ELEMENT || parser_impl->current_event
        == AXIOM_XML_READER_EMPTY_ELEMENT)
    {
        return parser_impl->current_attribute_count;
    }
    else
    {
        return 0;
    }
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_name_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl;
    AXIS2_ENV_CHECK(env, NULL);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);

    if(parser_impl->current_attribute_count > 0 && parser_impl->current_attribute_count >= i)
    {
        int ret =
            xmlTextReaderMoveToAttributeNo(parser_impl->reader, parser_impl->attribute_map[i]);
        if(ret == 1)
        {
            return (axis2_char_t *)xmlTextReaderLocalName(parser_impl->reader);
        }
        else
        {
            return NULL;
        }
    }
    return NULL;
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_prefix_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    AXIS2_ENV_CHECK(env, NULL);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);

    if(parser_impl->current_attribute_count > 0 && parser_impl->current_attribute_count >= i)
    {
        int ret =
            xmlTextReaderMoveToAttributeNo(parser_impl->reader, parser_impl->attribute_map[i]);
        if(ret == 1)
        {
            return (axis2_char_t *)xmlTextReaderPrefix(parser_impl->reader);
        }
        else
        {
            return NULL;
        }
    }
    return NULL;
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_value_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i)
{

    axis2_libxml2_reader_wrapper_impl_t *parser_impl;
    AXIS2_ENV_CHECK(env, NULL);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);

    if(parser_impl->current_attribute_count > 0 && parser_impl->current_attribute_count >= i)
    {
        int ret =
            xmlTextReaderMoveToAttributeNo(parser_impl->reader, parser_impl->attribute_map[i]);
        if(ret == 1)
        {
            return (axis2_char_t *)xmlTextReaderValue(parser_impl->reader);
        }
        else
        {
            return NULL;
        }
    }
    return NULL;
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_attribute_namespace_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl;
    AXIS2_ENV_CHECK(env, NULL);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);

    if(parser_impl->current_attribute_count > 0 && parser_impl->current_attribute_count >= i)
    {
        int ret =
            xmlTextReaderMoveToAttributeNo(parser_impl->reader, parser_impl->attribute_map[i]);

        if(ret == 1)
        {
            return (axis2_char_t *)xmlTextReaderNamespaceUri(parser_impl-> reader);
        }
        else
        {
            return NULL;
        }
    }
    return NULL;
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_value(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;

    AXIS2_ENV_CHECK(env, NULL);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    return (axis2_char_t *)xmlTextReaderValue(parser_impl->reader);

}

int AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_count(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    if(parser_impl->current_event == AXIOM_XML_READER_START_ELEMENT || parser_impl->current_event
        == AXIOM_XML_READER_EMPTY_ELEMENT)
    {
        return parser_impl->current_namespace_count;
    }
    else
    {
        return 0;
    }
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_uri_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    AXIS2_ENV_CHECK(env, NULL);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);

    if(parser_impl->current_namespace_count > 0 && parser_impl->current_namespace_count >= i)
    {

        int ret =
            xmlTextReaderMoveToAttributeNo(parser_impl->reader, parser_impl->namespace_map[i]);
        if(ret == 1)
        {
            return (axis2_char_t *)xmlTextReaderValue(parser_impl->reader);
        }
        else
            return NULL;
    }
    return NULL;
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_prefix_by_number(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    int i)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    AXIS2_ENV_CHECK(env, NULL);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);

    if(parser_impl->current_namespace_count > 0 && parser_impl->current_namespace_count >= i)
    {
        int ret =
            xmlTextReaderMoveToAttributeNo(parser_impl->reader, parser_impl->namespace_map[i]);

        if(ret == 1)
        {
            return (axis2_char_t *)xmlTextReaderLocalName(parser_impl->reader);
        }
        else
        {
            return NULL;
        }
    }
    return NULL;
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_prefix(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    xmlTextReaderMoveToElement(parser_impl->reader);
    return (axis2_char_t *)xmlTextReaderPrefix(parser_impl->reader);
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_name(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    xmlTextReaderMoveToElement(parser_impl->reader);
    return (axis2_char_t *)xmlTextReaderLocalName(parser_impl->reader);
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_pi_target(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    if(parser_impl->current_event == AXIOM_XML_READER_PROCESSING_INSTRUCTION)
    {
        return (axis2_char_t *)xmlTextReaderLocalName(parser_impl->reader);
    }
    else
    {
        return NULL;
    }
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_dtd(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    if(parser_impl->current_event == AXIOM_XML_READER_DOCUMENT_TYPE)
    {
        return (axis2_char_t *)xmlTextReaderLocalName(parser_impl->reader);
    }
    else
    {
        return NULL;
    }
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_pi_data(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    if(parser_impl->current_event == AXIOM_XML_READER_PROCESSING_INSTRUCTION)
    {
        return (axis2_char_t *)xmlTextReaderValue(parser_impl->reader);
    }
    else
    {
        return NULL;
    }
}

void AXIS2_CALL
axis2_libxml2_reader_wrapper_xml_free(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    void *data)
{
    AXIS2_ENV_CHECK(env, void);
    if(data)
        xmlFree(data);
    return;
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_char_set_encoding(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *reader_impl = NULL;
    reader_impl = AXIS2_INTF_TO_IMPL(parser);
    return (axis2_char_t *)xmlTextReaderConstEncoding(reader_impl->reader);
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_uri(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    return (axis2_char_t *)xmlTextReaderNamespaceUri(parser_impl->reader);
}

axis2_char_t *AXIS2_CALL
axis2_libxml2_reader_wrapper_get_namespace_uri_by_prefix(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env,
    axis2_char_t * prefix)
{
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    parser_impl = AXIS2_INTF_TO_IMPL(parser);
    if(!prefix || axutil_strcmp(prefix, "") == 0)
    {
        return NULL;
    }
    return (axis2_char_t *)xmlTextReaderLookupNamespace(parser_impl->reader,
        (const xmlChar *)prefix);
}

axis2_status_t
axis2_libxml2_reader_wrapper_fill_maps(
    axiom_xml_reader_t * parser,
    const axutil_env_t * env)
{
    int libxml2_attribute_count = 0;
    int attr_count = 0;
    int ns_count = 0;
    int i = 0;
    char *q_name = NULL;
    axis2_libxml2_reader_wrapper_impl_t *parser_impl = NULL;
    int map_size = 0;
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    parser_impl = AXIS2_INTF_TO_IMPL(parser);

    libxml2_attribute_count = xmlTextReaderAttributeCount(parser_impl->reader);
    if(libxml2_attribute_count == 0)
    {
        parser_impl->current_attribute_count = 0;
        parser_impl->current_namespace_count = 0;
        return AXIS2_SUCCESS;
    }

    /*
     * Security: Enforce attribute count limit to prevent DoS attacks.
     * Malicious XML with thousands of attributes can exhaust memory.
     */
    if(libxml2_attribute_count > (AXIS2_XML_MAX_ATTRIBUTES + AXIS2_XML_MAX_NAMESPACES))
    {
        const xmlChar *elem_name = xmlTextReaderConstLocalName(parser_impl->reader);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
            "Security: XML element '%s' has too many attributes (%d > %d). Possible DoS attack.",
            elem_name ? (const char*)elem_name : "unknown",
            libxml2_attribute_count, AXIS2_XML_MAX_ATTRIBUTES + AXIS2_XML_MAX_NAMESPACES);
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_XML_PARSER_INVALID_MEM_TYPE, AXIS2_FAILURE);
        return AXIS2_FAILURE;
    }

    map_size = libxml2_attribute_count + 1;
    if(parser_impl->namespace_map)
    {
        AXIS2_FREE(env->allocator, parser_impl->namespace_map);
        parser_impl->namespace_map = NULL;
    }
    if(parser_impl->attribute_map)
    {
        AXIS2_FREE(env->allocator, parser_impl->attribute_map);
        parser_impl->attribute_map = NULL;
    }
    parser_impl->attribute_map = AXIS2_MALLOC(env->allocator, sizeof(int) * map_size);
    memset(parser_impl->attribute_map, 0, map_size * sizeof(int));

    parser_impl->namespace_map = AXIS2_MALLOC(env->allocator, sizeof(int) * map_size);
    memset(parser_impl->namespace_map, 0, map_size * sizeof(int));

    for(i = 0; i < map_size; i++)
    {
        parser_impl->namespace_map[i] = -1;
        parser_impl->attribute_map[i] = -1;
    }

    for(i = 0; i < libxml2_attribute_count; i++)
    {
        xmlTextReaderMoveToAttributeNo(parser_impl->reader, i);
        q_name = (char *)xmlTextReaderName(parser_impl->reader);
        if(q_name)
        {
            if((strcmp(q_name, "xmlns") == 0) || (strncmp(q_name, "xmlns:", 6) == 0))
            {
                /* found a namespace */
                ns_count++;
                parser_impl->namespace_map[ns_count] = i;
            }
            else
            {
                /* found an attribute */
                attr_count++;
                parser_impl->attribute_map[attr_count] = i;
            }

            xmlFree(q_name);
            q_name = NULL;
        }

        parser_impl->current_attribute_count = attr_count;
        parser_impl->current_namespace_count = ns_count;
    }
    return AXIS2_SUCCESS;
}

static int
axis2_libxml2_reader_wrapper_read_input_callback(
    void *ctx,
    char *buffer,
    int size)
{
    return ((axis2_libxml2_reader_wrapper_impl_t *)ctx)-> read_input_callback(buffer, size,
        ((axis2_libxml2_reader_wrapper_impl_t *)ctx)->ctx);
}

void
axis2_libxml2_reader_wrapper_error_handler(
    void *arg,
    const char *msg,
    int severities,
    void *locator_ptr)
{
    const axutil_env_t *env = NULL;
    env = (const axutil_env_t *)arg;

    switch(severities)
    {
        case XML_PARSER_SEVERITY_VALIDITY_WARNING:
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "%s VALIDITY WARNTING", msg);
        }
            break;
        case XML_PARSER_SEVERITY_VALIDITY_ERROR:
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "%s -- VALIDITY ERROR", msg);
        }
            break;
        case XML_PARSER_SEVERITY_WARNING:
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "%s -- VALIDITY ERROR", msg);
        }
            break;
        case XML_PARSER_SEVERITY_ERROR:
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "%s -- SEVERITY_ERROR", msg);
        }
            break;
        default:
            break;
    }
}

static int
axis2_libxml2_reader_wrapper_close_input_callback(
    void *ctx)
{
    return ((axis2_libxml2_reader_wrapper_impl_t *)ctx)-> close_input_callback(
        ((axis2_libxml2_reader_wrapper_impl_t *)ctx)-> ctx);
}
