wininet/tests: Add tests for a prematurely closed connection.
[wine] / dlls / wininet / dialogs.c
1 /*
2  * Wininet
3  *
4  * Copyright 2003 Mike McCormack for CodeWeavers Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #if defined(__MINGW32__) || defined (_MSC_VER)
25 #include <ws2tcpip.h>
26 #endif
27
28 #include <stdarg.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winuser.h"
33 #include "winreg.h"
34 #include "wininet.h"
35 #include "winnetwk.h"
36 #include "wine/debug.h"
37 #include "winerror.h"
38 #define NO_SHLWAPI_STREAM
39 #include "shlwapi.h"
40 #include "cryptuiapi.h"
41
42 #include "internet.h"
43
44 #include "wine/unicode.h"
45
46 #include "resource.h"
47
48 #define MAX_STRING_LEN 1024
49
50 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
51
52 struct WININET_ErrorDlgParams
53 {
54     http_request_t *req;
55     HWND       hWnd;
56     DWORD      dwError;
57     DWORD      dwFlags;
58     LPVOID*    lppvData;
59 };
60
61 /***********************************************************************
62  *         WININET_GetAuthRealm
63  *
64  *  Determine the name of the (basic) Authentication realm
65  */
66 static BOOL WININET_GetAuthRealm( HINTERNET hRequest, LPWSTR szBuf, DWORD sz, BOOL proxy )
67 {
68     LPWSTR p, q;
69     DWORD index, query;
70     static const WCHAR szRealm[] = { 'r','e','a','l','m','=',0 };
71
72     if (proxy)
73         query = HTTP_QUERY_PROXY_AUTHENTICATE;
74     else
75         query = HTTP_QUERY_WWW_AUTHENTICATE;
76
77     /* extract the Realm from the response and show it */
78     index = 0;
79     if( !HttpQueryInfoW( hRequest, query, szBuf, &sz, &index) )
80         return FALSE;
81
82     /*
83      * FIXME: maybe we should check that we're
84      * dealing with 'Basic' Authentication
85      */
86     p = strchrW( szBuf, ' ' );
87     if( !p || strncmpW( p+1, szRealm, strlenW(szRealm) ) )
88     {
89         ERR("response wrong? (%s)\n", debugstr_w(szBuf));
90         return FALSE;
91     }
92
93     /* remove quotes */
94     p += 7;
95     if( *p == '"' )
96     {
97         p++;
98         q = strrchrW( p, '"' );
99         if( q )
100             *q = 0;
101     }
102     strcpyW( szBuf, p );
103
104     return TRUE;
105 }
106
107 /* These two are not defined in the public headers */
108 extern DWORD WINAPI WNetCachePassword(LPSTR,WORD,LPSTR,WORD,BYTE,WORD);
109 extern DWORD WINAPI WNetGetCachedPassword(LPSTR,WORD,LPSTR,LPWORD,BYTE);
110
111 /***********************************************************************
112  *         WININET_GetSetPassword
113  */
114 static BOOL WININET_GetSetPassword( HWND hdlg, LPCWSTR szServer, 
115                                     LPCWSTR szRealm, BOOL bSet )
116 {
117     WCHAR szResource[0x80], szUserPass[0x40];
118     LPWSTR p;
119     HWND hUserItem, hPassItem;
120     DWORD r, dwMagic = 19;
121     UINT r_len, u_len;
122     WORD sz;
123     static const WCHAR szColon[] = { ':',0 };
124     static const WCHAR szbs[] = { '/', 0 };
125
126     hUserItem = GetDlgItem( hdlg, IDC_USERNAME );
127     hPassItem = GetDlgItem( hdlg, IDC_PASSWORD );
128
129     /* now try fetch the username and password */
130     lstrcpyW( szResource, szServer);
131     lstrcatW( szResource, szbs);
132     lstrcatW( szResource, szRealm);
133
134     /*
135      * WNetCachePassword is only concerned with the length
136      * of the data stored (which we tell it) and it does
137      * not use strlen() internally so we can add WCHAR data
138      * instead of ASCII data and get it back the same way.
139      */
140     if( bSet )
141     {
142         szUserPass[0] = 0;
143         GetWindowTextW( hUserItem, szUserPass, 
144                         (sizeof szUserPass-1)/sizeof(WCHAR) );
145         lstrcatW(szUserPass, szColon);
146         u_len = strlenW( szUserPass );
147         GetWindowTextW( hPassItem, szUserPass+u_len, 
148                         (sizeof szUserPass)/sizeof(WCHAR)-u_len );
149
150         r_len = (strlenW( szResource ) + 1)*sizeof(WCHAR);
151         u_len = (strlenW( szUserPass ) + 1)*sizeof(WCHAR);
152         r = WNetCachePassword( (CHAR*)szResource, r_len,
153                                (CHAR*)szUserPass, u_len, dwMagic, 0 );
154
155         return ( r == WN_SUCCESS );
156     }
157
158     sz = sizeof szUserPass;
159     r_len = (strlenW( szResource ) + 1)*sizeof(WCHAR);
160     r = WNetGetCachedPassword( (CHAR*)szResource, r_len,
161                                (CHAR*)szUserPass, &sz, dwMagic );
162     if( r != WN_SUCCESS )
163         return FALSE;
164
165     p = strchrW( szUserPass, ':' );
166     if( p )
167     {
168         *p = 0;
169         SetWindowTextW( hUserItem, szUserPass );
170         SetWindowTextW( hPassItem, p+1 );
171     }
172
173     return TRUE;
174 }
175
176 /***********************************************************************
177  *         WININET_SetAuthorization
178  */
179 static BOOL WININET_SetAuthorization( http_request_t *request, LPWSTR username,
180                                       LPWSTR password, BOOL proxy )
181 {
182     http_session_t *session = request->session;
183     LPWSTR p, q;
184
185     p = heap_strdupW(username);
186     if( !p )
187         return FALSE;
188
189     q = heap_strdupW(password);
190     if( !q )
191     {
192         heap_free(p);
193         return FALSE;
194     }
195
196     if (proxy)
197     {
198         appinfo_t *hIC = session->appInfo;
199
200         heap_free(hIC->proxyUsername);
201         hIC->proxyUsername = p;
202
203         heap_free(hIC->proxyPassword);
204         hIC->proxyPassword = q;
205     }
206     else
207     {
208         heap_free(session->userName);
209         session->userName = p;
210
211         heap_free(session->password);
212         session->password = q;
213     }
214
215     return TRUE;
216 }
217
218 /***********************************************************************
219  *         WININET_ProxyPasswordDialog
220  */
221 static INT_PTR WINAPI WININET_ProxyPasswordDialog(
222     HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
223 {
224     HWND hitem;
225     struct WININET_ErrorDlgParams *params;
226     WCHAR szRealm[0x80], szServer[0x80];
227
228     if( uMsg == WM_INITDIALOG )
229     {
230         TRACE("WM_INITDIALOG (%08lx)\n", lParam);
231
232         /* save the parameter list */
233         params = (struct WININET_ErrorDlgParams*) lParam;
234         SetWindowLongPtrW( hdlg, GWLP_USERDATA, lParam );
235
236         /* extract the Realm from the proxy response and show it */
237         if( WININET_GetAuthRealm( params->req->hdr.hInternet,
238                                   szRealm, sizeof szRealm/sizeof(WCHAR), TRUE ) )
239         {
240             hitem = GetDlgItem( hdlg, IDC_REALM );
241             SetWindowTextW( hitem, szRealm );
242         }
243
244         hitem = GetDlgItem( hdlg, IDC_PROXY );
245         SetWindowTextW( hitem, params->req->session->appInfo->proxy );
246
247         WININET_GetSetPassword( hdlg, szServer, szRealm, FALSE );
248
249         return TRUE;
250     }
251
252     params = (struct WININET_ErrorDlgParams*)
253                  GetWindowLongPtrW( hdlg, GWLP_USERDATA );
254
255     switch( uMsg )
256     {
257     case WM_COMMAND:
258         if( wParam == IDOK )
259         {
260             WCHAR username[0x20], password[0x20];
261
262             username[0] = 0;
263             hitem = GetDlgItem( hdlg, IDC_USERNAME );
264             if( hitem )
265                 GetWindowTextW( hitem, username, sizeof username/sizeof(WCHAR) );
266             
267             password[0] = 0;
268             hitem = GetDlgItem( hdlg, IDC_PASSWORD );
269             if( hitem )
270                 GetWindowTextW( hitem, password, sizeof password/sizeof(WCHAR) );
271
272             hitem = GetDlgItem( hdlg, IDC_SAVEPASSWORD );
273             if( hitem &&
274                 SendMessageW( hitem, BM_GETSTATE, 0, 0 ) &&
275                 WININET_GetAuthRealm( params->req->hdr.hInternet,
276                                       szRealm, sizeof szRealm/sizeof(WCHAR), TRUE) )
277                 WININET_GetSetPassword( hdlg, params->req->session->appInfo->proxy, szRealm, TRUE );
278             WININET_SetAuthorization( params->req, username, password, TRUE );
279
280             EndDialog( hdlg, ERROR_INTERNET_FORCE_RETRY );
281             return TRUE;
282         }
283         if( wParam == IDCANCEL )
284         {
285             EndDialog( hdlg, 0 );
286             return TRUE;
287         }
288         break;
289     }
290     return FALSE;
291 }
292
293 /***********************************************************************
294  *         WININET_PasswordDialog
295  */
296 static INT_PTR WINAPI WININET_PasswordDialog(
297     HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
298 {
299     HWND hitem;
300     struct WININET_ErrorDlgParams *params;
301     WCHAR szRealm[0x80], szServer[0x80];
302
303     if( uMsg == WM_INITDIALOG )
304     {
305         TRACE("WM_INITDIALOG (%08lx)\n", lParam);
306
307         /* save the parameter list */
308         params = (struct WININET_ErrorDlgParams*) lParam;
309         SetWindowLongPtrW( hdlg, GWLP_USERDATA, lParam );
310
311         /* extract the Realm from the response and show it */
312         if( WININET_GetAuthRealm( params->req->hdr.hInternet,
313                                   szRealm, sizeof szRealm/sizeof(WCHAR), FALSE ) )
314         {
315             hitem = GetDlgItem( hdlg, IDC_REALM );
316             SetWindowTextW( hitem, szRealm );
317         }
318
319         hitem = GetDlgItem( hdlg, IDC_SERVER );
320         SetWindowTextW( hitem, params->req->session->hostName );
321
322         WININET_GetSetPassword( hdlg, szServer, szRealm, FALSE );
323
324         return TRUE;
325     }
326
327     params = (struct WININET_ErrorDlgParams*)
328                  GetWindowLongPtrW( hdlg, GWLP_USERDATA );
329
330     switch( uMsg )
331     {
332     case WM_COMMAND:
333         if( wParam == IDOK )
334         {
335             WCHAR username[0x20], password[0x20];
336
337             username[0] = 0;
338             hitem = GetDlgItem( hdlg, IDC_USERNAME );
339             if( hitem )
340                 GetWindowTextW( hitem, username, sizeof username/sizeof(WCHAR) );
341
342             password[0] = 0;
343             hitem = GetDlgItem( hdlg, IDC_PASSWORD );
344             if( hitem )
345                 GetWindowTextW( hitem, password, sizeof password/sizeof(WCHAR) );
346
347             hitem = GetDlgItem( hdlg, IDC_SAVEPASSWORD );
348             if( hitem &&
349                 SendMessageW( hitem, BM_GETSTATE, 0, 0 ) &&
350                 WININET_GetAuthRealm( params->req->hdr.hInternet,
351                                       szRealm, sizeof szRealm/sizeof(WCHAR), FALSE ))
352             {
353                 WININET_GetSetPassword( hdlg, params->req->session->hostName, szRealm, TRUE );
354             }
355             WININET_SetAuthorization( params->req, username, password, FALSE );
356
357             EndDialog( hdlg, ERROR_INTERNET_FORCE_RETRY );
358             return TRUE;
359         }
360         if( wParam == IDCANCEL )
361         {
362             EndDialog( hdlg, 0 );
363             return TRUE;
364         }
365         break;
366     }
367     return FALSE;
368 }
369
370 /***********************************************************************
371  *         WININET_InvalidCertificateDialog
372  */
373 static INT_PTR WINAPI WININET_InvalidCertificateDialog(
374     HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
375 {
376     struct WININET_ErrorDlgParams *params;
377     HWND hitem;
378     WCHAR buf[1024];
379
380     if( uMsg == WM_INITDIALOG )
381     {
382         TRACE("WM_INITDIALOG (%08lx)\n", lParam);
383
384         /* save the parameter list */
385         params = (struct WININET_ErrorDlgParams*) lParam;
386         SetWindowLongPtrW( hdlg, GWLP_USERDATA, lParam );
387
388         switch( params->dwError )
389         {
390         case ERROR_INTERNET_INVALID_CA:
391             LoadStringW( WININET_hModule, IDS_CERT_CA_INVALID, buf, 1024 );
392             break;
393         case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
394             LoadStringW( WININET_hModule, IDS_CERT_DATE_INVALID, buf, 1024 );
395             break;
396         case ERROR_INTERNET_SEC_CERT_CN_INVALID:
397             LoadStringW( WININET_hModule, IDS_CERT_CN_INVALID, buf, 1024 );
398             break;
399         case ERROR_INTERNET_SEC_CERT_ERRORS:
400             /* FIXME: We should fetch information about the
401              * certificate here and show all the relevant errors.
402              */
403             LoadStringW( WININET_hModule, IDS_CERT_ERRORS, buf, 1024 );
404             break;
405         default:
406             FIXME( "No message for error %d\n", params->dwError );
407             buf[0] = '\0';
408         }
409
410         hitem = GetDlgItem( hdlg, IDC_CERT_ERROR );
411         SetWindowTextW( hitem, buf );
412
413         return TRUE;
414     }
415
416     params = (struct WININET_ErrorDlgParams*)
417                  GetWindowLongPtrW( hdlg, GWLP_USERDATA );
418
419     switch( uMsg )
420     {
421     case WM_COMMAND:
422         if( wParam == IDOK )
423         {
424             BOOL res = TRUE;
425
426             if( params->dwFlags & FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS )
427             {
428                 http_request_t *req = params->req;
429                 DWORD flags, size = sizeof(flags);
430
431                 InternetQueryOptionW( req->hdr.hInternet, INTERNET_OPTION_SECURITY_FLAGS, &flags, &size );
432                 switch( params->dwError )
433                 {
434                 case ERROR_INTERNET_INVALID_CA:
435                     flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
436                     break;
437                 case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
438                     flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
439                     break;
440                 case ERROR_INTERNET_SEC_CERT_CN_INVALID:
441                     flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
442                     break;
443                 case ERROR_INTERNET_SEC_CERT_REV_FAILED:
444                     flags |= SECURITY_FLAG_IGNORE_REVOCATION;
445                     break;
446                 case ERROR_INTERNET_SEC_CERT_ERRORS:
447                     if(flags & _SECURITY_FLAG_CERT_REV_FAILED)
448                         flags |= SECURITY_FLAG_IGNORE_REVOCATION;
449                     if(flags & _SECURITY_FLAG_CERT_INVALID_CA)
450                         flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
451                     if(flags & _SECURITY_FLAG_CERT_INVALID_CN)
452                         flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
453                     if(flags & _SECURITY_FLAG_CERT_INVALID_DATE)
454                         flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
455                     break;
456                 }
457                 /* FIXME: Use helper function */
458                 flags |= SECURITY_FLAG_SECURE;
459                 req->security_flags |= flags;
460                 if(req->netconn)
461                     req->netconn->security_flags |= flags;
462             }
463
464             EndDialog( hdlg, res ? ERROR_SUCCESS : ERROR_NOT_SUPPORTED );
465             return TRUE;
466         }
467         if( wParam == IDCANCEL )
468         {
469             TRACE("Pressed cancel.\n");
470
471             EndDialog( hdlg, ERROR_CANCELLED );
472             return TRUE;
473         }
474         break;
475     }
476
477     return FALSE;
478 }
479
480 /***********************************************************************
481  *         InternetErrorDlg
482  */
483 DWORD WINAPI InternetErrorDlg(HWND hWnd, HINTERNET hRequest,
484                  DWORD dwError, DWORD dwFlags, LPVOID* lppvData)
485 {
486     struct WININET_ErrorDlgParams params;
487     http_request_t *req = NULL;
488     DWORD res = ERROR_SUCCESS;
489
490     TRACE("%p %p %d %08x %p\n", hWnd, hRequest, dwError, dwFlags, lppvData);
491
492     if( !hWnd && !(dwFlags & FLAGS_ERROR_UI_FLAGS_NO_UI) )
493         return ERROR_INVALID_HANDLE;
494
495     if(hRequest) {
496         req = (http_request_t*)get_handle_object(hRequest);
497         if(!req)
498             return ERROR_INVALID_HANDLE;
499         if(req->hdr.htype != WH_HHTTPREQ)
500             return ERROR_SUCCESS; /* Yes, that was tested */
501     }
502
503     params.req = req;
504     params.hWnd = hWnd;
505     params.dwError = dwError;
506     params.dwFlags = dwFlags;
507     params.lppvData = lppvData;
508
509     switch( dwError )
510     {
511     case ERROR_SUCCESS:
512     case ERROR_INTERNET_INCORRECT_PASSWORD: {
513         if( !dwError && !(dwFlags & FLAGS_ERROR_UI_FILTER_FOR_ERRORS ) )
514             break;
515         if(!req)
516             return ERROR_INVALID_HANDLE;
517
518         switch(req->status_code) {
519         case HTTP_STATUS_PROXY_AUTH_REQ:
520             res = DialogBoxParamW( WININET_hModule, MAKEINTRESOURCEW( IDD_PROXYDLG ),
521                                    hWnd, WININET_ProxyPasswordDialog, (LPARAM) &params );
522             break;
523         case HTTP_STATUS_DENIED:
524             res = DialogBoxParamW( WININET_hModule, MAKEINTRESOURCEW( IDD_AUTHDLG ),
525                                     hWnd, WININET_PasswordDialog, (LPARAM) &params );
526             break;
527         default:
528             WARN("unhandled status %u\n", req->status_code);
529         }
530         break;
531     }
532     case ERROR_INTERNET_SEC_CERT_ERRORS:
533     case ERROR_INTERNET_SEC_CERT_CN_INVALID:
534     case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
535     case ERROR_INTERNET_INVALID_CA:
536     case ERROR_INTERNET_SEC_CERT_REV_FAILED:
537         if( dwFlags & FLAGS_ERROR_UI_FLAGS_NO_UI ) {
538             res = ERROR_CANCELLED;
539             break;
540         }
541         if(!req)
542             return ERROR_INVALID_HANDLE;
543
544
545         if( dwFlags & ~FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS )
546             FIXME("%08x contains unsupported flags.\n", dwFlags);
547
548         res = DialogBoxParamW( WININET_hModule, MAKEINTRESOURCEW( IDD_INVCERTDLG ),
549                                hWnd, WININET_InvalidCertificateDialog, (LPARAM) &params );
550         break;
551     case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
552     case ERROR_INTERNET_POST_IS_NON_SECURE:
553         FIXME("Need to display dialog for error %d\n", dwError);
554         res = ERROR_SUCCESS;
555         break;
556     default:
557         res = ERROR_NOT_SUPPORTED;
558     }
559
560     if(req)
561         WININET_Release(&req->hdr);
562     return res;
563 }
564
565 /***********************************************************************
566  *           InternetShowSecurityInfoByURLA (@)
567  */
568 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
569 {
570    FIXME("stub: %s %p\n", url, window);
571    return FALSE;
572 }
573
574 /***********************************************************************
575  *           InternetShowSecurityInfoByURLW (@)
576  */
577 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
578 {
579    FIXME("stub: %s %p\n", debugstr_w(url), window);
580    return FALSE;
581 }
582
583 /***********************************************************************
584  *           ShowX509EncodedCertificate (@)
585  */
586 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
587 {
588     PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
589         cert, len);
590     DWORD ret;
591
592     if (certContext)
593     {
594         CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
595
596         memset(&view, 0, sizeof(view));
597         view.hwndParent = parent;
598         view.pCertContext = certContext;
599         if (CryptUIDlgViewCertificateW(&view, NULL))
600             ret = ERROR_SUCCESS;
601         else
602             ret = GetLastError();
603         CertFreeCertificateContext(certContext);
604     }
605     else
606         ret = GetLastError();
607     return ret;
608 }