/**************************************************************************
*** COPYRIGHT (c) 2002 by TransNexus, Inc.                              ***
***                                                                     ***
*** This software is property of TransNexus, Inc.                       ***
*** This software is freely available under license from TransNexus.    ***
*** The license terms and conditions for free use of this software by   ***
*** third parties are defined in the OSP Toolkit Software License       ***
*** Agreement (LICENSE.txt).  Any use of this software by third         ***
*** parties, which does not comply with the terms and conditions of the ***
*** OSP Toolkit Software License Agreement is prohibited without        ***
*** the prior, express, written consent of TransNexus, Inc.             ***
***                                                                     ***
*** Thank you for using the OSP ToolKit(TM).  Please report any bugs,   ***
*** suggestions or feedback to support@transnexus.com                   ***
***                                                                     ***
**************************************************************************/








/*
 * ospcomm.cpp - Communication object functions.
 */
#include "osp/osp.h"
#include "osp/ospcomm.h"
#include "osp/ospsocket.h"
#include "osp/osphttp.h"
#include "osp/osplist.h"
#include "osp/ospmsgque.h"
#include "osp/ospsecurity.h"

/*
 * monitors the message queue for new communication requests.
 * When a new request is placed on the message queue, the conditional variable
 * (CondVar) within the message queue structure is signalled and the number of
 * transactions (NumberOfTransactions) will be incremented. This function will
 * awaken and assign the request to an available HTTP connection. Upon the 
 * handoff of the request, this function will return to the wait condition 
 * until the next message queue request arrives.
 *
 * returns void (runs until the communication manager thread is terminated)
 */
static
OSPTTHREADRETURN
osppCommMonitor(
    void *ospvArg)  /* In - Comm pointer casted to a void */
{
    OSPTCOMM     **comm     = OSPC_OSNULL;
    OSPTMSGQUEUE *msgqueue  = OSPC_OSNULL;
    int          errorcode  = OSPC_ERR_NO_ERROR,
                 tmperror   = OSPC_ERR_NO_ERROR;
    OSPTBOOL     shutdown   = OSPC_FALSE,
        do_forever = OSPC_TRUE;

    comm     = (OSPTCOMM **)ospvArg;
    msgqueue = (*comm)->MsgQueue;

    while (do_forever)
    {
        /*
         * acquire message queue mutex
         */
        OSPM_MUTEX_LOCK(msgqueue->Mutex, errorcode);
        /* assert(errorcode == OSPC_ERR_NO_ERROR); 
           assert is being handled already below */

        if (errorcode == OSPC_ERR_NO_ERROR)
        {
            /*
             * wait for conditional variable on the transaction count
             */
            while (msgqueue->NumberOfTransactions == 0 &&
                ((*comm)->Flags & OSPC_COMM_SHUTDOWN_BIT) != OSPC_COMM_SHUTDOWN_BIT)
            {
                OSPM_CONDVAR_WAIT(msgqueue->CondVar,
                    msgqueue->Mutex, errorcode);

                /* assert(errorcode == OSPC_ERR_NO_ERROR); */
            }

            if (msgqueue->NumberOfTransactions > 0 && errorcode == OSPC_ERR_NO_ERROR)
            {
                /*
                 * release the mutex lock
                 */
                OSPM_MUTEX_UNLOCK(msgqueue->Mutex, tmperror);
                assert(tmperror == OSPC_ERR_NO_ERROR);

                /*
                 * send the new msg request to the HTTP connection
                 * module
                 */
                errorcode = OSPPHttpRequestHandoff((*comm), msgqueue);
            }
            else
            {
                /*
                 * NewTransactionsAllowed == OSPC_FALSE
                 *
                 * The provider has been deleted. See if a Timelimit
                 * has been specified to wait for pending transactions.
                 * If Timelimit < 0; wait indefinitely
                 *              > 0; wait for specified seconds
                 *             == 0; do not wait. terminate all connections immediately
                 */
                OSPPCommShutdownConnections((*comm), (*comm)->ShutdownTimeLimit);
                shutdown = OSPC_TRUE;
                /*
                 * release the mutex lock
                 */
                OSPM_MUTEX_UNLOCK(msgqueue->Mutex, tmperror);
                assert(tmperror == OSPC_ERR_NO_ERROR);
            }

            if (shutdown)
                break;
        }

    }

    /*
     * destroy the comm object. Make sure all http connections have been
     * freed.
     */
    while ((*comm)->HttpConnCount)
        OSPM_SLEEP(2);

    OSPPCommDelete(comm);

    OSPTTHREADRETURN_NULL();
}

/*
 * Starts the communication worker thread.
 * This function will create the new communication thread responsible
 * for monitoring the message queue for new requests.
 *
 * return OSPD_ERR_NO_ERROR if successful, else an error code.
 */
static
int
osppCommStartWorker(
    OSPTCOMM  **ospvComm)     /* In - Pointer to the Comm Mgr */
{
    int         errorcode = OSPC_ERR_NO_ERROR;
    OSPTTHRATTR thread_attr;

    /*
     * verify Provider, Comm, and MsgQueue pointers
     */
    if (!(*ospvComm) || !(*ospvComm)->MsgQueue)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm/MsgQueue is null");
    }
    else
    {
        if (errorcode == OSPC_ERR_NO_ERROR)
        {
            OSPM_THRATTR_INIT(thread_attr, errorcode);
            if (errorcode == OSPC_ERR_NO_ERROR)
            {
                OSPM_SETDETACHED_STATE(thread_attr, errorcode);
                if (errorcode == OSPC_ERR_NO_ERROR)
                {
                    /*
                     * create the new thread which will monitor the
                     * the provider's message queue for new requests
                     */
                    OSPM_CREATE_THREAD((*ospvComm)->ThreadId, &thread_attr,
                        osppCommMonitor, ospvComm, errorcode);
                }
                OSPM_THRATTR_DESTROY(thread_attr);
            }
        }
        if (errorcode != OSPC_ERR_NO_ERROR)
        {
            errorcode = OSPC_ERR_COMM_THREAD_INIT_FAILED;
            OSPM_DBGERRORLOG(errorcode, "thread initialization failed");
        }
    }
    return errorcode;
}


/*
 * create a new communication manager object.
 * This function creates the communication object which processes requests
 * to and from the TransNexus Service Points.
 *
 * return OSPC_ERR_NO_ERROR if successful, else error code
 */
int
OSPPCommNew(
    OSPTCOMM     **ospvComm)      /* In - Pointer to a Comm address           */
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (*ospvComm != (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is not NULL");
    }
    else 
    {
        /*
         * create a new communication manager
         */
        OSPM_MALLOC(*ospvComm, OSPTCOMM, sizeof(OSPTCOMM));
        if (*ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
        {
            errorcode = OSPC_ERR_COMM_NO_MEMORY;
            OSPM_DBGERRORLOG(errorcode, "ospvComm malloc failed");
        }
        else
        {
            OSPM_MEMSET(*ospvComm, 0, sizeof(OSPTCOMM));

            /*
             * initialize the communication manager mutex
             */
            OSPM_MUTEX_INIT((*ospvComm)->Mutex, 0, errorcode);
            /* assert(errorcode == OSPC_ERR_NO_ERROR); */

            /*
             * Initialize the Mutex to used for Http Obj Selection
             */
            if (errorcode == OSPC_ERR_NO_ERROR)
            {
                OSPM_MUTEX_INIT((*ospvComm)->HttpSelectMutex, 0, errorcode);
                if (errorcode == OSPC_ERR_NO_ERROR)
                {
                    OSPM_CONDVAR_INIT((*ospvComm)->HttpSelCondVar, NULL, errorcode);
                }
            }

            if (errorcode == OSPC_ERR_NO_ERROR)
            {
                /*
                 * create the message queue to the Communication Manager
                 */
                errorcode = OSPPMsgQueueNew(&(*ospvComm)->MsgQueue);
                if (errorcode != OSPC_ERR_NO_ERROR)
                {
                    OSPPCommDelete(ospvComm);
                }
                else
                {
                    /*
                     * start up the communication manager worker thread. This 
                     * thread will monitor the queue for new requests and hand 
                     * them off to an available HTTP connection pool for 
                     * fulfillment.
                     */
                    errorcode = osppCommStartWorker(ospvComm);
                }
            }
        }
    }
    return errorcode;
}

void
OSPPCommSetSecurity(
    OSPTCOMM *ospvComm,
    OSPTSEC  *ospvSecurity)
{
    ospvComm->Security = ospvSecurity;
    return;
}

OSPTSEC *
    OSPPCommGetSecurity(
    OSPTCOMM *ospvComm)
{
    return ospvComm->Security;
}

int
OSPPCommGetPersistence(
    OSPTCOMM *ospvComm,
    unsigned *ospvPersistence)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        *ospvPersistence = ospvComm->HttpPersistence;

    return errorcode;
}

int
OSPPCommGetRetryDelay(
    OSPTCOMM *ospvComm,
    unsigned *ospvRetryDelay)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        *ospvRetryDelay = ospvComm->HttpRetryDelay;

    return errorcode;
}

int
OSPPCommGetRetryLimit(
    OSPTCOMM *ospvComm,
    unsigned *ospvRetryLimit)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        *ospvRetryLimit = ospvComm->HttpRetryLimit;

    return errorcode;
}

int
OSPPCommGetTimeout(
    OSPTCOMM *ospvComm,
    unsigned *ospvTimeout)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        *ospvTimeout = ospvComm->HttpTimeout;

    return errorcode;
}

int
OSPPCommSetPersistence(
    OSPTCOMM *ospvComm,
    unsigned ospvPersistence)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        ospvComm->HttpPersistence = ospvPersistence;

    return errorcode;
}

int
OSPPCommSetRetryDelay(
    OSPTCOMM *ospvComm,
    unsigned ospvRetryDelay)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        ospvComm->HttpRetryDelay = ospvRetryDelay;

    return errorcode;
}

int
OSPPCommSetRetryLimit(
    OSPTCOMM *ospvComm,
    unsigned ospvRetryLimit)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        ospvComm->HttpRetryLimit = ospvRetryLimit;

    return errorcode;
}

int
OSPPCommSetConnSelectionTimeout(
    OSPTCOMM *ospvComm,
    OSPTUINT64 ospvTimeout)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        ospvComm->ConnSelectionTimeout = ospvTimeout;

    return errorcode;
}

int
OSPPCommSetTimeout(
    OSPTCOMM *ospvComm,
    unsigned ospvTimeout)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        ospvComm->HttpTimeout = ospvTimeout;

    return errorcode;
}

int
OSPPCommGetMaxConnections(
    OSPTCOMM *ospvComm, 
    unsigned *ospvMaxConnections)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        *ospvMaxConnections = ospvComm->HttpMaxConnections;

    return errorcode;
}

int
OSPPCommSetMaxConnections(
    OSPTCOMM *ospvComm,
    unsigned ospvMaxConnections)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        ospvComm->HttpMaxConnections = ospvMaxConnections;

    return errorcode;
}

int
OSPPCommIncrementHttpConnCount(
    OSPTCOMM *ospvComm)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        ospvComm->HttpConnCount++;

    return errorcode;
}

int
OSPPCommDecrementHttpConnCount(
    OSPTCOMM *ospvComm)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        ospvComm->HttpConnCount--;

    return errorcode;
}

int
OSPPCommGetHttpConnCount(
    OSPTCOMM *ospvComm,
    unsigned *ospvHttpConnCount)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL) 
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        *ospvHttpConnCount = ospvComm->HttpConnCount;

    return errorcode;
}

void
OSPPCommSetShutdown(
    OSPTCOMM     **ospvComm,
    int          ospvTimeLimit)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    (*ospvComm)->Flags |= OSPC_COMM_SHUTDOWN_BIT;
    (*ospvComm)->ShutdownTimeLimit = ospvTimeLimit;

    OSPM_CONDVAR_SIGNAL(OSPPMsgQueueGetCondVar((*ospvComm)->MsgQueue), errorcode);

    while (*ospvComm != (OSPTCOMM *)OSPC_OSNULL)
        OSPM_SLEEP(1);

    return;
}

void
OSPPCommSignalAllConnections(
    OSPTCOMM *ospvComm)
{
    OSPTHTTP *httpconn = OSPC_OSNULL;
    int      errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm != (OSPTCOMM *)OSPC_OSNULL)
    {
        /*
         * Lock COMM object while iterating though the list
         * of HTTP connections and signaling them to exit.
         */
        OSPM_MUTEX_LOCK(ospvComm->Mutex,errorcode);
        httpconn = (OSPTHTTP *)OSPPListFirst(
            (OSPTLIST *)&(ospvComm->HttpConnList));
    }
    if (httpconn != (OSPTHTTP *)OSPC_OSNULL)
    {
        do
        {
            OSPM_CONDVAR_SIGNAL(httpconn->CondVar, errorcode);

            if (!OSPPListEmpty((OSPTLIST *)&(ospvComm->HttpConnList)))
            {
                httpconn = (OSPTHTTP *)OSPPListNext(
                    (OSPTLIST *)&(ospvComm->HttpConnList),
                    (void *)httpconn);
            }
            else
            {
                httpconn = OSPC_OSNULL;
            }
        } while (httpconn != (OSPTHTTP *)OSPC_OSNULL);
    }

    if (ospvComm != (OSPTCOMM *)OSPC_OSNULL)
    {
        /*
         * Finished iterating, unlocking...
         */
        OSPM_MUTEX_UNLOCK(ospvComm->Mutex,errorcode);assert(errorcode == OSPC_ERR_NO_ERROR);
    }

    return;
}

void                
OSPPCommShutdownConnections(
    OSPTCOMM *ospvComm,
    int      ospvTimeLimit)
{
    unsigned count      = 0;
    OSPTBOOL do_forever = OSPC_TRUE;

    /*
     * check how many http connections are currently open
     */
    OSPPCommGetHttpConnCount(ospvComm, &count);
    if (count != 0)
    {
        /*
         * we've got open http connections
         */

        /*
         * sleep a specified time if requested
         */
        if (ospvTimeLimit > 0)
        {
            OSPM_SLEEP(ospvTimeLimit);
        }
        else
        {
            /*
             * if ospvTimeLimit is a negative number, sleep indefinitely.
             * Check connection status every 2 seconds
             */
            if (ospvTimeLimit < 0)
            {
                while (do_forever)
                {
                    OSPPCommGetHttpConnCount(ospvComm, &count);
                    if (count == 0)
                        break;

                    OSPM_SLEEP(2);
                }
            }
        }

        /*
         * make sure we still have connections
         */
        if (ospvComm->HttpConnCount)
        {
            /*
             * set the shutdown notification flag
             */
            ospvComm->Flags |= OSPC_COMM_HTTPSHUTDOWN_BIT;

            /*
             * signal each active HTTP connection 
             */
            OSPPCommSignalAllConnections(ospvComm);
        }
    }
    return;
}

int
OSPPCommGetNumberOfServicePoints(
    OSPTCOMM *ospvComm,
    unsigned *ospvNumberOfServicePoints)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else
        *ospvNumberOfServicePoints = ospvComm->NumberOfServicePoints;

    return errorcode;
}

void
OSPPCommGetServicePointList(
    OSPTCOMM *ospvComm, 
    OSPTSVCPT **ospvSvcPt)
{
    *ospvSvcPt = ospvComm->ServicePointList;
    return;
}


int
OSPPCommBuildServicePoint(
    OSPTSVCPT *ospvSvcPt,
    char *ospvBuffer)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if (!ospvSvcPt->HostName || !ospvSvcPt->URI)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else 
        OSPM_SPRINTF(ospvBuffer, "%s://%s:%d%s",
        (ospvSvcPt->Flags & OSPC_COMM_SVCPT_SECURITY_BIT) ?
        "https" : "http", ospvSvcPt->HostName,
        ntohs(ospvSvcPt->Port), ospvSvcPt->URI);

    return errorcode;
}

int
OSPPCommGetServicePoints(
    OSPTCOMM *ospvComm,
    unsigned ospvNumberOfServicePoints,
    unsigned ospvSizeOfServicePoint,
    char *ospvServicePoints[])
{
    unsigned  count     = 0;
    OSPTSVCPT *svcpt    = OSPC_OSNULL;
    int       errorcode = OSPC_ERR_NO_ERROR;

    if (ospvComm == (OSPTCOMM *)OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_COMM_INVALID_ARG;
        OSPM_DBGERRORLOG(errorcode, "ospvComm is NULL");
    }
    else 
    {
        for (count = 0, 
            svcpt = (OSPTSVCPT *)OSPPListFirst(
            (OSPTLIST *)&(ospvComm->ServicePointList));
            count < ospvComm->NumberOfServicePoints &&
            count < ospvNumberOfServicePoints; 
            count++,
            svcpt = (OSPTSVCPT *)OSPPListNext(
            (OSPTLIST *)&(ospvComm->ServicePointList), (void *)svcpt)) 
        {
            if (svcpt == (OSPTSVCPT *)OSPC_OSNULL)
                break;

            if ((strlen(svcpt->HostName) + strlen(svcpt->URI) 
                + OSPC_COMM_SPARE_CHARS) > ospvSizeOfServicePoint) 
            {
                errorcode = OSPC_ERR_COMM_INVALID_SIZE;
                count = 0;
                break;
            }

            errorcode = OSPPCommBuildServicePoint(svcpt, 
                ospvServicePoints[count]);
            if (errorcode != OSPC_ERR_NO_ERROR) 
            {
                count = 0;
                break;
            }
        }
    }
    while (count < ospvNumberOfServicePoints)
        ospvServicePoints[count++][0] = '\0';

    return errorcode;
}

static
void
osppCommDeleteServicePointList(
    OSPTSVCPT **svcpt,
    int ospvNumberOfServicePoints)
{
    int       count          = 0;
    OSPTSVCPT *deletesvcpt   = OSPC_OSNULL;

    for (count = 0; count < ospvNumberOfServicePoints; count++) 
    {
        deletesvcpt = (OSPTSVCPT *)OSPPListRemove((OSPTLIST *)svcpt);
        if (deletesvcpt != (OSPTSVCPT *)OSPC_OSNULL)
        {
            if (deletesvcpt->HostName)
                OSPM_FREE(deletesvcpt->HostName);

            if (deletesvcpt->URI)
                OSPM_FREE(deletesvcpt->URI);

            OSPM_FREE(deletesvcpt);
        }
    }
    OSPPListDelete((OSPTLIST *)svcpt);
    return;
}

/*
 * URLs have the following format
 *
 * http[s] : // FQDN [: port] / URI
 */
int
OSPPCommParseSvcPt(
    const char *ospvURL, 
    OSPTSVCPT  **ospvSvcPt,
    unsigned   ospvIndex)
{
#define OSPTMPBUFSZ    16

    int            result     = 0,
                   hasport    = 0,
                   hasbracket = 0;
    OSPTSVCPT     *svcpt     = OSPC_OSNULL;
    char          *curr      = OSPC_OSNULL,
                  *next      = OSPC_OSNULL,
                   TmpBuf[OSPTMPBUFSZ] = { "" };
    unsigned short secport    = OSPC_COMM_DEFAULT_SECURE_PORT,
                   nonsecport = OSPC_COMM_DEFAULT_NONSECURE_PORT;
    unsigned int   length     = 0;
    char  ErrStr[100];

    if (ospvURL == (char *)OSPC_OSNULL)
    {
        OSPM_DBGERRORLOG(OSPC_ERR_COMM_PARSER, "URL is NULL");
        return OSPC_ERR_COMM_PARSER;
    }

    OSPM_MALLOC(svcpt, OSPTSVCPT, sizeof(OSPTSVCPT));
    if (svcpt == (OSPTSVCPT *)OSPC_OSNULL)
        return OSPC_ERR_COMM_NO_MEMORY;

    OSPM_MEMSET(svcpt, 0, sizeof(OSPTSVCPT));

    /*
     * Parse security flag bit
     *
     * -- START --
     */
    /* This is "http" or "https" */
    if((result = OSPM_MEMCMP(ospvURL, "http", 4)) == 0)
    {

        if (ospvURL[4] == 's')
        {
            OSPM_DBGMISC(( "Setting security bit 1...\n" ));
            svcpt->Flags |= OSPC_COMM_SVCPT_SECURITY_BIT;
            curr = (char *)&ospvURL[5];
        }
        else
        {
            curr = (char *)&ospvURL[4];
        }
    }
    else if((result = OSPM_MEMCMP(&ospvURL[0], "[", 1)) == 0)
    {

        /* This is raw IP address in brackets "[172.65.4.2]" */        
        OSPM_DBGMISC(( "Setting security bit 2...\n" ));
        svcpt->Flags |= OSPC_COMM_SVCPT_SECURITY_BIT;
        curr = (char *)&ospvURL[1];
        hasbracket = 1;
    }
    else
    {
        /* This is either FQDN or error */
        OSPM_DBGMISC(( "Setting security bit 3...\n" ));
        svcpt->Flags |= OSPC_COMM_SVCPT_SECURITY_BIT;
        curr = (char *)&ospvURL[0];
    }

    /* -- END -- */


    if((result = OSPM_MEMCMP(curr, "://", 3)) == 0)
    {
        curr += 3;
    }

    if((result = OSPM_MEMCMP(curr, "[", 1)) == 0)
    {

        curr++;
        hasbracket = 1;
    }

    /*
     * Parse FQDN (Host Name)
     *
     * -- START --
     */
    /* first check for port, i.e. randr:4484 */
    next = strchr(curr, ':');
    if (!next) 
    {
        /* now, check for url, i.e. ra/scripts/simulator.dll?handler */
        next = strchr(curr, '/');
        if(!next)
        {
            length = strlen(curr);
            next = curr + length;
        }
    }
    else
    {
        hasport = 1;
    }

    OSPM_MALLOC(svcpt->HostName, char, next - curr + 1);
    if (!svcpt->HostName) 
    {
        OSPM_FREE(svcpt);
        OSPM_DBGERRORLOG(OSPC_ERR_COMM_PARSER, "hostname syntax error");
        return OSPC_ERR_COMM_PARSER;
    }
    OSPM_MEMCPY(svcpt->HostName, curr, next - curr);
    svcpt->HostName[next - curr] = '\0';

    /* if bracket at the end, NULL it out */
    if(hasbracket)
    {
        svcpt->HostName[strlen(svcpt->HostName)-1] = '\0';
    }

    curr = next + (hasport ? 1 : 0);

    /* get ip address in network byte order */
    OSPPSockGetHostIP(svcpt->HostName, &(svcpt->IpAddr));
    if (!svcpt->IpAddr)
    {
        sprintf(ErrStr,"Host not found. Invalid Address: %s",svcpt->HostName);
        OSPM_DBGERRORLOG(OSPC_ERR_COMM_PARSER, ErrStr);
        OSPM_FREE(svcpt->HostName);
        OSPM_FREE(svcpt);
        return OSPC_ERR_COMM_PARSER;
    }

    /* -- END -- */

    /*
     * (optional)Parse Port Number 
     *
     * -- START --
     */
    if (hasport) 
    {
       /* now, check for url, i.e. ra/scripts/simulator.dll?handler */
        next = strchr(curr, '/');
        if(!next)
        {
            length = strlen(curr);
            next = curr + length;
        }

        if (next - curr > OSPTMPBUFSZ)
        {
            OSPM_FREE(svcpt->HostName);
            OSPM_FREE(svcpt);
            OSPM_DBGERRORLOG(OSPC_ERR_COMM_PARSER, "port syntax error");
            return OSPC_ERR_COMM_PARSER;
        }

        OSPM_MEMCPY(TmpBuf, curr, next - curr);
        TmpBuf[next - curr] = '\0';
        svcpt->Port = htons((short)atoi(TmpBuf));
        curr = next;

    }
    else
        if (svcpt->Flags & OSPC_COMM_SVCPT_SECURITY_BIT)
        {
            svcpt->Port = htons(secport);
        }
        else
        {
            svcpt->Port = htons(nonsecport);
        }

    /* -- END -- */

    /*
     * Load URI
     *
     * -- START --
     */
    OSPM_MALLOC(svcpt->URI, char, strlen(curr) + 1);
    if (svcpt->URI == (char *)OSPC_OSNULL)
    {
        OSPM_FREE(svcpt->HostName);
        OSPM_FREE(svcpt);
        OSPM_DBGERRORLOG(OSPC_ERR_COMM_PARSER, "URI is OSPC_OSNULL");
        return OSPC_ERR_COMM_PARSER;
    }
    OSPM_MEMCPY(svcpt->URI, curr, strlen(curr));
    svcpt->URI[strlen(curr)] = '\0';

    /* -- END -- */

    svcpt->Index = ospvIndex;
    *ospvSvcPt = svcpt;
    return OSPC_ERR_NO_ERROR;
}

int
OSPPCommSetAuditURL(
    OSPTCOMM    *ospvComm,
    const char  *ospvAuditURL
)
{
    int errorcode = OSPC_ERR_NO_ERROR;

    if(ospvComm != OSPC_OSNULL)
    {
        errorcode = OSPPCommParseSvcPt(ospvAuditURL, &ospvComm->AuditURL, 0);

        if(errorcode != OSPC_ERR_NO_ERROR)
        {
            if (ospvComm->AuditURL)
            {
              if (ospvComm->AuditURL->HostName)
                  OSPM_FREE(ospvComm->AuditURL->HostName);

              if (ospvComm->AuditURL->URI)
                  OSPM_FREE(ospvComm->AuditURL->URI);

              OSPM_FREE(ospvComm->AuditURL);
            }
        }
    }

    return errorcode;
}

void     
OSPPCommSetAuditFlag(
    OSPTCOMM *ospvComm, 
    unsigned ospvAuditFlag
)
{
    if(ospvComm != OSPC_OSNULL)
    {
        if(ospvAuditFlag == OSPC_COMM_AUDIT_ON)
            ospvComm->Flags = (unsigned char)(ospvComm->Flags | ospvAuditFlag);
        else if(ospvAuditFlag == OSPC_COMM_AUDIT_OFF)
            ospvComm->Flags = (unsigned char)(ospvComm->Flags & ospvAuditFlag);
    }

    return;
}


/*
 * Validate Input - at least one valid URL is required
 * Signal to all current connections to exit, and then set
 * the new set of URLs
 */
int
OSPPCommUpdateURLs(
    OSPTCOMM        *ospvCommManager,          /* In - Provider handle     */
    unsigned        ospvNumberOfURLs,          /* In - New svc url cnt     */
    const char      *ospvURLs[])               /* In - New svc url strings */
{
    int            errorcode = OSPC_ERR_NO_ERROR;
    int            timeLimit = 0;
    unsigned char  origFlags = ospvCommManager->Flags;

    /*
     * Check input parameters, we want at least one URL
     */
    if (ospvNumberOfURLs <= 0 ||
        ospvURLs == OSPC_OSNULL)
    {
        errorcode = OSPC_ERR_PROV_INVALID_VALUE;
        OSPM_DBGERRORLOG(errorcode, "Invalid input for OSPPCommUpdateURLs");
        OSPM_DBGERRORLOG(errorcode, "At least one URLs is required");
    }


    /*
     * Make sure the URLs are valid
     */
    if (OSPC_ERR_NO_ERROR == errorcode)
    {
        errorcode = OSPPCommValidateSvcPts(ospvNumberOfURLs,ospvURLs);
    }



    /*
     * Signal to the manager to shut down, wait for the operation to complete,
     * and then update the service points
     */
    if (OSPC_ERR_NO_ERROR == errorcode)
    {
        OSPPCommShutdownConnections(ospvCommManager, timeLimit);
    
        while (ospvCommManager->HttpConnCount > 0)
        {
            /* OSPM_DBGMISC(( "waiting another sec...\n" )); */
            OSPM_SLEEP(1);
        }

        /*
         * All HTTP connection have exited, restore the Flag
         * It was set to SHUTDOWN state by a call to OSPPCommShutdownConnections
         */
        ospvCommManager->Flags = origFlags;


        errorcode =OSPPCommSetServicePoints(ospvCommManager, 
                                            ospvNumberOfURLs,
                                            ospvURLs);
    }

    return errorcode;
}



int
OSPPCommSetServicePoints(
    OSPTCOMM *ospvComm,
    unsigned ospvNumberOfServicePoints,
    const char **ospvServicePoint)
{
    unsigned  count        = 0;
    OSPTSVCPT *newsvcpt    = OSPC_OSNULL,
        *newroot    = OSPC_OSNULL;
    int       errorcode    = OSPC_ERR_NO_ERROR;

    /*
     * construct the new service point list
     */
    OSPPListNew((OSPTLIST *)&newroot);

    for (count = 0;  count < ospvNumberOfServicePoints; count++) 
    {

        errorcode = OSPPCommParseSvcPt(ospvServicePoint[count], 
            &newsvcpt, count+1);

        if (errorcode != OSPC_ERR_NO_ERROR)
            break;

        OSPPListAppend((OSPTLIST *)&newroot, (void *)newsvcpt);
    }

    /*
     * if memory has been allocated and a parse error has
     * occurred, free the partial list.
     */
    if (errorcode != OSPC_ERR_NO_ERROR)
        osppCommDeleteServicePointList(&newroot, count);
    else 
    {
        /*
         * If the new list has been successfully constructed,
         * free the old one and assign the new one.
         */
        osppCommDeleteServicePointList(
            &(ospvComm->ServicePointList),
            ospvComm->NumberOfServicePoints);

        ospvComm->ServicePointList = newroot;
        ospvComm->NumberOfServicePoints = count;
    }
    return errorcode;
}

void
OSPPCommDelete(
    OSPTCOMM  **ospvComm)
{
    int errorcode=0;

    /*
     * check the communication manager first: 
     */
    if ( ospvComm == OSPC_OSNULL )
    {
        OSPM_DBGMISC(( 
            "%s%s",
            "Warning; attempted to delete null reference to ",
            "communications manager.\n" ));
        return;
    }

    if ( *ospvComm == OSPC_OSNULL )
    {
        OSPM_DBGMISC(( 
            "Warning: attempted to delete null communications manager.\n" ));
        return;
    }
        
    /*
     * destroy service point list
     */
    osppCommDeleteServicePointList(&(*ospvComm)->ServicePointList,
        (*ospvComm)->NumberOfServicePoints);

    /*
     * destroy AuditURL, if necessary:
     */
    if ( (*ospvComm)->AuditURL != OSPC_OSNULL )
    {
        if ((*ospvComm)->AuditURL->HostName)
            OSPM_FREE((*ospvComm)->AuditURL->HostName);
    
        if ((*ospvComm)->AuditURL->URI)
            OSPM_FREE((*ospvComm)->AuditURL->URI);
    
        OSPM_FREE((*ospvComm)->AuditURL);
    }

    /*
     * destroy the message queue object
     */
    OSPPMsgQueueDelete(&(*ospvComm)->MsgQueue);

    /*
     * Destroy the mutex and the condition variable.
     */
     OSPM_MUTEX_DESTROY((*ospvComm)->HttpSelectMutex, errorcode);
     OSPM_CONDVAR_DESTROY((*ospvComm)->HttpSelCondVar, errorcode);

    /*
     * destroy the comm object
     */
    OSPM_FREE(*ospvComm);
    *ospvComm = (OSPTCOMM *)OSPC_OSNULL;

    return; 
}

int OSPPCommValidateSvcPts(unsigned ospvNumberOfServicePoints, const char **ospvServicePoint)
{
    int       errorcode    = OSPC_ERR_NO_ERROR;
    unsigned  count        = 0;
    OSPTSVCPT *svcpt       = OSPC_OSNULL;

    /*
     * Iterate through all Svc points and see if we can parse them
    */
    for (count = 0;  count < ospvNumberOfServicePoints; count++) 
    {
        errorcode = OSPPCommParseSvcPt(ospvServicePoint[count],
                                       &svcpt,
                                       count+1);

        /*
         * Now free the Svc point
        */
        if (svcpt != (OSPTSVCPT *)OSPC_OSNULL)
        {
            if (svcpt->HostName)
                OSPM_FREE(svcpt->HostName);

            if (svcpt->URI)
                OSPM_FREE(svcpt->URI);

            OSPM_FREE(svcpt);
        }

        /* Break on error */
        if (errorcode != OSPC_ERR_NO_ERROR)
            break;
    }

    return( errorcode );
}

int
OSPPCommGetNumberOfTransactions(
    OSPTCOMM *ospvComm,
    unsigned *ospvNumberOfTransactions)
{
    int errorcode = OSPPMsgQueueGetNumberOfTransactions(ospvComm->MsgQueue,ospvNumberOfTransactions);
    return errorcode;
}



