Fixed a couple of buffer overflows.
[wine] / dlls / wininet / http.c
1 /*
2  * Wininet - Http Implementation
3  *
4  * Copyright 1999 Corel Corporation
5  * Copyright 2002 CodeWeavers Inc.
6  * Copyright 2002 TransGaming Technologies Inc.
7  *
8  * Ulrich Czekalla
9  * Aric Stewart
10  * David Hammerton
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25  */
26
27 #include "config.h"
28
29 #include <sys/types.h>
30 #ifdef HAVE_SYS_SOCKET_H
31 # include <sys/socket.h>
32 #endif
33 #include <stdio.h>
34 #include <stdlib.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <errno.h>
39 #include <string.h>
40 #include <time.h>
41
42 #include "windef.h"
43 #include "winbase.h"
44 #include "wininet.h"
45 #include "winreg.h"
46 #include "winerror.h"
47 #define NO_SHLWAPI_STREAM
48 #include "shlwapi.h"
49
50 #include "internet.h"
51 #include "wine/debug.h"
52 #include "wine/unicode.h"
53
54 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
55
56 #define HTTPHEADER " HTTP/1.0"
57 #define HTTPHOSTHEADER "\r\nHost: "
58 #define MAXHOSTNAME 100
59 #define MAX_FIELD_VALUE_LEN 256
60 #define MAX_FIELD_LEN 256
61
62
63 #define HTTP_REFERER    "Referer"
64 #define HTTP_ACCEPT     "Accept"
65 #define HTTP_USERAGENT  "User-Agent"
66
67 #define HTTP_ADDHDR_FLAG_ADD                            0x20000000
68 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                     0x10000000
69 #define HTTP_ADDHDR_FLAG_COALESCE                       0x40000000
70 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA            0x40000000
71 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON        0x01000000
72 #define HTTP_ADDHDR_FLAG_REPLACE                        0x80000000
73 #define HTTP_ADDHDR_FLAG_REQ                            0x02000000
74
75
76 BOOL HTTP_OpenConnection(LPWININETHTTPREQA lpwhr);
77 int HTTP_WriteDataToStream(LPWININETHTTPREQA lpwhr,
78         void *Buffer, int BytesToWrite);
79 int HTTP_ReadDataFromStream(LPWININETHTTPREQA lpwhr,
80         void *Buffer, int BytesToRead);
81 BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQA lpwhr);
82 BOOL HTTP_ProcessHeader(LPWININETHTTPREQA lpwhr, LPCSTR field, LPCSTR value, DWORD dwModifier);
83 void HTTP_CloseConnection(LPWININETHTTPREQA lpwhr);
84 BOOL HTTP_InterpretHttpHeader(LPSTR buffer, LPSTR field, INT fieldlen, LPSTR value, INT valuelen);
85 INT HTTP_GetStdHeaderIndex(LPCSTR lpszField);
86 INT HTTP_InsertCustomHeader(LPWININETHTTPREQA lpwhr, LPHTTPHEADERA lpHdr);
87 INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQA lpwhr, LPCSTR lpszField);
88
89 inline static LPSTR HTTP_strdup( LPCSTR str )
90 {
91     LPSTR ret = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 );
92     if (ret) strcpy( ret, str );
93     return ret;
94 }
95
96 /***********************************************************************
97  *           HttpAddRequestHeadersA (WININET.@)
98  *
99  * Adds one or more HTTP header to the request handler
100  *
101  * RETURNS
102  *    TRUE  on success
103  *    FALSE on failure
104  *
105  */
106 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
107         LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
108 {
109     LPSTR lpszStart;
110     LPSTR lpszEnd;
111     LPSTR buffer;
112     CHAR value[MAX_FIELD_VALUE_LEN], field[MAX_FIELD_LEN];
113     BOOL bSuccess = FALSE;
114     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
115
116     TRACE("%p, %s, %li, %li\n", hHttpRequest, lpszHeader, dwHeaderLength,
117           dwModifier);
118
119
120     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
121     {
122         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
123         return FALSE;
124     }
125
126     if (!lpszHeader) 
127       return TRUE;
128
129     TRACE("copying header: %s\n", lpszHeader);
130     buffer = HTTP_strdup(lpszHeader);
131     lpszStart = buffer;
132
133     do
134     {
135         lpszEnd = lpszStart;
136
137         while (*lpszEnd != '\0')
138         {
139             if (*lpszEnd == '\r' && *(lpszEnd + 1) == '\n')
140                  break;
141             lpszEnd++;
142         }
143
144         if (*lpszEnd == '\0')
145             break;
146
147         *lpszEnd = '\0';
148
149         TRACE("interpreting header %s\n", debugstr_a(lpszStart));
150         if (HTTP_InterpretHttpHeader(lpszStart, field, MAX_FIELD_LEN, value, MAX_FIELD_VALUE_LEN))
151             bSuccess = HTTP_ProcessHeader(lpwhr, field, value, dwModifier | HTTP_ADDHDR_FLAG_REQ);
152
153         lpszStart = lpszEnd + 2; /* Jump over \0\n */
154
155     } while (bSuccess);
156
157     HeapFree(GetProcessHeap(), 0, buffer);
158     return bSuccess;
159 }
160
161 /***********************************************************************
162  *           HttpEndRequestA (WININET.@)
163  *
164  * Ends an HTTP request that was started by HttpSendRequestEx
165  *
166  * RETURNS
167  *    TRUE      if successful
168  *    FALSE     on failure
169  *
170  */
171 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest, LPINTERNET_BUFFERSA lpBuffersOut, 
172           DWORD dwFlags, DWORD dwContext)
173 {
174   FIXME("stub\n");
175   return FALSE;
176 }
177
178 /***********************************************************************
179  *           HttpEndRequestW (WININET.@)
180  *
181  * Ends an HTTP request that was started by HttpSendRequestEx
182  *
183  * RETURNS
184  *    TRUE      if successful
185  *    FALSE     on failure
186  *
187  */
188 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest, LPINTERNET_BUFFERSW lpBuffersOut, 
189           DWORD dwFlags, DWORD dwContext)
190 {
191   FIXME("stub\n");
192   return FALSE;
193 }
194
195 /***********************************************************************
196  *           HttpOpenRequestA (WININET.@)
197  *
198  * Open a HTTP request handle
199  *
200  * RETURNS
201  *    HINTERNET  a HTTP request handle on success
202  *    NULL       on failure
203  *
204  */
205 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
206         LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
207         LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
208         DWORD dwFlags, DWORD dwContext)
209 {
210     LPWININETHTTPSESSIONA lpwhs = (LPWININETHTTPSESSIONA) hHttpSession;
211     LPWININETAPPINFOA hIC = NULL;
212
213     TRACE("(%p, %s, %s, %s, %s, %p, %08lx, %08lx)\n", hHttpSession,
214           debugstr_a(lpszVerb), lpszObjectName,
215           debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
216           dwFlags, dwContext);
217     if(lpszAcceptTypes!=NULL)
218     {
219         int i;
220         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
221             TRACE("\taccept type: %s\n",lpszAcceptTypes[i]);
222     }    
223
224     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
225     {
226         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
227         return FALSE;
228     }
229     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
230
231     /*
232      * My tests seem to show that the windows version does not
233      * become asynchronous until after this point. And anyhow
234      * if this call was asynchronous then how would you get the
235      * necessary HINTERNET pointer returned by this function.
236      *
237      * I am leaving this here just in case I am wrong
238      *
239      * if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
240      */
241     if (0)
242     {
243         WORKREQUEST workRequest;
244
245         workRequest.asyncall = HTTPOPENREQUESTA;
246         workRequest.HFTPSESSION = (DWORD)hHttpSession;
247         workRequest.LPSZVERB = (DWORD)HTTP_strdup(lpszVerb);
248         workRequest.LPSZOBJECTNAME = (DWORD)HTTP_strdup(lpszObjectName);
249         if (lpszVersion)
250             workRequest.LPSZVERSION = (DWORD)HTTP_strdup(lpszVersion);
251         else
252             workRequest.LPSZVERSION = 0;
253         if (lpszReferrer)
254             workRequest.LPSZREFERRER = (DWORD)HTTP_strdup(lpszReferrer);
255         else
256             workRequest.LPSZREFERRER = 0;
257         workRequest.LPSZACCEPTTYPES = (DWORD)lpszAcceptTypes;
258         workRequest.DWFLAGS = dwFlags;
259         workRequest.DWCONTEXT = dwContext;
260
261         INTERNET_AsyncCall(&workRequest);
262         TRACE ("returning NULL\n");
263         return NULL;
264     }
265     else
266     {
267         HINTERNET rec = HTTP_HttpOpenRequestA(hHttpSession, lpszVerb, lpszObjectName,
268                                               lpszVersion, lpszReferrer, lpszAcceptTypes,
269                                               dwFlags, dwContext);
270         TRACE("returning %p\n", rec);
271         return rec;
272     }
273 }
274
275
276 /***********************************************************************
277  *           HttpOpenRequestW (WININET.@)
278  *
279  * Open a HTTP request handle
280  *
281  * RETURNS
282  *    HINTERNET  a HTTP request handle on success
283  *    NULL       on failure
284  *
285  * FIXME: This should be the other way around (A should call W)
286  */
287 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
288         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
289         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
290         DWORD dwFlags, DWORD dwContext)
291 {
292     CHAR *szVerb = NULL, *szObjectName = NULL;
293     CHAR *szVersion = NULL, *szReferrer = NULL, **szAcceptTypes = NULL;
294     INT len;
295     INT acceptTypesCount;
296     HINTERNET rc = FALSE;
297     TRACE("(%p, %s, %s, %s, %s, %p, %08lx, %08lx)\n", hHttpSession,
298           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
299           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
300           dwFlags, dwContext);
301
302     if (lpszVerb)
303     {
304         len = lstrlenW(lpszVerb)+1;
305         if (!(szVerb = (CHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR))))
306             goto end;
307         WideCharToMultiByte(CP_ACP, -1, lpszVerb, -1, szVerb, len, NULL, NULL);
308     }
309
310     if (lpszObjectName)
311     {
312         len = lstrlenW(lpszObjectName)+1;
313         if (!(szObjectName = (CHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR))))
314             goto end;
315         WideCharToMultiByte(CP_ACP, -1, lpszObjectName, -1, szObjectName, len, NULL, NULL);
316     }
317
318     if (lpszVersion)
319     {
320         len = lstrlenW(lpszVersion)+1;
321         if (!(szVersion = (CHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR))))
322             goto end;
323         WideCharToMultiByte(CP_ACP, -1, lpszVersion, -1, szVersion, len, NULL, NULL);
324     }
325
326     if (lpszReferrer)
327     {
328         len = lstrlenW(lpszReferrer)+1;
329         if (!(szReferrer = (CHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR))))
330             goto end;
331         WideCharToMultiByte(CP_ACP, -1, lpszReferrer, -1, szReferrer, len, NULL, NULL);
332     }
333
334     acceptTypesCount = 0;
335     if (lpszAcceptTypes)
336     {
337         while (lpszAcceptTypes[acceptTypesCount]) { acceptTypesCount++; } /* find out how many there are */
338         szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR *) * acceptTypesCount);
339         acceptTypesCount = 0;
340         while (lpszAcceptTypes[acceptTypesCount])
341         {
342             len = lstrlenW(lpszAcceptTypes[acceptTypesCount])+1;
343             if (!(szAcceptTypes[acceptTypesCount] = (CHAR *) HeapAlloc(GetProcessHeap(),
344                                                                        0, len * sizeof(CHAR))))
345                 goto end;
346             WideCharToMultiByte(CP_ACP, -1, lpszAcceptTypes[acceptTypesCount],
347                                 -1, szAcceptTypes[acceptTypesCount], len, NULL, NULL);
348             acceptTypesCount++;
349         }
350     }
351     else szAcceptTypes = 0;
352
353     rc = HttpOpenRequestA(hHttpSession, (LPCSTR)szVerb, (LPCSTR)szObjectName,
354                           (LPCSTR)szVersion, (LPCSTR)szReferrer,
355                           (LPCSTR *)szAcceptTypes, dwFlags, dwContext);
356
357 end:
358     if (szAcceptTypes)
359     {
360         acceptTypesCount = 0;
361         while (szAcceptTypes[acceptTypesCount])
362         {
363             HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
364             acceptTypesCount++;
365         }
366         HeapFree(GetProcessHeap(), 0, szAcceptTypes);
367     }
368     if (szReferrer) HeapFree(GetProcessHeap(), 0, szReferrer);
369     if (szVersion) HeapFree(GetProcessHeap(), 0, szVersion);
370     if (szObjectName) HeapFree(GetProcessHeap(), 0, szObjectName);
371     if (szVerb) HeapFree(GetProcessHeap(), 0, szVerb);
372
373     return rc;
374 }
375
376 /***********************************************************************
377  *           HTTP_HttpOpenRequestA (internal)
378  *
379  * Open a HTTP request handle
380  *
381  * RETURNS
382  *    HINTERNET  a HTTP request handle on success
383  *    NULL       on failure
384  *
385  */
386 HINTERNET WINAPI HTTP_HttpOpenRequestA(HINTERNET hHttpSession,
387         LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
388         LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
389         DWORD dwFlags, DWORD dwContext)
390 {
391     LPWININETHTTPSESSIONA lpwhs = (LPWININETHTTPSESSIONA) hHttpSession;
392     LPWININETAPPINFOA hIC = NULL;
393     LPWININETHTTPREQA lpwhr;
394     LPSTR lpszCookies;
395     LPSTR lpszUrl = NULL;
396     DWORD nCookieSize;
397
398     TRACE("--> \n");
399
400     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
401     {
402         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
403         return FALSE;
404     }
405
406     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
407
408     lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPREQA));
409     if (NULL == lpwhr)
410     {
411         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
412         return (HINTERNET) NULL;
413     }
414
415     lpwhr->hdr.htype = WH_HHTTPREQ;
416     lpwhr->hdr.lpwhparent = hHttpSession;
417     lpwhr->hdr.dwFlags = dwFlags;
418     lpwhr->hdr.dwContext = dwContext;
419     NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE);
420
421     if (NULL != lpszObjectName && strlen(lpszObjectName)) {
422         DWORD needed = 0;
423         HRESULT rc;
424         rc = UrlEscapeA(lpszObjectName, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
425         if (rc != E_POINTER)
426             needed = strlen(lpszObjectName)+1;
427         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed);
428         rc = UrlEscapeA(lpszObjectName, lpwhr->lpszPath, &needed,
429                    URL_ESCAPE_SPACES_ONLY);
430         if (rc)
431         {
432             ERR("Unable to escape string!(%s) (%ld)\n",lpszObjectName,rc);
433             strcpy(lpwhr->lpszPath,lpszObjectName);
434         }
435     }
436
437     if (NULL != lpszReferrer && strlen(lpszReferrer))
438         HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDHDR_FLAG_COALESCE);
439
440     if(lpszAcceptTypes!=NULL)
441     {
442         int i;
443         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
444             HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i], HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_REQ|HTTP_ADDHDR_FLAG_ADD_IF_NEW);
445     }
446
447     if (NULL == lpszVerb)
448         lpwhr->lpszVerb = HTTP_strdup("GET");
449     else if (strlen(lpszVerb))
450         lpwhr->lpszVerb = HTTP_strdup(lpszVerb);
451
452     if (NULL != lpszReferrer && strlen(lpszReferrer))
453     {
454         char buf[MAXHOSTNAME];
455         URL_COMPONENTSA UrlComponents;
456
457         UrlComponents.lpszExtraInfo = NULL;
458         UrlComponents.lpszPassword = NULL;
459         UrlComponents.lpszScheme = NULL;
460         UrlComponents.lpszUrlPath = NULL;
461         UrlComponents.lpszUserName = NULL;
462         UrlComponents.lpszHostName = buf;
463         UrlComponents.dwHostNameLength = MAXHOSTNAME;
464
465         InternetCrackUrlA(lpszReferrer, 0, 0, &UrlComponents);
466         if (strlen(UrlComponents.lpszHostName))
467             lpwhr->lpszHostName = HTTP_strdup(UrlComponents.lpszHostName);
468     } else if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0) {
469         char buf[MAXHOSTNAME];
470         char proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
471         URL_COMPONENTSA UrlComponents;
472
473         UrlComponents.lpszExtraInfo = NULL;
474         UrlComponents.lpszPassword = NULL;
475         UrlComponents.lpszScheme = NULL;
476         UrlComponents.lpszUrlPath = NULL;
477         UrlComponents.lpszUserName = NULL;
478         UrlComponents.lpszHostName = buf;
479         UrlComponents.dwHostNameLength = MAXHOSTNAME;
480
481         sprintf(proxy, "http://%s/", hIC->lpszProxy);
482         InternetCrackUrlA(proxy, 0, 0, &UrlComponents);
483         if (strlen(UrlComponents.lpszHostName)) {
484                          /* for constant 15 see above */
485              char* url = HeapAlloc(GetProcessHeap(), 0, strlen(lpwhs->lpszServerName) + strlen(lpwhr->lpszPath) + 15);
486
487              if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
488                  UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
489                         
490             if(lpwhr->lpszHostName != 0) {
491                 HeapFree(GetProcessHeap(), 0, lpwhr->lpszHostName);
492                 lpwhr->lpszHostName = 0;
493             }
494             sprintf(url, "http://%s:%d/%s", lpwhs->lpszServerName, lpwhs->nServerPort, lpwhr->lpszPath);
495             if(lpwhr->lpszPath)
496                 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
497             lpwhr->lpszPath = url;
498             /* FIXME: Do I have to free lpwhs->lpszServerName here ? */
499             lpwhs->lpszServerName = HTTP_strdup(UrlComponents.lpszHostName);
500             lpwhs->nServerPort = UrlComponents.nPort;
501         }
502     } else {
503         lpwhr->lpszHostName = HTTP_strdup(lpwhs->lpszServerName);
504     }
505
506     if (hIC->lpszAgent)
507     {
508         char *agent_header = HeapAlloc(GetProcessHeap(), 0, strlen(hIC->lpszAgent) + 1 + 14);
509         sprintf(agent_header, "User-Agent: %s\r\n", hIC->lpszAgent);
510         HttpAddRequestHeadersA((HINTERNET)lpwhr, agent_header, strlen(agent_header),
511                                HTTP_ADDREQ_FLAG_ADD);
512         HeapFree(GetProcessHeap(), 0, agent_header);
513     }
514
515     lpszUrl = HeapAlloc(GetProcessHeap(), 0, strlen(lpwhr->lpszHostName) + 1 + 7);
516     sprintf(lpszUrl, "http://%s", lpwhr->lpszHostName);
517     if (InternetGetCookieA(lpszUrl, NULL, NULL, &nCookieSize))
518     {
519         int cnt = 0;
520
521         lpszCookies = HeapAlloc(GetProcessHeap(), 0, nCookieSize + 1 + 8);
522
523         cnt += sprintf(lpszCookies, "Cookie: ");
524         InternetGetCookieA(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
525         cnt += nCookieSize - 1;
526         sprintf(lpszCookies + cnt, "\r\n");
527
528         HttpAddRequestHeadersA((HINTERNET)lpwhr, lpszCookies, strlen(lpszCookies),
529                                HTTP_ADDREQ_FLAG_ADD);
530         HeapFree(GetProcessHeap(), 0, lpszCookies);
531     }
532     HeapFree(GetProcessHeap(), 0, lpszUrl);
533
534
535
536     if (hIC->lpfnStatusCB)
537     {
538         INTERNET_ASYNC_RESULT iar;
539
540         iar.dwResult = (DWORD)lpwhr;
541         iar.dwError = ERROR_SUCCESS;
542
543         SendAsyncCallback(hIC, hHttpSession, dwContext,
544                       INTERNET_STATUS_HANDLE_CREATED, &iar,
545                       sizeof(INTERNET_ASYNC_RESULT));
546     }
547
548     /*
549      * A STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on windows
550      */
551
552     /*
553      * According to my tests. The name is not resolved until a request is Opened
554      */
555     SendAsyncCallback(hIC, hHttpSession, dwContext,
556                       INTERNET_STATUS_RESOLVING_NAME,
557                       lpwhs->lpszServerName,
558                       strlen(lpwhs->lpszServerName)+1);
559     if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
560                     &lpwhs->phostent, &lpwhs->socketAddress))
561     {
562         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
563         return FALSE;
564     }
565
566     SendAsyncCallback(hIC, hHttpSession, lpwhr->hdr.dwContext,
567                       INTERNET_STATUS_NAME_RESOLVED,
568                       &(lpwhs->socketAddress),
569                       sizeof(struct sockaddr_in));
570
571     TRACE("<--\n");
572     return (HINTERNET) lpwhr;
573 }
574
575
576 /***********************************************************************
577  *           HttpQueryInfoA (WININET.@)
578  *
579  * Queries for information about an HTTP request
580  *
581  * RETURNS
582  *    TRUE  on success
583  *    FALSE on failure
584  *
585  */
586 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
587         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
588 {
589     LPHTTPHEADERA lphttpHdr = NULL;
590     BOOL bSuccess = FALSE;
591     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
592
593     TRACE("(0x%08lx)--> %ld\n", dwInfoLevel, dwInfoLevel);
594
595     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
596     {
597         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
598         return FALSE;
599     }
600
601     /* Find requested header structure */
602     if ((dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK) == HTTP_QUERY_CUSTOM)
603     {
604         INT index = HTTP_GetCustomHeaderIndex(lpwhr, (LPSTR)lpBuffer);
605
606         if (index < 0)
607             goto lend;
608
609         lphttpHdr = &lpwhr->pCustHeaders[index];
610     }
611     else
612     {
613         INT index = dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK;
614
615         if (index == HTTP_QUERY_RAW_HEADERS_CRLF || index == HTTP_QUERY_RAW_HEADERS)
616         {
617             INT i, delim, size = 0, cnt = 0;
618
619             delim = index == HTTP_QUERY_RAW_HEADERS_CRLF ? 2 : 1;
620
621            /* Calculate length of custom reuqest headers */
622            for (i = 0; i < lpwhr->nCustHeaders; i++)
623            {
624                if ((~lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST) && lpwhr->pCustHeaders[i].lpszField &&
625                    lpwhr->pCustHeaders[i].lpszValue)
626                {
627                   size += strlen(lpwhr->pCustHeaders[i].lpszField) +
628                        strlen(lpwhr->pCustHeaders[i].lpszValue) + delim + 2;
629                }
630            }
631
632            /* Calculate the length of stadard request headers */
633            for (i = 0; i <= HTTP_QUERY_MAX; i++)
634            {
635               if ((~lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST) && lpwhr->StdHeaders[i].lpszField &&
636                    lpwhr->StdHeaders[i].lpszValue)
637               {
638                  size += strlen(lpwhr->StdHeaders[i].lpszField) +
639                     strlen(lpwhr->StdHeaders[i].lpszValue) + delim + 2;
640               }
641            }
642            size += delim;
643
644            if (size + 1 > *lpdwBufferLength)
645            {
646               *lpdwBufferLength = size + 1;
647               INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
648               goto lend;
649            }
650
651            /* Append standard request heades */
652            for (i = 0; i <= HTTP_QUERY_MAX; i++)
653            {
654                if ((~lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST) &&
655                                            lpwhr->StdHeaders[i].lpszField &&
656                                            lpwhr->StdHeaders[i].lpszValue)
657                {
658                    cnt += sprintf((char*)lpBuffer + cnt, "%s: %s%s", lpwhr->StdHeaders[i].lpszField, lpwhr->StdHeaders[i].lpszValue,
659                           index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "\0");
660                }
661             }
662
663             /* Append custom request heades */
664             for (i = 0; i < lpwhr->nCustHeaders; i++)
665             {
666                 if ((~lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST) &&
667                                                 lpwhr->pCustHeaders[i].lpszField &&
668                                                 lpwhr->pCustHeaders[i].lpszValue)
669                 {
670                    cnt += sprintf((char*)lpBuffer + cnt, "%s: %s%s",
671                     lpwhr->pCustHeaders[i].lpszField, lpwhr->pCustHeaders[i].lpszValue,
672                                         index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "\0");
673                 }
674             }
675
676             strcpy((char*)lpBuffer + cnt, index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "");
677
678            *lpdwBufferLength = cnt + delim;
679            bSuccess = TRUE;
680                 goto lend;
681         }
682         else if (index >= 0 && index <= HTTP_QUERY_MAX && lpwhr->StdHeaders[index].lpszValue)
683         {
684             lphttpHdr = &lpwhr->StdHeaders[index];
685         }
686         else
687             goto lend;
688     }
689
690     /* Ensure header satisifies requested attributes */
691     if ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
692             (~lphttpHdr->wFlags & HDR_ISREQUEST))
693         goto lend;
694
695     /* coalesce value to reuqested type */
696     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER)
697     {
698        *(int *)lpBuffer = atoi(lphttpHdr->lpszValue);
699            bSuccess = TRUE;
700     }
701     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME)
702     {
703         time_t tmpTime;
704         struct tm tmpTM;
705         SYSTEMTIME *STHook;
706
707         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
708
709         tmpTM = *gmtime(&tmpTime);
710         STHook = (SYSTEMTIME *) lpBuffer;
711         if(STHook==NULL)
712             goto lend;
713
714             STHook->wDay = tmpTM.tm_mday;
715             STHook->wHour = tmpTM.tm_hour;
716             STHook->wMilliseconds = 0;
717             STHook->wMinute = tmpTM.tm_min;
718             STHook->wDayOfWeek = tmpTM.tm_wday;
719             STHook->wMonth = tmpTM.tm_mon + 1;
720             STHook->wSecond = tmpTM.tm_sec;
721             STHook->wYear = tmpTM.tm_year;
722
723             bSuccess = TRUE;
724     }
725     else if (dwInfoLevel & HTTP_QUERY_FLAG_COALESCE)
726     {
727             if (*lpdwIndex >= lphttpHdr->wCount)
728                 {
729                 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
730                 }
731             else
732             {
733             /* Copy strncpy(lpBuffer, lphttpHdr[*lpdwIndex], len); */
734             (*lpdwIndex)++;
735             }
736     }
737     else
738     {
739         INT len = strlen(lphttpHdr->lpszValue);
740
741         if (len + 1 > *lpdwBufferLength)
742         {
743             *lpdwBufferLength = len + 1;
744             INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
745             goto lend;
746         }
747
748         strncpy(lpBuffer, lphttpHdr->lpszValue, len);
749         ((char*)lpBuffer)[len]=0;
750         *lpdwBufferLength = len;
751         bSuccess = TRUE;
752     }
753
754 lend:
755     TRACE("%d <--\n", bSuccess);
756     return bSuccess;
757 }
758
759 /***********************************************************************
760  *           HttpQueryInfoW (WININET.@)
761  *
762  * Queries for information about an HTTP request
763  *
764  * RETURNS
765  *    TRUE  on success
766  *    FALSE on failure
767  *
768  */
769 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
770         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
771 {
772     BOOL result;
773     DWORD charLen=*lpdwBufferLength;
774     char* tempBuffer=HeapAlloc(GetProcessHeap(), 0, charLen);
775     result=HttpQueryInfoA(hHttpRequest, dwInfoLevel, tempBuffer, &charLen, lpdwIndex);
776     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
777        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
778     {
779         memcpy(lpBuffer,tempBuffer,charLen);
780     }
781     else
782     {
783         int nChars=MultiByteToWideChar(CP_ACP,0, tempBuffer,charLen,lpBuffer,*lpdwBufferLength);
784         *lpdwBufferLength=nChars;
785     }
786     HeapFree(GetProcessHeap(), 0, tempBuffer);
787     return result;
788 }
789
790 /***********************************************************************
791  *           HttpSendRequestExA (WININET.@)
792  *
793  * Sends the specified request to the HTTP server and allows chunked
794  * transfers
795  */
796 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
797                                LPINTERNET_BUFFERSA lpBuffersIn,
798                                LPINTERNET_BUFFERSA lpBuffersOut,
799                                DWORD dwFlags, DWORD dwContext)
800 {
801   FIXME("(%p, %p, %p, %08lx, %08lx): stub\n", hRequest, lpBuffersIn,
802         lpBuffersOut, dwFlags, dwContext);
803   return FALSE;
804 }
805
806 /***********************************************************************
807  *           HttpSendRequestA (WININET.@)
808  *
809  * Sends the specified request to the HTTP server
810  *
811  * RETURNS
812  *    TRUE  on success
813  *    FALSE on failure
814  *
815  */
816 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
817         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
818 {
819     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
820     LPWININETHTTPSESSIONA lpwhs = NULL;
821     LPWININETAPPINFOA hIC = NULL;
822
823     TRACE("0x%08lx\n", (unsigned long)hHttpRequest);
824
825     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
826     {
827         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
828         return FALSE;
829     }
830
831     lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
832     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
833     {
834         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
835         return FALSE;
836     }
837
838     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
839     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
840     {
841         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
842         return FALSE;
843     }
844
845     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
846     {
847         WORKREQUEST workRequest;
848
849         workRequest.asyncall = HTTPSENDREQUESTA;
850         workRequest.HFTPSESSION = (DWORD)hHttpRequest;
851         if (lpszHeaders)
852             workRequest.LPSZHEADER = (DWORD)HTTP_strdup(lpszHeaders);
853         else
854             workRequest.LPSZHEADER = 0;
855         workRequest.DWHEADERLENGTH = dwHeaderLength;
856         workRequest.LPOPTIONAL = (DWORD)lpOptional;
857         workRequest.DWOPTIONALLENGTH = dwOptionalLength;
858
859         INTERNET_AsyncCall(&workRequest);
860         /*
861          * This is from windows.
862          */
863         SetLastError(ERROR_IO_PENDING);
864         return 0;
865     }
866     else
867     {
868         return HTTP_HttpSendRequestA(hHttpRequest, lpszHeaders,
869                 dwHeaderLength, lpOptional, dwOptionalLength);
870     }
871 }
872
873 /***********************************************************************
874  *           HttpSendRequestW (WININET.@)
875  *
876  * Sends the specified request to the HTTP server
877  *
878  * RETURNS
879  *    TRUE  on success
880  *    FALSE on failure
881  *
882  */
883 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
884         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
885 {
886     BOOL result;
887     char* szHeaders=NULL;
888     DWORD nLen=dwHeaderLength;
889     if(lpszHeaders!=NULL)
890     {
891         if(nLen==-1)
892             nLen=strlenW(lpszHeaders);
893         szHeaders=(char*)malloc(nLen+1);
894         WideCharToMultiByte(CP_ACP,0,lpszHeaders,nLen,szHeaders,nLen,NULL,NULL);
895     }
896     result=HttpSendRequestA(hHttpRequest, szHeaders, dwHeaderLength, lpOptional, dwOptionalLength);
897     if(szHeaders!=NULL)
898         free(szHeaders);
899     return result;
900 }
901
902 /***********************************************************************
903  *           HTTP_HandleRedirect (internal)
904  */
905 static BOOL HTTP_HandleRedirect(LPWININETHTTPREQA lpwhr, LPCSTR lpszUrl, LPCSTR lpszHeaders,
906                                 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength)
907 {
908     LPWININETHTTPSESSIONA lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
909     LPWININETAPPINFOA hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
910     char path[2048];
911     if(lpszUrl[0]=='/')
912     {
913         /* if it's an absolute path, keep the same session info */
914         strcpy(path,lpszUrl);
915     }
916     else
917     {
918         URL_COMPONENTSA urlComponents;
919         char protocol[32], hostName[MAXHOSTNAME], userName[1024];
920         char password[1024], extra[1024];
921         urlComponents.dwStructSize = sizeof(URL_COMPONENTSA);
922         urlComponents.lpszScheme = protocol;
923         urlComponents.dwSchemeLength = 32;
924         urlComponents.lpszHostName = hostName;
925         urlComponents.dwHostNameLength = MAXHOSTNAME;
926         urlComponents.lpszUserName = userName;
927         urlComponents.dwUserNameLength = 1024;
928         urlComponents.lpszPassword = password;
929         urlComponents.dwPasswordLength = 1024;
930         urlComponents.lpszUrlPath = path;
931         urlComponents.dwUrlPathLength = 2048;
932         urlComponents.lpszExtraInfo = extra;
933         urlComponents.dwExtraInfoLength = 1024;
934         if(!InternetCrackUrlA(lpszUrl, strlen(lpszUrl), 0, &urlComponents))
935             return FALSE;
936         
937         if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
938             urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
939
940         /* consider the current host as the referef */
941         if (NULL != lpwhs->lpszServerName && strlen(lpwhs->lpszServerName))
942             HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName, HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|HTTP_ADDHDR_FLAG_ADD_IF_NEW);
943         
944         if (NULL != lpwhs->lpszServerName)
945             HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
946         lpwhs->lpszServerName = HTTP_strdup(hostName);
947         if (NULL != lpwhs->lpszUserName)
948             HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
949         lpwhs->lpszUserName = HTTP_strdup(userName);
950         lpwhs->nServerPort = urlComponents.nPort;
951
952         if (NULL != lpwhr->lpszHostName)
953             HeapFree(GetProcessHeap(), 0, lpwhr->lpszHostName);
954         lpwhr->lpszHostName=HTTP_strdup(hostName);
955
956         SendAsyncCallback(hIC, lpwhs, lpwhr->hdr.dwContext,
957                       INTERNET_STATUS_RESOLVING_NAME,
958                       lpwhs->lpszServerName,
959                       strlen(lpwhs->lpszServerName)+1);
960
961         if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
962                     &lpwhs->phostent, &lpwhs->socketAddress))
963         {
964             INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
965             return FALSE;
966         }
967
968         SendAsyncCallback(hIC, lpwhs, lpwhr->hdr.dwContext,
969                       INTERNET_STATUS_NAME_RESOLVED,
970                       &(lpwhs->socketAddress),
971                       sizeof(struct sockaddr_in));
972
973     }
974
975     if(lpwhr->lpszPath)
976         HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
977     lpwhr->lpszPath=NULL;
978     if (strlen(path))
979     {
980         DWORD needed = 0;
981         HRESULT rc;
982         rc = UrlEscapeA(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
983         if (rc != E_POINTER)
984             needed = strlen(path)+1;
985         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed);
986         rc = UrlEscapeA(path, lpwhr->lpszPath, &needed,
987                         URL_ESCAPE_SPACES_ONLY);
988         if (rc)
989         {
990             ERR("Unable to escape string!(%s) (%ld)\n",path,rc);
991             strcpy(lpwhr->lpszPath,path);
992         }
993     }
994
995     return HttpSendRequestA((HINTERNET)lpwhr, lpszHeaders, dwHeaderLength, lpOptional, dwOptionalLength);
996 }
997
998 /***********************************************************************
999  *           HTTP_HttpSendRequestA (internal)
1000  *
1001  * Sends the specified request to the HTTP server
1002  *
1003  * RETURNS
1004  *    TRUE  on success
1005  *    FALSE on failure
1006  *
1007  */
1008 BOOL WINAPI HTTP_HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
1009         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
1010 {
1011     INT cnt;
1012     INT i;
1013     BOOL bSuccess = FALSE;
1014     LPSTR requestString = NULL;
1015     INT requestStringLen;
1016     INT responseLen;
1017     INT headerLength = 0;
1018     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
1019     LPWININETHTTPSESSIONA lpwhs = NULL;
1020     LPWININETAPPINFOA hIC = NULL;
1021     BOOL loop_next = FALSE;
1022     int CustHeaderIndex;
1023
1024     TRACE("--> 0x%08lx\n", (ULONG)hHttpRequest);
1025
1026     /* Verify our tree of internet handles */
1027     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
1028     {
1029         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1030         return FALSE;
1031     }
1032
1033     lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
1034     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
1035     {
1036         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1037         return FALSE;
1038     }
1039
1040     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
1041     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
1042     {
1043         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1044         return FALSE;
1045     }
1046
1047     /* Clear any error information */
1048     INTERNET_SetLastError(0);
1049
1050
1051     /* We must have a verb */
1052     if (NULL == lpwhr->lpszVerb)
1053     {
1054             goto lend;
1055     }
1056
1057     do
1058     {
1059         TRACE("Going to url %s %s\n", debugstr_a(lpwhr->lpszHostName), debugstr_a(lpwhr->lpszPath));
1060         loop_next = FALSE;
1061
1062         /* If we don't have a path we set it to root */
1063         if (NULL == lpwhr->lpszPath)
1064             lpwhr->lpszPath = HTTP_strdup("/");
1065
1066         if(strncmp(lpwhr->lpszPath, "http://", sizeof("http://") -1) != 0
1067            && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
1068         {
1069             char *fixurl = HeapAlloc(GetProcessHeap(), 0, strlen(lpwhr->lpszPath) + 2);
1070             *fixurl = '/';
1071             strcpy(fixurl + 1, lpwhr->lpszPath);
1072             HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
1073             lpwhr->lpszPath = fixurl;
1074         }
1075
1076         /* Calculate length of request string */
1077         requestStringLen =
1078             strlen(lpwhr->lpszVerb) +
1079             strlen(lpwhr->lpszPath) +
1080             strlen(HTTPHEADER) +
1081             5; /* " \r\n\r\n" */
1082
1083         /* Add length of passed headers */
1084         if (lpszHeaders)
1085         {
1086             headerLength = -1 == dwHeaderLength ?  strlen(lpszHeaders) : dwHeaderLength;
1087             requestStringLen += headerLength +  2; /* \r\n */
1088         }
1089
1090         /* Calculate length of custom request headers */
1091         for (i = 0; i < lpwhr->nCustHeaders; i++)
1092         {
1093             if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
1094             {
1095                 requestStringLen += strlen(lpwhr->pCustHeaders[i].lpszField) +
1096                     strlen(lpwhr->pCustHeaders[i].lpszValue) + 4; /*: \r\n */
1097             }
1098         }
1099
1100         /* Calculate the length of standard request headers */
1101         for (i = 0; i <= HTTP_QUERY_MAX; i++)
1102         {
1103             if (lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST)
1104             {
1105                 requestStringLen += strlen(lpwhr->StdHeaders[i].lpszField) +
1106                     strlen(lpwhr->StdHeaders[i].lpszValue) + 4; /*: \r\n */
1107             }
1108         }
1109
1110         if (lpwhr->lpszHostName)
1111             requestStringLen += (strlen(HTTPHOSTHEADER) + strlen(lpwhr->lpszHostName));
1112
1113
1114         /* Allocate string to hold entire request */
1115         requestString = HeapAlloc(GetProcessHeap(), 0, requestStringLen + 1);
1116         if (NULL == requestString)
1117         {
1118             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1119             goto lend;
1120         }
1121
1122         /* Build request string */
1123         cnt = sprintf(requestString, "%s %s%s",
1124                       lpwhr->lpszVerb,
1125                       lpwhr->lpszPath,
1126                       HTTPHEADER);
1127
1128         /* Append standard request headers */
1129         for (i = 0; i <= HTTP_QUERY_MAX; i++)
1130         {
1131             if (lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST)
1132             {
1133                 cnt += sprintf(requestString + cnt, "\r\n%s: %s",
1134                                lpwhr->StdHeaders[i].lpszField, lpwhr->StdHeaders[i].lpszValue);
1135                 TRACE("Adding header %s (%s)\n",lpwhr->StdHeaders[i].lpszField,lpwhr->StdHeaders[i].lpszValue);
1136             }
1137         }
1138
1139         /* Append custom request heades */
1140         for (i = 0; i < lpwhr->nCustHeaders; i++)
1141         {
1142             if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
1143             {
1144                 cnt += sprintf(requestString + cnt, "\r\n%s: %s",
1145                                lpwhr->pCustHeaders[i].lpszField, lpwhr->pCustHeaders[i].lpszValue);
1146                 TRACE("Adding custom header %s (%s)\n",lpwhr->pCustHeaders[i].lpszField,lpwhr->pCustHeaders[i].lpszValue);
1147             }
1148         }
1149
1150         if (lpwhr->lpszHostName)
1151             cnt += sprintf(requestString + cnt, "%s%s", HTTPHOSTHEADER, lpwhr->lpszHostName);
1152
1153         /* Append passed request headers */
1154         if (lpszHeaders)
1155         {
1156             strcpy(requestString + cnt, "\r\n");
1157             cnt += 2;
1158             strcpy(requestString + cnt, lpszHeaders);
1159             cnt += headerLength;
1160         }
1161
1162         /* Set termination string for request */
1163         strcpy(requestString + cnt, "\r\n\r\n");
1164
1165         TRACE("(%s) len(%d)\n", requestString, requestStringLen);
1166         /* Send the request and store the results */
1167         if (!HTTP_OpenConnection(lpwhr))
1168             goto lend;
1169
1170         SendAsyncCallback(hIC, hHttpRequest, lpwhr->hdr.dwContext,
1171                           INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1172
1173         NETCON_send(&lpwhr->netConnection, requestString, requestStringLen,
1174                     0, &cnt);
1175
1176
1177         SendAsyncCallback(hIC, hHttpRequest, lpwhr->hdr.dwContext,
1178                           INTERNET_STATUS_REQUEST_SENT,
1179                           &requestStringLen,sizeof(DWORD));
1180
1181         SendAsyncCallback(hIC, hHttpRequest, lpwhr->hdr.dwContext,
1182                           INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1183
1184         if (cnt < 0)
1185             goto lend;
1186
1187         responseLen = HTTP_GetResponseHeaders(lpwhr);
1188         if (responseLen)
1189             bSuccess = TRUE;
1190
1191         SendAsyncCallback(hIC, hHttpRequest, lpwhr->hdr.dwContext,
1192                           INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
1193                           sizeof(DWORD));
1194
1195         /* process headers here. Is this right? */
1196         CustHeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, "Set-Cookie");
1197         if (CustHeaderIndex >= 0)
1198         {
1199             LPHTTPHEADERA setCookieHeader;
1200             int nPosStart = 0, nPosEnd = 0;
1201
1202             setCookieHeader = &lpwhr->pCustHeaders[CustHeaderIndex];
1203
1204             while (setCookieHeader->lpszValue[nPosEnd] != '\0')
1205             {
1206                 LPSTR buf_cookie, cookie_name, cookie_data;
1207                 LPSTR buf_url;
1208                 LPSTR domain = NULL;
1209                 int nEqualPos = 0;
1210                 while (setCookieHeader->lpszValue[nPosEnd] != ';' && setCookieHeader->lpszValue[nPosEnd] != ',' &&
1211                        setCookieHeader->lpszValue[nPosEnd] != '\0')
1212                 {
1213                     nPosEnd++;
1214                 }
1215                 if (setCookieHeader->lpszValue[nPosEnd] == ';')
1216                 {
1217                     /* fixme: not case sensitive, strcasestr is gnu only */
1218                     int nDomainPosEnd = 0;
1219                     int nDomainPosStart = 0, nDomainLength = 0;
1220                     LPSTR lpszDomain = strstr(&setCookieHeader->lpszValue[nPosEnd], "domain=");
1221                     if (lpszDomain)
1222                     { /* they have specified their own domain, lets use it */
1223                         while (lpszDomain[nDomainPosEnd] != ';' && lpszDomain[nDomainPosEnd] != ',' &&
1224                                lpszDomain[nDomainPosEnd] != '\0')
1225                         {
1226                             nDomainPosEnd++;
1227                         }
1228                         nDomainPosStart = strlen("domain=");
1229                         nDomainLength = (nDomainPosEnd - nDomainPosStart) + 1;
1230                         domain = HeapAlloc(GetProcessHeap(), 0, nDomainLength + 1);
1231                         strncpy(domain, &lpszDomain[nDomainPosStart], nDomainLength);
1232                         domain[nDomainLength] = '\0';
1233                     }
1234                 }
1235                 if (setCookieHeader->lpszValue[nPosEnd] == '\0') break;
1236                 buf_cookie = HeapAlloc(GetProcessHeap(), 0, (nPosEnd - nPosStart) + 1);
1237                 strncpy(buf_cookie, &setCookieHeader->lpszValue[nPosStart], (nPosEnd - nPosStart));
1238                 buf_cookie[(nPosEnd - nPosStart)] = '\0';
1239                 TRACE("%s\n", buf_cookie);
1240                 while (buf_cookie[nEqualPos] != '=' && buf_cookie[nEqualPos] != '\0')
1241                 {
1242                     nEqualPos++;
1243                 }
1244                 if (buf_cookie[nEqualPos] == '\0' || buf_cookie[nEqualPos + 1] == '\0')
1245                 {
1246                     HeapFree(GetProcessHeap(), 0, buf_cookie);
1247                     break;
1248                 }
1249
1250                 cookie_name = HeapAlloc(GetProcessHeap(), 0, nEqualPos + 1);
1251                 strncpy(cookie_name, buf_cookie, nEqualPos);
1252                 cookie_name[nEqualPos] = '\0';
1253                 cookie_data = &buf_cookie[nEqualPos + 1];
1254
1255
1256                 buf_url = HeapAlloc(GetProcessHeap(), 0, strlen((domain ? domain : lpwhr->lpszHostName)) + strlen(lpwhr->lpszPath) + 9);
1257                 sprintf(buf_url, "http://%s/", (domain ? domain : lpwhr->lpszHostName)); /* FIXME PATH!!! */
1258                 InternetSetCookieA(buf_url, cookie_name, cookie_data);
1259
1260                 HeapFree(GetProcessHeap(), 0, buf_url);
1261                 HeapFree(GetProcessHeap(), 0, buf_cookie);
1262                 HeapFree(GetProcessHeap(), 0, cookie_name);
1263                 if (domain) HeapFree(GetProcessHeap(), 0, domain);
1264                 nPosStart = nPosEnd;
1265             }
1266         }
1267
1268         /* FIXME: is this right? I'm not sure if this should be here or elsewhere (the loop, too)
1269          * FIXME: don't do this if they specify INTERNET_FLAG_NO_AUTO_REDIRECT */
1270         if (lpwhr->StdHeaders[HTTP_QUERY_LOCATION].lpszValue)
1271         {
1272             URL_COMPONENTSA urlComponents;
1273             char protocol[32], hostName[MAXHOSTNAME], userName[1024];
1274             char password[1024], path[2048], extra[1024];
1275
1276             TRACE("Got a Location header: Going around to a new location: %s",
1277                   debugstr_a(lpwhr->StdHeaders[HTTP_QUERY_LOCATION].lpszValue));
1278
1279             urlComponents.dwStructSize = sizeof(URL_COMPONENTSA);
1280             urlComponents.lpszScheme = protocol;
1281             urlComponents.dwSchemeLength = 32;
1282             urlComponents.lpszHostName = hostName;
1283             urlComponents.dwHostNameLength = MAXHOSTNAME;
1284             urlComponents.lpszUserName = userName;
1285             urlComponents.dwUserNameLength = 1024;
1286             urlComponents.lpszPassword = password;
1287             urlComponents.dwPasswordLength = 1024;
1288             urlComponents.lpszUrlPath = path;
1289             urlComponents.dwUrlPathLength = 2048;
1290             urlComponents.lpszExtraInfo = extra;
1291             urlComponents.dwExtraInfoLength = 1024;
1292             if (!InternetCrackUrlA(lpwhr->StdHeaders[HTTP_QUERY_LOCATION].lpszValue,
1293                                    strlen(lpwhr->StdHeaders[HTTP_QUERY_LOCATION].lpszValue),
1294                                    0, &urlComponents))
1295                 goto lend;
1296
1297             if (urlComponents.nScheme != INTERNET_SCHEME_HTTP)
1298             {
1299                 FIXME("cannot redirect to non HTTP page\n");
1300                 goto lend;
1301             }
1302
1303             HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1304             HeapAlloc(GetProcessHeap(), 0, strlen(path) + 1);
1305             strcpy(lpwhr->lpszPath, path);
1306
1307             if (urlComponents.dwHostNameLength)
1308             {
1309                 HeapFree(GetProcessHeap(), 0, lpwhr->lpszHostName);
1310                 HeapAlloc(GetProcessHeap(), 0, strlen(hostName) + 1);
1311                 strcpy(lpwhr->lpszHostName, hostName);
1312             }
1313
1314             SendAsyncCallback(hIC, hHttpRequest, lpwhr->hdr.dwContext,
1315                               INTERNET_STATUS_REDIRECT, NULL, 0);
1316
1317             HeapFree(GetProcessHeap(), 0, lpwhr->StdHeaders[HTTP_QUERY_LOCATION].lpszValue);
1318             lpwhr->StdHeaders[HTTP_QUERY_LOCATION].lpszValue = NULL;
1319             loop_next = TRUE;
1320         }
1321     }
1322     while (loop_next);
1323
1324 lend:
1325
1326     if (requestString)
1327         HeapFree(GetProcessHeap(), 0, requestString);
1328
1329     /* TODO: send notification for P3P header */
1330     
1331     if(!(hIC->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
1332     {
1333         DWORD dwCode,dwCodeLength=sizeof(DWORD),dwIndex=0;
1334         if(HttpQueryInfoA(hHttpRequest,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,&dwCode,&dwCodeLength,&dwIndex) &&
1335             (dwCode==302 || dwCode==301))
1336         {
1337             char szNewLocation[2048];
1338             DWORD dwBufferSize=2048;
1339             dwIndex=0;
1340             if(HttpQueryInfoA(hHttpRequest,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,&dwIndex))
1341             {
1342                 SendAsyncCallback(hIC, hHttpRequest, lpwhr->hdr.dwContext,
1343                       INTERNET_STATUS_REDIRECT, szNewLocation,
1344                       dwBufferSize);
1345                 return HTTP_HandleRedirect(lpwhr, szNewLocation, lpszHeaders,
1346                                            dwHeaderLength, lpOptional, dwOptionalLength);
1347             }
1348         }
1349     }
1350
1351     if (hIC->lpfnStatusCB)
1352     {
1353         INTERNET_ASYNC_RESULT iar;
1354
1355         iar.dwResult = (DWORD)bSuccess;
1356         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1357
1358         SendAsyncCallback(hIC, hHttpRequest, lpwhr->hdr.dwContext,
1359                       INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1360                       sizeof(INTERNET_ASYNC_RESULT));
1361     }
1362
1363     TRACE("<--\n");
1364     return bSuccess;
1365 }
1366
1367
1368 /***********************************************************************
1369  *           HTTP_Connect  (internal)
1370  *
1371  * Create http session handle
1372  *
1373  * RETURNS
1374  *   HINTERNET a session handle on success
1375  *   NULL on failure
1376  *
1377  */
1378 HINTERNET HTTP_Connect(HINTERNET hInternet, LPCSTR lpszServerName,
1379         INTERNET_PORT nServerPort, LPCSTR lpszUserName,
1380         LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext)
1381 {
1382     BOOL bSuccess = FALSE;
1383     LPWININETAPPINFOA hIC = NULL;
1384     LPWININETHTTPSESSIONA lpwhs = NULL;
1385
1386     TRACE("-->\n");
1387
1388     if (((LPWININETHANDLEHEADER)hInternet)->htype != WH_HINIT)
1389         goto lerror;
1390
1391     hIC = (LPWININETAPPINFOA) hInternet;
1392     hIC->hdr.dwContext = dwContext;
1393     
1394     lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPSESSIONA));
1395     if (NULL == lpwhs)
1396     {
1397         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1398         goto lerror;
1399     }
1400
1401    /*
1402     * According to my tests. The name is not resolved until a request is sent
1403     */
1404
1405     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1406         nServerPort = INTERNET_DEFAULT_HTTP_PORT;
1407
1408     lpwhs->hdr.htype = WH_HHTTPSESSION;
1409     lpwhs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet;
1410     lpwhs->hdr.dwFlags = dwFlags;
1411     lpwhs->hdr.dwContext = dwContext;
1412     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1413         if(strchr(hIC->lpszProxy, ' '))
1414             FIXME("Several proxies not implemented.\n");
1415         if(hIC->lpszProxyBypass)
1416             FIXME("Proxy bypass is ignored.\n");
1417     }
1418     if (NULL != lpszServerName)
1419         lpwhs->lpszServerName = HTTP_strdup(lpszServerName);
1420     if (NULL != lpszUserName)
1421         lpwhs->lpszUserName = HTTP_strdup(lpszUserName);
1422     lpwhs->nServerPort = nServerPort;
1423
1424     if (hIC->lpfnStatusCB)
1425     {
1426         INTERNET_ASYNC_RESULT iar;
1427
1428         iar.dwResult = (DWORD)lpwhs;
1429         iar.dwError = ERROR_SUCCESS;
1430
1431         SendAsyncCallback(hIC, hInternet, dwContext,
1432                       INTERNET_STATUS_HANDLE_CREATED, &iar,
1433                       sizeof(INTERNET_ASYNC_RESULT));
1434     }
1435
1436     bSuccess = TRUE;
1437
1438 lerror:
1439     if (!bSuccess && lpwhs)
1440     {
1441         HeapFree(GetProcessHeap(), 0, lpwhs);
1442         lpwhs = NULL;
1443     }
1444
1445 /*
1446  * a INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
1447  * windows
1448  */
1449
1450     TRACE("%p -->\n", hInternet);
1451     return (HINTERNET)lpwhs;
1452 }
1453
1454
1455 /***********************************************************************
1456  *           HTTP_OpenConnection (internal)
1457  *
1458  * Connect to a web server
1459  *
1460  * RETURNS
1461  *
1462  *   TRUE  on success
1463  *   FALSE on failure
1464  */
1465 BOOL HTTP_OpenConnection(LPWININETHTTPREQA lpwhr)
1466 {
1467     BOOL bSuccess = FALSE;
1468     LPWININETHTTPSESSIONA lpwhs;
1469     LPWININETAPPINFOA hIC = NULL;
1470
1471     TRACE("-->\n");
1472
1473
1474     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
1475     {
1476         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1477         goto lend;
1478     }
1479
1480     lpwhs = (LPWININETHTTPSESSIONA)lpwhr->hdr.lpwhparent;
1481
1482     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
1483     SendAsyncCallback(hIC, lpwhr, lpwhr->hdr.dwContext,
1484                       INTERNET_STATUS_CONNECTING_TO_SERVER,
1485                       &(lpwhs->socketAddress),
1486                        sizeof(struct sockaddr_in));
1487
1488     if (!NETCON_create(&lpwhr->netConnection, lpwhs->phostent->h_addrtype,
1489                          SOCK_STREAM, 0))
1490     {
1491         WARN("Socket creation failed\n");
1492         goto lend;
1493     }
1494
1495     if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
1496                       sizeof(lpwhs->socketAddress)))
1497     {
1498        WARN("Unable to connect to host (%s)\n", strerror(errno));
1499        goto lend;
1500     }
1501
1502     SendAsyncCallback(hIC, lpwhr, lpwhr->hdr.dwContext,
1503                       INTERNET_STATUS_CONNECTED_TO_SERVER,
1504                       &(lpwhs->socketAddress),
1505                        sizeof(struct sockaddr_in));
1506
1507     bSuccess = TRUE;
1508
1509 lend:
1510     TRACE("%d <--\n", bSuccess);
1511     return bSuccess;
1512 }
1513
1514
1515 /***********************************************************************
1516  *           HTTP_GetResponseHeaders (internal)
1517  *
1518  * Read server response
1519  *
1520  * RETURNS
1521  *
1522  *   TRUE  on success
1523  *   FALSE on error
1524  */
1525 BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQA lpwhr)
1526 {
1527     INT cbreaks = 0;
1528     CHAR buffer[MAX_REPLY_LEN];
1529     DWORD buflen = MAX_REPLY_LEN;
1530     BOOL bSuccess = FALSE;
1531     INT  rc = 0;
1532     CHAR value[MAX_FIELD_VALUE_LEN], field[MAX_FIELD_LEN];
1533
1534     TRACE("-->\n");
1535
1536     if (!NETCON_connected(&lpwhr->netConnection))
1537         goto lend;
1538
1539     /*
1540      * HACK peek at the buffer
1541      */
1542     NETCON_recv(&lpwhr->netConnection, buffer, buflen, MSG_PEEK, &rc);
1543
1544     /*
1545      * We should first receive 'HTTP/1.x nnn' where nnn is the status code.
1546      */
1547     buflen = MAX_REPLY_LEN;
1548     memset(buffer, 0, MAX_REPLY_LEN);
1549     if (!NETCON_getNextLine(&lpwhr->netConnection, buffer, &buflen))
1550         goto lend;
1551
1552     if (strncmp(buffer, "HTTP", 4) != 0)
1553         goto lend;
1554
1555     buffer[12]='\0';
1556     HTTP_ProcessHeader(lpwhr, "Status", buffer+9, (HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE));
1557
1558     /* Parse each response line */
1559     do
1560     {
1561         buflen = MAX_REPLY_LEN;
1562         if (NETCON_getNextLine(&lpwhr->netConnection, buffer, &buflen))
1563         {
1564             TRACE("got line %s, now interpretting\n", debugstr_a(buffer));
1565             if (!HTTP_InterpretHttpHeader(buffer, field, MAX_FIELD_LEN, value, MAX_FIELD_VALUE_LEN))
1566                 break;
1567
1568             HTTP_ProcessHeader(lpwhr, field, value, (HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE));
1569         }
1570         else
1571         {
1572             cbreaks++;
1573             if (cbreaks >= 2)
1574                break;
1575         }
1576     }while(1);
1577
1578     bSuccess = TRUE;
1579
1580 lend:
1581
1582     TRACE("<--\n");
1583     if (bSuccess)
1584         return rc;
1585     else
1586         return FALSE;
1587 }
1588
1589
1590 /***********************************************************************
1591  *           HTTP_InterpretHttpHeader (internal)
1592  *
1593  * Parse server response
1594  *
1595  * RETURNS
1596  *
1597  *   TRUE  on success
1598  *   FALSE on error
1599  */
1600 INT stripSpaces(LPCSTR lpszSrc, LPSTR lpszStart, INT *len)
1601 {
1602     LPCSTR lpsztmp;
1603     INT srclen;
1604
1605     srclen = 0;
1606
1607     while (*lpszSrc == ' ' && *lpszSrc != '\0')
1608         lpszSrc++;
1609
1610     lpsztmp = lpszSrc;
1611     while(*lpsztmp != '\0')
1612     {
1613         if (*lpsztmp != ' ')
1614             srclen = lpsztmp - lpszSrc + 1;
1615
1616         lpsztmp++;
1617     }
1618
1619     *len = min(*len, srclen);
1620     strncpy(lpszStart, lpszSrc, *len);
1621     lpszStart[*len] = '\0';
1622
1623     return *len;
1624 }
1625
1626
1627 BOOL HTTP_InterpretHttpHeader(LPSTR buffer, LPSTR field, INT fieldlen, LPSTR value, INT valuelen)
1628 {
1629     CHAR *pd;
1630     BOOL bSuccess = FALSE;
1631
1632     TRACE("\n");
1633
1634     *field = '\0';
1635     *value = '\0';
1636
1637     pd = strchr(buffer, ':');
1638     if (pd)
1639     {
1640         *pd = '\0';
1641         if (stripSpaces(buffer, field, &fieldlen) > 0)
1642         {
1643             if (stripSpaces(pd+1, value, &valuelen) > 0)
1644                 bSuccess = TRUE;
1645         }
1646     }
1647
1648     TRACE("%d: field(%s) Value(%s)\n", bSuccess, field, value);
1649     return bSuccess;
1650 }
1651
1652
1653 /***********************************************************************
1654  *           HTTP_GetStdHeaderIndex (internal)
1655  *
1656  * Lookup field index in standard http header array
1657  *
1658  * FIXME: This should be stuffed into a hash table
1659  */
1660 INT HTTP_GetStdHeaderIndex(LPCSTR lpszField)
1661 {
1662     INT index = -1;
1663
1664     if (!strcasecmp(lpszField, "Content-Length"))
1665         index = HTTP_QUERY_CONTENT_LENGTH;
1666     else if (!strcasecmp(lpszField,"Status"))
1667         index = HTTP_QUERY_STATUS_CODE;
1668     else if (!strcasecmp(lpszField,"Content-Type"))
1669         index = HTTP_QUERY_CONTENT_TYPE;
1670     else if (!strcasecmp(lpszField,"Last-Modified"))
1671         index = HTTP_QUERY_LAST_MODIFIED;
1672     else if (!strcasecmp(lpszField,"Location"))
1673         index = HTTP_QUERY_LOCATION;
1674     else if (!strcasecmp(lpszField,"Accept"))
1675         index = HTTP_QUERY_ACCEPT;
1676     else if (!strcasecmp(lpszField,"Referer"))
1677         index = HTTP_QUERY_REFERER;
1678     else if (!strcasecmp(lpszField,"Content-Transfer-Encoding"))
1679         index = HTTP_QUERY_CONTENT_TRANSFER_ENCODING;
1680     else if (!strcasecmp(lpszField,"Date"))
1681         index = HTTP_QUERY_DATE;
1682     else if (!strcasecmp(lpszField,"Server"))
1683         index = HTTP_QUERY_SERVER;
1684     else if (!strcasecmp(lpszField,"Connection"))
1685         index = HTTP_QUERY_CONNECTION;
1686     else if (!strcasecmp(lpszField,"ETag"))
1687         index = HTTP_QUERY_ETAG;
1688     else if (!strcasecmp(lpszField,"Accept-Ranges"))
1689         index = HTTP_QUERY_ACCEPT_RANGES;
1690     else if (!strcasecmp(lpszField,"Expires"))
1691         index = HTTP_QUERY_EXPIRES;
1692     else if (!strcasecmp(lpszField,"Mime-Version"))
1693         index = HTTP_QUERY_MIME_VERSION;
1694     else if (!strcasecmp(lpszField,"Pragma"))
1695         index = HTTP_QUERY_PRAGMA;
1696     else if (!strcasecmp(lpszField,"Cache-Control"))
1697         index = HTTP_QUERY_CACHE_CONTROL;
1698     else if (!strcasecmp(lpszField,"Content-Length"))
1699         index = HTTP_QUERY_CONTENT_LENGTH;
1700     else if (!strcasecmp(lpszField,"User-Agent"))
1701         index = HTTP_QUERY_USER_AGENT;
1702     else
1703     {
1704        TRACE("Couldn't find %s in standard header table\n", lpszField);
1705     }
1706
1707     return index;
1708 }
1709
1710
1711 /***********************************************************************
1712  *           HTTP_ProcessHeader (internal)
1713  *
1714  * Stuff header into header tables according to <dwModifier>
1715  *
1716  */
1717
1718 #define COALESCEFLASG (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
1719
1720 BOOL HTTP_ProcessHeader(LPWININETHTTPREQA lpwhr, LPCSTR field, LPCSTR value, DWORD dwModifier)
1721 {
1722     LPHTTPHEADERA lphttpHdr = NULL;
1723     BOOL bSuccess = FALSE;
1724     INT index;
1725
1726     TRACE("--> %s: %s - 0x%08x\n", field, value, (unsigned int)dwModifier);
1727
1728     /* Adjust modifier flags */
1729     if (dwModifier & COALESCEFLASG)
1730         dwModifier |= HTTP_ADDHDR_FLAG_ADD;
1731
1732     /* Try to get index into standard header array */
1733     index = HTTP_GetStdHeaderIndex(field);
1734     if (index >= 0)
1735     {
1736         lphttpHdr = &lpwhr->StdHeaders[index];
1737     }
1738     else /* Find or create new custom header */
1739     {
1740         index = HTTP_GetCustomHeaderIndex(lpwhr, field);
1741         if (index >= 0)
1742         {
1743             if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
1744             {
1745                 return FALSE;
1746             }
1747             lphttpHdr = &lpwhr->pCustHeaders[index];
1748         }
1749         else
1750         {
1751             HTTPHEADERA hdr;
1752
1753             hdr.lpszField = (LPSTR)field;
1754             hdr.lpszValue = (LPSTR)value;
1755             hdr.wFlags = hdr.wCount = 0;
1756
1757             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
1758                 hdr.wFlags |= HDR_ISREQUEST;
1759
1760             index = HTTP_InsertCustomHeader(lpwhr, &hdr);
1761             return index >= 0;
1762         }
1763     }
1764
1765     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
1766         lphttpHdr->wFlags |= HDR_ISREQUEST;
1767     else
1768         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
1769
1770     if (!lphttpHdr->lpszValue && (dwModifier & (HTTP_ADDHDR_FLAG_ADD|HTTP_ADDHDR_FLAG_ADD_IF_NEW)))
1771     {
1772         INT slen;
1773
1774         if (!lpwhr->StdHeaders[index].lpszField)
1775         {
1776             lphttpHdr->lpszField = HTTP_strdup(field);
1777
1778             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
1779                 lphttpHdr->wFlags |= HDR_ISREQUEST;
1780         }
1781
1782         slen = strlen(value) + 1;
1783         lphttpHdr->lpszValue = HeapAlloc(GetProcessHeap(), 0, slen);
1784         if (lphttpHdr->lpszValue)
1785         {
1786             memcpy(lphttpHdr->lpszValue, value, slen);
1787             bSuccess = TRUE;
1788         }
1789         else
1790         {
1791             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1792         }
1793     }
1794     else if (lphttpHdr->lpszValue)
1795     {
1796         if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
1797         {
1798             LPSTR lpsztmp;
1799             INT len;
1800
1801             len = strlen(value);
1802
1803             if (len <= 0)
1804             {
1805                 /* if custom header delete from array */
1806                 HeapFree(GetProcessHeap(), 0, lphttpHdr->lpszValue);
1807                 lphttpHdr->lpszValue = NULL;
1808                 bSuccess = TRUE;
1809             }
1810             else
1811             {
1812                 lpsztmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  lphttpHdr->lpszValue, len+1);
1813                 if (lpsztmp)
1814                 {
1815                     lphttpHdr->lpszValue = lpsztmp;
1816                     strcpy(lpsztmp, value);
1817                     bSuccess = TRUE;
1818                 }
1819                 else
1820                 {
1821                     WARN("HeapReAlloc (%d bytes) failed\n",len+1);
1822                     INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1823                 }
1824             }
1825         }
1826         else if (dwModifier & COALESCEFLASG)
1827         {
1828             LPSTR lpsztmp;
1829             CHAR ch = 0;
1830             INT len = 0;
1831             INT origlen = strlen(lphttpHdr->lpszValue);
1832             INT valuelen = strlen(value);
1833
1834             if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
1835             {
1836                 ch = ',';
1837                 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
1838             }
1839             else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
1840             {
1841                 ch = ';';
1842                 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
1843             }
1844
1845             len = origlen + valuelen + ((ch > 0) ? 1 : 0);
1846
1847             lpsztmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  lphttpHdr->lpszValue, len+1);
1848             if (lpsztmp)
1849             {
1850                 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
1851                 if (ch > 0)
1852                 {
1853                     lphttpHdr->lpszValue[origlen] = ch;
1854                     origlen++;
1855                 }
1856
1857                 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen);
1858                 lphttpHdr->lpszValue[len] = '\0';
1859                 bSuccess = TRUE;
1860             }
1861             else
1862             {
1863                 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
1864                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1865             }
1866         }
1867     }
1868     TRACE("<-- %d\n",bSuccess);
1869     return bSuccess;
1870 }
1871
1872
1873 /***********************************************************************
1874  *           HTTP_CloseConnection (internal)
1875  *
1876  * Close socket connection
1877  *
1878  */
1879 VOID HTTP_CloseConnection(LPWININETHTTPREQA lpwhr)
1880 {
1881
1882
1883     LPWININETHTTPSESSIONA lpwhs = NULL;
1884     LPWININETAPPINFOA hIC = NULL;
1885
1886     TRACE("%p\n",lpwhr);
1887
1888     lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
1889     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
1890
1891     SendAsyncCallback(hIC, lpwhr, lpwhr->hdr.dwContext,
1892                       INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1893
1894     if (NETCON_connected(&lpwhr->netConnection))
1895     {
1896         NETCON_close(&lpwhr->netConnection);
1897     }
1898
1899     SendAsyncCallback(hIC, lpwhr, lpwhr->hdr.dwContext,
1900                       INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1901 }
1902
1903
1904 /***********************************************************************
1905  *           HTTP_CloseHTTPRequestHandle (internal)
1906  *
1907  * Deallocate request handle
1908  *
1909  */
1910 void HTTP_CloseHTTPRequestHandle(LPWININETHTTPREQA lpwhr)
1911 {
1912     int i;
1913     LPWININETHTTPSESSIONA lpwhs = NULL;
1914     LPWININETAPPINFOA hIC = NULL;
1915
1916     TRACE("\n");
1917
1918     if (NETCON_connected(&lpwhr->netConnection))
1919         HTTP_CloseConnection(lpwhr);
1920
1921     lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
1922     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
1923
1924     SendAsyncCallback(hIC, lpwhr, lpwhr->hdr.dwContext,
1925                       INTERNET_STATUS_HANDLE_CLOSING, lpwhr,
1926                       sizeof(HINTERNET));
1927
1928     if (lpwhr->lpszPath)
1929         HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1930     if (lpwhr->lpszVerb)
1931         HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1932     if (lpwhr->lpszHostName)
1933         HeapFree(GetProcessHeap(), 0, lpwhr->lpszHostName);
1934
1935     for (i = 0; i <= HTTP_QUERY_MAX; i++)
1936     {
1937            if (lpwhr->StdHeaders[i].lpszField)
1938             HeapFree(GetProcessHeap(), 0, lpwhr->StdHeaders[i].lpszField);
1939            if (lpwhr->StdHeaders[i].lpszValue)
1940             HeapFree(GetProcessHeap(), 0, lpwhr->StdHeaders[i].lpszValue);
1941     }
1942
1943     for (i = 0; i < lpwhr->nCustHeaders; i++)
1944     {
1945            if (lpwhr->pCustHeaders[i].lpszField)
1946             HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1947            if (lpwhr->pCustHeaders[i].lpszValue)
1948             HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1949     }
1950
1951     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1952     HeapFree(GetProcessHeap(), 0, lpwhr);
1953 }
1954
1955
1956 /***********************************************************************
1957  *           HTTP_CloseHTTPSessionHandle (internal)
1958  *
1959  * Deallocate session handle
1960  *
1961  */
1962 void HTTP_CloseHTTPSessionHandle(LPWININETHTTPSESSIONA lpwhs)
1963 {
1964     LPWININETAPPINFOA hIC = NULL;
1965     TRACE("%p\n", lpwhs);
1966
1967     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
1968
1969     SendAsyncCallback(hIC, lpwhs, lpwhs->hdr.dwContext,
1970                       INTERNET_STATUS_HANDLE_CLOSING, lpwhs,
1971                       sizeof(HINTERNET));
1972
1973     if (lpwhs->lpszServerName)
1974         HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1975     if (lpwhs->lpszUserName)
1976         HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
1977     HeapFree(GetProcessHeap(), 0, lpwhs);
1978 }
1979
1980
1981 /***********************************************************************
1982  *           HTTP_GetCustomHeaderIndex (internal)
1983  *
1984  * Return index of custom header from header array
1985  *
1986  */
1987 INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQA lpwhr, LPCSTR lpszField)
1988 {
1989     INT index;
1990
1991     TRACE("%s\n", lpszField);
1992
1993     for (index = 0; index < lpwhr->nCustHeaders; index++)
1994     {
1995         if (!strcasecmp(lpwhr->pCustHeaders[index].lpszField, lpszField))
1996             break;
1997
1998     }
1999
2000     if (index >= lpwhr->nCustHeaders)
2001         index = -1;
2002
2003     TRACE("Return: %d\n", index);
2004     return index;
2005 }
2006
2007
2008 /***********************************************************************
2009  *           HTTP_InsertCustomHeader (internal)
2010  *
2011  * Insert header into array
2012  *
2013  */
2014 INT HTTP_InsertCustomHeader(LPWININETHTTPREQA lpwhr, LPHTTPHEADERA lpHdr)
2015 {
2016     INT count;
2017     LPHTTPHEADERA lph = NULL;
2018
2019     TRACE("--> %s: %s\n", lpHdr->lpszField, lpHdr->lpszValue);
2020     count = lpwhr->nCustHeaders + 1;
2021     if (count > 1)
2022         lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERA) * count);
2023     else
2024         lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERA) * count);
2025
2026     if (NULL != lph)
2027     {
2028         lpwhr->pCustHeaders = lph;
2029         lpwhr->pCustHeaders[count-1].lpszField = HTTP_strdup(lpHdr->lpszField);
2030         lpwhr->pCustHeaders[count-1].lpszValue = HTTP_strdup(lpHdr->lpszValue);
2031         lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
2032         lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
2033         lpwhr->nCustHeaders++;
2034     }
2035     else
2036     {
2037         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2038         count = 0;
2039     }
2040
2041     TRACE("%d <--\n", count-1);
2042     return count - 1;
2043 }
2044
2045
2046 /***********************************************************************
2047  *           HTTP_DeleteCustomHeader (internal)
2048  *
2049  * Delete header from array
2050  *
2051  */
2052 BOOL HTTP_DeleteCustomHeader(INT index)
2053 {
2054     FIXME("STUB\n");
2055     return FALSE;
2056 }
2057
2058 /***********************************************************************
2059  *          IsHostInProxyBypassList (@)
2060  *
2061  * Undocumented
2062  *
2063  */
2064 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
2065 {
2066    FIXME("STUB: flags=%ld host=%s length=%ld\n",flags,szHost,length);
2067    return FALSE;
2068 }