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