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