/* * This is all there is to it. * * The message is probably HTML. * */ static int parsepopup(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_rxcallback_t userfunc; aim_tlvlist_t *tl; int ret = 0; char *msg, *url; fu16_t width, height, delay; tl = aim_readtlvchain(bs); msg = aim_gettlv_str(tl, 0x0001, 1); url = aim_gettlv_str(tl, 0x0002, 1); width = aim_gettlv16(tl, 0x0003, 1); height = aim_gettlv16(tl, 0x0004, 1); delay = aim_gettlv16(tl, 0x0005, 1); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, msg, url, width, height, delay); aim_freetlvchain(&tl); free(msg); free(url); return ret; }
static int negchan_middle(aim_session_t *sess, aim_frame_t *fr) { aim_tlvlist_t *tlvlist; char *msg = NULL; fu16_t code = 0; aim_rxcallback_t userfunc; int ret = 1; if (aim_bstream_empty(&fr->data) == 0) { /* XXX should do something with this */ return 1; } /* Used only by the older login protocol */ /* XXX remove this special case? */ if (fr->conn->type == AIM_CONN_TYPE_AUTH) return consumenonsnac(sess, fr, 0x0017, 0x0003); tlvlist = aim_readtlvchain(&fr->data); if (aim_gettlv(tlvlist, 0x0009, 1)) code = aim_gettlv16(tlvlist, 0x0009, 1); if (aim_gettlv(tlvlist, 0x000b, 1)) msg = aim_gettlv_str(tlvlist, 0x000b, 1); if ((userfunc = aim_callhandler(sess, fr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) ret = userfunc(sess, fr, code, msg); aim_freetlvchain(&tlvlist); free(msg); return ret; }
/* Subtype 0x0003 - BOS Rights. */ static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_rxcallback_t userfunc; aim_tlvlist_t *tlvlist; fu16_t maxpermits = 0, maxdenies = 0; int ret = 0; /* * TLVs follow */ tlvlist = aim_readtlvchain(bs); /* * TLV type 0x0001: Maximum number of buddies on permit list. */ if (aim_gettlv(tlvlist, 0x0001, 1)) maxpermits = aim_gettlv16(tlvlist, 0x0001, 1); /* * TLV type 0x0002: Maximum number of buddies on deny list. */ if (aim_gettlv(tlvlist, 0x0002, 1)) maxdenies = aim_gettlv16(tlvlist, 0x0002, 1); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, maxpermits, maxdenies); aim_freetlvchain(&tlvlist); return ret; }
static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_userinfo_t userinfo; char *text_encoding = NULL, *text = NULL; aim_rxcallback_t userfunc; aim_tlvlist_t *tlvlist; aim_snac_t *origsnac = NULL; struct aim_priv_inforeq *inforeq; int ret = 0; origsnac = aim_remsnac(sess, snac->id); if (!origsnac || !origsnac->data) { faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n"); return 0; } inforeq = (struct aim_priv_inforeq *)origsnac->data; if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) && (inforeq->infotype != AIM_GETINFO_AWAYMESSAGE)) { faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype); return 0; } aim_extractuserinfo(sess, bs, &userinfo); tlvlist = aim_readtlvchain(bs); /* * Depending on what informational text was requested, different * TLVs will appear here. * * Profile will be 1 and 2, away message will be 3 and 4. */ if (aim_gettlv(tlvlist, 0x0001, 1)) { text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1); text = aim_gettlv_str(tlvlist, 0x0002, 1); } else if (aim_gettlv(tlvlist, 0x0003, 1)) { text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1); text = aim_gettlv_str(tlvlist, 0x0004, 1); } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, &userinfo, text_encoding, text, inforeq->infotype); free(text_encoding); free(text); aim_freetlvchain(&tlvlist); if (origsnac) free(origsnac->data); free(origsnac); return ret; }
static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_userinfo_t userinfo; char *text_encoding = NULL, *text = NULL; guint16 text_length = 0; aim_rxcallback_t userfunc; aim_tlvlist_t *tlvlist; aim_tlv_t *tlv; aim_snac_t *origsnac = NULL; struct aim_priv_inforeq *inforeq; int ret = 0; origsnac = aim_remsnac(sess, snac->id); if (!origsnac || !origsnac->data) { imcb_error(sess->aux_data, "major problem: no snac stored!"); return 0; } inforeq = (struct aim_priv_inforeq *)origsnac->data; if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) && (inforeq->infotype != AIM_GETINFO_AWAYMESSAGE) && (inforeq->infotype != AIM_GETINFO_CAPABILITIES)) { imcb_error(sess->aux_data, "unknown infotype in request!"); return 0; } aim_extractuserinfo(sess, bs, &userinfo); tlvlist = aim_readtlvchain(bs); /* * Depending on what informational text was requested, different * TLVs will appear here. * * Profile will be 1 and 2, away message will be 3 and 4, caps * will be 5. */ if (inforeq->infotype == AIM_GETINFO_GENERALINFO) { text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1); if((tlv = aim_gettlv(tlvlist, 0x0002, 1))) { text = g_new0(char, tlv->length); memcpy(text, tlv->value, tlv->length); text_length = tlv->length; }
/* * Subtype 0x0003 - Rights. * */ static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_rxcallback_t userfunc; aim_tlvlist_t *tlvlist; fu16_t maxbuddies = 0, maxwatchers = 0; int ret = 0; /* * TLVs follow */ tlvlist = aim_readtlvchain(bs); /* * TLV type 0x0001: Maximum number of buddies. */ if (aim_gettlv(tlvlist, 0x0001, 1)) maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1); /* * TLV type 0x0002: Maximum number of watchers. * * Watchers are other users who have you on their buddy * list. (This is called the "reverse list" by a certain * other IM protocol.) * */ if (aim_gettlv(tlvlist, 0x0002, 1)) maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1); /* * TLV type 0x0003: Unknown. * * ICQ only? */ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, maxbuddies, maxwatchers); aim_freetlvchain(&tlvlist); return ret; }
/* * Normally contains: * t(0001) - short containing max profile length (value = 1024) * t(0002) - short - unknown (value = 16) [max MIME type length?] * t(0003) - short - unknown (value = 10) */ static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_tlvlist_t *tlvlist; aim_rxcallback_t userfunc; int ret = 0; fu16_t maxsiglen = 0; tlvlist = aim_readtlvchain(bs); if (aim_gettlv(tlvlist, 0x0001, 1)) maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, maxsiglen); aim_freetlvchain(&tlvlist); return ret; }
/* * Subtype 0x0003 * */ static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int j = 0, m, ret = 0; aim_tlvlist_t *tlvlist; char *cur = NULL, *buf = NULL; aim_rxcallback_t userfunc; aim_snac_t *snac2; char *searchaddr = NULL; if ((snac2 = aim_remsnac(sess, snac->id))) searchaddr = (char *)snac2->data; tlvlist = aim_readtlvchain(bs); m = aim_counttlvchain(&tlvlist); /* XXX uhm. */ while ((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) { buf = realloc(buf, (j+1) * (MAXSNLEN+1)); strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN); free(cur); j++; } aim_freetlvchain(&tlvlist); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, searchaddr, j, buf); /* XXX freesnac()? */ if (snac2) free(snac2->data); free(snac2); free(buf); return ret; }
static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) { aim_rxcallback_t userfunc; aim_tlvlist_t *tlvlist, *innerlist; char *ck = NULL, *fqcn = NULL, *name = NULL; fu16_t exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; fu32_t createtime = 0; fu8_t createperms = 0, detaillevel; int cklen; aim_tlv_t *bigblock; int ret = 0; aim_bstream_t bbbs; tlvlist = aim_readtlvchain(bs); if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) { faimdprintf(sess, 0, "no bigblock in top tlv in create room response\n"); aim_freetlvchain(&tlvlist); return 0; } aim_bstream_init(&bbbs, bigblock->value, bigblock->length); exchange = aimbs_get16(&bbbs); cklen = aimbs_get8(&bbbs); ck = aimbs_getstr(&bbbs, cklen); instance = aimbs_get16(&bbbs); detaillevel = aimbs_get8(&bbbs); if (detaillevel != 0x02) { faimdprintf(sess, 0, "unknown detaillevel in create room response (0x%02x)\n", detaillevel); aim_freetlvchain(&tlvlist); free(ck); return 0; } unknown = aimbs_get16(&bbbs); innerlist = aim_readtlvchain(&bbbs); if (aim_gettlv(innerlist, 0x006a, 1)) fqcn = aim_gettlv_str(innerlist, 0x006a, 1); if (aim_gettlv(innerlist, 0x00c9, 1)) flags = aim_gettlv16(innerlist, 0x00c9, 1); if (aim_gettlv(innerlist, 0x00ca, 1)) createtime = aim_gettlv32(innerlist, 0x00ca, 1); if (aim_gettlv(innerlist, 0x00d1, 1)) maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1); if (aim_gettlv(innerlist, 0x00d2, 1)) maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1); if (aim_gettlv(innerlist, 0x00d3, 1)) name = aim_gettlv_str(innerlist, 0x00d3, 1); if (aim_gettlv(innerlist, 0x00d5, 1)) createperms = aim_gettlv8(innerlist, 0x00d5, 1); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); } free(ck); free(name); free(fqcn); aim_freetlvchain(&innerlist); aim_freetlvchain(&tlvlist); return ret; }
/* * 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 incomingmsg(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_userinfo_t userinfo; aim_rxcallback_t userfunc; int ret = 0; fu8_t *cookie; fu16_t channel; aim_tlvlist_t *otl; char *msg = NULL; aim_msgcookie_t *ck; memset(&userinfo, 0, sizeof(aim_userinfo_t)); /* * ICBM Cookie. Uncache it. */ cookie = aimbs_getraw(bs, 8); if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) { free(ck->data); free(ck); } /* * Channel ID * * Channels 1 and 2 are implemented in the normal ICBM * parser. * * We only do channel 3 here. * */ 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_readtlvchain(bs); /* * Type 0x0003: Source User Information */ if (aim_gettlv(otl, 0x0003, 1)) { aim_tlv_t *userinfotlv; aim_bstream_t tbs; userinfotlv = aim_gettlv(otl, 0x0003, 1); aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length); aim_extractuserinfo(sess, &tbs, &userinfo); } /* * Type 0x0001: If present, it means it was a message to the * room (as opposed to a whisper). */ if (aim_gettlv(otl, 0x0001, 1)) ; /* * Type 0x0005: Message Block. Conains more TLVs. */ if (aim_gettlv(otl, 0x0005, 1)) { aim_tlvlist_t *itl; aim_tlv_t *msgblock; aim_bstream_t tbs; msgblock = aim_gettlv(otl, 0x0005, 1); aim_bstream_init(&tbs, msgblock->value, msgblock->length); itl = aim_readtlvchain(&tbs); /* * Type 0x0001: Message. */ if (aim_gettlv(itl, 0x0001, 1)) msg = aim_gettlv_str(itl, 0x0001, 1); aim_freetlvchain(&itl); } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, &userinfo, msg); free(cookie); free(msg); aim_freetlvchain(&otl); return ret; }
/* * General room information. Lots of stuff. * * Values I know are in here but I havent attached * them to any of the 'Unknown's: * - Language (English) * * SNAC 000e/0002 */ 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_readtlvchain(bs); /* * TLV type 0x006a is the room name in Human Readable Form. */ if (aim_gettlv(tlvlist, 0x006a, 1)) roomname = aim_gettlv_str(tlvlist, 0x006a, 1); /* * Type 0x006f: Number of occupants. */ if (aim_gettlv(tlvlist, 0x006f, 1)) usercount = aim_gettlv16(tlvlist, 0x006f, 1); /* * Type 0x0073: Occupant list. */ if (aim_gettlv(tlvlist, 0x0073, 1)) { int curoccupant = 0; aim_tlv_t *tmptlv; aim_bstream_t occbs; tmptlv = aim_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_extractuserinfo(sess, &occbs, &userinfo[curoccupant++]); } /* * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG) */ if (aim_gettlv(tlvlist, 0x00c9, 1)) flags = aim_gettlv16(tlvlist, 0x00c9, 1); /* * Type 0x00ca: Creation time (4 bytes) */ if (aim_gettlv(tlvlist, 0x00ca, 1)) creationtime = aim_gettlv32(tlvlist, 0x00ca, 1); /* * Type 0x00d1: Maximum Message Length */ if (aim_gettlv(tlvlist, 0x00d1, 1)) maxmsglen = aim_gettlv16(tlvlist, 0x00d1, 1); /* * Type 0x00d2: Unknown. (2 bytes) */ if (aim_gettlv(tlvlist, 0x00d2, 1)) unknown_d2 = aim_gettlv16(tlvlist, 0x00d2, 1); /* * Type 0x00d3: Room Description */ if (aim_gettlv(tlvlist, 0x00d3, 1)) roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1); /* * Type 0x000d4: Unknown (flag only) */ if (aim_gettlv(tlvlist, 0x000d4, 1)) ; /* * Type 0x00d5: Unknown. (1 byte) */ if (aim_gettlv(tlvlist, 0x00d5, 1)) unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1); /* * Type 0x00d6: Encoding 1 ("us-ascii") */ if (aim_gettlv(tlvlist, 0x000d6, 1)) ; /* * Type 0x00d7: Language 1 ("en") */ if (aim_gettlv(tlvlist, 0x000d7, 1)) ; /* * Type 0x00d8: Encoding 2 ("us-ascii") */ if (aim_gettlv(tlvlist, 0x000d8, 1)) ; /* * Type 0x00d9: Language 2 ("en") */ if (aim_gettlv(tlvlist, 0x000d9, 1)) ; /* * Type 0x00da: Maximum visible message length */ if (aim_gettlv(tlvlist, 0x000da, 1)) maxvisiblemsglen = aim_gettlv16(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); free(userinfo); free(roomname); free(roomdesc); aim_freetlvchain(&tlvlist); return ret; }
/* * Response to 15/2, contains an ICQ packet. */ static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_tlvlist_t *tl; aim_tlv_t *datatlv; aim_bstream_t qbs; fu32_t ouruin; fu16_t cmdlen, cmd, reqid; if (!(tl = aim_readtlvchain(bs)) || !(datatlv = aim_gettlv(tl, 0x0001, 1))) { aim_freetlvchain(&tl); faimdprintf(sess, 0, "corrupt ICQ response\n"); return 0; } aim_bstream_init(&qbs, datatlv->value, datatlv->length); cmdlen = aimbs_getle16(&qbs); ouruin = aimbs_getle32(&qbs); cmd = aimbs_getle16(&qbs); reqid = aimbs_getle16(&qbs); faimdprintf(sess, 1, "icq response: %d bytes, %ld, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid); if (cmd == 0x0041) { fu16_t msglen; struct aim_icq_offlinemsg msg; aim_rxcallback_t userfunc; memset(&msg, 0, sizeof(msg)); msg.sender = aimbs_getle32(&qbs); msg.year = aimbs_getle16(&qbs); msg.month = aimbs_getle8(&qbs); msg.day = aimbs_getle8(&qbs); msg.hour = aimbs_getle8(&qbs); msg.minute = aimbs_getle8(&qbs); msg.type = aimbs_getle16(&qbs); msglen = aimbs_getle16(&qbs); msg.msg = aimbs_getstr(&qbs, msglen); if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG))) ret = userfunc(sess, rx, &msg); free(msg.msg); } else if (cmd == 0x0042) { aim_rxcallback_t userfunc; if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE))) ret = userfunc(sess, rx); } else if (cmd == 0x07da) { fu16_t subtype; subtype = aimbs_getle16(&qbs); if (subtype == 0x019a) { fu16_t tlen; struct aim_icq_simpleinfo info; aim_rxcallback_t userfunc; memset(&info, 0, sizeof(info)); aimbs_getle8(&qbs); /* no clue */ aimbs_getle16(&qbs); /* no clue */ info.uin = aimbs_getle32(&qbs); tlen = aimbs_getle16(&qbs); info.nick = aimbs_getstr(&qbs, tlen); tlen = aimbs_getle16(&qbs); info.first = aimbs_getstr(&qbs, tlen); tlen = aimbs_getle16(&qbs); info.last = aimbs_getstr(&qbs, tlen); tlen = aimbs_getle16(&qbs); info.email = aimbs_getstr(&qbs, tlen); /* no clue what the rest of it is */ if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_SIMPLEINFO))) ret = userfunc(sess, rx, &info); free(info.nick); free(info.first); free(info.last); free(info.email); } else if (subtype == 100 || subtype == 150) { fu16_t tlen; struct aim_icq_smsresponse response; aim_rxcallback_t userfunc; memset(&response, 0, sizeof(response)); for(tlen = 0; tlen < 7; tlen++) aimbs_getle8(&qbs); /* no clue */ response.type = subtype; tlen = aimbs_getle16(&qbs); response.tag = aimbs_getstr(&qbs, tlen); tlen = aimbs_getle16(&qbs); response.xml = aimbs_getstr(&qbs, tlen); if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_SMSRESPONSE))) ret = userfunc(sess, rx, &response); free(response.tag); free(response.xml); } else faimdprintf(sess, 1, "unknown subtype 0x%04x\n", subtype); } aim_freetlvchain(&tl); return ret; }
static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie) { aim_rxcallback_t userfunc; aim_tlv_t *block1, *servdatatlv; aim_tlvlist_t *list2; struct aim_incomingim_ch2_args args; aim_bstream_t bbs, sdbs, *sdbsptr = NULL; guint8 *cookie2; int ret = 0; char clientip1[30] = {""}; char clientip2[30] = {""}; char verifiedip[30] = {""}; memset(&args, 0, sizeof(args)); /* * There's another block of TLVs embedded in the type 5 here. */ block1 = aim_gettlv(tlvlist, 0x0005, 1); aim_bstream_init(&bbs, block1->value, block1->length); /* * First two bytes represent the status of the connection. * * 0 is a request, 1 is a deny (?), 2 is an accept */ args.status = aimbs_get16(&bbs); /* * Next comes the cookie. Should match the ICBM cookie. */ cookie2 = aimbs_getraw(&bbs, 8); if (memcmp(cookie, cookie2, 8) != 0) imcb_error(sess->aux_data, "rend: warning cookies don't match!"); memcpy(args.cookie, cookie2, 8); g_free(cookie2); /* * The next 16bytes are a capability block so we can * identify what type of rendezvous this is. */ args.reqclass = aim_getcap(sess, &bbs, 0x10); /* * What follows may be TLVs or nothing, depending on the * purpose of the message. * * Ack packets for instance have nothing more to them. */ list2 = aim_readtlvchain(&bbs); /* * IP address from the perspective of the client. */ if (aim_gettlv(list2, 0x0002, 1)) { aim_tlv_t *iptlv; iptlv = aim_gettlv(list2, 0x0002, 1); g_snprintf(clientip1, sizeof(clientip1), "%d.%d.%d.%d", aimutil_get8(iptlv->value+0), aimutil_get8(iptlv->value+1), aimutil_get8(iptlv->value+2), aimutil_get8(iptlv->value+3)); } /* * Secondary IP address from the perspective of the client. */ if (aim_gettlv(list2, 0x0003, 1)) { aim_tlv_t *iptlv; iptlv = aim_gettlv(list2, 0x0003, 1); g_snprintf(clientip2, sizeof(clientip2), "%d.%d.%d.%d", aimutil_get8(iptlv->value+0), aimutil_get8(iptlv->value+1), aimutil_get8(iptlv->value+2), aimutil_get8(iptlv->value+3)); } /* * Verified IP address (from the perspective of Oscar). * * This is added by the server. */ if (aim_gettlv(list2, 0x0004, 1)) { aim_tlv_t *iptlv; iptlv = aim_gettlv(list2, 0x0004, 1); g_snprintf(verifiedip, sizeof(verifiedip), "%d.%d.%d.%d", aimutil_get8(iptlv->value+0), aimutil_get8(iptlv->value+1), aimutil_get8(iptlv->value+2), aimutil_get8(iptlv->value+3)); } /* * Port number for something. */ if (aim_gettlv(list2, 0x0005, 1)) args.port = aim_gettlv16(list2, 0x0005, 1); /* * Error code. */ if (aim_gettlv(list2, 0x000b, 1)) args.errorcode = aim_gettlv16(list2, 0x000b, 1); /* * Invitation message / chat description. */ if (aim_gettlv(list2, 0x000c, 1)) args.msg = aim_gettlv_str(list2, 0x000c, 1); /* * Character set. */ if (aim_gettlv(list2, 0x000d, 1)) args.encoding = aim_gettlv_str(list2, 0x000d, 1); /* * Language. */ if (aim_gettlv(list2, 0x000e, 1)) args.language = aim_gettlv_str(list2, 0x000e, 1); /* Unknown -- two bytes = 0x0001 */ if (aim_gettlv(list2, 0x000a, 1)) ; /* Unknown -- no value */ if (aim_gettlv(list2, 0x000f, 1)) ; if (strlen(clientip1)) args.clientip = (char *)clientip1; if (strlen(clientip2)) args.clientip2 = (char *)clientip2; if (strlen(verifiedip)) args.verifiedip = (char *)verifiedip; /* * This is must be present in PROPOSALs, but will probably not * exist in CANCELs and ACCEPTs. * * Service Data blocks are module-specific in format. */ if ((servdatatlv = aim_gettlv(list2, 0x2711 /* 10001 */, 1))) { aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length); sdbsptr = &sdbs; } if (args.reqclass & AIM_CAPS_ICQSERVERRELAY) incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr); else if (args.reqclass & AIM_CAPS_CHAT) incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, channel, userinfo, &args); if (args.destructor) ((ch2_args_destructor_t)args.destructor)(sess, &args); g_free((char *)args.msg); g_free((char *)args.encoding); g_free((char *)args.language); aim_freetlvchain(&list2); return ret; }
static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int i, ret = 0; aim_rxcallback_t userfunc; guint16 channel; aim_tlvlist_t *tlvlist; char *sn; int snlen; guint16 icbmflags = 0; guint8 flag1 = 0, flag2 = 0; char *msg = NULL; aim_tlv_t *msgblock; /* ICBM Cookie. */ for (i = 0; i < 8; i++) aimbs_get8(bs); /* Channel ID */ channel = aimbs_get16(bs); if (channel != 0x01) { imcb_error(sess->aux_data, "icbm: ICBM received on unsupported channel. Ignoring."); return 0; } snlen = aimbs_get8(bs); sn = aimbs_getstr(bs, snlen); tlvlist = aim_readtlvchain(bs); if (aim_gettlv(tlvlist, 0x0003, 1)) icbmflags |= AIM_IMFLAGS_ACK; if (aim_gettlv(tlvlist, 0x0004, 1)) icbmflags |= AIM_IMFLAGS_AWAY; if ((msgblock = aim_gettlv(tlvlist, 0x0002, 1))) { aim_bstream_t mbs; int featurelen, msglen; aim_bstream_init(&mbs, msgblock->value, msgblock->length); aimbs_get8(&mbs); aimbs_get8(&mbs); for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--) aimbs_get8(&mbs); aimbs_get8(&mbs); aimbs_get8(&mbs); msglen = aimbs_get16(&mbs) - 4; /* final block length */ flag1 = aimbs_get16(&mbs); flag2 = aimbs_get16(&mbs); msg = aimbs_getstr(&mbs, msglen); } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2); g_free(sn); aim_freetlvchain(&tlvlist); return ret; }
/* * It can easily be said that parsing ICBMs is THE single * most difficult thing to do in the in AIM protocol. In * fact, I think I just did say that. * * Below is the best damned solution I've come up with * over the past sixteen months of battling with it. This * can parse both away and normal messages from every client * I have access to. Its not fast, its not clean. But it works. * */ static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int i, ret = 0; guint8 cookie[8]; guint16 channel; aim_userinfo_t userinfo; memset(&userinfo, 0x00, sizeof(aim_userinfo_t)); /* * Read ICBM Cookie. And throw away. */ for (i = 0; i < 8; i++) cookie[i] = aimbs_get8(bs); /* * Channel ID. * * Channel 0x0001 is the message channel. There are * other channels for things called "rendevous" * which represent chat and some of the other new * features of AIM2/3/3.5. * * Channel 0x0002 is the Rendevous channel, which * is where Chat Invitiations and various client-client * connection negotiations come from. * * Channel 0x0004 is used for ICQ authorization, or * possibly any system notice. * */ channel = aimbs_get16(bs); /* * Extract the standard user info block. * * Note that although this contains TLVs that appear contiguous * with the TLVs read below, they are two different pieces. The * userinfo block contains the number of TLVs that contain user * information, the rest are not even though there is no seperation. * aim_extractuserinfo() returns the number of bytes used by the * userinfo tlvs, so you can start reading the rest of them right * afterward. * * That also means that TLV types can be duplicated between the * userinfo block and the rest of the message, however there should * never be two TLVs of the same type in one block. * */ aim_extractuserinfo(sess, bs, &userinfo); /* * From here on, its depends on what channel we're on. * * Technically all channels have a TLV list have this, however, * for the common channel 1 case, in-place parsing is used for * performance reasons (less memory allocation). */ if (channel == 1) { ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie); } else if (channel == 2) { aim_tlvlist_t *tlvlist; /* * Read block of TLVs (not including the userinfo data). All * further data is derived from what is parsed here. */ tlvlist = aim_readtlvchain(bs); ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); aim_freetlvchain(&tlvlist); } else if (channel == 4) { aim_tlvlist_t *tlvlist; tlvlist = aim_readtlvchain(bs); ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); aim_freetlvchain(&tlvlist); } else { imcb_error(sess->aux_data, "ICBM received on an unsupported channel. Ignoring."); return 0; } return ret; }
static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) { aim_rxcallback_t userfunc; int ret = 0; struct aim_chat_exchangeinfo *exchanges = NULL; int curexchange; aim_tlv_t *exchangetlv; fu8_t maxrooms = 0; aim_tlvlist_t *tlvlist, *innerlist; tlvlist = aim_readtlvchain(bs); /* * Type 0x0002: Maximum concurrent rooms. */ if (aim_gettlv(tlvlist, 0x0002, 1)) maxrooms = aim_gettlv8(tlvlist, 0x0002, 1); /* * Type 0x0003: Exchange information * * There can be any number of these, each one * representing another exchange. * */ for (curexchange = 0; ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))); ) { aim_bstream_t tbs; aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length); curexchange++; exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); /* exchange number */ exchanges[curexchange-1].number = aimbs_get16(&tbs); innerlist = aim_readtlvchain(&tbs); /* * Type 0x000a: Unknown. * * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others). * */ if (aim_gettlv(innerlist, 0x000a, 1)) ; /* * Type 0x000d: Unknown. */ if (aim_gettlv(innerlist, 0x000d, 1)) ; /* * Type 0x0004: Unknown */ if (aim_gettlv(innerlist, 0x0004, 1)) ; /* * Type 0x0002: Unknown */ if (aim_gettlv(innerlist, 0x0002, 1)) { fu16_t classperms; classperms = aim_gettlv16(innerlist, 0x0002, 1); faimdprintf(sess, 1, "faim: class permissions %x\n", classperms); } /* * Type 0x00c9: Flags * * 1 Evilable * 2 Nav Only * 4 Instancing Allowed * 8 Occupant Peek Allowed * */ if (aim_gettlv(innerlist, 0x00c9, 1)) exchanges[curexchange-1].flags = aim_gettlv16(innerlist, 0x00c9, 1); /* * Type 0x00ca: Creation Date */ if (aim_gettlv(innerlist, 0x00ca, 1)) ; /* * Type 0x00d0: Mandatory Channels? */ if (aim_gettlv(innerlist, 0x00d0, 1)) ; /* * Type 0x00d1: Maximum Message length */ if (aim_gettlv(innerlist, 0x00d1, 1)) ; /* * Type 0x00d2: Maximum Occupancy? */ if (aim_gettlv(innerlist, 0x00d2, 1)) ; /* * Type 0x00d3: Exchange Description */ if (aim_gettlv(innerlist, 0x00d3, 1)) exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1); else exchanges[curexchange-1].name = NULL; /* * Type 0x00d4: Exchange Description URL */ if (aim_gettlv(innerlist, 0x00d4, 1)) ; /* * Type 0x00d5: Creation Permissions * * 0 Creation not allowed * 1 Room creation allowed * 2 Exchange creation allowed * */ if (aim_gettlv(innerlist, 0x00d5, 1)) { fu8_t createperms; createperms = aim_gettlv8(innerlist, 0x00d5, 1); } /* * Type 0x00d6: Character Set (First Time) */ if (aim_gettlv(innerlist, 0x00d6, 1)) exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1); else exchanges[curexchange-1].charset1 = NULL; /* * Type 0x00d7: Language (First Time) */ if (aim_gettlv(innerlist, 0x00d7, 1)) exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1); else exchanges[curexchange-1].lang1 = NULL; /* * Type 0x00d8: Character Set (Second Time) */ if (aim_gettlv(innerlist, 0x00d8, 1)) exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1); else exchanges[curexchange-1].charset2 = NULL; /* * Type 0x00d9: Language (Second Time) */ if (aim_gettlv(innerlist, 0x00d9, 1)) exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1); else exchanges[curexchange-1].lang2 = NULL; /* * Type 0x00da: Unknown */ if (aim_gettlv(innerlist, 0x00da, 1)) ; aim_freetlvchain(&innerlist); } /* * Call client. */ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges); for (curexchange--; curexchange >= 0; curexchange--) { free(exchanges[curexchange].name); free(exchanges[curexchange].charset1); free(exchanges[curexchange].lang1); free(exchanges[curexchange].charset2); free(exchanges[curexchange].lang2); } free(exchanges); aim_freetlvchain(&tlvlist); return ret; }
/* * This is sent back as a general response to the login command. * It can be either an error or a success, depending on the * precense of certain TLVs. * * The client should check the value passed as errorcode. If * its nonzero, there was an error. * */ static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_tlvlist_t *tlvlist; aim_rxcallback_t userfunc; struct aim_authresp_info info; int ret = 0; memset(&info, 0, sizeof(info)); /* * Read block of TLVs. All further data is derived * from what is parsed here. */ tlvlist = aim_readtlvchain(bs); /* * No matter what, we should have a screen name. */ memset(sess->sn, 0, sizeof(sess->sn)); if (aim_gettlv(tlvlist, 0x0001, 1)) { info.sn = aim_gettlv_str(tlvlist, 0x0001, 1); strncpy(sess->sn, info.sn, sizeof(sess->sn)); } /* * Check for an error code. If so, we should also * have an error url. */ if (aim_gettlv(tlvlist, 0x0008, 1)) { info.errorcode = aim_gettlv16(tlvlist, 0x0008, 1); } if (aim_gettlv(tlvlist, 0x0004, 1)) { info.errorurl = aim_gettlv_str(tlvlist, 0x0004, 1); } /* * BOS server address. */ if (aim_gettlv(tlvlist, 0x0005, 1)) { info.bosip = aim_gettlv_str(tlvlist, 0x0005, 1); } /* * Authorization cookie. */ if (aim_gettlv(tlvlist, 0x0006, 1)) { aim_tlv_t *tmptlv; tmptlv = aim_gettlv(tlvlist, 0x0006, 1); info.cookie = tmptlv->value; } /* * The email address attached to this account * Not available for ICQ logins. */ if (aim_gettlv(tlvlist, 0x0011, 1)) { info.email = aim_gettlv_str(tlvlist, 0x0011, 1); } /* * The registration status. (Not real sure what it means.) * Not available for ICQ logins. * * 1 = No disclosure * 2 = Limited disclosure * 3 = Full disclosure * * This has to do with whether your email address is available * to other users or not. AFAIK, this feature is no longer used. * */ if (aim_gettlv(tlvlist, 0x0013, 1)) { info.regstatus = aim_gettlv16(tlvlist, 0x0013, 1); } if (aim_gettlv(tlvlist, 0x0040, 1)) { info.latestbeta.build = aim_gettlv32(tlvlist, 0x0040, 1); } if (aim_gettlv(tlvlist, 0x0041, 1)) { info.latestbeta.url = aim_gettlv_str(tlvlist, 0x0041, 1); } if (aim_gettlv(tlvlist, 0x0042, 1)) { info.latestbeta.info = aim_gettlv_str(tlvlist, 0x0042, 1); } if (aim_gettlv(tlvlist, 0x0043, 1)) { info.latestbeta.name = aim_gettlv_str(tlvlist, 0x0043, 1); } if (aim_gettlv(tlvlist, 0x0048, 1)) { ; /* no idea what this is */ } if (aim_gettlv(tlvlist, 0x0044, 1)) { info.latestrelease.build = aim_gettlv32(tlvlist, 0x0044, 1); } if (aim_gettlv(tlvlist, 0x0045, 1)) { info.latestrelease.url = aim_gettlv_str(tlvlist, 0x0045, 1); } if (aim_gettlv(tlvlist, 0x0046, 1)) { info.latestrelease.info = aim_gettlv_str(tlvlist, 0x0046, 1); } if (aim_gettlv(tlvlist, 0x0047, 1)) { info.latestrelease.name = aim_gettlv_str(tlvlist, 0x0047, 1); } if (aim_gettlv(tlvlist, 0x0049, 1)) { ; /* no idea what this is */ } if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003))) { ret = userfunc(sess, rx, &info); } g_free(info.sn); g_free(info.bosip); g_free(info.errorurl); g_free(info.email); g_free(info.latestrelease.name); g_free(info.latestrelease.url); g_free(info.latestrelease.info); g_free(info.latestbeta.name); g_free(info.latestbeta.url); g_free(info.latestbeta.info); aim_freetlvchain(&tlvlist); return ret; }
/** * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet. */ static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_tlvlist_t *tl; aim_tlv_t *datatlv; aim_bstream_t qbs; guint32 ouruin; guint16 cmdlen, cmd, reqid; if (!(tl = aim_readtlvchain(bs)) || !(datatlv = aim_gettlv(tl, 0x0001, 1))) { aim_freetlvchain(&tl); imcb_error(sess->aux_data, "corrupt ICQ response\n"); return 0; } aim_bstream_init(&qbs, datatlv->value, datatlv->length); cmdlen = aimbs_getle16(&qbs); ouruin = aimbs_getle32(&qbs); cmd = aimbs_getle16(&qbs); reqid = aimbs_getle16(&qbs); if (cmd == 0x0041) { /* offline message */ guint16 msglen; struct aim_icq_offlinemsg msg; aim_rxcallback_t userfunc; memset(&msg, 0, sizeof(msg)); msg.sender = aimbs_getle32(&qbs); msg.year = aimbs_getle16(&qbs); msg.month = aimbs_getle8(&qbs); msg.day = aimbs_getle8(&qbs); msg.hour = aimbs_getle8(&qbs); msg.minute = aimbs_getle8(&qbs); msg.type = aimbs_getle16(&qbs); msglen = aimbs_getle16(&qbs); msg.msg = aimbs_getstr(&qbs, msglen); if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG))) ret = userfunc(sess, rx, &msg); g_free(msg.msg); } else if (cmd == 0x0042) { aim_rxcallback_t userfunc; if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE))) ret = userfunc(sess, rx); } else if (cmd == 0x07da) { /* information */ guint16 subtype; struct aim_icq_info *info; aim_rxcallback_t userfunc; subtype = aimbs_getle16(&qbs); aim_bstream_advance(&qbs, 1); /* 0x0a */ /* find another data from the same request */ for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next); if (!info) { info = g_new0(struct aim_icq_info, 1); info->reqid = reqid; info->next = sess->icq_info; sess->icq_info = info; }