2 * Copyright 2008 Hans Leidekker for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include "wine/port.h"
21 #include "wine/debug.h"
30 #include "winhttp_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(winhttp
);
34 static void free_header( header_t
*header
)
36 heap_free( header
->field
);
37 heap_free( header
->value
);
41 static BOOL
valid_token_char( WCHAR c
)
43 if (c
< 32 || c
== 127) return FALSE
;
62 static header_t
*parse_header( LPCWSTR string
)
69 if (!(q
= strchrW( p
, ':' )))
71 WARN("no ':' in line %s\n", debugstr_w(string
));
76 WARN("empty field name in line %s\n", debugstr_w(string
));
81 if (!valid_token_char( *p
))
83 WARN("invalid character in field name %s\n", debugstr_w(string
));
89 if (!(header
= heap_alloc_zero( sizeof(header_t
) ))) return NULL
;
90 if (!(header
->field
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) )))
95 memcpy( header
->field
, string
, len
* sizeof(WCHAR
) );
96 header
->field
[len
] = 0;
98 q
++; /* skip past colon */
99 while (*q
== ' ') q
++;
102 WARN("no value in line %s\n", debugstr_w(string
));
106 if (!(header
->value
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) )))
108 free_header( header
);
111 memcpy( header
->value
, q
, len
* sizeof(WCHAR
) );
112 header
->value
[len
] = 0;
117 static int get_header_index( request_t
*request
, LPCWSTR field
, int requested_index
, BOOL request_only
)
121 TRACE("%s\n", debugstr_w(field
));
123 for (index
= 0; index
< request
->num_headers
; index
++)
125 if (strcmpiW( request
->headers
[index
].field
, field
)) continue;
126 if (request_only
&& !request
->headers
[index
].is_request
) continue;
127 if (!request_only
&& request
->headers
[index
].is_request
) continue;
129 if (!requested_index
) break;
132 if (index
>= request
->num_headers
) index
= -1;
133 TRACE("returning %d\n", index
);
137 static BOOL
insert_header( request_t
*request
, header_t
*header
)
142 TRACE("inserting %s: %s\n", debugstr_w(header
->field
), debugstr_w(header
->value
));
144 count
= request
->num_headers
+ 1;
146 hdrs
= heap_realloc_zero( request
->headers
, sizeof(header_t
) * count
);
148 hdrs
= heap_alloc_zero( sizeof(header_t
) * count
);
152 request
->headers
= hdrs
;
153 request
->headers
[count
- 1].field
= strdupW( header
->field
);
154 request
->headers
[count
- 1].value
= strdupW( header
->value
);
155 request
->headers
[count
- 1].is_request
= header
->is_request
;
156 request
->num_headers
++;
162 static BOOL
delete_header( request_t
*request
, DWORD index
)
164 if (!request
->num_headers
) return FALSE
;
165 if (index
>= request
->num_headers
) return FALSE
;
166 request
->num_headers
--;
168 heap_free( request
->headers
[index
].field
);
169 heap_free( request
->headers
[index
].value
);
171 memmove( &request
->headers
[index
], &request
->headers
[index
+ 1], (request
->num_headers
- index
) * sizeof(header_t
) );
172 memset( &request
->headers
[request
->num_headers
], 0, sizeof(header_t
) );
176 static BOOL
process_header( request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD flags
, BOOL request_only
)
181 TRACE("%s: %s 0x%08x\n", debugstr_w(field
), debugstr_w(value
), flags
);
183 /* replace wins out over add */
184 if (flags
& WINHTTP_ADDREQ_FLAG_REPLACE
) flags
&= ~WINHTTP_ADDREQ_FLAG_ADD
;
186 if (flags
& WINHTTP_ADDREQ_FLAG_ADD
) index
= -1;
188 index
= get_header_index( request
, field
, 0, request_only
);
192 if (flags
& WINHTTP_ADDREQ_FLAG_ADD_IF_NEW
) return FALSE
;
193 header
= &request
->headers
[index
];
199 hdr
.field
= (LPWSTR
)field
;
200 hdr
.value
= (LPWSTR
)value
;
201 hdr
.is_request
= request_only
;
203 return insert_header( request
, &hdr
);
205 /* no value to delete */
208 if (flags
& WINHTTP_ADDREQ_FLAG_REPLACE
)
210 delete_header( request
, index
);
215 hdr
.field
= (LPWSTR
)field
;
216 hdr
.value
= (LPWSTR
)value
;
217 hdr
.is_request
= request_only
;
219 return insert_header( request
, &hdr
);
223 else if (flags
& (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA
| WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON
))
226 int len
, orig_len
, value_len
;
228 orig_len
= strlenW( header
->value
);
229 value_len
= strlenW( value
);
231 if (flags
& WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA
) sep
= ',';
234 len
= orig_len
+ value_len
+ 2;
235 if ((tmp
= heap_realloc( header
->value
, (len
+ 1) * sizeof(WCHAR
) )))
239 header
->value
[orig_len
] = sep
;
241 header
->value
[orig_len
] = ' ';
244 memcpy( &header
->value
[orig_len
], value
, value_len
* sizeof(WCHAR
) );
245 header
->value
[len
] = 0;
252 static BOOL
add_request_headers( request_t
*request
, LPCWSTR headers
, DWORD len
, DWORD flags
)
255 WCHAR
*buffer
, *p
, *q
;
258 if (len
== ~0UL) len
= strlenW( headers
);
259 if (!(buffer
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) ))) return FALSE
;
260 strcpyW( buffer
, headers
);
268 if (q
[0] == '\r' && q
[1] == '\n') break;
275 q
+= 2; /* jump over \r\n */
277 if ((header
= parse_header( p
)))
279 ret
= process_header( request
, header
->field
, header
->value
, flags
, TRUE
);
280 free_header( header
);
289 /***********************************************************************
290 * WinHttpAddRequestHeaders (winhttp.@)
292 BOOL WINAPI
WinHttpAddRequestHeaders( HINTERNET hrequest
, LPCWSTR headers
, DWORD len
, DWORD flags
)
297 TRACE("%p, %s, 0x%x, 0x%08x\n", hrequest
, debugstr_w(headers
), len
, flags
);
301 set_last_error( ERROR_INVALID_PARAMETER
);
304 if (!(request
= (request_t
*)grab_object( hrequest
)))
306 set_last_error( ERROR_INVALID_HANDLE
);
309 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
311 release_object( &request
->hdr
);
312 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
316 ret
= add_request_headers( request
, headers
, len
, flags
);
318 release_object( &request
->hdr
);
322 static WCHAR
*build_request_string( request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
324 static const WCHAR space
[] = {' ',0};
325 static const WCHAR crlf
[] = {'\r','\n',0};
326 static const WCHAR colon
[] = {':',' ',0};
327 static const WCHAR twocrlf
[] = {'\r','\n','\r','\n',0};
330 const WCHAR
**headers
, **p
;
331 unsigned int len
, i
= 0, j
;
333 /* allocate space for an array of all the string pointers to be added */
334 len
= request
->num_headers
* 4 + 7;
335 if (!(headers
= heap_alloc( len
* sizeof(LPCWSTR
) ))) return NULL
;
338 headers
[i
++] = space
;
340 headers
[i
++] = space
;
341 headers
[i
++] = version
;
343 for (j
= 0; j
< request
->num_headers
; j
++)
345 if (request
->headers
[j
].is_request
)
348 headers
[i
++] = request
->headers
[j
].field
;
349 headers
[i
++] = colon
;
350 headers
[i
++] = request
->headers
[j
].value
;
352 TRACE("adding header %s (%s)\n", debugstr_w(request
->headers
[j
].field
),
353 debugstr_w(request
->headers
[j
].value
));
356 headers
[i
++] = twocrlf
;
360 for (p
= headers
; *p
; p
++) len
+= strlenW( *p
);
363 if (!(ret
= heap_alloc( len
* sizeof(WCHAR
) )))
365 heap_free( headers
);
369 for (p
= headers
; *p
; p
++) strcatW( ret
, *p
);
371 heap_free( headers
);
375 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
377 static BOOL
query_headers( request_t
*request
, DWORD level
, LPCWSTR name
, LPVOID buffer
, LPDWORD buflen
, LPDWORD index
)
379 header_t
*header
= NULL
;
380 BOOL request_only
, ret
= FALSE
;
381 int requested_index
, header_index
= -1;
384 request_only
= level
& WINHTTP_QUERY_FLAG_REQUEST_HEADERS
;
385 requested_index
= index
? *index
: 0;
387 attribute
= level
& ~QUERY_MODIFIER_MASK
;
390 case WINHTTP_QUERY_CUSTOM
:
392 header_index
= get_header_index( request
, name
, requested_index
, request_only
);
395 case WINHTTP_QUERY_RAW_HEADERS_CRLF
:
401 headers
= build_request_string( request
, request
->verb
, request
->path
, request
->version
);
403 headers
= request
->raw_headers
;
405 len
= strlenW( headers
) * sizeof(WCHAR
);
406 if (len
+ sizeof(WCHAR
) > *buflen
)
408 len
+= sizeof(WCHAR
);
409 set_last_error( ERROR_INSUFFICIENT_BUFFER
);
413 memcpy( buffer
, headers
, len
+ sizeof(WCHAR
) );
414 TRACE("returning data: %s\n", debugstr_wn(buffer
, len
/ sizeof(WCHAR
)));
418 if (request_only
) heap_free( headers
);
423 FIXME("attribute %u not implemented\n", attribute
);
428 if (header_index
>= 0)
430 header
= &request
->headers
[header_index
];
432 if (!header
|| (request_only
&& !header
->is_request
))
434 set_last_error( ERROR_WINHTTP_HEADER_NOT_FOUND
);
437 if (index
) *index
+= 1;
438 if (level
& WINHTTP_QUERY_FLAG_NUMBER
)
440 int *number
= buffer
;
441 if (sizeof(int) > *buflen
)
443 set_last_error( ERROR_INSUFFICIENT_BUFFER
);
447 *number
= atoiW( header
->value
);
448 TRACE("returning number: %d\n", *number
);
451 *buflen
= sizeof(int);
453 else if (level
& WINHTTP_QUERY_FLAG_SYSTEMTIME
)
455 SYSTEMTIME
*st
= buffer
;
456 if (sizeof(SYSTEMTIME
) > *buflen
)
458 set_last_error( ERROR_INSUFFICIENT_BUFFER
);
460 else if (st
&& (ret
= WinHttpTimeToSystemTime( header
->value
, st
)))
462 TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
463 st
->wYear
, st
->wMonth
, st
->wDay
, st
->wDayOfWeek
,
464 st
->wHour
, st
->wMinute
, st
->wSecond
, st
->wMilliseconds
);
466 *buflen
= sizeof(SYSTEMTIME
);
468 else if (header
->value
)
470 WCHAR
*string
= buffer
;
471 DWORD len
= (strlenW( header
->value
) + 1) * sizeof(WCHAR
);
474 set_last_error( ERROR_INSUFFICIENT_BUFFER
);
480 strcpyW( string
, header
->value
);
481 TRACE("returning string: %s\n", debugstr_w(string
));
484 *buflen
= len
- sizeof(WCHAR
);
489 /***********************************************************************
490 * WinHttpQueryHeaders (winhttp.@)
492 BOOL WINAPI
WinHttpQueryHeaders( HINTERNET hrequest
, DWORD level
, LPCWSTR name
, LPVOID buffer
, LPDWORD buflen
, LPDWORD index
)
497 TRACE("%p, 0x%08x, %s, %p, %p, %p\n", hrequest
, level
, debugstr_w(name
), buffer
, buflen
, index
);
499 if (!(request
= (request_t
*)grab_object( hrequest
)))
501 set_last_error( ERROR_INVALID_HANDLE
);
504 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
506 release_object( &request
->hdr
);
507 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
511 ret
= query_headers( request
, level
, name
, buffer
, buflen
, index
);
513 release_object( &request
->hdr
);