/* -*- C++ -*- */
/*
 *   Copyright 2003-2004 The Apache Software Foundation.
 *
 *   Licensed 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 <apache1_3/httpd.h>
#include <apache1_3/http_config.h>
#include <apache1_3/http_core.h>
#include <apache1_3/http_log.h>
#include <apache1_3/http_protocol.h>
#include <apache1_3/http_main.h>
#include <axis/server/Packet.hpp>
#include <string.h>
#include <malloc.h>

#define AXIS_URI_EXTENSION "/axis"

/* Following is the character that should be used to separate the method name in
 * the SOAPAction header value. Ex: "http://localhost:80/axis/Calculator#Add"
 */
#define SOAPACTION_METHODNAME_SEPARATOR "#"

/* #define CHUNCKED_DATA_SUPPORTED */

#define xxx ap_log_rerror(APLOG_MARK,APLOG_ERR, globr,"logged here");
#define yyy ap_log_rerror(APLOG_MARK, APLOG_ERR, globr,"logged here");

/* file: mod_axis.c */
/* here's the content handler */

extern int process_request (Ax_soapstream* str);
/* extern int process(soapstream *);*/
extern unsigned char chEBuf[1024];

//#define SIZEOFMODULEBUFFER 4096
#define SIZEOFMODULEBUFFER 32

/*
 * This method adds the http header to the Ax_soapstream. These headers will be
 * dispatched later before sending the http body
 */
static void AXISCALL set_transport_information 
    (AXIS_TRANSPORT_INFORMATION_TYPE type, const char*value, 
    Ax_soapstream* stream)
{
    const char* key = NULL;
    switch (type)
    {
        case SOAPACTION_HEADER:  /* needed only in the client side ? */
            break;
        case SERVICE_URI:        /* need to set ? */
            break;
        case OPERATION_NAME:     /* need to set ? */
            break;
        case SOAP_MESSAGE_LENGTH:
            key = "Content-Length"; /* This is apache module and transport is 
				     * http so the key 
				     */
            break;
        default:;
    }
    if (key)
    {
        ap_table_set (((request_rec*) stream->str.op_stream)->headers_out,
            key, value);
    }
#ifdef CHUNCKED_DATA_SUPPORTED
    ap_send_http_header ((request_rec*) hdr->str.op_stream);
    /* Should we remove the sent headers ? Sanjaya ? */
#endif
}

/* Call initialize_module() [of Packet.h] from within this method */
static void module_init (server_rec* svr_rec, pool* p)
{
    initialize_module (1);
}

/*
 * If this module is not handling chunked data transfer, it accumulates all 
 * buffers before sending them so that the http header "Content-Length" can be 
 * sent before sending the http body
 */

static AXIS_TRANSPORT_STATUS AXISCALL send_response_bytes (const char *buffer, 
    const void* bufferid, const void* pStream)
{
    const Ax_soapstream* stream = (Ax_soapstream*) pStream;
#ifndef CHUNCKED_DATA_SUPPORTED
    int index;
    sendbuffers *pbuffers;
#endif

    if (!bufferid) /* temporary buffers should always sent immediately */
    {
        ap_rputs (buffer, (request_rec*) (stream->str.op_stream));
        return TRANSPORT_FINISHED;
    }

#ifdef CHUNCKED_DATA_SUPPORTED
    // Do we need to send any headers and length of this chunk ? Sanjaya ?
    ap_rputs (buffer, (request_rec *) (stream->str.op_stream));
    /* Do we need to send any indication to mark the end of this 
     * chunk ? Sanjaya ?
     */ 
    return TRANSPORT_FINISHED;
#else
    pbuffers = (sendbuffers*) stream->reserved1;
    for (index = 0; index < NO_OF_SERIALIZE_BUFFERS; index++)
    {
        if (!pbuffers[index].buffer)
        {
            pbuffers[index].buffer = buffer;
            pbuffers[index].bufferid = bufferid;
            break;
        }
    }
    return TRANSPORT_IN_PROGRESS;
#endif
}

/* Call initialize_process() [of Packet.h] from within this method */
static void axis_Init (server_rec* svr_rec, pool* p)
{
}

/*Call finalize_process() [of Packet.h] from within this method*/
static void axis_Fini (server_rec* svr_rec, pool* p)
{
}

/*
 * This function is called by the Axis Engine whenever it needs to get bytes 
 * from the transport layer.
 */
static AXIS_TRANSPORT_STATUS AXISCALL get_request_bytes (const char **req, 
    int* retsize, const Ax_soapstream* stream)
{
#ifdef USE_XERCES_PARSER
    int nBufSize = *retsize;
    int len_read;
    char* pBuffer = *req;
    if (!(*req))
        return TRANSPORT_FAILED;
    ap_hard_timeout ("util_read", (request_rec*) stream->str.ip_stream);
    len_read = ap_get_client_block ((request_rec*) stream->str.ip_stream, *req,
        nBufSize);
    ap_reset_timeout ((request_rec*) stream->str.ip_stream);
    *retsize = len_read;
    if (len_read < nBufSize)
    {
        pBuffer[len_read] = '\0';
        return TRANSPORT_FINISHED;
    }
    else
        return TRANSPORT_IN_PROGRESS;
#else
    /*How can I detect an error when reading stream ? Sanjaya ?
     * In case of an error set buffer to null, size 0 and 
     * return TRANSPORT_FAILED */
    int len_read;
    char* pBuffer = stream->reserved2;
    if (!pBuffer)
        return TRANSPORT_FAILED;
    ap_hard_timeout ("util_read", (request_rec*) stream->str.ip_stream);
    len_read = ap_get_client_block ((request_rec*) stream->str.ip_stream,
        pBuffer, SIZEOFMODULEBUFFER);
    ap_reset_timeout ((request_rec*) stream->str.ip_stream);
    *req = pBuffer;
    *retsize = len_read;
    if (len_read < SIZEOFMODULEBUFFER)
    {
        pBuffer[len_read] = '\0';
        return TRANSPORT_FINISHED;
    }
    else
        return TRANSPORT_IN_PROGRESS;
#endif
}

static void AXISCALL release_receive_buffer (const char* buffer, 
    const Ax_soapstream* stream)
{

}

static const char* AXISCALL get_transport_information 
    (AXIS_TRANSPORT_INFORMATION_TYPE type, Ax_soapstream* stream)
{
    const char* ptemp;
    switch (type)
    {
        case SOAPACTION_HEADER:
            return get_property (stream, "SOAPAction");
        case SERVICE_URI:
            if (strstr (stream->so.http->uri_path, AXIS_URI_EXTENSION))
            {
                return strstr (stream->so.http->uri_path, AXIS_URI_EXTENSION) +
                    strlen (AXIS_URI_EXTENSION) + 1;
            }
            else
            {
                return stream->so.http->uri_path;
            }
        case OPERATION_NAME:
            ptemp = get_property (stream, "SOAPAction");
            if (ptemp)
            {
                if (strstr (ptemp, SOAPACTION_METHODNAME_SEPARATOR))
                {
                    return strstr (ptemp, SOAPACTION_METHODNAME_SEPARATOR) +
                          strlen (SOAPACTION_METHODNAME_SEPARATOR);
                }
                else
                {
                    return ptemp;
                }
            }
        case SOAP_MESSAGE_LENGTH:
            return get_property (stream, "Content-Length");       
	    /* this is apache module and transport is http so the key */
        default:;
    }
    return NULL;
}

static int axis_handler (request_rec* req_rec)
{
    int rc;
    Ax_soapstream* sstr;
    array_header* arr;
#ifndef CHUNCKED_DATA_SUPPORTED
    int contentLength = 0;
    int index;
    sendbuffers* pbuffers;
    char strtonum[8];
#endif
    sstr = malloc (sizeof (Ax_soapstream));
    /*populate Ax_soapstream struct with relevant transport function pointers */
    sstr->transport.pSendFunct = send_response_bytes;
    sstr->transport.pGetFunct = get_request_bytes;
    sstr->transport.pSetTrtFunct = set_transport_information;
    sstr->transport.pGetTrtFunct = get_transport_information;
    sstr->transport.pRelBufFunct = release_receive_buffer;
    sstr->trtype = APTHTTP;
    sstr->so.http = malloc (sizeof (Ax_stream_http));
    /* req_rec is used as both input and output streams */
    sstr->str.ip_stream = req_rec;
    sstr->str.op_stream = req_rec;
    /* just add some sessionid */
    sstr->sessionid = "this is temporary session id";
#ifdef CHUNCKED_DATA_SUPPORTED
    /* TODO */
    sstr->reserved1 = NULL;
    sstr->reserved2 = NULL;
#else
    sstr->reserved1 = calloc (NO_OF_SERIALIZE_BUFFERS, sizeof (sendbuffers));
#ifdef USE_XERCES_PARSER
#else
    sstr->reserved2 = malloc (SIZEOFMODULEBUFFER);
#endif
#endif
    req_rec->content_type = "text/xml"; 
    /* for SOAP 1.2 this this should be "application/soap+xml" but keep this for
     * the moment set up the read policy from the client. 
     */
    if ((rc = ap_setup_client_block (req_rec, REQUEST_CHUNKED_ERROR)) != OK)
    {
        if (sstr->reserved1)
            free (sstr->reserved1);
#ifdef USE_XERCES_PARSER
#else
        if (sstr->reserved2)
            free (sstr->reserved2);
#endif
        free (sstr->so.http);
        free (sstr);
        return rc;
    }

    /* the member, "path" of "parsed_uri" contains the uri of the */
    /* request (i.e "/abc/xyz" part of http://somehost/abc/xyz) */
    sstr->so.http->uri_path = req_rec->parsed_uri.path;

    /* ap_table_elts returns an array_header struct. The nelts element of that 
     * struct contains the number of input header elements. Finally assigns that
     * to the axis soap data structure. */
    sstr->so.http->ip_headercount = ap_table_elts (req_rec->headers_in)->nelts;

    /* casting req_rec->headers_in to axis header struct and assigning that to 
     * the axis soap structure. Hope this is ok 
     */

    /* obtain the array_header from the headers_in table and assign it to the 
     * axis soap structure 
     */
    arr = ap_table_elts (req_rec->headers_in);
    sstr->so.http->ip_headers = (Ax_header*) arr->elts;

    /*Determine the http method and assign it to the axis soap structure */
    switch (req_rec->method_number)
    {
        case M_GET:
            sstr->so.http->ip_method = AXIS_HTTP_GET;
            break;
        case M_POST:
            sstr->so.http->ip_method = AXIS_HTTP_POST;
            break;
        default:
            sstr->so.http->ip_method = AXIS_HTTP_UNSUPPORTED;
    }

    /* Tell the client that we are ready to receive content and check whether 
     * client will send content control will pass to this block only if there is
     * body content in the request 
     */
    if (ap_should_client_block (req_rec));

    if (0 != process_request (sstr))
    {
        if (sstr->reserved1)
            free (sstr->reserved1);
#ifdef USE_XERCES_PARSER
#else
        if (sstr->reserved2)
            free (sstr->reserved2);
#endif
        free (sstr->so.http);
        free (sstr);
        return OK;
    }
#ifdef CHUNCKED_DATA_SUPPORTED
    /* headers have already been sent. see set_transport_information
     * http body too have been sent
     * Do we need to send any indication to mark end of chuncked 
     * data ? Sanjaya ? 
     */
#else
    /* Calculate Content-Length and set header */
    pbuffers = (sendbuffers*) sstr->reserved1;
    for (index = 0; index < NO_OF_SERIALIZE_BUFFERS; index++)
    {
        if (!pbuffers[index].buffer)
            break;
        contentLength += strlen (pbuffers[index].buffer);
    }
    if (contentLength != 0)     /* do only if the http body is not empty. */
    {
        sprintf (strtonum, "%d", contentLength);
        set_transport_information (SOAP_MESSAGE_LENGTH, strtonum, sstr);
        ap_send_http_header (req_rec);
        /* Send all buffers */
        pbuffers = (sendbuffers*) sstr->reserved1;
        for (index = 0; index < NO_OF_SERIALIZE_BUFFERS; index++)
        {
            if (!pbuffers[index].buffer)
                break;
            ap_rputs (pbuffers[index].buffer, req_rec);
            /* Let Axis know that the buffer is no longer in use */
            axis_buffer_release (pbuffers[index].buffer, 
                pbuffers[index].bufferid, sstr);
        }
    }
    /* Free the array */
    if (sstr->reserved1)
        free (sstr->reserved1);
#ifdef USE_XERCES_PARSER
#else
    if (sstr->reserved2)
        free (sstr->reserved2);
#endif

#endif
    free (sstr->so.http);
    free (sstr);
    /* free(arr); */
    return OK;
}

/* Make the name of the content handler known to Apache */
static handler_rec axis_handlers[] = { {"axis", axis_handler}, {NULL} };

/* Tell Apache what phases of the transaction we handle */
module MODULE_VAR_EXPORT axis_module = {
    STANDARD_MODULE_STUFF,
    module_init,                /* module initializer                 */
    NULL,                       /* per-directory config creator       */
    NULL,                       /* dir config merger                  */
    NULL,                       /* server config creator              */
    NULL,                       /* server config merger               */
    NULL,                       /* command table                      */
    axis_handlers,              /* [9]  content handlers              */
    NULL,                       /* [2]  URI-to-filename translation   */
    NULL,                       /* [5]  check/validate user_id        */
    NULL,                       /* [6]  check user_id is valid *here* */
    NULL,                       /* [4]  check access by host address  */
    NULL,                       /* [7]  MIME type checker/setter      */
    NULL,                       /* [8]  fixups                        */
    NULL,                       /* [10] logger                        */
    NULL,                       /* [3]  header parser                 */
    axis_Init,                  /* process initialization             */
    axis_Fini,                  /* process exit/cleanup               */
    NULL                        /* [1]  post read_request handling    */
};
