X-Git-Url: http://andersk.mit.edu/gitweb/libfaim.git/blobdiff_plain/646c6b5273a2272632e50e7393f090f7c8bf06eb..HEAD:/src/info.c diff --git a/src/info.c b/src/info.c index de05b79..5a01c07 100644 --- a/src/info.c +++ b/src/info.c @@ -2,7 +2,7 @@ * aim_info.c * * The functions here are responsible for requesting and parsing information- - * gathering SNACs. + * gathering SNACs. Or something like that. * */ @@ -10,689 +10,614 @@ #include struct aim_priv_inforeq { - char sn[MAXSNLEN+1]; - unsigned short infotype; + char sn[MAXSNLEN+1]; + fu16_t infotype; }; -faim_export unsigned long aim_getinfo(struct aim_session_t *sess, - struct aim_conn_t *conn, - const char *sn, - unsigned short infotype) +faim_export int aim_getinfo(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu16_t infotype) { - struct command_tx_struct *newpacket; - struct aim_priv_inforeq privdata; - int i = 0; + struct aim_priv_inforeq privdata; + aim_frame_t *fr; + aim_snacid_t snacid; - if (!sess || !conn || !sn) - return 0; + if (!sess || !conn || !sn) + return -EINVAL; - if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 12+1+strlen(sn)))) - return -1; + if ((infotype != AIM_GETINFO_GENERALINFO) && (infotype != AIM_GETINFO_AWAYMESSAGE)) + return -EINVAL; - newpacket->lock = 1; + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn)))) + return -ENOMEM; - i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid); + strncpy(privdata.sn, sn, sizeof(privdata.sn)); + privdata.infotype = infotype; + snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq)); + + aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid); + aimbs_put16(&fr->data, infotype); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); - i += aimutil_put16(newpacket->data+i, infotype); - i += aimutil_put8(newpacket->data+i, strlen(sn)); - i += aimutil_putstr(newpacket->data+i, sn, strlen(sn)); + return 0; +} - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); +faim_export const char *aim_userinfo_sn(aim_userinfo_t *ui) +{ - strncpy(privdata.sn, sn, sizeof(privdata.sn)); - privdata.infotype = infotype; - aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq)); + if (!ui) + return NULL; - return sess->snac_nextid; + return ui->sn; } -faim_internal int aim_parse_locateerr(struct aim_session_t *sess, - struct command_rx_struct *command) +faim_export fu16_t aim_userinfo_flags(aim_userinfo_t *ui) { - u_long snacid = 0x000000000; - struct aim_snac_t *snac = NULL; - int ret = 0; - rxcallback_t userfunc = NULL; - char *dest; - unsigned short reason = 0; - - /* - * Get SNAC from packet and look it up - * the list of unrepliedto/outstanding - * SNACs. - * - */ - snacid = aimutil_get32(command->data+6); - snac = aim_remsnac(sess, snacid); - - if (!snac) { - faimdprintf(sess, 0, "locerr: got an locate-failed error on an unknown SNAC ID! (%08lx)\n", snacid); - dest = NULL; - } else - dest = snac->data; - - reason = aimutil_get16(command->data+10); - - /* - * Call client. - */ - userfunc = aim_callhandler(sess, command->conn, 0x0002, 0x0001); - if (userfunc) - ret = userfunc(sess, command, dest, reason); - else - ret = 0; - - if (snac) { - free(snac->data); - free(snac); - } - - return ret; + + if (!ui) + return 0; + + return ui->flags; } -/* - * Capability blocks. - */ -u_char aim_caps[8][16] = { - - /* Buddy icon */ - {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Voice */ - {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* IM image */ - {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Chat */ - {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Get file */ - {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Send file */ - {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Saves stock portfolios */ - {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Games */ - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, -}; +faim_export fu16_t aim_userinfo_idle(aim_userinfo_t *ui) +{ + + if (!ui) + return 0; + + return ui->idletime; +} -faim_internal unsigned short aim_getcap(struct aim_session_t *sess, unsigned char *capblock, int buflen) +faim_export float aim_userinfo_warnlevel(aim_userinfo_t *ui) { - u_short ret = 0; - int y; - int offset = 0; - int identified; - - while (offset < buflen) { - identified = 0; - for(y=0; y < (sizeof(aim_caps)/0x10); y++) { - if (memcmp(&aim_caps[y], capblock+offset, 0x10) == 0) { - switch(y) { - case 0: ret |= AIM_CAPS_BUDDYICON; identified++; break; - case 1: ret |= AIM_CAPS_VOICE; identified++; break; - case 2: ret |= AIM_CAPS_IMIMAGE; identified++; break; - case 3: ret |= AIM_CAPS_CHAT; identified++; break; - case 4: ret |= AIM_CAPS_GETFILE; identified++; break; - case 5: ret |= AIM_CAPS_SENDFILE; identified++; break; - case 6: ret |= AIM_CAPS_GAMES; identified++; break; - case 7: ret |= AIM_CAPS_SAVESTOCKS; identified++; break; - } - } - } - if (!identified) { - faimdprintf(sess, 0, "unknown capability!\n"); - ret |= 0xff00; - } - - offset += 0x10; - } - return ret; + + if (!ui) + return 0.00; + + return (ui->warnlevel / 10); } -faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps) +faim_export time_t aim_userinfo_membersince(aim_userinfo_t *ui) { - int offset = 0; - - if (!capblock) - return -1; - - if ((caps & AIM_CAPS_BUDDYICON) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[0], sizeof(aim_caps[0])); - offset += sizeof(aim_caps[1]); - } - if ((caps & AIM_CAPS_VOICE) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[1], sizeof(aim_caps[1])); - offset += sizeof(aim_caps[1]); - } - if ((caps & AIM_CAPS_IMIMAGE) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[2], sizeof(aim_caps[2])); - offset += sizeof(aim_caps[2]); - } - if ((caps & AIM_CAPS_CHAT) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[3], sizeof(aim_caps[3])); - offset += sizeof(aim_caps[3]); - } - if ((caps & AIM_CAPS_GETFILE) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[4], sizeof(aim_caps[4])); - offset += sizeof(aim_caps[4]); - } - if ((caps & AIM_CAPS_SENDFILE) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[5], sizeof(aim_caps[5])); - offset += sizeof(aim_caps[5]); - } - if ((caps & AIM_CAPS_GAMES) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[6], sizeof(aim_caps[6])); - offset += sizeof(aim_caps[6]); - } - if ((caps & AIM_CAPS_SAVESTOCKS) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[7], sizeof(aim_caps[7])); - offset += sizeof(aim_caps[7]); - } - - return offset; + + if (!ui) + return 0; + + return (time_t)ui->membersince; } -/* - * 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(struct aim_session_t *sess, unsigned char *buf, struct aim_userinfo_s *outinfo) +faim_export time_t aim_userinfo_onlinesince(aim_userinfo_t *ui) { - int i = 0; - int tlvcnt = 0; - int curtlv = 0; - int tlv1 = 0; - u_short curtype; - int lastvalid; - - - if (!buf || !outinfo) - return -1; - - /* Clear out old data first */ - memset(outinfo, 0x00, sizeof(struct aim_userinfo_s)); - - /* - * Screen name. Stored as an unterminated string prepended - * with an unsigned byte containing its length. - */ - if (buf[i] < MAXSNLEN) { - memcpy(outinfo->sn, &(buf[i+1]), buf[i]); - outinfo->sn[(int)buf[i]] = '\0'; - } else { - memcpy(outinfo->sn, &(buf[i+1]), MAXSNLEN-1); - outinfo->sn[MAXSNLEN] = '\0'; - } - i = 1 + (int)buf[i]; - - /* - * Warning Level. Stored as an unsigned short. - */ - outinfo->warnlevel = aimutil_get16(&buf[i]); - i += 2; - - /* - * TLV Count. Unsigned short representing the number of - * Type-Length-Value triples that follow. - */ - tlvcnt = aimutil_get16(&buf[i]); - i += 2; - - /* - * Parse out the Type-Length-Value triples as they're found. - */ - while (curtlv < tlvcnt) { - lastvalid = 1; - curtype = aimutil_get16(&buf[i]); - switch (curtype) { - /* - * Type = 0x0000: Invalid - * - * AOL has been trying to throw these in just to break us. - * They're real nice guys over there at AOL. - * - * Just skip the two zero bytes and continue on. (This doesn't - * count towards tlvcnt!) - */ - case 0x0000: - lastvalid = 0; - i += 2; - break; - - /* - * Type = 0x0001: User flags - * - * Specified as any of the following bitwise 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 - * - * In some odd cases, we can end up with more - * than one of these. We only want the first, - * as the others may not be something we want. - * - */ - case 0x0001: - if (tlv1) /* use only the first */ - break; - outinfo->flags = aimutil_get16(&buf[i+4]); - tlv1++; - break; - - /* - * Type = 0x0002: Member-Since date. - * - * The time/date that the user originally - * registered for the service, stored in - * time_t format - */ - case 0x0002: - outinfo->membersince = aimutil_get32(&buf[i+4]); - break; - - /* - * Type = 0x0003: On-Since date. - * - * The time/date that the user started - * their current session, stored in time_t - * format. - */ - case 0x0003: - outinfo->onlinesince = aimutil_get32(&buf[i+4]); - break; - - /* - * Type = 0x0004: Idle time. - * - * Number of seconds since the user - * actively used the service. - */ - case 0x0004: - outinfo->idletime = aimutil_get16(&buf[i+4]); - break; - - /* - * Type = 0x0006: ICQ Online Status - * - * ICQ's Away/DND/etc "enriched" status - * Some decoding of values done by Scott - */ - case 0x0006: - outinfo->icqinfo.status = aimutil_get16(buf+i+2+2+2); - break; - - - /* - * Type = 0x000a - * - * ICQ User IP Address. - * Ahh, the joy of ICQ security. - */ - case 0x000a: - outinfo->icqinfo.ipaddr = aimutil_get32(&buf[i+4]); - break; - - /* Type = 0x000c - * - * random crap containing the IP address, - * apparently a port number, and some Other Stuff. - * - */ - case 0x000c: - memcpy(outinfo->icqinfo.crap, &buf[i+4], 0x25); - break; - - /* - * Type = 0x000d - * - * Capability information. Not real sure of - * actual decoding. See comment on aim_bos_setprofile() - * in aim_misc.c about the capability block, its the same. - * - */ - case 0x000d: - { - int len; - len = aimutil_get16(buf+i+2); - if (!len) - break; - - outinfo->capabilities = aim_getcap(sess, buf+i+4, len); - } - break; - - /* - * Type = 0x000e - * - * Unknown. Always of zero length, and always only - * on AOL users. - * - * Ignore. - * - */ - case 0x000e: - break; - - /* - * 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). - * - */ - case 0x000f: - case 0x0010: - outinfo->sessionlen = aimutil_get32(&buf[i+4]); - break; - - /* - * 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. - * - */ - default: - { - int len,z = 0, y = 0, x = 0; - char tmpstr[160]; - - faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n"); - faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn); - faimdprintf(sess, 0, "userinfo: curtlv=0x%04x\n", curtlv); - faimdprintf(sess, 0, "userinfo: type =0x%04x\n",aimutil_get16(&buf[i])); - faimdprintf(sess, 0, "userinfo: length=0x%04x\n", len = aimutil_get16(&buf[i+2])); - faimdprintf(sess, 0, "userinfo: data: \n"); - while (zonlinesince; } -/* - * Oncoming Buddy notifications contain a subset of the - * user information structure. Its close enough to run - * through aim_extractuserinfo() however. - * - */ -faim_internal int aim_parse_oncoming_middle(struct aim_session_t *sess, - struct command_rx_struct *command) +faim_export fu32_t aim_userinfo_sessionlen(aim_userinfo_t *ui) { - struct aim_userinfo_s userinfo; - u_int i = 0; - rxcallback_t userfunc=NULL; - i = 10; - i += aim_extractuserinfo(sess, command->data+i, &userinfo); + if (!ui) + return 0; + + return ui->sessionlen; +} - userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING); - if (userfunc) - i = userfunc(sess, command, &userinfo); +faim_export int aim_userinfo_hascap(aim_userinfo_t *ui, fu16_t cap) +{ - return 1; + if (!ui || !ui->capspresent) + return -1; + + return !!(ui->capabilities & cap); } + /* - * Offgoing Buddy notifications contain no useful - * information other than the name it applies to. - * + * Capability blocks. + */ +static const struct { + unsigned short flag; + unsigned char data[16]; +} aim_caps[] = { + + {AIM_CAPS_BUDDYICON, + {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_VOICE, + {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_IMIMAGE, + {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_CHAT, + {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GETFILE, + {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SENDFILE, + {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SAVESTOCKS, + {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Indeed, there are two of these. The former appears to be correct, + * but in some versions of winaim, the second one is set. Either they + * forgot to fix endianness, or they made a typo. It really doesn't + * matter which. + */ + {AIM_CAPS_GAMES, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + {AIM_CAPS_GAMES2, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SENDBUDDYLIST, + {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_LAST} +}; + +/* + * This still takes a length parameter even with a bstream because capabilities + * are not naturally bounded. + * */ -faim_internal int aim_parse_offgoing_middle(struct aim_session_t *sess, - struct command_rx_struct *command) +faim_internal fu16_t aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len) { - char sn[MAXSNLEN+1]; - u_int i = 0; - rxcallback_t userfunc=NULL; + fu16_t flags = 0; + int offset; + + for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) { + fu8_t *cap; + int i, identified; + + cap = aimbs_getraw(bs, 0x10); + + for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { + + if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) { + flags |= aim_caps[i].flag; + identified++; + break; /* should only match once... */ + + } + } + + if (!identified) + faimdprintf(sess, 0, "unknown capability!\n"); + + free(cap); + } + + return flags; +} + +faim_internal int aim_putcap(aim_bstream_t *bs, fu16_t caps) +{ + int i; + + if (!bs) + return -EINVAL; - strncpy(sn, (char *)command->data+11, (int)command->data[10]); - sn[(int)command->data[10]] = '\0'; + for (i = 0; aim_bstream_empty(bs); i++) { - userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING); - if (userfunc) - i = userfunc(sess, command, sn); + if (aim_caps[i].flag == AIM_CAPS_LAST) + break; - return 1; + if (caps & aim_caps[i].flag) + aimbs_putraw(bs, aim_caps[i].data, 0x10); + + } + + return 0; } /* - * This parses the user info stuff out all nice and pretty then calls - * the higher-level callback (in the user app). - * + * AIM is fairly regular about providing user info. This is a generic + * routine to extract it in its standard form. */ -faim_internal int aim_parse_userinfo_middle(struct aim_session_t *sess, - struct command_rx_struct *command) +faim_internal int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo) { - struct aim_userinfo_s userinfo; - char *text_encoding = NULL; - char *text = NULL; - u_int i = 0; - rxcallback_t userfunc=NULL; - struct aim_tlvlist_t *tlvlist; - struct aim_snac_t *origsnac = NULL; - u_long snacid; - struct aim_priv_inforeq *inforeq; - - snacid = aimutil_get32(&command->data[6]); - origsnac = aim_remsnac(sess, snacid); - - if (!origsnac || !origsnac->data) { - faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n"); - return 1; - } - - inforeq = (struct aim_priv_inforeq *)origsnac->data; - - switch (inforeq->infotype) { - case AIM_GETINFO_GENERALINFO: - case AIM_GETINFO_AWAYMESSAGE: - i = 10; - - /* - * extractuserinfo will give us the basic metaTLV information - */ - i += aim_extractuserinfo(sess, command->data+i, &userinfo); - - /* - * However, in this command, there's usually more TLVs following... - */ - tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); - - /* - * 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); - } - - userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO); - if (userfunc) { - i = userfunc(sess, - command, - &userinfo, - text_encoding, - text, - inforeq->infotype); - } - - free(text_encoding); - free(text); - aim_freetlvchain(&tlvlist); - break; - default: - faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype); - break; - } - - if (origsnac) { - if (origsnac->data) - free(origsnac->data); - free(origsnac); - } - - return 1; + 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; } /* * Inverse of aim_extractuserinfo() */ -faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info) +faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info) { - int i = 0, numtlv = 0; - struct aim_tlvlist_t *tlvlist = NULL; + aim_tlvlist_t *tlvlist = NULL; - if (!buf || !info) - return 0; + if (!bs || !info) + return -EINVAL; - i += aimutil_put8(buf+i, strlen(info->sn)); - i += aimutil_putstr(buf+i, info->sn, strlen(info->sn)); + aimbs_put8(bs, strlen(info->sn)); + aimbs_putraw(bs, info->sn, strlen(info->sn)); - i += aimutil_put16(buf+i, info->warnlevel); + aimbs_put16(bs, info->warnlevel); - aim_addtlvtochain16(&tlvlist, 0x0001, info->flags); - numtlv++; + aim_addtlvtochain16(&tlvlist, 0x0001, info->flags); + aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince); + aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince); + aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime); - aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince); - numtlv++; +#if ICQ_OSCAR_SUPPORT + if (atoi(info->sn) != 0) { + aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status); + aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr); + } +#endif - aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince); - numtlv++; + aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities); - aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime); - numtlv++; + aim_addtlvtochain32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); -#if ICQ_OSCAR_SUPPORT - if(atoi(info->sn) != 0) { - aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status); - aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr); - } -#endif + aimbs_put16(bs, aim_counttlvchain(&tlvlist)); + aim_writetlvchain(bs, &tlvlist); + aim_freetlvchain(&tlvlist); + + return 0; +} - aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities); - numtlv++; +faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !info) + return -EINVAL; - aim_addtlvtochain32(&tlvlist, (unsigned short)((info->flags)&AIM_FLAG_AOL?0x0010:0x000f), info->sessionlen); - numtlv++; + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid); + aim_putuserinfo(&fr->data, info); - i += aimutil_put16(buf+i, numtlv); /* tlvcount */ - i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); /* tlvs */ - aim_freetlvchain(&tlvlist); + aim_tx_enqueue(sess, fr); - return i; + return 0; } -faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info) +faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn) { - struct command_tx_struct *tx; - int i = 0; + aim_frame_t *fr; + aim_snacid_t snacid; - if (!sess || !conn || !info) - return 0; + if (!sess || !conn || !sn) + return -EINVAL; - if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152))) - return -1; + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; - tx->lock = 1; + snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); - i += aimutil_put16(tx->data+i, 0x0003); - i += aimutil_put16(tx->data+i, 0x000b); - i += aimutil_put16(tx->data+i, 0x0000); - i += aimutil_put16(tx->data+i, 0x0000); - i += aimutil_put16(tx->data+i, 0x0000); + aim_tx_enqueue(sess, fr); - i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info); + return 0; +} - tx->commandlen = i; - tx->lock = 0; - aim_tx_enqueue(sess, tx); +/* + * Huh? What is this? + */ +faim_export int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); - return 0; + aim_tx_enqueue(sess, fr); + + return 0; } -faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn) +/* + * 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) { - struct command_tx_struct *tx; - int i = 0; + aim_tlvlist_t *tlvlist; + aim_rxcallback_t userfunc; + int ret = 0; + fu16_t maxsiglen = 0; + + tlvlist = aim_readtlvchain(bs); - if (!sess || !conn || !sn) - return 0; + if (aim_gettlv(tlvlist, 0x0001, 1)) + maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1); - if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn)))) - return -1; + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxsiglen); - tx->lock = 1; + aim_freetlvchain(&tlvlist); - i += aimutil_put16(tx->data+i, 0x0003); - i += aimutil_put16(tx->data+i, 0x000c); - i += aimutil_put16(tx->data+i, 0x0000); - i += aimutil_put16(tx->data+i, 0x0000); - i += aimutil_put16(tx->data+i, 0x0000); + 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); + } - i += aimutil_put8(tx->data+i, strlen(sn)); - i += aimutil_putstr(tx->data+i, sn, strlen(sn)); - - tx->lock = 0; - aim_tx_enqueue(sess, tx); + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo, text_encoding, text, inforeq->infotype); - return 0; + free(text_encoding); + free(text); + + aim_freetlvchain(&tlvlist); + + if (origsnac) + free(origsnac->data); + free(origsnac); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return userinfo(sess, mod, rx, snac, bs); + + return 0; } +faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0002; + mod->version = 0x0000; + mod->flags = 0; + strncpy(mod->name, "locate", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +}