Signed-off-by: Daniel Lehman dlehman25@gmail.com --- v2: reverting to the \r -> %c thing for the wine.pot build --- dlls/wininet/http.c | 71 +++++++++++++++++++++ dlls/wininet/resource.h | 3 + dlls/wininet/tests/http.c | 127 +++++++++++++++++++++++++++++++++++++- dlls/wininet/wininet.rc | 11 ++++ 4 files changed, 210 insertions(+), 2 deletions(-)
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c index 220493718c3..5d10db0a308 100644 --- a/dlls/wininet/http.c +++ b/dlls/wininet/http.c @@ -54,6 +54,7 @@
#include "internet.h" #include "zlib.h" +#include "resource.h" #include "wine/debug.h" #include "wine/exception.h"
@@ -2277,6 +2278,76 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe
return get_security_cert_struct(req, (INTERNET_CERTIFICATE_INFOA*)buffer); } + case INTERNET_OPTION_SECURITY_CERTIFICATE: { + DWORD err; + int needed; + char fmt[512]; + char strength[16]; + char start_date[32]; + char start_time[32]; + char expiry_date[32]; + char expiry_time[32]; + SYSTEMTIME start, expiry; + INTERNET_CERTIFICATE_INFOA info; + + if(!size) + return ERROR_INVALID_PARAMETER; + + if(!req->netconn) { + *size = 0; + return ERROR_INTERNET_INVALID_OPERATION; + } + + if(!buffer) { + *size = 1; + return ERROR_INSUFFICIENT_BUFFER; + } + + if((err = get_security_cert_struct(req, &info))) + return err; + + if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH) + FIXME("INTERNET_OPTION_SECURITY_CERTIFICATE currently English-only\n"); + + LoadStringA(WININET_hModule, IDS_CERT_FORMAT, fmt, sizeof(fmt)); + LoadStringA(WININET_hModule, info.dwKeySize >= 128 ? IDS_CERT_HIGH : IDS_CERT_LOW, + strength, sizeof(strength)); + + FileTimeToSystemTime(&info.ftStart, &start); + FileTimeToSystemTime(&info.ftExpiry, &expiry); + GetDateFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_date, sizeof(start_date)); + GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_time, sizeof(start_time)); + GetDateFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_date, sizeof(expiry_date)); + GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_time, sizeof(expiry_time)); + + needed = _scprintf(fmt, + '\r', info.lpszSubjectInfo, '\r', + '\r', info.lpszIssuerInfo, '\r', + start_date, start_time, '\r', + expiry_date, expiry_time, '\r', + '\r', '\r', '\r', + strength, info.dwKeySize); + if(needed < *size) { + err = ERROR_SUCCESS; + *size = snprintf(buffer, *size, fmt, + '\r', info.lpszSubjectInfo, '\r', + '\r', info.lpszIssuerInfo, '\r', + start_date, start_time, '\r', + expiry_date, expiry_time, '\r', + '\r', '\r', '\r', + strength, info.dwKeySize); + }else { + err = ERROR_INSUFFICIENT_BUFFER; + *size = 1; + } + + LocalFree(info.lpszSubjectInfo); + LocalFree(info.lpszIssuerInfo); + LocalFree(info.lpszProtocolName); + LocalFree(info.lpszSignatureAlgName); + LocalFree(info.lpszEncryptionAlgName); + return err; + } case INTERNET_OPTION_CONNECT_TIMEOUT: if (*size < sizeof(DWORD)) return ERROR_INSUFFICIENT_BUFFER; diff --git a/dlls/wininet/resource.h b/dlls/wininet/resource.h index 256a374af08..190d3cc397c 100644 --- a/dlls/wininet/resource.h +++ b/dlls/wininet/resource.h @@ -38,3 +38,6 @@ #define IDS_CERT_DATE_INVALID 0x502 #define IDS_CERT_CN_INVALID 0x503 #define IDS_CERT_ERRORS 0x504 +#define IDS_CERT_FORMAT 0x505 +#define IDS_CERT_HIGH 0x506 +#define IDS_CERT_LOW 0x507 diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c index c07d60d2a9d..e0a38e05c41 100644 --- a/dlls/wininet/tests/http.c +++ b/dlls/wininet/tests/http.c @@ -6112,9 +6112,26 @@ static const cert_struct_test_t test_winehq_com_cert = { "webmaster@winehq.org" };
+static const char *cert_string_fmt = + "Subject:\r\n%s\r\n" + "Issuer:\r\n%s\r\n" + "Effective Date:\t%s %s\r\n" + "Expiration Date:\t%s %s\r\n" + "Security Protocol:\t%s\r\n" + "Signature Type:\t%s\r\n" + "Encryption Type:\t%s\r\n" + "Privacy Strength:\t%s (%u bits)"; + static void test_cert_struct(HINTERNET req, const cert_struct_test_t *test) { INTERNET_CERTIFICATE_INFOA info; + SYSTEMTIME start, expiry; + char expiry_date[32]; + char expiry_time[32]; + char start_date[32]; + char start_time[32]; + char expect[512]; + char actual[512]; DWORD size; BOOL res;
@@ -6138,6 +6155,42 @@ static void test_cert_struct(HINTERNET req, const cert_struct_test_t *test) ok(!info.lpszProtocolName, "lpszProtocolName = %s\n", info.lpszProtocolName); ok(info.dwKeySize >= 128 && info.dwKeySize <= 256, "dwKeySize = %u\n", info.dwKeySize);
+ if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH) + { + skip("Non-English locale (test with hardcoded English)\n"); + release_cert_info(&info); + return; + } + + size = sizeof(actual); + SetLastError(0xdeadbeef); + memset(actual, 0x55, sizeof(actual)); + res = InternetQueryOptionA(req, INTERNET_OPTION_SECURITY_CERTIFICATE, actual, &size); + ok(res, "InternetQueryOption failed: %u\n", GetLastError()); + + FileTimeToSystemTime(&info.ftStart, &start); + FileTimeToSystemTime(&info.ftExpiry, &expiry); + + GetDateFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_date, sizeof(start_date)); + GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &start, NULL, start_time, sizeof(start_time)); + GetDateFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_date, sizeof(expiry_date)); + GetTimeFormatA(LOCALE_USER_DEFAULT, 0, &expiry, NULL, expiry_time, sizeof(expiry_time)); + + snprintf(expect, sizeof(expect), cert_string_fmt, info.lpszSubjectInfo, info.lpszIssuerInfo, + start_date, start_time, expiry_date, expiry_time, + info.lpszSignatureAlgName, info.lpszEncryptionAlgName, info.lpszProtocolName, + info.dwKeySize >= 128 ? "High" : "Low", info.dwKeySize); + ok(size == strlen(actual), "size = %u\n", size); + ok(!strcmp(actual, expect), "cert = actual\n%s\n", actual); + + --size; + SetLastError(0xdeadbeef); + memset(actual, 0x55, sizeof(actual)); + res = InternetQueryOptionA(req, INTERNET_OPTION_SECURITY_CERTIFICATE, actual, &size); + ok(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == 1, "unexpected size: %u\n", size); + ok(actual[0] == 0x55, "unexpected byte: %02x\n", actual[0]); + release_cert_info(&info); }
@@ -6217,7 +6270,7 @@ static void test_security_flags(void) INTERNET_CERTIFICATE_INFOA *cert; HINTERNET ses, conn, req; DWORD size, flags; - char buf[100]; + char buf[512]; BOOL res;
if (!https_support) @@ -6378,6 +6431,30 @@ static void test_security_flags(void) } HeapFree(GetProcessHeap(), 0, cert);
+ SetLastError(0xdeadbeef); + res = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, NULL, NULL); + ok(!res && GetLastError() == ERROR_INVALID_PARAMETER, "InternetQueryOption failed: %d\n", GetLastError()); + + size = 0; + SetLastError(0xdeadbeef); + res = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, NULL, &size); + ok(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == 1, "unexpected size: %u\n", size); + + size = 42; + SetLastError(0xdeadbeef); + memset(buf, 0x55, sizeof(buf)); + res = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, buf, &size); + ok(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == 1, "unexpected size: %u\n", size); + ok(buf[0] == 0x55, "unexpected byte: %02x\n", buf[0]); + + size = sizeof(buf); + SetLastError(0xdeadbeef); + res = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, buf, &size); + ok(res && GetLastError() == ERROR_SUCCESS, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size < sizeof(buf), "unexpected size: %u\n", size); + CHECK_NOTIFIED2(INTERNET_STATUS_CONNECTING_TO_SERVER, 2); CHECK_NOTIFIED2(INTERNET_STATUS_CONNECTED_TO_SERVER, 2); CHECK_NOTIFIED2(INTERNET_STATUS_CLOSING_CONNECTION, 2); @@ -6562,9 +6639,10 @@ static void test_secure_connection(void) static const WCHAR get[] = {'G','E','T',0}; static const WCHAR testpage[] = {'/','t','e','s','t','s','/','h','e','l','l','o','.','h','t','m','l',0}; HINTERNET ses, con, req; - DWORD size, flags, err; + DWORD size, size2, flags, err; INTERNET_CERTIFICATE_INFOA *certificate_structA = NULL; INTERNET_CERTIFICATE_INFOW *certificate_structW = NULL; + char certstr1[512], certstr2[512]; BOOL ret;
ses = InternetOpenA("Gizmo5", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); @@ -6629,6 +6707,19 @@ static void test_secure_connection(void) } HeapFree(GetProcessHeap(), 0, certificate_structW);
+ SetLastError(0xdeadbeef); + size = sizeof(certstr1); + ret = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE, certstr1, &size); + ok(ret && GetLastError() == ERROR_SUCCESS, "InternetQueryOption failed: %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + size2 = sizeof(certstr2); + ret = InternetQueryOptionA(req, INTERNET_OPTION_SECURITY_CERTIFICATE, certstr2, &size2); + ok(ret && GetLastError() == ERROR_SUCCESS, "InternetQueryOption failed: %d\n", GetLastError()); + + ok(size == size2, "expected same size\n"); + ok(!strcmp(certstr1, certstr2), "expected same string\n"); + InternetCloseHandle(req); InternetCloseHandle(con); InternetCloseHandle(ses); @@ -7517,6 +7608,37 @@ static void test_concurrent_header_access(void) CloseHandle( wait ); }
+static void test_cert_string(void) +{ + HINTERNET ses, con, req; + char actual[512]; + DWORD size; + BOOL res; + + ses = InternetOpenA( "winetest", 0, NULL, NULL, 0 ); + ok( ses != NULL, "InternetOpenA failed\n" ); + + con = InternetConnectA( ses, "test.winehq.org", INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, + INTERNET_SERVICE_HTTP, 0, 0 ); + ok( con != NULL, "InternetConnectA failed %u\n", GetLastError() ); + + req = HttpOpenRequestA( con, NULL, "/", NULL, NULL, NULL, 0, 0 ); + ok( req != NULL, "HttpOpenRequestA failed %u\n", GetLastError() ); + + size = sizeof(actual); + SetLastError( 0xdeadbeef ); + memset( actual, 0x55, sizeof(actual) ); + res = InternetQueryOptionA( req, INTERNET_OPTION_SECURITY_CERTIFICATE, actual, &size ); + ok( !res && GetLastError() == ERROR_INTERNET_INVALID_OPERATION, + "InternetQueryOption failed: %u\n", GetLastError() ); + ok( size == 0, "unexpected size: %u\n", size ); + ok( actual[0] == 0x55, "unexpected byte: %02x\n", actual[0] ); + + InternetCloseHandle( req ); + InternetCloseHandle( con ); + InternetCloseHandle( ses ); +} + START_TEST(http) { HMODULE hdll; @@ -7566,5 +7688,6 @@ START_TEST(http) test_connection_failure(); test_default_service_port(); test_concurrent_header_access(); + test_cert_string(); free_events(); } diff --git a/dlls/wininet/wininet.rc b/dlls/wininet/wininet.rc index b6e35629ca2..ede9edab09c 100644 --- a/dlls/wininet/wininet.rc +++ b/dlls/wininet/wininet.rc @@ -29,6 +29,17 @@ STRINGTABLE IDS_CERT_DATE_INVALID "The date on the certificate is invalid." IDS_CERT_CN_INVALID "The name on the certificate does not match the site." IDS_CERT_ERRORS "There is at least one unspecified security problem with this certificate." + + IDS_CERT_FORMAT "Subject:%c\n%s%c\n" \ + "Issuer:%c\n%s%c\n" \ + "Effective Date:\t%s %s%c\n" \ + "Expiration Date:\t%s %s%c\n" \ + "Security Protocol:\t(null)%c\n" \ + "Signature Type:\t(null)%c\n" \ + "Encryption Type:\t(null)%c\n" \ + "Privacy Strength:\t%s (%u bits)" + IDS_CERT_HIGH "High" + IDS_CERT_LOW "Low" }
IDD_PROXYDLG DIALOG 36, 24, 220, 146
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=77361
Your paranoid android.
=== w8adm (32 bit report) ===
wininet: http.c:683: Test failed: req_error = 12007 http.c:694: Test failed: expected status 11 (INTERNET_STATUS_NAME_RESOLVED) 1 times, received 0 times http.c:707: Test failed: expected status 30 (INTERNET_STATUS_SENDING_REQUEST) 1 times, received 0 times http.c:708: Test failed: expected status 31 (INTERNET_STATUS_REQUEST_SENT) 1 times, received 0 times http.c:709: Test failed: expected status 40 (INTERNET_STATUS_RECEIVING_RESPONSE) 1 times, received 0 times http.c:710: Test failed: expected status 41 (INTERNET_STATUS_RESPONSE_RECEIVED) 1 times, received 0 times http.c:719: Test failed: flags = 8, expected 0 http.c:733: Test failed: Expected any header character, got 0x00 http.c:760: Test failed: Expected 0x0000, got 7777 http.c:890: Test failed: Returned zero size in response to request complete http.c:356: Test failed: unexpected status 10 (INTERNET_STATUS_RESOLVING_NAME) http.c:356: Test failed: unexpected status 11 (INTERNET_STATUS_NAME_RESOLVED) http.c:704: Test failed: expected status 10 (INTERNET_STATUS_RESOLVING_NAME) 0 times, received 1 times http.c:705: Test failed: expected status 11 (INTERNET_STATUS_NAME_RESOLVED) 0 times, received 1 times
=== w8adm (32 bit report) ===
wininet: http.c:683: Test failed: req_error = 12007 http.c:694: Test failed: expected status 11 (INTERNET_STATUS_NAME_RESOLVED) 1 times, received 0 times
these are random and unrelated to my changes
thanks daniel
Hi Daniel,
On 20/08/2020 05:52, Daniel Lehman wrote:
Signed-off-by: Daniel Lehmandlehman25@gmail.com
v2: reverting to the \r -> %c thing for the wine.pot build
I'm not familiar with relevant code, but it seems to me that ideally wrc would support \r in this case. I'd hope that it's a matter of some kind of escaping handling. I don't mean to block your patch on this if it's too much work, but I think it would be good to at least consider fixing wrc instead.
Thanks,
Jacek
I'm not familiar with relevant code, but it seems to me that ideally wrc would support \r in this case.
i can get wrc to take \r. but when the message is passed to gettext, i get this: "warning: internationalized messages should not contain the '\r' escape sequence"
the same warning is shown for other escape sequences, like \v i can skip the \r and it's still multi-line, but the output miscompares with windows which has the \r the only alternative i see is is to break the multiline message into multiple single-line messages
thanks daniel
On 03/09/2020 15:43, Daniel Lehman wrote:
I'm not familiar with relevant code, but it seems to me that ideally wrc would support \r in this case.
i can get wrc to take \r. but when the message is passed to gettext, i get this: "warning: internationalized messages should not contain the '\r' escape sequence"
the same warning is shown for other escape sequences, like \v i can skip the \r and it's still multi-line, but the output miscompares with windows which has the \r the only alternative i see is is to break the multiline message into multiple single-line messages
Could we handle escaping ourselves and pass something like \r to gettext (or, if needed, something more generic like \xNN)?
Thanks,
Jacek
Could we handle escaping ourselves and pass something like \r to gettext (or, if needed, something more generic like \xNN)?
i played around with it but won't take it. from a search it sounds like a common issue and the reply is to split the lines. only \n and \t are accepted
it's easy enough to split the lines. i'll go with that
thanks daniel