STATUS getCanonicalQueryParams()

in src/source/Common/AwsV4Signer.c [249:403]


STATUS getCanonicalQueryParams(PCHAR pUrl, UINT32 urlLen, BOOL uriEncode, PCHAR* ppQuery, PUINT32 pQueryLen)
{
    ENTERS();
    STATUS retStatus = STATUS_SUCCESS;
    PCHAR pUriStart, pEndPtr, pQueryParamStart, pQueryParamEnd, pParamValue, pNewParam, pCurPtr, pParam, pQuery = NULL;
    BOOL iterate = TRUE, inserted, firstParam = TRUE, defaultPath;
    PSingleList pSingleList = NULL;
    PSingleListNode pCurNode, pPrevNode;
    UINT32 nameLen, valueLen, maxLen, remaining, queryLen = 0;
    UINT64 item;

    CHK(pUrl != NULL && pQueryLen != NULL && ppQuery != NULL, STATUS_NULL_ARG);

    if (urlLen == 0) {
        urlLen = (UINT32) STRNLEN(pUrl, MAX_URI_CHAR_LEN);
    }

    pEndPtr = pUrl + urlLen;

    // Set the start of the query params to the end of the canonical uri
    CHK_STATUS(getCanonicalUri(pUrl, urlLen, &pUriStart, &pQueryParamStart, &defaultPath));

    // Check if we have any params
    CHK(*pQueryParamStart == '?', retStatus);

    // Skip the '?'
    pQueryParamStart++;

    // Create the single list to hold the sorted params
    CHK_STATUS(singleListCreate(&pSingleList));

    while (iterate) {
        pQueryParamEnd = STRNCHR(pQueryParamStart, (UINT32)(pEndPtr - pQueryParamStart), '&');
        if (pQueryParamEnd == NULL) {
            // break the loop
            iterate = FALSE;

            pQueryParamEnd = pEndPtr;
        }

        // Process the resulting param name and value
        CHK(NULL != (pParamValue = STRNCHR(pQueryParamStart, (UINT32)(pQueryParamEnd - pQueryParamStart), '=')), STATUS_INVALID_ARG);
        nameLen = (UINT32)(pParamValue - pQueryParamStart);

        // Advance param start past '='
        pParamValue++;
        valueLen = (UINT32)(pQueryParamEnd - pParamValue);

        // Max len is 3 times the size of the allocation to account for max bloat for encoding
        maxLen = MIN(MAX_URI_CHAR_LEN, nameLen + 1 + valueLen * 3 + 1);

        CHK(NULL != (pNewParam = (PCHAR) MEMALLOC(maxLen * SIZEOF(CHAR))), STATUS_NOT_ENOUGH_MEMORY);

        pCurPtr = pNewParam;

        // Reconstruct the query string
        MEMCPY(pCurPtr, pQueryParamStart, nameLen * SIZEOF(CHAR));
        pCurPtr += nameLen;

        *pCurPtr++ = '=';

        // Calculate the remaining, taking into account '=' and the NULL terminator
        remaining = maxLen - nameLen - 1 - 1;

        // Encode the value
        if (uriEncode) {
            CHK_STATUS(uriEncodeString(pParamValue, valueLen, pCurPtr, &remaining));
        } else {
            STRNCPY(pCurPtr, pParamValue, valueLen);
        }

        // Iterate through the list and insert in an alpha order
        CHK_STATUS(singleListGetHeadNode(pSingleList, &pCurNode));
        inserted = FALSE;
        pPrevNode = NULL;
        while (!inserted && pCurNode != NULL) {
            CHK_STATUS(singleListGetNodeData(pCurNode, &item));
            pParam = (PCHAR) item;

            if (STRCMP(pNewParam, pParam) <= 0) {
                if (pPrevNode == NULL) {
                    // Insert at the head
                    CHK_STATUS(singleListInsertItemHead(pSingleList, (UINT64) pNewParam));
                } else {
                    CHK_STATUS(singleListInsertItemAfter(pSingleList, pPrevNode, (UINT64) pNewParam));
                }

                // Early return
                inserted = TRUE;
            }

            pPrevNode = pCurNode;

            CHK_STATUS(singleListGetNextNode(pCurNode, &pCurNode));
        }

        if (!inserted) {
            // If not inserted then add to the tail
            CHK_STATUS(singleListInsertItemTail(pSingleList, (UINT64) pNewParam));
        }

        // Advance the start
        pQueryParamStart = pQueryParamEnd + 1;
    }

    // Now, we can re-create the query params
    remaining = MAX_URI_CHAR_LEN;
    CHK(NULL != (pQuery = (PCHAR) MEMALLOC(remaining + 1)), STATUS_NOT_ENOUGH_MEMORY);

    *(pQuery + remaining) = '\0';
    pCurPtr = pQuery;

    CHK_STATUS(singleListGetHeadNode(pSingleList, &pCurNode));
    while (pCurNode != NULL) {
        CHK_STATUS(singleListGetNodeData(pCurNode, &item));
        pParam = (PCHAR) item;

        // Account for '&'
        maxLen = (UINT32) STRLEN(pParam);

        CHK(maxLen + 1 < remaining, STATUS_INVALID_ARG);

        if (firstParam) {
            firstParam = FALSE;
        } else {
            *pCurPtr++ = '&';
        }

        MEMCPY(pCurPtr, pParam, maxLen * SIZEOF(CHAR));
        pCurPtr += maxLen;

        CHK_STATUS(singleListGetNextNode(pCurNode, &pCurNode));
    }

    *pCurPtr = '\0';
    queryLen = (UINT32)(pCurPtr - pQuery);

CleanUp:

    if (pSingleList != NULL) {
        singleListClear(pSingleList, TRUE);
        singleListFree(pSingleList);
    }

    if (ppQuery != NULL) {
        *ppQuery = pQuery;
    }

    if (pQueryLen != NULL) {
        *pQueryLen = queryLen;
    }

    LEAVES();
    return retStatus;
}