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