I think this was accidentally changed in revision
[pidgin-git.git] / libpurple / conversation.c
blob4650de902b51ef458d8c8e6078d8c9e5dfe0d496
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
22 #include "internal.h"
23 #include "blist.h"
24 #include "cmds.h"
25 #include "conversation.h"
26 #include "dbus-maybe.h"
27 #include "debug.h"
28 #include "imgstore.h"
29 #include "notify.h"
30 #include "prefs.h"
31 #include "prpl.h"
32 #include "request.h"
33 #include "signals.h"
34 #include "util.h"
36 #define SEND_TYPED_TIMEOUT_SECONDS 5
38 static GList *conversations = NULL;
39 static GList *ims = NULL;
40 static GList *chats = NULL;
41 static PurpleConversationUiOps *default_ops = NULL;
43 /**
44 * A hash table used for efficient lookups of conversations by name.
45 * struct _purple_hconv => PurpleConversation*
47 static GHashTable *conversation_cache = NULL;
49 struct _purple_hconv {
50 PurpleConversationType type;
51 char *name;
52 const PurpleAccount *account;
55 static guint _purple_conversations_hconv_hash(struct _purple_hconv *hc)
57 return g_str_hash(hc->name) ^ hc->type ^ g_direct_hash(hc->account);
60 static guint _purple_conversations_hconv_equal(struct _purple_hconv *hc1, struct _purple_hconv *hc2)
62 return (hc1->type == hc2->type &&
63 hc1->account == hc2->account &&
64 g_str_equal(hc1->name, hc2->name));
67 static void _purple_conversations_hconv_free_key(struct _purple_hconv *hc)
69 g_free(hc->name);
70 g_free(hc);
73 void
74 purple_conversations_set_ui_ops(PurpleConversationUiOps *ops)
76 default_ops = ops;
79 static gboolean
80 reset_typing_cb(gpointer data)
82 PurpleConversation *c = (PurpleConversation *)data;
83 PurpleConvIm *im;
85 im = PURPLE_CONV_IM(c);
87 purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
88 purple_conv_im_stop_typing_timeout(im);
90 return FALSE;
93 static gboolean
94 send_typed_cb(gpointer data)
96 PurpleConversation *conv = (PurpleConversation *)data;
97 PurpleConnection *gc;
98 const char *name;
100 g_return_val_if_fail(conv != NULL, FALSE);
102 gc = purple_conversation_get_gc(conv);
103 name = purple_conversation_get_name(conv);
105 if (gc != NULL && name != NULL) {
106 /* We set this to 1 so that PURPLE_TYPING will be sent
107 * if the Purple user types anything else.
109 purple_conv_im_set_type_again(PURPLE_CONV_IM(conv), 1);
111 serv_send_typing(gc, name, PURPLE_TYPED);
113 purple_debug(PURPLE_DEBUG_MISC, "conversation", "typed...\n");
116 return FALSE;
119 static void
120 common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags msgflags)
122 PurpleConversationType type;
123 PurpleAccount *account;
124 PurpleConnection *gc;
125 char *displayed = NULL, *sent = NULL;
126 int err = 0;
128 if (*message == '\0')
129 return;
131 account = purple_conversation_get_account(conv);
132 gc = purple_conversation_get_gc(conv);
134 g_return_if_fail(account != NULL);
135 g_return_if_fail(gc != NULL);
137 type = purple_conversation_get_type(conv);
139 /* Always linkfy the text for display, unless we're
140 * explicitly asked to do otheriwse*/
141 if (!(msgflags & PURPLE_MESSAGE_INVISIBLE)) {
142 if(msgflags & PURPLE_MESSAGE_NO_LINKIFY)
143 displayed = g_strdup(message);
144 else
145 displayed = purple_markup_linkify(message);
148 if (displayed && (conv->features & PURPLE_CONNECTION_HTML) &&
149 !(msgflags & PURPLE_MESSAGE_RAW)) {
150 sent = g_strdup(displayed);
151 } else
152 sent = g_strdup(message);
154 msgflags |= PURPLE_MESSAGE_SEND;
156 if (type == PURPLE_CONV_TYPE_IM) {
157 PurpleConvIm *im = PURPLE_CONV_IM(conv);
159 purple_signal_emit(purple_conversations_get_handle(), "sending-im-msg",
160 account,
161 purple_conversation_get_name(conv), &sent);
163 if (sent != NULL && sent[0] != '\0') {
165 err = serv_send_im(gc, purple_conversation_get_name(conv),
166 sent, msgflags);
168 if ((err > 0) && (displayed != NULL))
169 purple_conv_im_write(im, NULL, displayed, msgflags, time(NULL));
171 purple_signal_emit(purple_conversations_get_handle(), "sent-im-msg",
172 account,
173 purple_conversation_get_name(conv), sent);
176 else {
177 purple_signal_emit(purple_conversations_get_handle(), "sending-chat-msg",
178 account, &sent,
179 purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
181 if (sent != NULL && sent[0] != '\0') {
182 err = serv_chat_send(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), sent, msgflags);
184 purple_signal_emit(purple_conversations_get_handle(), "sent-chat-msg",
185 account, sent,
186 purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
190 if (err < 0) {
191 const char *who;
192 const char *msg;
194 who = purple_conversation_get_name(conv);
196 if (err == -E2BIG) {
197 msg = _("Unable to send message: The message is too large.");
199 if (!purple_conv_present_error(who, account, msg)) {
200 char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
201 purple_notify_error(gc, NULL, msg2, _("The message is too large."));
202 g_free(msg2);
205 else if (err == -ENOTCONN) {
206 purple_debug(PURPLE_DEBUG_ERROR, "conversation",
207 "Not yet connected.\n");
209 else {
210 msg = _("Unable to send message.");
212 if (!purple_conv_present_error(who, account, msg)) {
213 char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
214 purple_notify_error(gc, NULL, msg2, NULL);
215 g_free(msg2);
220 g_free(displayed);
221 g_free(sent);
224 static void
225 open_log(PurpleConversation *conv)
227 conv->logs = g_list_append(NULL, purple_log_new(conv->type == PURPLE_CONV_TYPE_CHAT ? PURPLE_LOG_CHAT :
228 PURPLE_LOG_IM, conv->name, conv->account,
229 conv, time(NULL), NULL));
232 /* Functions that deal with PurpleConvMessage */
234 static void
235 add_message_to_history(PurpleConversation *conv, const char *who, const char *alias,
236 const char *message, PurpleMessageFlags flags, time_t when)
238 PurpleConvMessage *msg;
239 PurpleConnection *gc;
241 gc = purple_account_get_connection(conv->account);
243 if (flags & PURPLE_MESSAGE_SEND) {
244 const char *me = NULL;
245 if (gc)
246 me = purple_connection_get_display_name(gc);
247 if (!me)
248 me = conv->account->username;
249 who = me;
252 msg = g_new0(PurpleConvMessage, 1);
253 PURPLE_DBUS_REGISTER_POINTER(msg, PurpleConvMessage);
254 msg->who = g_strdup(who);
255 msg->alias = g_strdup(alias);
256 msg->flags = flags;
257 msg->what = g_strdup(message);
258 msg->when = when;
259 msg->conv = conv;
261 conv->message_history = g_list_prepend(conv->message_history, msg);
264 static void
265 free_conv_message(PurpleConvMessage *msg)
267 g_free(msg->who);
268 g_free(msg->alias);
269 g_free(msg->what);
270 PURPLE_DBUS_UNREGISTER_POINTER(msg);
271 g_free(msg);
274 static void
275 message_history_free(GList *list)
277 g_list_foreach(list, (GFunc)free_conv_message, NULL);
278 g_list_free(list);
281 /**************************************************************************
282 * Conversation API
283 **************************************************************************/
284 static void
285 purple_conversation_chat_cleanup_for_rejoin(PurpleConversation *conv)
287 const char *disp;
288 PurpleAccount *account;
289 PurpleConnection *gc;
291 account = purple_conversation_get_account(conv);
293 purple_conversation_close_logs(conv);
294 open_log(conv);
296 gc = purple_account_get_connection(account);
298 if ((disp = purple_connection_get_display_name(gc)) != NULL)
299 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(conv), disp);
300 else
302 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(conv),
303 purple_account_get_username(account));
306 purple_conv_chat_clear_users(PURPLE_CONV_CHAT(conv));
307 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, NULL);
308 PURPLE_CONV_CHAT(conv)->left = FALSE;
310 purple_conversation_update(conv, PURPLE_CONV_UPDATE_CHATLEFT);
313 PurpleConversation *
314 purple_conversation_new(PurpleConversationType type, PurpleAccount *account,
315 const char *name)
317 PurpleConversation *conv;
318 PurpleConnection *gc;
319 PurpleConversationUiOps *ops;
320 struct _purple_hconv *hc;
322 g_return_val_if_fail(type != PURPLE_CONV_TYPE_UNKNOWN, NULL);
323 g_return_val_if_fail(account != NULL, NULL);
324 g_return_val_if_fail(name != NULL, NULL);
326 /* Check if this conversation already exists. */
327 if ((conv = purple_find_conversation_with_account(type, name, account)) != NULL)
329 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
330 !purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) {
331 purple_debug_warning("conversation", "Trying to create multiple "
332 "chats (%s) with the same name is deprecated and will be "
333 "removed in libpurple 3.0.0", name);
337 * This hack is necessary because some prpls (MSN) have unnamed chats
338 * that all use the same name. A PurpleConversation for one of those
339 * is only ever re-used if the user has left, so calls to
340 * purple_conversation_new need to fall-through to creating a new
341 * chat.
342 * TODO 3.0.0: Remove this workaround and mandate unique names.
344 if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT ||
345 purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
347 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
348 purple_conversation_chat_cleanup_for_rejoin(conv);
350 return conv;
354 gc = purple_account_get_connection(account);
355 g_return_val_if_fail(gc != NULL, NULL);
357 conv = g_new0(PurpleConversation, 1);
358 PURPLE_DBUS_REGISTER_POINTER(conv, PurpleConversation);
360 conv->type = type;
361 conv->account = account;
362 conv->name = g_strdup(name);
363 conv->title = g_strdup(name);
364 conv->data = g_hash_table_new_full(g_str_hash, g_str_equal,
365 g_free, NULL);
366 /* copy features from the connection. */
367 conv->features = gc->flags;
369 if (type == PURPLE_CONV_TYPE_IM)
371 PurpleBuddyIcon *icon;
372 conv->u.im = g_new0(PurpleConvIm, 1);
373 conv->u.im->conv = conv;
374 PURPLE_DBUS_REGISTER_POINTER(conv->u.im, PurpleConvIm);
376 ims = g_list_prepend(ims, conv);
377 if ((icon = purple_buddy_icons_find(account, name)))
379 purple_conv_im_set_icon(conv->u.im, icon);
380 /* purple_conv_im_set_icon refs the icon. */
381 purple_buddy_icon_unref(icon);
384 if (purple_prefs_get_bool("/purple/logging/log_ims"))
386 purple_conversation_set_logging(conv, TRUE);
387 open_log(conv);
390 else if (type == PURPLE_CONV_TYPE_CHAT)
392 const char *disp;
394 conv->u.chat = g_new0(PurpleConvChat, 1);
395 conv->u.chat->conv = conv;
396 PURPLE_DBUS_REGISTER_POINTER(conv->u.chat, PurpleConvChat);
398 chats = g_list_prepend(chats, conv);
400 if ((disp = purple_connection_get_display_name(account->gc)))
401 purple_conv_chat_set_nick(conv->u.chat, disp);
402 else
403 purple_conv_chat_set_nick(conv->u.chat,
404 purple_account_get_username(account));
406 if (purple_prefs_get_bool("/purple/logging/log_chats"))
408 purple_conversation_set_logging(conv, TRUE);
409 open_log(conv);
413 conversations = g_list_prepend(conversations, conv);
415 hc = g_new(struct _purple_hconv, 1);
416 hc->name = g_strdup(purple_normalize(account, conv->name));
417 hc->account = account;
418 hc->type = type;
420 g_hash_table_insert(conversation_cache, hc, conv);
422 /* Auto-set the title. */
423 purple_conversation_autoset_title(conv);
425 /* Don't move this.. it needs to be one of the last things done otherwise
426 * it causes mysterious crashes on my system.
427 * -- Gary
429 ops = conv->ui_ops = default_ops;
430 if (ops != NULL && ops->create_conversation != NULL)
431 ops->create_conversation(conv);
433 purple_signal_emit(purple_conversations_get_handle(),
434 "conversation-created", conv);
436 return conv;
439 void
440 purple_conversation_destroy(PurpleConversation *conv)
442 PurplePluginProtocolInfo *prpl_info = NULL;
443 PurpleConversationUiOps *ops;
444 PurpleConnection *gc;
445 const char *name;
446 struct _purple_hconv hc;
448 g_return_if_fail(conv != NULL);
450 purple_request_close_with_handle(conv);
452 ops = purple_conversation_get_ui_ops(conv);
453 gc = purple_conversation_get_gc(conv);
454 name = purple_conversation_get_name(conv);
456 if (gc != NULL)
458 /* Still connected */
459 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
461 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
463 if (purple_prefs_get_bool("/purple/conversations/im/send_typing"))
464 serv_send_typing(gc, name, PURPLE_NOT_TYPING);
466 if (gc && prpl_info->convo_closed != NULL)
467 prpl_info->convo_closed(gc, name);
469 else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
471 int chat_id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
472 #if 0
474 * This is unfortunately necessary, because calling
475 * serv_chat_leave() calls this purple_conversation_destroy(),
476 * which leads to two calls here.. We can't just return after
477 * this, because then it'll return on the next pass. So, since
478 * serv_got_chat_left(), which is eventually called from the
479 * prpl that serv_chat_leave() calls, removes this conversation
480 * from the gc's buddy_chats list, we're going to check to see
481 * if this exists in the list. If so, we want to return after
482 * calling this, because it'll be called again. If not, fall
483 * through, because it'll have already been removed, and we'd
484 * be on the 2nd pass.
486 * Long paragraph. <-- Short sentence.
488 * -- ChipX86
491 if (gc && g_slist_find(gc->buddy_chats, conv) != NULL) {
492 serv_chat_leave(gc, chat_id);
494 return;
496 #endif
498 * Instead of all of that, lets just close the window when
499 * the user tells us to, and let the prpl deal with the
500 * internals on it's own time. Don't do this if the prpl already
501 * knows it left the chat.
503 if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
504 serv_chat_leave(gc, chat_id);
507 * If they didn't call serv_got_chat_left by now, it's too late.
508 * So we better do it for them before we destroy the thing.
510 if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
511 serv_got_chat_left(gc, chat_id);
515 /* remove from conversations and im/chats lists prior to emit */
516 conversations = g_list_remove(conversations, conv);
518 if(conv->type==PURPLE_CONV_TYPE_IM)
519 ims = g_list_remove(ims, conv);
520 else if(conv->type==PURPLE_CONV_TYPE_CHAT)
521 chats = g_list_remove(chats, conv);
523 hc.name = (gchar *)purple_normalize(conv->account, conv->name);
524 hc.account = conv->account;
525 hc.type = conv->type;
527 g_hash_table_remove(conversation_cache, &hc);
529 purple_signal_emit(purple_conversations_get_handle(),
530 "deleting-conversation", conv);
532 g_free(conv->name);
533 g_free(conv->title);
535 conv->name = NULL;
536 conv->title = NULL;
538 if (conv->type == PURPLE_CONV_TYPE_IM) {
539 purple_conv_im_stop_typing_timeout(conv->u.im);
540 purple_conv_im_stop_send_typed_timeout(conv->u.im);
542 purple_buddy_icon_unref(conv->u.im->icon);
543 conv->u.im->icon = NULL;
545 PURPLE_DBUS_UNREGISTER_POINTER(conv->u.im);
546 g_free(conv->u.im);
547 conv->u.im = NULL;
549 else if (conv->type == PURPLE_CONV_TYPE_CHAT) {
551 g_list_foreach(conv->u.chat->in_room, (GFunc)purple_conv_chat_cb_destroy, NULL);
552 g_list_free(conv->u.chat->in_room);
554 g_list_foreach(conv->u.chat->ignored, (GFunc)g_free, NULL);
555 g_list_free(conv->u.chat->ignored);
557 conv->u.chat->in_room = NULL;
558 conv->u.chat->ignored = NULL;
560 g_free(conv->u.chat->who);
561 conv->u.chat->who = NULL;
563 g_free(conv->u.chat->topic);
564 conv->u.chat->topic = NULL;
566 g_free(conv->u.chat->nick);
568 PURPLE_DBUS_UNREGISTER_POINTER(conv->u.chat);
569 g_free(conv->u.chat);
570 conv->u.chat = NULL;
573 g_hash_table_destroy(conv->data);
574 conv->data = NULL;
576 if (ops != NULL && ops->destroy_conversation != NULL)
577 ops->destroy_conversation(conv);
579 purple_conversation_close_logs(conv);
581 purple_conversation_clear_message_history(conv);
583 PURPLE_DBUS_UNREGISTER_POINTER(conv);
584 g_free(conv);
585 conv = NULL;
589 void
590 purple_conversation_present(PurpleConversation *conv) {
591 PurpleConversationUiOps *ops;
593 g_return_if_fail(conv != NULL);
595 ops = purple_conversation_get_ui_ops(conv);
596 if(ops && ops->present)
597 ops->present(conv);
601 void
602 purple_conversation_set_features(PurpleConversation *conv, PurpleConnectionFlags features)
604 g_return_if_fail(conv != NULL);
606 conv->features = features;
608 purple_conversation_update(conv, PURPLE_CONV_UPDATE_FEATURES);
612 PurpleConnectionFlags
613 purple_conversation_get_features(PurpleConversation *conv)
615 g_return_val_if_fail(conv != NULL, 0);
616 return conv->features;
620 PurpleConversationType
621 purple_conversation_get_type(const PurpleConversation *conv)
623 g_return_val_if_fail(conv != NULL, PURPLE_CONV_TYPE_UNKNOWN);
625 return conv->type;
628 void
629 purple_conversation_set_ui_ops(PurpleConversation *conv,
630 PurpleConversationUiOps *ops)
632 g_return_if_fail(conv != NULL);
634 if (conv->ui_ops == ops)
635 return;
637 if (conv->ui_ops != NULL && conv->ui_ops->destroy_conversation != NULL)
638 conv->ui_ops->destroy_conversation(conv);
640 conv->ui_data = NULL;
642 conv->ui_ops = ops;
645 PurpleConversationUiOps *
646 purple_conversation_get_ui_ops(const PurpleConversation *conv)
648 g_return_val_if_fail(conv != NULL, NULL);
650 return conv->ui_ops;
653 void
654 purple_conversation_set_account(PurpleConversation *conv, PurpleAccount *account)
656 g_return_if_fail(conv != NULL);
658 if (account == purple_conversation_get_account(conv))
659 return;
661 conv->account = account;
663 purple_conversation_update(conv, PURPLE_CONV_UPDATE_ACCOUNT);
666 PurpleAccount *
667 purple_conversation_get_account(const PurpleConversation *conv)
669 g_return_val_if_fail(conv != NULL, NULL);
671 return conv->account;
674 PurpleConnection *
675 purple_conversation_get_gc(const PurpleConversation *conv)
677 PurpleAccount *account;
679 g_return_val_if_fail(conv != NULL, NULL);
681 account = purple_conversation_get_account(conv);
683 if (account == NULL)
684 return NULL;
686 return account->gc;
689 void
690 purple_conversation_set_title(PurpleConversation *conv, const char *title)
692 g_return_if_fail(conv != NULL);
693 g_return_if_fail(title != NULL);
695 g_free(conv->title);
696 conv->title = g_strdup(title);
698 purple_conversation_update(conv, PURPLE_CONV_UPDATE_TITLE);
701 const char *
702 purple_conversation_get_title(const PurpleConversation *conv)
704 g_return_val_if_fail(conv != NULL, NULL);
706 return conv->title;
709 void
710 purple_conversation_autoset_title(PurpleConversation *conv)
712 PurpleAccount *account;
713 PurpleBuddy *b;
714 PurpleChat *chat;
715 const char *text = NULL, *name;
717 g_return_if_fail(conv != NULL);
719 account = purple_conversation_get_account(conv);
720 name = purple_conversation_get_name(conv);
722 if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
723 if(account && ((b = purple_find_buddy(account, name)) != NULL))
724 text = purple_buddy_get_contact_alias(b);
725 } else if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
726 if(account && ((chat = purple_blist_find_chat(account, name)) != NULL))
727 text = purple_chat_get_name(chat);
731 if(text == NULL)
732 text = name;
734 purple_conversation_set_title(conv, text);
737 void
738 purple_conversation_foreach(void (*func)(PurpleConversation *conv))
740 PurpleConversation *conv;
741 GList *l;
743 g_return_if_fail(func != NULL);
745 for (l = purple_get_conversations(); l != NULL; l = l->next) {
746 conv = (PurpleConversation *)l->data;
748 func(conv);
752 void
753 purple_conversation_set_name(PurpleConversation *conv, const char *name)
755 struct _purple_hconv *hc;
756 g_return_if_fail(conv != NULL);
758 hc = g_new(struct _purple_hconv, 1);
759 hc->type = conv->type;
760 hc->account = conv->account;
761 hc->name = (gchar *)purple_normalize(conv->account, conv->name);
763 g_hash_table_remove(conversation_cache, hc);
764 g_free(conv->name);
766 conv->name = g_strdup(name);
767 hc->name = g_strdup(purple_normalize(conv->account, conv->name));
768 g_hash_table_insert(conversation_cache, hc, conv);
770 purple_conversation_autoset_title(conv);
773 const char *
774 purple_conversation_get_name(const PurpleConversation *conv)
776 g_return_val_if_fail(conv != NULL, NULL);
778 return conv->name;
781 void
782 purple_conversation_set_logging(PurpleConversation *conv, gboolean log)
784 g_return_if_fail(conv != NULL);
786 if (conv->logging != log)
788 conv->logging = log;
789 purple_conversation_update(conv, PURPLE_CONV_UPDATE_LOGGING);
793 gboolean
794 purple_conversation_is_logging(const PurpleConversation *conv)
796 g_return_val_if_fail(conv != NULL, FALSE);
798 return conv->logging;
801 void
802 purple_conversation_close_logs(PurpleConversation *conv)
804 g_return_if_fail(conv != NULL);
806 g_list_foreach(conv->logs, (GFunc)purple_log_free, NULL);
807 g_list_free(conv->logs);
808 conv->logs = NULL;
811 PurpleConvIm *
812 purple_conversation_get_im_data(const PurpleConversation *conv)
814 g_return_val_if_fail(conv != NULL, NULL);
816 if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM)
817 return NULL;
819 return conv->u.im;
822 PurpleConvChat *
823 purple_conversation_get_chat_data(const PurpleConversation *conv)
825 g_return_val_if_fail(conv != NULL, NULL);
827 if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT)
828 return NULL;
830 return conv->u.chat;
833 void
834 purple_conversation_set_data(PurpleConversation *conv, const char *key,
835 gpointer data)
837 g_return_if_fail(conv != NULL);
838 g_return_if_fail(key != NULL);
840 g_hash_table_replace(conv->data, g_strdup(key), data);
843 gpointer
844 purple_conversation_get_data(PurpleConversation *conv, const char *key)
846 g_return_val_if_fail(conv != NULL, NULL);
847 g_return_val_if_fail(key != NULL, NULL);
849 return g_hash_table_lookup(conv->data, key);
852 GList *
853 purple_get_conversations(void)
855 return conversations;
858 GList *
859 purple_get_ims(void)
861 return ims;
864 GList *
865 purple_get_chats(void)
867 return chats;
871 PurpleConversation *
872 purple_find_conversation_with_account(PurpleConversationType type,
873 const char *name,
874 const PurpleAccount *account)
876 PurpleConversation *c = NULL;
877 struct _purple_hconv hc;
879 g_return_val_if_fail(name != NULL, NULL);
881 hc.name = (gchar *)purple_normalize(account, name);
882 hc.account = account;
883 hc.type = type;
885 switch (type) {
886 case PURPLE_CONV_TYPE_IM:
887 case PURPLE_CONV_TYPE_CHAT:
888 c = g_hash_table_lookup(conversation_cache, &hc);
889 break;
890 case PURPLE_CONV_TYPE_ANY:
891 hc.type = PURPLE_CONV_TYPE_IM;
892 c = g_hash_table_lookup(conversation_cache, &hc);
893 if (!c) {
894 hc.type = PURPLE_CONV_TYPE_CHAT;
895 c = g_hash_table_lookup(conversation_cache, &hc);
897 break;
898 default:
899 g_return_val_if_reached(NULL);
902 return c;
905 void
906 purple_conversation_write(PurpleConversation *conv, const char *who,
907 const char *message, PurpleMessageFlags flags,
908 time_t mtime)
910 PurplePluginProtocolInfo *prpl_info = NULL;
911 PurpleConnection *gc = NULL;
912 PurpleAccount *account;
913 PurpleConversationUiOps *ops;
914 const char *alias;
915 char *displayed = NULL;
916 PurpleBuddy *b;
917 int plugin_return;
918 PurpleConversationType type;
919 /* int logging_font_options = 0; */
921 g_return_if_fail(conv != NULL);
922 g_return_if_fail(message != NULL);
924 ops = purple_conversation_get_ui_ops(conv);
926 account = purple_conversation_get_account(conv);
927 type = purple_conversation_get_type(conv);
929 if (account != NULL)
930 gc = purple_account_get_connection(account);
932 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
933 (gc != NULL && !g_slist_find(gc->buddy_chats, conv)))
934 return;
936 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
937 !g_list_find(purple_get_conversations(), conv))
938 return;
940 displayed = g_strdup(message);
942 if (who == NULL || *who == '\0')
943 who = purple_conversation_get_name(conv);
944 alias = who;
946 plugin_return =
947 GPOINTER_TO_INT(purple_signal_emit_return_1(
948 purple_conversations_get_handle(),
949 (type == PURPLE_CONV_TYPE_IM ? "writing-im-msg" : "writing-chat-msg"),
950 account, who, &displayed, conv, flags));
952 if (displayed == NULL)
953 return;
955 if (plugin_return) {
956 g_free(displayed);
957 return;
960 if (account != NULL) {
961 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));
963 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM ||
964 !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
966 if (flags & PURPLE_MESSAGE_SEND) {
967 b = purple_find_buddy(account,
968 purple_account_get_username(account));
970 if (purple_account_get_alias(account) != NULL)
971 alias = account->alias;
972 else if (b != NULL && !purple_strequal(purple_buddy_get_name(b), purple_buddy_get_contact_alias(b)))
973 alias = purple_buddy_get_contact_alias(b);
974 else if (purple_connection_get_display_name(gc) != NULL)
975 alias = purple_connection_get_display_name(gc);
976 else
977 alias = purple_account_get_username(account);
979 else
981 b = purple_find_buddy(account, who);
983 if (b != NULL)
984 alias = purple_buddy_get_contact_alias(b);
989 if (!(flags & PURPLE_MESSAGE_NO_LOG) && purple_conversation_is_logging(conv)) {
990 GList *log;
992 if (conv->logs == NULL)
993 open_log(conv);
995 log = conv->logs;
996 while (log != NULL) {
997 purple_log_write((PurpleLog *)log->data, flags, alias, mtime, displayed);
998 log = log->next;
1002 if (ops && ops->write_conv)
1003 ops->write_conv(conv, who, alias, displayed, flags, mtime);
1005 add_message_to_history(conv, who, alias, message, flags, mtime);
1007 purple_signal_emit(purple_conversations_get_handle(),
1008 (type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"),
1009 account, who, displayed, conv, flags);
1011 g_free(displayed);
1014 gboolean
1015 purple_conversation_has_focus(PurpleConversation *conv)
1017 gboolean ret = FALSE;
1018 PurpleConversationUiOps *ops;
1020 g_return_val_if_fail(conv != NULL, FALSE);
1022 ops = purple_conversation_get_ui_ops(conv);
1024 if (ops != NULL && ops->has_focus != NULL)
1025 ret = ops->has_focus(conv);
1027 return ret;
1031 * TODO: Need to make sure calls to this function happen in the core
1032 * instead of the UI. That way UIs have less work to do, and the
1033 * core/UI split is cleaner. Also need to make sure this is called
1034 * when chats are added/removed from the blist.
1036 void
1037 purple_conversation_update(PurpleConversation *conv, PurpleConvUpdateType type)
1039 g_return_if_fail(conv != NULL);
1041 purple_signal_emit(purple_conversations_get_handle(),
1042 "conversation-updated", conv, type);
1045 /**************************************************************************
1046 * IM Conversation API
1047 **************************************************************************/
1048 PurpleConversation *
1049 purple_conv_im_get_conversation(const PurpleConvIm *im)
1051 g_return_val_if_fail(im != NULL, NULL);
1053 return im->conv;
1056 void
1057 purple_conv_im_set_icon(PurpleConvIm *im, PurpleBuddyIcon *icon)
1059 g_return_if_fail(im != NULL);
1061 if (im->icon != icon)
1063 purple_buddy_icon_unref(im->icon);
1065 im->icon = (icon == NULL ? NULL : purple_buddy_icon_ref(icon));
1068 purple_conversation_update(purple_conv_im_get_conversation(im),
1069 PURPLE_CONV_UPDATE_ICON);
1072 PurpleBuddyIcon *
1073 purple_conv_im_get_icon(const PurpleConvIm *im)
1075 g_return_val_if_fail(im != NULL, NULL);
1077 return im->icon;
1080 void
1081 purple_conv_im_set_typing_state(PurpleConvIm *im, PurpleTypingState state)
1083 g_return_if_fail(im != NULL);
1085 if (im->typing_state != state)
1087 im->typing_state = state;
1089 switch (state)
1091 case PURPLE_TYPING:
1092 purple_signal_emit(purple_conversations_get_handle(),
1093 "buddy-typing", im->conv->account, im->conv->name);
1094 break;
1095 case PURPLE_TYPED:
1096 purple_signal_emit(purple_conversations_get_handle(),
1097 "buddy-typed", im->conv->account, im->conv->name);
1098 break;
1099 case PURPLE_NOT_TYPING:
1100 purple_signal_emit(purple_conversations_get_handle(),
1101 "buddy-typing-stopped", im->conv->account, im->conv->name);
1102 break;
1105 purple_conv_im_update_typing(im);
1109 PurpleTypingState
1110 purple_conv_im_get_typing_state(const PurpleConvIm *im)
1112 g_return_val_if_fail(im != NULL, 0);
1114 return im->typing_state;
1117 void
1118 purple_conv_im_start_typing_timeout(PurpleConvIm *im, int timeout)
1120 PurpleConversation *conv;
1122 g_return_if_fail(im != NULL);
1124 if (im->typing_timeout > 0)
1125 purple_conv_im_stop_typing_timeout(im);
1127 conv = purple_conv_im_get_conversation(im);
1129 im->typing_timeout = purple_timeout_add_seconds(timeout, reset_typing_cb, conv);
1132 void
1133 purple_conv_im_stop_typing_timeout(PurpleConvIm *im)
1135 g_return_if_fail(im != NULL);
1137 if (im->typing_timeout == 0)
1138 return;
1140 purple_timeout_remove(im->typing_timeout);
1141 im->typing_timeout = 0;
1144 guint
1145 purple_conv_im_get_typing_timeout(const PurpleConvIm *im)
1147 g_return_val_if_fail(im != NULL, 0);
1149 return im->typing_timeout;
1152 void
1153 purple_conv_im_set_type_again(PurpleConvIm *im, unsigned int val)
1155 g_return_if_fail(im != NULL);
1157 if (val == 0)
1158 im->type_again = 0;
1159 else
1160 im->type_again = time(NULL) + val;
1163 time_t
1164 purple_conv_im_get_type_again(const PurpleConvIm *im)
1166 g_return_val_if_fail(im != NULL, 0);
1168 return im->type_again;
1171 void
1172 purple_conv_im_start_send_typed_timeout(PurpleConvIm *im)
1174 g_return_if_fail(im != NULL);
1176 im->send_typed_timeout = purple_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS,
1177 send_typed_cb,
1178 purple_conv_im_get_conversation(im));
1181 void
1182 purple_conv_im_stop_send_typed_timeout(PurpleConvIm *im)
1184 g_return_if_fail(im != NULL);
1186 if (im->send_typed_timeout == 0)
1187 return;
1189 purple_timeout_remove(im->send_typed_timeout);
1190 im->send_typed_timeout = 0;
1193 guint
1194 purple_conv_im_get_send_typed_timeout(const PurpleConvIm *im)
1196 g_return_val_if_fail(im != NULL, 0);
1198 return im->send_typed_timeout;
1201 void
1202 purple_conv_im_update_typing(PurpleConvIm *im)
1204 g_return_if_fail(im != NULL);
1206 purple_conversation_update(purple_conv_im_get_conversation(im),
1207 PURPLE_CONV_UPDATE_TYPING);
1210 void
1211 purple_conv_im_write(PurpleConvIm *im, const char *who, const char *message,
1212 PurpleMessageFlags flags, time_t mtime)
1214 PurpleConversation *c;
1216 g_return_if_fail(im != NULL);
1217 g_return_if_fail(message != NULL);
1219 c = purple_conv_im_get_conversation(im);
1221 if ((flags & PURPLE_MESSAGE_RECV) == PURPLE_MESSAGE_RECV) {
1222 purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
1225 /* Pass this on to either the ops structure or the default write func. */
1226 if (c->ui_ops != NULL && c->ui_ops->write_im != NULL)
1227 c->ui_ops->write_im(c, who, message, flags, mtime);
1228 else
1229 purple_conversation_write(c, who, message, flags, mtime);
1232 gboolean purple_conv_present_error(const char *who, PurpleAccount *account, const char *what)
1234 PurpleConversation *conv;
1236 g_return_val_if_fail(who != NULL, FALSE);
1237 g_return_val_if_fail(account !=NULL, FALSE);
1238 g_return_val_if_fail(what != NULL, FALSE);
1240 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
1241 if (conv != NULL)
1242 purple_conversation_write(conv, NULL, what, PURPLE_MESSAGE_ERROR, time(NULL));
1243 else
1244 return FALSE;
1246 return TRUE;
1249 void
1250 purple_conv_im_send(PurpleConvIm *im, const char *message)
1252 purple_conv_im_send_with_flags(im, message, 0);
1255 static void
1256 purple_conv_send_confirm_cb(gpointer *data)
1258 PurpleConversation *conv = data[0];
1259 char *message = data[1];
1261 g_free(data);
1262 common_send(conv, message, 0);
1265 void
1266 purple_conv_send_confirm(PurpleConversation *conv, const char *message)
1268 char *text;
1269 gpointer *data;
1271 g_return_if_fail(conv != NULL);
1272 g_return_if_fail(message != NULL);
1274 if (conv->ui_ops != NULL && conv->ui_ops->send_confirm != NULL)
1276 conv->ui_ops->send_confirm(conv, message);
1277 return;
1280 text = g_strdup_printf("You are about to send the following message:\n%s", message);
1281 data = g_new0(gpointer, 2);
1282 data[0] = conv;
1283 data[1] = (gpointer)message;
1285 purple_request_action(conv, NULL, _("Send Message"), text, 0,
1286 purple_conversation_get_account(conv), NULL, conv,
1287 data, 2,
1288 _("_Send Message"), G_CALLBACK(purple_conv_send_confirm_cb),
1289 _("Cancel"), NULL);
1292 void
1293 purple_conv_im_send_with_flags(PurpleConvIm *im, const char *message, PurpleMessageFlags flags)
1295 g_return_if_fail(im != NULL);
1296 g_return_if_fail(message != NULL);
1298 common_send(purple_conv_im_get_conversation(im), message, flags);
1301 gboolean
1302 purple_conv_custom_smiley_add(PurpleConversation *conv, const char *smile,
1303 const char *cksum_type, const char *chksum,
1304 gboolean remote)
1306 if (conv == NULL || smile == NULL || !*smile) {
1307 return FALSE;
1310 /* TODO: check if the icon is in the cache and return false if so */
1311 /* TODO: add an icon cache (that doesn't suck) */
1312 if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_add !=NULL) {
1313 return conv->ui_ops->custom_smiley_add(conv, smile, remote);
1314 } else {
1315 purple_debug_info("conversation", "Could not find add custom smiley function");
1316 return FALSE;
1321 void
1322 purple_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
1323 const guchar *data, gsize size)
1325 g_return_if_fail(conv != NULL);
1326 g_return_if_fail(smile != NULL && *smile);
1328 if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_write != NULL)
1329 conv->ui_ops->custom_smiley_write(conv, smile, data, size);
1330 else
1331 purple_debug_info("conversation", "Could not find the smiley write function");
1334 void
1335 purple_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
1337 g_return_if_fail(conv != NULL);
1338 g_return_if_fail(smile != NULL && *smile);
1340 if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_close != NULL)
1341 conv->ui_ops->custom_smiley_close(conv, smile);
1342 else
1343 purple_debug_info("conversation", "Could not find custom smiley close function");
1347 /**************************************************************************
1348 * Chat Conversation API
1349 **************************************************************************/
1351 PurpleConversation *
1352 purple_conv_chat_get_conversation(const PurpleConvChat *chat)
1354 g_return_val_if_fail(chat != NULL, NULL);
1356 return chat->conv;
1359 GList *
1360 purple_conv_chat_set_users(PurpleConvChat *chat, GList *users)
1362 g_return_val_if_fail(chat != NULL, NULL);
1364 chat->in_room = users;
1366 return users;
1369 GList *
1370 purple_conv_chat_get_users(const PurpleConvChat *chat)
1372 g_return_val_if_fail(chat != NULL, NULL);
1374 return chat->in_room;
1377 void
1378 purple_conv_chat_ignore(PurpleConvChat *chat, const char *name)
1380 g_return_if_fail(chat != NULL);
1381 g_return_if_fail(name != NULL);
1383 /* Make sure the user isn't already ignored. */
1384 if (purple_conv_chat_is_user_ignored(chat, name))
1385 return;
1387 purple_conv_chat_set_ignored(chat,
1388 g_list_append(chat->ignored, g_strdup(name)));
1391 void
1392 purple_conv_chat_unignore(PurpleConvChat *chat, const char *name)
1394 GList *item;
1396 g_return_if_fail(chat != NULL);
1397 g_return_if_fail(name != NULL);
1399 /* Make sure the user is actually ignored. */
1400 if (!purple_conv_chat_is_user_ignored(chat, name))
1401 return;
1403 item = g_list_find(purple_conv_chat_get_ignored(chat),
1404 purple_conv_chat_get_ignored_user(chat, name));
1406 purple_conv_chat_set_ignored(chat,
1407 g_list_remove_link(chat->ignored, item));
1409 g_free(item->data);
1410 g_list_free_1(item);
1413 GList *
1414 purple_conv_chat_set_ignored(PurpleConvChat *chat, GList *ignored)
1416 g_return_val_if_fail(chat != NULL, NULL);
1418 chat->ignored = ignored;
1420 return ignored;
1423 GList *
1424 purple_conv_chat_get_ignored(const PurpleConvChat *chat)
1426 g_return_val_if_fail(chat != NULL, NULL);
1428 return chat->ignored;
1431 const char *
1432 purple_conv_chat_get_ignored_user(const PurpleConvChat *chat, const char *user)
1434 GList *ignored;
1436 g_return_val_if_fail(chat != NULL, NULL);
1437 g_return_val_if_fail(user != NULL, NULL);
1439 for (ignored = purple_conv_chat_get_ignored(chat);
1440 ignored != NULL;
1441 ignored = ignored->next) {
1443 const char *ign = (const char *)ignored->data;
1445 if (!purple_utf8_strcasecmp(user, ign) ||
1446 ((*ign == '+' || *ign == '%') && !purple_utf8_strcasecmp(user, ign + 1)))
1447 return ign;
1449 if (*ign == '@') {
1450 ign++;
1452 if ((*ign == '+' && !purple_utf8_strcasecmp(user, ign + 1)) ||
1453 (*ign != '+' && !purple_utf8_strcasecmp(user, ign)))
1454 return ign;
1458 return NULL;
1461 gboolean
1462 purple_conv_chat_is_user_ignored(const PurpleConvChat *chat, const char *user)
1464 g_return_val_if_fail(chat != NULL, FALSE);
1465 g_return_val_if_fail(user != NULL, FALSE);
1467 return (purple_conv_chat_get_ignored_user(chat, user) != NULL);
1470 void
1471 purple_conv_chat_set_topic(PurpleConvChat *chat, const char *who, const char *topic)
1473 g_return_if_fail(chat != NULL);
1475 g_free(chat->who);
1476 g_free(chat->topic);
1478 chat->who = g_strdup(who);
1479 chat->topic = g_strdup(topic);
1481 purple_conversation_update(purple_conv_chat_get_conversation(chat),
1482 PURPLE_CONV_UPDATE_TOPIC);
1484 purple_signal_emit(purple_conversations_get_handle(), "chat-topic-changed",
1485 chat->conv, chat->who, chat->topic);
1488 const char *
1489 purple_conv_chat_get_topic(const PurpleConvChat *chat)
1491 g_return_val_if_fail(chat != NULL, NULL);
1493 return chat->topic;
1496 void
1497 purple_conv_chat_set_id(PurpleConvChat *chat, int id)
1499 g_return_if_fail(chat != NULL);
1501 chat->id = id;
1505 purple_conv_chat_get_id(const PurpleConvChat *chat)
1507 g_return_val_if_fail(chat != NULL, -1);
1509 return chat->id;
1512 void
1513 purple_conv_chat_write(PurpleConvChat *chat, const char *who, const char *message,
1514 PurpleMessageFlags flags, time_t mtime)
1516 PurpleAccount *account;
1517 PurpleConversation *conv;
1518 PurpleConnection *gc;
1520 g_return_if_fail(chat != NULL);
1521 g_return_if_fail(who != NULL);
1522 g_return_if_fail(message != NULL);
1524 conv = purple_conv_chat_get_conversation(chat);
1525 gc = purple_conversation_get_gc(conv);
1526 account = purple_connection_get_account(gc);
1528 /* Don't display this if the person who wrote it is ignored. */
1529 if (purple_conv_chat_is_user_ignored(chat, who))
1530 return;
1532 if (!(flags & PURPLE_MESSAGE_WHISPER)) {
1533 const char *str;
1535 str = purple_normalize(account, who);
1537 if (purple_strequal(str, chat->nick)) {
1538 flags |= PURPLE_MESSAGE_SEND;
1539 } else {
1540 flags |= PURPLE_MESSAGE_RECV;
1542 if (purple_utf8_has_word(message, chat->nick))
1543 flags |= PURPLE_MESSAGE_NICK;
1547 /* Pass this on to either the ops structure or the default write func. */
1548 if (conv->ui_ops != NULL && conv->ui_ops->write_chat != NULL)
1549 conv->ui_ops->write_chat(conv, who, message, flags, mtime);
1550 else
1551 purple_conversation_write(conv, who, message, flags, mtime);
1554 void
1555 purple_conv_chat_send(PurpleConvChat *chat, const char *message)
1557 purple_conv_chat_send_with_flags(chat, message, 0);
1560 void
1561 purple_conv_chat_send_with_flags(PurpleConvChat *chat, const char *message, PurpleMessageFlags flags)
1563 g_return_if_fail(chat != NULL);
1564 g_return_if_fail(message != NULL);
1566 common_send(purple_conv_chat_get_conversation(chat), message, flags);
1569 void
1570 purple_conv_chat_add_user(PurpleConvChat *chat, const char *user,
1571 const char *extra_msg, PurpleConvChatBuddyFlags flags,
1572 gboolean new_arrival)
1574 GList *users = g_list_append(NULL, (char *)user);
1575 GList *extra_msgs = g_list_append(NULL, (char *)extra_msg);
1576 GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags));
1578 purple_conv_chat_add_users(chat, users, extra_msgs, flags2, new_arrival);
1580 g_list_free(users);
1581 g_list_free(extra_msgs);
1582 g_list_free(flags2);
1585 static int
1586 purple_conv_chat_cb_compare(PurpleConvChatBuddy *a, PurpleConvChatBuddy *b)
1588 PurpleConvChatBuddyFlags f1 = 0, f2 = 0;
1589 char *user1 = NULL, *user2 = NULL;
1590 gint ret = 0;
1592 if (a) {
1593 f1 = a->flags;
1594 if (a->alias_key)
1595 user1 = a->alias_key;
1596 else if (a->name)
1597 user1 = a->name;
1600 if (b) {
1601 f2 = b->flags;
1602 if (b->alias_key)
1603 user2 = b->alias_key;
1604 else if (b->name)
1605 user2 = b->name;
1608 if (user1 == NULL || user2 == NULL) {
1609 if (!(user1 == NULL && user2 == NULL))
1610 ret = (user1 == NULL) ? -1: 1;
1611 } else if (f1 != f2) {
1612 /* sort more important users first */
1613 ret = (f1 > f2) ? -1 : 1;
1614 } else if (a->buddy != b->buddy) {
1615 ret = a->buddy ? -1 : 1;
1616 } else {
1617 ret = purple_utf8_strcasecmp(user1, user2);
1620 return ret;
1623 void
1624 purple_conv_chat_add_users(PurpleConvChat *chat, GList *users, GList *extra_msgs,
1625 GList *flags, gboolean new_arrivals)
1627 PurpleConversation *conv;
1628 PurpleConversationUiOps *ops;
1629 PurpleConvChatBuddy *cbuddy;
1630 PurpleConnection *gc;
1631 PurplePluginProtocolInfo *prpl_info;
1632 GList *ul, *fl;
1633 GList *cbuddies = NULL;
1635 g_return_if_fail(chat != NULL);
1636 g_return_if_fail(users != NULL);
1638 conv = purple_conv_chat_get_conversation(chat);
1639 ops = purple_conversation_get_ui_ops(conv);
1641 gc = purple_conversation_get_gc(conv);
1642 g_return_if_fail(gc != NULL);
1643 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
1644 g_return_if_fail(prpl_info != NULL);
1646 ul = users;
1647 fl = flags;
1648 while ((ul != NULL) && (fl != NULL)) {
1649 const char *user = (const char *)ul->data;
1650 const char *alias = user;
1651 gboolean quiet;
1652 PurpleConvChatBuddyFlags flag = GPOINTER_TO_INT(fl->data);
1653 const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
1655 if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1656 if (purple_strequal(chat->nick, purple_normalize(conv->account, user))) {
1657 const char *alias2 = purple_account_get_alias(conv->account);
1658 if (alias2 != NULL)
1659 alias = alias2;
1660 else
1662 const char *display_name = purple_connection_get_display_name(gc);
1663 if (display_name != NULL)
1664 alias = display_name;
1666 } else {
1667 PurpleBuddy *buddy;
1668 if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
1669 alias = purple_buddy_get_contact_alias(buddy);
1673 quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
1674 "chat-buddy-joining", conv, user, flag)) ||
1675 purple_conv_chat_is_user_ignored(chat, user);
1677 cbuddy = purple_conv_chat_cb_new(user, alias, flag);
1678 cbuddy->buddy = purple_find_buddy(conv->account, user) != NULL;
1679 /* This seems dumb. Why should we set users thousands of times? */
1680 purple_conv_chat_set_users(chat,
1681 g_list_prepend(chat->in_room, cbuddy));
1683 cbuddies = g_list_prepend(cbuddies, cbuddy);
1685 if (!quiet && new_arrivals) {
1686 char *alias_esc = g_markup_escape_text(alias, -1);
1687 char *tmp;
1689 if (extra_msg == NULL)
1690 tmp = g_strdup_printf(_("%s entered the room."), alias_esc);
1691 else {
1692 char *extra_msg_esc = g_markup_escape_text(extra_msg, -1);
1693 tmp = g_strdup_printf(_("%s [<I>%s</I>] entered the room."),
1694 alias_esc, extra_msg_esc);
1695 g_free(extra_msg_esc);
1697 g_free(alias_esc);
1699 purple_conversation_write(conv, NULL, tmp,
1700 PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
1701 time(NULL));
1702 g_free(tmp);
1705 purple_signal_emit(purple_conversations_get_handle(),
1706 "chat-buddy-joined", conv, user, flag, new_arrivals);
1707 ul = ul->next;
1708 fl = fl->next;
1709 if (extra_msgs != NULL)
1710 extra_msgs = extra_msgs->next;
1713 cbuddies = g_list_sort(cbuddies, (GCompareFunc)purple_conv_chat_cb_compare);
1715 if (ops != NULL && ops->chat_add_users != NULL)
1716 ops->chat_add_users(conv, cbuddies, new_arrivals);
1718 g_list_free(cbuddies);
1721 void
1722 purple_conv_chat_rename_user(PurpleConvChat *chat, const char *old_user,
1723 const char *new_user)
1725 PurpleConversation *conv;
1726 PurpleConversationUiOps *ops;
1727 PurpleConnection *gc;
1728 PurplePluginProtocolInfo *prpl_info;
1729 PurpleConvChatBuddy *cb;
1730 PurpleConvChatBuddyFlags flags;
1731 const char *new_alias = new_user;
1732 char tmp[BUF_LONG];
1733 gboolean is_me = FALSE;
1735 g_return_if_fail(chat != NULL);
1736 g_return_if_fail(old_user != NULL);
1737 g_return_if_fail(new_user != NULL);
1739 conv = purple_conv_chat_get_conversation(chat);
1740 ops = purple_conversation_get_ui_ops(conv);
1742 gc = purple_conversation_get_gc(conv);
1743 g_return_if_fail(gc != NULL);
1744 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
1745 g_return_if_fail(prpl_info != NULL);
1747 if (purple_strequal(chat->nick, purple_normalize(conv->account, old_user))) {
1748 const char *alias;
1750 /* Note this for later. */
1751 is_me = TRUE;
1753 if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1754 alias = purple_account_get_alias(conv->account);
1755 if (alias != NULL)
1756 new_alias = alias;
1757 else
1759 const char *display_name = purple_connection_get_display_name(gc);
1760 if (display_name != NULL)
1761 alias = display_name;
1764 } else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1765 PurpleBuddy *buddy;
1766 if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
1767 new_alias = purple_buddy_get_contact_alias(buddy);
1770 flags = purple_conv_chat_user_get_flags(chat, old_user);
1771 cb = purple_conv_chat_cb_new(new_user, new_alias, flags);
1772 cb->buddy = purple_find_buddy(conv->account, new_user) != NULL;
1773 purple_conv_chat_set_users(chat,
1774 g_list_prepend(chat->in_room, cb));
1776 if (ops != NULL && ops->chat_rename_user != NULL)
1777 ops->chat_rename_user(conv, old_user, new_user, new_alias);
1779 cb = purple_conv_chat_cb_find(chat, old_user);
1781 if (cb) {
1782 purple_conv_chat_set_users(chat,
1783 g_list_remove(chat->in_room, cb));
1784 purple_conv_chat_cb_destroy(cb);
1787 if (purple_conv_chat_is_user_ignored(chat, old_user)) {
1788 purple_conv_chat_unignore(chat, old_user);
1789 purple_conv_chat_ignore(chat, new_user);
1791 else if (purple_conv_chat_is_user_ignored(chat, new_user))
1792 purple_conv_chat_unignore(chat, new_user);
1794 if (is_me)
1795 purple_conv_chat_set_nick(chat, new_user);
1797 if (purple_prefs_get_bool("/purple/conversations/chat/show_nick_change") &&
1798 !purple_conv_chat_is_user_ignored(chat, new_user)) {
1800 if (is_me) {
1801 char *escaped = g_markup_escape_text(new_user, -1);
1802 g_snprintf(tmp, sizeof(tmp),
1803 _("You are now known as %s"), escaped);
1804 g_free(escaped);
1805 } else {
1806 const char *old_alias = old_user;
1807 const char *new_alias = new_user;
1808 char *escaped;
1809 char *escaped2;
1811 if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1812 PurpleBuddy *buddy;
1814 if ((buddy = purple_find_buddy(gc->account, old_user)) != NULL)
1815 old_alias = purple_buddy_get_contact_alias(buddy);
1816 if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
1817 new_alias = purple_buddy_get_contact_alias(buddy);
1820 escaped = g_markup_escape_text(old_alias, -1);
1821 escaped2 = g_markup_escape_text(new_alias, -1);
1822 g_snprintf(tmp, sizeof(tmp),
1823 _("%s is now known as %s"), escaped, escaped2);
1824 g_free(escaped);
1825 g_free(escaped2);
1828 purple_conversation_write(conv, NULL, tmp,
1829 PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
1830 time(NULL));
1834 void
1835 purple_conv_chat_remove_user(PurpleConvChat *chat, const char *user, const char *reason)
1837 GList *users = g_list_append(NULL, (char *)user);
1839 purple_conv_chat_remove_users(chat, users, reason);
1841 g_list_free(users);
1844 void
1845 purple_conv_chat_remove_users(PurpleConvChat *chat, GList *users, const char *reason)
1847 PurpleConversation *conv;
1848 PurpleConnection *gc;
1849 PurplePluginProtocolInfo *prpl_info;
1850 PurpleConversationUiOps *ops;
1851 PurpleConvChatBuddy *cb;
1852 GList *l;
1853 gboolean quiet;
1855 g_return_if_fail(chat != NULL);
1856 g_return_if_fail(users != NULL);
1858 conv = purple_conv_chat_get_conversation(chat);
1860 gc = purple_conversation_get_gc(conv);
1861 g_return_if_fail(gc != NULL);
1862 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
1863 g_return_if_fail(prpl_info != NULL);
1865 ops = purple_conversation_get_ui_ops(conv);
1867 for (l = users; l != NULL; l = l->next) {
1868 const char *user = (const char *)l->data;
1869 quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
1870 "chat-buddy-leaving", conv, user, reason)) |
1871 purple_conv_chat_is_user_ignored(chat, user);
1873 cb = purple_conv_chat_cb_find(chat, user);
1875 if (cb) {
1876 purple_conv_chat_set_users(chat,
1877 g_list_remove(chat->in_room, cb));
1878 purple_conv_chat_cb_destroy(cb);
1881 /* NOTE: Don't remove them from ignored in case they re-enter. */
1883 if (!quiet) {
1884 const char *alias = user;
1885 char *alias_esc;
1886 char *tmp;
1888 if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1889 PurpleBuddy *buddy;
1891 if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
1892 alias = purple_buddy_get_contact_alias(buddy);
1895 alias_esc = g_markup_escape_text(alias, -1);
1897 if (reason == NULL || !*reason)
1898 tmp = g_strdup_printf(_("%s left the room."), alias_esc);
1899 else {
1900 char *reason_esc = g_markup_escape_text(reason, -1);
1901 tmp = g_strdup_printf(_("%s left the room (%s)."),
1902 alias_esc, reason_esc);
1903 g_free(reason_esc);
1905 g_free(alias_esc);
1907 purple_conversation_write(conv, NULL, tmp,
1908 PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
1909 time(NULL));
1910 g_free(tmp);
1913 purple_signal_emit(purple_conversations_get_handle(), "chat-buddy-left",
1914 conv, user, reason);
1917 if (ops != NULL && ops->chat_remove_users != NULL)
1918 ops->chat_remove_users(conv, users);
1921 void
1922 purple_conv_chat_clear_users(PurpleConvChat *chat)
1924 PurpleConversation *conv;
1925 PurpleConversationUiOps *ops;
1926 GList *users;
1927 GList *l;
1928 GList *names = NULL;
1930 g_return_if_fail(chat != NULL);
1932 conv = purple_conv_chat_get_conversation(chat);
1933 ops = purple_conversation_get_ui_ops(conv);
1934 users = chat->in_room;
1936 if (ops != NULL && ops->chat_remove_users != NULL) {
1937 for (l = users; l; l = l->next) {
1938 PurpleConvChatBuddy *cb = l->data;
1939 names = g_list_prepend(names, cb->name);
1941 ops->chat_remove_users(conv, names);
1942 g_list_free(names);
1945 for (l = users; l; l = l->next)
1947 PurpleConvChatBuddy *cb = l->data;
1949 purple_signal_emit(purple_conversations_get_handle(),
1950 "chat-buddy-leaving", conv, cb->name, NULL);
1951 purple_signal_emit(purple_conversations_get_handle(),
1952 "chat-buddy-left", conv, cb->name, NULL);
1954 purple_conv_chat_cb_destroy(cb);
1957 g_list_free(users);
1958 purple_conv_chat_set_users(chat, NULL);
1962 gboolean
1963 purple_conv_chat_find_user(PurpleConvChat *chat, const char *user)
1965 g_return_val_if_fail(chat != NULL, FALSE);
1966 g_return_val_if_fail(user != NULL, FALSE);
1968 return (purple_conv_chat_cb_find(chat, user) != NULL);
1971 void
1972 purple_conv_chat_user_set_flags(PurpleConvChat *chat, const char *user,
1973 PurpleConvChatBuddyFlags flags)
1975 PurpleConversation *conv;
1976 PurpleConversationUiOps *ops;
1977 PurpleConvChatBuddy *cb;
1978 PurpleConvChatBuddyFlags oldflags;
1980 g_return_if_fail(chat != NULL);
1981 g_return_if_fail(user != NULL);
1983 cb = purple_conv_chat_cb_find(chat, user);
1985 if (!cb)
1986 return;
1988 if (flags == cb->flags)
1989 return;
1991 oldflags = cb->flags;
1992 cb->flags = flags;
1994 conv = purple_conv_chat_get_conversation(chat);
1995 ops = purple_conversation_get_ui_ops(conv);
1997 if (ops != NULL && ops->chat_update_user != NULL)
1998 ops->chat_update_user(conv, user);
2000 purple_signal_emit(purple_conversations_get_handle(),
2001 "chat-buddy-flags", conv, user, oldflags, flags);
2004 PurpleConvChatBuddyFlags
2005 purple_conv_chat_user_get_flags(PurpleConvChat *chat, const char *user)
2007 PurpleConvChatBuddy *cb;
2009 g_return_val_if_fail(chat != NULL, 0);
2010 g_return_val_if_fail(user != NULL, 0);
2012 cb = purple_conv_chat_cb_find(chat, user);
2014 if (!cb)
2015 return PURPLE_CBFLAGS_NONE;
2017 return cb->flags;
2020 void purple_conv_chat_set_nick(PurpleConvChat *chat, const char *nick) {
2021 g_return_if_fail(chat != NULL);
2023 g_free(chat->nick);
2024 chat->nick = g_strdup(purple_normalize(chat->conv->account, nick));
2027 const char *purple_conv_chat_get_nick(PurpleConvChat *chat) {
2028 g_return_val_if_fail(chat != NULL, NULL);
2030 return chat->nick;
2033 PurpleConversation *
2034 purple_find_chat(const PurpleConnection *gc, int id)
2036 GList *l;
2037 PurpleConversation *conv;
2039 for (l = purple_get_chats(); l != NULL; l = l->next) {
2040 conv = (PurpleConversation *)l->data;
2042 if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)) == id &&
2043 purple_conversation_get_gc(conv) == gc)
2044 return conv;
2047 return NULL;
2050 void
2051 purple_conv_chat_left(PurpleConvChat *chat)
2053 g_return_if_fail(chat != NULL);
2055 chat->left = TRUE;
2056 purple_conversation_update(chat->conv, PURPLE_CONV_UPDATE_CHATLEFT);
2059 static void
2060 invite_user_to_chat(gpointer data, PurpleRequestFields *fields)
2062 PurpleConversation *conv;
2063 PurpleConvChat *chat;
2064 const char *user, *message;
2066 conv = data;
2067 chat = PURPLE_CONV_CHAT(conv);
2068 user = purple_request_fields_get_string(fields, "screenname");
2069 message = purple_request_fields_get_string(fields, "message");
2071 serv_chat_invite(purple_conversation_get_gc(conv), chat->id, message, user);
2074 void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user,
2075 const char *message, gboolean confirm)
2077 PurpleAccount *account;
2078 PurpleConversation *conv;
2079 PurpleRequestFields *fields;
2080 PurpleRequestFieldGroup *group;
2081 PurpleRequestField *field;
2083 g_return_if_fail(chat);
2085 if (!user || !*user || !message || !*message)
2086 confirm = TRUE;
2088 conv = chat->conv;
2089 account = conv->account;
2091 if (!confirm) {
2092 serv_chat_invite(purple_account_get_connection(account),
2093 purple_conv_chat_get_id(chat), message, user);
2094 return;
2097 fields = purple_request_fields_new();
2098 group = purple_request_field_group_new(_("Invite to chat"));
2099 purple_request_fields_add_group(fields, group);
2101 field = purple_request_field_string_new("screenname", _("Buddy"), user, FALSE);
2102 purple_request_field_group_add_field(group, field);
2103 purple_request_field_set_required(field, TRUE);
2104 purple_request_field_set_type_hint(field, "screenname");
2106 field = purple_request_field_string_new("message", _("Message"), message, FALSE);
2107 purple_request_field_group_add_field(group, field);
2109 purple_request_fields(conv, _("Invite to chat"), NULL,
2110 _("Please enter the name of the user you wish to invite, "
2111 "along with an optional invite message."),
2112 fields,
2113 _("Invite"), G_CALLBACK(invite_user_to_chat),
2114 _("Cancel"), NULL,
2115 account, user, conv,
2116 conv);
2119 gboolean
2120 purple_conv_chat_has_left(PurpleConvChat *chat)
2122 g_return_val_if_fail(chat != NULL, TRUE);
2124 return chat->left;
2127 PurpleConvChatBuddy *
2128 purple_conv_chat_cb_new(const char *name, const char *alias, PurpleConvChatBuddyFlags flags)
2130 PurpleConvChatBuddy *cb;
2132 g_return_val_if_fail(name != NULL, NULL);
2134 cb = g_new0(PurpleConvChatBuddy, 1);
2135 cb->name = g_strdup(name);
2136 cb->flags = flags;
2137 cb->alias = g_strdup(alias);
2138 cb->attributes = g_hash_table_new_full(g_str_hash, g_str_equal,
2139 g_free, g_free);
2141 PURPLE_DBUS_REGISTER_POINTER(cb, PurpleConvChatBuddy);
2142 return cb;
2145 PurpleConvChatBuddy *
2146 purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name)
2148 GList *l;
2149 PurpleConvChatBuddy *cb = NULL;
2151 g_return_val_if_fail(chat != NULL, NULL);
2152 g_return_val_if_fail(name != NULL, NULL);
2154 for (l = purple_conv_chat_get_users(chat); l; l = l->next) {
2155 cb = l->data;
2156 if (!g_utf8_collate(cb->name, name))
2157 return cb;
2160 return NULL;
2163 void
2164 purple_conv_chat_cb_destroy(PurpleConvChatBuddy *cb)
2166 if (cb == NULL)
2167 return;
2169 g_free(cb->alias);
2170 g_free(cb->alias_key);
2171 g_free(cb->name);
2172 g_hash_table_destroy(cb->attributes);
2174 PURPLE_DBUS_UNREGISTER_POINTER(cb);
2175 g_free(cb);
2178 const char *
2179 purple_conv_chat_cb_get_name(PurpleConvChatBuddy *cb)
2181 g_return_val_if_fail(cb != NULL, NULL);
2183 return cb->name;
2186 const char *
2187 purple_conv_chat_cb_get_attribute(PurpleConvChatBuddy *cb, const char *key)
2189 g_return_val_if_fail(cb != NULL, NULL);
2190 g_return_val_if_fail(key != NULL, NULL);
2192 return g_hash_table_lookup(cb->attributes, key);
2195 static void
2196 append_attribute_key(gpointer key, gpointer value, gpointer user_data)
2198 GList **list = user_data;
2199 *list = g_list_prepend(*list, key);
2202 GList *
2203 purple_conv_chat_cb_get_attribute_keys(PurpleConvChatBuddy *cb)
2205 GList *keys = NULL;
2207 g_return_val_if_fail(cb != NULL, NULL);
2209 g_hash_table_foreach(cb->attributes, (GHFunc)append_attribute_key, &keys);
2211 return keys;
2214 void
2215 purple_conv_chat_cb_set_attribute(PurpleConvChat *chat, PurpleConvChatBuddy *cb, const char *key, const char *value)
2217 PurpleConversation *conv;
2218 PurpleConversationUiOps *ops;
2220 g_return_if_fail(cb != NULL);
2221 g_return_if_fail(key != NULL);
2222 g_return_if_fail(value != NULL);
2224 g_hash_table_replace(cb->attributes, g_strdup(key), g_strdup(value));
2226 conv = purple_conv_chat_get_conversation(chat);
2227 ops = purple_conversation_get_ui_ops(conv);
2229 if (ops != NULL && ops->chat_update_user != NULL)
2230 ops->chat_update_user(conv, cb->name);
2233 void
2234 purple_conv_chat_cb_set_attributes(PurpleConvChat *chat, PurpleConvChatBuddy *cb, GList *keys, GList *values)
2236 PurpleConversation *conv;
2237 PurpleConversationUiOps *ops;
2239 g_return_if_fail(cb != NULL);
2240 g_return_if_fail(keys != NULL);
2241 g_return_if_fail(values != NULL);
2243 while (keys != NULL && values != NULL) {
2244 g_hash_table_replace(cb->attributes, g_strdup(keys->data), g_strdup(values->data));
2245 keys = g_list_next(keys);
2246 values = g_list_next(values);
2249 conv = purple_conv_chat_get_conversation(chat);
2250 ops = purple_conversation_get_ui_ops(conv);
2252 if (ops != NULL && ops->chat_update_user != NULL)
2253 ops->chat_update_user(conv, cb->name);
2256 GList *
2257 purple_conversation_get_extended_menu(PurpleConversation *conv)
2259 GList *menu = NULL;
2261 g_return_val_if_fail(conv != NULL, NULL);
2263 purple_signal_emit(purple_conversations_get_handle(),
2264 "conversation-extended-menu", conv, &menu);
2265 return menu;
2268 void purple_conversation_clear_message_history(PurpleConversation *conv)
2270 GList *list = conv->message_history;
2271 message_history_free(list);
2272 conv->message_history = NULL;
2275 GList *purple_conversation_get_message_history(PurpleConversation *conv)
2277 return conv->message_history;
2280 const char *purple_conversation_message_get_sender(PurpleConvMessage *msg)
2282 g_return_val_if_fail(msg, NULL);
2283 return msg->who;
2286 const char *purple_conversation_message_get_message(PurpleConvMessage *msg)
2288 g_return_val_if_fail(msg, NULL);
2289 return msg->what;
2292 PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg)
2294 g_return_val_if_fail(msg, 0);
2295 return msg->flags;
2298 time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg)
2300 g_return_val_if_fail(msg, 0);
2301 return msg->when;
2304 gboolean
2305 purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
2306 const gchar *markup, gchar **error)
2308 char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
2309 PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
2310 g_free(mark);
2311 g_free(err);
2312 return (status == PURPLE_CMD_STATUS_OK);
2315 void *
2316 purple_conversations_get_handle(void)
2318 static int handle;
2320 return &handle;
2323 void
2324 purple_conversations_init(void)
2326 void *handle = purple_conversations_get_handle();
2328 conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
2329 (GEqualFunc)_purple_conversations_hconv_equal,
2330 (GDestroyNotify)_purple_conversations_hconv_free_key, NULL);
2332 /**********************************************************************
2333 * Register preferences
2334 **********************************************************************/
2336 /* Conversations */
2337 purple_prefs_add_none("/purple/conversations");
2339 /* Conversations -> Chat */
2340 purple_prefs_add_none("/purple/conversations/chat");
2341 purple_prefs_add_bool("/purple/conversations/chat/show_nick_change", TRUE);
2343 /* Conversations -> IM */
2344 purple_prefs_add_none("/purple/conversations/im");
2345 purple_prefs_add_bool("/purple/conversations/im/send_typing", TRUE);
2348 /**********************************************************************
2349 * Register signals
2350 **********************************************************************/
2351 purple_signal_register(handle, "writing-im-msg",
2352 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT,
2353 purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
2354 purple_value_new(PURPLE_TYPE_SUBTYPE,
2355 PURPLE_SUBTYPE_ACCOUNT),
2356 purple_value_new(PURPLE_TYPE_STRING),
2357 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2358 purple_value_new(PURPLE_TYPE_SUBTYPE,
2359 PURPLE_SUBTYPE_CONVERSATION),
2360 purple_value_new(PURPLE_TYPE_UINT));
2362 purple_signal_register(handle, "wrote-im-msg",
2363 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
2364 NULL, 5,
2365 purple_value_new(PURPLE_TYPE_SUBTYPE,
2366 PURPLE_SUBTYPE_ACCOUNT),
2367 purple_value_new(PURPLE_TYPE_STRING),
2368 purple_value_new(PURPLE_TYPE_STRING),
2369 purple_value_new(PURPLE_TYPE_SUBTYPE,
2370 PURPLE_SUBTYPE_CONVERSATION),
2371 purple_value_new(PURPLE_TYPE_UINT));
2373 purple_signal_register(handle, "sent-attention",
2374 purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
2375 NULL, 4,
2376 purple_value_new(PURPLE_TYPE_SUBTYPE,
2377 PURPLE_SUBTYPE_ACCOUNT),
2378 purple_value_new(PURPLE_TYPE_STRING),
2379 purple_value_new(PURPLE_TYPE_SUBTYPE,
2380 PURPLE_SUBTYPE_CONVERSATION),
2381 purple_value_new(PURPLE_TYPE_UINT));
2383 purple_signal_register(handle, "got-attention",
2384 purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
2385 NULL, 4,
2386 purple_value_new(PURPLE_TYPE_SUBTYPE,
2387 PURPLE_SUBTYPE_ACCOUNT),
2388 purple_value_new(PURPLE_TYPE_STRING),
2389 purple_value_new(PURPLE_TYPE_SUBTYPE,
2390 PURPLE_SUBTYPE_CONVERSATION),
2391 purple_value_new(PURPLE_TYPE_UINT));
2393 purple_signal_register(handle, "sending-im-msg",
2394 purple_marshal_VOID__POINTER_POINTER_POINTER,
2395 NULL, 3,
2396 purple_value_new(PURPLE_TYPE_SUBTYPE,
2397 PURPLE_SUBTYPE_ACCOUNT),
2398 purple_value_new(PURPLE_TYPE_STRING),
2399 purple_value_new_outgoing(PURPLE_TYPE_STRING));
2401 purple_signal_register(handle, "sent-im-msg",
2402 purple_marshal_VOID__POINTER_POINTER_POINTER,
2403 NULL, 3,
2404 purple_value_new(PURPLE_TYPE_SUBTYPE,
2405 PURPLE_SUBTYPE_ACCOUNT),
2406 purple_value_new(PURPLE_TYPE_STRING),
2407 purple_value_new(PURPLE_TYPE_STRING));
2409 purple_signal_register(handle, "receiving-im-msg",
2410 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
2411 purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
2412 purple_value_new(PURPLE_TYPE_SUBTYPE,
2413 PURPLE_SUBTYPE_ACCOUNT),
2414 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2415 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2416 purple_value_new(PURPLE_TYPE_SUBTYPE,
2417 PURPLE_SUBTYPE_CONVERSATION),
2418 purple_value_new_outgoing(PURPLE_TYPE_UINT));
2420 purple_signal_register(handle, "received-im-msg",
2421 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
2422 NULL, 5,
2423 purple_value_new(PURPLE_TYPE_SUBTYPE,
2424 PURPLE_SUBTYPE_ACCOUNT),
2425 purple_value_new(PURPLE_TYPE_STRING),
2426 purple_value_new(PURPLE_TYPE_STRING),
2427 purple_value_new(PURPLE_TYPE_SUBTYPE,
2428 PURPLE_SUBTYPE_CONVERSATION),
2429 purple_value_new(PURPLE_TYPE_UINT));
2431 purple_signal_register(handle, "blocked-im-msg",
2432 purple_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT,
2433 NULL, 5,
2434 purple_value_new(PURPLE_TYPE_SUBTYPE,
2435 PURPLE_SUBTYPE_ACCOUNT),
2436 purple_value_new(PURPLE_TYPE_STRING),
2437 purple_value_new(PURPLE_TYPE_STRING),
2438 purple_value_new(PURPLE_TYPE_UINT),
2439 purple_value_new(PURPLE_TYPE_UINT));
2441 purple_signal_register(handle, "writing-chat-msg",
2442 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT,
2443 purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
2444 purple_value_new(PURPLE_TYPE_SUBTYPE,
2445 PURPLE_SUBTYPE_ACCOUNT),
2446 purple_value_new(PURPLE_TYPE_STRING),
2447 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2448 purple_value_new(PURPLE_TYPE_SUBTYPE,
2449 PURPLE_SUBTYPE_CONVERSATION),
2450 purple_value_new(PURPLE_TYPE_UINT));
2452 purple_signal_register(handle, "wrote-chat-msg",
2453 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
2454 NULL, 5,
2455 purple_value_new(PURPLE_TYPE_SUBTYPE,
2456 PURPLE_SUBTYPE_ACCOUNT),
2457 purple_value_new(PURPLE_TYPE_STRING),
2458 purple_value_new(PURPLE_TYPE_STRING),
2459 purple_value_new(PURPLE_TYPE_SUBTYPE,
2460 PURPLE_SUBTYPE_CONVERSATION),
2461 purple_value_new(PURPLE_TYPE_UINT));
2463 purple_signal_register(handle, "sending-chat-msg",
2464 purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
2465 purple_value_new(PURPLE_TYPE_SUBTYPE,
2466 PURPLE_SUBTYPE_ACCOUNT),
2467 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2468 purple_value_new(PURPLE_TYPE_UINT));
2470 purple_signal_register(handle, "sent-chat-msg",
2471 purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
2472 purple_value_new(PURPLE_TYPE_SUBTYPE,
2473 PURPLE_SUBTYPE_ACCOUNT),
2474 purple_value_new(PURPLE_TYPE_STRING),
2475 purple_value_new(PURPLE_TYPE_UINT));
2477 purple_signal_register(handle, "receiving-chat-msg",
2478 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
2479 purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
2480 purple_value_new(PURPLE_TYPE_SUBTYPE,
2481 PURPLE_SUBTYPE_ACCOUNT),
2482 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2483 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2484 purple_value_new(PURPLE_TYPE_SUBTYPE,
2485 PURPLE_SUBTYPE_CONVERSATION),
2486 purple_value_new_outgoing(PURPLE_TYPE_UINT));
2488 purple_signal_register(handle, "received-chat-msg",
2489 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
2490 NULL, 5,
2491 purple_value_new(PURPLE_TYPE_SUBTYPE,
2492 PURPLE_SUBTYPE_ACCOUNT),
2493 purple_value_new(PURPLE_TYPE_STRING),
2494 purple_value_new(PURPLE_TYPE_STRING),
2495 purple_value_new(PURPLE_TYPE_SUBTYPE,
2496 PURPLE_SUBTYPE_CONVERSATION),
2497 purple_value_new(PURPLE_TYPE_UINT));
2499 purple_signal_register(handle, "conversation-created",
2500 purple_marshal_VOID__POINTER, NULL, 1,
2501 purple_value_new(PURPLE_TYPE_SUBTYPE,
2502 PURPLE_SUBTYPE_CONVERSATION));
2504 purple_signal_register(handle, "conversation-updated",
2505 purple_marshal_VOID__POINTER_UINT, NULL, 2,
2506 purple_value_new(PURPLE_TYPE_SUBTYPE,
2507 PURPLE_SUBTYPE_CONVERSATION),
2508 purple_value_new(PURPLE_TYPE_UINT));
2510 purple_signal_register(handle, "deleting-conversation",
2511 purple_marshal_VOID__POINTER, NULL, 1,
2512 purple_value_new(PURPLE_TYPE_SUBTYPE,
2513 PURPLE_SUBTYPE_CONVERSATION));
2515 purple_signal_register(handle, "buddy-typing",
2516 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2517 purple_value_new(PURPLE_TYPE_SUBTYPE,
2518 PURPLE_SUBTYPE_ACCOUNT),
2519 purple_value_new(PURPLE_TYPE_STRING));
2521 purple_signal_register(handle, "buddy-typed",
2522 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2523 purple_value_new(PURPLE_TYPE_SUBTYPE,
2524 PURPLE_SUBTYPE_ACCOUNT),
2525 purple_value_new(PURPLE_TYPE_STRING));
2527 purple_signal_register(handle, "buddy-typing-stopped",
2528 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2529 purple_value_new(PURPLE_TYPE_SUBTYPE,
2530 PURPLE_SUBTYPE_ACCOUNT),
2531 purple_value_new(PURPLE_TYPE_STRING));
2533 purple_signal_register(handle, "chat-buddy-joining",
2534 purple_marshal_BOOLEAN__POINTER_POINTER_UINT,
2535 purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
2536 purple_value_new(PURPLE_TYPE_SUBTYPE,
2537 PURPLE_SUBTYPE_CONVERSATION),
2538 purple_value_new(PURPLE_TYPE_STRING),
2539 purple_value_new(PURPLE_TYPE_UINT));
2541 purple_signal_register(handle, "chat-buddy-joined",
2542 purple_marshal_VOID__POINTER_POINTER_UINT_UINT, NULL, 4,
2543 purple_value_new(PURPLE_TYPE_SUBTYPE,
2544 PURPLE_SUBTYPE_CONVERSATION),
2545 purple_value_new(PURPLE_TYPE_STRING),
2546 purple_value_new(PURPLE_TYPE_UINT),
2547 purple_value_new(PURPLE_TYPE_BOOLEAN));
2549 purple_signal_register(handle, "chat-buddy-flags",
2550 purple_marshal_VOID__POINTER_POINTER_UINT_UINT, NULL, 4,
2551 purple_value_new(PURPLE_TYPE_SUBTYPE,
2552 PURPLE_SUBTYPE_CONVERSATION),
2553 purple_value_new(PURPLE_TYPE_STRING),
2554 purple_value_new(PURPLE_TYPE_UINT),
2555 purple_value_new(PURPLE_TYPE_UINT));
2557 purple_signal_register(handle, "chat-buddy-leaving",
2558 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
2559 purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
2560 purple_value_new(PURPLE_TYPE_SUBTYPE,
2561 PURPLE_SUBTYPE_CONVERSATION),
2562 purple_value_new(PURPLE_TYPE_STRING),
2563 purple_value_new(PURPLE_TYPE_STRING));
2565 purple_signal_register(handle, "chat-buddy-left",
2566 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
2567 purple_value_new(PURPLE_TYPE_SUBTYPE,
2568 PURPLE_SUBTYPE_CONVERSATION),
2569 purple_value_new(PURPLE_TYPE_STRING),
2570 purple_value_new(PURPLE_TYPE_STRING));
2572 purple_signal_register(handle, "chat-inviting-user",
2573 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
2574 purple_value_new(PURPLE_TYPE_SUBTYPE,
2575 PURPLE_SUBTYPE_CONVERSATION),
2576 purple_value_new(PURPLE_TYPE_STRING),
2577 purple_value_new_outgoing(PURPLE_TYPE_STRING));
2579 purple_signal_register(handle, "chat-invited-user",
2580 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
2581 purple_value_new(PURPLE_TYPE_SUBTYPE,
2582 PURPLE_SUBTYPE_CONVERSATION),
2583 purple_value_new(PURPLE_TYPE_STRING),
2584 purple_value_new(PURPLE_TYPE_STRING));
2586 purple_signal_register(handle, "chat-invited",
2587 purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER,
2588 purple_value_new(PURPLE_TYPE_INT), 5,
2589 purple_value_new(PURPLE_TYPE_SUBTYPE,
2590 PURPLE_SUBTYPE_ACCOUNT),
2591 purple_value_new(PURPLE_TYPE_STRING),
2592 purple_value_new(PURPLE_TYPE_STRING),
2593 purple_value_new(PURPLE_TYPE_STRING),
2594 purple_value_new(PURPLE_TYPE_POINTER));
2596 purple_signal_register(handle, "chat-invite-blocked",
2597 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER,
2598 NULL, 5,
2599 purple_value_new(PURPLE_TYPE_SUBTYPE,
2600 PURPLE_SUBTYPE_ACCOUNT),
2601 purple_value_new(PURPLE_TYPE_STRING),
2602 purple_value_new(PURPLE_TYPE_STRING),
2603 purple_value_new(PURPLE_TYPE_STRING),
2604 purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *"));
2606 purple_signal_register(handle, "chat-joined",
2607 purple_marshal_VOID__POINTER, NULL, 1,
2608 purple_value_new(PURPLE_TYPE_SUBTYPE,
2609 PURPLE_SUBTYPE_CONVERSATION));
2611 purple_signal_register(handle, "chat-join-failed",
2612 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2613 purple_value_new(PURPLE_TYPE_SUBTYPE,
2614 PURPLE_SUBTYPE_CONNECTION),
2615 purple_value_new(PURPLE_TYPE_POINTER));
2617 purple_signal_register(handle, "chat-left",
2618 purple_marshal_VOID__POINTER, NULL, 1,
2619 purple_value_new(PURPLE_TYPE_SUBTYPE,
2620 PURPLE_SUBTYPE_CONVERSATION));
2622 purple_signal_register(handle, "chat-topic-changed",
2623 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
2624 purple_value_new(PURPLE_TYPE_SUBTYPE,
2625 PURPLE_SUBTYPE_CONVERSATION),
2626 purple_value_new(PURPLE_TYPE_STRING),
2627 purple_value_new(PURPLE_TYPE_STRING));
2629 purple_signal_register(handle, "conversation-extended-menu",
2630 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2631 purple_value_new(PURPLE_TYPE_SUBTYPE,
2632 PURPLE_SUBTYPE_CONVERSATION),
2633 purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
2636 void
2637 purple_conversations_uninit(void)
2639 while (conversations)
2640 purple_conversation_destroy((PurpleConversation*)conversations->data);
2641 g_hash_table_destroy(conversation_cache);
2642 purple_signals_unregister_by_instance(purple_conversations_get_handle());