~ubuntu-branches/ubuntu/jaunty/libsoup2.4/jaunty-proposed

« back to all changes in this revision

Viewing changes to libsoup/soup-message-headers.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2008-12-17 14:51:11 UTC
  • mfrom: (1.1.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20081217145111-sqbz10l57fmrh4vz
Tags: 2.25.4-0ubuntu1
* New upstream version
* debian/control.in:
  - build-depends on libgconf2-dev, libproxy-dev
* Updated to list the new libsoup-gnome binaries
* debian/rules:
  - updated shlibs version and list the new library

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
 */
7
7
 
8
8
#include <stdio.h>
 
9
#include <string.h>
9
10
 
10
11
#include "soup-message-headers.h"
 
12
#include "soup-headers.h"
11
13
#include "soup-misc.h"
12
14
 
13
15
/**
23
25
 * SoupMessageHeadersType:
24
26
 * @SOUP_MESSAGE_HEADERS_REQUEST: request headers
25
27
 * @SOUP_MESSAGE_HEADERS_RESPONSE: response headers
 
28
 * @SOUP_MESSAGE_HEADERS_MULTIPART: multipart body part headers
26
29
 *
27
30
 * Value passed to soup_message_headers_new() to set certain default
28
31
 * behaviors.
44
47
        SoupEncoding encoding;
45
48
        goffset content_length;
46
49
        SoupExpectation expectations;
 
50
        char *content_type;
47
51
 
48
52
        int ref_count;
49
53
};
94
98
                g_array_free (hdrs->array, TRUE);
95
99
                if (hdrs->concat)
96
100
                        g_hash_table_destroy (hdrs->concat);
 
101
                g_free (hdrs->content_type);
97
102
                g_slice_free (SoupMessageHeaders, hdrs);
98
103
        }
99
104
}
377
382
static void transfer_encoding_setter (SoupMessageHeaders *, const char *);
378
383
static void content_length_setter (SoupMessageHeaders *, const char *);
379
384
static void expectation_setter (SoupMessageHeaders *, const char *);
 
385
static void content_type_setter (SoupMessageHeaders *, const char *);
380
386
 
381
387
static char *
382
388
intern_header_locked (const char *name)
411
417
                g_hash_table_insert (header_setters,
412
418
                                     intern_header_locked ("Expect"),
413
419
                                     expectation_setter);
 
420
                g_hash_table_insert (header_setters,
 
421
                                     intern_header_locked ("Content-Type"),
 
422
                                     content_type_setter);
414
423
        }
415
424
 
416
425
        interned = intern_header_locked (name);
499
508
                        return hdrs->encoding;
500
509
        }
501
510
 
502
 
        hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_REQUEST) ?
503
 
                SOUP_ENCODING_NONE : SOUP_ENCODING_EOF;
 
511
        hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
 
512
                SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
504
513
        return hdrs->encoding;
505
514
}
506
515
 
652
661
        else
653
662
                soup_message_headers_remove (hdrs, "Expect");
654
663
}
 
664
 
 
665
/**
 
666
 * SoupRange:
 
667
 * @start: the start of the range
 
668
 * @end: the end of the range
 
669
 *
 
670
 * Represents a byte range as used in the Range header.
 
671
 *
 
672
 * If @end is non-negative, then @start and @end represent the bounds
 
673
 * of of the range, counting from %0. (Eg, the first 500 bytes would be
 
674
 * represented as @start = %0 and @end = %499.)
 
675
 *
 
676
 * If @end is %-1 and @start is non-negative, then this represents a
 
677
 * range starting at @start and ending with the last byte of the
 
678
 * requested resource body. (Eg, all but the first 500 bytes would be
 
679
 * @start = %500, and @end = %-1.)
 
680
 *
 
681
 * If @end is %-1 and @start is negative, then it represents a "suffix
 
682
 * range", referring to the last -@start bytes of the resource body.
 
683
 * (Eg, the last 500 bytes would be @start = %-500 and @end = %-1.)
 
684
 **/
 
685
 
 
686
static int
 
687
sort_ranges (gconstpointer a, gconstpointer b)
 
688
{
 
689
        SoupRange *ra = (SoupRange *)a;
 
690
        SoupRange *rb = (SoupRange *)b;
 
691
 
 
692
        return ra->start - rb->start;
 
693
}
 
694
 
 
695
/**
 
696
 * soup_message_headers_get_ranges:
 
697
 * @hdrs: a #SoupMessageHeaders
 
698
 * @total_length: the total_length of the response body
 
699
 * @ranges: return location for an array of #SoupRange
 
700
 * @length: the length of the returned array
 
701
 *
 
702
 * Parses @hdrs's Range header and returns an array of the requested
 
703
 * byte ranges. The returned array must be freed with
 
704
 * soup_message_headers_free_ranges().
 
705
 *
 
706
 * If @total_length is non-0, its value will be used to adjust the
 
707
 * returned ranges to have explicit start and end values, and the
 
708
 * returned ranges will be sorted and non-overlapping. If
 
709
 * @total_length is 0, then some ranges may have an end value of -1,
 
710
 * as described under #SoupRange, and some of the ranges may be
 
711
 * redundant.
 
712
 *
 
713
 * Return value: %TRUE if @hdrs contained a "Range" header containing
 
714
 * byte ranges which could be parsed, %FALSE otherwise (in which case
 
715
 * @range and @length will not be set).
 
716
 **/
 
717
gboolean
 
718
soup_message_headers_get_ranges (SoupMessageHeaders  *hdrs,
 
719
                                 goffset              total_length,
 
720
                                 SoupRange          **ranges,
 
721
                                 int                 *length)
 
722
{
 
723
        const char *range = soup_message_headers_get (hdrs, "Range");
 
724
        GSList *range_list, *r;
 
725
        GArray *array;
 
726
        char *spec, *end;
 
727
        int i;
 
728
 
 
729
        if (!range || strncmp (range, "bytes", 5) != 0)
 
730
                return FALSE;
 
731
 
 
732
        range += 5;
 
733
        while (g_ascii_isspace (*range))
 
734
                range++;
 
735
        if (*range++ != '=')
 
736
                return FALSE;
 
737
        while (g_ascii_isspace (*range))
 
738
                range++;
 
739
 
 
740
        range_list = soup_header_parse_list (range);
 
741
        if (!range_list)
 
742
                return FALSE;
 
743
 
 
744
        array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
 
745
        for (r = range_list; r; r = r->next) {
 
746
                SoupRange cur;
 
747
 
 
748
                spec = r->data;
 
749
                if (*spec == '-') {
 
750
                        cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
 
751
                        cur.end = total_length - 1;
 
752
                } else {
 
753
                        cur.start = g_ascii_strtoull (spec, &end, 10);
 
754
                        if (*end == '-')
 
755
                                end++;
 
756
                        if (*end)
 
757
                                cur.end = g_ascii_strtoull (end, &end, 10);
 
758
                        else
 
759
                                cur.end = total_length - 1;
 
760
                }
 
761
                if (*end) {
 
762
                        g_array_free (array, TRUE);
 
763
                        soup_header_free_list (range_list);
 
764
                        return FALSE;
 
765
                }
 
766
 
 
767
                g_array_append_val (array, cur);
 
768
        }
 
769
 
 
770
        soup_header_free_list (range_list);
 
771
 
 
772
        if (total_length) {
 
773
                g_array_sort (array, sort_ranges);
 
774
                for (i = 1; i < array->len; i++) {
 
775
                        SoupRange *cur = &((SoupRange *)array->data)[i];
 
776
                        SoupRange *prev = &((SoupRange *)array->data)[i - 1];
 
777
 
 
778
                        if (cur->start <= prev->end) {
 
779
                                prev->end = MAX (prev->end, cur->end);
 
780
                                g_array_remove_index (array, i);
 
781
                        }
 
782
                }
 
783
        }
 
784
 
 
785
        *ranges = (SoupRange *)array->data;
 
786
        *length = array->len;
 
787
 
 
788
        g_array_free (array, FALSE);
 
789
        return TRUE;
 
790
}
 
791
 
 
792
/**
 
793
 * soup_message_headers_free_ranges:
 
794
 * @hdrs: a #SoupMessageHeaders
 
795
 * @ranges: an array of #SoupRange
 
796
 *
 
797
 * Frees the array of ranges returned from soup_message_headers_get_ranges().
 
798
 **/
 
799
void
 
800
soup_message_headers_free_ranges (SoupMessageHeaders  *hdrs,
 
801
                                  SoupRange           *ranges)
 
802
{
 
803
        g_free (ranges);
 
804
}
 
805
 
 
806
/**
 
807
 * soup_message_headers_set_ranges:
 
808
 * @hdrs: a #SoupMessageHeaders
 
809
 * @ranges: an array of #SoupRange
 
810
 * @length: the length of @range
 
811
 *
 
812
 * Sets @hdrs's Range header to request the indicated ranges. (If you
 
813
 * only want to request a single range, you can use
 
814
 * soup_message_headers_set_range().)
 
815
 **/
 
816
void
 
817
soup_message_headers_set_ranges (SoupMessageHeaders  *hdrs,
 
818
                                 SoupRange           *ranges,
 
819
                                 int                  length)
 
820
{
 
821
        GString *header;
 
822
        int i;
 
823
 
 
824
        header = g_string_new ("bytes=");
 
825
        for (i = 0; i < length; i++) {
 
826
                if (i > 0)
 
827
                        g_string_append_c (header, ',');
 
828
                if (ranges[i].end >= 0) {
 
829
                        g_string_append_printf (header, "%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT,
 
830
                                                ranges[i].start, ranges[i].end);
 
831
                } else if (ranges[i].start >= 0) {
 
832
                        g_string_append_printf (header,"%" G_GINT64_FORMAT "-",
 
833
                                                ranges[i].start);
 
834
                } else {
 
835
                        g_string_append_printf (header, "%" G_GINT64_FORMAT,
 
836
                                                ranges[i].start);
 
837
                }
 
838
        }
 
839
 
 
840
        soup_message_headers_replace (hdrs, "Range", header->str);
 
841
        g_string_free (header, TRUE);
 
842
}
 
843
 
 
844
/**
 
845
 * soup_message_headers_set_range:
 
846
 * @hdrs: a #SoupMessageHeaders
 
847
 * @start: the start of the range to request
 
848
 * @end: the end of the range to request
 
849
 *
 
850
 * Sets @hdrs's Range header to request the indicated range.
 
851
 * @start and @end are interpreted as in a #SoupRange.
 
852
 *
 
853
 * If you need to request multiple ranges, use
 
854
 * soup_message_headers_set_ranges().
 
855
 **/
 
856
void
 
857
soup_message_headers_set_range (SoupMessageHeaders  *hdrs,
 
858
                                goffset              start,
 
859
                                goffset              end)
 
860
{
 
861
        SoupRange range;
 
862
 
 
863
        range.start = start;
 
864
        range.end = end;
 
865
        soup_message_headers_set_ranges (hdrs, &range, 1);
 
866
}
 
867
 
 
868
/**
 
869
 * soup_message_headers_get_content_range:
 
870
 * @hdrs: a #SoupMessageHeaders
 
871
 * @start: return value for the start of the range
 
872
 * @end: return value for the end of the range
 
873
 * @total_length: return value for the total length of the resource,
 
874
 * or %NULL if you don't care.
 
875
 *
 
876
 * Parses @hdrs's Content-Range header and returns it in @start,
 
877
 * @end, and @total_length. If the total length field in the header
 
878
 * was specified as "*", then @total_length will be set to -1.
 
879
 *
 
880
 * Return value: %TRUE if @hdrs contained a "Content-Range" header
 
881
 * containing a byte range which could be parsed, %FALSE otherwise.
 
882
 **/
 
883
gboolean
 
884
soup_message_headers_get_content_range (SoupMessageHeaders  *hdrs,
 
885
                                        goffset             *start,
 
886
                                        goffset             *end,
 
887
                                        goffset             *total_length)
 
888
{
 
889
        const char *header = soup_message_headers_get (hdrs, "Content-Range");
 
890
        goffset length;
 
891
        char *p;
 
892
 
 
893
        if (!header || strncmp (header, "bytes ", 6) != 0)
 
894
                return FALSE;
 
895
 
 
896
        header += 6;
 
897
        while (g_ascii_isspace (*header))
 
898
                header++;
 
899
        if (!g_ascii_isdigit (*header))
 
900
                return FALSE;
 
901
 
 
902
        *start = g_ascii_strtoull (header, &p, 10);
 
903
        if (*p != '-')
 
904
                return FALSE;
 
905
        *end = g_ascii_strtoull (p + 1, &p, 10);
 
906
        if (*p != '/')
 
907
                return FALSE;
 
908
        p++;
 
909
        if (*p == '*') {
 
910
                length = -1;
 
911
                p++;
 
912
        } else
 
913
                length = g_ascii_strtoull (p, &p, 10);
 
914
 
 
915
        if (total_length)
 
916
                *total_length = length;
 
917
        return *p == '\0';
 
918
}
 
919
 
 
920
/**
 
921
 * soup_message_headers_set_content_range:
 
922
 * @hdrs: a #SoupMessageHeaders
 
923
 * @start: the start of the range
 
924
 * @end: the end of the range
 
925
 * @total_length: the total length of the resource, or -1 if unknown
 
926
 *
 
927
 * Sets @hdrs's Content-Range header according to the given values.
 
928
 * (Note that @total_length is the total length of the entire resource
 
929
 * that this is a range of, not simply @end - @start + 1.)
 
930
 **/
 
931
void
 
932
soup_message_headers_set_content_range (SoupMessageHeaders  *hdrs,
 
933
                                        goffset              start,
 
934
                                        goffset              end,
 
935
                                        goffset              total_length)
 
936
{
 
937
        char *header;
 
938
 
 
939
        if (total_length >= 0) {
 
940
                header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
 
941
                                          G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
 
942
                                          start, end, total_length);
 
943
        } else {
 
944
                header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
 
945
                                          G_GINT64_FORMAT "/*", start, end);
 
946
        }
 
947
        soup_message_headers_replace (hdrs, "Content-Range", header);
 
948
        g_free (header);
 
949
}
 
950
 
 
951
static gboolean
 
952
parse_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
 
953
                   char **foo, GHashTable **params)
 
954
{
 
955
        const char *header;
 
956
        char *semi;
 
957
 
 
958
        header = soup_message_headers_get (hdrs, header_name);
 
959
        if (!header)
 
960
                return FALSE;
 
961
 
 
962
        if (foo) {
 
963
                *foo = g_strdup (header);
 
964
                semi = strchr (*foo, ';');
 
965
                if (semi) {
 
966
                        char *p = semi;
 
967
 
 
968
                        *semi++ = '\0';
 
969
                        while (p - 1 > *foo && g_ascii_isspace(p[-1]))
 
970
                                *(--p) = '\0';
 
971
                }
 
972
        } else {
 
973
                semi = strchr (header, ';');
 
974
                if (semi)
 
975
                        semi++;
 
976
        }
 
977
 
 
978
        if (!params)
 
979
                return TRUE;
 
980
 
 
981
        if (!semi) {
 
982
                *params = soup_header_parse_semi_param_list ("");
 
983
                return TRUE;
 
984
        }
 
985
 
 
986
        *params = soup_header_parse_semi_param_list (semi);
 
987
        return TRUE;
 
988
}
 
989
 
 
990
static void
 
991
set_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
 
992
                 const char *foo, GHashTable *params)
 
993
{
 
994
        GString *str;
 
995
        GHashTableIter iter;
 
996
        gpointer key, value;
 
997
 
 
998
        str = g_string_new (foo);
 
999
        if (params) {
 
1000
                g_hash_table_iter_init (&iter, params);
 
1001
                while (g_hash_table_iter_next (&iter, &key, &value)) {
 
1002
                        g_string_append (str, "; ");
 
1003
                        soup_header_g_string_append_param (str, key, value);
 
1004
                }
 
1005
        }
 
1006
 
 
1007
        soup_message_headers_replace (hdrs, header_name, str->str);
 
1008
        g_string_free (str, TRUE);
 
1009
}
 
1010
 
 
1011
static void
 
1012
content_type_setter (SoupMessageHeaders *hdrs, const char *value)
 
1013
{
 
1014
        g_free (hdrs->content_type);
 
1015
        if (value) {
 
1016
                parse_content_foo (hdrs, "Content-Type",
 
1017
                                   &hdrs->content_type, NULL);
 
1018
        } else
 
1019
                hdrs->content_type = NULL;
 
1020
}
 
1021
 
 
1022
/**
 
1023
 * soup_message_headers_get_content_type:
 
1024
 * @hdrs: a #SoupMessageHeaders
 
1025
 * @params: return location for the Content-Type parameters (eg,
 
1026
 * "charset"), or %NULL
 
1027
 *
 
1028
 * Looks up the "Content-Type" header in @hdrs, parses it, and returns
 
1029
 * its value in *@content_type and *@params. @params can be %NULL if you
 
1030
 * are only interested in the content type itself.
 
1031
 *
 
1032
 * Return value: %TRUE if @hdrs contains a "Content-Type" header,
 
1033
 * %FALSE if not (in which case *@content_type and *@params will be
 
1034
 * unchanged).
 
1035
 **/
 
1036
const char *
 
1037
soup_message_headers_get_content_type (SoupMessageHeaders  *hdrs,
 
1038
                                       GHashTable         **params)
 
1039
{
 
1040
        if (!hdrs->content_type)
 
1041
                return FALSE;
 
1042
 
 
1043
        if (params)
 
1044
                parse_content_foo (hdrs, "Content-Type", NULL, params);
 
1045
        return hdrs->content_type;
 
1046
}
 
1047
 
 
1048
/**
 
1049
 * soup_message_headers_set_content_type:
 
1050
 * @hdrs: a #SoupMessageHeaders
 
1051
 * @content_type: the MIME type
 
1052
 * @params: additional parameters, or %NULL
 
1053
 *
 
1054
 * Sets the "Content-Type" header in @hdrs to @content_type,
 
1055
 * optionally with additional parameters specified in @params.
 
1056
 **/
 
1057
void
 
1058
soup_message_headers_set_content_type (SoupMessageHeaders  *hdrs,
 
1059
                                       const char          *content_type,
 
1060
                                       GHashTable          *params)
 
1061
{
 
1062
        set_content_foo (hdrs, "Content-Type", content_type, params);
 
1063
}
 
1064
 
 
1065
/**
 
1066
 * soup_message_headers_get_content_disposition:
 
1067
 * @hdrs: a #SoupMessageHeaders
 
1068
 * @disposition: return location for the disposition-type, or %NULL
 
1069
 * @params: return location for the Content-Disposition parameters, or
 
1070
 * %NULL
 
1071
 *
 
1072
 * Looks up the "Content-Disposition" header in @hdrs, parses it, and
 
1073
 * returns its value in *@disposition and *@params. @params can be
 
1074
 * %NULL if you are only interested in the disposition-type.
 
1075
 *
 
1076
 * In HTTP, the most common use of this header is to set a
 
1077
 * disposition-type of "attachment", to suggest to the browser that a
 
1078
 * response should be saved to disk rather than displayed in the
 
1079
 * browser. If @params contains a "filename" parameter, this is a
 
1080
 * suggestion of a filename to use. (If the parameter value in the
 
1081
 * header contains an absolute or relative path, libsoup will truncate
 
1082
 * it down to just the final path component, so you do not need to
 
1083
 * test this yourself.)
 
1084
 *
 
1085
 * Content-Disposition is also used in "multipart/form-data", however
 
1086
 * this is handled automatically by #SoupMultipart and the associated
 
1087
 * form methods.
 
1088
 *
 
1089
 * Return value: %TRUE if @hdrs contains a "Content-Disposition"
 
1090
 * header, %FALSE if not (in which case *@disposition and *@params
 
1091
 * will be unchanged).
 
1092
 **/
 
1093
gboolean
 
1094
soup_message_headers_get_content_disposition (SoupMessageHeaders  *hdrs,
 
1095
                                              char               **disposition,
 
1096
                                              GHashTable         **params)
 
1097
{
 
1098
        gpointer orig_key, orig_value;
 
1099
 
 
1100
        if (!parse_content_foo (hdrs, "Content-Disposition",
 
1101
                                disposition, params))
 
1102
                return FALSE;
 
1103
 
 
1104
        /* If there is a filename parameter, make sure it contains
 
1105
         * only a single path component
 
1106
         */
 
1107
        if (params && g_hash_table_lookup_extended (*params, "filename",
 
1108
                                                    &orig_key, &orig_value)) {
 
1109
                char *filename = strrchr (orig_value, '/');
 
1110
 
 
1111
                if (filename)
 
1112
                        g_hash_table_insert (*params, orig_key, filename + 1);
 
1113
        }
 
1114
        return TRUE;
 
1115
}
 
1116
 
 
1117
/**
 
1118
 * soup_message_headers_set_content_disposition:
 
1119
 * @hdrs: a #SoupMessageHeaders
 
1120
 * @disposition: the disposition-type
 
1121
 * @params: additional parameters, or %NULL
 
1122
 *
 
1123
 * Sets the "Content-Disposition" header in @hdrs to @disposition,
 
1124
 * optionally with additional parameters specified in @params.
 
1125
 *
 
1126
 * See soup_message_headers_get_content_disposition() for a discussion
 
1127
 * of how Content-Disposition is used in HTTP.
 
1128
 **/
 
1129
void
 
1130
soup_message_headers_set_content_disposition (SoupMessageHeaders  *hdrs,
 
1131
                                              const char          *disposition,
 
1132
                                              GHashTable          *params)
 
1133
{
 
1134
        set_content_foo (hdrs, "Content-Disposition", disposition, params);
 
1135
}
 
1136