comctl32/listview: Free ID array when removing all items.
[wine] / dlls / winhttp / session.c
1 /*
2  * Copyright 2008 Hans Leidekker for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20 #include "wine/port.h"
21 #include "wine/debug.h"
22
23 #include <stdarg.h>
24 #include <stdlib.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winhttp.h"
29 #include "wincrypt.h"
30 #include "winreg.h"
31
32 #include "winhttp_private.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
35
36 #define DEFAULT_CONNECT_TIMEOUT     60000
37 #define DEFAULT_SEND_TIMEOUT        30000
38 #define DEFAULT_RECEIVE_TIMEOUT     30000
39
40 void set_last_error( DWORD error )
41 {
42     /* FIXME */
43     SetLastError( error );
44 }
45
46 DWORD get_last_error( void )
47 {
48     /* FIXME */
49     return GetLastError();
50 }
51
52 void send_callback( object_header_t *hdr, DWORD status, LPVOID info, DWORD buflen )
53 {
54     TRACE("%p, 0x%08x, %p, %u\n", hdr, status, info, buflen);
55
56     if (hdr->callback && (hdr->notify_mask & status)) hdr->callback( hdr->handle, hdr->context, status, info, buflen );
57 }
58
59 /***********************************************************************
60  *          WinHttpCheckPlatform (winhttp.@)
61  */
62 BOOL WINAPI WinHttpCheckPlatform( void )
63 {
64     TRACE("\n");
65     return TRUE;
66 }
67
68 /***********************************************************************
69  *          session_destroy (internal)
70  */
71 static void session_destroy( object_header_t *hdr )
72 {
73     session_t *session = (session_t *)hdr;
74     struct list *item, *next;
75     domain_t *domain;
76
77     TRACE("%p\n", session);
78
79     LIST_FOR_EACH_SAFE( item, next, &session->cookie_cache )
80     {
81         domain = LIST_ENTRY( item, domain_t, entry );
82         delete_domain( domain );
83     }
84     heap_free( session->agent );
85     heap_free( session->proxy_server );
86     heap_free( session->proxy_bypass );
87     heap_free( session->proxy_username );
88     heap_free( session->proxy_password );
89     heap_free( session );
90 }
91
92 static BOOL session_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen )
93 {
94     switch (option)
95     {
96     case WINHTTP_OPTION_REDIRECT_POLICY:
97     {
98         if (!buffer || *buflen < sizeof(DWORD))
99         {
100             *buflen = sizeof(DWORD);
101             set_last_error( ERROR_INSUFFICIENT_BUFFER );
102             return FALSE;
103         }
104
105         *(DWORD *)buffer = hdr->redirect_policy;
106         *buflen = sizeof(DWORD);
107         return TRUE;
108     }
109     default:
110         FIXME("unimplemented option %u\n", option);
111         set_last_error( ERROR_INVALID_PARAMETER );
112         return FALSE;
113     }
114 }
115
116 static BOOL session_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen )
117 {
118     switch (option)
119     {
120     case WINHTTP_OPTION_PROXY:
121     {
122         WINHTTP_PROXY_INFO *pi = buffer;
123
124         FIXME("%u %s %s\n", pi->dwAccessType, debugstr_w(pi->lpszProxy), debugstr_w(pi->lpszProxyBypass));
125         return TRUE;
126     }
127     case WINHTTP_OPTION_REDIRECT_POLICY:
128     {
129         DWORD policy;
130
131         if (buflen != sizeof(policy))
132         {
133             set_last_error( ERROR_INSUFFICIENT_BUFFER );
134             return FALSE;
135         }
136
137         policy = *(DWORD *)buffer;
138         TRACE("0x%x\n", policy);
139         hdr->redirect_policy = policy;
140         return TRUE;
141     }
142     case WINHTTP_OPTION_DISABLE_FEATURE:
143         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
144         return FALSE;
145     default:
146         FIXME("unimplemented option %u\n", option);
147         set_last_error( ERROR_INVALID_PARAMETER );
148         return FALSE;
149     }
150 }
151
152 static const object_vtbl_t session_vtbl =
153 {
154     session_destroy,
155     session_query_option,
156     session_set_option
157 };
158
159 /***********************************************************************
160  *          WinHttpOpen (winhttp.@)
161  */
162 HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWSTR bypass, DWORD flags )
163 {
164     session_t *session;
165     HINTERNET handle = NULL;
166
167     TRACE("%s, %u, %s, %s, 0x%08x\n", debugstr_w(agent), access, debugstr_w(proxy), debugstr_w(bypass), flags);
168
169     if (!(session = heap_alloc_zero( sizeof(session_t) ))) return NULL;
170
171     session->hdr.type = WINHTTP_HANDLE_TYPE_SESSION;
172     session->hdr.vtbl = &session_vtbl;
173     session->hdr.flags = flags;
174     session->hdr.refs = 1;
175     session->hdr.redirect_policy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP;
176     list_init( &session->cookie_cache );
177
178     if (agent && !(session->agent = strdupW( agent ))) goto end;
179     if (access == WINHTTP_ACCESS_TYPE_DEFAULT_PROXY)
180     {
181         WINHTTP_PROXY_INFO info;
182
183         WinHttpGetDefaultProxyConfiguration( &info );
184         session->access = info.dwAccessType;
185         if (info.lpszProxy && !(session->proxy_server = strdupW( info.lpszProxy )))
186         {
187             GlobalFree( (LPWSTR)info.lpszProxy );
188             GlobalFree( (LPWSTR)info.lpszProxyBypass );
189             goto end;
190         }
191         if (info.lpszProxyBypass && !(session->proxy_bypass = strdupW( info.lpszProxyBypass )))
192         {
193             GlobalFree( (LPWSTR)info.lpszProxy );
194             GlobalFree( (LPWSTR)info.lpszProxyBypass );
195             goto end;
196         }
197     }
198     else if (access == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
199     {
200         session->access = access;
201         if (proxy && !(session->proxy_server = strdupW( proxy ))) goto end;
202         if (bypass && !(session->proxy_bypass = strdupW( bypass ))) goto end;
203     }
204
205     if (!(handle = alloc_handle( &session->hdr ))) goto end;
206     session->hdr.handle = handle;
207
208 end:
209     release_object( &session->hdr );
210     TRACE("returning %p\n", handle);
211     return handle;
212 }
213
214 /***********************************************************************
215  *          connect_destroy (internal)
216  */
217 static void connect_destroy( object_header_t *hdr )
218 {
219     connect_t *connect = (connect_t *)hdr;
220
221     TRACE("%p\n", connect);
222
223     release_object( &connect->session->hdr );
224
225     heap_free( connect->hostname );
226     heap_free( connect->servername );
227     heap_free( connect->username );
228     heap_free( connect->password );
229     heap_free( connect );
230 }
231
232 static const object_vtbl_t connect_vtbl =
233 {
234     connect_destroy,
235     NULL,
236     NULL
237 };
238
239 static BOOL domain_matches(LPCWSTR server, LPCWSTR domain)
240 {
241     static const WCHAR localW[] = { '<','l','o','c','a','l','>',0 };
242     BOOL ret = FALSE;
243
244     if (!strcmpiW( domain, localW ) && !strchrW( server, '.' ))
245         ret = TRUE;
246     else if (*domain == '*')
247     {
248         if (domain[1] == '.')
249         {
250             LPCWSTR dot;
251
252             /* For a hostname to match a wildcard, the last domain must match
253              * the wildcard exactly.  E.g. if the wildcard is *.a.b, and the
254              * hostname is www.foo.a.b, it matches, but a.b does not.
255              */
256             dot = strchrW( server, '.' );
257             if (dot)
258             {
259                 int len = strlenW( dot + 1 );
260
261                 if (len > strlenW( domain + 2 ))
262                 {
263                     LPCWSTR ptr;
264
265                     /* The server's domain is longer than the wildcard, so it
266                      * could be a subdomain.  Compare the last portion of the
267                      * server's domain.
268                      */
269                     ptr = dot + len + 1 - strlenW( domain + 2 );
270                     if (!strcmpiW( ptr, domain + 2 ))
271                     {
272                         /* This is only a match if the preceding character is
273                          * a '.', i.e. that it is a matching domain.  E.g.
274                          * if domain is '*.b.c' and server is 'www.ab.c' they
275                          * do not match.
276                          */
277                         ret = *(ptr - 1) == '.';
278                     }
279                 }
280                 else
281                     ret = !strcmpiW( dot + 1, domain + 2 );
282             }
283         }
284     }
285     else
286         ret = !strcmpiW( server, domain );
287     return ret;
288 }
289
290 /* Matches INTERNET_MAX_HOST_NAME_LENGTH in wininet.h, also RFC 1035 */
291 #define MAX_HOST_NAME_LENGTH 256
292
293 static BOOL should_bypass_proxy(session_t *session, LPCWSTR server)
294 {
295     LPCWSTR ptr;
296     BOOL ret = FALSE;
297
298     if (!session->proxy_bypass) return FALSE;
299     ptr = session->proxy_bypass;
300     do {
301         LPCWSTR tmp = ptr;
302
303         ptr = strchrW( ptr, ';' );
304         if (!ptr)
305             ptr = strchrW( tmp, ' ' );
306         if (ptr)
307         {
308             if (ptr - tmp < MAX_HOST_NAME_LENGTH)
309             {
310                 WCHAR domain[MAX_HOST_NAME_LENGTH];
311
312                 memcpy( domain, tmp, (ptr - tmp) * sizeof(WCHAR) );
313                 domain[ptr - tmp] = 0;
314                 ret = domain_matches( server, domain );
315             }
316             ptr += 1;
317         }
318         else if (*tmp)
319             ret = domain_matches( server, tmp );
320     } while (ptr && !ret);
321     return ret;
322 }
323
324 BOOL set_server_for_hostname( connect_t *connect, LPCWSTR server, INTERNET_PORT port )
325 {
326     session_t *session = connect->session;
327     BOOL ret = TRUE;
328
329     if (session->proxy_server && !should_bypass_proxy(session, server))
330     {
331         LPCWSTR colon;
332
333         if ((colon = strchrW( session->proxy_server, ':' )))
334         {
335             if (!connect->servername || strncmpiW( connect->servername,
336                 session->proxy_server, colon - session->proxy_server - 1 ))
337             {
338                 heap_free( connect->servername );
339                 if (!(connect->servername = heap_alloc(
340                     (colon - session->proxy_server + 1) * sizeof(WCHAR) )))
341                 {
342                     ret = FALSE;
343                     goto end;
344                 }
345                 memcpy( connect->servername, session->proxy_server,
346                     (colon - session->proxy_server) * sizeof(WCHAR) );
347                 connect->servername[colon - session->proxy_server] = 0;
348                 if (*(colon + 1))
349                     connect->serverport = atoiW( colon + 1 );
350                 else
351                     connect->serverport = INTERNET_DEFAULT_PORT;
352             }
353         }
354         else
355         {
356             if (!connect->servername || strcmpiW( connect->servername,
357                 session->proxy_server ))
358             {
359                 heap_free( connect->servername );
360                 if (!(connect->servername = strdupW( session->proxy_server )))
361                 {
362                     ret = FALSE;
363                     goto end;
364                 }
365                 connect->serverport = INTERNET_DEFAULT_PORT;
366             }
367         }
368     }
369     else if (server)
370     {
371         heap_free( connect->servername );
372         if (!(connect->servername = strdupW( server )))
373         {
374             ret = FALSE;
375             goto end;
376         }
377         connect->serverport = port;
378     }
379 end:
380     return ret;
381 }
382
383 /***********************************************************************
384  *          WinHttpConnect (winhttp.@)
385  */
386 HINTERNET WINAPI WinHttpConnect( HINTERNET hsession, LPCWSTR server, INTERNET_PORT port, DWORD reserved )
387 {
388     connect_t *connect;
389     session_t *session;
390     HINTERNET hconnect = NULL;
391
392     TRACE("%p, %s, %u, %x\n", hsession, debugstr_w(server), port, reserved);
393
394     if (!server)
395     {
396         set_last_error( ERROR_INVALID_PARAMETER );
397         return NULL;
398     }
399     if (!(session = (session_t *)grab_object( hsession )))
400     {
401         set_last_error( ERROR_INVALID_HANDLE );
402         return NULL;
403     }
404     if (session->hdr.type != WINHTTP_HANDLE_TYPE_SESSION)
405     {
406         release_object( &session->hdr );
407         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
408         return NULL;
409     }
410     if (!(connect = heap_alloc_zero( sizeof(connect_t) )))
411     {
412         release_object( &session->hdr );
413         return NULL;
414     }
415     connect->hdr.type = WINHTTP_HANDLE_TYPE_CONNECT;
416     connect->hdr.vtbl = &connect_vtbl;
417     connect->hdr.refs = 1;
418     connect->hdr.flags = session->hdr.flags;
419     connect->hdr.callback = session->hdr.callback;
420     connect->hdr.notify_mask = session->hdr.notify_mask;
421     connect->hdr.context = session->hdr.context;
422
423     addref_object( &session->hdr );
424     connect->session = session;
425     list_add_head( &session->hdr.children, &connect->hdr.entry );
426
427     if (server && !(connect->hostname = strdupW( server ))) goto end;
428     connect->hostport = port;
429
430     if (!set_server_for_hostname( connect, server, port ))
431         goto end;
432
433     if (!(hconnect = alloc_handle( &connect->hdr ))) goto end;
434     connect->hdr.handle = hconnect;
435
436     send_callback( &session->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hconnect, sizeof(hconnect) );
437
438 end:
439     release_object( &connect->hdr );
440
441     TRACE("returning %p\n", hconnect);
442     return hconnect;
443 }
444
445 /***********************************************************************
446  *          request_destroy (internal)
447  */
448 static void request_destroy( object_header_t *hdr )
449 {
450     request_t *request = (request_t *)hdr;
451     DWORD i;
452
453     TRACE("%p\n", request);
454
455     release_object( &request->connect->hdr );
456
457     heap_free( request->verb );
458     heap_free( request->path );
459     heap_free( request->version );
460     heap_free( request->raw_headers );
461     heap_free( request->status_text );
462     for (i = 0; i < request->num_headers; i++)
463     {
464         heap_free( request->headers[i].field );
465         heap_free( request->headers[i].value );
466     }
467     heap_free( request->headers );
468     heap_free( request );
469 }
470
471 static BOOL request_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen )
472 {
473     switch (option)
474     {
475     case WINHTTP_OPTION_SECURITY_FLAGS:
476     {
477         DWORD flags;
478
479         if (!buffer || *buflen < sizeof(flags))
480         {
481             *buflen = sizeof(flags);
482             set_last_error( ERROR_INSUFFICIENT_BUFFER );
483             return FALSE;
484         }
485
486         flags = 0;
487         if (hdr->flags & WINHTTP_FLAG_SECURE) flags |= SECURITY_FLAG_SECURE;
488         *(DWORD *)buffer = flags;
489         *buflen = sizeof(flags);
490         return TRUE;
491     }
492     case WINHTTP_OPTION_SERVER_CERT_CONTEXT:
493     {
494         const CERT_CONTEXT *cert;
495         request_t *request = (request_t *)hdr;
496
497         if (!buffer || *buflen < sizeof(cert))
498         {
499             *buflen = sizeof(cert);
500             set_last_error( ERROR_INSUFFICIENT_BUFFER );
501             return FALSE;
502         }
503
504         if (!(cert = netconn_get_certificate( &request->netconn ))) return FALSE;
505         *(CERT_CONTEXT **)buffer = (CERT_CONTEXT *)cert;
506         *buflen = sizeof(cert);
507         return TRUE;
508     }
509     case WINHTTP_OPTION_SECURITY_KEY_BITNESS:
510     {
511         if (!buffer || *buflen < sizeof(DWORD))
512         {
513             *buflen = sizeof(DWORD);
514             set_last_error( ERROR_INSUFFICIENT_BUFFER );
515             return FALSE;
516         }
517
518         *(DWORD *)buffer = 128; /* FIXME */
519         *buflen = sizeof(DWORD);
520         return TRUE;
521     }
522     default:
523         FIXME("unimplemented option %u\n", option);
524         set_last_error( ERROR_INVALID_PARAMETER );
525         return FALSE;
526     }
527 }
528
529 static BOOL request_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen )
530 {
531     switch (option)
532     {
533     case WINHTTP_OPTION_PROXY:
534     {
535         WINHTTP_PROXY_INFO *pi = buffer;
536
537         FIXME("%u %s %s\n", pi->dwAccessType, debugstr_w(pi->lpszProxy), debugstr_w(pi->lpszProxyBypass));
538         return TRUE;
539     }
540     case WINHTTP_OPTION_DISABLE_FEATURE:
541     {
542         DWORD disable;
543
544         if (buflen != sizeof(DWORD))
545         {
546             set_last_error( ERROR_INSUFFICIENT_BUFFER );
547             return FALSE;
548         }
549
550         disable = *(DWORD *)buffer;
551         TRACE("0x%x\n", disable);
552         hdr->disable_flags |= disable;
553         return TRUE;
554     }
555     case WINHTTP_OPTION_AUTOLOGON_POLICY:
556     {
557         DWORD policy;
558
559         if (buflen != sizeof(DWORD))
560         {
561             set_last_error( ERROR_INSUFFICIENT_BUFFER );
562             return FALSE;
563         }
564
565         policy = *(DWORD *)buffer;
566         TRACE("0x%x\n", policy);
567         hdr->logon_policy = policy;
568         return TRUE;
569     }
570     case WINHTTP_OPTION_REDIRECT_POLICY:
571     {
572         DWORD policy;
573
574         if (buflen != sizeof(DWORD))
575         {
576             set_last_error( ERROR_INSUFFICIENT_BUFFER );
577             return FALSE;
578         }
579
580         policy = *(DWORD *)buffer;
581         TRACE("0x%x\n", policy);
582         hdr->redirect_policy = policy;
583         return TRUE;
584     }
585     case WINHTTP_OPTION_SECURITY_FLAGS:
586         FIXME("WINHTTP_OPTION_SECURITY_FLAGS unimplemented (%08x)\n",
587               *(DWORD *)buffer);
588         return TRUE;
589     default:
590         FIXME("unimplemented option %u\n", option);
591         set_last_error( ERROR_INVALID_PARAMETER );
592         return TRUE;
593     }
594 }
595
596 static const object_vtbl_t request_vtbl =
597 {
598     request_destroy,
599     request_query_option,
600     request_set_option
601 };
602
603 /***********************************************************************
604  *          WinHttpOpenRequest (winhttp.@)
605  */
606 HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR object, LPCWSTR version,
607                                      LPCWSTR referrer, LPCWSTR *types, DWORD flags )
608 {
609     request_t *request;
610     connect_t *connect;
611     HINTERNET hrequest = NULL;
612
613     TRACE("%p, %s, %s, %s, %s, %p, 0x%08x\n", hconnect, debugstr_w(verb), debugstr_w(object),
614           debugstr_w(version), debugstr_w(referrer), types, flags);
615
616     if (!(connect = (connect_t *)grab_object( hconnect )))
617     {
618         set_last_error( ERROR_INVALID_HANDLE );
619         return NULL;
620     }
621     if (connect->hdr.type != WINHTTP_HANDLE_TYPE_CONNECT)
622     {
623         release_object( &connect->hdr );
624         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
625         return NULL;
626     }
627     if (!(request = heap_alloc_zero( sizeof(request_t) )))
628     {
629         release_object( &connect->hdr );
630         return NULL;
631     }
632     request->hdr.type = WINHTTP_HANDLE_TYPE_REQUEST;
633     request->hdr.vtbl = &request_vtbl;
634     request->hdr.refs = 1;
635     request->hdr.flags = flags;
636     request->hdr.callback = connect->hdr.callback;
637     request->hdr.notify_mask = connect->hdr.notify_mask;
638     request->hdr.context = connect->hdr.context;
639
640     addref_object( &connect->hdr );
641     request->connect = connect;
642     list_add_head( &connect->hdr.children, &request->hdr.entry );
643
644     if (!netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE )) goto end;
645     request->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
646     request->send_timeout = DEFAULT_SEND_TIMEOUT;
647     request->recv_timeout = DEFAULT_RECEIVE_TIMEOUT;
648
649     if (!verb || !verb[0]) verb = getW;
650     if (!(request->verb = strdupW( verb ))) goto end;
651
652     if (object)
653     {
654         WCHAR *path, *p;
655         unsigned int len;
656
657         len = strlenW( object ) + 1;
658         if (object[0] != '/') len++;
659         if (!(p = path = heap_alloc( len * sizeof(WCHAR) ))) goto end;
660
661         if (object[0] != '/') *p++ = '/';
662         strcpyW( p, object );
663         request->path = path;
664     }
665     else if (!(request->path = strdupW( slashW ))) goto end;
666
667     if (!version || !version[0]) version = http1_1;
668     if (!(request->version = strdupW( version ))) goto end;
669
670     if (!(hrequest = alloc_handle( &request->hdr ))) goto end;
671     request->hdr.handle = hrequest;
672
673     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hrequest, sizeof(hrequest) );
674
675 end:
676     release_object( &request->hdr );
677
678     TRACE("returning %p\n", hrequest);
679     return hrequest;
680 }
681
682 /***********************************************************************
683  *          WinHttpCloseHandle (winhttp.@)
684  */
685 BOOL WINAPI WinHttpCloseHandle( HINTERNET handle )
686 {
687     object_header_t *hdr;
688
689     TRACE("%p\n", handle);
690
691     if (!(hdr = grab_object( handle )))
692     {
693         set_last_error( ERROR_INVALID_HANDLE );
694         return FALSE;
695     }
696     release_object( hdr );
697     free_handle( handle );
698     return TRUE;
699 }
700
701 static BOOL query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen )
702 {
703     BOOL ret = FALSE;
704
705     if (!buflen)
706     {
707         set_last_error( ERROR_INVALID_PARAMETER );
708         return FALSE;
709     }
710
711     switch (option)
712     {
713     case WINHTTP_OPTION_CONTEXT_VALUE:
714     {
715         if (!buffer || *buflen < sizeof(DWORD_PTR))
716         {
717             *buflen = sizeof(DWORD_PTR);
718             set_last_error( ERROR_INSUFFICIENT_BUFFER );
719             return FALSE;
720         }
721
722         *(DWORD_PTR *)buffer = hdr->context;
723         *buflen = sizeof(DWORD_PTR);
724         return TRUE;
725     }
726     default:
727         if (hdr->vtbl->query_option) ret = hdr->vtbl->query_option( hdr, option, buffer, buflen );
728         else
729         {
730             FIXME("unimplemented option %u\n", option);
731             set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
732             return FALSE;
733         }
734         break;
735     }
736     return ret;
737 }
738
739 /***********************************************************************
740  *          WinHttpQueryOption (winhttp.@)
741  */
742 BOOL WINAPI WinHttpQueryOption( HINTERNET handle, DWORD option, LPVOID buffer, LPDWORD buflen )
743 {
744     BOOL ret = FALSE;
745     object_header_t *hdr;
746
747     TRACE("%p, %u, %p, %p\n", handle, option, buffer, buflen);
748
749     if (!(hdr = grab_object( handle )))
750     {
751         set_last_error( ERROR_INVALID_HANDLE );
752         return FALSE;
753     }
754
755     ret = query_option( hdr, option, buffer, buflen );
756
757     release_object( hdr );
758     return ret;
759 }
760
761 static BOOL set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen )
762 {
763     BOOL ret = TRUE;
764
765     if (!buffer)
766     {
767         set_last_error( ERROR_INVALID_PARAMETER );
768         return FALSE;
769     }
770
771     switch (option)
772     {
773     case WINHTTP_OPTION_CONTEXT_VALUE:
774     {
775         if (buflen != sizeof(DWORD_PTR))
776         {
777             set_last_error( ERROR_INSUFFICIENT_BUFFER );
778             return FALSE;
779         }
780
781         hdr->context = *(DWORD_PTR *)buffer;
782         return TRUE;
783     }
784     default:
785         if (hdr->vtbl->set_option) ret = hdr->vtbl->set_option( hdr, option, buffer, buflen );
786         else
787         {
788             FIXME("unimplemented option %u\n", option);
789             set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
790             return FALSE;
791         }
792         break;
793     }
794     return ret;
795 }
796
797 /***********************************************************************
798  *          WinHttpSetOption (winhttp.@)
799  */
800 BOOL WINAPI WinHttpSetOption( HINTERNET handle, DWORD option, LPVOID buffer, DWORD buflen )
801 {
802     BOOL ret = FALSE;
803     object_header_t *hdr;
804
805     TRACE("%p, %u, %p, %u\n", handle, option, buffer, buflen);
806
807     if (!(hdr = grab_object( handle )))
808     {
809         set_last_error( ERROR_INVALID_HANDLE );
810         return FALSE;
811     }
812
813     ret = set_option( hdr, option, buffer, buflen );
814
815     release_object( hdr );
816     return ret;
817 }
818
819 /***********************************************************************
820  *          WinHttpDetectAutoProxyConfigUrl (winhttp.@)
821  */
822 BOOL WINAPI WinHttpDetectAutoProxyConfigUrl( DWORD flags, LPWSTR *url )
823 {
824     FIXME("0x%08x, %p\n", flags, url);
825
826     set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED );
827     return FALSE;
828 }
829
830 static const WCHAR Connections[] = {
831     'S','o','f','t','w','a','r','e','\\',
832     'M','i','c','r','o','s','o','f','t','\\',
833     'W','i','n','d','o','w','s','\\',
834     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
835     'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
836     'C','o','n','n','e','c','t','i','o','n','s',0 };
837 static const WCHAR WinHttpSettings[] = {
838     'W','i','n','H','t','t','p','S','e','t','t','i','n','g','s',0 };
839 static const DWORD WINHTTPSETTINGS_MAGIC = 0x18;
840 static const DWORD WINHTTP_PROXY_TYPE_DIRECT = 1;
841 static const DWORD WINHTTP_PROXY_TYPE_PROXY = 2;
842
843 struct winhttp_settings_header
844 {
845     DWORD magic;
846     DWORD unknown; /* always zero? */
847     DWORD flags;   /* one of WINHTTP_PROXY_TYPE_* */
848 };
849
850 static inline void copy_char_to_wchar_sz(const BYTE *src, DWORD len, WCHAR *dst)
851 {
852     const BYTE *begin;
853
854     for (begin = src; src - begin < len; src++, dst++)
855         *dst = *src;
856     *dst = 0;
857 }
858
859 /***********************************************************************
860  *          WinHttpGetDefaultProxyConfiguration (winhttp.@)
861  */
862 BOOL WINAPI WinHttpGetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info )
863 {
864     LONG l;
865     HKEY key;
866     BOOL got_from_reg = FALSE, direct = TRUE;
867     char *envproxy;
868
869     TRACE("%p\n", info);
870
871     l = RegOpenKeyExW( HKEY_LOCAL_MACHINE, Connections, 0, KEY_READ, &key );
872     if (!l)
873     {
874         DWORD type, size = 0;
875
876         l = RegQueryValueExW( key, WinHttpSettings, NULL, &type, NULL, &size );
877         if (!l && type == REG_BINARY &&
878             size >= sizeof(struct winhttp_settings_header) + 2 * sizeof(DWORD))
879         {
880             BYTE *buf = heap_alloc( size );
881
882             if (buf)
883             {
884                 struct winhttp_settings_header *hdr =
885                     (struct winhttp_settings_header *)buf;
886                 DWORD *len = (DWORD *)(hdr + 1);
887
888                 l = RegQueryValueExW( key, WinHttpSettings, NULL, NULL, buf,
889                     &size );
890                 if (!l && hdr->magic == WINHTTPSETTINGS_MAGIC &&
891                     hdr->unknown == 0)
892                 {
893                     if (hdr->flags & WINHTTP_PROXY_TYPE_PROXY)
894                     {
895                        BOOL sane = FALSE;
896                        LPWSTR proxy = NULL;
897                        LPWSTR proxy_bypass = NULL;
898
899                         /* Sanity-check length of proxy string */
900                         if ((BYTE *)len - buf + *len <= size)
901                         {
902                             sane = TRUE;
903                             proxy = GlobalAlloc( 0, (*len + 1) * sizeof(WCHAR) );
904                             if (proxy)
905                                 copy_char_to_wchar_sz( (BYTE *)(len + 1), *len, proxy );
906                             len = (DWORD *)((BYTE *)(len + 1) + *len);
907                         }
908                         if (sane)
909                         {
910                             /* Sanity-check length of proxy bypass string */
911                             if ((BYTE *)len - buf + *len <= size)
912                             {
913                                 proxy_bypass = GlobalAlloc( 0, (*len + 1) * sizeof(WCHAR) );
914                                 if (proxy_bypass)
915                                     copy_char_to_wchar_sz( (BYTE *)(len + 1), *len, proxy_bypass );
916                             }
917                             else
918                             {
919                                 sane = FALSE;
920                                 GlobalFree( proxy );
921                                 proxy = NULL;
922                             }
923                         }
924                         info->lpszProxy = proxy;
925                         info->lpszProxyBypass = proxy_bypass;
926                         if (sane)
927                         {
928                             got_from_reg = TRUE;
929                             direct = FALSE;
930                             info->dwAccessType =
931                                 WINHTTP_ACCESS_TYPE_NAMED_PROXY;
932                             TRACE("http proxy (from registry) = %s, bypass = %s\n",
933                                 debugstr_w(info->lpszProxy),
934                                 debugstr_w(info->lpszProxyBypass));
935                         }
936                     }
937                 }
938                 heap_free( buf );
939             }
940         }
941         RegCloseKey( key );
942     }
943     if (!got_from_reg && (envproxy = getenv( "http_proxy" )))
944     {
945         char *colon, *http_proxy;
946
947         if ((colon = strchr( envproxy, ':' )))
948         {
949             if (*(colon + 1) == '/' && *(colon + 2) == '/')
950             {
951                 static const char http[] = "http://";
952
953                 /* It's a scheme, check that it's http */
954                 if (!strncmp( envproxy, http, strlen( http ) ))
955                     http_proxy = envproxy + strlen( http );
956                 else
957                 {
958                     WARN("unsupported scheme in $http_proxy: %s\n", envproxy);
959                     http_proxy = NULL;
960                 }
961             }
962             else
963                 http_proxy = envproxy;
964         }
965         else
966             http_proxy = envproxy;
967         if (http_proxy)
968         {
969             WCHAR *http_proxyW;
970             int len;
971
972             len = MultiByteToWideChar( CP_UNIXCP, 0, http_proxy, -1, NULL, 0 );
973             if ((http_proxyW = GlobalAlloc( 0, len * sizeof(WCHAR))))
974             {
975                 MultiByteToWideChar( CP_UNIXCP, 0, http_proxy, -1, http_proxyW, len );
976                 direct = FALSE;
977                 info->dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
978                 info->lpszProxy = http_proxyW;
979                 info->lpszProxyBypass = NULL;
980                 TRACE("http proxy (from environment) = %s\n",
981                     debugstr_w(info->lpszProxy));
982             }
983         }
984     }
985     if (direct)
986     {
987         info->dwAccessType    = WINHTTP_ACCESS_TYPE_NO_PROXY;
988         info->lpszProxy       = NULL;
989         info->lpszProxyBypass = NULL;
990     }
991     return TRUE;
992 }
993
994 /***********************************************************************
995  *          WinHttpGetIEProxyConfigForCurrentUser (winhttp.@)
996  */
997 BOOL WINAPI WinHttpGetIEProxyConfigForCurrentUser( WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *config )
998 {
999     TRACE("%p\n", config);
1000
1001     if (!config)
1002     {
1003         set_last_error( ERROR_INVALID_PARAMETER );
1004         return FALSE;
1005     }
1006
1007     /* FIXME: read from HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings */
1008
1009     FIXME("returning no proxy used\n");
1010     config->fAutoDetect       = FALSE;
1011     config->lpszAutoConfigUrl = NULL;
1012     config->lpszProxy         = NULL;
1013     config->lpszProxyBypass   = NULL;
1014
1015     return TRUE;
1016 }
1017
1018 /***********************************************************************
1019  *          WinHttpGetProxyForUrl (winhttp.@)
1020  */
1021 BOOL WINAPI WinHttpGetProxyForUrl( HINTERNET hsession, LPCWSTR url, WINHTTP_AUTOPROXY_OPTIONS *options,
1022                                    WINHTTP_PROXY_INFO *info )
1023 {
1024     FIXME("%p, %s, %p, %p\n", hsession, debugstr_w(url), options, info);
1025
1026     set_last_error( ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR );
1027     return FALSE;
1028 }
1029
1030 /***********************************************************************
1031  *          WinHttpSetDefaultProxyConfiguration (winhttp.@)
1032  */
1033 BOOL WINAPI WinHttpSetDefaultProxyConfiguration( WINHTTP_PROXY_INFO *info )
1034 {
1035     LONG l;
1036     HKEY key;
1037     BOOL ret = FALSE;
1038     const WCHAR *src;
1039
1040     TRACE("%p\n", info);
1041
1042     if (!info)
1043     {
1044         set_last_error( ERROR_INVALID_PARAMETER );
1045         return FALSE;
1046     }
1047     switch (info->dwAccessType)
1048     {
1049     case WINHTTP_ACCESS_TYPE_NO_PROXY:
1050         break;
1051     case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
1052         if (!info->lpszProxy)
1053         {
1054             set_last_error( ERROR_INVALID_PARAMETER );
1055             return FALSE;
1056         }
1057         /* Only ASCII characters are allowed */
1058         for (src = info->lpszProxy; *src; src++)
1059             if (*src > 0x7f)
1060             {
1061                 set_last_error( ERROR_INVALID_PARAMETER );
1062                 return FALSE;
1063             }
1064         if (info->lpszProxyBypass)
1065         {
1066             for (src = info->lpszProxyBypass; *src; src++)
1067                 if (*src > 0x7f)
1068                 {
1069                     set_last_error( ERROR_INVALID_PARAMETER );
1070                     return FALSE;
1071                 }
1072         }
1073         break;
1074     default:
1075         set_last_error( ERROR_INVALID_PARAMETER );
1076         return FALSE;
1077     }
1078
1079     l = RegCreateKeyExW( HKEY_LOCAL_MACHINE, Connections, 0, NULL, 0,
1080         KEY_WRITE, NULL, &key, NULL );
1081     if (!l)
1082     {
1083         DWORD size = sizeof(struct winhttp_settings_header) + 2 * sizeof(DWORD);
1084         BYTE *buf;
1085
1086         if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
1087         {
1088             size += strlenW( info->lpszProxy );
1089             if (info->lpszProxyBypass)
1090                 size += strlenW( info->lpszProxyBypass );
1091         }
1092         buf = heap_alloc( size );
1093         if (buf)
1094         {
1095             struct winhttp_settings_header *hdr =
1096                 (struct winhttp_settings_header *)buf;
1097             DWORD *len = (DWORD *)(hdr + 1);
1098
1099             hdr->magic = WINHTTPSETTINGS_MAGIC;
1100             hdr->unknown = 0;
1101             if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
1102             {
1103                 BYTE *dst;
1104
1105                 hdr->flags = WINHTTP_PROXY_TYPE_PROXY;
1106                 *len++ = strlenW( info->lpszProxy );
1107                 for (dst = (BYTE *)len, src = info->lpszProxy; *src;
1108                     src++, dst++)
1109                     *dst = *src;
1110                 len = (DWORD *)dst;
1111                 if (info->lpszProxyBypass)
1112                 {
1113                     *len++ = strlenW( info->lpszProxyBypass );
1114                     for (dst = (BYTE *)len, src = info->lpszProxyBypass; *src;
1115                         src++, dst++)
1116                         *dst = *src;
1117                 }
1118                 else
1119                     *len++ = 0;
1120             }
1121             else
1122             {
1123                 hdr->flags = WINHTTP_PROXY_TYPE_DIRECT;
1124                 *len++ = 0;
1125                 *len++ = 0;
1126             }
1127             l = RegSetValueExW( key, WinHttpSettings, 0, REG_BINARY, buf, size );
1128             if (!l)
1129                 ret = TRUE;
1130             heap_free( buf );
1131         }
1132         RegCloseKey( key );
1133     }
1134     return ret;
1135 }
1136
1137 /***********************************************************************
1138  *          WinHttpSetStatusCallback (winhttp.@)
1139  */
1140 WINHTTP_STATUS_CALLBACK WINAPI WinHttpSetStatusCallback( HINTERNET handle, WINHTTP_STATUS_CALLBACK callback,
1141                                                          DWORD flags, DWORD_PTR reserved )
1142 {
1143     object_header_t *hdr;
1144     WINHTTP_STATUS_CALLBACK ret;
1145
1146     TRACE("%p, %p, 0x%08x, 0x%lx\n", handle, callback, flags, reserved);
1147
1148     if (!(hdr = grab_object( handle )))
1149     {
1150         set_last_error( ERROR_INVALID_HANDLE );
1151         return WINHTTP_INVALID_STATUS_CALLBACK;
1152     }
1153     ret = hdr->callback;
1154     hdr->callback = callback;
1155     hdr->notify_mask = flags;
1156
1157     release_object( hdr );
1158     return ret;
1159 }
1160
1161 /***********************************************************************
1162  *          WinHttpSetTimeouts (winhttp.@)
1163  */
1164 BOOL WINAPI WinHttpSetTimeouts( HINTERNET handle, int resolve, int connect, int send, int receive )
1165 {
1166     BOOL ret = TRUE;
1167     request_t *request;
1168
1169     TRACE("%p, %d, %d, %d, %d\n", handle, resolve, connect, send, receive);
1170
1171     if (resolve < -1 || connect < -1 || send < -1 || receive < -1)
1172     {
1173         set_last_error( ERROR_INVALID_PARAMETER );
1174         return FALSE;
1175     }
1176
1177     if (resolve > 0)
1178         FIXME("resolve timeout (%d) not supported\n", resolve);
1179
1180     if (!(request = (request_t *)grab_object( handle )))
1181     {
1182         set_last_error( ERROR_INVALID_HANDLE );
1183         return FALSE;
1184     }
1185
1186     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
1187     {
1188         release_object( &request->hdr );
1189         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
1190         return FALSE;
1191     }
1192
1193     request->connect_timeout = connect;
1194
1195     if (send < 0) send = 0;
1196     request->send_timeout = send;
1197
1198     if (receive < 0) receive = 0;
1199     request->recv_timeout = receive;
1200
1201     if (netconn_connected( &request->netconn ))
1202     {
1203         if (netconn_set_timeout( &request->netconn, TRUE, send )) ret = FALSE;
1204         if (netconn_set_timeout( &request->netconn, FALSE, receive )) ret = FALSE;
1205     }
1206
1207     release_object( &request->hdr );
1208     return ret;
1209 }
1210
1211 static const WCHAR wkday[7][4] =
1212     {{'S','u','n', 0}, {'M','o','n', 0}, {'T','u','e', 0}, {'W','e','d', 0},
1213      {'T','h','u', 0}, {'F','r','i', 0}, {'S','a','t', 0}};
1214 static const WCHAR month[12][4] =
1215     {{'J','a','n', 0}, {'F','e','b', 0}, {'M','a','r', 0}, {'A','p','r', 0},
1216      {'M','a','y', 0}, {'J','u','n', 0}, {'J','u','l', 0}, {'A','u','g', 0},
1217      {'S','e','p', 0}, {'O','c','t', 0}, {'N','o','v', 0}, {'D','e','c', 0}};
1218
1219 /***********************************************************************
1220  *           WinHttpTimeFromSystemTime (WININET.@)
1221  */
1222 BOOL WINAPI WinHttpTimeFromSystemTime( const SYSTEMTIME *time, LPWSTR string )
1223 {
1224     static const WCHAR format[] =
1225         {'%','s',',',' ','%','0','2','d',' ','%','s',' ','%','4','d',' ','%','0',
1226          '2','d',':','%','0','2','d',':','%','0','2','d',' ','G','M','T', 0};
1227
1228     TRACE("%p, %p\n", time, string);
1229
1230     if (!time || !string) return FALSE;
1231
1232     sprintfW( string, format,
1233               wkday[time->wDayOfWeek],
1234               time->wDay,
1235               month[time->wMonth - 1],
1236               time->wYear,
1237               time->wHour,
1238               time->wMinute,
1239               time->wSecond );
1240
1241     return TRUE;
1242 }
1243
1244 /***********************************************************************
1245  *           WinHttpTimeToSystemTime (WININET.@)
1246  */
1247 BOOL WINAPI WinHttpTimeToSystemTime( LPCWSTR string, SYSTEMTIME *time )
1248 {
1249     unsigned int i;
1250     const WCHAR *s = string;
1251     WCHAR *end;
1252
1253     TRACE("%s, %p\n", debugstr_w(string), time);
1254
1255     if (!string || !time) return FALSE;
1256
1257     /* Windows does this too */
1258     GetSystemTime( time );
1259
1260     /*  Convert an RFC1123 time such as 'Fri, 07 Jan 2005 12:06:35 GMT' into
1261      *  a SYSTEMTIME structure.
1262      */
1263
1264     while (*s && !isalphaW( *s )) s++;
1265     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
1266     time->wDayOfWeek = 7;
1267
1268     for (i = 0; i < 7; i++)
1269     {
1270         if (toupperW( wkday[i][0] ) == toupperW( s[0] ) &&
1271             toupperW( wkday[i][1] ) == toupperW( s[1] ) &&
1272             toupperW( wkday[i][2] ) == toupperW( s[2] ) )
1273         {
1274             time->wDayOfWeek = i;
1275             break;
1276         }
1277     }
1278
1279     if (time->wDayOfWeek > 6) return TRUE;
1280     while (*s && !isdigitW( *s )) s++;
1281     time->wDay = strtolW( s, &end, 10 );
1282     s = end;
1283
1284     while (*s && !isalphaW( *s )) s++;
1285     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
1286     time->wMonth = 0;
1287
1288     for (i = 0; i < 12; i++)
1289     {
1290         if (toupperW( month[i][0]) == toupperW( s[0] ) &&
1291             toupperW( month[i][1]) == toupperW( s[1] ) &&
1292             toupperW( month[i][2]) == toupperW( s[2] ) )
1293         {
1294             time->wMonth = i + 1;
1295             break;
1296         }
1297     }
1298     if (time->wMonth == 0) return TRUE;
1299
1300     while (*s && !isdigitW( *s )) s++;
1301     if (*s == '\0') return TRUE;
1302     time->wYear = strtolW( s, &end, 10 );
1303     s = end;
1304
1305     while (*s && !isdigitW( *s )) s++;
1306     if (*s == '\0') return TRUE;
1307     time->wHour = strtolW( s, &end, 10 );
1308     s = end;
1309
1310     while (*s && !isdigitW( *s )) s++;
1311     if (*s == '\0') return TRUE;
1312     time->wMinute = strtolW( s, &end, 10 );
1313     s = end;
1314
1315     while (*s && !isdigitW( *s )) s++;
1316     if (*s == '\0') return TRUE;
1317     time->wSecond = strtolW( s, &end, 10 );
1318     s = end;
1319
1320     time->wMilliseconds = 0;
1321     return TRUE;
1322 }