kernel32: Don't try to load an empty initialization file.
[wine] / dlls / urlmon / http.c
1 /*
2  * Copyright 2005 Jacek Caban
3  * Copyright 2007 Misha Koshelev
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 /*
21  * TODO:
22  * - Handle redirects as native.
23  */
24
25 #include "urlmon_main.h"
26 #include "wininet.h"
27
28 #include "wine/debug.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
31
32 typedef struct {
33     Protocol base;
34
35     const IInternetProtocolVtbl *lpInternetProtocolVtbl;
36     const IInternetPriorityVtbl *lpInternetPriorityVtbl;
37
38     BOOL https;
39     IHttpNegotiate *http_negotiate;
40     LPWSTR full_header;
41
42     LONG ref;
43 } HttpProtocol;
44
45 #define PROTOCOL(x)  ((IInternetProtocol*)  &(x)->lpInternetProtocolVtbl)
46 #define PRIORITY(x)  ((IInternetPriority*)  &(x)->lpInternetPriorityVtbl)
47
48 /* Default headers from native */
49 static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
50                                    ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
51
52 static LPWSTR query_http_info(HttpProtocol *This, DWORD option)
53 {
54     LPWSTR ret = NULL;
55     DWORD len = 0;
56     BOOL res;
57
58     res = HttpQueryInfoW(This->base.request, option, NULL, &len, NULL);
59     if (!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
60         ret = heap_alloc(len);
61         res = HttpQueryInfoW(This->base.request, option, ret, &len, NULL);
62     }
63     if(!res) {
64         TRACE("HttpQueryInfoW(%d) failed: %08x\n", option, GetLastError());
65         heap_free(ret);
66         return NULL;
67     }
68
69     return ret;
70 }
71
72 #define ASYNCPROTOCOL_THIS(iface) DEFINE_THIS2(HttpProtocol, base, iface)
73
74 static HRESULT HttpProtocol_open_request(Protocol *prot, LPCWSTR url, DWORD request_flags,
75                                          IInternetBindInfo *bind_info)
76 {
77     HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
78     LPWSTR addl_header = NULL, post_cookie = NULL, optional = NULL;
79     IServiceProvider *service_provider = NULL;
80     IHttpNegotiate2 *http_negotiate2 = NULL;
81     LPWSTR host, user, pass, path;
82     LPOLESTR accept_mimes[257];
83     URL_COMPONENTSW url_comp;
84     BYTE security_id[512];
85     DWORD len = 0;
86     ULONG num = 0;
87     BOOL res;
88     HRESULT hres;
89
90     static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
91         {{'G','E','T',0},
92          {'P','O','S','T',0},
93          {'P','U','T',0}};
94
95     memset(&url_comp, 0, sizeof(url_comp));
96     url_comp.dwStructSize = sizeof(url_comp);
97     url_comp.dwSchemeLength = url_comp.dwHostNameLength = url_comp.dwUrlPathLength =
98         url_comp.dwUserNameLength = url_comp.dwPasswordLength = 1;
99     if (!InternetCrackUrlW(url, 0, 0, &url_comp))
100         return MK_E_SYNTAX;
101
102     if(!url_comp.nPort)
103         url_comp.nPort = This->https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
104
105     host = heap_strndupW(url_comp.lpszHostName, url_comp.dwHostNameLength);
106     user = heap_strndupW(url_comp.lpszUserName, url_comp.dwUserNameLength);
107     pass = heap_strndupW(url_comp.lpszPassword, url_comp.dwPasswordLength);
108     This->base.connection = InternetConnectW(This->base.internet, host, url_comp.nPort, user, pass,
109             INTERNET_SERVICE_HTTP, This->https ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)&This->base);
110     heap_free(pass);
111     heap_free(user);
112     heap_free(host);
113     if(!This->base.connection) {
114         WARN("InternetConnect failed: %d\n", GetLastError());
115         return INET_E_CANNOT_CONNECT;
116     }
117
118     num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
119     hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_ACCEPT_MIMES, accept_mimes, num, &num);
120     if(hres != S_OK) {
121         WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
122         return INET_E_NO_VALID_MEDIA;
123     }
124     accept_mimes[num] = 0;
125
126     path = heap_strndupW(url_comp.lpszUrlPath, url_comp.dwUrlPathLength);
127     if(This->https)
128         request_flags |= INTERNET_FLAG_SECURE;
129     This->base.request = HttpOpenRequestW(This->base.connection,
130             This->base.bind_info.dwBindVerb < BINDVERB_CUSTOM
131                 ? wszBindVerb[This->base.bind_info.dwBindVerb] : This->base.bind_info.szCustomVerb,
132             path, NULL, NULL, (LPCWSTR *)accept_mimes, request_flags, (DWORD_PTR)&This->base);
133     heap_free(path);
134     while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) && accept_mimes[num])
135         CoTaskMemFree(accept_mimes[num++]);
136     if (!This->base.request) {
137         WARN("HttpOpenRequest failed: %d\n", GetLastError());
138         return INET_E_RESOURCE_NOT_FOUND;
139     }
140
141     hres = IInternetProtocolSink_QueryInterface(This->base.protocol_sink, &IID_IServiceProvider,
142             (void **)&service_provider);
143     if (hres != S_OK) {
144         WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
145         return hres;
146     }
147
148     hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
149             &IID_IHttpNegotiate, (void **)&This->http_negotiate);
150     if (hres != S_OK) {
151         WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
152         return hres;
153     }
154
155     hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, url, wszHeaders,
156             0, &addl_header);
157     if(hres != S_OK) {
158         WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
159         IServiceProvider_Release(service_provider);
160         return hres;
161     }
162
163     if(addl_header) {
164         int len_addl_header = strlenW(addl_header);
165
166         This->full_header = heap_alloc(len_addl_header*sizeof(WCHAR)+sizeof(wszHeaders));
167
168         lstrcpyW(This->full_header, addl_header);
169         lstrcpyW(&This->full_header[len_addl_header], wszHeaders);
170         CoTaskMemFree(addl_header);
171     }else {
172         This->full_header = (LPWSTR)wszHeaders;
173     }
174
175     hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
176             &IID_IHttpNegotiate2, (void **)&http_negotiate2);
177     IServiceProvider_Release(service_provider);
178     if(hres != S_OK) {
179         WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
180         /* No goto done as per native */
181     }else {
182         len = sizeof(security_id)/sizeof(security_id[0]);
183         hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
184         IHttpNegotiate2_Release(http_negotiate2);
185         if (hres != S_OK)
186             WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
187     }
188
189     /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
190
191     if(This->base.bind_info.dwBindVerb == BINDVERB_POST) {
192         num = 0;
193         hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_POST_COOKIE, &post_cookie, 1, &num);
194         if(hres == S_OK && num) {
195             if(!InternetSetOptionW(This->base.request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
196                                    post_cookie, lstrlenW(post_cookie)))
197                 WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n", GetLastError());
198             CoTaskMemFree(post_cookie);
199         }
200     }
201
202     if(This->base.bind_info.dwBindVerb != BINDVERB_GET) {
203         /* Native does not use GlobalLock/GlobalUnlock, so we won't either */
204         if (This->base.bind_info.stgmedData.tymed != TYMED_HGLOBAL)
205             WARN("Expected This->base.bind_info.stgmedData.tymed to be TYMED_HGLOBAL, not %d\n",
206                  This->base.bind_info.stgmedData.tymed);
207         else
208             optional = (LPWSTR)This->base.bind_info.stgmedData.u.hGlobal;
209     }
210
211     res = HttpSendRequestW(This->base.request, This->full_header, lstrlenW(This->full_header),
212             optional, optional ? This->base.bind_info.cbstgmedData : 0);
213     if(!res && GetLastError() != ERROR_IO_PENDING) {
214         WARN("HttpSendRequest failed: %d\n", GetLastError());
215         return INET_E_DOWNLOAD_FAILURE;
216     }
217
218     return S_OK;
219 }
220
221 static HRESULT HttpProtocol_start_downloading(Protocol *prot)
222 {
223     HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
224     LPWSTR content_type = 0, content_length = 0;
225     DWORD len = sizeof(DWORD);
226     DWORD status_code;
227     BOOL res;
228     HRESULT hres;
229
230     static const WCHAR wszDefaultContentType[] =
231         {'t','e','x','t','/','h','t','m','l',0};
232
233     if(!This->http_negotiate) {
234         WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
235         return S_OK;
236     }
237
238     res = HttpQueryInfoW(This->base.request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
239             &status_code, &len, NULL);
240     if(res) {
241         LPWSTR response_headers = query_http_info(This, HTTP_QUERY_RAW_HEADERS_CRLF);
242         if(response_headers) {
243             hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code, response_headers,
244                     NULL, NULL);
245             heap_free(response_headers);
246             if (hres != S_OK) {
247                 WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
248                 return S_OK;
249             }
250         }
251     }else {
252         WARN("HttpQueryInfo failed: %d\n", GetLastError());
253     }
254
255     if(This->https)
256         IInternetProtocolSink_ReportProgress(This->base.protocol_sink, BINDSTATUS_ACCEPTRANGES, NULL);
257
258     content_type = query_http_info(This, HTTP_QUERY_CONTENT_TYPE);
259     if(content_type) {
260         /* remove the charset, if present */
261         LPWSTR p = strchrW(content_type, ';');
262         if (p) *p = '\0';
263
264         IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
265                 (This->base.bindf & BINDF_FROMURLMON)
266                  ? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE,
267                  content_type);
268         heap_free(content_type);
269     }else {
270         WARN("HttpQueryInfo failed: %d\n", GetLastError());
271         IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
272                  (This->base.bindf & BINDF_FROMURLMON)
273                   ? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE,
274                   wszDefaultContentType);
275     }
276
277     content_length = query_http_info(This, HTTP_QUERY_CONTENT_LENGTH);
278     if(content_length) {
279         This->base.content_length = atoiW(content_length);
280         heap_free(content_length);
281     }
282
283     return S_OK;
284 }
285
286 static void HttpProtocol_close_connection(Protocol *prot)
287 {
288     HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
289
290     if(This->http_negotiate) {
291         IHttpNegotiate_Release(This->http_negotiate);
292         This->http_negotiate = 0;
293     }
294
295     if(This->full_header) {
296         if(This->full_header != wszHeaders)
297             heap_free(This->full_header);
298         This->full_header = 0;
299     }
300 }
301
302 #undef ASYNCPROTOCOL_THIS
303
304 static const ProtocolVtbl AsyncProtocolVtbl = {
305     HttpProtocol_open_request,
306     HttpProtocol_start_downloading,
307     HttpProtocol_close_connection
308 };
309
310 #define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)
311
312 static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
313 {
314     HttpProtocol *This = PROTOCOL_THIS(iface);
315
316     *ppv = NULL;
317     if(IsEqualGUID(&IID_IUnknown, riid)) {
318         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
319         *ppv = PROTOCOL(This);
320     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
321         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
322         *ppv = PROTOCOL(This);
323     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
324         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
325         *ppv = PROTOCOL(This);
326     }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
327         TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
328         *ppv = PRIORITY(This);
329     }
330
331     if(*ppv) {
332         IInternetProtocol_AddRef(iface);
333         return S_OK;
334     }
335
336     WARN("not supported interface %s\n", debugstr_guid(riid));
337     return E_NOINTERFACE;
338 }
339
340 static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocol *iface)
341 {
342     HttpProtocol *This = PROTOCOL_THIS(iface);
343     LONG ref = InterlockedIncrement(&This->ref);
344     TRACE("(%p) ref=%d\n", This, ref);
345     return ref;
346 }
347
348 static ULONG WINAPI HttpProtocol_Release(IInternetProtocol *iface)
349 {
350     HttpProtocol *This = PROTOCOL_THIS(iface);
351     LONG ref = InterlockedDecrement(&This->ref);
352
353     TRACE("(%p) ref=%d\n", This, ref);
354
355     if(!ref) {
356         protocol_close_connection(&This->base);
357         heap_free(This);
358
359         URLMON_UnlockModule();
360     }
361
362     return ref;
363 }
364
365 static HRESULT WINAPI HttpProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
366         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
367         DWORD grfPI, DWORD dwReserved)
368 {
369     HttpProtocol *This = PROTOCOL_THIS(iface);
370
371     static const WCHAR httpW[] = {'h','t','t','p',':'};
372     static const WCHAR httpsW[] = {'h','t','t','p','s',':'};
373
374     TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
375             pOIBindInfo, grfPI, dwReserved);
376
377     if(This->https
378         ? strncmpW(szUrl, httpsW, sizeof(httpsW)/sizeof(WCHAR))
379         : strncmpW(szUrl, httpW, sizeof(httpW)/sizeof(WCHAR)))
380         return MK_E_SYNTAX;
381
382     return protocol_start(&This->base, PROTOCOL(This), szUrl, pOIProtSink, pOIBindInfo);
383 }
384
385 static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
386 {
387     HttpProtocol *This = PROTOCOL_THIS(iface);
388
389     TRACE("(%p)->(%p)\n", This, pProtocolData);
390
391     return protocol_continue(&This->base, pProtocolData);
392 }
393
394 static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
395         DWORD dwOptions)
396 {
397     HttpProtocol *This = PROTOCOL_THIS(iface);
398     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
399     return E_NOTIMPL;
400 }
401
402 static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
403 {
404     HttpProtocol *This = PROTOCOL_THIS(iface);
405
406     TRACE("(%p)->(%08x)\n", This, dwOptions);
407
408     protocol_close_connection(&This->base);
409     return S_OK;
410 }
411
412 static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocol *iface)
413 {
414     HttpProtocol *This = PROTOCOL_THIS(iface);
415     FIXME("(%p)\n", This);
416     return E_NOTIMPL;
417 }
418
419 static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocol *iface)
420 {
421     HttpProtocol *This = PROTOCOL_THIS(iface);
422     FIXME("(%p)\n", This);
423     return E_NOTIMPL;
424 }
425
426 static HRESULT WINAPI HttpProtocol_Read(IInternetProtocol *iface, void *pv,
427         ULONG cb, ULONG *pcbRead)
428 {
429     HttpProtocol *This = PROTOCOL_THIS(iface);
430
431     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
432
433     return protocol_read(&This->base, pv, cb, pcbRead);
434 }
435
436 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
437         DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
438 {
439     HttpProtocol *This = PROTOCOL_THIS(iface);
440     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
441     return E_NOTIMPL;
442 }
443
444 static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
445 {
446     HttpProtocol *This = PROTOCOL_THIS(iface);
447
448     TRACE("(%p)->(%08x)\n", This, dwOptions);
449
450     return protocol_lock_request(&This->base);
451 }
452
453 static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocol *iface)
454 {
455     HttpProtocol *This = PROTOCOL_THIS(iface);
456
457     TRACE("(%p)\n", This);
458
459     return protocol_unlock_request(&This->base);
460 }
461
462 #undef PROTOCOL_THIS
463
464 static const IInternetProtocolVtbl HttpProtocolVtbl = {
465     HttpProtocol_QueryInterface,
466     HttpProtocol_AddRef,
467     HttpProtocol_Release,
468     HttpProtocol_Start,
469     HttpProtocol_Continue,
470     HttpProtocol_Abort,
471     HttpProtocol_Terminate,
472     HttpProtocol_Suspend,
473     HttpProtocol_Resume,
474     HttpProtocol_Read,
475     HttpProtocol_Seek,
476     HttpProtocol_LockRequest,
477     HttpProtocol_UnlockRequest
478 };
479
480 #define PRIORITY_THIS(iface) DEFINE_THIS(HttpProtocol, InternetPriority, iface)
481
482 static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv)
483 {
484     HttpProtocol *This = PRIORITY_THIS(iface);
485     return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
486 }
487
488 static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface)
489 {
490     HttpProtocol *This = PRIORITY_THIS(iface);
491     return IInternetProtocol_AddRef(PROTOCOL(This));
492 }
493
494 static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface)
495 {
496     HttpProtocol *This = PRIORITY_THIS(iface);
497     return IInternetProtocol_Release(PROTOCOL(This));
498 }
499
500 static HRESULT WINAPI HttpPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
501 {
502     HttpProtocol *This = PRIORITY_THIS(iface);
503
504     TRACE("(%p)->(%d)\n", This, nPriority);
505
506     This->base.priority = nPriority;
507     return S_OK;
508 }
509
510 static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
511 {
512     HttpProtocol *This = PRIORITY_THIS(iface);
513
514     TRACE("(%p)->(%p)\n", This, pnPriority);
515
516     *pnPriority = This->base.priority;
517     return S_OK;
518 }
519
520 #undef PRIORITY_THIS
521
522 static const IInternetPriorityVtbl HttpPriorityVtbl = {
523     HttpPriority_QueryInterface,
524     HttpPriority_AddRef,
525     HttpPriority_Release,
526     HttpPriority_SetPriority,
527     HttpPriority_GetPriority
528 };
529
530 static HRESULT create_http_protocol(BOOL https, void **ppobj)
531 {
532     HttpProtocol *ret;
533
534     ret = heap_alloc_zero(sizeof(HttpProtocol));
535     if(!ret)
536         return E_OUTOFMEMORY;
537
538     ret->base.vtbl = &AsyncProtocolVtbl;
539     ret->lpInternetProtocolVtbl = &HttpProtocolVtbl;
540     ret->lpInternetPriorityVtbl = &HttpPriorityVtbl;
541
542     ret->https = https;
543     ret->ref = 1;
544
545     *ppobj = PROTOCOL(ret);
546     
547     URLMON_LockModule();
548     return S_OK;
549 }
550
551 HRESULT HttpProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
552 {
553     TRACE("(%p %p)\n", pUnkOuter, ppobj);
554
555     return create_http_protocol(FALSE, ppobj);
556 }
557
558 HRESULT HttpSProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
559 {
560     TRACE("(%p %p)\n", pUnkOuter, ppobj);
561
562     return create_http_protocol(TRUE, ppobj);
563 }