summaryrefslogtreecommitdiff
path: root/cpan
diff options
context:
space:
mode:
authorChris 'BinGOs' Williams <chris@bingosnet.co.uk>2022-01-19 14:00:58 +0000
committerChris 'BinGOs' Williams <chris@bingosnet.co.uk>2022-01-19 14:00:58 +0000
commit3fe11e08ed12031c30123faa114fa48661ea6226 (patch)
tree6a172717fdb4c1b27fbe7904e1d621f2524bd24d /cpan
parent8b9e957f94ada5241a25788d92476d18da1dab19 (diff)
downloadperl-3fe11e08ed12031c30123faa114fa48661ea6226.tar.gz
Update Win32 to CPAN version 0.58
[DELTA] 0.58 [2022-01-17] - add Win32::HttpGetFile (thanks to Craig Berry for the implementation and Tomasz Konojacki for code review) [PR/30] - skip failing Unicode.t on Cygwin because cwd() no longer returns an ANSI (short) path there. - Fixed test 14,15 of GetFullPathName.t when package is unpacked in a top level folder (thanks to Jianhong Feng) [PR/20]
Diffstat (limited to 'cpan')
-rw-r--r--cpan/Win32/Makefile.PL4
-rw-r--r--cpan/Win32/Win32.pm45
-rw-r--r--cpan/Win32/Win32.xs313
-rw-r--r--cpan/Win32/t/GetFullPathName.t4
-rw-r--r--cpan/Win32/t/Unicode.t3
5 files changed, 362 insertions, 7 deletions
diff --git a/cpan/Win32/Makefile.PL b/cpan/Win32/Makefile.PL
index d6ca5c68ae..90ad660144 100644
--- a/cpan/Win32/Makefile.PL
+++ b/cpan/Win32/Makefile.PL
@@ -22,10 +22,10 @@ my %param = (
$param{NO_META} = 1 if eval "$ExtUtils::MakeMaker::VERSION" >= 6.10_03;
if ($^O eq 'cygwin') {
- $param{LIBS} = ['-L/lib/w32api -lole32 -lversion -luserenv -lnetapi32']
+ $param{LIBS} = ['-L/lib/w32api -lole32 -lversion -luserenv -lnetapi32 -lwinhttp']
}
else {
- $param{LIBS} = ['-luserenv']
+ $param{LIBS} = ['-luserenv -lwinhttp']
}
my $test_requires = $ExtUtils::MakeMaker::VERSION >= 6.64
diff --git a/cpan/Win32/Win32.pm b/cpan/Win32/Win32.pm
index 1092c5ab52..ce6dee3ce6 100644
--- a/cpan/Win32/Win32.pm
+++ b/cpan/Win32/Win32.pm
@@ -8,7 +8,7 @@ package Win32;
require DynaLoader;
@ISA = qw|Exporter DynaLoader|;
- $VERSION = '0.57';
+ $VERSION = '0.58';
$XS_VERSION = $VERSION;
$VERSION = eval $VERSION;
@@ -1305,11 +1305,52 @@ of hex digits with surrounding braces. For example:
{09531CF1-D0C7-4860-840C-1C8C8735E2AD}
+=item Win32::HttpGetFile(URL, FILENAME [, IGNORE_CERT_ERRORS])
+
+Uses the WinHttp library to download the file specified by the URL
+parameter to the local file specified by FILENAME. The optional third
+parameter, if true, indicates that certficate errors are to be ignored
+for https connections; please use with caution in a safe environment,
+such as when testing locally using a self-signed certificate.
+
+Only http and https protocols are supported. Authentication is not
+supported. The function is not available when building with gcc prior to
+4.8.0 because the WinHttp library is not available.
+
+In scalar context returns a boolean success or failure, and in list
+context also returns, in addition to the boolean status, a second
+value containing message text related to the status.
+
+If the call fails, C<Win32::GetLastError()> will return a numeric
+error code, which may be a system error, a WinHttp error, or a
+user-defined error composed of 1e9 plus the HTTP status code.
+
+Scalar context example:
+
+ print Win32::GetLastError()
+ unless Win32::HttpGetFile('http://example.com/somefile.tar.gz',
+ '.\file.tgz');
+
+List context example:
+
+ my ($ok, $msg) = Win32::HttpGetFile('http://example.com/somefile.tar.gz',
+ '.\file.tgz');
+ if ($ok) {
+ print "Success!: $msg\n";
+ }
+ else {
+ print "Failure!: $msg\n";
+ my $err = Win32::GetLastError();
+ if ($err > 1e9) {
+ printf "HTTP status: %d\n", ($err - 1e9);
+ }
+ }
+
=item Win32::InitiateSystemShutdown
(MACHINE, MESSAGE, TIMEOUT, FORCECLOSE, REBOOT)
-Shutsdown the specified MACHINE, notifying users with the
+Shuts down the specified MACHINE, notifying users with the
supplied MESSAGE, within the specified TIMEOUT interval. Forces
closing of all documents without prompting the user if FORCECLOSE is
true, and reboots the machine if REBOOT is true. This function works
diff --git a/cpan/Win32/Win32.xs b/cpan/Win32/Win32.xs
index ca7519f0d6..dcffbd914d 100644
--- a/cpan/Win32/Win32.xs
+++ b/cpan/Win32/Win32.xs
@@ -7,6 +7,9 @@
#include <wchar.h>
#include <userenv.h>
#include <lm.h>
+#if !defined(__GNUC__) || (((100000 * __GNUC__) + (1000 * __GNUC_MINOR__)) >= 408000)
+# include <winhttp.h>
+#endif
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
@@ -1682,6 +1685,313 @@ XS(w32_IsDeveloperModeEnabled)
XSRETURN_NO;
}
+#ifdef WINHTTPAPI
+
+XS(w32_HttpGetFile)
+{
+ dXSARGS;
+ WCHAR *url = NULL, *file = NULL, *hostName = NULL, *urlPath = NULL;
+ bool bIgnoreCertErrors = FALSE;
+ WCHAR msgbuf[ONE_K_BUFSIZE];
+ BOOL bResults = FALSE;
+ HINTERNET hSession = NULL,
+ hConnect = NULL,
+ hRequest = NULL;
+ HANDLE hOut = INVALID_HANDLE_VALUE;
+ BOOL bParsed = FALSE,
+ bAborted = FALSE,
+ bFileError = FALSE,
+ bHttpError = FALSE;
+ DWORD error = 0;
+ URL_COMPONENTS urlComp;
+ LPCWSTR acceptTypes[] = { L"*/*", NULL };
+ DWORD dwHttpStatusCode = 0, dwQuerySize = 0;
+
+ if (items < 2 || items > 3)
+ croak("usage: Win32::HttpGetFile($url, $file[, $ignore_cert_errors])");
+
+ url = sv_to_wstr(aTHX_ ST(0));
+ file = sv_to_wstr(aTHX_ ST(1));
+
+ if (items == 3)
+ bIgnoreCertErrors = (BOOL)SvIV(ST(2));
+
+ /* Initialize the URL_COMPONENTS structure, setting the required
+ * component lengths to non-zero so that they get populated.
+ */
+ ZeroMemory(&urlComp, sizeof(urlComp));
+ urlComp.dwStructSize = sizeof(urlComp);
+ urlComp.dwSchemeLength = (DWORD)-1;
+ urlComp.dwHostNameLength = (DWORD)-1;
+ urlComp.dwUrlPathLength = (DWORD)-1;
+ urlComp.dwExtraInfoLength = (DWORD)-1;
+
+ /* Parse the URL. */
+ bParsed = WinHttpCrackUrl(url, (DWORD)wcslen(url), 0, &urlComp);
+
+ /* Only support http and htts, not ftp, gopher, etc. */
+ if (bParsed
+ && !(urlComp.nScheme == INTERNET_SCHEME_HTTPS
+ || urlComp.nScheme == INTERNET_SCHEME_HTTP)) {
+ SetLastError(12006); /* not a recognized protocol */
+ bParsed = FALSE;
+ }
+
+ if (bParsed) {
+ New(0, hostName, urlComp.dwHostNameLength + 1, WCHAR);
+ wcsncpy(hostName, urlComp.lpszHostName, urlComp.dwHostNameLength);
+ hostName[urlComp.dwHostNameLength] = 0;
+
+ New(0, urlPath, urlComp.dwUrlPathLength + urlComp.dwExtraInfoLength + 1, WCHAR);
+ wcsncpy(urlPath, urlComp.lpszUrlPath, urlComp.dwUrlPathLength + urlComp.dwExtraInfoLength);
+ urlPath[urlComp.dwUrlPathLength + urlComp.dwExtraInfoLength] = 0;
+
+ /* Use WinHttpOpen to obtain a session handle. */
+ hSession = WinHttpOpen(L"Perl",
+ WINHTTP_ACCESS_TYPE_NO_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+ }
+
+ /* Specify an HTTP server. */
+ if (hSession)
+ hConnect = WinHttpConnect(hSession,
+ hostName,
+ urlComp.nPort,
+ 0);
+
+ /* Create an HTTP request handle. */
+ if (hConnect)
+ hRequest = WinHttpOpenRequest(hConnect,
+ L"GET",
+ urlPath,
+ NULL,
+ WINHTTP_NO_REFERER,
+ acceptTypes,
+ urlComp.nScheme == INTERNET_SCHEME_HTTPS
+ ? WINHTTP_FLAG_SECURE
+ : 0);
+
+ /* If specified, disable certificate-related errors for https connections. */
+ if (hRequest
+ && bIgnoreCertErrors
+ && urlComp.nScheme == INTERNET_SCHEME_HTTPS) {
+ DWORD secFlags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID
+ | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
+ | SECURITY_FLAG_IGNORE_UNKNOWN_CA
+ | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
+ if(!WinHttpSetOption(hRequest,
+ WINHTTP_OPTION_SECURITY_FLAGS,
+ &secFlags,
+ sizeof(secFlags))) {
+ bAborted = TRUE;
+ }
+ }
+
+ /* Call WinHttpGetProxyForUrl with our target URL. If auto-proxy succeeds,
+ * then set the proxy info on the request handle. If auto-proxy fails,
+ * ignore the error and attempt to send the HTTP request directly to the
+ * target server (using the default WINHTTP_ACCESS_TYPE_NO_PROXY
+ * configuration, which the request handle will inherit from the session).
+ */
+ if (hRequest && !bAborted) {
+ WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions;
+ WINHTTP_PROXY_INFO ProxyInfo;
+ DWORD cbProxyInfoSize = sizeof(ProxyInfo);
+
+ ZeroMemory(&AutoProxyOptions, sizeof(AutoProxyOptions));
+ ZeroMemory(&ProxyInfo, sizeof(ProxyInfo));
+ AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
+ AutoProxyOptions.dwAutoDetectFlags =
+ WINHTTP_AUTO_DETECT_TYPE_DHCP |
+ WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+ AutoProxyOptions.fAutoLogonIfChallenged = TRUE;
+
+ if(WinHttpGetProxyForUrl(hSession,
+ url,
+ &AutoProxyOptions,
+ &ProxyInfo)) {
+ if(!WinHttpSetOption(hRequest,
+ WINHTTP_OPTION_PROXY,
+ &ProxyInfo,
+ cbProxyInfoSize)) {
+ bAborted = TRUE;
+ Perl_warn(aTHX_ "Win32::HttpGetFile: setting proxy options failed");
+ }
+ Safefree(ProxyInfo.lpszProxy);
+ Safefree(ProxyInfo.lpszProxyBypass);
+ }
+ }
+
+ /* Send a request. */
+ if (hRequest && !bAborted)
+ bResults = WinHttpSendRequest(hRequest,
+ WINHTTP_NO_ADDITIONAL_HEADERS,
+ 0,
+ WINHTTP_NO_REQUEST_DATA,
+ 0,
+ 0,
+ 0);
+
+ /* End the request. */
+ if (bResults)
+ bResults = WinHttpReceiveResponse(hRequest, NULL);
+
+ /* Retrieve HTTP status code. */
+ if (bResults) {
+ dwQuerySize = sizeof(dwHttpStatusCode);
+ bResults = WinHttpQueryHeaders(hRequest,
+ WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ &dwHttpStatusCode,
+ &dwQuerySize,
+ WINHTTP_NO_HEADER_INDEX);
+ }
+
+ /* Retrieve HTTP status text. Note this may be a success message. */
+ if (bResults) {
+ dwQuerySize = ONE_K_BUFSIZE * 2 - 2;
+ ZeroMemory(&msgbuf, ONE_K_BUFSIZE * 2);
+ bResults = WinHttpQueryHeaders(hRequest,
+ WINHTTP_QUERY_STATUS_TEXT,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ msgbuf,
+ &dwQuerySize,
+ WINHTTP_NO_HEADER_INDEX);
+ }
+
+ /* There is no point in successfully downloading an error page from
+ * the server, so consider HTTP errors to be failures.
+ */
+ if (bResults) {
+ if (dwHttpStatusCode < 200 || dwHttpStatusCode > 299) {
+ bResults = FALSE;
+ bHttpError = TRUE;
+ }
+ }
+
+ /* Create output file for download. */
+ if (bResults) {
+ hOut = CreateFileW(file,
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hOut == INVALID_HANDLE_VALUE)
+ bFileError = TRUE;
+ }
+
+ if (!bFileError && bResults) {
+ DWORD dwDownloaded = 0;
+ DWORD dwBytesWritten = 0;
+ DWORD dwSize = 65536;
+ char *pszOutBuffer;
+
+ New(0, pszOutBuffer, dwSize, char);
+
+ /* Keep checking for data until there is nothing left. */
+ while (1) {
+ if (!WinHttpReadData(hRequest,
+ (LPVOID)pszOutBuffer,
+ dwSize,
+ &dwDownloaded)) {
+ bAborted = TRUE;
+ break;
+ }
+ if (!dwDownloaded)
+ break;
+
+ /* Write what we just read to the output file */
+ if (!WriteFile(hOut,
+ pszOutBuffer,
+ dwDownloaded,
+ &dwBytesWritten,
+ NULL)) {
+ bAborted = TRUE;
+ bFileError = TRUE;
+ break;
+ }
+
+ }
+
+ Safefree(pszOutBuffer);
+ }
+ else {
+ bAborted = TRUE;
+ }
+
+ /* Clean-up may lose this. */
+ if (bAborted)
+ error = GetLastError();
+
+ /* If we successfully opened the output file but failed later, mark
+ * the file for deletion.
+ */
+ if (bAborted && hOut != INVALID_HANDLE_VALUE)
+ (void) DeleteFileW(file);
+
+ /* Close any open handles. */
+ if (hOut != INVALID_HANDLE_VALUE) CloseHandle(hOut);
+ if (hRequest) WinHttpCloseHandle(hRequest);
+ if (hConnect) WinHttpCloseHandle(hConnect);
+ if (hSession) WinHttpCloseHandle(hSession);
+
+ Safefree(url);
+ Safefree(file);
+ Safefree(hostName);
+ Safefree(urlPath);
+
+ /* Retrieve system and WinHttp error messages, or compose a user-defined
+ * error code if we got a failed HTTP status text above. Conveniently, adding
+ * 1e9 to the HTTP status sets bit 29, denoting a user-defined error code,
+ * and also makes it easy to lop off the upper part and just get HTTP status.
+ */
+ if (bAborted) {
+ if (bHttpError) {
+ SetLastError(dwHttpStatusCode + 1000000000);
+ }
+ else {
+ DWORD msgFlags = bFileError
+ ? FORMAT_MESSAGE_FROM_SYSTEM
+ : FORMAT_MESSAGE_FROM_HMODULE;
+ msgFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
+
+ ZeroMemory(&msgbuf, ONE_K_BUFSIZE * 2);
+ if (!FormatMessageW(msgFlags,
+ GetModuleHandleW(L"winhttp.dll"),
+ error,
+ 0,
+ msgbuf,
+ ONE_K_BUFSIZE - 1, /* TCHARs, not bytes */
+ NULL)) {
+ wcsncpy(msgbuf, L"unable to format error message", ONE_K_BUFSIZE - 1);
+ }
+ SetLastError(error);
+ }
+ }
+
+ if (GIMME_V == G_SCALAR) {
+ EXTEND(SP, 1);
+ ST(0) = !bAborted ? &PL_sv_yes : &PL_sv_no;
+ XSRETURN(1);
+ }
+ else if (GIMME_V == G_ARRAY) {
+ EXTEND(SP, 2);
+ ST(0) = !bAborted ? &PL_sv_yes : &PL_sv_no;
+ ST(1) = wstr_to_sv(aTHX_ msgbuf);
+ XSRETURN(2);
+ }
+ else {
+ XSRETURN_EMPTY;
+ }
+}
+
+#endif
+
MODULE = Win32 PACKAGE = Win32
PROTOTYPES: DISABLE
@@ -1756,5 +2066,8 @@ BOOT:
#ifdef __CYGWIN__
newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
#endif
+#ifdef WINHTTPAPI
+ newXS("Win32::HttpGetFile", w32_HttpGetFile, file);
+#endif
XSRETURN_YES;
}
diff --git a/cpan/Win32/t/GetFullPathName.t b/cpan/Win32/t/GetFullPathName.t
index ec716d15d2..db1dc829f0 100644
--- a/cpan/Win32/t/GetFullPathName.t
+++ b/cpan/Win32/t/GetFullPathName.t
@@ -27,8 +27,8 @@ ok((Win32::GetFullPathName(substr($cwd,2)))[1], $file);
ok(scalar Win32::GetFullPathName('/Foo Bar/'), substr($cwd,0,2)."\\Foo Bar\\");
-chdir($dir);
-ok(scalar Win32::GetFullPathName('.'), $dir);
+chdir(my $dird = $dir !~ /^[A-Z]:$/ ? $dir : "$dir\\");
+ok(scalar Win32::GetFullPathName('.'), $dird);
ok((Win32::GetFullPathName($file))[0], "$dir\\");
ok((Win32::GetFullPathName($file))[1], $file);
diff --git a/cpan/Win32/t/Unicode.t b/cpan/Win32/t/Unicode.t
index 8362853698..c1380e87e0 100644
--- a/cpan/Win32/t/Unicode.t
+++ b/cpan/Win32/t/Unicode.t
@@ -90,7 +90,8 @@ if ($^O eq "cygwin") {
$subdir = Cygwin::posix_to_win_path($subdir, 1);
}
$subdir =~ s,/,\\,g;
-ok(Win32::GetLongPathName($subdir), $long);
+# Cygwin64 no longer returns an ANSI name
+skip($^O eq "cygwin", Win32::GetLongPathName($subdir), $long);
# We can chdir() into the Unicode directory if we use the ANSI name
ok(chdir(Win32::GetANSIPathName($dir)));