Mercurial > pidgin.yaz
changeset 5351:2aa7e4237142
[gaim-migrate @ 5727]
Buddy icon support!
The MSN protocol does not support this, but it does allow for different
content-types, which no client (except a couple broken ones I can name)
will see. So, I managed to extend the protocol a bit to do buddy icons.
It should work like AIM. Setup your icon in your account editor, and
message somebody. If they change their icon, however, you will have to
close the conversation window, re-open it, and send another message. That's
just how it has to work for now, I'm afraid.
Oh, and another thing. MSNP7 (P6 as well? Not sure) times out inactive
conversations after 5 minutes. Right now, you're seeing "User has closed
the conversation window" messages, but they're really not. So, we now print
out a message saying it timed out. Ugly, yes, but unless we have both
messages, there's confusion. Oh well! Kick the hay!
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Sat, 10 May 2003 23:55:18 +0000 |
parents | a6146cbae03b |
children | 335f9353bf22 |
files | src/protocols/msn/Makefile.am src/protocols/msn/buddyicon.c src/protocols/msn/buddyicon.h src/protocols/msn/msn.c src/protocols/msn/session.c src/protocols/msn/switchboard.c src/protocols/msn/switchboard.h |
diffstat | 7 files changed, 621 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/src/protocols/msn/Makefile.am Sat May 10 23:10:54 2003 +0000 +++ b/src/protocols/msn/Makefile.am Sat May 10 23:55:18 2003 +0000 @@ -5,6 +5,8 @@ MSNSOURCES = \ away.c \ away.h \ + buddyicon.c \ + buddyicon.h \ dispatch.c \ dispatch.h \ error.c \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/buddyicon.c Sat May 10 23:55:18 2003 +0000 @@ -0,0 +1,513 @@ +/** + * @file buddyicon.c Buddy icon support + * + * gaim + * + * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "buddyicon.h" + +#define PACKET_LENGTH 1500 + +static const char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static char * +__base64_enc(const char *data, int len) +{ + char *dest; + char *buf; + + buf = dest = g_malloc(4 * len / 3 + 4); + + /* Encode 3 bytes at a time */ + while (len >= 3) { + buf[0] = alphabet[(data[0] >> 2) & 0x3F]; + buf[1] = alphabet[((data[0] << 4) & 0x30) | ((data[1] >> 4) & 0x0F)]; + buf[2] = alphabet[((data[1] << 2) & 0x3C) | ((data[2] >> 6) & 0x03)]; + buf[3] = alphabet[data[2] & 0x3F]; + data += 3; + buf += 4; + len -= 3; + } + + if (len > 0) { + buf[0] = alphabet[(data[0] >> 2) & 0x3F]; + buf[1] = alphabet[(data[0] << 4) & 0x30]; + + if (len > 1) { + buf[1] += (data[1] >> 4) & 0x0F; + buf[2] = alphabet[(data[1] << 2) & 0x3C]; + } + + else + buf[2] = '='; + + buf[3] = '='; + buf += 4; + } + + *buf = '\0'; + + return dest; +} + +static gboolean +__get_buddy_icon_info(struct gaim_account *account, char **base64, + char **md5sum, int *file_size, int *base64_size) +{ + FILE *fp; + struct stat sb; + md5_state_t st; + md5_byte_t di[16]; + + if (base64 != NULL) *base64 = NULL; + if (md5sum != NULL) *md5sum = NULL; + if (file_size != NULL) *file_size = 0; + if (base64_size != NULL) *base64_size = 0; + + if (!stat(account->iconfile, &sb)) { + if (file_size != NULL) + *file_size = sb.st_size; + + if ((fp = fopen(account->iconfile, "rb")) != NULL) { + char *buf = g_malloc(sb.st_size + 1); + char *temp; + + fread(buf, 1, sb.st_size, fp); + + buf[sb.st_size] = '\0'; + + temp = __base64_enc(buf, sb.st_size); + + if (base64_size != NULL) + *base64_size = strlen(temp); + + if (base64 != NULL) + *base64 = temp; + else + g_free(temp); + + if (md5sum != NULL) { + char buf2[3]; + int i; + + md5_init(&st); + md5_append(&st, (const md5_byte_t *)buf, sb.st_size); + md5_finish(&st, di); + + *md5sum = g_new0(char, 33); + + for (i = 0; i < 16; i++) { + g_snprintf(buf2, sizeof(buf2), "%02x", di[i]); + strcat(*md5sum, buf2); + } + } + + g_free(buf); + + fclose(fp); + } + else { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Cannot open buddy icon file!\n"); + + return FALSE; + } + } + + return TRUE; +} + +static gboolean +__send_icon_data(MsnSwitchBoard *swboard, MsnBuddyIconXfer *buddyicon) +{ + struct gaim_connection *gc = swboard->servconn->session->account->gc; + char buf[MSN_BUF_LEN]; + MsnMessage *msg; + int len; + + len = MIN(PACKET_LENGTH - 4, + buddyicon->total_size - buddyicon->bytes_xfer); + + strcpy(buf, "ICON"); + + strncat(buf, buddyicon->data + buddyicon->bytes_xfer, len); + + msg = msn_message_new(); + msn_message_set_content_type(msg, "application/x-buddyicon"); + msn_message_set_receiver(msg, buddyicon->user); + msn_message_set_charset(msg, NULL); + msn_message_set_attr(msg, "User-Agent", NULL); + + msn_message_set_body(msg, buf); + + if (!msn_switchboard_send_msg(swboard, msg)) { + msn_message_destroy(msg); + + msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); + swboard->buddy_icon_xfer = NULL; + + hide_login_progress(gc, _("Write error")); + signoff(gc); + + return FALSE; + } + + msn_message_destroy(msg); + + buddyicon->bytes_xfer += len; + + if (buddyicon->bytes_xfer == buddyicon->total_size) { + msg = msn_message_new(); + msn_message_set_content_type(msg, "application/x-buddyicon"); + msn_message_set_receiver(msg, buddyicon->user); + msn_message_set_charset(msg, NULL); + msn_message_set_attr(msg, "User-Agent", NULL); + + msn_message_set_body(msg, "Command: COMPLETE\r\n"); + + msn_switchboard_send_msg(swboard, msg); + + msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); + swboard->buddy_icon_xfer = NULL; + } + + return TRUE; +} + +static gboolean +__process_invite(MsnServConn *servconn, const MsnMessage *msg) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = session->account->gc; + MsnMessage *new_msg; + MsnSwitchBoard *swboard; + MsnBuddyIconXfer *buddyicon; + struct buddy *b; + GHashTable *table; + const char *command; + + table = msn_message_get_hashtable_from_body(msg); + + command = g_hash_table_lookup(table, "Command"); + + if (command == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Missing Command from buddy icon message.\n"); + return TRUE; + } + + if (!strcmp(command, "INVITE")) { + MsnUser *user; + const char *md5sum = g_hash_table_lookup(table, "MD5SUM"); + const char *size_s = g_hash_table_lookup(table, "File-Size"); + const char *base64_size_s = g_hash_table_lookup(table, "Base64-Size"); + const char *passport; + + if (md5sum == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Missing MD5SUM from buddy icon message.\n"); + + return TRUE; + } + + if (size_s == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Missing File-Size from buddy icon message.\n"); + + return TRUE; + } + + if (base64_size_s == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Missing Bas64-Size from buddy icon message.\n"); + + return TRUE; + } + + user = msn_message_get_sender(msg); + + passport = msn_user_get_passport(user); + + /* See if we actually need a new icon. */ + if ((b = gaim_find_buddy(gc->account, passport)) != NULL) { + const char *cur_md5sum; + + cur_md5sum = gaim_buddy_get_setting(b, "icon_checksum"); + + if (cur_md5sum != NULL && !strcmp(cur_md5sum, md5sum)) + return TRUE; + } + + /* Send a request for transfer. */ + new_msg = msn_message_new(); + msn_message_set_content_type(new_msg, "application/x-buddyicon"); + msn_message_set_receiver(new_msg, user); + msn_message_set_charset(new_msg, NULL); + msn_message_set_attr(new_msg, "User-Agent", NULL); + + msn_message_set_body(new_msg, "Command: REQUEST\r\n"); + + if ((swboard = msn_session_open_switchboard(session)) == NULL) { + msn_message_destroy(new_msg); + + hide_login_progress(gc, _("Write error")); + signoff(gc); + + return FALSE; + } + + swboard->hidden = TRUE; + msn_switchboard_set_user(swboard, user); + msn_switchboard_send_msg(swboard, new_msg); + + msn_message_destroy(new_msg); + + buddyicon = swboard->buddy_icon_xfer = msn_buddy_icon_xfer_new(); + + buddyicon->user = user; + msn_user_ref(buddyicon->user); + + buddyicon->md5sum = g_strdup(md5sum); + buddyicon->total_size = atoi(base64_size_s); + buddyicon->file_size = atoi(size_s); + + buddyicon->data = g_malloc(buddyicon->total_size + 1); + } + else if (!strcmp(command, "REQUEST")) { + swboard = (MsnSwitchBoard *)servconn->data; + + swboard->hidden = TRUE; + + swboard->buddy_icon_xfer = buddyicon = msn_buddy_icon_xfer_new(); + + if (!__get_buddy_icon_info(gc->account, + &buddyicon->data, + &buddyicon->md5sum, + &buddyicon->file_size, + &buddyicon->total_size)) { + + msn_buddy_icon_xfer_destroy(buddyicon); + + new_msg = msn_message_new(); + msn_message_set_content_type(new_msg, "application/x-buddyicon"); + msn_message_set_receiver(new_msg, msn_message_get_sender(msg)); + msn_message_set_charset(new_msg, NULL); + msn_message_set_attr(new_msg, "User-Agent", NULL); + + msn_message_set_body(new_msg, "Command: CANCEL\r\n"); + + if ((swboard = msn_session_open_switchboard(session)) == NULL) { + msn_message_destroy(new_msg); + + hide_login_progress(gc, _("Write error")); + signoff(gc); + + return FALSE; + } + + swboard->hidden = TRUE; + + msn_switchboard_send_msg(swboard, new_msg); + + msn_message_destroy(new_msg); + + msn_switchboard_destroy(swboard); + } + + return __send_icon_data(swboard, buddyicon); + } + else if (!strcmp(command, "ACK")) { + swboard = (MsnSwitchBoard *)servconn->data; + + buddyicon = swboard->buddy_icon_xfer; + + if (buddyicon != NULL) + return __send_icon_data(swboard, buddyicon); + } + else if (!strcmp(command, "COMPLETE")) { + const char *passport; + char *icon; + int icon_len; + + swboard = (MsnSwitchBoard *)servconn->data; + + buddyicon = swboard->buddy_icon_xfer; + + passport = msn_user_get_passport(buddyicon->user); + swboard->hidden = TRUE; + + frombase64(buddyicon->data, &icon, &icon_len); + + if ((b = gaim_find_buddy(gc->account, passport)) != NULL) { + gaim_buddy_set_setting(b, "icon_checksum", buddyicon->md5sum); + gaim_blist_save(); + } + + set_icon_data(gc, passport, icon, icon_len); + + g_free(icon); + + msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); + swboard->buddy_icon_xfer = NULL; + + msn_switchboard_destroy(swboard); + } + else if (!strcmp(command, "CANCEL")) { + swboard = (MsnSwitchBoard *)servconn->data; + + msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); + swboard->buddy_icon_xfer = NULL; + + msn_switchboard_destroy(swboard); + } + else { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Unknown buddy icon message command: %s\n", command); + } + + return TRUE; +} + +static gboolean +__process_data(MsnServConn *servconn, const MsnMessage *msg) +{ + struct gaim_connection *gc = servconn->session->account->gc; + MsnSwitchBoard *swboard; + MsnBuddyIconXfer *buddyicon; + MsnMessage *ack_msg; + const char *data; + int len; + + swboard = (MsnSwitchBoard *)servconn->data; + buddyicon = swboard->buddy_icon_xfer; + + data = msn_message_get_body(msg) + 4; + + len = strlen(data); + + /* Copy the data into our buffer. */ + strncpy(buddyicon->data + buddyicon->bytes_xfer, data, + buddyicon->total_size - buddyicon->bytes_xfer); + + buddyicon->bytes_xfer += len; + + /* Acknowledge this data. */ + ack_msg = msn_message_new(); + msn_message_set_content_type(ack_msg, "application/x-buddyicon"); + msn_message_set_receiver(ack_msg, msn_message_get_sender(msg)); + msn_message_set_charset(ack_msg, NULL); + msn_message_set_attr(ack_msg, "User-Agent", NULL); + msn_message_set_body(ack_msg, "Command: ACK\r\n"); + + if (!msn_switchboard_send_msg(swboard, ack_msg)) { + msn_message_destroy(ack_msg); + + msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); + swboard->buddy_icon_xfer = NULL; + + hide_login_progress(gc, _("Write error")); + signoff(gc); + + return FALSE; + } + + msn_message_destroy(ack_msg); + + return TRUE; +} + +MsnBuddyIconXfer * +msn_buddy_icon_xfer_new(void) +{ + return g_new0(MsnBuddyIconXfer, 1); +} + +void +msn_buddy_icon_xfer_destroy(MsnBuddyIconXfer *xfer) +{ + g_return_if_fail(xfer != NULL); + + if (xfer->user != NULL) + msn_user_unref(xfer->user); + + if (xfer->data != NULL) + g_free(xfer->data); + + g_free(xfer); +} + +gboolean +msn_buddy_icon_msg(MsnServConn *servconn, const MsnMessage *msg) +{ + if (!strncmp(msn_message_get_body(msg), "ICON", 4)) + return __process_data(servconn, msg); + else + return __process_invite(servconn, msg); +} + +void +msn_buddy_icon_invite(MsnSwitchBoard *swboard) +{ + struct gaim_account *account = swboard->servconn->session->account; + struct gaim_connection *gc = account->gc; + MsnMessage *msg; + char buf[MSN_BUF_LEN]; + char *md5sum; + int file_size, base64_size; + + g_return_if_fail(swboard != NULL); + + if (*account->iconfile == '\0') + return; /* We don't have an icon to send. */ + + if (!__get_buddy_icon_info(account, NULL, &md5sum, + &file_size, &base64_size)) { + return; + } + + msg = msn_message_new(); + msn_message_set_content_type(msg, "application/x-buddyicon"); + msn_message_set_receiver(msg, msn_message_get_sender(msg)); + msn_message_set_charset(msg, NULL); + msn_message_set_attr(msg, "User-Agent", NULL); + + g_snprintf(buf, sizeof(buf), + "Command: INVITE\r\n" + "MD5SUM: %s\r\n" + "File-Size: %d\r\n" + "Base64-Size: %d\r\n", + md5sum, file_size, base64_size); + + g_free(md5sum); + + msn_message_set_body(msg, buf); + + if (!msn_switchboard_send_msg(swboard, msg)) { + msn_message_destroy(msg); + + hide_login_progress(gc, _("Write error")); + signoff(gc); + + return; + } + + msn_message_destroy(msg); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/buddyicon.h Sat May 10 23:55:18 2003 +0000 @@ -0,0 +1,77 @@ +/** + * @file buddyicon.h Buddy icon support + * + * gaim + * + * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_BUDDY_ICON_H_ +#define _MSN_BUDDY_ICON_H_ + +typedef struct _MsnBuddyIconXfer MsnBuddyIconXfer; + +#include "servconn.h" + +/** + * State of a buddy icon transfer. + */ +struct _MsnBuddyIconXfer +{ + MsnUser *user; /**< The user on the other end of the transfer. */ + + gboolean sending; /**< True if sending this icon. */ + + size_t bytes_xfer; /**< The current bytes sent or retrieved. */ + size_t total_size; /**< The total size of the base64 icon data. */ + size_t file_size; /**< The file size of the actual icon. */ + + char *md5sum; /**< The MD5SUM of the icon. */ + char *data; /**< The buddy icon data. */ +}; + +/** + * Creates an MsnBuddyIconXfer structure. + * + * @return The MsnBuddyIconXfer structure. + */ +MsnBuddyIconXfer *msn_buddy_icon_xfer_new(void); + +/** + * Destroys an MsnBuddyIconXfer structure. + * + * @param The buddy icon structure. + */ +void msn_buddy_icon_xfer_destroy(MsnBuddyIconXfer *xfer); + +/** + * Processed application/x-buddyicon messages. + * + * @param servconn The server connection. + * @param msg The message. + * + * @return TRUE + */ +gboolean msn_buddy_icon_msg(MsnServConn *servconn, const MsnMessage *msg); + +/** + * Sends a buddy icon invitation message. + * + * @param swboard The switchboard to send to. + */ +void msn_buddy_icon_invite(MsnSwitchBoard *swboard); + +#endif /* _MSN_BUDDY_ICON_H_ */
--- a/src/protocols/msn/msn.c Sat May 10 23:10:54 2003 +0000 +++ b/src/protocols/msn/msn.c Sat May 10 23:55:18 2003 +0000 @@ -871,7 +871,7 @@ static GaimPluginProtocolInfo prpl_info = { GAIM_PROTO_MSN, - OPT_PROTO_MAIL_CHECK, + OPT_PROTO_MAIL_CHECK | OPT_PROTO_BUDDY_ICON, NULL, NULL, msn_list_icon,
--- a/src/protocols/msn/session.c Sat May 10 23:10:54 2003 +0000 +++ b/src/protocols/msn/session.c Sat May 10 23:55:18 2003 +0000 @@ -140,7 +140,8 @@ for (l = session->switches; l != NULL; l = l->next) { swboard = (MsnSwitchBoard *)l->data; - if (!g_ascii_strcasecmp(passport, + if (!swboard->hidden && + !g_ascii_strcasecmp(passport, msn_user_get_passport(swboard->user))) { return swboard; } @@ -179,7 +180,7 @@ for (l = session->switches; l != NULL; l = l->next) { swboard = (MsnSwitchBoard *)l->data; - if (!swboard->in_use) + if (!swboard->in_use && !swboard->hidden) return swboard; }
--- a/src/protocols/msn/switchboard.c Sat May 10 23:10:54 2003 +0000 +++ b/src/protocols/msn/switchboard.c Sat May 10 23:55:18 2003 +0000 @@ -34,6 +34,9 @@ { MsnMessage *msg; + if (swboard->buddy_icon_xfer != NULL) + return TRUE; + msg = msn_message_new(); msn_message_set_content_type(msg, "text/x-clientinfo"); msn_message_set_charset(msg, NULL); @@ -97,6 +100,9 @@ MsnSwitchBoard *swboard = servconn->data; const char *user = params[0]; + if (swboard->hidden) + return TRUE; + if (swboard->chat != NULL) gaim_chat_remove_user(GAIM_CHAT(swboard->chat), user, NULL); else { @@ -110,8 +116,13 @@ else username = user; - g_snprintf(buf, sizeof(buf), - _("%s has closed the conversation window."), username); + if (param_count == 2 && atoi(params[1]) == 1) + g_snprintf(buf, sizeof(buf), + _("The conversation has become inactive " + "and timed out.")); + else + g_snprintf(buf, sizeof(buf), + _("%s has closed the conversation window."), username); if ((conv = gaim_find_conversation(user)) != NULL) gaim_conversation_write(conv, NULL, buf, -1, WFLAG_SYSTEM, @@ -305,13 +316,18 @@ __clientinfo_msg(MsnServConn *servconn, const MsnMessage *msg) { MsnSession *session = servconn->session; + MsnSwitchBoard *swboard = servconn->data; MsnUser *user; GHashTable *clientinfo; + const char *value; user = msn_user_new(session, servconn->msg_passport, NULL); clientinfo = msn_message_get_hashtable_from_body(msg); + if ((value = g_hash_table_lookup(clientinfo, "Buddy-Icons")) != NULL) + msn_buddy_icon_invite(swboard); + return TRUE; } @@ -404,6 +420,8 @@ __control_msg); msn_servconn_register_msg_type(servconn, "text/x-clientinfo", __clientinfo_msg); + msn_servconn_register_msg_type(servconn, "application/x-buddyicon", + msn_buddy_icon_msg); /* Save these for future use. */ switchboard_commands = servconn->commands;
--- a/src/protocols/msn/switchboard.h Sat May 10 23:10:54 2003 +0000 +++ b/src/protocols/msn/switchboard.h Sat May 10 23:55:18 2003 +0000 @@ -27,6 +27,7 @@ #include "servconn.h" #include "msg.h" #include "user.h" +#include "buddyicon.h" struct _MsnSwitchBoard { @@ -49,6 +50,10 @@ int chat_id; int trId; + + gboolean hidden; + + MsnBuddyIconXfer *buddy_icon_xfer; }; /**