// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. #include "precomp.h" // Exit macros #define UriExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) #define UriExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) #define UriExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) #define UriExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) #define UriExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) #define UriExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) #define UriExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) #define UriExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) #define UriExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) #define UriExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) #define UriExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_URIUTIL, e, x, s, __VA_ARGS__) #define UriExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_URIUTIL, g, x, s, __VA_ARGS__) // // UriCanonicalize - canonicalizes a URI. // extern "C" HRESULT DAPI UriCanonicalize( __inout_z LPWSTR* psczUri ) { HRESULT hr = S_OK; WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; DWORD cch = countof(wz); if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE)) { UriExitWithLastError(hr, "Failed to canonicalize URI."); } hr = StrAllocString(psczUri, wz, cch); UriExitOnFailure(hr, "Failed copy canonicalized URI."); LExit: return hr; } // // UriCrack - cracks a URI into constituent parts. // extern "C" HRESULT DAPI UriCrack( __in_z LPCWSTR wzUri, __out_opt INTERNET_SCHEME* pScheme, __deref_opt_out_z LPWSTR* psczHostName, __out_opt INTERNET_PORT* pPort, __deref_opt_out_z LPWSTR* psczUser, __deref_opt_out_z LPWSTR* psczPassword, __deref_opt_out_z LPWSTR* psczPath, __deref_opt_out_z LPWSTR* psczQueryString ) { HRESULT hr = S_OK; URL_COMPONENTSW components = { }; WCHAR wzHostName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; WCHAR wzUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; WCHAR wzPassword[INTERNET_MAX_PASSWORD_LENGTH + 1]; WCHAR wzPath[INTERNET_MAX_PATH_LENGTH + 1]; WCHAR wzQueryString[INTERNET_MAX_PATH_LENGTH + 1]; components.dwStructSize = sizeof(URL_COMPONENTSW); if (psczHostName) { components.lpszHostName = wzHostName; components.dwHostNameLength = countof(wzHostName); } if (psczUser) { components.lpszUserName = wzUserName; components.dwUserNameLength = countof(wzUserName); } if (psczPassword) { components.lpszPassword = wzPassword; components.dwPasswordLength = countof(wzPassword); } if (psczPath) { components.lpszUrlPath = wzPath; components.dwUrlPathLength = countof(wzPath); } if (psczQueryString) { components.lpszExtraInfo = wzQueryString; components.dwExtraInfoLength = countof(wzQueryString); } if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components)) { UriExitWithLastError(hr, "Failed to crack URI."); } if (pScheme) { *pScheme = components.nScheme; } if (psczHostName) { hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength); UriExitOnFailure(hr, "Failed to copy host name."); } if (pPort) { *pPort = components.nPort; } if (psczUser) { hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength); UriExitOnFailure(hr, "Failed to copy user name."); } if (psczPassword) { hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength); UriExitOnFailure(hr, "Failed to copy password."); } if (psczPath) { hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength); UriExitOnFailure(hr, "Failed to copy path."); } if (psczQueryString) { hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength); UriExitOnFailure(hr, "Failed to copy query string."); } LExit: return hr; } // // UriCrackEx - cracks a URI into URI_INFO. // extern "C" HRESULT DAPI UriCrackEx( __in_z LPCWSTR wzUri, __in URI_INFO* pUriInfo ) { HRESULT hr = S_OK; hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString); UriExitOnFailure(hr, "Failed to crack URI."); LExit: return hr; } // // UriInfoUninitialize - frees the memory in a URI_INFO struct. // extern "C" void DAPI UriInfoUninitialize( __in URI_INFO* pUriInfo ) { ReleaseStr(pUriInfo->sczHostName); ReleaseStr(pUriInfo->sczUser); ReleaseStr(pUriInfo->sczPassword); ReleaseStr(pUriInfo->sczPath); ReleaseStr(pUriInfo->sczQueryString); memset(pUriInfo, 0, sizeof(URI_INFO)); } // // UriCreate - creates a URI from constituent parts. // extern "C" HRESULT DAPI UriCreate( __inout_z LPWSTR* psczUri, __in INTERNET_SCHEME scheme, __in_z_opt LPWSTR wzHostName, __in INTERNET_PORT port, __in_z_opt LPWSTR wzUser, __in_z_opt LPWSTR wzPassword, __in_z_opt LPWSTR wzPath, __in_z_opt LPWSTR wzQueryString ) { HRESULT hr = S_OK; WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; DWORD cch = countof(wz); URL_COMPONENTSW components = { }; components.dwStructSize = sizeof(URL_COMPONENTSW); components.nScheme = scheme; components.lpszHostName = wzHostName; components.nPort = port; components.lpszUserName = wzUser; components.lpszPassword = wzPassword; components.lpszUrlPath = wzPath; components.lpszExtraInfo = wzQueryString; if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch)) { UriExitWithLastError(hr, "Failed to create URI."); } hr = StrAllocString(psczUri, wz, cch); UriExitOnFailure(hr, "Failed copy created URI."); LExit: return hr; } // // UriGetServerAndResource - gets the server and resource as independent strings from a URI. // // NOTE: This function is useful for the InternetConnect/HttpRequest APIs. // extern "C" HRESULT DAPI UriGetServerAndResource( __in_z LPCWSTR wzUri, __out_z LPWSTR* psczServer, __out_z LPWSTR* psczResource ) { HRESULT hr = S_OK; INTERNET_SCHEME scheme = INTERNET_SCHEME_UNKNOWN; LPWSTR sczHostName = NULL; INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER; LPWSTR sczUser = NULL; LPWSTR sczPassword = NULL; LPWSTR sczPath = NULL; LPWSTR sczQueryString = NULL; hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString); UriExitOnFailure(hr, "Failed to crack URI."); hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL); UriExitOnFailure(hr, "Failed to allocate server URI."); hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString); UriExitOnFailure(hr, "Failed to allocate resource URI."); LExit: ReleaseStr(sczQueryString); ReleaseStr(sczPath); ReleaseStr(sczPassword); ReleaseStr(sczUser); ReleaseStr(sczHostName); return hr; } // // UriFile - returns the file part of the URI. // extern "C" HRESULT DAPI UriFile( __deref_out_z LPWSTR* psczFile, __in_z LPCWSTR wzUri ) { HRESULT hr = S_OK; WCHAR wz[MAX_PATH + 1]; DWORD cch = countof(wz); URL_COMPONENTSW uc = { }; uc.dwStructSize = sizeof(uc); uc.lpszUrlPath = wz; uc.dwUrlPathLength = cch; if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc)) { UriExitWithLastError(hr, "Failed to crack URI."); } // Copy only the file name. Fortunately, PathFile() understands that // forward slashes can be directory separators like backslashes. hr = StrAllocString(psczFile, PathFile(wz), 0); UriExitOnFailure(hr, "Failed to copy file name"); LExit: return hr; } /******************************************************************* UriProtocol - determines the protocol of a URI. ********************************************************************/ extern "C" HRESULT DAPI UriProtocol( __in_z LPCWSTR wzUri, __out URI_PROTOCOL* pProtocol ) { Assert(wzUri && *wzUri); Assert(pProtocol); HRESULT hr = S_OK; if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && (L't' == wzUri[1] || L'T' == wzUri[1]) && (L't' == wzUri[2] || L'T' == wzUri[2]) && (L'p' == wzUri[3] || L'P' == wzUri[3]) && (L's' == wzUri[4] || L'S' == wzUri[4]) && L':' == wzUri[5] && L'/' == wzUri[6] && L'/' == wzUri[7]) { *pProtocol = URI_PROTOCOL_HTTPS; } else if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && (L't' == wzUri[1] || L'T' == wzUri[1]) && (L't' == wzUri[2] || L'T' == wzUri[2]) && (L'p' == wzUri[3] || L'P' == wzUri[3]) && L':' == wzUri[4] && L'/' == wzUri[5] && L'/' == wzUri[6]) { *pProtocol = URI_PROTOCOL_HTTP; } else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && (L't' == wzUri[1] || L'T' == wzUri[1]) && (L'p' == wzUri[2] || L'P' == wzUri[2]) && L':' == wzUri[3] && L'/' == wzUri[4] && L'/' == wzUri[5]) { *pProtocol = URI_PROTOCOL_FTP; } else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && (L'i' == wzUri[1] || L'I' == wzUri[1]) && (L'l' == wzUri[2] || L'L' == wzUri[2]) && (L'e' == wzUri[3] || L'E' == wzUri[3]) && L':' == wzUri[4] && L'/' == wzUri[5] && L'/' == wzUri[6]) { *pProtocol = URI_PROTOCOL_FILE; } else { *pProtocol = URI_PROTOCOL_UNKNOWN; } return hr; } /******************************************************************* UriRoot - returns the root of the path specified in the URI. examples: file:///C:\path\path -> C:\ file://server/share/path/path -> \\server\share http://www.example.com/path/path -> http://www.example.com/ ftp://ftp.example.com/path/path -> ftp://www.example.com/ NOTE: This function should only be used on cannonicalized URIs. It does not cannonicalize itself. ********************************************************************/ extern "C" HRESULT DAPI UriRoot( __in_z LPCWSTR wzUri, __out LPWSTR* ppwzRoot, __out_opt URI_PROTOCOL* pProtocol ) { Assert(wzUri && *wzUri); Assert(ppwzRoot); HRESULT hr = S_OK; URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; LPCWSTR pwcSlash = NULL; hr = UriProtocol(wzUri, &protocol); UriExitOnFailure(hr, "Invalid URI."); switch (protocol) { case URI_PROTOCOL_FILE: if (L'/' == wzUri[7]) // file path { if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9]) { hr = StrAlloc(ppwzRoot, 4); UriExitOnFailure(hr, "Failed to allocate string for root of URI."); *ppwzRoot[0] = wzUri[8]; *ppwzRoot[1] = L':'; *ppwzRoot[2] = L'\\'; *ppwzRoot[3] = L'\0'; } else { hr = E_INVALIDARG; UriExitOnFailure(hr, "Invalid file path in URI."); } } else // UNC share { pwcSlash = wcschr(wzUri + 8, L'/'); if (!pwcSlash) { hr = E_INVALIDARG; UriExitOnFailure(hr, "Invalid server name in URI."); } else { hr = StrAllocString(ppwzRoot, L"\\\\", 64); UriExitOnFailure(hr, "Failed to allocate string for root of URI."); pwcSlash = wcschr(pwcSlash + 1, L'/'); if (pwcSlash) { hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8); UriExitOnFailure(hr, "Failed to add server/share to root of URI."); } else { hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0); UriExitOnFailure(hr, "Failed to add server/share to root of URI."); } // replace all slashes with backslashes to be truly UNC. for (LPWSTR pwc = *ppwzRoot; pwc && *pwc; ++pwc) { if (L'/' == *pwc) { *pwc = L'\\'; } } } } break; case URI_PROTOCOL_FTP: pwcSlash = wcschr(wzUri + 6, L'/'); if (pwcSlash) { hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); UriExitOnFailure(hr, "Failed allocate root from URI."); } else { hr = StrAllocString(ppwzRoot, wzUri, 0); UriExitOnFailure(hr, "Failed allocate root from URI."); } break; case URI_PROTOCOL_HTTP: pwcSlash = wcschr(wzUri + 7, L'/'); if (pwcSlash) { hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); UriExitOnFailure(hr, "Failed allocate root from URI."); } else { hr = StrAllocString(ppwzRoot, wzUri, 0); UriExitOnFailure(hr, "Failed allocate root from URI."); } break; default: hr = E_INVALIDARG; UriExitOnFailure(hr, "Unknown URI protocol."); } if (pProtocol) { *pProtocol = protocol; } LExit: return hr; } extern "C" HRESULT DAPI UriResolve( __in_z LPCWSTR wzUri, __in_opt LPCWSTR wzBaseUri, __out LPWSTR* ppwzResolvedUri, __out_opt URI_PROTOCOL* pResolvedProtocol ) { UNREFERENCED_PARAMETER(wzUri); UNREFERENCED_PARAMETER(wzBaseUri); UNREFERENCED_PARAMETER(ppwzResolvedUri); UNREFERENCED_PARAMETER(pResolvedProtocol); HRESULT hr = E_NOTIMPL; #if 0 URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; hr = UriProtocol(wzUri, &protocol); UriExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri); UriExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided"); if (URI_PROTOCOL_UNKNOWN == protocol) { UriExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL"); if (L'/' == *wzUri || L'\\' == *wzUri) { hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol); UriExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri); hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); UriExitOnFailure(hr, "Failed to concat file to base URI."); } else { hr = UriProtocol(wzBaseUri, &protocol); UriExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri); LPCWSTR pwcFile = const_cast (UriFile(wzBaseUri)); if (!pwcFile) { hr = E_INVALIDARG; UriExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri); } hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri); UriExitOnFailure(hr, "Failed to allocate string for resolved URI."); hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); UriExitOnFailure(hr, "Failed to concat file to resolved URI."); } } else { hr = StrAllocString(ppwzResolvedUri, wzUri, 0); UriExitOnFailure(hr, "Failed to copy resolved URI."); } if (pResolvedProtocol) { *pResolvedProtocol = protocol; } LExit: #endif return hr; }