aboutsummaryrefslogtreecommitdiff
path: root/src/msnp2p.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/msnp2p.c')
-rw-r--r--src/msnp2p.c1325
1 files changed, 1325 insertions, 0 deletions
diff --git a/src/msnp2p.c b/src/msnp2p.c
new file mode 100644
index 0000000..5cd2473
--- /dev/null
+++ b/src/msnp2p.c
@@ -0,0 +1,1325 @@
+/*
+ * msnp2p.c
+ *
+ * Wizb Technologies' Combined MSNP2P/MSNSLP layer
+ *
+ * (c) 2002-2005 Thomas White <taw27@srcf.ucam.org>
+ * Part of TuxMessenger - GTK+-based MSN Messenger client
+ *
+ * This package 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; version 2 dated June, 1991.
+ *
+ * This package 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 package; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <glob.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "sbsessions.h"
+#include "mime.h"
+#include "options.h"
+#include "debug.h"
+#include "sbprotocol.h"
+#include "avatars.h"
+#include "routines.h"
+#include "xml.h"
+#include "contactlist.h"
+
+#define MSNP2P_DEBUG
+
+static GList *msnp2p_list = NULL;
+
+struct _mpsession {
+
+ SbSession *parent;
+ char *username;
+ enum {
+ MSNP2P_DIR_RECV,
+ MSNP2P_DIR_SEND
+ } direction;
+
+ unsigned int baseidentifier;
+ unsigned int id_increment;
+ enum {
+ MSNP2P_TYPE_DP,
+ MSNP2P_TYPE_INK,
+ MSNP2P_TYPE_FT
+ } type;
+ unsigned int session_id;
+ FILE *fh;
+ char *save_filename;
+ char *sha1d; /* The SHA1D field from the MSNObject, after xml_killillegalchars. */
+ char *via;
+ char *call_id;
+ size_t offset;
+ void *data;
+ guint delete_timeout;
+
+};
+typedef struct _mpsession MpSession;
+
+struct _mpheader {
+
+ uint32_t session_id;
+ uint32_t identifier;
+ uint64_t offset;
+ uint64_t total_size;
+ uint32_t size;
+ uint32_t flags;
+ uint32_t ack_id;
+ uint32_t ack_sess;
+ uint64_t ack_size;
+
+};
+typedef struct _mpheader MpHeader;
+
+static MpSession *msnp2p_new() {
+
+ MpSession *mpsession = malloc(sizeof(MpSession));
+
+ mpsession->save_filename = NULL;
+ mpsession->sha1d = NULL;
+ mpsession->via = NULL;
+ mpsession->call_id = NULL;
+ mpsession->username = NULL;
+ mpsession->data = NULL;
+ mpsession->delete_timeout = 0;
+
+ return mpsession;
+
+}
+
+static gboolean msnp2p_closesession(MpSession *mpsession) {
+
+ assert(mpsession != NULL);
+
+ if ( mpsession->via != NULL ) {
+ free(mpsession->via);
+ }
+ if ( mpsession->call_id != NULL ) {
+ free(mpsession->call_id);
+ }
+ if ( mpsession->username != NULL ) {
+ debug_print("MP %8p: Deleting an MSNP2P session to '%s'\n", mpsession, mpsession->username);
+ free(mpsession->username);
+ } else {
+ debug_print("MP %8p: Deleting an MSNP2P session.\n", mpsession);
+ }
+ if ( mpsession->save_filename != NULL ) {
+ free(mpsession->save_filename);
+ }
+ if ( mpsession->sha1d != NULL ) {
+ free(mpsession->sha1d);
+ }
+ if ( mpsession->delete_timeout != 0 ) {
+ g_source_remove(mpsession->delete_timeout);
+ }
+ if ( mpsession->data != NULL ) {
+ free(mpsession->data);
+ }
+
+ msnp2p_list = g_list_remove(msnp2p_list, mpsession);
+
+ free(mpsession);
+
+ return FALSE;
+
+}
+
+/* Yes. Not Pretty. */
+static void msnp2p_send(SbSession *session, MpHeader mpheader, const char *username, const char *payload, int payload_length, int app_id) {
+
+ int total_message_length;
+ char *msnp2p_text;
+ char *message_stub;
+ int stub_length;
+ int sf;
+ char *final;
+
+ /* Create the overall header for the message. */
+ msnp2p_text = malloc(74 + strlen(username));
+ if ( msnp2p_text == NULL ) {
+ debug_print("MP: Not enough memory to send MSNP2P message.\n");
+ return;
+ }
+ strcpy(msnp2p_text, "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: ");
+ strcat(msnp2p_text, username);
+ strcat(msnp2p_text, "\r\n\r\n");
+
+ /* Calculate the total message length. */
+ total_message_length = strlen(msnp2p_text) + 48 + payload_length + 4;
+
+ /* Snippet */
+ message_stub = malloc(9);
+ if ( message_stub == NULL ) {
+
+ debug_print("MP: Not enough memory to send MSNP2P message.\n");
+ free(msnp2p_text);
+ return;
+
+ }
+ assert(total_message_length < 10000); /* i.e. fits into four digits. */
+ sprintf(message_stub, "D %i\r\n", total_message_length);
+ stub_length = strlen(message_stub);
+
+ final = malloc(stub_length + strlen(msnp2p_text) + 48 + payload_length + 4);
+ if ( final == NULL ) {
+
+ debug_print("MP: Not enough memory to send MSNP2P message.\n");
+ free(message_stub);
+ free(msnp2p_text);
+ return;
+
+ }
+
+ strcpy(final, message_stub);
+ free(message_stub);
+ strcat(final, msnp2p_text);
+ free(msnp2p_text);
+ sf = strlen(final);
+
+ memcpy (final+sf, &mpheader, 48);
+ memcpy (final+sf+48, payload, payload_length);
+ *((int *)(final+sf+48+payload_length)) = htonl(app_id);
+
+ sbprotocol_sendtr_nonewline(session, "MSG", final, total_message_length+stub_length);
+ free(final);
+
+}
+
+static gint msnp2p_compare_identifier(MpSession *mpsession, unsigned int *identifier) {
+
+ if ( mpsession->baseidentifier == *identifier ) {
+ return 0;
+ }
+
+ return 1;
+
+}
+
+static gint msnp2p_compare_parent(MpSession *mpsession, SbSession **parent) {
+
+ if ( mpsession->parent == *parent ) {
+ return 0;
+ }
+
+ return 1;
+
+}
+
+static gint msnp2p_compare_sessionid(MpSession *mpsession, unsigned int *sessionid) {
+
+ if ( mpsession->session_id == *sessionid ) {
+ return 0;
+ }
+
+ return 1;
+
+}
+
+static gint msnp2p_compare_callid(MpSession *mpsession, char *call_id) {
+
+ if ( mpsession->call_id != NULL ) {
+ if ( strcmp(mpsession->call_id, call_id) == 0 ) {
+ return 0;
+ }
+ }
+
+ return 1;
+
+}
+
+static gint msnp2p_compare_savefilename(MpSession *mpsession, char *save_filename) {
+
+ if ( mpsession->save_filename != NULL ) {
+ if ( strcmp(mpsession->save_filename, save_filename) == 0 ) {
+ return 0;
+ }
+ }
+
+ return 1;
+
+}
+
+/*static gint msnp2p_compare_sha1d(MpSession *mpsession, char *sha1d) {
+
+ if ( mpsession->sha1d != NULL ) {
+ if ( strcmp(mpsession->sha1d, sha1d) == 0 ) {
+ return 0;
+ }
+ }
+
+ return 1;
+
+}*/
+
+static MpSession *msnp2p_find_identifier(unsigned long int identifier) {
+
+ GList *result;
+
+ result = g_list_find_custom(msnp2p_list, &identifier, (GCompareFunc)msnp2p_compare_identifier);
+
+ if ( result != NULL ) {
+ return (MpSession *)result->data;
+ }
+
+ return NULL;
+
+}
+
+static MpSession *msnp2p_find_sessionid(unsigned long int sessionid) {
+
+ GList *result;
+
+ result = g_list_find_custom(msnp2p_list, &sessionid, (GCompareFunc)msnp2p_compare_sessionid);
+
+ if ( result != NULL ) {
+ return (MpSession *)result->data;
+ }
+
+ return NULL;
+
+}
+
+static MpSession *msnp2p_find_callid(char *callid) {
+
+ GList *result;
+
+ result = g_list_find_custom(msnp2p_list, callid, (GCompareFunc)msnp2p_compare_callid);
+
+ if ( result != NULL ) {
+ return (MpSession *)result->data;
+ }
+
+ return NULL;
+
+}
+
+/* Who invented this cr*p? */
+static void msnp2p_inc(MpSession *mpsession) {
+
+ mpsession->id_increment++;
+ if ( mpsession->id_increment == -1 ) {
+ mpsession->id_increment = 1;
+ }
+
+}
+
+static MpSession *msnp2p_check_ok(const char *msnslp) {
+
+ char *session_id_string;
+ MpSession *mpsession;
+
+ session_id_string = mime_getfield_anywhere(msnslp, "SessionID");
+ if ( strlen(session_id_string) == 0 ) {
+ debug_print("MP: Couldn't find SessionID field.\n");
+ free(session_id_string);
+ return NULL;
+ }
+ mpsession = msnp2p_find_sessionid(atoi(session_id_string));
+ free(session_id_string);
+
+ if ( strncmp(msnslp, "MSNSLP/1.0 200 OK", 17) == 0 ) {
+
+ debug_print("MP: Got MSNSLP/1.0 200 OK - ");
+ if ( mpsession == NULL ) {
+ debug_print("but session not found.\n");
+ } else {
+ debug_print("and session found.\n");
+ }
+
+ }
+
+ return mpsession;
+
+}
+
+static int msnp2p_check_bye(SbSession *session, const char *msnslp, MpHeader *mpheader) {
+
+ char *callid_string;
+ MpSession *mpsession;
+
+ callid_string = mime_getfield_anywhere(msnslp, "Call-ID");
+ if ( callid_string == NULL ) {
+ debug_print("MP: Couldn't find Call-ID field.\n");
+ return 0;
+ }
+ mpsession = msnp2p_find_callid(callid_string);
+ free(callid_string);
+
+ if ( strncmp(msnslp, "BYE MSNMSGR", 11) == 0 ) {
+
+ debug_print("MP: Got BYE MSNMSGR - ");
+ if ( mpsession == NULL ) {
+ debug_print("but session not found.\n");
+ } else {
+
+ MpHeader acknowledgement;
+
+ debug_print("and session found.\n");
+
+ /* Send BYE ACK message. */
+ acknowledgement.session_id = 0;
+ acknowledgement.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ /* msnp2p_inc(mpsession); */
+ acknowledgement.offset = 0;
+ acknowledgement.total_size = 0;
+ acknowledgement.size = 0;
+ acknowledgement.flags = GINT32_TO_LE(0x42);
+ acknowledgement.ack_sess = mpheader->identifier;
+ acknowledgement.ack_id = mpheader->ack_sess;
+ acknowledgement.ack_size = mpheader->size;
+
+ debug_print("MP: Sending BYE ACK\n");
+ msnp2p_send(session, acknowledgement, mpsession->username, "", 0, 0);
+
+ /* Delay before deletion allows straggling packets to be collected. */
+ debug_print("MP: Closing MSNP2P session in ten seconds...\n");
+ mpsession->delete_timeout = g_timeout_add(10000, (GSourceFunc)msnp2p_closesession, mpsession);
+
+ return 1;
+
+ }
+
+ }
+
+ return 0;
+
+}
+
+static unsigned int msnp2p_cookie() {
+
+ FILE *fh;
+ unsigned int seed;
+ float num;
+ unsigned int val;
+
+ fh = fopen("/dev/urandom", "r");
+ fread(&seed, sizeof(seed), 1, fh);
+ fclose(fh);
+ srand(seed);
+
+ num = (float)rand() / (float)RAND_MAX;
+ val = (num * 16777216)+8;
+
+ return val;
+
+}
+
+static MpSession *msnp2p_send_dp(MpSession *mpsession, SbSession *session, const char *msnslp) {
+
+ MpHeader msnp2p_binary;
+ char *msnslp_block;
+ size_t msnslp_length;
+ glob_t glob_result;
+ struct stat *statbuf;
+ void *picture_data;
+ unsigned int file_offs;
+ int zero;
+ struct stat stat_buffer;
+ FILE *fh;
+ unsigned int size;
+ int readval;
+ char *request_details;
+ size_t request_details_length;
+ char *local_avatar;
+
+ mpsession->via = mime_getfield_anywhere(msnslp, "Via:");
+ if ( mpsession->via == NULL ) {
+ debug_print("MP: Couldn't find Via field.\n");
+ free(mpsession);
+ return NULL;
+ }
+ mpsession->call_id = mime_getfield_anywhere(msnslp, "Call-ID:");
+ if ( mpsession->call_id == NULL ) {
+ debug_print("MP: Couldn't find Call-ID field.\n");
+ free(mpsession);
+ return NULL;
+ }
+
+ request_details = malloc(26);
+ debug_print("MP: New session ID=%i\n", mpsession->session_id);
+ assert(mpsession->session_id <= 2147483647);
+ strcpy(request_details, "SessionID: ");
+ sprintf(request_details+strlen(request_details), "%i\r\n\r\n", mpsession->session_id);
+ request_details_length = strlen(request_details);
+
+ msnslp_block = malloc( 176 + strlen(options_username()) + strlen(mpsession->username) + strlen(mpsession->via) + strlen(mpsession->call_id) + strlen(request_details) );
+ strcpy(msnslp_block, "MSNSLP/1.0 200 OK\r\nTo: <msnmsgr:");
+ strncat(msnslp_block, mpsession->username, 64);
+ strcat(msnslp_block, ">\r\nFrom: <msnmsgr:");
+ strncat(msnslp_block, options_username(), 64);
+ strcat(msnslp_block, ">\r\nVia: ");
+ strncat(msnslp_block, mpsession->via, 256);
+ strcat(msnslp_block, "\r\nCSeq: 1 \r\nCall-ID: ");
+ strncat(msnslp_block, mpsession->call_id, 256);
+ strcat(msnslp_block, "\r\nMax-Forwards: 0\r\nContent-Type: application/x-msnmsgr-sessionreqbody\r\nContent-Length: ");
+ assert(request_details_length + 1 < 10000);
+ sprintf(msnslp_block+strlen(msnslp_block), "%i", request_details_length+1);
+ strcat(msnslp_block, "\r\n\r\n");
+ strcat(msnslp_block, request_details);
+ free(request_details);
+
+ msnslp_length = strlen(msnslp_block);
+
+ msnp2p_binary.session_id = 0;
+ msnp2p_binary.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ msnp2p_inc(mpsession);
+ msnp2p_binary.offset = 0;
+ msnp2p_binary.total_size = GINT64_TO_LE(msnslp_length+1);
+ msnp2p_binary.size = GINT64_TO_LE(msnslp_length+1);
+ msnp2p_binary.flags = 0;
+ msnp2p_binary.ack_sess = GINT32_TO_LE(mpsession->session_id);
+ msnp2p_binary.ack_id = 0;
+ msnp2p_binary.ack_size = 0;
+ debug_print("MP: Sending 200 OK message\n");
+ msnp2p_send(session, msnp2p_binary, mpsession->username, msnslp_block, strlen(msnslp_block)+1, 0);
+ free(msnslp_block);
+
+ local_avatar = avatars_local();
+ glob(local_avatar, GLOB_TILDE, NULL, &glob_result);
+ free(local_avatar);
+
+ statbuf = &stat_buffer;
+ if ( stat(glob_result.gl_pathv[0], statbuf) == -1 ) {
+ debug_print("MP: Couldn't find avatar file :(\n");
+ free(mpsession);
+ return NULL;
+ }
+
+ size = (int)statbuf->st_size;
+ assert(size > 0);
+
+ fh = fopen(glob_result.gl_pathv[0], "r");
+ if ( fh == NULL ) {
+ debug_print("MP: Couldn't open avatar file.\n");
+ free(mpsession);
+ return NULL;
+ }
+ picture_data = malloc(size);
+ readval = fread(picture_data, 1, size, fh);
+ if ( readval < 0 ) {
+ debug_print("MP: Couldn't read avatar file.\n");
+ free(mpsession);
+ free(picture_data);
+ return NULL;
+ }
+ debug_print("MP: Read %i bytes of avatar file\n", readval);
+ fclose(fh);
+ globfree(&glob_result);
+
+ msnp2p_binary.session_id = GINT32_TO_LE(mpsession->session_id);
+ msnp2p_binary.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ msnp2p_inc(mpsession);
+ msnp2p_binary.offset = 0;
+ msnp2p_binary.total_size = GINT64_TO_LE(4);
+ msnp2p_binary.size = GINT64_TO_LE(4);
+ msnp2p_binary.flags = 0;
+ msnp2p_binary.ack_sess = GINT32_TO_LE(123456);
+ msnp2p_binary.ack_id = 0;
+ msnp2p_binary.ack_size = 0;
+ zero = 0;
+ debug_print("MP: Sending data preparation message\n");
+ msnp2p_send(session, msnp2p_binary, mpsession->username, (char *)&zero, 4, 1);
+
+ msnp2p_binary.session_id = GINT32_TO_LE(mpsession->session_id);
+ msnp2p_binary.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ msnp2p_inc(mpsession);
+ msnp2p_binary.total_size = GINT64_TO_LE(size);
+ msnp2p_binary.flags = GINT32_TO_LE(0x20);
+ msnp2p_binary.ack_id = 0;
+ msnp2p_binary.ack_size = 0;
+
+ for ( file_offs=0; file_offs<size; file_offs+=1202 ) {
+
+ msnp2p_binary.offset = file_offs;
+ if ( (size-file_offs) > 1201 ) {
+ msnp2p_binary.size = GINT64_TO_LE(1202);
+ } else {
+ msnp2p_binary.size = GINT64_TO_LE((size-file_offs) % 1202);
+ }
+ msnp2p_binary.ack_sess = msnp2p_cookie();
+ debug_print("MP: Sending %i bytes: %i-%i of %i\n", msnp2p_binary.size, (int)msnp2p_binary.offset, (int)(msnp2p_binary.offset+msnp2p_binary.size)-1, (int)msnp2p_binary.total_size);
+ msnp2p_send(session, msnp2p_binary, mpsession->username, picture_data+file_offs, msnp2p_binary.size, 1);
+
+ }
+
+ free(picture_data);
+
+ return mpsession;
+
+}
+
+static MpSession *msnp2p_incomingsession(SbSession *session, const char *msnslp, MpHeader *mpheader) {
+
+ MpSession *mpsession;
+ char *temp_name;
+ char *session_id_string;
+ unsigned int session_id;
+ MpHeader acknowledgement;
+ char *euf_guid;
+ char *content_type;
+
+ if ( strncmp(msnslp, "INVITE MSNMSGR:", 15) != 0 ) {
+ return NULL;
+ }
+
+ content_type = mime_getfield(msnslp, "Content-Type");
+ if ( strcmp(content_type, "application/x-msnmsgr-transreqbody") == 0 ) {
+ }
+ free(content_type);
+
+ session_id_string = mime_getfield_anywhere(msnslp, "SessionID:");
+ if ( strlen(session_id_string) == 0 ) {
+ debug_print("MP: Couldn't find SessionID field.\n");
+ free(session_id_string);
+ return NULL;
+ }
+ session_id = atoi(session_id_string);
+ free(session_id_string);
+ /* Return if this packet isn't to be dealt with here. */
+ if ( session_id == 0 ) {
+ return NULL;
+ }
+
+ euf_guid = mime_getfield_anywhere(msnslp, "EUF-GUID:");
+ if ( strlen(euf_guid) == 0 ) {
+ debug_print("MP: Couldn't find EUF-GUID field.\n");
+ return NULL;
+ }
+
+ mpsession = msnp2p_new();
+ if ( mpsession == NULL ) {
+ debug_print("MP: Not enough memory to start MSNP2P transfer.\n");
+ return NULL;
+ }
+
+ if ( strcmp(euf_guid, "{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}") == 0 ) {
+ mpsession->type = MSNP2P_TYPE_DP;
+ } else if ( strcmp(euf_guid, "{5D3E02AB-6190-11D3-BBBB-00C04F795683}") == 0 ) {
+ mpsession->type = MSNP2P_TYPE_FT;
+ } else {
+ debug_print("MP: Unrecognised MSNSLP INVITE.\n");
+ free(euf_guid);
+ return NULL;
+ }
+ free(euf_guid);
+
+ mpsession->parent = session;
+ mpsession->baseidentifier = msnp2p_cookie();
+ mpsession->id_increment = -3;
+ mpsession->session_id = session_id;
+ mpsession->direction = MSNP2P_DIR_SEND;
+ mpsession->save_filename = NULL;
+ mpsession->sha1d = NULL;
+
+ /* Get actual username. */
+ temp_name = mime_getfield(msnslp, "From:");
+ if ( temp_name == NULL ) {
+ debug_print("MP: Couldn't find From field.\n");
+ free(mpsession);
+ return NULL;
+ }
+ if ( strncmp(temp_name, "<msnmsgr:", 9) != 0 ) {
+ debug_print("MP: MSNSLP request not from \"<msnmsgr:*>\" - giving up.\n");
+ free(mpsession);
+ return NULL;
+ }
+ mpsession->username = strdup(temp_name+9);
+ free(temp_name);
+ if ( mpsession->username == NULL ) {
+ debug_print("MP: Couldn't find From field.\n");
+ free(mpsession);
+ return NULL;
+ }
+ mpsession->username[strlen(mpsession->username)-1] = '\0';
+
+ switch ( mpsession->type ) {
+ case MSNP2P_TYPE_DP : debug_print("MP: Got Display Picture Invite from %s\n", mpsession->username); break;
+ case MSNP2P_TYPE_FT : debug_print("MP: Got File Transfer Invite from %s\n", mpsession->username); break;
+ case MSNP2P_TYPE_INK : debug_print("MP: This doesn't happen.\n"); break;
+ }
+
+ debug_print("MSNSLP: '%s'\n", msnslp);
+
+ acknowledgement.session_id = 0;
+ acknowledgement.identifier = GINT32_TO_LE(mpsession->baseidentifier);
+ acknowledgement.offset = 0;
+ acknowledgement.total_size = mpheader->total_size;
+ acknowledgement.size = 0;
+ acknowledgement.flags = GINT32_TO_LE(2);
+ acknowledgement.ack_sess = mpheader->identifier;
+ acknowledgement.ack_id = mpheader->ack_sess;
+ acknowledgement.ack_size = mpheader->total_size;
+ debug_print("MP: Sending BaseIdentifier message\n");
+ msnp2p_send(session, acknowledgement, mpsession->username, "", 0, 0);
+
+ if ( mpsession->type == MSNP2P_TYPE_DP ) {
+ mpsession = msnp2p_send_dp(mpsession, session, msnslp);
+ }
+
+ if ( mpsession != NULL ) {
+ msnp2p_list = g_list_append(msnp2p_list, mpsession);
+ }
+
+ return mpsession;
+
+}
+
+static void msnp2p_sendbye(SbSession *session, MpSession *mpsession) {
+
+ char *msnslp_block;
+ MpHeader mpheader;
+ int msnslp_length;
+
+ msnslp_block = malloc( 294 + 2*strlen(mpsession->username) + strlen(options_username()) );
+ strcpy(msnslp_block, "BYE MSNMSGR:");
+ strncat(msnslp_block, mpsession->username, 64);
+ strcat(msnslp_block, " MSNSLP/1.0\r\nTo: <msnmsgr:");
+ strncat(msnslp_block, mpsession->username, 64);
+ strcat(msnslp_block, ">\r\nFrom: <msnmsgr:");
+ strncat(msnslp_block, options_username(), 64);
+ strcat(msnslp_block, ">\r\nVia: MSNSLP/1.0/TLP ;branch={33517CE4-02FC-4428-B6F4-39927229B722}\r\nCSeq: 0 \r\nCall-ID: ");
+ strncat(msnslp_block, mpsession->call_id, 38);
+ strcat(msnslp_block, "\r\nMax-Forwards: 0\r\nContent-Type: application/x-msnmsgr-sessionclosebody\r\nContent-Length: 3");
+ strcat(msnslp_block, "\r\n\r\n");
+
+ msnslp_length = strlen(msnslp_block);
+
+ mpheader.session_id = 0;
+ mpheader.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ msnp2p_inc(mpsession);
+ mpheader.offset = 0;
+ mpheader.total_size = GINT64_TO_LE(msnslp_length+1);
+ mpheader.size = GINT64_TO_LE(msnslp_length+1);
+ mpheader.flags = 0;
+ mpheader.ack_sess = GINT32_TO_LE(mpsession->session_id);
+ mpheader.ack_id = 0;
+ mpheader.ack_size = 0;
+
+ debug_print("MP: Sending MSNSLP BYE for display picture.\n");
+ msnp2p_send(session, mpheader, mpsession->username, msnslp_block, msnslp_length+1, 0);
+
+ free(msnslp_block);
+
+}
+
+static void msnp2p_handledatapacket(SbSession *session, MpHeader *mpheader, MpSession *mpsession, const char *msnslp) {
+
+ FILE *fh;
+
+ debug_print("MP: Got data packet: %i-%i of %i\n", (int)mpheader->offset, (int)(mpheader->offset+mpheader->size)-1, (int)mpheader->total_size);
+
+ fh = fopen(mpsession->save_filename, "a");
+ if ( fh == NULL ) {
+ debug_print("MP: Couldn't open avatar file to write data.\n");
+ return;
+ }
+ fwrite(msnslp, 1, mpheader->size, fh);
+ fclose(fh);
+
+ /* Got all of data? */
+ if ( mpheader->offset+mpheader->size >= mpheader->total_size ) {
+
+ /* Send an acknowledgement for the data */
+ MpHeader acknowledgement;
+
+ acknowledgement.session_id = mpheader->session_id;
+ acknowledgement.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ msnp2p_inc(mpsession);
+ acknowledgement.offset = 0;
+ acknowledgement.total_size = 0;
+ acknowledgement.size = 0;
+ acknowledgement.flags = GINT32_TO_LE(2);
+ acknowledgement.ack_sess = mpheader->session_id;
+ acknowledgement.ack_id = mpheader->identifier;
+ acknowledgement.ack_size = mpheader->total_size;
+
+ msnp2p_send(session, acknowledgement, mpsession->username, "", 0, 0);
+
+ msnp2p_sendbye(session, mpsession);
+ contactlist_picturekick_sha1d(mpsession->sha1d);
+
+ /* Delay before deletion allows straggling packets to be collected. */
+ debug_print("MP: Closing MSNP2P session in ten seconds...\n");
+ mpsession->delete_timeout = g_timeout_add(10000, (GSourceFunc)msnp2p_closesession, mpsession);
+
+ }
+
+}
+
+static MpSession *msnp2p_check_ink(SbSession *session, const char *username, MpHeader *header, const void *data, unsigned long int app_id) {
+
+ MpSession *mpsession;
+ size_t datalength = GINT32_TO_LE(header->size);
+
+ if ( !(header->session_id == 64) || !(app_id == 3) ) {
+ return NULL;
+ }
+
+ debug_print("MP: Ink packet from '%s'\n", username);
+
+ if ( header->offset == 0 ) {
+
+
+ debug_print("MP: First in a sequence of Ink packets.\n");
+ mpsession = msnp2p_new();
+
+ mpsession->baseidentifier = GINT32_TO_LE(header->identifier);
+ mpsession->type = MSNP2P_TYPE_INK;
+ mpsession->direction = MSNP2P_DIR_RECV;
+ mpsession->parent = session;
+ mpsession->username = strdup(username);
+ mpsession->session_id = 0;
+ mpsession->via = NULL;
+ mpsession->save_filename = NULL;
+ mpsession->sha1d = NULL;
+ mpsession->call_id = NULL;
+
+ if ( datalength <= 2000 ) { /* Sanity check */
+
+ mpsession->data = malloc(datalength);
+ memcpy(mpsession->data, data, datalength);
+ mpsession->offset = datalength;
+
+ } else {
+
+ debug_print("MP: Ink packet too big - binning.\n");
+ free(mpsession);
+ return NULL;
+
+ }
+
+ msnp2p_list = g_list_append(msnp2p_list, mpsession);
+
+ } else {
+
+ mpsession = msnp2p_find_identifier(GINT32_TO_LE(header->identifier));
+ if ( mpsession == NULL ) {
+ debug_print("MP: Unrecognised Ink packet.\n");
+ return NULL;
+ }
+
+ if ( datalength <= 2000 ) {
+
+ mpsession->data = realloc(mpsession->data, (mpsession->offset) + datalength);
+ if ( mpsession->data == NULL ) {
+ debug_print("MP: Whoops! Couldn't allocate memory.\n");
+ return NULL;
+ }
+ memcpy((mpsession->data)+(mpsession->offset), data, datalength);
+ mpsession->offset += datalength;
+ debug_print("MP: Got some continuing Ink data.\n");
+
+ } else {
+ debug_print("MP: Ink packet too big - binning.\n");
+ return NULL;
+ }
+
+
+ }
+
+ assert(mpsession != NULL);
+
+ /* NB It's possible for a single message to be both the first and last in a sequence. */
+ debug_print("MP: %i of %i bytes.\n", GINT64_TO_LE(header->offset), GINT64_TO_LE(header->total_size));
+ if ( GINT64_TO_LE(header->offset) >= GINT64_TO_LE(header->total_size) ) {
+
+ gchar *body;
+ glong new_length;
+ glong items_in;
+ GError *error;
+
+ debug_print("MP: Last in a sequence of Ink packets.\n");
+
+ debug_print("MP: Decoding UTF-16: %i bytes in.\n", mpsession->offset);
+ body = g_utf16_to_utf8(mpsession->data, mpsession->offset, &items_in, &new_length, &error);
+ debug_print("MP: %i items in, %i items out.\n", items_in, new_length);
+ if ( error == NULL ) {
+ debug_print("MP: No error.\n");
+ } else {
+ debug_print("MP: Error!\n");
+ debug_print("%s'\n", error->message);
+ }
+ if ( body == NULL ) {
+ debug_print("MP: Conversion to UTF-8 failed: '%s'\n", error->message);
+ } else {
+ sbprotocol_parsemsg(session, body, new_length);
+ debug_print("MP: Ink (%i bytes): '%s'\n", new_length, body);
+ g_free(body);
+ }
+
+ debug_print("MP: Closing MSNP2P session in ten seconds...\n");
+ mpsession->delete_timeout = g_timeout_add(10000, (GSourceFunc)msnp2p_closesession, mpsession);
+
+ }
+
+ return mpsession;
+
+}
+
+void msnp2p_parsemsg(SbSession *session, const char *username, const char *whole_msg, size_t len) {
+
+ char *p2p_dest;
+ MpSession *mpsession;
+ MpHeader *mpheader;
+ const char *msnslp;
+ const char *msg;
+ unsigned long int app_id;
+
+ /* First check the destination of the message and ignore if appropriate. */
+ p2p_dest = mime_getfield(whole_msg, "P2P-Dest");
+ if ( p2p_dest == NULL ) {
+ debug_print("MP: Couldn't find P2P-Dest field.\n");
+ return;
+ }
+ if ( strcasecmp(options_username(), p2p_dest) != 0 ) {
+ free(p2p_dest);
+ debug_print("MP: Ignoring misaddressed MSNP2P message.\n");
+ return;
+ }
+ free(p2p_dest);
+
+ /* Now forget about the MIME header. */
+ len -= mime_headerlength(whole_msg);
+ msg = mime_getbody(whole_msg);
+ mpheader = (MpHeader *)msg; /* First bytes of "msg" are the header. Obviously. */
+ msnslp = msg+sizeof(MpHeader); /* Contents */
+
+ /* Check the provided packet size is sane. */
+ if ( len < GINT64_TO_LE(mpheader->size) ) {
+ debug_print("MP: 'size' field in header too big - rejecting packet.\n");
+ return;
+ }
+ app_id = ntohl(*(int *)(msg+(mpheader->size)+48));
+
+#ifdef MSNP2P_DEBUG
+ debug_print("MP: ---- MSNP2P packet ----\n");
+ debug_print("MP: sessionid = 0x%lx\n", mpheader->session_id);
+ debug_print("MP: identifier = 0x%lx\n", mpheader->identifier);
+ debug_print("MP: offset = 0x%llx\n", mpheader->offset);
+ debug_print("MP: total_size = 0x%llx\n", mpheader->total_size);
+ debug_print("MP: size = 0x%lx\n", mpheader->size);
+ debug_print("MP: flags = 0x%lx\n", mpheader->flags);
+ debug_print("MP: ack_id = 0x%lx\n", mpheader->ack_id);
+ debug_print("MP: ack_sess = 0x%lx\n", mpheader->ack_sess);
+ debug_print("MP: ack_size = 0x%llx\n", mpheader->ack_size);
+ debug_print("MP: app_id = 0x%lx\n", app_id);
+#endif /* MSNP2P_DEBUG */
+
+ /* Arrrggggggggggghhhhhhhhhhhhhhhhhhhhhh. */
+ mpsession = msnp2p_find_identifier(GINT32_TO_LE(mpheader->ack_sess));
+ if ( mpsession == NULL ) {
+ mpsession = msnp2p_find_sessionid(GINT32_TO_LE(mpheader->session_id));
+ }
+ if ( mpsession == NULL ) {
+ mpsession = msnp2p_check_ok(msnslp);
+ }
+ if ( mpsession == NULL ) {
+ mpsession = msnp2p_find_sessionid(GINT32_TO_LE(mpheader->ack_id));
+ }
+ if ( (mpsession == NULL) && (mpheader->session_id == 0) ) {
+ mpsession = msnp2p_incomingsession(session, msnslp, mpheader);
+ }
+ if ( msnp2p_check_bye(session, msnslp, mpheader) != 0 ) {
+ debug_print("MP: Got BYE.\n");
+ return;
+ }
+ if ( mpsession == NULL ) {
+ mpsession = msnp2p_check_ink(session, username, mpheader, msnslp, app_id);
+ }
+
+ /* Past this point, if the message hasn't been matched to a session then it's unrecognised. */
+
+ if ( mpsession == NULL ) {
+ debug_print("MP: Unrecognised MSNP2P ");
+ if ( (GINT32_TO_LE(mpheader->flags) & 2) != 0 ) {
+ debug_print("ack.\n");
+ } else {
+ debug_print("packet:\n");
+ debug_print("MP: Flags = %x\n", GINT32_TO_LE(mpheader->flags));
+ debug_print("MP: Data: '%s'\n", msnslp);
+ }
+ return;
+ }
+
+ if ( ( GINT32_TO_LE(mpheader->flags) & 2) != 0 ) {
+ debug_print("MP: Got ACK.\n");
+ }
+
+ if ( ( (GINT32_TO_LE(mpheader->flags) & 2) == 0) && (mpheader->size == mpheader->total_size) ) {
+
+ /* Message wasn't an ACK, so probably needs an ACK sending */
+
+ MpHeader acknowledgement;
+
+ acknowledgement.session_id = GINT32_TO_LE(mpheader->session_id);
+ acknowledgement.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ /* msnp2p_inc(mpsession); */
+ acknowledgement.offset = 0;
+ acknowledgement.total_size = 0;
+ acknowledgement.size = 0;
+ if ( GINT32_TO_LE(mpheader->flags) == 0x40 ) {
+ acknowledgement.flags = GINT32_TO_LE(0x80);
+ } else {
+ acknowledgement.flags = GINT32_TO_LE(2);
+ }
+ /* Copying directly from received header: don't touch endianness. */
+ acknowledgement.ack_sess = mpheader->session_id;
+ acknowledgement.ack_id = mpheader->identifier;
+ acknowledgement.ack_size = mpheader->size;
+
+ debug_print("MP: Sending ACK.\n");
+ msnp2p_send(session, acknowledgement, mpsession->username, "", 0, 0);
+
+ }
+
+ if ( (app_id == 1) && (GINT64_TO_LE(mpheader->total_size) != 4) ) {
+ msnp2p_handledatapacket(session, mpheader, mpsession, msnslp);
+ }
+
+}
+
+/* Check for a receive session for the DP SHA1D given. */
+int msnp2p_retrieving(const char *save_filename) {
+
+ GList *result = NULL;
+
+ result = g_list_find_custom(msnp2p_list, save_filename, (GCompareFunc)msnp2p_compare_savefilename);
+ if ( result != NULL ) {
+ /* At least one session exists. Don't care what it is... */
+ return 1;
+ }
+
+ /* Nothing found. */
+ return 0;
+
+}
+
+/* Initiate the downloading of a picture. */
+void msnp2p_getpicture(SbSession *session, const char *username, const char *dpobject) {
+
+ MpSession *mpsession;
+ MpHeader mpheader;
+
+ char *dpobject_decoded;
+ char *context;
+ char *data_hash;
+ char *filename;
+
+ char *request_details;
+ int request_details_length;
+ char *request_details_length_string;
+ char *msnslp_block;
+ int msnslp_length;
+ glob_t glob_result;
+ struct stat stat_buffer;
+ struct stat *statbuf;
+
+ debug_print("MP: Beginning download for DP from '%s'\n", username);
+
+ assert(username != NULL);
+ assert(session != NULL);
+
+ if ( dpobject == NULL ) {
+ debug_print("MP: Attempt to download avatar for user who doesn't have one.\n");
+ return;
+ }
+
+ if ( sbsessions_sessionready(session) == 0 ) {
+ debug_print("MP: Session isn't ready to download picture - not trying.\n");
+ return;
+ }
+
+ mpsession = msnp2p_new();
+ if ( mpsession == NULL ) {
+ debug_print("MP: Not enough memory to do picture request - giving up.\n");
+ return;
+ }
+
+ mpsession->parent = session;
+ mpsession->baseidentifier = msnp2p_cookie();
+ mpsession->session_id = msnp2p_cookie();
+ mpsession->direction = MSNP2P_DIR_RECV;
+ mpsession->id_increment = -3;
+ mpsession->call_id = routines_guid();
+ mpsession->via = NULL;
+
+ debug_print("MP: Generated: Session ID '%i', BaseIdentifier '%i', Call-ID '%s'.\n", mpsession->session_id, mpsession->baseidentifier, mpsession->call_id);
+
+ /* urldecode() the MSNObject and then base64 it to get the "Context" field,
+ then retrieve the "SHA1D" field from the (urldecoded) MSNObject to generate
+ the filename to save to. */
+ dpobject_decoded = routines_urldecode(dpobject);
+ context = routines_base64(dpobject_decoded);
+ data_hash = xml_getfield(dpobject_decoded, "SHA1D");
+ debug_print("MP: Got data hash: %s\n", data_hash);
+ free(dpobject_decoded);
+ filename = xml_killillegalchars(data_hash);
+ mpsession->sha1d = data_hash; /* Don't free this yet. */
+
+ /* Determine the full filename to save to. */
+ glob("~", GLOB_TILDE, NULL, &glob_result);
+ mpsession->save_filename = malloc(strlen(filename)+strlen("/.tuxmessenger/avatars/")+strlen(glob_result.gl_pathv[0]) + 1);
+ if ( mpsession->save_filename == NULL ) {
+
+ debug_print("MP: Not enough memory to do picture request - giving up.\n");
+ free(mpsession->call_id);
+ free(mpsession);
+ return;
+
+ }
+ strcpy(mpsession->save_filename, glob_result.gl_pathv[0]);
+ strcat(mpsession->save_filename, "/.tuxmessenger/avatars/");
+ strcat(mpsession->save_filename, filename);
+ globfree(&glob_result);
+ free(filename);
+
+ /* Check to see there isn't already a receive session going on for this user. */
+
+ if ( msnp2p_retrieving(mpsession->save_filename) != 0 ) {
+
+ debug_print("MP: A DP receive session is already in progress to '%s' - not starting a new one.\n", mpsession->save_filename);
+ free(mpsession->call_id);
+ free(mpsession);
+ return;
+
+ }
+
+ /* Check if we already have this picture or not */
+ statbuf = &stat_buffer;
+ if ( stat(mpsession->save_filename, statbuf) != -1 ) {
+
+ /* messagewindow_trypicture should have worked this out before. */
+ debug_print("MP: Already have this picture: %s\n", mpsession->save_filename);
+ free(mpsession->save_filename);
+ free(mpsession->call_id);
+ free(mpsession);
+ return;
+
+ }
+
+ /* Silly preliminaries out of the way. Time to do the request... */
+
+ /* Create the payload for the MSNSLP request */
+ request_details = malloc( 96 + strlen(context) + 9 );
+ if ( request_details == NULL ) {
+
+ debug_print("MP: Not enough memory to do picture request - giving up.\n");
+ free(mpsession->save_filename);
+ free(mpsession->call_id);
+ free(mpsession);
+ return;
+
+ }
+ strcpy(request_details, "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\nSessionID: ");
+ /* Check we're not about to screw ourselves over. 8 bytes were allowed
+ above to fit the sessionid into. */
+ if ( mpsession->session_id > 99999999 ) {
+
+ debug_print("MP: SessionID is too big - giving up.\n");
+ free(request_details);
+ free(mpsession->save_filename);
+ free(mpsession->call_id);
+ free(mpsession);
+ return;
+
+ }
+ sprintf(request_details+strlen(request_details), "%i", mpsession->session_id);
+ strcat(request_details, "\r\nAppID: 1\r\nContext: ");
+ strcat(request_details, context);
+ free(context);
+ strcat(request_details, "\r\n\r\n");
+ request_details_length = strlen(request_details);
+
+ request_details_length_string = malloc(5);
+ assert(request_details_length < 9998); /* Sanity check. */
+ sprintf(request_details_length_string, "%i", request_details_length+1);
+
+ /* Now create the MSNSLP request itself */
+ msnslp_block = malloc( 291 + (2*strlen(username)) + strlen(options_username()) + strlen(request_details) + strlen(request_details_length_string) );
+ strcpy(msnslp_block, "INVITE MSNMSGR:");
+ strncat(msnslp_block, username, 64);
+ strcat(msnslp_block, " MSNSLP/1.0\r\nTo: <msnmsgr:");
+ strncat(msnslp_block, username, 64);
+ strcat(msnslp_block, ">\r\nFrom: <msnmsgr:");
+ strncat(msnslp_block, options_username(), 64);
+ strcat(msnslp_block, ">\r\nVia: MSNSLP/1.0/TLP ;branch={33517CE4-02FC-4428-B6F4-39927229B722}\r\nCSeq: 0 \r\nCall-ID: ");
+ strncat(msnslp_block, mpsession->call_id, 38);
+ strcat(msnslp_block, "\r\nMax-Forwards: 0\r\nContent-Type: application/x-msnmsgr-sessionreqbody\r\nContent-Length: ");
+ strncat(msnslp_block, request_details_length_string, 30);
+ free(request_details_length_string);
+ strcat(msnslp_block, "\r\n\r\n");
+ strcat(msnslp_block, request_details);
+ free(request_details);
+ msnslp_length = strlen(msnslp_block);
+
+ debug_print("MP: Sending MSNSLP INVITE for display picture.\n");
+
+ /* Now wrap it up with the MSNP2P rubbish */
+ mpheader.session_id = 0;
+ mpheader.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ msnp2p_inc(mpsession);
+ mpheader.offset = 0;
+ mpheader.total_size = GINT64_TO_LE(msnslp_length+1);
+ mpheader.size = GINT64_TO_LE(msnslp_length+1);
+ mpheader.flags = 0;
+ mpheader.ack_sess = GINT32_TO_LE(mpsession->session_id);
+ mpheader.ack_id = 0;
+ mpheader.ack_size = 0;
+
+ msnp2p_send(session, mpheader, username, msnslp_block, strlen(msnslp_block)+1, 0);
+
+ free(msnslp_block);
+
+ mpsession->username = strdup(username);
+ msnp2p_list = g_list_append(msnp2p_list, mpsession);
+
+}
+
+/* Bin all the MSNP2P/MSNSLP sessions in a given switchboard session. */
+void msnp2p_abortall(SbSession *session) {
+
+ GList *result;
+
+ result = g_list_find_custom(msnp2p_list, &session, (GCompareFunc)msnp2p_compare_parent);
+
+ while ( result != NULL ) {
+
+ MpSession *mpsession;
+ mpsession = result->data;
+ msnp2p_closesession(mpsession);
+
+ result = g_list_find_custom(msnp2p_list, &session, (GCompareFunc)msnp2p_compare_parent);
+
+ }
+
+}
+
+void msnp2p_offerfile(SbSession *session, const char *username, const char *filename) {
+
+ MpSession *mpsession;
+ MpHeader mpheader;
+ char *context;
+ char *request_details;
+ size_t request_details_length;
+ char *request_details_length_string;
+ char *msnslp_block;
+ size_t msnslp_length;
+
+ assert(session != NULL);
+ assert(username != NULL);
+ assert(filename != NULL);
+
+ mpsession = msnp2p_new();
+ if ( mpsession == NULL ) {
+ debug_print("MP: Not enough memory to send file - giving up.\n");
+ return;
+ }
+
+ mpsession->parent = session;
+ mpsession->baseidentifier = msnp2p_cookie();
+ mpsession->session_id = msnp2p_cookie();
+ mpsession->direction = MSNP2P_DIR_SEND;
+ mpsession->id_increment = 0;
+ mpsession->call_id = routines_guid();
+ mpsession->via = NULL;
+
+ context = strdup("WIBBLE!!!112"); /* File preview data... */
+
+ debug_print("MP: Generated: Session ID '%i', BaseIdentifier '%i', Call-ID '%s'.\n", mpsession->session_id, mpsession->baseidentifier, mpsession->call_id);
+
+ /* Create the payload for the MSNSLP request */
+ request_details = malloc( 96 + strlen(context) + 9 );
+ if ( request_details == NULL ) {
+
+ debug_print("MP: Not enough memory to do picture request - giving up.\n");
+ free(mpsession->save_filename);
+ free(mpsession->call_id);
+ free(mpsession);
+ return;
+
+ }
+ strcpy(request_details, "EUF-GUID: {5D3E02AB-6190-11D3-BBBB-00C04F795683}\r\nSessionID: ");
+ /* Check we're not about to screw ourselves over. 8 bytes were allowed
+ above to fit the sessionid into. */
+ if ( mpsession->session_id > 99999999 ) {
+
+ debug_print("MP: SessionID is too big - giving up.\n");
+ free(request_details);
+ free(mpsession->call_id);
+ free(mpsession);
+ return;
+
+ }
+ sprintf(request_details+strlen(request_details), "%i", mpsession->session_id);
+ strcat(request_details, "\r\nAppID: 1\r\nContext: ");
+ strcat(request_details, context);
+ free(context);
+ strcat(request_details, "\r\n\r\n");
+ request_details_length = strlen(request_details);
+
+ request_details_length_string = malloc(5);
+ assert(request_details_length < 9998); /* Sanity check. */
+ sprintf(request_details_length_string, "%i", request_details_length+1);
+
+ /* Now create the MSNSLP request itself */
+ msnslp_block = malloc( 291 + (2*strlen(username)) + strlen(options_username()) + strlen(request_details) + strlen(request_details_length_string) );
+ strcpy(msnslp_block, "INVITE MSNMSGR:");
+ strncat(msnslp_block, username, 64);
+ strcat(msnslp_block, " MSNSLP/1.0\r\nTo: <msnmsgr:");
+ strncat(msnslp_block, username, 64);
+ strcat(msnslp_block, ">\r\nFrom: <msnmsgr:");
+ strncat(msnslp_block, options_username(), 64);
+ strcat(msnslp_block, ">\r\nVia: MSNSLP/1.0/TLP ;branch={33517CE4-02FC-4428-B6F4-39927229B722}\r\nCSeq: 0 \r\nCall-ID: ");
+ strncat(msnslp_block, mpsession->call_id, 38);
+ strcat(msnslp_block, "\r\nMax-Forwards: 0\r\nContent-Type: application/x-msnmsgr-sessionreqbody\r\nContent-Length: ");
+ strncat(msnslp_block, request_details_length_string, 30);
+ free(request_details_length_string);
+ strcat(msnslp_block, "\r\n\r\n");
+ strcat(msnslp_block, request_details);
+ free(request_details);
+ msnslp_length = strlen(msnslp_block);
+
+ debug_print("MP: Sending MSNSLP INVITE for file transfer...\n");
+
+ /* Now wrap it up with the MSNP2P rubbish */
+ mpheader.session_id = 0;
+ mpheader.identifier = GINT32_TO_LE(mpsession->baseidentifier + mpsession->id_increment);
+ msnp2p_inc(mpsession);
+ mpheader.offset = 0;
+ mpheader.total_size = GINT64_TO_LE(msnslp_length+1);
+ mpheader.size = GINT64_TO_LE(msnslp_length+1);
+ mpheader.flags = 0;
+ mpheader.ack_sess = GINT32_TO_LE(mpsession->session_id);
+ mpheader.ack_id = 0;
+ mpheader.ack_size = 0;
+
+ msnp2p_send(session, mpheader, username, msnslp_block, strlen(msnslp_block)+1, 0);
+
+ free(msnslp_block);
+
+ mpsession->username = strdup(username);
+ msnp2p_list = g_list_append(msnp2p_list, mpsession);
+
+}