Coverage Report

Created: 2026-10-13 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pidgin/libpurple/prpl.c
Line
Count
Source
1
/*
2
 * purple
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 "conversation.h"
25
#include "debug.h"
26
#include "network.h"
27
#include "notify.h"
28
#include "prpl.h"
29
#include "request.h"
30
#include "util.h"
31
32
/**************************************************************************/
33
/** @name Attention Type API                                              */
34
/**************************************************************************/
35
PurpleAttentionType *
36
purple_attention_type_new(const char *ulname, const char *name,
37
            const char *inc_desc, const char *out_desc)
38
0
{
39
0
  PurpleAttentionType *attn = g_new0(PurpleAttentionType, 1);
40
41
0
  purple_attention_type_set_name(attn, name);
42
0
  purple_attention_type_set_incoming_desc(attn, inc_desc);
43
0
  purple_attention_type_set_outgoing_desc(attn, out_desc);
44
0
  purple_attention_type_set_unlocalized_name(attn, ulname);
45
46
0
  return attn;
47
0
}
48
49
50
void
51
purple_attention_type_set_name(PurpleAttentionType *type, const char *name)
52
0
{
53
0
  g_return_if_fail(type != NULL);
54
55
0
  type->name = name;
56
0
}
57
58
void
59
purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc)
60
0
{
61
0
  g_return_if_fail(type != NULL);
62
63
0
  type->incoming_description = desc;
64
0
}
65
66
void
67
purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc)
68
0
{
69
0
  g_return_if_fail(type != NULL);
70
71
0
  type->outgoing_description = desc;
72
0
}
73
74
void
75
purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name)
76
0
{
77
0
  g_return_if_fail(type != NULL);
78
79
0
  type->icon_name = name;
80
0
}
81
82
void
83
purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname)
84
0
{
85
0
  g_return_if_fail(type != NULL);
86
87
0
  type->unlocalized_name = ulname;
88
0
}
89
90
const char *
91
purple_attention_type_get_name(const PurpleAttentionType *type)
92
0
{
93
0
  g_return_val_if_fail(type != NULL, NULL);
94
95
0
  return type->name;
96
0
}
97
98
const char *
99
purple_attention_type_get_incoming_desc(const PurpleAttentionType *type)
100
0
{
101
0
  g_return_val_if_fail(type != NULL, NULL);
102
103
0
  return type->incoming_description;
104
0
}
105
106
const char *
107
purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type)
108
0
{
109
0
  g_return_val_if_fail(type != NULL, NULL);
110
111
0
  return type->outgoing_description;
112
0
}
113
114
const char *
115
purple_attention_type_get_icon_name(const PurpleAttentionType *type)
116
0
{
117
0
  g_return_val_if_fail(type != NULL, NULL);
118
119
0
  if(type->icon_name == NULL || *(type->icon_name) == '\0')
120
0
    return NULL;
121
122
0
  return type->icon_name;
123
0
}
124
125
const char *
126
purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type)
127
0
{
128
0
  g_return_val_if_fail(type != NULL, NULL);
129
130
0
  return type->unlocalized_name;
131
0
}
132
133
/**************************************************************************/
134
/** @name Protocol Plugin API  */
135
/**************************************************************************/
136
void
137
purple_prpl_got_account_idle(PurpleAccount *account, gboolean idle,
138
               time_t idle_time)
139
0
{
140
0
  g_return_if_fail(account != NULL);
141
0
  g_return_if_fail(purple_account_is_connected(account));
142
143
0
  purple_presence_set_idle(purple_account_get_presence(account),
144
0
               idle, idle_time);
145
0
}
146
147
void
148
purple_prpl_got_account_login_time(PurpleAccount *account, time_t login_time)
149
0
{
150
0
  PurplePresence *presence;
151
152
0
  g_return_if_fail(account != NULL);
153
0
  g_return_if_fail(purple_account_is_connected(account));
154
155
0
  if (login_time == 0)
156
0
    login_time = time(NULL);
157
158
0
  presence = purple_account_get_presence(account);
159
160
0
  purple_presence_set_login_time(presence, login_time);
161
0
}
162
163
void
164
purple_prpl_got_account_status(PurpleAccount *account, const char *status_id, ...)
165
0
{
166
0
  PurplePresence *presence;
167
0
  PurpleStatus *status;
168
0
  va_list args;
169
170
0
  g_return_if_fail(account   != NULL);
171
0
  g_return_if_fail(status_id != NULL);
172
0
  g_return_if_fail(purple_account_is_connected(account));
173
174
0
  presence = purple_account_get_presence(account);
175
0
  status   = purple_presence_get_status(presence, status_id);
176
177
0
  g_return_if_fail(status != NULL);
178
179
0
  va_start(args, status_id);
180
0
  purple_status_set_active_with_attrs(status, TRUE, args);
181
0
  va_end(args);
182
0
}
183
184
void
185
purple_prpl_got_account_actions(PurpleAccount *account)
186
0
{
187
188
0
  g_return_if_fail(account != NULL);
189
0
  g_return_if_fail(purple_account_is_connected(account));
190
191
0
  purple_signal_emit(purple_accounts_get_handle(), "account-actions-changed",
192
0
                     account);
193
0
}
194
195
void
196
purple_prpl_got_user_idle(PurpleAccount *account, const char *name,
197
    gboolean idle, time_t idle_time)
198
0
{
199
0
  PurplePresence *presence;
200
0
  GSList *list;
201
202
0
  g_return_if_fail(account != NULL);
203
0
  g_return_if_fail(name    != NULL);
204
0
  g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
205
206
0
  if ((list = purple_find_buddies(account, name)) == NULL)
207
0
    return;
208
209
0
  while (list) {
210
0
    presence = purple_buddy_get_presence(list->data);
211
0
    list = g_slist_delete_link(list, list);
212
0
    purple_presence_set_idle(presence, idle, idle_time);
213
0
  }
214
0
}
215
216
void
217
purple_prpl_got_user_login_time(PurpleAccount *account, const char *name,
218
    time_t login_time)
219
0
{
220
0
  GSList *list;
221
0
  PurplePresence *presence;
222
223
0
  g_return_if_fail(account != NULL);
224
0
  g_return_if_fail(name    != NULL);
225
226
0
  if ((list = purple_find_buddies(account, name)) == NULL)
227
0
    return;
228
229
0
  if (login_time == 0)
230
0
    login_time = time(NULL);
231
232
0
  while (list) {
233
0
    PurpleBuddy *buddy = list->data;
234
0
    presence = purple_buddy_get_presence(buddy);
235
0
    list = g_slist_delete_link(list, list);
236
237
0
    if (purple_presence_get_login_time(presence) != login_time)
238
0
    {
239
0
      purple_presence_set_login_time(presence, login_time);
240
241
0
      purple_signal_emit(purple_blist_get_handle(), "buddy-got-login-time", buddy);
242
0
    }
243
0
  }
244
0
}
245
246
void
247
purple_prpl_got_user_status(PurpleAccount *account, const char *name,
248
    const char *status_id, ...)
249
0
{
250
0
  GSList *list, *l;
251
0
  PurpleBuddy *buddy;
252
0
  PurplePresence *presence;
253
0
  PurpleStatus *status;
254
0
  PurpleStatus *old_status;
255
0
  va_list args;
256
257
0
  g_return_if_fail(account   != NULL);
258
0
  g_return_if_fail(name      != NULL);
259
0
  g_return_if_fail(status_id != NULL);
260
0
  g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
261
262
0
  if((list = purple_find_buddies(account, name)) == NULL)
263
0
    return;
264
265
0
  for(l = list; l != NULL; l = l->next) {
266
0
    buddy = l->data;
267
268
0
    presence = purple_buddy_get_presence(buddy);
269
0
    status   = purple_presence_get_status(presence, status_id);
270
271
0
    if(NULL == status)
272
      /*
273
       * TODO: This should never happen, right?  We should call
274
       *       g_warning() or something.
275
       */
276
0
      continue;
277
278
0
    old_status = purple_presence_get_active_status(presence);
279
280
0
    va_start(args, status_id);
281
0
    purple_status_set_active_with_attrs(status, TRUE, args);
282
0
    va_end(args);
283
284
0
    purple_blist_update_buddy_status(buddy, old_status);
285
0
  }
286
287
0
  g_slist_free(list);
288
289
  /* The buddy is no longer online, they are therefore by definition not
290
   * still typing to us. */
291
0
  if (!purple_status_is_online(status)) {
292
0
    serv_got_typing_stopped(purple_account_get_connection(account), name);
293
0
    purple_prpl_got_media_caps(account, name);
294
0
  }
295
0
}
296
297
void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *name,
298
          const char *status_id)
299
0
{
300
0
  GSList *list, *l;
301
0
  PurpleBuddy *buddy;
302
0
  PurplePresence *presence;
303
0
  PurpleStatus *status;
304
305
0
  g_return_if_fail(account   != NULL);
306
0
  g_return_if_fail(name      != NULL);
307
0
  g_return_if_fail(status_id != NULL);
308
0
  g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
309
310
0
  if((list = purple_find_buddies(account, name)) == NULL)
311
0
    return;
312
313
0
  for(l = list; l != NULL; l = l->next) {
314
0
    buddy = l->data;
315
316
0
    presence = purple_buddy_get_presence(buddy);
317
0
    status   = purple_presence_get_status(presence, status_id);
318
319
0
    if(NULL == status)
320
0
      continue;
321
322
0
    if (purple_status_is_active(status)) {
323
0
      purple_status_set_active(status, FALSE);
324
0
      purple_blist_update_buddy_status(buddy, status);
325
0
    }
326
0
  }
327
328
0
  g_slist_free(list);
329
0
}
330
331
static void
332
do_prpl_change_account_status(PurpleAccount *account,
333
                PurpleStatus *old_status, PurpleStatus *new_status)
334
0
{
335
0
  PurplePlugin *prpl;
336
0
  PurplePluginProtocolInfo *prpl_info;
337
338
0
  if (purple_status_is_online(new_status) &&
339
0
    purple_account_is_disconnected(account) &&
340
0
    purple_network_is_available())
341
0
  {
342
0
    purple_account_connect(account);
343
0
    return;
344
0
  }
345
346
0
  if (!purple_status_is_online(new_status))
347
0
  {
348
0
    if (!purple_account_is_disconnected(account))
349
0
      purple_account_disconnect(account);
350
    /* Clear out the unsaved password if we're already disconnected and we switch to offline status */
351
0
    else if (!purple_account_get_remember_password(account))
352
0
      purple_account_set_password(account, NULL);
353
0
    return;
354
0
  }
355
356
0
  if (purple_account_is_connecting(account))
357
    /*
358
     * We don't need to call the set_status PRPL function because
359
     * the PRPL will take care of setting its status during the
360
     * connection process.
361
     */
362
0
    return;
363
364
0
  prpl = purple_find_prpl(purple_account_get_protocol_id(account));
365
366
0
  if (prpl == NULL)
367
0
    return;
368
369
0
  prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
370
371
0
  if (!purple_account_is_disconnected(account) && prpl_info->set_status != NULL)
372
0
  {
373
0
    prpl_info->set_status(account, new_status);
374
0
  }
375
0
}
376
377
void
378
purple_prpl_change_account_status(PurpleAccount *account,
379
                PurpleStatus *old_status, PurpleStatus *new_status)
380
0
{
381
0
  g_return_if_fail(account    != NULL);
382
0
  g_return_if_fail(new_status != NULL);
383
0
  g_return_if_fail(!purple_status_is_exclusive(new_status) || old_status != NULL);
384
385
0
  purple_signal_emit(purple_accounts_get_handle(), "account-status-changing",
386
0
          account, old_status, new_status);
387
388
0
  do_prpl_change_account_status(account, old_status, new_status);
389
390
0
  purple_signal_emit(purple_accounts_get_handle(), "account-status-changed",
391
0
          account, old_status, new_status);
392
0
}
393
394
GList *
395
purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence)
396
0
{
397
0
  GList *statuses = NULL;
398
0
  GList *l;
399
0
  PurpleStatus *status;
400
401
0
  g_return_val_if_fail(account  != NULL, NULL);
402
0
  g_return_val_if_fail(presence != NULL, NULL);
403
404
0
  for (l = purple_account_get_status_types(account); l != NULL; l = l->next)
405
0
  {
406
0
    status = purple_status_new((PurpleStatusType *)l->data, presence);
407
0
    statuses = g_list_prepend(statuses, status);
408
0
  }
409
410
0
  statuses = g_list_reverse(statuses);
411
412
0
  return statuses;
413
0
}
414
415
static void
416
purple_prpl_attention(PurpleConversation *conv, const char *who,
417
  guint type, PurpleMessageFlags flags, time_t mtime)
418
0
{
419
0
  PurpleAccount *account = purple_conversation_get_account(conv);
420
0
  purple_signal_emit(purple_conversations_get_handle(),
421
0
    flags == PURPLE_MESSAGE_SEND ? "sent-attention" : "got-attention",
422
0
    account, who, conv, type);
423
0
}
424
425
void
426
purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code)
427
0
{
428
0
  PurpleAttentionType *attn;
429
0
  PurpleMessageFlags flags;
430
0
  PurplePlugin *prpl;
431
0
  PurpleConversation *conv;
432
0
  gboolean (*send_attention)(PurpleConnection *, const char *, guint);
433
0
  PurpleBuddy *buddy;
434
0
  const char *alias;
435
0
  gchar *description;
436
0
  time_t mtime;
437
438
0
  g_return_if_fail(gc != NULL);
439
0
  g_return_if_fail(who != NULL);
440
441
0
  prpl = purple_find_prpl(purple_account_get_protocol_id(gc->account));
442
0
  send_attention = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention;
443
0
  g_return_if_fail(send_attention != NULL);
444
445
0
  mtime = time(NULL);
446
447
0
  attn = purple_get_attention_type_from_code(gc->account, type_code);
448
449
0
  if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL)
450
0
    alias = purple_buddy_get_contact_alias(buddy);
451
0
  else
452
0
    alias = who;
453
454
0
  if (attn && purple_attention_type_get_outgoing_desc(attn)) {
455
0
    description = g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias);
456
0
  } else {
457
0
    description = g_strdup_printf(_("Requesting %s's attention..."), alias);
458
0
  }
459
460
0
  flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM;
461
462
0
  purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n",
463
0
      description, who);
464
465
0
  if (!send_attention(gc, who, type_code))
466
0
    return;
467
468
0
  conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who);
469
0
  purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime);
470
0
  purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_SEND, time(NULL));
471
472
0
  g_free(description);
473
0
}
474
475
static void
476
got_attention(PurpleConnection *gc, int id, const char *who, guint type_code)
477
0
{
478
0
  PurpleMessageFlags flags;
479
0
  PurpleAttentionType *attn;
480
0
  PurpleBuddy *buddy;
481
0
  const char *alias;
482
0
  gchar *description;
483
0
  time_t mtime;
484
485
0
  mtime = time(NULL);
486
487
0
  attn = purple_get_attention_type_from_code(gc->account, type_code);
488
489
  /* PURPLE_MESSAGE_NOTIFY is for attention messages. */
490
0
  flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV;
491
492
  /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display
493
   * it next to the attention command. And if it is null, display a generic icon. */
494
495
0
  if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL)
496
0
    alias = purple_buddy_get_contact_alias(buddy);
497
0
  else
498
0
    alias = who;
499
500
0
  if (attn && purple_attention_type_get_incoming_desc(attn)) {
501
0
    description = g_strdup_printf(purple_attention_type_get_incoming_desc(attn), alias);
502
0
  } else {
503
0
    description = g_strdup_printf(_("%s has requested your attention!"), alias);
504
0
  }
505
506
0
  purple_debug_info("server", "got_attention: got '%s' from %s\n",
507
0
      description, who);
508
509
0
  if (id == -1)
510
0
    serv_got_im(gc, who, description, flags, mtime);
511
0
  else
512
0
    serv_got_chat_in(gc, id, who, flags, description, mtime);
513
514
  /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */
515
516
0
  g_free(description);
517
0
}
518
519
void
520
purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code)
521
0
{
522
0
  PurpleConversation *conv = NULL;
523
0
  PurpleAccount *account = purple_connection_get_account(gc);
524
525
0
  got_attention(gc, -1, who, type_code);
526
0
  conv =
527
0
    purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
528
0
  if (conv)
529
0
    purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_RECV,
530
0
      time(NULL));
531
0
}
532
533
void
534
purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code)
535
0
{
536
0
  got_attention(gc, id, who, type_code);
537
0
}
538
539
gboolean
540
purple_prpl_initiate_media(PurpleAccount *account,
541
         const char *who,
542
         PurpleMediaSessionType type)
543
0
{
544
#ifdef USE_VV
545
  PurpleConnection *gc = NULL;
546
  PurplePlugin *prpl = NULL;
547
  PurplePluginProtocolInfo *prpl_info = NULL;
548
549
  if (account)
550
    gc = purple_account_get_connection(account);
551
  if (gc)
552
    prpl = purple_connection_get_prpl(gc);
553
  if (prpl)
554
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
555
556
  if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, initiate_media)) {
557
    /* should check that the protocol supports this media type here? */
558
    return prpl_info->initiate_media(account, who, type);
559
  } else
560
#endif
561
0
  return FALSE;
562
0
}
563
564
PurpleMediaCaps
565
purple_prpl_get_media_caps(PurpleAccount *account, const char *who)
566
0
{
567
#ifdef USE_VV
568
  PurpleConnection *gc = NULL;
569
  PurplePlugin *prpl = NULL;
570
  PurplePluginProtocolInfo *prpl_info = NULL;
571
572
  if (account)
573
    gc = purple_account_get_connection(account);
574
  if (gc)
575
    prpl = purple_connection_get_prpl(gc);
576
  if (prpl)
577
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
578
579
  if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info,
580
      get_media_caps)) {
581
    return prpl_info->get_media_caps(account, who);
582
  }
583
#endif
584
0
  return PURPLE_MEDIA_CAPS_NONE;
585
0
}
586
587
void
588
purple_prpl_got_media_caps(PurpleAccount *account, const char *name)
589
0
{
590
#ifdef USE_VV
591
  GSList *list;
592
593
  g_return_if_fail(account != NULL);
594
  g_return_if_fail(name    != NULL);
595
596
  if ((list = purple_find_buddies(account, name)) == NULL)
597
    return;
598
599
  while (list) {
600
    PurpleBuddy *buddy = list->data;
601
    PurpleMediaCaps oldcaps = purple_buddy_get_media_caps(buddy);
602
    PurpleMediaCaps newcaps = 0;
603
    const gchar *bname = purple_buddy_get_name(buddy);
604
    list = g_slist_delete_link(list, list);
605
606
607
    newcaps = purple_prpl_get_media_caps(account, bname);
608
    purple_buddy_set_media_caps(buddy, newcaps);
609
610
    if (oldcaps == newcaps)
611
      continue;
612
613
    purple_signal_emit(purple_blist_get_handle(),
614
        "buddy-caps-changed", buddy,
615
        newcaps, oldcaps);
616
  }
617
#endif
618
0
}
619
620
/**************************************************************************
621
 * Protocol Plugin Subsystem API
622
 **************************************************************************/
623
624
PurplePlugin *
625
purple_find_prpl(const char *id)
626
0
{
627
0
  GList *l;
628
0
  PurplePlugin *plugin;
629
630
0
  g_return_val_if_fail(id != NULL, NULL);
631
632
  /* libpurple3 compatibility.
633
   * prpl-xmpp isn't used yet (it's prpl-jabber),
634
   * but may be used in the future.
635
   */
636
0
  if (purple_strequal(id, "prpl-xmpp") ||
637
0
    purple_strequal(id, "prpl-gtalk"))
638
0
  {
639
0
    id = "prpl-jabber";
640
0
  }
641
642
0
  for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) {
643
0
    plugin = (PurplePlugin *)l->data;
644
645
0
    if (purple_strequal(plugin->info->id, id))
646
0
      return plugin;
647
0
  }
648
649
0
  return NULL;
650
0
}