~vish/ubuntu/maverick/pidgin/bug25979

« back to all changes in this revision

Viewing changes to libpurple/protocols/jabber/jutil.c

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2009-10-09 19:40:26 UTC
  • mfrom: (1.4.1 upstream) (46.1.10 karmic)
  • Revision ID: james.westby@ubuntu.com-20091009194026-wbqqh0bsbz19nx5q
Tags: 1:2.6.2-1ubuntu7
* Don't stick the buddy list window to all desktops as some
  window managers have trouble to properly unstick it (LP: #346840)
  - debian/patches/11_buddy_list_really_show.patch
* Always use default tray icon size on KDE (LP: #209440)
  - debian/patches/62_tray_icon_size_kde.patch
* Use scrollbars in the preferences dialog if the screen height is
  below 700 px instead of 600 px
  - debian/patches/60_1024x600_gtkprefs.c.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 * purple - Jabber Protocol Plugin
3
3
 *
4
 
 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com>
 
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.
5
7
 *
6
8
 * This program is free software; you can redistribute it and/or modify
7
9
 * it under the terms of the GNU General Public License as published by
31
33
#include "presence.h"
32
34
#include "jutil.h"
33
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;
 
85
                        resource_len = null - (slash + 1);
 
86
                } else {
 
87
                        domain_len = null - (str + 1);
 
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
        } else {
 
130
                /* Apply nameprep */
 
131
                if (stringprep_nameprep(idn_buffer, sizeof(idn_buffer)) != STRINGPREP_OK) {
 
132
                        jabber_id_free(jid);
 
133
                        jid = NULL;
 
134
                        goto out;
 
135
                }
 
136
 
 
137
                /* And now ToASCII */
 
138
                if (idna_to_ascii_8z(idn_buffer, &out, IDNA_USE_STD3_ASCII_RULES) != IDNA_SUCCESS) {
 
139
                        jabber_id_free(jid);
 
140
                        jid = NULL;
 
141
                        goto out;
 
142
                }
 
143
 
 
144
                /* This *MUST* be freed using 'free', not 'g_free' */
 
145
                free(out);
 
146
                jid->domain = g_strdup(idn_buffer);
 
147
        }
 
148
 
 
149
        if (resource) {
 
150
                strncpy(idn_buffer, resource, resource_len);
 
151
                idn_buffer[resource_len] = '\0';
 
152
 
 
153
                if (!jabber_resourceprep(idn_buffer, sizeof(idn_buffer))) {
 
154
                        jabber_id_free(jid);
 
155
                        jid = NULL;
 
156
                        /* goto out; */
 
157
                }
 
158
 
 
159
                jid->resource = g_strdup(idn_buffer);
 
160
        }
 
161
 
 
162
out:
 
163
        return jid;
 
164
}
 
165
 
 
166
#endif /* USE_IDN */
 
167
 
34
168
gboolean jabber_nodeprep_validate(const char *str)
35
169
{
 
170
#ifdef USE_IDN
 
171
        gboolean result;
 
172
#else
36
173
        const char *c;
 
174
#endif
37
175
 
38
176
        if(!str)
39
177
                return TRUE;
41
179
        if(strlen(str) > 1023)
42
180
                return FALSE;
43
181
 
 
182
#ifdef USE_IDN
 
183
        strncpy(idn_buffer, str, sizeof(idn_buffer) - 1);
 
184
        idn_buffer[sizeof(idn_buffer) - 1] = '\0';
 
185
        result = jabber_nodeprep(idn_buffer, sizeof(idn_buffer));
 
186
        return result;
 
187
#else /* USE_IDN */
44
188
        c = str;
45
189
        while(c && *c) {
46
190
                gunichar ch = g_utf8_get_char(c);
52
196
        }
53
197
 
54
198
        return TRUE;
 
199
#endif /* USE_IDN */
55
200
}
56
201
 
57
 
gboolean jabber_nameprep_validate(const char *str)
 
202
gboolean jabber_domain_validate(const char *str)
58
203
{
59
204
        const char *c;
 
205
        size_t len;
60
206
 
61
207
        if(!str)
62
208
                return TRUE;
63
209
 
64
 
        if(strlen(str) > 1023)
 
210
        len = strlen(str);
 
211
        if (len > 1023)
65
212
                return FALSE;
66
213
 
67
214
        c = str;
 
215
 
 
216
        if (*c == '[') {
 
217
                /* Check if str is a valid IPv6 identifier */
 
218
                gboolean valid = FALSE;
 
219
 
 
220
                if (*(c + len - 1) != ']')
 
221
                        return FALSE;
 
222
 
 
223
                /* Ugly, but in-place */
 
224
                *(gchar *)(c + len - 1) = '\0';
 
225
                valid = purple_ipv6_address_is_valid(c + 1);
 
226
                *(gchar *)(c + len - 1) = ']';
 
227
 
 
228
                return valid;
 
229
        }
 
230
 
68
231
        while(c && *c) {
69
232
                gunichar ch = g_utf8_get_char(c);
70
 
                if(!g_unichar_isgraph(ch))
 
233
                /* The list of characters allowed in domain names is pretty small */
 
234
                if ((ch <= 0x7F && !( (ch >= 'a' && ch <= 'z')
 
235
                                || (ch >= '0' && ch <= '9')
 
236
                                || (ch >= 'A' && ch <= 'Z')
 
237
                                || ch == '.'
 
238
                                || ch == '-' )) || (ch >= 0x80 && !g_unichar_isgraph(ch)))
71
239
                        return FALSE;
72
240
 
73
241
                c = g_utf8_next_char(c);
74
242
        }
75
243
 
76
 
 
77
244
        return TRUE;
78
245
}
79
246
 
80
247
gboolean jabber_resourceprep_validate(const char *str)
81
248
{
 
249
#ifdef USE_IDN
 
250
        gboolean result;
 
251
#else
82
252
        const char *c;
 
253
#endif
83
254
 
84
255
        if(!str)
85
256
                return TRUE;
87
258
        if(strlen(str) > 1023)
88
259
                return FALSE;
89
260
 
 
261
#ifdef USE_IDN
 
262
        strncpy(idn_buffer, str, sizeof(idn_buffer) - 1);
 
263
        idn_buffer[sizeof(idn_buffer) - 1] = '\0';
 
264
        result = jabber_resourceprep(idn_buffer, sizeof(idn_buffer));
 
265
        return result;
 
266
#else /* USE_IDN */
90
267
        c = str;
91
268
        while(c && *c) {
92
269
                gunichar ch = g_utf8_get_char(c);
97
274
        }
98
275
 
99
276
        return TRUE;
 
277
#endif /* USE_IDN */
100
278
}
101
279
 
102
 
 
103
 
JabberID*
104
 
jabber_id_new(const char *str)
 
280
static JabberID*
 
281
jabber_id_new_internal(const char *str, gboolean allow_terminating_slash)
105
282
{
106
 
        char *at;
107
 
        char *slash;
 
283
        const char *at = NULL;
 
284
        const char *slash = NULL;
 
285
        const char *c;
 
286
        gboolean needs_validation = FALSE;
 
287
#if 0
 
288
        gboolean node_is_required = FALSE;
 
289
#endif
 
290
#ifndef USE_IDN
 
291
        char *node = NULL;
 
292
        char *domain;
 
293
#endif
108
294
        JabberID *jid;
109
295
 
110
 
        if(!str || !g_utf8_validate(str, -1, NULL))
111
 
                return NULL;
 
296
        if (!str)
 
297
                return NULL;
 
298
 
 
299
        for (c = str; *c != '\0'; c++)
 
300
        {
 
301
                switch (*c) {
 
302
                        case '@':
 
303
                                if (!slash) {
 
304
                                        if (at) {
 
305
                                                /* Multiple @'s in the node/domain portion, not a valid JID! */
 
306
                                                return NULL;
 
307
                                        }
 
308
                                        if (c == str) {
 
309
                                                /* JIDs cannot start with @ */
 
310
                                                return NULL;
 
311
                                        }
 
312
                                        if (c[1] == '\0') {
 
313
                                                /* JIDs cannot end with @ */
 
314
                                                return NULL;
 
315
                                        }
 
316
                                        at = c;
 
317
                                }
 
318
                                break;
 
319
 
 
320
                        case '/':
 
321
                                if (!slash) {
 
322
                                        if (c == str) {
 
323
                                                /* JIDs cannot start with / */
 
324
                                                return NULL;
 
325
                                        }
 
326
                                        if (c[1] == '\0' && !allow_terminating_slash) {
 
327
                                                /* JIDs cannot end with / */
 
328
                                                return NULL;
 
329
                                        }
 
330
                                        slash = c;
 
331
                                }
 
332
                                break;
 
333
 
 
334
                        default:
 
335
                                /* characters allowed everywhere */
 
336
                                if ((*c >= 'a' && *c <= 'z')
 
337
                                                || (*c >= '0' && *c <= '9')
 
338
                                                || (*c >= 'A' && *c <= 'Z')
 
339
                                                || *c == '.' || *c == '-')
 
340
                                        /* We're good */
 
341
                                        break;
 
342
 
 
343
#if 0
 
344
                                if (slash != NULL) {
 
345
                                        /* characters allowed only in the resource */
 
346
                                        if (implement_me)
 
347
                                                /* We're good */
 
348
                                                break;
 
349
                                }
 
350
 
 
351
                                /* characters allowed only in the node */
 
352
                                if (implement_me) {
 
353
                                        /*
 
354
                                         * Ok, this character is valid, but only if it's a part
 
355
                                         * of the node and not the domain.  But we don't know
 
356
                                         * if "c" is a part of the node or the domain until after
 
357
                                         * we've found the @.  So set a flag for now and check
 
358
                                         * that we found an @ later.
 
359
                                         */
 
360
                                        node_is_required = TRUE;
 
361
                                        break;
 
362
                                }
 
363
#endif
 
364
 
 
365
                                /*
 
366
                                 * Hmm, this character is a bit more exotic.  Better fall
 
367
                                 * back to using the more expensive UTF-8 compliant
 
368
                                 * stringprep functions.
 
369
                                 */
 
370
                                needs_validation = TRUE;
 
371
                                break;
 
372
                }
 
373
        }
 
374
 
 
375
#if 0
 
376
        if (node_is_required && at == NULL)
 
377
                /* Found invalid characters in the domain */
 
378
                return NULL;
 
379
#endif
 
380
 
 
381
        if (!needs_validation) {
 
382
                /* JID is made of only ASCII characters--just lowercase and return */
 
383
                jid = g_new0(JabberID, 1);
 
384
 
 
385
                if (at) {
 
386
                        jid->node = g_ascii_strdown(str, at - str);
 
387
                        if (slash) {
 
388
                                jid->domain = g_ascii_strdown(at + 1, slash - (at + 1));
 
389
                                if (*(slash + 1))
 
390
                                        jid->resource = g_strdup(slash + 1);
 
391
                        } else {
 
392
                                jid->domain = g_ascii_strdown(at + 1, -1);
 
393
                        }
 
394
                } else {
 
395
                        if (slash) {
 
396
                                jid->domain = g_ascii_strdown(str, slash - str);
 
397
                                if (*(slash + 1))
 
398
                                        jid->resource = g_strdup(slash + 1);
 
399
                        } else {
 
400
                                jid->domain = g_ascii_strdown(str, -1);
 
401
                        }
 
402
                }
 
403
                return jid;
 
404
        }
 
405
 
 
406
        /*
 
407
         * If we get here, there are some non-ASCII chars in the string, so
 
408
         * we'll need to validate it, normalize, and finally do a full jabber
 
409
         * nodeprep on the jid.
 
410
         */
 
411
 
 
412
        if (!g_utf8_validate(str, -1, NULL))
 
413
                return NULL;
 
414
 
 
415
#ifdef USE_IDN
 
416
        return jabber_idn_validate(str, at, slash, c /* points to the null */);
 
417
#else /* USE_IDN */
112
418
 
113
419
        jid = g_new0(JabberID, 1);
114
420
 
115
 
        at = g_utf8_strchr(str, -1, '@');
116
 
        slash = g_utf8_strchr(str, -1, '/');
117
 
 
 
421
        /* normalization */
118
422
        if(at) {
119
 
                jid->node = g_utf8_normalize(str, at-str, G_NORMALIZE_NFKC);
 
423
                node = g_utf8_casefold(str, at-str);
120
424
                if(slash) {
121
 
                        jid->domain = g_utf8_normalize(at+1, slash-(at+1), G_NORMALIZE_NFKC);
122
 
                        jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC);
 
425
                        domain = g_utf8_casefold(at+1, slash-(at+1));
 
426
                        if (*(slash + 1))
 
427
                                jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC);
123
428
                } else {
124
 
                        jid->domain = g_utf8_normalize(at+1, -1, G_NORMALIZE_NFKC);
 
429
                        domain = g_utf8_casefold(at+1, -1);
125
430
                }
126
431
        } else {
127
432
                if(slash) {
128
 
                        jid->domain = g_utf8_normalize(str, slash-str, G_NORMALIZE_NFKC);
129
 
                        jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC);
 
433
                        domain = g_utf8_casefold(str, slash-str);
 
434
                        if (*(slash + 1))
 
435
                                jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC);
130
436
                } else {
131
 
                        jid->domain = g_utf8_normalize(str, -1, G_NORMALIZE_NFKC);
 
437
                        domain = g_utf8_casefold(str, -1);
132
438
                }
133
439
        }
134
440
 
135
 
 
 
441
        if (node) {
 
442
                jid->node = g_utf8_normalize(node, -1, G_NORMALIZE_NFKC);
 
443
                g_free(node);
 
444
        }
 
445
 
 
446
        if (domain) {
 
447
                jid->domain = g_utf8_normalize(domain, -1, G_NORMALIZE_NFKC);
 
448
                g_free(domain);
 
449
        }
 
450
 
 
451
        /* and finally the jabber nodeprep */
136
452
        if(!jabber_nodeprep_validate(jid->node) ||
137
 
                        !jabber_nameprep_validate(jid->domain) ||
 
453
                        !jabber_domain_validate(jid->domain) ||
138
454
                        !jabber_resourceprep_validate(jid->resource)) {
139
455
                jabber_id_free(jid);
140
456
                return NULL;
141
457
        }
142
458
 
143
459
        return jid;
 
460
#endif /* USE_IDN */
144
461
}
145
462
 
146
463
void
172
489
        return out;
173
490
}
174
491
 
175
 
char *jabber_get_bare_jid(const char *in)
 
492
char *
 
493
jabber_get_bare_jid(const char *in)
176
494
{
177
495
        JabberID *jid = jabber_id_new(in);
178
496
        char *out;
179
497
 
180
 
        if(!jid)
 
498
        if (!jid)
181
499
                return NULL;
182
 
 
183
 
        out = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
184
 
                        jid->node ? "@" : "", jid->domain);
 
500
        out = jabber_id_get_bare_jid(jid);
185
501
        jabber_id_free(jid);
186
502
 
187
503
        return out;
188
504
}
189
505
 
 
506
char *
 
507
jabber_id_get_bare_jid(const JabberID *jid)
 
508
{
 
509
        g_return_val_if_fail(jid != NULL, NULL);
 
510
 
 
511
        return g_strconcat(jid->node ? jid->node : "",
 
512
                           jid->node ? "@" : "",
 
513
                           jid->domain,
 
514
                           NULL);
 
515
}
 
516
 
 
517
JabberID *
 
518
jabber_id_new(const char *str)
 
519
{
 
520
        return jabber_id_new_internal(str, FALSE);
 
521
}
 
522
 
190
523
const char *jabber_normalize(const PurpleAccount *account, const char *in)
191
524
{
192
525
        PurpleConnection *gc = account ? account->gc : NULL;
193
526
        JabberStream *js = gc ? gc->proto_data : NULL;
194
527
        static char buf[3072]; /* maximum legal length of a jabber jid */
195
528
        JabberID *jid;
196
 
        char *node, *domain;
197
 
 
198
 
        jid = jabber_id_new(in);
199
 
 
 
529
 
 
530
        jid = jabber_id_new_internal(in, TRUE);
200
531
        if(!jid)
201
532
                return NULL;
202
533
 
203
 
        node = jid->node ? g_utf8_strdown(jid->node, -1) : NULL;
204
 
        domain = g_utf8_strdown(jid->domain, -1);
205
 
 
206
 
 
207
 
        if(js && node && jid->resource &&
208
 
                        jabber_chat_find(js, node, domain))
209
 
                g_snprintf(buf, sizeof(buf), "%s@%s/%s", node, domain,
 
534
        if(js && jid->node && jid->resource &&
 
535
                        jabber_chat_find(js, jid->node, jid->domain))
 
536
                g_snprintf(buf, sizeof(buf), "%s@%s/%s", jid->node, jid->domain,
210
537
                                jid->resource);
211
538
        else
212
 
                g_snprintf(buf, sizeof(buf), "%s%s%s", node ? node : "",
213
 
                                node ? "@" : "", domain);
 
539
                g_snprintf(buf, sizeof(buf), "%s%s%s", jid->node ? jid->node : "",
 
540
                                jid->node ? "@" : "", jid->domain);
214
541
 
215
542
        jabber_id_free(jid);
216
 
        g_free(node);
217
 
        g_free(domain);
218
543
 
219
544
        return buf;
220
545
}
221
546
 
 
547
gboolean
 
548
jabber_is_own_server(JabberStream *js, const char *str)
 
549
{
 
550
        JabberID *jid;
 
551
        gboolean equal;
 
552
 
 
553
        if (str == NULL)
 
554
                return FALSE;
 
555
 
 
556
        g_return_val_if_fail(*str != '\0', FALSE);
 
557
 
 
558
        jid = jabber_id_new(str);
 
559
        if (!jid)
 
560
                return FALSE;
 
561
 
 
562
        equal = (jid->node == NULL &&
 
563
                 g_str_equal(jid->domain, js->user->domain) &&
 
564
                 jid->resource == NULL);
 
565
        jabber_id_free(jid);
 
566
        return equal;
 
567
}
 
568
 
 
569
gboolean
 
570
jabber_is_own_account(JabberStream *js, const char *str)
 
571
{
 
572
        JabberID *jid;
 
573
        gboolean equal;
 
574
 
 
575
        if (str == NULL)
 
576
                return TRUE;
 
577
 
 
578
        g_return_val_if_fail(*str != '\0', FALSE);
 
579
 
 
580
        jid = jabber_id_new(str);
 
581
        if (!jid)
 
582
                return FALSE;
 
583
 
 
584
        equal = (purple_strequal(jid->node, js->user->node) &&
 
585
                 g_str_equal(jid->domain, js->user->domain) &&
 
586
                 (jid->resource == NULL ||
 
587
                     g_str_equal(jid->resource, js->user->resource)));
 
588
        jabber_id_free(jid);
 
589
        return equal;
 
590
}
 
591
 
222
592
PurpleConversation *
223
593
jabber_find_unnormalized_conv(const char *name, PurpleAccount *account)
224
594
{