Coverage Report

Created: 2026-08-31 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libsoup/libsoup/soup-misc.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
/*
3
 * soup-misc.c: Miscellaneous functions
4
5
 * Copyright (C) 2000-2003, Ximian, Inc.
6
 */
7
8
#ifdef HAVE_CONFIG_H
9
#include <config.h>
10
#endif
11
12
#include <string.h>
13
14
#include "soup-misc.h"
15
16
/**
17
 * soup_str_case_hash:
18
 * @key: ASCII string to hash
19
 *
20
 * Hashes @key in a case-insensitive manner.
21
 *
22
 * Returns: the hash code.
23
 **/
24
guint
25
soup_str_case_hash (gconstpointer key)
26
0
{
27
0
  const char *p = key;
28
0
  guint h = g_ascii_toupper(*p);
29
30
0
  if (h)
31
0
    for (p += 1; *p != '\0'; p++)
32
0
      h = (h << 5) - h + g_ascii_toupper(*p);
33
34
0
  return h;
35
0
}
36
37
/**
38
 * soup_str_case_equal:
39
 * @v1: an ASCII string
40
 * @v2: another ASCII string
41
 *
42
 * Compares @v1 and @v2 in a case-insensitive manner
43
 *
44
 * Returns: %TRUE if they are equal (modulo case)
45
 **/
46
gboolean
47
soup_str_case_equal (gconstpointer v1,
48
         gconstpointer v2)
49
0
{
50
0
  const char *string1 = v1;
51
0
  const char *string2 = v2;
52
53
0
  return g_ascii_strcasecmp (string1, string2) == 0;
54
0
}
55
56
GSource *
57
soup_add_completion_reffed (GMainContext   *async_context,
58
          GSourceFunc     function,
59
          gpointer        data,
60
          GDestroyNotify  dnotify)
61
0
{
62
0
  GSource *source = g_idle_source_new ();
63
64
0
  g_source_set_static_name (source, "SoupCompletion");
65
0
  g_source_set_priority (source, G_PRIORITY_DEFAULT);
66
0
  g_source_set_callback (source, function, data, dnotify);
67
0
  g_source_attach (source, async_context);
68
0
  return source;
69
0
}
70
71
/*
72
 * soup_add_completion: (skip)
73
 * @async_context: (nullable): the #GMainContext to dispatch the I/O
74
 * watch in, or %NULL for the default context
75
 * @function: the callback to invoke
76
 * @data: user data to pass to @function
77
 *
78
 * Adds @function to be executed from inside @async_context with the
79
 * default priority. Use this when you want to complete an action in
80
 * @async_context's main loop, as soon as possible.
81
 *
82
 */
83
void
84
soup_add_completion (GMainContext *async_context,
85
               GSourceFunc function, gpointer data)
86
0
{
87
0
  GSource *source;
88
89
0
  source = soup_add_completion_reffed (async_context, function, data, NULL);
90
0
  g_source_unref (source);
91
0
}
92
93
/**
94
 * soup_add_timeout: (skip)
95
 * @async_context: (nullable): the #GMainContext to dispatch the I/O
96
 *   watch in, or %NULL for the default context
97
 * @interval: the timeout interval, in milliseconds
98
 * @function: the callback to invoke at timeout time
99
 * @data: user data to pass to @function
100
 *
101
 * Adds a timeout as with [func@GLib.timeout_add], but using the given
102
 * @async_context.
103
 *
104
 * Returns: (transfer full): a #GSource, which can be removed from @async_context
105
 *   with [method@GLib.Source.destroy].
106
 **/
107
GSource *
108
soup_add_timeout (GMainContext *async_context,
109
      guint interval,
110
      GSourceFunc function, gpointer data)
111
0
{
112
0
  GSource *source = g_timeout_source_new (interval);
113
0
  g_source_set_static_name (source, "SoupTimeout");
114
0
  g_source_set_callback (source, function, data, NULL);
115
0
  g_source_attach (source, async_context);
116
0
  return source;
117
0
}
118
119
GMainContext *
120
soup_thread_default_context (void)
121
0
{
122
0
        GMainContext *context;
123
124
0
        context = g_main_context_get_thread_default ();
125
0
        if (!context)
126
0
                context = g_main_context_default ();
127
128
0
        return context;
129
0
}
130
131
/* 00 URI_UNRESERVED
132
 * 01 URI_PCT_ENCODED
133
 * 02 URI_GEN_DELIMS
134
 * 04 URI_SUB_DELIMS
135
 * 08 HTTP_SEPARATOR
136
 * 10 HTTP_CTL
137
 */
138
const char soup_char_attributes[] = {
139
  /* 0x00 - 0x07 */
140
  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
141
  /* 0x08 - 0x0f */
142
  0x11, 0x19, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
143
  /* 0x10 - 0x17 */
144
  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
145
  /* 0x18 - 0x1f */
146
  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
147
  /*  !"#$%&' */
148
  0x09, 0x04, 0x09, 0x02, 0x04, 0x01, 0x04, 0x04,
149
  /* ()*+,-./ */
150
  0x0c, 0x0c, 0x04, 0x04, 0x0c, 0x00, 0x00, 0x0a,
151
  /* 01234567 */
152
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153
  /* 89:;<=>? */
154
  0x00, 0x00, 0x0a, 0x0c, 0x09, 0x0a, 0x09, 0x0a,
155
  /* @ABCDEFG */
156
  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157
  /* HIJKLMNO */
158
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159
  /* PQRSTUVW */
160
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161
  /* XYZ[\]^_ */
162
  0x00, 0x00, 0x00, 0x0a, 0x09, 0x0a, 0x01, 0x00,
163
  /* `abcdefg */
164
  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165
  /* hijklmno */
166
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167
  /* pqrstuvw */
168
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169
  /* xyz{|}~  */
170
  0x00, 0x00, 0x00, 0x09, 0x01, 0x09, 0x00, 0x11,
171
  /* 0x80 - 0xFF */
172
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
173
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
174
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
175
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
176
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
177
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
178
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
179
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
180
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
181
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
182
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
183
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
184
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
185
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
186
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
187
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
188
};
189
190
/**
191
 * soup_host_matches_host
192
 * @host: a URI
193
 * @compare_with: a URI
194
 *
195
 * Checks if the @host and @compare_with exactly match or prefixed with a dot.
196
 *
197
 * Returns: %TRUE if the hosts match, %FALSE otherwise
198
 *
199
 **/
200
gboolean
201
soup_host_matches_host (const gchar *host, const gchar *compare_with)
202
0
{
203
0
  g_return_val_if_fail (host != NULL, FALSE);
204
0
  g_return_val_if_fail (compare_with != NULL, FALSE);
205
206
0
  if (!g_ascii_strcasecmp (host, compare_with))
207
0
    return TRUE;
208
0
  if (*host != '.')
209
0
    return FALSE;
210
0
  if (!g_ascii_strcasecmp (host + 1, compare_with))
211
0
    return TRUE;
212
0
  return g_str_has_suffix (compare_with, host);
213
0
}
214
215
/* Converts a language in POSIX format and to be RFC2616 compliant    */
216
/* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
217
static gchar *
218
posix_lang_to_rfc2616 (const gchar *language)
219
0
{
220
  /* Don't include charset variants, etc */
221
0
  if (strchr (language, '.') || strchr (language, '@'))
222
0
    return NULL;
223
224
  /* Ignore "C" locale, which g_get_language_names() always
225
   * includes as a fallback.
226
   */
227
0
  if (!strcmp (language, "C"))
228
0
    return NULL;
229
230
0
  return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
231
0
}
232
233
/* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
234
static gchar *
235
add_quality_value (const gchar *str, int quality)
236
0
{
237
0
  g_return_val_if_fail (str != NULL, NULL);
238
239
0
  if (quality >= 0 && quality < 100) {
240
    /* We don't use %.02g because of "." vs "," locale issues */
241
0
    if (quality % 10)
242
0
      return g_strdup_printf ("%s;q=0.%02d", str, quality);
243
0
    else
244
0
      return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
245
0
  } else
246
0
    return g_strdup (str);
247
0
}
248
249
/* Returns a RFC2616 compliant languages list from system locales */
250
gchar *
251
soup_get_accept_languages_from_system (void)
252
0
{
253
0
  const char * const * lang_names;
254
0
  GPtrArray *langs = NULL;
255
0
  char *lang, *langs_str;
256
0
  int delta;
257
0
  guint i;
258
259
0
  lang_names = g_get_language_names ();
260
0
  g_return_val_if_fail (lang_names != NULL, NULL);
261
262
  /* Build the array of languages */
263
0
  langs = g_ptr_array_new_with_free_func (g_free);
264
0
  for (i = 0; lang_names[i] != NULL; i++) {
265
0
    lang = posix_lang_to_rfc2616 (lang_names[i]);
266
0
    if (lang)
267
0
      g_ptr_array_add (langs, lang);
268
0
  }
269
270
  /* Add quality values */
271
0
  if (langs->len < 10)
272
0
    delta = 10;
273
0
  else if (langs->len < 20)
274
0
    delta = 5;
275
0
  else
276
0
    delta = 1;
277
278
0
  for (i = 0; i < langs->len; i++) {
279
0
    lang = langs->pdata[i];
280
0
    langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
281
0
    g_free (lang);
282
0
  }
283
284
  /* Fallback: add "en" if list is empty */
285
0
  if (langs->len == 0)
286
0
    g_ptr_array_add (langs, g_strdup ("en"));
287
288
0
  g_ptr_array_add (langs, NULL);
289
0
  langs_str = g_strjoinv (", ", (char **)langs->pdata);
290
0
  g_ptr_array_free (langs, TRUE);
291
292
0
  return langs_str;
293
0
}
294
295
const char *
296
soup_http_version_to_string (SoupHTTPVersion version)
297
0
{
298
0
        switch (version) {
299
0
        case SOUP_HTTP_1_0:
300
0
                return "1.0";
301
0
        case SOUP_HTTP_1_1:
302
0
                return "1.1";
303
0
        case SOUP_HTTP_2_0:
304
0
                return "2";
305
0
        }
306
307
0
        g_assert_not_reached ();
308
0
        return NULL;
309
0
}