Logo Search packages:      
Sourcecode: gaim version File versions

switchboard.c

Go to the documentation of this file.
/**
 * @file switchboard.c MSN switchboard functions
 *
 * gaim
 *
 * Gaim 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "msn.h"
#include "prefs.h"
#include "switchboard.h"
#include "notification.h"
#include "utils.h"

#include "error.h"

/* #define MSN_DEBUG_SWBOARD */

static MsnTable *cbs_table;

static void msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg,
                                           MsnMsgErrorType error);

/**************************************************************************
 * Main
 **************************************************************************/

MsnSwitchBoard *
00044 msn_switchboard_new(MsnSession *session)
{
      MsnSwitchBoard *swboard;
      MsnServConn *servconn;

      g_return_val_if_fail(session != NULL, NULL);

      swboard = g_new0(MsnSwitchBoard, 1);

      swboard->session = session;
      swboard->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_SB);
      swboard->cmdproc = servconn->cmdproc;

      swboard->msg_queue = g_queue_new();
      swboard->empty = TRUE;

      swboard->cmdproc->data = swboard;
      swboard->cmdproc->cbs_table = cbs_table;

      session->switches = g_list_append(session->switches, swboard);

      return swboard;
}

void
00069 msn_switchboard_destroy(MsnSwitchBoard *swboard)
{
      MsnSession *session;
      MsnMessage *msg;
      GList *l;

#ifdef MSN_DEBUG_SWBOARD
      gaim_debug_info("msn", "switchboard_destroy: swboard(%p)\n", swboard);
#endif

      g_return_if_fail(swboard != NULL);

      if (swboard->destroying)
            return;

      swboard->destroying = TRUE;

      /* If it linked us is because its looking for trouble */
      if (swboard->slplink != NULL)
            msn_slplink_destroy(swboard->slplink);

      /* Destroy the message queue */
      while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
      {
            if (swboard->error != MSN_SB_ERROR_NONE)
            {
                  /* The messages could not be sent due to a switchboard error */
                  msg_error_helper(swboard->cmdproc, msg,
                                           MSN_MSG_ERROR_SB);
            }
            msn_message_unref(msg);
      }

      g_queue_free(swboard->msg_queue);

      for (l = swboard->ack_list; l != NULL; l = l->next)
            msn_message_unref(l->data);

      if (swboard->im_user != NULL)
            g_free(swboard->im_user);

      if (swboard->auth_key != NULL)
            g_free(swboard->auth_key);

      if (swboard->session_id != NULL)
            g_free(swboard->session_id);

      for (l = swboard->users; l != NULL; l = l->next)
            g_free(l->data);

      session = swboard->session;
      session->switches = g_list_remove(session->switches, swboard);

#if 0
      /* This should never happen or we are in trouble. */
      if (swboard->servconn != NULL)
            msn_servconn_destroy(swboard->servconn);
#endif

      swboard->cmdproc->data = NULL;

      msn_servconn_set_disconnect_cb(swboard->servconn, NULL);

      msn_servconn_destroy(swboard->servconn);

      g_free(swboard);
}

void
00138 msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key)
{
      g_return_if_fail(swboard != NULL);
      g_return_if_fail(key != NULL);

      swboard->auth_key = g_strdup(key);
}

const char *
00147 msn_switchboard_get_auth_key(MsnSwitchBoard *swboard)
{
      g_return_val_if_fail(swboard != NULL, NULL);

      return swboard->auth_key;
}

void
00155 msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id)
{
      g_return_if_fail(swboard != NULL);
      g_return_if_fail(id != NULL);

      if (swboard->session_id != NULL)
            g_free(swboard->session_id);

      swboard->session_id = g_strdup(id);
}

const char *
00167 msn_switchboard_get_session_id(MsnSwitchBoard *swboard)
{
      g_return_val_if_fail(swboard != NULL, NULL);

      return swboard->session_id;
}

void
00175 msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited)
{
      g_return_if_fail(swboard != NULL);

      swboard->invited = invited;
}

gboolean
00183 msn_switchboard_is_invited(MsnSwitchBoard *swboard)
{
      g_return_val_if_fail(swboard != NULL, FALSE);

      return swboard->invited;
}

/**************************************************************************
 * Utility
 **************************************************************************/

static void
send_clientcaps(MsnSwitchBoard *swboard)
{
      MsnMessage *msg;

      msg = msn_message_new(MSN_MSG_CAPS);
      msn_message_set_content_type(msg, "text/x-clientcaps");
      msn_message_set_flag(msg, 'U');
      msn_message_set_bin_data(msg, MSN_CLIENTINFO, strlen(MSN_CLIENTINFO));

      msn_switchboard_send_msg(swboard, msg, TRUE);

      msn_message_destroy(msg);
}

void
msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
{
      MsnCmdProc *cmdproc;
      GaimAccount *account;

      g_return_if_fail(swboard != NULL);

      cmdproc = swboard->cmdproc;
      account = cmdproc->session->account;

      swboard->users = g_list_prepend(swboard->users, g_strdup(user));
      swboard->current_users++;
      swboard->empty = FALSE;

#ifdef MSN_DEBUG_CHAT
      gaim_debug_info("msn", "user=[%s], total=%d\n", user,
                              swboard->current_users);
#endif

      if (!(swboard->flag & MSN_SB_FLAG_IM))
      {
            /* This is a helper switchboard. */
            if (swboard->conv != NULL)
                  gaim_debug_error("msn", "switchboard_add_user: conv != NULL\n");

            return;
      }

      if ((swboard->conv != NULL) &&
            (gaim_conversation_get_type(swboard->conv) == GAIM_CONV_CHAT))
      {
            gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv), user, NULL,
                                                GAIM_CBFLAGS_NONE, TRUE);
      }
      else if (swboard->current_users > 1 || swboard->total_users > 1)
      {
            if (swboard->conv == NULL ||
                  gaim_conversation_get_type(swboard->conv) != GAIM_CONV_CHAT)
            {
                  GList *l;

#ifdef MSN_DEBUG_CHAT
                  gaim_debug_info("msn", "[chat] Switching to chat.\n");
#endif

#if 0
                  /* this is bad - it causes msn_switchboard_close to be called on the
                   * switchboard we're in the middle of using :( */
                  if (swboard->conv != NULL)
                        gaim_conversation_destroy(swboard->conv);
#endif

                  cmdproc->session->conv_seq++;
                  swboard->chat_id = cmdproc->session->conv_seq;

                  swboard->conv = serv_got_joined_chat(account->gc,
                                                                         swboard->chat_id,
                                                                         "MSN Chat");

                  for (l = swboard->users; l != NULL; l = l->next)
                  {
                        const char *tmp_user;

                        tmp_user = l->data;

#ifdef MSN_DEBUG_CHAT
                        gaim_debug_info("msn", "[chat] Adding [%s].\n", tmp_user);
#endif

                        gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv),
                                                            tmp_user, NULL, GAIM_CBFLAGS_NONE, TRUE);
                  }

#ifdef MSN_DEBUG_CHAT
                  gaim_debug_info("msn", "[chat] We add ourselves.\n");
#endif

                  gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv),
                                                      gaim_account_get_username(account),
                                                      NULL, GAIM_CBFLAGS_NONE, TRUE);

                  g_free(swboard->im_user);
                  swboard->im_user = NULL;
            }
      }
      else if (swboard->conv == NULL)
      {
            swboard->conv = gaim_find_conversation_with_account(user, account);
      }
      else
      {
            gaim_debug_warning("msn", "switchboard_add_user: This should not happen!\n");
      }
}

GaimConversation *
msn_switchboard_get_conv(MsnSwitchBoard *swboard)
{
      GaimAccount *account;

      g_return_val_if_fail(swboard != NULL, NULL);

      if (swboard->conv != NULL)
            return swboard->conv;

      gaim_debug_error("msn", "Switchboard with unassigned conversation\n");

      account = swboard->session->account;

      return gaim_find_conversation_with_account(swboard->im_user, account);
}

void
msn_switchboard_report_user(MsnSwitchBoard *swboard, GaimMessageFlags flags, const char *msg)
{
      GaimConversation *conv;

      g_return_if_fail(swboard != NULL);
      g_return_if_fail(msg != NULL);

      if ((conv = msn_switchboard_get_conv(swboard)) != NULL)
      {
            gaim_conversation_write(conv, NULL, msg, flags, time(NULL));
      }
}

static void
swboard_error_helper(MsnSwitchBoard *swboard, int reason, const char *passport)
{
      g_return_if_fail(swboard != NULL);

      gaim_debug_info("msg", "Error: Unable to call the user %s\n", passport);

      /* TODO: if current_users > 0, this is probably a chat and an invite failed,
       * we should report that in the chat or something */
      if (swboard->current_users == 0)
      {
            swboard->error = reason;
            msn_switchboard_close(swboard);
      }
}

static void
cal_error_helper(MsnTransaction *trans, int reason)
{
      MsnSwitchBoard *swboard;
      const char *passport;
      char **params;

      params = g_strsplit(trans->params, " ", 0);

      passport = params[0];

      swboard = trans->data;

      swboard_error_helper(swboard, reason, passport);

      g_strfreev(params);
}

static void
msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error)
{
      MsnSwitchBoard *swboard;

      g_return_if_fail(cmdproc != NULL);
      g_return_if_fail(msg     != NULL);

      if ((error != MSN_MSG_ERROR_SB) && (msg->nak_cb != NULL))
            msg->nak_cb(msg, msg->ack_data);

      swboard = cmdproc->data;

      /* This is not good, and should be fixed somewhere else. */
      g_return_if_fail(swboard != NULL);

      if (msg->type == MSN_MSG_TEXT)
      {
            const char *format, *str_reason;
            char *body_str, *body_enc, *pre, *post;

#if 0
            if (swboard->conv == NULL)
            {
                  if (msg->ack_ref)
                        msn_message_unref(msg);

                  return;
            }
#endif

            if (error == MSN_MSG_ERROR_TIMEOUT)
            {
                  str_reason = _("Message may have not been sent "
                                       "because a timeout occurred:");
            }
            else if (error == MSN_MSG_ERROR_SB)
            {
                  switch (swboard->error)
                  {
                        case MSN_SB_ERROR_OFFLINE:
                              str_reason = _("Message could not be sent, "
                                                   "not allowed while invisible:");
                              break;
                        case MSN_SB_ERROR_USER_OFFLINE:
                              str_reason = _("Message could not be sent "
                                                   "because the user is offline:");
                              break;
                        case MSN_SB_ERROR_CONNECTION:
                              str_reason = _("Message could not be sent "
                                                   "because a connection error occurred:");
                              break;
                        default:
                              str_reason = _("Message could not be sent "
                                                   "because an error with "
                                                   "the switchboard occurred:");
                              break;
                  }
            }
            else
            {
                  str_reason = _("Message may have not been sent "
                                       "because an unknown error occurred:");
            }

            body_str = msn_message_to_string(msg);
            body_enc = gaim_escape_html(body_str);
            g_free(body_str);

            format = msn_message_get_attr(msg, "X-MMS-IM-Format");
            msn_parse_format(format, &pre, &post);
            body_str = g_strdup_printf("%s%s%s", pre ? pre : "",
                                                   body_enc ? body_enc : "", post ? post : "");
            g_free(body_enc);
            g_free(pre);
            g_free(post);

            msn_switchboard_report_user(swboard, GAIM_MESSAGE_ERROR,
                                                      str_reason);
            msn_switchboard_report_user(swboard, GAIM_MESSAGE_NO_LOG,
                                                      body_str);

            g_free(body_str);
      }

      /* If a timeout occures we will want the msg around just in case we
       * receive the ACK after the timeout. */
      if (msg->ack_ref && error != MSN_MSG_ERROR_TIMEOUT)
      {
            swboard->ack_list = g_list_remove(swboard->ack_list, msg);
            msn_message_unref(msg);
      }
}

/**************************************************************************
 * Message Stuff
 **************************************************************************/

/** Called when a message times out. */
static void
00470 msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
{
      MsnMessage *msg;

      msg = trans->data;

      msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT);
}

/** Called when we receive an error of a message. */
static void
00481 msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
{
      msg_error_helper(cmdproc, trans->data, MSN_MSG_ERROR_UNKNOWN);
}

#if 0
/** Called when we receive an ack of a special message. */
static void
msg_ack(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnMessage *msg;

      msg = cmd->trans->data;

      if (msg->ack_cb != NULL)
            msg->ack_cb(msg->ack_data);

      msn_message_unref(msg);
}

/** Called when we receive a nak of a special message. */
static void
msg_nak(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnMessage *msg;

      msg = cmd->trans->data;

      msn_message_unref(msg);
}
#endif

static void
release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
{
      MsnCmdProc *cmdproc;
      MsnTransaction *trans;
      char *payload;
      gsize payload_len;

      g_return_if_fail(swboard != NULL);
      g_return_if_fail(msg     != NULL);

      cmdproc = swboard->cmdproc;

      payload = msn_message_gen_payload(msg, &payload_len);

      /* msn_message_show_readable(msg, "SB SEND", FALSE); */

      trans = msn_transaction_new(cmdproc, "MSG", "%c %d",
                                                msn_message_get_flag(msg), payload_len);

      /* Data for callbacks */
      msn_transaction_set_data(trans, msg);

      if (msg->type == MSN_MSG_TEXT)
      {
            msg->ack_ref = TRUE;
            msn_message_ref(msg);
            swboard->ack_list = g_list_append(swboard->ack_list, msg);
            msn_transaction_set_timeout_cb(trans, msg_timeout);
      }
      else if (msg->type == MSN_MSG_SLP)
      {
            msg->ack_ref = TRUE;
            msn_message_ref(msg);
            swboard->ack_list = g_list_append(swboard->ack_list, msg);
            msn_transaction_set_timeout_cb(trans, msg_timeout);
#if 0
            if (msg->ack_cb != NULL)
            {
                  msn_transaction_add_cb(trans, "ACK", msg_ack);
                  msn_transaction_add_cb(trans, "NAK", msg_nak);
            }
#endif
      }

      trans->payload = payload;
      trans->payload_len = payload_len;

      msg->trans = trans;

      msn_cmdproc_send_trans(cmdproc, trans);
}

static void
queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
{
      g_return_if_fail(swboard != NULL);
      g_return_if_fail(msg     != NULL);

      gaim_debug_info("msn", "Appending message to queue.\n");

      g_queue_push_tail(swboard->msg_queue, msg);

      msn_message_ref(msg);
}

static void
process_queue(MsnSwitchBoard *swboard)
{
      MsnMessage *msg;

      g_return_if_fail(swboard != NULL);

      gaim_debug_info("msn", "Processing queue\n");

      while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
      {
            gaim_debug_info("msn", "Sending message\n");
            release_msg(swboard, msg);
            msn_message_unref(msg);
      }
}

gboolean
00597 msn_switchboard_can_send(MsnSwitchBoard *swboard)
{
      g_return_val_if_fail(swboard != NULL, FALSE);

      if (swboard->empty || !g_queue_is_empty(swboard->msg_queue))
            return FALSE;

      return TRUE;
}

void
00608 msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
                                     gboolean queue)
{
      g_return_if_fail(swboard != NULL);
      g_return_if_fail(msg     != NULL);

      if (msn_switchboard_can_send(swboard))
            release_msg(swboard, msg);
      else if (queue)
            queue_msg(swboard, msg);
}

/**************************************************************************
 * Switchboard Commands
 **************************************************************************/

static void
ans_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnSwitchBoard *swboard;

      swboard = cmdproc->data;
      swboard->ready = TRUE;
}

static void
bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnSwitchBoard *swboard;
      const char *user;

      swboard = cmdproc->data;
      user = cmd->params[0];

#if 0
      if (!(swboard->flag & MSN_SB_FLAG_IM))
      {
            /* TODO: This is a helper switchboard. It would be better if
             * swboard->conv is NULL, but it isn't. */
            /* Umm? I think swboard->conv is NULL for all helper switchboards now? */
            msn_switchboard_destroy(swboard);
            return;
      }
#else
      if (!(swboard->flag & MSN_SB_FLAG_IM))
            gaim_debug_error("msn_switchboard", "bye_cmd: helper bug\n");
#endif

      if (swboard->conv == NULL)
      {
            /* This is a helper switchboard */
            msn_switchboard_destroy(swboard);
      }
      else if ((swboard->current_users > 1) ||
                   (gaim_conversation_get_type(swboard->conv) == GAIM_CONV_CHAT))
      {
            /* This is a switchboard used for a chat */
            gaim_conv_chat_remove_user(GAIM_CONV_CHAT(swboard->conv), user, NULL);
            swboard->current_users--;
            if (swboard->current_users == 0)
                  msn_switchboard_destroy(swboard);
      }
      else
      {
            /* This is a switchboard used for a im session */

            char *str = NULL;

            if (cmd->param_count == 2 && atoi(cmd->params[1]) == 1)
            {
                  if (gaim_prefs_get_bool("/plugins/prpl/msn/conv_timeout_notice"))
                  {
                        str = g_strdup_printf(_("The conversation has become "
                                                            "inactive and timed out."));
                  }
            }
            else
            {
                  if (gaim_prefs_get_bool("/plugins/prpl/msn/conv_close_notice"))
                  {
                        char *username;
                        GaimAccount *account;
                        GaimBuddy *b;

                        account = cmdproc->session->account;

                        if ((b = gaim_find_buddy(account, user)) != NULL)
                              username = gaim_escape_html(gaim_buddy_get_alias(b));
                        else
                              username = gaim_escape_html(user);

                        str = g_strdup_printf(_("%s has closed the conversation "
                                                            "window."), username);

                        g_free(username);
                  }
            }

            if (str != NULL)
            {
                  msn_switchboard_report_user(swboard, GAIM_MESSAGE_SYSTEM, str);
                  g_free(str);
            }

            msn_switchboard_destroy(swboard);
      }
}

static void
iro_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      GaimAccount *account;
      GaimConnection *gc;
      MsnSwitchBoard *swboard;

      account = cmdproc->session->account;
      gc = account->gc;
      swboard = cmdproc->data;

      swboard->total_users = atoi(cmd->params[2]);

      msn_switchboard_add_user(swboard, cmd->params[3]);
}

static void
joi_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnSession *session;
      GaimAccount *account;
      GaimConnection *gc;
      MsnSwitchBoard *swboard;
      const char *passport;

      passport = cmd->params[0];

      session = cmdproc->session;
      account = session->account;
      gc = account->gc;
      swboard = cmdproc->data;

      msn_switchboard_add_user(swboard, passport);

      process_queue(swboard);

      if (!session->http_method)
            send_clientcaps(swboard);

      if (swboard->closed)
            msn_switchboard_close(swboard);
}

static void
msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
{
      MsnMessage *msg;

      msg = msn_message_new_from_cmd(cmdproc->session, cmd);

      msn_message_parse_payload(msg, payload, len);
      /* msn_message_show_readable(msg, "SB RECV", FALSE); */

      if (msg->remote_user != NULL)
            g_free (msg->remote_user);

      msg->remote_user = g_strdup(cmd->params[0]);
      msn_cmdproc_process_msg(cmdproc, msg);

      msn_message_destroy(msg);
}

static void
msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      cmdproc->servconn->payload_len = atoi(cmd->params[2]);
      cmdproc->last_cmd->payload_cb = msg_cmd_post;
}

static void
nak_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnMessage *msg;

      msg = cmd->trans->data;
      g_return_if_fail(msg != NULL);

      msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_NAK);
}

static void
ack_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnSwitchBoard *swboard;
      MsnMessage *msg;

      msg = cmd->trans->data;

      if (msg->ack_cb != NULL)
            msg->ack_cb(msg, msg->ack_data);

      swboard = cmdproc->data;
      swboard->ack_list = g_list_remove(swboard->ack_list, msg);
      msn_message_unref(msg);
}

static void
out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      GaimConnection *gc;
      MsnSwitchBoard *swboard;

      gc = cmdproc->session->account->gc;
      swboard = cmdproc->data;

      if (swboard->current_users > 1)
            serv_got_chat_left(gc, swboard->chat_id);

      msn_switchboard_disconnect(swboard);
}

static void
usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnSwitchBoard *swboard;

      swboard = cmdproc->data;

#if 0
      GList *l;

      for (l = swboard->users; l != NULL; l = l->next)
      {
            const char *user;
            user = l->data;

            msn_cmdproc_send(cmdproc, "CAL", "%s", user);
      }
#endif

      swboard->ready = TRUE;
      msn_cmdproc_process_queue(cmdproc);
}

/**************************************************************************
 * Message Handlers
 **************************************************************************/
static void
plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
      GaimConnection *gc;
      MsnSwitchBoard *swboard;
      const char *body;
      char *body_str;
      char *body_enc;
      char *body_final;
      size_t body_len;
      const char *passport;
      const char *value;

      gc = cmdproc->session->account->gc;
      swboard = cmdproc->data;

      body = msn_message_get_bin_data(msg, &body_len);
      body_str = g_strndup(body, body_len);
      body_enc = gaim_escape_html(body_str);
      g_free(body_str);

      passport = msg->remote_user;

      if (!strcmp(passport, "messenger@microsoft.com") &&
            strstr(body, "immediate security update"))
      {
            return;
      }

#if 0
      if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL)
      {
            gaim_debug_misc("msn", "User-Agent = '%s'\n", value);
      }
#endif

      if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
      {
            char *pre, *post;

            msn_parse_format(value, &pre, &post);

            body_final = g_strdup_printf("%s%s%s", pre ? pre : "",
                                                       body_enc ? body_enc : "", post ? post : "");

            g_free(pre);
            g_free(post);
            g_free(body_enc);
      }
      else
      {
            body_final = body_enc;
      }

      swboard->flag |= MSN_SB_FLAG_IM;

      if (swboard->current_users > 1 ||
            ((swboard->conv != NULL) &&
             gaim_conversation_get_type(swboard->conv) == GAIM_CONV_CHAT))
      {
            /* If current_users is always ok as it should then there is no need to
             * check if this is a chat. */
            if (swboard->current_users <= 1)
                  gaim_debug_misc("msn", "plain_msg: current_users(%d)\n",
                                          swboard->current_users);

            serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final,
                                     time(NULL));
            if (swboard->conv == NULL)
            {
                  swboard->conv = gaim_find_chat(gc, swboard->chat_id);
                  swboard->flag |= MSN_SB_FLAG_IM;
            }
      }
      else
      {
            serv_got_im(gc, passport, body_final, 0, time(NULL));
            if (swboard->conv == NULL)
            {
                  swboard->conv = gaim_find_conversation_with_account(passport,
                                                                  gaim_connection_get_account(gc));
                  swboard->flag |= MSN_SB_FLAG_IM;
            }
      }

      g_free(body_final);
}

static void
control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
      GaimConnection *gc;
      MsnSwitchBoard *swboard;
      const char *value;
      char *passport;

      gc = cmdproc->session->account->gc;
      swboard = cmdproc->data;
      passport = msg->remote_user;

      if (swboard->current_users == 1 &&
            (value = msn_message_get_attr(msg, "TypingUser")) != NULL)
      {
            serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
                                    GAIM_TYPING);
      }
}

static void
clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
#if 0
      MsnSession *session;
      MsnSwitchBoard *swboard;
      MsnUser *user;
      GHashTable *clientcaps;
      const char *value;

      char *passport = msg->sender;

      session = cmdproc->session;
      swboard = cmdproc->servconn->swboard;

      clientcaps = msn_message_get_hashtable_from_body(msg);
#endif
}

/**************************************************************************
 * Connect stuff
 **************************************************************************/
static void
connect_cb(MsnServConn *servconn)
{
      MsnSwitchBoard *swboard;
      MsnCmdProc *cmdproc;
      GaimAccount *account;

      cmdproc = servconn->cmdproc;
      g_return_if_fail(cmdproc != NULL);

      account = cmdproc->session->account;
      swboard = cmdproc->data;
      g_return_if_fail(swboard != NULL);

      if (msn_switchboard_is_invited(swboard))
      {
            swboard->empty = FALSE;

            msn_cmdproc_send(cmdproc, "ANS", "%s %s %s",
                                     gaim_account_get_username(account),
                                     swboard->auth_key, swboard->session_id);
      }
      else
      {
            msn_cmdproc_send(cmdproc, "USR", "%s %s",
                                     gaim_account_get_username(account),
                                     swboard->auth_key);
      }
}

static void
disconnect_cb(MsnServConn *servconn)
{
      MsnSwitchBoard *swboard;

      swboard = servconn->cmdproc->data;
      g_return_if_fail(swboard != NULL);

      msn_servconn_set_disconnect_cb(swboard->servconn, NULL);

      msn_switchboard_destroy(swboard);
}

gboolean
01027 msn_switchboard_connect(MsnSwitchBoard *swboard, const char *host, int port)
{
      g_return_val_if_fail(swboard != NULL, FALSE);

      msn_servconn_set_connect_cb(swboard->servconn, connect_cb);
      msn_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb);

      return msn_servconn_connect(swboard->servconn, host, port);
}

void
01038 msn_switchboard_disconnect(MsnSwitchBoard *swboard)
{
      g_return_if_fail(swboard != NULL);

      msn_servconn_disconnect(swboard->servconn);
}

/**************************************************************************
 * Call stuff
 **************************************************************************/
static void
got_cal(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
#if 0
      MsnSwitchBoard *swboard;
      const char *user;

      swboard = cmdproc->data;

      user = cmd->params[0];

      msn_switchboard_add_user(swboard, user);
#endif
}

static void
cal_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
{
      cal_error_helper(trans, MSN_SB_ERROR_UNKNOWN);
}

static void
cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
{
      int reason = MSN_SB_ERROR_UNKNOWN;

      if (error == 215)
      {
            gaim_debug_info("msn", "Invited user already in switchboard\n");
            return;
      }
      else if (error == 217)
      {
            reason = MSN_SB_ERROR_USER_OFFLINE;
      }

      cal_error_helper(trans, reason);
}

void
msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user)
{
      MsnTransaction *trans;
      MsnCmdProc *cmdproc;

      g_return_if_fail(swboard != NULL);

      cmdproc = swboard->cmdproc;

      trans = msn_transaction_new(cmdproc, "CAL", "%s", user);
      /* this doesn't do anything, but users seem to think that
       * 'Unhandled command' is some kind of error, so we don't report it */
      msn_transaction_add_cb(trans, "CAL", got_cal);

      msn_transaction_set_data(trans, swboard);
      msn_transaction_set_timeout_cb(trans, cal_timeout);

      if (swboard->ready)
            msn_cmdproc_send_trans(cmdproc, trans);
      else
            msn_cmdproc_queue_trans(cmdproc, trans);
}

/**************************************************************************
 * Create & Transfer stuff
 **************************************************************************/

static void
got_swboard(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
      MsnSwitchBoard *swboard;
      char *host;
      int port;
      swboard = cmd->trans->data;

      if (g_list_find(cmdproc->session->switches, swboard) == NULL)
            /* The conversation window was closed. */
            return;

      msn_switchboard_set_auth_key(swboard, cmd->params[4]);

      msn_parse_socket(cmd->params[2], &host, &port);

      if (!msn_switchboard_connect(swboard, host, port))
            msn_switchboard_destroy(swboard);

      g_free(host);
}

static void
xfr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
{
      MsnSwitchBoard *swboard;
      int reason = MSN_SB_ERROR_UNKNOWN;

      if (error == 913)
            reason = MSN_SB_ERROR_OFFLINE;

      swboard = trans->data;

      swboard_error_helper(swboard, reason, swboard->im_user);
}

void
msn_switchboard_request(MsnSwitchBoard *swboard)
{
      MsnCmdProc *cmdproc;
      MsnTransaction *trans;

      g_return_if_fail(swboard != NULL);

      cmdproc = swboard->session->notification->cmdproc;

      trans = msn_transaction_new(cmdproc, "XFR", "%s", "SB");
      msn_transaction_add_cb(trans, "XFR", got_swboard);

      msn_transaction_set_data(trans, swboard);
      msn_transaction_set_error_cb(trans, xfr_error);

      msn_cmdproc_send_trans(cmdproc, trans);
}

void
01171 msn_switchboard_close(MsnSwitchBoard *swboard)
{
      g_return_if_fail(swboard != NULL);

      if (swboard->error != MSN_SB_ERROR_NONE)
      {
            msn_switchboard_destroy(swboard);
      }
      else if (g_queue_is_empty(swboard->msg_queue) ||
                   !swboard->session->connected)
      {
            MsnCmdProc *cmdproc;
            cmdproc = swboard->cmdproc;
            msn_cmdproc_send_quick(cmdproc, "OUT", NULL, NULL);

            msn_switchboard_destroy(swboard);
      }
      else
      {
            swboard->closed = TRUE;
      }
}

gboolean
01195 msn_switchboard_release(MsnSwitchBoard *swboard, MsnSBFlag flag)
{
      g_return_val_if_fail(swboard != NULL, FALSE);

      swboard->flag &= ~flag;

      if (flag == MSN_SB_FLAG_IM)
            /* Forget any conversation that used to be associated with this
             * swboard. */
            swboard->conv = NULL;

      if (swboard->flag == 0)
      {
            msn_switchboard_close(swboard);
            return TRUE;
      }

      return FALSE;
}

/**************************************************************************
 * Init stuff
 **************************************************************************/

void
01220 msn_switchboard_init(void)
{
      cbs_table = msn_table_new();

      msn_table_add_cmd(cbs_table, "ANS", "ANS", ans_cmd);
      msn_table_add_cmd(cbs_table, "ANS", "IRO", iro_cmd);

      msn_table_add_cmd(cbs_table, "MSG", "ACK", ack_cmd);
      msn_table_add_cmd(cbs_table, "MSG", "NAK", nak_cmd);

      msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);

      msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
      msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd);
      msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd);
      msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);

#if 0
      /* They might skip the history */
      msn_table_add_cmd(cbs_table, NULL, "ACK", NULL);
#endif

      msn_table_add_error(cbs_table, "MSG", msg_error);
      msn_table_add_error(cbs_table, "CAL", cal_error);

      /* Register the message type callbacks. */
      msn_table_add_msg_type(cbs_table, "text/plain",
                                       plain_msg);
      msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol",
                                       control_msg);
      msn_table_add_msg_type(cbs_table, "text/x-clientcaps",
                                       clientcaps_msg);
      msn_table_add_msg_type(cbs_table, "text/x-clientinfo",
                                       clientcaps_msg);
      msn_table_add_msg_type(cbs_table, "application/x-msnmsgrp2p",
                                       msn_p2p_msg);
      msn_table_add_msg_type(cbs_table, "text/x-mms-emoticon",
                                       msn_emoticon_msg);
#if 0
      msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite",
                                       msn_invite_msg);
#endif
}

void
01265 msn_switchboard_end(void)
{
      msn_table_destroy(cbs_table);
}

Generated by  Doxygen 1.6.0   Back to index