/** * @file switchboard.c NATEON switchboard functions * @ingroup nateon * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nateon.h" #include "prefs.h" #include "switchboard.h" #include "notification.h" #include "nateon-utils.h" #include "xfer.h" #include "error.h" static NateonTable *cbs_table; static void msg_error_helper(NateonCmdProc *cmdproc, NateonMessage *msg, NateonMsgErrorType error); /************************************************************************** * Main **************************************************************************/ NateonSwitchBoard * nateon_switchboard_new(NateonSession *session) { NateonSwitchBoard *swboard; NateonServConn *servconn; g_return_val_if_fail(session != NULL, NULL); swboard = g_new0(NateonSwitchBoard, 1); swboard->session = session; swboard->servconn = servconn = nateon_servconn_new(session, NATEON_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 nateon_switchboard_destroy(NateonSwitchBoard *swboard) { NateonSession *session; NateonMessage *msg; GList *l; #ifdef NATEON_DEBUG_SB purple_debug_info("nateon", "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) // nateon_slplink_destroy(swboard->slplink); /* Destroy the message queue */ while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL) { if (swboard->error != NATEON_SB_ERROR_NONE) { /* The messages could not be sent due to a switchboard error */ msg_error_helper(swboard->cmdproc, msg, NATEON_MSG_ERROR_SB); } nateon_message_unref(msg); } g_queue_free(swboard->msg_queue); // /* msg_error_helper will both remove the msg from ack_list and // unref it, so we don't need to do either here */ // while ((l = swboard->ack_list) != NULL) // msg_error_helper(swboard->cmdproc, l->data, NATEON_MSG_ERROR_SB); 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) nateon_servconn_destroy(swboard->servconn); #endif swboard->cmdproc->data = NULL; nateon_servconn_set_disconnect_cb(swboard->servconn, NULL); nateon_servconn_destroy(swboard->servconn); g_free(swboard); } void nateon_switchboard_set_auth_key(NateonSwitchBoard *swboard, const char *key) { g_return_if_fail(swboard != NULL); g_return_if_fail(key != NULL); swboard->auth_key = g_strdup(key); } //const char * //nateon_switchboard_get_auth_key(NateonSwitchBoard *swboard) //{ // g_return_val_if_fail(swboard != NULL, NULL); // // return swboard->auth_key; //} // //void //nateon_switchboard_set_session_id(NateonSwitchBoard *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 * //nateon_switchboard_get_session_id(NateonSwitchBoard *swboard) //{ // g_return_val_if_fail(swboard != NULL, NULL); // // return swboard->session_id; //} void nateon_switchboard_set_invited(NateonSwitchBoard *swboard, gboolean invited) { g_return_if_fail(swboard != NULL); swboard->invited = invited; } gboolean nateon_switchboard_is_invited(NateonSwitchBoard *swboard) { g_return_val_if_fail(swboard != NULL, FALSE); return swboard->invited; } /************************************************************************** * Utility **************************************************************************/ //static void //send_clientcaps(NateonSwitchBoard *swboard) //{ // NateonMessage *msg; // // msg = nateon_message_new(NATEON_MSG_CAPS); // nateon_message_set_content_type(msg, "text/x-clientcaps"); // nateon_message_set_flag(msg, 'U'); // nateon_message_set_bin_data(msg, NATEON_CLIENTINFO, strlen(NATEON_CLIENTINFO)); // // nateon_switchboard_send_msg(swboard, msg, TRUE); // // nateon_message_destroy(msg); //} static void nateon_switchboard_add_user(NateonSwitchBoard *swboard, const char *user) { NateonCmdProc *cmdproc; PurpleAccount *account; purple_debug_info("nateon", "[%s:%d]\n", __FUNCTION__, __LINE__); 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 NATEON_DEBUG_CHAT purple_debug_info("nateon", "user=[%s], total=%d\n", user, swboard->current_users); #endif if (!(swboard->flag & NATEON_SB_FLAG_IM) && (swboard->conv != NULL)) { /* This is a helper switchboard. */ purple_debug_error("nateon", "switchboard_add_user: conv != NULL\n"); return; } if ((swboard->conv != NULL) && (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) { purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL, PURPLE_CBFLAGS_NONE, TRUE); } else if (swboard->current_users > 1 || swboard->total_users > 1) { if (swboard->conv == NULL || purple_conversation_get_type(swboard->conv) != PURPLE_CONV_TYPE_CHAT) { GList *l; #ifdef NATEON_DEBUG_CHAT purple_debug_info("nateon", "[chat] Switching to chat.\n"); #endif #if 0 /* this is bad - it causes nateon_switchboard_close to be called on the * switchboard we're in the middle of using :( */ if (swboard->conv != NULL) purple_conversation_destroy(swboard->conv); #endif cmdproc->session->conv_seq++; swboard->chat_id = cmdproc->session->conv_seq; swboard->flag |= NATEON_SB_FLAG_IM; swboard->conv = serv_got_joined_chat(account->gc, swboard->chat_id, "NATEON Chat"); for (l = swboard->users; l != NULL; l = l->next) { const char *tmp_user; tmp_user = l->data; #ifdef NATEON_DEBUG_CHAT purple_debug_info("nateon", "[chat] Adding [%s].\n", tmp_user); #endif purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), tmp_user, NULL, PURPLE_CBFLAGS_NONE, TRUE); } #ifdef NATEON_DEBUG_CHAT purple_debug_info("nateon", "[chat] We add ourselves.\n"); #endif purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), purple_account_get_username(account), NULL, PURPLE_CBFLAGS_NONE, TRUE); g_free(swboard->im_user); swboard->im_user = NULL; } } else if (swboard->conv == NULL) { swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, user, account); } else { purple_debug_warning("nateon", "switchboard_add_user: This should not happen!\n"); } } //static PurpleConversation * //nateon_switchboard_get_conv(NateonSwitchBoard *swboard) //{ // PurpleAccount *account; // // g_return_val_if_fail(swboard != NULL, NULL); // // if (swboard->conv != NULL) // return swboard->conv; // // purple_debug_error("nateon", "Switchboard with unassigned conversation\n"); // // account = swboard->session->account; // // return (swboard->conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, // account, swboard->im_user)); //} // //static void //nateon_switchboard_report_user(NateonSwitchBoard *swboard, PurpleMessageFlags flags, const char *msg) //{ // PurpleConversation *conv; // // g_return_if_fail(swboard != NULL); // g_return_if_fail(msg != NULL); // // if ((conv = nateon_switchboard_get_conv(swboard)) != NULL) // { // purple_conversation_write(conv, NULL, msg, flags, time(NULL)); // } //} static void swboard_error_helper(NateonSwitchBoard *swboard, int reason, const char *account_name) { g_return_if_fail(swboard != NULL); purple_debug_warning("msg", "Error: Unable to call the user %s for reason %i\n", account_name ? account_name : "(null)", reason); /* 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; nateon_switchboard_close(swboard); } } static void invt_error_helper(NateonTransaction *trans, int reason) { NateonSwitchBoard *swboard; const char *account_name; char **params; params = g_strsplit(trans->params, " ", 0); account_name = params[0]; swboard = trans->data; purple_debug_warning("nateon", "invt_error_helper: command %s failed for reason %i\n",trans->command,reason); swboard_error_helper(swboard, reason, account_name); g_strfreev(params); } static void msg_error_helper(NateonCmdProc *cmdproc, NateonMessage *msg, NateonMsgErrorType error) { NateonSwitchBoard *swboard; g_return_if_fail(cmdproc != NULL); g_return_if_fail(msg != NULL); purple_debug_info("nateon", "[%s]\n", __FUNCTION__); // if ((error != NATEON_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 == NATEON_MSG_TEXT) // { // const char *format, *str_reason; // char *body_str, *body_enc, *pre, *post; // //#if 0 // if (swboard->conv == NULL) // { // if (msg->ack_ref) // nateon_message_unref(msg); // // return; // } //#endif // // if (error == NATEON_MSG_ERROR_TIMEOUT) // { // str_reason = _("Message may have not been sent " // "because a timeout occurred:"); // } // else if (error == NATEON_MSG_ERROR_SB) // { // switch (swboard->error) // { // case NATEON_SB_ERROR_OFFLINE: // str_reason = _("Message could not be sent, " // "not allowed while invisible:"); // break; // case NATEON_SB_ERROR_USER_OFFLINE: // str_reason = _("Message could not be sent " // "because the user is offline:"); // break; // case NATEON_SB_ERROR_CONNECTION: // str_reason = _("Message could not be sent " // "because a connection error occurred:"); // break; // case NATEON_SB_ERROR_TOO_FAST: // str_reason = _("Message could not be sent " // "because we are sending too quickly:"); // 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 = nateon_message_to_string(msg); // body_enc = g_markup_escape_text(body_str, -1); // g_free(body_str); // // format = nateon_message_get_attr(msg, "X-MMS-IM-Format"); // nateon_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); // // nateon_switchboard_report_user(swboard, PURPLE_MESSAGE_ERROR, // str_reason); // nateon_switchboard_report_user(swboard, PURPLE_MESSAGE_RAW, // 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 != NATEON_MSG_ERROR_TIMEOUT) // { // swboard->ack_list = g_list_remove(swboard->ack_list, msg); // nateon_message_unref(msg); // } } /************************************************************************** * Message Stuff **************************************************************************/ /** Called when a message times out. */ static void msg_timeout(NateonCmdProc *cmdproc, NateonTransaction *trans) { NateonMessage *msg; msg = trans->data; msg_error_helper(cmdproc, msg, NATEON_MSG_ERROR_TIMEOUT); } ///** Called when we receive an error of a message. */ //static void //msg_error(NateonCmdProc *cmdproc, NateonTransaction *trans, int error) //{ // msg_error_helper(cmdproc, trans->data, NATEON_MSG_ERROR_UNKNOWN); //} // //#if 0 ///** Called when we receive an ack of a special message. */ //static void //msg_ack(NateonCmdProc *cmdproc, NateonCommand *cmd) //{ // NateonMessage *msg; // // msg = cmd->trans->data; // // if (msg->ack_cb != NULL) // msg->ack_cb(msg->ack_data); // // nateon_message_unref(msg); //} // ///** Called when we receive a nak of a special message. */ //static void //msg_nak(NateonCmdProc *cmdproc, NateonCommand *cmd) //{ // NateonMessage *msg; // // msg = cmd->trans->data; // // nateon_message_unref(msg); //} //#endif static void release_msg(NateonSwitchBoard *swboard, NateonMessage *msg) { NateonCmdProc *cmdproc; NateonTransaction *trans; //char *payload; //gsize payload_len; purple_debug_info("nateon", "%s\n", msg); g_return_if_fail(swboard != NULL); g_return_if_fail(msg != NULL); cmdproc = swboard->cmdproc; // payload = nateon_message_gen_payload(msg, &payload_len); //#ifdef NATEON_DEBUG_SB // nateon_message_show_readable(msg, "SB SEND", FALSE); //#endif trans = nateon_transaction_new(cmdproc, "MESG", "%s", msg->body); /* Data for callbacks */ nateon_transaction_set_data(trans, msg); if (msg->type == NATEON_MSG_TEXT) { msg->ack_ref = TRUE; nateon_message_ref(msg); swboard->ack_list = g_list_append(swboard->ack_list, msg); nateon_transaction_set_timeout_cb(trans, msg_timeout); } // else if (msg->type == NATEON_MSG_SLP) // { // msg->ack_ref = TRUE; // nateon_message_ref(msg); // swboard->ack_list = g_list_append(swboard->ack_list, msg); // nateon_transaction_set_timeout_cb(trans, msg_timeout); //#if 0 // if (msg->ack_cb != NULL) // { // nateon_transaction_add_cb(trans, "ACK", msg_ack); // nateon_transaction_add_cb(trans, "NAK", msg_nak); // } //#endif // } // trans->payload = payload; // trans->payload_len = payload_len; msg->trans = trans; nateon_cmdproc_send_trans(cmdproc, trans); } static void queue_msg(NateonSwitchBoard *swboard, NateonMessage *msg) { g_return_if_fail(swboard != NULL); g_return_if_fail(msg != NULL); purple_debug_info("nateon", "Appending message to queue.\n"); g_queue_push_tail(swboard->msg_queue, msg); nateon_message_ref(msg); } static void process_queue(NateonSwitchBoard *swboard) { NateonMessage *msg; g_return_if_fail(swboard != NULL); purple_debug_info("nateon", "Processing queue\n"); while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL) { purple_debug_info("nateon", "Sending message\n"); release_msg(swboard, msg); nateon_message_unref(msg); } } gboolean nateon_switchboard_can_send(NateonSwitchBoard *swboard) { g_return_val_if_fail(swboard != NULL, FALSE); if (swboard->empty || !g_queue_is_empty(swboard->msg_queue)) return FALSE; return TRUE; } void nateon_switchboard_send_msg(NateonSwitchBoard *swboard, NateonMessage *msg, gboolean queue) { g_return_if_fail(swboard != NULL); g_return_if_fail(msg != NULL); if (nateon_switchboard_can_send(swboard)) release_msg(swboard, msg); else if (queue) queue_msg(swboard, msg); } ///************************************************************************** // * Switchboard Commands // **************************************************************************/ static void entr_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) { NateonSwitchBoard *swboard; NateonNotification *notification; swboard = cmdproc->data; swboard->ready = TRUE; notification = swboard->session->notification; nateon_cmdproc_process_queue(notification->cmdproc); } static void quit_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) { NateonSwitchBoard *swboard; const char *user; swboard = cmdproc->data; user = cmd->params[1]; if (!(swboard->flag & NATEON_SB_FLAG_IM) && (swboard->conv != NULL)) purple_debug_error("nateon_switchboard", "bye_cmd: helper bug\n"); if (swboard->conv == NULL) { /* This is a helper switchboard */ nateon_switchboard_destroy(swboard); } else if ((swboard->current_users > 1) || (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) { /* This is a switchboard used for a chat */ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL); swboard->current_users--; if (swboard->current_users == 0) nateon_switchboard_destroy(swboard); } else { /* This is a switchboard used for a im session */ nateon_switchboard_destroy(swboard); } } //static void //iro_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) //{ // PurpleAccount *account; // PurpleConnection *gc; // NateonSwitchBoard *swboard; // // account = cmdproc->session->account; // gc = account->gc; // swboard = cmdproc->data; // // swboard->total_users = atoi(cmd->params[2]); // // nateon_switchboard_add_user(swboard, cmd->params[3]); //} static void join_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) { NateonSession *session; PurpleAccount *account; PurpleConnection *gc; NateonSwitchBoard *swboard; const char *account_name; account_name = cmd->params[1]; session = cmdproc->session; account = session->account; gc = account->gc; swboard = cmdproc->data; nateon_switchboard_add_user(swboard, account_name); process_queue(swboard); nateon_cmdproc_process_queue(swboard->cmdproc); // if (!session->http_method) // send_clientcaps(swboard); if (swboard->closed) nateon_switchboard_close(swboard); } static void whsp_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) { NateonSession *session; NateonSwitchBoard *swboard; const char *account_name; purple_debug_info("nateon", "[%s]\n", __FUNCTION__); session = cmdproc->session; swboard = cmdproc->data; account_name = cmd->params[1]; if (cmd->param_count == 4 && !strcmp(cmd->params[2], "FILE") && \ !strncmp(cmd->params[3], "REQUEST", strlen("REQUEST"))) { /* Receiving File Transfer */ char **split; int i; int num_files; split = g_strsplit(cmd->params[3], "%09", 0); num_files = atoi(split[1]); for (i = 0; i < num_files; i++) { char **file_data; char *filename; file_data = g_strsplit(split[i+2], "|", 0); filename = purple_strreplace(file_data[0], "%20", " "); nateon_xfer_receive_file(session, swboard, account_name, filename, \ atoi(file_data[1]), file_data[2]); g_free(filename); g_strfreev(file_data); } g_strfreev(split); } else if (cmd->param_count == 4 && !strcmp(cmd->params[2], "FILE") && \ !strncmp(cmd->params[3], "NACK", strlen("NACK")) ) { /* file transfer denied */ char **split; int i; int num_files; split = g_strsplit(cmd->params[3], "%09", 0); num_files = atoi(split[1]); for (i = 0; i < num_files; i++) { char **file_data; char *filename; file_data = g_strsplit(split[i+2], "|", 0); filename = purple_strreplace(file_data[0], "%20", " "); nateon_xfer_cancel_transfer(session, account_name, filename, file_data[2]); g_free(filename); g_strfreev(file_data); } g_strfreev(split); } else if (cmd->param_count == 4 && !strcmp(cmd->params[2], "FILE") && \ !strncmp(cmd->params[3], "CANCEL", strlen("CANCEL")) ) { /* file transfer cancelled */ char **split; int i; int num_files; split = g_strsplit(cmd->params[3], "%09", 0); num_files = atoi(split[1]); nateon_xfer_cancel_transfer(session, account_name, NULL, split[2]); g_strfreev(split); } else if (cmd->param_count == 4 && !strcmp(cmd->params[2], "DPIMG") && \ !strncmp(cmd->params[3], "REQUEST", strlen("REQUEST")) ) { /* Receive Buddy Image */ char **split; char **file_data; PurpleBuddyIcon *icon; char *icon_checksum; split = g_strsplit(cmd->params[3], "%09", 0); file_data = g_strsplit(split[2], "|", 0); icon = purple_buddy_icons_find(session->account, account_name); if ( (icon == NULL) || \ ( strcmp(purple_buddy_icon_get_checksum(icon), file_data[0]) != 0)) { nateon_xfer_receive_buddyimage(session, swboard, account_name, file_data[0],\ atoi(file_data[1]), file_data[2]); } if (icon) purple_buddy_icon_unref(icon); g_strfreev(split); } } //static void //msg_cmd_post(NateonCmdProc *cmdproc, NateonCommand *cmd, char *payload, size_t len) //{ // NateonMessage *msg; // // msg = nateon_message_new_from_cmd(cmdproc->session, cmd); // // nateon_message_parse_payload(msg, payload, len); //#ifdef NATEON_DEBUG_SB // nateon_message_show_readable(msg, "SB RECV", FALSE); //#endif // // if (msg->remote_user != NULL) // g_free (msg->remote_user); // // msg->remote_user = g_strdup(cmd->params[0]); // nateon_cmdproc_process_msg(cmdproc, msg); // // nateon_message_destroy(msg); //} // //static void //msg_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) //{ // cmdproc->servconn->payload_len = atoi(cmd->params[2]); // cmdproc->last_cmd->payload_cb = msg_cmd_post; //} static void //msg_cmd(NateonCmdProc *cmdproc, NateonMessage *msg) msg_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) { PurpleConnection *gc; NateonSwitchBoard *swboard; // const char *body; char *body_str; char *body_enc; char *body_final; // size_t body_len; const char *account_name; // const char *value; gc = cmdproc->session->account->gc; swboard = cmdproc->data; // body = nateon_message_get_bin_data(msg, &body_len); // body_str = g_strndup(body, body_len); // body_enc = g_markup_escape_text(body_str, -1); // g_free(body_str); purple_debug_info("nateon", "%s\n", __FUNCTION__); account_name = cmd->params[1]; body_str = cmd->params[3]; // { // char **params; // // params = g_strsplit(body_str, "%09", 0); // // body_final = g_strdup(purple_url_decode(params[3])); // } body_enc = g_markup_escape_text(body_str, -1); body_final = nateon_parse_format(body_enc); purple_debug_info("nateon", "%s - %s %s\n", __FUNCTION__, account_name, body_final); // account_name = msg->remote_user; // if (!strcmp(account_name, "messenger@microsoft.com") && // strstr(body, "immediate security update")) // { // return; // } #if 0 if ((value = nateon_message_get_attr(msg, "User-Agent")) != NULL) { purple_debug_misc("nateon", "User-Agent = '%s'\n", value); } #endif // if ((value = nateon_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) { // char *pre, *post; // nateon_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 |= NATEON_SB_FLAG_IM; if (swboard->current_users > 1 || ((swboard->conv != NULL) && purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_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) purple_debug_misc("nateon", "plain_msg: current_users(%d)\n", swboard->current_users); serv_got_chat_in(gc, swboard->chat_id, account_name, 0, body_final, time(NULL)); if (swboard->conv == NULL) { swboard->conv = purple_find_chat(gc, swboard->chat_id); swboard->flag |= NATEON_SB_FLAG_IM; } } else { serv_got_im(gc, account_name, body_final, 0, time(NULL)); if (swboard->conv == NULL) { swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, account_name, purple_connection_get_account(gc)); swboard->flag |= NATEON_SB_FLAG_IM; } } g_free(body_final); } static void mesg_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) { PurpleConnection *gc; NateonSwitchBoard *swboard; const char *subcmd; //g_return_if_fail(cmd->param_count > 2); if (cmd->param_count < 3) return; subcmd = cmd->params[2]; purple_debug_info("nateon", "%s - %s %s\n", __FUNCTION__, cmd->params[1], cmd->params[3]); if (!strcmp(subcmd, "MSG")) { msg_cmd(cmdproc, cmd); } else if (!strcmp(subcmd, "TYPING")) { gc = cmdproc->session->account->gc; swboard = cmdproc->data; if (!strcmp(cmd->params[3], "1")) { serv_got_typing(gc, swboard->im_user, NATEON_TYPING_RECV_TIMEOUT, PURPLE_TYPING); } else { serv_got_typing_stopped(gc, swboard->im_user); } } } //static void //nak_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) //{ // NateonMessage *msg; // // msg = cmd->trans->data; // g_return_if_fail(msg != NULL); // // msg_error_helper(cmdproc, msg, NATEON_MSG_ERROR_NAK); //} // //static void //ack_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) //{ // NateonSwitchBoard *swboard; // NateonMessage *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); // nateon_message_unref(msg); //} // //static void //out_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) //{ // PurpleConnection *gc; // NateonSwitchBoard *swboard; // // gc = cmdproc->session->account->gc; // swboard = cmdproc->data; // // if (swboard->current_users > 1) // serv_got_chat_left(gc, swboard->chat_id); // // nateon_switchboard_disconnect(swboard); //} //static void //quit_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) //{ // PurpleConnection *gc; // NateonSwitchBoard *swboard; // // gc = cmdproc->session->account->gc; // swboard = cmdproc->data; // // if (swboard->current_users > 1) // serv_got_chat_left(gc, swboard->chat_id); // // nateon_switchboard_disconnect(swboard); //} static void user_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) { NateonSwitchBoard *swboard; purple_debug_info("nateon", "[%s:%d]\n", __FUNCTION__, __LINE__); swboard = cmdproc->data; // 수정필요 =.=/ swboard->total_users = atoi(cmd->params[2]); nateon_switchboard_add_user(swboard, cmd->params[3]); #if 0 GList *l; for (l = swboard->users; l != NULL; l = l->next) { const char *user; user = l->data; nateon_cmdproc_send(cmdproc, "CAL", "%s", user); } #endif swboard->ready = TRUE; nateon_cmdproc_process_queue(cmdproc); } ///************************************************************************** // * Message Handlers // **************************************************************************/ //static void //plain_msg(NateonCmdProc *cmdproc, NateonMessage *msg) //{ // PurpleConnection *gc; // NateonSwitchBoard *swboard; // const char *body; // char *body_str; // char *body_enc; // char *body_final; // size_t body_len; // const char *account_name; // const char *value; // // gc = cmdproc->session->account->gc; // swboard = cmdproc->data; // // body = nateon_message_get_bin_data(msg, &body_len); // body_str = g_strndup(body, body_len); // body_enc = g_markup_escape_text(body_str, -1); // g_free(body_str); // // account_name = msg->remote_user; // // if (!strcmp(account_name, "messenger@microsoft.com") && // strstr(body, "immediate security update")) // { // return; // } // //#if 0 // if ((value = nateon_message_get_attr(msg, "User-Agent")) != NULL) // { // purple_debug_misc("nateon", "User-Agent = '%s'\n", value); // } //#endif // // if ((value = nateon_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) // { // char *pre, *post; // // nateon_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 |= NATEON_SB_FLAG_IM; // // if (swboard->current_users > 1 || // ((swboard->conv != NULL) && // purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_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) // purple_debug_misc("nateon", "plain_msg: current_users(%d)\n", // swboard->current_users); // // serv_got_chat_in(gc, swboard->chat_id, account_name, 0, body_final, // time(NULL)); // if (swboard->conv == NULL) // { // swboard->conv = purple_find_chat(gc, swboard->chat_id); // swboard->flag |= NATEON_SB_FLAG_IM; // } // } // else // { // serv_got_im(gc, account_name, body_final, 0, time(NULL)); // if (swboard->conv == NULL) // { // swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, // account_name, purple_connection_get_account(gc)); // swboard->flag |= NATEON_SB_FLAG_IM; // } // } // // g_free(body_final); //} // //static void //control_msg(NateonCmdProc *cmdproc, NateonMessage *msg) //{ // PurpleConnection *gc; // NateonSwitchBoard *swboard; // const char *value; // char *account_name; // // gc = cmdproc->session->account->gc; // swboard = cmdproc->data; // account_name = msg->remote_user; // // if (swboard->current_users == 1 && // (value = nateon_message_get_attr(msg, "TypingUser")) != NULL) // { // serv_got_typing(gc, account_name, NATEON_TYPING_RECV_TIMEOUT, // PURPLE_TYPING); // } //} // //static void //clientcaps_msg(NateonCmdProc *cmdproc, NateonMessage *msg) //{ //#if 0 // NateonSession *session; // NateonSwitchBoard *swboard; // NateonUser *user; // GHashTable *clientcaps; // const char *value; // // char *account_name = msg->sender; // // session = cmdproc->session; // swboard = cmdproc->servconn->swboard; // // clientcaps = nateon_message_get_hashtable_from_body(msg); //#endif //} // //static void //nudge_msg(NateonCmdProc *cmdproc, NateonMessage *msg) //{ // NateonSwitchBoard *swboard; // char *username, *str; // PurpleAccount *account; // PurpleBuddy *buddy; // const char *user; // // swboard = cmdproc->data; // account = cmdproc->session->account; // user = msg->remote_user; // // if ((buddy = purple_find_buddy(account, user)) != NULL) // username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1); // else // username = g_markup_escape_text(user, -1); // // str = g_strdup_printf(_("%s just sent you a Nudge!"), username); // g_free(username); // nateon_switchboard_report_user(swboard, PURPLE_MESSAGE_SYSTEM, str); // g_free(str); //} /************************************************************************** * Connect stuff **************************************************************************/ static void entr_error(NateonCmdProc *cmdproc, NateonTransaction *trans, int error); static void connect_cb(NateonServConn *servconn) { NateonSwitchBoard *swboard; NateonTransaction *trans; NateonCmdProc *cmdproc; PurpleAccount *account; NateonUser *user; purple_debug_info("nateon", "switchboard - %s\n", __FUNCTION__); cmdproc = servconn->cmdproc; g_return_if_fail(cmdproc != NULL); account = cmdproc->session->account; swboard = cmdproc->data; g_return_if_fail(swboard != NULL); user = cmdproc->session->user; if (nateon_switchboard_is_invited(swboard)) { purple_debug_info("nateon", "== invited ==\n"); swboard->empty = FALSE; trans = nateon_transaction_new(cmdproc, "ENTR", "%s %s %s %s UTF8 P", purple_account_get_username(account), purple_strreplace(user->store_name, " ", "%20"), user->friendly_name, swboard->auth_key); } else { trans = nateon_transaction_new(cmdproc, "ENTR", "%s %s %s %s UTF8 P", purple_account_get_username(account), purple_strreplace(user->store_name, " ", "%20"), user->friendly_name, swboard->auth_key); } nateon_transaction_set_error_cb(trans, entr_error); nateon_transaction_set_data(trans, swboard); nateon_cmdproc_send_trans(cmdproc, trans); } static void entr_error(NateonCmdProc *cmdproc, NateonTransaction *trans, int error) { NateonSwitchBoard *swboard; char **params; char *account; // int reason = MSN_SB_ERROR_UNKNOWN; purple_debug_info("nateon", "[%s] error(%d)\n", __FUNCTION__, error); // if (error == 911) // { // reason = MSN_SB_ERROR_AUTHFAILED; // } purple_debug_warning("nateon", "nateon_error: command %s gave error %i\n", trans->command, error); params = g_strsplit(trans->params, " ", 0); account = params[0]; swboard = trans->data; swboard_error_helper(swboard, NATEON_SB_ERROR_UNKNOWN, account); g_strfreev(params); } static void disconnect_cb(NateonServConn *servconn) { NateonSwitchBoard *swboard; swboard = servconn->cmdproc->data; g_return_if_fail(swboard != NULL); nateon_servconn_set_disconnect_cb(swboard->servconn, NULL); nateon_switchboard_destroy(swboard); } gboolean nateon_switchboard_connect(NateonSwitchBoard *swboard, const char *host, int port) { g_return_val_if_fail(swboard != NULL, FALSE); purple_debug_info("nateon", "%s\n", __FUNCTION__); purple_debug_info("nateon", "host %s\n", host); purple_debug_info("nateon", "port %d\n", port); nateon_servconn_set_connect_cb(swboard->servconn, connect_cb); nateon_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb); return nateon_servconn_connect(swboard->servconn, host, port); } void nateon_switchboard_disconnect(NateonSwitchBoard *swboard) { g_return_if_fail(swboard != NULL); nateon_servconn_disconnect(swboard->servconn); } /************************************************************************** * Invite stuff **************************************************************************/ static void got_invt(NateonCmdProc *cmdproc, NateonCommand *cmd) { //#if 0 // NateonSwitchBoard *swboard; // const char *user; // // swboard = cmdproc->data; // // user = cmd->params[0]; // // nateon_switchboard_add_user(swboard, user); //#endif } static void invt_timeout(NateonCmdProc *cmdproc, NateonTransaction *trans) { purple_debug_warning("nateon", "invt_timeout: command %s timed out\n", trans->command); invt_error_helper(trans, NATEON_SB_ERROR_UNKNOWN); } static void invt_error(NateonCmdProc *cmdproc, NateonTransaction *trans, int error) { purple_debug_info("nateon", "[%s]\n", __FUNCTION__); } void nateon_switchboard_request_add_user(NateonSwitchBoard *swboard, const char *user) { NateonCmdProc *cmdproc; NateonTransaction *trans; NateonServConn *servconn; PurpleAccount *account; const char *username; char *payload; size_t payload_len; g_return_if_fail(swboard != NULL); cmdproc = swboard->session->notification->cmdproc; account = cmdproc->session->account; servconn = swboard->servconn; username = purple_account_get_username(account); payload = g_strdup_printf("INVT %s %s 5004 %s", username, servconn->host, swboard->auth_key); payload_len = strlen(payload); trans = nateon_transaction_new(cmdproc, "CTOC", "%s A %d", user, payload_len); /* this doesn't do anything, but users seem to think that * 'Unhandled command' is some kind of error, so we don't report it */ nateon_transaction_add_cb(trans, "PACK", got_invt); nateon_transaction_set_payload(trans, payload, payload_len); // nateon_transaction_set_data(trans, swboard); nateon_transaction_set_timeout_cb(trans, invt_timeout); g_free(payload); if (swboard->ready) { purple_debug_info("nateon", "[%s] send_trans\n", __FUNCTION__); nateon_cmdproc_send_trans(cmdproc, trans); } else { purple_debug_info("nateon", "[%s] queue_trans\n", __FUNCTION__); nateon_cmdproc_queue_trans(cmdproc, trans); } } /************************************************************************** * Create & Transfer stuff **************************************************************************/ static void got_swboard(NateonCmdProc *cmdproc, NateonCommand *cmd) { NateonSwitchBoard *swboard; char *host; int port; purple_debug_info("nateon", "[%s]\n", __FUNCTION__); swboard = cmd->trans->data; if (g_list_find(cmdproc->session->switches, swboard) == NULL) /* The conversation window was closed. */ return; nateon_switchboard_set_auth_key(swboard, cmd->params[3]); host = g_strdup(cmd->params[1]); port = atoi(cmd->params[2]); if (!nateon_switchboard_connect(swboard, host, port)) { purple_debug_info("nateon", "[%s] connection failed\n", __FUNCTION__); nateon_switchboard_destroy(swboard); } else { purple_debug_info("nateon", "[%s] connection success\n", __FUNCTION__); nateon_switchboard_request_add_user(swboard, swboard->im_user); } // { // NateonCmdProc *cmdproc; // NateonSession *session = swboard->session; // NateonServConn *servconn; // PurpleAccount *account = session->account; // const char *username; // NateonUser *user; // // NateonTransaction *trans; // char *payload; // size_t payload_len; // // cmdproc = swboard->session->notification->cmdproc; // servconn = cmdproc->servconn; // // user = nateon_userlist_find_user_with_name(session->userlist, swboard->im_user); // username = purple_account_get_username(account); // // payload = g_strdup_printf("INVT %s %s %d %s", username, host, port, swboard->auth_key); // payload_len = strlen(payload); // // purple_debug_info("nateon", "[%s] payload(%s)\n", __FUNCTION__, payload); // // purple_debug_info("nateon", "[%s] CTOC에서 A가 들어가야하나? ㅡ.ㅡa\n", __FUNCTION__); // //// trans = nateon_transaction_new(cmdproc, "CTOC", "%s %s %d", swboard->im_user, user->status, payload_len); // trans = nateon_transaction_new(cmdproc, "CTOC", "%s A %d", swboard->im_user, payload_len); //// nateon_transaction_add_cb(trans, "PACK", got_invt); // // nateon_transaction_set_payload(trans, payload, payload_len); //// nateon_transaction_set_data(trans, swboard); //// nateon_transaction_set_timeout_cb(trans, cal_timeout); // // g_free(payload); // // nateon_cmdproc_send_trans(cmdproc, trans); // } g_free(host); } static void ress_error(NateonCmdProc *cmdproc, NateonTransaction *trans, int error) { // NateonSwitchBoard *swboard; // int reason = NATEON_SB_ERROR_UNKNOWN; purple_debug_info("nateon", "[%s]\n", __FUNCTION__); purple_debug_info("nateon", "[%s] error(%s)\n", __FUNCTION__, error); // if (error == 913) // reason = NATEON_SB_ERROR_OFFLINE; // else if (error == 800) // reason = NATEON_SB_ERROR_TOO_FAST; // // swboard = trans->data; // // purple_debug_info("nateon", "xfr_error %i for %s: trans %x, command %s, reason %i\n", // error, (swboard->im_user ? swboard->im_user : "(null)"), trans, // (trans->command ? trans->command : "(null)"), reason); // // swboard_error_helper(swboard, reason, swboard->im_user); } void nateon_switchboard_request(NateonSwitchBoard *swboard) { NateonCmdProc *cmdproc; NateonTransaction *trans; g_return_if_fail(swboard != NULL); cmdproc = swboard->session->notification->cmdproc; trans = nateon_transaction_new(cmdproc, "RESS", NULL); nateon_transaction_add_cb(trans, "RESS", got_swboard); nateon_transaction_set_data(trans, swboard); nateon_transaction_set_error_cb(trans, ress_error); nateon_cmdproc_send_trans(cmdproc, trans); } void nateon_switchboard_close(NateonSwitchBoard *swboard) { g_return_if_fail(swboard != NULL); purple_debug_info("nateon", "%s\n", __FUNCTION__); if (swboard->error != NATEON_SB_ERROR_NONE) { nateon_switchboard_destroy(swboard); } else if (g_queue_is_empty(swboard->msg_queue) || !swboard->session->connected) { NateonCmdProc *cmdproc; cmdproc = swboard->cmdproc; //nateon_cmdproc_send_quick(cmdproc, "QUIT", NULL, NULL); nateon_cmdproc_send(cmdproc, "QUIT", NULL, NULL); nateon_switchboard_destroy(swboard); } else { swboard->closed = TRUE; } } gboolean nateon_switchboard_release(NateonSwitchBoard *swboard, NateonSBFlag flag) { g_return_val_if_fail(swboard != NULL, FALSE); swboard->flag &= ~flag; if (flag == NATEON_SB_FLAG_IM) /* Forget any conversation that used to be associated with this * swboard. */ swboard->conv = NULL; if (swboard->flag == 0) { nateon_switchboard_close(swboard); return TRUE; } return FALSE; } /************************************************************************** * PRS **************************************************************************/ static void rcon_cmd(NateonCmdProc *cmdproc, NateonCommand *cmd) { connect_cb(cmdproc->servconn); } /************************************************************************** * Init stuff **************************************************************************/ void nateon_switchboard_init(void) { cbs_table = nateon_table_new(); nateon_table_add_cmd(cbs_table, "RCON", "RCON", rcon_cmd); nateon_table_add_cmd(cbs_table, "ENTR", "ENTR", entr_cmd); // nateon_table_add_cmd(cbs_table, "ANS", "ANS", ans_cmd); // nateon_table_add_cmd(cbs_table, "ANS", "IRO", iro_cmd); // // nateon_table_add_cmd(cbs_table, "MSG", "ACK", ack_cmd); // nateon_table_add_cmd(cbs_table, "MSG", "NAK", nak_cmd); // // nateon_table_add_cmd(cbs_table, "USR", "USR", usr_cmd); nateon_table_add_cmd(cbs_table, NULL, "USER", user_cmd); // nateon_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd); nateon_table_add_cmd(cbs_table, NULL, "MESG", mesg_cmd); // nateon_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd); nateon_table_add_cmd(cbs_table, NULL, "JOIN", join_cmd); // nateon_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd); // nateon_table_add_cmd(cbs_table, NULL, "OUT", out_cmd); nateon_table_add_cmd(cbs_table, NULL, "WHSP", whsp_cmd); nateon_table_add_cmd(cbs_table, NULL, "QUIT", quit_cmd); // //#if 0 // /* They might skip the history */ // nateon_table_add_cmd(cbs_table, NULL, "ACK", NULL); //#endif // nateon_table_add_error(cbs_table, "MESG", mesg_error); nateon_table_add_error(cbs_table, "INVT", invt_error); // // /* Register the message type callbacks. */ // nateon_table_add_msg_type(cbs_table, "text/plain", // plain_msg); // nateon_table_add_msg_type(cbs_table, "text/x-msmsgscontrol", // control_msg); // nateon_table_add_msg_type(cbs_table, "text/x-clientcaps", // clientcaps_msg); // nateon_table_add_msg_type(cbs_table, "text/x-clientinfo", // clientcaps_msg); // nateon_table_add_msg_type(cbs_table, "application/x-nateonmsgrp2p", // nateon_p2p_msg); // nateon_table_add_msg_type(cbs_table, "text/x-mms-emoticon", // nateon_emoticon_msg); // nateon_table_add_msg_type(cbs_table, "text/x-mms-animemoticon", // nateon_emoticon_msg); // nateon_table_add_msg_type(cbs_table, "text/x-nateonmsgr-datacast", // nudge_msg); //#if 0 // nateon_table_add_msg_type(cbs_table, "text/x-msmmsginvite", // nateon_invite_msg); //#endif } void nateon_switchboard_end(void) { nateon_table_destroy(cbs_table); } /* vim: ts=4 sw=4: */