1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* soup-form.c : utility functions for HTML forms */
5 * Copyright 2008 Red Hat, Inc.
14 #include "soup-form.h"
15 #include "soup-message.h"
20 * @short_description: HTML form handling
21 * @see_also: #SoupMultipart
23 * libsoup contains several help methods for processing HTML forms as
25 * url="http://www.w3.org/TR/html401/interact/forms.html#h-17.13">the
26 * HTML 4.01 specification</ulink>.
30 * SOUP_FORM_MIME_TYPE_URLENCODED:
32 * A macro containing the value
33 * <literal>"application/x-www-form-urlencoded"</literal>; the default
34 * MIME type for POSTing HTML form data.
40 * SOUP_FORM_MIME_TYPE_MULTIPART:
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.
49 #define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
50 #define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
53 form_decode (char *part)
57 s = d = (unsigned char *)part;
60 if (!g_ascii_isxdigit (s[1]) ||
61 !g_ascii_isxdigit (s[2]))
76 * @encoded_form: data of type "application/x-www-form-urlencoded"
78 * Decodes @form, which is an urlencoded dataset as defined in the
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().
86 soup_form_decode (const char *encoded_form)
88 GHashTable *form_data_set;
89 char **pairs, *eq, *name, *value;
92 form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
94 pairs = g_strsplit (encoded_form, "&", -1);
95 for (i = 0; pairs[i]; i++) {
97 eq = strchr (name, '=');
103 if (!value || !form_decode (name) || !form_decode (value)) {
108 g_hash_table_replace (form_data_set, name, value);
112 return form_data_set;
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
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.
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().
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().
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.
151 soup_form_decode_multipart (SoupMessage *msg, const char *file_control_name,
152 char **filename, char **content_type,
155 SoupMultipart *multipart;
156 GHashTable *form_data_set, *params;
157 SoupMessageHeaders *part_headers;
158 SoupBuffer *part_body;
159 char *disposition, *name;
162 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
164 multipart = soup_multipart_new_from_message (msg->request_headers,
172 *content_type = NULL;
176 form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
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, ¶ms))
183 name = g_hash_table_lookup (params, "name");
184 if (g_ascii_strcasecmp (disposition, "form-data") != 0 ||
186 g_free (disposition);
187 g_hash_table_destroy (params);
191 if (file_control_name && !strcmp (name, file_control_name)) {
193 *filename = g_strdup (g_hash_table_lookup (params, "filename"));
195 *content_type = g_strdup (soup_message_headers_get_content_type (part_headers, NULL));
197 *file = soup_buffer_copy (part_body);
199 g_hash_table_insert (form_data_set,
201 g_strndup (part_body->data,
205 g_free (disposition);
206 g_hash_table_destroy (params);
209 soup_multipart_free (multipart);
210 return form_data_set;
214 append_form_encoded (GString *str, const char *in)
216 const unsigned char *s = (const unsigned char *)in;
220 g_string_append_c (str, '+');
222 } else if (!g_ascii_isalnum (*s))
223 g_string_append_printf (str, "%%%02X", (int)*s++);
225 g_string_append_c (str, *s++);
230 encode_pair (GString *str, const char *name, const char *value)
232 g_return_if_fail (name != NULL);
233 g_return_if_fail (value != NULL);
236 g_string_append_c (str, '&');
237 append_form_encoded (str, name);
238 g_string_append_c (str, '=');
239 append_form_encoded (str, value);
243 hash_encode_foreach (gpointer name, gpointer value, gpointer str)
245 encode_pair (str, name, value);
250 * @first_field: name of the first form field
251 * @...: value of @first_field, followed by additional field names
252 * and values, terminated by %NULL.
254 * Encodes the given field names and values into a value of type
255 * "application/x-www-form-urlencoded", as defined in the HTML 4.01
258 * This method requires you to know the names of the form fields (or
259 * at the very least, the total number of fields) at compile time; for
260 * working with dynamic forms, use soup_form_encode_hash() or
261 * soup_form_encode_datalist().
263 * Return value: the encoded form
266 soup_form_encode (const char *first_field, ...)
271 va_start (args, first_field);
272 encoded = soup_form_encode_valist (first_field, args);
279 * soup_form_encode_hash:
280 * @form_data_set: (element-type utf8 utf8): a hash table containing
281 * name/value pairs (as strings)
283 * Encodes @form_data_set into a value of type
284 * "application/x-www-form-urlencoded", as defined in the HTML 4.01
287 * Note that the HTML spec states that "The control names/values are
288 * listed in the order they appear in the document." Since this method
289 * takes a hash table, it cannot enforce that; if you care about the
290 * ordering of the form fields, use soup_form_encode_datalist().
292 * Return value: the encoded form
295 soup_form_encode_hash (GHashTable *form_data_set)
297 GString *str = g_string_new (NULL);
299 g_hash_table_foreach (form_data_set, hash_encode_foreach, str);
300 return g_string_free (str, FALSE);
304 datalist_encode_foreach (GQuark key_id, gpointer value, gpointer str)
306 encode_pair (str, g_quark_to_string (key_id), value);
310 * soup_form_encode_datalist:
311 * @form_data_set: a datalist containing name/value pairs
313 * Encodes @form_data_set into a value of type
314 * "application/x-www-form-urlencoded", as defined in the HTML 4.01
315 * spec. Unlike soup_form_encode_hash(), this preserves the ordering
316 * of the form elements, which may be required in some situations.
318 * Return value: the encoded form
321 soup_form_encode_datalist (GData **form_data_set)
323 GString *str = g_string_new (NULL);
325 g_datalist_foreach (form_data_set, datalist_encode_foreach, str);
326 return g_string_free (str, FALSE);
330 * soup_form_encode_valist:
331 * @first_field: name of the first form field
332 * @args: pointer to additional values, as in soup_form_encode()
334 * See soup_form_encode(). This is mostly an internal method, used by
335 * various other methods such as soup_uri_set_query_from_fields() and
336 * soup_form_request_new().
338 * Return value: the encoded form
341 soup_form_encode_valist (const char *first_field, va_list args)
343 GString *str = g_string_new (NULL);
344 const char *name, *value;
347 value = va_arg (args, const char *);
348 while (name && value) {
349 encode_pair (str, name, value);
351 name = va_arg (args, const char *);
353 value = va_arg (args, const char *);
356 return g_string_free (str, FALSE);
360 soup_form_request_for_data (const char *method, const char *uri_string,
366 uri = soup_uri_new (uri_string);
370 if (!strcmp (method, "GET")) {
372 uri->query = form_data;
374 msg = soup_message_new_from_uri (method, uri);
375 } else if (!strcmp (method, "POST") || !strcmp (method, "PUT")) {
376 msg = soup_message_new_from_uri (method, uri);
378 soup_message_set_request (
379 msg, SOUP_FORM_MIME_TYPE_URLENCODED,
381 form_data, strlen (form_data));
383 g_warning ("invalid method passed to soup_form_request_new");
387 msg = soup_message_new_from_uri (method, uri);
395 * soup_form_request_new:
396 * @method: the HTTP method, either "GET" or "POST"
397 * @uri: the URI to send the form data to
398 * @first_field: name of the first form field
399 * @...: value of @first_field, followed by additional field names
400 * and values, terminated by %NULL.
402 * Creates a new %SoupMessage and sets it up to send the given data
403 * to @uri via @method. (That is, if @method is "GET", it will encode
404 * the form data into @uri's query field, and if @method is "POST", it
405 * will encode it into the %SoupMessage's request_body.)
407 * Return value: (transfer full): the new %SoupMessage
410 soup_form_request_new (const char *method, const char *uri,
411 const char *first_field, ...)
416 va_start (args, first_field);
417 form_data = soup_form_encode_valist (first_field, args);
420 return soup_form_request_for_data (method, uri, form_data);
424 * soup_form_request_new_from_hash:
425 * @method: the HTTP method, either "GET" or "POST"
426 * @uri: the URI to send the form data to
427 * @form_data_set: (element-type utf8 utf8): the data to send to @uri
429 * Creates a new %SoupMessage and sets it up to send @form_data_set to
430 * @uri via @method, as with soup_form_request_new().
432 * Return value: (transfer full): the new %SoupMessage
435 soup_form_request_new_from_hash (const char *method, const char *uri,
436 GHashTable *form_data_set)
438 return soup_form_request_for_data (
439 method, uri, soup_form_encode_hash (form_data_set));
443 * soup_form_request_new_from_datalist:
444 * @method: the HTTP method, either "GET" or "POST"
445 * @uri: the URI to send the form data to
446 * @form_data_set: the data to send to @uri
448 * Creates a new %SoupMessage and sets it up to send @form_data_set to
449 * @uri via @method, as with soup_form_request_new().
451 * Return value: (transfer full): the new %SoupMessage
454 soup_form_request_new_from_datalist (const char *method, const char *uri,
455 GData **form_data_set)
457 return soup_form_request_for_data (
458 method, uri, soup_form_encode_datalist (form_data_set));
462 * soup_form_request_new_from_multipart:
463 * @uri: the URI to send the form data to
464 * @multipart: a "multipart/form-data" #SoupMultipart
466 * Creates a new %SoupMessage and sets it up to send @multipart to
469 * To send a <literal>"multipart/form-data"</literal> POST, first
470 * create a #SoupMultipart, using %SOUP_FORM_MIME_TYPE_MULTIPART as
471 * the MIME type. Then use soup_multipart_append_form_string() and
472 * soup_multipart_append_form_file() to add the value of each form
473 * control to the multipart. (These are just convenience methods, and
474 * you can use soup_multipart_append_part() if you need greater
475 * control over the part headers.) Finally, call
476 * soup_form_request_new_from_multipart() to serialize the multipart
477 * structure and create a #SoupMessage.
479 * Return value: (transfer full): the new %SoupMessage
484 soup_form_request_new_from_multipart (const char *uri,
485 SoupMultipart *multipart)
489 msg = soup_message_new ("POST", uri);
490 soup_multipart_to_message (multipart, msg->request_headers,