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);
+}