VirtualBox

Ignore:
Timestamp:
Oct 6, 2015 11:47:43 PM (9 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
103124
Message:

IPRT: Added RTUriFilePathEx, removed RTUriFileNPath as we ignored the cchMax param. Fixed special handling of 'file://localhost/C:/Windows/memory.dmp' style URLs as well as teating '|' as an alternative to the drive letter ':' character. Also extended testcases with windows legacy encodings and taught RTUriFilePathEx to handle them. (RTUriFilePath is calling RTUriFilePathEx of course.)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/misc/uri.cpp

    r58067 r58068  
    159159{
    160160    AssertReturn(pszString, VERR_INVALID_POINTER);
     161    AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
    161162
    162163    /*
     
    259260        size_t cchDecoded = pchDst - pszDecoded;
    260261        Assert(cchDecoded <= cchString);
    261         // if (cchString - cchDecoded > 64)  -  enable later!
     262        if (cchString - cchDecoded > 64)
    262263            RTStrRealloc(&pszDecoded, cchDecoded + 1);
    263264    }
    264265    return pszDecoded;
    265266}
     267
     268
     269/**
     270 * Calculates the decoded string length.
     271 *
     272 * @returns Number of chars (excluding the terminator).
     273 * @param   pszString       The string to decode.
     274 * @param   cchMax          The maximum string length (e.g. RTSTR_MAX).
     275 */
     276static size_t rtUriCalcDecodedLength(const char *pszString, size_t cchMax)
     277{
     278    size_t cchDecoded;
     279    if (pszString)
     280    {
     281        size_t cchSrcLeft = cchDecoded = RTStrNLen(pszString, cchMax);
     282        while (cchSrcLeft-- > 0)
     283        {
     284            char const ch = *pszString++;
     285            if (ch != '%')
     286            { /* typical */}
     287            else if (   cchSrcLeft >= 2
     288                     && RT_C_IS_XDIGIT(pszString[0])
     289                     && RT_C_IS_XDIGIT(pszString[1]))
     290            {
     291                cchDecoded -= 2;
     292                pszString  += 2;
     293                cchSrcLeft -= 2;
     294            }
     295        }
     296    }
     297    else
     298        cchDecoded = 0;
     299    return cchDecoded;
     300}
     301
     302
     303/**
     304 * Decodes a string into a buffer.
     305 *
     306 * @returns IPRT status code.
     307 * @param   pchSrc      The source string.
     308 * @param   cchSrc      The max number of bytes to decode in the source string.
     309 * @param   pszDst      The destination buffer.
     310 * @param   cbDst       The size of the buffer (including terminator).
     311 */
     312static int rtUriDecodeIntoBuffer(const char *pchSrc, size_t cchSrc, char *pszDst, size_t cbDst)
     313{
     314    AssertPtrReturn(pchSrc, VERR_INVALID_POINTER);
     315    AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
     316
     317    /*
     318     * Knowing that the pszString itself is valid UTF-8, we only have to
     319     * validate the escape sequences.
     320     */
     321    cchSrc = RTStrNLen(pchSrc, cchSrc);
     322    while (cchSrc > 0)
     323    {
     324        const char *pchPct = (const char *)memchr(pchSrc, '%', cchSrc);
     325        if (pchPct)
     326        {
     327            size_t cchBefore = pchPct - pchSrc;
     328            AssertReturn(cchBefore + 1 < cbDst, VERR_BUFFER_OVERFLOW);
     329            if (cchBefore)
     330            {
     331                memcpy(pszDst, pchSrc, cchBefore);
     332                pszDst += cchBefore;
     333                cbDst  -= cchBefore;
     334                pchSrc += cchBefore;
     335                cchSrc -= cchBefore;
     336            }
     337
     338            char chHigh, chLow;
     339            if (   cchSrc >= 3
     340                && RT_C_IS_XDIGIT(chHigh = pchSrc[1])
     341                && RT_C_IS_XDIGIT(chLow  = pchSrc[2]))
     342            {
     343                uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10;
     344                b <<= 4;
     345                b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10;
     346                *pszDst++ = (char)b;
     347                pchSrc += 3;
     348                cchSrc -= 3;
     349            }
     350            else
     351            {
     352                AssertFailed();
     353                *pszDst++ = *pchSrc++;
     354                cchSrc--;
     355            }
     356            cbDst -= 1;
     357        }
     358        else
     359        {
     360            AssertReturn(cchSrc < cbDst, VERR_BUFFER_OVERFLOW);
     361            memcpy(pszDst, pchSrc, cchSrc);
     362            pszDst += cchSrc;
     363            cbDst  -= cchSrc;
     364            pchSrc += cchSrc;
     365            cchSrc  = 0;
     366            break;
     367        }
     368    }
     369
     370    AssertReturn(cbDst > 0, VERR_BUFFER_OVERFLOW);
     371    *pszDst = '\0';
     372    return VINF_SUCCESS;
     373}
     374
    266375
    267376
     
    838947
    839948
    840 RTDECL(char *) RTUriFilePath(const char *pszUri, uint32_t uFormat)
    841 {
    842     return RTUriFileNPath(pszUri, uFormat, RTSTR_MAX);
    843 }
    844 
    845 
    846 RTDECL(char *) RTUriFileNPath(const char *pszUri, uint32_t uFormat, size_t cchMax)
    847 {
    848     AssertPtrReturn(pszUri, NULL);
    849     AssertReturn(uFormat == URI_FILE_FORMAT_AUTO || uFormat == URI_FILE_FORMAT_UNIX || uFormat == URI_FILE_FORMAT_WIN, NULL);
    850 
    851     /* Auto is based on the current OS. */
    852     if (uFormat == URI_FILE_FORMAT_AUTO)
    853 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    854         uFormat = URI_FILE_FORMAT_WIN;
    855 #else
    856         uFormat = URI_FILE_FORMAT_UNIX;
    857 #endif
    858 
    859     /* Check that this is a file URI. */
    860     if (RTStrNICmp(pszUri, RT_STR_TUPLE("file:")) != 0)
    861         return NULL;
    862 
     949RTDECL(int) RTUriFilePathEx(const char *pszUri, uint32_t fPathStyle, char **ppszPath, size_t cbPath, size_t *pcchPath)
     950{
     951    /*
     952     * Validate and adjust input.
     953     */
     954    if (pcchPath)
     955    {
     956        AssertPtrReturn(pcchPath, VERR_INVALID_POINTER);
     957        *pcchPath = ~(size_t)0;
     958    }
     959    AssertPtrReturn(ppszPath, VERR_INVALID_POINTER);
     960    AssertReturn(!(fPathStyle & ~RTPATH_STR_F_STYLE_MASK) && fPathStyle != RTPATH_STR_F_STYLE_RESERVED, VERR_INVALID_FLAGS);
     961    if (fPathStyle == RTPATH_STR_F_STYLE_HOST)
     962        fPathStyle = RTPATH_STYLE;
     963    AssertPtrReturn(pszUri, VERR_INVALID_POINTER);
     964
     965    /*
     966     * Check that this is a file URI.
     967     */
     968    if (RTStrNICmp(pszUri, RT_STR_TUPLE("file:")) == 0)
     969    { /* likely */ }
     970    else
     971        return VERR_URI_NOT_FILE_SCHEME;
     972
     973    /*
     974     * We may have a number of variations here, mostly thanks to
     975     * various windows software.  First the canonical variations:
     976     *     - file:///C:/Windows/System32/kernel32.dll
     977     *     - file:///C|/Windows/System32/kernel32.dll
     978     *     - file:///C:%5CWindows%5CSystem32%5Ckernel32.dll
     979     *     - file://localhost/C:%5CWindows%5CSystem32%5Ckernel32.dll
     980     *     - file://cifsserver.dev/systemshare%5CWindows%5CSystem32%5Ckernel32.dll
     981     *     - file://cifsserver.dev:139/systemshare%5CWindows%5CSystem32%5Ckernel32.dll  (not quite sure here, but whatever)
     982     *
     983     * Legacy variant without any slashes after the schema:
     984     *     - file:C:/Windows/System32/kernel32.dll
     985     *     - file:C|/Windows/System32%5Ckernel32.dll
     986     *     - file:~/.bashrc
     987     *            \--path-/
     988     *
     989     * Legacy variant with exactly one slashes after the schema:
     990     *     - file:/C:/Windows/System32%5Ckernel32.dll
     991     *     - file:/C|/Windows/System32/kernel32.dll
     992     *     - file:/usr/bin/env
     993     *            \---path---/
     994     *
     995     * Legacy variant with two slashes after the schema and an unescaped DOS path:
     996     *     - file://C:/Windows/System32\kernel32.dll (**)
     997     *     - file://C|/Windows/System32\kernel32.dll
     998     *                \---path---------------------/
     999     *              -- authority, with ':' as non-working port separator
     1000     *
     1001     * Legacy variant with exactly four slashes after the schema and an unescaped DOS path.
     1002     *     - file:////C:/Windows\System32\user32.dll
     1003     *
     1004     * Legacy variant with four or more slashes after the schema and an unescaped UNC path:
     1005     *     - file:////cifsserver.dev/systemshare/System32%\kernel32.dll
     1006     *     - file://///cifsserver.dev/systemshare/System32\kernel32.dll
     1007     *              \---path--------------------------------------------/
     1008     *
     1009     * The the two unescaped variants shouldn't be handed to rtUriParse, which
     1010     * is good as we cannot actually handle the one marked by (**).  So, handle
     1011     * those two special when parsing.
     1012     */
    8631013    RTURIPARSED Parsed;
    864     int rc = rtUriParse(pszUri, &Parsed);
     1014    int         rc;
     1015    size_t      cSlashes = 0;
     1016    while (pszUri[5 + cSlashes] == '/')
     1017        cSlashes++;
     1018    if (   (cSlashes == 2 || cSlashes == 4)
     1019        && RT_C_IS_ALPHA(pszUri[5 + cSlashes])
     1020        && (pszUri[5 + cSlashes + 1] == ':' || pszUri[5 + cSlashes + 1] == '|'))
     1021    {
     1022        RT_ZERO(Parsed); /* RTURIPARSED_F_CONTAINS_ESCAPED_CHARS is now clear. */
     1023        Parsed.offPath = 5 + cSlashes;
     1024        Parsed.cchPath = strlen(&pszUri[Parsed.offPath]);
     1025        rc = RTStrValidateEncoding(&pszUri[Parsed.offPath]);
     1026    }
     1027    else if (cSlashes >= 4)
     1028    {
     1029        RT_ZERO(Parsed);
     1030        Parsed.fFlags  = cSlashes > 4 ? RTURIPARSED_F_CONTAINS_ESCAPED_CHARS : 0;
     1031        Parsed.offPath = 5 + cSlashes - 2;
     1032        Parsed.cchPath = strlen(&pszUri[Parsed.offPath]);
     1033        rc = RTStrValidateEncoding(&pszUri[Parsed.offPath]);
     1034    }
     1035    else
     1036        rc = rtUriParse(pszUri, &Parsed);
    8651037    if (RT_SUCCESS(rc))
    8661038    {
    867         /* No path detected? Take authority as path then. */
    868         if (!Parsed.cchPath)
    869         {
    870             Parsed.cchPath      = Parsed.cchAuthority;
    871             Parsed.offPath      = Parsed.offAuthority;
    872             Parsed.cchAuthority = 0;
    873         }
    874     }
    875 
    876     if (   RT_SUCCESS(rc)
    877         && Parsed.cchPath)
    878     {
     1039        /*
     1040         * Ignore localhost as hostname (it's implicit).
     1041         */
     1042        static char const s_szLocalhost[] = "localhost";
     1043        if (    Parsed.cchAuthorityHost == sizeof(s_szLocalhost) - 1U
     1044            &&  RTStrNICmp(&pszUri[Parsed.offAuthorityHost], RT_STR_TUPLE(s_szLocalhost)) == 0)
     1045        {
     1046            Parsed.cchAuthorityHost = 0;
     1047            Parsed.cchAuthority     = 0;
     1048        }
     1049
     1050        /*
     1051         * Ignore leading path slash/separator if we detect a DOS drive letter
     1052         * and we don't have a host name.
     1053         */
     1054        if (   Parsed.cchPath >= 3
     1055            && Parsed.cchAuthorityHost    == 0
     1056            && pszUri[Parsed.offPath]     == '/'           /* Leading path slash/separator. */
     1057            && (   pszUri[Parsed.offPath + 2] == ':'       /* Colon after drive letter. */
     1058                || pszUri[Parsed.offPath + 2] == '|')      /* Colon alternative. */
     1059            && RT_C_IS_ALPHA(pszUri[Parsed.offPath + 1]) ) /* Drive letter. */
     1060        {
     1061            Parsed.offPath++;
     1062            Parsed.cchPath--;
     1063        }
     1064
    8791065        /*
    8801066         * Calculate the size of the encoded result.
     1067         *
     1068         * Since we're happily returning "C:/Windows/System32/kernel.dll"
     1069         * style paths when the caller requested UNIX style paths, we will
     1070         * return straight UNC paths too ("//cifsserver/share/dir/file").
    8811071         */
    882         size_t cbResult = 0;
    883 
    884         /* Skip the leading slash if a DOS drive letter (e.g. "C:") is detected right after it. */
    885         if (   Parsed.cchPath >= 3
    886             && pszUri[Parsed.offPath]  == '/'        /* Leading slash. */
    887             && RT_C_IS_ALPHA(pszUri[Parsed.offPath + 1]) /* Drive letter. */
    888             && pszUri[Parsed.offPath + 2]  == ':')
    889         {
    890             Parsed.offPath++;
    891             Parsed.cchPath--;
    892         }
    893 
    894         /* Windows: Authority given? Include authority as part of UNC path */
    895         if (uFormat == URI_FILE_FORMAT_WIN && Parsed.cchAuthority)
    896         {
    897             cbResult += 2; /* UNC slashes "\\". */
    898             cbResult += Parsed.cchAuthority;
    899         }
    900 
    901         cbResult += Parsed.cchPath;
    902         cbResult += 1; /* Zero termination. */
    903 
    904         /*
    905          * Compose encoded string.
    906          */
    907         char *pszResult;
    908         char *pszTmp = pszResult = RTStrAlloc(cbResult);
    909         if (pszTmp)
    910         {
    911             size_t cbTmp = cbResult;
    912 
    913             /* Windows: If an authority is given, add the required UNC prefix. */
    914             if (uFormat == URI_FILE_FORMAT_WIN && Parsed.cchAuthority)
    915             {
    916                 rc = RTStrCatP(&pszTmp, &cbTmp, "\\\\");
     1072        size_t cchDecodedHost = 0;
     1073        size_t cbResult;
     1074        if (Parsed.fFlags & RTURIPARSED_F_CONTAINS_ESCAPED_CHARS)
     1075        {
     1076            cchDecodedHost = rtUriCalcDecodedLength(&pszUri[Parsed.offAuthorityHost], Parsed.cchAuthorityHost);
     1077            cbResult = cchDecodedHost + rtUriCalcDecodedLength(&pszUri[Parsed.offPath], Parsed.cchPath) + 1;
     1078        }
     1079        else
     1080        {
     1081            cchDecodedHost = 0;
     1082            cbResult = Parsed.cchAuthorityHost + Parsed.cchPath + 1;
     1083        }
     1084        if (pcchPath)
     1085            *pcchPath = cbResult - 1;
     1086        if (cbResult > 1)
     1087        {
     1088            /*
     1089             * Prepare the necessary buffer space for the result.
     1090             */
     1091            char  *pszDst;
     1092            char  *pszFreeMe = NULL;
     1093            if (!cbPath || *ppszPath == NULL)
     1094            {
     1095                cbPath = RT_MAX(cbPath, cbResult);
     1096                *ppszPath = pszFreeMe = pszDst = RTStrAlloc(cbPath);
     1097                AssertReturn(pszDst, VERR_NO_STR_MEMORY);
     1098            }
     1099            else if (cbResult <= cbPath)
     1100                pszDst = *ppszPath;
     1101            else
     1102                return VERR_BUFFER_OVERFLOW;
     1103
     1104            /*
     1105             * Compose the result.
     1106             */
     1107            if (Parsed.fFlags & RTURIPARSED_F_CONTAINS_ESCAPED_CHARS)
     1108            {
     1109                rc = rtUriDecodeIntoBuffer(&pszUri[Parsed.offAuthorityHost],Parsed.cchAuthorityHost,
     1110                                           pszDst, cchDecodedHost + 1);
     1111                Assert(RT_SUCCESS(rc) && strlen(pszDst) == cchDecodedHost);
    9171112                if (RT_SUCCESS(rc))
    918                     rc = RTStrCatPEx(&pszTmp, &cbTmp, &pszUri[Parsed.offAuthority], Parsed.cchAuthority);
     1113                    rc = rtUriDecodeIntoBuffer(&pszUri[Parsed.offPath], Parsed.cchPath,
     1114                                               &pszDst[cchDecodedHost], cbResult - cchDecodedHost);
     1115                Assert(RT_SUCCESS(rc) && strlen(pszDst) == cbResult - 1);
     1116            }
     1117            else
     1118            {
     1119                memcpy(pszDst, &pszUri[Parsed.offAuthorityHost], Parsed.cchAuthorityHost);
     1120                memcpy(&pszDst[Parsed.cchAuthorityHost], &pszUri[Parsed.offPath], Parsed.cchPath);
     1121                pszDst[cbResult - 1] = '\0';
    9191122            }
    9201123            if (RT_SUCCESS(rc))
    921                 rc = RTStrCatPEx(&pszTmp, &cbTmp, &pszUri[Parsed.offPath], Parsed.cchPath);
    922             AssertRC(rc); /* Shall not happen! */
    923             if (RT_SUCCESS(rc))
    9241124            {
    9251125                /*
    926                  * Decode the string and switch the slashes around the request way before returning.
     1126                 * Convert colon DOS driver letter colon alternative.
     1127                 * We do this regardless of the desired path style.
    9271128                 */
    928                 char *pszPath = rtUriPercentDecodeN(pszResult, cbResult - 1 /* Minus termination */);
    929                 if (pszPath)
    930                 {
    931                     RTStrFree(pszResult);
    932 
    933                     if (uFormat == URI_FILE_FORMAT_UNIX)
    934                         return RTPathChangeToUnixSlashes(pszPath, true);
    935                     Assert(uFormat == URI_FILE_FORMAT_WIN);
    936                     return RTPathChangeToDosSlashes(pszPath, true);
    937                 }
    938 
    939                 /* Failed. */
     1129                if (   RT_C_IS_ALPHA(pszDst[0])
     1130                    && pszDst[1] == '|')
     1131                    pszDst[1] = ':';
     1132
     1133                /*
     1134                 * Fix slashes.
     1135                 */
     1136                if (fPathStyle == RTPATH_STR_F_STYLE_DOS)
     1137                    RTPathChangeToDosSlashes(pszDst, true);
     1138                else if (fPathStyle == RTPATH_STR_F_STYLE_UNIX)
     1139                    RTPathChangeToUnixSlashes(pszDst, true); /** @todo not quite sure how this actually makes sense... */
     1140                else
     1141                    AssertFailed();
     1142                return rc;
    9401143            }
    941             RTStrFree(pszResult);
    942         }
    943     }
     1144
     1145            /* bail out */
     1146            RTStrFree(pszFreeMe);
     1147        }
     1148        else
     1149            rc = VERR_PATH_ZERO_LENGTH;
     1150    }
     1151    return rc;
     1152}
     1153
     1154
     1155RTDECL(char *) RTUriFilePath(const char *pszUri, uint32_t uFormat)
     1156{
     1157    uint32_t fPathStyle;
     1158    switch (uFormat)
     1159    {
     1160        case URI_FILE_FORMAT_WIN:  fPathStyle = RTPATH_STR_F_STYLE_DOS; break;
     1161        case URI_FILE_FORMAT_UNIX: fPathStyle = RTPATH_STR_F_STYLE_UNIX; break;
     1162        case URI_FILE_FORMAT_AUTO: fPathStyle = RTPATH_STR_F_STYLE_HOST; break;
     1163        default: AssertFailedReturn(NULL);
     1164    }
     1165
     1166    char *pszPath = NULL;
     1167    int rc = RTUriFilePathEx(pszUri, fPathStyle, &pszPath, 0 /*cbPath*/, NULL /*pcchPath*/);
     1168    if (RT_SUCCESS(rc))
     1169        return pszPath;
    9441170    return NULL;
    9451171}
    9461172
     1173
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette