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) && (inforeq->infotype != AIM_GETINFO_CAPABILITIES)) { 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, caps * will be 5. */ if (inforeq->infotype == AIM_GETINFO_GENERALINFO) { text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1); text = aim_gettlv_str(tlvlist, 0x0002, 1); } else if (inforeq->infotype == AIM_GETINFO_AWAYMESSAGE) { text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1); text = aim_gettlv_str(tlvlist, 0x0004, 1); } else if (inforeq->infotype == AIM_GETINFO_CAPABILITIES) { aim_tlv_t *ct; if ((ct = aim_gettlv(tlvlist, 0x0005, 1))) { aim_bstream_t cbs; aim_bstream_init(&cbs, ct->value, ct->length); userinfo.capabilities = aim_getcap(sess, &cbs, ct->length); userinfo.present = AIM_USERINFO_PRESENT_CAPABILITIES; } } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, &userinfo, inforeq->infotype, text_encoding, text); free(text_encoding); free(text); aim_freetlvchain(&tlvlist); if (origsnac) free(origsnac->data); free(origsnac); return ret; }
/* * AIM is fairly regular about providing user info. This is a generic * routine to extract it in its standard form. */ faim_internal int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo) { int curtlv, tlvcnt; fu8_t snlen; if (!bs || !outinfo) return -EINVAL; /* Clear out old data first */ memset(outinfo, 0x00, sizeof(aim_userinfo_t)); /* * Screen name. Stored as an unterminated string prepended with a * byte containing its length. */ snlen = aimbs_get8(bs); aimbs_getrawbuf(bs, outinfo->sn, snlen); /* * Warning Level. Stored as an unsigned short. */ outinfo->warnlevel = aimbs_get16(bs); /* * TLV Count. Unsigned short representing the number of * Type-Length-Value triples that follow. */ tlvcnt = aimbs_get16(bs); /* * Parse out the Type-Length-Value triples as they're found. */ for (curtlv = 0; curtlv < tlvcnt; curtlv++) { int endpos; fu16_t type, length; type = aimbs_get16(bs); length = aimbs_get16(bs); endpos = aim_bstream_curpos(bs) + length; if (type == 0x0001) { /* * Type = 0x0001: User flags * * Specified as any of the following ORed together: * 0x0001 Trial (user less than 60days) * 0x0002 Unknown bit 2 * 0x0004 AOL Main Service user * 0x0008 Unknown bit 4 * 0x0010 Free (AIM) user * 0x0020 Away * 0x0400 ActiveBuddy * */ outinfo->flags = aimbs_get16(bs); } else if (type == 0x0002) { /* * Type = 0x0002: Member-Since date. * * The time/date that the user originally registered for * the service, stored in time_t format. */ outinfo->membersince = aimbs_get32(bs); } else if (type == 0x0003) { /* * Type = 0x0003: On-Since date. * * The time/date that the user started their current * session, stored in time_t format. */ outinfo->onlinesince = aimbs_get32(bs); } else if (type == 0x0004) { /* * Type = 0x0004: Idle time. * * Number of seconds since the user actively used the * service. * * Note that the client tells the server when to start * counting idle times, so this may or may not be * related to reality. */ outinfo->idletime = aimbs_get16(bs); } else if (type == 0x0006) { /* * Type = 0x0006: ICQ Online Status * * ICQ's Away/DND/etc "enriched" status. Some decoding * of values done by Scott <*****@*****.**> */ aimbs_get16(bs); outinfo->icqinfo.status = aimbs_get16(bs); } else if (type == 0x000a) { /* * Type = 0x000a * * ICQ User IP Address. * Ahh, the joy of ICQ security. */ outinfo->icqinfo.ipaddr = aimbs_get32(bs); } else if (type == 0x000c) { /* * Type = 0x000c * * random crap containing the IP address, * apparently a port number, and some Other Stuff. * */ aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25); } else if (type == 0x000d) { /* * Type = 0x000d * * Capability information. * */ outinfo->capabilities = aim_getcap(sess, bs, length); outinfo->capspresent = 1; } else if (type == 0x000e) { /* * Type = 0x000e * * Unknown. Always of zero length, and always only * on AOL users. * * Ignore. * */ } else if ((type == 0x000f) || (type == 0x0010)) { /* * Type = 0x000f: Session Length. (AIM) * Type = 0x0010: Session Length. (AOL) * * The duration, in seconds, of the user's current * session. * * Which TLV type this comes in depends on the * service the user is using (AIM or AOL). * */ outinfo->sessionlen = aimbs_get32(bs); } else { /* * Reaching here indicates that either AOL has * added yet another TLV for us to deal with, * or the parsing has gone Terribly Wrong. * * Either way, inform the owner and attempt * recovery. * */ faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n"); faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn); faimdprintf(sess, 0, "userinfo: type =0x%04x\n",type); faimdprintf(sess, 0, "userinfo: length=0x%04x\n", length); } /* Save ourselves. */ aim_bstream_setpos(bs, endpos); } return 0; }
/* * AIM is fairly regular about providing user info. This is a generic * routine to extract it in its standard form. */ faim_internal int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo) { int curtlv, tlvcnt; fu8_t snlen; if (!bs || !outinfo) return -EINVAL; /* Clear out old data first */ memset(outinfo, 0x00, sizeof(aim_userinfo_t)); /* * Screen name. Stored as an unterminated string prepended with a * byte containing its length. */ snlen = aimbs_get8(bs); aimbs_getrawbuf(bs, outinfo->sn, snlen); /* * Warning Level. Stored as an unsigned short. */ outinfo->warnlevel = aimbs_get16(bs); /* * TLV Count. Unsigned short representing the number of * Type-Length-Value triples that follow. */ tlvcnt = aimbs_get16(bs); /* * Parse out the Type-Length-Value triples as they're found. */ for (curtlv = 0; curtlv < tlvcnt; curtlv++) { int endpos; fu16_t type, length; type = aimbs_get16(bs); length = aimbs_get16(bs); endpos = aim_bstream_curpos(bs) + length; if (type == 0x0001) { /* * Type = 0x0001: User flags * * Specified as any of the following ORed together: * 0x0001 Trial (user less than 60days) * 0x0002 Unknown bit 2 * 0x0004 AOL Main Service user * 0x0008 Unknown bit 4 * 0x0010 Free (AIM) user * 0x0020 Away * 0x0400 ActiveBuddy * */ outinfo->flags = aimbs_get16(bs); outinfo->present |= AIM_USERINFO_PRESENT_FLAGS; } else if (type == 0x0002) { /* * Type = 0x0002: Account creation time. * * The time/date that the user originally registered for * the service, stored in time_t format. * * I'm not sure how this differs from type 5 ("member * since"). * * Note: This is the field formerly known as "member * since". All these years and I finally found out * that I got the name wrong. */ outinfo->createtime = aimbs_get32(bs); outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME; } else if (type == 0x0003) { /* * Type = 0x0003: On-Since date. * * The time/date that the user started their current * session, stored in time_t format. */ outinfo->onlinesince = aimbs_get32(bs); outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE; } else if (type == 0x0004) { /* * Type = 0x0004: Idle time. * * Number of seconds since the user actively used the * service. * * Note that the client tells the server when to start * counting idle times, so this may or may not be * related to reality. */ outinfo->idletime = aimbs_get16(bs); outinfo->present |= AIM_USERINFO_PRESENT_IDLE; } else if (type == 0x0005) { /* * Type = 0x0005: Member since date. * * The time/date that the user originally registered for * the service, stored in time_t format. * * This is sometimes sent instead of type 2 ("account * creation time"), particularly in the self-info. */ outinfo->membersince = aimbs_get32(bs); outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE; } else if (type == 0x0006) { /* * Type = 0x0006: ICQ Online Status * * ICQ's Away/DND/etc "enriched" status. Some decoding * of values done by Scott <*****@*****.**> */ aimbs_get16(bs); outinfo->icqinfo.status = aimbs_get16(bs); outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS; } else if (type == 0x000a) { /* * Type = 0x000a * * ICQ User IP Address. * Ahh, the joy of ICQ security. */ outinfo->icqinfo.ipaddr = aimbs_get32(bs); outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR; } else if (type == 0x000c) { /* * Type = 0x000c * * random crap containing the IP address, * apparently a port number, and some Other Stuff. * */ aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25); outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA; } else if (type == 0x000d) { /* * Type = 0x000d * * Capability information. * */ outinfo->capabilities = aim_getcap(sess, bs, length); outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; } else if (type == 0x000e) { /* * Type = 0x000e * * Unknown. Always of zero length, and always only * on AOL users. * * Ignore. * */ } else if ((type == 0x000f) || (type == 0x0010)) { /* * Type = 0x000f: Session Length. (AIM) * Type = 0x0010: Session Length. (AOL) * * The duration, in seconds, of the user's current * session. * * Which TLV type this comes in depends on the * service the user is using (AIM or AOL). * */ outinfo->sessionlen = aimbs_get32(bs); outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN; } else if (type == 0x001d) { /* * Type 29: Unknown. * * Currently very rare. Always 18 bytes of mostly zero. */ } else if (type == 0x001e) { /* * Type 30: Unknown. * * Always four bytes, but it doesn't look like an int. */ } else { /* * Reaching here indicates that either AOL has * added yet another TLV for us to deal with, * or the parsing has gone Terribly Wrong. * * Either way, inform the owner and attempt * recovery. * */ faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n"); faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn); dumptlv(sess, type, bs, length); } /* Save ourselves. */ aim_bstream_setpos(bs, endpos); } return 0; }
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; }