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