Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pidgin/libpurple/server.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
24
/* This file is the fullcrap */
25
26
#include "internal.h"
27
#include "blist.h"
28
#include "conversation.h"
29
#include "debug.h"
30
#include "log.h"
31
#include "notify.h"
32
#include "prefs.h"
33
#include "privacy.h"
34
#include "prpl.h"
35
#include "request.h"
36
#include "signals.h"
37
#include "server.h"
38
#include "status.h"
39
#include "util.h"
40
41
0
#define SECS_BEFORE_RESENDING_AUTORESPONSE 600
42
#define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married"
43
44
unsigned int
45
serv_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
46
0
{
47
0
  PurplePlugin *prpl;
48
0
  PurplePluginProtocolInfo *prpl_info;
49
50
0
  if (gc) {
51
0
    prpl = purple_connection_get_prpl(gc);
52
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
53
54
0
    if (prpl_info->send_typing)
55
0
      return prpl_info->send_typing(gc, name, state);
56
0
  }
57
58
0
  return 0;
59
0
}
60
61
static GSList *last_auto_responses = NULL;
62
struct last_auto_response {
63
  PurpleConnection *gc;
64
  char name[80];
65
  time_t sent;
66
};
67
68
static gboolean
69
expire_last_auto_responses(gpointer data)
70
0
{
71
0
  GSList *tmp, *cur;
72
0
  struct last_auto_response *lar;
73
74
0
  tmp = last_auto_responses;
75
76
0
  while (tmp) {
77
0
    cur = tmp;
78
0
    tmp = tmp->next;
79
0
    lar = (struct last_auto_response *)cur->data;
80
81
0
    if ((time(NULL) - lar->sent) > SECS_BEFORE_RESENDING_AUTORESPONSE) {
82
0
      last_auto_responses = g_slist_remove(last_auto_responses, lar);
83
0
      g_free(lar);
84
0
    }
85
0
  }
86
87
0
  return FALSE; /* do not run again */
88
0
}
89
90
static struct last_auto_response *
91
get_last_auto_response(PurpleConnection *gc, const char *name)
92
0
{
93
0
  GSList *tmp;
94
0
  struct last_auto_response *lar;
95
96
  /* because we're modifying or creating a lar, schedule the
97
   * function to expire them as the pref dictates */
98
0
  purple_timeout_add_seconds((SECS_BEFORE_RESENDING_AUTORESPONSE + 1), expire_last_auto_responses, NULL);
99
100
0
  tmp = last_auto_responses;
101
102
0
  while (tmp) {
103
0
    lar = (struct last_auto_response *)tmp->data;
104
105
0
    if (gc == lar->gc && !strncmp(name, lar->name, sizeof(lar->name)))
106
0
      return lar;
107
108
0
    tmp = tmp->next;
109
0
  }
110
111
0
  lar = (struct last_auto_response *)g_new0(struct last_auto_response, 1);
112
0
  g_snprintf(lar->name, sizeof(lar->name), "%s", name);
113
0
  lar->gc = gc;
114
0
  lar->sent = 0;
115
0
  last_auto_responses = g_slist_prepend(last_auto_responses, lar);
116
117
0
  return lar;
118
0
}
119
120
int serv_send_im(PurpleConnection *gc, const char *name, const char *message,
121
         PurpleMessageFlags flags)
122
0
{
123
0
  PurpleConversation *conv = NULL;
124
0
  PurpleAccount *account = NULL;
125
0
  PurplePresence *presence = NULL;
126
0
  PurplePlugin *prpl = NULL;
127
0
  PurplePluginProtocolInfo *prpl_info = NULL;
128
0
  int val = -EINVAL;
129
0
  const gchar *auto_reply_pref = NULL;
130
131
0
  g_return_val_if_fail(gc != NULL, val);
132
133
0
  prpl = purple_connection_get_prpl(gc);
134
135
0
  g_return_val_if_fail(prpl != NULL, val);
136
137
0
  prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
138
139
0
  account  = purple_connection_get_account(gc);
140
0
  presence = purple_account_get_presence(account);
141
142
0
  conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
143
144
0
  if (prpl_info->send_im)
145
0
    val = prpl_info->send_im(gc, name, message, flags);
146
147
  /*
148
   * XXX - If "only auto-reply when away & idle" is set, then shouldn't
149
   * this only reset lar->sent if we're away AND idle?
150
   */
151
0
  auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
152
0
  if((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
153
0
      !purple_presence_is_available(presence) &&
154
0
      !purple_strequal(auto_reply_pref, "never")) {
155
156
0
    struct last_auto_response *lar;
157
0
    lar = get_last_auto_response(gc, name);
158
0
    lar->sent = time(NULL);
159
0
  }
160
161
0
  if(conv && purple_conv_im_get_send_typed_timeout(PURPLE_CONV_IM(conv)))
162
0
    purple_conv_im_stop_send_typed_timeout(PURPLE_CONV_IM(conv));
163
164
0
  return val;
165
0
}
166
167
void serv_get_info(PurpleConnection *gc, const char *name)
168
0
{
169
0
  PurplePlugin *prpl;
170
0
  PurplePluginProtocolInfo *prpl_info;
171
172
0
  if (gc) {
173
0
    prpl = purple_connection_get_prpl(gc);
174
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
175
176
0
    if (prpl_info->get_info)
177
0
      prpl_info->get_info(gc, name);
178
0
  }
179
0
}
180
181
void serv_set_info(PurpleConnection *gc, const char *info)
182
0
{
183
0
  PurplePlugin *prpl;
184
0
  PurplePluginProtocolInfo *prpl_info;
185
0
  PurpleAccount *account;
186
187
0
  if (gc) {
188
0
    prpl = purple_connection_get_prpl(gc);
189
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
190
191
0
    if (prpl_info->set_info) {
192
0
      account = purple_connection_get_account(gc);
193
194
0
      if (purple_signal_emit_return_1(purple_accounts_get_handle(),
195
0
          "account-setting-info", account, info))
196
0
        return;
197
198
0
      prpl_info->set_info(gc, info);
199
200
0
      purple_signal_emit(purple_accounts_get_handle(),
201
0
          "account-set-info", account, info);
202
0
    }
203
0
  }
204
0
}
205
206
/*
207
 * Set buddy's alias on server roster/list
208
 */
209
void serv_alias_buddy(PurpleBuddy *b)
210
0
{
211
0
  PurpleAccount *account;
212
0
  PurpleConnection *gc;
213
0
  PurplePlugin *prpl;
214
0
  PurplePluginProtocolInfo *prpl_info;
215
216
0
  if (b) {
217
0
    account = purple_buddy_get_account(b);
218
219
0
    if (account) {
220
0
      gc = purple_account_get_connection(account);
221
222
0
      if (gc) {
223
0
        prpl = purple_connection_get_prpl(gc);
224
0
        prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
225
226
0
        if (prpl_info->alias_buddy)
227
0
          prpl_info->alias_buddy(gc,
228
0
              purple_buddy_get_name(b),
229
0
              purple_buddy_get_local_buddy_alias(b));
230
0
      }
231
0
    }
232
0
  }
233
0
}
234
235
void
236
serv_got_alias(PurpleConnection *gc, const char *who, const char *alias)
237
0
{
238
0
  PurpleAccount *account;
239
0
  GSList *buddies;
240
0
  PurpleBuddy *b;
241
0
  PurpleConversation *conv;
242
243
0
  account = purple_connection_get_account(gc);
244
0
  buddies = purple_find_buddies(account, who);
245
246
0
  while (buddies != NULL)
247
0
  {
248
0
    const char *server_alias;
249
250
0
    b = buddies->data;
251
0
    buddies = g_slist_delete_link(buddies, buddies);
252
253
0
    server_alias = purple_buddy_get_server_alias(b);
254
255
0
    if (purple_strequal(server_alias, alias))
256
0
      continue;
257
258
0
    purple_blist_server_alias_buddy(b, alias);
259
260
0
    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, purple_buddy_get_name(b), account);
261
0
    if (conv != NULL && alias != NULL && !purple_strequal(alias, who))
262
0
    {
263
0
      char *escaped = g_markup_escape_text(who, -1);
264
0
      char *escaped2 = g_markup_escape_text(alias, -1);
265
0
      char *tmp = g_strdup_printf(_("%s is now known as %s.\n"),
266
0
                    escaped, escaped2);
267
268
0
      purple_conversation_write(conv, NULL, tmp,
269
0
          PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
270
0
          time(NULL));
271
272
0
      g_free(tmp);
273
0
      g_free(escaped2);
274
0
      g_free(escaped);
275
0
    }
276
0
  }
277
0
}
278
279
void
280
purple_serv_got_private_alias(PurpleConnection *gc, const char *who, const char *alias)
281
0
{
282
0
  PurpleAccount *account = NULL;
283
0
  GSList *buddies = NULL;
284
0
  PurpleBuddy *b = NULL;
285
286
0
  account = purple_connection_get_account(gc);
287
0
  buddies = purple_find_buddies(account, who);
288
289
0
  while(buddies != NULL) {
290
0
    const char *balias;
291
0
    b = buddies->data;
292
293
0
    buddies = g_slist_delete_link(buddies, buddies);
294
295
0
    balias = purple_buddy_get_local_buddy_alias(b);
296
0
    if (purple_strequal(balias, alias))
297
0
      continue;
298
299
0
    purple_blist_alias_buddy(b, alias);
300
0
  }
301
0
}
302
303
304
PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code)
305
0
{
306
0
  PurplePlugin *prpl;
307
0
  PurpleAttentionType* attn;
308
0
  GList *(*get_attention_types)(PurpleAccount *);
309
310
0
  g_return_val_if_fail(account != NULL, NULL);
311
312
0
  prpl = purple_find_prpl(purple_account_get_protocol_id(account));
313
314
  /* Lookup the attention type in the protocol's attention_types list, if any. */
315
0
  get_attention_types = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->get_attention_types;
316
0
  if (get_attention_types) {
317
0
    GList *attention_types;
318
319
0
    attention_types = get_attention_types(account);
320
0
    attn = (PurpleAttentionType *)g_list_nth_data(attention_types, type_code);
321
0
  } else {
322
0
    attn = NULL;
323
0
  }
324
325
0
  return attn;
326
0
}
327
328
void
329
serv_send_attention(PurpleConnection *gc, const char *who, guint type_code)
330
0
{
331
0
  purple_prpl_send_attention(gc, who, type_code);
332
0
}
333
334
void
335
serv_got_attention(PurpleConnection *gc, const char *who, guint type_code)
336
0
{
337
0
  purple_prpl_got_attention(gc, who, type_code);
338
0
}
339
340
341
/*
342
 * Move a buddy from one group to another on server.
343
 *
344
 * Note: For now we'll not deal with changing gc's at the same time, but
345
 * it should be possible.  Probably needs to be done, someday.  Although,
346
 * the UI for that would be difficult, because groups are Purple-wide.
347
 */
348
void serv_move_buddy(PurpleBuddy *b, PurpleGroup *og, PurpleGroup *ng)
349
0
{
350
0
  PurpleAccount *account;
351
0
  PurpleConnection *gc;
352
0
  PurplePlugin *prpl;
353
0
  PurplePluginProtocolInfo *prpl_info;
354
355
0
  g_return_if_fail(b != NULL);
356
0
  g_return_if_fail(og != NULL);
357
0
  g_return_if_fail(ng != NULL);
358
359
0
  account = purple_buddy_get_account(b);
360
0
  gc = purple_account_get_connection(account);
361
362
0
  if (gc) {
363
0
    prpl = purple_connection_get_prpl(gc);
364
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
365
366
0
    if (prpl_info->group_buddy)
367
0
      prpl_info->group_buddy(gc, purple_buddy_get_name(b),
368
0
          purple_group_get_name(og),
369
0
          purple_group_get_name(ng));
370
0
  }
371
0
}
372
373
void serv_add_permit(PurpleConnection *gc, const char *name)
374
0
{
375
0
  PurplePlugin *prpl;
376
0
  PurplePluginProtocolInfo *prpl_info;
377
378
0
  if (gc) {
379
0
    prpl = purple_connection_get_prpl(gc);
380
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
381
382
0
    if (prpl_info->add_permit)
383
0
      prpl_info->add_permit(gc, name);
384
0
  }
385
0
}
386
387
void serv_add_deny(PurpleConnection *gc, const char *name)
388
0
{
389
0
  PurplePlugin *prpl;
390
0
  PurplePluginProtocolInfo *prpl_info;
391
392
0
  if (gc) {
393
0
    prpl = purple_connection_get_prpl(gc);
394
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
395
396
0
    if (prpl_info->add_deny)
397
0
      prpl_info->add_deny(gc, name);
398
0
  }
399
0
}
400
401
void serv_rem_permit(PurpleConnection *gc, const char *name)
402
0
{
403
0
  PurplePlugin *prpl;
404
0
  PurplePluginProtocolInfo *prpl_info;
405
406
0
  if (gc) {
407
0
    prpl = purple_connection_get_prpl(gc);
408
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
409
410
0
    if (prpl_info->rem_permit)
411
0
      prpl_info->rem_permit(gc, name);
412
0
  }
413
0
}
414
415
void serv_rem_deny(PurpleConnection *gc, const char *name)
416
0
{
417
0
  PurplePlugin *prpl;
418
0
  PurplePluginProtocolInfo *prpl_info;
419
420
0
  if (gc) {
421
0
    prpl = purple_connection_get_prpl(gc);
422
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
423
424
0
    if (prpl_info->rem_deny)
425
0
      prpl_info->rem_deny(gc, name);
426
0
  }
427
0
}
428
429
void serv_set_permit_deny(PurpleConnection *gc)
430
0
{
431
0
  PurplePlugin *prpl;
432
0
  PurplePluginProtocolInfo *prpl_info;
433
434
0
  if (gc) {
435
0
    prpl = purple_connection_get_prpl(gc);
436
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
437
438
    /*
439
     * this is called when either you import a buddy list, and make lots
440
     * of changes that way, or when the user toggles the permit/deny mode
441
     * in the prefs. In either case you should probably be resetting and
442
     * resending the permit/deny info when you get this.
443
     */
444
0
    if (prpl_info->set_permit_deny)
445
0
      prpl_info->set_permit_deny(gc);
446
0
  }
447
0
}
448
449
void serv_join_chat(PurpleConnection *gc, GHashTable *data)
450
0
{
451
0
  PurplePlugin *prpl;
452
0
  PurplePluginProtocolInfo *prpl_info;
453
454
0
  if (gc) {
455
0
    prpl = purple_connection_get_prpl(gc);
456
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
457
458
0
    if (prpl_info->join_chat)
459
0
      prpl_info->join_chat(gc, data);
460
0
  }
461
0
}
462
463
464
void serv_reject_chat(PurpleConnection *gc, GHashTable *data)
465
0
{
466
0
  PurplePlugin *prpl;
467
0
  PurplePluginProtocolInfo *prpl_info;
468
469
0
  if (gc) {
470
0
    prpl = purple_connection_get_prpl(gc);
471
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
472
473
0
    if (prpl_info->reject_chat)
474
0
      prpl_info->reject_chat(gc, data);
475
0
  }
476
0
}
477
478
void serv_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
479
0
{
480
0
  PurplePlugin *prpl = NULL;
481
0
  PurplePluginProtocolInfo *prpl_info = NULL;
482
0
  PurpleConversation *conv;
483
0
  char *buffy = message && *message ? g_strdup(message) : NULL;
484
485
0
  conv = purple_find_chat(gc, id);
486
487
0
  if(conv == NULL)
488
0
    return;
489
490
0
  if(gc)
491
0
    prpl = purple_connection_get_prpl(gc);
492
493
0
  if(prpl)
494
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
495
496
0
  purple_signal_emit(purple_conversations_get_handle(), "chat-inviting-user",
497
0
           conv, name, &buffy);
498
499
0
  if (prpl_info && prpl_info->chat_invite)
500
0
    prpl_info->chat_invite(gc, id, buffy, name);
501
502
0
  purple_signal_emit(purple_conversations_get_handle(), "chat-invited-user",
503
0
           conv, name, buffy);
504
505
0
  g_free(buffy);
506
0
}
507
508
/* Ya know, nothing uses this except purple_conversation_destroy(),
509
 * I think I'll just merge it into that later...
510
 * Then again, something might want to use this, from outside prpl-land
511
 * to leave a chat without destroying the conversation.
512
 */
513
void serv_chat_leave(PurpleConnection *gc, int id)
514
0
{
515
0
  PurplePlugin *prpl;
516
0
  PurplePluginProtocolInfo *prpl_info;
517
518
0
  prpl = purple_connection_get_prpl(gc);
519
0
  prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
520
521
0
  if (prpl_info->chat_leave)
522
0
    prpl_info->chat_leave(gc, id);
523
0
}
524
525
void serv_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message)
526
0
{
527
0
  PurplePlugin *prpl;
528
0
  PurplePluginProtocolInfo *prpl_info;
529
530
0
  if (gc) {
531
0
    prpl = purple_connection_get_prpl(gc);
532
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
533
534
0
    if (prpl_info->chat_whisper)
535
0
      prpl_info->chat_whisper(gc, id, who, message);
536
0
  }
537
0
}
538
539
int serv_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
540
0
{
541
0
  PurplePlugin *prpl;
542
0
  PurplePluginProtocolInfo *prpl_info;
543
544
0
  prpl = purple_connection_get_prpl(gc);
545
0
  prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
546
547
0
  if (prpl_info->chat_send)
548
0
    return prpl_info->chat_send(gc, id, message, flags);
549
550
0
  return -EINVAL;
551
0
}
552
553
/*
554
 * woo. i'm actually going to comment this function. isn't that fun. make
555
 * sure to follow along, kids
556
 */
557
void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
558
         PurpleMessageFlags flags, time_t mtime)
559
0
{
560
0
  PurpleAccount *account;
561
0
  PurpleConversation *conv;
562
0
  char *message, *name;
563
0
  char *angel, *buffy;
564
0
  int plugin_return;
565
566
0
  g_return_if_fail(msg != NULL);
567
568
0
  account  = purple_connection_get_account(gc);
569
570
0
  if (mtime < 0) {
571
0
    purple_debug_error("server",
572
0
        "serv_got_im ignoring negative timestamp\n");
573
    /* TODO: Would be more appropriate to use a value that indicates
574
       that the timestamp is unknown, and surface that in the UI. */
575
0
    mtime = time(NULL);
576
0
  }
577
578
  /*
579
   * XXX: Should we be setting this here, or relying on prpls to set it?
580
   */
581
0
  flags |= PURPLE_MESSAGE_RECV;
582
583
0
  if (!purple_privacy_check(account, who)) {
584
0
    purple_signal_emit(purple_conversations_get_handle(), "blocked-im-msg",
585
0
        account, who, msg, flags, (unsigned int)mtime);
586
0
    return;
587
0
  }
588
589
  /*
590
   * We should update the conversation window buttons and menu,
591
   * if it exists.
592
   */
593
0
  conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, gc->account);
594
595
  /*
596
   * Make copies of the message and the sender in case plugins want
597
   * to free these strings and replace them with a modifed version.
598
   */
599
0
  buffy = g_strdup(msg);
600
0
  angel = g_strdup(who);
601
602
0
  plugin_return = GPOINTER_TO_INT(
603
0
    purple_signal_emit_return_1(purple_conversations_get_handle(),
604
0
                  "receiving-im-msg", gc->account,
605
0
                  &angel, &buffy, conv, &flags));
606
607
0
  if (!buffy || !angel || plugin_return) {
608
0
    g_free(buffy);
609
0
    g_free(angel);
610
0
    return;
611
0
  }
612
613
0
  name = angel;
614
0
  message = buffy;
615
616
0
  purple_signal_emit(purple_conversations_get_handle(), "received-im-msg", gc->account,
617
0
           name, message, conv, flags);
618
619
  /* search for conversation again in case it was created by received-im-msg handler */
620
0
  if (conv == NULL)
621
0
    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
622
623
0
  if (conv == NULL)
624
0
    conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
625
626
0
  purple_conv_im_write(PURPLE_CONV_IM(conv), name, message, flags, mtime);
627
0
  g_free(message);
628
629
  /*
630
   * Don't autorespond if:
631
   *
632
   *  - it's not supported on this connection
633
   *  - we are available
634
   *  - or it's disabled
635
   *  - or we're not idle and the 'only auto respond if idle' pref
636
   *    is set
637
   */
638
0
  if (gc->flags & PURPLE_CONNECTION_AUTO_RESP)
639
0
  {
640
0
    PurplePresence *presence;
641
0
    PurpleStatus *status;
642
0
    PurpleStatusType *status_type;
643
0
    PurpleStatusPrimitive primitive;
644
0
    const gchar *auto_reply_pref;
645
0
    const char *away_msg = NULL;
646
0
    gboolean mobile = FALSE;
647
648
0
    auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
649
650
0
    presence = purple_account_get_presence(account);
651
0
    status = purple_presence_get_active_status(presence);
652
0
    status_type = purple_status_get_type(status);
653
0
    primitive = purple_status_type_get_primitive(status_type);
654
0
    mobile = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE);
655
0
    if ((primitive == PURPLE_STATUS_AVAILABLE) ||
656
0
      (primitive == PURPLE_STATUS_INVISIBLE) ||
657
0
      mobile ||
658
0
        purple_strequal(auto_reply_pref, "never") ||
659
0
        (!purple_presence_is_idle(presence) && purple_strequal(auto_reply_pref, "awayidle")))
660
0
    {
661
0
      g_free(name);
662
0
      return;
663
0
    }
664
665
0
    away_msg = purple_value_get_string(
666
0
      purple_status_get_attr_value(status, "message"));
667
668
0
    if ((away_msg != NULL) && (*away_msg != '\0')) {
669
0
      struct last_auto_response *lar;
670
0
      time_t now = time(NULL);
671
672
      /*
673
       * This used to be based on the conversation window. But um, if
674
       * you went away, and someone sent you a message and got your
675
       * auto-response, and then you closed the window, and then they
676
       * sent you another one, they'd get the auto-response back too
677
       * soon. Besides that, we need to keep track of this even if we've
678
       * got a queue. So the rest of this block is just the auto-response,
679
       * if necessary.
680
       */
681
0
      lar = get_last_auto_response(gc, name);
682
0
      if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE)
683
0
      {
684
        /*
685
         * We don't want to send an autoresponse in response to the other user's
686
         * autoresponse.  We do, however, not want to then send one in response to the
687
         * _next_ message, so we still set lar->sent to now.
688
         */
689
0
        lar->sent = now;
690
691
0
        if (!(flags & PURPLE_MESSAGE_AUTO_RESP))
692
0
        {
693
0
          serv_send_im(gc, name, away_msg, PURPLE_MESSAGE_AUTO_RESP);
694
695
0
          purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, away_msg,
696
0
                     PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
697
0
                     mtime);
698
0
        }
699
0
      }
700
0
    }
701
0
  }
702
703
0
  g_free(name);
704
0
}
705
706
void serv_got_typing(PurpleConnection *gc, const char *name, int timeout,
707
0
           PurpleTypingState state) {
708
0
  PurpleConversation *conv;
709
0
  PurpleConvIm *im = NULL;
710
711
0
  conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
712
0
  if (conv != NULL) {
713
0
    im = PURPLE_CONV_IM(conv);
714
715
0
    purple_conv_im_set_typing_state(im, state);
716
0
  } else {
717
0
    switch (state)
718
0
    {
719
0
      case PURPLE_TYPING:
720
0
        purple_signal_emit(purple_conversations_get_handle(),
721
0
                   "buddy-typing", gc->account, name);
722
0
        break;
723
0
      case PURPLE_TYPED:
724
0
        purple_signal_emit(purple_conversations_get_handle(),
725
0
                   "buddy-typed", gc->account, name);
726
0
        break;
727
0
      case PURPLE_NOT_TYPING:
728
0
        purple_signal_emit(purple_conversations_get_handle(),
729
0
                   "buddy-typing-stopped", gc->account, name);
730
0
        break;
731
0
    }
732
0
  }
733
734
0
  if (conv != NULL && timeout > 0)
735
0
    purple_conv_im_start_typing_timeout(im, timeout);
736
0
}
737
738
0
void serv_got_typing_stopped(PurpleConnection *gc, const char *name) {
739
740
0
  PurpleConversation *conv;
741
0
  PurpleConvIm *im;
742
743
0
  conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
744
0
  if (conv != NULL)
745
0
  {
746
0
    im = PURPLE_CONV_IM(conv);
747
748
0
    if (im->typing_state == PURPLE_NOT_TYPING)
749
0
      return;
750
751
0
    purple_conv_im_stop_typing_timeout(im);
752
0
    purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
753
0
  }
754
0
  else
755
0
  {
756
0
    purple_signal_emit(purple_conversations_get_handle(),
757
0
             "buddy-typing-stopped", gc->account, name);
758
0
  }
759
0
}
760
761
struct chat_invite_data {
762
  PurpleConnection *gc;
763
  GHashTable *components;
764
};
765
766
static void chat_invite_data_free(struct chat_invite_data *cid)
767
0
{
768
0
  if (cid->components)
769
0
    g_hash_table_destroy(cid->components);
770
0
  g_free(cid);
771
0
}
772
773
774
static void chat_invite_reject(struct chat_invite_data *cid)
775
0
{
776
0
  serv_reject_chat(cid->gc, cid->components);
777
0
  chat_invite_data_free(cid);
778
0
}
779
780
781
static void chat_invite_accept(struct chat_invite_data *cid)
782
0
{
783
0
  serv_join_chat(cid->gc, cid->components);
784
0
  chat_invite_data_free(cid);
785
0
}
786
787
788
789
void serv_got_chat_invite(PurpleConnection *gc, const char *name,
790
              const char *who, const char *message, GHashTable *data)
791
0
{
792
0
  PurpleAccount *account;
793
0
  struct chat_invite_data *cid;
794
0
  int plugin_return;
795
796
0
  g_return_if_fail(name != NULL);
797
0
  g_return_if_fail(who != NULL);
798
799
0
  account = purple_connection_get_account(gc);
800
0
  if (!purple_privacy_check(account, who)) {
801
0
    purple_signal_emit(purple_conversations_get_handle(), "chat-invite-blocked",
802
0
        account, who, name, message, data);
803
0
    return;
804
0
  }
805
806
0
  cid = g_new0(struct chat_invite_data, 1);
807
808
0
  plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
809
0
          purple_conversations_get_handle(),
810
0
          "chat-invited", account, who, name, message, data));
811
812
0
  cid->gc = gc;
813
0
  cid->components = data;
814
815
0
  if (plugin_return == 0)
816
0
  {
817
0
    char *buf2;
818
819
0
    if (message != NULL)
820
0
    {
821
0
      buf2 = g_strdup_printf(
822
0
           _("%s has invited %s to the chat room %s:\n%s"),
823
0
           who, purple_account_get_username(account), name, message);
824
0
    }
825
0
    else
826
0
      buf2 = g_strdup_printf(
827
0
           _("%s has invited %s to the chat room %s\n"),
828
0
           who, purple_account_get_username(account), name);
829
830
831
0
    purple_request_accept_cancel(gc, NULL, _("Accept chat invitation?"), buf2,
832
0
                 PURPLE_DEFAULT_ACTION_NONE, account, who, NULL,
833
0
                 cid, G_CALLBACK(chat_invite_accept),
834
0
                 G_CALLBACK(chat_invite_reject));
835
836
0
    g_free(buf2);
837
0
  }
838
0
  else if (plugin_return > 0)
839
0
    chat_invite_accept(cid);
840
0
  else
841
0
    chat_invite_reject(cid);
842
0
}
843
844
PurpleConversation *serv_got_joined_chat(PurpleConnection *gc,
845
                         int id, const char *name)
846
0
{
847
0
  PurpleConversation *conv;
848
0
  PurpleConvChat *chat;
849
0
  PurpleAccount *account;
850
851
0
  account = purple_connection_get_account(gc);
852
853
0
  g_return_val_if_fail(account != NULL, NULL);
854
0
  g_return_val_if_fail(name != NULL, NULL);
855
856
0
  conv = purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name);
857
0
  g_return_val_if_fail(conv != NULL, NULL);
858
859
0
  chat = PURPLE_CONV_CHAT(conv);
860
861
0
  if (!g_slist_find(gc->buddy_chats, conv))
862
0
    gc->buddy_chats = g_slist_append(gc->buddy_chats, conv);
863
864
0
  purple_conv_chat_set_id(chat, id);
865
866
0
  purple_signal_emit(purple_conversations_get_handle(), "chat-joined", conv);
867
868
0
  return conv;
869
0
}
870
871
void serv_got_chat_left(PurpleConnection *g, int id)
872
0
{
873
0
  GSList *bcs;
874
0
  PurpleConversation *conv = NULL;
875
0
  PurpleConvChat *chat = NULL;
876
877
0
  for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
878
0
    conv = (PurpleConversation *)bcs->data;
879
880
0
    chat = PURPLE_CONV_CHAT(conv);
881
882
0
    if (purple_conv_chat_get_id(chat) == id)
883
0
      break;
884
885
0
    conv = NULL;
886
0
  }
887
888
0
  if (!conv)
889
0
    return;
890
891
0
  purple_debug(PURPLE_DEBUG_INFO, "server", "Leaving room: %s\n",
892
0
         purple_conversation_get_name(conv));
893
894
0
  g->buddy_chats = g_slist_remove(g->buddy_chats, conv);
895
896
0
  purple_conv_chat_left(PURPLE_CONV_CHAT(conv));
897
898
0
  purple_signal_emit(purple_conversations_get_handle(), "chat-left", conv);
899
0
}
900
901
void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data)
902
0
{
903
0
  purple_signal_emit(purple_conversations_get_handle(), "chat-join-failed",
904
0
          gc, data);
905
0
}
906
907
void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
908
            PurpleMessageFlags flags, const char *message, time_t mtime)
909
0
{
910
0
  GSList *bcs;
911
0
  PurpleConversation *conv = NULL;
912
0
  PurpleConvChat *chat = NULL;
913
0
  char *buffy, *angel;
914
0
  int plugin_return;
915
916
0
  g_return_if_fail(who != NULL);
917
0
  g_return_if_fail(message != NULL);
918
919
0
  if (mtime < 0) {
920
0
    purple_debug_error("server",
921
0
        "serv_got_chat_in ignoring negative timestamp\n");
922
    /* TODO: Would be more appropriate to use a value that indicates
923
       that the timestamp is unknown, and surface that in the UI. */
924
0
    mtime = time(NULL);
925
0
  }
926
927
0
  for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
928
0
    conv = (PurpleConversation *)bcs->data;
929
930
0
    chat = PURPLE_CONV_CHAT(conv);
931
932
0
    if (purple_conv_chat_get_id(chat) == id)
933
0
      break;
934
935
0
    conv = NULL;
936
0
  }
937
938
0
  if (!conv)
939
0
    return;
940
941
  /* Did I send the message? */
942
0
  if (purple_strequal(purple_conv_chat_get_nick(chat),
943
0
        purple_normalize(purple_conversation_get_account(conv), who))) {
944
0
    flags |= PURPLE_MESSAGE_SEND;
945
0
    flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some prpl sets it! */
946
0
  } else {
947
0
    flags |= PURPLE_MESSAGE_RECV;
948
0
  }
949
950
  /*
951
   * Make copies of the message and the sender in case plugins want
952
   * to free these strings and replace them with a modifed version.
953
   */
954
0
  buffy = g_strdup(message);
955
0
  angel = g_strdup(who);
956
957
0
  plugin_return = GPOINTER_TO_INT(
958
0
    purple_signal_emit_return_1(purple_conversations_get_handle(),
959
0
                  "receiving-chat-msg", g->account,
960
0
                  &angel, &buffy, conv, &flags));
961
962
0
  if (!buffy || !angel || plugin_return) {
963
0
    g_free(buffy);
964
0
    g_free(angel);
965
0
    return;
966
0
  }
967
968
0
  who = angel;
969
0
  message = buffy;
970
971
0
  purple_signal_emit(purple_conversations_get_handle(), "received-chat-msg", g->account,
972
0
           who, message, conv, flags);
973
974
0
  purple_conv_chat_write(chat, who, message, flags, mtime);
975
976
0
  g_free(angel);
977
0
  g_free(buffy);
978
0
}
979
980
void serv_send_file(PurpleConnection *gc, const char *who, const char *file)
981
0
{
982
0
  PurplePlugin *prpl;
983
0
  PurplePluginProtocolInfo *prpl_info;
984
985
0
  g_return_if_fail(gc != NULL);
986
987
0
  prpl = purple_connection_get_prpl(gc);
988
0
  prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
989
990
0
  if (prpl_info->send_file && (!prpl_info->can_receive_file
991
0
             || prpl_info->can_receive_file(gc, who))) {
992
0
    prpl_info->send_file(gc, who, file);
993
0
  }
994
0
}
995
996
void serv_chat_send_file(PurpleConnection *gc, int id, const char *file)
997
0
{
998
0
  PurplePlugin *prpl;
999
0
  PurplePluginProtocolInfo *prpl_info;
1000
1001
0
  g_return_if_fail(gc != NULL);
1002
1003
0
  prpl = purple_connection_get_prpl(gc);
1004
0
  prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
1005
1006
0
  if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_send_file) &&
1007
0
      (!PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_can_receive_file)
1008
0
       || prpl_info->chat_can_receive_file(gc, id))) {
1009
0
    prpl_info->chat_send_file(gc, id, file);
1010
0
  }
1011
0
}