@@ -1691,24 +1691,31 @@ XS(w32_HttpGetFile)
1691
1691
{
1692
1692
dXSARGS ;
1693
1693
WCHAR * url = NULL , * file = NULL , * hostName = NULL , * urlPath = NULL ;
1694
+ bool bIgnoreCertErrors = FALSE;
1695
+ WCHAR msgbuf [ONE_K_BUFSIZE ];
1694
1696
BOOL bResults = FALSE;
1695
1697
HINTERNET hSession = NULL ,
1696
1698
hConnect = NULL ,
1697
1699
hRequest = NULL ;
1698
1700
HANDLE hOut = NULL ;
1699
1701
BOOL bParsed = FALSE,
1700
1702
bAborted = FALSE,
1701
- bFileError = FALSE;
1703
+ bFileError = FALSE,
1704
+ bHttpError = FALSE;
1702
1705
DWORD error = 0 ;
1703
1706
URL_COMPONENTS urlComp ;
1704
1707
LPCWSTR acceptTypes [] = { L"*/*" , NULL };
1708
+ DWORD dwHttpStatusCode = 0 , dwQuerySize = 0 ;
1705
1709
1706
- if (items != 2 )
1707
- croak ("usage: Win32::HttpGetFile($url, $file)" );
1710
+ if (items < 2 || items > 3 )
1711
+ croak ("usage: Win32::HttpGetFile($url, $file[, $ignore_cert_errors] )" );
1708
1712
1709
1713
url = sv_to_wstr (aTHX_ ST (0 ));
1710
1714
file = sv_to_wstr (aTHX_ ST (1 ));
1711
1715
1716
+ if (items == 3 )
1717
+ bIgnoreCertErrors = (BOOL )SvIV (ST (2 ));
1718
+
1712
1719
/* Initialize the URL_COMPONENTS structure, setting the required
1713
1720
* component lengths to non-zero so that they get populated.
1714
1721
*/
@@ -1766,13 +1773,29 @@ XS(w32_HttpGetFile)
1766
1773
? WINHTTP_FLAG_SECURE
1767
1774
: 0 );
1768
1775
1776
+ /* If specified, disable certificate-related errors for https connections. */
1777
+ if (hRequest
1778
+ && bIgnoreCertErrors
1779
+ && urlComp .nScheme == INTERNET_SCHEME_HTTPS ) {
1780
+ DWORD secFlags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID
1781
+ | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
1782
+ | SECURITY_FLAG_IGNORE_UNKNOWN_CA
1783
+ | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE ;
1784
+ if (!WinHttpSetOption (hRequest ,
1785
+ WINHTTP_OPTION_SECURITY_FLAGS ,
1786
+ & secFlags ,
1787
+ sizeof (secFlags ))) {
1788
+ bAborted = TRUE;
1789
+ }
1790
+ }
1791
+
1769
1792
/* Call WinHttpGetProxyForUrl with our target URL. If auto-proxy succeeds,
1770
1793
* then set the proxy info on the request handle. If auto-proxy fails,
1771
1794
* ignore the error and attempt to send the HTTP request directly to the
1772
1795
* target server (using the default WINHTTP_ACCESS_TYPE_NO_PROXY
1773
1796
* configuration, which the request handle will inherit from the session).
1774
1797
*/
1775
- if (hRequest ) {
1798
+ if (hRequest && ! bAborted ) {
1776
1799
WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions ;
1777
1800
WINHTTP_PROXY_INFO ProxyInfo ;
1778
1801
DWORD cbProxyInfoSize = sizeof (ProxyInfo );
@@ -1815,6 +1838,39 @@ XS(w32_HttpGetFile)
1815
1838
if (bResults )
1816
1839
bResults = WinHttpReceiveResponse (hRequest , NULL );
1817
1840
1841
+ /* Retrieve HTTP status code. */
1842
+ if (bResults ) {
1843
+ dwQuerySize = sizeof (dwHttpStatusCode );
1844
+ bResults = WinHttpQueryHeaders (hRequest ,
1845
+ WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER ,
1846
+ WINHTTP_HEADER_NAME_BY_INDEX ,
1847
+ & dwHttpStatusCode ,
1848
+ & dwQuerySize ,
1849
+ WINHTTP_NO_HEADER_INDEX );
1850
+ }
1851
+
1852
+ /* Retrieve HTTP status text. Note this may be a success message. */
1853
+ if (bResults ) {
1854
+ dwQuerySize = ONE_K_BUFSIZE * 2 - 2 ;
1855
+ ZeroMemory (& msgbuf , ONE_K_BUFSIZE * 2 );
1856
+ bResults = WinHttpQueryHeaders (hRequest ,
1857
+ WINHTTP_QUERY_STATUS_TEXT ,
1858
+ WINHTTP_HEADER_NAME_BY_INDEX ,
1859
+ msgbuf ,
1860
+ & dwQuerySize ,
1861
+ WINHTTP_NO_HEADER_INDEX );
1862
+ }
1863
+
1864
+ /* There is no point in successfully downloading an error page from
1865
+ * the server, so consider HTTP errors to be failures.
1866
+ */
1867
+ if (bResults ) {
1868
+ if (dwHttpStatusCode < 200 || dwHttpStatusCode > 299 ) {
1869
+ bResults = FALSE;
1870
+ bHttpError = TRUE;
1871
+ }
1872
+ }
1873
+
1818
1874
/* Create output file for download. */
1819
1875
if (bResults ) {
1820
1876
hOut = CreateFileW (file ,
@@ -1883,26 +1939,42 @@ XS(w32_HttpGetFile)
1883
1939
Safefree (hostName );
1884
1940
Safefree (urlPath );
1885
1941
1886
- if (bAborted ) {
1887
- char msgbuf [ONE_K_BUFSIZE ];
1942
+ /* Retrieve system and WinHttp error messages, but not if we already
1943
+ * got a failed HTTP status text above.
1944
+ */
1945
+ if (bAborted && !bHttpError ) {
1888
1946
DWORD msgFlags = bFileError
1889
1947
? FORMAT_MESSAGE_FROM_SYSTEM
1890
1948
: FORMAT_MESSAGE_FROM_HMODULE ;
1891
-
1892
- if (FormatMessageA (msgFlags ,
1893
- GetModuleHandleA ("winhttp.dll" ),
1894
- error ,
1895
- 0 ,
1896
- msgbuf ,
1897
- sizeof (msgbuf ) - 1 ,
1898
- NULL )) {
1899
- Perl_warn (aTHX_ "Error %lu in Win32::HttpGetFile: %s" , error , msgbuf );
1949
+ msgFlags |= FORMAT_MESSAGE_IGNORE_INSERTS ;
1950
+
1951
+ ZeroMemory (& msgbuf , ONE_K_BUFSIZE * 2 );
1952
+ if (!FormatMessageW (msgFlags ,
1953
+ GetModuleHandleW (L"winhttp.dll" ),
1954
+ error ,
1955
+ 0 ,
1956
+ msgbuf ,
1957
+ ONE_K_BUFSIZE - 1 , /* TCHARs, not bytes */
1958
+ NULL )) {
1959
+ wcsncpy (msgbuf , L"unable to format error message" , ONE_K_BUFSIZE - 1 );
1900
1960
}
1901
1961
SetLastError (error );
1902
- XSRETURN_NO ;
1903
1962
}
1904
1963
1905
- XSRETURN_YES ;
1964
+ if (GIMME_V == G_SCALAR ) {
1965
+ EXTEND (SP , 1 );
1966
+ ST (0 ) = !bAborted ? & PL_sv_yes : & PL_sv_no ;
1967
+ XSRETURN (1 );
1968
+ }
1969
+ else if (GIMME_V == G_ARRAY ) {
1970
+ EXTEND (SP , 2 );
1971
+ ST (0 ) = !bAborted ? & PL_sv_yes : & PL_sv_no ;
1972
+ ST (1 ) = wstr_to_sv (aTHX_ msgbuf );
1973
+ XSRETURN (2 );
1974
+ }
1975
+ else {
1976
+ XSRETURN_EMPTY ;
1977
+ }
1906
1978
}
1907
1979
1908
1980
#endif
0 commit comments