Coverage Report

Created: 2025-12-31 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pidgin/libpurple/status.c
Line
Count
Source
1
/**
2
 * @file status.c Status API
3
 * @ingroup core
4
 */
5
6
/* purple
7
 *
8
 * Purple is the legal property of its developers, whose names are too numerous
9
 * to list here.  Please refer to the COPYRIGHT file distributed with this
10
 * source distribution.
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
25
 */
26
#define _PURPLE_STATUS_C_
27
28
#include "internal.h"
29
30
#include "blist.h"
31
#include "core.h"
32
#include "dbus-maybe.h"
33
#include "debug.h"
34
#include "glibcompat.h"
35
#include "notify.h"
36
#include "prefs.h"
37
#include "status.h"
38
39
/**
40
 * A type of status.
41
 */
42
struct _PurpleStatusType
43
{
44
  PurpleStatusPrimitive primitive;
45
46
  char *id;
47
  char *name;
48
  char *primary_attr_id;
49
50
  gboolean saveable;
51
  gboolean user_settable;
52
  gboolean independent;
53
54
  GList *attrs;
55
};
56
57
/**
58
 * A status attribute.
59
 */
60
struct _PurpleStatusAttr
61
{
62
  char *id;
63
  char *name;
64
  PurpleValue *value_type;
65
};
66
67
/**
68
 * A list of statuses.
69
 */
70
struct _PurplePresence
71
{
72
  PurplePresenceContext context;
73
74
  gboolean idle;
75
  time_t idle_time;
76
  time_t login_time;
77
78
  GList *statuses;
79
  GHashTable *status_table;
80
81
  PurpleStatus *active_status;
82
83
  union
84
  {
85
    PurpleAccount *account;
86
87
    struct
88
    {
89
      PurpleConversation *conv;
90
      char *user;
91
92
    } chat;
93
94
    struct
95
    {
96
      PurpleAccount *account;
97
      char *name;
98
      PurpleBuddy *buddy;
99
100
    } buddy;
101
102
  } u;
103
};
104
105
/**
106
 * An active status.
107
 */
108
struct _PurpleStatus
109
{
110
  PurpleStatusType *type;
111
  PurplePresence *presence;
112
113
  gboolean active;
114
115
  /*
116
   * The current values of the attributes for this status.  The
117
   * key is a string containing the name of the attribute.  It is
118
   * a borrowed reference from the list of attrs in the
119
   * PurpleStatusType.  The value is a PurpleValue.
120
   */
121
  GHashTable *attr_values;
122
};
123
124
typedef struct
125
{
126
  PurpleAccount *account;
127
  char *name;
128
} PurpleStatusBuddyKey;
129
130
static int primitive_scores[] =
131
{
132
  0,      /* unset                    */
133
  -500,   /* offline                  */
134
  100,    /* available                */
135
  -75,    /* unavailable              */
136
  -50,    /* invisible                */
137
  -100,   /* away                     */
138
  -200,   /* extended away            */
139
  -400,   /* mobile                   */
140
  0,      /* tune                     */
141
  0,      /* mood                     */
142
  -10,    /* idle, special case.      */
143
  -5,     /* idle time, special case. */
144
  10      /* Offline messageable      */
145
};
146
147
0
#define SCORE_IDLE      9
148
0
#define SCORE_IDLE_TIME 10
149
0
#define SCORE_OFFLINE_MESSAGE 11
150
151
/**************************************************************************
152
 * PurpleStatusPrimitive API
153
 **************************************************************************/
154
static struct PurpleStatusPrimitiveMap
155
{
156
  PurpleStatusPrimitive type;
157
  const char *id;
158
  const char *name;
159
160
} const status_primitive_map[] =
161
{
162
  { PURPLE_STATUS_UNSET,           "unset",           N_("Unset")               },
163
  { PURPLE_STATUS_OFFLINE,         "offline",         N_("Offline")             },
164
  { PURPLE_STATUS_AVAILABLE,       "available",       N_("Available")           },
165
  { PURPLE_STATUS_UNAVAILABLE,     "unavailable",     N_("Do not disturb")      },
166
  { PURPLE_STATUS_INVISIBLE,       "invisible",       N_("Invisible")           },
167
  { PURPLE_STATUS_AWAY,            "away",            N_("Away")                },
168
  { PURPLE_STATUS_EXTENDED_AWAY,   "extended_away",   N_("Extended away")       },
169
  { PURPLE_STATUS_MOBILE,          "mobile",          N_("Mobile")              },
170
  { PURPLE_STATUS_TUNE,            "tune",            N_("Listening to music"), },
171
  { PURPLE_STATUS_MOOD,            "mood",            N_("Feeling")             },
172
};
173
174
const char *
175
purple_primitive_get_id_from_type(PurpleStatusPrimitive type)
176
0
{
177
0
    int i;
178
179
0
    for (i = 0; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
180
0
    {
181
0
    if (type == status_primitive_map[i].type)
182
0
      return status_primitive_map[i].id;
183
0
    }
184
185
0
    return status_primitive_map[0].id;
186
0
}
187
188
const char *
189
purple_primitive_get_name_from_type(PurpleStatusPrimitive type)
190
0
{
191
0
    int i;
192
193
0
    for (i = 0; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
194
0
    {
195
0
  if (type == status_primitive_map[i].type)
196
0
    return _(status_primitive_map[i].name);
197
0
    }
198
199
0
    return _(status_primitive_map[0].name);
200
0
}
201
202
PurpleStatusPrimitive
203
purple_primitive_get_type_from_id(const char *id)
204
0
{
205
0
    int i;
206
207
0
    g_return_val_if_fail(id != NULL, PURPLE_STATUS_UNSET);
208
209
0
    for (i = 0; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
210
0
    {
211
0
    if (purple_strequal(id, status_primitive_map[i].id))
212
0
            return status_primitive_map[i].type;
213
0
    }
214
215
0
    return status_primitive_map[0].type;
216
0
}
217
218
219
/**************************************************************************
220
 * PurpleStatusType API
221
 **************************************************************************/
222
PurpleStatusType *
223
purple_status_type_new_full(PurpleStatusPrimitive primitive, const char *id,
224
              const char *name, gboolean saveable,
225
              gboolean user_settable, gboolean independent)
226
0
{
227
0
  PurpleStatusType *status_type;
228
229
0
  g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, NULL);
230
231
0
  status_type = g_new0(PurpleStatusType, 1);
232
0
  PURPLE_DBUS_REGISTER_POINTER(status_type, PurpleStatusType);
233
234
0
  status_type->primitive     = primitive;
235
0
  status_type->saveable      = saveable;
236
0
  status_type->user_settable = user_settable;
237
0
  status_type->independent   = independent;
238
239
0
  if (id != NULL)
240
0
    status_type->id = g_strdup(id);
241
0
  else
242
0
    status_type->id = g_strdup(purple_primitive_get_id_from_type(primitive));
243
244
0
  if (name != NULL)
245
0
    status_type->name = g_strdup(name);
246
0
  else
247
0
    status_type->name = g_strdup(purple_primitive_get_name_from_type(primitive));
248
249
0
  return status_type;
250
0
}
251
252
PurpleStatusType *
253
purple_status_type_new(PurpleStatusPrimitive primitive, const char *id,
254
           const char *name, gboolean user_settable)
255
0
{
256
0
  g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, NULL);
257
258
0
  return purple_status_type_new_full(primitive, id, name, TRUE,
259
0
      user_settable, FALSE);
260
0
}
261
262
PurpleStatusType *
263
purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive,
264
    const char *id, const char *name,
265
    gboolean saveable, gboolean user_settable,
266
    gboolean independent, const char *attr_id,
267
    const char *attr_name, PurpleValue *attr_value,
268
    ...)
269
0
{
270
0
  PurpleStatusType *status_type;
271
0
  va_list args;
272
273
0
  g_return_val_if_fail(primitive  != PURPLE_STATUS_UNSET, NULL);
274
0
  g_return_val_if_fail(attr_id    != NULL,              NULL);
275
0
  g_return_val_if_fail(attr_name  != NULL,              NULL);
276
0
  g_return_val_if_fail(attr_value != NULL,              NULL);
277
278
0
  status_type = purple_status_type_new_full(primitive, id, name, saveable,
279
0
      user_settable, independent);
280
281
  /* Add the first attribute */
282
0
  purple_status_type_add_attr(status_type, attr_id, attr_name, attr_value);
283
284
0
  va_start(args, attr_value);
285
0
  purple_status_type_add_attrs_vargs(status_type, args);
286
0
  va_end(args);
287
288
0
  return status_type;
289
0
}
290
291
void
292
purple_status_type_destroy(PurpleStatusType *status_type)
293
0
{
294
0
  g_return_if_fail(status_type != NULL);
295
296
0
  g_free(status_type->id);
297
0
  g_free(status_type->name);
298
0
  g_free(status_type->primary_attr_id);
299
300
0
  g_list_free_full(status_type->attrs,
301
0
                   (GDestroyNotify)purple_status_attr_destroy);
302
303
0
  PURPLE_DBUS_UNREGISTER_POINTER(status_type);
304
0
  g_free(status_type);
305
0
}
306
307
void
308
purple_status_type_set_primary_attr(PurpleStatusType *status_type, const char *id)
309
0
{
310
0
  g_return_if_fail(status_type != NULL);
311
312
0
  g_free(status_type->primary_attr_id);
313
0
  status_type->primary_attr_id = g_strdup(id);
314
0
}
315
316
void
317
purple_status_type_add_attr(PurpleStatusType *status_type, const char *id,
318
    const char *name, PurpleValue *value)
319
0
{
320
0
  PurpleStatusAttr *attr;
321
322
0
  g_return_if_fail(status_type != NULL);
323
0
  g_return_if_fail(id          != NULL);
324
0
  g_return_if_fail(name        != NULL);
325
0
  g_return_if_fail(value       != NULL);
326
327
0
  attr = purple_status_attr_new(id, name, value);
328
329
0
  status_type->attrs = g_list_append(status_type->attrs, attr);
330
0
}
331
332
void
333
purple_status_type_add_attrs_vargs(PurpleStatusType *status_type, va_list args)
334
0
{
335
0
  const char *id, *name;
336
0
  PurpleValue *value;
337
338
0
  g_return_if_fail(status_type != NULL);
339
340
0
  while ((id = va_arg(args, const char *)) != NULL)
341
0
  {
342
0
    name = va_arg(args, const char *);
343
0
    g_return_if_fail(name != NULL);
344
345
0
    value = va_arg(args, PurpleValue *);
346
0
    g_return_if_fail(value != NULL);
347
348
0
    purple_status_type_add_attr(status_type, id, name, value);
349
0
  }
350
0
}
351
352
void
353
purple_status_type_add_attrs(PurpleStatusType *status_type, const char *id,
354
    const char *name, PurpleValue *value, ...)
355
0
{
356
0
  va_list args;
357
358
0
  g_return_if_fail(status_type != NULL);
359
0
  g_return_if_fail(id          != NULL);
360
0
  g_return_if_fail(name        != NULL);
361
0
  g_return_if_fail(value       != NULL);
362
363
  /* Add the first attribute */
364
0
  purple_status_type_add_attr(status_type, id, name, value);
365
366
0
  va_start(args, value);
367
0
  purple_status_type_add_attrs_vargs(status_type, args);
368
0
  va_end(args);
369
0
}
370
371
PurpleStatusPrimitive
372
purple_status_type_get_primitive(const PurpleStatusType *status_type)
373
0
{
374
0
  g_return_val_if_fail(status_type != NULL, PURPLE_STATUS_UNSET);
375
376
0
  return status_type->primitive;
377
0
}
378
379
const char *
380
purple_status_type_get_id(const PurpleStatusType *status_type)
381
0
{
382
0
  g_return_val_if_fail(status_type != NULL, NULL);
383
384
0
  return status_type->id;
385
0
}
386
387
const char *
388
purple_status_type_get_name(const PurpleStatusType *status_type)
389
0
{
390
0
  g_return_val_if_fail(status_type != NULL, NULL);
391
392
0
  return status_type->name;
393
0
}
394
395
gboolean
396
purple_status_type_is_saveable(const PurpleStatusType *status_type)
397
0
{
398
0
  g_return_val_if_fail(status_type != NULL, FALSE);
399
400
0
  return status_type->saveable;
401
0
}
402
403
gboolean
404
purple_status_type_is_user_settable(const PurpleStatusType *status_type)
405
0
{
406
0
  g_return_val_if_fail(status_type != NULL, FALSE);
407
408
0
  return status_type->user_settable;
409
0
}
410
411
gboolean
412
purple_status_type_is_independent(const PurpleStatusType *status_type)
413
0
{
414
0
  g_return_val_if_fail(status_type != NULL, FALSE);
415
416
0
  return status_type->independent;
417
0
}
418
419
gboolean
420
purple_status_type_is_exclusive(const PurpleStatusType *status_type)
421
0
{
422
0
  g_return_val_if_fail(status_type != NULL, FALSE);
423
424
0
  return !status_type->independent;
425
0
}
426
427
gboolean
428
purple_status_type_is_available(const PurpleStatusType *status_type)
429
0
{
430
0
  PurpleStatusPrimitive primitive;
431
432
0
  g_return_val_if_fail(status_type != NULL, FALSE);
433
434
0
  primitive = purple_status_type_get_primitive(status_type);
435
436
0
  return (primitive == PURPLE_STATUS_AVAILABLE);
437
0
}
438
439
const char *
440
purple_status_type_get_primary_attr(const PurpleStatusType *status_type)
441
0
{
442
0
  g_return_val_if_fail(status_type != NULL, NULL);
443
444
0
  return status_type->primary_attr_id;
445
0
}
446
447
PurpleStatusAttr *
448
purple_status_type_get_attr(const PurpleStatusType *status_type, const char *id)
449
0
{
450
0
  GList *l;
451
452
0
  g_return_val_if_fail(status_type != NULL, NULL);
453
0
  g_return_val_if_fail(id          != NULL, NULL);
454
455
0
  for (l = status_type->attrs; l != NULL; l = l->next)
456
0
  {
457
0
    PurpleStatusAttr *attr = (PurpleStatusAttr *)l->data;
458
459
0
    if (purple_strequal(purple_status_attr_get_id(attr), id))
460
0
      return attr;
461
0
  }
462
463
0
  return NULL;
464
0
}
465
466
GList *
467
purple_status_type_get_attrs(const PurpleStatusType *status_type)
468
0
{
469
0
  g_return_val_if_fail(status_type != NULL, NULL);
470
471
0
  return status_type->attrs;
472
0
}
473
474
const PurpleStatusType *
475
purple_status_type_find_with_id(GList *status_types, const char *id)
476
0
{
477
0
  PurpleStatusType *status_type;
478
479
0
  g_return_val_if_fail(id != NULL, NULL);
480
481
0
  while (status_types != NULL)
482
0
  {
483
0
    status_type = status_types->data;
484
485
0
    if (purple_strequal(id, status_type->id))
486
0
      return status_type;
487
488
0
    status_types = status_types->next;
489
0
  }
490
491
0
  return NULL;
492
0
}
493
494
495
/**************************************************************************
496
* PurpleStatusAttr API
497
**************************************************************************/
498
PurpleStatusAttr *
499
purple_status_attr_new(const char *id, const char *name, PurpleValue *value_type)
500
0
{
501
0
  PurpleStatusAttr *attr;
502
503
0
  g_return_val_if_fail(id         != NULL, NULL);
504
0
  g_return_val_if_fail(name       != NULL, NULL);
505
0
  g_return_val_if_fail(value_type != NULL, NULL);
506
507
0
  attr = g_new0(PurpleStatusAttr, 1);
508
0
  PURPLE_DBUS_REGISTER_POINTER(attr, PurpleStatusAttr);
509
510
0
  attr->id         = g_strdup(id);
511
0
  attr->name       = g_strdup(name);
512
0
  attr->value_type = value_type;
513
514
0
  return attr;
515
0
}
516
517
void
518
purple_status_attr_destroy(PurpleStatusAttr *attr)
519
0
{
520
0
  g_return_if_fail(attr != NULL);
521
522
0
  g_free(attr->id);
523
0
  g_free(attr->name);
524
525
0
  purple_value_destroy(attr->value_type);
526
527
0
  PURPLE_DBUS_UNREGISTER_POINTER(attr);
528
0
  g_free(attr);
529
0
}
530
531
const char *
532
purple_status_attr_get_id(const PurpleStatusAttr *attr)
533
0
{
534
0
  g_return_val_if_fail(attr != NULL, NULL);
535
536
0
  return attr->id;
537
0
}
538
539
const char *
540
purple_status_attr_get_name(const PurpleStatusAttr *attr)
541
0
{
542
0
  g_return_val_if_fail(attr != NULL, NULL);
543
544
0
  return attr->name;
545
0
}
546
547
PurpleValue *
548
purple_status_attr_get_value(const PurpleStatusAttr *attr)
549
0
{
550
0
  g_return_val_if_fail(attr != NULL, NULL);
551
552
0
  return attr->value_type;
553
0
}
554
555
556
/**************************************************************************
557
* PurpleStatus API
558
**************************************************************************/
559
PurpleStatus *
560
purple_status_new(PurpleStatusType *status_type, PurplePresence *presence)
561
0
{
562
0
  PurpleStatus *status;
563
0
  GList *l;
564
565
0
  g_return_val_if_fail(status_type != NULL, NULL);
566
0
  g_return_val_if_fail(presence    != NULL, NULL);
567
568
0
  status = g_new0(PurpleStatus, 1);
569
0
  PURPLE_DBUS_REGISTER_POINTER(status, PurpleStatus);
570
571
0
  status->type     = status_type;
572
0
  status->presence = presence;
573
574
0
  status->attr_values =
575
0
    g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
576
0
    (GDestroyNotify)purple_value_destroy);
577
578
0
  for (l = purple_status_type_get_attrs(status_type); l != NULL; l = l->next)
579
0
  {
580
0
    PurpleStatusAttr *attr = (PurpleStatusAttr *)l->data;
581
0
    PurpleValue *value = purple_status_attr_get_value(attr);
582
0
    PurpleValue *new_value = purple_value_dup(value);
583
584
0
    g_hash_table_insert(status->attr_values,
585
0
              (char *)purple_status_attr_get_id(attr),
586
0
              new_value);
587
0
  }
588
589
0
  return status;
590
0
}
591
592
/*
593
 * TODO: If the PurpleStatus is in a PurplePresence, then
594
 *       remove it from the PurplePresence?
595
 */
596
void
597
purple_status_destroy(PurpleStatus *status)
598
0
{
599
0
  g_return_if_fail(status != NULL);
600
601
0
  g_hash_table_destroy(status->attr_values);
602
603
0
  PURPLE_DBUS_UNREGISTER_POINTER(status);
604
0
  g_free(status);
605
0
}
606
607
static void
608
notify_buddy_status_update(PurpleBuddy *buddy, PurplePresence *presence,
609
    PurpleStatus *old_status, PurpleStatus *new_status)
610
0
{
611
0
  if (purple_prefs_get_bool("/purple/logging/log_system"))
612
0
  {
613
0
    time_t current_time = time(NULL);
614
0
    const char *buddy_alias = purple_buddy_get_alias(buddy);
615
0
    char *tmp, *logtmp;
616
0
    PurpleLog *log;
617
618
0
    if (old_status != NULL)
619
0
    {
620
0
      tmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias,
621
0
                            purple_buddy_get_name(buddy),
622
0
                            purple_status_get_name(old_status),
623
0
                            purple_status_get_name(new_status));
624
0
      logtmp = g_markup_escape_text(tmp, -1);
625
0
    }
626
0
    else
627
0
    {
628
      /* old_status == NULL when an independent status is toggled. */
629
630
0
      if (purple_status_is_active(new_status))
631
0
      {
632
0
        tmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias,
633
0
                              purple_buddy_get_name(buddy),
634
0
                              purple_status_get_name(new_status));
635
0
        logtmp = g_markup_escape_text(tmp, -1);
636
0
      }
637
0
      else
638
0
      {
639
0
        tmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias,
640
0
                              purple_buddy_get_name(buddy),
641
0
                              purple_status_get_name(new_status));
642
0
        logtmp = g_markup_escape_text(tmp, -1);
643
0
      }
644
0
    }
645
646
0
    log = purple_account_get_log(purple_buddy_get_account(buddy), FALSE);
647
0
    if (log != NULL)
648
0
    {
649
0
      purple_log_write(log, PURPLE_MESSAGE_SYSTEM, buddy_alias,
650
0
                     current_time, logtmp);
651
0
    }
652
653
0
    g_free(tmp);
654
0
    g_free(logtmp);
655
0
  }
656
0
}
657
658
static void
659
notify_status_update(PurplePresence *presence, PurpleStatus *old_status,
660
           PurpleStatus *new_status)
661
0
{
662
0
  PurplePresenceContext context = purple_presence_get_context(presence);
663
664
0
  if (context == PURPLE_PRESENCE_CONTEXT_ACCOUNT)
665
0
  {
666
0
    PurpleAccount *account = purple_presence_get_account(presence);
667
0
    PurpleAccountUiOps *ops = purple_accounts_get_ui_ops();
668
669
0
    if (purple_account_get_enabled(account, purple_core_get_ui()))
670
0
      purple_prpl_change_account_status(account, old_status, new_status);
671
672
0
    if (ops != NULL && ops->status_changed != NULL)
673
0
    {
674
0
      ops->status_changed(account, new_status);
675
0
    }
676
0
  }
677
0
  else if (context == PURPLE_PRESENCE_CONTEXT_BUDDY)
678
0
  {
679
0
      notify_buddy_status_update(purple_presence_get_buddy(presence), presence,
680
0
          old_status, new_status);
681
0
  }
682
0
}
683
684
static void
685
status_has_changed(PurpleStatus *status)
686
0
{
687
0
  PurplePresence *presence;
688
0
  PurpleStatus *old_status;
689
690
0
  presence   = purple_status_get_presence(status);
691
692
  /*
693
   * If this status is exclusive, then we must be setting it to "active."
694
   * Since we are setting it to active, we want to set the currently
695
   * active status to "inactive."
696
   */
697
0
  if (purple_status_is_exclusive(status))
698
0
  {
699
0
    old_status = purple_presence_get_active_status(presence);
700
0
    if (old_status != NULL && (old_status != status))
701
0
      old_status->active = FALSE;
702
0
    presence->active_status = status;
703
0
  }
704
0
  else
705
0
    old_status = NULL;
706
707
0
  notify_status_update(presence, old_status, status);
708
0
}
709
710
void
711
purple_status_set_active(PurpleStatus *status, gboolean active)
712
0
{
713
0
  purple_status_set_active_with_attrs_list(status, active, NULL);
714
0
}
715
716
/*
717
 * This used to parse the va_list directly, but now it creates a GList
718
 * and passes it to purple_status_set_active_with_attrs_list().  That
719
 * function was created because accounts.c needs to pass a GList of
720
 * attributes to the status API.
721
 */
722
void
723
purple_status_set_active_with_attrs(PurpleStatus *status, gboolean active, va_list args)
724
0
{
725
0
  GList *attrs = NULL;
726
0
  const gchar *id;
727
0
  gpointer data;
728
729
0
  while ((id = va_arg(args, const char *)) != NULL)
730
0
  {
731
0
    attrs = g_list_append(attrs, (char *)id);
732
0
    data = va_arg(args, void *);
733
0
    attrs = g_list_append(attrs, data);
734
0
  }
735
0
  purple_status_set_active_with_attrs_list(status, active, attrs);
736
0
  g_list_free(attrs);
737
0
}
738
739
void
740
purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
741
                     GList *attrs)
742
0
{
743
0
  gboolean changed = FALSE;
744
0
  GList *l;
745
0
  GList *specified_attr_ids = NULL;
746
0
  PurpleStatusType *status_type;
747
748
0
  g_return_if_fail(status != NULL);
749
750
0
  if (!active && purple_status_is_exclusive(status))
751
0
  {
752
0
    purple_debug_error("status",
753
0
           "Cannot deactivate an exclusive status (%s).\n",
754
0
           purple_status_get_id(status));
755
0
    return;
756
0
  }
757
758
0
  if (status->active != active)
759
0
  {
760
0
    changed = TRUE;
761
0
  }
762
763
0
  status->active = active;
764
765
  /* Set any attributes */
766
0
  l = attrs;
767
0
  while (l != NULL)
768
0
  {
769
0
    const gchar *id;
770
0
    PurpleValue *value;
771
772
0
    id = l->data;
773
0
    l = l->next;
774
0
    value = purple_status_get_attr_value(status, id);
775
0
    if (value == NULL)
776
0
    {
777
0
      purple_debug_warning("status", "The attribute \"%s\" on the status \"%s\" is "
778
0
                 "not supported.\n", id, status->type->name);
779
      /* Skip over the data and move on to the next attribute */
780
0
      l = l->next;
781
0
      continue;
782
0
    }
783
784
0
    specified_attr_ids = g_list_prepend(specified_attr_ids, (gpointer)id);
785
786
0
    if (value->type == PURPLE_TYPE_STRING)
787
0
    {
788
0
      const gchar *string_data = l->data;
789
0
      l = l->next;
790
0
      if (purple_strequal(string_data, value->data.string_data))
791
0
        continue;
792
0
      purple_status_set_attr_string(status, id, string_data);
793
0
      changed = TRUE;
794
0
    }
795
0
    else if (value->type == PURPLE_TYPE_INT)
796
0
    {
797
0
      int int_data = GPOINTER_TO_INT(l->data);
798
0
      l = l->next;
799
0
      if (int_data == value->data.int_data)
800
0
        continue;
801
0
      purple_status_set_attr_int(status, id, int_data);
802
0
      changed = TRUE;
803
0
    }
804
0
    else if (value->type == PURPLE_TYPE_BOOLEAN)
805
0
    {
806
0
      gboolean boolean_data = GPOINTER_TO_INT(l->data);
807
0
      l = l->next;
808
0
      if (boolean_data == value->data.boolean_data)
809
0
        continue;
810
0
      purple_status_set_attr_boolean(status, id, boolean_data);
811
0
      changed = TRUE;
812
0
    }
813
0
    else
814
0
    {
815
      /* We don't know what the data is--skip over it */
816
0
      l = l->next;
817
0
    }
818
0
  }
819
820
  /* Reset any unspecified attributes to their default value */
821
0
  status_type = purple_status_get_type(status);
822
0
  l = purple_status_type_get_attrs(status_type);
823
0
  while (l != NULL) {
824
0
    PurpleStatusAttr *attr;
825
826
0
    attr = l->data;
827
0
    l = l->next;
828
829
0
    if (!g_list_find_custom(specified_attr_ids, attr->id, (GCompareFunc)strcmp)) {
830
0
      PurpleValue *default_value;
831
0
      default_value = purple_status_attr_get_value(attr);
832
0
      if (default_value->type == PURPLE_TYPE_STRING) {
833
0
        const char *cur = purple_status_get_attr_string(status, attr->id);
834
0
        const char *def = purple_value_get_string(default_value);
835
0
        if (purple_strequal(cur, def)) {
836
0
          continue;
837
0
        }
838
839
0
        purple_status_set_attr_string(status, attr->id, def);
840
0
      } else if (default_value->type == PURPLE_TYPE_INT) {
841
0
        int cur = purple_status_get_attr_int(status, attr->id);
842
0
        int def = purple_value_get_int(default_value);
843
0
        if (cur == def)
844
0
          continue;
845
846
0
        purple_status_set_attr_int(status, attr->id, def);
847
0
      } else if (default_value->type == PURPLE_TYPE_BOOLEAN) {
848
0
        gboolean cur = purple_status_get_attr_boolean(status, attr->id);
849
0
        gboolean def = purple_value_get_boolean(default_value);
850
0
        if (cur == def)
851
0
          continue;
852
853
0
        purple_status_set_attr_boolean(status, attr->id, def);
854
0
      }
855
0
      changed = TRUE;
856
0
    }
857
0
  }
858
0
  g_list_free(specified_attr_ids);
859
860
0
  if (!changed)
861
0
    return;
862
0
  status_has_changed(status);
863
0
}
864
865
void
866
purple_status_set_attr_boolean(PurpleStatus *status, const char *id,
867
    gboolean value)
868
0
{
869
0
  PurpleValue *attr_value;
870
871
0
  g_return_if_fail(status != NULL);
872
0
  g_return_if_fail(id     != NULL);
873
874
  /* Make sure this attribute exists and is the correct type. */
875
0
  attr_value = purple_status_get_attr_value(status, id);
876
0
  g_return_if_fail(attr_value != NULL);
877
0
  g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_BOOLEAN);
878
879
0
  purple_value_set_boolean(attr_value, value);
880
0
}
881
882
void
883
purple_status_set_attr_int(PurpleStatus *status, const char *id, int value)
884
0
{
885
0
  PurpleValue *attr_value;
886
887
0
  g_return_if_fail(status != NULL);
888
0
  g_return_if_fail(id     != NULL);
889
890
  /* Make sure this attribute exists and is the correct type. */
891
0
  attr_value = purple_status_get_attr_value(status, id);
892
0
  g_return_if_fail(attr_value != NULL);
893
0
  g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_INT);
894
895
0
  purple_value_set_int(attr_value, value);
896
0
}
897
898
void
899
purple_status_set_attr_string(PurpleStatus *status, const char *id,
900
    const char *value)
901
0
{
902
0
  PurpleValue *attr_value;
903
904
0
  g_return_if_fail(status != NULL);
905
0
  g_return_if_fail(id     != NULL);
906
907
  /* Make sure this attribute exists and is the correct type. */
908
0
  attr_value = purple_status_get_attr_value(status, id);
909
  /* This used to be g_return_if_fail, but it's failing a LOT, so
910
   * let's generate a log error for now. */
911
  /* g_return_if_fail(attr_value != NULL); */
912
0
  if (attr_value == NULL) {
913
0
    purple_debug_error("status",
914
0
         "Attempted to set status attribute '%s' for "
915
0
         "status '%s', which is not legal.  Fix "
916
0
                                 "this!\n", id,
917
0
         purple_status_type_get_name(purple_status_get_type(status)));
918
0
    return;
919
0
  }
920
0
  g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_STRING);
921
922
  /* XXX: Check if the value has actually changed. If it has, and the status
923
   * is active, should this trigger 'status_has_changed'? */
924
0
  purple_value_set_string(attr_value, value);
925
0
}
926
927
PurpleStatusType *
928
purple_status_get_type(const PurpleStatus *status)
929
0
{
930
0
  g_return_val_if_fail(status != NULL, NULL);
931
932
0
  return status->type;
933
0
}
934
935
PurplePresence *
936
purple_status_get_presence(const PurpleStatus *status)
937
0
{
938
0
  g_return_val_if_fail(status != NULL, NULL);
939
940
0
  return status->presence;
941
0
}
942
943
const char *
944
purple_status_get_id(const PurpleStatus *status)
945
0
{
946
0
  g_return_val_if_fail(status != NULL, NULL);
947
948
0
  return purple_status_type_get_id(purple_status_get_type(status));
949
0
}
950
951
const char *
952
purple_status_get_name(const PurpleStatus *status)
953
0
{
954
0
  g_return_val_if_fail(status != NULL, NULL);
955
956
0
  return purple_status_type_get_name(purple_status_get_type(status));
957
0
}
958
959
gboolean
960
purple_status_is_independent(const PurpleStatus *status)
961
0
{
962
0
  g_return_val_if_fail(status != NULL, FALSE);
963
964
0
  return purple_status_type_is_independent(purple_status_get_type(status));
965
0
}
966
967
gboolean
968
purple_status_is_exclusive(const PurpleStatus *status)
969
0
{
970
0
  g_return_val_if_fail(status != NULL, FALSE);
971
972
0
  return purple_status_type_is_exclusive(purple_status_get_type(status));
973
0
}
974
975
gboolean
976
purple_status_is_available(const PurpleStatus *status)
977
0
{
978
0
  g_return_val_if_fail(status != NULL, FALSE);
979
980
0
  return purple_status_type_is_available(purple_status_get_type(status));
981
0
}
982
983
gboolean
984
purple_status_is_active(const PurpleStatus *status)
985
0
{
986
0
  g_return_val_if_fail(status != NULL, FALSE);
987
988
0
  return status->active;
989
0
}
990
991
gboolean
992
purple_status_is_online(const PurpleStatus *status)
993
0
{
994
0
  PurpleStatusPrimitive primitive;
995
996
0
  g_return_val_if_fail( status != NULL, FALSE);
997
998
0
  primitive = purple_status_type_get_primitive(purple_status_get_type(status));
999
1000
0
  return (primitive != PURPLE_STATUS_UNSET &&
1001
0
      primitive != PURPLE_STATUS_OFFLINE);
1002
0
}
1003
1004
PurpleValue *
1005
purple_status_get_attr_value(const PurpleStatus *status, const char *id)
1006
0
{
1007
0
  g_return_val_if_fail(status != NULL, NULL);
1008
0
  g_return_val_if_fail(id     != NULL, NULL);
1009
1010
0
  return (PurpleValue *)g_hash_table_lookup(status->attr_values, id);
1011
0
}
1012
1013
gboolean
1014
purple_status_get_attr_boolean(const PurpleStatus *status, const char *id)
1015
0
{
1016
0
  const PurpleValue *value;
1017
1018
0
  g_return_val_if_fail(status != NULL, FALSE);
1019
0
  g_return_val_if_fail(id     != NULL, FALSE);
1020
1021
0
  if ((value = purple_status_get_attr_value(status, id)) == NULL)
1022
0
    return FALSE;
1023
1024
0
  g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN, FALSE);
1025
1026
0
  return purple_value_get_boolean(value);
1027
0
}
1028
1029
int
1030
purple_status_get_attr_int(const PurpleStatus *status, const char *id)
1031
0
{
1032
0
  const PurpleValue *value;
1033
1034
0
  g_return_val_if_fail(status != NULL, 0);
1035
0
  g_return_val_if_fail(id     != NULL, 0);
1036
1037
0
  if ((value = purple_status_get_attr_value(status, id)) == NULL)
1038
0
    return 0;
1039
1040
0
  g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_INT, 0);
1041
1042
0
  return purple_value_get_int(value);
1043
0
}
1044
1045
const char *
1046
purple_status_get_attr_string(const PurpleStatus *status, const char *id)
1047
0
{
1048
0
  const PurpleValue *value;
1049
1050
0
  g_return_val_if_fail(status != NULL, NULL);
1051
0
  g_return_val_if_fail(id     != NULL, NULL);
1052
1053
0
  if ((value = purple_status_get_attr_value(status, id)) == NULL)
1054
0
    return NULL;
1055
1056
0
  g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_STRING, NULL);
1057
1058
0
  return purple_value_get_string(value);
1059
0
}
1060
1061
gint
1062
purple_status_compare(const PurpleStatus *status1, const PurpleStatus *status2)
1063
0
{
1064
0
  PurpleStatusType *type1, *type2;
1065
0
  int score1 = 0, score2 = 0;
1066
1067
0
  if ((status1 == NULL && status2 == NULL) ||
1068
0
      (status1 == status2))
1069
0
  {
1070
0
    return 0;
1071
0
  }
1072
0
  else if (status1 == NULL)
1073
0
    return 1;
1074
0
  else if (status2 == NULL)
1075
0
    return -1;
1076
1077
0
  type1 = purple_status_get_type(status1);
1078
0
  type2 = purple_status_get_type(status2);
1079
1080
0
  if (purple_status_is_active(status1))
1081
0
    score1 = primitive_scores[purple_status_type_get_primitive(type1)];
1082
1083
0
  if (purple_status_is_active(status2))
1084
0
    score2 = primitive_scores[purple_status_type_get_primitive(type2)];
1085
1086
0
  if (score1 > score2)
1087
0
    return -1;
1088
0
  else if (score1 < score2)
1089
0
    return 1;
1090
1091
0
  return 0;
1092
0
}
1093
1094
1095
/**************************************************************************
1096
* PurplePresence API
1097
**************************************************************************/
1098
PurplePresence *
1099
purple_presence_new(PurplePresenceContext context)
1100
0
{
1101
0
  PurplePresence *presence;
1102
1103
0
  g_return_val_if_fail(context != PURPLE_PRESENCE_CONTEXT_UNSET, NULL);
1104
1105
0
  presence = g_new0(PurplePresence, 1);
1106
0
  PURPLE_DBUS_REGISTER_POINTER(presence, PurplePresence);
1107
1108
0
  presence->context = context;
1109
1110
0
  presence->status_table =
1111
0
    g_hash_table_new_full(g_str_hash, g_str_equal,
1112
0
                g_free, NULL);
1113
1114
0
  return presence;
1115
0
}
1116
1117
PurplePresence *
1118
purple_presence_new_for_account(PurpleAccount *account)
1119
0
{
1120
0
  PurplePresence *presence = NULL;
1121
0
  g_return_val_if_fail(account != NULL, NULL);
1122
1123
0
  presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_ACCOUNT);
1124
0
  presence->u.account = account;
1125
0
  presence->statuses = purple_prpl_get_statuses(account, presence);
1126
1127
0
  return presence;
1128
0
}
1129
1130
PurplePresence *
1131
purple_presence_new_for_conv(PurpleConversation *conv)
1132
0
{
1133
0
  PurplePresence *presence;
1134
1135
0
  g_return_val_if_fail(conv != NULL, NULL);
1136
1137
0
  presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_CONV);
1138
0
  presence->u.chat.conv = conv;
1139
  /* presence->statuses = purple_prpl_get_statuses(conv->account, presence); ? */
1140
1141
0
  return presence;
1142
0
}
1143
1144
PurplePresence *
1145
purple_presence_new_for_buddy(PurpleBuddy *buddy)
1146
0
{
1147
0
  PurplePresence *presence;
1148
0
  PurpleAccount *account;
1149
1150
0
  g_return_val_if_fail(buddy != NULL, NULL);
1151
0
  account = purple_buddy_get_account(buddy);
1152
1153
0
  presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_BUDDY);
1154
1155
0
  presence->u.buddy.name    = g_strdup(purple_buddy_get_name(buddy));
1156
0
  presence->u.buddy.account = account;
1157
0
  presence->statuses = purple_prpl_get_statuses(account, presence);
1158
1159
0
  presence->u.buddy.buddy = buddy;
1160
1161
0
  return presence;
1162
0
}
1163
1164
void
1165
purple_presence_destroy(PurplePresence *presence)
1166
0
{
1167
0
  g_return_if_fail(presence != NULL);
1168
1169
0
  if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_BUDDY)
1170
0
  {
1171
0
    g_free(presence->u.buddy.name);
1172
0
  }
1173
0
  else if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_CONV)
1174
0
  {
1175
0
    g_free(presence->u.chat.user);
1176
0
  }
1177
1178
0
  g_list_free_full(presence->statuses, (GDestroyNotify)purple_status_destroy);
1179
1180
0
  g_hash_table_destroy(presence->status_table);
1181
1182
0
  PURPLE_DBUS_UNREGISTER_POINTER(presence);
1183
0
  g_free(presence);
1184
0
}
1185
1186
void
1187
purple_presence_add_status(PurplePresence *presence, PurpleStatus *status)
1188
0
{
1189
0
  g_return_if_fail(presence != NULL);
1190
0
  g_return_if_fail(status   != NULL);
1191
1192
0
  presence->statuses = g_list_append(presence->statuses, status);
1193
1194
0
  g_hash_table_insert(presence->status_table,
1195
0
  g_strdup(purple_status_get_id(status)), status);
1196
0
}
1197
1198
void
1199
purple_presence_add_list(PurplePresence *presence, GList *source_list)
1200
0
{
1201
0
  GList *l;
1202
1203
0
  g_return_if_fail(presence    != NULL);
1204
0
  g_return_if_fail(source_list != NULL);
1205
1206
0
  for (l = source_list; l != NULL; l = l->next)
1207
0
    purple_presence_add_status(presence, (PurpleStatus *)l->data);
1208
0
}
1209
1210
void
1211
purple_presence_set_status_active(PurplePresence *presence, const char *status_id,
1212
    gboolean active)
1213
0
{
1214
0
  PurpleStatus *status;
1215
1216
0
  g_return_if_fail(presence  != NULL);
1217
0
  g_return_if_fail(status_id != NULL);
1218
1219
0
  status = purple_presence_get_status(presence, status_id);
1220
1221
0
  g_return_if_fail(status != NULL);
1222
  /* TODO: Should we do the following? */
1223
  /* g_return_if_fail(active == status->active); */
1224
1225
0
  if (purple_status_is_exclusive(status))
1226
0
  {
1227
0
    if (!active)
1228
0
    {
1229
0
      purple_debug_warning("status",
1230
0
          "Attempted to set a non-independent status "
1231
0
          "(%s) inactive. Only independent statuses "
1232
0
          "can be specifically marked inactive.",
1233
0
          status_id);
1234
0
      return;
1235
0
    }
1236
0
  }
1237
1238
0
  purple_status_set_active(status, active);
1239
0
}
1240
1241
void
1242
purple_presence_switch_status(PurplePresence *presence, const char *status_id)
1243
0
{
1244
0
  purple_presence_set_status_active(presence, status_id, TRUE);
1245
0
}
1246
1247
static void
1248
update_buddy_idle(PurpleBuddy *buddy, PurplePresence *presence,
1249
    time_t current_time, gboolean old_idle, gboolean idle)
1250
0
{
1251
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1252
0
  PurpleAccount *account = purple_buddy_get_account(buddy);
1253
1254
0
  if (!old_idle && idle)
1255
0
  {
1256
0
    if (purple_prefs_get_bool("/purple/logging/log_system"))
1257
0
    {
1258
0
      PurpleLog *log = purple_account_get_log(account, FALSE);
1259
1260
0
      if (log != NULL)
1261
0
      {
1262
0
        char *tmp, *tmp2;
1263
0
        tmp = g_strdup_printf(_("%s became idle"),
1264
0
        purple_buddy_get_alias(buddy));
1265
0
        tmp2 = g_markup_escape_text(tmp, -1);
1266
0
        g_free(tmp);
1267
1268
0
        purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
1269
0
        purple_buddy_get_alias(buddy), current_time, tmp2);
1270
0
        g_free(tmp2);
1271
0
      }
1272
0
    }
1273
0
  }
1274
0
  else if (old_idle && !idle)
1275
0
  {
1276
0
    if (purple_prefs_get_bool("/purple/logging/log_system"))
1277
0
    {
1278
0
      PurpleLog *log = purple_account_get_log(account, FALSE);
1279
1280
0
      if (log != NULL)
1281
0
      {
1282
0
        char *tmp, *tmp2;
1283
0
        tmp = g_strdup_printf(_("%s became unidle"),
1284
0
        purple_buddy_get_alias(buddy));
1285
0
        tmp2 = g_markup_escape_text(tmp, -1);
1286
0
        g_free(tmp);
1287
1288
0
        purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
1289
0
        purple_buddy_get_alias(buddy), current_time, tmp2);
1290
0
        g_free(tmp2);
1291
0
      }
1292
0
    }
1293
0
  }
1294
1295
0
  if (old_idle != idle)
1296
0
    purple_signal_emit(purple_blist_get_handle(), "buddy-idle-changed", buddy,
1297
0
                     old_idle, idle);
1298
1299
0
  purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
1300
1301
  /* Should this be done here? It'd perhaps make more sense to
1302
   * connect to buddy-[un]idle signals and update from there
1303
   */
1304
1305
0
  if (ops != NULL && ops->update != NULL)
1306
0
    ops->update(purple_get_blist(), (PurpleBlistNode *)buddy);
1307
0
}
1308
1309
void
1310
purple_presence_set_idle(PurplePresence *presence, gboolean idle, time_t idle_time)
1311
0
{
1312
0
  gboolean old_idle;
1313
0
  time_t current_time;
1314
1315
0
  g_return_if_fail(presence != NULL);
1316
1317
0
  if (presence->idle == idle && presence->idle_time == idle_time)
1318
0
    return;
1319
1320
0
  old_idle            = presence->idle;
1321
0
  presence->idle      = idle;
1322
0
  presence->idle_time = (idle ? idle_time : 0);
1323
1324
0
  current_time = time(NULL);
1325
1326
0
  if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_BUDDY)
1327
0
  {
1328
0
    update_buddy_idle(purple_presence_get_buddy(presence), presence, current_time,
1329
0
                      old_idle, idle);
1330
0
  }
1331
0
  else if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_ACCOUNT)
1332
0
  {
1333
0
    PurpleAccount *account;
1334
0
    PurpleConnection *gc = NULL;
1335
0
    PurplePlugin *prpl = NULL;
1336
0
    PurplePluginProtocolInfo *prpl_info = NULL;
1337
1338
0
    account = purple_presence_get_account(presence);
1339
1340
0
    if (purple_prefs_get_bool("/purple/logging/log_system"))
1341
0
    {
1342
0
      PurpleLog *log = purple_account_get_log(account, FALSE);
1343
1344
0
      if (log != NULL)
1345
0
      {
1346
0
        char *msg, *tmp;
1347
1348
0
        if (idle)
1349
0
          tmp = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
1350
0
        else
1351
0
          tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
1352
1353
0
        msg = g_markup_escape_text(tmp, -1);
1354
0
        g_free(tmp);
1355
0
        purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
1356
0
                         purple_account_get_username(account),
1357
0
                         (idle ? idle_time : current_time), msg);
1358
0
        g_free(msg);
1359
0
      }
1360
0
    }
1361
1362
0
    gc = purple_account_get_connection(account);
1363
1364
0
    if(gc)
1365
0
      prpl = purple_connection_get_prpl(gc);
1366
1367
0
    if(PURPLE_CONNECTION_IS_CONNECTED(gc) && prpl != NULL)
1368
0
      prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
1369
1370
0
    if (prpl_info && prpl_info->set_idle)
1371
0
      prpl_info->set_idle(gc, (idle ? (current_time - idle_time) : 0));
1372
0
  }
1373
0
}
1374
1375
void
1376
purple_presence_set_login_time(PurplePresence *presence, time_t login_time)
1377
0
{
1378
0
  g_return_if_fail(presence != NULL);
1379
1380
0
  if (presence->login_time == login_time)
1381
0
    return;
1382
1383
0
  presence->login_time = login_time;
1384
0
}
1385
1386
PurplePresenceContext
1387
purple_presence_get_context(const PurplePresence *presence)
1388
0
{
1389
0
  g_return_val_if_fail(presence != NULL, PURPLE_PRESENCE_CONTEXT_UNSET);
1390
1391
0
  return presence->context;
1392
0
}
1393
1394
PurpleAccount *
1395
purple_presence_get_account(const PurplePresence *presence)
1396
0
{
1397
0
  PurplePresenceContext context;
1398
1399
0
  g_return_val_if_fail(presence != NULL, NULL);
1400
1401
0
  context = purple_presence_get_context(presence);
1402
1403
0
  g_return_val_if_fail(context == PURPLE_PRESENCE_CONTEXT_ACCOUNT ||
1404
0
      context == PURPLE_PRESENCE_CONTEXT_BUDDY, NULL);
1405
1406
0
  return presence->u.account;
1407
0
}
1408
1409
PurpleConversation *
1410
purple_presence_get_conversation(const PurplePresence *presence)
1411
0
{
1412
0
  g_return_val_if_fail(presence != NULL, NULL);
1413
0
  g_return_val_if_fail(purple_presence_get_context(presence) ==
1414
0
      PURPLE_PRESENCE_CONTEXT_CONV, NULL);
1415
1416
0
  return presence->u.chat.conv;
1417
0
}
1418
1419
const char *
1420
purple_presence_get_chat_user(const PurplePresence *presence)
1421
0
{
1422
0
  g_return_val_if_fail(presence != NULL, NULL);
1423
0
  g_return_val_if_fail(purple_presence_get_context(presence) ==
1424
0
      PURPLE_PRESENCE_CONTEXT_CONV, NULL);
1425
1426
0
  return presence->u.chat.user;
1427
0
}
1428
1429
PurpleBuddy *
1430
purple_presence_get_buddy(const PurplePresence *presence)
1431
0
{
1432
0
  g_return_val_if_fail(presence != NULL, NULL);
1433
0
  g_return_val_if_fail(purple_presence_get_context(presence) ==
1434
0
      PURPLE_PRESENCE_CONTEXT_BUDDY, NULL);
1435
1436
0
  return presence->u.buddy.buddy;
1437
0
}
1438
1439
GList *
1440
purple_presence_get_statuses(const PurplePresence *presence)
1441
0
{
1442
0
  g_return_val_if_fail(presence != NULL, NULL);
1443
1444
0
  return presence->statuses;
1445
0
}
1446
1447
PurpleStatus *
1448
purple_presence_get_status(const PurplePresence *presence, const char *status_id)
1449
0
{
1450
0
  PurpleStatus *status;
1451
0
  GList *l = NULL;
1452
1453
0
  g_return_val_if_fail(presence  != NULL, NULL);
1454
0
  g_return_val_if_fail(status_id != NULL, NULL);
1455
1456
  /* What's the purpose of this hash table? */
1457
0
  status = (PurpleStatus *)g_hash_table_lookup(presence->status_table,
1458
0
               status_id);
1459
1460
0
  if (status == NULL) {
1461
0
    for (l = purple_presence_get_statuses(presence);
1462
0
       l != NULL && status == NULL; l = l->next)
1463
0
    {
1464
0
      PurpleStatus *temp_status = l->data;
1465
1466
0
      if (purple_strequal(status_id, purple_status_get_id(temp_status)))
1467
0
        status = temp_status;
1468
0
    }
1469
1470
0
    if (status != NULL)
1471
0
      g_hash_table_insert(presence->status_table,
1472
0
                g_strdup(purple_status_get_id(status)), status);
1473
0
  }
1474
1475
0
  return status;
1476
0
}
1477
1478
PurpleStatus *
1479
purple_presence_get_active_status(const PurplePresence *presence)
1480
0
{
1481
0
  g_return_val_if_fail(presence != NULL, NULL);
1482
1483
0
  return presence->active_status;
1484
0
}
1485
1486
gboolean
1487
purple_presence_is_available(const PurplePresence *presence)
1488
0
{
1489
0
  PurpleStatus *status;
1490
1491
0
  g_return_val_if_fail(presence != NULL, FALSE);
1492
1493
0
  status = purple_presence_get_active_status(presence);
1494
1495
0
  return ((status != NULL && purple_status_is_available(status)) &&
1496
0
      !purple_presence_is_idle(presence));
1497
0
}
1498
1499
gboolean
1500
purple_presence_is_online(const PurplePresence *presence)
1501
0
{
1502
0
  PurpleStatus *status;
1503
1504
0
  g_return_val_if_fail(presence != NULL, FALSE);
1505
1506
0
  if ((status = purple_presence_get_active_status(presence)) == NULL)
1507
0
    return FALSE;
1508
1509
0
  return purple_status_is_online(status);
1510
0
}
1511
1512
gboolean
1513
purple_presence_is_status_active(const PurplePresence *presence,
1514
    const char *status_id)
1515
0
{
1516
0
  PurpleStatus *status;
1517
1518
0
  g_return_val_if_fail(presence  != NULL, FALSE);
1519
0
  g_return_val_if_fail(status_id != NULL, FALSE);
1520
1521
0
  status = purple_presence_get_status(presence, status_id);
1522
1523
0
  return (status != NULL && purple_status_is_active(status));
1524
0
}
1525
1526
gboolean
1527
purple_presence_is_status_primitive_active(const PurplePresence *presence,
1528
    PurpleStatusPrimitive primitive)
1529
0
{
1530
0
  GList *l;
1531
1532
0
  g_return_val_if_fail(presence  != NULL,              FALSE);
1533
0
  g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, FALSE);
1534
1535
0
  for (l = purple_presence_get_statuses(presence);
1536
0
       l != NULL; l = l->next)
1537
0
  {
1538
0
    PurpleStatus *temp_status = l->data;
1539
0
    PurpleStatusType *type = purple_status_get_type(temp_status);
1540
1541
0
    if (purple_status_type_get_primitive(type) == primitive &&
1542
0
        purple_status_is_active(temp_status))
1543
0
      return TRUE;
1544
0
  }
1545
0
  return FALSE;
1546
0
}
1547
1548
gboolean
1549
purple_presence_is_idle(const PurplePresence *presence)
1550
0
{
1551
0
  g_return_val_if_fail(presence != NULL, FALSE);
1552
1553
0
  return purple_presence_is_online(presence) && presence->idle;
1554
0
}
1555
1556
time_t
1557
purple_presence_get_idle_time(const PurplePresence *presence)
1558
0
{
1559
0
  g_return_val_if_fail(presence != NULL, 0);
1560
1561
0
  return presence->idle_time;
1562
0
}
1563
1564
time_t
1565
purple_presence_get_login_time(const PurplePresence *presence)
1566
0
{
1567
0
  g_return_val_if_fail(presence != NULL, 0);
1568
1569
0
  return purple_presence_is_online(presence) ? presence->login_time : 0;
1570
0
}
1571
1572
static int
1573
purple_presence_compute_score(const PurplePresence *presence)
1574
0
{
1575
0
  GList *l;
1576
0
  int score = 0;
1577
1578
0
  for (l = purple_presence_get_statuses(presence); l != NULL; l = l->next) {
1579
0
    PurpleStatus *status = (PurpleStatus *)l->data;
1580
0
    PurpleStatusType *type = purple_status_get_type(status);
1581
1582
0
    if (purple_status_is_active(status)) {
1583
0
      score += primitive_scores[purple_status_type_get_primitive(type)];
1584
0
      if (!purple_status_is_online(status)) {
1585
0
        PurpleBuddy *b = purple_presence_get_buddy(presence);
1586
0
        if (b && purple_account_supports_offline_message(purple_buddy_get_account(b), b))
1587
0
          score += primitive_scores[SCORE_OFFLINE_MESSAGE];
1588
0
      }
1589
0
    }
1590
0
  }
1591
0
  score += purple_account_get_int(purple_presence_get_account(presence), "score", 0);
1592
0
  if (purple_presence_is_idle(presence))
1593
0
    score += primitive_scores[SCORE_IDLE];
1594
0
  return score;
1595
0
}
1596
1597
gint
1598
purple_presence_compare(const PurplePresence *presence1,
1599
    const PurplePresence *presence2)
1600
0
{
1601
0
  time_t idle_time_1, idle_time_2;
1602
0
  int score1 = 0, score2 = 0;
1603
1604
0
  if (presence1 == presence2)
1605
0
    return 0;
1606
0
  else if (presence1 == NULL)
1607
0
    return 1;
1608
0
  else if (presence2 == NULL)
1609
0
    return -1;
1610
1611
0
  if (purple_presence_is_online(presence1) &&
1612
0
      !purple_presence_is_online(presence2))
1613
0
    return -1;
1614
0
  else if (purple_presence_is_online(presence2) &&
1615
0
      !purple_presence_is_online(presence1))
1616
0
    return 1;
1617
1618
  /* Compute the score of the first set of statuses. */
1619
0
  score1 = purple_presence_compute_score(presence1);
1620
1621
  /* Compute the score of the second set of statuses. */
1622
0
  score2 = purple_presence_compute_score(presence2);
1623
1624
0
  idle_time_1 = time(NULL) - purple_presence_get_idle_time(presence1);
1625
0
  idle_time_2 = time(NULL) - purple_presence_get_idle_time(presence2);
1626
1627
0
  if (idle_time_1 > idle_time_2)
1628
0
    score1 += primitive_scores[SCORE_IDLE_TIME];
1629
0
  else if (idle_time_1 < idle_time_2)
1630
0
    score2 += primitive_scores[SCORE_IDLE_TIME];
1631
1632
0
  if (score1 < score2)
1633
0
    return 1;
1634
0
  else if (score1 > score2)
1635
0
    return -1;
1636
1637
0
  return 0;
1638
0
}
1639
1640
1641
/**************************************************************************
1642
* Status subsystem
1643
**************************************************************************/
1644
static void
1645
score_pref_changed_cb(const char *name, PurplePrefType type,
1646
            gconstpointer value, gpointer data)
1647
0
{
1648
0
  int index = GPOINTER_TO_INT(data);
1649
1650
0
  primitive_scores[index] = GPOINTER_TO_INT(value);
1651
0
}
1652
1653
void *
1654
0
purple_status_get_handle(void) {
1655
0
  static int handle;
1656
1657
0
  return &handle;
1658
0
}
1659
1660
void
1661
purple_status_init(void)
1662
0
{
1663
0
  void *handle = purple_status_get_handle();
1664
1665
0
  purple_prefs_add_none("/purple/status");
1666
0
  purple_prefs_add_none("/purple/status/scores");
1667
1668
0
  purple_prefs_add_int("/purple/status/scores/offline",
1669
0
      primitive_scores[PURPLE_STATUS_OFFLINE]);
1670
0
  purple_prefs_add_int("/purple/status/scores/available",
1671
0
      primitive_scores[PURPLE_STATUS_AVAILABLE]);
1672
0
  purple_prefs_add_int("/purple/status/scores/invisible",
1673
0
      primitive_scores[PURPLE_STATUS_INVISIBLE]);
1674
0
  purple_prefs_add_int("/purple/status/scores/away",
1675
0
      primitive_scores[PURPLE_STATUS_AWAY]);
1676
0
  purple_prefs_add_int("/purple/status/scores/extended_away",
1677
0
      primitive_scores[PURPLE_STATUS_EXTENDED_AWAY]);
1678
0
  purple_prefs_add_int("/purple/status/scores/idle",
1679
0
      primitive_scores[SCORE_IDLE]);
1680
0
  purple_prefs_add_int("/purple/status/scores/offline_msg",
1681
0
      primitive_scores[SCORE_OFFLINE_MESSAGE]);
1682
1683
0
  purple_prefs_connect_callback(handle, "/purple/status/scores/offline",
1684
0
      score_pref_changed_cb,
1685
0
      GINT_TO_POINTER(PURPLE_STATUS_OFFLINE));
1686
0
  purple_prefs_connect_callback(handle, "/purple/status/scores/available",
1687
0
      score_pref_changed_cb,
1688
0
      GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE));
1689
0
  purple_prefs_connect_callback(handle, "/purple/status/scores/invisible",
1690
0
      score_pref_changed_cb,
1691
0
      GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE));
1692
0
  purple_prefs_connect_callback(handle, "/purple/status/scores/away",
1693
0
      score_pref_changed_cb,
1694
0
      GINT_TO_POINTER(PURPLE_STATUS_AWAY));
1695
0
  purple_prefs_connect_callback(handle, "/purple/status/scores/extended_away",
1696
0
      score_pref_changed_cb,
1697
0
      GINT_TO_POINTER(PURPLE_STATUS_EXTENDED_AWAY));
1698
0
  purple_prefs_connect_callback(handle, "/purple/status/scores/idle",
1699
0
      score_pref_changed_cb,
1700
0
      GINT_TO_POINTER(SCORE_IDLE));
1701
0
  purple_prefs_connect_callback(handle, "/purple/status/scores/offline_msg",
1702
0
      score_pref_changed_cb,
1703
0
      GINT_TO_POINTER(SCORE_OFFLINE_MESSAGE));
1704
1705
0
  purple_prefs_trigger_callback("/purple/status/scores/offline");
1706
0
  purple_prefs_trigger_callback("/purple/status/scores/available");
1707
0
  purple_prefs_trigger_callback("/purple/status/scores/invisible");
1708
0
  purple_prefs_trigger_callback("/purple/status/scores/away");
1709
0
  purple_prefs_trigger_callback("/purple/status/scores/extended_away");
1710
0
  purple_prefs_trigger_callback("/purple/status/scores/idle");
1711
0
  purple_prefs_trigger_callback("/purple/status/scores/offline_msg");
1712
0
}
1713
1714
void
1715
purple_status_uninit(void)
1716
0
{
1717
0
  purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
1718
0
}