Mercurial > pidgin
changeset 21993:bb1190d6961c
Partial support for reading ICQ 6 status notes. The status note will
show up next to the person's name in the buddy list, but only if they
don't have another status message set.
This is from Collin from ComBOTS GmbH. Fixes #3208.
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sat, 05 Jan 2008 07:33:13 +0000 |
parents | cca516eb7610 |
children | 1c60783cfcd9 |
files | ChangeLog libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/family_icq.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h |
diffstat | 5 files changed, 442 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Sat Jan 05 02:16:30 2008 +0000 +++ b/ChangeLog Sat Jan 05 07:33:13 2008 +0000 @@ -7,6 +7,8 @@ * Eliminated unmaintained Howl backend implementation for the Bonjour protocol. Avahi (or Apple's Bonjour runtime on win32) is now required to use Bonjour. + * Partial support for viewing ICQ status notes (Collin from + ComBOTS GmbH). Pidgin: * Added the ability to theme conversation name colors (red and blue)
--- a/libpurple/protocols/oscar/family_icbm.c Sat Jan 05 02:16:30 2008 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Sat Jan 05 07:33:13 2008 +0000 @@ -51,6 +51,9 @@ #include "win32dep.h" #endif +#include "util.h" + + /** * Add a standard ICBM header to the given bstream with the given * information. @@ -2335,11 +2338,166 @@ sn = byte_stream_getstr(bs, snlen); reason = byte_stream_get16(bs); - if (channel == 0x0002) { /* File transfer declined */ + if (channel == 0x0002) + { + /* parse status note text */ + + struct aim_icq_info *info = NULL; + struct aim_icq_info *prev_info = NULL; + char *response = NULL; + char *encoding = NULL; + char *stripped_encoding = NULL; + char *status_note_text = NULL; + char *stripped_status_note_text = NULL; + char *status_note = NULL; + + /* + * TODO: Using a while statement here is kind of an ugly hack + * to be able to use 'break'. We might as well be using + * 'goto'. Should probably get rid of this. + */ + while (reason == 0x0003) /* channel-specific */ + { + guint32 length; + guint16 version; + guint32 capability; + guint8 message_type; + guint16 status_code; + guint16 text_length; + guint32 request_length; + guint32 response_length; + guint32 encoding_length; + PurpleAccount *account; + PurpleBuddy *buddy; + PurplePresence *presence; + PurpleStatus *status; + + for (info = od->icq_info; info != NULL; info = info->next) + { + if (memcmp(&info->icbm_cookie, cookie, 8) == 0) + { + if (prev_info == NULL) + od->icq_info = info->next; + else + prev_info->next = info->next; + + break; + } + + prev_info = info; + } + + if (info == NULL) + break; + + if ((length = byte_stream_getle16(bs)) != 27) + { + purple_debug_misc("oscar", "clientautoresp: incorrect header size; expected 27, received %u.\n", length); + break; + } + if ((version = byte_stream_getle16(bs)) != 9) + { + purple_debug_misc("oscar", "clientautoresp: incorrect version; expected 9, received %u.\n", version); + break; + } + capability = aim_locate_getcaps(od, bs, 0x10); + if (capability != OSCAR_CAPABILITY_EMPTY) + { + purple_debug_misc("oscar", "clientautoresp: plugin ID is not null.\n"); + break; + } + byte_stream_advance(bs, 2); /* unknown */ + byte_stream_advance(bs, 4); /* client capabilities flags */ + byte_stream_advance(bs, 1); /* unknown */ + byte_stream_advance(bs, 2); /* downcouner? */ + + if ((length = byte_stream_getle16(bs)) != 14) + { + purple_debug_misc("oscar", "clientautoresp: incorrect header size; expected 14, received %u.\n", length); + break; + } + byte_stream_advance(bs, 2); /* downcounter? */ + byte_stream_advance(bs, 12); /* unknown */ + + if ((message_type = byte_stream_get8(bs)) != 0x1a) + { + purple_debug_misc("oscar", "clientautoresp: incorrect message type; expected 0x1a, received 0x%x.\n", message_type); + break; + } + byte_stream_advance(bs, 1); /* message flags */ + if ((status_code = byte_stream_getle16(bs)) != 0) + { + purple_debug_misc("oscar", "clientautoresp: incorrect status code; expected 0, received %u.\n", status_code); + break; + } + byte_stream_advance(bs, 2); /* priority code */ + + text_length = byte_stream_getle16(bs); + byte_stream_advance(bs, text_length); /* text */ + + length = byte_stream_getle16(bs); + byte_stream_advance(bs, 18); /* unknown */ + if (length != 18 + 4 + (request_length = byte_stream_getle32(bs)) + 17) + { + purple_debug_misc("oscar", "clientautoresp: incorrect block; expected length is %u, got %u.\n", 18 + 4 + request_length + 17, length); + break; + } + byte_stream_advance(bs, request_length); /* x request */ + byte_stream_advance(bs, 17); /* unknown */ + + length = byte_stream_getle32(bs); + response_length = byte_stream_getle32(bs); + response = byte_stream_getstr(bs, response_length); + if (length != 4 + response_length + 4 + (encoding_length = byte_stream_getle32(bs))) + { + purple_debug_misc("oscar", "clientautoresp: incorrect block; expected length is %u, got %u.\n", 4 + response_length + 4 + encoding_length, length); + break; + } + encoding = byte_stream_getstr(bs, encoding_length); + + account = purple_connection_get_account(od->gc); + stripped_encoding = oscar_encoding_extract(encoding); + status_note_text = oscar_encoding_to_utf8(account, stripped_encoding, response, response_length); + stripped_status_note_text = purple_markup_strip_html(status_note_text); + + if (stripped_status_note_text != NULL && stripped_status_note_text[0] != 0) + status_note = g_strdup_printf("%s: %s", info->status_note_title, stripped_status_note_text); + else + status_note = g_strdup(info->status_note_title); + + buddy = purple_find_buddy(account, sn); + if (buddy == NULL) + { + purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", sn); + break; + } + + purple_debug_misc("oscar", "clientautoresp: setting status message to \"%s\".\n", status_note); + + presence = purple_buddy_get_presence(buddy); + status = purple_presence_get_active_status(presence); + + purple_prpl_got_user_status(account, sn, + purple_status_get_id(status), + "message", status_note, NULL); + + break; + } + + g_free(status_note); + g_free(stripped_status_note_text); + g_free(status_note_text); + g_free(stripped_encoding); + g_free(encoding); + g_free(response); + g_free(info->status_note_title); + g_free(info); + byte_stream_get16(bs); /* Unknown */ byte_stream_get16(bs); /* Unknown */ if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, channel, sn, reason, cookie); + } else if (channel == 0x0004) { /* ICQ message */ switch (reason) { case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */
--- a/libpurple/protocols/oscar/family_icq.c Sat Jan 05 02:16:30 2008 +0000 +++ b/libpurple/protocols/oscar/family_icq.c Sat Jan 05 07:33:13 2008 +0000 @@ -435,6 +435,65 @@ return 0; } +/* + * getstatusnote may be a misleading name because the response + * contains a lot of different information but currently it's only + * used to get that. + */ +int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len) +{ + FlapConnection *conn; + FlapFrame *frame; + aim_snacid_t snacid; + int bslen; + + purple_debug_misc("oscar", "aim_icq_getstatusnote: requesting status note for %s.\n", uin); + + if (!od || !(conn = flap_connection_findbygroup(od, 0x0015))) + { + purple_debug_misc("oscar", "aim_icq_getstatusnote: no connection.\n"); + return -EINVAL; + } + + bslen = 2 + 4 + 2 + 2 + 2 + 2 + 58 + strlen(uin); + + frame = flap_frame_new(od, 0x02, 10 + 4 + bslen); + + snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&frame->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + byte_stream_put16(&frame->data, 0x0001); + byte_stream_put16(&frame->data, bslen); + + byte_stream_putle16(&frame->data, bslen - 2); + byte_stream_putle32(&frame->data, atoi(od->sn)); + byte_stream_putle16(&frame->data, 0x07d0); /* I command thee. */ + byte_stream_putle16(&frame->data, snacid); /* eh. */ + byte_stream_putle16(&frame->data, 0x0fa0); /* shrug. */ + byte_stream_putle16(&frame->data, 58 + strlen(uin)); + + byte_stream_put32(&frame->data, 0x05b90002); /* don't ask */ + byte_stream_put32(&frame->data, 0x80000000); + byte_stream_put32(&frame->data, 0x00000006); + byte_stream_put32(&frame->data, 0x00010002); + byte_stream_put32(&frame->data, 0x00020000); + byte_stream_put32(&frame->data, 0x04e30000); + byte_stream_put32(&frame->data, 0x00020002); + byte_stream_put32(&frame->data, 0x00000001); + + byte_stream_put16(&frame->data, 24 + strlen(uin)); + byte_stream_put32(&frame->data, 0x003c0010); + byte_stream_putraw(&frame->data, note_hash, 16); /* status note hash */ + byte_stream_put16(&frame->data, 0x0032); /* buddy uin */ + byte_stream_put16(&frame->data, strlen(uin)); + byte_stream_putstr(&frame->data, uin); + + flap_connection_send(conn, frame); + + return 0; +} + static void aim_icq_freeinfo(struct aim_icq_info *info) { int i; @@ -467,6 +526,7 @@ g_free(info->workposition); g_free(info->workwebpage); g_free(info->info); + g_free(info->status_note_title); g_free(info); } @@ -641,6 +701,178 @@ info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); /* Then 0x00 02 00 00 00 00 00 */ } break; + + /* status note title and send request for status note text */ + case 0x0fb4: { + GSList *tlvlist; + aim_tlv_t *tlv; + FlapConnection *conn; + char *uin = NULL; + char *status_note_title = NULL; + + conn = flap_connection_findbygroup(od, 0x0004); + if (conn == NULL) + { + purple_debug_misc("oscar", "icq/0x0fb4: flap connection was not found.\n"); + break; + } + + byte_stream_advance(&qbs, 0x02); /* length */ + byte_stream_advance(&qbs, 0x2f); /* unknown stuff */ + + tlvlist = aim_tlvlist_read(&qbs); + + tlv = aim_tlv_gettlv(tlvlist, 0x0032, 1); + if (tlv != NULL) + /* Get user number */ + uin = aim_tlv_getvalue_as_string(tlv); + + tlv = aim_tlv_gettlv(tlvlist, 0x0226, 1); + if (tlv != NULL) + /* Get status note title */ + status_note_title = aim_tlv_getvalue_as_string(tlv); + + aim_tlvlist_free(tlvlist); + + if (uin == NULL || status_note_title == NULL) + { + purple_debug_misc("oscar", "icq/0x0fb4: uin or " + "status_note_title was not found\n"); + g_free(uin); + g_free(status_note_title); + break; + } + + if (status_note_title[0] == '\0') + { + PurpleAccount *account; + PurpleBuddy *buddy; + PurplePresence *presence; + PurpleStatus *status; + + account = purple_connection_get_account(od->gc); + buddy = purple_find_buddy(account, uin); + presence = purple_buddy_get_presence(buddy); + status = purple_presence_get_active_status(presence); + + purple_prpl_got_user_status(account, uin, + purple_status_get_id(status), + "message", NULL, NULL); + + g_free(status_note_title); + } + else + { + struct aim_icq_info *info; + guint32 data_len; + FlapFrame *frame; + aim_snacid_t snacid; + guchar cookie[8]; + + info = g_new0(struct aim_icq_info, 1); + + if (info == NULL) + { + g_free(uin); + g_free(status_note_title); + + break; + } + + data_len = 13 + strlen(uin) + 30 + 6 + 4 + 55 + 85 + 4; + frame = flap_frame_new(od, 0x0002, 10 + 4 + data_len); + snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0); + + aim_putsnac(&frame->data, 0x0004, 0x0006, 0x0000, snacid); + + aim_icbm_makecookie(cookie); + + byte_stream_putraw(&frame->data, cookie, 8); /* ICBM cookie */ + byte_stream_put16(&frame->data, 0x0002); /* message channel */ + byte_stream_put8(&frame->data, strlen(uin)); /* uin */ + byte_stream_putstr(&frame->data, uin); + + byte_stream_put16(&frame->data, 0x0005); /* rendez vous data */ + byte_stream_put16(&frame->data, 0x00b2); + byte_stream_put16(&frame->data, 0x0000); /* request */ + byte_stream_putraw(&frame->data, cookie, 8); /* ICBM cookie */ + byte_stream_put32(&frame->data, 0x09461349); /* ICQ server relaying */ + byte_stream_put16(&frame->data, 0x4c7f); + byte_stream_put16(&frame->data, 0x11d1); + byte_stream_put32(&frame->data, 0x82224445); + byte_stream_put32(&frame->data, 0x53540000); + + byte_stream_put16(&frame->data, 0x000a); /* unknown TLV */ + byte_stream_put16(&frame->data, 0x0002); + byte_stream_put16(&frame->data, 0x0001); + + byte_stream_put16(&frame->data, 0x000f); /* unknown TLV */ + byte_stream_put16(&frame->data, 0x0000); + + byte_stream_put16(&frame->data, 0x2711); /* extended data */ + byte_stream_put16(&frame->data, 0x008a); + byte_stream_putle16(&frame->data, 0x001b); /* length */ + byte_stream_putle16(&frame->data, 0x0009); /* version */ + byte_stream_putle32(&frame->data, 0x00000000); /* plugin: none */ + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_putle16(&frame->data, 0x0000); /* unknown */ + byte_stream_putle32(&frame->data, 0x00000000); /* client capabilities flags */ + byte_stream_put8(&frame->data, 0x00); /* unknown */ + byte_stream_putle16(&frame->data, 0x0064); /* downcounter? */ + byte_stream_putle16(&frame->data, 0x000e); /* length */ + byte_stream_putle16(&frame->data, 0x0064); /* downcounter? */ + byte_stream_putle32(&frame->data, 0x00000000); /* unknown */ + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_put8(&frame->data, 0x1a); /* message type: plugin message descibed by text string */ + byte_stream_put8(&frame->data, 0x00); /* message flags */ + byte_stream_putle16(&frame->data, 0x0000); /* status code */ + byte_stream_putle16(&frame->data, 0x0001); /* priority code */ + byte_stream_putle16(&frame->data, 0x0000); /* text length */ + + byte_stream_put8(&frame->data, 0x3a); /* message dump */ + byte_stream_put32(&frame->data, 0x00811a18); + byte_stream_put32(&frame->data, 0xbc0e6c18); + byte_stream_put32(&frame->data, 0x47a5916f); + byte_stream_put32(&frame->data, 0x18dcc76f); + byte_stream_put32(&frame->data, 0x1a010013); + byte_stream_put32(&frame->data, 0x00000041); + byte_stream_put32(&frame->data, 0x77617920); + byte_stream_put32(&frame->data, 0x53746174); + byte_stream_put32(&frame->data, 0x7573204d); + byte_stream_put32(&frame->data, 0x65737361); + byte_stream_put32(&frame->data, 0x67650100); + byte_stream_put32(&frame->data, 0x00000000); + byte_stream_put32(&frame->data, 0x00000000); + byte_stream_put32(&frame->data, 0x00000000); + byte_stream_put32(&frame->data, 0x00000015); + byte_stream_put32(&frame->data, 0x00000000); + byte_stream_put32(&frame->data, 0x0000000d); + byte_stream_put32(&frame->data, 0x00000074); + byte_stream_put32(&frame->data, 0x6578742f); + byte_stream_put32(&frame->data, 0x782d616f); + byte_stream_put32(&frame->data, 0x6c727466); + + byte_stream_put16(&frame->data, 0x0003); /* server ACK requested */ + byte_stream_put16(&frame->data, 0x0000); + + info->uin = atoi(uin); + info->status_note_title = status_note_title; + + memcpy(&info->icbm_cookie, cookie, 8); + + info->next = od->icq_info; + od->icq_info = info; + + flap_connection_send(conn, frame); + } + + g_free(uin); + + } break; + } /* End switch statement */ if (!(snac->flags & 0x0001)) {
--- a/libpurple/protocols/oscar/oscar.c Sat Jan 05 02:16:30 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.c Sat Jan 05 07:33:13 2008 +0000 @@ -1879,6 +1879,31 @@ g_free(b16); } + /* + * If we didn't receive a status message with the status change, + * or if the message is empty, and we have a note hash, then + * query the ICQ6 status note. + * + * TODO: We should probably always query the status note regardless + * of whether they have a status message set, and we should + * figure out a way to display both the status note and the + * status message at the same time. + */ + if (info->status == NULL || info->status[0] == '\0') + { + struct aim_ssi_item *ssi_item; + aim_tlv_t *note_hash; + + ssi_item = aim_ssi_itemlist_finditem(od->ssi.local, + NULL, info->sn, AIM_SSI_TYPE_BUDDY); + if (ssi_item != NULL) + { + note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); + if (note_hash != NULL) + aim_icq_getstatusnote(od, info->sn, note_hash->value, note_hash->length); + } + } + return 1; } @@ -4520,12 +4545,11 @@ /* This is needed for us to un-set any previous away message. */ away = g_strdup(""); } - else if ((primitive == PURPLE_STATUS_AWAY) || - (primitive == PURPLE_STATUS_EXTENDED_AWAY)) + else { htmlaway = purple_status_get_attr_string(status, "message"); if ((htmlaway == NULL) || (*htmlaway == '\0')) - htmlaway = _("Away"); + htmlaway = purple_status_type_get_name(status_type); away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding); if (awaylen > od->rights.maxawaymsglen) @@ -5099,6 +5123,8 @@ char *gname, *gname_utf8, *alias, *alias_utf8; PurpleBuddy *b; PurpleGroup *g; + struct aim_ssi_item *ssi_item; + aim_tlv_t *note_hash; va_list ap; guint16 snac_subtype, type; const char *name; @@ -5166,6 +5192,21 @@ } + ssi_item = aim_ssi_itemlist_finditem(od->ssi.local, + gname, name, AIM_SSI_TYPE_BUDDY); + if (ssi_item != NULL) + { + note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); + if (note_hash != NULL) + aim_icq_getstatusnote(od, name, note_hash->value, note_hash->length); + } + else + { + purple_debug_error("oscar", "purple_ssi_parseaddmod: " + "Could not find ssi item for oncoming buddy %s, " + "group %s\n", name, gname); + } + g_free(gname_utf8); g_free(alias_utf8);
--- a/libpurple/protocols/oscar/oscar.h Sat Jan 05 02:16:30 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.h Sat Jan 05 07:33:13 2008 +0000 @@ -3,8 +3,6 @@ * This file is the legal property of its developers. * Please see the AUTHORS file distributed alongside this file. * - * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com> - * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -1329,6 +1327,10 @@ /* we keep track of these in a linked list because we're 1337 */ struct aim_icq_info *next; + + /* status note info */ + guint8 icbm_cookie[8]; + char *status_note_title; }; int aim_icq_reqofflinemsgs(OscarData *od); @@ -1339,7 +1341,7 @@ int aim_icq_getalias(OscarData *od, const char *uin); int aim_icq_getallinfo(OscarData *od, const char *uin); int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias); - +int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len); /* 0x0017 - family_auth.c */