Changeset 58068 in vbox for trunk/src/VBox/Runtime/common/misc
- Timestamp:
- Oct 6, 2015 11:47:43 PM (9 years ago)
- svn:sync-xref-src-repo-rev:
- 103124
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/misc/uri.cpp
r58067 r58068 159 159 { 160 160 AssertReturn(pszString, VERR_INVALID_POINTER); 161 AssertPtrReturn(pszDst, VERR_INVALID_POINTER); 161 162 162 163 /* … … 259 260 size_t cchDecoded = pchDst - pszDecoded; 260 261 Assert(cchDecoded <= cchString); 261 // if (cchString - cchDecoded > 64) - enable later!262 if (cchString - cchDecoded > 64) 262 263 RTStrRealloc(&pszDecoded, cchDecoded + 1); 263 264 } 264 265 return pszDecoded; 265 266 } 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 */ 276 static 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 */ 312 static 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 266 375 267 376 … … 838 947 839 948 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 949 RTDECL(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 */ 863 1013 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); 865 1037 if (RT_SUCCESS(rc)) 866 1038 { 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 879 1065 /* 880 1066 * 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"). 881 1071 */ 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); 917 1112 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'; 919 1122 } 920 1123 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))924 1124 { 925 1125 /* 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. 927 1128 */ 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; 940 1143 } 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 1155 RTDECL(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; 944 1170 return NULL; 945 1171 } 946 1172 1173
Note:
See TracChangeset
for help on using the changeset viewer.