/* * uriparser - RFC 3986 URI parsing library * * Copyright (C) 2007, Weijia Song * Copyright (C) 2007, Sebastian Pipping * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the nor the names of its * contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /* What encodings are enabled? */ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ # ifdef URI_ENABLE_ANSI # define URI_PASS_ANSI 1 # include "UriQuery.c" # undef URI_PASS_ANSI # endif # ifdef URI_ENABLE_UNICODE # define URI_PASS_UNICODE 1 # include "UriQuery.c" # undef URI_PASS_UNICODE # endif #else # ifdef URI_PASS_ANSI # include # else # include # include # endif #ifndef URI_DOXYGEN # include # include "UriCommon.h" #endif static int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten, int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks); static UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) ** prevNext, int * itemCount, const URI_CHAR * keyFirst, const URI_CHAR * keyAfter, const URI_CHAR * valueFirst, const URI_CHAR * valueAfter, UriBool plusToSpace, UriBreakConversion breakConversion); int URI_FUNC(ComposeQueryCharsRequired)(const URI_TYPE(QueryList) * queryList, int * charsRequired) { const UriBool spaceToPlus = URI_TRUE; const UriBool normalizeBreaks = URI_TRUE; return URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, charsRequired, spaceToPlus, normalizeBreaks); } int URI_FUNC(ComposeQueryCharsRequiredEx)(const URI_TYPE(QueryList) * queryList, int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks) { if ((queryList == NULL) || (charsRequired == NULL)) { return URI_ERROR_NULL; } return URI_FUNC(ComposeQueryEngine)(NULL, queryList, 0, NULL, charsRequired, spaceToPlus, normalizeBreaks); } int URI_FUNC(ComposeQuery)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten) { const UriBool spaceToPlus = URI_TRUE; const UriBool normalizeBreaks = URI_TRUE; return URI_FUNC(ComposeQueryEx)(dest, queryList, maxChars, charsWritten, spaceToPlus, normalizeBreaks); } int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten, UriBool spaceToPlus, UriBool normalizeBreaks) { if ((dest == NULL) || (queryList == NULL)) { return URI_ERROR_NULL; } if (maxChars < 1) { return URI_ERROR_OUTPUT_TOO_LARGE; } return URI_FUNC(ComposeQueryEngine)(dest, queryList, maxChars, charsWritten, NULL, spaceToPlus, normalizeBreaks); } int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest, const URI_TYPE(QueryList) * queryList) { const UriBool spaceToPlus = URI_TRUE; const UriBool normalizeBreaks = URI_TRUE; return URI_FUNC(ComposeQueryMallocEx)(dest, queryList, spaceToPlus, normalizeBreaks); } int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest, const URI_TYPE(QueryList) * queryList, UriBool spaceToPlus, UriBool normalizeBreaks) { int charsRequired; int res; URI_CHAR * queryString; if (dest == NULL) { return URI_ERROR_NULL; } /* Calculate space */ res = URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, &charsRequired, spaceToPlus, normalizeBreaks); if (res != URI_SUCCESS) { return res; } charsRequired++; /* Allocate space */ queryString = malloc(charsRequired * sizeof(URI_CHAR)); if (queryString == NULL) { return URI_ERROR_MALLOC; } /* Put query in */ res = URI_FUNC(ComposeQueryEx)(queryString, queryList, charsRequired, NULL, spaceToPlus, normalizeBreaks); if (res != URI_SUCCESS) { free(queryString); return res; } *dest = queryString; return URI_SUCCESS; } int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten, int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks) { UriBool firstItem = URI_TRUE; int ampersandLen = 0; URI_CHAR * write = dest; /* Subtract terminator */ if (dest == NULL) { *charsRequired = 0; } else { maxChars--; } while (queryList != NULL) { const URI_CHAR * const key = queryList->key; const URI_CHAR * const value = queryList->value; const int worstCase = (normalizeBreaks == URI_TRUE ? 6 : 3); const int keyLen = (key == NULL) ? 0 : (int)URI_STRLEN(key); const int keyRequiredChars = worstCase * keyLen; const int valueLen = (value == NULL) ? 0 : (int)URI_STRLEN(value); const int valueRequiredChars = worstCase * valueLen; if (dest == NULL) { if (firstItem == URI_TRUE) { ampersandLen = 1; firstItem = URI_FALSE; } (*charsRequired) += ampersandLen + keyRequiredChars + ((value == NULL) ? 0 : 1 + valueRequiredChars); } else { URI_CHAR * afterKey; if ((write - dest) + ampersandLen + keyRequiredChars > maxChars) { return URI_ERROR_OUTPUT_TOO_LARGE; } /* Copy key */ if (firstItem == URI_TRUE) { firstItem = URI_FALSE; } else { write[0] = _UT('&'); write++; } afterKey = URI_FUNC(EscapeEx)(key, key + keyLen, write, spaceToPlus, normalizeBreaks); write += (afterKey - write); if (value != NULL) { URI_CHAR * afterValue; if ((write - dest) + 1 + valueRequiredChars > maxChars) { return URI_ERROR_OUTPUT_TOO_LARGE; } /* Copy value */ write[0] = _UT('='); write++; afterValue = URI_FUNC(EscapeEx)(value, value + valueLen, write, spaceToPlus, normalizeBreaks); write += (afterValue - write); } } queryList = queryList->next; } if (dest != NULL) { write[0] = _UT('\0'); if (charsWritten != NULL) { *charsWritten = (int)(write - dest) + 1; /* .. for terminator */ } } return URI_SUCCESS; } UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) ** prevNext, int * itemCount, const URI_CHAR * keyFirst, const URI_CHAR * keyAfter, const URI_CHAR * valueFirst, const URI_CHAR * valueAfter, UriBool plusToSpace, UriBreakConversion breakConversion) { const int keyLen = (int)(keyAfter - keyFirst); const int valueLen = (int)(valueAfter - valueFirst); URI_CHAR * key; URI_CHAR * value; if ((prevNext == NULL) || (itemCount == NULL) || (keyFirst == NULL) || (keyAfter == NULL) || (keyFirst > keyAfter) || (valueFirst > valueAfter) || ((keyFirst == keyAfter) && (valueFirst == NULL) && (valueAfter == NULL))) { return URI_TRUE; } /* Append new empty item */ *prevNext = malloc(1 * sizeof(URI_TYPE(QueryList))); if (*prevNext == NULL) { return URI_FALSE; /* Raises malloc error */ } (*prevNext)->next = NULL; /* Fill key */ key = malloc((keyLen + 1) * sizeof(URI_CHAR)); if (key == NULL) { free(*prevNext); *prevNext = NULL; return URI_FALSE; /* Raises malloc error */ } key[keyLen] = _UT('\0'); if (keyLen > 0) { /* Copy 1:1 */ memcpy(key, keyFirst, keyLen * sizeof(URI_CHAR)); /* Unescape */ URI_FUNC(UnescapeInPlaceEx)(key, plusToSpace, breakConversion); } (*prevNext)->key = key; /* Fill value */ if (valueFirst != NULL) { value = malloc((valueLen + 1) * sizeof(URI_CHAR)); if (value == NULL) { free(key); free(*prevNext); *prevNext = NULL; return URI_FALSE; /* Raises malloc error */ } value[valueLen] = _UT('\0'); if (valueLen > 0) { /* Copy 1:1 */ memcpy(value, valueFirst, valueLen * sizeof(URI_CHAR)); /* Unescape */ URI_FUNC(UnescapeInPlaceEx)(value, plusToSpace, breakConversion); } (*prevNext)->value = value; } else { value = NULL; } (*prevNext)->value = value; (*itemCount)++; return URI_TRUE; } void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList) { while (queryList != NULL) { URI_TYPE(QueryList) * nextBackup = queryList->next; free((URI_CHAR *)queryList->key); /* const cast */ free((URI_CHAR *)queryList->value); /* const cast */ free(queryList); queryList = nextBackup; } } int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) ** dest, int * itemCount, const URI_CHAR * first, const URI_CHAR * afterLast) { const UriBool plusToSpace = URI_TRUE; const UriBreakConversion breakConversion = URI_BR_DONT_TOUCH; return URI_FUNC(DissectQueryMallocEx)(dest, itemCount, first, afterLast, plusToSpace, breakConversion); } int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) ** dest, int * itemCount, const URI_CHAR * first, const URI_CHAR * afterLast, UriBool plusToSpace, UriBreakConversion breakConversion) { const URI_CHAR * walk = first; const URI_CHAR * keyFirst = first; const URI_CHAR * keyAfter = NULL; const URI_CHAR * valueFirst = NULL; const URI_CHAR * valueAfter = NULL; URI_TYPE(QueryList) ** prevNext = dest; int nullCounter; int * itemsAppended = (itemCount == NULL) ? &nullCounter : itemCount; if ((dest == NULL) || (first == NULL) || (afterLast == NULL)) { return URI_ERROR_NULL; } if (first > afterLast) { return URI_ERROR_RANGE_INVALID; } *dest = NULL; *itemsAppended = 0; /* Parse query string */ for (; walk < afterLast; walk++) { switch (*walk) { case _UT('&'): if (valueFirst != NULL) { valueAfter = walk; } else { keyAfter = walk; } if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter, valueFirst, valueAfter, plusToSpace, breakConversion) == URI_FALSE) { /* Free list we built */ *itemsAppended = 0; URI_FUNC(FreeQueryList)(*dest); return URI_ERROR_MALLOC; } /* Make future items children of the current */ if ((prevNext != NULL) && (*prevNext != NULL)) { prevNext = &((*prevNext)->next); } if (walk + 1 < afterLast) { keyFirst = walk + 1; } else { keyFirst = NULL; } keyAfter = NULL; valueFirst = NULL; valueAfter = NULL; break; case _UT('='): /* NOTE: WE treat the first '=' as a separator, */ /* all following go into the value part */ if (keyAfter == NULL) { keyAfter = walk; if (walk + 1 <= afterLast) { valueFirst = walk + 1; valueAfter = walk + 1; } } break; default: break; } } if (valueFirst != NULL) { /* Must be key/value pair */ valueAfter = walk; } else { /* Must be key only */ keyAfter = walk; } if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter, valueFirst, valueAfter, plusToSpace, breakConversion) == URI_FALSE) { /* Free list we built */ *itemsAppended = 0; URI_FUNC(FreeQueryList)(*dest); return URI_ERROR_MALLOC; } return URI_SUCCESS; } #endif