/** * 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); }
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); }
/** * 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; }
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; }
/* 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; }