Remove unused Meson option for enchant.
[pidgin-git.git] / libpurple / server.c
blob560ca70a0028aa4e793f135e369d7fdfeb5072e3
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_IS_PROTOCOL_IM(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, 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, 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_IS_PROTOCOL_ATTENTION(protocol)) {
299 GList *attention_types;
301 attention_types = purple_protocol_attention_get_types(
302 PURPLE_PROTOCOL_ATTENTION(protocol), account);
303 attn = (PurpleAttentionType *)g_list_nth_data(attention_types, type_code);
304 } else {
305 attn = NULL;
308 return attn;
313 * Move a buddy from one group to another on server.
315 * Note: For now we'll not deal with changing gc's at the same time, but
316 * it should be possible. Probably needs to be done, someday. Although,
317 * the UI for that would be difficult, because groups are Purple-wide.
319 void purple_serv_move_buddy(PurpleBuddy *buddy, PurpleGroup *orig, PurpleGroup *dest)
321 PurpleAccount *account;
322 PurpleConnection *gc;
323 PurpleProtocol *protocol;
325 g_return_if_fail(buddy != NULL);
326 g_return_if_fail(orig != NULL);
327 g_return_if_fail(dest != NULL);
329 account = purple_buddy_get_account(buddy);
330 gc = purple_account_get_connection(account);
332 if (gc) {
333 protocol = purple_connection_get_protocol(gc);
334 purple_protocol_server_iface_group_buddy(protocol, gc, purple_buddy_get_name(buddy),
335 purple_group_get_name(orig),
336 purple_group_get_name(dest));
340 void purple_serv_add_permit(PurpleConnection *gc, const char *name)
342 PurpleProtocol *protocol;
344 if (gc) {
345 protocol = purple_connection_get_protocol(gc);
346 purple_protocol_privacy_iface_add_permit(protocol, gc, name);
350 void purple_serv_add_deny(PurpleConnection *gc, const char *name)
352 PurpleProtocol *protocol;
354 if (gc) {
355 protocol = purple_connection_get_protocol(gc);
356 purple_protocol_privacy_iface_add_deny(protocol, gc, name);
360 void purple_serv_rem_permit(PurpleConnection *gc, const char *name)
362 PurpleProtocol *protocol;
364 if (gc) {
365 protocol = purple_connection_get_protocol(gc);
366 purple_protocol_privacy_iface_rem_permit(protocol, gc, name);
370 void purple_serv_rem_deny(PurpleConnection *gc, const char *name)
372 PurpleProtocol *protocol;
374 if (gc) {
375 protocol = purple_connection_get_protocol(gc);
376 purple_protocol_privacy_iface_rem_deny(protocol, gc, name);
380 void purple_serv_set_permit_deny(PurpleConnection *gc)
382 PurpleProtocol *protocol;
384 if (gc) {
385 protocol = purple_connection_get_protocol(gc);
388 * this is called when either you import a buddy list, and make lots
389 * of changes that way, or when the user toggles the permit/deny mode
390 * in the prefs. In either case you should probably be resetting and
391 * resending the permit/deny info when you get this.
393 purple_protocol_privacy_iface_set_permit_deny(protocol, gc);
397 void purple_serv_join_chat(PurpleConnection *gc, GHashTable *data)
399 PurpleProtocol *protocol;
401 if (gc) {
402 protocol = purple_connection_get_protocol(gc);
403 purple_protocol_chat_iface_join(protocol, gc, data);
408 void purple_serv_reject_chat(PurpleConnection *gc, GHashTable *data)
410 PurpleProtocol *protocol;
412 if (gc) {
413 protocol = purple_connection_get_protocol(gc);
414 purple_protocol_chat_iface_reject(protocol, gc, data);
418 void purple_serv_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
420 PurpleProtocol *protocol = NULL;
421 PurpleChatConversation *chat;
422 char *buffy;
424 chat = purple_conversations_find_chat(gc, id);
426 if(chat == NULL)
427 return;
429 if(gc)
430 protocol = purple_connection_get_protocol(gc);
432 buffy = message && *message ? g_strdup(message) : NULL;
433 purple_signal_emit(purple_conversations_get_handle(), "chat-inviting-user",
434 chat, name, &buffy);
436 if (protocol)
437 purple_protocol_chat_iface_invite(protocol, gc, id, buffy, name);
439 purple_signal_emit(purple_conversations_get_handle(), "chat-invited-user",
440 chat, name, buffy);
442 g_free(buffy);
445 /* Ya know, nothing uses this except purple_chat_conversation_finalize(),
446 * I think I'll just merge it into that later...
447 * Then again, something might want to use this, from outside protocol-land
448 * to leave a chat without destroying the conversation.
450 void purple_serv_chat_leave(PurpleConnection *gc, int id)
452 PurpleProtocol *protocol;
454 protocol = purple_connection_get_protocol(gc);
455 purple_protocol_chat_iface_leave(protocol, gc, id);
458 int purple_serv_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg)
460 PurpleProtocol *protocol;
461 protocol = purple_connection_get_protocol(gc);
463 g_return_val_if_fail(msg != NULL, -EINVAL);
465 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, send))
466 return purple_protocol_chat_iface_send(protocol, gc, id, msg);
468 return -EINVAL;
472 * woo. i'm actually going to comment this function. isn't that fun. make
473 * sure to follow along, kids
475 void purple_serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
476 PurpleMessageFlags flags, time_t mtime)
478 PurpleAccount *account;
479 PurpleIMConversation *im;
480 char *message, *name;
481 char *angel, *buffy;
482 int plugin_return;
483 PurpleMessage *pmsg;
485 g_return_if_fail(msg != NULL);
487 account = purple_connection_get_account(gc);
489 if (mtime < 0) {
490 purple_debug_error("server",
491 "purple_serv_got_im ignoring negative timestamp\n");
492 /* TODO: Would be more appropriate to use a value that indicates
493 that the timestamp is unknown, and surface that in the UI. */
494 mtime = time(NULL);
498 * XXX: Should we be setting this here, or relying on protocols to set it?
500 flags |= PURPLE_MESSAGE_RECV;
502 if (!purple_account_privacy_check(account, who)) {
503 purple_signal_emit(purple_conversations_get_handle(), "blocked-im-msg",
504 account, who, msg, flags, (unsigned int)mtime);
505 return;
509 * We should update the conversation window buttons and menu,
510 * if it exists.
512 im = purple_conversations_find_im_with_account(who, purple_connection_get_account(gc));
515 * Make copies of the message and the sender in case plugins want
516 * to free these strings and replace them with a modifed version.
518 buffy = g_strdup(msg);
519 angel = g_strdup(who);
521 plugin_return = GPOINTER_TO_INT(
522 purple_signal_emit_return_1(purple_conversations_get_handle(),
523 "receiving-im-msg", purple_connection_get_account(gc),
524 &angel, &buffy, im, &flags));
526 if (!buffy || !angel || plugin_return) {
527 g_free(buffy);
528 g_free(angel);
529 return;
532 name = angel;
533 message = buffy;
535 purple_signal_emit(purple_conversations_get_handle(), "received-im-msg", purple_connection_get_account(gc),
536 name, message, im, flags);
538 /* search for conversation again in case it was created by received-im-msg handler */
539 if (im == NULL)
540 im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
542 if (im == NULL)
543 im = purple_im_conversation_new(account, name);
545 pmsg = purple_message_new_incoming(name, message, flags, mtime);
546 purple_conversation_write_message(PURPLE_CONVERSATION(im), pmsg);
547 g_free(message);
550 * Don't autorespond if:
552 * - it's not supported on this connection
553 * - we are available
554 * - or it's disabled
555 * - or we're not idle and the 'only auto respond if idle' pref
556 * is set
558 if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_AUTO_RESP)
560 PurplePresence *presence;
561 PurpleStatus *status;
562 PurpleStatusType *status_type;
563 PurpleStatusPrimitive primitive;
564 const gchar *auto_reply_pref;
565 const char *away_msg = NULL;
566 gboolean mobile = FALSE;
568 auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
570 presence = purple_account_get_presence(account);
571 status = purple_presence_get_active_status(presence);
572 status_type = purple_status_get_status_type(status);
573 primitive = purple_status_type_get_primitive(status_type);
574 mobile = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE);
575 if ((primitive == PURPLE_STATUS_AVAILABLE) ||
576 (primitive == PURPLE_STATUS_INVISIBLE) ||
577 mobile ||
578 purple_strequal(auto_reply_pref, "never") ||
579 (!purple_presence_is_idle(presence) && purple_strequal(auto_reply_pref, "awayidle")))
581 g_free(name);
582 return;
585 away_msg = g_value_get_string(
586 purple_status_get_attr_value(status, "message"));
588 if ((away_msg != NULL) && (*away_msg != '\0')) {
589 struct last_auto_response *lar;
590 time_t now = time(NULL);
593 * This used to be based on the conversation window. But um, if
594 * you went away, and someone sent you a message and got your
595 * auto-response, and then you closed the window, and then they
596 * sent you another one, they'd get the auto-response back too
597 * soon. Besides that, we need to keep track of this even if we've
598 * got a queue. So the rest of this block is just the auto-response,
599 * if necessary.
601 lar = get_last_auto_response(gc, name);
602 if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE)
605 * We don't want to send an autoresponse in response to the other user's
606 * autoresponse. We do, however, not want to then send one in response to the
607 * _next_ message, so we still set lar->sent to now.
609 lar->sent = now;
611 if (!(flags & PURPLE_MESSAGE_AUTO_RESP))
613 PurpleMessage *msg;
615 msg = purple_message_new_outgoing(name,
616 away_msg, PURPLE_MESSAGE_AUTO_RESP);
618 purple_serv_send_im(gc, msg);
619 purple_conversation_write_message(PURPLE_CONVERSATION(im), msg);
625 g_free(name);
628 void purple_serv_got_typing(PurpleConnection *gc, const char *name, int timeout,
629 PurpleIMTypingState state) {
630 PurpleIMConversation *im;
632 im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
633 if (im != NULL) {
634 purple_im_conversation_set_typing_state(im, state);
635 } else {
636 switch (state)
638 case PURPLE_IM_TYPING:
639 purple_signal_emit(purple_conversations_get_handle(),
640 "buddy-typing", purple_connection_get_account(gc), name);
641 break;
642 case PURPLE_IM_TYPED:
643 purple_signal_emit(purple_conversations_get_handle(),
644 "buddy-typed", purple_connection_get_account(gc), name);
645 break;
646 case PURPLE_IM_NOT_TYPING:
647 purple_signal_emit(purple_conversations_get_handle(),
648 "buddy-typing-stopped", purple_connection_get_account(gc), name);
649 break;
653 if (im != NULL && timeout > 0)
654 purple_im_conversation_start_typing_timeout(im, timeout);
657 void purple_serv_got_typing_stopped(PurpleConnection *gc, const char *name) {
659 PurpleIMConversation *im;
661 im = purple_conversations_find_im_with_account(name, purple_connection_get_account(gc));
662 if (im != NULL)
664 if (purple_im_conversation_get_typing_state(im) == PURPLE_IM_NOT_TYPING)
665 return;
667 purple_im_conversation_stop_typing_timeout(im);
668 purple_im_conversation_set_typing_state(im, PURPLE_IM_NOT_TYPING);
670 else
672 purple_signal_emit(purple_conversations_get_handle(),
673 "buddy-typing-stopped", purple_connection_get_account(gc), name);
677 struct chat_invite_data {
678 PurpleConnection *gc;
679 GHashTable *components;
682 static void chat_invite_data_free(struct chat_invite_data *cid)
684 if (cid->components)
685 g_hash_table_destroy(cid->components);
686 g_free(cid);
690 static void chat_invite_reject(struct chat_invite_data *cid)
692 purple_serv_reject_chat(cid->gc, cid->components);
693 chat_invite_data_free(cid);
697 static void chat_invite_accept(struct chat_invite_data *cid)
699 purple_serv_join_chat(cid->gc, cid->components);
700 chat_invite_data_free(cid);
705 void purple_serv_got_chat_invite(PurpleConnection *gc, const char *name,
706 const char *who, const char *message, GHashTable *data)
708 PurpleAccount *account;
709 struct chat_invite_data *cid;
710 int plugin_return;
712 g_return_if_fail(name != NULL);
713 g_return_if_fail(who != NULL);
715 account = purple_connection_get_account(gc);
716 if (!purple_account_privacy_check(account, who)) {
717 purple_signal_emit(purple_conversations_get_handle(), "chat-invite-blocked",
718 account, who, name, message, data);
719 return;
722 cid = g_new0(struct chat_invite_data, 1);
724 plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
725 purple_conversations_get_handle(),
726 "chat-invited", account, who, name, message, data));
728 cid->gc = gc;
729 cid->components = data;
731 if (plugin_return == 0)
733 char *buf2;
735 if (message != NULL)
737 buf2 = g_strdup_printf(
738 _("%s has invited %s to the chat room %s:\n%s"),
739 who, purple_account_get_username(account), name, message);
741 else
742 buf2 = g_strdup_printf(
743 _("%s has invited %s to the chat room %s\n"),
744 who, purple_account_get_username(account), name);
747 purple_request_accept_cancel(gc, NULL,
748 _("Accept chat invitation?"), buf2,
749 PURPLE_DEFAULT_ACTION_NONE,
750 purple_request_cpar_from_connection(gc), cid,
751 G_CALLBACK(chat_invite_accept),
752 G_CALLBACK(chat_invite_reject));
753 g_free(buf2);
755 else if (plugin_return > 0)
756 chat_invite_accept(cid);
757 else
758 chat_invite_reject(cid);
761 PurpleChatConversation *purple_serv_got_joined_chat(PurpleConnection *gc,
762 int id, const char *name)
764 PurpleChatConversation *chat;
765 PurpleAccount *account;
767 account = purple_connection_get_account(gc);
769 g_return_val_if_fail(account != NULL, NULL);
770 g_return_val_if_fail(name != NULL, NULL);
772 chat = purple_chat_conversation_new(account, name);
773 g_return_val_if_fail(chat != NULL, NULL);
775 if (!g_slist_find(purple_connection_get_active_chats(gc), chat))
776 _purple_connection_add_active_chat(gc, chat);
778 purple_chat_conversation_set_id(chat, id);
780 purple_signal_emit(purple_conversations_get_handle(), "chat-joined", chat);
782 return chat;
785 void purple_serv_got_chat_left(PurpleConnection *g, int id)
787 GSList *bcs;
788 PurpleChatConversation *chat = NULL;
790 for (bcs = purple_connection_get_active_chats(g); bcs != NULL; bcs = bcs->next) {
791 if (purple_chat_conversation_get_id(
792 PURPLE_CHAT_CONVERSATION(bcs->data)) == id) {
793 chat = (PurpleChatConversation *)bcs->data;
794 break;
798 if (!chat)
799 return;
801 purple_debug(PURPLE_DEBUG_INFO, "server", "Leaving room: %s\n",
802 purple_conversation_get_name(PURPLE_CONVERSATION(chat)));
804 _purple_connection_remove_active_chat(g, chat);
806 purple_chat_conversation_leave(chat);
808 purple_signal_emit(purple_conversations_get_handle(), "chat-left", chat);
811 void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data)
813 purple_signal_emit(purple_conversations_get_handle(), "chat-join-failed",
814 gc, data);
817 void purple_serv_got_chat_in(PurpleConnection *g, int id, const char *who,
818 PurpleMessageFlags flags, const char *message, time_t mtime)
820 GSList *bcs;
821 PurpleChatConversation *chat = NULL;
822 char *buffy, *angel;
823 int plugin_return;
824 PurpleMessage *pmsg;
826 g_return_if_fail(who != NULL);
827 g_return_if_fail(message != NULL);
829 if (mtime < 0) {
830 purple_debug_error("server",
831 "purple_serv_got_chat_in ignoring negative timestamp\n");
832 /* TODO: Would be more appropriate to use a value that indicates
833 that the timestamp is unknown, and surface that in the UI. */
834 mtime = time(NULL);
837 for (bcs = purple_connection_get_active_chats(g); bcs != NULL; bcs = bcs->next) {
838 if (purple_chat_conversation_get_id(
839 PURPLE_CHAT_CONVERSATION(bcs->data)) == id) {
840 chat = (PurpleChatConversation *)bcs->data;
841 break;
845 if (!chat)
846 return;
848 /* Did I send the message? */
849 if (purple_strequal(purple_chat_conversation_get_nick(chat),
850 purple_normalize(purple_conversation_get_account(
851 PURPLE_CONVERSATION(chat)), who))) {
852 flags |= PURPLE_MESSAGE_SEND;
853 flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some protocol sets it! */
854 } else {
855 flags |= PURPLE_MESSAGE_RECV;
859 * Make copies of the message and the sender in case plugins want
860 * to free these strings and replace them with a modifed version.
862 buffy = g_strdup(message);
863 angel = g_strdup(who);
865 plugin_return = GPOINTER_TO_INT(
866 purple_signal_emit_return_1(purple_conversations_get_handle(),
867 "receiving-chat-msg", purple_connection_get_account(g),
868 &angel, &buffy, chat, &flags));
870 if (!buffy || !angel || plugin_return) {
871 g_free(buffy);
872 g_free(angel);
873 return;
876 who = angel;
877 message = buffy;
879 purple_signal_emit(purple_conversations_get_handle(), "received-chat-msg", purple_connection_get_account(g),
880 who, message, chat, flags);
882 if (flags & PURPLE_MESSAGE_RECV)
883 pmsg = purple_message_new_incoming(who, message, flags, mtime);
884 else {
885 pmsg = purple_message_new_outgoing(who, message, flags);
886 purple_message_set_time(pmsg, mtime);
888 purple_conversation_write_message(PURPLE_CONVERSATION(chat), pmsg);
890 g_free(angel);
891 g_free(buffy);
894 void purple_serv_send_file(PurpleConnection *gc, const char *who, const char *file)
896 PurpleProtocol *protocol;
898 if (gc) {
899 protocol = purple_connection_get_protocol(gc);
901 if(PURPLE_IS_PROTOCOL_XFER(protocol)) {
902 PurpleProtocolXfer *xfer = PURPLE_PROTOCOL_XFER(protocol);
904 if(purple_protocol_xfer_can_receive(xfer, gc, who)) {
905 purple_protocol_xfer_send_file(xfer,
906 gc, who, file);