# HG changeset patch # User Elliott Sales de Andrade # Date 1260697467 0 # Node ID bd794230823469384a8eeb846a7d4c5171ada848 # Parent fc7798a661f7c43b1de827d1f4f6c1dfc6230fd4# Parent ec101fb8111134bd7bba063bc6ac2ce7d379d23a propagate from branch 'im.pidgin.pidgin' (head d813bbb8c323184987e92c9bd801fd7e64262104) to branch 'im.pidgin.cpw.qulogic.msnp16' (head ae093be5fdd4311e82faf775ce3c3690f0a7df65) diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/msn.h --- a/libpurple/protocols/msn/msn.h Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/msn.h Sun Dec 13 09:44:27 2009 +0000 @@ -52,9 +52,9 @@ #define MSN_SERVER "messenger.hotmail.com" #define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com" #define MSN_PORT 1863 -#define WLM_PROT_VER 15 +#define WLM_PROT_VER 16 -#define WLM_MAX_PROTOCOL 15 +#define WLM_MAX_PROTOCOL 16 #define WLM_MIN_PROTOCOL 15 #define MSN_TYPING_RECV_TIMEOUT 6 @@ -137,15 +137,14 @@ } MsnClientVerId; -#define MSN_CLIENT_ID_VERSION MSN_CLIENT_VER_7_0 +#define MSN_CLIENT_ID_VERSION MSN_CLIENT_VER_9_0 #define MSN_CLIENT_ID_CAPABILITIES (MSN_CLIENT_CAP_PACKET|MSN_CLIENT_CAP_INK_GIF|MSN_CLIENT_CAP_VOICEIM) +#define MSN_CLIENT_ID_EXT_CAPS (0) #define MSN_CLIENT_ID \ ((MSN_CLIENT_ID_VERSION << 24) | \ (MSN_CLIENT_ID_CAPABILITIES)) -#define MSN_CLIENT_EXT_ID 0 - gboolean msn_email_is_valid(const char *passport); void msn_act_id(PurpleConnection *gc, const char *entry); void msn_send_privacy(PurpleConnection *gc); diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/notification.c Sun Dec 13 09:44:27 2009 +0000 @@ -161,7 +161,10 @@ msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END); - msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response); + if (session->protocol_ver >= 16) + msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s %s", ticket, response, session->guid); + else + msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response); } static void @@ -233,20 +236,22 @@ MsnSession *session; PurpleAccount *account; gboolean protocol_supported = FALSE; - char proto_str[8]; + int proto_ver; size_t i; session = cmdproc->session; account = session->account; - g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver); - + session->protocol_ver = 0; for (i = 1; i < cmd->param_count; i++) { - if (!strcmp(cmd->params[i], proto_str)) - { - protocol_supported = TRUE; - break; + if (sscanf(cmd->params[i], "MSNP%d", &proto_ver) == 1) { + if (proto_ver >= WLM_MIN_PROTOCOL + && proto_ver <= WLM_MAX_PROTOCOL + && proto_ver > session->protocol_ver) { + protocol_supported = TRUE; + session->protocol_ver = proto_ver; + } } } @@ -257,6 +262,8 @@ return; } + purple_debug_info("msn", "Negotiated protocol version %d with the server.\n", session->protocol_ver); + /* * Windows Live Messenger 8.5 * Notice :CVR String discriminate! @@ -1081,7 +1088,8 @@ PurpleConnection *gc; MsnUser *user; MsnObject *msnobj = NULL; - unsigned long clientid; + unsigned long clientid, extcaps; + char *extcap_str; int networkid = 0; const char *state, *passport; char *friendly; @@ -1102,7 +1110,11 @@ /* Yahoo! Buddy, looks like */ networkid = atoi(cmd->params[3]); friendly = g_strdup(purple_url_decode(cmd->params[4])); - clientid = strtoul(cmd->params[5], NULL, 10); + clientid = strtoul(cmd->params[5], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; /* cmd->params[7] seems to be a URL to a Yahoo! icon: https://sec.yimg.com/i/us/nt/b/purpley.1.0.png @@ -1112,7 +1124,11 @@ /* MSNP14+ with Display Picture object */ networkid = atoi(cmd->params[3]); friendly = g_strdup(purple_url_decode(cmd->params[4])); - clientid = strtoul(cmd->params[5], NULL, 10); + clientid = strtoul(cmd->params[5], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6])); } else if (cmd->param_count == 6) { /* Yes, this is 5. The friendly name could start with a number, @@ -1121,17 +1137,29 @@ /* MSNP14 without Display Picture object */ networkid = atoi(cmd->params[3]); friendly = g_strdup(purple_url_decode(cmd->params[4])); - clientid = strtoul(cmd->params[5], NULL, 10); + clientid = strtoul(cmd->params[5], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; } else { /* MSNP8+ with Display Picture object */ friendly = g_strdup(purple_url_decode(cmd->params[3])); - clientid = strtoul(cmd->params[4], NULL, 10); + clientid = strtoul(cmd->params[4], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5])); } } else if (cmd->param_count == 5) { /* MSNP8+ without Display Picture object */ friendly = g_strdup(purple_url_decode(cmd->params[3])); - clientid = strtoul(cmd->params[4], NULL, 10); + clientid = strtoul(cmd->params[4], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; } else { purple_debug_warning("msn", "Received ILN with unknown number of parameters.\n"); return; @@ -1146,6 +1174,7 @@ user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+'); msn_user_set_clientid(user, clientid); + msn_user_set_extcaps(user, extcaps); msn_user_set_network(user, networkid); msn_user_set_state(user, state); @@ -1284,7 +1313,8 @@ PurpleConnection *gc; MsnUser *user; MsnObject *msnobj; - unsigned long clientid; + unsigned long clientid, extcaps; + char *extcap_str; int networkid; const char *state, *passport, *friendly; @@ -1297,7 +1327,10 @@ networkid = atoi(cmd->params[2]); friendly = purple_url_decode(cmd->params[3]); - user = msn_userlist_find_user(session->userlist, passport); + if (g_str_equal(passport, session->user->passport)) + user = session->user; + else + user = msn_userlist_find_user(session->userlist, passport); if (user == NULL) return; if (msn_user_set_friendly_name(user, friendly)) @@ -1315,10 +1348,15 @@ msn_user_set_object(user, NULL); } - clientid = strtoul(cmd->params[4], NULL, 10); + clientid = strtoul(cmd->params[4], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+'); msn_user_set_clientid(user, clientid); + msn_user_set_extcaps(user, extcaps); msn_user_set_network(user, networkid); msn_user_set_state(user, state); @@ -1599,6 +1637,42 @@ /*get the payload content*/ } +static void +parse_user_endpoints(MsnUser *user, xmlnode *payloadNode) +{ + xmlnode *epNode, *capsNode; + MsnUserEndpoint data; + const char *id; + char *caps, *tmp; + + purple_debug_info("msn", "Get EndpointData\n"); + + for (epNode = xmlnode_get_child(payloadNode, "EndpointData"); + epNode; + epNode = xmlnode_get_next_twin(epNode)) { + id = xmlnode_get_attrib(epNode, "id"); + capsNode = xmlnode_get_child(epNode, "Capabilities"); + + if (capsNode != NULL) { + caps = xmlnode_get_data(capsNode); + + data.clientid = strtoul(caps, &tmp, 10); + if (tmp && *tmp) + data.extcaps = strtoul(tmp + 1, NULL, 10); + else + data.extcaps = 0; + + g_free(caps); + + } else { + data.clientid = 0; + data.extcaps = 0; + } + + msn_user_set_endpoint_data(user, id, &data); + } +} + /* * Get the UBX's PSM info * Post it to the User status @@ -1612,6 +1686,7 @@ PurpleAccount *account; MsnUser *user; const char *passport; + xmlnode *payloadNode; char *psm_str, *str; CurrentMedia media = {CURRENT_MEDIA_UNKNOWN, NULL, NULL, NULL}; @@ -1619,7 +1694,10 @@ account = session->account; passport = cmd->params[0]; - user = msn_userlist_find_user(session->userlist, passport); + if (g_str_equal(passport, session->user->passport)) + user = session->user; + else + user = msn_userlist_find_user(session->userlist, passport); if (user == NULL) { char *str = g_strndup(payload, len); purple_debug_info("msn", "unknown user %s, payload is %s\n", @@ -1629,11 +1707,22 @@ } if (len != 0) { - psm_str = msn_get_psm(cmd->payload,len); + payloadNode = xmlnode_from_str(payload, len); + if (!payloadNode) { + purple_debug_error("msn", "UBX XML parse Error!\n"); + + msn_user_set_statusline(user, NULL); + msn_user_set_currentmedia(user, NULL); + + msn_user_update(user); + return; + } + + psm_str = msn_get_psm(payloadNode); msn_user_set_statusline(user, psm_str); g_free(psm_str); - str = msn_get_currentmedia(cmd->payload, len); + str = msn_get_currentmedia(payloadNode); if (msn_parse_currentmedia(str, &media)) msn_user_set_currentmedia(user, &media); else @@ -1643,6 +1732,10 @@ g_free(media.artist); g_free(str); + parse_user_endpoints(user, payloadNode); + + xmlnode_free(payloadNode); + } else { msn_user_set_statusline(user, NULL); msn_user_set_currentmedia(user, NULL); @@ -1676,6 +1769,86 @@ cmd->payload_len = atoi(cmd->params[1]); } +void +msn_notification_send_uux(MsnSession *session, const char *payload) +{ + MsnTransaction *trans; + MsnCmdProc *cmdproc; + size_t len = strlen(payload); + + cmdproc = session->notification->cmdproc; + purple_debug_misc("msn", "Sending UUX command with payload: %s\n", payload); + trans = msn_transaction_new(cmdproc, "UUX", "%" G_GSIZE_FORMAT, len); + msn_transaction_set_payload(trans, payload, len); + msn_cmdproc_send_trans(cmdproc, trans); +} + +void msn_notification_send_uux_endpointdata(MsnSession *session) +{ + xmlnode *epDataNode; + xmlnode *capNode; + char *caps; + char *payload; + int length; + + epDataNode = xmlnode_new("EndpointData"); + + capNode = xmlnode_new_child(epDataNode, "Capabilities"); + caps = g_strdup_printf("%d:%02d", MSN_CLIENT_ID_CAPABILITIES, MSN_CLIENT_ID_EXT_CAPS); + xmlnode_insert_data(capNode, caps, -1); + g_free(caps); + + payload = xmlnode_to_str(epDataNode, &length); + + msn_notification_send_uux(session, payload); + + xmlnode_free(epDataNode); + g_free(payload); +} + +void msn_notification_send_uux_private_endpointdata(MsnSession *session) +{ + xmlnode *private; + xmlnode *epname; + xmlnode *idle; + xmlnode *client_type; + xmlnode *state; + char *payload; + int length; + + private = xmlnode_new("PrivateEndPointData"); + + /* TODO: "Pidgin" is a temp EndPointName.. we must use hostid or some.*/ + epname = xmlnode_new_child(private, "EpName"); + xmlnode_insert_data(epname, "Pidgin", -1); + + idle = xmlnode_new_child(private, "Idle"); + xmlnode_insert_data(idle, "false", -1); + + /* TODO: support different client types */ + /* ClientType info (from amsn guys): + 0: None + 1: Computer + 2: Website + 3: Mobile / none + 4: Xbox / phone /mobile + 9: MsnGroup + 32: Email member, currently Yahoo! + */ + client_type = xmlnode_new_child(private, "ClientType"); + xmlnode_insert_data(client_type, "1", -1); + + state = xmlnode_new_child(private, "State"); + xmlnode_insert_data(state, msn_state_get_text(msn_state_from_account(session->account)), -1); + + payload = xmlnode_to_str(private, &length); + + msn_notification_send_uux(session, payload); + + xmlnode_free(private); + g_free(payload); +} + /************************************************************************** * Message Types **************************************************************************/ diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/notification.h --- a/libpurple/protocols/msn/notification.h Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/notification.h Sun Dec 13 09:44:27 2009 +0000 @@ -82,6 +82,12 @@ void msn_notification_disconnect(MsnNotification *notification); void msn_notification_dump_contact(MsnSession *session); +void msn_notification_send_uux(MsnSession *session, const char *payload); + +void msn_notification_send_uux_endpointdata(MsnSession *session); + +void msn_notification_send_uux_private_endpointdata(MsnSession *session); + /** * Closes a notification. * diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/session.c --- a/libpurple/protocols/msn/session.c Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/session.c Sun Dec 13 09:44:27 2009 +0000 @@ -22,6 +22,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "msn.h" +#include "msnutils.h" #include "session.h" #include "notification.h" #include "oim.h" @@ -45,7 +46,9 @@ purple_account_get_username(account), NULL); session->oim = msn_oim_new(session); - session->protocol_ver = WLM_PROT_VER; + session->protocol_ver = 0; + + session->guid = rand_guid(); return session; } @@ -90,6 +93,7 @@ msn_userlist_destroy(session->userlist); g_free(session->psm); + g_free(session->guid); g_free(session->abch_cachekey); #if 0 g_free(session->blocked_text); @@ -448,6 +452,11 @@ msn_session_sync_users(session); } + if (session->protocol_ver >= 16) { + /* TODO: Send this when updating status instead? */ + msn_notification_send_uux_endpointdata(session); + /*msn_notification_send_uux_private_endpointdata(session);*/ + } msn_change_status(session); } diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/session.h --- a/libpurple/protocols/msn/session.h Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/session.h Sun Dec 13 09:44:27 2009 +0000 @@ -126,6 +126,7 @@ GHashTable *soap_table; guint soap_cleanup_handle; + char *guid; }; /** diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/state.c --- a/libpurple/protocols/msn/state.c Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/state.c Sun Dec 13 09:44:27 2009 +0000 @@ -26,6 +26,7 @@ #include "core.h" #include "msn.h" +#include "notification.h" #include "state.h" static const char *away_text[] = @@ -42,10 +43,6 @@ N_("Available") }; -/* Local Function Prototype*/ -static char *msn_build_psm(const char *psmstr,const char *mediastr, - const char *guidstr); - /* * WLM media PSM info build prcedure * @@ -55,7 +52,7 @@ * \0Office\01\0Office Message\0Office App Name\0" */ static char * -msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr) +msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr, guint protocol_ver) { xmlnode *dataNode,*psmNode,*mediaNode,*guidNode; char *result; @@ -81,6 +78,12 @@ } xmlnode_insert_child(dataNode, guidNode); + if (protocol_ver >= 16) { + /* TODO: What is this for? */ + xmlnode *ddpNode = xmlnode_new("DDP"); + xmlnode_insert_child(dataNode, ddpNode); + } + result = xmlnode_to_str(dataNode, &length); xmlnode_free(dataNode); return result; @@ -158,55 +161,39 @@ return parsed; } -/* get the CurrentMedia info from the XML string */ +/* get the CurrentMedia info from the XML node */ char * -msn_get_currentmedia(char *xml_str, gsize len) +msn_get_currentmedia(xmlnode *payloadNode) { - xmlnode *payloadNode, *currentmediaNode; + xmlnode *currentmediaNode; char *currentmedia; purple_debug_info("msn", "Get CurrentMedia\n"); - payloadNode = xmlnode_from_str(xml_str, len); - if (!payloadNode) { - purple_debug_error("msn", "PSM XML parse Error!\n"); - return NULL; - } currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia"); if (currentmediaNode == NULL) { purple_debug_info("msn", "No CurrentMedia Node\n"); - xmlnode_free(payloadNode); return NULL; } currentmedia = xmlnode_get_data(currentmediaNode); - xmlnode_free(payloadNode); - return currentmedia; } -/*get the PSM info from the XML string*/ +/* Get the PSM info from the XML node */ char * -msn_get_psm(char *xml_str, gsize len) +msn_get_psm(xmlnode *payloadNode) { - xmlnode *payloadNode, *psmNode; + xmlnode *psmNode; char *psm; purple_debug_info("msn", "msn get PSM\n"); - payloadNode = xmlnode_from_str(xml_str, len); - if (!payloadNode) { - purple_debug_error("msn", "PSM XML parse Error!\n"); - return NULL; - } psmNode = xmlnode_get_child(payloadNode, "PSM"); if (psmNode == NULL) { purple_debug_info("msn", "No PSM status Node\n"); - xmlnode_free(payloadNode); return NULL; } psm = xmlnode_get_data(psmNode); - xmlnode_free(payloadNode); - return psm; } @@ -252,8 +239,6 @@ PurpleAccount *account; PurplePresence *presence; PurpleStatus *status; - MsnCmdProc *cmdproc; - MsnTransaction *trans; char *payload; const char *statusline; gchar *statusline_stripped, *media = NULL; @@ -262,7 +247,6 @@ g_return_if_fail(session->notification != NULL); account = session->account; - cmdproc = session->notification->cmdproc; /* Get the PSM string from Purple's Status Line */ presence = purple_account_get_presence(account); @@ -273,13 +257,11 @@ statusline_stripped = purple_markup_strip_html(statusline); media = create_media_string(presence); g_free(session->psm); - session->psm = msn_build_psm(statusline_stripped, media, NULL); + session->psm = msn_build_psm(statusline_stripped, media, session->protocol_ver >= 16 ? session->guid : NULL, session->protocol_ver); payload = session->psm; - purple_debug_misc("msn", "Sending UUX command with payload: %s\n", payload); - trans = msn_transaction_new(cmdproc, "UUX", "%" G_GSIZE_FORMAT, strlen(payload)); - msn_transaction_set_payload(trans, payload, strlen(payload)); - msn_cmdproc_send_trans(cmdproc, trans); + + msn_notification_send_uux(session, payload); g_free(statusline_stripped); g_free(media); @@ -327,11 +309,16 @@ if (!session->logged_in) return; + msn_set_psm(session); + msnobj = msn_user_get_object(user); if (msnobj == NULL) { - msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text, caps); + if (session->protocol_ver >= 16) + msn_cmdproc_send(cmdproc, "CHG", "%s %u:%02u 0", state_text, caps, MSN_CLIENT_ID_EXT_CAPS); + else + msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text, caps); } else { @@ -339,12 +326,15 @@ msnobj_str = msn_object_to_string(msnobj); - msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state_text, - caps, purple_url_encode(msnobj_str)); + if (session->protocol_ver >= 16) + msn_cmdproc_send(cmdproc, "CHG", "%s %u:%02u %s", state_text, + caps, MSN_CLIENT_ID_EXT_CAPS, purple_url_encode(msnobj_str)); + else + msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state_text, + caps, purple_url_encode(msnobj_str)); g_free(msnobj_str); } - msn_set_psm(session); } const char * diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/state.h --- a/libpurple/protocols/msn/state.h Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/state.h Sun Dec 13 09:44:27 2009 +0000 @@ -64,11 +64,11 @@ /* Parse CurrentMedia string */ gboolean msn_parse_currentmedia(const char *cmedia, CurrentMedia *media); -/* Get the CurrentMedia info from the XML string */ -char * msn_get_currentmedia(char *xml_str,gsize len); +/* Get the CurrentMedia info from the XML node */ +char * msn_get_currentmedia(xmlnode *payloadNode); -/*get the PSM info from the XML string*/ -char * msn_get_psm(char *xml_str,gsize len); +/* Get the PSM info from the XML node */ +char * msn_get_psm(xmlnode *payloadNode); MsnAwayType msn_state_from_account(PurpleAccount *account); diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/switchboard.c --- a/libpurple/protocols/msn/switchboard.c Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/switchboard.c Sun Dec 13 09:44:27 2009 +0000 @@ -949,6 +949,7 @@ MsnTransaction *trans; MsnCmdProc *cmdproc; PurpleAccount *account; + char *username; cmdproc = servconn->cmdproc; g_return_if_fail(cmdproc != NULL); @@ -957,24 +958,33 @@ swboard = cmdproc->data; g_return_if_fail(swboard != NULL); + if (servconn->session->protocol_ver >= 16) + username = g_strdup(purple_account_get_username(account)); + else + username = g_strdup_printf("%s;{%s}", + purple_account_get_username(account), + servconn->session->guid); + if (msn_switchboard_is_invited(swboard)) { swboard->empty = FALSE; trans = msn_transaction_new(cmdproc, "ANS", "%s %s %s", - purple_account_get_username(account), + username, swboard->auth_key, swboard->session_id); } else { trans = msn_transaction_new(cmdproc, "USR", "%s %s", - purple_account_get_username(account), + username, swboard->auth_key); } msn_transaction_set_error_cb(trans, ans_usr_error); msn_transaction_set_data(trans, swboard); msn_cmdproc_send_trans(cmdproc, trans); + + g_free(username); } static void diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/user.c --- a/libpurple/protocols/msn/user.c Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/user.c Sun Dec 13 09:44:27 2009 +0000 @@ -39,6 +39,8 @@ msn_user_set_passport(user, passport); msn_user_set_friendly_name(user, friendly_name); + user->endpoints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + return user; } @@ -48,6 +50,9 @@ { g_return_if_fail(user != NULL); + if (user->endpoints != NULL) + g_hash_table_destroy(user->endpoints); + if (user->clientcaps != NULL) g_hash_table_destroy(user->clientcaps); @@ -229,6 +234,28 @@ } void +msn_user_set_endpoint_data(MsnUser *user, const char *endpoint, MsnUserEndpoint *data) +{ + MsnUserEndpoint *new; + g_return_if_fail(user != NULL); + + if (data == NULL) { + g_hash_table_remove(user->endpoints, endpoint); + return; + } + + new = g_hash_table_lookup(user->endpoints, endpoint); + if (!new) { + new = g_new0(MsnUserEndpoint, 1); + new->id = g_strdup(endpoint); + g_hash_table_insert(user->endpoints, g_strdup(endpoint), new); + } + + new->clientid = data->clientid; + new->extcaps = data->extcaps; +} + +void msn_user_set_op(MsnUser *user, int list_op) { g_return_if_fail(user != NULL); @@ -397,6 +424,14 @@ } void +msn_user_set_extcaps(MsnUser *user, guint extcaps) +{ + g_return_if_fail(user != NULL); + + user->extcaps = extcaps; +} + +void msn_user_set_network(MsnUser *user, MsnNetwork network) { g_return_if_fail(user != NULL); @@ -487,6 +522,22 @@ return user->clientid; } +guint +msn_user_get_extcaps(const MsnUser *user) +{ + g_return_val_if_fail(user != NULL, 0); + + return user->extcaps; +} + +MsnUserEndpoint * +msn_user_get_endpoint_data(MsnUser *user, const char *endpoint) +{ + g_return_val_if_fail(user != NULL, NULL); + + return g_hash_table_lookup(user->endpoints, endpoint); +} + MsnObject * msn_user_get_object(const MsnUser *user) { @@ -511,3 +562,22 @@ return user->invite_message; } +gboolean +msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap) +{ + g_return_val_if_fail(user != NULL, FALSE); + + if (endpoint != NULL) { + MsnUserEndpoint *ep = g_hash_table_lookup(user->endpoints, endpoint); + if (ep != NULL) + return (ep->clientid & capability) == capability + && (ep->extcaps & extcap) == extcap; + else + return FALSE; + } + + return (user->clientid & capability) == capability + && (user->extcaps & extcap) == extcap; +} + + diff -r ec101fb81111 -r bd7942308234 libpurple/protocols/msn/user.h --- a/libpurple/protocols/msn/user.h Sun Dec 13 09:40:58 2009 +0000 +++ b/libpurple/protocols/msn/user.h Sun Dec 13 09:44:27 2009 +0000 @@ -72,6 +72,7 @@ char *friendly_name; /**< The friendly name. */ char * uid; /*< User Id */ + GHashTable *endpoints; /*< Endpoint-specific data */ const char *status; /**< The state of the user. */ char *statusline; /**< The state of the user. */ @@ -98,6 +99,7 @@ GHashTable *clientcaps; /**< The client's capabilities. */ guint clientid; /**< The client's ID */ + guint extcaps; /**< The client's extended capabilities */ MsnNetwork networkid; /**< The user's network */ @@ -109,6 +111,16 @@ char *invite_message; /**< Invite message of user request */ }; +/** + * A specific user endpoint. + */ +typedef struct MsnUserEndpoint { + char *id; /**< The client's endpoint ID */ + guint clientid; /**< The client's ID */ + guint extcaps; /**< The client's extended capabilites */ + +} MsnUserEndpoint; + /************************************************************************** ** @name User API * **************************************************************************/ @@ -253,6 +265,16 @@ void msn_user_set_uid(MsnUser *user, const char *uid); /** + * Sets endpoint data for a user. + * + * @param user The user. + * @param endpoint The endpoint. + * @param data The endpoint data. + */ +void +msn_user_set_endpoint_data(MsnUser *user, const char *endpoint, MsnUserEndpoint *data); + +/** * Sets the client id for a user. * * @param user The user. @@ -261,6 +283,14 @@ void msn_user_set_clientid(MsnUser *user, guint clientid); /** + * Sets the client id for a user. + * + * @param user The user. + * @param extcaps The client's extended capabilities. + */ +void msn_user_set_extcaps(MsnUser *user, guint extcaps); + +/** * Sets the network id for a user. * * @param user The user. @@ -347,6 +377,17 @@ const char *msn_user_get_mobile_phone(const MsnUser *user); /** + * Gets endpoint data for a user. + * + * @param user The user. + * @param endpoint The endpoint. + * + * @return The user's endpoint data. + */ +MsnUserEndpoint * +msn_user_get_endpoint_data(MsnUser *user, const char *endpoint); + +/** * Returns the client id for a user. * * @param user The user. @@ -356,6 +397,15 @@ guint msn_user_get_clientid(const MsnUser *user); /** + * Returns the extended capabilities for a user. + * + * @param user The user. + * + * @return The user's extended capabilities. + */ +guint msn_user_get_extcaps(const MsnUser *user); + +/** * Returns the network id for a user. * * @param user The user. @@ -406,6 +456,19 @@ void msn_user_set_op(MsnUser *user, int list_op); void msn_user_unset_op(MsnUser *user, int list_op); +/** + * Checks whether a user is capable of some task. + * + * @param user The user. + * @param endpoint The endpoint. Can be @NULL to check overall capabilities. + * @param capability The capability (including client version). + * @param extcap The extended capability. + * + * @return Whether the user supports the capability. + */ +gboolean +msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap); + /*@}*/