applied changes from fb4435d514398a0b1febebe8bf46339e2c2b52b6
[pidgin-git.git] / libpurple / conversation.c
blob2ac306e6457045ab437b2bbecd09bd8e4af0d28d
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);
578 conv->ui_data = NULL;
580 purple_conversation_close_logs(conv);
582 purple_conversation_clear_message_history(conv);
584 PURPLE_DBUS_UNREGISTER_POINTER(conv);
585 g_free(conv);
586 conv = NULL;
590 void
591 purple_conversation_present(PurpleConversation *conv) {
592 PurpleConversationUiOps *ops;
594 g_return_if_fail(conv != NULL);
596 ops = purple_conversation_get_ui_ops(conv);
597 if(ops && ops->present)
598 ops->present(conv);
602 void
603 purple_conversation_set_features(PurpleConversation *conv, PurpleConnectionFlags features)
605 g_return_if_fail(conv != NULL);
607 conv->features = features;
609 purple_conversation_update(conv, PURPLE_CONV_UPDATE_FEATURES);
613 PurpleConnectionFlags
614 purple_conversation_get_features(PurpleConversation *conv)
616 g_return_val_if_fail(conv != NULL, 0);
617 return conv->features;
621 PurpleConversationType
622 purple_conversation_get_type(const PurpleConversation *conv)
624 g_return_val_if_fail(conv != NULL, PURPLE_CONV_TYPE_UNKNOWN);
626 return conv->type;
629 void
630 purple_conversation_set_ui_ops(PurpleConversation *conv,
631 PurpleConversationUiOps *ops)
633 g_return_if_fail(conv != NULL);
635 if (conv->ui_ops == ops)
636 return;
638 if (conv->ui_ops != NULL && conv->ui_ops->destroy_conversation != NULL)
639 conv->ui_ops->destroy_conversation(conv);
641 conv->ui_data = NULL;
643 conv->ui_ops = ops;
646 PurpleConversationUiOps *
647 purple_conversation_get_ui_ops(const PurpleConversation *conv)
649 g_return_val_if_fail(conv != NULL, NULL);
651 return conv->ui_ops;
654 void
655 purple_conversation_set_account(PurpleConversation *conv, PurpleAccount *account)
657 g_return_if_fail(conv != NULL);
659 if (account == purple_conversation_get_account(conv))
660 return;
662 conv->account = account;
664 purple_conversation_update(conv, PURPLE_CONV_UPDATE_ACCOUNT);
667 PurpleAccount *
668 purple_conversation_get_account(const PurpleConversation *conv)
670 g_return_val_if_fail(conv != NULL, NULL);
672 return conv->account;
675 PurpleConnection *
676 purple_conversation_get_gc(const PurpleConversation *conv)
678 PurpleAccount *account;
680 g_return_val_if_fail(conv != NULL, NULL);
682 account = purple_conversation_get_account(conv);
684 if (account == NULL)
685 return NULL;
687 return account->gc;
690 void
691 purple_conversation_set_title(PurpleConversation *conv, const char *title)
693 g_return_if_fail(conv != NULL);
694 g_return_if_fail(title != NULL);
696 g_free(conv->title);
697 conv->title = g_strdup(title);
699 purple_conversation_update(conv, PURPLE_CONV_UPDATE_TITLE);
702 const char *
703 purple_conversation_get_title(const PurpleConversation *conv)
705 g_return_val_if_fail(conv != NULL, NULL);
707 return conv->title;
710 void
711 purple_conversation_autoset_title(PurpleConversation *conv)
713 PurpleAccount *account;
714 PurpleBuddy *b;
715 PurpleChat *chat;
716 const char *text = NULL, *name;
718 g_return_if_fail(conv != NULL);
720 account = purple_conversation_get_account(conv);
721 name = purple_conversation_get_name(conv);
723 if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
724 if(account && ((b = purple_find_buddy(account, name)) != NULL))
725 text = purple_buddy_get_contact_alias(b);
726 } else if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
727 if(account && ((chat = purple_blist_find_chat(account, name)) != NULL))
728 text = purple_chat_get_name(chat);
732 if(text == NULL)
733 text = name;
735 purple_conversation_set_title(conv, text);
738 void
739 purple_conversation_foreach(void (*func)(PurpleConversation *conv))
741 PurpleConversation *conv;
742 GList *l;
744 g_return_if_fail(func != NULL);
746 for (l = purple_get_conversations(); l != NULL; l = l->next) {
747 conv = (PurpleConversation *)l->data;
749 func(conv);
753 void
754 purple_conversation_set_name(PurpleConversation *conv, const char *name)
756 struct _purple_hconv *hc;
757 g_return_if_fail(conv != NULL);
759 hc = g_new(struct _purple_hconv, 1);
760 hc->type = conv->type;
761 hc->account = conv->account;
762 hc->name = (gchar *)purple_normalize(conv->account, conv->name);
764 g_hash_table_remove(conversation_cache, hc);
765 g_free(conv->name);
767 conv->name = g_strdup(name);
768 hc->name = g_strdup(purple_normalize(conv->account, conv->name));
769 g_hash_table_insert(conversation_cache, hc, conv);
771 purple_conversation_autoset_title(conv);
774 const char *
775 purple_conversation_get_name(const PurpleConversation *conv)
777 g_return_val_if_fail(conv != NULL, NULL);
779 return conv->name;
782 void
783 purple_conversation_set_logging(PurpleConversation *conv, gboolean log)
785 g_return_if_fail(conv != NULL);
787 if (conv->logging != log)
789 conv->logging = log;
790 purple_conversation_update(conv, PURPLE_CONV_UPDATE_LOGGING);
794 gboolean
795 purple_conversation_is_logging(const PurpleConversation *conv)
797 g_return_val_if_fail(conv != NULL, FALSE);
799 return conv->logging;
802 void
803 purple_conversation_close_logs(PurpleConversation *conv)
805 g_return_if_fail(conv != NULL);
807 g_list_foreach(conv->logs, (GFunc)purple_log_free, NULL);
808 g_list_free(conv->logs);
809 conv->logs = NULL;
812 PurpleConvIm *
813 purple_conversation_get_im_data(const PurpleConversation *conv)
815 g_return_val_if_fail(conv != NULL, NULL);
817 if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM)
818 return NULL;
820 return conv->u.im;
823 PurpleConvChat *
824 purple_conversation_get_chat_data(const PurpleConversation *conv)
826 g_return_val_if_fail(conv != NULL, NULL);
828 if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT)
829 return NULL;
831 return conv->u.chat;
834 void
835 purple_conversation_set_data(PurpleConversation *conv, const char *key,
836 gpointer data)
838 g_return_if_fail(conv != NULL);
839 g_return_if_fail(key != NULL);
841 g_hash_table_replace(conv->data, g_strdup(key), data);
844 gpointer
845 purple_conversation_get_data(PurpleConversation *conv, const char *key)
847 g_return_val_if_fail(conv != NULL, NULL);
848 g_return_val_if_fail(key != NULL, NULL);
850 return g_hash_table_lookup(conv->data, key);
853 GList *
854 purple_get_conversations(void)
856 return conversations;
859 GList *
860 purple_get_ims(void)
862 return ims;
865 GList *
866 purple_get_chats(void)
868 return chats;
872 PurpleConversation *
873 purple_find_conversation_with_account(PurpleConversationType type,
874 const char *name,
875 const PurpleAccount *account)
877 PurpleConversation *c = NULL;
878 struct _purple_hconv hc;
880 g_return_val_if_fail(name != NULL, NULL);
882 hc.name = (gchar *)purple_normalize(account, name);
883 hc.account = account;
884 hc.type = type;
886 switch (type) {
887 case PURPLE_CONV_TYPE_IM:
888 case PURPLE_CONV_TYPE_CHAT:
889 c = g_hash_table_lookup(conversation_cache, &hc);
890 break;
891 case PURPLE_CONV_TYPE_ANY:
892 hc.type = PURPLE_CONV_TYPE_IM;
893 c = g_hash_table_lookup(conversation_cache, &hc);
894 if (!c) {
895 hc.type = PURPLE_CONV_TYPE_CHAT;
896 c = g_hash_table_lookup(conversation_cache, &hc);
898 break;
899 default:
900 g_return_val_if_reached(NULL);
903 return c;
906 void
907 purple_conversation_write(PurpleConversation *conv, const char *who,
908 const char *message, PurpleMessageFlags flags,
909 time_t mtime)
911 PurplePluginProtocolInfo *prpl_info = NULL;
912 PurpleConnection *gc = NULL;
913 PurpleAccount *account;
914 PurpleConversationUiOps *ops;
915 const char *alias;
916 char *displayed = NULL;
917 PurpleBuddy *b;
918 int plugin_return;
919 PurpleConversationType type;
920 /* int logging_font_options = 0; */
922 g_return_if_fail(conv != NULL);
923 g_return_if_fail(message != NULL);
925 ops = purple_conversation_get_ui_ops(conv);
927 account = purple_conversation_get_account(conv);
928 type = purple_conversation_get_type(conv);
930 if (account != NULL)
931 gc = purple_account_get_connection(account);
933 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
934 (gc != NULL && !g_slist_find(gc->buddy_chats, conv)))
935 return;
937 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
938 !g_list_find(purple_get_conversations(), conv))
939 return;
941 displayed = g_strdup(message);
943 if (who == NULL || *who == '\0')
944 who = purple_conversation_get_name(conv);
945 alias = who;
947 plugin_return =
948 GPOINTER_TO_INT(purple_signal_emit_return_1(
949 purple_conversations_get_handle(),
950 (type == PURPLE_CONV_TYPE_IM ? "writing-im-msg" : "writing-chat-msg"),
951 account, who, &displayed, conv, flags));
953 if (displayed == NULL)
954 return;
956 if (plugin_return) {
957 g_free(displayed);
958 return;
961 if (account != NULL) {
962 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));
964 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM ||
965 !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
967 if (flags & PURPLE_MESSAGE_SEND) {
968 b = purple_find_buddy(account,
969 purple_account_get_username(account));
971 if (purple_account_get_alias(account) != NULL)
972 alias = account->alias;
973 else if (b != NULL && !purple_strequal(purple_buddy_get_name(b), purple_buddy_get_contact_alias(b)))
974 alias = purple_buddy_get_contact_alias(b);
975 else if (purple_connection_get_display_name(gc) != NULL)
976 alias = purple_connection_get_display_name(gc);
977 else
978 alias = purple_account_get_username(account);
980 else
982 b = purple_find_buddy(account, who);
984 if (b != NULL)
985 alias = purple_buddy_get_contact_alias(b);
990 if (!(flags & PURPLE_MESSAGE_NO_LOG) && purple_conversation_is_logging(conv)) {
991 GList *log;
993 if (conv->logs == NULL)
994 open_log(conv);
996 log = conv->logs;
997 while (log != NULL) {
998 purple_log_write((PurpleLog *)log->data, flags, alias, mtime, displayed);
999 log = log->next;
1003 if (ops && ops->write_conv)
1004 ops->write_conv(conv, who, alias, displayed, flags, mtime);
1006 add_message_to_history(conv, who, alias, message, flags, mtime);
1008 purple_signal_emit(purple_conversations_get_handle(),
1009 (type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"),
1010 account, who, displayed, conv, flags);
1012 g_free(displayed);
1015 gboolean
1016 purple_conversation_has_focus(PurpleConversation *conv)
1018 gboolean ret = FALSE;
1019 PurpleConversationUiOps *ops;
1021 g_return_val_if_fail(conv != NULL, FALSE);
1023 ops = purple_conversation_get_ui_ops(conv);
1025 if (ops != NULL && ops->has_focus != NULL)
1026 ret = ops->has_focus(conv);
1028 return ret;
1032 * TODO: Need to make sure calls to this function happen in the core
1033 * instead of the UI. That way UIs have less work to do, and the
1034 * core/UI split is cleaner. Also need to make sure this is called
1035 * when chats are added/removed from the blist.
1037 void
1038 purple_conversation_update(PurpleConversation *conv, PurpleConvUpdateType type)
1040 g_return_if_fail(conv != NULL);
1042 purple_signal_emit(purple_conversations_get_handle(),
1043 "conversation-updated", conv, type);
1046 /**************************************************************************
1047 * IM Conversation API
1048 **************************************************************************/
1049 PurpleConversation *
1050 purple_conv_im_get_conversation(const PurpleConvIm *im)
1052 g_return_val_if_fail(im != NULL, NULL);
1054 return im->conv;
1057 void
1058 purple_conv_im_set_icon(PurpleConvIm *im, PurpleBuddyIcon *icon)
1060 g_return_if_fail(im != NULL);
1062 if (im->icon != icon)
1064 purple_buddy_icon_unref(im->icon);
1066 im->icon = (icon == NULL ? NULL : purple_buddy_icon_ref(icon));
1069 purple_conversation_update(purple_conv_im_get_conversation(im),
1070 PURPLE_CONV_UPDATE_ICON);
1073 PurpleBuddyIcon *
1074 purple_conv_im_get_icon(const PurpleConvIm *im)
1076 g_return_val_if_fail(im != NULL, NULL);
1078 return im->icon;
1081 void
1082 purple_conv_im_set_typing_state(PurpleConvIm *im, PurpleTypingState state)
1084 g_return_if_fail(im != NULL);
1086 if (im->typing_state != state)
1088 im->typing_state = state;
1090 switch (state)
1092 case PURPLE_TYPING:
1093 purple_signal_emit(purple_conversations_get_handle(),
1094 "buddy-typing", im->conv->account, im->conv->name);
1095 break;
1096 case PURPLE_TYPED:
1097 purple_signal_emit(purple_conversations_get_handle(),
1098 "buddy-typed", im->conv->account, im->conv->name);
1099 break;
1100 case PURPLE_NOT_TYPING:
1101 purple_signal_emit(purple_conversations_get_handle(),
1102 "buddy-typing-stopped", im->conv->account, im->conv->name);
1103 break;
1106 purple_conv_im_update_typing(im);
1110 PurpleTypingState
1111 purple_conv_im_get_typing_state(const PurpleConvIm *im)
1113 g_return_val_if_fail(im != NULL, 0);
1115 return im->typing_state;
1118 void
1119 purple_conv_im_start_typing_timeout(PurpleConvIm *im, int timeout)
1121 PurpleConversation *conv;
1123 g_return_if_fail(im != NULL);
1125 if (im->typing_timeout > 0)
1126 purple_conv_im_stop_typing_timeout(im);
1128 conv = purple_conv_im_get_conversation(im);
1130 im->typing_timeout = purple_timeout_add_seconds(timeout, reset_typing_cb, conv);
1133 void
1134 purple_conv_im_stop_typing_timeout(PurpleConvIm *im)
1136 g_return_if_fail(im != NULL);
1138 if (im->typing_timeout == 0)
1139 return;
1141 purple_timeout_remove(im->typing_timeout);
1142 im->typing_timeout = 0;
1145 guint
1146 purple_conv_im_get_typing_timeout(const PurpleConvIm *im)
1148 g_return_val_if_fail(im != NULL, 0);
1150 return im->typing_timeout;
1153 void
1154 purple_conv_im_set_type_again(PurpleConvIm *im, unsigned int val)
1156 g_return_if_fail(im != NULL);
1158 if (val == 0)
1159 im->type_again = 0;
1160 else
1161 im->type_again = time(NULL) + val;
1164 time_t
1165 purple_conv_im_get_type_again(const PurpleConvIm *im)
1167 g_return_val_if_fail(im != NULL, 0);
1169 return im->type_again;
1172 void
1173 purple_conv_im_start_send_typed_timeout(PurpleConvIm *im)
1175 g_return_if_fail(im != NULL);
1177 im->send_typed_timeout = purple_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS,
1178 send_typed_cb,
1179 purple_conv_im_get_conversation(im));
1182 void
1183 purple_conv_im_stop_send_typed_timeout(PurpleConvIm *im)
1185 g_return_if_fail(im != NULL);
1187 if (im->send_typed_timeout == 0)
1188 return;
1190 purple_timeout_remove(im->send_typed_timeout);
1191 im->send_typed_timeout = 0;
1194 guint
1195 purple_conv_im_get_send_typed_timeout(const PurpleConvIm *im)
1197 g_return_val_if_fail(im != NULL, 0);
1199 return im->send_typed_timeout;
1202 void
1203 purple_conv_im_update_typing(PurpleConvIm *im)
1205 g_return_if_fail(im != NULL);
1207 purple_conversation_update(purple_conv_im_get_conversation(im),
1208 PURPLE_CONV_UPDATE_TYPING);
1211 void
1212 purple_conv_im_write(PurpleConvIm *im, const char *who, const char *message,
1213 PurpleMessageFlags flags, time_t mtime)
1215 PurpleConversation *c;
1217 g_return_if_fail(im != NULL);
1218 g_return_if_fail(message != NULL);
1220 c = purple_conv_im_get_conversation(im);
1222 if ((flags & PURPLE_MESSAGE_RECV) == PURPLE_MESSAGE_RECV) {
1223 purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
1226 /* Pass this on to either the ops structure or the default write func. */
1227 if (c->ui_ops != NULL && c->ui_ops->write_im != NULL)
1228 c->ui_ops->write_im(c, who, message, flags, mtime);
1229 else
1230 purple_conversation_write(c, who, message, flags, mtime);
1233 gboolean purple_conv_present_error(const char *who, PurpleAccount *account, const char *what)
1235 PurpleConversation *conv;
1237 g_return_val_if_fail(who != NULL, FALSE);
1238 g_return_val_if_fail(account !=NULL, FALSE);
1239 g_return_val_if_fail(what != NULL, FALSE);
1241 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
1242 if (conv != NULL)
1243 purple_conversation_write(conv, NULL, what, PURPLE_MESSAGE_ERROR, time(NULL));
1244 else
1245 return FALSE;
1247 return TRUE;
1250 void
1251 purple_conv_im_send(PurpleConvIm *im, const char *message)
1253 purple_conv_im_send_with_flags(im, message, 0);
1256 static void
1257 purple_conv_send_confirm_cb(gpointer *data)
1259 PurpleConversation *conv = data[0];
1260 char *message = data[1];
1262 g_free(data);
1263 common_send(conv, message, 0);
1266 void
1267 purple_conv_send_confirm(PurpleConversation *conv, const char *message)
1269 char *text;
1270 gpointer *data;
1272 g_return_if_fail(conv != NULL);
1273 g_return_if_fail(message != NULL);
1275 if (conv->ui_ops != NULL && conv->ui_ops->send_confirm != NULL)
1277 conv->ui_ops->send_confirm(conv, message);
1278 return;
1281 text = g_strdup_printf("You are about to send the following message:\n%s", message);
1282 data = g_new0(gpointer, 2);
1283 data[0] = conv;
1284 data[1] = (gpointer)message;
1286 purple_request_action(conv, NULL, _("Send Message"), text, 0,
1287 purple_conversation_get_account(conv), NULL, conv,
1288 data, 2,
1289 _("_Send Message"), G_CALLBACK(purple_conv_send_confirm_cb),
1290 _("Cancel"), NULL);
1293 void
1294 purple_conv_im_send_with_flags(PurpleConvIm *im, const char *message, PurpleMessageFlags flags)
1296 g_return_if_fail(im != NULL);
1297 g_return_if_fail(message != NULL);
1299 common_send(purple_conv_im_get_conversation(im), message, flags);
1302 gboolean
1303 purple_conv_custom_smiley_add(PurpleConversation *conv, const char *smile,
1304 const char *cksum_type, const char *chksum,
1305 gboolean remote)
1307 if (conv == NULL || smile == NULL || !*smile) {
1308 return FALSE;
1311 /* TODO: check if the icon is in the cache and return false if so */
1312 /* TODO: add an icon cache (that doesn't suck) */
1313 if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_add !=NULL) {
1314 return conv->ui_ops->custom_smiley_add(conv, smile, remote);
1315 } else {
1316 purple_debug_info("conversation", "Could not find add custom smiley function");
1317 return FALSE;
1322 void
1323 purple_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
1324 const guchar *data, gsize size)
1326 g_return_if_fail(conv != NULL);
1327 g_return_if_fail(smile != NULL && *smile);
1329 if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_write != NULL)
1330 conv->ui_ops->custom_smiley_write(conv, smile, data, size);
1331 else
1332 purple_debug_info("conversation", "Could not find the smiley write function");
1335 void
1336 purple_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
1338 g_return_if_fail(conv != NULL);
1339 g_return_if_fail(smile != NULL && *smile);
1341 if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_close != NULL)
1342 conv->ui_ops->custom_smiley_close(conv, smile);
1343 else
1344 purple_debug_info("conversation", "Could not find custom smiley close function");
1348 /**************************************************************************
1349 * Chat Conversation API
1350 **************************************************************************/
1352 PurpleConversation *
1353 purple_conv_chat_get_conversation(const PurpleConvChat *chat)
1355 g_return_val_if_fail(chat != NULL, NULL);
1357 return chat->conv;
1360 GList *
1361 purple_conv_chat_set_users(PurpleConvChat *chat, GList *users)
1363 g_return_val_if_fail(chat != NULL, NULL);
1365 chat->in_room = users;
1367 return users;
1370 GList *
1371 purple_conv_chat_get_users(const PurpleConvChat *chat)
1373 g_return_val_if_fail(chat != NULL, NULL);
1375 return chat->in_room;
1378 void
1379 purple_conv_chat_ignore(PurpleConvChat *chat, const char *name)
1381 g_return_if_fail(chat != NULL);
1382 g_return_if_fail(name != NULL);
1384 /* Make sure the user isn't already ignored. */
1385 if (purple_conv_chat_is_user_ignored(chat, name))
1386 return;
1388 purple_conv_chat_set_ignored(chat,
1389 g_list_append(chat->ignored, g_strdup(name)));
1392 void
1393 purple_conv_chat_unignore(PurpleConvChat *chat, const char *name)
1395 GList *item;
1397 g_return_if_fail(chat != NULL);
1398 g_return_if_fail(name != NULL);
1400 /* Make sure the user is actually ignored. */
1401 if (!purple_conv_chat_is_user_ignored(chat, name))
1402 return;
1404 item = g_list_find(purple_conv_chat_get_ignored(chat),
1405 purple_conv_chat_get_ignored_user(chat, name));
1407 purple_conv_chat_set_ignored(chat,
1408 g_list_remove_link(chat->ignored, item));
1410 g_free(item->data);
1411 g_list_free_1(item);
1414 GList *
1415 purple_conv_chat_set_ignored(PurpleConvChat *chat, GList *ignored)
1417 g_return_val_if_fail(chat != NULL, NULL);
1419 chat->ignored = ignored;
1421 return ignored;
1424 GList *
1425 purple_conv_chat_get_ignored(const PurpleConvChat *chat)
1427 g_return_val_if_fail(chat != NULL, NULL);
1429 return chat->ignored;
1432 const char *
1433 purple_conv_chat_get_ignored_user(const PurpleConvChat *chat, const char *user)
1435 GList *ignored;
1437 g_return_val_if_fail(chat != NULL, NULL);
1438 g_return_val_if_fail(user != NULL, NULL);
1440 for (ignored = purple_conv_chat_get_ignored(chat);
1441 ignored != NULL;
1442 ignored = ignored->next) {
1444 const char *ign = (const char *)ignored->data;
1446 if (!purple_utf8_strcasecmp(user, ign) ||
1447 ((*ign == '+' || *ign == '%') && !purple_utf8_strcasecmp(user, ign + 1)))
1448 return ign;
1450 if (*ign == '@') {
1451 ign++;
1453 if ((*ign == '+' && !purple_utf8_strcasecmp(user, ign + 1)) ||
1454 (*ign != '+' && !purple_utf8_strcasecmp(user, ign)))
1455 return ign;
1459 return NULL;
1462 gboolean
1463 purple_conv_chat_is_user_ignored(const PurpleConvChat *chat, const char *user)
1465 g_return_val_if_fail(chat != NULL, FALSE);
1466 g_return_val_if_fail(user != NULL, FALSE);
1468 return (purple_conv_chat_get_ignored_user(chat, user) != NULL);
1471 void
1472 purple_conv_chat_set_topic(PurpleConvChat *chat, const char *who, const char *topic)
1474 g_return_if_fail(chat != NULL);
1476 g_free(chat->who);
1477 g_free(chat->topic);
1479 chat->who = g_strdup(who);
1480 chat->topic = g_strdup(topic);
1482 purple_conversation_update(purple_conv_chat_get_conversation(chat),
1483 PURPLE_CONV_UPDATE_TOPIC);
1485 purple_signal_emit(purple_conversations_get_handle(), "chat-topic-changed",
1486 chat->conv, chat->who, chat->topic);
1489 const char *
1490 purple_conv_chat_get_topic(const PurpleConvChat *chat)
1492 g_return_val_if_fail(chat != NULL, NULL);
1494 return chat->topic;
1497 void
1498 purple_conv_chat_set_id(PurpleConvChat *chat, int id)
1500 g_return_if_fail(chat != NULL);
1502 chat->id = id;
1506 purple_conv_chat_get_id(const PurpleConvChat *chat)
1508 g_return_val_if_fail(chat != NULL, -1);
1510 return chat->id;
1513 void
1514 purple_conv_chat_write(PurpleConvChat *chat, const char *who, const char *message,
1515 PurpleMessageFlags flags, time_t mtime)
1517 PurpleAccount *account;
1518 PurpleConversation *conv;
1519 PurpleConnection *gc;
1521 g_return_if_fail(chat != NULL);
1522 g_return_if_fail(who != NULL);
1523 g_return_if_fail(message != NULL);
1525 conv = purple_conv_chat_get_conversation(chat);
1526 gc = purple_conversation_get_gc(conv);
1527 account = purple_connection_get_account(gc);
1529 /* Don't display this if the person who wrote it is ignored. */
1530 if (purple_conv_chat_is_user_ignored(chat, who))
1531 return;
1533 if (!(flags & PURPLE_MESSAGE_WHISPER)) {
1534 const char *str;
1536 str = purple_normalize(account, who);
1538 if (purple_strequal(str, chat->nick)) {
1539 flags |= PURPLE_MESSAGE_SEND;
1540 } else {
1541 flags |= PURPLE_MESSAGE_RECV;
1543 if (purple_utf8_has_word(message, chat->nick))
1544 flags |= PURPLE_MESSAGE_NICK;
1548 /* Pass this on to either the ops structure or the default write func. */
1549 if (conv->ui_ops != NULL && conv->ui_ops->write_chat != NULL)
1550 conv->ui_ops->write_chat(conv, who, message, flags, mtime);
1551 else
1552 purple_conversation_write(conv, who, message, flags, mtime);
1555 void
1556 purple_conv_chat_send(PurpleConvChat *chat, const char *message)
1558 purple_conv_chat_send_with_flags(chat, message, 0);
1561 void
1562 purple_conv_chat_send_with_flags(PurpleConvChat *chat, const char *message, PurpleMessageFlags flags)
1564 g_return_if_fail(chat != NULL);
1565 g_return_if_fail(message != NULL);
1567 common_send(purple_conv_chat_get_conversation(chat), message, flags);
1570 void
1571 purple_conv_chat_add_user(PurpleConvChat *chat, const char *user,
1572 const char *extra_msg, PurpleConvChatBuddyFlags flags,
1573 gboolean new_arrival)
1575 GList *users = g_list_append(NULL, (char *)user);
1576 GList *extra_msgs = g_list_append(NULL, (char *)extra_msg);
1577 GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags));
1579 purple_conv_chat_add_users(chat, users, extra_msgs, flags2, new_arrival);
1581 g_list_free(users);
1582 g_list_free(extra_msgs);
1583 g_list_free(flags2);
1586 static int
1587 purple_conv_chat_cb_compare(PurpleConvChatBuddy *a, PurpleConvChatBuddy *b)
1589 PurpleConvChatBuddyFlags f1 = 0, f2 = 0;
1590 char *user1 = NULL, *user2 = NULL;
1591 gint ret = 0;
1593 if (a) {
1594 f1 = a->flags;
1595 if (a->alias_key)
1596 user1 = a->alias_key;
1597 else if (a->name)
1598 user1 = a->name;
1601 if (b) {
1602 f2 = b->flags;
1603 if (b->alias_key)
1604 user2 = b->alias_key;
1605 else if (b->name)
1606 user2 = b->name;
1609 if (user1 == NULL || user2 == NULL) {
1610 if (!(user1 == NULL && user2 == NULL))
1611 ret = (user1 == NULL) ? -1: 1;
1612 } else if (f1 != f2) {
1613 /* sort more important users first */
1614 ret = (f1 > f2) ? -1 : 1;
1615 } else if (a->buddy != b->buddy) {
1616 ret = a->buddy ? -1 : 1;
1617 } else {
1618 ret = purple_utf8_strcasecmp(user1, user2);
1621 return ret;
1624 void
1625 purple_conv_chat_add_users(PurpleConvChat *chat, GList *users, GList *extra_msgs,
1626 GList *flags, gboolean new_arrivals)
1628 PurpleConversation *conv;
1629 PurpleConversationUiOps *ops;
1630 PurpleConvChatBuddy *cbuddy;
1631 PurpleConnection *gc;
1632 PurplePluginProtocolInfo *prpl_info;
1633 GList *ul, *fl;
1634 GList *cbuddies = NULL;
1636 g_return_if_fail(chat != NULL);
1637 g_return_if_fail(users != NULL);
1639 conv = purple_conv_chat_get_conversation(chat);
1640 ops = purple_conversation_get_ui_ops(conv);
1642 gc = purple_conversation_get_gc(conv);
1643 g_return_if_fail(gc != NULL);
1644 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
1645 g_return_if_fail(prpl_info != NULL);
1647 ul = users;
1648 fl = flags;
1649 while ((ul != NULL) && (fl != NULL)) {
1650 const char *user = (const char *)ul->data;
1651 const char *alias = user;
1652 gboolean quiet;
1653 PurpleConvChatBuddyFlags flag = GPOINTER_TO_INT(fl->data);
1654 const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
1656 if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1657 if (purple_strequal(chat->nick, purple_normalize(conv->account, user))) {
1658 const char *alias2 = purple_account_get_alias(conv->account);
1659 if (alias2 != NULL)
1660 alias = alias2;
1661 else
1663 const char *display_name = purple_connection_get_display_name(gc);
1664 if (display_name != NULL)
1665 alias = display_name;
1667 } else {
1668 PurpleBuddy *buddy;
1669 if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
1670 alias = purple_buddy_get_contact_alias(buddy);
1674 quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
1675 "chat-buddy-joining", conv, user, flag)) ||
1676 purple_conv_chat_is_user_ignored(chat, user);
1678 cbuddy = purple_conv_chat_cb_new(user, alias, flag);
1679 cbuddy->buddy = purple_find_buddy(conv->account, user) != NULL;
1680 /* This seems dumb. Why should we set users thousands of times? */
1681 purple_conv_chat_set_users(chat,
1682 g_list_prepend(chat->in_room, cbuddy));
1684 cbuddies = g_list_prepend(cbuddies, cbuddy);
1686 if (!quiet && new_arrivals) {
1687 char *alias_esc = g_markup_escape_text(alias, -1);
1688 char *tmp;
1690 if (extra_msg == NULL)
1691 tmp = g_strdup_printf(_("%s entered the room."), alias_esc);
1692 else {
1693 char *extra_msg_esc = g_markup_escape_text(extra_msg, -1);
1694 tmp = g_strdup_printf(_("%s [<I>%s</I>] entered the room."),
1695 alias_esc, extra_msg_esc);
1696 g_free(extra_msg_esc);
1698 g_free(alias_esc);
1700 purple_conversation_write(conv, NULL, tmp,
1701 PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
1702 time(NULL));
1703 g_free(tmp);
1706 purple_signal_emit(purple_conversations_get_handle(),
1707 "chat-buddy-joined", conv, user, flag, new_arrivals);
1708 ul = ul->next;
1709 fl = fl->next;
1710 if (extra_msgs != NULL)
1711 extra_msgs = extra_msgs->next;
1714 cbuddies = g_list_sort(cbuddies, (GCompareFunc)purple_conv_chat_cb_compare);
1716 if (ops != NULL && ops->chat_add_users != NULL)
1717 ops->chat_add_users(conv, cbuddies, new_arrivals);
1719 g_list_free(cbuddies);
1722 void
1723 purple_conv_chat_rename_user(PurpleConvChat *chat, const char *old_user,
1724 const char *new_user)
1726 PurpleConversation *conv;
1727 PurpleConversationUiOps *ops;
1728 PurpleConnection *gc;
1729 PurplePluginProtocolInfo *prpl_info;
1730 PurpleConvChatBuddy *cb;
1731 PurpleConvChatBuddyFlags flags;
1732 const char *new_alias = new_user;
1733 char tmp[BUF_LONG];
1734 gboolean is_me = FALSE;
1736 g_return_if_fail(chat != NULL);
1737 g_return_if_fail(old_user != NULL);
1738 g_return_if_fail(new_user != NULL);
1740 conv = purple_conv_chat_get_conversation(chat);
1741 ops = purple_conversation_get_ui_ops(conv);
1743 gc = purple_conversation_get_gc(conv);
1744 g_return_if_fail(gc != NULL);
1745 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
1746 g_return_if_fail(prpl_info != NULL);
1748 if (purple_strequal(chat->nick, purple_normalize(conv->account, old_user))) {
1749 const char *alias;
1751 /* Note this for later. */
1752 is_me = TRUE;
1754 if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1755 alias = purple_account_get_alias(conv->account);
1756 if (alias != NULL)
1757 new_alias = alias;
1758 else
1760 const char *display_name = purple_connection_get_display_name(gc);
1761 if (display_name != NULL)
1762 alias = display_name;
1765 } else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1766 PurpleBuddy *buddy;
1767 if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
1768 new_alias = purple_buddy_get_contact_alias(buddy);
1771 flags = purple_conv_chat_user_get_flags(chat, old_user);
1772 cb = purple_conv_chat_cb_new(new_user, new_alias, flags);
1773 cb->buddy = purple_find_buddy(conv->account, new_user) != NULL;
1774 purple_conv_chat_set_users(chat,
1775 g_list_prepend(chat->in_room, cb));
1777 if (ops != NULL && ops->chat_rename_user != NULL)
1778 ops->chat_rename_user(conv, old_user, new_user, new_alias);
1780 cb = purple_conv_chat_cb_find(chat, old_user);
1782 if (cb) {
1783 purple_conv_chat_set_users(chat,
1784 g_list_remove(chat->in_room, cb));
1785 purple_conv_chat_cb_destroy(cb);
1788 if (purple_conv_chat_is_user_ignored(chat, old_user)) {
1789 purple_conv_chat_unignore(chat, old_user);
1790 purple_conv_chat_ignore(chat, new_user);
1792 else if (purple_conv_chat_is_user_ignored(chat, new_user))
1793 purple_conv_chat_unignore(chat, new_user);
1795 if (is_me)
1796 purple_conv_chat_set_nick(chat, new_user);
1798 if (purple_prefs_get_bool("/purple/conversations/chat/show_nick_change") &&
1799 !purple_conv_chat_is_user_ignored(chat, new_user)) {
1801 if (is_me) {
1802 char *escaped = g_markup_escape_text(new_user, -1);
1803 g_snprintf(tmp, sizeof(tmp),
1804 _("You are now known as %s"), escaped);
1805 g_free(escaped);
1806 } else {
1807 const char *old_alias = old_user;
1808 const char *new_alias = new_user;
1809 char *escaped;
1810 char *escaped2;
1812 if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1813 PurpleBuddy *buddy;
1815 if ((buddy = purple_find_buddy(gc->account, old_user)) != NULL)
1816 old_alias = purple_buddy_get_contact_alias(buddy);
1817 if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
1818 new_alias = purple_buddy_get_contact_alias(buddy);
1821 escaped = g_markup_escape_text(old_alias, -1);
1822 escaped2 = g_markup_escape_text(new_alias, -1);
1823 g_snprintf(tmp, sizeof(tmp),
1824 _("%s is now known as %s"), escaped, escaped2);
1825 g_free(escaped);
1826 g_free(escaped2);
1829 purple_conversation_write(conv, NULL, tmp,
1830 PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
1831 time(NULL));
1835 void
1836 purple_conv_chat_remove_user(PurpleConvChat *chat, const char *user, const char *reason)
1838 GList *users = g_list_append(NULL, (char *)user);
1840 purple_conv_chat_remove_users(chat, users, reason);
1842 g_list_free(users);
1845 void
1846 purple_conv_chat_remove_users(PurpleConvChat *chat, GList *users, const char *reason)
1848 PurpleConversation *conv;
1849 PurpleConnection *gc;
1850 PurplePluginProtocolInfo *prpl_info;
1851 PurpleConversationUiOps *ops;
1852 PurpleConvChatBuddy *cb;
1853 GList *l;
1854 gboolean quiet;
1856 g_return_if_fail(chat != NULL);
1857 g_return_if_fail(users != NULL);
1859 conv = purple_conv_chat_get_conversation(chat);
1861 gc = purple_conversation_get_gc(conv);
1862 g_return_if_fail(gc != NULL);
1863 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
1864 g_return_if_fail(prpl_info != NULL);
1866 ops = purple_conversation_get_ui_ops(conv);
1868 for (l = users; l != NULL; l = l->next) {
1869 const char *user = (const char *)l->data;
1870 quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
1871 "chat-buddy-leaving", conv, user, reason)) |
1872 purple_conv_chat_is_user_ignored(chat, user);
1874 cb = purple_conv_chat_cb_find(chat, user);
1876 if (cb) {
1877 purple_conv_chat_set_users(chat,
1878 g_list_remove(chat->in_room, cb));
1879 purple_conv_chat_cb_destroy(cb);
1882 /* NOTE: Don't remove them from ignored in case they re-enter. */
1884 if (!quiet) {
1885 const char *alias = user;
1886 char *alias_esc;
1887 char *tmp;
1889 if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1890 PurpleBuddy *buddy;
1892 if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
1893 alias = purple_buddy_get_contact_alias(buddy);
1896 alias_esc = g_markup_escape_text(alias, -1);
1898 if (reason == NULL || !*reason)
1899 tmp = g_strdup_printf(_("%s left the room."), alias_esc);
1900 else {
1901 char *reason_esc = g_markup_escape_text(reason, -1);
1902 tmp = g_strdup_printf(_("%s left the room (%s)."),
1903 alias_esc, reason_esc);
1904 g_free(reason_esc);
1906 g_free(alias_esc);
1908 purple_conversation_write(conv, NULL, tmp,
1909 PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
1910 time(NULL));
1911 g_free(tmp);
1914 purple_signal_emit(purple_conversations_get_handle(), "chat-buddy-left",
1915 conv, user, reason);
1918 if (ops != NULL && ops->chat_remove_users != NULL)
1919 ops->chat_remove_users(conv, users);
1922 void
1923 purple_conv_chat_clear_users(PurpleConvChat *chat)
1925 PurpleConversation *conv;
1926 PurpleConversationUiOps *ops;
1927 GList *users;
1928 GList *l;
1929 GList *names = NULL;
1931 g_return_if_fail(chat != NULL);
1933 conv = purple_conv_chat_get_conversation(chat);
1934 ops = purple_conversation_get_ui_ops(conv);
1935 users = chat->in_room;
1937 if (ops != NULL && ops->chat_remove_users != NULL) {
1938 for (l = users; l; l = l->next) {
1939 PurpleConvChatBuddy *cb = l->data;
1940 names = g_list_prepend(names, cb->name);
1942 ops->chat_remove_users(conv, names);
1943 g_list_free(names);
1946 for (l = users; l; l = l->next)
1948 PurpleConvChatBuddy *cb = l->data;
1950 purple_signal_emit(purple_conversations_get_handle(),
1951 "chat-buddy-leaving", conv, cb->name, NULL);
1952 purple_signal_emit(purple_conversations_get_handle(),
1953 "chat-buddy-left", conv, cb->name, NULL);
1955 purple_conv_chat_cb_destroy(cb);
1958 g_list_free(users);
1959 purple_conv_chat_set_users(chat, NULL);
1963 gboolean
1964 purple_conv_chat_find_user(PurpleConvChat *chat, const char *user)
1966 g_return_val_if_fail(chat != NULL, FALSE);
1967 g_return_val_if_fail(user != NULL, FALSE);
1969 return (purple_conv_chat_cb_find(chat, user) != NULL);
1972 void
1973 purple_conv_chat_user_set_flags(PurpleConvChat *chat, const char *user,
1974 PurpleConvChatBuddyFlags flags)
1976 PurpleConversation *conv;
1977 PurpleConversationUiOps *ops;
1978 PurpleConvChatBuddy *cb;
1979 PurpleConvChatBuddyFlags oldflags;
1981 g_return_if_fail(chat != NULL);
1982 g_return_if_fail(user != NULL);
1984 cb = purple_conv_chat_cb_find(chat, user);
1986 if (!cb)
1987 return;
1989 if (flags == cb->flags)
1990 return;
1992 oldflags = cb->flags;
1993 cb->flags = flags;
1995 conv = purple_conv_chat_get_conversation(chat);
1996 ops = purple_conversation_get_ui_ops(conv);
1998 if (ops != NULL && ops->chat_update_user != NULL)
1999 ops->chat_update_user(conv, user);
2001 purple_signal_emit(purple_conversations_get_handle(),
2002 "chat-buddy-flags", conv, user, oldflags, flags);
2005 PurpleConvChatBuddyFlags
2006 purple_conv_chat_user_get_flags(PurpleConvChat *chat, const char *user)
2008 PurpleConvChatBuddy *cb;
2010 g_return_val_if_fail(chat != NULL, 0);
2011 g_return_val_if_fail(user != NULL, 0);
2013 cb = purple_conv_chat_cb_find(chat, user);
2015 if (!cb)
2016 return PURPLE_CBFLAGS_NONE;
2018 return cb->flags;
2021 void purple_conv_chat_set_nick(PurpleConvChat *chat, const char *nick) {
2022 g_return_if_fail(chat != NULL);
2024 g_free(chat->nick);
2025 chat->nick = g_strdup(purple_normalize(chat->conv->account, nick));
2028 const char *purple_conv_chat_get_nick(PurpleConvChat *chat) {
2029 g_return_val_if_fail(chat != NULL, NULL);
2031 return chat->nick;
2034 PurpleConversation *
2035 purple_find_chat(const PurpleConnection *gc, int id)
2037 GList *l;
2038 PurpleConversation *conv;
2040 for (l = purple_get_chats(); l != NULL; l = l->next) {
2041 conv = (PurpleConversation *)l->data;
2043 if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)) == id &&
2044 purple_conversation_get_gc(conv) == gc)
2045 return conv;
2048 return NULL;
2051 void
2052 purple_conv_chat_left(PurpleConvChat *chat)
2054 g_return_if_fail(chat != NULL);
2056 chat->left = TRUE;
2057 purple_conversation_update(chat->conv, PURPLE_CONV_UPDATE_CHATLEFT);
2060 static void
2061 invite_user_to_chat(gpointer data, PurpleRequestFields *fields)
2063 PurpleConversation *conv;
2064 PurpleConvChat *chat;
2065 const char *user, *message;
2067 conv = data;
2068 chat = PURPLE_CONV_CHAT(conv);
2069 user = purple_request_fields_get_string(fields, "screenname");
2070 message = purple_request_fields_get_string(fields, "message");
2072 serv_chat_invite(purple_conversation_get_gc(conv), chat->id, message, user);
2075 void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user,
2076 const char *message, gboolean confirm)
2078 PurpleAccount *account;
2079 PurpleConversation *conv;
2080 PurpleRequestFields *fields;
2081 PurpleRequestFieldGroup *group;
2082 PurpleRequestField *field;
2084 g_return_if_fail(chat);
2086 if (!user || !*user || !message || !*message)
2087 confirm = TRUE;
2089 conv = chat->conv;
2090 account = conv->account;
2092 if (!confirm) {
2093 serv_chat_invite(purple_account_get_connection(account),
2094 purple_conv_chat_get_id(chat), message, user);
2095 return;
2098 fields = purple_request_fields_new();
2099 group = purple_request_field_group_new(_("Invite to chat"));
2100 purple_request_fields_add_group(fields, group);
2102 field = purple_request_field_string_new("screenname", _("Buddy"), user, FALSE);
2103 purple_request_field_group_add_field(group, field);
2104 purple_request_field_set_required(field, TRUE);
2105 purple_request_field_set_type_hint(field, "screenname");
2107 field = purple_request_field_string_new("message", _("Message"), message, FALSE);
2108 purple_request_field_group_add_field(group, field);
2110 purple_request_fields(conv, _("Invite to chat"), NULL,
2111 _("Please enter the name of the user you wish to invite, "
2112 "along with an optional invite message."),
2113 fields,
2114 _("Invite"), G_CALLBACK(invite_user_to_chat),
2115 _("Cancel"), NULL,
2116 account, user, conv,
2117 conv);
2120 gboolean
2121 purple_conv_chat_has_left(PurpleConvChat *chat)
2123 g_return_val_if_fail(chat != NULL, TRUE);
2125 return chat->left;
2128 PurpleConvChatBuddy *
2129 purple_conv_chat_cb_new(const char *name, const char *alias, PurpleConvChatBuddyFlags flags)
2131 PurpleConvChatBuddy *cb;
2133 g_return_val_if_fail(name != NULL, NULL);
2135 cb = g_new0(PurpleConvChatBuddy, 1);
2136 cb->name = g_strdup(name);
2137 cb->flags = flags;
2138 cb->alias = g_strdup(alias);
2139 cb->attributes = g_hash_table_new_full(g_str_hash, g_str_equal,
2140 g_free, g_free);
2142 PURPLE_DBUS_REGISTER_POINTER(cb, PurpleConvChatBuddy);
2143 return cb;
2146 PurpleConvChatBuddy *
2147 purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name)
2149 GList *l;
2150 PurpleConvChatBuddy *cb = NULL;
2152 g_return_val_if_fail(chat != NULL, NULL);
2153 g_return_val_if_fail(name != NULL, NULL);
2155 for (l = purple_conv_chat_get_users(chat); l; l = l->next) {
2156 cb = l->data;
2157 if (!g_utf8_collate(cb->name, name))
2158 return cb;
2161 return NULL;
2164 void
2165 purple_conv_chat_cb_destroy(PurpleConvChatBuddy *cb)
2167 if (cb == NULL)
2168 return;
2170 g_free(cb->alias);
2171 g_free(cb->alias_key);
2172 g_free(cb->name);
2173 g_hash_table_destroy(cb->attributes);
2175 PURPLE_DBUS_UNREGISTER_POINTER(cb);
2176 g_free(cb);
2179 const char *
2180 purple_conv_chat_cb_get_name(PurpleConvChatBuddy *cb)
2182 g_return_val_if_fail(cb != NULL, NULL);
2184 return cb->name;
2187 const char *
2188 purple_conv_chat_cb_get_attribute(PurpleConvChatBuddy *cb, const char *key)
2190 g_return_val_if_fail(cb != NULL, NULL);
2191 g_return_val_if_fail(key != NULL, NULL);
2193 return g_hash_table_lookup(cb->attributes, key);
2196 static void
2197 append_attribute_key(gpointer key, gpointer value, gpointer user_data)
2199 GList **list = user_data;
2200 *list = g_list_prepend(*list, key);
2203 GList *
2204 purple_conv_chat_cb_get_attribute_keys(PurpleConvChatBuddy *cb)
2206 GList *keys = NULL;
2208 g_return_val_if_fail(cb != NULL, NULL);
2210 g_hash_table_foreach(cb->attributes, (GHFunc)append_attribute_key, &keys);
2212 return keys;
2215 void
2216 purple_conv_chat_cb_set_attribute(PurpleConvChat *chat, PurpleConvChatBuddy *cb, const char *key, const char *value)
2218 PurpleConversation *conv;
2219 PurpleConversationUiOps *ops;
2221 g_return_if_fail(cb != NULL);
2222 g_return_if_fail(key != NULL);
2223 g_return_if_fail(value != NULL);
2225 g_hash_table_replace(cb->attributes, g_strdup(key), g_strdup(value));
2227 conv = purple_conv_chat_get_conversation(chat);
2228 ops = purple_conversation_get_ui_ops(conv);
2230 if (ops != NULL && ops->chat_update_user != NULL)
2231 ops->chat_update_user(conv, cb->name);
2234 void
2235 purple_conv_chat_cb_set_attributes(PurpleConvChat *chat, PurpleConvChatBuddy *cb, GList *keys, GList *values)
2237 PurpleConversation *conv;
2238 PurpleConversationUiOps *ops;
2240 g_return_if_fail(cb != NULL);
2241 g_return_if_fail(keys != NULL);
2242 g_return_if_fail(values != NULL);
2244 while (keys != NULL && values != NULL) {
2245 g_hash_table_replace(cb->attributes, g_strdup(keys->data), g_strdup(values->data));
2246 keys = g_list_next(keys);
2247 values = g_list_next(values);
2250 conv = purple_conv_chat_get_conversation(chat);
2251 ops = purple_conversation_get_ui_ops(conv);
2253 if (ops != NULL && ops->chat_update_user != NULL)
2254 ops->chat_update_user(conv, cb->name);
2257 GList *
2258 purple_conversation_get_extended_menu(PurpleConversation *conv)
2260 GList *menu = NULL;
2262 g_return_val_if_fail(conv != NULL, NULL);
2264 purple_signal_emit(purple_conversations_get_handle(),
2265 "conversation-extended-menu", conv, &menu);
2266 return menu;
2269 void purple_conversation_clear_message_history(PurpleConversation *conv)
2271 GList *list = conv->message_history;
2272 message_history_free(list);
2273 conv->message_history = NULL;
2275 purple_signal_emit(purple_conversations_get_handle(),
2276 "cleared-message-history", conv);
2279 GList *purple_conversation_get_message_history(PurpleConversation *conv)
2281 return conv->message_history;
2284 const char *purple_conversation_message_get_sender(PurpleConvMessage *msg)
2286 g_return_val_if_fail(msg, NULL);
2287 return msg->who;
2290 const char *purple_conversation_message_get_message(PurpleConvMessage *msg)
2292 g_return_val_if_fail(msg, NULL);
2293 return msg->what;
2296 PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg)
2298 g_return_val_if_fail(msg, 0);
2299 return msg->flags;
2302 time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg)
2304 g_return_val_if_fail(msg, 0);
2305 return msg->when;
2308 gboolean
2309 purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
2310 const gchar *markup, gchar **error)
2312 char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
2313 PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
2314 g_free(mark);
2315 g_free(err);
2316 return (status == PURPLE_CMD_STATUS_OK);
2319 void *
2320 purple_conversations_get_handle(void)
2322 static int handle;
2324 return &handle;
2327 void
2328 purple_conversations_init(void)
2330 void *handle = purple_conversations_get_handle();
2332 conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
2333 (GEqualFunc)_purple_conversations_hconv_equal,
2334 (GDestroyNotify)_purple_conversations_hconv_free_key, NULL);
2336 /**********************************************************************
2337 * Register preferences
2338 **********************************************************************/
2340 /* Conversations */
2341 purple_prefs_add_none("/purple/conversations");
2343 /* Conversations -> Chat */
2344 purple_prefs_add_none("/purple/conversations/chat");
2345 purple_prefs_add_bool("/purple/conversations/chat/show_nick_change", TRUE);
2347 /* Conversations -> IM */
2348 purple_prefs_add_none("/purple/conversations/im");
2349 purple_prefs_add_bool("/purple/conversations/im/send_typing", TRUE);
2352 /**********************************************************************
2353 * Register signals
2354 **********************************************************************/
2355 purple_signal_register(handle, "writing-im-msg",
2356 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT,
2357 purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
2358 purple_value_new(PURPLE_TYPE_SUBTYPE,
2359 PURPLE_SUBTYPE_ACCOUNT),
2360 purple_value_new(PURPLE_TYPE_STRING),
2361 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2362 purple_value_new(PURPLE_TYPE_SUBTYPE,
2363 PURPLE_SUBTYPE_CONVERSATION),
2364 purple_value_new(PURPLE_TYPE_UINT));
2366 purple_signal_register(handle, "wrote-im-msg",
2367 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
2368 NULL, 5,
2369 purple_value_new(PURPLE_TYPE_SUBTYPE,
2370 PURPLE_SUBTYPE_ACCOUNT),
2371 purple_value_new(PURPLE_TYPE_STRING),
2372 purple_value_new(PURPLE_TYPE_STRING),
2373 purple_value_new(PURPLE_TYPE_SUBTYPE,
2374 PURPLE_SUBTYPE_CONVERSATION),
2375 purple_value_new(PURPLE_TYPE_UINT));
2377 purple_signal_register(handle, "sent-attention",
2378 purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
2379 NULL, 4,
2380 purple_value_new(PURPLE_TYPE_SUBTYPE,
2381 PURPLE_SUBTYPE_ACCOUNT),
2382 purple_value_new(PURPLE_TYPE_STRING),
2383 purple_value_new(PURPLE_TYPE_SUBTYPE,
2384 PURPLE_SUBTYPE_CONVERSATION),
2385 purple_value_new(PURPLE_TYPE_UINT));
2387 purple_signal_register(handle, "got-attention",
2388 purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
2389 NULL, 4,
2390 purple_value_new(PURPLE_TYPE_SUBTYPE,
2391 PURPLE_SUBTYPE_ACCOUNT),
2392 purple_value_new(PURPLE_TYPE_STRING),
2393 purple_value_new(PURPLE_TYPE_SUBTYPE,
2394 PURPLE_SUBTYPE_CONVERSATION),
2395 purple_value_new(PURPLE_TYPE_UINT));
2397 purple_signal_register(handle, "sending-im-msg",
2398 purple_marshal_VOID__POINTER_POINTER_POINTER,
2399 NULL, 3,
2400 purple_value_new(PURPLE_TYPE_SUBTYPE,
2401 PURPLE_SUBTYPE_ACCOUNT),
2402 purple_value_new(PURPLE_TYPE_STRING),
2403 purple_value_new_outgoing(PURPLE_TYPE_STRING));
2405 purple_signal_register(handle, "sent-im-msg",
2406 purple_marshal_VOID__POINTER_POINTER_POINTER,
2407 NULL, 3,
2408 purple_value_new(PURPLE_TYPE_SUBTYPE,
2409 PURPLE_SUBTYPE_ACCOUNT),
2410 purple_value_new(PURPLE_TYPE_STRING),
2411 purple_value_new(PURPLE_TYPE_STRING));
2413 purple_signal_register(handle, "receiving-im-msg",
2414 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
2415 purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
2416 purple_value_new(PURPLE_TYPE_SUBTYPE,
2417 PURPLE_SUBTYPE_ACCOUNT),
2418 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2419 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2420 purple_value_new(PURPLE_TYPE_SUBTYPE,
2421 PURPLE_SUBTYPE_CONVERSATION),
2422 purple_value_new_outgoing(PURPLE_TYPE_UINT));
2424 purple_signal_register(handle, "received-im-msg",
2425 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
2426 NULL, 5,
2427 purple_value_new(PURPLE_TYPE_SUBTYPE,
2428 PURPLE_SUBTYPE_ACCOUNT),
2429 purple_value_new(PURPLE_TYPE_STRING),
2430 purple_value_new(PURPLE_TYPE_STRING),
2431 purple_value_new(PURPLE_TYPE_SUBTYPE,
2432 PURPLE_SUBTYPE_CONVERSATION),
2433 purple_value_new(PURPLE_TYPE_UINT));
2435 purple_signal_register(handle, "blocked-im-msg",
2436 purple_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT,
2437 NULL, 5,
2438 purple_value_new(PURPLE_TYPE_SUBTYPE,
2439 PURPLE_SUBTYPE_ACCOUNT),
2440 purple_value_new(PURPLE_TYPE_STRING),
2441 purple_value_new(PURPLE_TYPE_STRING),
2442 purple_value_new(PURPLE_TYPE_UINT),
2443 purple_value_new(PURPLE_TYPE_UINT));
2445 purple_signal_register(handle, "writing-chat-msg",
2446 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT,
2447 purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
2448 purple_value_new(PURPLE_TYPE_SUBTYPE,
2449 PURPLE_SUBTYPE_ACCOUNT),
2450 purple_value_new(PURPLE_TYPE_STRING),
2451 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2452 purple_value_new(PURPLE_TYPE_SUBTYPE,
2453 PURPLE_SUBTYPE_CONVERSATION),
2454 purple_value_new(PURPLE_TYPE_UINT));
2456 purple_signal_register(handle, "wrote-chat-msg",
2457 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
2458 NULL, 5,
2459 purple_value_new(PURPLE_TYPE_SUBTYPE,
2460 PURPLE_SUBTYPE_ACCOUNT),
2461 purple_value_new(PURPLE_TYPE_STRING),
2462 purple_value_new(PURPLE_TYPE_STRING),
2463 purple_value_new(PURPLE_TYPE_SUBTYPE,
2464 PURPLE_SUBTYPE_CONVERSATION),
2465 purple_value_new(PURPLE_TYPE_UINT));
2467 purple_signal_register(handle, "sending-chat-msg",
2468 purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
2469 purple_value_new(PURPLE_TYPE_SUBTYPE,
2470 PURPLE_SUBTYPE_ACCOUNT),
2471 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2472 purple_value_new(PURPLE_TYPE_UINT));
2474 purple_signal_register(handle, "sent-chat-msg",
2475 purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
2476 purple_value_new(PURPLE_TYPE_SUBTYPE,
2477 PURPLE_SUBTYPE_ACCOUNT),
2478 purple_value_new(PURPLE_TYPE_STRING),
2479 purple_value_new(PURPLE_TYPE_UINT));
2481 purple_signal_register(handle, "receiving-chat-msg",
2482 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
2483 purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
2484 purple_value_new(PURPLE_TYPE_SUBTYPE,
2485 PURPLE_SUBTYPE_ACCOUNT),
2486 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2487 purple_value_new_outgoing(PURPLE_TYPE_STRING),
2488 purple_value_new(PURPLE_TYPE_SUBTYPE,
2489 PURPLE_SUBTYPE_CONVERSATION),
2490 purple_value_new_outgoing(PURPLE_TYPE_UINT));
2492 purple_signal_register(handle, "received-chat-msg",
2493 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
2494 NULL, 5,
2495 purple_value_new(PURPLE_TYPE_SUBTYPE,
2496 PURPLE_SUBTYPE_ACCOUNT),
2497 purple_value_new(PURPLE_TYPE_STRING),
2498 purple_value_new(PURPLE_TYPE_STRING),
2499 purple_value_new(PURPLE_TYPE_SUBTYPE,
2500 PURPLE_SUBTYPE_CONVERSATION),
2501 purple_value_new(PURPLE_TYPE_UINT));
2503 purple_signal_register(handle, "conversation-created",
2504 purple_marshal_VOID__POINTER, NULL, 1,
2505 purple_value_new(PURPLE_TYPE_SUBTYPE,
2506 PURPLE_SUBTYPE_CONVERSATION));
2508 purple_signal_register(handle, "conversation-updated",
2509 purple_marshal_VOID__POINTER_UINT, NULL, 2,
2510 purple_value_new(PURPLE_TYPE_SUBTYPE,
2511 PURPLE_SUBTYPE_CONVERSATION),
2512 purple_value_new(PURPLE_TYPE_UINT));
2514 purple_signal_register(handle, "deleting-conversation",
2515 purple_marshal_VOID__POINTER, NULL, 1,
2516 purple_value_new(PURPLE_TYPE_SUBTYPE,
2517 PURPLE_SUBTYPE_CONVERSATION));
2519 purple_signal_register(handle, "buddy-typing",
2520 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2521 purple_value_new(PURPLE_TYPE_SUBTYPE,
2522 PURPLE_SUBTYPE_ACCOUNT),
2523 purple_value_new(PURPLE_TYPE_STRING));
2525 purple_signal_register(handle, "buddy-typed",
2526 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2527 purple_value_new(PURPLE_TYPE_SUBTYPE,
2528 PURPLE_SUBTYPE_ACCOUNT),
2529 purple_value_new(PURPLE_TYPE_STRING));
2531 purple_signal_register(handle, "buddy-typing-stopped",
2532 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2533 purple_value_new(PURPLE_TYPE_SUBTYPE,
2534 PURPLE_SUBTYPE_ACCOUNT),
2535 purple_value_new(PURPLE_TYPE_STRING));
2537 purple_signal_register(handle, "chat-buddy-joining",
2538 purple_marshal_BOOLEAN__POINTER_POINTER_UINT,
2539 purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
2540 purple_value_new(PURPLE_TYPE_SUBTYPE,
2541 PURPLE_SUBTYPE_CONVERSATION),
2542 purple_value_new(PURPLE_TYPE_STRING),
2543 purple_value_new(PURPLE_TYPE_UINT));
2545 purple_signal_register(handle, "chat-buddy-joined",
2546 purple_marshal_VOID__POINTER_POINTER_UINT_UINT, NULL, 4,
2547 purple_value_new(PURPLE_TYPE_SUBTYPE,
2548 PURPLE_SUBTYPE_CONVERSATION),
2549 purple_value_new(PURPLE_TYPE_STRING),
2550 purple_value_new(PURPLE_TYPE_UINT),
2551 purple_value_new(PURPLE_TYPE_BOOLEAN));
2553 purple_signal_register(handle, "chat-buddy-flags",
2554 purple_marshal_VOID__POINTER_POINTER_UINT_UINT, NULL, 4,
2555 purple_value_new(PURPLE_TYPE_SUBTYPE,
2556 PURPLE_SUBTYPE_CONVERSATION),
2557 purple_value_new(PURPLE_TYPE_STRING),
2558 purple_value_new(PURPLE_TYPE_UINT),
2559 purple_value_new(PURPLE_TYPE_UINT));
2561 purple_signal_register(handle, "chat-buddy-leaving",
2562 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
2563 purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
2564 purple_value_new(PURPLE_TYPE_SUBTYPE,
2565 PURPLE_SUBTYPE_CONVERSATION),
2566 purple_value_new(PURPLE_TYPE_STRING),
2567 purple_value_new(PURPLE_TYPE_STRING));
2569 purple_signal_register(handle, "chat-buddy-left",
2570 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
2571 purple_value_new(PURPLE_TYPE_SUBTYPE,
2572 PURPLE_SUBTYPE_CONVERSATION),
2573 purple_value_new(PURPLE_TYPE_STRING),
2574 purple_value_new(PURPLE_TYPE_STRING));
2576 purple_signal_register(handle, "chat-inviting-user",
2577 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
2578 purple_value_new(PURPLE_TYPE_SUBTYPE,
2579 PURPLE_SUBTYPE_CONVERSATION),
2580 purple_value_new(PURPLE_TYPE_STRING),
2581 purple_value_new_outgoing(PURPLE_TYPE_STRING));
2583 purple_signal_register(handle, "chat-invited-user",
2584 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
2585 purple_value_new(PURPLE_TYPE_SUBTYPE,
2586 PURPLE_SUBTYPE_CONVERSATION),
2587 purple_value_new(PURPLE_TYPE_STRING),
2588 purple_value_new(PURPLE_TYPE_STRING));
2590 purple_signal_register(handle, "chat-invited",
2591 purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER,
2592 purple_value_new(PURPLE_TYPE_INT), 5,
2593 purple_value_new(PURPLE_TYPE_SUBTYPE,
2594 PURPLE_SUBTYPE_ACCOUNT),
2595 purple_value_new(PURPLE_TYPE_STRING),
2596 purple_value_new(PURPLE_TYPE_STRING),
2597 purple_value_new(PURPLE_TYPE_STRING),
2598 purple_value_new(PURPLE_TYPE_POINTER));
2600 purple_signal_register(handle, "chat-invite-blocked",
2601 purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER,
2602 NULL, 5,
2603 purple_value_new(PURPLE_TYPE_SUBTYPE,
2604 PURPLE_SUBTYPE_ACCOUNT),
2605 purple_value_new(PURPLE_TYPE_STRING),
2606 purple_value_new(PURPLE_TYPE_STRING),
2607 purple_value_new(PURPLE_TYPE_STRING),
2608 purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *"));
2610 purple_signal_register(handle, "chat-joined",
2611 purple_marshal_VOID__POINTER, NULL, 1,
2612 purple_value_new(PURPLE_TYPE_SUBTYPE,
2613 PURPLE_SUBTYPE_CONVERSATION));
2615 purple_signal_register(handle, "chat-join-failed",
2616 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2617 purple_value_new(PURPLE_TYPE_SUBTYPE,
2618 PURPLE_SUBTYPE_CONNECTION),
2619 purple_value_new(PURPLE_TYPE_POINTER));
2621 purple_signal_register(handle, "chat-left",
2622 purple_marshal_VOID__POINTER, NULL, 1,
2623 purple_value_new(PURPLE_TYPE_SUBTYPE,
2624 PURPLE_SUBTYPE_CONVERSATION));
2626 purple_signal_register(handle, "chat-topic-changed",
2627 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
2628 purple_value_new(PURPLE_TYPE_SUBTYPE,
2629 PURPLE_SUBTYPE_CONVERSATION),
2630 purple_value_new(PURPLE_TYPE_STRING),
2631 purple_value_new(PURPLE_TYPE_STRING));
2633 purple_signal_register(handle, "cleared-message-history",
2634 purple_marshal_VOID__POINTER, NULL, 1,
2635 purple_value_new(PURPLE_TYPE_SUBTYPE,
2636 PURPLE_SUBTYPE_CONVERSATION));
2638 purple_signal_register(handle, "conversation-extended-menu",
2639 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
2640 purple_value_new(PURPLE_TYPE_SUBTYPE,
2641 PURPLE_SUBTYPE_CONVERSATION),
2642 purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
2645 void
2646 purple_conversations_uninit(void)
2648 while (conversations)
2649 purple_conversation_destroy((PurpleConversation*)conversations->data);
2650 g_hash_table_destroy(conversation_cache);
2651 purple_signals_unregister_by_instance(purple_conversations_get_handle());