X-Git-Url: http://andersk.mit.edu/gitweb/libfaim.git/blobdiff_plain/49c8a2fa4d66462a98cd44074818ca2e942d0f6f..5b401785eea9c5fe50e5cdacb83c586703b7c143:/aim_im.c diff --git a/aim_im.c b/aim_im.c index 54adc73..867750b 100644 --- a/aim_im.c +++ b/aim_im.c @@ -5,7 +5,7 @@ * */ -#include "aim.h" +#include /* * Send an ICBM (instant message). @@ -17,7 +17,9 @@ * when the message is received (of type 0x0004/0x000c) * */ -u_long aim_send_im(struct aim_conn_t *conn, char *destsn, int flags, char *msg) +u_long aim_send_im(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *destsn, u_int flags, char *msg) { int curbyte,i; @@ -28,7 +30,7 @@ u_long aim_send_im(struct aim_conn_t *conn, char *destsn, int flags, char *msg) if (conn) newpacket.conn = conn; else - newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS); + newpacket.conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS); /* * Its simplest to set this arbitrarily large and waste @@ -36,14 +38,20 @@ u_long aim_send_im(struct aim_conn_t *conn, char *destsn, int flags, char *msg) */ newpacket.commandlen = 1152; - newpacket.data = (char *) calloc(1, newpacket.commandlen); + newpacket.data = (u_char *) calloc(1, newpacket.commandlen); curbyte = 0; curbyte += aim_putsnac(newpacket.data+curbyte, - 0x0004, 0x0006, 0x0000, aim_snac_nextid); + 0x0004, 0x0006, 0x0000, sess->snac_nextid); /* * Generate a random message cookie + * + * We could cache these like we do SNAC IDs. (In fact, it + * might be a good idea.) In the message error functions, + * the 8byte message cookie is returned as well as the + * SNAC ID. + * */ for (i=0;i<8;i++) curbyte += aimutil_put8(newpacket.data+curbyte, (u_char) random()); @@ -109,13 +117,13 @@ u_long aim_send_im(struct aim_conn_t *conn, char *destsn, int flags, char *msg) newpacket.commandlen = curbyte; - aim_tx_enqueue(&newpacket); + aim_tx_enqueue(sess, &newpacket); #ifdef USE_SNAC_FOR_IMS { struct aim_snac_t snac; - snac.id = aim_snac_nextid; + snac.id = sess->snac_nextid; snac.family = 0x0004; snac.type = 0x0006; snac.flags = 0x0000; @@ -123,13 +131,13 @@ u_long aim_send_im(struct aim_conn_t *conn, char *destsn, int flags, char *msg) snac.data = malloc(strlen(destsn)+1); memcpy(snac.data, destsn, strlen(destsn)+1); - aim_newsnac(&snac); + aim_newsnac(sess, &snac); } - aim_cleansnacs(60); /* clean out all SNACs over 60sec old */ + aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */ #endif - return (aim_snac_nextid++); + return (sess->snac_nextid++); } /* @@ -147,23 +155,19 @@ u_long aim_send_im(struct aim_conn_t *conn, char *destsn, int flags, char *msg) * room we're invited to, but obviously can't attend... * */ -int aim_parse_incoming_im_middle(struct command_rx_struct *command) +int aim_parse_incoming_im_middle(struct aim_session_t *sess, + struct command_rx_struct *command) { - struct aim_userinfo_s userinfo; - u_int i = 0, j = 0, y = 0, z = 0; - char *msg = NULL; - int isautoresponse = 0; + u_int i = 0,z; rxcallback_t userfunc = NULL; u_char cookie[8]; int channel; struct aim_tlvlist_t *tlvlist; - struct aim_tlv_t *msgblocktlv, *tmptlv; - u_char *msgblock; + struct aim_userinfo_s userinfo; u_short wastebits; - u_short flag1,flag2; memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s)); - + i = 10; /* Skip SNAC header */ /* @@ -178,12 +182,19 @@ int aim_parse_incoming_im_middle(struct command_rx_struct *command) * 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. We only support - * standard messages; those on channel 0x0001. + * features of AIM2/3/3.5. + * + * Channel 0x0002 is the Rendevous channel, which + * is where Chat Invitiations come from. + * */ channel = aimutil_get16(command->data+i); i += 2; - if (channel != 0x0001) + + /* + * + */ + if ((channel != 0x01) && (channel != 0x02)) { printf("faim: icbm: ICBM received on an unsupported channel. Ignoring.\n (chan = %04x)", channel); return 1; @@ -197,13 +208,17 @@ int aim_parse_incoming_im_middle(struct command_rx_struct *command) i += 1 + (int)command->data[i]; /* - * Unknown bits. + * Warning Level */ - wastebits = aimutil_get16(command->data+i); + userinfo.warnlevel = aimutil_get16(command->data+i); /* guess */ i += 2; + + /* + * Number of TLVs that follow. Not needed. + */ wastebits = aimutil_get16(command->data+i); i += 2; - + /* * Read block of TLVs. All further data is derived * from what is parsed here. @@ -211,122 +226,254 @@ int aim_parse_incoming_im_middle(struct command_rx_struct *command) tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); /* - * Check Autoresponse status. If it is an autoresponse, - * it will contain a second type 0x0004 TLV, with zero length. + * From here on, its depends on what channel we're on. */ - if (aim_gettlv(tlvlist, 0x0004, 2)) - isautoresponse = 1; - - /* - * Extract the various pieces of the userinfo struct. - */ - /* Class. */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) - userinfo.class = aimutil_get16(tmptlv->value); - /* Member-since date. */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1))) + if (channel == 1) { - /* If this is larger than 4, its probably the message block, skip */ - if (tmptlv->length <= 4) - userinfo.membersince = aimutil_get32(tmptlv->value); + u_int j = 0, y = 0, z = 0; + char *msg = NULL; + u_int icbmflags = 0; + struct aim_tlv_t *msgblocktlv, *tmptlv; + u_char *msgblock; + u_short flag1,flag2; + + /* + * Check Autoresponse status. If it is an autoresponse, + * it will contain a second type 0x0004 TLV, with zero length. + */ + if (aim_gettlv(tlvlist, 0x0004, 2)) + icbmflags |= AIM_IMFLAGS_AWAY; + + /* + * Check Ack Request status. + */ + if (aim_gettlv(tlvlist, 0x0003, 2)) + icbmflags |= AIM_IMFLAGS_ACK; + + /* + * Extract the various pieces of the userinfo struct. + */ + /* Class. */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) + userinfo.class = aimutil_get16(tmptlv->value); + /* Member-since date. */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1))) + { + /* If this is larger than 4, its probably the message block, skip */ + if (tmptlv->length <= 4) + userinfo.membersince = aimutil_get32(tmptlv->value); + } + /* On-since date */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) + userinfo.onlinesince = aimutil_get32(tmptlv->value); + /* Idle-time */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) + userinfo.idletime = aimutil_get16(tmptlv->value); + /* Session Length (AIM) */ + if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) + userinfo.sessionlen = aimutil_get16(tmptlv->value); + /* Session Length (AOL) */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) + userinfo.sessionlen = aimutil_get16(tmptlv->value); + + /* + * Message block. + * + * XXX: Will the msgblock always be the second 0x0002? + */ + msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1); + if (!msgblocktlv) + { + printf("faim: icbm: major error! no message block TLV found!\n"); + aim_freetlvchain(&tlvlist); + } + + /* + * Extracting the message from the unknown cruft. + * + * This is a bit messy, and I'm not really qualified, + * even as the author, to comment on it. At least + * its not as bad as a while loop shooting into infinity. + * + * "Do you believe in magic?" + * + */ + msgblock = msgblocktlv->value; + j = 0; + + wastebits = aimutil_get8(msgblock+j++); + wastebits = aimutil_get8(msgblock+j++); + + y = aimutil_get16(msgblock+j); + j += 2; + for (z = 0; z < y; z++) + wastebits = aimutil_get8(msgblock+j++); + wastebits = aimutil_get8(msgblock+j++); + wastebits = aimutil_get8(msgblock+j++); + + /* + * Message string length, including flag words. + */ + i = aimutil_get16(msgblock+j); + j += 2; + + /* + * Flag words. + * + * Its rumored that these can kick in some funky + * 16bit-wide char stuff that used to really kill + * libfaim. Hopefully the latter is no longer true. + * + * Though someone should investiagte the former. + * + */ + flag1 = aimutil_get16(msgblock+j); + j += 2; + flag2 = aimutil_get16(msgblock+j); + j += 2; + + if (flag1 || flag2) + printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2); + + /* + * Message string. + */ + i -= 4; + msg = (char *)malloc(i+1); + memcpy(msg, msgblock+j, i); + msg[i] = '\0'; + + /* + * Call client. + */ + userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); + if (userfunc) + i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2); + else + i = 0; + + free(msg); } - /* On-since date */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) - userinfo.onlinesince = aimutil_get32(tmptlv->value); - /* Idle-time */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) - userinfo.idletime = aimutil_get16(tmptlv->value); - /* Session Length (AIM) */ - if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) - userinfo.sessionlen = aimutil_get16(tmptlv->value); - /* Session Length (AOL) */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) - userinfo.sessionlen = aimutil_get16(tmptlv->value); - - /* - * Message block. - * - * XXX: Will the msgblock always be the second 0x0002? - */ - msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1); - if (!msgblocktlv) - { - printf("faim: icbm: major error! no message block TLV found!\n"); - aim_freetlvchain(&tlvlist); + else if (channel == 0x0002) + { + int rendtype; + struct aim_tlv_t *block1; + struct aim_tlvlist_t *list2; + struct aim_tlv_t *tmptlv; + int a; + + /* Class. */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) + userinfo.class = aimutil_get16(tmptlv->value); + /* On-since date */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) + userinfo.onlinesince = aimutil_get32(tmptlv->value); + /* Idle-time */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) + userinfo.idletime = aimutil_get16(tmptlv->value); + /* Session Length (AIM) */ + if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) + userinfo.sessionlen = aimutil_get16(tmptlv->value); + /* Session Length (AOL) */ + if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) + userinfo.sessionlen = aimutil_get16(tmptlv->value); + + /* + * There's another block of TLVs embedded in the type 5 here. + */ + block1 = aim_gettlv(tlvlist, 0x0005, 1); + if (!block1) + return 1; /* major problem */ + + a = 0x1a; /* skip -- not sure what this information is! */ + + /* + * XXX: Ignore if there's no data, only cookie information. + * + * Its probably just an accepted invitation or something. + * + */ + if (block1->length <= 0x1a) + { + aim_freetlvchain(&tlvlist); + return 1; + } + + list2 = aim_readtlvchain(block1->value+a, block1->length-a); + + if (aim_gettlv(list2, 0x0004, 1) /* start connection */ || + aim_gettlv(list2, 0x000b, 1) /* close conncetion */) + { + rendtype = 1; /* voice request */ + + /* + * Call client. + */ + userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); + if (userfunc) + i = userfunc(sess, + command, + channel, + rendtype, + &userinfo); + else + i = 0; + } + else + { + struct aim_chat_roominfo roominfo; + char *msg=NULL,*encoding=NULL,*lang=NULL; + + rendtype = 0; /* chat invite */ + if (aim_gettlv(list2, 0x2711, 1)) + { + struct aim_tlv_t *nametlv; + + nametlv = aim_gettlv(list2, 0x2711, 1); + aim_chat_readroominfo(nametlv->value, &roominfo); + } + + if (aim_gettlv(list2, 0x000c, 1)) + msg = aim_gettlv_str(list2, 0x000c, 1); + + if (aim_gettlv(list2, 0x000d, 1)) + encoding = aim_gettlv_str(list2, 0x000d, 1); + + if (aim_gettlv(list2, 0x000e, 1)) + lang = aim_gettlv_str(list2, 0x000e, 1); + + /* + * Call client. + */ + userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); + if (userfunc) + i = userfunc(sess, + command, + channel, + rendtype, + &userinfo, + &roominfo, + msg, + encoding?encoding+1:NULL, + lang?lang+1:NULL); + else + i = 0; + + free(roominfo.name); + free(msg); + free(encoding); + free(lang); + } + aim_freetlvchain(&list2); } - /* - * Extracting the message from the unknown cruft. - * - * This is a bit messy, and I'm not really qualified, - * even as the author, to comment on it. At least - * its not as bad as a while loop shooting into infinity. - * - * "Do you believe in magic?" - * - */ - msgblock = msgblocktlv->value; - j = 0; - - wastebits = aimutil_get8(msgblock+j++); - wastebits = aimutil_get8(msgblock+j++); - - y = aimutil_get16(msgblock+j); - j += 2; - for (z = 0; z < y; z++) - wastebits = aimutil_get8(msgblock+j++); - wastebits = aimutil_get8(msgblock+j++); - wastebits = aimutil_get8(msgblock+j++); - - /* - * Message string length, including flag words. - */ - i = aimutil_get16(msgblock+j); - j += 2; - - /* - * Flag words. - * - * Its rumored that these can kick in some funky - * 16bit-wide char stuff that used to really kill - * libfaim. Hopefully the latter is no longer true. - * - * Though someone should investiagte the former. - * - */ - flag1 = aimutil_get16(msgblock+j); - j += 2; - flag2 = aimutil_get16(msgblock+j); - j += 2; - - if (flag1 || flag2) - printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2); - - /* - * Message string. - */ - i -= 4; - msg = (char *)malloc(i+1); - memcpy(msg, msgblock+j, i); - msg[i] = '\0'; - /* * Free up the TLV chain. */ aim_freetlvchain(&tlvlist); + - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); - if (userfunc) - i = userfunc(command, &userinfo, msg, isautoresponse, flag1, flag2); - else - i = 0; - - free(msg); - - return 1; + return i; } /* @@ -336,7 +483,8 @@ int aim_parse_incoming_im_middle(struct command_rx_struct *command) * idea. * */ -u_long aim_seticbmparam(struct aim_conn_t *conn) +u_long aim_seticbmparam(struct aim_session_t *sess, + struct aim_conn_t *conn) { struct command_tx_struct newpacket; int curbyte; @@ -345,13 +493,13 @@ u_long aim_seticbmparam(struct aim_conn_t *conn) if (conn) newpacket.conn = conn; else - newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS); + newpacket.conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS); newpacket.type = 0x02; newpacket.commandlen = 10 + 16; newpacket.data = (u_char *) malloc (newpacket.commandlen); - curbyte = aim_putsnac(newpacket.data, 0x0004, 0x0002, 0x0000, aim_snac_nextid); + curbyte = aim_putsnac(newpacket.data, 0x0004, 0x0002, 0x0000, sess->snac_nextid); curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000); curbyte += aimutil_put32(newpacket.data+curbyte, 0x00000003); curbyte += aimutil_put8(newpacket.data+curbyte, 0x1f); @@ -363,7 +511,48 @@ u_long aim_seticbmparam(struct aim_conn_t *conn) curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000); curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000); - aim_tx_enqueue(&newpacket); + aim_tx_enqueue(sess, &newpacket); + + return (sess->snac_nextid++); +} + +int aim_parse_msgerror_middle(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + u_long snacid = 0x000000000; + struct aim_snac_t *snac = NULL; + int ret = 0; + rxcallback_t userfunc = NULL; + + /* + * Get SNAC from packet and look it up + * the list of unrepliedto/outstanding + * SNACs. + * + * After its looked up, the SN that the + * message should've gone to will be + * in the ->data element of the snac struct. + * + */ + snacid = aimutil_get32(command->data+6); + snac = aim_remsnac(sess, snacid); + + if (!snac) + { + printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid); + } + + /* + * Call client. + */ + userfunc = aim_callhandler(command->conn, 0x0004, 0x0001); + if (userfunc) + ret = userfunc(sess, command, (snac)?snac->data:"(UNKNOWN)"); + else + ret = 0; + + free(snac->data); + free(snac); - return (aim_snac_nextid++); + return ret; }