Mercurial > pidgin.yaz
diff src/protocols/msn/slplink.c @ 9193:502707ca1836
[gaim-migrate @ 9988]
Patch by Felipe Contreras to add MSN file transfer and buddy icons. Please
test and report any bugs!
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Sun, 06 Jun 2004 02:39:08 +0000 |
parents | |
children | ab6636c5a136 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/slplink.c Sun Jun 06 02:39:08 2004 +0000 @@ -0,0 +1,656 @@ +#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, "w"); + 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) +{ + MsnSlpLink *slplink; + GList *l; + + for (l = session->slplinks; l != NULL; l = l->next) + { + 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; + + for (l = slplink->slp_calls; l != NULL; l = l->next) + { + slpcall = l->data; + + if (!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); + } + } +} + +void t_ack(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + MsnSlpMessage *slpmsg; + long long real_size; + + slpmsg = cmd->trans->data; + +#if 0 + if (slpmsg->wasted) + { + gaim_debug_info("msn", "slpmsg cancelled %p\n", slpmsg); + + if (slpmsg->slpcall != NULL) + { + if (slpmsg->slpcall->cb != NULL) + slpmsg->slpcall->cb(slpmsg->slpcall, NULL, -1); + + msn_slpcall_destroy(slpmsg->slpcall); + } + + msn_slpmsg_destroy(slpmsg); + } +#endif + + 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->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->slpcall != NULL) && + (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 = t_ack; + msg->ack_data = slpmsg; + + msn_slplink_send_msgpart(slplink, slpmsg); +} + +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); +} + +void +send_file(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)); + + gaim_xfer_add(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; + slpmsg->buffer = g_malloc(slpmsg->size); + + 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) + { + GaimXfer *xfer; + + xfer = slpmsg->slpcall->xfer; + + if (xfer != NULL) + { + slpmsg->fp = + fopen(gaim_xfer_get_local_filename(slpmsg->slpcall->xfer), "w"); + } + } + } + } + else + { + slpmsg = msn_slplink_message_find(slplink, msg->msnslp_header.id); + } + + if (slpmsg != NULL) + { + if (slpmsg->fp) + { + /* fseek(slpmsg->fp, offset, SEEK_SET); */ + len = fwrite(data, 1, len, slpmsg->fp); + } + else + { + memcpy(slpmsg->buffer + offset, data, len); + } + } + else + { + gaim_debug_error("msn", "Couldn't find slpmsg\n"); + g_return_if_reached(); + } + + if ((slpmsg->slpcall != NULL) && + (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 id) +{ + GList *e; + + for (e = slplink->slp_msgs; e != NULL; e = e->next) + { + MsnSlpMessage *slpmsg = e->data; + + if (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 + +char * +gen_context(const char *file_name) +{ + struct stat st; + gsize size = 0; + MsnContextHeader header; + gchar *u8; + gchar *base, *n; + gunichar2 *uni; + glong uni_len; + gsize len; + + if (stat(file_name, &st) == 0) + size = st.st_size; + + u8 = g_locale_to_utf8(g_basename(file_name), -1, NULL, NULL, NULL); + uni = g_utf8_to_utf16(u8, -1, NULL, &uni_len, NULL); + g_free(u8); + + 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 = n = g_malloc(len + 1); + + memcpy(n, &header, sizeof(MsnContextHeader)); + n += sizeof(MsnContextHeader); + + memset(n, 0x00, MAX_FILE_NAME_LEN); + memcpy(n, uni, uni_len * 2); + 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; + + fn = gaim_xfer_get_local_filename(xfer); + + g_return_if_fail(slplink != NULL); + g_return_if_fail(fn != NULL); + + slpcall = msn_slp_call_new(slplink); + msn_slp_call_init(slpcall, MSN_SLPCALL_DC); + + slpcall->session_init_cb = send_file; + slpcall->progress_cb = msn_xfer_progress_cb; + slpcall->cb = msn_xfer_finish_cb; + slpcall->xfer = xfer; + + gaim_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel); + + xfer->data = slpcall; + + context = gen_context(fn); + + 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, + 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; + + msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1, + msnobj_base64); + + g_free(msnobj_base64); +}