source: trunk/third/libsoup/libsoup/soup-headers.c @ 21108

Revision 21108, 6.8 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21107, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2/*
3 * soup-headers.c: HTTP message header parsing
4 *
5 * Copyright (C) 2001-2003, Ximian, Inc.
6 */
7
8#include <string.h>
9#include <stdio.h>
10#include <ctype.h>
11
12#include "soup-headers.h"
13#include "soup-misc.h"
14
15/*
16 * "HTTP/1.1 200 OK\r\nContent-Length: 1234\r\n          567\r\n\r\n"
17 *                     ^             ^ ^    ^            ^   ^
18 *                     |             | |    |            |   |
19 *                    key            0 val  0          val+  0
20 *                                         , <---memmove-...
21 *
22 * key: "Content-Length"
23 * val: "1234, 567"
24 */
25static gboolean
26soup_headers_parse (char       *str,
27                    int         len,
28                    GHashTable *dest)
29{
30        char *key = NULL, *val = NULL, *end = NULL;
31        int offset = 0, lws = 0;
32
33        key = strstr (str, "\r\n");
34        key += 2;
35
36        /* join continuation headers, using a comma */
37        while ((key = strstr (key, "\r\n"))) {
38                key += 2;
39                offset = key - str;
40
41                if (!*key)
42                        break;
43
44                /* check if first character on the line is whitespace */
45                if (*key == ' ' || *key == '\t') {
46                        key -= 2;
47
48                        /* eat any trailing space from the previous line*/
49                        while (key [-1] == ' ' || key [-1] == '\t') key--;
50
51                        /* count how many characters are whitespace */
52                        lws = strspn (key, " \t\r\n");
53
54                        /* if continuation line, replace whitespace with ", " */
55                        if (key [-1] != ':') {
56                                lws -= 2;
57                                key [0] = ',';
58                                key [1] = ' ';
59                        }
60
61                        g_memmove (key, &key [lws], len - offset - lws);
62                }
63        }
64
65        key = str;
66
67        /* set eos for header key and value and add to hashtable */
68        while ((key = strstr (key, "\r\n"))) {
69                GSList *exist_hdrs;
70               
71                /* set end of last val, or end of http reason phrase */
72                key [0] = '\0';
73                key += 2;
74
75                if (!*key)
76                        break;
77
78                val = strchr (key, ':'); /* find start of val */
79
80                if (!val || val > strchr (key, '\r'))
81                        return FALSE;
82
83                /* set end of key */
84                val [0] = '\0';
85               
86                val++;
87                val += strspn (val, " \t");  /* skip whitespace */
88
89                /* find the end of the value */
90                end = strstr (val, "\r\n");
91                if (!end)
92                        return FALSE;
93
94                exist_hdrs = g_hash_table_lookup (dest, key);
95                exist_hdrs = g_slist_append (exist_hdrs,
96                                             g_strndup (val, end - val));
97
98                if (!exist_hdrs->next)
99                        g_hash_table_insert (dest, g_strdup (key), exist_hdrs);
100
101                key = end;
102        }
103
104        return TRUE;
105}
106
107gboolean
108soup_headers_parse_request (char             *str,
109                            int               len,
110                            GHashTable       *dest,
111                            char            **req_method,
112                            char            **req_path,
113                            SoupHttpVersion  *ver)
114{
115        guint http_major, http_minor;
116        char method[16], path[1024];
117
118        if (!str || !*str)
119                return FALSE;
120
121        if (sscanf (str,
122                    "%16s %1024s HTTP/%1u.%1u",
123                    method,
124                    path,
125                    &http_major,
126                    &http_minor) < 4)
127                return FALSE;
128
129        if (!soup_headers_parse (str, len, dest))
130                return FALSE;
131
132        *req_method = g_strdup (method);
133        *req_path = g_strdup (path);
134
135        if (ver) {
136                if (http_major == 1 && http_minor == 1)
137                        *ver = SOUP_HTTP_1_1;
138                else
139                        *ver = SOUP_HTTP_1_0;
140        }
141
142        return TRUE;
143}
144
145gboolean
146soup_headers_parse_status_line (const char       *status_line,
147                                SoupHttpVersion  *ver,
148                                guint            *status_code,
149                                char            **status_phrase)
150{
151        guint http_major, http_minor, code;
152        guint phrase_start = 0;
153
154        if (sscanf (status_line,
155                    "HTTP/%1u.%1u %3u %n",
156                    &http_major,
157                    &http_minor,
158                    &code,
159                    &phrase_start) < 3 || !phrase_start)
160                return FALSE;
161
162        if (ver) {
163                if (http_major == 1 && http_minor == 1)
164                        *ver = SOUP_HTTP_1_1;
165                else
166                        *ver = SOUP_HTTP_1_0;
167        }
168
169        if (status_code)
170                *status_code = code;
171
172        if (status_phrase)
173                *status_phrase = g_strdup (status_line + phrase_start);
174
175        return TRUE;
176}
177
178gboolean
179soup_headers_parse_response (char             *str,
180                             int               len,
181                             GHashTable       *dest,
182                             SoupHttpVersion  *ver,
183                             guint            *status_code,
184                             char            **status_phrase)
185{
186        if (!str || !*str || len < sizeof ("HTTP/0.0 000 A\r\n\r\n"))
187                return FALSE;
188
189        if (!soup_headers_parse (str, len, dest))
190                return FALSE;
191
192        if (!soup_headers_parse_status_line (str,
193                                             ver,
194                                             status_code,
195                                             status_phrase))
196                return FALSE;
197
198        return TRUE;
199}
200
201
202/*
203 * HTTP parameterized header parsing
204 */
205
206char *
207soup_header_param_copy_token (GHashTable *tokens, char *t)
208{
209        char *data;
210
211        g_return_val_if_fail (tokens, NULL);
212        g_return_val_if_fail (t, NULL);
213
214        if ( (data = g_hash_table_lookup (tokens, t)))
215                return g_strdup (data);
216        else
217                return NULL;
218}
219
220static void
221decode_lwsp (char **in)
222{
223        char *inptr = *in;
224
225        while (isspace (*inptr))
226                inptr++;
227
228        *in = inptr;
229}
230
231static char *
232decode_quoted_string (char **in)
233{
234        char *inptr = *in;
235        char *out = NULL, *outptr;
236        int outlen;
237        int c;
238
239        decode_lwsp (&inptr);
240        if (*inptr == '"') {
241                char *intmp;
242                int skip = 0;
243
244                /* first, calc length */
245                inptr++;
246                intmp = inptr;
247                while ( (c = *intmp++) && c != '"') {
248                        if (c == '\\' && *intmp) {
249                                intmp++;
250                                skip++;
251                        }
252                }
253
254                outlen = intmp - inptr - skip;
255                out = outptr = g_malloc (outlen + 1);
256
257                while ( (c = *inptr++) && c != '"') {
258                        if (c == '\\' && *inptr) {
259                                c = *inptr++;
260                        }
261                        *outptr++ = c;
262                }
263                *outptr = 0;
264        }
265
266        *in = inptr;
267
268        return out;
269}
270
271char *
272soup_header_param_decode_token (char **in)
273{
274        char *inptr = *in;
275        char *start;
276
277        decode_lwsp (&inptr);
278        start = inptr;
279
280        while (*inptr && *inptr != '=' && *inptr != ',')
281                inptr++;
282
283        if (inptr > start) {
284                *in = inptr;
285                return g_strndup (start, inptr - start);
286        }
287        else
288                return NULL;
289}
290
291static char *
292decode_value (char **in)
293{
294        char *inptr = *in;
295
296        decode_lwsp (&inptr);
297        if (*inptr == '"')
298                return decode_quoted_string (in);
299        else
300                return soup_header_param_decode_token (in);
301}
302
303GHashTable *
304soup_header_param_parse_list (const char *header)
305{
306        char *ptr;
307        gboolean added = FALSE;
308        GHashTable *params = g_hash_table_new (soup_str_case_hash,
309                                               soup_str_case_equal);
310
311        ptr = (char *) header;
312        while (ptr && *ptr) {
313                char *name;
314                char *value;
315
316                name = soup_header_param_decode_token (&ptr);
317                if (*ptr == '=') {
318                        ptr++;
319                        value = decode_value (&ptr);
320                        g_hash_table_insert (params, name, value);
321                        added = TRUE;
322                }
323
324                if (*ptr == ',')
325                        ptr++;
326        }
327
328        if (!added) {
329                g_hash_table_destroy (params);
330                params = NULL;
331        }
332
333        return params;
334}
335
336static void
337destroy_param_hash_elements (gpointer key, gpointer value, gpointer user_data)
338{
339        g_free (key);
340        g_free (value);
341}
342
343void
344soup_header_param_destroy_hash (GHashTable *table)
345{
346        g_hash_table_foreach (table, destroy_param_hash_elements, NULL);
347        g_hash_table_destroy (table);
348}
Note: See TracBrowser for help on using the repository browser.