Remove build warning
[platform/upstream/libsoup.git] / libsoup / soup-form.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* soup-form.c : utility functions for HTML forms */
3
4 /*
5  * Copyright 2008 Red Hat, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <string.h>
13
14 #include "soup-form.h"
15 #include "soup.h"
16 #include "TIZEN.h"
17
18 /**
19  * SECTION:soup-form
20  * @short_description: HTML form handling
21  * @see_also: #SoupMultipart
22  *
23  * libsoup contains several help methods for processing HTML forms as
24  * defined by <ulink
25  * url="http://www.w3.org/TR/html401/interact/forms.html#h-17.13">the
26  * HTML 4.01 specification</ulink>.
27  **/
28
29 /**
30  * SOUP_FORM_MIME_TYPE_URLENCODED:
31  *
32  * A macro containing the value
33  * <literal>"application/x-www-form-urlencoded"</literal>; the default
34  * MIME type for POSTing HTML form data.
35  *
36  * Since: 2.26
37  **/
38
39 /**
40  * SOUP_FORM_MIME_TYPE_MULTIPART:
41  *
42  * A macro containing the value
43  * <literal>"multipart/form-data"</literal>; the MIME type used for
44  * posting form data that contains files to be uploaded.
45  *
46  * Since: 2.26
47  **/
48
49 #define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
50 #define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
51
52 static gboolean
53 form_decode (char *part)
54 {
55         unsigned char *s, *d;
56
57         s = d = (unsigned char *)part;
58         do {
59                 if (*s == '%') {
60                         if (!g_ascii_isxdigit (s[1]) ||
61                             !g_ascii_isxdigit (s[2]))
62                                 return FALSE;
63                         *d++ = HEXCHAR (s);
64                         s += 2;
65                 } else if (*s == '+')
66                         *d++ = ' ';
67                 else
68                         *d++ = *s;
69         } while (*s++);
70
71         return TRUE;
72 }
73
74 /**
75  * soup_form_decode:
76  * @encoded_form: data of type "application/x-www-form-urlencoded"
77  *
78  * Decodes @form, which is an urlencoded dataset as defined in the
79  * HTML 4.01 spec.
80  *
81  * Return value: (element-type utf8 utf8) (transfer full): a hash
82  * table containing the name/value pairs from @encoded_form, which you
83  * can free with g_hash_table_destroy().
84  **/
85 GHashTable *
86 soup_form_decode (const char *encoded_form)
87 {
88         GHashTable *form_data_set;
89         char **pairs, *eq, *name, *value;
90         int i;
91
92         form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
93                                                g_free, NULL);
94         pairs = g_strsplit (encoded_form, "&", -1);
95         for (i = 0; pairs[i]; i++) {
96                 name = pairs[i];
97                 eq = strchr (name, '=');
98                 if (eq) {
99                         *eq = '\0';
100                         value = eq + 1;
101                 } else
102                         value = NULL;
103                 if (!value || !form_decode (name) || !form_decode (value)) {
104                         g_free (name);
105                         continue;
106                 }
107
108                 g_hash_table_replace (form_data_set, name, value);
109         }
110         g_free (pairs);
111
112         return form_data_set;
113 }
114
115 /**
116  * soup_form_decode_multipart:
117  * @msg: a #SoupMessage containing a "multipart/form-data" request body
118  * @file_control_name: (allow-none): the name of the HTML file upload control, or %NULL
119  * @filename: (out) (allow-none): return location for the name of the uploaded file, or %NULL
120  * @content_type: (out) (allow-none): return location for the MIME type of the uploaded file, or %NULL
121  * @file: (out) (allow-none): return location for the uploaded file data, or %NULL
122  *
123  * Decodes the "multipart/form-data" request in @msg; this is a
124  * convenience method for the case when you have a single file upload
125  * control in a form. (Or when you don't have any file upload
126  * controls, but are still using "multipart/form-data" anyway.) Pass
127  * the name of the file upload control in @file_control_name, and
128  * soup_form_decode_multipart() will extract the uploaded file data
129  * into @filename, @content_type, and @file. All of the other form
130  * control data will be returned (as strings, as with
131  * soup_form_decode()) in the returned #GHashTable.
132  *
133  * You may pass %NULL for @filename, @content_type and/or @file if you do not
134  * care about those fields. soup_form_decode_multipart() may also
135  * return %NULL in those fields if the client did not provide that
136  * information. You must free the returned filename and content-type
137  * with g_free(), and the returned file data with soup_buffer_free().
138  *
139  * If you have a form with more than one file upload control, you will
140  * need to decode it manually, using soup_multipart_new_from_message()
141  * and soup_multipart_get_part().
142  *
143  * Return value: (element-type utf8 utf8) (transfer full): a hash
144  * table containing the name/value pairs (other than
145  * @file_control_name) from @msg, which you can free with
146  * g_hash_table_destroy(). On error, it will return %NULL.
147  *
148  * Since: 2.26
149  **/
150 GHashTable *
151 soup_form_decode_multipart (SoupMessage *msg, const char *file_control_name,
152                             char **filename, char **content_type,
153                             SoupBuffer **file)
154 {
155         SoupMultipart *multipart;
156         GHashTable *form_data_set, *params;
157         SoupMessageHeaders *part_headers;
158         SoupBuffer *part_body;
159         char *disposition, *name;
160         int i;
161
162         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
163
164         multipart = soup_multipart_new_from_message (msg->request_headers,
165                                                      msg->request_body);
166         if (!multipart)
167                 return NULL;
168
169         if (filename)
170                 *filename = NULL;
171         if (content_type)
172                 *content_type = NULL;
173         if (file)
174                 *file = NULL;
175
176         form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
177                                                g_free, g_free);
178         for (i = 0; i < soup_multipart_get_length (multipart); i++) {
179                 soup_multipart_get_part (multipart, i, &part_headers, &part_body);
180                 if (!soup_message_headers_get_content_disposition (
181                             part_headers, &disposition, &params))
182                         continue;
183                 name = g_hash_table_lookup (params, "name");
184                 if (g_ascii_strcasecmp (disposition, "form-data") != 0 ||
185                     !name) {
186                         g_free (disposition);
187                         g_hash_table_destroy (params);
188                         continue;
189                 }
190
191                 if (file_control_name && !strcmp (name, file_control_name)) {
192                         if (filename)
193                                 *filename = g_strdup (g_hash_table_lookup (params, "filename"));
194                         if (content_type)
195                                 *content_type = g_strdup (soup_message_headers_get_content_type (part_headers, NULL));
196                         if (file)
197                                 *file = soup_buffer_copy (part_body);
198                 } else {
199                         g_hash_table_insert (form_data_set,
200                                              g_strdup (name),
201                                              g_strndup (part_body->data,
202                                                         part_body->length));
203                 }
204
205                 g_free (disposition);
206                 g_hash_table_destroy (params);
207         }
208
209         soup_multipart_free (multipart);
210         return form_data_set;
211 }
212
213 static void
214 append_form_encoded (GString *str, const char *in)
215 {
216         const unsigned char *s = (const unsigned char *)in;
217
218         while (*s) {
219                 if (*s == ' ') {
220                         g_string_append_c (str, '+');
221                         s++;
222                 } else if (!g_ascii_isalnum (*s) && (*s != '-') && (*s != '_')
223                            && (*s != '.'))
224                         g_string_append_printf (str, "%%%02X", (int)*s++);
225                 else
226                         g_string_append_c (str, *s++);
227         }
228 }
229
230 static void
231 encode_pair (GString *str, const char *name, const char *value)
232 {
233         g_return_if_fail (name != NULL);
234         g_return_if_fail (value != NULL);
235
236         if (str->len)
237                 g_string_append_c (str, '&');
238         append_form_encoded (str, name);
239         g_string_append_c (str, '=');
240         append_form_encoded (str, value);
241 }
242
243 /**
244  * soup_form_encode:
245  * @first_field: name of the first form field
246  * @...: value of @first_field, followed by additional field names
247  * and values, terminated by %NULL.
248  *
249  * Encodes the given field names and values into a value of type
250  * "application/x-www-form-urlencoded", as defined in the HTML 4.01
251  * spec.
252  *
253  * This method requires you to know the names of the form fields (or
254  * at the very least, the total number of fields) at compile time; for
255  * working with dynamic forms, use soup_form_encode_hash() or
256  * soup_form_encode_datalist().
257  *
258  * Return value: the encoded form
259  **/
260 char *
261 soup_form_encode (const char *first_field, ...)
262 {
263         va_list args;
264         char *encoded;
265
266         va_start (args, first_field);
267         encoded = soup_form_encode_valist (first_field, args);
268         va_end (args);
269
270         return encoded;
271 }
272
273 /**
274  * soup_form_encode_hash:
275  * @form_data_set: (element-type utf8 utf8): a hash table containing
276  * name/value pairs (as strings)
277  *
278  * Encodes @form_data_set into a value of type
279  * "application/x-www-form-urlencoded", as defined in the HTML 4.01
280  * spec.
281  *
282  * Note that the HTML spec states that "The control names/values are
283  * listed in the order they appear in the document." Since this method
284  * takes a hash table, it cannot enforce that; if you care about the
285  * ordering of the form fields, use soup_form_encode_datalist().
286  *
287  * Return value: the encoded form
288  **/
289 char *
290 soup_form_encode_hash (GHashTable *form_data_set)
291 {
292         GString *str = g_string_new (NULL);
293         GHashTableIter iter;
294         gpointer name, value;
295
296         g_hash_table_iter_init (&iter, form_data_set);
297         while (g_hash_table_iter_next (&iter, &name, &value))
298                 encode_pair (str, name, value);
299         return g_string_free (str, FALSE);
300 }
301
302 static void
303 datalist_encode_foreach (GQuark key_id, gpointer value, gpointer str)
304 {
305         encode_pair (str, g_quark_to_string (key_id), value);
306 }
307
308 /**
309  * soup_form_encode_datalist:
310  * @form_data_set: a datalist containing name/value pairs
311  *
312  * Encodes @form_data_set into a value of type
313  * "application/x-www-form-urlencoded", as defined in the HTML 4.01
314  * spec. Unlike soup_form_encode_hash(), this preserves the ordering
315  * of the form elements, which may be required in some situations.
316  *
317  * Return value: the encoded form
318  **/
319 char *
320 soup_form_encode_datalist (GData **form_data_set)
321 {
322         GString *str = g_string_new (NULL);
323
324         g_datalist_foreach (form_data_set, datalist_encode_foreach, str);
325         return g_string_free (str, FALSE);
326 }
327
328 /**
329  * soup_form_encode_valist:
330  * @first_field: name of the first form field
331  * @args: pointer to additional values, as in soup_form_encode()
332  *
333  * See soup_form_encode(). This is mostly an internal method, used by
334  * various other methods such as soup_uri_set_query_from_fields() and
335  * soup_form_request_new().
336  *
337  * Return value: the encoded form
338  **/
339 char *
340 soup_form_encode_valist (const char *first_field, va_list args)
341 {
342         GString *str = g_string_new (NULL);
343         const char *name, *value;
344
345         name = first_field;
346         value = va_arg (args, const char *);
347         while (name && value) {
348                 encode_pair (str, name, value);
349
350                 name = va_arg (args, const char *);
351                 if (name)
352                         value = va_arg (args, const char *);
353         }
354
355         return g_string_free (str, FALSE);
356 }
357
358 static SoupMessage *
359 soup_form_request_for_data (const char *method, const char *uri_string,
360                             char *form_data)
361 {
362         SoupMessage *msg;
363         SoupURI *uri;
364
365         uri = soup_uri_new (uri_string);
366         if (!uri)
367                 return NULL;
368
369         if (!strcmp (method, "GET")) {
370                 g_free (uri->query);
371                 uri->query = form_data;
372
373                 msg = soup_message_new_from_uri (method, uri);
374         } else if (!strcmp (method, "POST") || !strcmp (method, "PUT")) {
375                 msg = soup_message_new_from_uri (method, uri);
376
377                 soup_message_set_request (
378                         msg, SOUP_FORM_MIME_TYPE_URLENCODED,
379                         SOUP_MEMORY_TAKE,
380                         form_data, strlen (form_data));
381         } else {
382                 g_warning ("invalid method passed to soup_form_request_new");
383                 g_free (form_data);
384
385                 /* Don't crash */
386                 msg = soup_message_new_from_uri (method, uri);
387         }
388         soup_uri_free (uri);
389
390         return msg;
391 }
392
393 /**
394  * soup_form_request_new:
395  * @method: the HTTP method, either "GET" or "POST"
396  * @uri: the URI to send the form data to
397  * @first_field: name of the first form field
398  * @...: value of @first_field, followed by additional field names
399  * and values, terminated by %NULL.
400  *
401  * Creates a new %SoupMessage and sets it up to send the given data
402  * to @uri via @method. (That is, if @method is "GET", it will encode
403  * the form data into @uri's query field, and if @method is "POST", it
404  * will encode it into the %SoupMessage's request_body.)
405  *
406  * Return value: (transfer full): the new %SoupMessage
407  **/
408 SoupMessage *
409 soup_form_request_new (const char *method, const char *uri,
410                        const char  *first_field, ...)
411 {
412         va_list args;
413         char *form_data;
414
415         va_start (args, first_field);
416         form_data = soup_form_encode_valist (first_field, args);
417         va_end (args);
418
419         return soup_form_request_for_data (method, uri, form_data);
420 }
421
422 /**
423  * soup_form_request_new_from_hash:
424  * @method: the HTTP method, either "GET" or "POST"
425  * @uri: the URI to send the form data to
426  * @form_data_set: (element-type utf8 utf8): the data to send to @uri
427  *
428  * Creates a new %SoupMessage and sets it up to send @form_data_set to
429  * @uri via @method, as with soup_form_request_new().
430  *
431  * Return value: (transfer full): the new %SoupMessage
432  **/
433 SoupMessage *
434 soup_form_request_new_from_hash (const char *method, const char *uri,
435                                  GHashTable *form_data_set)
436 {
437         return soup_form_request_for_data (
438                 method, uri, soup_form_encode_hash (form_data_set));
439 }
440
441 /**
442  * soup_form_request_new_from_datalist:
443  * @method: the HTTP method, either "GET" or "POST"
444  * @uri: the URI to send the form data to
445  * @form_data_set: the data to send to @uri
446  *
447  * Creates a new %SoupMessage and sets it up to send @form_data_set to
448  * @uri via @method, as with soup_form_request_new().
449  *
450  * Return value: (transfer full): the new %SoupMessage
451  **/
452 SoupMessage *
453 soup_form_request_new_from_datalist (const char *method, const char *uri,
454                                      GData **form_data_set)
455 {
456         return soup_form_request_for_data (
457                 method, uri, soup_form_encode_datalist (form_data_set));
458 }
459
460 /**
461  * soup_form_request_new_from_multipart:
462  * @uri: the URI to send the form data to
463  * @multipart: a "multipart/form-data" #SoupMultipart
464  *
465  * Creates a new %SoupMessage and sets it up to send @multipart to
466  * @uri via POST.
467  *
468  * To send a <literal>"multipart/form-data"</literal> POST, first
469  * create a #SoupMultipart, using %SOUP_FORM_MIME_TYPE_MULTIPART as
470  * the MIME type. Then use soup_multipart_append_form_string() and
471  * soup_multipart_append_form_file() to add the value of each form
472  * control to the multipart. (These are just convenience methods, and
473  * you can use soup_multipart_append_part() if you need greater
474  * control over the part headers.) Finally, call
475  * soup_form_request_new_from_multipart() to serialize the multipart
476  * structure and create a #SoupMessage.
477  *
478  * Return value: (transfer full): the new %SoupMessage
479  *
480  * Since: 2.26
481  **/
482 SoupMessage *
483 soup_form_request_new_from_multipart (const char *uri,
484                                       SoupMultipart *multipart)
485 {
486         SoupMessage *msg;
487
488         msg = soup_message_new ("POST", uri);
489 #if ENABLE(TIZEN_EXT)
490         if (msg)
491 #endif
492                 soup_multipart_to_message (multipart, msg->request_headers,
493                                    msg->request_body);
494
495         return msg;
496 }