#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include "gskhttpheader.h"
#include "../gskerror.h"
#include "../gskmacros.h"
static GObjectClass *parent_class = NULL;
static GEnumClass *gsk_http_connection_class = NULL;
#define ACTUAL_LENGTH(str) ((str) ? (strlen (str) + 1) : 0)
enum
{
PROP_HEADER_0,
PROP_HEADER_MAJOR_VERSION,
PROP_HEADER_MINOR_VERSION,
PROP_HEADER_CONNECTION,
PROP_HEADER_CONNECTION_STRING,
PROP_HEADER_CONTENT_ENCODING,
PROP_HEADER_CONTENT_TYPE,
PROP_HEADER_CONTENT_SUBTYPE,
PROP_HEADER_CONTENT_CHARSET,
PROP_HEADER_TRANSFER_ENCODING,
PROP_HEADER_CONTENT_ENCODING_STRING,
PROP_HEADER_TRANSFER_ENCODING_STRING,
PROP_HEADER_CONTENT_LENGTH,
PROP_HEADER_RANGE_START,
PROP_HEADER_RANGE_END,
PROP_HEADER_DATE
};
/* --- string setting --- */
/**
* gsk_http_header_set_string:
* @http_header: HTTP header which owns the string.
* @p_str: pointer to the string's location.
* @str: string to copy and assign to *@p_str.
* (May be NULL)
*
* Private function to set a string in a HTTP header.
* (Currently this uses strdup, but we may eventually switch
* allocation strategies).
*/
void
gsk_http_header_set_string (gpointer http_header,
char **p_str,
const char *str)
{
char *cpy;
g_return_if_fail (GSK_IS_HTTP_HEADER (http_header));
cpy = g_strdup (str);
if (*p_str)
g_free (*p_str);
*p_str = cpy;
}
/**
* gsk_http_header_set_string_val:
* @http_header: HTTP header which owns the string.
* @p_str: pointer to the string's location.
* @value: a value which holds a string.
*
* Private function to set a string in a HTTP header from a value.
* This is used from the set_property methods in the various HTTP header
* class implementations.
*/
void
gsk_http_header_set_string_val (gpointer http_header,
char **p_str,
const GValue *value)
{
gsk_http_header_set_string (http_header, p_str,
g_value_get_string (value));
}
/**
* gsk_http_header_cut_string:
* @http_header: HTTP header which owns the string.
* @start: the start of the string
* @end: immediately past the input string.
*
* private.
* Copy a substring assuming that http_header is responsible
* for it.
*
* returns: the NUL-terminated copy of the string between @start
* and @end.
*/
char *
gsk_http_header_cut_string (gpointer http_header,
const char *start,
const char *end)
{
char *rv = g_new (char, end - start + 1);
memcpy (rv, start, end - start);
rv[end - start] = 0;
return rv;
}
/**
* gsk_http_header_free_string:
* @http_header: HTTP header which owns the string.
* @str: the string to free.
*
* Deallocate a string allocated with
* gsk_http_header_set_string(),
* gsk_http_header_set_string_val(),
* gsk_http_header_cut_string().
*/
void
gsk_http_header_free_string (gpointer http_header,
char *str)
{
g_return_if_fail (GSK_IS_HTTP_HEADER (http_header));
g_free (str);
}
/* --- GskHttpHeader implementation --- */
/**
* gsk_http_header_set_connection_string:
* @header: the HTTP header to affect.
* @str: connection type, as a string. (The string is exactly
* the same as that which is found literally in the header.)
*
* Change the Connection type reflected in this header.
*
* [From RFC 2616, Section 14.10]
* The Connection general-header field allows the sender to specify
* options that are desired for that particular connection and MUST NOT
* be communicated by proxies over further connections.
*/
void
gsk_http_header_set_connection_string (GskHttpHeader *header,
const char *str)
{
char *tmp = g_ascii_strdown (str, -1);
GEnumValue *enum_value = g_enum_get_value_by_nick (gsk_http_connection_class, tmp);
g_free (tmp);
if (enum_value != NULL)
header->connection_type = enum_value->value;
else
header->connection_type = GSK_HTTP_CONNECTION_CLOSE;
}
/**
* gsk_http_header_set_content_encoding_string:
* @header: the HTTP header to affect.
* @str: content-encoding type, as a string. (The string is exactly
* the same as that which is found literally in the header.)
*
* Change the Content-Encoding type reflected in this header.
*
* The actual encoding is done transparently by #GskHttpClient and #GskHttpServer usually.
* Encoding is only used for POST and PUT requests
* and any response that has a body (most do).
* The default is the identity encoding.
*/
void
gsk_http_header_set_content_encoding_string (GskHttpHeader *header,
const char *str)
{
if (g_ascii_strcasecmp (str, "identity") == 0)
header->content_encoding_type = GSK_HTTP_CONTENT_ENCODING_IDENTITY;
else if (g_ascii_strcasecmp (str, "gzip") == 0)
header->content_encoding_type = GSK_HTTP_CONTENT_ENCODING_GZIP;
else if (g_ascii_strcasecmp (str, "compress") == 0)
header->content_encoding_type = GSK_HTTP_CONTENT_ENCODING_COMPRESS;
else
{
header->content_encoding_type = GSK_HTTP_CONTENT_ENCODING_UNRECOGNIZED;
header->unrecognized_content_encoding = g_ascii_strdown (str, -1);
}
if (header->content_encoding_type != GSK_HTTP_CONTENT_ENCODING_UNRECOGNIZED
&& header->unrecognized_content_encoding != NULL)
{
g_free (header->unrecognized_content_encoding);
header->unrecognized_content_encoding = NULL;
}
}
/**
* gsk_http_header_set_transfer_encoding_string:
* @header: the HTTP header to affect.
* @str: transfer-encoding type, as a string. (The string is exactly
* the same as that which is found literally in the header.)
*
* Set the Transfer-Encoding type, as a string.
*
* [From RFC 2616, Section 14.41]
* The Transfer-Encoding general-header field indicates what (if any)
* type of transformation has been applied to the message body in order
* to safely transfer it between the sender and the recipient. This
* differs from the content-coding in that the transfer-coding is a
* property of the message, not of the entity.
*/
void
gsk_http_header_set_transfer_encoding_string (GskHttpHeader *header,
const char *str)
{
if (g_ascii_strcasecmp (str, "none") == 0)
header->transfer_encoding_type = GSK_HTTP_TRANSFER_ENCODING_NONE;
else if (g_ascii_strcasecmp (str, "chunked") == 0)
header->transfer_encoding_type = GSK_HTTP_TRANSFER_ENCODING_CHUNKED;
else
{
header->transfer_encoding_type = GSK_HTTP_TRANSFER_ENCODING_UNRECOGNIZED;
header->unrecognized_transfer_encoding = g_ascii_strdown (str, -1);
}
if (header->transfer_encoding_type != GSK_HTTP_TRANSFER_ENCODING_UNRECOGNIZED
&& header->unrecognized_transfer_encoding != NULL)
{
g_free (header->unrecognized_transfer_encoding);
header->unrecognized_transfer_encoding = NULL;
}
}
static void
gsk_http_header_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GskHttpHeader *header = GSK_HTTP_HEADER (object);
switch (property_id)
{
case PROP_HEADER_MAJOR_VERSION:
header->http_major_version = g_value_get_uint (value);
break;
case PROP_HEADER_MINOR_VERSION:
header->http_minor_version = g_value_get_uint (value);
break;
case PROP_HEADER_CONNECTION:
header->connection_type = g_value_get_enum (value);
break;
case PROP_HEADER_CONTENT_ENCODING:
header->content_encoding_type = g_value_get_enum (value);
break;
#if 0
case PROP_HEADER_CONTENT_ENCODING:
gsk_http_header_set_string_val (response, &response->content_encoding, value);
break;
#endif
case PROP_HEADER_CONTENT_TYPE:
gsk_http_header_set_string_val (header, &header->content_type, value);
header->has_content_type = 1; /* why is this needed? */
break;
case PROP_HEADER_CONTENT_SUBTYPE:
gsk_http_header_set_string_val (header, &header->content_subtype, value);
break;
case PROP_HEADER_CONTENT_CHARSET:
gsk_http_header_set_string_val (header, &header->content_charset, value);
break;
case PROP_HEADER_TRANSFER_ENCODING:
header->transfer_encoding_type = g_value_get_enum (value);
break;
case PROP_HEADER_CONNECTION_STRING:
gsk_http_header_set_connection_string (header, g_value_get_string (value));
break;
case PROP_HEADER_TRANSFER_ENCODING_STRING:
gsk_http_header_set_transfer_encoding_string (header, g_value_get_string (value));
break;
case PROP_HEADER_CONTENT_ENCODING_STRING:
gsk_http_header_set_content_encoding_string (header, g_value_get_string (value));
break;
case PROP_HEADER_CONTENT_LENGTH:
header->content_length = g_value_get_int (value);
break;
case PROP_HEADER_RANGE_START:
header->range_start = g_value_get_int (value);
break;
case PROP_HEADER_RANGE_END:
header->range_end = g_value_get_int (value);
break;
case PROP_HEADER_DATE:
header->date = g_value_get_long (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gsk_http_header_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GskHttpHeader *header = GSK_HTTP_HEADER (object);
switch (property_id)
{
case PROP_HEADER_MAJOR_VERSION:
g_value_set_uint (value, header->http_major_version);
break;
case PROP_HEADER_MINOR_VERSION:
g_value_set_uint (value, header->http_minor_version);
break;
case PROP_HEADER_CONNECTION:
g_value_set_enum (value, header->connection_type);
break;
case PROP_HEADER_CONTENT_ENCODING:
g_value_set_enum (value, header->content_encoding_type);
break;
case PROP_HEADER_CONTENT_TYPE:
g_value_set_string (value, header->content_type);
break;
case PROP_HEADER_CONTENT_SUBTYPE:
g_value_set_string (value, header->content_subtype);
break;
case PROP_HEADER_CONTENT_CHARSET:
g_value_set_string (value, header->content_charset);
break;
case PROP_HEADER_TRANSFER_ENCODING:
g_value_set_enum (value, header->transfer_encoding_type);
break;
case PROP_HEADER_CONNECTION_STRING:
{
GEnumValue *enum_value = g_enum_get_value (gsk_http_connection_class, header->connection_type);
const char *str = enum_value ? enum_value->value_nick : "unknown";
g_value_set_string (value, str);
}
break;
case PROP_HEADER_CONTENT_ENCODING_STRING:
{
switch (header->transfer_encoding_type)
{
case GSK_HTTP_CONTENT_ENCODING_IDENTITY:
g_value_set_string (value, "none");
break;
case GSK_HTTP_CONTENT_ENCODING_GZIP:
g_value_set_string (value, "gzip");
break;
case GSK_HTTP_CONTENT_ENCODING_COMPRESS:
g_value_set_string (value, "compress");
break;
case GSK_HTTP_CONTENT_ENCODING_UNRECOGNIZED:
g_value_set_string (value, header->unrecognized_content_encoding);
break;
default:
g_value_set_string (value, "unknown");
break;
}
}
break;
case PROP_HEADER_TRANSFER_ENCODING_STRING:
{
switch (header->transfer_encoding_type)
{
case GSK_HTTP_TRANSFER_ENCODING_NONE:
g_value_set_string (value, "none");
break;
case GSK_HTTP_TRANSFER_ENCODING_CHUNKED:
g_value_set_string (value, "chunked");
break;
case GSK_HTTP_TRANSFER_ENCODING_UNRECOGNIZED:
g_value_set_string (value, header->unrecognized_transfer_encoding);
break;
default:
g_value_set_string (value, "unknown");
break;
}
}
break;
case PROP_HEADER_CONTENT_LENGTH:
g_value_set_int (value, header->content_length);
break;
case PROP_HEADER_RANGE_START:
g_value_set_int (value, header->range_start);
break;
case PROP_HEADER_RANGE_END:
g_value_set_int (value, header->range_end);
break;
case PROP_HEADER_DATE:
g_value_set_long (value, header->date);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* gsk_http_header_get_connection:
* @header: the HTTP header to query.
*
* Returns the connection semantics.
* Note that this will never return #GSK_HTTP_CONNECTION_NONE;
* it will figure out if the default is keep-alive or close.
*
* Use gsk_http_header_get_connection_type() to get the
* actual connection-type that will be transmitted over the
* wire (with NONE meaning that there is no Connection: header).
*
* Note that the client can make Connection: requests,
* but the server is allowed to ignore keep-alive directives.
* So calling this on the request gets the client's suggestions,
* and calling this on the response gets the server's intentions.
*
* returns: the connection semantics.
*/
GskHttpConnection
gsk_http_header_get_connection (GskHttpHeader *header)
{
if (header->connection_type == GSK_HTTP_CONNECTION_NONE)
{
if (header->http_major_version == 0
|| (header->http_major_version == 1
&& header->http_minor_version == 0))
return GSK_HTTP_CONNECTION_CLOSE;
if (header->content_length >= 0
|| header->transfer_encoding_type == GSK_HTTP_TRANSFER_ENCODING_CHUNKED)
return GSK_HTTP_CONNECTION_KEEPALIVE;
/* default */
return GSK_HTTP_CONNECTION_CLOSE;
}
/* TODO: sanity checks */
return header->connection_type;
}
/**
* gsk_http_header_set_version:
* @header: the HTTP header to affect.
* @major: the major HTTP version number.
* @minor: the minor HTTP version number.
*
* Set the major and minor versions;
* for example, to use HTTP 1.0, one would set @major to 1,
* and @minor to 0.
*
* In addition to setting the version numbers,
* this function suppresses features not available in
* the respective http implementations.
*
* Only HTTP 1.0 and 1.1 are supported.
*/
void
gsk_http_header_set_version (GskHttpHeader *header,
gint major,
gint minor)
{
/* Only HTTP 1.0 and 1.1 are supported. */
g_return_if_fail (major == 1 && (0 <= minor && minor <= 1));
switch (minor)
{
case 0: /* HTTP 1.0 */
/* See RFC 1945. */
/* For now, we don't support HTTP 1.0 style keep-alive. (see RFC 2068) */
if (header->connection_type == GSK_HTTP_CONNECTION_KEEPALIVE)
header->connection_type = GSK_HTTP_CONNECTION_CLOSE;
/* No Transfer-Encoding header in HTTP 1.0 */
header->transfer_encoding_type = GSK_HTTP_TRANSFER_ENCODING_NONE;
break;
case 1: /* HTTP 1.1 */
break;
}
header->http_major_version = major;
header->http_minor_version = minor;
}
void
gsk_http_header_add_pragma (GskHttpHeader *header,
const char *pragma)
{
header->pragmas = g_slist_append (header->pragmas, g_strdup (pragma));
}
void
gsk_http_header_add_accepted_range (GskHttpHeader *header,
GskHttpRange range)
{
GskHttpRangeSet *rs = gsk_http_range_set_new (range);
GskHttpRangeSet *cur_last_range = header->accepted_range_units;
if (cur_last_range)
{
while (cur_last_range->next)
cur_last_range = cur_last_range->next;
cur_last_range->next = rs;
}
else
header->accepted_range_units = rs;
}
static void
gsk_http_header_finalize (GObject *object)
{
GskHttpHeader *header = GSK_HTTP_HEADER (object);
gsk_http_header_free_string (header, header->content_encoding);
gsk_http_header_free_string (header, header->content_type);
gsk_http_header_free_string (header, header->content_subtype);
gsk_http_header_free_string (header, header->content_charset);
if (header->content_languages)
g_strfreev (header->content_languages);
while (header->content_additional)
{
gchar *str = header->content_additional->data;
header->content_additional = g_slist_remove (header->content_additional, str);
gsk_http_header_free_string (header, str);
}
while (header->accepted_range_units)
{
GskHttpRangeSet *next = header->accepted_range_units->next;
gsk_http_range_set_free (header->accepted_range_units);
header->accepted_range_units = next;
}
if (header->g_error)
g_error_free (header->g_error);
g_free (header->unrecognized_transfer_encoding);
g_free (header->unrecognized_content_encoding);
if (header->header_lines)
g_hash_table_destroy (header->header_lines);
g_slist_foreach (header->errors, (GFunc) g_free, NULL);
g_slist_foreach (header->pragmas, (GFunc) g_free, NULL);
g_slist_free (header->errors);
g_slist_free (header->pragmas);
parent_class->finalize (object);
}
static void
gsk_http_header_init (GskHttpHeader *http_header)
{
http_header->http_major_version = 1;
http_header->http_minor_version = 1;
http_header->connection_type = GSK_HTTP_CONNECTION_NONE;
http_header->content_length = -1;
http_header->date = -1;
http_header->range_start = http_header->range_end = -1;
http_header->transfer_encoding_type = GSK_HTTP_TRANSFER_ENCODING_NONE;
http_header->content_encoding_type = GSK_HTTP_CONTENT_ENCODING_IDENTITY;
}
static void
gsk_http_header_class_init (GskHttpHeaderClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
parent_class = g_type_class_peek_parent (class);
object_class->get_property = gsk_http_header_get_property;
object_class->set_property = gsk_http_header_set_property;
object_class->finalize = gsk_http_header_finalize;
gsk_http_connection_class = g_type_class_ref (GSK_TYPE_HTTP_CONNECTION);
g_object_class_install_property (object_class,
PROP_HEADER_MAJOR_VERSION,
g_param_spec_uint ("major-version",
_("Major Version"),
_("major http version"),
0, 2, 1, G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_MINOR_VERSION,
g_param_spec_uint ("minor-version",
_("Minor Version"),
_("minor http version"),
0, 10, 1, G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_CONNECTION,
g_param_spec_enum ("connection",
_("Connection-Type"),
_("type of connection"),
GSK_TYPE_HTTP_CONNECTION,
GSK_HTTP_CONNECTION_NONE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_CONTENT_ENCODING,
g_param_spec_enum ("content-encoding",
_("Content Encoding Type"),
_("type of content encoding"),
GSK_TYPE_HTTP_CONTENT_ENCODING,
GSK_HTTP_CONTENT_ENCODING_IDENTITY,
G_PARAM_READWRITE));
#if 0
g_object_class_install_property (object_class,
PROP_RESPONSE_CONTENT_ENCODING,
g_param_spec_string ("content-encoding",
_("Content-Encoding"),
_("The method by which the content is encoded"),
NULL,
G_PARAM_READWRITE));
#endif
g_object_class_install_property (object_class,
PROP_HEADER_CONTENT_TYPE,
g_param_spec_string ("content-type",
_("Content-Type"),
_("The major type of content"),
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_CONTENT_SUBTYPE,
g_param_spec_string ("content-subtype",
_("Content-Subtype"),
_("The minor type of content"),
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_CONTENT_CHARSET,
g_param_spec_string ("content-charset",
_("Content-Charset"),
_("The character set used for text content"),
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_TRANSFER_ENCODING,
g_param_spec_enum ("transfer-encoding",
_("Transfer Encoding Type"),
_("type of transfer encoding"),
GSK_TYPE_HTTP_TRANSFER_ENCODING,
GSK_HTTP_TRANSFER_ENCODING_NONE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_CONNECTION_STRING,
g_param_spec_string ("connection-string",
_("Connection-Type (string)"),
_("type of connection as string"),
"close",
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_CONTENT_ENCODING_STRING,
g_param_spec_string ("content-encoding-string",
_("Content Encoding-Type (string)"),
_("type of content encoding as string"),
"identity",
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_TRANSFER_ENCODING_STRING,
g_param_spec_string ("transfer-encoding-string",
_("Transfer Encoding-Type (string)"),
_("type of transfer encoding as string"),
"none",
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_CONTENT_LENGTH,
g_param_spec_int ("content-length",
_("Content Length"),
_("length of the data"),
-1, G_MAXINT, -1, G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_RANGE_START,
g_param_spec_int ("range-start",
_("Range Start"),
_("beginning of the data"),
-1, G_MAXINT, -1, G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_RANGE_END,
g_param_spec_int ("range-end",
_("Range End"),
_("end of the data"),
-1, G_MAXINT, -1, G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_HEADER_DATE,
g_param_spec_int ("date",
_("Date"),
_("date of the content"),
-1, G_MAXLONG, -1, G_PARAM_READWRITE));
}
GType gsk_http_header_get_type()
{
static GType http_header_type = 0;
if (!http_header_type)
{
static const GTypeInfo http_header_info =
{
sizeof(GskHttpHeaderClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gsk_http_header_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GskHttpHeader),
0, /* n_preallocs */
(GInstanceInitFunc) gsk_http_header_init,
NULL /* value_table */
};
http_header_type = g_type_register_static (G_TYPE_OBJECT,
"GskHttpHeader",
&http_header_info,
G_TYPE_FLAG_ABSTRACT);
}
return http_header_type;
}
/* --- foreign headers --- */
/**
* gsk_http_header_add_misc:
* @header: the header to affect.
* @key: a case-insensitive ASCII string for the key for this foreign header.
* This key should not be a standard HTTP tag.
* @value: a case-sensitive value for that key.
*
* Add a raw header line to the header, with an associated value.
*/
void
gsk_http_header_add_misc (GskHttpHeader *header,
const char *key,
const char *value)
{
/* TODO: should use a case-insensitive hash function,
instead of g_ascii_strdown()!!! */
if (header->header_lines == NULL)
header->header_lines = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_insert (header->header_lines, g_ascii_strdown (key, -1), g_strdup (value));
}
/**
* gsk_http_header_remove_misc:
* @header: the header to affect.
* @key: a case-insensitive ASCII string for the key for this foreign header.
* This key should not be a standard HTTP tag.
*
* Remove a raw header line from the header.
*/
void
gsk_http_header_remove_misc (GskHttpHeader *header,
const char *key)
{
guint len;
char *lower;
guint i;
len = strlen (key);
lower = g_alloca (len + 1);
for (i = 0; i < len; i++)
lower[i] = g_ascii_tolower (key[i]);
lower[i] = 0;
g_return_if_fail (g_hash_table_lookup (header->header_lines, lower) != NULL);
g_hash_table_remove (header->header_lines, lower);
}
/**
* gsk_http_header_lookup_misc:
* @header: the header to query.
* @key: a case-insensitive ASCII string for the key for this foreign header.
* This key should not be a standard HTTP tag.
* returns: value of the key.
*
* Lookup a miscellaneous HTTP header. All headers that begin with X-
* are stored in here.
*/
const char *
gsk_http_header_lookup_misc (GskHttpHeader *header,
const char *key)
{
guint len;
char *lower;
guint i;
if (header->header_lines == NULL)
return NULL;
len = strlen (key);
lower = g_alloca (len + 1);
for (i = 0; i < len; i++)
lower[i] = g_ascii_tolower (key[i]);
lower[i] = 0;
return g_hash_table_lookup (header->header_lines, lower);
}
/* --- Enum type registration --- */
#include "gskhttpheader.inc" /* machine-generated enum-value tables */
#define DEFINE_ENUM_GET_TYPE_FUNC(class, lower) \
GType lower ## _get_type (void) \
{ \
static GType type = 0; \
if (type == 0) \
type = g_enum_register_static (#class, lower ## _enum_values); \
return type; \
}
DEFINE_ENUM_GET_TYPE_FUNC(GskHttpConnection, gsk_http_connection)
DEFINE_ENUM_GET_TYPE_FUNC(GskHttpVerb, gsk_http_verb)
DEFINE_ENUM_GET_TYPE_FUNC(GskHttpRange, gsk_http_range)
DEFINE_ENUM_GET_TYPE_FUNC(GskHttpTransferEncoding, gsk_http_transfer_encoding)
DEFINE_ENUM_GET_TYPE_FUNC(GskHttpContentEncoding, gsk_http_content_encoding)
DEFINE_ENUM_GET_TYPE_FUNC(GskHttpStatus, gsk_http_status)
#undef DEFINE_ENUM_GET_TYPE_FUNC
/* --- Miscellaneous boxed types --- */
/* set OUT to NULL if IN is NULL,
or SLAB_AT otherwise. Copy IN to SLAB_AT and
advance SLAB_AT. */
#define MAYBE_COPY(out, in, slab_at) \
G_STMT_START{ \
if (in == NULL) \
out = NULL; \
else \
{ \
out = slab_at; \
slab_at = g_stpcpy (slab_at, in) + 1; \
} \
}G_STMT_END
/* GskHttpAuthenticate */
/**
* gsk_http_authenticate_new_unknown:
* @challenge: the string to challenge with.
*
* Handling your special purpose authenticator.
* Note that Basic and Digest authenticator parsers
* are built-in to GSK, this is only for other types.
*
* It is given from the server to the client.
* The client must response (in another message) with
* an authorization.
*
* The purpose of a challenge in cryptographic systems
* is to randomize the transaction so that an attacker cannot
* use the same Authorization lines to get access to a machine.
*
* Of course, if you use an insecure transport layer, it is MUCH
* harder to make things secure -- using SSL is recommended.
*
* Some WWW-Authenticate header is required
* in a GSK_HTTP_STATUS_UNAUTHORIZED response message.
*
* returns: the new challenge.
*/
GskHttpAuthenticate*
gsk_http_authenticate_new_unknown (const char *auth_scheme_name,
const char *realm,
const char *options)
{
guint len = sizeof (GskHttpAuthenticate)
+ ACTUAL_LENGTH (auth_scheme_name)
+ ACTUAL_LENGTH (realm)
+ ACTUAL_LENGTH (options);
char *at;
GskHttpAuthenticate *auth = g_malloc (len);
auth->mode = GSK_HTTP_AUTH_MODE_UNKNOWN;
auth->ref_count = 1;
at = (char *)(auth + 1);
MAYBE_COPY(auth->auth_scheme_name, auth_scheme_name, at);
MAYBE_COPY(auth->realm, realm, at);
MAYBE_COPY(auth->info.unknown.options, realm, at);
return auth;
}
/**
* gsk_http_authenticate_new_basic:
* @realm: the area on the site to authenticate for.
* This is the name used by the RFC.
*
* Create an #GskHttpAuthenticate for basic authentication.
* Basic authentication is very insecure: the password
* is merely transmitted base64 encoded.
*
* returns: the new challenge.
*/
GskHttpAuthenticate *gsk_http_authenticate_new_basic (const char *realm)
{
guint len = sizeof (GskHttpAuthenticate)
+ ACTUAL_LENGTH (realm);
char *at;
GskHttpAuthenticate *auth = g_malloc (len);
auth->mode = GSK_HTTP_AUTH_MODE_BASIC;
at = (char *)(auth + 1);
auth->auth_scheme_name = "Basic";
auth->ref_count = 1;
MAYBE_COPY (auth->realm, realm, at);
return auth;
}
/**
* gsk_http_authenticate_new_digest:
* @realm: the area on the site to authenticate for.
* @domain:
* @nonce: value hashed into the digest to keep it secure against replay attacks.
* @opaque: value which must be sent back to the server,
* which the client does not interpret.
* @algorithm: digest algorithm. Only MD5 is supported for now,
* and is equivalent to specifying NULL.
*
* Create an #GskHttpAuthenticate for Digest authentication.
* Digest authentication improves Basic authentication by
* using a hash of the password.
*
* returns: the new challenge.
*/
GskHttpAuthenticate *gsk_http_authenticate_new_digest (const char *realm,
const char *domain,
const char *nonce,
const char *opaque,
const char *algorithm)
{
gboolean is_md5 = algorithm == NULL || strcmp (algorithm, "MD5") == 0;
guint len = sizeof (GskHttpAuthenticate)
+ ACTUAL_LENGTH (realm)
+ ACTUAL_LENGTH (domain)
+ ACTUAL_LENGTH (nonce)
+ ACTUAL_LENGTH (opaque)
+ (is_md5 ? 0 : (strlen (algorithm) + 1));
GskHttpAuthenticate *auth = g_malloc (len);
char *at;
auth->ref_count = 1;
auth->mode = GSK_HTTP_AUTH_MODE_DIGEST;
auth->auth_scheme_name = "Digest";
at = (char*)(auth + 1);
auth->realm = at;
at = g_stpcpy (at, realm) + 1;
MAYBE_COPY (auth->realm, realm, at);
MAYBE_COPY (auth->info.digest.domain, domain, at);
MAYBE_COPY (auth->info.digest.nonce, nonce, at);
MAYBE_COPY (auth->info.digest.opaque, opaque, at);
auth->info.digest.algorithm = is_md5 ? NULL : strcpy (at, algorithm);
return auth;
}
#if 0
static GskHttpAuthenticate *
gsk_http_authenticate_copy (const GskHttpAuthenticate *auth)
{
switch (auth->mode)
{
case GSK_HTTP_AUTH_MODE_UNKNOWN:
return gsk_http_authenticate_new_unknown (auth->auth_scheme_name,
auth->realm,
auth->info.unknown.options);
case GSK_HTTP_AUTH_MODE_BASIC:
return gsk_http_authenticate_new_basic (auth->realm);
case GSK_HTTP_AUTH_MODE_DIGEST:
return gsk_http_authenticate_new_digest (auth->realm,
auth->info.digest.domain,
auth->info.digest.nonce,
auth->info.digest.opaque,
auth->info.digest.algorithm);
default:
g_return_val_if_reached (NULL);
}
}
#endif
GskHttpAuthenticate *
gsk_http_authenticate_ref (GskHttpAuthenticate *auth)
{
g_return_val_if_fail (auth->ref_count != 0, auth);
++(auth->ref_count);
return auth;
}
void
gsk_http_authenticate_unref (GskHttpAuthenticate *auth)
{
g_return_if_fail (auth->ref_count != 0);
if (--(auth->ref_count) == 0)
{
g_free (auth);
}
}
/* GskHttpAuthorization */
/**
* gsk_http_authorization_new_unknown:
* @auth_scheme_name: name of the authentification scheme.
*
* Arbitrary authorization response.
*
* We recommend you use gsk_http_authorization_new_digest()
* instead, or gsk_http_authorization_new_basic() if that's all
* the server can do.
*
* returns: the response to the authentication request.
*/
GskHttpAuthorization *gsk_http_authorization_new_unknown (const char *auth_scheme_name,
const char *response)
{
guint len = sizeof (GskHttpAuthorization)
+ ACTUAL_LENGTH (auth_scheme_name)
+ ACTUAL_LENGTH (response);
char *at;
GskHttpAuthorization *auth = g_malloc (len);
at = (char *)(auth + 1);
auth->mode = GSK_HTTP_AUTH_MODE_UNKNOWN;
MAYBE_COPY (auth->auth_scheme_name, auth_scheme_name, at);
MAYBE_COPY (auth->info.unknown.response, response, at);
auth->ref_count = 1;
return auth;
}
/**
* gsk_http_authorization_new_basic:
* @realm: the area on the site to authenticate for.
* @user: name of the account to authenticate for.
* @password: password of the account to authenticate for.
*
* Basic HTTP authentication response.
* Not very secure because your password is provided plain-text.
*
* returns: the response to the authentication request.
*/
GskHttpAuthorization *gsk_http_authorization_new_basic (const char *user,
const char *password)
{
guint len = sizeof (GskHttpAuthorization)
+ ACTUAL_LENGTH (user)
+ ACTUAL_LENGTH (password);
char *at;
GskHttpAuthorization *auth = g_malloc (len);
at = (char *)(auth + 1);
auth->mode = GSK_HTTP_AUTH_MODE_BASIC;
auth->auth_scheme_name = "Basic";
MAYBE_COPY (auth->info.basic.user, user, at);
MAYBE_COPY (auth->info.basic.password, password, at);
auth->ref_count = 1;
return auth;
}
/**
* gsk_http_authorization_new_digest:
* @realm: the area on the site to authenticate for.
* @domain:
* @nonce: value hashed into the digest to keep it secure against replay attacks.
* @opaque: value which must be sent back to the server,
* which the client does not interpret.
* @algorithm: digest algorithm. Only MD5 is supported for now,
* and is equivalent to specifying NULL.
* @user: name of the account to authenticate for.
* @password: password of the account to authenticate for. [unnecessary if you have the response digest]
* This value is NOT transmitted to the remote host.
* @response_digest: the securely-hashed response. If NULL, Gsk will compute the response.
* @entity_digest: the securely-hashed information about your POST content.
*
* Create a new Digest-based authentication response.
* Many of the parameters must match the Authenticate header.
*
* The response_digest is a value computed from nonce, user, and password.
* The security of Digest authentication derives from the difficulty of
* computing the password from the digest, since the password itself is
* not transmitted cleartext.
*
* returns: the response to the authentication request.
*/
GskHttpAuthorization *gsk_http_authorization_new_digest (const char *realm,
const char *domain,
const char *nonce,
const char *opaque,
const char *algorithm,
const char *user,
const char *password,
const char *response_digest,
const char *entity_digest)
{
/* NB: nonce, response_digest, entity_digest are all allocated separately */
gboolean is_md5 = algorithm == NULL || strcmp (algorithm, "MD5") == 0;
guint len = sizeof (GskHttpAuthorization)
+ ACTUAL_LENGTH (realm)
+ ACTUAL_LENGTH (domain)
+ ACTUAL_LENGTH (opaque)
+ ACTUAL_LENGTH (user)
+ ACTUAL_LENGTH (password)
+ (is_md5 ? 0 : (strlen (algorithm) + 1));
char *at;
GskHttpAuthorization *auth = g_malloc (len);
at = (char *)(auth + 1);
auth->mode = GSK_HTTP_AUTH_MODE_DIGEST;
auth->auth_scheme_name = "Digest";
MAYBE_COPY (auth->info.digest.realm, realm, at);
MAYBE_COPY (auth->info.digest.domain, domain, at);
MAYBE_COPY (auth->info.digest.opaque, opaque, at);
MAYBE_COPY (auth->info.digest.user, user, at);
MAYBE_COPY (auth->info.digest.password, password, at);
if (is_md5)
auth->info.digest.algorithm = NULL;
else
auth->info.digest.algorithm = strcpy (at, algorithm);
auth->info.digest.response_digest = g_strdup (response_digest);
auth->info.digest.entity_digest = g_strdup (entity_digest);
auth->ref_count = 1;
return auth;
}
/**
* gsk_http_authorization_new_respond:
* @auth: the authentication request to respond to (given as a challenge by the server).
* @user: the username of the account (or whatever) to use.
* @password: password of the account to authenticate for.
* @error: optional place to put the error if something goes wrong.
*
* Attempt to use the given user/password pair as authentication information for
* the given request.
*
* returns: the new response, or NULL if something goes wrong.
*/
GskHttpAuthorization *
gsk_http_authorization_new_respond (const GskHttpAuthenticate *auth,
const char *user,
const char *password,
GError **error)
{
switch (auth->mode)
{
case GSK_HTTP_AUTH_MODE_UNKNOWN:
g_set_error (error,
GSK_G_ERROR_DOMAIN,
GSK_ERROR_UNKNOWN,
"cannot response to unknown authentication scheme %s",
auth->auth_scheme_name);
return NULL;
case GSK_HTTP_AUTH_MODE_BASIC:
return gsk_http_authorization_new_basic (user, password);
case GSK_HTTP_AUTH_MODE_DIGEST:
return gsk_http_authorization_new_digest (auth->realm,
auth->info.digest.domain,
auth->info.digest.nonce,
auth->info.digest.opaque,
auth->info.digest.algorithm,
user,
password,
NULL,
NULL);
default:
g_return_val_if_reached (NULL);
}
}
void
gsk_http_authorization_set_nonce (GskHttpAuthorization *auth,
const char *nonce)
{
char *copy;
g_return_if_fail (auth->mode == GSK_HTTP_AUTH_MODE_DIGEST);
g_return_if_fail (auth->info.digest.password != NULL);
copy = g_strdup (nonce);
g_free (auth->info.digest.nonce);
auth->info.digest.nonce = copy;
g_free (auth->info.digest.response_digest);
auth->info.digest.response_digest = NULL;
}
GskHttpAuthorization *
gsk_http_authorization_copy (const GskHttpAuthorization *auth)
{
switch (auth->mode)
{
case GSK_HTTP_AUTH_MODE_UNKNOWN:
return gsk_http_authorization_new_unknown (auth->auth_scheme_name,
auth->info.unknown.response);
case GSK_HTTP_AUTH_MODE_BASIC:
return gsk_http_authorization_new_basic (auth->info.basic.user,
auth->info.basic.password);
case GSK_HTTP_AUTH_MODE_DIGEST:
return gsk_http_authorization_new_digest (auth->info.digest.realm,
auth->info.digest.domain,
auth->info.digest.nonce,
auth->info.digest.opaque,
auth->info.digest.algorithm,
auth->info.digest.user,
auth->info.digest.password,
auth->info.digest.response_digest,
auth->info.digest.entity_digest);
default:
g_return_val_if_reached (NULL);
}
}
GskHttpAuthorization *
gsk_http_authorization_ref (GskHttpAuthorization *auth)
{
g_return_val_if_fail (auth->ref_count != 0, auth);
++(auth->ref_count);
return auth;
}
void
gsk_http_authorization_unref (GskHttpAuthorization *auth)
{
g_return_if_fail (auth->ref_count != 0);
if (--(auth->ref_count) == 0)
{
if (auth->mode == GSK_HTTP_AUTH_MODE_DIGEST)
{
g_free (auth->info.digest.nonce);
g_free (auth->info.digest.response_digest);
g_free (auth->info.digest.entity_digest);
}
g_free (auth);
}
}
/* GskHttpResponseCacheDirective */
/**
*
* gsk_http_response_cache_directive_new:
* Create a new cache respnse directive.
* @Returns: the new cache response directive.
*/
GskHttpResponseCacheDirective *
gsk_http_response_cache_directive_new (void)
{
GskHttpResponseCacheDirective *directive =
g_new0 (GskHttpResponseCacheDirective, 1);
directive->is_public = 1;
return directive;
}
/**
* gsk_http_response_cache_directive_set_private_name:
* @directive: the cache directive to affect.
* @name: new qualifier for how the private data is allowed to be used.
* @name_len: length of name string
*
* Intended for more detailed specification of private cache data.
*/
void
gsk_http_response_cache_directive_set_private_name (
GskHttpResponseCacheDirective *cd,
const char *name,
gsize name_len)
{
char *n = g_strndup (name, name_len);
g_free (cd->private_name);
cd->private_name = n;
}
/**
* gsk_http_response_cache_directive_set_no_cache_name:
* @directive: the cache directive to affect.
* @name: new qualifier for how the no-cache data is allowed to be used.
*
* Intended for more detailed specification of no-cache data,
* which is never supposed to be cached.
*
* [RFC 2616, 14.9.1]
* If the no-cache directive does specify one or more field-names,
* then a cache MAY use the response to satisfy a subsequent request,
* subject to any other restrictions on caching. However, the
* specified field-name(s) MUST NOT be sent in the response to a
* subsequent request without successful revalidation with the origin
* server. This allows an origin server to prevent the re-use of
* certain header fields in a response, while still allowing caching
* of the rest of the response.
*
* good luck.
*/
void
gsk_http_response_cache_directive_set_no_cache_name (
GskHttpResponseCacheDirective *cd,
const char *name,
gsize name_len)
{
char *n = g_strndup (name, name_len);
g_free (cd->no_cache_name);
cd->no_cache_name = n;
}
/**
* gsk_http_response_cache_directive_free:
* @directive: cache-directive to de-allocate.
*
* Deallocate a GskHttpResponseCacheDirective.
*/
void
gsk_http_response_cache_directive_free (GskHttpResponseCacheDirective *directive)
{
g_free (directive->no_cache_name);
g_free (directive->private_name);
g_free (directive);
}
static GskHttpResponseCacheDirective *
gsk_http_response_cache_directive_copy (GskHttpResponseCacheDirective *directive)
{
GskHttpResponseCacheDirective *rv;
rv = g_memdup (directive, sizeof (*directive));
rv->no_cache_name = g_strdup (rv->no_cache_name);
rv->private_name = g_strdup (rv->private_name);
return rv;
}
/* GskHttpRequestCacheDirective */
/**
*
* gsk_http_request_cache_directive_new:
* Create a new cache respnse directive.
* returns: the new cache request directive.
*/
GskHttpRequestCacheDirective *
gsk_http_request_cache_directive_new (void)
{
return g_new0 (GskHttpRequestCacheDirective, 1);
}
/**
* gsk_http_request_cache_directive_free:
* @directive: cache-directive to de-allocate.
*
* Deallocate a GskHttpRequestCacheDirective.
*/
void
gsk_http_request_cache_directive_free (GskHttpRequestCacheDirective *directive)
{
g_free (directive);
}
static GskHttpRequestCacheDirective *
gsk_http_request_cache_directive_copy (GskHttpRequestCacheDirective *directive)
{
return g_memdup (directive, sizeof (*directive));
}
/* GskHttpCharSet */
/**
* gsk_http_char_set_new:
* @charset_name: name of the character set.
* @quality: quality from 0 to 1, or -1 if no quality flag was given.
*
* Allocate a single GskHttpCharSet preference.
* You may wish to build a list of these.
*
* returns: the new character-set.
*/
GskHttpCharSet *
gsk_http_char_set_new (const char *charset_name,
gfloat quality)
{
GskHttpCharSet *char_set = g_new (GskHttpCharSet, 1);
char_set->charset_name = g_strdup (charset_name);
char_set->quality = quality;
char_set->next = NULL;
return char_set;
}
static GskHttpCharSet *
gsk_http_char_set_copy (GskHttpCharSet *char_set)
{
return gsk_http_char_set_new (char_set->charset_name,
char_set->quality);
}
/**
* gsk_http_char_set_free:
* @char_set: character set to free.
*
* Deallocate a GskHttpCharSet.
*/
void
gsk_http_char_set_free(GskHttpCharSet *char_set)
{
g_free (char_set->charset_name);
g_free (char_set);
}
/* GskHttpCookie */
/**
* gsk_http_cookie_new:
* @key: token used to identify the cookie.
* @value: information associated with the cookie.
* @path: applicable area in server directory structure for this cookie.
* @domain: domain names this cookie applies to.
* @expire_date: when this cookie should be discarded.
* @comment: miscellaneous information about this cookie.
* @max_age: maximum number of seconds that this cookie should be retained.
*
* Allocate a cookie. Once allocated, it cannot be changed.
*
* Cookies are a mechanism for tracking users.
* The remote host sends a Set-Cookie directive which,
* should be client choose to accept it, may be sent with
* subsequent relevant requests.
*
* NOTE: GSK does not automatically handle cookies.
* That is up to the application.
*
* TODO: GSK should have a cookie database mechanism.
*
* See RFC 2964 and 2965.
*
* returns: the newly allocated cookie.
*/
GskHttpCookie *
gsk_http_cookie_new (const char *key,
const char *value,
const char *path,
const char *domain,
const char *expire_date,
const char *comment,
int max_age)
{
#define ACTUAL_LENGTH(str) ((str) ? (strlen (str) + 1) : 0)
guint alloc_length = sizeof (GskHttpCookie)
+ ACTUAL_LENGTH (key)
+ ACTUAL_LENGTH (value)
+ ACTUAL_LENGTH (path)
+ ACTUAL_LENGTH (domain)
+ ACTUAL_LENGTH (expire_date)
+ ACTUAL_LENGTH (comment);
guint at = sizeof (GskHttpCookie);
char *raw = g_new (char, alloc_length);
GskHttpCookie *rv = (GskHttpCookie *) raw;
rv->max_age = max_age;
#define INIT_RV_STRING(name) \
G_STMT_START{ \
if (name == NULL) \
rv->name = NULL; \
else \
{ \
rv->name = strcpy (raw + at, name); \
at += strlen (name) + 1; \
} \
}G_STMT_END
INIT_RV_STRING (key);
INIT_RV_STRING (value);
INIT_RV_STRING (path);
INIT_RV_STRING (domain);
INIT_RV_STRING (expire_date);
INIT_RV_STRING (comment);
#if 0 /* uh, we "reuse"/"abuse" these below in MediaTypeSet */
#undef ACTUAL_LENGTH
#undef INIT_RV_STRING
#endif
g_assert (at == alloc_length);
rv->version = 0;
rv->secure = FALSE;
return rv;
}
/**
* gsk_http_cookie_copy:
* @orig: the cookie to copy.
*
* Copy a cookie.
*
* returns: the new cookie.
*/
GskHttpCookie *
gsk_http_cookie_copy (const GskHttpCookie *orig)
{
GskHttpCookie *rv = gsk_http_cookie_new (orig->key,
orig->value,
orig->path,
orig->domain,
orig->expire_date,
orig->comment,
orig->max_age);
rv->secure = orig->secure;
rv->version = orig->version;
return rv;
}
/**
* gsk_http_cookie_free:
* @orig: the cookie to deallocate.
*
* Free the memory associated with the cookie.
*/
void
gsk_http_cookie_free (GskHttpCookie *orig)
{
g_return_if_fail (orig != NULL);
g_free (orig);
}
/* GskHttpContentEncodingSet */
/**
* gsk_http_content_encoding_set_new:
* @encoding: the encoding to list a preference for.
* @quality: relative preference for this encoding.
* A value of -1 means that the quality was omitted,
* which means it should be treated as 1.
*
* Allocate a new node in a GskHttpContentEncodingSet list.
*
* returns: the new encoding node.
*/
GskHttpContentEncodingSet *
gsk_http_content_encoding_set_new (GskHttpContentEncoding encoding,
gfloat quality)
{
GskHttpContentEncodingSet *set = g_new (GskHttpContentEncodingSet, 1);
set->encoding = encoding;
set->quality = quality;
set->next = NULL;
return set;
}
static GskHttpContentEncodingSet *
gsk_http_content_encoding_set_copy (GskHttpContentEncodingSet *set)
{
return gsk_http_content_encoding_set_new (set->encoding, set->quality);
}
/**
* gsk_http_content_encoding_set_free:
* @encoding_set: the encoding set to deallocate.
*
* Deallocate the encoding.
*/
void
gsk_http_content_encoding_set_free(GskHttpContentEncodingSet *encoding_set)
{
g_return_if_fail (encoding_set != NULL);
g_free (encoding_set);
}
/* GskHttpTransferEncodingSet */
/**
* gsk_http_transfer_encoding_set_new:
* @encoding: the encoding to list a preference for.
* @quality: relative preference for this encoding.
* A value of -1 means that the quality was omitted,
* which means it should be treated as 1.
*
* Allocate a new node in a GskHttpTransferEncodingSet list.
*
* returns: the new encoding node.
*/
GskHttpTransferEncodingSet *
gsk_http_transfer_encoding_set_new (GskHttpTransferEncoding encoding,
gfloat quality)
{
GskHttpTransferEncodingSet *set = g_new (GskHttpTransferEncodingSet, 1);
set->encoding = encoding;
set->quality = quality;
set->next = NULL;
return set;
}
static GskHttpTransferEncodingSet *
gsk_http_transfer_encoding_set_copy (GskHttpTransferEncodingSet *set)
{
return gsk_http_transfer_encoding_set_new (set->encoding, set->quality);
}
/**
* gsk_http_transfer_encoding_set_free:
* @encoding_set: the encoding set to deallocate.
*
* Deallocate the encoding (a single node in the list).
*/
void
gsk_http_transfer_encoding_set_free(GskHttpTransferEncodingSet *encoding_set)
{
g_return_if_fail (encoding_set != NULL);
g_free (encoding_set);
}
/* GskHttpLanguageSet */
/**
* gsk_http_language_set_new:
* @language: the human language code to list a preference for.
* @quality: relative preference for this encoding.
* A value of -1 means that the quality was omitted,
* which means it should be treated as 1.
*
* Allocate a new node in a GskHttpLanguageSet list.
* Though any ASCII string is basically allowed,
* a two letter language code (en, de, pl, it, etc)
* is the usual start of the language name;
* a two letter country code (US, UK, DE, etc)
* is the second more optional part, like "en-US".
*
* returns: the new language node.
*/
GskHttpLanguageSet *
gsk_http_language_set_new (const char *language,
gfloat quality)
{
/* ugh, these macros come from gsk_http_cookie_new above */
guint alloc_length = sizeof (GskHttpLanguageSet)
+ strlen (language) + 1;
GskHttpLanguageSet *rv = g_malloc (alloc_length);
char *mem_at = (char*)(rv + 1);
rv->quality = quality;
rv->next = NULL;
rv->language = mem_at;
strcpy (rv->language, language);
return rv;
}
static GskHttpLanguageSet *
gsk_http_language_set_copy(GskHttpLanguageSet *set)
{
return gsk_http_language_set_new (set->language,
set->quality);
}
/**
* gsk_http_language_set_free:
* @set: the language set node to deallocate.
*
* Deallocate the node in the language-set.
*/
void
gsk_http_language_set_free(GskHttpLanguageSet *set)
{
g_return_if_fail (set != NULL);
g_free (set);
}
/* GskHttpMediaType */
/**
* gsk_http_media_type_set_new:
* @type: major type of the media to allow, like "text" or "image".
* An asterisk can be used to allow any major type.
* @subtype: format of the media to allow, like "html", "plain" or "jpeg".
* An asterisk can be used to allow any format.
* @quality: relative preference for this encoding. -1 means "not specified", which has a default of 1.
*
* Allocate a new node in a GskHttpMediaTypeSet list.
*
* returns: the newly allocated node.
*/
GskHttpMediaTypeSet *
gsk_http_media_type_set_new (const char *type,
const char *subtype,
gfloat quality)
{
/* ugh, these macros come from gsk_http_cookie_new above */
guint alloc_length = sizeof (GskHttpMediaTypeSet)
+ ACTUAL_LENGTH (type)
+ ACTUAL_LENGTH (subtype);
guint at = sizeof (GskHttpMediaTypeSet);
char *raw = g_new (char, alloc_length);
GskHttpMediaTypeSet *rv = (GskHttpMediaTypeSet *) raw;
rv->quality = quality;
rv->next = NULL;
INIT_RV_STRING (type);
INIT_RV_STRING (subtype);
g_assert (at == alloc_length);
return rv;
}
static GskHttpMediaTypeSet *
gsk_http_media_type_set_copy (GskHttpMediaTypeSet *set)
{
return gsk_http_media_type_set_new (set->type, set->subtype,
set->quality);
}
/**
* gsk_http_media_type_set_free:
* @set: the media type set to deallocate.
*
* Free the memory associated with the media-type-set node.
*/
void
gsk_http_media_type_set_free (GskHttpMediaTypeSet *set)
{
g_return_if_fail (set != NULL);
g_free (set);
}
/* GskHttpRangeSet */
/**
* gsk_http_range_set_new:
* @range_type: allocate a new node in a allowable range units list.
*
* Allocate a node telling what units are available for
* specifying partial content.
*
* returns: the newly allocated node.
*/
GskHttpRangeSet *
gsk_http_range_set_new (GskHttpRange range_type)
{
GskHttpRangeSet *rv = g_new (GskHttpRangeSet, 1);
rv->range_type = range_type;
rv->next = NULL;
return rv;
}
static GskHttpRangeSet *
gsk_http_range_set_copy (GskHttpRangeSet *orig)
{
return gsk_http_range_set_new (orig->range_type);
}
/**
* gsk_http_range_set_free:
* @set: range-unit node to deallocate.
*
* Free the memory associated with the node in the range-set.
*/
void
gsk_http_range_set_free(GskHttpRangeSet *set)
{
g_return_if_fail (set != NULL);
g_free (set);
}
#define _DEFINE_BOXED_GET_TYPE(Class, class, _copy, _free) \
GType \
class ## _get_type (void) \
{ \
static GType type = 0; \
if (type == 0) \
type = g_boxed_type_register_static (#Class, \
(GBoxedCopyFunc) class ## _copy, \
(GBoxedFreeFunc) class ## _free); \
return type; \
}
#define DEFINE_BOXED_GET_TYPE(Class, class) _DEFINE_BOXED_GET_TYPE(Class,class,_copy,_free)
#define DEFINE_REFCOUNTED_BOXED_GET_TYPE(Class, class) _DEFINE_BOXED_GET_TYPE(Class,class,_ref,_unref)
DEFINE_REFCOUNTED_BOXED_GET_TYPE (GskHttpAuthenticate, gsk_http_authenticate)
DEFINE_REFCOUNTED_BOXED_GET_TYPE (GskHttpAuthorization, gsk_http_authorization)
DEFINE_BOXED_GET_TYPE (GskHttpResponseCacheDirective,
gsk_http_response_cache_directive)
DEFINE_BOXED_GET_TYPE (GskHttpRequestCacheDirective,
gsk_http_request_cache_directive)
DEFINE_BOXED_GET_TYPE (GskHttpCharSet, gsk_http_char_set)
DEFINE_BOXED_GET_TYPE (GskHttpCookie, gsk_http_cookie)
DEFINE_BOXED_GET_TYPE (GskHttpLanguageSet, gsk_http_language_set)
DEFINE_BOXED_GET_TYPE (GskHttpContentEncodingSet, gsk_http_content_encoding_set)
DEFINE_BOXED_GET_TYPE (GskHttpTransferEncodingSet, gsk_http_transfer_encoding_set)
DEFINE_BOXED_GET_TYPE (GskHttpMediaTypeSet, gsk_http_media_type_set)
DEFINE_BOXED_GET_TYPE (GskHttpRangeSet, gsk_http_range_set)
#undef DEFINE_BOXED_GET_TYPE
/* GskHttpHeader public methods */
syntax highlighted by Code2HTML, v. 0.9.1