soup-auth-manager: add soup_auth_manager_use_auth()
[platform/upstream/libsoup.git] / libsoup / soup-message-headers.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message-headers.c: HTTP message header arrays
4  *
5  * Copyright (C) 2007, 2008 Red Hat, Inc.
6  */
7
8 #include <string.h>
9
10 #include "soup-message-headers.h"
11 #include "soup.h"
12
13 /**
14  * SECTION:soup-message-headers
15  * @short_description: HTTP message headers
16  * @see_also: #SoupMessage
17  *
18  * #SoupMessageHeaders represents the HTTP message headers associated
19  * with a request or response.
20  **/
21
22 /**
23  * SoupMessageHeaders:
24  *
25  * The HTTP message headers associated with a request or response.
26  */
27
28 /**
29  * SoupMessageHeadersType:
30  * @SOUP_MESSAGE_HEADERS_REQUEST: request headers
31  * @SOUP_MESSAGE_HEADERS_RESPONSE: response headers
32  * @SOUP_MESSAGE_HEADERS_MULTIPART: multipart body part headers
33  *
34  * Value passed to soup_message_headers_new() to set certain default
35  * behaviors.
36  **/
37
38 typedef void (*SoupHeaderSetter) (SoupMessageHeaders *, const char *);
39 static const char *intern_header_name (const char *name, SoupHeaderSetter *setter);
40 static void clear_special_headers (SoupMessageHeaders *hdrs);
41
42 typedef struct {
43         const char *name;
44         char *value;
45 } SoupHeader;
46
47 struct SoupMessageHeaders {
48         GArray *array;
49         GHashTable *concat;
50         SoupMessageHeadersType type;
51
52         SoupEncoding encoding;
53         goffset content_length;
54         SoupExpectation expectations;
55         char *content_type;
56
57         int ref_count;
58 };
59
60 /**
61  * soup_message_headers_new:
62  * @type: the type of headers
63  *
64  * Creates a #SoupMessageHeaders. (#SoupMessage does this
65  * automatically for its own headers. You would only need to use this
66  * method if you are manually parsing or generating message headers.)
67  *
68  * Return value: a new #SoupMessageHeaders
69  **/
70 SoupMessageHeaders *
71 soup_message_headers_new (SoupMessageHeadersType type)
72 {
73         SoupMessageHeaders *hdrs;
74
75         hdrs = g_slice_new0 (SoupMessageHeaders);
76         /* FIXME: is "5" a good default? */
77         hdrs->array = g_array_sized_new (TRUE, FALSE, sizeof (SoupHeader), 5);
78         hdrs->type = type;
79         hdrs->encoding = -1;
80         hdrs->ref_count = 1;
81
82         return hdrs;
83 }
84
85 static SoupMessageHeaders *
86 soup_message_headers_copy (SoupMessageHeaders *hdrs)
87 {
88         hdrs->ref_count++;
89         return hdrs;
90 }
91
92 /**
93  * soup_message_headers_free:
94  * @hdrs: a #SoupMessageHeaders
95  *
96  * Frees @hdrs.
97  **/
98 void
99 soup_message_headers_free (SoupMessageHeaders *hdrs)
100 {
101         if (--hdrs->ref_count == 0) {
102                 soup_message_headers_clear (hdrs);
103                 g_array_free (hdrs->array, TRUE);
104                 g_clear_pointer (&hdrs->concat, g_hash_table_destroy);
105                 g_slice_free (SoupMessageHeaders, hdrs);
106         }
107 }
108
109 G_DEFINE_BOXED_TYPE (SoupMessageHeaders, soup_message_headers, soup_message_headers_copy, soup_message_headers_free)
110
111 /**
112  * soup_message_headers_clear:
113  * @hdrs: a #SoupMessageHeaders
114  *
115  * Clears @hdrs.
116  **/
117 void
118 soup_message_headers_clear (SoupMessageHeaders *hdrs)
119 {
120         SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
121         int i;
122
123         for (i = 0; i < hdrs->array->len; i++)
124                 g_free (hdr_array[i].value);
125         g_array_set_size (hdrs->array, 0);
126
127         if (hdrs->concat)
128                 g_hash_table_remove_all (hdrs->concat);
129
130         clear_special_headers (hdrs);
131 }
132
133 /**
134  * soup_message_headers_clean_connection_headers:
135  * @hdrs: a #SoupMessageHeaders
136  *
137  * Removes all the headers listed in the Connection header.
138  *
139  * Since: 2.36
140  */
141 void
142 soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs)
143 {
144         /* RFC 2616 14.10 */
145         const char *connection;
146         GSList *tokens, *t;
147
148         connection = soup_message_headers_get_list (hdrs, "Connection");
149         if (!connection)
150                 return;
151
152         tokens = soup_header_parse_list (connection);
153         for (t = tokens; t; t = t->next)
154                 soup_message_headers_remove (hdrs, t->data);
155         soup_header_free_list (tokens);
156 }
157
158 /**
159  * soup_message_headers_append:
160  * @hdrs: a #SoupMessageHeaders
161  * @name: the header name to add
162  * @value: the new value of @name
163  *
164  * Appends a new header with name @name and value @value to @hdrs. (If
165  * there is an existing header with name @name, then this creates a
166  * second one, which is only allowed for list-valued headers; see also
167  * soup_message_headers_replace().)
168  *
169  * The caller is expected to make sure that @name and @value are
170  * syntactically correct.
171  **/
172 void
173 soup_message_headers_append (SoupMessageHeaders *hdrs,
174                              const char *name, const char *value)
175 {
176         SoupHeader header;
177         SoupHeaderSetter setter;
178
179         g_return_if_fail (name != NULL);
180         g_return_if_fail (value != NULL);
181
182         /* Setting a syntactically invalid header name or value is
183          * considered to be a programming error. However, it can also
184          * be a security hole, so we want to fail here even if
185          * compiled with G_DISABLE_CHECKS.
186          */
187 #ifndef G_DISABLE_CHECKS
188         g_return_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL);
189         g_return_if_fail (strpbrk (value, "\r\n") == NULL);
190 #else
191         if (*name && strpbrk (name, " \t\r\n:")) {
192                 g_warning ("soup_message_headers_append: Ignoring bad name '%s'", name);
193                 return;
194         }
195         if (strpbrk (value, "\r\n")) {
196                 g_warning ("soup_message_headers_append: Ignoring bad value '%s'", value);
197                 return;
198         }
199 #endif
200
201         header.name = intern_header_name (name, &setter);
202         header.value = g_strdup (value);
203         g_array_append_val (hdrs->array, header);
204         if (hdrs->concat)
205                 g_hash_table_remove (hdrs->concat, header.name);
206         if (setter)
207                 setter (hdrs, header.value);
208 }
209
210 /**
211  * soup_message_headers_replace:
212  * @hdrs: a #SoupMessageHeaders
213  * @name: the header name to replace
214  * @value: the new value of @name
215  *
216  * Replaces the value of the header @name in @hdrs with @value. (See
217  * also soup_message_headers_append().)
218  *
219  * The caller is expected to make sure that @name and @value are
220  * syntactically correct.
221  **/
222 void
223 soup_message_headers_replace (SoupMessageHeaders *hdrs,
224                               const char *name, const char *value)
225 {
226         soup_message_headers_remove (hdrs, name);
227         soup_message_headers_append (hdrs, name, value);
228 }
229
230 static int
231 find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
232 {
233         int i;
234
235         for (i = 0; hdr_array[i].name; i++) {
236                 if (hdr_array[i].name == interned_name) {
237                         if (nth-- == 0)
238                                 return i;
239                 }
240         }
241         return -1;
242 }
243
244 static int
245 find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name, int nth)
246 {
247         int i;
248
249         for (i = length; i >= 0; i--) {
250                 if (hdr_array[i].name == interned_name) {
251                         if (nth-- == 0)
252                                 return i;
253                 }
254         }
255         return -1;
256 }
257
258 /**
259  * soup_message_headers_remove:
260  * @hdrs: a #SoupMessageHeaders
261  * @name: the header name to remove
262  *
263  * Removes @name from @hdrs. If there are multiple values for @name,
264  * they are all removed.
265  **/
266 void
267 soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
268 {
269         SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
270         SoupHeaderSetter setter;
271         int index;
272
273         g_return_if_fail (name != NULL);
274
275         name = intern_header_name (name, &setter);
276         while ((index = find_header (hdr_array, name, 0)) != -1) {
277                 g_free (hdr_array[index].value);
278                 g_array_remove_index (hdrs->array, index);
279         }
280         if (hdrs->concat)
281                 g_hash_table_remove (hdrs->concat, name);
282         if (setter)
283                 setter (hdrs, NULL);
284 }
285
286 /**
287  * soup_message_headers_get_one:
288  * @hdrs: a #SoupMessageHeaders
289  * @name: header name
290  * 
291  * Gets the value of header @name in @hdrs. Use this for headers whose
292  * values are <emphasis>not</emphasis> comma-delimited lists, and
293  * which therefore can only appear at most once in the headers. For
294  * list-valued headers, use soup_message_headers_get_list().
295  *
296  * If @hdrs does erroneously contain multiple copies of the header, it
297  * is not defined which one will be returned. (Ideally, it will return
298  * whichever one makes libsoup most compatible with other HTTP
299  * implementations.)
300  *
301  * Return value: the header's value or %NULL if not found.
302  *
303  * Since: 2.28
304  **/
305 const char *
306 soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
307 {
308         SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
309         guint hdr_length = hdrs->array->len;
310         int index;
311
312         g_return_val_if_fail (name != NULL, NULL);
313
314         name = intern_header_name (name, NULL);
315
316         index = find_last_header (hdr_array, hdr_length, name, 0);
317
318         return (index == -1) ? NULL : hdr_array[index].value;
319 }
320
321 /**
322  * soup_message_headers_get_list:
323  * @hdrs: a #SoupMessageHeaders
324  * @name: header name
325  * 
326  * Gets the value of header @name in @hdrs. Use this for headers whose
327  * values are comma-delimited lists, and which are therefore allowed
328  * to appear multiple times in the headers. For non-list-valued
329  * headers, use soup_message_headers_get_one().
330  *
331  * If @name appears multiple times in @hdrs,
332  * soup_message_headers_get_list() will concatenate all of the values
333  * together, separated by commas. This is sometimes awkward to parse
334  * (eg, WWW-Authenticate, Set-Cookie), but you have to be able to deal
335  * with it anyway, because the HTTP spec explicitly states that this
336  * transformation is allowed, and so an upstream proxy could do the
337  * same thing.
338  * 
339  * Return value: the header's value or %NULL if not found.
340  *
341  * Since: 2.28
342  **/
343 const char *
344 soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name)
345 {
346         SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
347         GString *concat;
348         char *value;
349         int index, i;
350
351         g_return_val_if_fail (name != NULL, NULL);
352
353         name = intern_header_name (name, NULL);
354         if (hdrs->concat) {
355                 value = g_hash_table_lookup (hdrs->concat, name);
356                 if (value)
357                         return value;
358         }
359
360         index = find_header (hdr_array, name, 0);
361         if (index == -1)
362                 return NULL;
363         else if (find_header (hdr_array, name, 1) == -1)
364                 return hdr_array[index].value;
365
366         concat = g_string_new (NULL);
367         for (i = 0; (index = find_header (hdr_array, name, i)) != -1; i++) {
368                 if (i != 0)
369                         g_string_append (concat, ", ");
370                 g_string_append (concat, hdr_array[index].value);
371         }
372         value = g_string_free (concat, FALSE);
373
374         if (!hdrs->concat)
375                 hdrs->concat = g_hash_table_new_full (NULL, NULL, NULL, g_free);
376         g_hash_table_insert (hdrs->concat, (gpointer)name, value);
377         return value;
378 }
379
380 /**
381  * soup_message_headers_get:
382  * @hdrs: a #SoupMessageHeaders
383  * @name: header name
384  * 
385  * Gets the value of header @name in @hdrs.
386  *
387  * This method was supposed to work correctly for both single-valued
388  * and list-valued headers, but because some HTTP clients/servers
389  * mistakenly send multiple copies of headers that are supposed to be
390  * single-valued, it sometimes returns incorrect results. To fix this,
391  * the methods soup_message_headers_get_one() and
392  * soup_message_headers_get_list() were introduced, so callers can
393  * explicitly state which behavior they are expecting.
394  *
395  * Return value: as with soup_message_headers_get_list().
396  * 
397  * Deprecated: Use soup_message_headers_get_one() or
398  * soup_message_headers_get_list() instead.
399  **/
400 const char *
401 soup_message_headers_get (SoupMessageHeaders *hdrs, const char *name)
402 {
403         return soup_message_headers_get_list (hdrs, name);
404 }
405
406 /**
407  * SoupMessageHeadersIter:
408  *
409  * An opaque type used to iterate over a %SoupMessageHeaders
410  * structure.
411  *
412  * After intializing the iterator with
413  * soup_message_headers_iter_init(), call
414  * soup_message_headers_iter_next() to fetch data from it.
415  *
416  * You may not modify the headers while iterating over them.
417  **/
418
419 typedef struct {
420         SoupMessageHeaders *hdrs;
421         int index;
422 } SoupMessageHeadersIterReal;
423
424 /**
425  * soup_message_headers_iter_init:
426  * @iter: (out) (transfer none): a pointer to a %SoupMessageHeadersIter
427  * structure
428  * @hdrs: a %SoupMessageHeaders
429  *
430  * Initializes @iter for iterating @hdrs.
431  **/
432 void
433 soup_message_headers_iter_init (SoupMessageHeadersIter *iter,
434                                 SoupMessageHeaders *hdrs)
435 {
436         SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
437
438         real->hdrs = hdrs;
439         real->index = 0;
440 }
441
442 /**
443  * soup_message_headers_iter_next:
444  * @iter: (inout) (transfer none): a %SoupMessageHeadersIter
445  * @name: (out) (transfer none): pointer to a variable to return
446  * the header name in
447  * @value: (out) (transfer none): pointer to a variable to return
448  * the header value in
449  *
450  * Yields the next name/value pair in the %SoupMessageHeaders being
451  * iterated by @iter. If @iter has already yielded the last header,
452  * then soup_message_headers_iter_next() will return %FALSE and @name
453  * and @value will be unchanged.
454  *
455  * Return value: %TRUE if another name and value were returned, %FALSE
456  * if the end of the headers has been reached.
457  **/
458 gboolean
459 soup_message_headers_iter_next (SoupMessageHeadersIter *iter,
460                                 const char **name, const char **value)
461 {
462         SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
463         SoupHeader *hdr_array = (SoupHeader *)real->hdrs->array->data;
464
465         if (real->index >= real->hdrs->array->len)
466                 return FALSE;
467
468         *name = hdr_array[real->index].name;
469         *value = hdr_array[real->index].value;
470         real->index++;
471         return TRUE;
472 }
473
474 /**
475  * SoupMessageHeadersForeachFunc:
476  * @name: the header name
477  * @value: the header value
478  * @user_data: the data passed to soup_message_headers_foreach()
479  *
480  * The callback passed to soup_message_headers_foreach().
481  **/
482
483 /**
484  * soup_message_headers_foreach:
485  * @hdrs: a #SoupMessageHeaders
486  * @func: (scope call): callback function to run for each header
487  * @user_data: data to pass to @func
488  * 
489  * Calls @func once for each header value in @hdrs.
490  *
491  * Beware that unlike soup_message_headers_get(), this processes the
492  * headers in exactly the way they were added, rather than
493  * concatenating multiple same-named headers into a single value.
494  * (This is intentional; it ensures that if you call
495  * soup_message_headers_append() multiple times with the same name,
496  * then the I/O code will output multiple copies of the header when
497  * sending the message to the remote implementation, which may be
498  * required for interoperability in some cases.)
499  *
500  * You may not modify the headers from @func.
501  **/
502 void
503 soup_message_headers_foreach (SoupMessageHeaders *hdrs,
504                               SoupMessageHeadersForeachFunc func,
505                               gpointer            user_data)
506 {
507         SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
508         int i;
509
510         for (i = 0; i < hdrs->array->len; i++)
511                 func (hdr_array[i].name, hdr_array[i].value, user_data);
512 }
513
514
515 G_LOCK_DEFINE_STATIC (header_pool);
516 static GHashTable *header_pool, *header_setters;
517
518 static void transfer_encoding_setter (SoupMessageHeaders *, const char *);
519 static void content_length_setter (SoupMessageHeaders *, const char *);
520 static void expectation_setter (SoupMessageHeaders *, const char *);
521 static void content_type_setter (SoupMessageHeaders *, const char *);
522
523 static char *
524 intern_header_locked (const char *name)
525 {
526         char *interned;
527
528         interned = g_hash_table_lookup (header_pool, name);
529         if (!interned) {
530                 char *dup = g_strdup (name);
531                 g_hash_table_insert (header_pool, dup, dup);
532                 interned = dup;
533         }
534         return interned;
535 }
536
537 static const char *
538 intern_header_name (const char *name, SoupHeaderSetter *setter)
539 {
540         const char *interned;
541
542         G_LOCK (header_pool);
543
544         if (!header_pool) {
545                 header_pool = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
546                 header_setters = g_hash_table_new (NULL, NULL);
547                 g_hash_table_insert (header_setters,
548                                      intern_header_locked ("Transfer-Encoding"),
549                                      transfer_encoding_setter);
550                 g_hash_table_insert (header_setters,
551                                      intern_header_locked ("Content-Length"),
552                                      content_length_setter);
553                 g_hash_table_insert (header_setters,
554                                      intern_header_locked ("Expect"),
555                                      expectation_setter);
556                 g_hash_table_insert (header_setters,
557                                      intern_header_locked ("Content-Type"),
558                                      content_type_setter);
559         }
560
561         interned = intern_header_locked (name);
562         if (setter)
563                 *setter = g_hash_table_lookup (header_setters, interned);
564
565         G_UNLOCK (header_pool);
566         return interned;
567 }
568
569 static void
570 clear_special_headers (SoupMessageHeaders *hdrs)
571 {
572         SoupHeaderSetter setter;
573         GHashTableIter iter;
574         gpointer key, value;
575
576         /* Make sure header_setters has been initialized */
577         intern_header_name ("", NULL);
578
579         g_hash_table_iter_init (&iter, header_setters);
580         while (g_hash_table_iter_next (&iter, &key, &value)) {
581                 setter = value;
582                 setter (hdrs, NULL);
583         }
584 }
585
586 /* Specific headers */
587
588 static void
589 transfer_encoding_setter (SoupMessageHeaders *hdrs, const char *value)
590 {
591         if (value) {
592                 if (g_ascii_strcasecmp (value, "chunked") == 0)
593                         hdrs->encoding = SOUP_ENCODING_CHUNKED;
594                 else
595                         hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
596         } else
597                 hdrs->encoding = -1;
598 }
599
600 static void
601 content_length_setter (SoupMessageHeaders *hdrs, const char *value)
602 {
603         /* Transfer-Encoding trumps Content-Length */
604         if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
605                 return;
606
607         if (value) {
608                 char *end;
609
610                 hdrs->content_length = g_ascii_strtoull (value, &end, 10);
611                 if (*end)
612                         hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
613                 else
614                         hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
615         } else
616                 hdrs->encoding = -1;
617 }
618
619 /**
620  * SoupEncoding:
621  * @SOUP_ENCODING_UNRECOGNIZED: unknown / error
622  * @SOUP_ENCODING_NONE: no body is present (which is not the same as a
623  * 0-length body, and only occurs in certain places)
624  * @SOUP_ENCODING_CONTENT_LENGTH: Content-Length encoding
625  * @SOUP_ENCODING_EOF: Response body ends when the connection is closed
626  * @SOUP_ENCODING_CHUNKED: chunked encoding (currently only supported
627  * for response)
628  * @SOUP_ENCODING_BYTERANGES: multipart/byteranges (Reserved for future
629  * use: NOT CURRENTLY IMPLEMENTED)
630  *
631  * How a message body is encoded for transport
632  **/
633
634 /**
635  * soup_message_headers_get_encoding:
636  * @hdrs: a #SoupMessageHeaders
637  *
638  * Gets the message body encoding that @hdrs declare. This may not
639  * always correspond to the encoding used on the wire; eg, a HEAD
640  * response may declare a Content-Length or Transfer-Encoding, but
641  * it will never actually include a body.
642  *
643  * Return value: the encoding declared by @hdrs.
644  **/
645 SoupEncoding
646 soup_message_headers_get_encoding (SoupMessageHeaders *hdrs)
647 {
648         const char *header;
649
650         if (hdrs->encoding != -1)
651                 return hdrs->encoding;
652
653         /* If Transfer-Encoding was set, hdrs->encoding would already
654          * be set. So we don't need to check that possibility.
655          */
656         header = soup_message_headers_get_one (hdrs, "Content-Length");
657         if (header) {
658                 content_length_setter (hdrs, header);
659                 if (hdrs->encoding != -1)
660                         return hdrs->encoding;
661         }
662
663         /* Per RFC 2616 4.4, a response body that doesn't indicate its
664          * encoding otherwise is terminated by connection close, and a
665          * request that doesn't indicate otherwise has no body. Note
666          * that SoupMessage calls soup_message_headers_set_encoding()
667          * to override the response body default for our own
668          * server-side messages.
669          */
670         hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
671                 SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
672         return hdrs->encoding;
673 }
674
675 /**
676  * soup_message_headers_set_encoding:
677  * @hdrs: a #SoupMessageHeaders
678  * @encoding: a #SoupEncoding
679  *
680  * Sets the message body encoding that @hdrs will declare. In particular,
681  * you should use this if you are going to send a request or response in
682  * chunked encoding.
683  **/
684 void
685 soup_message_headers_set_encoding (SoupMessageHeaders *hdrs,
686                                    SoupEncoding        encoding)
687 {
688         if (encoding == hdrs->encoding)
689                 return;
690
691         switch (encoding) {
692         case SOUP_ENCODING_NONE:
693         case SOUP_ENCODING_EOF:
694                 soup_message_headers_remove (hdrs, "Transfer-Encoding");
695                 soup_message_headers_remove (hdrs, "Content-Length");
696                 break;
697
698         case SOUP_ENCODING_CONTENT_LENGTH:
699                 soup_message_headers_remove (hdrs, "Transfer-Encoding");
700                 break;
701
702         case SOUP_ENCODING_CHUNKED:
703                 soup_message_headers_remove (hdrs, "Content-Length");
704                 soup_message_headers_replace (hdrs, "Transfer-Encoding", "chunked");
705                 break;
706
707         default:
708                 g_return_if_reached ();
709         }
710
711         hdrs->encoding = encoding;
712 }
713
714 /**
715  * soup_message_headers_get_content_length:
716  * @hdrs: a #SoupMessageHeaders
717  *
718  * Gets the message body length that @hdrs declare. This will only
719  * be non-0 if soup_message_headers_get_encoding() returns
720  * %SOUP_ENCODING_CONTENT_LENGTH.
721  *
722  * Return value: the message body length declared by @hdrs.
723  **/
724 goffset
725 soup_message_headers_get_content_length (SoupMessageHeaders *hdrs)
726 {
727         SoupEncoding encoding;
728
729         encoding = soup_message_headers_get_encoding (hdrs);
730         if (encoding == SOUP_ENCODING_CONTENT_LENGTH)
731                 return hdrs->content_length;
732         else
733                 return 0;
734 }
735
736 /**
737  * soup_message_headers_set_content_length:
738  * @hdrs: a #SoupMessageHeaders
739  * @content_length: the message body length
740  *
741  * Sets the message body length that @hdrs will declare, and sets
742  * @hdrs's encoding to %SOUP_ENCODING_CONTENT_LENGTH.
743  *
744  * You do not normally need to call this; if @hdrs is set to use
745  * Content-Length encoding, libsoup will automatically set its
746  * Content-Length header for you immediately before sending the
747  * headers. One situation in which this method is useful is when
748  * generating the response to a HEAD request; Calling
749  * soup_message_headers_set_content_length() allows you to put the
750  * correct content length into the response without needing to waste
751  * memory by filling in a response body which won't actually be sent.
752  **/
753 void
754 soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
755                                          goffset             content_length)
756 {
757         char length[128];
758
759         g_snprintf (length, sizeof (length), "%" G_GUINT64_FORMAT,
760                     content_length);
761         soup_message_headers_remove (hdrs, "Transfer-Encoding");
762         soup_message_headers_replace (hdrs, "Content-Length", length);
763 }
764
765 static void
766 expectation_setter (SoupMessageHeaders *hdrs, const char *value)
767 {
768         if (value) {
769                 if (!g_ascii_strcasecmp (value, "100-continue"))
770                         hdrs->expectations = SOUP_EXPECTATION_CONTINUE;
771                 else
772                         hdrs->expectations = SOUP_EXPECTATION_UNRECOGNIZED;
773         } else
774                 hdrs->expectations = 0;
775 }
776
777 /**
778  * SoupExpectation:
779  * @SOUP_EXPECTATION_CONTINUE: "100-continue"
780  * @SOUP_EXPECTATION_UNRECOGNIZED: any unrecognized expectation
781  *
782  * Represents the parsed value of the "Expect" header.
783  **/
784
785 /**
786  * soup_message_headers_get_expectations:
787  * @hdrs: a #SoupMessageHeaders
788  *
789  * Gets the expectations declared by @hdrs's "Expect" header.
790  * Currently this will either be %SOUP_EXPECTATION_CONTINUE or
791  * %SOUP_EXPECTATION_UNRECOGNIZED.
792  *
793  * Return value: the contents of @hdrs's "Expect" header
794  **/
795 SoupExpectation
796 soup_message_headers_get_expectations (SoupMessageHeaders *hdrs)
797 {
798         return hdrs->expectations;
799 }
800
801 /**
802  * soup_message_headers_set_expectations:
803  * @hdrs: a #SoupMessageHeaders
804  * @expectations: the expectations to set
805  *
806  * Sets @hdrs's "Expect" header according to @expectations.
807  *
808  * Currently %SOUP_EXPECTATION_CONTINUE is the only known expectation
809  * value. You should set this value on a request if you are sending a
810  * large message body (eg, via POST or PUT), and want to give the
811  * server a chance to reject the request after seeing just the headers
812  * (eg, because it will require authentication before allowing you to
813  * post, or because you're POSTing to a URL that doesn't exist). This
814  * saves you from having to transmit the large request body when the
815  * server is just going to ignore it anyway.
816  **/
817 void
818 soup_message_headers_set_expectations (SoupMessageHeaders *hdrs,
819                                        SoupExpectation     expectations)
820 {
821         g_return_if_fail ((expectations & ~SOUP_EXPECTATION_CONTINUE) == 0);
822
823         if (expectations & SOUP_EXPECTATION_CONTINUE)
824                 soup_message_headers_replace (hdrs, "Expect", "100-continue");
825         else
826                 soup_message_headers_remove (hdrs, "Expect");
827 }
828
829 /**
830  * SoupRange:
831  * @start: the start of the range
832  * @end: the end of the range
833  *
834  * Represents a byte range as used in the Range header.
835  *
836  * If @end is non-negative, then @start and @end represent the bounds
837  * of of the range, counting from 0. (Eg, the first 500 bytes would be
838  * represented as @start = 0 and @end = 499.)
839  *
840  * If @end is -1 and @start is non-negative, then this represents a
841  * range starting at @start and ending with the last byte of the
842  * requested resource body. (Eg, all but the first 500 bytes would be
843  * @start = 500, and @end = -1.)
844  *
845  * If @end is -1 and @start is negative, then it represents a "suffix
846  * range", referring to the last -@start bytes of the resource body.
847  * (Eg, the last 500 bytes would be @start = -500 and @end = -1.)
848  *
849  * Since: 2.26
850  **/
851
852 static int
853 sort_ranges (gconstpointer a, gconstpointer b)
854 {
855         SoupRange *ra = (SoupRange *)a;
856         SoupRange *rb = (SoupRange *)b;
857
858         return ra->start - rb->start;
859 }
860
861 /**
862  * soup_message_headers_get_ranges:
863  * @hdrs: a #SoupMessageHeaders
864  * @total_length: the total_length of the response body
865  * @ranges: (out): return location for an array of #SoupRange
866  * @length: the length of the returned array
867  *
868  * Parses @hdrs's Range header and returns an array of the requested
869  * byte ranges. The returned array must be freed with
870  * soup_message_headers_free_ranges().
871  *
872  * If @total_length is non-0, its value will be used to adjust the
873  * returned ranges to have explicit start and end values, and the
874  * returned ranges will be sorted and non-overlapping. If
875  * @total_length is 0, then some ranges may have an end value of -1,
876  * as described under #SoupRange, and some of the ranges may be
877  * redundant.
878  *
879  * Return value: %TRUE if @hdrs contained a "Range" header containing
880  * byte ranges which could be parsed, %FALSE otherwise (in which case
881  * @range and @length will not be set).
882  *
883  * Since: 2.26
884  **/
885 gboolean
886 soup_message_headers_get_ranges (SoupMessageHeaders  *hdrs,
887                                  goffset              total_length,
888                                  SoupRange          **ranges,
889                                  int                 *length)
890 {
891         const char *range = soup_message_headers_get_one (hdrs, "Range");
892         GSList *range_list, *r;
893         GArray *array;
894         char *spec, *end;
895         int i;
896
897         if (!range || strncmp (range, "bytes", 5) != 0)
898                 return FALSE;
899
900         range += 5;
901         while (g_ascii_isspace (*range))
902                 range++;
903         if (*range++ != '=')
904                 return FALSE;
905         while (g_ascii_isspace (*range))
906                 range++;
907
908         range_list = soup_header_parse_list (range);
909         if (!range_list)
910                 return FALSE;
911
912         array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
913         for (r = range_list; r; r = r->next) {
914                 SoupRange cur;
915
916                 spec = r->data;
917                 if (*spec == '-') {
918                         cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
919                         cur.end = total_length - 1;
920                 } else {
921                         cur.start = g_ascii_strtoull (spec, &end, 10);
922                         if (*end == '-')
923                                 end++;
924                         if (*end)
925                                 cur.end = g_ascii_strtoull (end, &end, 10);
926                         else
927                                 cur.end = total_length - 1;
928                 }
929                 if (*end) {
930                         g_array_free (array, TRUE);
931                         soup_header_free_list (range_list);
932                         return FALSE;
933                 }
934
935                 g_array_append_val (array, cur);
936         }
937
938         soup_header_free_list (range_list);
939
940         if (total_length) {
941                 g_array_sort (array, sort_ranges);
942                 for (i = 1; i < array->len; i++) {
943                         SoupRange *cur = &((SoupRange *)array->data)[i];
944                         SoupRange *prev = &((SoupRange *)array->data)[i - 1];
945
946                         if (cur->start <= prev->end) {
947                                 prev->end = MAX (prev->end, cur->end);
948                                 g_array_remove_index (array, i);
949                         }
950                 }
951         }
952
953         *ranges = (SoupRange *)array->data;
954         *length = array->len;
955
956         g_array_free (array, FALSE);
957         return TRUE;
958 }
959
960 /**
961  * soup_message_headers_free_ranges:
962  * @hdrs: a #SoupMessageHeaders
963  * @ranges: an array of #SoupRange
964  *
965  * Frees the array of ranges returned from soup_message_headers_get_ranges().
966  *
967  * Since: 2.26
968  **/
969 void
970 soup_message_headers_free_ranges (SoupMessageHeaders  *hdrs,
971                                   SoupRange           *ranges)
972 {
973         g_free (ranges);
974 }
975
976 /**
977  * soup_message_headers_set_ranges:
978  * @hdrs: a #SoupMessageHeaders
979  * @ranges: an array of #SoupRange
980  * @length: the length of @range
981  *
982  * Sets @hdrs's Range header to request the indicated ranges. (If you
983  * only want to request a single range, you can use
984  * soup_message_headers_set_range().)
985  *
986  * Since: 2.26
987  **/
988 void
989 soup_message_headers_set_ranges (SoupMessageHeaders  *hdrs,
990                                  SoupRange           *ranges,
991                                  int                  length)
992 {
993         GString *header;
994         int i;
995
996         header = g_string_new ("bytes=");
997         for (i = 0; i < length; i++) {
998                 if (i > 0)
999                         g_string_append_c (header, ',');
1000                 if (ranges[i].end >= 0) {
1001                         g_string_append_printf (header, "%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT,
1002                                                 ranges[i].start, ranges[i].end);
1003                 } else if (ranges[i].start >= 0) {
1004                         g_string_append_printf (header,"%" G_GINT64_FORMAT "-",
1005                                                 ranges[i].start);
1006                 } else {
1007                         g_string_append_printf (header, "%" G_GINT64_FORMAT,
1008                                                 ranges[i].start);
1009                 }
1010         }
1011
1012         soup_message_headers_replace (hdrs, "Range", header->str);
1013         g_string_free (header, TRUE);
1014 }
1015
1016 /**
1017  * soup_message_headers_set_range:
1018  * @hdrs: a #SoupMessageHeaders
1019  * @start: the start of the range to request
1020  * @end: the end of the range to request
1021  *
1022  * Sets @hdrs's Range header to request the indicated range.
1023  * @start and @end are interpreted as in a #SoupRange.
1024  *
1025  * If you need to request multiple ranges, use
1026  * soup_message_headers_set_ranges().
1027  *
1028  * Since: 2.26
1029  **/
1030 void
1031 soup_message_headers_set_range (SoupMessageHeaders  *hdrs,
1032                                 goffset              start,
1033                                 goffset              end)
1034 {
1035         SoupRange range;
1036
1037         range.start = start;
1038         range.end = end;
1039         soup_message_headers_set_ranges (hdrs, &range, 1);
1040 }
1041
1042 /**
1043  * soup_message_headers_get_content_range:
1044  * @hdrs: a #SoupMessageHeaders
1045  * @start: return value for the start of the range
1046  * @end: return value for the end of the range
1047  * @total_length: return value for the total length of the resource,
1048  * or %NULL if you don't care.
1049  *
1050  * Parses @hdrs's Content-Range header and returns it in @start,
1051  * @end, and @total_length. If the total length field in the header
1052  * was specified as "*", then @total_length will be set to -1.
1053  *
1054  * Return value: %TRUE if @hdrs contained a "Content-Range" header
1055  * containing a byte range which could be parsed, %FALSE otherwise.
1056  *
1057  * Since: 2.26
1058  **/
1059 gboolean
1060 soup_message_headers_get_content_range (SoupMessageHeaders  *hdrs,
1061                                         goffset             *start,
1062                                         goffset             *end,
1063                                         goffset             *total_length)
1064 {
1065         const char *header = soup_message_headers_get_one (hdrs, "Content-Range");
1066         goffset length;
1067         char *p;
1068
1069         if (!header || strncmp (header, "bytes ", 6) != 0)
1070                 return FALSE;
1071
1072         header += 6;
1073         while (g_ascii_isspace (*header))
1074                 header++;
1075         if (!g_ascii_isdigit (*header))
1076                 return FALSE;
1077
1078         *start = g_ascii_strtoull (header, &p, 10);
1079         if (*p != '-')
1080                 return FALSE;
1081         *end = g_ascii_strtoull (p + 1, &p, 10);
1082         if (*p != '/')
1083                 return FALSE;
1084         p++;
1085         if (*p == '*') {
1086                 length = -1;
1087                 p++;
1088         } else
1089                 length = g_ascii_strtoull (p, &p, 10);
1090
1091         if (total_length)
1092                 *total_length = length;
1093         return *p == '\0';
1094 }
1095
1096 /**
1097  * soup_message_headers_set_content_range:
1098  * @hdrs: a #SoupMessageHeaders
1099  * @start: the start of the range
1100  * @end: the end of the range
1101  * @total_length: the total length of the resource, or -1 if unknown
1102  *
1103  * Sets @hdrs's Content-Range header according to the given values.
1104  * (Note that @total_length is the total length of the entire resource
1105  * that this is a range of, not simply @end - @start + 1.)
1106  *
1107  * Since: 2.26
1108  **/
1109 void
1110 soup_message_headers_set_content_range (SoupMessageHeaders  *hdrs,
1111                                         goffset              start,
1112                                         goffset              end,
1113                                         goffset              total_length)
1114 {
1115         char *header;
1116
1117         if (total_length >= 0) {
1118                 header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
1119                                           G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
1120                                           start, end, total_length);
1121         } else {
1122                 header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
1123                                           G_GINT64_FORMAT "/*", start, end);
1124         }
1125         soup_message_headers_replace (hdrs, "Content-Range", header);
1126         g_free (header);
1127 }
1128
1129 static gboolean
1130 parse_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
1131                    char **foo, GHashTable **params)
1132 {
1133         const char *header;
1134         char *semi;
1135
1136         header = soup_message_headers_get_one (hdrs, header_name);
1137         if (!header)
1138                 return FALSE;
1139
1140         if (foo) {
1141                 *foo = g_strdup (header);
1142                 semi = strchr (*foo, ';');
1143                 if (semi) {
1144                         char *p = semi;
1145
1146                         *semi++ = '\0';
1147                         while (p - 1 > *foo && g_ascii_isspace(p[-1]))
1148                                 *(--p) = '\0';
1149                 }
1150         } else {
1151                 semi = strchr (header, ';');
1152                 if (semi)
1153                         semi++;
1154         }
1155
1156         if (!params)
1157                 return TRUE;
1158
1159         if (!semi) {
1160                 *params = soup_header_parse_semi_param_list ("");
1161                 return TRUE;
1162         }
1163
1164         *params = soup_header_parse_semi_param_list (semi);
1165         return TRUE;
1166 }
1167
1168 static void
1169 set_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
1170                  const char *foo, GHashTable *params)
1171 {
1172         GString *str;
1173         GHashTableIter iter;
1174         gpointer key, value;
1175
1176         str = g_string_new (foo);
1177         if (params) {
1178                 g_hash_table_iter_init (&iter, params);
1179                 while (g_hash_table_iter_next (&iter, &key, &value)) {
1180                         g_string_append (str, "; ");
1181                         soup_header_g_string_append_param (str, key, value);
1182                 }
1183         }
1184
1185         soup_message_headers_replace (hdrs, header_name, str->str);
1186         g_string_free (str, TRUE);
1187 }
1188
1189 static void
1190 content_type_setter (SoupMessageHeaders *hdrs, const char *value)
1191 {
1192         g_free (hdrs->content_type);
1193         if (value) {
1194                 char *content_type, *p;
1195
1196                 parse_content_foo (hdrs, "Content-Type", &content_type, NULL);
1197                 p = strpbrk (content_type, " /");
1198                 if (!p || *p != '/' || strpbrk (p + 1, " /")) {
1199                         g_free (content_type);
1200                         hdrs->content_type = NULL;
1201                 } else
1202                         hdrs->content_type = content_type;
1203         } else
1204                 hdrs->content_type = NULL;
1205 }
1206
1207 /**
1208  * soup_message_headers_get_content_type:
1209  * @hdrs: a #SoupMessageHeaders
1210  * @params: (out) (element-type utf8 utf8) (allow-none) (transfer full):
1211  *   return location for the Content-Type parameters (eg, "charset"), or
1212  *   %NULL
1213  *
1214  * Looks up the "Content-Type" header in @hdrs, parses it, and returns
1215  * its value in *@content_type and *@params. @params can be %NULL if you
1216  * are only interested in the content type itself.
1217  *
1218  * Return value: a string with the value of the "Content-Type" header
1219  * or NULL if @hdrs does not contain that header or it cannot be
1220  * parsed (in which case *@params will be unchanged).
1221  *
1222  * Since: 2.26
1223  **/
1224 const char *
1225 soup_message_headers_get_content_type (SoupMessageHeaders  *hdrs,
1226                                        GHashTable         **params)
1227 {
1228         if (!hdrs->content_type)
1229                 return NULL;
1230
1231         if (params)
1232                 parse_content_foo (hdrs, "Content-Type", NULL, params);
1233         return hdrs->content_type;
1234 }
1235
1236 /**
1237  * soup_message_headers_set_content_type:
1238  * @hdrs: a #SoupMessageHeaders
1239  * @content_type: the MIME type
1240  * @params: (allow-none) (element-type utf8 utf8): additional
1241  * parameters, or %NULL
1242  *
1243  * Sets the "Content-Type" header in @hdrs to @content_type,
1244  * optionally with additional parameters specified in @params.
1245  *
1246  * Since: 2.26
1247  **/
1248 void
1249 soup_message_headers_set_content_type (SoupMessageHeaders  *hdrs,
1250                                        const char          *content_type,
1251                                        GHashTable          *params)
1252 {
1253         set_content_foo (hdrs, "Content-Type", content_type, params);
1254 }
1255
1256 /**
1257  * soup_message_headers_get_content_disposition:
1258  * @hdrs: a #SoupMessageHeaders
1259  * @disposition: (out) (transfer full): return location for the
1260  * disposition-type, or %NULL
1261  * @params: (out) (transfer full) (element-type utf8 utf8): return
1262  * location for the Content-Disposition parameters, or %NULL
1263  *
1264  * Looks up the "Content-Disposition" header in @hdrs, parses it, and
1265  * returns its value in *@disposition and *@params. @params can be
1266  * %NULL if you are only interested in the disposition-type.
1267  *
1268  * In HTTP, the most common use of this header is to set a
1269  * disposition-type of "attachment", to suggest to the browser that a
1270  * response should be saved to disk rather than displayed in the
1271  * browser. If @params contains a "filename" parameter, this is a
1272  * suggestion of a filename to use. (If the parameter value in the
1273  * header contains an absolute or relative path, libsoup will truncate
1274  * it down to just the final path component, so you do not need to
1275  * test this yourself.)
1276  *
1277  * Content-Disposition is also used in "multipart/form-data", however
1278  * this is handled automatically by #SoupMultipart and the associated
1279  * form methods.
1280  *
1281  * Return value: %TRUE if @hdrs contains a "Content-Disposition"
1282  * header, %FALSE if not (in which case *@disposition and *@params
1283  * will be unchanged).
1284  *
1285  * Since: 2.26
1286  **/
1287 gboolean
1288 soup_message_headers_get_content_disposition (SoupMessageHeaders  *hdrs,
1289                                               char               **disposition,
1290                                               GHashTable         **params)
1291 {
1292         gpointer orig_key, orig_value;
1293
1294         if (!parse_content_foo (hdrs, "Content-Disposition",
1295                                 disposition, params))
1296                 return FALSE;
1297
1298         /* If there is a filename parameter, make sure it contains
1299          * only a single path component
1300          */
1301         if (params && g_hash_table_lookup_extended (*params, "filename",
1302                                                     &orig_key, &orig_value)) {
1303                 char *filename = strrchr (orig_value, '/');
1304
1305                 if (filename)
1306                         g_hash_table_insert (*params, g_strdup (orig_key), filename + 1);
1307         }
1308         return TRUE;
1309 }
1310
1311 /**
1312  * soup_message_headers_set_content_disposition:
1313  * @hdrs: a #SoupMessageHeaders
1314  * @disposition: the disposition-type
1315  * @params: (allow-none) (element-type utf8 utf8): additional
1316  * parameters, or %NULL
1317  *
1318  * Sets the "Content-Disposition" header in @hdrs to @disposition,
1319  * optionally with additional parameters specified in @params.
1320  *
1321  * See soup_message_headers_get_content_disposition() for a discussion
1322  * of how Content-Disposition is used in HTTP.
1323  *
1324  * Since: 2.26
1325  **/
1326 void
1327 soup_message_headers_set_content_disposition (SoupMessageHeaders  *hdrs,
1328                                               const char          *disposition,
1329                                               GHashTable          *params)
1330 {
1331         set_content_foo (hdrs, "Content-Disposition", disposition, params);
1332 }
1333