/**************************************************************************
*** 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                   ***
***                                                                     ***
**************************************************************************/

/* ospdest.c - OSP destination functions */

#include "osp/osp.h"
#include "osp/osperrno.h"
#include "osp/ospbfr.h"
#include "osp/osplist.h"
#include "osp/ospxmlattr.h"
#include "osp/ospxmlelem.h"
#include "osp/ospmsgattr.h"
#include "osp/ospmsgelem.h"
#include "osp/ospcallid.h"
#include "osp/osptoken.h"
#include "osp/ospdest.h"
#include "osp/ospusage.h"
#include "osp/ospaltinfo.h"

/* Array that associates protocols and names */
const OSPT_MSG_DESC OSPV_PROTNAME_DESCS[OSPC_PROTNAME_NUMBER] = {
    { OSPC_PROTNAME_SIP,   "sip" },
    { OSPC_PROTNAME_Q931,  "h323-Q931" },
    { OSPC_PROTNAME_LRQ,   "h323-LRQ" },
    { OSPC_PROTNAME_IAX,   "iax" },
    { OSPC_PROTNAME_T37,   "fax-T37" },
    { OSPC_PROTNAME_T38,   "fax-T38" },
    { OSPC_PROTNAME_SKYPE, "skype" },
    { OSPC_PROTNAME_SMPP,  "smpp" },
    { OSPC_PROTNAME_XMPP,  "xmpp" },
    { OSPC_PROTNAME_SMS,   "sms" }
};

/*
 * OSPPDestSetOSPVersion() - set the OSP Version for a destination
 */
void OSPPDestSetOSPVersion(     /* nothing returned */
    OSPT_DEST *ospvDest,        /* destination to set */
    const char *ospvVersion)    /* Version (as string) */
{
    if (ospvDest != OSPC_OSNULL) {
        if (!(OSPM_STRCMP(ospvVersion, DEST_OSP_DIABLED))) {
            ospvDest->OSPVersion = OSPC_DOSP_FALSE;
        } else {
            if (!(OSPM_STRCMP(ospvVersion, DEST_OSP_UNKNOWN))) {
                ospvDest->OSPVersion = OSPC_DOSP_UNKNOWN;
            } else {
                ospvDest->OSPVersion = OSPC_DOSP_TRUE;
            }
        }
    }
}

/*
 * OSPPDestSetProtocol() - set the protocol for a destination
 */
void OSPPDestSetProtocol(   /* Nothing returned */
    OSPT_DEST *ospvDest,    /* Destination to set */
    const char *ospvProt)   /* Protocol (as string) */
{
    if (ospvDest != OSPC_OSNULL) {
        ospvDest->Protocol = OSPPDestProtocolGetPart(ospvProt);
    }
}

/*
 * OSPPDestHasNumber() - does the destination include a called number?
 */
OSPTBOOL OSPPDestHasNumber( /* returns non-zero if number exists */
    OSPT_DEST *ospvDest)    /* destination effected */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->DestinationNumber[0] != '\0');
    }

    return has;
}

/*
 * OSPPDestHasSrcNumber() - does the destination include a calling number?
 */
OSPTBOOL OSPPDestHasSrcNumber(  /* returns non-zero if number exists */
    OSPT_DEST *ospvDest)        /* destination effected */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->SourceNumber[0] != '\0');
    }

    return has;
}

/*
 * OSPPDestSetSrcNumber() - set the calling number for a destination
 */
void OSPPDestSetSrcNumber(  /* nothing returned */
    OSPT_DEST *ospvDest,    /* destination to set */
    const char *ospvNum)    /* calling number (as string) */
{
    if (ospvDest != OSPC_OSNULL) {
        if (ospvNum != OSPC_OSNULL) {
            OSPM_MEMCPY(ospvDest->SourceNumber, ospvNum, OSPM_MIN(OSPM_STRLEN(ospvNum) + 1, OSPC_SIZE_E164NUM));
        }
    }
}

/*
 * OSPPDestGetSrcNumber() - returns the calling number for a destination
 */
const char *OSPPDestGetSrcNumber(   /* returns number as string */
    OSPT_DEST *ospvDest)            /* destination */
{
    const char *num = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        num = ospvDest->SourceNumber;
    }

    return num;
}

/*
 * OSPPDestSetNumber() - set the called number for a destination
 */
void OSPPDestSetNumber(     /* nothing returned */
    OSPT_DEST *ospvDest,    /* destination to set */
    const char *ospvNum)    /* called number (as string) */
{
    if (ospvDest != OSPC_OSNULL) {
        if (ospvNum != OSPC_OSNULL) {
            OSPM_MEMCPY(ospvDest->DestinationNumber, ospvNum, OSPM_MIN(OSPM_STRLEN(ospvNum) + 1, OSPC_SIZE_E164NUM));
        }
    }
}

/*
 * OSPPDestGetNumber() - returns the called number for a destination
 */
const char *OSPPDestGetNumber(  /* returns number as string */
    OSPT_DEST *ospvDest)        /* destination */
{
    const char *num = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        num = ospvDest->DestinationNumber;
    }

    return num;
}

/*
 * OSPPDestHasAddr() - does a destination have a Addr?
 */
OSPTBOOL OSPPDestHasAddr(   /* returns non-zero if exists */
    OSPT_DEST *ospvDest)    /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->DestinationAddr[0] != '\0');
    }

    return has;
}

/*
 * OSPPDestSetAddr() - sets the signalling address for a destination
 */
void OSPPDestSetAddr(       /* nothing returned */
    OSPT_DEST *ospvDest,    /* destination */
    const char *ospvAddr)   /* signal address as string */
{
    if (ospvDest != OSPC_OSNULL) {
        if (ospvAddr != OSPC_OSNULL) {
            OSPM_MEMCPY(ospvDest->DestinationAddr, ospvAddr, OSPM_MIN(OSPM_STRLEN(ospvAddr) + 1, OSPC_SIZE_SIGNALADDR));
        }
    }
}

/*
 * OSPPDestGetAddr() - returns the signalling address for a destination
 */
const char *OSPPDestGetAddr(    /* returns address as string */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    const char *addr = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        addr = ospvDest->DestinationAddr;
    }

    return addr;
}

/*
 * OSPPDestDevHasAddr() - does a destination have a Dev Addr?
 */
OSPTBOOL OSPPDestDevHasAddr(    /* returns non-zero if exists */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->DestinationDevAddr[0] != '\0');
    }

    return has;
}

/*
 * OSPPDestHasNetworkAddr() - does a destination have a Network Addr?
 */
OSPTBOOL OSPPDestHasNetworkAddr(    /* returns non-zero if exists */
    OSPT_DEST *ospvDest)            /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->NetworkId[0] != '\0');
    }

    return has;
}

/*
 * OSPPDestSetNetworkAddr() - sets the network address for a destination
 *  device
 */
void OSPPDestSetNetworkAddr(    /* nothing returned */
    OSPT_DEST *ospvDest,        /* destination */
    const char *ospvAddr)       /* network address as string */
{
    if (ospvDest != OSPC_OSNULL) {
        if (ospvAddr != OSPC_OSNULL) {
            OSPM_MEMCPY(ospvDest->NetworkId, ospvAddr, OSPM_MIN(OSPM_STRLEN(ospvAddr) + 1, OSPC_SIZE_NORID));
        }
    }
}

/*
 * OSPPDestGetNetworkAddr() - returns the network address for a destination device
 */
const char *OSPPDestGetNetworkAddr( /* returns address as string */
    OSPT_DEST *ospvDest)            /* destination in question */
{
    const char *nid = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        nid = ospvDest->NetworkId;
    }

    return nid;
}

/*
 * OSPPDestDevSetAddr() - sets the signalling address for a destination device
 */
void OSPPDestDevSetAddr(    /* nothing returned */
    OSPT_DEST *ospvDest,    /* destination */
    const char *ospvAddr)   /* signal address as string */
{
    if (ospvDest != OSPC_OSNULL) {
        if (ospvAddr != OSPC_OSNULL) {
            OSPM_MEMCPY(ospvDest->DestinationDevAddr, ospvAddr, OSPM_MIN(OSPM_STRLEN(ospvAddr) + 1, OSPC_SIZE_SIGNALADDR));
        }
    }
}

/*
 * OSPPDestDevGetAddr() - returns the signaling address for a destination device
 */
const char *OSPPDestDevGetAddr( /* returns address as string */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    const char *addr = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        addr = ospvDest->DestinationDevAddr;
    }

    return addr;
}

/*
 * OSPPDestHasValidAfter() - Does a destination have a valid after time?
 */
OSPTBOOL OSPPDestHasValidAfter( /* returns non-zero if time */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->ValidAfter != OSPC_TIMEMIN);
    }

    return has;
}

/*
 * OSPPDestSetValidAfter() - sets the valid after time for a destination
 */
void OSPPDestSetValidAfter(     /* nothing returned */
    OSPT_DEST *ospvDest,
    OSPTTIME ospvTime)
{
    if (ospvDest != OSPC_OSNULL) {
        ospvDest->ValidAfter = (ospvTime);
    }
}

/*
 * OSPPDestGetValidAfter() - returns valid after time for a destination
 */
OSPTTIME OSPPDestGetValidAfter( /* returns the time value */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    OSPTTIME time = 0;

    if (ospvDest != OSPC_OSNULL) {
        time = ospvDest->ValidAfter;
    }

    return time;
}

/*
 * OSPPDestHasValidUntil() - does destination have a valid until time?
 */
OSPTBOOL OSPPDestHasValidUntil( /* returns non-zero if time */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->ValidUntil != OSPC_TIMEMAX);
    }

    return has;
}

/*
 * OSPPDestSetValidUntil() - sets valid until time for destination
 */
void OSPPDestSetValidUntil( /* nothing returned */
    OSPT_DEST *ospvDest,    /* destination in question */
    OSPTTIME ospvTime)      /* time to set */
{
    if (ospvDest != OSPC_OSNULL) {
        ospvDest->ValidUntil = ospvTime;
    }
}

/*
 * OSPPDestGetValidUntil() - returns valid until time for destination
 */
OSPTTIME OSPPDestGetValidUntil( /* returns time */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    OSPTTIME time = 0;

    if (ospvDest != OSPC_OSNULL) {
        time = ospvDest->ValidUntil;
    }

    return time;
}

/*
 * OSPPDestHasAuthority() - does an authority URL exist for destination?
 */
OSPTBOOL OSPPDestHasAuthority(  /* returns non-zero if exists */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->Authority[0] != '\0');
    }

    return has;
}

/*
 * OSPPDestSetAuthority() - sets authority URL for destination
 */
void OSPPDestSetAuthority(  /* nothing returned */
    OSPT_DEST *ospvDest,    /* destination in question */
    const char *ospvAuth)   /* authority URL */
{
    if (ospvDest != OSPC_OSNULL) {
        if (ospvAuth != OSPC_OSNULL) {
            OSPM_STRNCPY(ospvDest->Authority, ospvAuth, OSPM_MIN(OSPM_STRLEN(ospvAuth) + 1, OSPC_SIZE_URL));
        }
    }
}

/*
 * OSPPDestHasCallId() - does a destination have a Call ID?
 */
OSPTBOOL OSPPDestHasCallId( /* returns non-zero if exists */
    OSPT_DEST *ospvDest)    /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (ospvDest->CallId != OSPC_OSNULL);
    }

    return has;
}

/*
 * OSPPDestGetCallId() - gets the call ID for a destination
 */
OSPT_CALL_ID *OSPPDestGetCallId(/* returns call ID pointer */
    OSPT_DEST *ospvDest)        /* destination in question */
{
    OSPT_CALL_ID *callid = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        callid = ospvDest->CallId;
    }

    return callid;
}

/*
 * OSPPDestHasToken() - does a destination have a Token?
 */
OSPTBOOL OSPPDestHasToken(  /* returns non-zero if exists */
    OSPT_DEST *ospvDest)    /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = (OSPPListFirst(&(ospvDest->Tokens)) != OSPC_OSNULL);
    }

    return has;
}

/*
 * OSPPDestAddToken() - adds a token to a destination
 */
void OSPPDestAddToken(      /* nothing returned */
    OSPT_DEST *ospvDest,    /* destination in question */
    OSPT_TOKEN *ospvToken)  /* token to add */
{
    if (ospvDest != OSPC_OSNULL) {
        OSPPListAppend(&(ospvDest->Tokens), ospvToken);
    }
}

/*
 * OSPPDestFirstToken() - gets first token for destination
 */
OSPT_TOKEN *OSPPDestFirstToken( /* returns null if none */
    OSPT_DEST *ospvDest)
{
    OSPT_TOKEN *token = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        token = (OSPT_TOKEN *)OSPPListFirst(&ospvDest->Tokens);
    }

    return token;
}

/*
 * OSPPDestNextToken() - gets next token (in list) for destination
 */
OSPT_TOKEN *OSPPDestNextToken(  /* returns NULL if no more */
    OSPT_DEST *ospvDest,        /* destination in question */
    OSPT_TOKEN *ospvToken)      /* current token */
{
    OSPT_TOKEN *token = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        if (ospvToken != OSPC_OSNULL) {
            token = (OSPT_TOKEN *)OSPPListNext(&ospvDest->Tokens, ospvToken);
        }
    }

    return token;
}

/*
 * OSPPDestHasLimit() - does a destination have a usage limit?
 */
OSPTBOOL OSPPDestHasLimit(  /* returns non-zero if limit */
    OSPT_DEST *ospvDest)    /* destination in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = ospvDest->HasLimit;
    }

    return has;
}

/*
 * OSPPDestSetLimit() -  sets usage limit for a destination
 */
void OSPPDestSetLimit(      /* nothing returned */
    OSPT_DEST *ospvDest,    /* destination in question */
    unsigned ospvLimit)     /* limit to set */
{
    if (ospvDest != OSPC_OSNULL) {
        ospvDest->Limit = ospvLimit;
        ospvDest->HasLimit = OSPC_TRUE;
    }
}

/*
 * OSPPDestGetLimit() - returns the usage limit for a destination
 */
unsigned OSPPDestGetLimit(  /* returns usage limit */
    OSPT_DEST *ospvDest)    /* destination in question */
{
    unsigned limit = 0;

    if (ospvDest != OSPC_OSNULL) {
        limit = ospvDest->Limit;
    }

    return limit;
}

/*
 * OSPPDestHasTermCause() - Does destination have the Fail Reason
 */
OSPTBOOL OSPPDestHasTermCause(      /* Returns non-zero if time */
    OSPT_DEST *ospvDest,            /* Destination in question */
    OSPE_TERM_CAUSE ospvType)       /* Termination cause type */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = OSPPHasTermCause(&ospvDest->TermCause, ospvType);
    }

    return has;
}

/*
 * OSPPDestHasTermCauseAny() - Does dest have a Fail Reason
 */
OSPTBOOL OSPPDestHasTermCauseAny(   /* returns non-zero if time */
    OSPT_DEST *ospvDest)            /* dest in question */
{
    OSPTBOOL has = OSPC_FALSE;

    if (ospvDest != OSPC_OSNULL) {
        has = OSPPHasTermCauseAny(&ospvDest->TermCause);
    }

    return has;
}

/*
 * OSPPDestSetTermCause() - Set Fail Reason
 */
void OSPPDestSetTermCause(  /* nothing returned */
    OSPT_DEST *ospvDest,
    OSPE_TERM_CAUSE ospvType,
    unsigned ospvTCCode,
    const char *ospvTCDesc)
{
    if (ospvDest != OSPC_OSNULL) {
        OSPPSetTermCause(&ospvDest->TermCause, ospvType, ospvTCCode, ospvTCDesc);
    }
}

/*
 * OSPPDestGetTermCause() - returns Fail Reason structure for an destination
 */
OSPT_TERM_CAUSE *OSPPDestGetTermCause(
    OSPT_DEST *ospvDest)    /* destination */
{
    OSPT_TERM_CAUSE *cause = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        cause = &ospvDest->TermCause;
    }

    return cause;
}

/*
 * OSPPDestGetTCCode() - returns Fail Reason value for an destination
 */
unsigned OSPPDestGetTCCode(
    OSPT_DEST *ospvDest,        /* destination */
    OSPE_TERM_CAUSE ospvType)   /* fail reason type */
{
    unsigned code = 0;

    if (ospvDest != OSPC_OSNULL) {
        code = OSPPGetTCCode(&ospvDest->TermCause, ospvType);
    }

    return code;
}

/*
 * OSPPDestGetTCDesc() - returns Fail Reason description for an destination
 */
const char *OSPPDestGetTCDesc(
    OSPT_DEST *ospvDest,        /* destination */
    OSPE_TERM_CAUSE ospvType)   /* fail reason type */
{
    const char *desc = OSPC_OSNULL;

    if (ospvDest != OSPC_OSNULL) {
        desc = OSPPGetTCDesc(&ospvDest->TermCause, ospvType);
    }

    return desc;
}

/*
 * OSPPDestNew() - creates a new (empty) destination
 */
OSPT_DEST *OSPPDestNew(void)    /* returns pointer or NULL */
{
    OSPT_DEST *dest = OSPC_OSNULL;

    OSPM_MALLOC(dest, OSPT_DEST, sizeof(OSPT_DEST));

    if (dest != OSPC_OSNULL) {
        /* start with setting all to 0 */
        OSPM_MEMSET(dest, 0, sizeof(OSPT_DEST));
        OSPPListLinkNew(&(dest->Link));
        dest->ValidAfter = OSPC_TIMEMIN;
        dest->ValidUntil = OSPC_TIMEMAX;
        dest->Protocol = OSPC_PROTNAME_UNDEFINED;
        dest->OSPVersion = OSPC_DOSP_UNDEFINED;
        OSPPListNew(&(dest->Tokens));
        OSPPListNew(&(dest->UpdatedSourceAddr));
        OSPPListNew(&(dest->UpdatedDeviceInfo));
        dest->HasLimit = OSPC_FALSE;
        dest->Count = 0;
        dest->SetupAttempt = -1;
        dest->RoleState = OSPC_RSTATE_UNKNOWN;
        dest->RoleFormat = OSPC_RFORMAT_UNKNOWN;
        dest->RoleVendor = OSPC_RVENDOR_UNKNOWN;
        dest->TransferStatus = OSPC_TSTATUS_UNKNOWN;
        dest->ProviderPDD = -1;
        dest->ServiceType = OSPC_SERVICE_UNKNOWN;
    }

    return dest;
}

/*
 * OSPPDestDelete() - deletes a Destination structure
 */
void OSPPDestDelete(
    OSPT_DEST **ospvDest)
{
    OSPT_TOKEN *tmptoken = OSPC_OSNULL;
    OSPT_ALTINFO *altinfo = OSPC_OSNULL;

    if (*ospvDest != OSPC_OSNULL) {
        /* first delete the callid */
        if (((*ospvDest)->CallId) != OSPC_OSNULL) {
            OSPPCallIdDelete(&((*ospvDest)->CallId));
        }

        while (!OSPPListEmpty((OSPTLIST *)&((*ospvDest)->Tokens))) {
            tmptoken = (OSPT_TOKEN *)OSPPListRemove((OSPTLIST *)&((*ospvDest)->Tokens));
            if (tmptoken != OSPC_OSNULL) {
                OSPPTokenDelete(&tmptoken);
            }
        }
        OSPPListDelete((OSPTLIST *)&((*ospvDest)->Tokens));

        while (!OSPPListEmpty((OSPTLIST *)&((*ospvDest)->UpdatedSourceAddr))) {
            altinfo = (OSPT_ALTINFO *)OSPPListRemove((OSPTLIST *)&((*ospvDest)->UpdatedSourceAddr));
            if (altinfo != OSPC_OSNULL) {
                OSPM_FREE(altinfo);
                altinfo = OSPC_OSNULL;
            }
        }
        OSPPListDelete((OSPTLIST *)&((*ospvDest)->UpdatedSourceAddr));

        while (!OSPPListEmpty((OSPTLIST *)&((*ospvDest)->UpdatedDeviceInfo))) {
            altinfo = (OSPT_ALTINFO *)OSPPListRemove((OSPTLIST *)&((*ospvDest)->UpdatedDeviceInfo));
            if (altinfo != OSPC_OSNULL) {
                OSPM_FREE(altinfo);
                altinfo = OSPC_OSNULL;
            }
        }
        OSPPListDelete((OSPTLIST *)&((*ospvDest)->UpdatedDeviceInfo));
        /* finally delete the object */
        OSPM_FREE(*ospvDest);
        *ospvDest = OSPC_OSNULL;
    }
}

/*
 * OSPPDestFromElement() - get destination from an XML element
 */
int OSPPDestFromElement(        /* returns error code */
    OSPT_XML_ELEM *ospvElem,    /* input is XML element */
    OSPT_DEST **ospvDest)       /* where to put destination pointer */
{
    int error = OSPC_ERR_NO_ERROR;
    OSPT_XML_ELEM *elem = OSPC_OSNULL;
    OSPT_DEST *dest = OSPC_OSNULL;
    OSPT_TOKEN *token = OSPC_OSNULL;
    OSPT_CALL_ID *callid = OSPC_OSNULL;
    unsigned long failure = 0L;
    OSPTTIME t = 0L;

    if (ospvElem == OSPC_OSNULL) {
        error = OSPC_ERR_XML_NO_ELEMENT;
    }

    if (error == OSPC_ERR_NO_ERROR) {
        if (ospvDest == OSPC_OSNULL) {
            error = OSPC_ERR_DATA_NO_DEST;
        }
    }

    if (error == OSPC_ERR_NO_ERROR) {
        /* create the destination object */
        dest = OSPPDestNew();
        if (dest == OSPC_OSNULL) {
            error = OSPC_ERR_DATA_NO_DEST;
        }
    }

    if (error == OSPC_ERR_NO_ERROR) {
        /*
         * The Destination element should consist of several child
         * elements. We'll run through what's there and pick out
         * the information we need.
         */
        for (elem = OSPPXMLElemFirstChild(ospvElem);
            (elem != OSPC_OSNULL) && (error == OSPC_ERR_NO_ERROR);
            elem = OSPPXMLElemNextChild(ospvElem, elem))
        {
            switch (OSPPMsgElemGetPart(OSPPXMLElemGetName(elem))) {
            case OSPC_MELEM_PROTOCOL:
                OSPPDestProtocolFromElement(elem, dest);
                break;
            case OSPC_MELEM_DESTOSPVERSION:
                OSPPDestSetOSPVersion(dest, OSPPXMLElemGetValue(elem));
                break;
            case OSPC_MELEM_SRCINFO:
                OSPPSrcInfoFromElement(elem, dest);
                break;
            case OSPC_MELEM_DESTINFO:
                OSPPDestInfoFromElement(elem, dest);
                break;
            case OSPC_MELEM_DESTALT:
                OSPPDestAltFromElement(elem, dest);
                break;
            case OSPC_MELEM_DESTSIGADDR:
                OSPPDestSetAddr(dest, OSPPXMLElemGetValue(elem));
                break;
            case OSPC_MELEM_TOKEN:
                if ((error = OSPPTokenFromElement(elem, &token)) == OSPC_ERR_NO_ERROR) {
                    OSPPDestAddToken(dest, token);
                }
                break;
            case OSPC_MELEM_VALIDAFTER:
                t = 0;
                if (*(OSPPXMLElemGetValue(elem)) != 0) {
                    error = OSPPMsgTimeFromElement(elem, &t);
                } else {
                    t = OSPC_TIMEMIN;
                }
                if (error == OSPC_ERR_NO_ERROR) {
                    OSPPDestSetValidAfter(dest, t);
                }
                break;
            case OSPC_MELEM_VALIDUNTIL:
                t = 0;
                if (*(OSPPXMLElemGetValue(elem)) != 0) {
                    error = OSPPMsgTimeFromElement(elem, &t);
                } else {
                    t = OSPC_TIMEMAX;
                }
                if (error == OSPC_ERR_NO_ERROR) {
                    OSPPDestSetValidUntil(dest, t);
                }
                break;
            case OSPC_MELEM_USAGEDETAIL:
                error = OSPPUsageDetailFromElement(elem, dest);
                break;
            case OSPC_MELEM_AUTHURL:
                OSPPDestSetAuthority(dest, OSPPXMLElemGetValue(elem));
                break;
            case OSPC_MELEM_CALLID:
                if ((error = OSPPCallIdFromElement(elem, &callid)) == OSPC_ERR_NO_ERROR) {
                    OSPPDestSetCallId(dest, callid->Value, callid->Length);
                    OSPPCallIdDelete(&callid);
                }
                break;
            case OSPC_MELEM_TERMCAUSE:
                if ((error = OSPPMsgNumFromElement(elem, &failure)) == OSPC_ERR_NO_ERROR) {
                    OSPPDestSetTermCause(dest, OSPC_TCAUSE_Q850, (unsigned)failure, OSPC_OSNULL);
                }
                break;
            default:
                /*
                 * This is an element we don't understand. If it's
                 * critical, then we have to report an error.
                 * Otherwise we can ignore it.
                 */
                if (OSPPMsgElemIsCritical(elem)) {
                    error = OSPC_ERR_XML_BAD_ELEMENT;
                }
                break;
            }
        }
    }

    if (error == OSPC_ERR_NO_ERROR) {
        *ospvDest = dest;
    } else {
        if (dest != OSPC_OSNULL) {
            OSPPDestDelete(&dest);
        }
    }

    return error;
}

/*
 * OSPPDestSetCallId() - sets the call ID for a destination
 */
void OSPPDestSetCallId(             /* nothing returned */
    OSPT_DEST *ospvDest,            /* destination */
    const unsigned char *ospvValue, /* call ID value */
    unsigned ospvLen)
{
    if (ospvDest != OSPC_OSNULL) {
        if (ospvValue != OSPC_OSNULL) {
            if (ospvDest->CallId != OSPC_OSNULL) {
                OSPPCallIdDelete(&(ospvDest->CallId));
            }
            ospvDest->CallId = OSPPCallIdNew(ospvLen, ospvValue);
        }
    }
}

void OSPPDestSetDestinationCount(
    OSPT_DEST *ospvDest,
    unsigned ospvCount)
{
    if (ospvDest != OSPC_OSNULL) {
        ospvDest->Count = ospvCount;
    }
}

unsigned OSPPDestGetDestinationCount(
    OSPT_DEST *ospvDest)
{
    unsigned count = 0;

    if (ospvDest != OSPC_OSNULL) {
        count = ospvDest->Count;
    }

    return count;
}

void OSPPDestSetSetupAttempt(
    OSPT_DEST *ospvDest,
    int ospvAttempt)
{
    if (ospvDest != OSPC_OSNULL) {
        ospvDest->SetupAttempt = ospvAttempt;
    }
}

int OSPPDestGetSetupAttempt(
    OSPT_DEST *ospvDest)
{
    int attempt = -1;

    if (ospvDest != OSPC_OSNULL) {
        attempt = ospvDest->SetupAttempt;
    }

    return attempt;
}

OSPE_PROTOCOL_NAME OSPPDestProtocolGetPart(
    const char *ospvName)
{
    OSPE_PROTOCOL_NAME part = OSPC_PROTNAME_UNKNOWN;

    if (ospvName != OSPC_OSNULL) {
        part = (OSPE_PROTOCOL_NAME)OSPPMsgDescGetPart(ospvName, OSPV_PROTNAME_DESCS, OSPC_PROTNAME_NUMBER);
    }

    return part;
}

const char *OSPPDestProtocolGetName(
    OSPE_PROTOCOL_NAME ospvPart)
{
    const char *name = OSPC_OSNULL;

    if ((ospvPart >= OSPC_PROTNAME_START) && (ospvPart < OSPC_PROTNAME_NUMBER)) {
        name = OSPPMsgDescGetName((OSPT_MSG_PART)ospvPart, OSPV_PROTNAME_DESCS, OSPC_PROTNAME_NUMBER);
    }

    return name;
}

void OSPPDestProtocolFromElement(
    OSPT_XML_ELEM *ospvElem,
    OSPT_DEST *ospvDest)
{
    OSPT_XML_ATTR* attr = OSPC_OSNULL;
    OSPE_ALTINFO type = OSPC_ALTINFO_DESTINATION;

    for (attr = (OSPT_XML_ATTR*)OSPPXMLElemFirstAttr(ospvElem);
        (attr != OSPC_OSNULL);
        attr = (OSPT_XML_ATTR*)OSPPXMLElemNextAttr(ospvElem, attr))
    {
        if (OSPPMsgAttrGetPart(OSPPXMLAttrGetName(attr)) == OSPC_MATTR_TYPE) {
            type = OSPPAltInfoTypeGetPart(OSPPXMLAttrGetValue(attr));
        }
    }
    switch (type) {
    case OSPC_ALTINFO_DESTINATION:
    case OSPC_ALTINFO_NA:
        OSPPDestSetProtocol(ospvDest, OSPPXMLElemGetValue(ospvElem));
        break;
    case OSPC_ALTINFO_SOURCE:
    default:
        break;
    }
}

void OSPPSrcInfoFromElement(
    OSPT_XML_ELEM *ospvElem,
    OSPT_DEST *ospvDest)
{
    OSPT_XML_ATTR* attr = OSPC_OSNULL;

    for (attr = (OSPT_XML_ATTR*)OSPPXMLElemFirstAttr(ospvElem);
        (attr != OSPC_OSNULL);
        attr = (OSPT_XML_ATTR*)OSPPXMLElemNextAttr(ospvElem, attr))
    {
        if (OSPPMsgAttrGetPart(OSPPXMLAttrGetName(attr)) == OSPC_MATTR_TYPE) {
            switch (OSPPAltInfoTypeGetPart(OSPPXMLAttrGetValue(attr))) {
            case OSPC_ALTINFO_E164:
                OSPPDestSetSrcNumber(ospvDest, OSPPXMLElemGetValue(ospvElem));
                break;
            case OSPC_ALTINFO_CNAM:
                OSPM_STRNCPY(ospvDest->CNAM, OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->CNAM));
                break;
            default:
                break;
            }
        }
    }
}

void OSPPDestInfoFromElement(
    OSPT_XML_ELEM *ospvElem,
    OSPT_DEST *ospvDest)
{
    OSPT_XML_ATTR* attr = OSPC_OSNULL;

    for (attr = (OSPT_XML_ATTR*)OSPPXMLElemFirstAttr(ospvElem);
        (attr != OSPC_OSNULL);
        attr = (OSPT_XML_ATTR*)OSPPXMLElemNextAttr(ospvElem, attr))
    {
        if (OSPPMsgAttrGetPart(OSPPXMLAttrGetName(attr)) == OSPC_MATTR_TYPE) {
            switch (OSPPAltInfoTypeGetPart(OSPPXMLAttrGetValue(attr))) {
            case OSPC_ALTINFO_E164:
                OSPM_STRNCPY(ospvDest->DestinationNumber, OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->DestinationNumber));
                break;
            case OSPC_ALTINFO_NPRN:
                OSPM_STRNCPY(ospvDest->NPRn, OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->NPRn));
                break;
            case OSPC_ALTINFO_NPCIC:
                OSPM_STRNCPY(ospvDest->NPCic, OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->NPCic));
                break;
            case OSPC_ALTINFO_NPDI:
                if (OSPM_STRCASECMP(OSPPXMLElemGetValue(ospvElem), OSPPAltInfoTypeGetName(OSPC_ALTINFO_TRUE)) == 0) {
                    ospvDest->NPNpdi = OSPC_TRUE;
                } else {
                    ospvDest->NPNpdi = OSPC_FALSE;
                }
                break;
            case OSPC_ALTINFO_SPID:
                OSPM_STRNCPY(ospvDest->OpName[OSPC_OPNAME_SPID], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->OpName[OSPC_OPNAME_SPID]));
                break;
            case OSPC_ALTINFO_ALTSPID:
                OSPM_STRNCPY(ospvDest->OpName[OSPC_OPNAME_ALTSPID], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->OpName[OSPC_OPNAME_ALTSPID]));
                break;
            case OSPC_ALTINFO_OCN:
                OSPM_STRNCPY(ospvDest->OpName[OSPC_OPNAME_OCN], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->OpName[OSPC_OPNAME_OCN]));
                break;
            case OSPC_ALTINFO_SPN:
                OSPM_STRNCPY(ospvDest->OpName[OSPC_OPNAME_SPN], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->OpName[OSPC_OPNAME_SPN]));
                break;
            case OSPC_ALTINFO_ALTSPN:
                OSPM_STRNCPY(ospvDest->OpName[OSPC_OPNAME_ALTSPN], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->OpName[OSPC_OPNAME_ALTSPN]));
                break;
            case OSPC_ALTINFO_MCC:
                OSPM_STRNCPY(ospvDest->OpName[OSPC_OPNAME_MCC], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->OpName[OSPC_OPNAME_MCC]));
                break;
            case OSPC_ALTINFO_MNC:
                OSPM_STRNCPY(ospvDest->OpName[OSPC_OPNAME_MNC], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->OpName[OSPC_OPNAME_MNC]));
                break;
            case OSPC_ALTINFO_SIP:
                OSPM_STRNCPY(ospvDest->URL[OSPC_URL_SIP], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->URL[OSPC_URL_SIP]));
                break;
            case OSPC_ALTINFO_SMS:
                OSPM_STRNCPY(ospvDest->URL[OSPC_URL_SMS], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->URL[OSPC_URL_SMS]));
                break;
            case OSPC_ALTINFO_MMS:
                OSPM_STRNCPY(ospvDest->URL[OSPC_URL_MMS], OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->URL[OSPC_URL_MMS]));
                break;
            default:
                break;
            }
        }
    }
}

void OSPPDestAltFromElement(
    OSPT_XML_ELEM *ospvElem,
    OSPT_DEST *ospvDest)
{
    OSPT_XML_ATTR* attr = OSPC_OSNULL;

    for (attr = (OSPT_XML_ATTR*)OSPPXMLElemFirstAttr(ospvElem);
        (attr != OSPC_OSNULL);
        attr = (OSPT_XML_ATTR*)OSPPXMLElemNextAttr(ospvElem, attr))
    {
        if (OSPPMsgAttrGetPart(OSPPXMLAttrGetName(attr)) == OSPC_MATTR_TYPE) {
            switch (OSPPAltInfoTypeGetPart(OSPPXMLAttrGetValue(attr))) {
            case OSPC_ALTINFO_NETWORK:
                OSPPDestSetNetworkAddr(ospvDest, OSPPXMLElemGetValue(ospvElem));
                break;
            case OSPC_ALTINFO_SWITCHID:
                OSPM_STRNCPY(ospvDest->SwitchId, OSPPXMLElemGetValue(ospvElem), sizeof(ospvDest->SwitchId));
                break;
            default:
                break;
            }
        }
    }
}

int OSPPUsageDetailFromElement(
    OSPT_XML_ELEM *ospvElem,
    OSPT_DEST *ospvDest)
{
    int error = OSPC_ERR_NO_ERROR;
    OSPT_XML_ELEM *elem = OSPC_OSNULL;
    OSPTBOOL hasamount = OSPC_FALSE;
    OSPTBOOL hasincrement = OSPC_FALSE;
    OSPTBOOL hasunit = OSPC_FALSE;
    unsigned long amount = 0;
    unsigned long increment = 0;

    if (ospvElem == OSPC_OSNULL) {
        error = OSPC_ERR_XML_NO_ELEMENT;
    }

    if (error == OSPC_ERR_NO_ERROR) {
        if (ospvDest == OSPC_OSNULL) {
            error = OSPC_ERR_DATA_NO_DEST;
        }
    }

    if (error == OSPC_ERR_NO_ERROR) {
        for (elem = OSPPXMLElemFirstChild(ospvElem);
            (elem != OSPC_OSNULL) && (error == OSPC_ERR_NO_ERROR);
            elem = OSPPXMLElemNextChild(ospvElem, elem))
        {
            switch (OSPPMsgElemGetPart(OSPPXMLElemGetName(elem))) {
            case OSPC_MELEM_AMOUNT:
                if ((error = OSPPMsgNumFromElement(elem, &amount)) == OSPC_ERR_NO_ERROR) {
                    hasamount = OSPC_TRUE;
                }
                break;
            case OSPC_MELEM_INCREMENT:
                if ((error = OSPPMsgNumFromElement(elem, &increment)) == OSPC_ERR_NO_ERROR) {
                    hasincrement = OSPC_TRUE;
                }
                break;
            case OSPC_MELEM_UNIT:
                /* The only unit we support is seconds. If this is different, then we declare an error. */
                if (OSPM_STRCMP(OSPPXMLElemGetValue(elem), "s") != 0) {
                    error = OSPC_ERR_BAD_SERVICE;
                } else {
                    hasunit = OSPC_TRUE;
                }
                break;
            case OSPC_MELEM_SERVICE:
                OSPPServiceFromElement(elem, ospvDest);
                break;
            default:
                /*
                 * This is an element we don't understand. If it's
                 * critical, then we have to report an error.
                 * Otherwise we can ignore it.
                 */
                if (OSPPMsgElemIsCritical(elem)) {
                    error = OSPC_ERR_XML_BAD_ELEMENT;
                }
                break;
            }
        }
    }

    if (error == OSPC_ERR_NO_ERROR) {
        if (hasamount && hasincrement && hasunit) {
            OSPPDestSetLimit(ospvDest, (unsigned)(increment * amount));
        } else {
            if ((ospvDest->ServiceType == OSPC_SERVICE_NPQUERY) || (ospvDest->ServiceType == OSPC_SERVICE_CNAMQUERY)) {
                OSPPDestSetLimit(ospvDest, 0);
            } else {
                error = OSPC_ERR_XML_BAD_ELEMENT;
            }
        }
    }

    return error;
}

void OSPPServiceFromElement(
    OSPT_XML_ELEM *ospvElem,
    OSPT_DEST *ospvDest)
{
    int error = OSPC_ERR_NO_ERROR;
    OSPT_XML_ELEM *elem = OSPC_OSNULL;
    OSPT_XML_ATTR* attr = OSPC_OSNULL;

    for (elem = OSPPXMLElemFirstChild(ospvElem);
        (elem != OSPC_OSNULL) && (error == OSPC_ERR_NO_ERROR);
        elem = OSPPXMLElemNextChild(ospvElem, elem))
    {
        switch (OSPPMsgElemGetPart(OSPPXMLElemGetName(elem))) {
        case OSPC_MELEM_SERVICETYPE:
            for (attr = OSPPXMLElemFirstAttr(elem); (attr != OSPC_OSNULL); attr = OSPPXMLElemNextAttr(elem, attr)) {
                if (OSPPMsgAttrGetPart(OSPPXMLAttrGetName(attr)) == OSPC_MATTR_TYPE) {
                    ospvDest->ServiceType = OSPPServiceGetPart(OSPPXMLAttrGetValue(attr));
                    break;
                }
            }
            break;
        default:
            break;
        }
    }
}
