Removed a few dependencies on kernel32 functions.
[wine] / dlls / wininet / http.c
1 /* 
2  * Wininet - Http Implementation
3  *
4  * Copyright 1999 Corel Corporation
5  *
6  * Ulrich Czekalla
7  *
8  */
9
10 #include "config.h"
11
12 #include "windows.h"
13 #include "wininet.h"
14 #include "debugtools.h"
15 #include "winerror.h"
16 #include "heap.h"
17 #include "winsock.h"
18
19 #include <sys/types.h>
20 #ifdef HAVE_SYS_SOCKET_H
21 # include <sys/socket.h>
22 #endif
23 #include <netdb.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #include "internet.h"
30
31 DEFAULT_DEBUG_CHANNEL(wininet)
32
33 #define HTTPHEADER " HTTP/1.0"
34 #define HTTPHOSTHEADER "\r\nHost: "
35 #define MAXHOSTNAME 100
36 #define MAX_FIELD_VALUE_LEN 256
37 #define MAX_FIELD_LEN 256
38
39
40 #define HTTP_REFERER    "Referer"
41 #define HTTP_ACCEPT             "Accept"
42
43 #define HTTP_ADDHDR_FLAG_ADD                            0x20000000
44 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                     0x10000000
45 #define HTTP_ADDHDR_FLAG_COALESCE                       0x40000000
46 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA            0x40000000
47 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON        0x01000000
48 #define HTTP_ADDHDR_FLAG_REPLACE                        0x80000000
49 #define HTTP_ADDHDR_FLAG_REQ                            0x02000000
50
51
52 BOOL HTTP_OpenConnection(LPWININETHTTPREQA lpwhr);
53 int HTTP_WriteDataToStream(LPWININETHTTPREQA lpwhr, 
54         void *Buffer, int BytesToWrite);
55 int HTTP_ReadDataFromStream(LPWININETHTTPREQA lpwhr, 
56         void *Buffer, int BytesToRead);
57 BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQA lpwhr);
58 BOOL HTTP_ProcessHeader(LPWININETHTTPREQA lpwhr, LPCSTR field, LPCSTR value, DWORD dwModifier);
59 void HTTP_CloseConnection(LPWININETHTTPREQA lpwhr);
60 BOOL HTTP_InterpretHttpHeader(LPSTR buffer, LPSTR field, INT fieldlen, LPSTR value, INT valuelen);
61 INT HTTP_GetStdHeaderIndex(LPCSTR lpszField);
62 INT HTTP_InsertCustomHeader(LPWININETHTTPREQA lpwhr, LPHTTPHEADERA lpHdr);
63 INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQA lpwhr, LPCSTR lpszField);
64
65 /***********************************************************************
66  *           HttpAddRequestHeadersA (WININET.68)
67  *
68  * Adds one or more HTTP header to the request handler
69  *
70  * RETURNS
71  *    TRUE  on success
72  *    FALSE on failure
73  *
74  */
75 INTERNETAPI BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest, 
76         LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
77 {
78     LPSTR lpszStart;
79     LPSTR lpszEnd;
80     LPSTR buffer;
81     CHAR value[MAX_FIELD_VALUE_LEN], field[MAX_FIELD_LEN];
82     BOOL bSuccess = FALSE;
83     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
84
85     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
86     {
87         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
88         return FALSE;
89     }
90
91     buffer = HEAP_strdupA(GetProcessHeap(), 0, lpszHeader);
92     lpszStart = buffer;
93
94     do
95     {
96         lpszEnd = lpszStart;
97
98         while (*lpszEnd != '\0')
99         {
100             if (*lpszEnd == '\r' && *(lpszEnd + 1) == '\n')
101                  break;
102             lpszEnd++;
103         }
104
105         if (*lpszEnd == '\0')
106             break;
107
108         *lpszEnd = '\0';
109
110         if (HTTP_InterpretHttpHeader(lpszStart, field, MAX_FIELD_LEN, value, MAX_FIELD_VALUE_LEN))
111             bSuccess = HTTP_ProcessHeader(lpwhr, field, value, dwModifier | HTTP_ADDHDR_FLAG_REQ);
112
113         lpszStart = lpszEnd + 2; /* Jump over \0\n */
114
115     } while (bSuccess);
116
117     HeapFree(GetProcessHeap(), 0, buffer);
118     return bSuccess;
119 }
120
121
122 /***********************************************************************
123  *           HttpOpenRequestA (WININET.72)
124  *
125  * Open a HTTP request handle
126  *
127  * RETURNS
128  *    HINTERNET  a HTTP request handle on success
129  *    NULL       on failure
130  *
131  */
132 INTERNETAPI HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
133         LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
134         LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes, 
135         DWORD dwFlags, DWORD dwContext)
136 {
137     LPWININETHTTPSESSIONA lpwhs = (LPWININETHTTPSESSIONA) hHttpSession;
138     LPWININETAPPINFOA hIC = NULL;
139
140     TRACE("\n");
141
142     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
143     {
144         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
145         return FALSE;
146     }
147
148     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
149
150     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
151     {
152         WORKREQUEST workRequest;
153
154         workRequest.asyncall = HTTPOPENREQUESTA;
155         workRequest.HFTPSESSION = (DWORD)hHttpSession;
156         workRequest.LPSZVERB = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszVerb);
157         workRequest.LPSZOBJECTNAME = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszObjectName);
158         workRequest.LPSZVERSION = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszVersion);
159         workRequest.LPSZREFERRER = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszReferrer);
160         workRequest.LPSZACCEPTTYPES = (DWORD)lpszAcceptTypes;
161         workRequest.DWFLAGS = dwFlags;
162         workRequest.DWCONTEXT = dwContext;
163
164         return (HINTERNET)INTERNET_AsyncCall(&workRequest);
165     }
166     else
167     {
168         return HTTP_HttpOpenRequestA(hHttpSession, lpszVerb, lpszObjectName, 
169                 lpszVersion, lpszReferrer, lpszAcceptTypes, dwFlags, dwContext);
170     }
171 }
172
173
174 /***********************************************************************
175  *           HTTP_HttpOpenRequestA (internal)
176  *
177  * Open a HTTP request handle
178  *
179  * RETURNS
180  *    HINTERNET  a HTTP request handle on success
181  *    NULL       on failure
182  *
183  */
184 INTERNETAPI HINTERNET WINAPI HTTP_HttpOpenRequestA(HINTERNET hHttpSession,
185         LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
186         LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes, 
187         DWORD dwFlags, DWORD dwContext)
188 {
189     LPWININETHTTPSESSIONA lpwhs = (LPWININETHTTPSESSIONA) hHttpSession;
190     LPWININETAPPINFOA hIC = NULL;
191     LPWININETHTTPREQA lpwhr;
192
193     TRACE("\n");
194
195     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
196     {
197         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
198         return FALSE;
199     }
200
201     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
202
203     lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPREQA));
204     if (NULL == lpwhr)
205     {
206         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
207         return (HINTERNET) NULL;
208     }
209
210     lpwhr->hdr.htype = WH_HHTTPREQ;
211     lpwhr->hdr.lpwhparent = hHttpSession;
212     lpwhr->hdr.dwFlags = dwFlags;
213     lpwhr->hdr.dwContext = dwContext;
214
215     if (NULL != lpszObjectName && strlen(lpszObjectName))
216         lpwhr->lpszPath = HEAP_strdupA(GetProcessHeap(), 0, lpszObjectName);
217
218     if (NULL != lpszReferrer && strlen(lpszReferrer))
219         HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDHDR_FLAG_COALESCE);
220
221     //! FIXME
222     if (NULL != lpszAcceptTypes && strlen(*lpszAcceptTypes))
223         HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, *lpszAcceptTypes, HTTP_ADDHDR_FLAG_COALESCE);
224
225     if (NULL == lpszVerb)
226         lpwhr->lpszVerb = HEAP_strdupA(GetProcessHeap(), 0, "GET");
227     else if (strlen(lpszVerb))
228         lpwhr->lpszVerb = HEAP_strdupA(GetProcessHeap(), 0, lpszVerb);
229
230     if (NULL != lpszReferrer)
231     {
232         char buf[MAXHOSTNAME];
233         URL_COMPONENTSA UrlComponents;
234
235         UrlComponents.lpszExtraInfo = NULL;
236         UrlComponents.lpszPassword = NULL;
237         UrlComponents.lpszScheme = NULL;
238         UrlComponents.lpszUrlPath = NULL;
239         UrlComponents.lpszUserName = NULL;
240         UrlComponents.lpszHostName = buf;
241         UrlComponents.dwHostNameLength = MAXHOSTNAME;
242
243         InternetCrackUrlA(lpszReferrer, 0, 0, &UrlComponents);
244         if (strlen(UrlComponents.lpszHostName))
245             lpwhr->lpszHostName = HEAP_strdupA(GetProcessHeap(), 0, UrlComponents.lpszHostName);
246     } else {
247         lpwhr->lpszHostName = HEAP_strdupA(GetProcessHeap(), 0,
248                                            lpwhs->lpszServerName);
249     }
250
251     if (hIC->lpfnStatusCB)
252     {
253         INTERNET_ASYNC_RESULT iar;
254
255         iar.dwResult = (DWORD)lpwhr;
256         iar.dwError = ERROR_SUCCESS;
257
258         hIC->lpfnStatusCB(hHttpSession, dwContext, INTERNET_STATUS_HANDLE_CREATED,
259              &iar, sizeof(INTERNET_ASYNC_RESULT));
260     }
261
262     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
263     {
264         INTERNET_ASYNC_RESULT iar;
265                
266         iar.dwResult = (DWORD)lpwhr;
267         iar.dwError = lpwhr ? ERROR_SUCCESS : INTERNET_GetLastError();
268         hIC->lpfnStatusCB(hHttpSession, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
269             &iar, sizeof(INTERNET_ASYNC_RESULT));
270     }
271
272     return (HINTERNET) lpwhr;
273 }
274
275
276 /***********************************************************************
277  *           HttpQueryInfoA (WININET.74)
278  *
279  * Queries for information about an HTTP request
280  *
281  * RETURNS
282  *    TRUE  on success
283  *    FALSE on failure
284  *
285  */
286 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
287         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
288 {
289     LPHTTPHEADERA lphttpHdr = NULL; 
290     BOOL bSuccess = FALSE;
291     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
292                 
293     TRACE("(0x%08lx)--> %ld\n", dwInfoLevel, dwInfoLevel);
294
295     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
296     {
297         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
298         return FALSE;
299     }
300
301     /* Find requested header structure */
302     if ((dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK) == HTTP_QUERY_CUSTOM)
303     {
304         INT index = HTTP_GetCustomHeaderIndex(lpwhr, (LPSTR)lpBuffer);
305
306         if (index < 0)
307             goto lend;
308
309         lphttpHdr = &lpwhr->pCustHeaders[index];
310     }
311     else
312     {
313         INT index = dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK;
314
315         if (index == HTTP_QUERY_RAW_HEADERS_CRLF || index == HTTP_QUERY_RAW_HEADERS)
316         {
317             INT i, delim, size = 0, cnt = 0;
318
319             delim = index == HTTP_QUERY_RAW_HEADERS_CRLF ? 2 : 1;
320
321            /* Calculate length of custom reuqest headers */
322            for (i = 0; i < lpwhr->nCustHeaders; i++)
323            {
324                if ((~lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST) && lpwhr->pCustHeaders[i].lpszField &&
325                    lpwhr->pCustHeaders[i].lpszValue)
326                {
327                   size += strlen(lpwhr->pCustHeaders[i].lpszField) + 
328                        strlen(lpwhr->pCustHeaders[i].lpszValue) + delim + 2;
329                }
330            }
331
332            /* Calculate the length of stadard request headers */
333            for (i = 0; i <= HTTP_QUERY_MAX; i++)
334            {
335               if ((~lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST) && lpwhr->StdHeaders[i].lpszField && 
336                    lpwhr->StdHeaders[i].lpszValue)
337               {
338                  size += strlen(lpwhr->StdHeaders[i].lpszField) + 
339                     strlen(lpwhr->StdHeaders[i].lpszValue) + delim + 2;
340               }
341            }
342
343            size += delim;
344
345            if (size + 1 > *lpdwBufferLength)
346            {
347               *lpdwBufferLength = size + 1;
348               INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
349               goto lend;
350            }
351
352            /* Append standard request heades */
353            for (i = 0; i <= HTTP_QUERY_MAX; i++)
354            {
355                if ((~lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST) &&
356                                            lpwhr->StdHeaders[i].lpszField &&
357                                            lpwhr->StdHeaders[i].lpszValue)
358                {
359                    cnt += sprintf(lpBuffer + cnt, "%s: %s%s", lpwhr->StdHeaders[i].lpszField, lpwhr->StdHeaders[i].lpszValue,
360                           index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "\0");
361                }
362             }
363
364             /* Append custom request heades */
365             for (i = 0; i < lpwhr->nCustHeaders; i++)
366             {
367                 if ((~lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST) &&
368                                                 lpwhr->pCustHeaders[i].lpszField &&
369                                                 lpwhr->pCustHeaders[i].lpszValue)
370                 {
371                    cnt += sprintf(lpBuffer + cnt, "%s: %s%s", 
372                     lpwhr->pCustHeaders[i].lpszField, lpwhr->pCustHeaders[i].lpszValue,
373                                         index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "\0");
374                 }
375             }
376
377             strcpy(lpBuffer + cnt, index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "");
378
379            *lpdwBufferLength = cnt + delim;
380            bSuccess = TRUE;
381                 goto lend;
382         }
383         else if (index >= 0 && index <= HTTP_QUERY_MAX && lpwhr->StdHeaders[index].lpszValue)
384         { 
385             lphttpHdr = &lpwhr->StdHeaders[index];
386         }
387         else
388             goto lend;
389     }
390
391     /* Ensure header satisifies requested attributes */
392     if ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) && 
393             (~lphttpHdr->wFlags & HDR_ISREQUEST))
394         goto lend;
395      
396     /* coalesce value to reuqested type */
397     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER)
398     {
399        *(int *)lpBuffer = atoi(lphttpHdr->lpszValue);
400            bSuccess = TRUE;
401     }
402     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME)
403     {
404         time_t tmpTime;
405         struct tm tmpTM;
406         SYSTEMTIME *STHook;
407              
408         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
409
410         tmpTM = *gmtime(&tmpTime);
411         STHook = (SYSTEMTIME *) lpBuffer;
412         if(STHook==NULL)
413             goto lend;
414
415             STHook->wDay = tmpTM.tm_mday;
416             STHook->wHour = tmpTM.tm_hour;
417             STHook->wMilliseconds = 0;
418             STHook->wMinute = tmpTM.tm_min;
419             STHook->wDayOfWeek = tmpTM.tm_wday;
420             STHook->wMonth = tmpTM.tm_mon + 1;
421             STHook->wSecond = tmpTM.tm_sec;
422             STHook->wYear = tmpTM.tm_year;
423
424             bSuccess = TRUE;
425     }
426     else if (dwInfoLevel & HTTP_QUERY_FLAG_COALESCE)
427     {
428             if (*lpdwIndex >= lphttpHdr->wCount)
429                 {
430                 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
431                 }
432             else
433             {
434             //! Copy strncpy(lpBuffer, lphttpHdr[*lpdwIndex], len);
435             (*lpdwIndex)++;
436             }
437     }
438     else
439     {
440         INT len = strlen(lphttpHdr->lpszValue);
441
442         if (len + 1 > *lpdwBufferLength)
443         {
444             *lpdwBufferLength = len + 1;
445             INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
446             goto lend;
447         }
448
449         strncpy(lpBuffer, lphttpHdr->lpszValue, len);
450         *lpdwBufferLength = len;
451         bSuccess = TRUE; 
452     }
453
454 lend:
455     TRACE("%d <--\n", bSuccess);
456     return bSuccess;
457 }
458
459 /***********************************************************************
460  *           HttpSendRequestA (WININET.76)
461  *
462  * Sends the specified request to the HTTP server
463  *
464  * RETURNS
465  *    TRUE  on success
466  *    FALSE on failure
467  *
468  */
469 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
470         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
471 {       
472     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
473     LPWININETHTTPSESSIONA lpwhs = NULL;
474     LPWININETAPPINFOA hIC = NULL;
475
476     TRACE("0x%08lx\n", (unsigned long)hHttpRequest);
477
478     if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
479     {
480         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
481         return FALSE;
482     }
483
484     lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
485     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
486     {
487         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
488         return FALSE;
489     }
490
491     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
492     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
493     {
494         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
495         return FALSE;
496     }
497
498     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
499     {
500         WORKREQUEST workRequest;
501
502         workRequest.asyncall = HTTPSENDREQUESTA;
503         workRequest.HFTPSESSION = (DWORD)hHttpRequest;
504         workRequest.LPSZHEADER = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszHeaders);
505         workRequest.DWHEADERLENGTH = dwHeaderLength;
506         workRequest.LPOPTIONAL = (DWORD)lpOptional;
507         workRequest.DWOPTIONALLENGTH = dwOptionalLength;
508
509         return INTERNET_AsyncCall(&workRequest);
510     }
511     else
512     {
513         return HTTP_HttpSendRequestA(hHttpRequest, lpszHeaders, 
514                 dwHeaderLength, lpOptional, dwOptionalLength);
515     }
516 }
517
518
519 /***********************************************************************
520  *           HTTP_HttpSendRequestA (internal)
521  *
522  * Sends the specified request to the HTTP server
523  *
524  * RETURNS
525  *    TRUE  on success
526  *    FALSE on failure
527  *
528  */
529 BOOL WINAPI HTTP_HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
530         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
531 {
532     INT cnt;
533     INT i;
534     BOOL bSuccess = FALSE;
535     LPSTR requestString = NULL;
536     INT requestStringLen;
537     INT headerLength = 0;
538     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
539     LPWININETHTTPSESSIONA lpwhs = NULL;
540     LPWININETAPPINFOA hIC = NULL;
541
542     TRACE("0x%08lx\n", (ULONG)hHttpRequest);
543
544     /* Verify our tree of internet handles */
545     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
546     {
547         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
548         return FALSE;
549     }
550
551     lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
552     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
553     {
554         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
555         return FALSE;
556     }
557
558     hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
559     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
560     {
561         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
562         return FALSE;
563     }
564
565     /* Clear any error information */
566     INTERNET_SetLastError(0);
567
568     /* We must have a verb */
569     if (NULL == lpwhr->lpszVerb)
570     {
571             goto lend;
572     }
573
574     /* If we don't have a path we set it to root */
575     if (NULL == lpwhr->lpszPath)
576         lpwhr->lpszPath = HEAP_strdupA(GetProcessHeap(), 0, "/");
577
578     /* Calculate length of request string */
579     requestStringLen = 
580         strlen(lpwhr->lpszVerb) +
581         strlen(lpwhr->lpszPath) +
582         (lpwhr->lpszHostName ? (strlen(HTTPHOSTHEADER) + strlen(lpwhr->lpszHostName)) : 0) +
583         strlen(HTTPHEADER) +
584         5; /* " \r\n\r\n" */
585
586     /* Add length of passed headers */
587     if (lpszHeaders)
588     {
589         headerLength = -1 == dwHeaderLength ?  strlen(lpszHeaders) : dwHeaderLength; 
590         requestStringLen += headerLength +  2; /* \r\n */
591     }
592
593     /* Calculate length of custom request headers */
594     for (i = 0; i < lpwhr->nCustHeaders; i++)
595     {
596             if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
597             {
598             requestStringLen += strlen(lpwhr->pCustHeaders[i].lpszField) + 
599                 strlen(lpwhr->pCustHeaders[i].lpszValue) + 4; /*: \r\n */
600             }
601     }
602
603     /* Calculate the length of standard request headers */
604     for (i = 0; i <= HTTP_QUERY_MAX; i++)
605     {
606        if (lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST)
607        {
608           requestStringLen += strlen(lpwhr->StdHeaders[i].lpszField) + 
609              strlen(lpwhr->StdHeaders[i].lpszValue) + 4; /*: \r\n */
610        }
611     }
612
613     /* Allocate string to hold entire request */
614     requestString = HeapAlloc(GetProcessHeap(), 0, requestStringLen + 1);
615     if (NULL == requestString)
616     {
617         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
618         goto lend;
619     }
620
621     /* Build request string */
622     cnt = sprintf(requestString, "%s %s%s%s",
623         lpwhr->lpszVerb,
624         lpwhr->lpszPath,
625         lpwhr->lpszHostName ? (HTTPHEADER HTTPHOSTHEADER) : HTTPHEADER,
626         lpwhr->lpszHostName ? lpwhr->lpszHostName : "");
627
628     /* Append standard request headers */
629     for (i = 0; i <= HTTP_QUERY_MAX; i++)
630     {
631        if (lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST)
632        {
633            cnt += sprintf(requestString + cnt, "\r\n%s: %s", 
634                lpwhr->StdHeaders[i].lpszField, lpwhr->StdHeaders[i].lpszValue);
635        }
636     }
637
638     /* Append custom request heades */
639     for (i = 0; i < lpwhr->nCustHeaders; i++)
640     {
641        if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
642        {
643            cnt += sprintf(requestString + cnt, "\r\n%s: %s", 
644                lpwhr->pCustHeaders[i].lpszField, lpwhr->pCustHeaders[i].lpszValue);
645        }
646     }
647
648     /* Append passed request headers */
649     if (lpszHeaders)
650     {
651         strcpy(requestString + cnt, "\r\n");
652         cnt += 2;
653         strcpy(requestString + cnt, lpszHeaders);
654         cnt += headerLength;
655     }
656
657     /* Set termination string for request */
658     strcpy(requestString + cnt, "\r\n\r\n");
659
660     if (hIC->lpfnStatusCB)
661         hIC->lpfnStatusCB(hHttpRequest, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
662
663     TRACE("(%s) len(%d)\n", requestString, requestStringLen);
664     /* Send the request and store the results */
665     if (!HTTP_OpenConnection(lpwhr))
666         goto lend;
667
668     cnt = INTERNET_WriteDataToStream(lpwhr->nSocketFD, requestString, requestStringLen);
669
670     if (cnt < 0)
671         goto lend;
672
673     if (HTTP_GetResponseHeaders(lpwhr))
674             bSuccess = TRUE;
675
676 lend:
677
678     if (requestString)
679         HeapFree(GetProcessHeap(), 0, requestString);
680
681     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC  && hIC->lpfnStatusCB)
682     {
683         INTERNET_ASYNC_RESULT iar;
684                
685         iar.dwResult = (DWORD)bSuccess;
686         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
687         hIC->lpfnStatusCB(hHttpRequest, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
688             &iar, sizeof(INTERNET_ASYNC_RESULT));
689     }
690
691     TRACE("<--\n");
692     return bSuccess;
693 }
694
695
696 /***********************************************************************
697  *           HTTP_Connect  (internal)
698  *
699  * Create http session handle
700  *
701  * RETURNS
702  *   HINTERNET a session handle on success
703  *   NULL on failure
704  *
705  */
706 HINTERNET HTTP_Connect(HINTERNET hInternet, LPCSTR lpszServerName, 
707         INTERNET_PORT nServerPort, LPCSTR lpszUserName,
708         LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext)
709 {
710     BOOL bSuccess = FALSE;
711     LPWININETAPPINFOA hIC = NULL;
712     LPWININETHTTPSESSIONA lpwhs = NULL;
713
714     TRACE("\n");
715
716     if (((LPWININETHANDLEHEADER)hInternet)->htype != WH_HINIT)
717         goto lerror;
718
719     hIC = (LPWININETAPPINFOA) hInternet;
720
721     lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPSESSIONA));
722     if (NULL == lpwhs)
723     {
724         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
725         goto lerror;
726     }
727
728     if (hIC->lpfnStatusCB)
729         hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_RESOLVING_NAME,
730            (LPVOID)lpszServerName, strlen(lpszServerName));
731
732     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
733         nServerPort = INTERNET_DEFAULT_HTTP_PORT;
734
735     if (!GetAddress(lpszServerName, nServerPort, &lpwhs->phostent, &lpwhs->socketAddress))
736     {
737             INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
738             goto lerror;
739     }
740
741     if (hIC->lpfnStatusCB)
742         hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_NAME_RESOLVED,
743            (LPVOID)lpszServerName, strlen(lpszServerName));
744
745     lpwhs->hdr.htype = WH_HHTTPSESSION;
746     lpwhs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet;
747     lpwhs->hdr.dwFlags = dwFlags;
748     lpwhs->hdr.dwContext = dwContext;
749     if (NULL != lpszServerName)
750         lpwhs->lpszServerName = HEAP_strdupA(GetProcessHeap(), 0, lpszServerName);
751     if (NULL != lpszUserName)
752         lpwhs->lpszUserName = HEAP_strdupA(GetProcessHeap(), 0, lpszUserName);
753     lpwhs->nServerPort = nServerPort;
754
755     if (hIC->lpfnStatusCB)
756     {
757         INTERNET_ASYNC_RESULT iar;
758
759         iar.dwResult = (DWORD)lpwhs;
760         iar.dwError = ERROR_SUCCESS;
761
762         hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_HANDLE_CREATED,
763              &iar, sizeof(INTERNET_ASYNC_RESULT));
764     }
765
766     bSuccess = TRUE;
767
768 lerror:
769     if (!bSuccess && lpwhs)
770     {
771         HeapFree(GetProcessHeap(), 0, lpwhs);
772         lpwhs = NULL;
773     }
774
775     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
776     {
777         INTERNET_ASYNC_RESULT iar;
778                
779         iar.dwResult = (DWORD)lpwhs;
780         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
781         hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
782             &iar, sizeof(INTERNET_ASYNC_RESULT));
783     }
784 TRACE("<--\n");
785     return (HINTERNET)lpwhs;
786 }
787
788
789 /***********************************************************************
790  *           HTTP_OpenConnection (internal)
791  *
792  * Connect to a web server
793  *
794  * RETURNS
795  *
796  *   TRUE  on success
797  *   FALSE on failure
798  */
799 BOOL HTTP_OpenConnection(LPWININETHTTPREQA lpwhr)
800 {
801     BOOL bSuccess = FALSE;
802     INT result;
803     LPWININETHTTPSESSIONA lpwhs;
804
805     TRACE("\n");
806
807     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
808     {
809         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
810         goto lend;
811     }
812
813     lpwhs = (LPWININETHTTPSESSIONA)lpwhr->hdr.lpwhparent;
814
815     lpwhr->nSocketFD = socket(lpwhs->phostent->h_addrtype,SOCK_STREAM,0);
816     if (INVALID_SOCKET == lpwhr->nSocketFD)
817     {
818         WARN("Socket creation failed\n");
819         goto lend;
820     }
821
822     result = connect(lpwhr->nSocketFD, (struct sockaddr *)&lpwhs->socketAddress,
823         sizeof(lpwhs->socketAddress));
824
825     if (SOCKET_ERROR == result)
826     {
827        WARN("Unable to connect to host (%s)\n", strerror(errno));
828        goto lend;
829     }
830
831     bSuccess = TRUE;
832
833 lend:
834     TRACE(": %d\n", bSuccess);
835     return bSuccess;
836 }
837
838
839 /***********************************************************************
840  *           HTTP_GetResponseHeaders (internal)
841  *
842  * Read server response
843  *
844  * RETURNS
845  *
846  *   TRUE  on success
847  *   FALSE on error
848  */
849 BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQA lpwhr)
850 {
851     INT cbreaks = 0;
852     CHAR buffer[MAX_REPLY_LEN];
853     DWORD buflen = MAX_REPLY_LEN;
854     BOOL bSuccess = FALSE;
855     CHAR value[MAX_FIELD_VALUE_LEN], field[MAX_FIELD_LEN];
856
857     TRACE("\n");
858
859     if (INVALID_SOCKET == lpwhr->nSocketFD)
860         goto lend;
861
862     /* 
863      * We should first receive 'HTTP/1.x nnn' where nnn is the status code.
864      */
865     if (!INTERNET_GetNextLine(lpwhr->nSocketFD, buffer, &buflen))
866         goto lend;
867
868     if (strncmp(buffer, "HTTP", 4) != 0)
869         goto lend;
870         
871     buffer[12]='\0';
872     HTTP_ProcessHeader(lpwhr, "Status", buffer+9, (HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE));
873
874     /* Parse each response line */
875     do
876     {
877         buflen = MAX_REPLY_LEN;
878         if (INTERNET_GetNextLine(lpwhr->nSocketFD, buffer, &buflen))
879         {
880             if (!HTTP_InterpretHttpHeader(buffer, field, MAX_FIELD_LEN, value, MAX_FIELD_VALUE_LEN))
881                 break;
882
883             HTTP_ProcessHeader(lpwhr, field, value, (HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE));
884         }
885         else
886         {
887             cbreaks++;
888             if (cbreaks >= 2)
889                break;
890         }
891     }while(1);
892
893     bSuccess = TRUE;
894
895 lend:
896
897     return bSuccess;
898 }
899
900
901 /***********************************************************************
902  *           HTTP_InterpretHttpHeader (internal)
903  *
904  * Parse server response
905  *
906  * RETURNS
907  *
908  *   TRUE  on success
909  *   FALSE on error
910  */
911 INT stripSpaces(LPCSTR lpszSrc, LPSTR lpszStart, INT *len)
912 {
913     LPCSTR lpsztmp;
914     INT srclen;
915
916     srclen = 0;
917
918     while (*lpszSrc == ' ' && *lpszSrc != '\0')
919         lpszSrc++;
920         
921     lpsztmp = lpszSrc;
922     while(*lpsztmp != '\0')
923     {
924         if (*lpsztmp != ' ')
925             srclen = lpsztmp - lpszSrc + 1;
926
927         lpsztmp++;
928     }
929
930     *len = min(*len, srclen);
931     strncpy(lpszStart, lpszSrc, *len);
932     lpszStart[*len] = '\0';
933
934     return *len;
935 }
936
937
938 BOOL HTTP_InterpretHttpHeader(LPSTR buffer, LPSTR field, INT fieldlen, LPSTR value, INT valuelen)
939 {
940     CHAR *pd;
941     BOOL bSuccess = FALSE;
942
943     TRACE("\n");
944
945     *field = '\0';
946     *value = '\0';
947
948     pd = strchr(buffer, ':');
949     if (pd)
950     {
951         *pd = '\0';
952         if (stripSpaces(buffer, field, &fieldlen) > 0)
953         {
954             if (stripSpaces(pd+1, value, &valuelen) > 0)
955                 bSuccess = TRUE;
956         }
957     }
958
959     TRACE("%d: field(%s) Value(%s)\n", bSuccess, field, value);
960     return bSuccess;
961 }
962
963
964 /***********************************************************************
965  *           HTTP_GetStdHeaderIndex (internal)
966  *
967  * Lookup field index in standard http header array
968  *
969  * FIXME: This should be stuffed into a hash table
970  */
971 INT HTTP_GetStdHeaderIndex(LPCSTR lpszField)
972 {
973     INT index = -1;
974
975     if (!strcasecmp(lpszField, "Content-Length"))
976         index = HTTP_QUERY_CONTENT_LENGTH;
977     else if (!strcasecmp(lpszField,"Status"))
978         index = HTTP_QUERY_STATUS_CODE;
979     else if (!strcasecmp(lpszField,"Content-Type"))
980         index = HTTP_QUERY_CONTENT_TYPE;
981     else if (!strcasecmp(lpszField,"Last-Modified"))
982         index = HTTP_QUERY_LAST_MODIFIED;
983     else if (!strcasecmp(lpszField,"Location"))
984         index = HTTP_QUERY_LOCATION;
985     else if (!strcasecmp(lpszField,"Accept"))
986         index = HTTP_QUERY_ACCEPT;
987     else if (!strcasecmp(lpszField,"Referer"))
988         index = HTTP_QUERY_REFERER;
989     else if (!strcasecmp(lpszField,"Content-Transfer-Encoding"))
990         index = HTTP_QUERY_CONTENT_TRANSFER_ENCODING;
991     else if (!strcasecmp(lpszField,"Date"))
992         index = HTTP_QUERY_DATE;
993     else if (!strcasecmp(lpszField,"Server"))
994         index = HTTP_QUERY_SERVER;
995     else if (!strcasecmp(lpszField,"Connection"))
996         index = HTTP_QUERY_CONNECTION;
997     else if (!strcasecmp(lpszField,"ETag"))
998         index = HTTP_QUERY_ETAG;
999     else if (!strcasecmp(lpszField,"Accept-Ranges"))
1000         index = HTTP_QUERY_ACCEPT_RANGES;
1001     else if (!strcasecmp(lpszField,"Expires"))
1002         index = HTTP_QUERY_EXPIRES;
1003     else if (!strcasecmp(lpszField,"Mime-Version"))
1004         index = HTTP_QUERY_MIME_VERSION;
1005     else
1006     {
1007        FIXME("Couldn't find %s in standard header table\n", lpszField);
1008     }
1009
1010     return index;
1011 }
1012
1013
1014 /***********************************************************************
1015  *           HTTP_ProcessHeader (internal)
1016  *
1017  * Stuff header into header tables according to <dwModifier>
1018  *
1019  */
1020
1021 #define COALESCEFLASG (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
1022
1023 BOOL HTTP_ProcessHeader(LPWININETHTTPREQA lpwhr, LPCSTR field, LPCSTR value, DWORD dwModifier)
1024 {
1025     LPHTTPHEADERA lphttpHdr = NULL;
1026     BOOL bSuccess = FALSE;
1027     INT index;
1028
1029     TRACE("%s:%s - 0x%08x\n", field, value, (unsigned int)dwModifier);
1030
1031     /* Adjust modifier flags */
1032     if (dwModifier & COALESCEFLASG)
1033         dwModifier |= HTTP_ADDHDR_FLAG_ADD;
1034
1035     /* Try to get index into standard header array */
1036     index = HTTP_GetStdHeaderIndex(field);
1037     if (index >= 0)
1038     {
1039         lphttpHdr = &lpwhr->StdHeaders[index];
1040     }
1041     else /* Find or create new custom header */
1042     {
1043         index = HTTP_GetCustomHeaderIndex(lpwhr, field);
1044         if (index >= 0)
1045         {
1046             if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
1047             {
1048                 return FALSE;
1049             }
1050             lphttpHdr = &lpwhr->pCustHeaders[index];
1051         }
1052         else
1053         {
1054             HTTPHEADERA hdr;
1055
1056             hdr.lpszField = (LPSTR)field;
1057             hdr.lpszValue = (LPSTR)value;
1058             hdr.wFlags = hdr.wCount = 0;
1059
1060             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
1061                 hdr.wFlags |= HDR_ISREQUEST;
1062
1063             index = HTTP_InsertCustomHeader(lpwhr, &hdr);
1064             return index >= 0;
1065         }
1066     }
1067  
1068     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
1069         lphttpHdr->wFlags |= HDR_ISREQUEST;
1070     else
1071         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
1072   
1073     if (!lphttpHdr->lpszValue && (dwModifier & (HTTP_ADDHDR_FLAG_ADD|HTTP_ADDHDR_FLAG_ADD_IF_NEW)))
1074     {
1075         INT slen;
1076
1077         if (!lpwhr->StdHeaders[index].lpszField)
1078         {
1079             lphttpHdr->lpszField = HEAP_strdupA(GetProcessHeap(), 0, field);
1080
1081             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
1082                 lphttpHdr->wFlags |= HDR_ISREQUEST;
1083         }
1084
1085         slen = strlen(value) + 1;
1086         lphttpHdr->lpszValue = HeapAlloc(GetProcessHeap(), 0, slen);
1087         if (lphttpHdr->lpszValue)
1088         {
1089             memcpy(lphttpHdr->lpszValue, value, slen);
1090             bSuccess = TRUE;
1091         }
1092         else
1093         {
1094             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1095         }
1096     }
1097     else if (lphttpHdr->lpszValue) 
1098     {
1099         if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE) 
1100         {
1101             LPSTR lpsztmp;
1102             INT len;
1103
1104             len = strlen(value);
1105
1106             if (len <= 0)
1107             {
1108                 //! if custom header delete from array
1109                 HeapFree(GetProcessHeap(), 0, lphttpHdr->lpszValue);
1110                 lphttpHdr->lpszValue = NULL;
1111                 bSuccess = TRUE;
1112             }
1113             else
1114             {
1115                 lpsztmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  lphttpHdr->lpszValue, len+1);
1116                 if (lpsztmp)
1117                 {
1118                     lphttpHdr->lpszValue = lpsztmp;
1119                     strcpy(lpsztmp, value);
1120                     bSuccess = TRUE;
1121                 }
1122                 else
1123                 {
1124                     INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1125                 }
1126             }
1127         }
1128         else if (dwModifier & COALESCEFLASG)
1129         {
1130             LPSTR lpsztmp;
1131             CHAR ch = 0;
1132             INT len = 0;
1133             INT origlen = strlen(lphttpHdr->lpszValue);
1134             INT valuelen = strlen(value);
1135
1136             if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
1137             {
1138                 ch = ',';
1139                 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
1140             }
1141             else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
1142             {
1143                 ch = ';';
1144                 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
1145             }
1146
1147             len = origlen + valuelen + (ch > 0) ? 1 : 0;
1148
1149             lpsztmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  lphttpHdr->lpszValue, len+1);
1150             if (lpsztmp)
1151             {
1152                 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */ 
1153                 if (ch > 0)
1154                 {
1155                     lphttpHdr->lpszValue[origlen] = ch;
1156                     origlen++;
1157                 }
1158
1159                 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen);
1160                 lphttpHdr->lpszValue[len] = '\0';
1161                 bSuccess = TRUE;
1162             }
1163             else
1164             {
1165                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1166             }
1167         }
1168     }
1169
1170     return bSuccess;
1171 }
1172
1173
1174 /***********************************************************************
1175  *           HTTP_CloseConnection (internal)
1176  *
1177  * Close socket connection
1178  *
1179  */
1180 VOID HTTP_CloseConnection(LPWININETHTTPREQA lpwhr)
1181 {
1182         if (lpwhr->nSocketFD != INVALID_SOCKET)
1183         {
1184                 close(lpwhr->nSocketFD);
1185                 lpwhr->nSocketFD = INVALID_SOCKET;
1186         }
1187 }
1188
1189
1190 /***********************************************************************
1191  *           HTTP_CloseHTTPRequestHandle (internal)
1192  *
1193  * Deallocate request handle
1194  *
1195  */
1196 void HTTP_CloseHTTPRequestHandle(LPWININETHTTPREQA lpwhr)
1197 {
1198     int i;
1199
1200     TRACE("\n");
1201
1202     if (lpwhr->nSocketFD != INVALID_SOCKET)
1203         HTTP_CloseConnection(lpwhr);
1204
1205     if (lpwhr->lpszPath)
1206         HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1207     if (lpwhr->lpszVerb)
1208         HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1209     if (lpwhr->lpszHostName)
1210         HeapFree(GetProcessHeap(), 0, lpwhr->lpszHostName);
1211
1212     for (i = 0; i <= HTTP_QUERY_MAX; i++)
1213     {
1214            if (lpwhr->StdHeaders[i].lpszField)
1215             HeapFree(GetProcessHeap(), 0, lpwhr->StdHeaders[i].lpszField);
1216            if (lpwhr->StdHeaders[i].lpszValue)
1217             HeapFree(GetProcessHeap(), 0, lpwhr->StdHeaders[i].lpszValue);
1218     }
1219
1220     for (i = 0; i < lpwhr->nCustHeaders; i++)
1221     {
1222            if (lpwhr->pCustHeaders[i].lpszField)
1223             HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1224            if (lpwhr->pCustHeaders[i].lpszValue)
1225             HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1226     }
1227
1228     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1229     HeapFree(GetProcessHeap(), 0, lpwhr);
1230 }
1231
1232
1233 /***********************************************************************
1234  *           HTTP_CloseHTTPSessionHandle (internal)
1235  *
1236  * Deallocate session handle
1237  *
1238  */
1239 void HTTP_CloseHTTPSessionHandle(LPWININETHTTPSESSIONA lpwhs)
1240 {
1241     TRACE("\n");
1242
1243     if (lpwhs->lpszServerName)
1244         HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1245     if (lpwhs->lpszUserName)
1246         HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
1247     HeapFree(GetProcessHeap(), 0, lpwhs);
1248 }
1249
1250
1251 /***********************************************************************
1252  *           HTTP_GetCustomHeaderIndex (internal)
1253  *
1254  * Return index of custom header from header array
1255  *
1256  */
1257 INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQA lpwhr, LPCSTR lpszField)
1258 {
1259     INT index;
1260
1261     TRACE("%s\n", lpszField);
1262
1263     for (index = 0; index < lpwhr->nCustHeaders; index++)
1264     {
1265         if (!strcasecmp(lpwhr->pCustHeaders[index].lpszField, lpszField))
1266             break;
1267
1268     }
1269
1270     if (index >= lpwhr->nCustHeaders)
1271         index = -1;
1272
1273     TRACE("Return: %d\n", index);
1274     return index;
1275 }
1276
1277
1278 /***********************************************************************
1279  *           HTTP_InsertCustomHeader (internal)
1280  *
1281  * Insert header into array
1282  *
1283  */
1284 INT HTTP_InsertCustomHeader(LPWININETHTTPREQA lpwhr, LPHTTPHEADERA lpHdr)
1285 {
1286     INT count;
1287     LPHTTPHEADERA lph = NULL;
1288
1289     TRACE("%s: %s\n", lpHdr->lpszField, lpHdr->lpszValue);
1290     count = lpwhr->nCustHeaders + 1;
1291     if (count > 1)
1292         lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERA) * count);
1293     else
1294         lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERA) * count);
1295
1296     if (NULL != lph)
1297     {
1298         lpwhr->pCustHeaders = lph;
1299         lpwhr->pCustHeaders[count-1].lpszField = HEAP_strdupA(GetProcessHeap(), 0, lpHdr->lpszField);
1300         lpwhr->pCustHeaders[count-1].lpszValue = HEAP_strdupA(GetProcessHeap(), 0, lpHdr->lpszValue);
1301         lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
1302         lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
1303         lpwhr->nCustHeaders++;
1304     }
1305     else
1306     {
1307         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1308         count = 0;
1309     }
1310
1311     TRACE("%d <--\n", count-1);
1312     return count - 1;
1313 }
1314
1315
1316 /***********************************************************************
1317  *           HTTP_DeleteCustomHeader (internal)
1318  *
1319  * Delete header from array
1320  *
1321  */
1322 BOOL HTTP_DeleteCustomHeader(INT index)
1323 {
1324     TRACE("\n");
1325     return FALSE;
1326 }