<commit_before>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* soup-form.c : utility functions for HTML forms */

/*
 * Copyright 2008 Red Hat, Inc.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>

#include "soup-form.h"
#include "soup-message.h"
#include "soup-uri.h"

/**
 * SECTION:soup-form
 * @short_description: HTML form handling
 * @see_also: #SoupMultipart
 *
 * libsoup contains several help methods for processing HTML forms as
 * defined by <ulink
 * url="http://www.w3.org/TR/html401/interact/forms.html#h-17.13">the
 * HTML 4.01 specification</ulink>.
 **/

/**
 * SOUP_FORM_MIME_TYPE_URLENCODED:
 *
 * A macro containing the value
 * <literal>"application/x-www-form-urlencoded"</literal>; the default
 * MIME type for POSTing HTML form data.
 *
 * Since: 2.26
 **/

/**
 * SOUP_FORM_MIME_TYPE_MULTIPART:
 *
 * A macro containing the value
 * <literal>"multipart/form-data"</literal>; the MIME type used for
 * posting form data that contains files to be uploaded.
 *
 * Since: 2.26
 **/

#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))

static gboolean
form_decode (char *part)
{
	unsigned char *s, *d;

	s = d = (unsigned char *)part;
	do {
		if (*s == '%') {
			if (!g_ascii_isxdigit (s[1]) ||
			    !g_ascii_isxdigit (s[2]))
				return FALSE;
			*d++ = HEXCHAR (s);
			s += 2;
		} else if (*s == '+')
			*d++ = ' ';
		else
			*d++ = *s;
	} while (*s++);

	return TRUE;
}

/**
 * soup_form_decode:
 * @encoded_form: data of type "application/x-www-form-urlencoded"
 *
 * Decodes @form, which is an urlencoded dataset as defined in the
 * HTML 4.01 spec.
 *
 * Return value: (element-type utf8 utf8) (transfer full): a hash
 * table containing the name/value pairs from @encoded_form, which you
 * can free with g_hash_table_destroy().
 **/
GHashTable *
soup_form_decode (const char *encoded_form)
{
	GHashTable *form_data_set;
	char **pairs, *eq, *name, *value;
	int i;

	form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
					       g_free, NULL);
	pairs = g_strsplit (encoded_form, "&", -1);
	for (i = 0; pairs[i]; i++) {
		name = pairs[i];
		eq = strchr (name, '=');
		if (eq) {
			*eq = '\0';
			value = eq + 1;
		} else
			value = NULL;
		if (!value || !form_decode (name) || !form_decode (value)) {
			g_free (name);
			continue;
		}

		g_hash_table_replace (form_data_set, name, value);
	}
	g_free (pairs);

	return form_data_set;
}

/**
 * soup_form_decode_multipart:
 * @msg: a #SoupMessage containing a "multipart/form-data" request body
 * @file_control_name: (allow-none): the name of the HTML file upload control, or %NULL
 * @filename: (out) (allow-none): return location for the name of the uploaded file, or %NULL
 * @content_type: (out) (allow-none): return location for the MIME type of the uploaded file, or %NULL
 * @file: (out) (allow-none): return location for the uploaded file data, or %NULL
 *
 * Decodes the "multipart/form-data" request in @msg; this is a
 * convenience method for the case when you have a single file upload
 * control in a form. (Or when you don't have any file upload
 * controls, but are still using "multipart/form-data" anyway.) Pass
 * the name of the file upload control in @file_control_name, and
 * soup_form_decode_multipart() will extract the uploaded file data
 * into @filename, @content_type, and @file. All of the other form
 * control data will be returned (as strings, as with
 * soup_form_decode()) in the returned #GHashTable.
 *
 * You may pass %NULL for @filename, @content_type and/or @file if you do not
 * care about those fields. soup_form_decode_multipart() may also
 * return %NULL in those fields if the client did not provide that
 * information. You must free the returned filename and content-type
 * with g_free(), and the returned file data with soup_buffer_free().
 *
 * If you have a form with more than one file upload control, you will
 * need to decode it manually, using soup_multipart_new_from_message()
 * and soup_multipart_get_part().
 *
 * Return value: (element-type utf8 utf8) (transfer full): a hash
 * table containing the name/value pairs (other than
 * @file_control_name) from @msg, which you can free with
 * g_hash_table_destroy(). On error, it will return %NULL.
 *
 * Since: 2.26
 **/
GHashTable *
soup_form_decode_multipart (SoupMessage *msg, const char *file_control_name,
			    char **filename, char **content_type,
			    SoupBuffer **file)
{
	SoupMultipart *multipart;
	GHashTable *form_data_set, *params;
	SoupMessageHeaders *part_headers;
	SoupBuffer *part_body;
	char *disposition, *name;
	int i;

	multipart = soup_multipart_new_from_message (msg->request_headers,
						     msg->request_body);
	if (!multipart)
		return NULL;

	if (filename)
		*filename = NULL;
	if (content_type)
		*content_type = NULL;
	if (file)
		*file = NULL;

	form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
					       g_free, g_free);
	for (i = 0; i < soup_multipart_get_length (multipart); i++) {
		soup_multipart_get_part (multipart, i, &part_headers, &part_body);
		if (!soup_message_headers_get_content_disposition (
			    part_headers, &disposition, &params))
			continue;
		name = g_hash_table_lookup (params, "name");
		if (g_ascii_strcasecmp (disposition, "form-data") != 0 ||
		    !name) {
			g_free (disposition);
			g_hash_table_destroy (params);
			continue;
		}

		if (file_control_name && !strcmp (name, file_control_name)) {
			if (filename)
				*filename = g_strdup (g_hash_table_lookup (params, "filename"));<commit_msg>soup_form_decode_multipart: check that msg really is a non-NULL message<commit_after>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* soup-form.c : utility functions for HTML forms */

/*
 * Copyright 2008 Red Hat, Inc.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>

#include "soup-form.h"
#include "soup-message.h"
#include "soup-uri.h"

/**
 * SECTION:soup-form
 * @short_description: HTML form handling
 * @see_also: #SoupMultipart
 *
 * libsoup contains several help methods for processing HTML forms as
 * defined by <ulink
 * url="http://www.w3.org/TR/html401/interact/forms.html#h-17.13">the
 * HTML 4.01 specification</ulink>.
 **/

/**
 * SOUP_FORM_MIME_TYPE_URLENCODED:
 *
 * A macro containing the value
 * <literal>"application/x-www-form-urlencoded"</literal>; the default
 * MIME type for POSTing HTML form data.
 *
 * Since: 2.26
 **/

/**
 * SOUP_FORM_MIME_TYPE_MULTIPART:
 *
 * A macro containing the value
 * <literal>"multipart/form-data"</literal>; the MIME type used for
 * posting form data that contains files to be uploaded.
 *
 * Since: 2.26
 **/

#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))

static gboolean
form_decode (char *part)
{
	unsigned char *s, *d;

	s = d = (unsigned char *)part;
	do {
		if (*s == '%') {
			if (!g_ascii_isxdigit (s[1]) ||
			    !g_ascii_isxdigit (s[2]))
				return FALSE;
			*d++ = HEXCHAR (s);
			s += 2;
		} else if (*s == '+')
			*d++ = ' ';
		else
			*d++ = *s;
	} while (*s++);

	return TRUE;
}

/**
 * soup_form_decode:
 * @encoded_form: data of type "application/x-www-form-urlencoded"
 *
 * Decodes @form, which is an urlencoded dataset as defined in the
 * HTML 4.01 spec.
 *
 * Return value: (element-type utf8 utf8) (transfer full): a hash
 * table containing the name/value pairs from @encoded_form, which you
 * can free with g_hash_table_destroy().
 **/
GHashTable *
soup_form_decode (const char *encoded_form)
{
	GHashTable *form_data_set;
	char **pairs, *eq, *name, *value;
	int i;

	form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
					       g_free, NULL);
	pairs = g_strsplit (encoded_form, "&", -1);
	for (i = 0; pairs[i]; i++) {
		name = pairs[i];
		eq = strchr (name, '=');
		if (eq) {
			*eq = '\0';
			value = eq + 1;
		} else
			value = NULL;
		if (!value || !form_decode (name) || !form_decode (value)) {
			g_free (name);
			continue;
		}

		g_hash_table_replace (form_data_set, name, value);
	}
	g_free (pairs);

	return form_data_set;
}

/**
 * soup_form_decode_multipart:
 * @msg: a #SoupMessage containing a "multipart/form-data" request body
 * @file_control_name: (allow-none): the name of the HTML file upload control, or %NULL
 * @filename: (out) (allow-none): return location for the name of the uploaded file, or %NULL
 * @content_type: (out) (allow-none): return location for the MIME type of the uploaded file, or %NULL
 * @file: (out) (allow-none): return location for the uploaded file data, or %NULL
 *
 * Decodes the "multipart/form-data" request in @msg; this is a
 * convenience method for the case when you have a single file upload
 * control in a form. (Or when you don't have any file upload
 * controls, but are still using "multipart/form-data" anyway.) Pass
 * the name of the file upload control in @file_control_name, and
 * soup_form_decode_multipart() will extract the uploaded file data
 * into @filename, @content_type, and @file. All of the other form
 * control data will be returned (as strings, as with
 * soup_form_decode()) in the returned #GHashTable.
 *
 * You may pass %NULL for @filename, @content_type and/or @file if you do not
 * care about those fields. soup_form_decode_multipart() may also
 * return %NULL in those fields if the client did not provide that
 * information. You must free the returned filename and content-type
 * with g_free(), and the returned file data with soup_buffer_free().
 *
 * If you have a form with more than one file upload control, you will
 * need to decode it manually, using soup_multipart_new_from_message()
 * and soup_multipart_get_part().
 *
 * Return value: (element-type utf8 utf8) (transfer full): a hash
 * table containing the name/value pairs (other than
 * @file_control_name) from @msg, which you can free with
 * g_hash_table_destroy(). On error, it will return %NULL.
 *
 * Since: 2.26
 **/
GHashTable *
soup_form_decode_multipart (SoupMessage *msg, const char *file_control_name,
			    char **filename, char **content_type,
			    SoupBuffer **file)
{
	SoupMultipart *multipart;
	GHashTable *form_data_set, *params;
	SoupMessageHeaders *part_headers;
	SoupBuffer *part_body;
	char *disposition, *name;
	int i;

	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);

	multipart = soup_multipart_new_from_message (msg->request_headers,
						     msg->request_body);
	if (!multipart)
		return NULL;

	if (filename)
		*filename = NULL;
	if (content_type)
		*content_type = NULL;
	if (file)
		*file = NULL;

	form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
					       g_free, g_free);
	for (i = 0; i < soup_multipart_get_length (multipart); i++) {
		soup_multipart_get_part (multipart, i, &part_headers, &part_body);
		if (!soup_message_headers_get_content_disposition (
			    part_headers, &disposition, &params))
			continue;
		name = g_hash_table_lookup (params, "name");
		if (g_ascii_strcasecmp (disposition, "form-data") != 0 ||
		    !name) {
			g_free (disposition);
			g_hash_table_destroy (params);
			continue;
		}

		if (file_control_name && !strcmp (name, file_control_name)) {
			if (filename)
				*filename = g_strdup (g_hash_table_lookup (params, "filename"));
{'fraction_non_alphanumeric': 0.13126323218066338, 'fraction_numerical': 0.00899788285109386, 'mean_word_length': 3.933420365535248, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '70169', 'n_tokens_mistral': 4236, 'n_tokens_neox': 3948, 'n_words': 1223}