X-Git-Url: http://andersk.mit.edu/gitweb/libfaim.git/blobdiff_plain/0b4acf97ffe79995944d4041857260bbd5e16492..f0a7908e6ac789e81e591519d5554ea84ba8feb9:/aim_info.c diff --git a/aim_info.c b/aim_info.c index b0c9b74..88f07fc 100644 --- a/aim_info.c +++ b/aim_info.c @@ -9,30 +9,35 @@ #include +struct aim_priv_inforeq { + char sn[MAXSNLEN]; + unsigned short infotype; +}; + u_long aim_getinfo(struct aim_session_t *sess, struct aim_conn_t *conn, - const char *sn) + const char *sn, + unsigned short infotype) { - struct command_tx_struct newpacket; + struct command_tx_struct *newpacket; + int i = 0; - if (conn) - newpacket.conn = conn; - else - newpacket.conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS); + if (!sess || !conn || !sn) + return 0; - newpacket.lock = 1; - newpacket.type = 0x0002; + if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 12+1+strlen(sn)))) + return -1; - newpacket.commandlen = 12 + 1 + strlen(sn); - newpacket.data = (char *) malloc(newpacket.commandlen); + newpacket->lock = 1; - aim_putsnac(newpacket.data, 0x0002, 0x0005, 0x0000, sess->snac_nextid); + i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid); - aimutil_put16(newpacket.data+10, 0x0001); - aimutil_put8(newpacket.data+12, strlen(sn)); - aimutil_putstr(newpacket.data+13, sn, strlen(sn)); + i += aimutil_put16(newpacket->data+i, infotype); + i += aimutil_put8(newpacket->data+i, strlen(sn)); + i += aimutil_putstr(newpacket->data+i, sn, strlen(sn)); - aim_tx_enqueue(sess, &newpacket); + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); { struct aim_snac_t snac; @@ -42,8 +47,9 @@ u_long aim_getinfo(struct aim_session_t *sess, snac.type = 0x0005; snac.flags = 0x0000; - snac.data = malloc(strlen(sn)+1); - memcpy(snac.data, sn, strlen(sn)+1); + snac.data = malloc(sizeof(struct aim_priv_inforeq)); + strcpy(((struct aim_priv_inforeq *)snac.data)->sn, sn); + ((struct aim_priv_inforeq *)snac.data)->infotype = infotype; aim_newsnac(sess, &snac); } @@ -51,6 +57,97 @@ u_long aim_getinfo(struct aim_session_t *sess, return (sess->snac_nextid++); } + +/* + * Capability blocks. + */ +u_char aim_caps[6][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} +}; + +u_short aim_getcap(unsigned char *capblock, int buflen) +{ + u_short ret = 0; + int y; + int offset = 0; + + while (offset < buflen) { + 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; break; + case 1: ret |= AIM_CAPS_VOICE; break; + case 2: ret |= AIM_CAPS_IMIMAGE; break; + case 3: ret |= AIM_CAPS_CHAT; break; + case 4: ret |= AIM_CAPS_GETFILE; break; + case 5: ret |= AIM_CAPS_SENDFILE; break; + default: ret |= 0xff00; break; + } + } + } + offset += 0x10; + } + return ret; +} + +int aim_putcap(unsigned char *capblock, int buflen, u_short caps) +{ + 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]); + } + + return offset; +} + /* * AIM is fairly regular about providing user info. This * is a generic routine to extract it in its standard form. @@ -108,6 +205,7 @@ int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo) * 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, @@ -160,10 +258,16 @@ int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo) * actual decoding. See comment on aim_bos_setprofile() * in aim_misc.c about the capability block, its the same. * - * Ignore. - * */ case 0x000d: + { + int len; + len = aimutil_get16(buf+i+2); + if (!len) + break; + + outinfo->capabilities = aim_getcap(buf+i+4, len); + } break; /* @@ -283,8 +387,8 @@ int aim_parse_offgoing_middle(struct aim_session_t *sess, u_int i = 0; rxcallback_t userfunc=NULL; - /* Protect against future SN length extensions */ - strncpy(sn, command->data+11, (((int)(command->data+10))<=MAXSNLEN)?(int)command->data+10:MAXSNLEN); + strncpy(sn, command->data+11, (int)command->data[10]); + sn[(int)command->data[10]] = '\0'; userfunc = aim_callhandler(command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING); if (userfunc) @@ -302,74 +406,166 @@ int aim_parse_userinfo_middle(struct aim_session_t *sess, struct command_rx_struct *command) { struct aim_userinfo_s userinfo; - char *prof_encoding = NULL; - char *prof = NULL; + 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); - { - u_long snacid = 0x000000000; - struct aim_snac_t *snac = NULL; + if (!origsnac || !origsnac->data) { + printf("faim: parse_userinfo_middle: major problem: no snac stored!\n"); + return 1; + } - snacid = aimutil_get32(&command->data[6]); - snac = aim_remsnac(sess, snacid); + inforeq = (struct aim_priv_inforeq *)origsnac->data; - free(snac->data); - free(snac); + switch (inforeq->infotype) { + case AIM_GETINFO_GENERALINFO: + case AIM_GETINFO_AWAYMESSAGE: + i = 10; - } + /* + * extractuserinfo will give us the basic metaTLV information + */ + i += aim_extractuserinfo(command->data+i, &userinfo); - i = 10; - i += aim_extractuserinfo(command->data+i, &userinfo); - - if (i < command->commandlen) - { - if (aimutil_get16(&command->data[i]) == 0x0001) - { - int len = 0; - - len = aimutil_get16(&command->data[i+2]); - - prof_encoding = (char *) malloc(len+1); - memcpy(prof_encoding, &(command->data[i+4]), len); - prof_encoding[len] = '\0'; - - i += (2+2+len); - } - else - { - printf("faim: userinfo: **warning: unexpected TLV after TLVblock t(%04x) l(%04x)\n", aimutil_get16(command->data+i), aimutil_get16(command->data+i+2)); - i += 2 + 2 + command->data[i+3]; - } - } - - if (i < command->commandlen) - { - if (aimutil_get16(&command->data[i]) == 0x0002) - { - int len = 0; - len = aimutil_get16(&command->data[i+2]); - - prof = (char *) malloc(len+1); - memcpy(prof, &(command->data[i+4]), len); - prof[len] = '\0'; - } - else - printf("faim:userinfo: **warning: profile not found, but still have data\n"); + /* + * 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(command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO); - if (userfunc) - { + userfunc = aim_callhandler(command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO); + if (userfunc) { i = userfunc(sess, command, &userinfo, - prof_encoding, - prof); + text_encoding, + text, + inforeq->infotype); } + + free(text_encoding); + free(text); + aim_freetlvchain(&tlvlist); + break; + default: + printf("faim: parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype); + break; + } - free(prof_encoding); - free(prof); + if (origsnac) { + if (origsnac->data) + free(origsnac->data); + free(origsnac); + } return 1; } + +/* + * Inverse of aim_extractuserinfo() + */ +int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info) +{ + int i = 0; + struct aim_tlvlist_t *tlvlist = NULL; + + if (!buf || !info) + return 0; + + i += aimutil_put8(buf+i, strlen(info->sn)); + i += aimutil_putstr(buf+i, info->sn, strlen(info->sn)); + + i += aimutil_put16(buf+i, info->warnlevel); + + /* XXX: we only put down five */ + i += aimutil_put16(buf+i, 5); + aim_addtlvtochain16(&tlvlist, 0x0001, info->class); + aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince); + aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince); + aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime); + /* XXX: should put caps here */ + aim_addtlvtochain32(&tlvlist, (info->class)&AIM_CLASS_AOL?0x0010:0x000f, info->sessionlen); + + i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); + aim_freetlvchain(&tlvlist); + + return i; +} + +int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info) +{ + struct command_tx_struct *tx; + int i = 0; + + if (!sess || !conn || !info) + return 0; + + if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152))) + return -1; + + tx->lock = 1; + + 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); + + i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info); + + tx->commandlen = i; + tx->lock = 0; + aim_tx_enqueue(sess, tx); + + return 0; +} + +int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn) +{ + struct command_tx_struct *tx; + int i = 0; + + if (!sess || !conn || !sn) + return 0; + + if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+1+strlen(sn)))) + return -1; + + tx->lock = 1; + + 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); + + 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); + + return 0; +} +