oleaut32: Thousands separator support for VarFormat.
[wine/hacks.git] / dlls / winhttp / request.c
blobab3dc810103237f67ed020abd6c0b99be346dcda
1 /*
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
19 #include "config.h"
20 #include "wine/port.h"
21 #include "wine/debug.h"
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winnls.h"
28 #include "winhttp.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 );
38 heap_free( header );
41 static BOOL valid_token_char( WCHAR c )
43 if (c < 32 || c == 127) return FALSE;
44 switch (c)
46 case '(': case ')':
47 case '<': case '>':
48 case '@': case ',':
49 case ';': case ':':
50 case '\\': case '\"':
51 case '/': case '[':
52 case ']': case '?':
53 case '=': case '{':
54 case '}': case ' ':
55 case '\t':
56 return FALSE;
57 default:
58 return TRUE;
62 static header_t *parse_header( LPCWSTR string )
64 const WCHAR *p, *q;
65 header_t *header;
66 int len;
68 p = string;
69 if (!(q = strchrW( p, ':' )))
71 WARN("no ':' in line %s\n", debugstr_w(string));
72 return NULL;
74 if (q == string)
76 WARN("empty field name in line %s\n", debugstr_w(string));
77 return NULL;
79 while (*p != ':')
81 if (!valid_token_char( *p ))
83 WARN("invalid character in field name %s\n", debugstr_w(string));
84 return NULL;
86 p++;
88 len = q - string;
89 if (!(header = heap_alloc_zero( sizeof(header_t) ))) return NULL;
90 if (!(header->field = heap_alloc( (len + 1) * sizeof(WCHAR) )))
92 heap_free( header );
93 return NULL;
95 memcpy( header->field, string, len * sizeof(WCHAR) );
96 header->field[len] = 0;
98 q++; /* skip past colon */
99 while (*q == ' ') q++;
100 if (!*q)
102 WARN("no value in line %s\n", debugstr_w(string));
103 return header;
105 len = strlenW( q );
106 if (!(header->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
108 free_header( header );
109 return NULL;
111 memcpy( header->value, q, len * sizeof(WCHAR) );
112 header->value[len] = 0;
114 return header;
117 static int get_header_index( request_t *request, LPCWSTR field, int requested_index, BOOL request_only )
119 int index;
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;
130 requested_index--;
132 if (index >= request->num_headers) index = -1;
133 TRACE("returning %d\n", index);
134 return index;
137 static BOOL insert_header( request_t *request, header_t *header )
139 DWORD count;
140 header_t *hdrs;
142 TRACE("inserting %s: %s\n", debugstr_w(header->field), debugstr_w(header->value));
144 count = request->num_headers + 1;
145 if (count > 1)
146 hdrs = heap_realloc_zero( request->headers, sizeof(header_t) * count );
147 else
148 hdrs = heap_alloc_zero( sizeof(header_t) * count );
150 if (hdrs)
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++;
157 return TRUE;
159 return FALSE;
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) );
173 return TRUE;
176 static BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DWORD flags, BOOL request_only )
178 int index;
179 header_t *header;
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;
187 else
188 index = get_header_index( request, field, 0, request_only );
190 if (index >= 0)
192 if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return FALSE;
193 header = &request->headers[index];
195 else if (value)
197 header_t hdr;
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 */
206 else return TRUE;
208 if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
210 delete_header( request, index );
211 if (value)
213 header_t hdr;
215 hdr.field = (LPWSTR)field;
216 hdr.value = (LPWSTR)value;
217 hdr.is_request = request_only;
219 return insert_header( request, &hdr );
221 return TRUE;
223 else if (flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON))
225 WCHAR sep, *tmp;
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 = ',';
232 else sep = ';';
234 len = orig_len + value_len + 2;
235 if ((tmp = heap_realloc( header->value, (len + 1) * sizeof(WCHAR) )))
237 header->value = tmp;
239 header->value[orig_len] = sep;
240 orig_len++;
241 header->value[orig_len] = ' ';
242 orig_len++;
244 memcpy( &header->value[orig_len], value, value_len * sizeof(WCHAR) );
245 header->value[len] = 0;
246 return TRUE;
249 return TRUE;
252 static BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD flags )
254 BOOL ret = FALSE;
255 WCHAR *buffer, *p, *q;
256 header_t *header;
258 if (len == ~0UL) len = strlenW( headers );
259 if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
260 strcpyW( buffer, headers );
262 p = buffer;
265 q = p;
266 while (*q)
268 if (q[0] == '\r' && q[1] == '\n') break;
269 q++;
271 if (!*p) break;
272 if (*q == '\r')
274 *q = 0;
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 );
282 p = q;
283 } while (ret);
285 heap_free( buffer );
286 return ret;
289 /***********************************************************************
290 * WinHttpAddRequestHeaders (winhttp.@)
292 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, LPCWSTR headers, DWORD len, DWORD flags )
294 BOOL ret;
295 request_t *request;
297 TRACE("%p, %s, 0x%x, 0x%08x\n", hrequest, debugstr_w(headers), len, flags);
299 if (!headers)
301 set_last_error( ERROR_INVALID_PARAMETER );
302 return FALSE;
304 if (!(request = (request_t *)grab_object( hrequest )))
306 set_last_error( ERROR_INVALID_HANDLE );
307 return FALSE;
309 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
311 release_object( &request->hdr );
312 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
313 return FALSE;
316 ret = add_request_headers( request, headers, len, flags );
318 release_object( &request->hdr );
319 return ret;
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};
329 WCHAR *ret;
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;
337 headers[i++] = verb;
338 headers[i++] = space;
339 headers[i++] = path;
340 headers[i++] = space;
341 headers[i++] = version;
343 for (j = 0; j < request->num_headers; j++)
345 if (request->headers[j].is_request)
347 headers[i++] = crlf;
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;
357 headers[i] = NULL;
359 len = 0;
360 for (p = headers; *p; p++) len += strlenW( *p );
361 len++;
363 if (!(ret = heap_alloc( len * sizeof(WCHAR) )))
365 heap_free( headers );
366 return NULL;
368 *ret = 0;
369 for (p = headers; *p; p++) strcatW( ret, *p );
371 heap_free( headers );
372 return ret;
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;
382 DWORD attribute;
384 request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
385 requested_index = index ? *index : 0;
387 attribute = level & ~QUERY_MODIFIER_MASK;
388 switch (attribute)
390 case WINHTTP_QUERY_CUSTOM:
392 header_index = get_header_index( request, name, requested_index, request_only );
393 break;
395 case WINHTTP_QUERY_RAW_HEADERS_CRLF:
397 WCHAR *headers;
398 DWORD len;
400 if (request_only)
401 headers = build_request_string( request, request->verb, request->path, request->version );
402 else
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 );
411 else if (buffer)
413 memcpy( buffer, headers, len + sizeof(WCHAR) );
414 TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
415 ret = TRUE;
417 *buflen = len;
418 if (request_only) heap_free( headers );
419 return ret;
421 default:
423 FIXME("attribute %u not implemented\n", attribute);
424 return FALSE;
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 );
435 return FALSE;
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 );
445 else if (number)
447 *number = atoiW( header->value );
448 TRACE("returning number: %d\n", *number);
449 ret = TRUE;
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);
472 if (len > *buflen)
474 set_last_error( ERROR_INSUFFICIENT_BUFFER );
475 *buflen = len;
476 return FALSE;
478 else if (string)
480 strcpyW( string, header->value );
481 TRACE("returning string: %s\n", debugstr_w(string));
482 ret = TRUE;
484 *buflen = len - sizeof(WCHAR);
486 return ret;
489 /***********************************************************************
490 * WinHttpQueryHeaders (winhttp.@)
492 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
494 BOOL ret;
495 request_t *request;
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 );
502 return FALSE;
504 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
506 release_object( &request->hdr );
507 set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
508 return FALSE;
511 ret = query_headers( request, level, name, buffer, buflen, index );
513 release_object( &request->hdr );
514 return ret;