Mercurial > pidgin.yaz
view src/protocols/msn/slplink.c @ 10284:f776e117c17b
[gaim-migrate @ 11454]
Several MSN memory leaks identified and fixed by Miah Gregory and Felipe
Contreras, plus my own fix for bug 1075347.
As normal, thank them for fixes, blame me for breakages.
Did I mention the new MSN icon? It rocks!
committer: Tailor Script <tailor@pidgin.im>
author | Stu Tomlinson <stu@nosnilmot.com> |
---|---|
date | Wed, 01 Dec 2004 02:30:47 +0000 |
parents | ecf3ce2e2ab1 |
children | a7b2fd5efcf2 |
line wrap: on
line source
/** * @file slplink.c MSNSLP Link support * * 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 "slplink.h" #include "switchboard.h" #include "slp.h" void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg); #ifdef DEBUG_SLP_FILES static int m_sc = 0; static int m_rc = 0; static void debug_msg_to_file(MsnMessage *msg, gboolean send) { char *tmp; char *dir; char *pload; FILE *tf; int c; gsize pload_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); tf = fopen(tmp, "wb"); pload = msn_message_gen_payload(msg, &pload_size); fwrite(pload, 1, pload_size, tf); fclose(tf); g_free(tmp); } #endif MsnSlpLink * msn_slplink_new(MsnSession *session, const char *username) { MsnSlpLink *slplink; slplink = g_new0(MsnSlpLink, 1); slplink->session = session; slplink->slp_seq_id = rand() % 0xFFFFFF00 + 4; slplink->local_user = g_strdup(msn_user_get_passport(session->user)); slplink->remote_user = g_strdup(username); slplink->slp_msg_queue = g_queue_new(); session->slplinks = g_list_append(session->slplinks, slplink); return slplink; } void msn_slplink_destroy(MsnSlpLink *slplink) { MsnSession *session; session = slplink->session; if (slplink->local_user != NULL) g_free(slplink->local_user); if (slplink->remote_user != NULL) g_free(slplink->remote_user); if (slplink->directconn != NULL) msn_directconn_destroy(slplink->directconn); session->slplinks = g_list_remove(session->slplinks, slplink); g_free(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; slplink = msn_session_find_slplink(session, username); if (slplink == NULL) slplink = msn_slplink_new(session, username); return slplink; } MsnSlpSession * msn_slplink_find_slp_session(MsnSlpLink *slplink, long session_id) { GList *l; MsnSlpSession *slpsession; for (l = slplink->slp_sessions; l != NULL; l = l->next) { slpsession = l->data; if (slpsession->id == session_id) return slpsession; } return 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; } void msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg) { if (slplink->directconn != NULL) { msn_directconn_send_msg(slplink->directconn, msg); } else { MsnSwitchBoard *swboard; swboard = msn_session_get_swboard(slplink->session, slplink->remote_user); if (swboard == NULL) return; if (!g_queue_is_empty(swboard->im_queue) || !swboard->user_joined) { msn_switchboard_queue_msg(swboard, msg); } else { msn_switchboard_send_msg(swboard, msg); } } } /* We have received the message receive ack */ static void msg_ack(void *data) { MsnSlpMessage *slpmsg; long long real_size; slpmsg = data; real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size; if (slpmsg->offset < real_size) { msn_slplink_send_msgpart(slpmsg->slplink, slpmsg); } else { /* The whole message has been sent */ if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) { if ((slpmsg->slpcall != NULL) && (slpmsg->slpcall->cb != NULL)) { slpmsg->slpcall->cb(slpmsg->slpcall, NULL, 0); } } msn_slpmsg_destroy(slpmsg); } } void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { MsnMessage *msg; long long real_size; size_t len = 0; msg = slpmsg->msg; real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size; if (slpmsg->offset < real_size) { if (slpmsg->fp) { char data[1202]; len = fread(data, 1, sizeof(data), slpmsg->fp); msn_message_set_bin_data(msg, data, len); } else { len = slpmsg->size - slpmsg->offset; if (len > 1202) len = 1202; msn_message_set_bin_data(msg, slpmsg->buffer + slpmsg->offset, len); } msg->msnslp_header.offset = slpmsg->offset; msg->msnslp_header.length = len; } #ifdef DEBUG_SLP msn_message_show_readable(msg, slpmsg->info, slpmsg->text_body); #endif #ifdef DEBUG_SLP_FILES debug_msg_to_file(msg, TRUE); #endif msn_slplink_send_msg(slplink, msg); if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) && (slpmsg->slpcall != NULL)) { if (slpmsg->slpcall->timer) { gaim_timeout_remove(slpmsg->slpcall->timer); slpmsg->slpcall->timer = gaim_timeout_add(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpmsg->slpcall); } if (slpmsg->slpcall->progress_cb != NULL) { slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size, len, slpmsg->offset); } } slpmsg->offset += len; } void msn_slplink_release_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { MsnMessage *msg; slpmsg->msg = msg = msn_message_new_msnslp(); if (slpmsg->flags == 0x0) { msg->msnslp_header.session_id = slpmsg->session_id; msg->msnslp_header.ack_id = rand() % 0xFFFFFF00; } else if (slpmsg->flags == 0x2) { msg->msnslp_header.session_id = slpmsg->session_id; msg->msnslp_header.ack_id = slpmsg->ack_id; msg->msnslp_header.ack_size = slpmsg->ack_size; } else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) { MsnSlpSession *slpsession; slpsession = slpmsg->slpsession; g_return_if_fail(slpsession != NULL); msg->msnslp_header.session_id = slpsession->id; msg->msnslp_footer.value = slpsession->app_id; msg->msnslp_header.ack_id = rand() % 0xFFFFFF00; } else if (slpmsg->flags == 0x100) { msg->msnslp_header.ack_id = slpmsg->ack_id; msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id; msg->msnslp_header.ack_size = slpmsg->ack_size; } msg->msnslp_header.id = slpmsg->id; msg->msnslp_header.flags = slpmsg->flags; msg->msnslp_header.total_size = slpmsg->size; msn_message_set_attr(msg, "P2P-Dest", slplink->remote_user); msg->ack_cb = msg_ack; msg->ack_data = slpmsg; msn_slplink_send_msgpart(slplink, slpmsg); msn_message_destroy(msg); } void msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { slpmsg->id = slplink->slp_seq_id++; g_queue_push_head(slplink->slp_msg_queue, slpmsg); } void msn_slplink_send_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { slpmsg->id = slplink->slp_seq_id++; msn_slplink_release_msg(slplink, slpmsg); } void msn_slplink_unleash(MsnSlpLink *slplink) { MsnSlpMessage *slpmsg; /* Send the queued msgs in the order they came. */ while ((slpmsg = g_queue_pop_tail(slplink->slp_msg_queue)) != NULL) msn_slplink_release_msg(slplink, slpmsg); } void msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg) { MsnSlpMessage *slpmsg; slpmsg = msn_slpmsg_new(slplink); slpmsg->session_id = msg->msnslp_header.session_id; slpmsg->size = msg->msnslp_header.total_size; slpmsg->flags = 0x02; slpmsg->ack_id = msg->msnslp_header.id; slpmsg->ack_sub_id = msg->msnslp_header.ack_id; slpmsg->ack_size = msg->msnslp_header.total_size; #ifdef DEBUG_SLP slpmsg->info = "SLP ACK"; #endif msn_slplink_send_slpmsg(slplink, slpmsg); } static void send_file_cb(MsnSlpSession *slpsession) { MsnSlpCall *slpcall; MsnSlpMessage *slpmsg; slpcall = slpsession->slpcall; slpmsg = msn_slpmsg_new(slpcall->slplink); slpmsg->flags = 0x1000030; slpmsg->slpsession = slpsession; #ifdef DEBUG_SLP slpmsg->info = "SLP FILE"; #endif slpmsg->slpcall = slpcall; msn_slpmsg_open_file(slpmsg, gaim_xfer_get_local_filename(slpcall->xfer)); msn_slplink_send_slpmsg(slpcall->slplink, slpmsg); } void msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg) { MsnSlpMessage *slpmsg; const char *data; gsize offset; gsize len; #ifdef DEBUG_SLP msn_slpmsg_show(msg); #endif #ifdef DEBUG_SLP_FILES debug_msg_to_file(msg, FALSE); #endif if (msg->msnslp_header.total_size < msg->msnslp_header.length) { gaim_debug_error("msn", "This can't be good\n"); g_return_if_reached(); } slpmsg = NULL; data = msn_message_get_bin_data(msg, &len); /* OVERHEAD! if (msg->msnslp_header.length < msg->msnslp_header.total_size) */ offset = msg->msnslp_header.offset; if (offset == 0) { slpmsg = msn_slpmsg_new(slplink); slpmsg->id = msg->msnslp_header.id; slpmsg->session_id = msg->msnslp_header.session_id; slpmsg->size = msg->msnslp_header.total_size; slpmsg->flags = msg->msnslp_header.flags; if (slpmsg->session_id) { if (slpmsg->slpcall == NULL) slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); if (slpmsg->slpcall != NULL) { if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) { GaimXfer *xfer; xfer = slpmsg->slpcall->xfer; if (xfer != NULL) { slpmsg->fp = fopen(gaim_xfer_get_local_filename(slpmsg->slpcall->xfer), "wb"); } } } } if (!slpmsg->fp && slpmsg->size) { slpmsg->buffer = g_try_malloc(slpmsg->size); if (slpmsg->buffer == NULL) { gaim_debug_error("msn", "Failed to allocate buffer for slpmsg\n"); return; } } } else { slpmsg = msn_slplink_message_find(slplink, msg->msnslp_header.session_id, msg->msnslp_header.id); } if (slpmsg != NULL) { if (slpmsg->fp) { /* fseek(slpmsg->fp, offset, SEEK_SET); */ len = fwrite(data, 1, len, slpmsg->fp); } else if (slpmsg->size) { if ((offset + len) > slpmsg->size) { gaim_debug_error("msn", "Oversized slpmsg\n"); g_return_if_reached(); } else memcpy(slpmsg->buffer + offset, data, len); } } else { gaim_debug_error("msn", "Couldn't find slpmsg\n"); g_return_if_reached(); } if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) && (slpmsg->slpcall != NULL)) { if (slpmsg->slpcall->timer) { gaim_timeout_remove(slpmsg->slpcall->timer); slpmsg->slpcall->timer = gaim_timeout_add(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpmsg->slpcall); } if (slpmsg->slpcall->progress_cb != NULL) { slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size, len, offset); } } #if 0 if (slpmsg->buffer == NULL) return; #endif if (msg->msnslp_header.offset + msg->msnslp_header.length >= msg->msnslp_header.total_size) { /* All the pieces of the slpmsg have been received */ MsnSlpCall *slpcall; slpcall = msn_slp_process_msg(slplink, slpmsg); if (slpmsg->flags == 0x100) { MsnDirectConn *directconn; directconn = slplink->directconn; if (!directconn->acked) msn_directconn_send_handshake(directconn); } else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) { /* Release all the messages and send the ACK */ msn_slplink_send_ack(slplink, msg); msn_slplink_unleash(slplink); } msn_slpmsg_destroy(slpmsg); if (slpcall != NULL && slpcall->wasted) msn_slp_call_destroy(slpcall); } } 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->session_id == session_id) && (slpmsg->id == id)) return slpmsg; } return NULL; } typedef struct { guint32 length; guint32 unk1; guint32 file_size; guint32 unk2; guint32 unk3; } MsnContextHeader; #define MAX_FILE_NAME_LEN 0x226 static char * gen_context(const char *file_name, const char *file_path) { struct stat st; gsize size = 0; MsnContextHeader header; gchar *u8 = NULL; gchar *base, *n; gunichar2 *uni = NULL; glong currentChar = 0; glong uni_len = 0; gsize len; if (stat(file_path, &st) == 0) size = st.st_size; if(!file_name) { u8 = gaim_utf8_try_convert(g_basename(file_path)); file_name = u8; } uni = g_utf8_to_utf16(file_name, -1, NULL, &uni_len, NULL); if(u8) { g_free(u8); file_name = NULL; u8 = NULL; } len = sizeof(MsnContextHeader) + MAX_FILE_NAME_LEN + 4; header.length = GUINT32_TO_LE(len); header.unk1 = GUINT32_TO_LE(2); header.file_size = GUINT32_TO_LE(size); header.unk2 = GUINT32_TO_LE(0); header.unk3 = GUINT32_TO_LE(0); base = g_malloc(len + 1); n = base; memcpy(n, &header, sizeof(MsnContextHeader)); n += sizeof(MsnContextHeader); memset(n, 0x00, MAX_FILE_NAME_LEN); for(currentChar = 0; currentChar < uni_len; currentChar++) { *((gunichar2 *)n + currentChar) = GUINT16_TO_LE(uni[currentChar]); } n += MAX_FILE_NAME_LEN; memset(n, 0xFF, 4); n += 4; g_free(uni); return gaim_base64_encode(base, len); } void msn_slplink_request_ft(MsnSlpLink *slplink, GaimXfer *xfer) { MsnSlpCall *slpcall; char *context; const char *fn; const char *fp; fn = gaim_xfer_get_filename(xfer); fp = gaim_xfer_get_local_filename(xfer); g_return_if_fail(slplink != NULL); g_return_if_fail(fp != NULL); slpcall = msn_slp_call_new(slplink); msn_slp_call_init(slpcall, MSN_SLPCALL_DC); slpcall->session_init_cb = send_file_cb; slpcall->end_cb = msn_xfer_end_cb; slpcall->progress_cb = msn_xfer_progress_cb; slpcall->cb = msn_xfer_completed_cb; slpcall->xfer = xfer; gaim_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel); xfer->data = slpcall; context = gen_context(fn, fp); msn_slp_call_invite(slpcall, "5D3E02AB-6190-11D3-BBBB-00C04F795683", 2, context); g_free(context); } 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 = gaim_base64_encode(msnobj_data, strlen(msnobj_data)); g_free(msnobj_data); slpcall = msn_slp_call_new(slplink); msn_slp_call_init(slpcall, MSN_SLPCALL_ANY); slpcall->data_info = g_strdup(info); slpcall->cb = cb; slpcall->end_cb = end_cb; msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1, msnobj_base64); g_free(msnobj_base64); }