Mercurial > pidgin
view libpurple/protocols/msn/slplink.c @ 30859:2c2ca0aed68e
Split a little more msn_slplink_process_msg. I think this way is easier to read.
author | masca@cpw.pidgin.im |
---|---|
date | Thu, 29 Jul 2010 20:54:50 +0000 |
parents | 2d00d29a45fd |
children | 5ad928319db0 |
line wrap: on
line source
/** * @file slplink.c MSNSLP Link support * * 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 "debug.h" #include "msn.h" #include "slplink.h" #include "slpmsg_part.h" #include "sbconn.h" #include "switchboard.h" #include "slp.h" #include "p2p.h" #ifdef MSN_DEBUG_SLP_FILES static int m_sc = 0; static int m_rc = 0; static void debug_part_to_file(MsnSlpMessage *msg, gboolean send) { char *tmp; char *dir; char *data; int c; gsize data_size; dir = send ? "send" : "recv"; c = send ? m_sc++ : m_rc++; tmp = g_strdup_printf("%s/msntest/%s/%03d", g_get_home_dir(), dir, c); data = msn_slpmsg_serialize(msg, &data_size); if (!purple_util_write_data_to_file_absolute(tmp, data, data_size)) { purple_debug_error("msn", "could not save debug file\n"); } g_free(tmp); } #endif /************************************************************************** * Main **************************************************************************/ static MsnSlpLink * msn_slplink_new(MsnSession *session, const char *username) { MsnSlpLink *slplink; g_return_val_if_fail(session != NULL, NULL); slplink = g_new0(MsnSlpLink, 1); if (purple_debug_is_verbose()) purple_debug_info("msn", "slplink_new: slplink(%p)\n", slplink); slplink->session = session; slplink->slp_seq_id = rand() % 0xFFFFFF00 + 4; slplink->remote_user = g_strdup(username); slplink->slp_msg_queue = g_queue_new(); session->slplinks = g_list_append(session->slplinks, slplink); return msn_slplink_ref(slplink); } static void msn_slplink_destroy(MsnSlpLink *slplink) { MsnSession *session; if (purple_debug_is_verbose()) purple_debug_info("msn", "slplink_destroy: slplink(%p)\n", slplink); g_return_if_fail(slplink != NULL); if (slplink->swboard != NULL) { slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink); slplink->swboard = NULL; } if (slplink->refs > 1) { slplink->refs--; return; } session = slplink->session; if (slplink->dc != NULL) { slplink->dc->slplink = NULL; msn_dc_destroy(slplink->dc); slplink->dc = NULL; } while (slplink->slp_calls != NULL) msn_slpcall_destroy(slplink->slp_calls->data); g_queue_free(slplink->slp_msg_queue); session->slplinks = g_list_remove(session->slplinks, slplink); g_free(slplink->remote_user); g_free(slplink); } MsnSlpLink * msn_slplink_ref(MsnSlpLink *slplink) { g_return_val_if_fail(slplink != NULL, NULL); slplink->refs++; if (purple_debug_is_verbose()) purple_debug_info("msn", "slplink ref (%p)[%d]\n", slplink, slplink->refs); return slplink; } void msn_slplink_unref(MsnSlpLink *slplink) { g_return_if_fail(slplink != NULL); slplink->refs--; if (purple_debug_is_verbose()) purple_debug_info("msn", "slplink unref (%p)[%d]\n", slplink, slplink->refs); if (slplink->refs == 0) msn_slplink_destroy(slplink); } MsnSlpLink * msn_session_find_slplink(MsnSession *session, const char *who) { GList *l; for (l = session->slplinks; l != NULL; l = l->next) { MsnSlpLink *slplink; slplink = l->data; if (!strcmp(slplink->remote_user, who)) return slplink; } return NULL; } MsnSlpLink * msn_session_get_slplink(MsnSession *session, const char *username) { MsnSlpLink *slplink; g_return_val_if_fail(session != NULL, NULL); g_return_val_if_fail(username != NULL, NULL); slplink = msn_session_find_slplink(session, username); if (slplink == NULL) slplink = msn_slplink_new(session, username); return slplink; } void msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall) { if (slplink->swboard != NULL) slplink->swboard->flag |= MSN_SB_FLAG_FT; slplink->slp_calls = g_list_append(slplink->slp_calls, slpcall); /* if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABLISHED) msn_dc_ref(slplink->dc); */ } void msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall) { /* if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABLISHED) msn_dc_unref(slplink->dc); */ slplink->slp_calls = g_list_remove(slplink->slp_calls, slpcall); /* The slplink has no slpcalls in it, release it from MSN_SB_FLAG_FT. * If nothing else is using it then this might cause swboard to be * destroyed. */ if (slplink->slp_calls == NULL && slplink->swboard != NULL) { slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink); msn_switchboard_release(slplink->swboard, MSN_SB_FLAG_FT); slplink->swboard = NULL; } if (slplink->dc != NULL) { if ((slplink->dc->state != DC_STATE_ESTABLISHED && slplink->dc->slpcall == slpcall) || (slplink->slp_calls == NULL)) { /* The DC is not established and its corresponding slpcall is dead, * or the slplink has no slpcalls in it and no longer needs the DC. */ slplink->dc->slplink = NULL; msn_dc_destroy(slplink->dc); slplink->dc = NULL; } } } MsnSlpCall * msn_slplink_find_slp_call(MsnSlpLink *slplink, const char *id) { GList *l; MsnSlpCall *slpcall; if (!id) return NULL; for (l = slplink->slp_calls; l != NULL; l = l->next) { slpcall = l->data; if (slpcall->id && !strcmp(slpcall->id, id)) return slpcall; } return NULL; } MsnSlpCall * msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id) { GList *l; MsnSlpCall *slpcall; for (l = slplink->slp_calls; l != NULL; l = l->next) { slpcall = l->data; if (slpcall->session_id == id) return slpcall; } return NULL; } static void msn_slplink_send_part(MsnSlpLink *slplink, MsnSlpMessagePart *part) { if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABLISHED) { msn_dc_enqueue_part(slplink->dc, part); } else { msn_sbconn_send_part(slplink, part); } } void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { MsnSlpMessagePart *part; long long real_size; size_t len = 0; /* Maybe we will want to create a new msg for this slpmsg instead of * reusing the same one all the time. */ part = msn_slpmsgpart_new(slpmsg->header, slpmsg->footer); part->ack_data = slpmsg; real_size = (slpmsg->flags == P2P_ACK) ? 0 : slpmsg->size; if (slpmsg->offset < real_size) { if (slpmsg->slpcall && slpmsg->slpcall->xfer && purple_xfer_get_type(slpmsg->slpcall->xfer) == PURPLE_XFER_SEND && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED) { len = MIN(MSN_SBCONN_MAX_SIZE, slpmsg->slpcall->u.outgoing.len); msn_slpmsgpart_set_bin_data(part, slpmsg->slpcall->u.outgoing.data, len); } else { len = slpmsg->size - slpmsg->offset; if (len > MSN_SBCONN_MAX_SIZE) len = MSN_SBCONN_MAX_SIZE; msn_slpmsgpart_set_bin_data(part, slpmsg->buffer + slpmsg->offset, len); } slpmsg->header->offset = slpmsg->offset; slpmsg->header->length = len; } #if 0 /* TODO: port this function to SlpMessageParts */ if (purple_debug_is_verbose()) msn_message_show_readable(msg, slpmsg->info, slpmsg->text_body); #endif #ifdef MSN_DEBUG_SLP_FILES debug_part_to_file(slpmsg, TRUE); #endif slpmsg->parts = g_list_append(slpmsg->parts, part); msn_slplink_send_part(slplink, part); if ((slpmsg->flags == P2P_MSN_OBJ_DATA || slpmsg->flags == (P2P_WML2009_COMP | P2P_MSN_OBJ_DATA) || slpmsg->flags == P2P_FILE_DATA) && (slpmsg->slpcall != NULL)) { slpmsg->slpcall->progress = TRUE; if (slpmsg->slpcall->progress_cb != NULL) { slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size, len, slpmsg->offset); } } /* slpmsg->offset += len; */ } static void msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { slpmsg = slpmsg; slpmsg->footer = g_new0(MsnP2PFooter, 1); if (slpmsg->flags == P2P_NO_FLAG) { slpmsg->header->ack_id = rand() % 0xFFFFFF00; } else if (slpmsg->flags == P2P_ACK) { slpmsg->header->ack_id = slpmsg->ack_id; slpmsg->header->ack_size = slpmsg->ack_size; slpmsg->header->ack_sub_id = slpmsg->ack_sub_id; } else if (slpmsg->flags == P2P_MSN_OBJ_DATA || slpmsg->flags == (P2P_WML2009_COMP | P2P_MSN_OBJ_DATA) || slpmsg->flags == P2P_FILE_DATA) { MsnSlpCall *slpcall; slpcall = slpmsg->slpcall; g_return_if_fail(slpcall != NULL); slpmsg->header->session_id = slpcall->session_id; slpmsg->footer->value = slpcall->app_id; slpmsg->header->ack_id = rand() % 0xFFFFFF00; } else if (slpmsg->flags == 0x100) { slpmsg->header->ack_id = slpmsg->ack_id; slpmsg->header->ack_sub_id = slpmsg->ack_sub_id; slpmsg->header->ack_size = slpmsg->ack_size; } slpmsg->header->id = slpmsg->id; slpmsg->header->flags = (guint32)slpmsg->flags; slpmsg->header->total_size = slpmsg->size; msn_slplink_send_msgpart(slplink, slpmsg); } void msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { g_return_if_fail(slpmsg != NULL); slpmsg->id = slplink->slp_seq_id++; g_queue_push_tail(slplink->slp_msg_queue, slpmsg); } void msn_slplink_send_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { slpmsg->id = slplink->slp_seq_id++; msn_slplink_release_slpmsg(slplink, slpmsg); } void msn_slplink_send_queued_slpmsgs(MsnSlpLink *slplink) { MsnSlpMessage *slpmsg; /* Send the queued msgs in the order they were created */ while ((slpmsg = g_queue_pop_head(slplink->slp_msg_queue)) != NULL) { msn_slplink_release_slpmsg(slplink, slpmsg); } } static MsnSlpMessage * msn_slplink_create_ack(MsnSlpLink *slplink, MsnP2PHeader *header) { MsnSlpMessage *slpmsg; slpmsg = msn_slpmsg_ack_new(header); msn_slpmsg_set_slplink(slpmsg, slplink); return slpmsg; } static void msn_slplink_send_ack(MsnSlpLink *slplink, MsnP2PHeader *header) { MsnSlpMessage *slpmsg = msn_slplink_create_ack(slplink, header); msn_slplink_send_slpmsg(slplink, slpmsg); msn_slpmsg_destroy(slpmsg); } static MsnSlpMessage * msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id) { GList *e; for (e = slplink->slp_msgs; e != NULL; e = e->next) { MsnSlpMessage *slpmsg = e->data; if ((slpmsg->header->session_id == session_id) && (slpmsg->id == id)) return slpmsg; } return NULL; } static MsnSlpMessage * init_first_msg(MsnSlpLink *slplink, MsnP2PHeader *header) { MsnSlpMessage *slpmsg; slpmsg = msn_slpmsg_new(slplink); slpmsg->id = header->id; slpmsg->header->session_id = header->session_id; slpmsg->size = header->total_size; slpmsg->flags = header->flags; if (slpmsg->header->session_id) { slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->header->session_id); if (slpmsg->slpcall != NULL) { if (slpmsg->flags == P2P_MSN_OBJ_DATA || slpmsg->flags == (P2P_WML2009_COMP | P2P_MSN_OBJ_DATA) || slpmsg->flags == P2P_FILE_DATA) { PurpleXfer *xfer = slpmsg->slpcall->xfer; if (xfer != NULL) { slpmsg->ft = TRUE; slpmsg->slpcall->xfer_msg = slpmsg; purple_xfer_ref(xfer); purple_xfer_start(xfer, -1, NULL, 0); if (xfer->data == NULL) { purple_xfer_unref(xfer); msn_slpmsg_destroy(slpmsg); g_return_val_if_reached(NULL); } else { purple_xfer_unref(xfer); } } } } } if (!slpmsg->ft && slpmsg->size) { slpmsg->buffer = g_try_malloc(slpmsg->size); if (slpmsg->buffer == NULL) { purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n"); msn_slpmsg_destroy(slpmsg); return NULL; } } return slpmsg; } static void process_complete_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg, MsnP2PHeader *header) { MsnSlpCall *slpcall; slpcall = msn_slp_process_msg(slplink, slpmsg); if (slpcall == NULL) { msn_slpmsg_destroy(slpmsg); return; } purple_debug_info("msn", "msn_slplink_process_msg: slpmsg complete\n"); if (/* !slpcall->wasted && */ slpmsg->flags == 0x100) { #if 0 MsnDirectConn *directconn; directconn = slplink->directconn; if (!directconn->acked) msn_directconn_send_handshake(directconn); #endif } else if (slpmsg->flags == P2P_NO_FLAG || slpmsg->flags == P2P_WML2009_COMP || slpmsg->flags == P2P_MSN_OBJ_DATA || slpmsg->flags == (P2P_WML2009_COMP | P2P_MSN_OBJ_DATA) || slpmsg->flags == P2P_FILE_DATA) { /* Release all the messages and send the ACK */ if (slpcall->wait_for_socket) { /* * Save ack for later because we have to send * a 200 OK message to the previous direct connect * invitation before ACK but the listening socket isn't * created yet. */ purple_debug_info("msn", "msn_slplink_process_msg: save ACK\n"); slpcall->slplink->dc->prev_ack = msn_slplink_create_ack(slplink, header); } else if (!slpcall->wasted) { purple_debug_info("msn", "msn_slplink_process_msg: send ACK\n"); msn_slplink_send_ack(slplink, header); msn_slplink_send_queued_slpmsgs(slplink); } } msn_slpmsg_destroy(slpmsg); if (!slpcall->wait_for_socket && slpcall->wasted) msn_slpcall_destroy(slpcall); } static void slpmsg_add_part(MsnSlpMessage *slpmsg, MsnSlpMessagePart *part) { if (slpmsg->ft) { slpmsg->slpcall->u.incoming_data = g_byte_array_append(slpmsg->slpcall->u.incoming_data, (const guchar *)part->buffer, part->size); purple_xfer_prpl_ready(slpmsg->slpcall->xfer); } else if (slpmsg->size && slpmsg->buffer) { if (G_MAXSIZE - part->size < part->header->offset || (part->header->offset + part->size) > slpmsg->size || slpmsg->offset != part->header->offset) { purple_debug_error("msn", "Oversized slpmsg - msgsize=%lld offset=%" G_GUINT64_FORMAT " len=%" G_GSIZE_FORMAT "\n", slpmsg->size, part->header->offset, part->size); g_return_if_reached(); } else { memcpy(slpmsg->buffer + part->header->offset, part->buffer, part->size); slpmsg->offset += part->size; } } } void msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpMessagePart *part) { MsnSlpMessage *slpmsg; MsnP2PHeader *header; guint64 offset; header = part->header; if (header->total_size < header->length) { /* We seem to have received a bad header */ purple_debug_warning("msn", "Total size listed in SLP binary header " "was less than length of this particular message. This " "should not happen. Dropping message.\n"); return; } offset = header->offset; if (offset == 0) slpmsg = init_first_msg(slplink, header); else { slpmsg = msn_slplink_message_find(slplink, header->session_id, header->id); if (slpmsg == NULL) { /* Probably the transfer was canceled */ purple_debug_error("msn", "Couldn't find slpmsg\n"); return; } } slpmsg_add_part(slpmsg, part); if ((slpmsg->flags == P2P_MSN_OBJ_DATA || slpmsg->flags == (P2P_WML2009_COMP | P2P_MSN_OBJ_DATA) || slpmsg->flags == P2P_FILE_DATA) && (slpmsg->slpcall != NULL)) { slpmsg->slpcall->progress = TRUE; if (slpmsg->slpcall->progress_cb != NULL) { slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size, part->size, offset); } } #if 0 if (slpmsg->buffer == NULL) return; #endif /* All the pieces of the slpmsg have been received */ if (header->offset + header->length >= header->total_size) process_complete_msg(slplink, slpmsg, header); } void msn_slplink_request_object(MsnSlpLink *slplink, const char *info, MsnSlpCb cb, MsnSlpEndCb end_cb, const MsnObject *obj) { MsnSlpCall *slpcall; char *msnobj_data; char *msnobj_base64; g_return_if_fail(slplink != NULL); g_return_if_fail(obj != NULL); msnobj_data = msn_object_to_string(obj); msnobj_base64 = purple_base64_encode((const guchar *)msnobj_data, strlen(msnobj_data)); g_free(msnobj_data); slpcall = msn_slpcall_new(slplink); msn_slpcall_init(slpcall, MSN_SLPCALL_ANY); slpcall->data_info = g_strdup(info); slpcall->cb = cb; slpcall->end_cb = end_cb; msn_slpcall_invite(slpcall, MSN_OBJ_GUID, P2P_APPID_OBJ, msnobj_base64); g_free(msnobj_base64); }