view src/protocols/msn/slplink.c @ 9259:f5f7482678d2

[gaim-migrate @ 10058] Patch by Felipe Contreras. He says, "This patch creates a different callback for the end of a slpcall, so there is no mistake with the current callback for a sucessful slpcall ... when we reach the end of a slpcall the slpcall data has been destroyed, the got_userdisplay cb should be called only as the sucessfull slpcall callback." Essentially, it fixes the corrupt buddy icon checksum issue, though it needs another patch that has to be reworked first, if I understood right. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Thu, 10 Jun 2004 20:57:52 +0000
parents e20af87d8721
children b4dc3827470a
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)
{
	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;

	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)
	{
		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)
			{
				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");
					}
				}
			}
		}
	}
	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->flags == 0x20 || slpmsg->flags == 0x1000030)
	{
		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_completed_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);
}