/**************************************************************************
*** 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 != 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 == 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;
}

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

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

    if (ospvComm == 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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 errcode = OSPC_ERR_NO_ERROR;

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

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

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

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

    if (ospvComm != 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 != 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 != OSPC_OSNULL);
    }

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

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);
        }
    }
}

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

    if (ospvComm == 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;
}

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 == 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 == OSPC_OSNULL)
                break;

            if ((OSPM_STRLEN(svcpt->HostName) + OSPM_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 != OSPC_OSNULL) {
            if (deletesvcpt->HostName)
                OSPM_FREE(deletesvcpt->HostName);

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

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

/*
 * 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 == OSPC_OSNULL) {
        OSPM_DBGERRORLOG(OSPC_ERR_COMM_PARSER, "URL is NULL");
        return OSPC_ERR_COMM_PARSER;
    }

    OSPM_MALLOC(svcpt, OSPTSVCPT, sizeof(OSPTSVCPT));
    if (svcpt == 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 = OSPM_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[OSPM_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 = OSPM_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, OSPM_STRLEN(curr) + 1);
    if (svcpt->URI == 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, OSPM_STRLEN(curr));
    svcpt->URI[OSPM_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);
    }
}

/*
 * 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 errcode = 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, errcode);
    OSPM_CONDVAR_DESTROY((*ospvComm)->HttpSelCondVar, errcode);

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

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 != 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;
}
