Logo Search packages:      
Sourcecode: pidgin version File versions  Download package

conversation.c

/*
 * purple
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 */
#include "internal.h"
#include "blist.h"
#include "cmds.h"
#include "conversation.h"
#include "dbus-maybe.h"
#include "debug.h"
#include "imgstore.h"
#include "notify.h"
#include "prefs.h"
#include "prpl.h"
#include "request.h"
#include "signals.h"
#include "util.h"

#define SEND_TYPED_TIMEOUT_SECONDS 5

static GList *conversations = NULL;
static GList *ims = NULL;
static GList *chats = NULL;
static PurpleConversationUiOps *default_ops = NULL;

/**
 * A hash table used for efficient lookups of conversations by name.
 * struct _purple_hconv => PurpleConversation*
 */
static GHashTable *conversation_cache = NULL;

struct _purple_hconv {
      PurpleConversationType type;
      char *name;
      const PurpleAccount *account;
};

static guint _purple_conversations_hconv_hash(struct _purple_hconv *hc)
{
      return g_str_hash(hc->name) ^ hc->type ^ g_direct_hash(hc->account);
}

static guint _purple_conversations_hconv_equal(struct _purple_hconv *hc1, struct _purple_hconv *hc2)
{
      return (hc1->type == hc2->type &&
              hc1->account == hc2->account &&
              g_str_equal(hc1->name, hc2->name));
}

static void _purple_conversations_hconv_free_key(struct _purple_hconv *hc)
{
      g_free(hc->name);
      g_free(hc);
}

void
00074 purple_conversations_set_ui_ops(PurpleConversationUiOps *ops)
{
      default_ops = ops;
}

static gboolean
reset_typing_cb(gpointer data)
{
      PurpleConversation *c = (PurpleConversation *)data;
      PurpleConvIm *im;

      im = PURPLE_CONV_IM(c);

      purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
      purple_conv_im_stop_typing_timeout(im);

      return FALSE;
}

static gboolean
send_typed_cb(gpointer data)
{
      PurpleConversation *conv = (PurpleConversation *)data;
      PurpleConnection *gc;
      const char *name;

      g_return_val_if_fail(conv != NULL, FALSE);

      gc   = purple_conversation_get_gc(conv);
      name = purple_conversation_get_name(conv);

      if (gc != NULL && name != NULL) {
            /* We set this to 1 so that PURPLE_TYPING will be sent
             * if the Purple user types anything else.
             */
            purple_conv_im_set_type_again(PURPLE_CONV_IM(conv), 1);

            serv_send_typing(gc, name, PURPLE_TYPED);

            purple_debug(PURPLE_DEBUG_MISC, "conversation", "typed...\n");
      }

      return FALSE;
}

static void
common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags msgflags)
{
      PurpleConversationType type;
      PurpleAccount *account;
      PurpleConnection *gc;
      char *displayed = NULL, *sent = NULL;
      int err = 0;

      if (*message == '\0')
            return;

      account = purple_conversation_get_account(conv);
      gc = purple_conversation_get_gc(conv);

      g_return_if_fail(account != NULL);
      g_return_if_fail(gc != NULL);

      type = purple_conversation_get_type(conv);

      /* Always linkfy the text for display, unless we're
       * explicitly asked to do otheriwse*/
      if (!(msgflags & PURPLE_MESSAGE_INVISIBLE)) {
            if(msgflags & PURPLE_MESSAGE_NO_LINKIFY)
                  displayed = g_strdup(message);
            else
                  displayed = purple_markup_linkify(message);
      }

      if (displayed && (conv->features & PURPLE_CONNECTION_HTML) &&
            !(msgflags & PURPLE_MESSAGE_RAW)) {
            sent = g_strdup(displayed);
      } else
            sent = g_strdup(message);

      msgflags |= PURPLE_MESSAGE_SEND;

      if (type == PURPLE_CONV_TYPE_IM) {
            PurpleConvIm *im = PURPLE_CONV_IM(conv);

            purple_signal_emit(purple_conversations_get_handle(), "sending-im-msg",
                                     account,
                                     purple_conversation_get_name(conv), &sent);

            if (sent != NULL && sent[0] != '\0') {

                  err = serv_send_im(gc, purple_conversation_get_name(conv),
                                     sent, msgflags);

                  if ((err > 0) && (displayed != NULL))
                        purple_conv_im_write(im, NULL, displayed, msgflags, time(NULL));

                  purple_signal_emit(purple_conversations_get_handle(), "sent-im-msg",
                                           account,
                                           purple_conversation_get_name(conv), sent);
            }
      }
      else {
            purple_signal_emit(purple_conversations_get_handle(), "sending-chat-msg",
                                     account, &sent,
                                     purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));

            if (sent != NULL && sent[0] != '\0') {
                  err = serv_chat_send(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), sent, msgflags);

                  purple_signal_emit(purple_conversations_get_handle(), "sent-chat-msg",
                                           account, sent,
                                           purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
            }
      }

      if (err < 0) {
            const char *who;
            const char *msg;

            who = purple_conversation_get_name(conv);

            if (err == -E2BIG) {
                  msg = _("Unable to send message: The message is too large.");

                  if (!purple_conv_present_error(who, account, msg)) {
                        char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
                        purple_notify_error(gc, NULL, msg2, _("The message is too large."));
                        g_free(msg2);
                  }
            }
            else if (err == -ENOTCONN) {
                  purple_debug(PURPLE_DEBUG_ERROR, "conversation",
                                 "Not yet connected.\n");
            }
            else {
                  msg = _("Unable to send message.");

                  if (!purple_conv_present_error(who, account, msg)) {
                        char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
                        purple_notify_error(gc, NULL, msg2, NULL);
                        g_free(msg2);
                  }
            }
      }

      g_free(displayed);
      g_free(sent);
}

static void
open_log(PurpleConversation *conv)
{
      conv->logs = g_list_append(NULL, purple_log_new(conv->type == PURPLE_CONV_TYPE_CHAT ? PURPLE_LOG_CHAT :
                                             PURPLE_LOG_IM, conv->name, conv->account,
                                             conv, time(NULL), NULL));
}

/* Functions that deal with PurpleConvMessage */

static void
add_message_to_history(PurpleConversation *conv, const char *who, const char *alias,
            const char *message, PurpleMessageFlags flags, time_t when)
{
      PurpleConvMessage *msg;
      PurpleConnection *gc;

      gc = purple_account_get_connection(conv->account);

      if (flags & PURPLE_MESSAGE_SEND) {
            const char *me = NULL;
            if (gc)
                  me = purple_connection_get_display_name(gc);
            if (!me)
                  me = conv->account->username;
            who = me;
      }

      msg = g_new0(PurpleConvMessage, 1);
      PURPLE_DBUS_REGISTER_POINTER(msg, PurpleConvMessage);
      msg->who = g_strdup(who);
      msg->alias = g_strdup(alias);
      msg->flags = flags;
      msg->what = g_strdup(message);
      msg->when = when;
      msg->conv = conv;

      conv->message_history = g_list_prepend(conv->message_history, msg);
}

static void
free_conv_message(PurpleConvMessage *msg)
{
      g_free(msg->who);
      g_free(msg->alias);
      g_free(msg->what);
      PURPLE_DBUS_UNREGISTER_POINTER(msg);
      g_free(msg);
}

static void
message_history_free(GList *list)
{
      g_list_foreach(list, (GFunc)free_conv_message, NULL);
      g_list_free(list);
}

/**************************************************************************
 * Conversation API
 **************************************************************************/
static void
purple_conversation_chat_cleanup_for_rejoin(PurpleConversation *conv)
{
      const char *disp;
      PurpleAccount *account;
      PurpleConnection *gc;

      account = purple_conversation_get_account(conv);

      purple_conversation_close_logs(conv);
      open_log(conv);

      gc = purple_account_get_connection(account);

      if ((disp = purple_connection_get_display_name(gc)) != NULL)
            purple_conv_chat_set_nick(PURPLE_CONV_CHAT(conv), disp);
      else
      {
            purple_conv_chat_set_nick(PURPLE_CONV_CHAT(conv),
                                                purple_account_get_username(account));
      }

      purple_conv_chat_clear_users(PURPLE_CONV_CHAT(conv));
      purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, NULL);
      PURPLE_CONV_CHAT(conv)->left = FALSE;

      purple_conversation_update(conv, PURPLE_CONV_UPDATE_CHATLEFT);
}

PurpleConversation *
00314 purple_conversation_new(PurpleConversationType type, PurpleAccount *account,
                                const char *name)
{
      PurpleConversation *conv;
      PurpleConnection *gc;
      PurpleConversationUiOps *ops;
      struct _purple_hconv *hc;

      g_return_val_if_fail(type    != PURPLE_CONV_TYPE_UNKNOWN, NULL);
      g_return_val_if_fail(account != NULL, NULL);
      g_return_val_if_fail(name    != NULL, NULL);

      /* Check if this conversation already exists. */
      if ((conv = purple_find_conversation_with_account(type, name, account)) != NULL)
      {
            if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
                        !purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) {
                  purple_debug_warning("conversation", "Trying to create multiple "
                              "chats (%s) with the same name is deprecated and will be "
                              "removed in libpurple 3.0.0", name);
            }

            /*
             * This hack is necessary because some prpls (MSN) have unnamed chats
             * that all use the same name.  A PurpleConversation for one of those
             * is only ever re-used if the user has left, so calls to
             * purple_conversation_new need to fall-through to creating a new
             * chat.
             * TODO 3.0.0: Remove this workaround and mandate unique names.
             */
            if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT ||
                        purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
            {
                  if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
                        purple_conversation_chat_cleanup_for_rejoin(conv);

                  return conv;
            }
      }

      gc = purple_account_get_connection(account);
      g_return_val_if_fail(gc != NULL, NULL);

      conv = g_new0(PurpleConversation, 1);
      PURPLE_DBUS_REGISTER_POINTER(conv, PurpleConversation);

      conv->type         = type;
      conv->account      = account;
      conv->name         = g_strdup(name);
      conv->title        = g_strdup(name);
      conv->data         = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                                     g_free, NULL);
      /* copy features from the connection. */
      conv->features = gc->flags;

      if (type == PURPLE_CONV_TYPE_IM)
      {
            PurpleBuddyIcon *icon;
            conv->u.im = g_new0(PurpleConvIm, 1);
            conv->u.im->conv = conv;
            PURPLE_DBUS_REGISTER_POINTER(conv->u.im, PurpleConvIm);

            ims = g_list_prepend(ims, conv);
            if ((icon = purple_buddy_icons_find(account, name)))
            {
                  purple_conv_im_set_icon(conv->u.im, icon);
                  /* purple_conv_im_set_icon refs the icon. */
                  purple_buddy_icon_unref(icon);
            }

            if (purple_prefs_get_bool("/purple/logging/log_ims"))
            {
                  purple_conversation_set_logging(conv, TRUE);
                  open_log(conv);
            }
      }
      else if (type == PURPLE_CONV_TYPE_CHAT)
      {
            const char *disp;

            conv->u.chat = g_new0(PurpleConvChat, 1);
            conv->u.chat->conv = conv;
            PURPLE_DBUS_REGISTER_POINTER(conv->u.chat, PurpleConvChat);

            chats = g_list_prepend(chats, conv);

            if ((disp = purple_connection_get_display_name(account->gc)))
                  purple_conv_chat_set_nick(conv->u.chat, disp);
            else
                  purple_conv_chat_set_nick(conv->u.chat,
                                                      purple_account_get_username(account));

            if (purple_prefs_get_bool("/purple/logging/log_chats"))
            {
                  purple_conversation_set_logging(conv, TRUE);
                  open_log(conv);
            }
      }

      conversations = g_list_prepend(conversations, conv);

      hc = g_new(struct _purple_hconv, 1);
      hc->name = g_strdup(purple_normalize(account, conv->name));
      hc->account = account;
      hc->type = type;

      g_hash_table_insert(conversation_cache, hc, conv);

      /* Auto-set the title. */
      purple_conversation_autoset_title(conv);

      /* Don't move this.. it needs to be one of the last things done otherwise
       * it causes mysterious crashes on my system.
       *  -- Gary
       */
      ops  = conv->ui_ops = default_ops;
      if (ops != NULL && ops->create_conversation != NULL)
            ops->create_conversation(conv);

      purple_signal_emit(purple_conversations_get_handle(),
                               "conversation-created", conv);

      return conv;
}

void
00440 purple_conversation_destroy(PurpleConversation *conv)
{
      PurplePluginProtocolInfo *prpl_info = NULL;
      PurpleConversationUiOps *ops;
      PurpleConnection *gc;
      const char *name;
      struct _purple_hconv hc;

      g_return_if_fail(conv != NULL);

      purple_request_close_with_handle(conv);

      ops  = purple_conversation_get_ui_ops(conv);
      gc   = purple_conversation_get_gc(conv);
      name = purple_conversation_get_name(conv);

      if (gc != NULL)
      {
            /* Still connected */
            prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));

            if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
            {
                  if (purple_prefs_get_bool("/purple/conversations/im/send_typing"))
                        serv_send_typing(gc, name, PURPLE_NOT_TYPING);

                  if (gc && prpl_info->convo_closed != NULL)
                        prpl_info->convo_closed(gc, name);
            }
            else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
            {
                  int chat_id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
#if 0
                  /*
                   * This is unfortunately necessary, because calling
                   * serv_chat_leave() calls this purple_conversation_destroy(),
                   * which leads to two calls here.. We can't just return after
                   * this, because then it'll return on the next pass. So, since
                   * serv_got_chat_left(), which is eventually called from the
                   * prpl that serv_chat_leave() calls, removes this conversation
                   * from the gc's buddy_chats list, we're going to check to see
                   * if this exists in the list. If so, we want to return after
                   * calling this, because it'll be called again. If not, fall
                   * through, because it'll have already been removed, and we'd
                   * be on the 2nd pass.
                   *
                   * Long paragraph. <-- Short sentence.
                   *
                   *   -- ChipX86
                   */

                  if (gc && g_slist_find(gc->buddy_chats, conv) != NULL) {
                        serv_chat_leave(gc, chat_id);

                        return;
                  }
#endif
                  /*
                   * Instead of all of that, lets just close the window when
                   * the user tells us to, and let the prpl deal with the
                   * internals on it's own time. Don't do this if the prpl already
                   * knows it left the chat.
                   */
                  if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
                        serv_chat_leave(gc, chat_id);

                  /*
                   * If they didn't call serv_got_chat_left by now, it's too late.
                   * So we better do it for them before we destroy the thing.
                   */
                  if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
                        serv_got_chat_left(gc, chat_id);
            }
      }

      /* remove from conversations and im/chats lists prior to emit */
      conversations = g_list_remove(conversations, conv);

      if(conv->type==PURPLE_CONV_TYPE_IM)
            ims = g_list_remove(ims, conv);
      else if(conv->type==PURPLE_CONV_TYPE_CHAT)
            chats = g_list_remove(chats, conv);

      hc.name = (gchar *)purple_normalize(conv->account, conv->name);
      hc.account = conv->account;
      hc.type = conv->type;

      g_hash_table_remove(conversation_cache, &hc);

      purple_signal_emit(purple_conversations_get_handle(),
                               "deleting-conversation", conv);

      g_free(conv->name);
      g_free(conv->title);

      conv->name = NULL;
      conv->title = NULL;

      if (conv->type == PURPLE_CONV_TYPE_IM) {
            purple_conv_im_stop_typing_timeout(conv->u.im);
            purple_conv_im_stop_send_typed_timeout(conv->u.im);

            purple_buddy_icon_unref(conv->u.im->icon);
            conv->u.im->icon = NULL;

            PURPLE_DBUS_UNREGISTER_POINTER(conv->u.im);
            g_free(conv->u.im);
            conv->u.im = NULL;
      }
      else if (conv->type == PURPLE_CONV_TYPE_CHAT) {

            g_list_foreach(conv->u.chat->in_room, (GFunc)purple_conv_chat_cb_destroy, NULL);
            g_list_free(conv->u.chat->in_room);

            g_list_foreach(conv->u.chat->ignored, (GFunc)g_free, NULL);
            g_list_free(conv->u.chat->ignored);

            conv->u.chat->in_room = NULL;
            conv->u.chat->ignored = NULL;

            g_free(conv->u.chat->who);
            conv->u.chat->who = NULL;

            g_free(conv->u.chat->topic);
            conv->u.chat->topic = NULL;

            g_free(conv->u.chat->nick);

            PURPLE_DBUS_UNREGISTER_POINTER(conv->u.chat);
            g_free(conv->u.chat);
            conv->u.chat = NULL;
      }

      g_hash_table_destroy(conv->data);
      conv->data = NULL;

      if (ops != NULL && ops->destroy_conversation != NULL)
            ops->destroy_conversation(conv);

      purple_conversation_close_logs(conv);

      purple_conversation_clear_message_history(conv);

      PURPLE_DBUS_UNREGISTER_POINTER(conv);
      g_free(conv);
      conv = NULL;
}


void
00590 purple_conversation_present(PurpleConversation *conv) {
      PurpleConversationUiOps *ops;

      g_return_if_fail(conv != NULL);

      ops = purple_conversation_get_ui_ops(conv);
      if(ops && ops->present)
            ops->present(conv);
}


void
00602 purple_conversation_set_features(PurpleConversation *conv, PurpleConnectionFlags features)
{
      g_return_if_fail(conv != NULL);

      conv->features = features;

      purple_conversation_update(conv, PURPLE_CONV_UPDATE_FEATURES);
}


PurpleConnectionFlags
00613 purple_conversation_get_features(PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, 0);
      return conv->features;
}


PurpleConversationType
00621 purple_conversation_get_type(const PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, PURPLE_CONV_TYPE_UNKNOWN);

      return conv->type;
}

void
00629 purple_conversation_set_ui_ops(PurpleConversation *conv,
                                           PurpleConversationUiOps *ops)
{
      g_return_if_fail(conv != NULL);

      if (conv->ui_ops == ops)
            return;

      if (conv->ui_ops != NULL && conv->ui_ops->destroy_conversation != NULL)
            conv->ui_ops->destroy_conversation(conv);

      conv->ui_data = NULL;

      conv->ui_ops = ops;
}

PurpleConversationUiOps *
00646 purple_conversation_get_ui_ops(const PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, NULL);

      return conv->ui_ops;
}

void
00654 purple_conversation_set_account(PurpleConversation *conv, PurpleAccount *account)
{
      g_return_if_fail(conv != NULL);

      if (account == purple_conversation_get_account(conv))
            return;

      conv->account = account;

      purple_conversation_update(conv, PURPLE_CONV_UPDATE_ACCOUNT);
}

PurpleAccount *
00667 purple_conversation_get_account(const PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, NULL);

      return conv->account;
}

PurpleConnection *
00675 purple_conversation_get_gc(const PurpleConversation *conv)
{
      PurpleAccount *account;

      g_return_val_if_fail(conv != NULL, NULL);

      account = purple_conversation_get_account(conv);

      if (account == NULL)
            return NULL;

      return account->gc;
}

void
00690 purple_conversation_set_title(PurpleConversation *conv, const char *title)
{
      g_return_if_fail(conv  != NULL);
      g_return_if_fail(title != NULL);

      g_free(conv->title);
      conv->title = g_strdup(title);

      purple_conversation_update(conv, PURPLE_CONV_UPDATE_TITLE);
}

const char *
00702 purple_conversation_get_title(const PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, NULL);

      return conv->title;
}

void
00710 purple_conversation_autoset_title(PurpleConversation *conv)
{
      PurpleAccount *account;
      PurpleBuddy *b;
      PurpleChat *chat;
      const char *text = NULL, *name;

      g_return_if_fail(conv != NULL);

      account = purple_conversation_get_account(conv);
      name = purple_conversation_get_name(conv);

      if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
            if(account && ((b = purple_find_buddy(account, name)) != NULL))
                  text = purple_buddy_get_contact_alias(b);
      } else if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
            if(account && ((chat = purple_blist_find_chat(account, name)) != NULL))
                  text = purple_chat_get_name(chat);
      }


      if(text == NULL)
            text = name;

      purple_conversation_set_title(conv, text);
}

void
00738 purple_conversation_foreach(void (*func)(PurpleConversation *conv))
{
      PurpleConversation *conv;
      GList *l;

      g_return_if_fail(func != NULL);

      for (l = purple_get_conversations(); l != NULL; l = l->next) {
            conv = (PurpleConversation *)l->data;

            func(conv);
      }
}

void
00753 purple_conversation_set_name(PurpleConversation *conv, const char *name)
{
      struct _purple_hconv *hc;
      g_return_if_fail(conv != NULL);

      hc = g_new(struct _purple_hconv, 1);
      hc->type = conv->type;
      hc->account = conv->account;
      hc->name = (gchar *)purple_normalize(conv->account, conv->name);

      g_hash_table_remove(conversation_cache, hc);
      g_free(conv->name);

      conv->name = g_strdup(name);
      hc->name = g_strdup(purple_normalize(conv->account, conv->name));
      g_hash_table_insert(conversation_cache, hc, conv);

      purple_conversation_autoset_title(conv);
}

const char *
00774 purple_conversation_get_name(const PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, NULL);

      return conv->name;
}

void
00782 purple_conversation_set_logging(PurpleConversation *conv, gboolean log)
{
      g_return_if_fail(conv != NULL);

      if (conv->logging != log)
      {
            conv->logging = log;
            purple_conversation_update(conv, PURPLE_CONV_UPDATE_LOGGING);
      }
}

gboolean
00794 purple_conversation_is_logging(const PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, FALSE);

      return conv->logging;
}

void
00802 purple_conversation_close_logs(PurpleConversation *conv)
{
      g_return_if_fail(conv != NULL);

      g_list_foreach(conv->logs, (GFunc)purple_log_free, NULL);
      g_list_free(conv->logs);
      conv->logs = NULL;
}

PurpleConvIm *
00812 purple_conversation_get_im_data(const PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, NULL);

      if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM)
            return NULL;

      return conv->u.im;
}

PurpleConvChat *
00823 purple_conversation_get_chat_data(const PurpleConversation *conv)
{
      g_return_val_if_fail(conv != NULL, NULL);

      if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT)
            return NULL;

      return conv->u.chat;
}

void
00834 purple_conversation_set_data(PurpleConversation *conv, const char *key,
                                       gpointer data)
{
      g_return_if_fail(conv != NULL);
      g_return_if_fail(key  != NULL);

      g_hash_table_replace(conv->data, g_strdup(key), data);
}

gpointer
00844 purple_conversation_get_data(PurpleConversation *conv, const char *key)
{
      g_return_val_if_fail(conv != NULL, NULL);
      g_return_val_if_fail(key  != NULL, NULL);

      return g_hash_table_lookup(conv->data, key);
}

GList *
00853 purple_get_conversations(void)
{
      return conversations;
}

GList *
00859 purple_get_ims(void)
{
      return ims;
}

GList *
00865 purple_get_chats(void)
{
      return chats;
}


PurpleConversation *
00872 purple_find_conversation_with_account(PurpleConversationType type,
                                                      const char *name,
                                                      const PurpleAccount *account)
{
      PurpleConversation *c = NULL;
      struct _purple_hconv hc;

      g_return_val_if_fail(name != NULL, NULL);

      hc.name = (gchar *)purple_normalize(account, name);
      hc.account = account;
      hc.type = type;

      switch (type) {
            case PURPLE_CONV_TYPE_IM:
            case PURPLE_CONV_TYPE_CHAT:
                  c = g_hash_table_lookup(conversation_cache, &hc);
                  break;
            case PURPLE_CONV_TYPE_ANY:
                  hc.type = PURPLE_CONV_TYPE_IM;
                  c = g_hash_table_lookup(conversation_cache, &hc);
                  if (!c) {
                        hc.type = PURPLE_CONV_TYPE_CHAT;
                        c = g_hash_table_lookup(conversation_cache, &hc);
                  }
                  break;
            default:
                  g_return_val_if_reached(NULL);
      }

      return c;
}

void
00906 purple_conversation_write(PurpleConversation *conv, const char *who,
                                    const char *message, PurpleMessageFlags flags,
                                    time_t mtime)
{
      PurplePluginProtocolInfo *prpl_info = NULL;
      PurpleConnection *gc = NULL;
      PurpleAccount *account;
      PurpleConversationUiOps *ops;
      const char *alias;
      char *displayed = NULL;
      PurpleBuddy *b;
      int plugin_return;
      PurpleConversationType type;
      /* int logging_font_options = 0; */

      g_return_if_fail(conv    != NULL);
      g_return_if_fail(message != NULL);

      ops = purple_conversation_get_ui_ops(conv);

      account = purple_conversation_get_account(conv);
      type = purple_conversation_get_type(conv);

      if (account != NULL)
            gc = purple_account_get_connection(account);

      if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
            (gc != NULL && !g_slist_find(gc->buddy_chats, conv)))
            return;

      if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
            !g_list_find(purple_get_conversations(), conv))
            return;

      displayed = g_strdup(message);

      if (who == NULL || *who == '\0')
            who = purple_conversation_get_name(conv);
      alias = who;

      plugin_return =
            GPOINTER_TO_INT(purple_signal_emit_return_1(
                  purple_conversations_get_handle(),
                  (type == PURPLE_CONV_TYPE_IM ? "writing-im-msg" : "writing-chat-msg"),
                  account, who, &displayed, conv, flags));

      if (displayed == NULL)
            return;

      if (plugin_return) {
            g_free(displayed);
            return;
      }

      if (account != NULL) {
            prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));

            if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM ||
                  !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {

                  if (flags & PURPLE_MESSAGE_SEND) {
                        b = purple_find_buddy(account,
                                          purple_account_get_username(account));

                        if (purple_account_get_alias(account) != NULL)
                              alias = account->alias;
                        else if (b != NULL && !purple_strequal(purple_buddy_get_name(b), purple_buddy_get_contact_alias(b)))
                              alias = purple_buddy_get_contact_alias(b);
                        else if (purple_connection_get_display_name(gc) != NULL)
                              alias = purple_connection_get_display_name(gc);
                        else
                              alias = purple_account_get_username(account);
                  }
                  else
                  {
                        b = purple_find_buddy(account, who);

                        if (b != NULL)
                              alias = purple_buddy_get_contact_alias(b);
                  }
            }
      }

      if (!(flags & PURPLE_MESSAGE_NO_LOG) && purple_conversation_is_logging(conv)) {
            GList *log;

            if (conv->logs == NULL)
                  open_log(conv);

            log = conv->logs;
            while (log != NULL) {
                  purple_log_write((PurpleLog *)log->data, flags, alias, mtime, displayed);
                  log = log->next;
            }
      }

      if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
            if ((flags & PURPLE_MESSAGE_RECV) == PURPLE_MESSAGE_RECV) {
                  purple_conv_im_set_typing_state(PURPLE_CONV_IM(conv), PURPLE_NOT_TYPING);
            }
      }

      if (ops && ops->write_conv)
            ops->write_conv(conv, who, alias, displayed, flags, mtime);

      add_message_to_history(conv, who, alias, message, flags, mtime);

      purple_signal_emit(purple_conversations_get_handle(),
            (type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"),
            account, who, displayed, conv, flags);

      g_free(displayed);
}

gboolean
01021 purple_conversation_has_focus(PurpleConversation *conv)
{
      gboolean ret = FALSE;
      PurpleConversationUiOps *ops;

      g_return_val_if_fail(conv != NULL, FALSE);

      ops = purple_conversation_get_ui_ops(conv);

      if (ops != NULL && ops->has_focus != NULL)
            ret = ops->has_focus(conv);

      return ret;
}

/*
 * TODO: Need to make sure calls to this function happen in the core
 * instead of the UI.  That way UIs have less work to do, and the
 * core/UI split is cleaner.  Also need to make sure this is called
 * when chats are added/removed from the blist.
 */
void
01043 purple_conversation_update(PurpleConversation *conv, PurpleConvUpdateType type)
{
      g_return_if_fail(conv != NULL);

      purple_signal_emit(purple_conversations_get_handle(),
                               "conversation-updated", conv, type);
}

/**************************************************************************
 * IM Conversation API
 **************************************************************************/
PurpleConversation *
01055 purple_conv_im_get_conversation(const PurpleConvIm *im)
{
      g_return_val_if_fail(im != NULL, NULL);

      return im->conv;
}

void
01063 purple_conv_im_set_icon(PurpleConvIm *im, PurpleBuddyIcon *icon)
{
      g_return_if_fail(im != NULL);

      if (im->icon != icon)
      {
            purple_buddy_icon_unref(im->icon);

            im->icon = (icon == NULL ? NULL : purple_buddy_icon_ref(icon));
      }

      purple_conversation_update(purple_conv_im_get_conversation(im),
                                           PURPLE_CONV_UPDATE_ICON);
}

PurpleBuddyIcon *
01079 purple_conv_im_get_icon(const PurpleConvIm *im)
{
      g_return_val_if_fail(im != NULL, NULL);

      return im->icon;
}

void
01087 purple_conv_im_set_typing_state(PurpleConvIm *im, PurpleTypingState state)
{
      g_return_if_fail(im != NULL);

      if (im->typing_state != state)
      {
            im->typing_state = state;

            switch (state)
            {
                  case PURPLE_TYPING:
                        purple_signal_emit(purple_conversations_get_handle(),
                                                   "buddy-typing", im->conv->account, im->conv->name);
                        break;
                  case PURPLE_TYPED:
                        purple_signal_emit(purple_conversations_get_handle(),
                                                   "buddy-typed", im->conv->account, im->conv->name);
                        break;
                  case PURPLE_NOT_TYPING:
                        purple_signal_emit(purple_conversations_get_handle(),
                                                   "buddy-typing-stopped", im->conv->account, im->conv->name);
                        break;
            }

            purple_conv_im_update_typing(im);
      }
}

PurpleTypingState
01116 purple_conv_im_get_typing_state(const PurpleConvIm *im)
{
      g_return_val_if_fail(im != NULL, 0);

      return im->typing_state;
}

void
01124 purple_conv_im_start_typing_timeout(PurpleConvIm *im, int timeout)
{
      PurpleConversation *conv;
      const char *name;

      g_return_if_fail(im != NULL);

      if (im->typing_timeout > 0)
            purple_conv_im_stop_typing_timeout(im);

      conv = purple_conv_im_get_conversation(im);
      name = purple_conversation_get_name(conv);

      im->typing_timeout = purple_timeout_add_seconds(timeout, reset_typing_cb, conv);
}

void
01141 purple_conv_im_stop_typing_timeout(PurpleConvIm *im)
{
      g_return_if_fail(im != NULL);

      if (im->typing_timeout == 0)
            return;

      purple_timeout_remove(im->typing_timeout);
      im->typing_timeout = 0;
}

guint
01153 purple_conv_im_get_typing_timeout(const PurpleConvIm *im)
{
      g_return_val_if_fail(im != NULL, 0);

      return im->typing_timeout;
}

void
01161 purple_conv_im_set_type_again(PurpleConvIm *im, unsigned int val)
{
      g_return_if_fail(im != NULL);

      if (val == 0)
            im->type_again = 0;
      else
            im->type_again = time(NULL) + val;
}

time_t
01172 purple_conv_im_get_type_again(const PurpleConvIm *im)
{
      g_return_val_if_fail(im != NULL, 0);

      return im->type_again;
}

void
01180 purple_conv_im_start_send_typed_timeout(PurpleConvIm *im)
{
      g_return_if_fail(im != NULL);

      im->send_typed_timeout = purple_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS,
                                                          send_typed_cb,
                                                          purple_conv_im_get_conversation(im));
}

void
01190 purple_conv_im_stop_send_typed_timeout(PurpleConvIm *im)
{
      g_return_if_fail(im != NULL);

      if (im->send_typed_timeout == 0)
            return;

      purple_timeout_remove(im->send_typed_timeout);
      im->send_typed_timeout = 0;
}

guint
01202 purple_conv_im_get_send_typed_timeout(const PurpleConvIm *im)
{
      g_return_val_if_fail(im != NULL, 0);

      return im->send_typed_timeout;
}

void
01210 purple_conv_im_update_typing(PurpleConvIm *im)
{
      g_return_if_fail(im != NULL);

      purple_conversation_update(purple_conv_im_get_conversation(im),
                                           PURPLE_CONV_UPDATE_TYPING);
}

void
01219 purple_conv_im_write(PurpleConvIm *im, const char *who, const char *message,
                    PurpleMessageFlags flags, time_t mtime)
{
      PurpleConversation *c;

      g_return_if_fail(im != NULL);
      g_return_if_fail(message != NULL);

      c = purple_conv_im_get_conversation(im);

      /* Pass this on to either the ops structure or the default write func. */
      if (c->ui_ops != NULL && c->ui_ops->write_im != NULL)
            c->ui_ops->write_im(c, who, message, flags, mtime);
      else
            purple_conversation_write(c, who, message, flags, mtime);
}

01236 gboolean purple_conv_present_error(const char *who, PurpleAccount *account, const char *what)
{
      PurpleConversation *conv;

      g_return_val_if_fail(who != NULL, FALSE);
      g_return_val_if_fail(account !=NULL, FALSE);
      g_return_val_if_fail(what != NULL, FALSE);

      conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
      if (conv != NULL)
            purple_conversation_write(conv, NULL, what, PURPLE_MESSAGE_ERROR, time(NULL));
      else
            return FALSE;

      return TRUE;
}

void
01254 purple_conv_im_send(PurpleConvIm *im, const char *message)
{
      purple_conv_im_send_with_flags(im, message, 0);
}

static void
purple_conv_send_confirm_cb(gpointer *data)
{
      PurpleConversation *conv = data[0];
      char *message = data[1];

      g_free(data);
      common_send(conv, message, 0);
}

void
01270 purple_conv_send_confirm(PurpleConversation *conv, const char *message)
{
      char *text;
      gpointer *data;

      g_return_if_fail(conv != NULL);
      g_return_if_fail(message != NULL);

      if (conv->ui_ops != NULL && conv->ui_ops->send_confirm != NULL)
      {
            conv->ui_ops->send_confirm(conv, message);
            return;
      }

      text = g_strdup_printf("You are about to send the following message:\n%s", message);
      data = g_new0(gpointer, 2);
      data[0] = conv;
      data[1] = (gpointer)message;

      purple_request_action(conv, NULL, _("Send Message"), text, 0,
                                      purple_conversation_get_account(conv), NULL, conv,
                                      data, 2,
                                      _("_Send Message"), G_CALLBACK(purple_conv_send_confirm_cb),
                                      _("Cancel"), NULL);
}

void
01297 purple_conv_im_send_with_flags(PurpleConvIm *im, const char *message, PurpleMessageFlags flags)
{
      g_return_if_fail(im != NULL);
      g_return_if_fail(message != NULL);

      common_send(purple_conv_im_get_conversation(im), message, flags);
}

gboolean
01306 purple_conv_custom_smiley_add(PurpleConversation *conv, const char *smile,
                            const char *cksum_type, const char *chksum,
                                          gboolean remote)
{
      if (conv == NULL || smile == NULL || !*smile) {
            return FALSE;
      }

      /* TODO: check if the icon is in the cache and return false if so */
      /* TODO: add an icon cache (that doesn't suck) */
      if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_add !=NULL) {
            return conv->ui_ops->custom_smiley_add(conv, smile, remote);
      } else {
            purple_debug_info("conversation", "Could not find add custom smiley function");
            return FALSE;
      }

}

void
01326 purple_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
                                   const guchar *data, gsize size)
{
      g_return_if_fail(conv != NULL);
      g_return_if_fail(smile != NULL && *smile);

      if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_write != NULL)
            conv->ui_ops->custom_smiley_write(conv, smile, data, size);
      else
            purple_debug_info("conversation", "Could not find the smiley write function");
}

void
01339 purple_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
{
      g_return_if_fail(conv != NULL);
      g_return_if_fail(smile != NULL && *smile);

      if (conv->ui_ops != NULL && conv->ui_ops->custom_smiley_close != NULL)
            conv->ui_ops->custom_smiley_close(conv, smile);
      else
            purple_debug_info("conversation", "Could not find custom smiley close function");
}


/**************************************************************************
 * Chat Conversation API
 **************************************************************************/

PurpleConversation *
01356 purple_conv_chat_get_conversation(const PurpleConvChat *chat)
{
      g_return_val_if_fail(chat != NULL, NULL);

      return chat->conv;
}

GList *
01364 purple_conv_chat_set_users(PurpleConvChat *chat, GList *users)
{
      g_return_val_if_fail(chat != NULL, NULL);

      chat->in_room = users;

      return users;
}

GList *
01374 purple_conv_chat_get_users(const PurpleConvChat *chat)
{
      g_return_val_if_fail(chat != NULL, NULL);

      return chat->in_room;
}

void
01382 purple_conv_chat_ignore(PurpleConvChat *chat, const char *name)
{
      g_return_if_fail(chat != NULL);
      g_return_if_fail(name != NULL);

      /* Make sure the user isn't already ignored. */
      if (purple_conv_chat_is_user_ignored(chat, name))
            return;

      purple_conv_chat_set_ignored(chat,
            g_list_append(chat->ignored, g_strdup(name)));
}

void
01396 purple_conv_chat_unignore(PurpleConvChat *chat, const char *name)
{
      GList *item;

      g_return_if_fail(chat != NULL);
      g_return_if_fail(name != NULL);

      /* Make sure the user is actually ignored. */
      if (!purple_conv_chat_is_user_ignored(chat, name))
            return;

      item = g_list_find(purple_conv_chat_get_ignored(chat),
                                 purple_conv_chat_get_ignored_user(chat, name));

      purple_conv_chat_set_ignored(chat,
            g_list_remove_link(chat->ignored, item));

      g_free(item->data);
      g_list_free_1(item);
}

GList *
01418 purple_conv_chat_set_ignored(PurpleConvChat *chat, GList *ignored)
{
      g_return_val_if_fail(chat != NULL, NULL);

      chat->ignored = ignored;

      return ignored;
}

GList *
01428 purple_conv_chat_get_ignored(const PurpleConvChat *chat)
{
      g_return_val_if_fail(chat != NULL, NULL);

      return chat->ignored;
}

const char *
01436 purple_conv_chat_get_ignored_user(const PurpleConvChat *chat, const char *user)
{
      GList *ignored;

      g_return_val_if_fail(chat != NULL, NULL);
      g_return_val_if_fail(user != NULL, NULL);

      for (ignored = purple_conv_chat_get_ignored(chat);
             ignored != NULL;
             ignored = ignored->next) {

            const char *ign = (const char *)ignored->data;

            if (!purple_utf8_strcasecmp(user, ign) ||
                  ((*ign == '+' || *ign == '%') && !purple_utf8_strcasecmp(user, ign + 1)))
                  return ign;

            if (*ign == '@') {
                  ign++;

                  if ((*ign == '+' && !purple_utf8_strcasecmp(user, ign + 1)) ||
                        (*ign != '+' && !purple_utf8_strcasecmp(user, ign)))
                        return ign;
            }
      }

      return NULL;
}

gboolean
01466 purple_conv_chat_is_user_ignored(const PurpleConvChat *chat, const char *user)
{
      g_return_val_if_fail(chat != NULL, FALSE);
      g_return_val_if_fail(user != NULL, FALSE);

      return (purple_conv_chat_get_ignored_user(chat, user) != NULL);
}

void
01475 purple_conv_chat_set_topic(PurpleConvChat *chat, const char *who, const char *topic)
{
      g_return_if_fail(chat != NULL);

      g_free(chat->who);
      g_free(chat->topic);

      chat->who   = g_strdup(who);
      chat->topic = g_strdup(topic);

      purple_conversation_update(purple_conv_chat_get_conversation(chat),
                                           PURPLE_CONV_UPDATE_TOPIC);

      purple_signal_emit(purple_conversations_get_handle(), "chat-topic-changed",
                               chat->conv, chat->who, chat->topic);
}

const char *
01493 purple_conv_chat_get_topic(const PurpleConvChat *chat)
{
      g_return_val_if_fail(chat != NULL, NULL);

      return chat->topic;
}

void
01501 purple_conv_chat_set_id(PurpleConvChat *chat, int id)
{
      g_return_if_fail(chat != NULL);

      chat->id = id;
}

int
01509 purple_conv_chat_get_id(const PurpleConvChat *chat)
{
      g_return_val_if_fail(chat != NULL, -1);

      return chat->id;
}

void
01517 purple_conv_chat_write(PurpleConvChat *chat, const char *who, const char *message,
                        PurpleMessageFlags flags, time_t mtime)
{
      PurpleAccount *account;
      PurpleConversation *conv;
      PurpleConnection *gc;
      PurplePluginProtocolInfo *prpl_info;

      g_return_if_fail(chat != NULL);
      g_return_if_fail(who != NULL);
      g_return_if_fail(message != NULL);

      conv      = purple_conv_chat_get_conversation(chat);
      gc        = purple_conversation_get_gc(conv);
      account   = purple_connection_get_account(gc);
      prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));

      /* Don't display this if the person who wrote it is ignored. */
      if (purple_conv_chat_is_user_ignored(chat, who))
            return;

      if (!(flags & PURPLE_MESSAGE_WHISPER)) {
            const char *str;

            str = purple_normalize(account, who);

            if (purple_strequal(str, chat->nick)) {
                  flags |= PURPLE_MESSAGE_SEND;
            } else {
                  flags |= PURPLE_MESSAGE_RECV;

                  if (purple_utf8_has_word(message, chat->nick))
                        flags |= PURPLE_MESSAGE_NICK;
            }
      }

      /* Pass this on to either the ops structure or the default write func. */
      if (conv->ui_ops != NULL && conv->ui_ops->write_chat != NULL)
            conv->ui_ops->write_chat(conv, who, message, flags, mtime);
      else
            purple_conversation_write(conv, who, message, flags, mtime);
}

void
01561 purple_conv_chat_send(PurpleConvChat *chat, const char *message)
{
      purple_conv_chat_send_with_flags(chat, message, 0);
}

void
01567 purple_conv_chat_send_with_flags(PurpleConvChat *chat, const char *message, PurpleMessageFlags flags)
{
      g_return_if_fail(chat != NULL);
      g_return_if_fail(message != NULL);

      common_send(purple_conv_chat_get_conversation(chat), message, flags);
}

void
01576 purple_conv_chat_add_user(PurpleConvChat *chat, const char *user,
                                    const char *extra_msg, PurpleConvChatBuddyFlags flags,
                                    gboolean new_arrival)
{
      GList *users = g_list_append(NULL, (char *)user);
      GList *extra_msgs = g_list_append(NULL, (char *)extra_msg);
      GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags));

      purple_conv_chat_add_users(chat, users, extra_msgs, flags2, new_arrival);

      g_list_free(users);
      g_list_free(extra_msgs);
      g_list_free(flags2);
}

static int
purple_conv_chat_cb_compare(PurpleConvChatBuddy *a, PurpleConvChatBuddy *b)
{
      PurpleConvChatBuddyFlags f1 = 0, f2 = 0;
      char *user1 = NULL, *user2 = NULL;
      gint ret = 0;

      if (a) {
            f1 = a->flags;
            if (a->alias_key)
                  user1 = a->alias_key;
            else if (a->name)
                  user1 = a->name;
      }

      if (b) {
            f2 = b->flags;
            if (b->alias_key)
                  user2 = b->alias_key;
            else if (b->name)
                  user2 = b->name;
      }

      if (user1 == NULL || user2 == NULL) {
            if (!(user1 == NULL && user2 == NULL))
                  ret = (user1 == NULL) ? -1: 1;
      } else if (f1 != f2) {
            /* sort more important users first */
            ret = (f1 > f2) ? -1 : 1;
      } else if (a->buddy != b->buddy) {
            ret = a->buddy ? -1 : 1;
      } else {
            ret = purple_utf8_strcasecmp(user1, user2);
      }

      return ret;
}

void
01630 purple_conv_chat_add_users(PurpleConvChat *chat, GList *users, GList *extra_msgs,
                                     GList *flags, gboolean new_arrivals)
{
      PurpleConversation *conv;
      PurpleConversationUiOps *ops;
      PurpleConvChatBuddy *cbuddy;
      PurpleConnection *gc;
      PurplePluginProtocolInfo *prpl_info;
      GList *ul, *fl;
      GList *cbuddies = NULL;

      g_return_if_fail(chat  != NULL);
      g_return_if_fail(users != NULL);

      conv = purple_conv_chat_get_conversation(chat);
      ops  = purple_conversation_get_ui_ops(conv);

      gc = purple_conversation_get_gc(conv);
      g_return_if_fail(gc != NULL);
      prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
      g_return_if_fail(prpl_info != NULL);

      ul = users;
      fl = flags;
      while ((ul != NULL) && (fl != NULL)) {
            const char *user = (const char *)ul->data;
            const char *alias = user;
            gboolean quiet;
            PurpleConvChatBuddyFlags flag = GPOINTER_TO_INT(fl->data);
            const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);

            if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
                  if (purple_strequal(chat->nick, purple_normalize(conv->account, user))) {
                        const char *alias2 = purple_account_get_alias(conv->account);
                        if (alias2 != NULL)
                              alias = alias2;
                        else
                        {
                              const char *display_name = purple_connection_get_display_name(gc);
                              if (display_name != NULL)
                                    alias = display_name;
                        }
                  } else {
                        PurpleBuddy *buddy;
                        if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
                              alias = purple_buddy_get_contact_alias(buddy);
                  }
            }

            quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
                                     "chat-buddy-joining", conv, user, flag)) ||
                        purple_conv_chat_is_user_ignored(chat, user);

            cbuddy = purple_conv_chat_cb_new(user, alias, flag);
            cbuddy->buddy = purple_find_buddy(conv->account, user) != NULL;
            /* This seems dumb. Why should we set users thousands of times? */
            purple_conv_chat_set_users(chat,
                        g_list_prepend(chat->in_room, cbuddy));

            cbuddies = g_list_prepend(cbuddies, cbuddy);

            if (!quiet && new_arrivals) {
                  char *alias_esc = g_markup_escape_text(alias, -1);
                  char *tmp;

                  if (extra_msg == NULL)
                        tmp = g_strdup_printf(_("%s entered the room."), alias_esc);
                  else {
                        char *extra_msg_esc = g_markup_escape_text(extra_msg, -1);
                        tmp = g_strdup_printf(_("%s [<I>%s</I>] entered the room."),
                                              alias_esc, extra_msg_esc);
                        g_free(extra_msg_esc);
                  }
                  g_free(alias_esc);

                  purple_conversation_write(conv, NULL, tmp,
                              PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
                              time(NULL));
                  g_free(tmp);
            }

            purple_signal_emit(purple_conversations_get_handle(),
                                     "chat-buddy-joined", conv, user, flag, new_arrivals);
            ul = ul->next;
            fl = fl->next;
            if (extra_msgs != NULL)
                  extra_msgs = extra_msgs->next;
      }

      cbuddies = g_list_sort(cbuddies, (GCompareFunc)purple_conv_chat_cb_compare);

      if (ops != NULL && ops->chat_add_users != NULL)
            ops->chat_add_users(conv, cbuddies, new_arrivals);

      g_list_free(cbuddies);
}

void
01728 purple_conv_chat_rename_user(PurpleConvChat *chat, const char *old_user,
                                       const char *new_user)
{
      PurpleConversation *conv;
      PurpleConversationUiOps *ops;
      PurpleConnection *gc;
      PurplePluginProtocolInfo *prpl_info;
      PurpleConvChatBuddy *cb;
      PurpleConvChatBuddyFlags flags;
      const char *new_alias = new_user;
      char tmp[BUF_LONG];
      gboolean is_me = FALSE;

      g_return_if_fail(chat != NULL);
      g_return_if_fail(old_user != NULL);
      g_return_if_fail(new_user != NULL);

      conv = purple_conv_chat_get_conversation(chat);
      ops  = purple_conversation_get_ui_ops(conv);

      gc = purple_conversation_get_gc(conv);
      g_return_if_fail(gc != NULL);
      prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
      g_return_if_fail(prpl_info != NULL);

      if (purple_strequal(chat->nick, purple_normalize(conv->account, old_user))) {
            const char *alias;

            /* Note this for later. */
            is_me = TRUE;

            if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
                  alias = purple_account_get_alias(conv->account);
                  if (alias != NULL)
                        new_alias = alias;
                  else
                  {
                        const char *display_name = purple_connection_get_display_name(gc);
                        if (display_name != NULL)
                              alias = display_name;
                  }
            }
      } else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
            PurpleBuddy *buddy;
            if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
                  new_alias = purple_buddy_get_contact_alias(buddy);
      }

      flags = purple_conv_chat_user_get_flags(chat, old_user);
      cb = purple_conv_chat_cb_new(new_user, new_alias, flags);
      cb->buddy = purple_find_buddy(conv->account, new_user) != NULL;
      purple_conv_chat_set_users(chat,
            g_list_prepend(chat->in_room, cb));

      if (ops != NULL && ops->chat_rename_user != NULL)
            ops->chat_rename_user(conv, old_user, new_user, new_alias);

      cb = purple_conv_chat_cb_find(chat, old_user);

      if (cb) {
            purple_conv_chat_set_users(chat,
                        g_list_remove(chat->in_room, cb));
            purple_conv_chat_cb_destroy(cb);
      }

      if (purple_conv_chat_is_user_ignored(chat, old_user)) {
            purple_conv_chat_unignore(chat, old_user);
            purple_conv_chat_ignore(chat, new_user);
      }
      else if (purple_conv_chat_is_user_ignored(chat, new_user))
            purple_conv_chat_unignore(chat, new_user);

      if (is_me)
            purple_conv_chat_set_nick(chat, new_user);

      if (purple_prefs_get_bool("/purple/conversations/chat/show_nick_change") &&
          !purple_conv_chat_is_user_ignored(chat, new_user)) {

            if (is_me) {
                  char *escaped = g_markup_escape_text(new_user, -1);
                  g_snprintf(tmp, sizeof(tmp),
                              _("You are now known as %s"), escaped);
                  g_free(escaped);
            } else {
                  const char *old_alias = old_user;
                  const char *new_alias = new_user;
                  char *escaped;
                  char *escaped2;

                  if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
                        PurpleBuddy *buddy;

                        if ((buddy = purple_find_buddy(gc->account, old_user)) != NULL)
                              old_alias = purple_buddy_get_contact_alias(buddy);
                        if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
                              new_alias = purple_buddy_get_contact_alias(buddy);
                  }

                  escaped = g_markup_escape_text(old_alias, -1);
                  escaped2 = g_markup_escape_text(new_alias, -1);
                  g_snprintf(tmp, sizeof(tmp),
                              _("%s is now known as %s"), escaped, escaped2);
                  g_free(escaped);
                  g_free(escaped2);
            }

            purple_conversation_write(conv, NULL, tmp,
                        PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
                        time(NULL));
      }
}

void
01841 purple_conv_chat_remove_user(PurpleConvChat *chat, const char *user, const char *reason)
{
      GList *users = g_list_append(NULL, (char *)user);

      purple_conv_chat_remove_users(chat, users, reason);

      g_list_free(users);
}

void
01851 purple_conv_chat_remove_users(PurpleConvChat *chat, GList *users, const char *reason)
{
      PurpleConversation *conv;
      PurpleConnection *gc;
      PurplePluginProtocolInfo *prpl_info;
      PurpleConversationUiOps *ops;
      PurpleConvChatBuddy *cb;
      GList *l;
      gboolean quiet;

      g_return_if_fail(chat  != NULL);
      g_return_if_fail(users != NULL);

      conv = purple_conv_chat_get_conversation(chat);

      gc = purple_conversation_get_gc(conv);
      g_return_if_fail(gc != NULL);
      prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
      g_return_if_fail(prpl_info != NULL);

      ops  = purple_conversation_get_ui_ops(conv);

      for (l = users; l != NULL; l = l->next) {
            const char *user = (const char *)l->data;
            quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
                              "chat-buddy-leaving", conv, user, reason)) |
                        purple_conv_chat_is_user_ignored(chat, user);

            cb = purple_conv_chat_cb_find(chat, user);

            if (cb) {
                  purple_conv_chat_set_users(chat,
                              g_list_remove(chat->in_room, cb));
                  purple_conv_chat_cb_destroy(cb);
            }

            /* NOTE: Don't remove them from ignored in case they re-enter. */

            if (!quiet) {
                  const char *alias = user;
                  char *alias_esc;
                  char *tmp;

                  if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
                        PurpleBuddy *buddy;

                        if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
                              alias = purple_buddy_get_contact_alias(buddy);
                  }

                  alias_esc = g_markup_escape_text(alias, -1);

                  if (reason == NULL || !*reason)
                        tmp = g_strdup_printf(_("%s left the room."), alias_esc);
                  else {
                        char *reason_esc = g_markup_escape_text(reason, -1);
                        tmp = g_strdup_printf(_("%s left the room (%s)."),
                                              alias_esc, reason_esc);
                        g_free(reason_esc);
                  }
                  g_free(alias_esc);

                  purple_conversation_write(conv, NULL, tmp,
                              PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
                              time(NULL));
                  g_free(tmp);
            }

            purple_signal_emit(purple_conversations_get_handle(), "chat-buddy-left",
                                     conv, user, reason);
      }

      if (ops != NULL && ops->chat_remove_users != NULL)
            ops->chat_remove_users(conv, users);
}

void
01928 purple_conv_chat_clear_users(PurpleConvChat *chat)
{
      PurpleConversation *conv;
      PurpleConversationUiOps *ops;
      GList *users;
      GList *l;
      GList *names = NULL;

      g_return_if_fail(chat != NULL);

      conv  = purple_conv_chat_get_conversation(chat);
      ops   = purple_conversation_get_ui_ops(conv);
      users = chat->in_room;

      if (ops != NULL && ops->chat_remove_users != NULL) {
            for (l = users; l; l = l->next) {
                  PurpleConvChatBuddy *cb = l->data;
                  names = g_list_prepend(names, cb->name);
            }
            ops->chat_remove_users(conv, names);
            g_list_free(names);
      }

      for (l = users; l; l = l->next)
      {
            PurpleConvChatBuddy *cb = l->data;

            purple_signal_emit(purple_conversations_get_handle(),
                                     "chat-buddy-leaving", conv, cb->name, NULL);
            purple_signal_emit(purple_conversations_get_handle(),
                                     "chat-buddy-left", conv, cb->name, NULL);

            purple_conv_chat_cb_destroy(cb);
      }

      g_list_free(users);
      purple_conv_chat_set_users(chat, NULL);
}


gboolean
01969 purple_conv_chat_find_user(PurpleConvChat *chat, const char *user)
{
      g_return_val_if_fail(chat != NULL, FALSE);
      g_return_val_if_fail(user != NULL, FALSE);

      return (purple_conv_chat_cb_find(chat, user) != NULL);
}

void
01978 purple_conv_chat_user_set_flags(PurpleConvChat *chat, const char *user,
                                            PurpleConvChatBuddyFlags flags)
{
      PurpleConversation *conv;
      PurpleConversationUiOps *ops;
      PurpleConvChatBuddy *cb;
      PurpleConvChatBuddyFlags oldflags;

      g_return_if_fail(chat != NULL);
      g_return_if_fail(user != NULL);

      cb = purple_conv_chat_cb_find(chat, user);

      if (!cb)
            return;

      if (flags == cb->flags)
            return;

      oldflags = cb->flags;
      cb->flags = flags;

      conv = purple_conv_chat_get_conversation(chat);
      ops = purple_conversation_get_ui_ops(conv);

      if (ops != NULL && ops->chat_update_user != NULL)
            ops->chat_update_user(conv, user);

      purple_signal_emit(purple_conversations_get_handle(),
                               "chat-buddy-flags", conv, user, oldflags, flags);
}

PurpleConvChatBuddyFlags
02011 purple_conv_chat_user_get_flags(PurpleConvChat *chat, const char *user)
{
      PurpleConvChatBuddy *cb;

      g_return_val_if_fail(chat != NULL, 0);
      g_return_val_if_fail(user != NULL, 0);

      cb = purple_conv_chat_cb_find(chat, user);

      if (!cb)
            return PURPLE_CBFLAGS_NONE;

      return cb->flags;
}

02026 void purple_conv_chat_set_nick(PurpleConvChat *chat, const char *nick) {
      g_return_if_fail(chat != NULL);

      g_free(chat->nick);
      chat->nick = g_strdup(purple_normalize(chat->conv->account, nick));
}

02033 const char *purple_conv_chat_get_nick(PurpleConvChat *chat) {
      g_return_val_if_fail(chat != NULL, NULL);

      return chat->nick;
}

PurpleConversation *
02040 purple_find_chat(const PurpleConnection *gc, int id)
{
      GList *l;
      PurpleConversation *conv;

      for (l = purple_get_chats(); l != NULL; l = l->next) {
            conv = (PurpleConversation *)l->data;

            if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)) == id &&
                  purple_conversation_get_gc(conv) == gc)
                  return conv;
      }

      return NULL;
}

void
02057 purple_conv_chat_left(PurpleConvChat *chat)
{
      g_return_if_fail(chat != NULL);

      chat->left = TRUE;
      purple_conversation_update(chat->conv, PURPLE_CONV_UPDATE_CHATLEFT);
}

static void
invite_user_to_chat(gpointer data, PurpleRequestFields *fields)
{
      PurpleConversation *conv;
      PurpleConvChat *chat;
      const char *user, *message;

      conv = data;
      chat = PURPLE_CONV_CHAT(conv);
      user = purple_request_fields_get_string(fields, "screenname");
      message = purple_request_fields_get_string(fields, "message");

      serv_chat_invite(purple_conversation_get_gc(conv), chat->id, message, user);
}

02080 void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user,
            const char *message, gboolean confirm)
{
      PurpleAccount *account;
      PurpleConversation *conv;
      PurpleRequestFields *fields;
      PurpleRequestFieldGroup *group;
      PurpleRequestField *field;

      g_return_if_fail(chat);

      if (!user || !*user || !message || !*message)
            confirm = TRUE;

      conv = chat->conv;
      account = conv->account;

      if (!confirm) {
            serv_chat_invite(purple_account_get_connection(account),
                        purple_conv_chat_get_id(chat), message, user);
            return;
      }

      fields = purple_request_fields_new();
      group = purple_request_field_group_new(_("Invite to chat"));
      purple_request_fields_add_group(fields, group);

      field = purple_request_field_string_new("screenname", _("Buddy"), user, FALSE);
      purple_request_field_group_add_field(group, field);
      purple_request_field_set_required(field, TRUE);
      purple_request_field_set_type_hint(field, "screenname");

      field = purple_request_field_string_new("message", _("Message"), message, FALSE);
      purple_request_field_group_add_field(group, field);

      purple_request_fields(conv, _("Invite to chat"), NULL,
                  _("Please enter the name of the user you wish to invite, "
                        "along with an optional invite message."),
                  fields,
                  _("Invite"), G_CALLBACK(invite_user_to_chat),
                  _("Cancel"), NULL,
                  account, user, conv,
                  conv);
}

gboolean
02126 purple_conv_chat_has_left(PurpleConvChat *chat)
{
      g_return_val_if_fail(chat != NULL, TRUE);

      return chat->left;
}

PurpleConvChatBuddy *
02134 purple_conv_chat_cb_new(const char *name, const char *alias, PurpleConvChatBuddyFlags flags)
{
      PurpleConvChatBuddy *cb;

      g_return_val_if_fail(name != NULL, NULL);

      cb = g_new0(PurpleConvChatBuddy, 1);
      cb->name = g_strdup(name);
      cb->flags = flags;
      cb->alias = g_strdup(alias);

      PURPLE_DBUS_REGISTER_POINTER(cb, PurpleConvChatBuddy);
      return cb;
}

PurpleConvChatBuddy *
02150 purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name)
{
      GList *l;
      PurpleConvChatBuddy *cb = NULL;

      g_return_val_if_fail(chat != NULL, NULL);
      g_return_val_if_fail(name != NULL, NULL);

      for (l = purple_conv_chat_get_users(chat); l; l = l->next) {
            cb = l->data;
            if (!g_utf8_collate(cb->name, name))
                  return cb;
      }

      return NULL;
}

void
02168 purple_conv_chat_cb_destroy(PurpleConvChatBuddy *cb)
{
      if (cb == NULL)
            return;

      g_free(cb->alias);
      g_free(cb->alias_key);
      g_free(cb->name);

      PURPLE_DBUS_UNREGISTER_POINTER(cb);
      g_free(cb);
}

const char *
02182 purple_conv_chat_cb_get_name(PurpleConvChatBuddy *cb)
{
      g_return_val_if_fail(cb != NULL, NULL);

      return cb->name;
}

GList *
02190 purple_conversation_get_extended_menu(PurpleConversation *conv)
{
      GList *menu = NULL;

      g_return_val_if_fail(conv != NULL, NULL);

      purple_signal_emit(purple_conversations_get_handle(),
                  "conversation-extended-menu", conv, &menu);
      return menu;
}

02201 void purple_conversation_clear_message_history(PurpleConversation *conv)
{
      GList *list = conv->message_history;
      message_history_free(list);
      conv->message_history = NULL;
}

02208 GList *purple_conversation_get_message_history(PurpleConversation *conv)
{
      return conv->message_history;
}

02213 const char *purple_conversation_message_get_sender(PurpleConvMessage *msg)
{
      g_return_val_if_fail(msg, NULL);
      return msg->who;
}

02219 const char *purple_conversation_message_get_message(PurpleConvMessage *msg)
{
      g_return_val_if_fail(msg, NULL);
      return msg->what;
}

02225 PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg)
{
      g_return_val_if_fail(msg, 0);
      return msg->flags;
}

02231 time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg)
{
      g_return_val_if_fail(msg, 0);
      return msg->when;
}

gboolean
02238 purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
                        const gchar *markup, gchar **error)
{
      char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
      PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
      g_free(mark);
      g_free(err);
      return (status == PURPLE_CMD_STATUS_OK);
}

void *
02249 purple_conversations_get_handle(void)
{
      static int handle;

      return &handle;
}

void
02257 purple_conversations_init(void)
{
      void *handle = purple_conversations_get_handle();

      conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
                                    (GEqualFunc)_purple_conversations_hconv_equal,
                                    (GDestroyNotify)_purple_conversations_hconv_free_key, NULL);

      /**********************************************************************
       * Register preferences
       **********************************************************************/

      /* Conversations */
      purple_prefs_add_none("/purple/conversations");

      /* Conversations -> Chat */
      purple_prefs_add_none("/purple/conversations/chat");
      purple_prefs_add_bool("/purple/conversations/chat/show_nick_change", TRUE);

      /* Conversations -> IM */
      purple_prefs_add_none("/purple/conversations/im");
      purple_prefs_add_bool("/purple/conversations/im/send_typing", TRUE);


      /**********************************************************************
       * Register signals
       **********************************************************************/
      purple_signal_register(handle, "writing-im-msg",
                                     purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT,
                                     purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "wrote-im-msg",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
                                     NULL, 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "sending-im-msg",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER,
                                     NULL, 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "sent-im-msg",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER,
                                     NULL, 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "receiving-im-msg",
                                     purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
                                     purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new_outgoing(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "received-im-msg",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
                                     NULL, 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "blocked-im-msg",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT,
                                     NULL, 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                           PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_UINT),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "writing-chat-msg",
                                     purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT,
                                     purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "wrote-chat-msg",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
                                     NULL, 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "sending-chat-msg",
                                     purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "sent-chat-msg",
                                     purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "receiving-chat-msg",
                                     purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
                                     purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new_outgoing(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "received-chat-msg",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT,
                                     NULL, 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "conversation-created",
                                     purple_marshal_VOID__POINTER, NULL, 1,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION));

      purple_signal_register(handle, "conversation-updated",
                                     purple_marshal_VOID__POINTER_UINT, NULL, 2,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "deleting-conversation",
                                     purple_marshal_VOID__POINTER, NULL, 1,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION));

      purple_signal_register(handle, "buddy-typing",
                                     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "buddy-typed",
                                     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "buddy-typing-stopped",
                                     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "chat-buddy-joining",
                                     purple_marshal_BOOLEAN__POINTER_POINTER_UINT,
                                     purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "chat-buddy-joined",
                                     purple_marshal_VOID__POINTER_POINTER_UINT_UINT, NULL, 4,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_UINT),
                                     purple_value_new(PURPLE_TYPE_BOOLEAN));

      purple_signal_register(handle, "chat-buddy-flags",
                                     purple_marshal_VOID__POINTER_POINTER_UINT_UINT, NULL, 4,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_UINT),
                                     purple_value_new(PURPLE_TYPE_UINT));

      purple_signal_register(handle, "chat-buddy-leaving",
                                     purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
                                     purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "chat-buddy-left",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "chat-inviting-user",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new_outgoing(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "chat-invited-user",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "chat-invited",
                                     purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER,
                                     purple_value_new(PURPLE_TYPE_INT), 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_POINTER));

      purple_signal_register(handle, "chat-invite-blocked",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER,
                                     NULL, 5,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                           PURPLE_SUBTYPE_ACCOUNT),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *"));

      purple_signal_register(handle, "chat-joined",
                                     purple_marshal_VOID__POINTER, NULL, 1,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION));

      purple_signal_register(handle, "chat-join-failed",
                                       purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                                       purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONNECTION),
                                       purple_value_new(PURPLE_TYPE_POINTER));

      purple_signal_register(handle, "chat-left",
                                     purple_marshal_VOID__POINTER, NULL, 1,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION));

      purple_signal_register(handle, "chat-topic-changed",
                                     purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
                                     purple_value_new(PURPLE_TYPE_SUBTYPE,
                                                            PURPLE_SUBTYPE_CONVERSATION),
                                     purple_value_new(PURPLE_TYPE_STRING),
                                     purple_value_new(PURPLE_TYPE_STRING));

      purple_signal_register(handle, "conversation-extended-menu",
                       purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                       purple_value_new(PURPLE_TYPE_SUBTYPE,
                                  PURPLE_SUBTYPE_CONVERSATION),
                       purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
}

void
02550 purple_conversations_uninit(void)
{
      while (conversations)
            purple_conversation_destroy((PurpleConversation*)conversations->data);
      g_hash_table_destroy(conversation_cache);
      purple_signals_unregister_by_instance(purple_conversations_get_handle());
}


Generated by  Doxygen 1.6.0   Back to index