summaryrefslogtreecommitdiff
path: root/libpurple/protocols/msn/slp.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpurple/protocols/msn/slp.c')
-rw-r--r--libpurple/protocols/msn/slp.c1435
1 files changed, 166 insertions, 1269 deletions
diff --git a/libpurple/protocols/msn/slp.c b/libpurple/protocols/msn/slp.c
index 710a0c7265..159613c3f8 100644
--- a/libpurple/protocols/msn/slp.c
+++ b/libpurple/protocols/msn/slp.c
@@ -21,7 +21,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+
#include "slp.h"
#include "slpcall.h"
#include "slpmsg.h"
@@ -29,16 +32,14 @@
#include "object.h"
#include "user.h"
-#include "switchboard.h"
+#include "sbconn.h"
#include "directconn.h"
-
-#include "smiley.h"
+#include "p2p.h"
+#include "xfer.h"
/* seconds to delay between sending buddy icon requests to the server. */
#define BUDDY_ICON_DELAY 20
-static void request_user_display(MsnUser *user);
-
typedef struct {
MsnSession *session;
const char *remote_user;
@@ -46,167 +47,6 @@ typedef struct {
} MsnFetchUserDisplayData;
/**************************************************************************
- * Util
- **************************************************************************/
-
-static char *
-get_token(const char *str, const char *start, const char *end)
-{
- const char *c, *c2;
-
- if ((c = strstr(str, start)) == NULL)
- return NULL;
-
- c += strlen(start);
-
- if (end != NULL)
- {
- if ((c2 = strstr(c, end)) == NULL)
- return NULL;
-
- return g_strndup(c, c2 - c);
- }
- else
- {
- /* This has to be changed */
- return g_strdup(c);
- }
-
-}
-
-/**************************************************************************
- * Xfer
- **************************************************************************/
-
-static void
-msn_xfer_init(PurpleXfer *xfer)
-{
- MsnSlpCall *slpcall;
- /* MsnSlpLink *slplink; */
- char *content;
-
- purple_debug_info("msn", "xfer_init\n");
-
- slpcall = xfer->data;
-
- /* Send Ok */
- content = g_strdup_printf("SessionID: %lu\r\n\r\n",
- slpcall->session_id);
-
- msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
- content);
-
- g_free(content);
- msn_slplink_send_queued_slpmsgs(slpcall->slplink);
-}
-
-void
-msn_xfer_cancel(PurpleXfer *xfer)
-{
- MsnSlpCall *slpcall;
- char *content;
-
- g_return_if_fail(xfer != NULL);
- g_return_if_fail(xfer->data != NULL);
-
- slpcall = xfer->data;
-
- if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
- {
- if (slpcall->started)
- {
- msn_slpcall_close(slpcall);
- }
- else
- {
- content = g_strdup_printf("SessionID: %lu\r\n\r\n",
- slpcall->session_id);
-
- msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
- content);
-
- g_free(content);
- msn_slplink_send_queued_slpmsgs(slpcall->slplink);
-
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
- slpcall->wasted = TRUE;
- else
- msn_slpcall_destroy(slpcall);
- }
- }
-}
-
-gssize
-msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
-{
- MsnSlpCall *slpcall;
-
- g_return_val_if_fail(xfer != NULL, -1);
- g_return_val_if_fail(data != NULL, -1);
- g_return_val_if_fail(len > 0, -1);
-
- g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1);
-
- slpcall = xfer->data;
- /* Not sure I trust it'll be there */
- g_return_val_if_fail(slpcall != NULL, -1);
-
- g_return_val_if_fail(slpcall->xfer_msg != NULL, -1);
-
- slpcall->u.outgoing.len = len;
- slpcall->u.outgoing.data = data;
- msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg);
- msn_message_unref(slpcall->xfer_msg->msg);
- return MIN(1202, len);
-}
-
-gssize
-msn_xfer_read(guchar **data, PurpleXfer *xfer)
-{
- MsnSlpCall *slpcall;
- gsize len;
-
- g_return_val_if_fail(xfer != NULL, -1);
- g_return_val_if_fail(data != NULL, -1);
-
- g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1);
-
- slpcall = xfer->data;
- /* Not sure I trust it'll be there */
- g_return_val_if_fail(slpcall != NULL, -1);
-
- /* Just pass up the whole GByteArray. We'll make another. */
- *data = slpcall->u.incoming_data->data;
- len = slpcall->u.incoming_data->len;
-
- g_byte_array_free(slpcall->u.incoming_data, FALSE);
- slpcall->u.incoming_data = g_byte_array_new();
-
- return len;
-}
-
-void
-msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
-{
- if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) &&
- (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) &&
- (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL))
- {
- purple_xfer_cancel_remote(slpcall->xfer);
- }
-}
-
-void
-msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
- gsize size)
-{
- PurpleXfer *xfer = slpcall->xfer;
-
- purple_xfer_set_completed(xfer, TRUE);
- purple_xfer_end(xfer);
-}
-
-/**************************************************************************
* SLP Control
**************************************************************************/
@@ -250,1042 +90,10 @@ msn_slp_send_decline(MsnSlpCall *slpcall, const char *branch,
msn_slplink_queue_slpmsg(slplink, slpmsg);
}
-/* XXX: this could be improved if we tracked custom smileys
- * per-protocol, per-account, per-session or (ideally) per-conversation
- */
-static PurpleStoredImage *
-find_valid_emoticon(PurpleAccount *account, const char *path)
-{
- GList *smileys;
-
- if (!purple_account_get_bool(account, "custom_smileys", TRUE))
- return NULL;
-
- smileys = purple_smileys_get_all();
-
- for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
- PurpleSmiley *smiley;
- PurpleStoredImage *img;
-
- smiley = smileys->data;
- img = purple_smiley_get_stored_image(smiley);
-
- if (purple_strequal(path, purple_imgstore_get_filename(img))) {
- g_list_free(smileys);
- return img;
- }
-
- purple_imgstore_unref(img);
- }
-
- purple_debug_error("msn", "Received illegal request for file %s\n", path);
- return NULL;
-}
-
-static char *
-parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype)
-{
- char *nonce;
-
- *ntype = DC_NONCE_UNKNOWN;
-
- nonce = get_token(content, "Hashed-Nonce: {", "}\r\n");
- if (nonce) {
- *ntype = DC_NONCE_SHA1;
- } else {
- guint32 n1, n6;
- guint16 n2, n3, n4, n5;
- nonce = get_token(content, "Nonce: {", "}\r\n");
- if (nonce
- && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x",
- &n1, &n2, &n3, &n4, &n5, &n6) == 6) {
- *ntype = DC_NONCE_PLAIN;
- g_free(nonce);
- nonce = g_malloc(16);
- *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1);
- *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2);
- *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3);
- *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4);
- *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5);
- *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6);
- } else {
- /* Invalid nonce, so ignore request */
- g_free(nonce);
- nonce = NULL;
- }
- }
-
- return nonce;
-}
-
-static void
-msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content)
-{
- /* A direct connection negotiation response */
- char *bridge;
- char *nonce;
- char *listening;
- MsnDirectConn *dc = slpcall->slplink->dc;
- MsnDirectConnNonceType ntype;
-
- purple_debug_info("msn", "process_transresp\n");
-
- /* Direct connections are disabled. */
- if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE))
- return;
-
- g_return_if_fail(dc != NULL);
- g_return_if_fail(dc->state == DC_STATE_CLOSED);
-
- bridge = get_token(content, "Bridge: ", "\r\n");
- nonce = parse_dc_nonce(content, &ntype);
- listening = get_token(content, "Listening: ", "\r\n");
- if (listening && bridge && !strcmp(bridge, "TCPv1")) {
- /* Ok, the client supports direct TCP connection */
-
- /* We always need this. */
- if (ntype == DC_NONCE_SHA1) {
- strncpy(dc->remote_nonce, nonce, 36);
- dc->remote_nonce[36] = '\0';
- }
-
- if (!strcasecmp(listening, "false")) {
- if (dc->listen_data != NULL) {
- /*
- * We'll listen for incoming connections but
- * the listening socket isn't ready yet so we cannot
- * send the INVITE packet now. Put the slpcall into waiting mode
- * and let the callback send the invite.
- */
- slpcall->wait_for_socket = TRUE;
-
- } else if (dc->listenfd != -1) {
- /* The listening socket is ready. Send the INVITE here. */
- msn_dc_send_invite(dc);
-
- } else {
- /* We weren't able to create a listener either. Use SB. */
- msn_dc_fallback_to_sb(dc);
- }
-
- } else {
- /*
- * We should connect to the client so parse
- * IP/port from response.
- */
- char *ip, *port_str;
- int port = 0;
-
- if (ntype == DC_NONCE_PLAIN) {
- /* Only needed for listening side. */
- memcpy(dc->nonce, nonce, 16);
- }
-
- /* Cancel any listen attempts because we don't need them. */
- if (dc->listenfd_handle != 0) {
- purple_input_remove(dc->listenfd_handle);
- dc->listenfd_handle = 0;
- }
- if (dc->connect_timeout_handle != 0) {
- purple_timeout_remove(dc->connect_timeout_handle);
- dc->connect_timeout_handle = 0;
- }
- if (dc->listenfd != -1) {
- purple_network_remove_port_mapping(dc->listenfd);
- close(dc->listenfd);
- dc->listenfd = -1;
- }
- if (dc->listen_data != NULL) {
- purple_network_listen_cancel(dc->listen_data);
- dc->listen_data = NULL;
- }
-
- /* Save external IP/port for later use. We'll try local connection first. */
- dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n");
- port_str = get_token(content, "IPv4External-Port: ", "\r\n");
- if (port_str) {
- dc->ext_port = atoi(port_str);
- g_free(port_str);
- }
-
- ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
- port_str = get_token(content, "IPv4Internal-Port: ", "\r\n");
- if (port_str) {
- port = atoi(port_str);
- g_free(port_str);
- }
-
- if (ip && port) {
- /* Try internal address first */
- dc->connect_data = purple_proxy_connect(
- NULL,
- slpcall->slplink->session->account,
- ip,
- port,
- msn_dc_connected_to_peer_cb,
- dc
- );
-
- if (dc->connect_data) {
- /* Add connect timeout handle */
- dc->connect_timeout_handle = purple_timeout_add_seconds(
- DC_OUTGOING_TIMEOUT,
- msn_dc_outgoing_connection_timeout_cb,
- dc
- );
- } else {
- /*
- * Connection failed
- * Try external IP/port (if specified)
- */
- msn_dc_outgoing_connection_timeout_cb(dc);
- }
-
- } else {
- /*
- * Omitted or invalid internal IP address / port
- * Try external IP/port (if specified)
- */
- msn_dc_outgoing_connection_timeout_cb(dc);
- }
-
- g_free(ip);
- }
-
- } else {
- /*
- * Invalid direct connect invitation or
- * TCP connection is not supported
- */
- }
-
- g_free(listening);
- g_free(nonce);
- g_free(bridge);
-
- return;
-}
-
-static void
-got_sessionreq(MsnSlpCall *slpcall, const char *branch,
- const char *euf_guid, const char *context)
-{
- gboolean accepted = FALSE;
-
- if (!strcmp(euf_guid, MSN_OBJ_GUID))
- {
- /* Emoticon or UserDisplay */
- char *content;
- gsize len;
- MsnSlpLink *slplink;
- MsnSlpMessage *slpmsg;
- MsnObject *obj;
- char *msnobj_data;
- PurpleStoredImage *img = NULL;
- int type;
-
- /* Send Ok */
- content = g_strdup_printf("SessionID: %lu\r\n\r\n",
- slpcall->session_id);
-
- msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
- content);
-
- g_free(content);
-
- slplink = slpcall->slplink;
-
- msnobj_data = (char *)purple_base64_decode(context, &len);
- obj = msn_object_new_from_string(msnobj_data);
- type = msn_object_get_type(obj);
- g_free(msnobj_data);
- if (type == MSN_OBJECT_EMOTICON) {
- img = find_valid_emoticon(slplink->session->account, obj->location);
- } else if (type == MSN_OBJECT_USERTILE) {
- img = msn_object_get_image(obj);
- if (img)
- purple_imgstore_ref(img);
- }
- msn_object_destroy(obj);
-
- if (img != NULL) {
- /* DATA PREP */
- slpmsg = msn_slpmsg_new(slplink);
- slpmsg->slpcall = slpcall;
- slpmsg->session_id = slpcall->session_id;
- msn_slpmsg_set_body(slpmsg, NULL, 4);
- slpmsg->info = "SLP DATA PREP";
- msn_slplink_queue_slpmsg(slplink, slpmsg);
-
- /* DATA */
- slpmsg = msn_slpmsg_new(slplink);
- slpmsg->slpcall = slpcall;
- slpmsg->flags = 0x20;
- slpmsg->info = "SLP DATA";
- msn_slpmsg_set_image(slpmsg, img);
- msn_slplink_queue_slpmsg(slplink, slpmsg);
- purple_imgstore_unref(img);
-
- accepted = TRUE;
-
- } else {
- purple_debug_error("msn", "Wrong object.\n");
- }
- }
-
- else if (!strcmp(euf_guid, MSN_FT_GUID))
- {
- /* File Transfer */
- PurpleAccount *account;
- PurpleXfer *xfer;
- MsnFileContext *header;
- gsize bin_len;
- guint32 file_size;
- char *file_name;
-
- account = slpcall->slplink->session->account;
-
- slpcall->end_cb = msn_xfer_end_cb;
- slpcall->branch = g_strdup(branch);
-
- slpcall->pending = TRUE;
-
- xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
- slpcall->slplink->remote_user);
-
- header = (MsnFileContext *)purple_base64_decode(context, &bin_len);
- if (bin_len >= sizeof(MsnFileContext) - 1 &&
- (header->version == 2 ||
- (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) {
- file_size = GUINT64_FROM_LE(header->file_size);
-
- file_name = g_convert((const gchar *)&header->file_name,
- MAX_FILE_NAME_LEN * 2,
- "UTF-8", "UTF-16LE",
- NULL, NULL, NULL);
-
- purple_xfer_set_filename(xfer, file_name ? file_name : "");
- g_free(file_name);
- purple_xfer_set_size(xfer, file_size);
- purple_xfer_set_init_fnc(xfer, msn_xfer_init);
- purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
- purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
- purple_xfer_set_read_fnc(xfer, msn_xfer_read);
- purple_xfer_set_write_fnc(xfer, msn_xfer_write);
-
- slpcall->u.incoming_data = g_byte_array_new();
-
- slpcall->xfer = xfer;
- purple_xfer_ref(slpcall->xfer);
-
- xfer->data = slpcall;
-
- if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) {
- purple_xfer_set_thumbnail(xfer, &header->preview,
- bin_len - sizeof(MsnFileContext),
- "image/png");
- }
-
- purple_xfer_request(xfer);
- }
- g_free(header);
-
- accepted = TRUE;
-
- } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) {
- purple_debug_info("msn", "Cam request.\n");
- if (slpcall && slpcall->slplink &&
- slpcall->slplink->session) {
- PurpleConversation *conv;
- gchar *from = slpcall->slplink->remote_user;
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, from,
- slpcall->slplink->session->account);
- if (conv) {
- char *buf;
- buf = g_strdup_printf(
- _("%s requests to view your "
- "webcam, but this request is "
- "not yet supported."), from);
- purple_conversation_write(conv, NULL, buf,
- PURPLE_MESSAGE_SYSTEM |
- PURPLE_MESSAGE_NOTIFY,
- time(NULL));
- g_free(buf);
- }
- }
-
- } else if (!strcmp(euf_guid, MSN_CAM_GUID)) {
- purple_debug_info("msn", "Cam invite.\n");
- if (slpcall && slpcall->slplink &&
- slpcall->slplink->session) {
- PurpleConversation *conv;
- gchar *from = slpcall->slplink->remote_user;
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, from,
- slpcall->slplink->session->account);
- if (conv) {
- char *buf;
- buf = g_strdup_printf(
- _("%s invited you to view his/her webcam, but "
- "this is not yet supported."), from);
- purple_conversation_write(conv, NULL, buf,
- PURPLE_MESSAGE_SYSTEM |
- PURPLE_MESSAGE_NOTIFY,
- time(NULL));
- g_free(buf);
- }
- }
-
- } else
- purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid);
-
- if (!accepted) {
- char *content = g_strdup_printf("SessionID: %lu\r\n\r\n",
- slpcall->session_id);
- msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content);
- g_free(content);
- }
-}
-
-void
-send_bye(MsnSlpCall *slpcall, const char *type)
-{
- MsnSlpLink *slplink;
- PurpleAccount *account;
- MsnSlpMessage *slpmsg;
- char *header;
-
- slplink = slpcall->slplink;
-
- g_return_if_fail(slplink != NULL);
-
- account = slplink->session->account;
-
- header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0",
- purple_account_get_username(account));
-
- slpmsg = msn_slpmsg_sip_new(slpcall, 0, header,
- "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32",
- type,
- "\r\n");
- g_free(header);
-
- slpmsg->info = "SLP BYE";
- slpmsg->text_body = TRUE;
-
- msn_slplink_queue_slpmsg(slplink, slpmsg);
-}
-
-static void
-got_invite(MsnSlpCall *slpcall,
- const char *branch, const char *type, const char *content)
-{
- MsnSlpLink *slplink;
-
- slplink = slpcall->slplink;
-
- if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
- {
- char *euf_guid, *context;
- char *temp;
-
- euf_guid = get_token(content, "EUF-GUID: {", "}\r\n");
-
- temp = get_token(content, "SessionID: ", "\r\n");
- if (temp != NULL)
- slpcall->session_id = atoi(temp);
- g_free(temp);
-
- temp = get_token(content, "AppID: ", "\r\n");
- if (temp != NULL)
- slpcall->app_id = atoi(temp);
- g_free(temp);
-
- context = get_token(content, "Context: ", "\r\n");
-
- if (context != NULL)
- got_sessionreq(slpcall, branch, euf_guid, context);
-
- g_free(context);
- g_free(euf_guid);
- }
- else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
- {
- /* A direct connection negotiation request */
- char *bridges;
- char *nonce;
- MsnDirectConnNonceType ntype;
-
- purple_debug_info("msn", "got_invite: transreqbody received\n");
-
- /* Direct connections may be disabled. */
- if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) {
- msn_slp_send_ok(slpcall, branch,
- "application/x-msnmsgr-transrespbody",
- "Bridge: TCPv1\r\n"
- "Listening: false\r\n"
- "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
- "\r\n");
- msn_slpcall_session_init(slpcall);
-
- return;
- }
-
- /* Don't do anything if we already have a direct connection */
- if (slplink->dc != NULL)
- return;
-
- bridges = get_token(content, "Bridges: ", "\r\n");
- nonce = parse_dc_nonce(content, &ntype);
- if (bridges && strstr(bridges, "TCPv1") != NULL) {
- /*
- * Ok, the client supports direct TCP connection
- * Try to create a listening port
- */
- MsnDirectConn *dc;
-
- dc = msn_dc_new(slpcall);
- if (ntype == DC_NONCE_PLAIN) {
- /* There is only one nonce for plain auth. */
- dc->nonce_type = ntype;
- memcpy(dc->nonce, nonce, 16);
- } else if (ntype == DC_NONCE_SHA1) {
- /* Each side has a nonce in SHA1 auth. */
- dc->nonce_type = ntype;
- strncpy(dc->remote_nonce, nonce, 36);
- dc->remote_nonce[36] = '\0';
- }
-
- dc->listen_data = purple_network_listen_range(
- 0, 0,
- SOCK_STREAM,
- msn_dc_listen_socket_created_cb,
- dc
- );
-
- if (dc->listen_data == NULL) {
- /* Listen socket creation failed */
-
- purple_debug_info("msn", "got_invite: listening failed\n");
-
- if (dc->nonce_type != DC_NONCE_PLAIN)
- msn_slp_send_ok(slpcall, branch,
- "application/x-msnmsgr-transrespbody",
- "Bridge: TCPv1\r\n"
- "Listening: false\r\n"
- "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
- "\r\n");
- else
- msn_slp_send_ok(slpcall, branch,
- "application/x-msnmsgr-transrespbody",
- "Bridge: TCPv1\r\n"
- "Listening: false\r\n"
- "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
- "\r\n");
-
- } else {
- /*
- * Listen socket created successfully.
- * Don't send anything here because we don't know the parameters
- * of the created socket yet. msn_dc_send_ok will be called from
- * the callback function: dc_listen_socket_created_cb
- */
- purple_debug_info("msn", "got_invite: listening socket created\n");
-
- dc->send_connection_info_msg_cb = msn_dc_send_ok;
- slpcall->wait_for_socket = TRUE;
- }
-
- } else {
- /*
- * Invalid direct connect invitation or
- * TCP connection is not supported.
- */
- }
-
- g_free(nonce);
- g_free(bridges);
- }
- else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
- {
- /* A direct connection negotiation response */
- msn_slp_process_transresp(slpcall, content);
- }
-}
-
-static void
-got_ok(MsnSlpCall *slpcall,
- const char *type, const char *content)
-{
- g_return_if_fail(slpcall != NULL);
- g_return_if_fail(type != NULL);
-
- if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
- {
- char *content;
- char *header;
- char *nonce = NULL;
- MsnSession *session = slpcall->slplink->session;
- MsnSlpMessage *msg;
- MsnDirectConn *dc;
- MsnUser *user;
-
- if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) {
- /* Don't attempt a direct connection if disabled. */
- msn_slpcall_session_init(slpcall);
- return;
- }
-
- if (slpcall->slplink->dc != NULL) {
- /* If we already have an established direct connection
- * then just start the transfer.
- */
- msn_slpcall_session_init(slpcall);
- return;
- }
-
- user = msn_userlist_find_user(session->userlist,
- slpcall->slplink->remote_user);
- if (!user || !(user->clientid & 0xF0000000)) {
- /* Just start a normal SB transfer. */
- msn_slpcall_session_init(slpcall);
- return;
- }
-
- /* Try direct file transfer by sending a second INVITE */
- dc = msn_dc_new(slpcall);
- slpcall->branch = rand_guid();
-
- dc->listen_data = purple_network_listen_range(
- 0, 0,
- SOCK_STREAM,
- msn_dc_listen_socket_created_cb,
- dc
- );
-
- header = g_strdup_printf(
- "INVITE MSNMSGR:%s MSNSLP/1.0",
- slpcall->slplink->remote_user
- );
-
- if (dc->nonce_type == DC_NONCE_SHA1)
- nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash);
-
- if (dc->listen_data == NULL) {
- /* Listen socket creation failed */
- purple_debug_info("msn", "got_ok: listening failed\n");
-
- content = g_strdup_printf(
- "Bridges: TCPv1\r\n"
- "NetID: %u\r\n"
- "Conn-Type: IP-Restrict-NAT\r\n"
- "UPnPNat: false\r\n"
- "ICF: false\r\n"
- "%s"
- "\r\n",
-
- rand() % G_MAXUINT32,
- nonce ? nonce : ""
- );
-
- } else {
- /* Listen socket created successfully. */
- purple_debug_info("msn", "got_ok: listening socket created\n");
-
- content = g_strdup_printf(
- "Bridges: TCPv1\r\n"
- "NetID: 0\r\n"
- "Conn-Type: Direct-Connect\r\n"
- "UPnPNat: false\r\n"
- "ICF: false\r\n"
- "%s"
- "\r\n",
-
- nonce ? nonce : ""
- );
- }
-
- msg = msn_slpmsg_sip_new(
- slpcall,
- 0,
- header,
- slpcall->branch,
- "application/x-msnmsgr-transreqbody",
- content
- );
- msg->info = "DC INVITE";
- msg->text_body = TRUE;
- g_free(nonce);
- g_free(header);
- g_free(content);
-
- msn_slplink_queue_slpmsg(slpcall->slplink, msg);
- }
- else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
- {
- /* Do we get this? */
- purple_debug_info("msn", "OK with transreqbody\n");
- }
- else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
- {
- msn_slp_process_transresp(slpcall, content);
- }
-}
-
-static void
-got_error(MsnSlpCall *slpcall,
- const char *error, const char *type, const char *content)
-{
- /* It's not valid. Kill this off. */
- purple_debug_error("msn", "Received non-OK result: %s\n",
- error ? error : "Unknown");
-
- if (type && (!strcmp(type, "application/x-msnmsgr-transreqbody")
- || !strcmp(type, "application/x-msnmsgr-transrespbody"))) {
- MsnDirectConn *dc = slpcall->slplink->dc;
- if (dc) {
- msn_dc_fallback_to_sb(dc);
- return;
- }
- }
-
- slpcall->wasted = TRUE;
-}
-
-MsnSlpCall *
-msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
-{
- MsnSlpCall *slpcall;
-
- if (body == NULL)
- {
- purple_debug_warning("msn", "received bogus message\n");
- return NULL;
- }
-
- if (!strncmp(body, "INVITE", strlen("INVITE")))
- {
- char *branch;
- char *call_id;
- char *content;
- char *content_type;
-
- /* From: <msnmsgr:buddy@hotmail.com> */
-#if 0
- slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n");
-#endif
-
- branch = get_token(body, ";branch={", "}");
-
- call_id = get_token(body, "Call-ID: {", "}");
-
-#if 0
- long content_len = -1;
-
- temp = get_token(body, "Content-Length: ", "\r\n");
- if (temp != NULL)
- content_len = atoi(temp);
- g_free(temp);
-#endif
- content_type = get_token(body, "Content-Type: ", "\r\n");
-
- content = get_token(body, "\r\n\r\n", NULL);
-
- slpcall = NULL;
- if (branch && call_id)
- {
- slpcall = msn_slplink_find_slp_call(slplink, call_id);
- if (slpcall)
- {
- g_free(slpcall->branch);
- slpcall->branch = g_strdup(branch);
- got_invite(slpcall, branch, content_type, content);
- }
- else if (content_type && content)
- {
- slpcall = msn_slpcall_new(slplink);
- slpcall->id = g_strdup(call_id);
- got_invite(slpcall, branch, content_type, content);
- }
- }
-
- g_free(call_id);
- g_free(branch);
- g_free(content_type);
- g_free(content);
- }
- else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 ")))
- {
- char *content;
- char *content_type;
- /* Make sure this is "OK" */
- const char *status = body + strlen("MSNSLP/1.0 ");
- char *call_id;
-
- call_id = get_token(body, "Call-ID: {", "}");
- slpcall = msn_slplink_find_slp_call(slplink, call_id);
- g_free(call_id);
-
- g_return_val_if_fail(slpcall != NULL, NULL);
-
- content_type = get_token(body, "Content-Type: ", "\r\n");
-
- content = get_token(body, "\r\n\r\n", NULL);
-
- if (strncmp(status, "200 OK", 6))
- {
- char *error = NULL;
- const char *c;
-
- /* Eww */
- if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
- (c = strchr(status, '\0')))
- {
- size_t len = c - status;
- error = g_strndup(status, len);
- }
-
- got_error(slpcall, error, content_type, content);
- g_free(error);
-
- } else {
- /* Everything's just dandy */
- got_ok(slpcall, content_type, content);
- }
-
- g_free(content_type);
- g_free(content);
- }
- else if (!strncmp(body, "BYE", strlen("BYE")))
- {
- char *call_id;
-
- call_id = get_token(body, "Call-ID: {", "}");
- slpcall = msn_slplink_find_slp_call(slplink, call_id);
- g_free(call_id);
-
- if (slpcall != NULL)
- slpcall->wasted = TRUE;
-
- /* msn_slpcall_destroy(slpcall); */
- }
- else
- slpcall = NULL;
-
- return slpcall;
-}
-
/**************************************************************************
* Msg Callbacks
**************************************************************************/
-void
-msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
- MsnSession *session;
- MsnSlpLink *slplink;
- const char *data;
- gsize len;
-
- session = cmdproc->servconn->session;
- slplink = msn_session_get_slplink(session, msg->remote_user);
-
- if (slplink->swboard == NULL)
- {
- /*
- * We will need swboard in order to change its flags. If its
- * NULL, something has probably gone wrong earlier on. I
- * didn't want to do this, but MSN 7 is somehow causing us
- * to crash here, I couldn't reproduce it to debug more,
- * and people are reporting bugs. Hopefully this doesn't
- * cause more crashes. Stu.
- */
- if (cmdproc->data == NULL)
- g_warning("msn_p2p_msg cmdproc->data was NULL\n");
- else {
- slplink->swboard = (MsnSwitchBoard *)cmdproc->data;
- slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
- }
- }
-
- data = msn_message_get_bin_data(msg, &len);
-
- msn_slplink_process_msg(slplink, &msg->msnslp_header, data, len);
-}
-
-static void
-got_emoticon(MsnSlpCall *slpcall,
- const guchar *data, gsize size)
-{
- PurpleConversation *conv;
- MsnSwitchBoard *swboard;
-
- swboard = slpcall->slplink->swboard;
- conv = swboard->conv;
-
- if (conv) {
- /* FIXME: it would be better if we wrote the data as we received it
- instead of all at once, calling write multiple times and
- close once at the very end
- */
- purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
- purple_conv_custom_smiley_close(conv, slpcall->data_info );
- }
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
-}
-
-void
-msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
- MsnSession *session;
- MsnSlpLink *slplink;
- MsnSwitchBoard *swboard;
- MsnObject *obj;
- char **tokens;
- char *smile, *body_str;
- const char *body, *who, *sha1;
- guint tok;
- size_t body_len;
-
- PurpleConversation *conv;
-
- session = cmdproc->servconn->session;
-
- if (!purple_account_get_bool(session->account, "custom_smileys", TRUE))
- return;
-
- swboard = cmdproc->data;
- conv = swboard->conv;
-
- body = msn_message_get_bin_data(msg, &body_len);
- if (!body || !body_len)
- return;
- body_str = g_strndup(body, body_len);
-
- /* MSN Messenger 7 may send more than one MSNObject in a single message...
- * Maybe 10 tokens is a reasonable max value. */
- tokens = g_strsplit(body_str, "\t", 10);
-
- g_free(body_str);
-
- for (tok = 0; tok < 9; tok += 2) {
- if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
- break;
- }
-
- smile = tokens[tok];
- obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1]));
-
- if (obj == NULL)
- break;
-
- who = msn_object_get_creator(obj);
- sha1 = msn_object_get_sha1(obj);
-
- slplink = msn_session_get_slplink(session, who);
- if (slplink->swboard != swboard) {
- if (slplink->swboard != NULL)
- /*
- * Apparently we're using a different switchboard now or
- * something? I don't know if this is normal, but it
- * definitely happens. So make sure the old switchboard
- * doesn't still have a reference to us.
- */
- slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
- slplink->swboard = swboard;
- slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
- }
-
- /* If the conversation doesn't exist then this is a custom smiley
- * used in the first message in a MSN conversation: we need to create
- * the conversation now, otherwise the custom smiley won't be shown.
- * This happens because every GtkIMHtml has its own smiley tree: if
- * the conversation doesn't exist then we cannot associate the new
- * smiley with its GtkIMHtml widget. */
- if (!conv) {
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who);
- }
-
- if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) {
- msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
- }
-
- msn_object_destroy(obj);
- obj = NULL;
- who = NULL;
- sha1 = NULL;
- }
- g_strfreev(tokens);
-}
-
-static gboolean
-buddy_icon_cached(PurpleConnection *gc, MsnObject *obj)
-{
- PurpleAccount *account;
- PurpleBuddy *buddy;
- const char *old;
- const char *new;
-
- g_return_val_if_fail(obj != NULL, FALSE);
-
- account = purple_connection_get_account(gc);
-
- buddy = purple_find_buddy(account, msn_object_get_creator(obj));
- if (buddy == NULL)
- return FALSE;
-
- old = purple_buddy_icons_get_checksum_for_user(buddy);
- new = msn_object_get_sha1(obj);
-
- if (new == NULL)
- return FALSE;
-
- /* If the old and new checksums are the same, and the file actually exists,
- * then return TRUE */
- if (old != NULL && !strcmp(old, new))
- return TRUE;
-
- return FALSE;
-}
-
-static void
-msn_release_buddy_icon_request(MsnUserList *userlist)
-{
- MsnUser *user;
-
- g_return_if_fail(userlist != NULL);
-
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Releasing buddy icon request\n");
-
- if (userlist->buddy_icon_window > 0)
- {
- GQueue *queue;
- PurpleAccount *account;
- const char *username;
-
- queue = userlist->buddy_icon_requests;
-
- if (g_queue_is_empty(userlist->buddy_icon_requests))
- return;
-
- user = g_queue_pop_head(queue);
-
- account = userlist->session->account;
- username = user->passport;
-
- userlist->buddy_icon_window--;
- request_user_display(user);
-
- if (purple_debug_is_verbose())
- purple_debug_info("msn",
- "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
- userlist->buddy_icon_window);
- }
-}
-
/*
* Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next
* buddy icon request if there is one.
@@ -1306,48 +114,10 @@ msn_release_buddy_icon_request_timeout(gpointer data)
return FALSE;
}
-void
-msn_queue_buddy_icon_request(MsnUser *user)
-{
- PurpleAccount *account;
- MsnObject *obj;
- GQueue *queue;
-
- g_return_if_fail(user != NULL);
-
- account = user->userlist->session->account;
-
- obj = msn_user_get_object(user);
-
- if (obj == NULL)
- {
- purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL);
- return;
- }
-
- if (!buddy_icon_cached(account->gc, obj))
- {
- MsnUserList *userlist;
-
- userlist = user->userlist;
- queue = userlist->buddy_icon_requests;
-
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
- user->passport, userlist->buddy_icon_window);
-
- g_queue_push_tail(queue, user);
-
- if (userlist->buddy_icon_window > 0)
- msn_release_buddy_icon_request(userlist);
- }
-}
-
static void
got_user_display(MsnSlpCall *slpcall,
const guchar *data, gsize size)
{
- MsnUserList *userlist;
const char *info;
PurpleAccount *account;
@@ -1357,21 +127,10 @@ got_user_display(MsnSlpCall *slpcall,
if (purple_debug_is_verbose())
purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
- userlist = slpcall->slplink->session->userlist;
account = slpcall->slplink->session->account;
purple_buddy_icons_set_for_user(account, slpcall->slplink->remote_user,
g_memdup(data, size), size, info);
-
-#if 0
- /* Free one window slot */
- userlist->buddy_icon_window++;
-
- purple_debug_info("msn", "got_user_display(): buddy_icon_window++ yields =%d\n",
- userlist->buddy_icon_window);
-
- msn_release_buddy_icon_request(userlist);
-#endif
}
static void
@@ -1431,7 +190,43 @@ fetched_user_display(PurpleUtilFetchUrlData *url_data, gpointer user_data,
}
static void
-request_user_display(MsnUser *user)
+request_own_user_display(MsnUser *user)
+{
+ PurpleAccount *account;
+ MsnSession *session;
+ MsnObject *my_obj = NULL;
+ gconstpointer data = NULL;
+ const char *info = NULL;
+ size_t len = 0;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "Requesting our own user display\n");
+
+ session = user->userlist->session;
+ account = session->account;
+ my_obj = msn_user_get_object(user);
+
+ if (my_obj != NULL) {
+ PurpleStoredImage *img = msn_object_get_image(my_obj);
+ data = purple_imgstore_get_data(img);
+ len = purple_imgstore_get_size(img);
+ info = msn_object_get_sha1(my_obj);
+ }
+
+ purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
+
+ /* Free one window slot */
+ session->userlist->buddy_icon_window++;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n",
+ session->userlist->buddy_icon_window);
+
+ msn_release_buddy_icon_request(session->userlist);
+}
+
+void
+msn_request_user_display(MsnUser *user)
{
PurpleAccount *account;
MsnSession *session;
@@ -1467,32 +262,134 @@ request_user_display(MsnUser *user)
}
}
else
- {
- MsnObject *my_obj = NULL;
- gconstpointer data = NULL;
- size_t len = 0;
+ request_own_user_display(user);
+}
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Requesting our own user display\n");
+static void
+send_file_cb(MsnSlpCall *slpcall)
+{
+ MsnSlpMessage *slpmsg;
+ PurpleXfer *xfer;
- my_obj = msn_user_get_object(session->user);
+ xfer = (PurpleXfer *)slpcall->xfer;
+ if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED)
+ return;
- if (my_obj != NULL)
- {
- PurpleStoredImage *img = msn_object_get_image(my_obj);
- data = purple_imgstore_get_data(img);
- len = purple_imgstore_get_size(img);
- }
+ purple_xfer_ref(xfer);
+ purple_xfer_start(xfer, -1, NULL, 0);
+ if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) {
+ purple_xfer_unref(xfer);
+ return;
+ }
+ purple_xfer_unref(xfer);
+
+ slpmsg = msn_slpmsg_file_new(slpcall, purple_xfer_get_size(xfer));
+ msn_slpmsg_set_slplink(slpmsg, slpcall->slplink);
+
+ msn_slplink_send_slpmsg(slpcall->slplink, slpmsg);
+}
+
+static gchar *
+gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
+{
+ gsize size = 0;
+ MsnFileContext *header;
+ gchar *u8 = NULL;
+ gchar *ret;
+ gunichar2 *uni = NULL;
+ glong currentChar = 0;
+ glong len = 0;
+ const char *preview;
+ gsize preview_len;
+
+ size = purple_xfer_get_size(xfer);
+
+ purple_xfer_prepare_thumbnail(xfer, "png");
+
+ if (!file_name) {
+ gchar *basename = g_path_get_basename(file_path);
+ u8 = purple_utf8_try_convert(basename);
+ g_free(basename);
+ file_name = u8;
+ }
+
+ uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL);
- purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
+ if (u8) {
+ g_free(u8);
+ file_name = NULL;
+ u8 = NULL;
+ }
+
+ preview = purple_xfer_get_thumbnail(xfer, &preview_len);
+ header = g_malloc(sizeof(MsnFileContext) + preview_len);
- /* Free one window slot */
- session->userlist->buddy_icon_window++;
+ header->length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1);
+ header->version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */
+ header->file_size = GUINT64_TO_LE(size);
+ if (preview)
+ header->type = GUINT32_TO_LE(0);
+ else
+ header->type = GUINT32_TO_LE(1);
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n",
- session->userlist->buddy_icon_window);
+ len = MIN(len, MAX_FILE_NAME_LEN);
+ for (currentChar = 0; currentChar < len; currentChar++) {
+ header->file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]);
+ }
+ memset(&header->file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
- msn_release_buddy_icon_request(session->userlist);
+ memset(&header->unknown1, 0, sizeof(header->unknown1));
+ header->unknown2 = GUINT32_TO_LE(0xffffffff);
+ if (preview) {
+ memcpy(&header->preview, preview, preview_len);
}
+ header->preview[preview_len] = '\0';
+
+ g_free(uni);
+ ret = purple_base64_encode((const guchar *)header, sizeof(MsnFileContext) + preview_len);
+ g_free(header);
+ return ret;
}
+
+void
+msn_request_ft(PurpleXfer *xfer)
+{
+ MsnSlpCall *slpcall;
+ MsnSlpLink *slplink;
+ char *context;
+ const char *fn;
+ const char *fp;
+
+ fn = purple_xfer_get_filename(xfer);
+ fp = purple_xfer_get_local_filename(xfer);
+
+ slplink = xfer->data;
+
+ g_return_if_fail(slplink != NULL);
+ g_return_if_fail(fp != NULL);
+
+ slpcall = msn_slpcall_new(slplink);
+ msn_slpcall_init(slpcall, MSN_SLPCALL_DC);
+
+ slpcall->session_init_cb = send_file_cb;
+ slpcall->end_cb = msn_xfer_end_cb;
+ slpcall->cb = msn_xfer_completed_cb;
+ slpcall->xfer = xfer;
+ purple_xfer_ref(slpcall->xfer);
+
+ slpcall->pending = TRUE;
+
+ purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel);
+ purple_xfer_set_read_fnc(xfer, msn_xfer_read);
+ purple_xfer_set_write_fnc(xfer, msn_xfer_write);
+
+ xfer->data = slpcall;
+
+ context = gen_context(xfer, fn, fp);
+
+ msn_slpcall_invite(slpcall, MSN_FT_GUID, P2P_APPID_FILE, context);
+ msn_slplink_unref(slplink);
+
+ g_free(context);
+}
+