/* * Subtypes 0x000b (SNAC_SUBTYPE_BUDDY_ONCOMING) and 0x000c (SNAC_SUBTYPE_BUDDY_OFFGOING) - Change in buddy status * * Oncoming Buddy notifications contain a subset of the * user information structure. It's close enough to run * through aim_info_extract() however. * * Although the offgoing notification contains no information, * it is still in a format parsable by aim_info_extract(). * */ static int buddychange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { int ret = 0; aim_userinfo_t userinfo; aim_rxcallback_t userfunc; aim_info_extract(od, bs, &userinfo); if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, &userinfo); if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ) { PurpleAccount *account = purple_connection_get_account(od->gc); PurpleBuddy *buddy = purple_find_buddy(account, userinfo.bn); if (buddy) { PurplePresence *presence = purple_buddy_get_presence(buddy); if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD)) icq_im_xstatus_request(od, userinfo.bn); } } aim_info_free(&userinfo); return ret; }
/* Subtype 0x0006 */ static int userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { int ret = 0; aim_rxcallback_t userfunc; aim_userinfo_t *userinfo, *userinfo2; GSList *tlvlist; aim_tlv_t *tlv = NULL; int was_explicit; userinfo = (aim_userinfo_t *)g_malloc(sizeof(aim_userinfo_t)); aim_info_extract(od, bs, userinfo); tlvlist = aim_tlvlist_read(bs); /* Profile will be 1 and 2 */ userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { userinfo->info = (char *)g_malloc(tlv->length); memcpy(userinfo->info, tlv->value, tlv->length); userinfo->info_len = tlv->length; } /* Away message will be 3 and 4 */ userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { userinfo->away = (char *)g_malloc(tlv->length); memcpy(userinfo->away, tlv->value, tlv->length); userinfo->away_len = tlv->length; } /* Caps will be 5 */ if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { ByteStream cbs; byte_stream_init(&cbs, tlv->value, tlv->length); userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length); userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; } aim_tlvlist_free(tlvlist); aim_locate_adduserinfo(od, userinfo); userinfo2 = aim_locate_finduserinfo(od, userinfo->sn); aim_info_free(userinfo); g_free(userinfo); /* * Remove this screen name from our queue. If the client requested * this buddy's info explicitly, then notify them that we have info * for this buddy. */ if (userinfo2 != NULL) { was_explicit = aim_locate_gotuserinfo(od, conn, userinfo2->sn); if (was_explicit == TRUE) if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, userinfo2); } return ret; }
/* Subtype 0x0006 */ static int userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { int ret = 0; aim_userinfo_t *userinfo, *userinfo2; GSList *tlvlist; aim_tlv_t *tlv = NULL; userinfo = (aim_userinfo_t *)g_malloc(sizeof(aim_userinfo_t)); aim_info_extract(od, bs, userinfo); tlvlist = aim_tlvlist_read(bs); /* Profile will be 1 and 2 */ userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { userinfo->info = (char *)g_malloc(tlv->length); memcpy(userinfo->info, tlv->value, tlv->length); userinfo->info_len = tlv->length; } /* Away message will be 3 and 4 */ userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { userinfo->away = (char *)g_malloc(tlv->length); memcpy(userinfo->away, tlv->value, tlv->length); userinfo->away_len = tlv->length; } /* Caps will be 5 */ if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { ByteStream cbs; PurpleAccount *account = purple_connection_get_account(od->gc); const char *mood; byte_stream_init(&cbs, tlv->value, tlv->length); userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length); byte_stream_rewind(&cbs); userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; mood = aim_receive_custom_icon(od, &cbs, tlv->length); if (mood) purple_prpl_got_user_status(account, userinfo->bn, "mood", PURPLE_MOOD_NAME, mood, NULL); else purple_prpl_got_user_status_deactive(account, userinfo->bn, "mood"); } aim_tlvlist_free(tlvlist); aim_locate_adduserinfo(od, userinfo); userinfo2 = aim_locate_finduserinfo(od, userinfo->bn); aim_info_free(userinfo); g_free(userinfo); /* Show the info to the user */ oscar_user_info_display_aim(od, userinfo2); return ret; }
static void locate_shutdown(OscarData *od, aim_module_t *mod) { aim_userinfo_t *del; while (od->locate.userinfo) { del = od->locate.userinfo; od->locate.userinfo = od->locate.userinfo->next; aim_info_free(del); g_free(del); } }
/* Subtype 0x0006 */ static int userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { int ret = 0; aim_rxcallback_t userfunc; aim_userinfo_t *userinfo, *userinfo2; GSList *tlvlist; aim_tlv_t *tlv = NULL; userinfo = (aim_userinfo_t *)g_malloc(sizeof(aim_userinfo_t)); aim_info_extract(od, bs, userinfo); tlvlist = aim_tlvlist_read(bs); /* Profile will be 1 and 2 */ userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { userinfo->info = (char *)g_malloc(tlv->length); memcpy(userinfo->info, tlv->value, tlv->length); userinfo->info_len = tlv->length; } /* Away message will be 3 and 4 */ userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { userinfo->away = (char *)g_malloc(tlv->length); memcpy(userinfo->away, tlv->value, tlv->length); userinfo->away_len = tlv->length; } /* Caps will be 5 */ if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { ByteStream cbs; byte_stream_init(&cbs, tlv->value, tlv->length); userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length); userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; } aim_tlvlist_free(tlvlist); aim_locate_adduserinfo(od, userinfo); userinfo2 = aim_locate_finduserinfo(od, userinfo->bn); aim_info_free(userinfo); g_free(userinfo); /* Show the info to the user */ if (userinfo2 != NULL && ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))) ret = userfunc(od, conn, frame, userinfo2); return ret; }
/* * Subtypes 0x000b and 0x000c - Change in buddy status * * Oncoming Buddy notifications contain a subset of the * user information structure. It's close enough to run * through aim_info_extract() however. * * Although the offgoing notification contains no information, * it is still in a format parsable by aim_info_extract(). * */ static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_userinfo_t userinfo; aim_rxcallback_t userfunc; aim_info_extract(sess, bs, &userinfo); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, &userinfo); if (snac->subtype == 0x000b) aim_locate_requestuserinfo(sess, userinfo.sn); aim_info_free(&userinfo); return ret; }
/* * Subtypes 0x000b (SNAC_SUBTYPE_BUDDY_ONCOMING) and 0x000c (SNAC_SUBTYPE_BUDDY_OFFGOING) - Change in buddy status * * Oncoming Buddy notifications contain a subset of the * user information structure. It's close enough to run * through aim_info_extract() however. * * Although the offgoing notification contains no information, * it is still in a format parsable by aim_info_extract(). * */ static int buddychange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { int ret = 0; aim_userinfo_t userinfo; aim_rxcallback_t userfunc; aim_info_extract(od, bs, &userinfo); if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, &userinfo); if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && userinfo.flags & AIM_FLAG_AWAY) aim_locate_autofetch_away_message(od, userinfo.sn); aim_info_free(&userinfo); return ret; }
/* Subtypes 0x0003 and 0x0004 */ static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_userinfo_t *userinfo = NULL; aim_rxcallback_t userfunc; int curcount = 0, ret = 0; while (aim_bstream_empty(bs)) { curcount++; userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t)); aim_info_extract(sess, bs, &userinfo[curcount-1]); } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, curcount, userinfo); aim_info_free(userinfo); free(userinfo); return ret; }
/* Subtypes 0x0003 and 0x0004 */ static int userlistchange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { aim_userinfo_t *userinfo = NULL; aim_rxcallback_t userfunc; int curcount = 0, ret = 0; while (byte_stream_bytes_left(bs)) { curcount++; userinfo = g_realloc(userinfo, curcount * sizeof(aim_userinfo_t)); aim_info_extract(od, bs, &userinfo[curcount-1]); } if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, curcount, userinfo); aim_info_free(userinfo); g_free(userinfo); return ret; }
/* * Subtype 0x0006 * * We could probably include this in the normal ICBM parsing * code as channel 0x0003, however, since only the start * would be the same, we might as well do it here. * * General outline of this SNAC: * snac * cookie * channel id * tlvlist * unknown * source user info * name * evility * userinfo tlvs * online time * etc * message metatlv * message tlv * message string * possibly others * */ static int incomingim_ch3(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0, i; aim_rxcallback_t userfunc; aim_userinfo_t userinfo; fu8_t cookie[8]; fu16_t channel; aim_tlvlist_t *otl; char *msg = NULL; int len = 0; char *encoding = NULL, *language = NULL; aim_msgcookie_t *ck; memset(&userinfo, 0, sizeof(aim_userinfo_t)); /* * Read ICBM Cookie. */ for (i = 0; i < 8; i++) cookie[i] = aimbs_get8(bs); if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) { free(ck->data); free(ck); } /* * Channel ID * * Channel 0x0003 is used for chat messages. * */ channel = aimbs_get16(bs); if (channel != 0x0003) { faimdprintf(sess, 0, "faim: chat_incoming: unknown channel! (0x%04x)\n", channel); return 0; } /* * Start parsing TLVs right away. */ otl = aim_tlvlist_read(bs); /* * Type 0x0003: Source User Information */ if (aim_tlv_gettlv(otl, 0x0003, 1)) { aim_tlv_t *userinfotlv; aim_bstream_t tbs; userinfotlv = aim_tlv_gettlv(otl, 0x0003, 1); aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length); aim_info_extract(sess, &tbs, &userinfo); } /* * Type 0x0001: If present, it means it was a message to the * room (as opposed to a whisper). */ if (aim_tlv_gettlv(otl, 0x0001, 1)) ; /* * Type 0x0005: Message Block. Conains more TLVs. */ if (aim_tlv_gettlv(otl, 0x0005, 1)) { aim_tlvlist_t *itl; aim_tlv_t *msgblock; aim_bstream_t tbs; msgblock = aim_tlv_gettlv(otl, 0x0005, 1); aim_bstream_init(&tbs, msgblock->value, msgblock->length); itl = aim_tlvlist_read(&tbs); /* * Type 0x0001: Message. */ if (aim_tlv_gettlv(itl, 0x0001, 1)) { msg = aim_tlv_getstr(itl, 0x0001, 1); len = aim_tlv_gettlv(itl, 0x0001, 1)->length; } /* * Type 0x0002: Encoding. */ if (aim_tlv_gettlv(itl, 0x0002, 1)) encoding = aim_tlv_getstr(itl, 0x0002, 1); /* * Type 0x0003: Language. */ if (aim_tlv_gettlv(itl, 0x0003, 1)) language = aim_tlv_getstr(itl, 0x0003, 1); aim_tlvlist_free(&itl); } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, &userinfo, len, msg, encoding, language); aim_info_free(&userinfo); free(msg); aim_tlvlist_free(&otl); return ret; }
/* * Subtype 0x0002 - General room information. Lots of stuff. * * Values I know are in here but I haven't attached * them to any of the 'Unknown's: * - Language (English) * */ static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_userinfo_t *userinfo = NULL; aim_rxcallback_t userfunc; int ret = 0; int usercount = 0; fu8_t detaillevel = 0; char *roomname = NULL; struct aim_chat_roominfo roominfo; fu16_t tlvcount = 0; aim_tlvlist_t *tlvlist; char *roomdesc = NULL; fu16_t flags = 0; fu32_t creationtime = 0; fu16_t maxmsglen = 0, maxvisiblemsglen = 0; fu16_t unknown_d2 = 0, unknown_d5 = 0; aim_chat_readroominfo(bs, &roominfo); detaillevel = aimbs_get8(bs); if (detaillevel != 0x02) { faimdprintf(sess, 0, "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel); return 1; } tlvcount = aimbs_get16(bs); /* * Everything else are TLVs. */ tlvlist = aim_tlvlist_read(bs); /* * TLV type 0x006a is the room name in Human Readable Form. */ if (aim_tlv_gettlv(tlvlist, 0x006a, 1)) roomname = aim_tlv_getstr(tlvlist, 0x006a, 1); /* * Type 0x006f: Number of occupants. */ if (aim_tlv_gettlv(tlvlist, 0x006f, 1)) usercount = aim_tlv_get16(tlvlist, 0x006f, 1); /* * Type 0x0073: Occupant list. */ if (aim_tlv_gettlv(tlvlist, 0x0073, 1)) { int curoccupant = 0; aim_tlv_t *tmptlv; aim_bstream_t occbs; tmptlv = aim_tlv_gettlv(tlvlist, 0x0073, 1); /* Allocate enough userinfo structs for all occupants */ userinfo = calloc(usercount, sizeof(aim_userinfo_t)); aim_bstream_init(&occbs, tmptlv->value, tmptlv->length); while (curoccupant < usercount) aim_info_extract(sess, &occbs, &userinfo[curoccupant++]); } /* * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG) */ if (aim_tlv_gettlv(tlvlist, 0x00c9, 1)) flags = aim_tlv_get16(tlvlist, 0x00c9, 1); /* * Type 0x00ca: Creation time (4 bytes) */ if (aim_tlv_gettlv(tlvlist, 0x00ca, 1)) creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1); /* * Type 0x00d1: Maximum Message Length */ if (aim_tlv_gettlv(tlvlist, 0x00d1, 1)) maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1); /* * Type 0x00d2: Unknown. (2 bytes) */ if (aim_tlv_gettlv(tlvlist, 0x00d2, 1)) unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1); /* * Type 0x00d3: Room Description */ if (aim_tlv_gettlv(tlvlist, 0x00d3, 1)) roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1); /* * Type 0x000d4: Unknown (flag only) */ if (aim_tlv_gettlv(tlvlist, 0x000d4, 1)) ; /* * Type 0x00d5: Unknown. (1 byte) */ if (aim_tlv_gettlv(tlvlist, 0x00d5, 1)) unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1); /* * Type 0x00d6: Encoding 1 ("us-ascii") */ if (aim_tlv_gettlv(tlvlist, 0x000d6, 1)) ; /* * Type 0x00d7: Language 1 ("en") */ if (aim_tlv_gettlv(tlvlist, 0x000d7, 1)) ; /* * Type 0x00d8: Encoding 2 ("us-ascii") */ if (aim_tlv_gettlv(tlvlist, 0x000d8, 1)) ; /* * Type 0x00d9: Language 2 ("en") */ if (aim_tlv_gettlv(tlvlist, 0x000d9, 1)) ; /* * Type 0x00da: Maximum visible message length */ if (aim_tlv_gettlv(tlvlist, 0x000da, 1)) maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { ret = userfunc(sess, rx, &roominfo, roomname, usercount, userinfo, roomdesc, flags, creationtime, maxmsglen, unknown_d2, unknown_d5, maxvisiblemsglen); } free(roominfo.name); while (usercount > 0) aim_info_free(&userinfo[--usercount]); free(userinfo); free(roomname); free(roomdesc); aim_tlvlist_free(&tlvlist); return ret; }
/* * Subtype 0x0006 * * We could probably include this in the normal ICBM parsing * code as channel 0x0003, however, since only the start * would be the same, we might as well do it here. * * General outline of this SNAC: * snac * cookie * channel id * tlvlist * unknown * source user info * name * evility * userinfo tlvs * online time * etc * message metatlv * message tlv * message string * possibly others * */ static int incomingim_ch3(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { int ret = 0, i; aim_rxcallback_t userfunc; aim_userinfo_t userinfo; guint8 cookie[8]; guint16 channel; GSList *tlvlist; char *msg = NULL; int len = 0; char *encoding = NULL, *language = NULL; IcbmCookie *ck; aim_tlv_t *tlv; ByteStream tbs; memset(&userinfo, 0, sizeof(aim_userinfo_t)); /* * Read ICBM Cookie. */ for (i = 0; i < 8; i++) cookie[i] = byte_stream_get8(bs); if ((ck = aim_uncachecookie(od, cookie, AIM_COOKIETYPE_CHAT))) { g_free(ck->data); g_free(ck); } /* * Channel ID * * Channel 0x0003 is used for chat messages. * */ channel = byte_stream_get16(bs); if (channel != 0x0003) { purple_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel); return 0; } /* * Start parsing TLVs right away. */ tlvlist = aim_tlvlist_read(bs); /* * Type 0x0003: Source User Information */ tlv = aim_tlv_gettlv(tlvlist, 0x0003, 1); if (tlv != NULL) { byte_stream_init(&tbs, tlv->value, tlv->length); aim_info_extract(od, &tbs, &userinfo); } /* * Type 0x0005: Message Block. Conains more TLVs. */ tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1); if (tlv != NULL) { GSList *inner_tlvlist; aim_tlv_t *inner_tlv; byte_stream_init(&tbs, tlv->value, tlv->length); inner_tlvlist = aim_tlvlist_read(&tbs); /* * Type 0x0001: Message. */ inner_tlv = aim_tlv_gettlv(inner_tlvlist, 0x0001, 1); if (inner_tlv != NULL) { len = inner_tlv->length; msg = aim_tlv_getvalue_as_string(inner_tlv); } /* * Type 0x0002: Encoding. */ encoding = aim_tlv_getstr(inner_tlvlist, 0x0002, 1); /* * Type 0x0003: Language. */ language = aim_tlv_getstr(inner_tlvlist, 0x0003, 1); aim_tlvlist_free(inner_tlvlist); } if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, &userinfo, len, msg, encoding, language); aim_info_free(&userinfo); g_free(msg); g_free(encoding); g_free(language); aim_tlvlist_free(tlvlist); return ret; }