/src/pidgin/libpurple/protocols/jabber/jutil.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * purple - Jabber Protocol Plugin |
3 | | * |
4 | | * Purple is the legal property of its developers, whose names are too numerous |
5 | | * to list here. Please refer to the COPYRIGHT file distributed with this |
6 | | * source distribution. |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
21 | | * |
22 | | */ |
23 | | #include "internal.h" |
24 | | #include "account.h" |
25 | | #include "cipher.h" |
26 | | #include "conversation.h" |
27 | | #include "debug.h" |
28 | | #include "server.h" |
29 | | #include "util.h" |
30 | | #include "xmlnode.h" |
31 | | |
32 | | #include "chat.h" |
33 | | #include "presence.h" |
34 | | #include "jutil.h" |
35 | | |
36 | | #ifdef USE_IDN |
37 | | #include <idna.h> |
38 | | #include <stringprep.h> |
39 | | static char idn_buffer[1024]; |
40 | | #endif |
41 | | |
42 | | #ifdef USE_IDN |
43 | | static gboolean jabber_nodeprep(char *str, size_t buflen) |
44 | | { |
45 | | return stringprep_xmpp_nodeprep(str, buflen) == STRINGPREP_OK; |
46 | | } |
47 | | |
48 | | static gboolean jabber_resourceprep(char *str, size_t buflen) |
49 | | { |
50 | | return stringprep_xmpp_resourceprep(str, buflen) == STRINGPREP_OK; |
51 | | } |
52 | | |
53 | | static JabberID* |
54 | | jabber_idn_validate(const char *str, const char *at, const char *slash, |
55 | | const char *null) |
56 | | { |
57 | | const char *node = NULL; |
58 | | const char *domain = NULL; |
59 | | const char *resource = NULL; |
60 | | int node_len = 0; |
61 | | int domain_len = 0; |
62 | | int resource_len = 0; |
63 | | char *out; |
64 | | JabberID *jid; |
65 | | |
66 | | /* Ensure no parts are > 1023 bytes */ |
67 | | if (at) { |
68 | | node = str; |
69 | | node_len = at - str; |
70 | | |
71 | | domain = at + 1; |
72 | | if (slash) { |
73 | | domain_len = slash - (at + 1); |
74 | | resource = slash + 1; |
75 | | resource_len = null - (slash + 1); |
76 | | } else { |
77 | | domain_len = null - (at + 1); |
78 | | } |
79 | | } else { |
80 | | domain = str; |
81 | | |
82 | | if (slash) { |
83 | | domain_len = slash - str; |
84 | | resource = slash + 1; |
85 | | resource_len = null - (slash + 1); |
86 | | } else { |
87 | | domain_len = null - str; |
88 | | } |
89 | | } |
90 | | |
91 | | if (node && node_len > 1023) |
92 | | return NULL; |
93 | | if (domain_len > 1023) |
94 | | return NULL; |
95 | | if (resource && resource_len > 1023) |
96 | | return NULL; |
97 | | |
98 | | jid = g_new0(JabberID, 1); |
99 | | |
100 | | if (node) { |
101 | | strncpy(idn_buffer, node, node_len); |
102 | | idn_buffer[node_len] = '\0'; |
103 | | |
104 | | if (!jabber_nodeprep(idn_buffer, sizeof(idn_buffer))) { |
105 | | jabber_id_free(jid); |
106 | | jid = NULL; |
107 | | goto out; |
108 | | } |
109 | | |
110 | | jid->node = g_strdup(idn_buffer); |
111 | | } |
112 | | |
113 | | /* domain *must* be here */ |
114 | | strncpy(idn_buffer, domain, domain_len); |
115 | | idn_buffer[domain_len] = '\0'; |
116 | | if (domain[0] == '[') { /* IPv6 address */ |
117 | | gboolean valid = FALSE; |
118 | | |
119 | | if (idn_buffer[domain_len - 1] == ']') { |
120 | | idn_buffer[domain_len - 1] = '\0'; |
121 | | valid = purple_ipv6_address_is_valid(idn_buffer + 1); |
122 | | } |
123 | | |
124 | | if (!valid) { |
125 | | jabber_id_free(jid); |
126 | | jid = NULL; |
127 | | goto out; |
128 | | } |
129 | | |
130 | | jid->domain = g_strndup(domain, domain_len); |
131 | | } else { |
132 | | /* Apply nameprep */ |
133 | | if (stringprep_nameprep(idn_buffer, sizeof(idn_buffer)) != STRINGPREP_OK) { |
134 | | jabber_id_free(jid); |
135 | | jid = NULL; |
136 | | goto out; |
137 | | } |
138 | | |
139 | | /* And now ToASCII */ |
140 | | if (idna_to_ascii_8z(idn_buffer, &out, IDNA_USE_STD3_ASCII_RULES) != IDNA_SUCCESS) { |
141 | | jabber_id_free(jid); |
142 | | jid = NULL; |
143 | | goto out; |
144 | | } |
145 | | |
146 | | /* This *MUST* be freed using 'free', not 'g_free' */ |
147 | | free(out); |
148 | | jid->domain = g_strdup(idn_buffer); |
149 | | } |
150 | | |
151 | | if (resource) { |
152 | | strncpy(idn_buffer, resource, resource_len); |
153 | | idn_buffer[resource_len] = '\0'; |
154 | | |
155 | | if (!jabber_resourceprep(idn_buffer, sizeof(idn_buffer))) { |
156 | | jabber_id_free(jid); |
157 | | jid = NULL; |
158 | | goto out; |
159 | | } else |
160 | | jid->resource = g_strdup(idn_buffer); |
161 | | } |
162 | | |
163 | | out: |
164 | | return jid; |
165 | | } |
166 | | |
167 | | #endif /* USE_IDN */ |
168 | | |
169 | | gboolean jabber_nodeprep_validate(const char *str) |
170 | 0 | { |
171 | | #ifdef USE_IDN |
172 | | gboolean result; |
173 | | #else |
174 | 0 | const char *c; |
175 | 0 | #endif |
176 | |
|
177 | 0 | if(!str) |
178 | 0 | return TRUE; |
179 | | |
180 | 0 | if(strlen(str) > 1023) |
181 | 0 | return FALSE; |
182 | | |
183 | | #ifdef USE_IDN |
184 | | strncpy(idn_buffer, str, sizeof(idn_buffer) - 1); |
185 | | idn_buffer[sizeof(idn_buffer) - 1] = '\0'; |
186 | | result = jabber_nodeprep(idn_buffer, sizeof(idn_buffer)); |
187 | | return result; |
188 | | #else /* USE_IDN */ |
189 | 0 | c = str; |
190 | 0 | while(c && *c) { |
191 | 0 | gunichar ch = g_utf8_get_char(c); |
192 | 0 | if(ch == '\"' || ch == '&' || ch == '\'' || ch == '/' || ch == ':' || |
193 | 0 | ch == '<' || ch == '>' || ch == '@' || !g_unichar_isgraph(ch)) { |
194 | 0 | return FALSE; |
195 | 0 | } |
196 | 0 | c = g_utf8_next_char(c); |
197 | 0 | } |
198 | | |
199 | 0 | return TRUE; |
200 | 0 | #endif /* USE_IDN */ |
201 | 0 | } |
202 | | |
203 | | gboolean jabber_domain_validate(const char *str) |
204 | 0 | { |
205 | 0 | const char *c; |
206 | 0 | size_t len; |
207 | |
|
208 | 0 | if(!str) |
209 | 0 | return TRUE; |
210 | | |
211 | 0 | len = strlen(str); |
212 | 0 | if (len > 1023) |
213 | 0 | return FALSE; |
214 | | |
215 | 0 | c = str; |
216 | |
|
217 | 0 | if (*c == '[') { |
218 | | /* Check if str is a valid IPv6 identifier */ |
219 | 0 | gboolean valid = FALSE; |
220 | |
|
221 | 0 | if (*(c + len - 1) != ']') |
222 | 0 | return FALSE; |
223 | | |
224 | | /* Ugly, but in-place */ |
225 | 0 | *(gchar *)(c + len - 1) = '\0'; |
226 | 0 | valid = purple_ipv6_address_is_valid(c + 1); |
227 | 0 | *(gchar *)(c + len - 1) = ']'; |
228 | |
|
229 | 0 | return valid; |
230 | 0 | } |
231 | | |
232 | 0 | while(c && *c) { |
233 | 0 | gunichar ch = g_utf8_get_char(c); |
234 | | /* The list of characters allowed in domain names is pretty small */ |
235 | 0 | if ((ch <= 0x7F && !( (ch >= 'a' && ch <= 'z') |
236 | 0 | || (ch >= '0' && ch <= '9') |
237 | 0 | || (ch >= 'A' && ch <= 'Z') |
238 | 0 | || ch == '.' |
239 | 0 | || ch == '-' )) || (ch >= 0x80 && !g_unichar_isgraph(ch))) |
240 | 0 | return FALSE; |
241 | | |
242 | 0 | c = g_utf8_next_char(c); |
243 | 0 | } |
244 | | |
245 | 0 | return TRUE; |
246 | 0 | } |
247 | | |
248 | | gboolean jabber_resourceprep_validate(const char *str) |
249 | 0 | { |
250 | | #ifdef USE_IDN |
251 | | gboolean result; |
252 | | #else |
253 | 0 | const char *c; |
254 | 0 | #endif |
255 | |
|
256 | 0 | if(!str) |
257 | 0 | return TRUE; |
258 | | |
259 | 0 | if(strlen(str) > 1023) |
260 | 0 | return FALSE; |
261 | | |
262 | | #ifdef USE_IDN |
263 | | strncpy(idn_buffer, str, sizeof(idn_buffer) - 1); |
264 | | idn_buffer[sizeof(idn_buffer) - 1] = '\0'; |
265 | | result = jabber_resourceprep(idn_buffer, sizeof(idn_buffer)); |
266 | | return result; |
267 | | #else /* USE_IDN */ |
268 | 0 | c = str; |
269 | 0 | while(c && *c) { |
270 | 0 | gunichar ch = g_utf8_get_char(c); |
271 | 0 | if(!g_unichar_isgraph(ch) && ch != ' ') |
272 | 0 | return FALSE; |
273 | | |
274 | 0 | c = g_utf8_next_char(c); |
275 | 0 | } |
276 | | |
277 | 0 | return TRUE; |
278 | 0 | #endif /* USE_IDN */ |
279 | 0 | } |
280 | | |
281 | | char *jabber_saslprep(const char *in) |
282 | 0 | { |
283 | | #ifdef USE_IDN |
284 | | char *out; |
285 | | |
286 | | g_return_val_if_fail(in != NULL, NULL); |
287 | | g_return_val_if_fail(strlen(in) <= sizeof(idn_buffer) - 1, NULL); |
288 | | |
289 | | strncpy(idn_buffer, in, sizeof(idn_buffer) - 1); |
290 | | idn_buffer[sizeof(idn_buffer) - 1] = '\0'; |
291 | | |
292 | | if (STRINGPREP_OK != stringprep(idn_buffer, sizeof(idn_buffer), 0, |
293 | | stringprep_saslprep)) { |
294 | | memset(idn_buffer, 0, sizeof(idn_buffer)); |
295 | | return NULL; |
296 | | } |
297 | | |
298 | | out = g_strdup(idn_buffer); |
299 | | memset(idn_buffer, 0, sizeof(idn_buffer)); |
300 | | return out; |
301 | | #else /* USE_IDN */ |
302 | | /* TODO: Something better than disallowing all non-ASCII characters */ |
303 | | /* TODO: Is this even correct? */ |
304 | 0 | const guchar *c; |
305 | |
|
306 | 0 | c = (const guchar *)in; |
307 | 0 | for ( ; *c; ++c) { |
308 | 0 | if (*c > 0x7f || /* Non-ASCII characters */ |
309 | 0 | *c == 0x7f || /* ASCII Delete character */ |
310 | 0 | (*c < 0x20 && *c != '\t' && *c != '\n' && *c != '\r')) |
311 | | /* ASCII control characters */ |
312 | 0 | return NULL; |
313 | 0 | } |
314 | | |
315 | 0 | return g_strdup(in); |
316 | 0 | #endif /* USE_IDN */ |
317 | 0 | } |
318 | | |
319 | | static JabberID* |
320 | | jabber_id_new_internal(const char *str, gboolean allow_terminating_slash) |
321 | 0 | { |
322 | 0 | const char *at = NULL; |
323 | 0 | const char *slash = NULL; |
324 | 0 | const char *c; |
325 | 0 | gboolean needs_validation = FALSE; |
326 | | #if 0 |
327 | | gboolean node_is_required = FALSE; |
328 | | #endif |
329 | 0 | #ifndef USE_IDN |
330 | 0 | char *node = NULL; |
331 | 0 | char *domain; |
332 | 0 | #endif |
333 | 0 | JabberID *jid; |
334 | |
|
335 | 0 | if (!str) |
336 | 0 | return NULL; |
337 | | |
338 | 0 | for (c = str; *c != '\0'; c++) |
339 | 0 | { |
340 | 0 | switch (*c) { |
341 | 0 | case '@': |
342 | 0 | if (!slash) { |
343 | 0 | if (at) { |
344 | | /* Multiple @'s in the node/domain portion, not a valid JID! */ |
345 | 0 | return NULL; |
346 | 0 | } |
347 | 0 | if (c == str) { |
348 | | /* JIDs cannot start with @ */ |
349 | 0 | return NULL; |
350 | 0 | } |
351 | 0 | if (c[1] == '\0') { |
352 | | /* JIDs cannot end with @ */ |
353 | 0 | return NULL; |
354 | 0 | } |
355 | 0 | at = c; |
356 | 0 | } |
357 | 0 | break; |
358 | | |
359 | 0 | case '/': |
360 | 0 | if (!slash) { |
361 | 0 | if (c == str) { |
362 | | /* JIDs cannot start with / */ |
363 | 0 | return NULL; |
364 | 0 | } |
365 | 0 | if (c[1] == '\0' && !allow_terminating_slash) { |
366 | | /* JIDs cannot end with / */ |
367 | 0 | return NULL; |
368 | 0 | } |
369 | 0 | slash = c; |
370 | 0 | } |
371 | 0 | break; |
372 | | |
373 | 0 | default: |
374 | | /* characters allowed everywhere */ |
375 | 0 | if ((*c >= 'a' && *c <= 'z') |
376 | 0 | || (*c >= '0' && *c <= '9') |
377 | 0 | || (*c >= 'A' && *c <= 'Z') |
378 | 0 | || *c == '.' || *c == '-') |
379 | | /* We're good */ |
380 | 0 | break; |
381 | | |
382 | | #if 0 |
383 | | if (slash != NULL) { |
384 | | /* characters allowed only in the resource */ |
385 | | if (implement_me) |
386 | | /* We're good */ |
387 | | break; |
388 | | } |
389 | | |
390 | | /* characters allowed only in the node */ |
391 | | if (implement_me) { |
392 | | /* |
393 | | * Ok, this character is valid, but only if it's a part |
394 | | * of the node and not the domain. But we don't know |
395 | | * if "c" is a part of the node or the domain until after |
396 | | * we've found the @. So set a flag for now and check |
397 | | * that we found an @ later. |
398 | | */ |
399 | | node_is_required = TRUE; |
400 | | break; |
401 | | } |
402 | | #endif |
403 | | |
404 | | /* |
405 | | * Hmm, this character is a bit more exotic. Better fall |
406 | | * back to using the more expensive UTF-8 compliant |
407 | | * stringprep functions. |
408 | | */ |
409 | 0 | needs_validation = TRUE; |
410 | 0 | break; |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | | #if 0 |
415 | | if (node_is_required && at == NULL) |
416 | | /* Found invalid characters in the domain */ |
417 | | return NULL; |
418 | | #endif |
419 | | |
420 | 0 | if (!needs_validation) { |
421 | | /* JID is made of only ASCII characters--just lowercase and return */ |
422 | 0 | jid = g_new0(JabberID, 1); |
423 | |
|
424 | 0 | if (at) { |
425 | 0 | jid->node = g_ascii_strdown(str, at - str); |
426 | 0 | if (slash) { |
427 | 0 | jid->domain = g_ascii_strdown(at + 1, slash - (at + 1)); |
428 | 0 | if (*(slash + 1)) |
429 | 0 | jid->resource = g_strdup(slash + 1); |
430 | 0 | } else { |
431 | 0 | jid->domain = g_ascii_strdown(at + 1, -1); |
432 | 0 | } |
433 | 0 | } else { |
434 | 0 | if (slash) { |
435 | 0 | jid->domain = g_ascii_strdown(str, slash - str); |
436 | 0 | if (*(slash + 1)) |
437 | 0 | jid->resource = g_strdup(slash + 1); |
438 | 0 | } else { |
439 | 0 | jid->domain = g_ascii_strdown(str, -1); |
440 | 0 | } |
441 | 0 | } |
442 | 0 | return jid; |
443 | 0 | } |
444 | | |
445 | | /* |
446 | | * If we get here, there are some non-ASCII chars in the string, so |
447 | | * we'll need to validate it, normalize, and finally do a full jabber |
448 | | * nodeprep on the jid. |
449 | | */ |
450 | | |
451 | 0 | if (!g_utf8_validate(str, -1, NULL)) |
452 | 0 | return NULL; |
453 | | |
454 | | #ifdef USE_IDN |
455 | | return jabber_idn_validate(str, at, slash, c /* points to the null */); |
456 | | #else /* USE_IDN */ |
457 | | |
458 | 0 | jid = g_new0(JabberID, 1); |
459 | | |
460 | | /* normalization */ |
461 | 0 | if(at) { |
462 | 0 | node = g_utf8_casefold(str, at-str); |
463 | 0 | if(slash) { |
464 | 0 | domain = g_utf8_casefold(at+1, slash-(at+1)); |
465 | 0 | if (*(slash + 1)) |
466 | 0 | jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC); |
467 | 0 | } else { |
468 | 0 | domain = g_utf8_casefold(at+1, -1); |
469 | 0 | } |
470 | 0 | } else { |
471 | 0 | if(slash) { |
472 | 0 | domain = g_utf8_casefold(str, slash-str); |
473 | 0 | if (*(slash + 1)) |
474 | 0 | jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC); |
475 | 0 | } else { |
476 | 0 | domain = g_utf8_casefold(str, -1); |
477 | 0 | } |
478 | 0 | } |
479 | |
|
480 | 0 | if (node) { |
481 | 0 | jid->node = g_utf8_normalize(node, -1, G_NORMALIZE_NFKC); |
482 | 0 | g_free(node); |
483 | 0 | } |
484 | |
|
485 | 0 | if (domain) { |
486 | 0 | jid->domain = g_utf8_normalize(domain, -1, G_NORMALIZE_NFKC); |
487 | 0 | g_free(domain); |
488 | 0 | } |
489 | | |
490 | | /* and finally the jabber nodeprep */ |
491 | 0 | if(!jabber_nodeprep_validate(jid->node) || |
492 | 0 | !jabber_domain_validate(jid->domain) || |
493 | 0 | !jabber_resourceprep_validate(jid->resource)) { |
494 | 0 | jabber_id_free(jid); |
495 | 0 | return NULL; |
496 | 0 | } |
497 | | |
498 | 0 | return jid; |
499 | 0 | #endif /* USE_IDN */ |
500 | 0 | } |
501 | | |
502 | | void |
503 | | jabber_id_free(JabberID *jid) |
504 | 0 | { |
505 | 0 | if(jid) { |
506 | 0 | g_free(jid->node); |
507 | 0 | g_free(jid->domain); |
508 | 0 | g_free(jid->resource); |
509 | 0 | g_free(jid); |
510 | 0 | } |
511 | 0 | } |
512 | | |
513 | | |
514 | | gboolean |
515 | | jabber_id_equal(const JabberID *jid1, const JabberID *jid2) |
516 | 0 | { |
517 | 0 | if (!jid1 && !jid2) { |
518 | | /* Both are null therefore equal */ |
519 | 0 | return TRUE; |
520 | 0 | } |
521 | | |
522 | 0 | if (!jid1 || !jid2) { |
523 | | /* One is null, other is non-null, therefore not equal */ |
524 | 0 | return FALSE; |
525 | 0 | } |
526 | | |
527 | 0 | return purple_strequal(jid1->node, jid2->node) && |
528 | 0 | purple_strequal(jid1->domain, jid2->domain) && |
529 | 0 | purple_strequal(jid1->resource, jid2->resource); |
530 | 0 | } |
531 | | |
532 | | char *jabber_get_domain(const char *in) |
533 | 0 | { |
534 | 0 | JabberID *jid = jabber_id_new(in); |
535 | 0 | char *out; |
536 | |
|
537 | 0 | if (!jid) |
538 | 0 | return NULL; |
539 | | |
540 | 0 | out = g_strdup(jid->domain); |
541 | 0 | jabber_id_free(jid); |
542 | |
|
543 | 0 | return out; |
544 | 0 | } |
545 | | |
546 | | char *jabber_get_resource(const char *in) |
547 | 0 | { |
548 | 0 | JabberID *jid = jabber_id_new(in); |
549 | 0 | char *out; |
550 | |
|
551 | 0 | if(!jid) |
552 | 0 | return NULL; |
553 | | |
554 | 0 | out = g_strdup(jid->resource); |
555 | 0 | jabber_id_free(jid); |
556 | |
|
557 | 0 | return out; |
558 | 0 | } |
559 | | |
560 | | JabberID * |
561 | | jabber_id_to_bare_jid(const JabberID *jid) |
562 | 0 | { |
563 | 0 | JabberID *result = g_new0(JabberID, 1); |
564 | |
|
565 | 0 | result->node = g_strdup(jid->node); |
566 | 0 | result->domain = g_strdup(jid->domain); |
567 | |
|
568 | 0 | return result; |
569 | 0 | } |
570 | | |
571 | | char * |
572 | | jabber_get_bare_jid(const char *in) |
573 | 0 | { |
574 | 0 | JabberID *jid = jabber_id_new(in); |
575 | 0 | char *out; |
576 | |
|
577 | 0 | if (!jid) |
578 | 0 | return NULL; |
579 | 0 | out = jabber_id_get_bare_jid(jid); |
580 | 0 | jabber_id_free(jid); |
581 | |
|
582 | 0 | return out; |
583 | 0 | } |
584 | | |
585 | | char * |
586 | | jabber_id_get_bare_jid(const JabberID *jid) |
587 | 0 | { |
588 | 0 | g_return_val_if_fail(jid != NULL, NULL); |
589 | | |
590 | 0 | return g_strconcat(jid->node ? jid->node : "", |
591 | 0 | jid->node ? "@" : "", |
592 | 0 | jid->domain, |
593 | 0 | NULL); |
594 | 0 | } |
595 | | |
596 | | char * |
597 | | jabber_id_get_full_jid(const JabberID *jid) |
598 | 0 | { |
599 | 0 | g_return_val_if_fail(jid != NULL, NULL); |
600 | | |
601 | 0 | return g_strconcat(jid->node ? jid->node : "", |
602 | 0 | jid->node ? "@" : "", |
603 | 0 | jid->domain, |
604 | 0 | jid->resource ? "/" : "", |
605 | 0 | jid->resource ? jid->resource : "", |
606 | 0 | NULL); |
607 | 0 | } |
608 | | |
609 | | gboolean |
610 | | jabber_jid_is_domain(const char *jid) |
611 | 0 | { |
612 | 0 | const char *c; |
613 | |
|
614 | 0 | for (c = jid; *c; ++c) { |
615 | 0 | if (*c == '@' || *c == '/') |
616 | 0 | return FALSE; |
617 | 0 | } |
618 | | |
619 | 0 | return TRUE; |
620 | 0 | } |
621 | | |
622 | | |
623 | | JabberID * |
624 | | jabber_id_new(const char *str) |
625 | 0 | { |
626 | 0 | return jabber_id_new_internal(str, FALSE); |
627 | 0 | } |
628 | | |
629 | | const char *jabber_normalize(const PurpleAccount *account, const char *in) |
630 | 0 | { |
631 | 0 | PurpleConnection *gc = account ? account->gc : NULL; |
632 | 0 | JabberStream *js = gc ? gc->proto_data : NULL; |
633 | 0 | static char buf[3072]; /* maximum legal length of a jabber jid */ |
634 | 0 | JabberID *jid; |
635 | |
|
636 | 0 | jid = jabber_id_new_internal(in, TRUE); |
637 | 0 | if(!jid) |
638 | 0 | return NULL; |
639 | | |
640 | 0 | if(js && jid->node && jid->resource && |
641 | 0 | jabber_chat_find(js, jid->node, jid->domain)) |
642 | 0 | g_snprintf(buf, sizeof(buf), "%s@%s/%s", jid->node, jid->domain, |
643 | 0 | jid->resource); |
644 | 0 | else |
645 | 0 | g_snprintf(buf, sizeof(buf), "%s%s%s", jid->node ? jid->node : "", |
646 | 0 | jid->node ? "@" : "", jid->domain); |
647 | |
|
648 | 0 | jabber_id_free(jid); |
649 | |
|
650 | 0 | return buf; |
651 | 0 | } |
652 | | |
653 | | gboolean |
654 | | jabber_is_own_server(JabberStream *js, const char *str) |
655 | 0 | { |
656 | 0 | JabberID *jid; |
657 | 0 | gboolean equal; |
658 | |
|
659 | 0 | if (str == NULL) |
660 | 0 | return FALSE; |
661 | | |
662 | 0 | g_return_val_if_fail(*str != '\0', FALSE); |
663 | | |
664 | 0 | jid = jabber_id_new(str); |
665 | 0 | if (!jid) |
666 | 0 | return FALSE; |
667 | | |
668 | 0 | equal = (jid->node == NULL && |
669 | 0 | purple_strequal(jid->domain, js->user->domain) && |
670 | 0 | jid->resource == NULL); |
671 | 0 | jabber_id_free(jid); |
672 | 0 | return equal; |
673 | 0 | } |
674 | | |
675 | | gboolean |
676 | | jabber_is_own_account(JabberStream *js, const char *str) |
677 | 0 | { |
678 | 0 | JabberID *jid; |
679 | 0 | gboolean equal; |
680 | |
|
681 | 0 | if (str == NULL) |
682 | 0 | return TRUE; |
683 | | |
684 | 0 | g_return_val_if_fail(*str != '\0', FALSE); |
685 | | |
686 | 0 | jid = jabber_id_new(str); |
687 | 0 | if (!jid) |
688 | 0 | return FALSE; |
689 | | |
690 | 0 | equal = (purple_strequal(jid->node, js->user->node) && |
691 | 0 | purple_strequal(jid->domain, js->user->domain) && |
692 | 0 | (jid->resource == NULL || |
693 | 0 | purple_strequal(jid->resource, js->user->resource))); |
694 | 0 | jabber_id_free(jid); |
695 | 0 | return equal; |
696 | 0 | } |
697 | | |
698 | | static const struct { |
699 | | const char *status_id; /* link to core */ |
700 | | const char *show; /* The show child's cdata in a presence stanza */ |
701 | | const char *readable; /* readable representation */ |
702 | | JabberBuddyState state; |
703 | | } jabber_statuses[] = { |
704 | | { "offline", NULL, N_("Offline"), JABBER_BUDDY_STATE_UNAVAILABLE }, |
705 | | { "available", NULL, N_("Available"), JABBER_BUDDY_STATE_ONLINE}, |
706 | | { "freeforchat", "chat", N_("Chatty"), JABBER_BUDDY_STATE_CHAT }, |
707 | | { "away", "away", N_("Away"), JABBER_BUDDY_STATE_AWAY }, |
708 | | { "extended_away", "xa", N_("Extended Away"), JABBER_BUDDY_STATE_XA }, |
709 | | { "dnd", "dnd", N_("Do Not Disturb"), JABBER_BUDDY_STATE_DND }, |
710 | | { "error", NULL, N_("Error"), JABBER_BUDDY_STATE_ERROR } |
711 | | }; |
712 | | |
713 | | const char * |
714 | | jabber_buddy_state_get_name(const JabberBuddyState state) |
715 | 0 | { |
716 | 0 | gsize i; |
717 | 0 | for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) |
718 | 0 | if (jabber_statuses[i].state == state) |
719 | 0 | return _(jabber_statuses[i].readable); |
720 | | |
721 | 0 | return _("Unknown"); |
722 | 0 | } |
723 | | |
724 | | JabberBuddyState |
725 | | jabber_buddy_status_id_get_state(const char *id) |
726 | 0 | { |
727 | 0 | gsize i; |
728 | 0 | if (!id) |
729 | 0 | return JABBER_BUDDY_STATE_UNKNOWN; |
730 | | |
731 | 0 | for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) |
732 | 0 | if (purple_strequal(id, jabber_statuses[i].status_id)) |
733 | 0 | return jabber_statuses[i].state; |
734 | | |
735 | 0 | return JABBER_BUDDY_STATE_UNKNOWN; |
736 | 0 | } |
737 | | |
738 | | JabberBuddyState jabber_buddy_show_get_state(const char *id) |
739 | 0 | { |
740 | 0 | gsize i; |
741 | |
|
742 | 0 | g_return_val_if_fail(id != NULL, JABBER_BUDDY_STATE_UNKNOWN); |
743 | | |
744 | 0 | for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) |
745 | 0 | if (jabber_statuses[i].show && purple_strequal(id, jabber_statuses[i].show)) |
746 | 0 | return jabber_statuses[i].state; |
747 | | |
748 | 0 | purple_debug_warning("jabber", "Invalid value of presence <show/> " |
749 | 0 | "attribute: %s\n", id); |
750 | 0 | return JABBER_BUDDY_STATE_UNKNOWN; |
751 | 0 | } |
752 | | |
753 | | const char * |
754 | | jabber_buddy_state_get_show(JabberBuddyState state) |
755 | 0 | { |
756 | 0 | gsize i; |
757 | 0 | for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) |
758 | 0 | if (state == jabber_statuses[i].state) |
759 | 0 | return jabber_statuses[i].show; |
760 | | |
761 | 0 | return NULL; |
762 | 0 | } |
763 | | |
764 | | const char * |
765 | | jabber_buddy_state_get_status_id(JabberBuddyState state) |
766 | 0 | { |
767 | 0 | gsize i; |
768 | 0 | for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i) |
769 | 0 | if (state == jabber_statuses[i].state) |
770 | 0 | return jabber_statuses[i].status_id; |
771 | | |
772 | 0 | return NULL; |
773 | 0 | } |
774 | | |
775 | | char * |
776 | | jabber_calculate_data_hash(gconstpointer data, size_t len, |
777 | | const gchar *hash_algo) |
778 | 0 | { |
779 | 0 | PurpleCipherContext *context; |
780 | 0 | static gchar digest[129]; /* 512 bits hex + \0 */ |
781 | |
|
782 | 0 | context = purple_cipher_context_new_by_name(hash_algo, NULL); |
783 | 0 | if (context == NULL) |
784 | 0 | { |
785 | 0 | purple_debug_error("jabber", "Could not find %s cipher\n", hash_algo); |
786 | 0 | g_return_val_if_reached(NULL); |
787 | 0 | } |
788 | | |
789 | | /* Hash the data */ |
790 | 0 | purple_cipher_context_append(context, data, len); |
791 | 0 | if (!purple_cipher_context_digest_to_str(context, sizeof(digest), digest, NULL)) |
792 | 0 | { |
793 | 0 | purple_debug_error("jabber", "Failed to get digest for %s cipher.\n", |
794 | 0 | hash_algo); |
795 | 0 | g_return_val_if_reached(NULL); |
796 | 0 | } |
797 | 0 | purple_cipher_context_destroy(context); |
798 | |
|
799 | 0 | return g_strdup(digest); |
800 | 0 | } |
801 | | |