/*
 * uriparser - RFC 3986 URI parsing library
 *
 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
 * Copyright (C) 2007, Sebastian Pipping <sebastian@pipping.org>
 * All rights reserved.
 *
 * Redistribution and use in source  and binary forms, with or without
 * modification, are permitted provided  that the following conditions
 * are met:
 *
 *     1. Redistributions  of  source  code   must  retain  the  above
 *        copyright notice, this list  of conditions and the following
 *        disclaimer.
 *
 *     2. 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.
 *
 *     3. Neither the  name of the  copyright holder 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 HOLDER  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.
 */

/**
 * @file UriNormalize.c
 * Holds the RFC 3986 %URI normalization implementation.
 * NOTE: This source file includes itself twice.
 */

/* What encodings are enabled? */
#include <uriparser/UriDefsConfig.h>
#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
/* Include SELF twice */
#  ifdef URI_ENABLE_ANSI
#    define URI_PASS_ANSI 1
#    include "UriNormalize.c"
#    undef URI_PASS_ANSI
#  endif
#  ifdef URI_ENABLE_UNICODE
#    define URI_PASS_UNICODE 1
#    include "UriNormalize.c"
#    undef URI_PASS_UNICODE
#  endif
#else
#  ifdef URI_PASS_ANSI
#    include <uriparser/UriDefsAnsi.h>
#  else
#    include <uriparser/UriDefsUnicode.h>
#    include <wchar.h>
#  endif

#  ifndef URI_DOXYGEN
#    include <uriparser/Uri.h>
#    include "UriNormalizeBase.h"
#    include "UriCommon.h"
#    include "UriMemory.h"
#  endif

#  include <assert.h>
#  include <stdint.h>  // SIZE_MAX

static int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask,
                                           unsigned int * outMask,
                                           UriMemoryManager * memory);

static UriBool URI_FUNC(MakeRangeOwner)(unsigned int * revertMask, unsigned int maskTest,
                                        URI_TYPE(TextRange) * range,
                                        UriMemoryManager * memory);
static UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, unsigned int * revertMask,
                                         UriMemoryManager * memory);

static void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first,
                                                const URI_CHAR ** afterLast);
static UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first,
                                                  const URI_CHAR ** afterLast,
                                                  UriMemoryManager * memory);
static void URI_FUNC(FixPercentEncodingEngine)(const URI_CHAR * inFirst,
                                               const URI_CHAR * inAfterLast,
                                               const URI_CHAR * outFirst,
                                               const URI_CHAR ** outAfterLast);

static UriBool URI_FUNC(ContainsUppercaseLetters)(const URI_CHAR * first,
                                                  const URI_CHAR * afterLast);
static UriBool URI_FUNC(ContainsUglyPercentEncoding)(const URI_CHAR * first,
                                                     const URI_CHAR * afterLast);

static void URI_FUNC(LowercaseInplace)(const URI_CHAR * first,
                                       const URI_CHAR * afterLast);
static void URI_FUNC(LowercaseInplaceExceptPercentEncoding)(const URI_CHAR * first,
                                                            const URI_CHAR * afterLast);
static UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first,
                                         const URI_CHAR ** afterLast,
                                         UriMemoryManager * memory);

void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, unsigned int revertMask,
                              UriMemoryManager * memory) {
    if (revertMask & URI_NORMALIZE_SCHEME) {
        /* NOTE: A scheme cannot be the empty string
         *       so no need to compare .first with .afterLast, here. */
        memory->free(memory, (URI_CHAR *)uri->scheme.first);
        uri->scheme.first = NULL;
        uri->scheme.afterLast = NULL;
    }

    if (revertMask & URI_NORMALIZE_USER_INFO) {
        if (uri->userInfo.first != uri->userInfo.afterLast) {
            memory->free(memory, (URI_CHAR *)uri->userInfo.first);
        }
        uri->userInfo.first = NULL;
        uri->userInfo.afterLast = NULL;
    }

    if (revertMask & URI_NORMALIZE_HOST) {
        if (uri->hostData.ipFuture.first != NULL) {
            /* IPvFuture */
            /* NOTE: An IPvFuture address cannot be the empty string
             *       so no need to compare .first with .afterLast, here. */
            memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first);
            uri->hostData.ipFuture.first = NULL;
            uri->hostData.ipFuture.afterLast = NULL;
            uri->hostText.first = NULL;
            uri->hostText.afterLast = NULL;
        } else if (uri->hostText.first != NULL) {
            /* Regname */
            if (uri->hostText.first != uri->hostText.afterLast) {
                memory->free(memory, (URI_CHAR *)uri->hostText.first);
            }
            uri->hostText.first = NULL;
            uri->hostText.afterLast = NULL;
        }
    }

    /* NOTE: Port cannot happen! */

    if (revertMask & URI_NORMALIZE_PATH) {
        URI_TYPE(PathSegment) * walker = uri->pathHead;
        while (walker != NULL) {
            URI_TYPE(PathSegment) * const next = walker->next;
            if (walker->text.afterLast > walker->text.first) {
                memory->free(memory, (URI_CHAR *)walker->text.first);
            }
            memory->free(memory, walker);
            walker = next;
        }
        uri->pathHead = NULL;
        uri->pathTail = NULL;
    }

    if (revertMask & URI_NORMALIZE_QUERY) {
        if (uri->query.first != uri->query.afterLast) {
            memory->free(memory, (URI_CHAR *)uri->query.first);
        }
        uri->query.first = NULL;
        uri->query.afterLast = NULL;
    }

    if (revertMask & URI_NORMALIZE_FRAGMENT) {
        if (uri->fragment.first != uri->fragment.afterLast) {
            memory->free(memory, (URI_CHAR *)uri->fragment.first);
        }
        uri->fragment.first = NULL;
        uri->fragment.afterLast = NULL;
    }
}

static URI_INLINE UriBool URI_FUNC(ContainsUppercaseLetters)(const URI_CHAR * first,
                                                             const URI_CHAR * afterLast) {
    if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
        const URI_CHAR * i = first;
        for (; i < afterLast; i++) {
            /* 6.2.2.1 Case Normalization: uppercase letters in scheme or host */
            if ((*i >= _UT('A')) && (*i <= _UT('Z'))) {
                return URI_TRUE;
            }
        }
    }
    return URI_FALSE;
}

static URI_INLINE UriBool URI_FUNC(ContainsUglyPercentEncoding)(
    const URI_CHAR * first, const URI_CHAR * afterLast) {
    if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
        const URI_CHAR * i = first;
        for (; i + 2 < afterLast; i++) {
            if (i[0] == _UT('%')) {
                /* 6.2.2.1 Case Normalization: *
                 * lowercase percent-encodings */
                if (((i[1] >= _UT('a')) && (i[1] <= _UT('f')))
                    || ((i[2] >= _UT('a')) && (i[2] <= _UT('f')))) {
                    return URI_TRUE;
                } else {
                    /* 6.2.2.2 Percent-Encoding Normalization: *
                     * percent-encoded unreserved characters   */
                    const unsigned char left = URI_FUNC(HexdigToInt)(i[1]);
                    const unsigned char right = URI_FUNC(HexdigToInt)(i[2]);
                    const int code = 16 * left + right;
                    if (uriIsUnreserved(code)) {
                        return URI_TRUE;
                    }
                }
            }
        }
    }
    return URI_FALSE;
}

static URI_INLINE void URI_FUNC(LowercaseInplace)(const URI_CHAR * first,
                                                  const URI_CHAR * afterLast) {
    if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
        URI_CHAR * i = (URI_CHAR *)first;
        const int lowerUpperDiff = (_UT('a') - _UT('A'));
        for (; i < afterLast; i++) {
            if ((*i >= _UT('A')) && (*i <= _UT('Z'))) {
                *i = (URI_CHAR)(*i + lowerUpperDiff);
            }
        }
    }
}

static URI_INLINE void
URI_FUNC(LowercaseInplaceExceptPercentEncoding)(const URI_CHAR * first,
                                                const URI_CHAR * afterLast) {
    if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
        URI_CHAR * i = (URI_CHAR *)first;
        const int lowerUpperDiff = (_UT('a') - _UT('A'));
        for (; i < afterLast; i++) {
            if ((*i >= _UT('A')) && (*i <= _UT('Z'))) {
                *i = (URI_CHAR)(*i + lowerUpperDiff);
            } else if (*i == _UT('%')) {
                if (i + 3 >= afterLast) {
                    return;
                }
                i += 2;
            }
        }
    }
}

static URI_INLINE UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first,
                                                    const URI_CHAR ** afterLast,
                                                    UriMemoryManager * memory) {
    const int lowerUpperDiff = (_UT('a') - _UT('A'));
    URI_CHAR * buffer;
    size_t i = 0;

    if ((first == NULL) || (afterLast == NULL) || (*first == NULL)
        || (*afterLast == NULL)) {
        return URI_FALSE;
    }

    const size_t lenInChars = *afterLast - *first;
    if (lenInChars == 0) {
        return URI_TRUE;
    }

    // Detect and avoid integer overflow
    if (lenInChars > SIZE_MAX / sizeof(URI_CHAR)) {
        return URI_FALSE;
    }

    buffer = memory->malloc(memory, lenInChars * sizeof(URI_CHAR));
    if (buffer == NULL) {
        return URI_FALSE;
    }

    for (; i < lenInChars; i++) {
        if (((*first)[i] >= _UT('A')) && ((*first)[i] <= _UT('Z'))) {
            buffer[i] = (URI_CHAR)((*first)[i] + lowerUpperDiff);
        } else {
            buffer[i] = (*first)[i];
        }
    }

    *first = buffer;
    *afterLast = buffer + lenInChars;
    return URI_TRUE;
}

/* NOTE: Implementation must stay inplace-compatible */
static URI_INLINE void
URI_FUNC(FixPercentEncodingEngine)(const URI_CHAR * inFirst, const URI_CHAR * inAfterLast,
                                   const URI_CHAR * outFirst,
                                   const URI_CHAR ** outAfterLast) {
    URI_CHAR * write = (URI_CHAR *)outFirst;
    const size_t lenInChars = inAfterLast - inFirst;
    size_t i = 0;

    /* All but last two */
    for (; i + 2 < lenInChars; i++) {
        if (inFirst[i] != _UT('%')) {
            write[0] = inFirst[i];
            write++;
        } else {
            /* 6.2.2.2 Percent-Encoding Normalization: *
             * percent-encoded unreserved characters   */
            const URI_CHAR one = inFirst[i + 1];
            const URI_CHAR two = inFirst[i + 2];
            const unsigned char left = URI_FUNC(HexdigToInt)(one);
            const unsigned char right = URI_FUNC(HexdigToInt)(two);
            const int code = 16 * left + right;
            if (uriIsUnreserved(code)) {
                write[0] = (URI_CHAR)(code);
                write++;
            } else {
                /* 6.2.2.1 Case Normalization: *
                 * uppercase percent-encodings */
                write[0] = _UT('%');
                write[1] = URI_FUNC(HexToLetterEx)(left, URI_TRUE);
                write[2] = URI_FUNC(HexToLetterEx)(right, URI_TRUE);
                write += 3;
            }

            i += 2; /* For the two chars of the percent group we just ate */
        }
    }

    /* Last two */
    for (; i < lenInChars; i++) {
        write[0] = inFirst[i];
        write++;
    }

    *outAfterLast = write;
}

static URI_INLINE void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first,
                                                           const URI_CHAR ** afterLast) {
    /* Death checks */
    if ((first == NULL) || (afterLast == NULL) || (*afterLast == NULL)) {
        return;
    }

    /* Fix inplace */
    URI_FUNC(FixPercentEncodingEngine)(first, *afterLast, first, afterLast);
}

static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first,
                                                             const URI_CHAR ** afterLast,
                                                             UriMemoryManager * memory) {
    URI_CHAR * buffer;

    /* Death checks */
    if ((first == NULL) || (afterLast == NULL) || (*first == NULL)
        || (*afterLast == NULL)) {
        return URI_FALSE;
    }

    /* Old text length */
    const size_t lenInChars = *afterLast - *first;
    if (lenInChars == 0) {
        return URI_TRUE;
    }

    // Detect and avoid integer overflow
    if (lenInChars > SIZE_MAX / sizeof(URI_CHAR)) {
        return URI_FALSE;
    }

    /* New buffer */
    buffer = memory->malloc(memory, lenInChars * sizeof(URI_CHAR));
    if (buffer == NULL) {
        return URI_FALSE;
    }

    /* Fix on copy */
    URI_FUNC(FixPercentEncodingEngine)(*first, *afterLast, buffer, afterLast);
    *first = buffer;
    return URI_TRUE;
}

static URI_INLINE UriBool URI_FUNC(MakeRangeOwner)(unsigned int * revertMask,
                                                   unsigned int maskTest,
                                                   URI_TYPE(TextRange) * range,
                                                   UriMemoryManager * memory) {
    if (((*revertMask & maskTest) == 0) && (range->first != NULL)
        && (range->afterLast != NULL) && (range->afterLast > range->first)) {
        if (URI_FUNC(CopyRange)(range, range, memory) == URI_FALSE) {
            return URI_FALSE;
        }
        *revertMask |= maskTest;
    }
    return URI_TRUE;
}

static URI_INLINE UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri,
                                                    unsigned int * revertMask,
                                                    UriMemoryManager * memory) {
    URI_TYPE(PathSegment) * walker = uri->pathHead;
    if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_SCHEME, &(uri->scheme),
                                  memory)
        || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_USER_INFO,
                                     &(uri->userInfo), memory)
        || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_QUERY, &(uri->query),
                                     memory)
        || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_FRAGMENT, &(uri->fragment),
                                     memory)) {
        return URI_FALSE; /* Raises malloc error */
    }

    /* Host */
    if ((*revertMask & URI_NORMALIZE_HOST) == 0) {
        if (uri->hostData.ipFuture.first != NULL) {
            /* IPvFuture */
            if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_HOST,
                                          &(uri->hostData.ipFuture), memory)) {
                return URI_FALSE; /* Raises malloc error */
            }
            uri->hostText.first = uri->hostData.ipFuture.first;
            uri->hostText.afterLast = uri->hostData.ipFuture.afterLast;
        } else if (uri->hostText.first != NULL) {
            /* Regname */
            if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_HOST,
                                          &(uri->hostText), memory)) {
                return URI_FALSE; /* Raises malloc error */
            }
        }
    }

    /* Path */
    if ((*revertMask & URI_NORMALIZE_PATH) == 0) {
        while (walker != NULL) {
            if (!URI_FUNC(MakeRangeOwner)(revertMask, 0, &(walker->text), memory)) {
                /* Free allocations done so far and kill path */

                /* Kill path to one before walker (if any) */
                URI_TYPE(PathSegment) * ranger = uri->pathHead;
                while (ranger != walker) {
                    URI_TYPE(PathSegment) * const next = ranger->next;
                    if ((ranger->text.first != NULL) && (ranger->text.afterLast != NULL)
                        && (ranger->text.afterLast > ranger->text.first)) {
                        memory->free(memory, (URI_CHAR *)ranger->text.first);
                    }
                    memory->free(memory, ranger);
                    ranger = next;
                }

                /* Kill path from walker */
                while (walker != NULL) {
                    URI_TYPE(PathSegment) * const next = walker->next;
                    memory->free(memory, walker);
                    walker = next;
                }

                uri->pathHead = NULL;
                uri->pathTail = NULL;
                return URI_FALSE; /* Raises malloc error */
            }
            walker = walker->next;
        }
        *revertMask |= URI_NORMALIZE_PATH;
    }

    /* Port text, must come last so we don't have to undo that one if it fails. *
     * Otherwise we would need and extra enum flag for it although the port      *
     * cannot go unnormalized...                                                */
    if (!URI_FUNC(MakeRangeOwner)(revertMask, 0, &(uri->portText), memory)) {
        return URI_FALSE; /* Raises malloc error */
    }

    return URI_TRUE;
}

unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)(const URI_TYPE(Uri) * uri) {
    unsigned int outMask = URI_NORMALIZED; /* for NULL uri */
    URI_FUNC(NormalizeSyntaxMaskRequiredEx)(uri, &outMask);
    return outMask;
}

int URI_FUNC(NormalizeSyntaxMaskRequiredEx)(const URI_TYPE(Uri) * uri,
                                            unsigned int * outMask) {
    UriMemoryManager * const memory = NULL; /* no use of memory manager */

#  if defined(__GNUC__) \
      && ((__GNUC__ > 4) \
          || ((__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 2)))
    /* Slower code that fixes a warning, not sure if this is a smart idea */
    URI_TYPE(Uri) writeableClone;
#  endif

    if ((uri == NULL) || (outMask == NULL)) {
        return URI_ERROR_NULL;
    }

#  if defined(__GNUC__) \
      && ((__GNUC__ > 4) \
          || ((__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 2)))
    /* Slower code that fixes a warning, not sure if this is a smart idea */
    memcpy(&writeableClone, uri, 1 * sizeof(URI_TYPE(Uri)));
    URI_FUNC(NormalizeSyntaxEngine)(&writeableClone, 0, outMask, memory);
#  else
    URI_FUNC(NormalizeSyntaxEngine)((URI_TYPE(Uri) *)uri, 0, outMask, memory);
#  endif
    return URI_SUCCESS;
}

int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, unsigned int mask) {
    return URI_FUNC(NormalizeSyntaxExMm)(uri, mask, NULL);
}

int URI_FUNC(NormalizeSyntaxExMm)(URI_TYPE(Uri) * uri, unsigned int mask,
                                  UriMemoryManager * memory) {
    URI_CHECK_MEMORY_MANAGER(memory); /* may return */
    return URI_FUNC(NormalizeSyntaxEngine)(uri, mask, NULL, memory);
}

int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri) {
    return URI_FUNC(NormalizeSyntaxEx)(uri, (unsigned int)-1);
}

static const URI_CHAR * URI_FUNC(PastLeadingZeros)(const URI_CHAR * first,
                                                   const URI_CHAR * afterLast) {
    assert(first != NULL);
    assert(afterLast != NULL);
    assert(first != afterLast);

    /* Find the first non-zero character */
    const URI_CHAR * remainderFirst = first;
    while ((remainderFirst < afterLast) && (remainderFirst[0] == _UT('0'))) {
        remainderFirst++;
    }

    /* Is the string /all/ zeros? */
    if (remainderFirst == afterLast) {
        /* Yes, and length is >=1 because we ruled out the empty string earlier;
         * pull back onto rightmost zero */
        assert(remainderFirst > first);
        remainderFirst--;
        assert(remainderFirst[0] == _UT('0'));
    }

    return remainderFirst;
}

static void URI_FUNC(DropLeadingZerosInplace)(URI_CHAR * first,
                                              const URI_CHAR ** afterLast) {
    assert(first != NULL);
    assert(afterLast != NULL);
    assert(*afterLast != NULL);

    if (first == *afterLast) {
        return;
    }

    const URI_CHAR * const remainderFirst = URI_FUNC(PastLeadingZeros)(first, *afterLast);

    if (remainderFirst > first) {
        const size_t remainderLen = *afterLast - remainderFirst;
        memmove(first, remainderFirst, remainderLen * sizeof(URI_CHAR));
        first[remainderLen] = _UT('\0');
        *afterLast = first + remainderLen;
    }
}

static void URI_FUNC(AdvancePastLeadingZeros)(const URI_CHAR ** first,
                                              const URI_CHAR * afterLast) {
    assert(first != NULL);
    assert(*first != NULL);
    assert(afterLast != NULL);

    if (*first == afterLast) {
        return;
    }

    const URI_CHAR * const remainderFirst = URI_FUNC(PastLeadingZeros)(*first, afterLast);

    /* Cut off leading zeros */
    *first = remainderFirst;
}

static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri,
                                                      unsigned int inMask,
                                                      unsigned int * outMask,
                                                      UriMemoryManager * memory) {
    unsigned int revertMask = URI_NORMALIZED;

    /* Not just doing inspection? -> memory manager required! */
    if (outMask == NULL) {
        assert(memory != NULL);
    }

    if (uri == NULL) {
        if (outMask != NULL) {
            *outMask = URI_NORMALIZED;
            return URI_SUCCESS;
        } else {
            return URI_ERROR_NULL;
        }
    }

    if (outMask != NULL) {
        /* Reset mask */
        *outMask = URI_NORMALIZED;
    } else if (inMask == URI_NORMALIZED) {
        /* Nothing to do */
        return URI_SUCCESS;
    }

    /* Scheme, host */
    if (outMask != NULL) {
        const UriBool normalizeScheme =
            URI_FUNC(ContainsUppercaseLetters)(uri->scheme.first, uri->scheme.afterLast);
        const UriBool normalizeHostCase = URI_FUNC(ContainsUppercaseLetters)(
            uri->hostText.first, uri->hostText.afterLast);
        if (normalizeScheme) {
            *outMask |= URI_NORMALIZE_SCHEME;
        }

        if (normalizeHostCase) {
            *outMask |= URI_NORMALIZE_HOST;
        } else {
            const UriBool normalizeHostPrecent = URI_FUNC(ContainsUglyPercentEncoding)(
                uri->hostText.first, uri->hostText.afterLast);
            if (normalizeHostPrecent) {
                *outMask |= URI_NORMALIZE_HOST;
            }
        }
    } else {
        /* Scheme */
        if ((inMask & URI_NORMALIZE_SCHEME) && (uri->scheme.first != NULL)) {
            if (uri->owner) {
                URI_FUNC(LowercaseInplace)(uri->scheme.first, uri->scheme.afterLast);
            } else {
                if (!URI_FUNC(LowercaseMalloc)(&(uri->scheme.first),
                                               &(uri->scheme.afterLast), memory)) {
                    URI_FUNC(PreventLeakage)(uri, revertMask, memory);
                    return URI_ERROR_MALLOC;
                }
                revertMask |= URI_NORMALIZE_SCHEME;
            }
        }

        /* Host */
        if (inMask & URI_NORMALIZE_HOST) {
            if (uri->hostData.ipFuture.first != NULL) {
                /* IPvFuture */
                if (uri->owner) {
                    URI_FUNC(LowercaseInplace)(uri->hostData.ipFuture.first,
                                               uri->hostData.ipFuture.afterLast);
                } else {
                    if (!URI_FUNC(LowercaseMalloc)(&(uri->hostData.ipFuture.first),
                                                   &(uri->hostData.ipFuture.afterLast),
                                                   memory)) {
                        URI_FUNC(PreventLeakage)(uri, revertMask, memory);
                        return URI_ERROR_MALLOC;
                    }
                    revertMask |= URI_NORMALIZE_HOST;
                }
                uri->hostText.first = uri->hostData.ipFuture.first;
                uri->hostText.afterLast = uri->hostData.ipFuture.afterLast;
            } else if ((uri->hostText.first != NULL) && (uri->hostData.ip4 == NULL)) {
                /* Regname or IPv6 */
                if (uri->owner) {
                    URI_FUNC(FixPercentEncodingInplace)(uri->hostText.first,
                                                        &(uri->hostText.afterLast));
                } else {
                    if (!URI_FUNC(FixPercentEncodingMalloc)(
                            &(uri->hostText.first), &(uri->hostText.afterLast), memory)) {
                        URI_FUNC(PreventLeakage)(uri, revertMask, memory);
                        return URI_ERROR_MALLOC;
                    }
                    revertMask |= URI_NORMALIZE_HOST;
                }

                URI_FUNC(LowercaseInplaceExceptPercentEncoding)(uri->hostText.first,
                                                                uri->hostText.afterLast);
            }
        }
    }

    /* Port */
    if (outMask != NULL) {
        /* Is there a port even? */
        if (uri->portText.first != NULL) {
            /* Determine whether the port is already normalized, i.e. either "", "0" or no
             * leading zeros */
            const size_t portLen = uri->portText.afterLast - uri->portText.first;
            if ((portLen > 1) && (uri->portText.first[0] == _UT('0'))) {
                *outMask |= URI_NORMALIZE_PORT;
            }
        }
    } else {
        /* Normalize the port, i.e. drop leading zeros (except for string "0") */
        if ((inMask & URI_NORMALIZE_PORT) && (uri->portText.first != NULL)) {
            if (uri->owner) {
                URI_FUNC(DropLeadingZerosInplace)((URI_CHAR *)uri->portText.first,
                                                  &(uri->portText.afterLast));
            } else {
                URI_FUNC(AdvancePastLeadingZeros)(&(uri->portText.first),
                                                  uri->portText.afterLast);
            }
        }
    }

    /* User info */
    if (outMask != NULL) {
        const UriBool normalizeUserInfo = URI_FUNC(ContainsUglyPercentEncoding)(
            uri->userInfo.first, uri->userInfo.afterLast);
        if (normalizeUserInfo) {
            *outMask |= URI_NORMALIZE_USER_INFO;
        }
    } else {
        if ((inMask & URI_NORMALIZE_USER_INFO) && (uri->userInfo.first != NULL)) {
            if (uri->owner) {
                URI_FUNC(FixPercentEncodingInplace)(uri->userInfo.first,
                                                    &(uri->userInfo.afterLast));
            } else {
                if (!URI_FUNC(FixPercentEncodingMalloc)(
                        &(uri->userInfo.first), &(uri->userInfo.afterLast), memory)) {
                    URI_FUNC(PreventLeakage)(uri, revertMask, memory);
                    return URI_ERROR_MALLOC;
                }
                revertMask |= URI_NORMALIZE_USER_INFO;
            }
        }
    }

    /* Path */
    if (outMask != NULL) {
        const URI_TYPE(PathSegment) * walker = uri->pathHead;
        while (walker != NULL) {
            const URI_CHAR * const first = walker->text.first;
            const URI_CHAR * const afterLast = walker->text.afterLast;
            if ((first != NULL) && (afterLast != NULL) && (afterLast > first)
                && ((((afterLast - first) == 1) && (first[0] == _UT('.')))
                    || (((afterLast - first) == 2) && (first[0] == _UT('.'))
                        && (first[1] == _UT('.')))
                    || URI_FUNC(ContainsUglyPercentEncoding)(first, afterLast))) {
                *outMask |= URI_NORMALIZE_PATH;
                break;
            }
            walker = walker->next;
        }
    } else if (inMask & URI_NORMALIZE_PATH) {
        URI_TYPE(PathSegment) * walker;
        const UriBool relative =
            ((uri->scheme.first == NULL) && !uri->absolutePath) ? URI_TRUE : URI_FALSE;

        /* Fix percent-encoding for each segment */
        walker = uri->pathHead;
        if (uri->owner) {
            while (walker != NULL) {
                URI_FUNC(FixPercentEncodingInplace)(walker->text.first,
                                                    &(walker->text.afterLast));
                walker = walker->next;
            }
        } else {
            while (walker != NULL) {
                if (!URI_FUNC(FixPercentEncodingMalloc)(
                        &(walker->text.first), &(walker->text.afterLast), memory)) {
                    URI_FUNC(PreventLeakage)(uri, revertMask, memory);
                    return URI_ERROR_MALLOC;
                }
                walker = walker->next;
            }
            revertMask |= URI_NORMALIZE_PATH;
        }

        /* 6.2.2.3 Path Segment Normalization */
        if (!URI_FUNC(RemoveDotSegmentsEx)(
                uri, relative,
                (uri->owner == URI_TRUE) || ((revertMask & URI_NORMALIZE_PATH) != 0),
                memory)) {
            URI_FUNC(PreventLeakage)(uri, revertMask, memory);
            return URI_ERROR_MALLOC;
        }
        URI_FUNC(FixEmptyTrailSegment)(uri, memory);
    }

    /* Query, fragment */
    if (outMask != NULL) {
        const UriBool normalizeQuery =
            URI_FUNC(ContainsUglyPercentEncoding)(uri->query.first, uri->query.afterLast);
        const UriBool normalizeFragment = URI_FUNC(ContainsUglyPercentEncoding)(
            uri->fragment.first, uri->fragment.afterLast);
        if (normalizeQuery) {
            *outMask |= URI_NORMALIZE_QUERY;
        }

        if (normalizeFragment) {
            *outMask |= URI_NORMALIZE_FRAGMENT;
        }
    } else {
        /* Query */
        if ((inMask & URI_NORMALIZE_QUERY) && (uri->query.first != NULL)) {
            if (uri->owner) {
                URI_FUNC(FixPercentEncodingInplace)(uri->query.first,
                                                    &(uri->query.afterLast));
            } else {
                if (!URI_FUNC(FixPercentEncodingMalloc)(
                        &(uri->query.first), &(uri->query.afterLast), memory)) {
                    URI_FUNC(PreventLeakage)(uri, revertMask, memory);
                    return URI_ERROR_MALLOC;
                }
                revertMask |= URI_NORMALIZE_QUERY;
            }
        }

        /* Fragment */
        if ((inMask & URI_NORMALIZE_FRAGMENT) && (uri->fragment.first != NULL)) {
            if (uri->owner) {
                URI_FUNC(FixPercentEncodingInplace)(uri->fragment.first,
                                                    &(uri->fragment.afterLast));
            } else {
                if (!URI_FUNC(FixPercentEncodingMalloc)(
                        &(uri->fragment.first), &(uri->fragment.afterLast), memory)) {
                    URI_FUNC(PreventLeakage)(uri, revertMask, memory);
                    return URI_ERROR_MALLOC;
                }
                revertMask |= URI_NORMALIZE_FRAGMENT;
            }
        }
    }

    /* Dup all not duped yet */
    if ((outMask == NULL) && !uri->owner) {
        if (!URI_FUNC(MakeOwnerEngine)(uri, &revertMask, memory)) {
            URI_FUNC(PreventLeakage)(uri, revertMask, memory);
            return URI_ERROR_MALLOC;
        }
        uri->owner = URI_TRUE;
    }

    return URI_SUCCESS;
}

int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) {
    unsigned int revertMask = URI_NORMALIZED;

    URI_CHECK_MEMORY_MANAGER(memory); /* may return */

    if (uri == NULL) {
        return URI_ERROR_NULL;
    }

    if (uri->owner == URI_TRUE) {
        return URI_SUCCESS;
    }

    if (!URI_FUNC(MakeOwnerEngine)(uri, &revertMask, memory)) {
        URI_FUNC(PreventLeakage)(uri, revertMask, memory);
        return URI_ERROR_MALLOC;
    }

    uri->owner = URI_TRUE;

    return URI_SUCCESS;
}

int URI_FUNC(MakeOwner)(URI_TYPE(Uri) * uri) {
    return URI_FUNC(MakeOwnerMm)(uri, NULL);
}

#endif
