/**
 * soup_message_headers_clean_connection_headers:
 * @hdrs: a #SoupMessageHeaders
 *
 * Removes all the headers listed in the Connection header.
 *
 * Since: 2.36
 */
void
soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs)
{
	/* RFC 2616 14.10 */
	const char *connection;
	GSList *tokens, *t;

	connection = soup_message_headers_get_list (hdrs, "Connection");
	if (!connection)
		return;

	tokens = soup_header_parse_list (connection);
	for (t = tokens; t; t = t->next)
		soup_message_headers_remove (hdrs, t->data);
	soup_header_free_list (tokens);
}
Esempio n. 2
0
SoupAuthDigestQop
soup_auth_digest_parse_qop (const char *qop)
{
	GSList *qop_values, *iter;
	SoupAuthDigestQop out = 0;

	g_return_val_if_fail (qop != NULL, 0);

	qop_values = soup_header_parse_list (qop);
	for (iter = qop_values; iter; iter = iter->next) {
		if (!g_ascii_strcasecmp (iter->data, "auth"))
			out |= SOUP_AUTH_DIGEST_QOP_AUTH;
		else if (!g_ascii_strcasecmp (iter->data, "auth-int"))
			out |= SOUP_AUTH_DIGEST_QOP_AUTH_INT;
	}
	soup_header_free_list (qop_values);

	return out;
}
static char *
soup_auth_manager_extract_challenge (const char *challenges, const char *scheme)
{
	GSList *items, *i, *next;
	int schemelen = strlen (scheme);
	char *item;
	GString *challenge;

	items = soup_header_parse_list (challenges);

	/* First item will start with the scheme name, followed by
	 * either nothing, or else a space and then the first
	 * auth-param.
	 */
	for (i = items; i; i = next_challenge_start (i->next)) {
		item = i->data;
		if (!g_ascii_strncasecmp (item, scheme, schemelen) &&
		    (!item[schemelen] || g_ascii_isspace (item[schemelen])))
			break;
	}
	if (!i) {
		soup_header_free_list (items);
		return NULL;
	}

	next = next_challenge_start (i->next);
	challenge = g_string_new (item);
	for (i = i->next; i != next; i = i->next) {
		item = i->data;
		g_string_append (challenge, ", ");
		g_string_append (challenge, item);
	}

	soup_header_free_list (items);
	return g_string_free (challenge, FALSE);
}
Esempio n. 4
0
/**
 * soup_header_parse_quality_list:
 * @header: a header value
 * @unacceptable: (out) (allow-none) (transfer full) (element-type utf8): on
 * return, will contain a list of unacceptable values
 *
 * Parses a header whose content is a list of items with optional
 * "qvalue"s (eg, Accept, Accept-Charset, Accept-Encoding,
 * Accept-Language, TE).
 *
 * If @unacceptable is not %NULL, then on return, it will contain the
 * items with qvalue 0. Either way, those items will be removed from
 * the main list.
 *
 * Return value: (transfer full) (element-type utf8): a #GSList of
 * acceptable values (as allocated strings), highest-qvalue first.
 **/
GSList *
soup_header_parse_quality_list (const char *header, GSList **unacceptable)
{
	GSList *unsorted;
	QualityItem *array;
	GSList *sorted, *iter;
	char *item, *semi;
	const char *param, *equal, *value;
	double qval;
	int n;

	g_return_val_if_fail (header != NULL, NULL);

	if (unacceptable)
		*unacceptable = NULL;

	unsorted = soup_header_parse_list (header);
	array = g_new0 (QualityItem, g_slist_length (unsorted));
	for (iter = unsorted, n = 0; iter; iter = iter->next) {
		item = iter->data;
		qval = 1.0;
		for (semi = strchr (item, ';'); semi; semi = strchr (semi + 1, ';')) {
			param = skip_lws (semi + 1);
			if (*param != 'q')
				continue;
			equal = skip_lws (param + 1);
			if (!equal || *equal != '=')
				continue;
			value = skip_lws (equal + 1);
			if (!value)
				continue;

			if (value[0] != '0' && value[0] != '1')
				continue;
			qval = (double)(value[0] - '0');
			if (value[0] == '0' && value[1] == '.') {
				if (g_ascii_isdigit (value[2])) {
					qval += (double)(value[2] - '0') / 10;
					if (g_ascii_isdigit (value[3])) {
						qval += (double)(value[3] - '0') / 100;
						if (g_ascii_isdigit (value[4]))
							qval += (double)(value[4] - '0') / 1000;
					}
				}
			}

			*semi = '\0';
			break;
		}

		if (qval == 0.0) {
			if (unacceptable) {
				*unacceptable = g_slist_prepend (*unacceptable,
								 item);
			}
		} else {
			array[n].item = item;
			array[n].qval = qval;
			n++;
		}
	}
	g_slist_free (unsorted);

	qsort (array, n, sizeof (QualityItem), sort_by_qval);
	sorted = NULL;
	while (n--)
		sorted = g_slist_prepend (sorted, array[n].item);
	g_free (array);

	return sorted;
}
Esempio n. 5
0
static void
resource_available (GSSDPResourceBrowser *resource_browser,
                    SoupMessageHeaders   *headers)
{
        GSSDPResourceBrowserPrivate *priv;
        const char *usn;
        const char *header;
        Resource *resource;
        gboolean was_cached;
        guint timeout;
        GList *locations;
        gboolean destroyLocations;
        GList *it1, *it2;
        char *canonical_usn;

        priv = gssdp_resource_browser_get_instance_private (resource_browser);
        usn = soup_message_headers_get_one (headers, "USN");
        if (!usn)
                return; /* No USN specified */

        /* Build list of locations */
        locations = NULL;
        destroyLocations = TRUE;

        header = soup_message_headers_get_one (headers, "Location");
        if (header)
                locations = g_list_append (locations, g_strdup (header));

        header = soup_message_headers_get_one (headers, "AL");
        if (header) {
                /* Parse AL header. The format is:
                 * <uri1><uri2>... */
                const char *start, *end;
                char *uri;

                start = header;
                while ((start = strchr (start, '<'))) {
                        start += 1;
                        if (!start || !*start)
                                break;

                        end = strchr (start, '>');
                        if (!end || !*end)
                                break;

                        uri = g_strndup (start, end - start);
                        locations = g_list_append (locations, uri);

                        start = end;
                }
        }

        if (!locations)
                return; /* No location specified */

        if (priv->version > 0) {
                char *version;

                version = g_strrstr (usn, ":");
                canonical_usn = g_strndup (usn, version - usn);
        } else {
                canonical_usn = g_strdup (usn);
        }

        /* Get from cache, if possible */
        resource = g_hash_table_lookup (priv->resources,
                                        canonical_usn);
        /* Put usn into fresh resources, so this resource will not be
         * removed on cache refreshing. */
        if (priv->fresh_resources != NULL) {
                g_hash_table_add (priv->fresh_resources,
                                  g_strdup (canonical_usn));
        }

        /* If location does not match, expect that we missed bye bye packet */
        if (resource) {
                for (it1 = locations, it2 = resource->locations;
                     it1 && it2;
                     it1 = it1->next, it2 = it2->next) {
                        if (strcmp ((const char *) it1->data,
                                    (const char *) it2->data) != 0) {
                               resource_unavailable (resource_browser, headers);
                               /* Will be destroyed by resource_unavailable */
                               resource = NULL;

                               break;
                        }
                }
        }

        if (resource) {
                /* Remove old timeout */
                g_source_destroy (resource->timeout_src);

                was_cached = TRUE;
        } else {
                /* Create new Resource data structure */
                resource = g_slice_new (Resource);

                resource->resource_browser = resource_browser;
                resource->usn              = g_strdup (usn);
                resource->locations        = locations;
                destroyLocations = FALSE; /* Ownership passed to resource */
                
                g_hash_table_insert (priv->resources,
                                     canonical_usn,
                                     resource);
                
                was_cached = FALSE;

                /* hash-table takes ownership of this */
                canonical_usn = NULL;
        }

        g_free (canonical_usn);

        /* Calculate new timeout */
        header = soup_message_headers_get_one (headers, "Cache-Control");
        if (header) {
                GSList *list;
                int res;

                res = 0;

                for (list = soup_header_parse_list (header);
                     list;
                     list = list->next) {
                        res = sscanf (list->data,
                                      "max-age = %d",
                                      &timeout);
                        if (res == 1)
                                break;
                }

                if (res != 1) {
                        g_warning ("Invalid 'Cache-Control' header. Assuming "
                                   "default max-age of %d.\n"
                                   "Header was:\n%s",
                                   SSDP_DEFAULT_MAX_AGE,
                                   header);

                        timeout = SSDP_DEFAULT_MAX_AGE;
                }

                soup_header_free_list (list);
        } else {
                const char *expires;

                expires = soup_message_headers_get_one (headers, "Expires");
                if (expires) {
                        SoupDate *soup_exp_time;
                        time_t exp_time, cur_time;

                        soup_exp_time = soup_date_new_from_string (expires);
                        exp_time = soup_date_to_time_t (soup_exp_time);
                        soup_date_free (soup_exp_time);

                        cur_time = time (NULL);

                        if (exp_time > cur_time)
                                timeout = exp_time - cur_time;
                        else {
                                g_warning ("Invalid 'Expires' header. Assuming "
                                           "default max-age of %d.\n"
                                           "Header was:\n%s",
                                           SSDP_DEFAULT_MAX_AGE,
                                           expires);

                                timeout = SSDP_DEFAULT_MAX_AGE;
                        }
                } else {
                        g_warning ("No 'Cache-Control' nor any 'Expires' "
                                   "header was specified. Assuming default "
                                   "max-age of %d.", SSDP_DEFAULT_MAX_AGE);

                        timeout = SSDP_DEFAULT_MAX_AGE;
                }
        }

        resource->timeout_src = g_timeout_source_new_seconds (timeout);
        g_source_set_callback (resource->timeout_src,
                               resource_expire,
                               resource, NULL);

        g_source_attach (resource->timeout_src,
                         g_main_context_get_thread_default ());

        g_source_unref (resource->timeout_src);

        /* Only continue with signal emission if this resource was not
         * cached already */
        if (!was_cached) {
                /* Emit signal */
                g_signal_emit (resource_browser,
                               signals[RESOURCE_AVAILABLE],
                               0,
                               usn,
                               locations);
        }
        /* Cleanup */
        if (destroyLocations)
                g_list_free_full (locations, g_free);
}
/**
 * soup_message_headers_get_ranges:
 * @hdrs: a #SoupMessageHeaders
 * @total_length: the total_length of the response body
 * @ranges: (out): return location for an array of #SoupRange
 * @length: the length of the returned array
 *
 * Parses @hdrs's Range header and returns an array of the requested
 * byte ranges. The returned array must be freed with
 * soup_message_headers_free_ranges().
 *
 * If @total_length is non-0, its value will be used to adjust the
 * returned ranges to have explicit start and end values, and the
 * returned ranges will be sorted and non-overlapping. If
 * @total_length is 0, then some ranges may have an end value of -1,
 * as described under #SoupRange, and some of the ranges may be
 * redundant.
 *
 * Return value: %TRUE if @hdrs contained a "Range" header containing
 * byte ranges which could be parsed, %FALSE otherwise (in which case
 * @range and @length will not be set).
 *
 * Since: 2.26
 **/
gboolean
soup_message_headers_get_ranges (SoupMessageHeaders  *hdrs,
				 goffset              total_length,
				 SoupRange          **ranges,
				 int                 *length)
{
	const char *range = soup_message_headers_get_one (hdrs, "Range");
	GSList *range_list, *r;
	GArray *array;
	char *spec, *end;
	int i;

	if (!range || strncmp (range, "bytes", 5) != 0)
		return FALSE;

	range += 5;
	while (g_ascii_isspace (*range))
		range++;
	if (*range++ != '=')
		return FALSE;
	while (g_ascii_isspace (*range))
		range++;

	range_list = soup_header_parse_list (range);
	if (!range_list)
		return FALSE;

	array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
	for (r = range_list; r; r = r->next) {
		SoupRange cur;

		spec = r->data;
		if (*spec == '-') {
			cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
			cur.end = total_length - 1;
		} else {
			cur.start = g_ascii_strtoull (spec, &end, 10);
			if (*end == '-')
				end++;
			if (*end)
				cur.end = g_ascii_strtoull (end, &end, 10);
			else
				cur.end = total_length - 1;
		}
		if (*end) {
			g_array_free (array, TRUE);
			soup_header_free_list (range_list);
			return FALSE;
		}

		g_array_append_val (array, cur);
	}

	soup_header_free_list (range_list);

	if (total_length) {
		g_array_sort (array, sort_ranges);
		for (i = 1; i < array->len; i++) {
			SoupRange *cur = &((SoupRange *)array->data)[i];
			SoupRange *prev = &((SoupRange *)array->data)[i - 1];

			if (cur->start <= prev->end) {
				prev->end = MAX (prev->end, cur->end);
				g_array_remove_index (array, i);
			}
		}
	}

	*ranges = (SoupRange *)array->data;
	*length = array->len;

	g_array_free (array, FALSE);
	return TRUE;
}
Esempio n. 7
0
/* like soup_message_headers_get_ranges(), except it returns:
 *   SOUP_STATUS_OK if there is no Range or it should be ignored.
 *   SOUP_STATUS_PARTIAL_CONTENT if there is at least one satisfiable range.
 *   SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE if @check_satisfiable
 *     is %TRUE and the request is not satisfiable given @total_length.
 */
guint
soup_message_headers_get_ranges_internal (SoupMessageHeaders  *hdrs,
					  goffset              total_length,
					  gboolean             check_satisfiable,
					  SoupRange          **ranges,
					  int                 *length)
{
	const char *range = soup_message_headers_get_one (hdrs, "Range");
	GSList *range_list, *r;
	GArray *array;
	char *spec, *end;
	int i;
	guint status = SOUP_STATUS_OK;

	if (!range || strncmp (range, "bytes", 5) != 0)
		return status;

	range += 5;
	while (g_ascii_isspace (*range))
		range++;
	if (*range++ != '=')
		return status;
	while (g_ascii_isspace (*range))
		range++;

	range_list = soup_header_parse_list (range);
	if (!range_list)
		return status;

	array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
	for (r = range_list; r; r = r->next) {
		SoupRange cur;

		spec = r->data;
		if (*spec == '-') {
			cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
			cur.end = total_length - 1;
		} else {
			cur.start = g_ascii_strtoull (spec, &end, 10);
			if (*end == '-')
				end++;
			if (*end) {
				cur.end = g_ascii_strtoull (end, &end, 10);
				if (cur.end < cur.start) {
					status = SOUP_STATUS_OK;
					break;
				}
			} else
				cur.end = total_length - 1;
		}
		if (*end) {
			status = SOUP_STATUS_OK;
			break;
		} else if (check_satisfiable && cur.start >= total_length) {
			if (status == SOUP_STATUS_OK)
				status = SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE;
			continue;
		}

		g_array_append_val (array, cur);
		status = SOUP_STATUS_PARTIAL_CONTENT;
	}
	soup_header_free_list (range_list);

	if (status != SOUP_STATUS_PARTIAL_CONTENT) {
		g_array_free (array, TRUE);
		return status;
	}

	if (total_length) {
		g_array_sort (array, sort_ranges);
		for (i = 1; i < array->len; i++) {
			SoupRange *cur = &((SoupRange *)array->data)[i];
			SoupRange *prev = &((SoupRange *)array->data)[i - 1];

			if (cur->start <= prev->end) {
				prev->end = MAX (prev->end, cur->end);
				g_array_remove_index (array, i);
			}
		}
	}

	*ranges = (SoupRange *)array->data;
	*length = array->len;

	g_array_free (array, FALSE);
	return SOUP_STATUS_PARTIAL_CONTENT;
}