Ejemplo n.º 1
0
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);
}
/**
 * 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);
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
0
static void
server_callback (SoupServer *server, SoupMessage *msg,
		 const char *path, GHashTable *query,
		 SoupClientContext *context, gpointer data)
{
	const char *accept_encoding, *options;
	GSList *codings;
	char *file = NULL, *contents;
	gsize length;

	options = soup_message_headers_get_one (msg->request_headers,
						"X-Test-Options");
	if (!options)
		options = "";

	accept_encoding = soup_message_headers_get_list (msg->request_headers,
							 "Accept-Encoding");
	if (accept_encoding && !soup_header_contains (options, "force-encode"))
		codings = soup_header_parse_quality_list (accept_encoding, NULL);
	else
		codings = NULL;

	if (codings) {
		gboolean claim_deflate, claim_gzip;
		const char *file_path = NULL, *encoding = NULL;

		claim_deflate = g_slist_find_custom (codings, "deflate", (GCompareFunc)g_ascii_strcasecmp) != NULL;
		claim_gzip = g_slist_find_custom (codings, "gzip", (GCompareFunc)g_ascii_strcasecmp) != NULL;

		if (claim_gzip && (!claim_deflate ||
				   (!soup_header_contains (options, "prefer-deflate-zlib") &&
				    !soup_header_contains (options, "prefer-deflate-raw")))) {
			file_path = SRCDIR "/resources%s.gz";
			encoding = "gzip";
		} else if (claim_deflate) {
			if (soup_header_contains (options, "prefer-deflate-raw")) {
				file_path = SRCDIR "/resources%s.raw";
				encoding = "deflate";
			} else {
				file_path = SRCDIR "/resources%s.zlib";
				encoding = "deflate";
			}
		}
		if (file_path && encoding) {
			file = g_strdup_printf (file_path, path);
			if (g_file_test (file, G_FILE_TEST_EXISTS)) {
				soup_message_headers_append (msg->response_headers,
							     "Content-Encoding",
							     encoding);
			} else {
				g_free (file);
				file = NULL;
			}
		}
	}

	soup_header_free_list (codings);

	if (!file)
		file = g_strdup_printf (SRCDIR "/resources%s", path);
	if (!g_file_get_contents (file, &contents, &length, NULL)) {
		/* If path.gz exists but can't be read, we'll send back
		 * the error with "Content-Encoding: gzip" but there's
		 * no body, so, eh.
		 */
		g_free (file);
		soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
		return;
	}
	g_free (file);

	if (soup_header_contains (options, "force-encode")) {
		const gchar *encoding = "gzip";

		if (soup_header_contains (options, "prefer-deflate-zlib") ||
		    soup_header_contains (options, "prefer-deflate-raw"))
			encoding = "deflate";

		soup_message_headers_replace (msg->response_headers,
					      "Content-Encoding",
					      encoding);
	}

	/* Content-Type matches the "real" format, not the sent format */
	if (g_str_has_suffix (path, ".gz")) {
		soup_message_headers_append (msg->response_headers,
					     "Content-Type",
					     "application/gzip");
	} else {
		soup_message_headers_append (msg->response_headers,
					     "Content-Type",
					     "text/plain");
	}

	soup_message_set_status (msg, SOUP_STATUS_OK);
	soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CHUNKED);

	if (!soup_header_contains (options, "empty")) {
		soup_message_body_append (msg->response_body,
					  SOUP_MEMORY_TAKE, contents, length);
	} else
		g_free (contents);

	if (soup_header_contains (options, "trailing-junk")) {
		soup_message_body_append (msg->response_body, SOUP_MEMORY_COPY,
					  options, strlen (options));
	}
	soup_message_body_complete (msg->response_body);
}
Ejemplo 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;
}
Ejemplo 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;
}