Mercurial > pidgin.yaz
changeset 29812:365b90fa23cf
This patch comes from the combined work of contributors minstrel, NightFox,
bob007, salieff, and nops (these are their trac usernames). I have made
some minor tweaks to the patch, but these shouldn't be a problem. This patch
needs some TLC before we can merge it anywhere else; it adds API so it must
hit im.pidgin.pidgin.next.minor before hitting im.pidgin.pidgin. Refs #4508.
author | John Bailey <rekkanoryo@rekkanoryo.org> |
---|---|
date | Thu, 13 Nov 2008 17:04:53 +0000 |
parents | f18f37a62971 |
children | 297d83036107 |
files | libpurple/protocols/oscar/family_buddy.c libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/family_locate.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h libpurple/protocols/oscar/tlv.c libpurple/request.c libpurple/request.h pidgin/gtkrequest.c pidgin/pixmaps/Makefile.am |
diffstat | 10 files changed, 950 insertions(+), 38 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/protocols/oscar/family_buddy.c Thu Nov 13 09:00:45 2008 +0000 +++ b/libpurple/protocols/oscar/family_buddy.c Thu Nov 13 17:04:53 2008 +0000 @@ -224,6 +224,10 @@ if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && userinfo.flags & AIM_FLAG_AWAY) aim_locate_autofetch_away_message(od, userinfo.sn); + if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && + userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ && userinfo.customicon > 0) + icq_im_xstatus_request(od, userinfo.sn); + aim_info_free(&userinfo); return ret;
--- a/libpurple/protocols/oscar/family_icbm.c Thu Nov 13 09:00:45 2008 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Thu Nov 13 17:04:53 2008 +0000 @@ -2533,6 +2533,15 @@ char *sn; guchar *cookie; guint8 snlen; + char *xml = NULL; + int hdrlen; + int curpos; + int num1,num2; + char *desc, *title, *temp; + PurpleAccount *account; + PurpleBuddy *buddy; + PurplePresence *presence; + PurpleStatus *status; cookie = byte_stream_getraw(bs, 8); channel = byte_stream_get16(bs); @@ -2540,16 +2549,56 @@ sn = byte_stream_getstr(bs, snlen); reason = byte_stream_get16(bs); - if (channel == 0x0002) - { - if (reason == 0x0003) /* channel-specific */ - /* parse status note text */ - parse_status_note_text(od, cookie, sn, bs); - - 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); + if (channel == 0x0002) { + + hdrlen = byte_stream_getle16(bs); + if ( ((hdrlen == 27 ) && (bs->len > (27 + 51)))) { + byte_stream_advance(bs, 51); + num1 = byte_stream_getle16(bs); + num2 = byte_stream_getle16(bs); + purple_debug_misc("oscar", "X-Status: Num1 %i, num2 %i\n",num1, num2); + + if(((num1 == 0x4f00)&&(num2 == 0x3b00))) { + byte_stream_advance(bs, 86); + curpos = byte_stream_curpos(bs); + xml = byte_stream_getstr(bs, bs->len - curpos); + purple_debug_misc("oscar", "X-Status: Received XML reply\n"); + if(xml) { + /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", (const char*) xml); */ + if ((desc=strstr(xml,"<desc>")) != NULL) { + temp=strstr(xml,"</desc>"); + temp[0]=0; + desc=desc+12; + } + if ((title=strstr(xml,"<title>")) != NULL) { + temp=strstr(xml,"</title>"); + temp[0]=0; + title=title+13; + } else { + title=""; + } + strcpy(xml,title); + if (desc) { + strcat(xml, " - "); + strcat(xml, desc); + } + purple_debug_misc("oscar", "X-Status reply: %s\n", (const char*)xml); + account = purple_connection_get_account(od->gc); + buddy = purple_find_buddy(account, sn); + 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", xml, NULL); + } else { + purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n"); + } + } else { + purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n" ); + /* if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) + ret = userfunc(od, conn, frame, channel, sn, reason); */ + } + + } } else if (channel == 0x0004) { /* ICQ message */ switch (reason) { @@ -2608,6 +2657,7 @@ g_free(cookie); g_free(sn); + if (xml) g_free(xml); return ret; } @@ -2721,6 +2771,181 @@ } /* + * Subtype 0x0006 - Send eXtra Status request + */ +int icq_im_xstatus_request(OscarData *od, const char *sn) +{ + FlapConnection *conn; + aim_snacid_t snacid; + guchar cookie[8]; + GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; + ByteStream bs, header, plugindata; + PurpleAccount *account; + const char *fmt; + char *statxml; + int xmllen; + + static const guint8 pluginid[] = + { + 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00 + }; + + static const guint8 c_plugindata[] = + { + 0x1B, 0x00, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C, + 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74, + 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, + 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00 + }; + + if (!od || !(conn = flap_connection_findbygroup(od, 0x0004))) + return -EINVAL; + + if (!sn) + return -EINVAL; + + fmt = "<N><QUERY><Q><PluginID>srvMng</PluginID></Q></QUERY><NOTIFY><srv><id>cAwaySrv</id><req><id>AwayStat</id><trans>2</trans><senderId>%s</senderId></req></srv></NOTIFY></N>\r\n"; + + account = purple_connection_get_account(od->gc); + xmllen = strlen(fmt) - 2 + strlen(account->username); + + statxml = (char*) g_malloc(xmllen); + snprintf(statxml, xmllen, fmt, account->username); + + aim_icbm_makecookie(cookie); + + byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + + 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2 + + 2 + 2 + sizeof(c_plugindata) + xmllen + + 2 + 2); + + snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_im_puticbm(&bs, cookie, 0x0002, sn); + + byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */ + byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen)); + + byte_stream_put16(&header, 0x0000); /* Message Type: Request */ + byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */ + byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */ + + aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001); + aim_tlvlist_add_noval(&inner_tlvlist, 0x000f); + + /* Add Plugin Specific Data */ + byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */ + byte_stream_putstr(&plugindata, statxml); + + aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data); + + aim_tlvlist_write(&header, &inner_tlvlist); + + + aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data); + aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); /* Empty TLV 0x0003 */ + + aim_tlvlist_write(&bs, &outer_tlvlist); + + purple_debug_misc("oscar", "X-Status Request\n"); + flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, TRUE); + + aim_tlvlist_free(inner_tlvlist); + aim_tlvlist_free(outer_tlvlist); + byte_stream_destroy(&header); + byte_stream_destroy(&plugindata); + byte_stream_destroy(&bs); + g_free(statxml); + + return 0; +} + +int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie) +{ + FlapConnection *conn; + ByteStream bs; + aim_snacid_t snacid; + PurpleAccount *account; + PurpleStatus *status; + const char *fmt; + const char *formatted_msg; + char *msg; + char *statxml; + const char *title; + int len; + + static const guint8 plugindata[] = { + 0x1B, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F, + 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, + 0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75, + 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74, + 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00 + }; + + fmt = "<NR><RES><ret event='OnRemoteNotification'><srv><id>cAwaySrv</id><val srv_id='cAwaySrv'><Root><CASXtraSetAwayMessage></CASXtraSetAwayMessage>&l t;uin>%s</uin><index>1</index><title>%s</title><desc>%s</desc></Root></val></srv><srv><id>cRandomizerSrv</id><val srv_id='cRandomizerSrv'>undefined</val></srv></ret></RES></NR>\r\n"; + + + if (!od || !(conn = flap_connection_findbygroup(od, 0x0002))) + return -EINVAL; + + if (!sn) + return -EINVAL; + + account = purple_connection_get_account(od->gc); + if(!account) return -EINVAL; + +/* if (!strcmp(account->username, sn)) + icq_im_xstatus_request(od, sn); */ + + status = purple_presence_get_active_status(account->presence); + if (!status) return -EINVAL; + title = purple_status_get_name(status); + if (!title) return -EINVAL; + formatted_msg = purple_status_get_attr_string(status, "message"); + if (!formatted_msg) return -EINVAL; + msg = purple_markup_strip_html(formatted_msg); + if (!msg) return -EINVAL; + len = strlen(fmt)-6+strlen(account->username)+strlen(title)+strlen(msg); + statxml = (char*) g_malloc(len); + + snprintf(statxml, len, fmt, + account->username, title, msg); + + purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg); + + byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + strlen(statxml)); /* 16 extra */ + + snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0); + aim_im_puticbm(&bs, cookie, 0x0002, sn); + byte_stream_put16(&bs, 0x0003); + byte_stream_putraw(&bs, plugindata, sizeof(plugindata)); + byte_stream_putraw(&bs, (const guint8*)statxml, strlen(statxml)); + + flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, 0x0000, snacid, &bs, TRUE); + + g_free(statxml); + g_free(msg); + byte_stream_destroy(&bs); + + return 0; +} + +/* * Subtype 0x0014 - Receive a mini typing notification (mtn) packet. * * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
--- a/libpurple/protocols/oscar/family_locate.c Thu Nov 13 09:00:45 2008 +0000 +++ b/libpurple/protocols/oscar/family_locate.c Thu Nov 13 17:04:53 2008 +0000 @@ -166,6 +166,16 @@ {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + /* New format of caps (xtraz icons) */ + {OSCAR_CAPABILITY_NEWCAPS, + {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* Support xtraz statuses */ + {OSCAR_CAPABILITY_XTRAZ, + {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xFD, 0x4e, 0xc5, + 0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}}, + {OSCAR_CAPABILITY_SENDBUDDYLIST, {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, @@ -242,6 +252,155 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, }; +#define AIM_CUSTOM_ICONS_COUNT 35 + +static const struct { + char *filename; + char *descriptivename; + guint8 data[16]; +} aim_custom_icons[AIM_CUSTOM_ICONS_COUNT] = { + /* empty X-Status for the case when customicon == 0 */ + {NULL, NULL, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + + {"xstatus_thinking", "Thinking", + {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60, + 0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}}, + + {"xstatus_busy", "Busy", + {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08, + 0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}}, + + {"xstatus_shopping", "Shopping", + {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff, + 0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}}, + + {"xstatus_typing", "Typing", + {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1, + 0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}}, + + {"xstatus_question", "Question Mark", + {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0, + 0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}}, + + {"xstatus_angry", "Angry", + {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a, + 0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}}, + + {"xstatus_eating", "Eating", + {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42, + 0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}}, + + {"xstatus_cinema", "Cinema", + {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4, + 0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}}, + + {"xstatus_sick", "Sick", + {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60, + 0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}}, + + {"xstatus_crap", "Unknown 1", + {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70, + 0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}}, + + {"xstatus_iron", "Unknown 2", + {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27, + 0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}}, + + {"xstatus_bathing", "Bathing", + {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c, + 0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}}, + + {"xstatus_tv", "Watching TV", + {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76, + 0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}}, + + {"xstatus_fun", "Having fun", + {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff, + 0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}}, + + {"xstatus_sleeping", "Sleeping", + {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65, + 0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}}, + + {"xstatus_pda", "PDA device", + {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9, + 0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}}, + + {"xstatus_heart", "In love", + {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48, + 0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}}, + + {"xstatus_tired", "Tired", + {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78, + 0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}}, + + {"xstatus_friends", "Friends", + {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d, + 0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}}, + + {"xstatus_phone", "On the phone", + {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66, + 0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}}, + + {"xstatus_surfing", "Surfing", + {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4, + 0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}}, + + {"xstatus_mobile", "Cell phone", + {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3, + 0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}}, + + {"xstatus_google", "Googling", + {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5, + 0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}}, + + {"xstatus_party", "Party", + {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1, + 0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}}, + + {"xstatus_coffee", "Coffee", + {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38, + 0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}}, + + {"xstatus_gaming", "Playing", + {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0, + 0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}}, + + {"xstatus_internet", "Internet", + {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e, + 0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}}, + + {"xstatus_zzz", "Snoozing", + {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17, + 0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}}, + + {"xstatus_writing", "Writing", + {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd, + 0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}}, + + {"xstatus_beer", "Drinking", + {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86, + 0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}}, + + {"xstatus_music", "Music", + {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d, + 0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}}, + + {"xstatus_studying", "Studying", + {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6, + 0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}}, + + {"xstatus_engineering", "Engineering", + {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b, + 0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}}, + + {"xstatus_crapping", "In the restroom", + {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35, + 0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}} +}; + /* * Add the userinfo to our linked list. If we already have userinfo * for this buddy, then just overwrite parts of the old data. @@ -278,6 +437,9 @@ cur->sessionlen = userinfo->sessionlen; if (userinfo->capabilities != 0) cur->capabilities = userinfo->capabilities; + if (userinfo->customicon != 0) + cur->customicon = userinfo->customicon; + cur->present |= userinfo->present; if (userinfo->iconcsumlen > 0) { @@ -469,13 +631,38 @@ cap[8], cap[9], cap[10], cap[11], cap[12], cap[13], cap[14], cap[15]); - g_free(cap); } return flags; } +gint32 +aim_get_custom_icon(OscarData *od, ByteStream *bs, int len) +{ + int offset; + gint32 result = -1; + + for (offset = 0; byte_stream_empty(bs) && (offset < len); offset += 0x10) { + /* check wheather this capability is a custom user icon */ + guint8 *cap; + int i; + + cap = byte_stream_getraw(bs, 0x10); + + for (i = 1; i < AIM_CUSTOM_ICONS_COUNT; i++) { + if (memcmp(&aim_custom_icons[i].data, cap, 0x10) == 0) { + purple_debug_misc("oscar", "Custom user icon: %s\n", aim_custom_icons[i].descriptivename); + result = i; + break; /* should only match once... */ + } + } + g_free(cap); + } + + return result; +} + guint32 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len) { @@ -566,6 +753,37 @@ g_free(info->away_encoding); } +#define ICQMOODS_COUNT 23 + +static const struct { + char *mood; + gint32 icon_num; +} icqmoods[ICQMOODS_COUNT] = { + {"icqmood1", 12}, + {"icqmood2", 18}, + {"icqmood3", 24}, + {"icqmood4", 30}, + {"icqmood5", 1}, + {"icqmood6", 7}, + {"icqmood7", 13}, + {"icqmood8", 19}, + {"icqmood9", 25}, + {"icqmood10", 31}, + {"icqmood11", 11}, + {"icqmood12", 8}, + {"icqmood13", 14}, + {"icqmood14", 20}, + {"icqmood15", 26}, + {"icqmood16", 32}, + {"icqmood17", 9}, + {"icqmood18", 15}, + {"icqmood19", 21}, + {"icqmood20", 27}, + {"icqmood21", 33}, + {"icqmood22", 10}, + {"icqmood23", 6}, +}; + /* * AIM is fairly regular about providing user info. This is a generic * routine to extract it in its standard form. @@ -606,11 +824,12 @@ for (curtlv = 0; curtlv < tlvcnt; curtlv++) { guint16 type, length; int endpos; + int curpos; type = byte_stream_get16(bs); length = byte_stream_get16(bs); - - endpos = byte_stream_curpos(bs) + MIN(length, byte_stream_empty(bs)); + curpos = byte_stream_curpos(bs); + endpos = curpos + MIN(length, byte_stream_empty(bs)); if (type == 0x0001) { /* @@ -725,6 +944,8 @@ */ outinfo->capabilities |= aim_locate_getcaps(od, bs, length); outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; + byte_stream_setpos(bs, curpos); + outinfo->customicon = aim_get_custom_icon(od, bs, length); } else if (type == 0x000e) { /* @@ -853,6 +1074,27 @@ outinfo->itmsurl_encoding = NULL; } } break; + + case 0x000e: { /* ICQ mood */ + char *mood; + gint32 i; + gint32 icon_num = -1; + + mood = byte_stream_getstr(bs, length2); + + for (i = 0; i < ICQMOODS_COUNT; i++) + if (!strcmp(mood, icqmoods[i].mood)) { + icon_num = icqmoods[i].icon_num; + break; /* should only match once... */ + } + + if (icon_num >= 0) + outinfo->customicon = icon_num; + else + purple_debug_warning("oscar", "Unknown icqmood: %s", mood); + + g_free(mood); + } break; } /* Save ourselves. */ @@ -933,7 +1175,7 @@ #endif if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) - aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities); + aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities, info->customicon); if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); @@ -1140,7 +1382,7 @@ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) return -EINVAL; - aim_tlvlist_add_caps(&tlvlist, 0x0005, caps); + aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, purple_account_get_int(purple_connection_get_account(od->gc), "customicon", -1)); byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); @@ -1226,6 +1468,8 @@ ByteStream cbs; byte_stream_init(&cbs, tlv->value, tlv->length); userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length); + byte_stream_rewind(&cbs); + userinfo->customicon = aim_get_custom_icon(od, &cbs, tlv->length); userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; } aim_tlvlist_free(tlvlist); @@ -1453,3 +1697,33 @@ return 0; } + +guint32 +aim_get_custom_icons_count() +{ + return AIM_CUSTOM_ICONS_COUNT; +} + +char* +aim_get_custom_icon_filename(gint32 no) +{ + if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1) + return NULL; + return aim_custom_icons[no].filename; +} + +char* +aim_get_custom_icon_descriptivename(gint32 no) +{ + if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1) + return NULL; + return aim_custom_icons[no].descriptivename; +} + +guint8* +aim_get_custom_icon_data(gint32 no) +{ + if (no >= AIM_CUSTOM_ICONS_COUNT || no < 1) + return NULL; + return (guint8 *)aim_custom_icons[no].data; +}
--- a/libpurple/protocols/oscar/oscar.c Thu Nov 13 09:00:45 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.c Thu Nov 13 17:04:53 2008 +0000 @@ -59,6 +59,11 @@ #define OSCAR_STATUS_ID_NA "na" #define OSCAR_STATUS_ID_OCCUPIED "occupied" #define OSCAR_STATUS_ID_FREE4CHAT "free4chat" +#define OSCAR_STATUS_ID_EVIL "evil" +#define OSCAR_STATUS_ID_DEPRESSION "depression" +#define OSCAR_STATUS_ID_ATHOME "athome" +#define OSCAR_STATUS_ID_ATWORK "atwork" +#define OSCAR_STATUS_ID_LUNCH "lunch" #define OSCAR_STATUS_ID_CUSTOM "custom" #define OSCAR_STATUS_ID_MOBILE "mobile" @@ -68,7 +73,7 @@ static OscarCapability purple_caps = (OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM | OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE | - OSCAR_CAPABILITY_SHORTCAPS); + OSCAR_CAPABILITY_SHORTCAPS | OSCAR_CAPABILITY_ICQSERVERRELAY | OSCAR_CAPABILITY_NEWCAPS | OSCAR_CAPABILITY_XTRAZ); static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02}; static guint8 features_icq[] = {0x01, 0x06}; @@ -670,6 +675,9 @@ case OSCAR_CAPABILITY_GAMES: case OSCAR_CAPABILITY_GAMES2: tmp = _("Games"); + case OSCAR_CAPABILITY_XTRAZ: + case OSCAR_CAPABILITY_NEWCAPS: + tmp = _("ICQ Xtraz"); break; case OSCAR_CAPABILITY_ADDINS: tmp = _("Add-Ins"); @@ -750,9 +758,17 @@ return g_strdup_printf(_("Away")); else if (state & AIM_ICQ_STATE_WEBAWARE) return g_strdup_printf(_("Web Aware")); - else if (state & AIM_ICQ_STATE_INVISIBLE) - return g_strdup_printf(_("Invisible")); - else + else if (state & AIM_ICQ_STATE_EVIL) + return g_strdup_printf(_("Evil")); + else if (state & AIM_ICQ_STATE_DEPRESSION) + return g_strdup_printf(_("Depression")); + else if (state & AIM_ICQ_STATE_ATHOME) + return g_strdup_printf(_("At home")); + else if (state & AIM_ICQ_STATE_ATWORK) + return g_strdup_printf(_("At work")); + else if (state & AIM_ICQ_STATE_LUNCH) + return g_strdup_printf(_("At lunch")); + else return g_strdup_printf(_("Online")); } @@ -1992,6 +2008,16 @@ status_id = OSCAR_STATUS_ID_AWAY; else if (type & AIM_ICQ_STATE_INVISIBLE) status_id = OSCAR_STATUS_ID_INVISIBLE; + else if (type & AIM_ICQ_STATE_EVIL) + status_id = OSCAR_STATUS_ID_EVIL; + else if (type & AIM_ICQ_STATE_DEPRESSION) + status_id = OSCAR_STATUS_ID_DEPRESSION; + else if (type & AIM_ICQ_STATE_ATHOME) + status_id = OSCAR_STATUS_ID_ATHOME; + else if (type & AIM_ICQ_STATE_ATWORK) + status_id = OSCAR_STATUS_ID_ATWORK; + else if (type & AIM_ICQ_STATE_LUNCH) + status_id = OSCAR_STATUS_ID_LUNCH; else status_id = OSCAR_STATUS_ID_AVAILABLE; } else { @@ -2308,7 +2334,9 @@ { PurpleConnection *gc; PurpleAccount *account; + PurpleMessageFlags flags = 0; char *message = NULL; + char *rtfmsg = NULL; g_return_val_if_fail(od != NULL, 0); g_return_val_if_fail(od->gc != NULL, 0); @@ -2338,6 +2366,21 @@ } } + + if (args->info.rtfmsg.rtfmsg != NULL) + { + if (args->encoding != NULL) + { + char *encoding = NULL; + encoding = oscar_encoding_extract(args->encoding); + rtfmsg = oscar_encoding_to_utf8(account, encoding, args->info.rtfmsg.rtfmsg, + strlen(args->info.rtfmsg.rtfmsg)); + g_free(encoding); + } else { + if (g_utf8_validate(args->info.rtfmsg.rtfmsg, strlen(args->info.rtfmsg.rtfmsg), NULL)) + rtfmsg = g_strdup(args->info.rtfmsg.rtfmsg); + } + } if (args->type & OSCAR_CAPABILITY_CHAT) { char *encoding, *utf8name, *tmp; @@ -2425,6 +2468,21 @@ { purple_debug_error("oscar", "Got an ICQ Server Relay message of " "type %d\n", args->info.rtfmsg.msgtype); + purple_debug_error("oscar", "Sending X-Status Reply\n"); + + if(args->info.rtfmsg.msgtype == 26) + icq_relay_xstatus(od, userinfo->sn, args->cookie); + + if(args->info.rtfmsg.msgtype == 1) + { + if(rtfmsg) + serv_got_im(gc, userinfo->sn, rtfmsg, flags, + time(NULL)); + else + serv_got_im(gc, userinfo->sn, args->info.rtfmsg.rtfmsg, flags, + time(NULL)); + + } } else @@ -2828,7 +2886,6 @@ args = va_arg(ap, struct aim_incomingim_ch4_args *); ret = incomingim_chan4(od, conn, userinfo, args, 0); } break; - default: { purple_debug_warning("oscar", "ICBM received on unsupported channel (channel " @@ -2976,6 +3033,28 @@ } break; + case 0x0006: { /* Reply from an ICQ status message request */ + char *statusmsg, **splitmsg; + PurpleNotifyUserInfo *user_info; + + /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */ + statusmsg = oscar_icqstatus(state); + splitmsg = g_strsplit(msg, "\r\n", 0); + + user_info = purple_notify_user_info_new(); + + purple_notify_user_info_add_pair(user_info, _("UIN"), who); + purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg); + purple_notify_user_info_add_section_break(user_info); + purple_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg)); + + g_free(statusmsg); + g_strfreev(splitmsg); + + purple_notify_userinfo(gc, who, user_info, NULL, NULL); + purple_notify_user_info_destroy(user_info); + + } break; default: { purple_debug_warning("oscar", "Received an unknown client auto-response from %s. " @@ -4629,10 +4708,22 @@ data |= AIM_ICQ_STATE_CHAT; else if (!strcmp(status_id, OSCAR_STATUS_ID_INVISIBLE)) data |= AIM_ICQ_STATE_INVISIBLE; + else if (!strcmp(status_id, OSCAR_STATUS_ID_EVIL)) + data |= AIM_ICQ_STATE_EVIL; + else if (!strcmp(status_id, OSCAR_STATUS_ID_DEPRESSION)) + data |= AIM_ICQ_STATE_DEPRESSION; + else if (!strcmp(status_id, OSCAR_STATUS_ID_ATWORK)) + data |= AIM_ICQ_STATE_ATWORK; + else if (!strcmp(status_id, OSCAR_STATUS_ID_ATHOME)) + data |= AIM_ICQ_STATE_ATHOME; + else if (!strcmp(status_id, OSCAR_STATUS_ID_LUNCH)) + data |= AIM_ICQ_STATE_LUNCH; else if (!strcmp(status_id, OSCAR_STATUS_ID_CUSTOM)) data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY; + aim_srv_setextrainfo(od, TRUE, data, FALSE, NULL, NULL); + } static void @@ -5777,6 +5868,7 @@ return "secure"; if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY) return "birthday"; + return aim_get_custom_icon_filename(userinfo->customicon); } return NULL; } @@ -5998,9 +6090,53 @@ purple_value_new(PURPLE_TYPE_STRING), NULL); status_types = g_list_prepend(status_types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, OSCAR_STATUS_ID_FREE4CHAT, - _("Free For Chat"), TRUE, is_icq, FALSE); + _("Free For Chat"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + + status_types = g_list_prepend(status_types, type); + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_EVIL, + _("Evil"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_DEPRESSION, + _("Depression"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_ATHOME, + _("At home"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_ATWORK, + _("At work"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + + status_types = g_list_prepend(status_types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_LUNCH, + _("Lunch"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, @@ -6010,9 +6146,12 @@ purple_value_new(PURPLE_TYPE_STRING), NULL); status_types = g_list_prepend(status_types, type); - type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, + type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE, OSCAR_STATUS_ID_INVISIBLE, - NULL, TRUE, TRUE, FALSE); + NULL, TRUE, TRUE, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, OSCAR_STATUS_ID_MOBILE, NULL, FALSE, FALSE, TRUE); @@ -6180,6 +6319,23 @@ aim_locate_getinfoshort(gc->proto_data, purple_buddy_get_name(buddy), 0x00000003); } +static void oscar_get_icqxstatusmsg (PurpleBlistNode *node, gpointer ignore) +{ + PurpleBuddy *buddy; + PurpleConnection *gc; + PurpleAccount *account; + + + g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); + + buddy = (PurpleBuddy *)node; + gc = purple_account_get_connection(buddy->account); + account = purple_connection_get_account(gc); + purple_debug_info("oscar", "Manual X-Status Get From %s to %s:\n", purple_buddy_get_name(buddy), account->username); + + icq_im_xstatus_request(gc->proto_data, purple_buddy_get_name(buddy)); +} + static GList * oscar_buddy_menu(PurpleBuddy *buddy) { @@ -6207,15 +6363,14 @@ NULL, NULL); menu = g_list_prepend(menu, act); -#if 0 if (od->icq) { - act = purple_menu_action_new(_("Get Status Msg"), - PURPLE_CALLBACK(oscar_get_icqstatusmsg), + act = purple_menu_action_new(_("Get X-Status Msg"), + PURPLE_CALLBACK(oscar_get_icqxstatusmsg), NULL, NULL); menu = g_list_prepend(menu, act); } -#endif + if (userinfo && aim_sncmp(purple_account_get_username(buddy->account), buddy->name) && @@ -6599,6 +6754,79 @@ purple_xfer_request(xfer); } +static void +oscar_show_icq_custom_icons_cb(PurpleConnection *gc, PurpleRequestFields *fields) { + OscarData *od = gc->proto_data; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleRequestField *f; + GList *l; + + f = purple_request_fields_get_field(fields, "customicon"); + l = purple_request_field_list_get_selected(f); + + if (l) { + gpointer d = purple_request_field_list_get_data(f, l->data); + purple_account_set_int(account, "customicon", GPOINTER_TO_INT(d)); + } + + aim_locate_setcaps(od, purple_caps); +} + +static void +oscar_show_icq_custom_icons(PurplePluginAction *action) +{ + guint32 i; + gint32 customicon; + PurpleConnection *gc = (PurpleConnection *) action->context; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleRequestFields *fields; + PurpleRequestFieldGroup *g; + PurpleRequestField *f; + char* na_fn; + + customicon = purple_account_get_int(account, "customicon", 0); + + fields = purple_request_fields_new(); + + g = purple_request_field_group_new(NULL); + + f = purple_request_field_list_new("customicon", _("XStatus")); + + purple_request_field_list_set_pixbuf(f, TRUE); + + na_fn = g_build_filename("pixmaps", "pidgin", "emblems", "16", "not-authorized.png", NULL); + + purple_request_field_list_add_icon(f, _("None"), na_fn, GINT_TO_POINTER(-1)); + if (customicon == 0) + purple_request_field_list_add_selected(f, _("None")); + + g_free(na_fn); + + for (i = 1; i < aim_get_custom_icons_count(); i++) { + char* icon_path = g_strdup_printf("%s.png", aim_get_custom_icon_filename(i)); + char* filename = g_build_filename("pixmaps", "pidgin", "emblems", "16", icon_path, NULL); + + purple_request_field_list_add_icon(f, _(aim_get_custom_icon_descriptivename(i)), filename, GINT_TO_POINTER(i)); + + if (customicon == i) + purple_request_field_list_add_selected(f, _(aim_get_custom_icon_descriptivename(i))); + + g_free(filename); + g_free(icon_path); + } + purple_request_field_group_add_field(g, f); + + purple_request_fields_add_group(fields, g); + + purple_request_fields(gc, _("Set Custom Icon"), _("Set Custom Icon"), + NULL, fields, + _("OK"), G_CALLBACK(oscar_show_icq_custom_icons_cb), + _("Cancel"), NULL, + purple_connection_get_account(gc), NULL, NULL, + gc); +} + + GList * oscar_actions(PurplePlugin *plugin, gpointer context) { @@ -6641,6 +6869,10 @@ act = purple_plugin_action_new(_("Set Privacy Options..."), oscar_show_icq_privacy_opts); menu = g_list_prepend(menu, act); + + act = purple_plugin_action_new(_("Set Custom Icon..."), + oscar_show_icq_custom_icons); + menu = g_list_prepend(menu, act); } else {
--- a/libpurple/protocols/oscar/oscar.h Thu Nov 13 09:00:45 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.h Thu Nov 13 17:04:53 2008 +0000 @@ -362,8 +362,10 @@ OSCAR_CAPABILITY_LIVEVIDEO = 0x02000000, OSCAR_CAPABILITY_CAMERA = 0x04000000, OSCAR_CAPABILITY_ICHAT_SCREENSHARE = 0x08000000, - OSCAR_CAPABILITY_GENERICUNKNOWN = 0x10000000, - OSCAR_CAPABILITY_LAST = 0x20000000 + OSCAR_CAPABILITY_NEWCAPS = 0x10000000, + OSCAR_CAPABILITY_XTRAZ = 0x20000000, + OSCAR_CAPABILITY_GENERICUNKNOWN = 0x40000000, + OSCAR_CAPABILITY_LAST = 0x80000000 } OscarCapability; /* @@ -557,6 +559,12 @@ #define AIM_ICQ_STATE_OUT 0x00000004 #define AIM_ICQ_STATE_BUSY 0x00000010 #define AIM_ICQ_STATE_CHAT 0x00000020 +#define AIM_ICQ_STATE_EVIL 0x00003000 +#define AIM_ICQ_STATE_DEPRESSION 0x00004000 +#define AIM_ICQ_STATE_ATHOME 0x00005000 +#define AIM_ICQ_STATE_ATWORK 0x00006000 +#define AIM_ICQ_STATE_LUNCH 0x00002001 +#define AIM_ICQ_STATE_EVIL 0x00003000 #define AIM_ICQ_STATE_INVISIBLE 0x00000100 #define AIM_ICQ_STATE_WEBAWARE 0x00010000 #define AIM_ICQ_STATE_HIDEIP 0x00020000 @@ -966,6 +974,7 @@ /* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *sn, const guchar *cookie, guint16 code); /* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od); /* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *sn, guint16 type2); +/* 0x000b */ int icq_relay_xstatus (OscarData *od, const char *sn, const guchar* cookie); void aim_icbm_makecookie(guchar* cookie); gchar *oscar_encoding_extract(const char *encoding); gchar *oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen); @@ -1019,6 +1028,7 @@ guint32 onlinesince; /* time_t */ guint32 sessionlen; /* in seconds */ guint32 capabilities; + gint32 customicon; struct { guint32 status; guint32 ipaddr; @@ -1097,8 +1107,12 @@ void aim_info_free(aim_userinfo_t *); int aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *); int aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info); - - +gint32 aim_get_custom_icon(OscarData *od, ByteStream *bs, int len); +guint32 aim_get_custom_icons_count(void); +char* aim_get_custom_icon_filename(gint32 no); +char* aim_get_custom_icon_descriptivename(gint32 no); +guint8* aim_get_custom_icon_data(gint32 no); +int icq_im_xstatus_request(OscarData *od, const char *sn); /* 0x0003 - family_buddy.c */ /* 0x0002 */ void aim_buddylist_reqrights(OscarData *, FlapConnection *); @@ -1428,7 +1442,7 @@ int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value); int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value); int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value); -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps); +int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, gint32 customicon); int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo); int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance); int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tl);
--- a/libpurple/protocols/oscar/tlv.c Thu Nov 13 09:00:45 2008 +0000 +++ b/libpurple/protocols/oscar/tlv.c Thu Nov 13 17:04:53 2008 +0000 @@ -407,10 +407,11 @@ * @param caps Bitfield of capability flags to send * @return The size of the value added. */ -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps) +int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, const gint32 customicon) { guint8 buf[256]; /* TODO: Don't use a fixed length buffer */ ByteStream bs; + guint8 *data; if (caps == 0) return 0; /* nothing there anyway */ @@ -418,6 +419,12 @@ byte_stream_init(&bs, buf, sizeof(buf)); byte_stream_putcaps(&bs, caps); + + /* adding of custom icon GUID */ + data = aim_get_custom_icon_data(customicon); + if (data != NULL) { + byte_stream_putraw(&bs, data, 16); + } return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf); }
--- a/libpurple/request.c Thu Nov 13 09:00:45 2008 +0000 +++ b/libpurple/request.c Thu Nov 13 17:04:53 2008 +0000 @@ -790,6 +790,25 @@ } void +purple_request_field_list_set_pixbuf(PurpleRequestField *field, + gboolean pixbuf) +{ + g_return_if_fail(field != NULL); + g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST); + + field->u.list.pixbuf = pixbuf; +} + +gboolean +purple_request_field_list_get_pixbuf(const PurpleRequestField *field) +{ + g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST, FALSE); + + return field->u.list.pixbuf; +} + +void purple_request_field_list_set_multi_select(PurpleRequestField *field, gboolean multi_select) { @@ -834,6 +853,22 @@ } void +purple_request_field_list_add_icon(PurpleRequestField *field, const char *item, const char* icon_path, + void *data) +{ + g_return_if_fail(field != NULL); + g_return_if_fail(item != NULL); + g_return_if_fail(data != NULL); + g_return_if_fail(icon_path != NULL); + g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST); + + field->u.list.items = g_list_append(field->u.list.items, g_strdup(item)); + field->u.list.icons = g_list_append(field->u.list.icons, g_strdup(icon_path)); + + g_hash_table_insert(field->u.list.item_data, g_strdup(item), data); +} + +void purple_request_field_list_add_selected(PurpleRequestField *field, const char *item) { g_return_if_fail(field != NULL); @@ -935,6 +970,16 @@ return field->u.list.items; } +GList * +purple_request_field_list_get_icons(const PurpleRequestField *field) +{ + g_return_val_if_fail(field != NULL, NULL); + g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST, NULL); + g_return_val_if_fail(field->u.list.pixbuf == TRUE, NULL); + + return field->u.list.icons; +} + PurpleRequestField * purple_request_field_label_new(const char *id, const char *text) {
--- a/libpurple/request.h Thu Nov 13 09:00:45 2008 +0000 +++ b/libpurple/request.h Thu Nov 13 17:04:53 2008 +0000 @@ -147,10 +147,12 @@ { GList *items; GHashTable *item_data; + GList *icons; GList *selected; GHashTable *selected_table; gboolean multiple_selection; + gboolean pixbuf; } list; @@ -877,6 +879,26 @@ PurpleRequestField *purple_request_field_list_new(const char *id, const char *text); /** + * Sets whether or not a list field allows icons. + * + * @param field The list field. + * @param multi_select TRUE if icons are enabled, + * or FALSE otherwise. + */ +void purple_request_field_list_set_pixbuf(PurpleRequestField *field, + gboolean pixbuf); + +/** + * Returns whether or not a list field allows icons. + * + * @param field The list field. + * + * @return TRUE if icons are enabled, or FALSE otherwise. + */ +gboolean purple_request_field_list_get_pixbuf( + const PurpleRequestField *field); + +/** * Sets whether or not a list field allows multiple selection. * * @param field The list field. @@ -918,6 +940,17 @@ const char *item, void *data); /** + * Adds an item with icon to a list field. + * + * @param field The list field. + * @param item The list item. + * @param icon_path The path to icon file. + * @param data The associated data. + */ +void purple_request_field_list_add_icon(PurpleRequestField *field, + const char *item, const char* icon_path, void* data); + +/** * Adds a selected item to the list field. * * @param field The field. @@ -975,6 +1008,15 @@ */ GList *purple_request_field_list_get_items(const PurpleRequestField *field); +/** + * Returns a list of icons in a list field. + * + * @param field The field. + * + * @constreturn The list of icons. + */ +GList *purple_request_field_list_get_icons(const PurpleRequestField *field); + /*@}*/ /**************************************************************************/
--- a/pidgin/gtkrequest.c Thu Nov 13 09:00:45 2008 +0000 +++ b/pidgin/gtkrequest.c Thu Nov 13 17:04:53 2008 +0000 @@ -995,7 +995,9 @@ GtkTreeSelection *sel; GtkTreeViewColumn *column; GtkTreeIter iter; - GList *l; + GList *l, *ic = NULL; + GdkPixbuf* pixbuf; + gboolean icon = purple_request_field_list_get_pixbuf(field); /* Create the scrolled window */ sw = gtk_scrolled_window_new(NULL, NULL); @@ -1007,7 +1009,10 @@ gtk_widget_show(sw); /* Create the list store */ - store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING); + if (icon) + store = gtk_list_store_new(3, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF); + else + store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING); /* Create the tree view */ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); @@ -1026,13 +1031,42 @@ gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "text", 1); + if(icon == TRUE) + { + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", 2); + + gtk_widget_set_size_request(treeview, 200, 400); + } + + if(icon == TRUE) + ic = purple_request_field_list_get_icons(field); + for (l = purple_request_field_list_get_items(field); l != NULL; l = l->next) { const char *text = (const char *)l->data; gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, + if(icon == TRUE) + { + const char *icon_path = (const char *)ic->data; + char* filename = g_build_filename(DATADIR, icon_path, NULL); + + pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + + g_free(filename); + + gtk_list_store_set(store, &iter, + 0, purple_request_field_list_get_data(field, text), + 1, text, + 2, pixbuf, + -1); + ic = ic->next; + } + else + gtk_list_store_set(store, &iter, 0, purple_request_field_list_get_data(field, text), 1, text, -1);
--- a/pidgin/pixmaps/Makefile.am Thu Nov 13 09:00:45 2008 +0000 +++ b/pidgin/pixmaps/Makefile.am Thu Nov 13 17:04:53 2008 +0000 @@ -126,7 +126,42 @@ emblems/16/secure.png \ emblems/16/unavailable.png \ emblems/16/video.png \ - emblems/16/voice.png + emblems/16/voice.png \ + emblems/16/xstatus_angry.png \ + emblems/16/xstatus_bathing.png \ + emblems/16/xstatus_beer.png \ + emblems/16/xstatus_busy.png \ + emblems/16/xstatus_cinema.png \ + emblems/16/xstatus_coffee.png \ + emblems/16/xstatus_crap.png \ + emblems/16/xstatus_crapping.png \ + emblems/16/xstatus_eating.png \ + emblems/16/xstatus_engineering.png \ + emblems/16/xstatus_friends.png \ + emblems/16/xstatus_fun.png \ + emblems/16/xstatus_gaming.png \ + emblems/16/xstatus_google.png \ + emblems/16/xstatus_heart.png \ + emblems/16/xstatus_internet.png \ + emblems/16/xstatus_iron.png \ + emblems/16/xstatus_mobile.png \ + emblems/16/xstatus_music.png \ + emblems/16/xstatus_party.png \ + emblems/16/xstatus_pda.png \ + emblems/16/xstatus_phone.png \ + emblems/16/xstatus_question.png \ + emblems/16/xstatus_shopping.png \ + emblems/16/xstatus_sick.png \ + emblems/16/xstatus_sleeping.png \ + emblems/16/xstatus_studying.png \ + emblems/16/xstatus_surfing.png \ + emblems/16/xstatus_thinking.png \ + emblems/16/xstatus_tired.png \ + emblems/16/xstatus_tv.png \ + emblems/16/xstatus_typing.png \ + emblems/16/xstatus_writing.png \ + emblems/16/xstatus_zzz.png + EMOTES_DEFAULT_24_SCALABLE = \ emotes/default/24/scalable/airplane.svg \