/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 | } |