1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ |
---|
2 | /* |
---|
3 | * soup-misc.c: Miscellaneous functions |
---|
4 | |
---|
5 | * Copyright (C) 2000-2003, Ximian, Inc. |
---|
6 | */ |
---|
7 | |
---|
8 | #include <ctype.h> |
---|
9 | #include <string.h> |
---|
10 | |
---|
11 | #include "soup-misc.h" |
---|
12 | |
---|
13 | /** |
---|
14 | * soup_str_case_hash: |
---|
15 | * @key: ASCII string to hash |
---|
16 | * |
---|
17 | * Hashes @key in a case-insensitive manner. |
---|
18 | * |
---|
19 | * Return value: the hash code. |
---|
20 | **/ |
---|
21 | guint |
---|
22 | soup_str_case_hash (gconstpointer key) |
---|
23 | { |
---|
24 | const char *p = key; |
---|
25 | guint h = g_ascii_toupper(*p); |
---|
26 | |
---|
27 | if (h) |
---|
28 | for (p += 1; *p != '\0'; p++) |
---|
29 | h = (h << 5) - h + g_ascii_toupper(*p); |
---|
30 | |
---|
31 | return h; |
---|
32 | } |
---|
33 | |
---|
34 | /** |
---|
35 | * soup_str_case_equal: |
---|
36 | * @v1: an ASCII string |
---|
37 | * @v2: another ASCII string |
---|
38 | * |
---|
39 | * Compares @v1 and @v2 in a case-insensitive manner |
---|
40 | * |
---|
41 | * Return value: %TRUE if they are equal (modulo case) |
---|
42 | **/ |
---|
43 | gboolean |
---|
44 | soup_str_case_equal (gconstpointer v1, |
---|
45 | gconstpointer v2) |
---|
46 | { |
---|
47 | const char *string1 = v1; |
---|
48 | const char *string2 = v2; |
---|
49 | |
---|
50 | return g_ascii_strcasecmp (string1, string2) == 0; |
---|
51 | } |
---|
52 | |
---|
53 | /* Base64 utils (straight from camel-mime-utils.c) */ |
---|
54 | #define d(x) |
---|
55 | |
---|
56 | static char *base64_alphabet = |
---|
57 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
---|
58 | |
---|
59 | /* |
---|
60 | * call this when finished encoding everything, to |
---|
61 | * flush off the last little bit |
---|
62 | */ |
---|
63 | int |
---|
64 | soup_base64_encode_close (const guchar *in, |
---|
65 | int inlen, |
---|
66 | gboolean break_lines, |
---|
67 | guchar *out, |
---|
68 | int *state, |
---|
69 | int *save) |
---|
70 | { |
---|
71 | int c1, c2; |
---|
72 | unsigned char *outptr = out; |
---|
73 | |
---|
74 | if (inlen > 0) |
---|
75 | outptr += soup_base64_encode_step (in, |
---|
76 | inlen, |
---|
77 | break_lines, |
---|
78 | outptr, |
---|
79 | state, |
---|
80 | save); |
---|
81 | |
---|
82 | c1 = ((unsigned char *) save) [1]; |
---|
83 | c2 = ((unsigned char *) save) [2]; |
---|
84 | |
---|
85 | d(printf("mode = %d\nc1 = %c\nc2 = %c\n", |
---|
86 | (int)((char *) save) [0], |
---|
87 | (int)((char *) save) [1], |
---|
88 | (int)((char *) save) [2])); |
---|
89 | |
---|
90 | switch (((char *) save) [0]) { |
---|
91 | case 2: |
---|
92 | outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ]; |
---|
93 | g_assert (outptr [2] != 0); |
---|
94 | goto skip; |
---|
95 | case 1: |
---|
96 | outptr[2] = '='; |
---|
97 | skip: |
---|
98 | outptr [0] = base64_alphabet [ c1 >> 2 ]; |
---|
99 | outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )]; |
---|
100 | outptr [3] = '='; |
---|
101 | outptr += 4; |
---|
102 | break; |
---|
103 | } |
---|
104 | if (break_lines) |
---|
105 | *outptr++ = '\n'; |
---|
106 | |
---|
107 | *save = 0; |
---|
108 | *state = 0; |
---|
109 | |
---|
110 | return outptr-out; |
---|
111 | } |
---|
112 | |
---|
113 | /* |
---|
114 | * performs an 'encode step', only encodes blocks of 3 characters to the |
---|
115 | * output at a time, saves left-over state in state and save (initialise to |
---|
116 | * 0 on first invocation). |
---|
117 | */ |
---|
118 | int |
---|
119 | soup_base64_encode_step (const guchar *in, |
---|
120 | int len, |
---|
121 | gboolean break_lines, |
---|
122 | guchar *out, |
---|
123 | int *state, |
---|
124 | int *save) |
---|
125 | { |
---|
126 | register guchar *outptr; |
---|
127 | register const guchar *inptr; |
---|
128 | |
---|
129 | if (len <= 0) |
---|
130 | return 0; |
---|
131 | |
---|
132 | inptr = in; |
---|
133 | outptr = out; |
---|
134 | |
---|
135 | d (printf ("we have %d chars, and %d saved chars\n", |
---|
136 | len, |
---|
137 | ((char *) save) [0])); |
---|
138 | |
---|
139 | if (len + ((char *) save) [0] > 2) { |
---|
140 | const guchar *inend = in+len-2; |
---|
141 | register int c1, c2, c3; |
---|
142 | register int already; |
---|
143 | |
---|
144 | already = *state; |
---|
145 | |
---|
146 | switch (((char *) save) [0]) { |
---|
147 | case 1: c1 = ((unsigned char *) save) [1]; goto skip1; |
---|
148 | case 2: c1 = ((unsigned char *) save) [1]; |
---|
149 | c2 = ((unsigned char *) save) [2]; goto skip2; |
---|
150 | } |
---|
151 | |
---|
152 | /* |
---|
153 | * yes, we jump into the loop, no i'm not going to change it, |
---|
154 | * it's beautiful! |
---|
155 | */ |
---|
156 | while (inptr < inend) { |
---|
157 | c1 = *inptr++; |
---|
158 | skip1: |
---|
159 | c2 = *inptr++; |
---|
160 | skip2: |
---|
161 | c3 = *inptr++; |
---|
162 | *outptr++ = base64_alphabet [ c1 >> 2 ]; |
---|
163 | *outptr++ = base64_alphabet [ c2 >> 4 | |
---|
164 | ((c1&0x3) << 4) ]; |
---|
165 | *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) | |
---|
166 | (c3 >> 6) ]; |
---|
167 | *outptr++ = base64_alphabet [ c3 & 0x3f ]; |
---|
168 | /* this is a bit ugly ... */ |
---|
169 | if (break_lines && (++already)>=19) { |
---|
170 | *outptr++='\n'; |
---|
171 | already = 0; |
---|
172 | } |
---|
173 | } |
---|
174 | |
---|
175 | ((char *)save)[0] = 0; |
---|
176 | len = 2-(inptr-inend); |
---|
177 | *state = already; |
---|
178 | } |
---|
179 | |
---|
180 | d(printf("state = %d, len = %d\n", |
---|
181 | (int)((char *)save)[0], |
---|
182 | len)); |
---|
183 | |
---|
184 | if (len>0) { |
---|
185 | register char *saveout; |
---|
186 | |
---|
187 | /* points to the slot for the next char to save */ |
---|
188 | saveout = & (((char *)save)[1]) + ((char *)save)[0]; |
---|
189 | |
---|
190 | /* len can only be 0 1 or 2 */ |
---|
191 | switch(len) { |
---|
192 | case 2: *saveout++ = *inptr++; |
---|
193 | case 1: *saveout++ = *inptr++; |
---|
194 | } |
---|
195 | ((char *)save)[0]+=len; |
---|
196 | } |
---|
197 | |
---|
198 | d(printf("mode = %d\nc1 = %c\nc2 = %c\n", |
---|
199 | (int)((char *)save)[0], |
---|
200 | (int)((char *)save)[1], |
---|
201 | (int)((char *)save)[2])); |
---|
202 | |
---|
203 | return outptr-out; |
---|
204 | } |
---|
205 | |
---|
206 | /** |
---|
207 | * soup_base64_encode: |
---|
208 | * @text: the binary data to encode. |
---|
209 | * @len: the length of @text. |
---|
210 | * |
---|
211 | * Encode a sequence of binary data into it's Base-64 stringified |
---|
212 | * representation. |
---|
213 | * |
---|
214 | * Return value: The Base-64 encoded string representing @text. |
---|
215 | */ |
---|
216 | char * |
---|
217 | soup_base64_encode (const char *text, int len) |
---|
218 | { |
---|
219 | unsigned char *out; |
---|
220 | int state = 0, outlen; |
---|
221 | unsigned int save = 0; |
---|
222 | |
---|
223 | out = g_malloc (len * 4 / 3 + 5); |
---|
224 | outlen = soup_base64_encode_close (text, |
---|
225 | len, |
---|
226 | FALSE, |
---|
227 | out, |
---|
228 | &state, |
---|
229 | &save); |
---|
230 | out[outlen] = '\0'; |
---|
231 | return (char *) out; |
---|
232 | } |
---|
233 | |
---|
234 | static unsigned char camel_mime_base64_rank[256] = { |
---|
235 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
236 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
237 | 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, |
---|
238 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, |
---|
239 | 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
---|
240 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, |
---|
241 | 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
---|
242 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, |
---|
243 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
244 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
245 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
246 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
247 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
248 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
249 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
250 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, |
---|
251 | }; |
---|
252 | |
---|
253 | /** |
---|
254 | * base64_decode_step: decode a chunk of base64 encoded data |
---|
255 | * @in: input stream |
---|
256 | * @len: max length of data to decode |
---|
257 | * @out: output stream |
---|
258 | * @state: holds the number of bits that are stored in @save |
---|
259 | * @save: leftover bits that have not yet been decoded |
---|
260 | * |
---|
261 | * Decodes a chunk of base64 encoded data |
---|
262 | **/ |
---|
263 | int |
---|
264 | soup_base64_decode_step (const guchar *in, |
---|
265 | int len, |
---|
266 | guchar *out, |
---|
267 | int *state, |
---|
268 | guint *save) |
---|
269 | { |
---|
270 | register const guchar *inptr; |
---|
271 | register guchar *outptr; |
---|
272 | const guchar *inend; |
---|
273 | guchar c; |
---|
274 | register unsigned int v; |
---|
275 | int i; |
---|
276 | |
---|
277 | inend = in+len; |
---|
278 | outptr = out; |
---|
279 | |
---|
280 | /* convert 4 base64 bytes to 3 normal bytes */ |
---|
281 | v=*save; |
---|
282 | i=*state; |
---|
283 | inptr = in; |
---|
284 | while (inptr < inend) { |
---|
285 | c = camel_mime_base64_rank [*inptr++]; |
---|
286 | if (c != 0xff) { |
---|
287 | v = (v<<6) | c; |
---|
288 | i++; |
---|
289 | if (i==4) { |
---|
290 | *outptr++ = v>>16; |
---|
291 | *outptr++ = v>>8; |
---|
292 | *outptr++ = v; |
---|
293 | i=0; |
---|
294 | } |
---|
295 | } |
---|
296 | } |
---|
297 | |
---|
298 | *save = v; |
---|
299 | *state = i; |
---|
300 | |
---|
301 | /* quick scan back for '=' on the end somewhere */ |
---|
302 | /* fortunately we can drop 1 output char for each trailing = (upto 2) */ |
---|
303 | i=2; |
---|
304 | while (inptr > in && i) { |
---|
305 | inptr--; |
---|
306 | if (camel_mime_base64_rank [*inptr] != 0xff) { |
---|
307 | if (*inptr == '=') |
---|
308 | outptr--; |
---|
309 | i--; |
---|
310 | } |
---|
311 | } |
---|
312 | |
---|
313 | /* if i!= 0 then there is a truncation error! */ |
---|
314 | return outptr - out; |
---|
315 | } |
---|
316 | |
---|
317 | char * |
---|
318 | soup_base64_decode (const char *text, |
---|
319 | int *out_len) |
---|
320 | { |
---|
321 | char *ret; |
---|
322 | int inlen, state = 0, save = 0; |
---|
323 | |
---|
324 | inlen = strlen (text); |
---|
325 | ret = g_malloc0 (inlen); |
---|
326 | |
---|
327 | *out_len = soup_base64_decode_step (text, inlen, ret, &state, &save); |
---|
328 | |
---|
329 | return ret; |
---|
330 | } |
---|
331 | |
---|
332 | typedef struct { |
---|
333 | gpointer instance; |
---|
334 | guint signal_id; |
---|
335 | } SoupSignalOnceData; |
---|
336 | |
---|
337 | static void |
---|
338 | signal_once_object_destroyed (gpointer ssod, GObject *ex_object) |
---|
339 | { |
---|
340 | g_free (ssod); |
---|
341 | } |
---|
342 | |
---|
343 | static void |
---|
344 | signal_once_metamarshal (GClosure *closure, GValue *return_value, |
---|
345 | guint n_param_values, const GValue *param_values, |
---|
346 | gpointer invocation_hint, gpointer marshal_data) |
---|
347 | { |
---|
348 | SoupSignalOnceData *ssod = marshal_data; |
---|
349 | |
---|
350 | closure->marshal (closure, return_value, n_param_values, |
---|
351 | param_values, invocation_hint, |
---|
352 | ((GCClosure *)closure)->callback); |
---|
353 | |
---|
354 | if (g_signal_handler_is_connected (ssod->instance, ssod->signal_id)) |
---|
355 | g_signal_handler_disconnect (ssod->instance, ssod->signal_id); |
---|
356 | g_object_weak_unref (G_OBJECT (ssod->instance), signal_once_object_destroyed, ssod); |
---|
357 | g_free (ssod); |
---|
358 | } |
---|
359 | |
---|
360 | /** |
---|
361 | * soup_signal_connect_once: |
---|
362 | * @instance: an object |
---|
363 | * @detailed_signal: "signal-name" or "signal-name::detail" to connect to |
---|
364 | * @c_handler: the #GCallback to connect |
---|
365 | * @data: data to pass to @c_handler calls |
---|
366 | * |
---|
367 | * Connects a #GCallback function to a signal as with |
---|
368 | * g_signal_connect(), but automatically removes the signal handler |
---|
369 | * after its first invocation. |
---|
370 | * |
---|
371 | * Return value: the signal handler id |
---|
372 | **/ |
---|
373 | guint |
---|
374 | soup_signal_connect_once (gpointer instance, const char *detailed_signal, |
---|
375 | GCallback c_handler, gpointer data) |
---|
376 | { |
---|
377 | SoupSignalOnceData *ssod; |
---|
378 | GClosure *closure; |
---|
379 | |
---|
380 | g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0); |
---|
381 | g_return_val_if_fail (detailed_signal != NULL, 0); |
---|
382 | g_return_val_if_fail (c_handler != NULL, 0); |
---|
383 | |
---|
384 | ssod = g_new0 (SoupSignalOnceData, 1); |
---|
385 | ssod->instance = instance; |
---|
386 | g_object_weak_ref (G_OBJECT (instance), signal_once_object_destroyed, ssod); |
---|
387 | |
---|
388 | closure = g_cclosure_new (c_handler, data, NULL); |
---|
389 | g_closure_set_meta_marshal (closure, ssod, signal_once_metamarshal); |
---|
390 | |
---|
391 | ssod->signal_id = g_signal_connect_closure (instance, detailed_signal, |
---|
392 | closure, FALSE); |
---|
393 | return ssod->signal_id; |
---|
394 | } |
---|