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