Coverage Report

Created: 2025-08-28 06:24

/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