Merged default into xdg-dirs
[pidgin-git.git] / libpurple / server.c
blob13be8259faddc7ed7668777fcd3b76245c813575
1 /*
2 * purple
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.
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.
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.
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
24 /* This file is the fullcrap */
26 #include "internal.h"
27 #include "buddylist.h"
28 #include "conversation.h"
29 #include "debug.h"
30 #include "log.h"
31 #include "notify.h"
32 #include "prefs.h"
33 #include "protocol.h"
34 #include "request.h"
35 #include "signals.h"
36 #include "server.h"
37 #include "status.h"
38 #include "util.h"
40 #define SECS_BEFORE_RESENDING_AUTORESPONSE 600
41 #define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married"
43 unsigned int
44 purple_serv_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state)
46 PurpleProtocol *protocol;
48 if (gc) {
49 protocol = purple_connection_get_protocol(gc);
50 return purple_protocol_im_iface_send_typing(protocol, gc, name, state);
53 return 0;
56 static GSList *last_auto_responses = NULL;
57 struct last_auto_response {
58 PurpleConnection *gc;
59 char name[80];
60 time_t sent;
63 static gboolean
64 expire_last_auto_responses(gpointer data)
66 GSList *tmp, *cur;
67 struct last_auto_response *lar;
69 tmp = last_auto_responses;
71 while (tmp) {
72 cur = tmp;
73 tmp = tmp->next;
74 lar = (struct last_auto_response *)cur->data;
76 if ((time(NULL) - lar->sent) > SECS_BEFORE_RESENDING_AUTORESPONSE) {
77 last_auto_responses = g_slist_remove(last_auto_responses, lar);
78 g_free(lar);
82 return FALSE; /* do not run again */
85 static struct last_auto_response *
86 get_last_auto_response(PurpleConnection *gc, const char *name)
88 GSList *tmp;
89 struct last_auto_response *lar;
91 /* because we're modifying or creating a lar, schedule the
92 * function to expire them as the pref dictates */
93 g_timeout_add_seconds((SECS_BEFORE_RESENDING_AUTORESPONSE + 1), expire_last_auto_responses, NULL);
95 tmp = last_auto_responses;
97 while (tmp) {
98 lar = (struct last_auto_response *)tmp->data;
100 if (gc == lar->gc && !strncmp(name, lar->name, sizeof(lar->name)))
101 return lar;
103 tmp = tmp->next;
106 lar = g_new0(struct last_auto_response, 1);
107 g_snprintf(lar->name, sizeof(lar->name), "%s", name);
108 lar->gc = gc;
109 lar->sent = 0;
110 last_auto_responses = g_slist_prepend(last_auto_responses, lar);
112 return lar;
115 int purple_serv_send_im(PurpleConnection *gc, PurpleMessage *msg)
117 PurpleIMConversation *im = NULL;
118 PurpleAccount *account = NULL;
119 PurplePresence *presence = NULL;
120 PurpleProtocol *protocol = NULL;
121 int val = -EINVAL;
122 const gchar *auto_reply_pref = NULL;
123 const gchar *recipient;
125 g_return_val_if_fail(gc != NULL, val);
126 g_return_val_if_fail(msg != NULL, val);
128 protocol = purple_connection_get_protocol(gc);
130 g_return_val_if_fail(protocol != NULL, val);
131 g_return_val_if_fail(PURPLE_PROTOCOL_HAS_IM_IFACE(protocol), val);
133 account = purple_connection_get_account(gc);
134 presence = purple_account_get_presence(account);
135 recipient = purple_message_get_recipient(msg);
137 im = purple_conversations_find_im_with_account(recipient, account);
139 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, IM_IFACE, send))
140 val = purple_protocol_im_iface_send(protocol, gc, msg);
143 * XXX - If "only auto-reply when away & idle" is set, then shouldn't
144 * this only reset lar->sent if we're away AND idle?
146 auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
147 if((purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_AUTO_RESP) &&
148 !purple_presence_is_available(presence) &&
149 !purple_strequal(auto_reply_pref, "never")) {
151 struct last_auto_response *lar;
152 lar = get_last_auto_response(gc, recipient);
153 lar->sent = time(NULL);
156 if(im && purple_im_conversation_get_send_typed_timeout(im))
157 purple_im_conversation_stop_send_typed_timeout(im);
159 return val;
162 void purple_serv_get_info(PurpleConnection *gc, const char *name)
164 PurpleProtocol *protocol;
166 if (gc) {
167 protocol = purple_connection_get_protocol(gc);
168 purple_protocol_server_iface_get_info(protocol, gc, name);
172 void purple_serv_set_info(PurpleConnection *gc, const char *info)
174 PurpleProtocol *protocol;
175 PurpleAccount *account;
177 if (gc) {
178 protocol = purple_connection_get_protocol(gc);
180 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, set_info)) {
181 account = purple_connection_get_account(gc);
183 purple_signal_emit(purple_accounts_get_handle(),
184 "account-setting-info", account, info);
186 purple_protocol_server_iface_set_info(protocol, gc, info);
188 purple_signal_emit(purple_accounts_get_handle(),
189 "account-set-info", account, info);
195 * Set buddy's alias on server roster/list
197 void purple_serv_alias_buddy(PurpleBuddy *b)
199 PurpleAccount *account;
200 PurpleConnection *gc;
201 PurpleProtocol *protocol;
203 if (b) {
204 account = purple_buddy_get_account(b);
206 if (account) {
207 gc = purple_account_get_connection(account);
209 if (gc) {
210 protocol = purple_connection_get_protocol(gc);
211 purple_protocol_server_iface_alias_buddy(protocol, gc,
212 purple_buddy_get_name(b),
213 purple_buddy_get_local_alias(b));
219 void
220 purple_serv_got_alias(PurpleConnection *gc, const char *who, const char *alias)
222 PurpleAccount *account;
223 GSList *buddies;
224 PurpleBuddy *b;
225 PurpleIMConversation *im;
227 account = purple_connection_get_account(gc);
228 buddies = purple_blist_find_buddies(account, who);
230 while (buddies != NULL)
232 const char *server_alias;
234 b = buddies->data;
235 buddies = g_slist_delete_link(buddies, buddies);
237 server_alias = purple_buddy_get_server_alias(b);
239 if (purple_strequal(server_alias, alias))
240 continue;
242 purple_buddy_set_server_alias(b, alias);
244 im = purple_conversations_find_im_with_account(purple_buddy_get_name(b), account);
245 if (im != NULL && alias != NULL && !purple_strequal(alias, who))
247 char *escaped = g_markup_escape_text(who, -1);
248 char *escaped2 = g_markup_escape_text(alias, -1);
249 char *tmp = g_strdup_printf(_("%s is now known as %s.\n"),
250 escaped, escaped2);
252 purple_conversation_write_system_message(
253 PURPLE_CONVERSATION(im), tmp,
254 PURPLE_MESSAGE_NO_LINKIFY);
256 g_free(tmp);
257 g_free(escaped2);
258 g_free(escaped);
263 void
264 purple_serv_got_private_alias(PurpleConnection *gc, const char *who, const char *alias)
266 PurpleAccount *account = NULL;
267 GSList *buddies = NULL;
268 PurpleBuddy *b = NULL;
270 account = purple_connection_get_account(gc);
271 buddies = purple_blist_find_buddies(account, who);
273 while(buddies != NULL) {
274 const char *balias;
275 b = buddies->data;
277 buddies = g_slist_delete_link(buddies, buddies);
279 balias = purple_buddy_get_local_alias(b);
280 if (purple_strequal(balias, alias))
281 continue;
283 purple_buddy_set_local_alias(b, alias);
288 PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code)
290 PurpleProtocol *protocol;
291 PurpleAttentionType* attn;
293 g_return_val_if_fail(account != NULL, NULL);
295 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
297 /* Lookup the attention type in the protocol's attention_types list, if any. */
298 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, ATTENTION_IFACE, get_types)) {
299 GList *attention_types;
301 attention_types = purple_protocol_attention_iface_get_types(protocol, account);
302 attn = (PurpleAttentionType *)g_list_nth_data(attention_types, type_code);
303 } else {
304 attn = NULL;
307 return attn;
312 * Move a buddy from one group to another on server.
314 * Note: For now we'll not deal with changing gc's at the same time, but
315 * it should be possible. Probably needs to be done, someday. Although,
316 * the UI for that would be difficult, because groups are Purple-wide.
318 void purple_serv_move_buddy(PurpleBuddy *buddy, PurpleGroup *orig, PurpleGroup *dest)
320 PurpleAccount *account;
321 PurpleConnection *gc;
322 PurpleProtocol *protocol;
324 g_return_if_fail(buddy != NULL);
325 g_return_if_fail(orig != NULL);
326 g_return_if_fail(dest != NULL);
328 account = purple_buddy_get_account(buddy);
329 gc = purple_account_get_connection(account);
331 if (gc) {
332 protocol = purple_connection_get_protocol(gc);
333 purple_protocol_server_iface_group_buddy(protocol, gc, purple_buddy_get_name(buddy),
334 purple_group_get_name(orig),
335 purple_group_get_name(dest));
339 void purple_serv_add_permit(PurpleConnection *gc, const char *name)
341 PurpleProtocol *protocol;
343 if (gc) {
344 protocol = purple_connection_get_protocol(gc);
345 purple_protocol_privacy_iface_add_permit(protocol, gc, name);
349 void purple_serv_add_deny(PurpleConnection *gc, const char *name)
351 PurpleProtocol *protocol;
353 if (gc) {
354 protocol = purple_connection_get_protocol(gc);
355 purple_protocol_privacy_iface_add_deny(protocol, gc, name);
359 void purple_serv_rem_permit(PurpleConnection *gc, const char *name)
361 PurpleProtocol *protocol;
363 if (gc) {
364 protocol = purple_connection_get_protocol(gc);
365 purple_protocol_privacy_iface_rem_permit(protocol, gc, name);
369 void purple_serv_rem_deny(PurpleConnection *gc, const char *name)
371 PurpleProtocol *protocol;
373 if (gc) {
374 protocol = purple_connection_get_protocol(gc);
375 purple_protocol_privacy_iface_rem_deny(protocol, gc, name);
379 void purple_serv_set_permit_deny(PurpleConnection *gc)
381 PurpleProtocol *protocol;
383 if (gc) {
384 protocol = purple_connection_get_protocol(gc);
387 * this is called when either you import a buddy list, and make lots
388 * of changes that way, or when the user toggles the permit/deny mode
389 * in the prefs. In either case you should probably be resetting and
390 * resending the permit/deny info when you get this.
392 purple_protocol_privacy_iface_set_permit_deny(protocol, gc);
396 void purple_serv_join_chat(PurpleConnection *gc, GHashTable *data)
398 PurpleProtocol *protocol;
400 if (gc) {
401 protocol = purple_connection_get_protocol(gc);
402 purple_protocol_chat_iface_join(protocol, gc, data);
407 void purple_serv_reject_chat(PurpleConnection *gc, GHashTable *data)
409 PurpleProtocol *protocol;
411 if (gc) {
412 protocol = purple_connection_get_protocol(gc);
413 purple_protocol_chat_iface_reject(protocol, gc, data);
417 void purple_serv_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
419 PurpleProtocol *protocol = NULL;
420 PurpleChatConversation *chat;
421 char *buffy = message && *message ? g_strdup(message) : NULL;
423 chat = purple_conversations_find_chat(gc, id);
425 if(chat == NULL)
426 return;
428 if(gc)
429 protocol = purple_connection_get_protocol(gc);
431 purple_signal_emit(purple_conversations_get_handle(), "chat-inviting-user",
432 chat, name, &buffy);
434 if (protocol)
435 purple_protocol_chat_iface_invite(protocol, gc, id, buffy, name);
437 purple_signal_emit(purple_conversations_get_handle(), "chat-invited-user",
438 chat, name, buffy);
440 g_free(buffy);
443 /* Ya know, nothing uses this except purple_chat_conversation_finalize(),
444 * I think I'll just merge it into that later...
445 * Then again, something might want to use this, from outside protocol-land
446 * to leave a chat without destroying the conversation.
448 void purple_serv_chat_leave(PurpleConnection *gc, int id)
450 PurpleProtocol *protocol;
452 protocol = purple_connection_get_protocol(gc);
453 purple_protocol_chat_iface_leave(protocol, gc, id);
456 int purple_serv_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg)
458 PurpleProtocol *protocol;
459 protocol = purple_connection_get_protocol(gc);
461 g_return_val_if_fail(msg != NULL, -EINVAL);
463 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, send))
464 return purple_protocol_chat_iface_send(protocol, gc, id, msg);
466 return -EINVAL;
470 * woo. i'm actually going to comment this function. isn't that fun. make
471 * sure to follow along, kids
473 void purple_serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
474 PurpleMessageFlags flags, time_t mtime)
476 PurpleAccount *account;
477 PurpleIMConversation *im;
478 char *message, *name;
479 char *angel, *buffy;
480 int plugin_return;
481 PurpleMessage *pmsg;
483 g_return_if_fail(msg != NULL);
485 account = purple_connection_get_account(gc);
487 if (mtime < 0) {
488 purple_debug_error("server",
489 "purple_serv_got_im ignoring negative timestamp\n");
490 /* TODO: Would be more appropriate to use a value that indicates
491 that the timestamp is unknown, and surface that in the UI. */
492 mtime = time(NULL);
496 * XXX: Should we be setting this here, or relying on protocols to set it?
498 flags |= PURPLE_MESSAGE_RECV;
500 if (!purple_account_privacy_check(account, who)) {
501 purple_signal_emit(purple_conversations_get_handle(), "blocked-im-msg",
502 account, who, msg, flags, (unsigned int)mtime);
503 return;
507 * We should update the conversation window buttons and menu,
508 * if it exists.
510 im = purple_conversations_find_im_with_account(who, purple_connection_get_account(gc));
513 * Make copies of the message and the sender in case plugins want
514 * to free these strings and replace them with a modifed version.
516 buffy = g_strdup(msg);
517 angel = g_strdup(who);
519 plugin_return = GPOINTER_TO_INT(
520 purple_signal_emit_return_1(purple_conversations_get_handle(),
521 "receiving-im-msg", purple_connection_get_account(gc),
522 &angel, &buffy, im, &flags));
524 if (!buffy || !angel || plugin_return) {
525 g_free(buffy);
526 g_free(angel);
527 return;
530 name = angel;
531 message = buffy;
533 purple_signal_emit(purple_conversations_get_handle(), "received-im-msg", purple_connection_get_account(gc),
534 name, message, im, flags);
536 /* search for conversation again in case it was created by received-im-msg handler */
537 if (im == NULL)
538 im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
540 if (im == NULL)
541 im = purple_im_conversation_new(account, name);
543 pmsg = purple_message_new_incoming(name, message, flags, mtime);
544 purple_conversation_write_message(PURPLE_CONVERSATION(im), pmsg);
545 g_free(message);
548 * Don't autorespond if:
550 * - it's not supported on this connection
551 * - we are available
552 * - or it's disabled
553 * - or we're not idle and the 'only auto respond if idle' pref
554 * is set
556 if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_AUTO_RESP)
558 PurplePresence *presence;
559 PurpleStatus *status;
560 PurpleStatusType *status_type;
561 PurpleStatusPrimitive primitive;
562 const gchar *auto_reply_pref;
563 const char *away_msg = NULL;
564 gboolean mobile = FALSE;
566 auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
568 presence = purple_account_get_presence(account);
569 status = purple_presence_get_active_status(presence);
570 status_type = purple_status_get_status_type(status);
571 primitive = purple_status_type_get_primitive(status_type);
572 mobile = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE);
573 if ((primitive == PURPLE_STATUS_AVAILABLE) ||
574 (primitive == PURPLE_STATUS_INVISIBLE) ||
575 mobile ||
576 purple_strequal(auto_reply_pref, "never") ||
577 (!purple_presence_is_idle(presence) && purple_strequal(auto_reply_pref, "awayidle")))
579 g_free(name);
580 return;
583 away_msg = g_value_get_string(
584 purple_status_get_attr_value(status, "message"));
586 if ((away_msg != NULL) && (*away_msg != '\0')) {
587 struct last_auto_response *lar;
588 time_t now = time(NULL);
591 * This used to be based on the conversation window. But um, if
592 * you went away, and someone sent you a message and got your
593 * auto-response, and then you closed the window, and then they
594 * sent you another one, they'd get the auto-response back too
595 * soon. Besides that, we need to keep track of this even if we've
596 * got a queue. So the rest of this block is just the auto-response,
597 * if necessary.
599 lar = get_last_auto_response(gc, name);
600 if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE)
603 * We don't want to send an autoresponse in response to the other user's
604 * autoresponse. We do, however, not want to then send one in response to the
605 * _next_ message, so we still set lar->sent to now.
607 lar->sent = now;
609 if (!(flags & PURPLE_MESSAGE_AUTO_RESP))
611 PurpleMessage *msg;
613 msg = purple_message_new_outgoing(name,
614 away_msg, PURPLE_MESSAGE_AUTO_RESP);
616 purple_serv_send_im(gc, msg);
617 purple_conversation_write_message(PURPLE_CONVERSATION(im), msg);
623 g_free(name);
626 void purple_serv_got_typing(PurpleConnection *gc, const char *name, int timeout,
627 PurpleIMTypingState state) {
628 PurpleIMConversation *im;
630 im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
631 if (im != NULL) {
632 purple_im_conversation_set_typing_state(im, state);
633 } else {
634 switch (state)
636 case PURPLE_IM_TYPING:
637 purple_signal_emit(purple_conversations_get_handle(),
638 "buddy-typing", purple_connection_get_account(gc), name);
639 break;
640 case PURPLE_IM_TYPED:
641 purple_signal_emit(purple_conversations_get_handle(),
642 "buddy-typed", purple_connection_get_account(gc), name);
643 break;
644 case PURPLE_IM_NOT_TYPING:
645 purple_signal_emit(purple_conversations_get_handle(),
646 "buddy-typing-stopped", purple_connection_get_account(gc), name);
647 break;
651 if (im != NULL && timeout > 0)
652 purple_im_conversation_start_typing_timeout(im, timeout);
655 void purple_serv_got_typing_stopped(PurpleConnection *gc, const char *name) {
657 PurpleIMConversation *im;
659 im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
660 if (im != NULL)
662 if (purple_im_conversation_get_typing_state(im) == PURPLE_IM_NOT_TYPING)
663 return;
665 purple_im_conversation_stop_typing_timeout(im);
666 purple_im_conversation_set_typing_state(im, PURPLE_IM_NOT_TYPING);
668 else
670 purple_signal_emit(purple_conversations_get_handle(),
671 "buddy-typing-stopped", purple_connection_get_account(gc), name);
675 struct chat_invite_data {
676 PurpleConnection *gc;
677 GHashTable *components;
680 static void chat_invite_data_free(struct chat_invite_data *cid)
682 if (cid->components)
683 g_hash_table_destroy(cid->components);
684 g_free(cid);
688 static void chat_invite_reject(struct chat_invite_data *cid)
690 purple_serv_reject_chat(cid->gc, cid->components);
691 chat_invite_data_free(cid);
695 static void chat_invite_accept(struct chat_invite_data *cid)
697 purple_serv_join_chat(cid->gc, cid->components);
698 chat_invite_data_free(cid);
703 void purple_serv_got_chat_invite(PurpleConnection *gc, const char *name,
704 const char *who, const char *message, GHashTable *data)
706 PurpleAccount *account;
707 char buf2[BUF_LONG];
708 struct chat_invite_data *cid;
709 int plugin_return;
711 g_return_if_fail(name != NULL);
712 g_return_if_fail(who != NULL);
714 account = purple_connection_get_account(gc);
715 if (!purple_account_privacy_check(account, who)) {
716 purple_signal_emit(purple_conversations_get_handle(), "chat-invite-blocked",
717 account, who, name, message, data);
718 return;
721 cid = g_new0(struct chat_invite_data, 1);
723 plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
724 purple_conversations_get_handle(),
725 "chat-invited", account, who, name, message, data));
727 cid->gc = gc;
728 cid->components = data;
730 if (plugin_return == 0)
732 if (message != NULL)
734 g_snprintf(buf2, sizeof(buf2),
735 _("%s has invited %s to the chat room %s:\n%s"),
736 who, purple_account_get_username(account), name, message);
738 else
739 g_snprintf(buf2, sizeof(buf2),
740 _("%s has invited %s to the chat room %s\n"),
741 who, purple_account_get_username(account), name);
744 purple_request_accept_cancel(gc, NULL,
745 _("Accept chat invitation?"), buf2,
746 PURPLE_DEFAULT_ACTION_NONE,
747 purple_request_cpar_from_connection(gc), cid,
748 G_CALLBACK(chat_invite_accept),
749 G_CALLBACK(chat_invite_reject));
751 else if (plugin_return > 0)
752 chat_invite_accept(cid);
753 else
754 chat_invite_reject(cid);
757 PurpleChatConversation *purple_serv_got_joined_chat(PurpleConnection *gc,
758 int id, const char *name)
760 PurpleChatConversation *chat;
761 PurpleAccount *account;
763 account = purple_connection_get_account(gc);
765 g_return_val_if_fail(account != NULL, NULL);
766 g_return_val_if_fail(name != NULL, NULL);
768 chat = purple_chat_conversation_new(account, name);
769 g_return_val_if_fail(chat != NULL, NULL);
771 if (!g_slist_find(purple_connection_get_active_chats(gc), chat))
772 _purple_connection_add_active_chat(gc, chat);
774 purple_chat_conversation_set_id(chat, id);
776 purple_signal_emit(purple_conversations_get_handle(), "chat-joined", chat);
778 return chat;
781 void purple_serv_got_chat_left(PurpleConnection *g, int id)
783 GSList *bcs;
784 PurpleChatConversation *chat = NULL;
786 for (bcs = purple_connection_get_active_chats(g); bcs != NULL; bcs = bcs->next) {
787 if (purple_chat_conversation_get_id(
788 PURPLE_CHAT_CONVERSATION(bcs->data)) == id) {
789 chat = (PurpleChatConversation *)bcs->data;
790 break;
794 if (!chat)
795 return;
797 purple_debug(PURPLE_DEBUG_INFO, "server", "Leaving room: %s\n",
798 purple_conversation_get_name(PURPLE_CONVERSATION(chat)));
800 _purple_connection_remove_active_chat(g, chat);
802 purple_chat_conversation_leave(chat);
804 purple_signal_emit(purple_conversations_get_handle(), "chat-left", chat);
807 void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data)
809 purple_signal_emit(purple_conversations_get_handle(), "chat-join-failed",
810 gc, data);
813 void purple_serv_got_chat_in(PurpleConnection *g, int id, const char *who,
814 PurpleMessageFlags flags, const char *message, time_t mtime)
816 GSList *bcs;
817 PurpleChatConversation *chat = NULL;
818 char *buffy, *angel;
819 int plugin_return;
820 PurpleMessage *pmsg;
822 g_return_if_fail(who != NULL);
823 g_return_if_fail(message != NULL);
825 if (mtime < 0) {
826 purple_debug_error("server",
827 "purple_serv_got_chat_in ignoring negative timestamp\n");
828 /* TODO: Would be more appropriate to use a value that indicates
829 that the timestamp is unknown, and surface that in the UI. */
830 mtime = time(NULL);
833 for (bcs = purple_connection_get_active_chats(g); bcs != NULL; bcs = bcs->next) {
834 if (purple_chat_conversation_get_id(
835 PURPLE_CHAT_CONVERSATION(bcs->data)) == id) {
836 chat = (PurpleChatConversation *)bcs->data;
837 break;
841 if (!chat)
842 return;
844 /* Did I send the message? */
845 if (purple_strequal(purple_chat_conversation_get_nick(chat),
846 purple_normalize(purple_conversation_get_account(
847 PURPLE_CONVERSATION(chat)), who))) {
848 flags |= PURPLE_MESSAGE_SEND;
849 flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some protocol sets it! */
850 } else {
851 flags |= PURPLE_MESSAGE_RECV;
855 * Make copies of the message and the sender in case plugins want
856 * to free these strings and replace them with a modifed version.
858 buffy = g_strdup(message);
859 angel = g_strdup(who);
861 plugin_return = GPOINTER_TO_INT(
862 purple_signal_emit_return_1(purple_conversations_get_handle(),
863 "receiving-chat-msg", purple_connection_get_account(g),
864 &angel, &buffy, chat, &flags));
866 if (!buffy || !angel || plugin_return) {
867 g_free(buffy);
868 g_free(angel);
869 return;
872 who = angel;
873 message = buffy;
875 purple_signal_emit(purple_conversations_get_handle(), "received-chat-msg", purple_connection_get_account(g),
876 who, message, chat, flags);
878 if (flags & PURPLE_MESSAGE_RECV)
879 pmsg = purple_message_new_incoming(who, message, flags, mtime);
880 else {
881 pmsg = purple_message_new_outgoing(who, message, flags);
882 purple_message_set_time(pmsg, mtime);
884 purple_conversation_write_message(PURPLE_CONVERSATION(chat), pmsg);
886 g_free(angel);
887 g_free(buffy);
890 void purple_serv_send_file(PurpleConnection *gc, const char *who, const char *file)
892 PurpleProtocol *protocol;
894 if (gc) {
895 protocol = purple_connection_get_protocol(gc);
897 if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, XFER_IFACE, can_receive) ||
898 purple_protocol_xfer_iface_can_receive(protocol, gc, who))
900 purple_protocol_xfer_iface_send(protocol, gc, who, file);