Mercurial > pidgin
view libpurple/protocols/msn/slp.c @ 26517:3609fb4bc78e
Inform the user when someone tries to initiate a video call with them in msn.
author | Mike Ruprecht <maiku@soc.pidgin.im> |
---|---|
date | Wed, 08 Apr 2009 22:49:14 +0000 |
parents | 809de3ab85d1 |
children | 35b6fd563056 |
line wrap: on
line source
/** * @file msnslp.c MSNSLP support * * purple * * Purple 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "msn.h" #include "slp.h" #include "slpcall.h" #include "slpmsg.h" #include "object.h" #include "user.h" #include "switchboard.h" #include "smiley.h" /* ms to delay between sending buddy icon requests to the server. */ #define BUDDY_ICON_DELAY 20 /*debug SLP*/ #define MSN_DEBUG_UD static void send_ok(MsnSlpCall *slpcall, const char *branch, const char *type, const char *content); static void send_decline(MsnSlpCall *slpcall, const char *branch, const char *type, const char *content); static void request_user_display(MsnUser *user); /************************************************************************** * 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); 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); send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", content); g_free(content); msn_slplink_send_queued_slpmsgs(slpcall->slplink); msn_slpcall_destroy(slpcall); } } } void msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize len, gsize offset) { PurpleXfer *xfer; xfer = slpcall->xfer; xfer->bytes_sent = (offset + len); xfer->bytes_remaining = total_length - (offset + len); purple_xfer_update_progress(xfer); } 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 **************************************************************************/ #if 0 static void got_transresp(MsnSlpCall *slpcall, const char *nonce, const char *ips_str, int port) { MsnDirectConn *directconn; char **ip_addrs, **c; directconn = msn_directconn_new(slpcall->slplink); directconn->initial_call = slpcall; /* msn_directconn_parse_nonce(directconn, nonce); */ directconn->nonce = g_strdup(nonce); ip_addrs = g_strsplit(ips_str, " ", -1); for (c = ip_addrs; *c != NULL; c++) { purple_debug_info("msn", "ip_addr = %s\n", *c); if (msn_directconn_connect(directconn, *c, port)) break; } g_strfreev(ip_addrs); } #endif static void send_ok(MsnSlpCall *slpcall, const char *branch, const char *type, const char *content) { MsnSlpLink *slplink; MsnSlpMessage *slpmsg; slplink = slpcall->slplink; /* 200 OK */ slpmsg = msn_slpmsg_sip_new(slpcall, 1, "MSNSLP/1.0 200 OK", branch, type, content); #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP 200 OK"; slpmsg->text_body = TRUE; #endif msn_slplink_queue_slpmsg(slplink, slpmsg); msn_slpcall_session_init(slpcall); } static void send_decline(MsnSlpCall *slpcall, const char *branch, const char *type, const char *content) { MsnSlpLink *slplink; MsnSlpMessage *slpmsg; slplink = slpcall->slplink; /* 603 Decline */ slpmsg = msn_slpmsg_sip_new(slpcall, 1, "MSNSLP/1.0 603 Decline", branch, type, content); #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP 603 Decline"; slpmsg->text_body = TRUE; #endif msn_slplink_queue_slpmsg(slplink, slpmsg); } #define MAX_FILE_NAME_LEN 0x226 static void got_sessionreq(MsnSlpCall *slpcall, const char *branch, const char *euf_guid, const char *context) { 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; int type; /* Send Ok */ content = g_strdup_printf("SessionID: %lu\r\n\r\n", slpcall->session_id); 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_USERTILE) && (type != MSN_OBJECT_EMOTICON)) { purple_debug_error("msn", "Wrong object?\n"); msn_object_destroy(obj); g_return_if_reached(); } if (type == MSN_OBJECT_EMOTICON) { char *path; path = g_build_filename(purple_smileys_get_storing_dir(), obj->location, NULL); img = purple_imgstore_new_from_file(path); g_free(path); } else { img = msn_object_get_image(obj); if (img) purple_imgstore_ref(img); } msn_object_destroy(obj); if (img == NULL) { purple_debug_error("msn", "Wrong object.\n"); g_return_if_reached(); } /* DATA PREP */ slpmsg = msn_slpmsg_new(slplink); slpmsg->slpcall = slpcall; slpmsg->session_id = slpcall->session_id; msn_slpmsg_set_body(slpmsg, NULL, 4); #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP DATA PREP"; #endif msn_slplink_queue_slpmsg(slplink, slpmsg); /* DATA */ slpmsg = msn_slpmsg_new(slplink); slpmsg->slpcall = slpcall; slpmsg->flags = 0x20; #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP DATA"; #endif msn_slpmsg_set_image(slpmsg, img); msn_slplink_queue_slpmsg(slplink, slpmsg); purple_imgstore_unref(img); } else if (!strcmp(euf_guid, MSN_FT_GUID)) { /* File Transfer */ PurpleAccount *account; PurpleXfer *xfer; char *bin; gsize bin_len; guint32 file_size; char *file_name; gunichar2 *uni_name; account = slpcall->slplink->session->account; slpcall->cb = msn_xfer_completed_cb; slpcall->end_cb = msn_xfer_end_cb; slpcall->progress_cb = msn_xfer_progress_cb; slpcall->branch = g_strdup(branch); slpcall->pending = TRUE; xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, slpcall->slplink->remote_user); if (xfer) { bin = (char *)purple_base64_decode(context, &bin_len); file_size = GUINT32_FROM_LE(*(gsize *)(bin + 8)); uni_name = (gunichar2 *)(bin + 20); while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) { *uni_name = GUINT16_FROM_LE(*uni_name); uni_name++; } file_name = g_utf16_to_utf8((const gunichar2 *)(bin + 20), -1, NULL, NULL, NULL); g_free(bin); purple_xfer_set_filename(xfer, 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); slpcall->xfer = xfer; purple_xfer_ref(slpcall->xfer); xfer->data = slpcall; purple_xfer_request(xfer); } } 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 has sent you a webcam " "invite, which 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); } 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); #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP BYE"; slpmsg->text_body = TRUE; #endif 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? */ char *listening, *nonce; char *content; if (FALSE) { #if 0 MsnDirectConn *directconn; /* const char *ip_addr; */ char *ip_port; int port; /* ip_addr = purple_prefs_get_string("/purple/ft/public_ip"); */ ip_port = "5190"; listening = "true"; nonce = rand_guid(); directconn = msn_directconn_new(slplink); /* msn_directconn_parse_nonce(directconn, nonce); */ directconn->nonce = g_strdup(nonce); msn_directconn_listen(directconn); port = directconn->port; content = g_strdup_printf( "Bridge: TCPv1\r\n" "Listening: %s\r\n" "Nonce: {%s}\r\n" "Ipv4Internal-Addrs: 192.168.0.82\r\n" "Ipv4Internal-Port: %d\r\n" "\r\n", listening, nonce, port); #endif } else { listening = "false"; nonce = g_strdup("00000000-0000-0000-0000-000000000000"); content = g_strdup_printf( "Bridge: TCPv1\r\n" "Listening: %s\r\n" "Nonce: {%s}\r\n" "\r\n", listening, nonce); } send_ok(slpcall, branch, "application/x-msnmsgr-transrespbody", content); g_free(content); g_free(nonce); } else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) { #if 0 char *ip_addrs; char *temp; char *nonce; int port; nonce = get_token(content, "Nonce: {", "}\r\n"); if (ip_addrs == NULL) return; ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); temp = get_token(content, "IPv4Internal-Port: ", "\r\n"); if (temp != NULL) port = atoi(temp); else port = -1; g_free(temp); if (port > 0) got_transresp(slpcall, nonce, ip_addrs, port); g_free(nonce); g_free(ip_addrs); #endif } } 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")) { #if 0 if (slpcall->type == MSN_SLPCALL_DC) { /* First let's try a DirectConnection. */ MsnSlpLink *slplink; MsnSlpMessage *slpmsg; char *header; char *content; char *branch; slplink = slpcall->slplink; branch = rand_guid(); content = g_strdup_printf( "Bridges: TRUDPv1 TCPv1\r\n" "NetID: 0\r\n" "Conn-Type: Direct-Connect\r\n" "UPnPNat: false\r\n" "ICF: false\r\n" ); header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0", slplink->remote_user); slpmsg = msn_slp_sipmsg_new(slpcall, 0, header, branch, "application/x-msnmsgr-transreqbody", content); #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP INVITE"; slpmsg->text_body = TRUE; #endif msn_slplink_send_slpmsg(slplink, slpmsg); g_free(header); g_free(content); g_free(branch); } else { msn_slpcall_session_init(slpcall); } #else msn_slpcall_session_init(slpcall); #endif } 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")) { #if 0 char *ip_addrs; char *temp; char *nonce; int port; nonce = get_token(content, "Nonce: {", "}\r\n"); if (ip_addrs == NULL) return; ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); temp = get_token(content, "IPv4Internal-Port: ", "\r\n"); if (temp != NULL) port = atoi(temp); else port = -1; g_free(temp); if (port > 0) got_transresp(slpcall, nonce, ip_addrs, port); g_free(nonce); g_free(ip_addrs); #endif } } 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 *content; char *content_type; slpcall = msn_slpcall_new(slplink); /* From: <msnmsgr:buddy@hotmail.com> */ #if 0 slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n"); #endif branch = get_token(body, ";branch={", "}"); slpcall->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); got_invite(slpcall, branch, content_type, content); 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); if (strncmp(status, "200 OK", 6)) { /* It's not valid. Kill this off. */ char temp[32]; const char *c; /* Eww */ if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) || (c = strchr(status, '\0'))) { size_t offset = c - status; if (offset >= sizeof(temp)) offset = sizeof(temp) - 1; strncpy(temp, status, offset); temp[offset] = '\0'; } purple_debug_error("msn", "Received non-OK result: %s\n", temp); slpcall->wasted = TRUE; /* msn_slpcall_destroy(slpcall); */ return slpcall; } content_type = get_token(body, "Content-Type: ", "\r\n"); content = get_token(body, "\r\n\r\n", NULL); 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; 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); } } msn_slplink_process_msg(slplink, msg); } 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 ); } #ifdef MSN_DEBUG_UD purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info); #endif } 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); 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); #ifdef MSN_DEBUG_UD purple_debug_info("msn", "Releasing buddy icon request\n"); #endif 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); #ifdef MSN_DEBUG_UD purple_debug_info("msn", "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n", userlist->buddy_icon_window); #endif } } /* * 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. */ static gboolean msn_release_buddy_icon_request_timeout(gpointer data) { MsnUserList *userlist = (MsnUserList *)data; /* Free one window slot */ userlist->buddy_icon_window++; /* Clear the tag for our former request timer */ userlist->buddy_icon_request_timer = 0; msn_release_buddy_icon_request(userlist); 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; #ifdef MSN_DEBUG_UD purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n", user->passport, userlist->buddy_icon_window); #endif 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; g_return_if_fail(slpcall != NULL); info = slpcall->data_info; #ifdef MSN_DEBUG_UD purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user); #endif 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 end_user_display(MsnSlpCall *slpcall, MsnSession *session) { MsnUserList *userlist; g_return_if_fail(session != NULL); #ifdef MSN_DEBUG_UD purple_debug_info("msn", "End User Display\n"); #endif userlist = session->userlist; /* If the session is being destroyed we better stop doing anything. */ if (session->destroying) return; /* Delay before freeing a buddy icon window slot and requesting the next icon, if appropriate. * If we don't delay, we'll rapidly hit the MSN equivalent of AIM's rate limiting; the server will * send us an error 800 like so: * * C: NS 000: XFR 21 SB * S: NS 000: 800 21 */ if (userlist->buddy_icon_request_timer) { /* Free the window slot used by this previous request */ userlist->buddy_icon_window++; /* Clear our pending timeout */ purple_timeout_remove(userlist->buddy_icon_request_timer); } /* Wait BUDDY_ICON_DELAY s before freeing our window slot and requesting the next icon. */ userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY, msn_release_buddy_icon_request_timeout, userlist); } static void request_user_display(MsnUser *user) { PurpleAccount *account; MsnSession *session; MsnSlpLink *slplink; MsnObject *obj; const char *info; session = user->userlist->session; account = session->account; slplink = msn_session_get_slplink(session, user->passport); obj = msn_user_get_object(user); info = msn_object_get_sha1(obj); if (g_ascii_strcasecmp(user->passport, purple_account_get_username(account))) { msn_slplink_request_object(slplink, info, got_user_display, end_user_display, obj); } else { MsnObject *my_obj = NULL; gconstpointer data = NULL; size_t len = 0; #ifdef MSN_DEBUG_UD purple_debug_info("msn", "Requesting our own user display\n"); #endif my_obj = msn_user_get_object(session->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); } purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info); /* Free one window slot */ session->userlist->buddy_icon_window++; #ifdef MSN_DEBUG_UD purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n", session->userlist->buddy_icon_window); #endif msn_release_buddy_icon_request(session->userlist); } }