if (cur->type != AIM_CONN_TYPE_CHAT)
continue;
if (!cur->priv) {
- printf("faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
+ faimdprintf(sess, 0, "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
continue;
}
if (strcmp((char *)cur->priv, name) == 0)
return 0;
}
-/* XXX convert this to use tlvchains */
+/*
+ * Send a Chat Message.
+ *
+ * Possible flags:
+ * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages
+ * should be sent to their sender.
+ * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse
+ * (Note that WinAIM does not honor this,
+ * and displays the message as normal.)
+ *
+ * XXX convert this to use tlvchains
+ */
faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess,
struct aim_conn_t *conn,
- char *msg)
+ unsigned short flags,
+ const char *msg,
+ int msglen)
{
int curbyte,i;
struct command_tx_struct *newpacket;
struct aim_msgcookie_t *cookie;
- if (!sess || !conn || !msg)
+ if (!sess || !conn || !msg || (msglen <= 0))
return 0;
- if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152)))
+ if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
return -1;
newpacket->lock = 1; /* lock struct */
curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
/*
- * Type 1: Unknown. Blank.
+ * Type 1: Flag meaning this message is destined to the room.
*/
curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
/*
- * Type 6: Unknown. Blank.
+ * Type 6: Reflect
*/
- curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006);
- curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+ if (!(flags & AIM_CHATFLAGS_NOREFLECT)) {
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+ }
+
+ /*
+ * Type 7: Autoresponse
+ */
+ if (flags & AIM_CHATFLAGS_AWAY) {
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0007);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+ }
/*
* Type 5: Message block. Contains more TLVs.
if (!sess || !conn || !roomname)
return 0;
- if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+9+strlen(roomname)+2)))
+ if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+9+strlen(roomname)+2)))
return -1;
newpacket->lock = 1;
return i;
}
-
-/*
- * 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
- */
-faim_internal int aim_chat_parse_infoupdate(struct aim_session_t *sess,
- struct command_rx_struct *command)
-{
- struct aim_userinfo_s *userinfo = NULL;
- rxcallback_t userfunc=NULL;
- int ret = 1, i = 0;
- int usercount = 0;
- u_char detaillevel = 0;
- char *roomname = NULL;
- struct aim_chat_roominfo roominfo;
- u_short tlvcount = 0;
- struct aim_tlvlist_t *tlvlist;
- char *roomdesc = NULL;
- unsigned short unknown_c9 = 0;
- unsigned long creationtime = 0;
- unsigned short maxmsglen = 0;
- unsigned short unknown_d2 = 0, unknown_d5 = 0;
-
- i = 10;
- i += aim_chat_readroominfo(command->data+i, &roominfo);
-
- detaillevel = aimutil_get8(command->data+i);
- i++;
-
- if (detaillevel != 0x02) {
- if (detaillevel == 0x01)
- printf("faim: chat_roomupdateinfo: detail level 1 not supported\n");
- else
- printf("faim: chat_roomupdateinfo: unknown detail level %d\n", detaillevel);
- return 1;
- }
-
- tlvcount = aimutil_get16(command->data+i);
- i += 2;
-
- /*
- * Everything else are TLVs.
- */
- tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
-
- /*
- * 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;
- struct aim_tlv_t *tmptlv;
-
- tmptlv = aim_gettlv(tlvlist, 0x0073, 1);
-
- /* Allocate enough userinfo structs for all occupants */
- userinfo = calloc(usercount, sizeof(struct aim_userinfo_s));
-
- i = 0;
- while (curoccupant < usercount)
- i += aim_extractuserinfo(tmptlv->value+i, &userinfo[curoccupant++]);
- }
-
- /*
- * Type 0x00c9: Unknown. (2 bytes)
- */
- if (aim_gettlv(tlvlist, 0x00c9, 1))
- unknown_c9 = 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 0x00d5: Unknown. (1 byte)
- */
- if (aim_gettlv(tlvlist, 0x00d5, 1))
- unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1);
-
-
- if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE))) {
- ret = userfunc(sess,
- command,
- &roominfo,
- roomname,
- usercount,
- userinfo,
- roomdesc,
- unknown_c9,
- creationtime,
- maxmsglen,
- unknown_d2,
- unknown_d5);
- }
- free(roominfo.name);
- free(userinfo);
- free(roomname);
- free(roomdesc);
- aim_freetlvchain(&tlvlist);
-
- return ret;
-}
-
-faim_internal int aim_chat_parse_joined(struct aim_session_t *sess,
- struct command_rx_struct *command)
-{
- struct aim_userinfo_s *userinfo = NULL;
- rxcallback_t userfunc=NULL;
- int i = 10, curcount = 0, ret = 1;
-
- while (i < command->commandlen) {
- curcount++;
- userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
- i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]);
- }
-
- if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN))) {
- ret = userfunc(sess,
- command,
- curcount,
- userinfo);
- }
-
- free(userinfo);
-
- return ret;
-}
-
-faim_internal int aim_chat_parse_leave(struct aim_session_t *sess,
- struct command_rx_struct *command)
-{
-
- struct aim_userinfo_s *userinfo = NULL;
- rxcallback_t userfunc=NULL;
- int i = 10, curcount = 0, ret = 1;
-
- while (i < command->commandlen) {
- curcount++;
- userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
- i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]);
- }
-
- if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE))) {
- ret = userfunc(sess,
- command,
- curcount,
- userinfo);
- }
-
- free(userinfo);
-
- 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
- *
- */
-faim_internal int aim_chat_parse_incoming(struct aim_session_t *sess,
- struct command_rx_struct *command)
-{
- struct aim_userinfo_s userinfo;
- rxcallback_t userfunc=NULL;
- int ret = 1, i = 0, z = 0;
- unsigned char cookie[8];
- int channel;
- struct aim_tlvlist_t *outerlist;
- char *msg = NULL;
- struct aim_msgcookie_t *ck;
-
- memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
-
- i = 10; /* skip snac */
-
- /*
- * ICBM Cookie. Cache it.
- */
- for (z=0; z<8; z++,i++)
- cookie[z] = command->data[i];
-
- if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
- if (ck->data)
- 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 = aimutil_get16(command->data+i);
- i += 2;
-
- if (channel != 0x0003) {
- printf("faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
- return 1;
- }
-
- /*
- * Start parsing TLVs right away.
- */
- outerlist = aim_readtlvchain(command->data+i, command->commandlen-i);
-
- /*
- * Type 0x0003: Source User Information
- */
- if (aim_gettlv(outerlist, 0x0003, 1)) {
- struct aim_tlv_t *userinfotlv;
-
- userinfotlv = aim_gettlv(outerlist, 0x0003, 1);
- aim_extractuserinfo(userinfotlv->value, &userinfo);
- }
-
- /*
- * Type 0x0001: Unknown.
- */
- if (aim_gettlv(outerlist, 0x0001, 1))
- ;
-
- /*
- * Type 0x0005: Message Block. Conains more TLVs.
- */
- if (aim_gettlv(outerlist, 0x0005, 1))
- {
- struct aim_tlvlist_t *innerlist;
- struct aim_tlv_t *msgblock;
-
- msgblock = aim_gettlv(outerlist, 0x0005, 1);
- innerlist = aim_readtlvchain(msgblock->value, msgblock->length);
-
- /*
- * Type 0x0001: Message.
- */
- if (aim_gettlv(innerlist, 0x0001, 1))
- msg = aim_gettlv_str(innerlist, 0x0001, 1);
-
- aim_freetlvchain(&innerlist);
- }
-
- userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG);
- if (userfunc)
- {
- ret = userfunc(sess,
- command,
- &userinfo,
- msg);
- }
- free(msg);
- aim_freetlvchain(&outerlist);
-
- return ret;
-}
-
faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess,
struct aim_conn_t *conn)
{
struct command_tx_struct *newpacket;
int i;
- if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 0x20)))
+ if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x20)))
return -1;
newpacket->lock = 1;
if (conn->type != AIM_CONN_TYPE_BOS)
return -1;
- if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
+ if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
return -1;
newpacket->lock = 1;
return (sess->snac_nextid++);
}
+
+/*
+ * 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(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+{
+ struct aim_userinfo_s *userinfo = NULL;
+ aim_rxcallback_t userfunc=NULL;
+ int ret = 0, i = 0;
+ int usercount = 0;
+ unsigned char detaillevel = 0;
+ char *roomname = NULL;
+ struct aim_chat_roominfo roominfo;
+ unsigned short tlvcount = 0;
+ struct aim_tlvlist_t *tlvlist;
+ char *roomdesc = NULL;
+ unsigned short unknown_c9 = 0;
+ unsigned long creationtime = 0;
+ unsigned short maxmsglen = 0, maxvisiblemsglen = 0;
+ unsigned short unknown_d2 = 0, unknown_d5 = 0;
+
+ i += aim_chat_readroominfo(data+i, &roominfo);
+
+ detaillevel = aimutil_get8(data+i);
+ i++;
+
+ if (detaillevel != 0x02) {
+ if (detaillevel == 0x01)
+ faimdprintf(sess, 0, "faim: chat_roomupdateinfo: detail level 1 not supported\n");
+ else
+ faimdprintf(sess, 0, "faim: chat_roomupdateinfo: unknown detail level %d\n", detaillevel);
+ return 1;
+ }
+
+ tlvcount = aimutil_get16(data+i);
+ i += 2;
+
+ /*
+ * Everything else are TLVs.
+ */
+ tlvlist = aim_readtlvchain(data+i, datalen-i);
+
+ /*
+ * 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;
+ struct aim_tlv_t *tmptlv;
+
+ tmptlv = aim_gettlv(tlvlist, 0x0073, 1);
+
+ /* Allocate enough userinfo structs for all occupants */
+ userinfo = calloc(usercount, sizeof(struct aim_userinfo_s));
+
+ for (i = 0; curoccupant < usercount; )
+ i += aim_extractuserinfo(sess, tmptlv->value+i, &userinfo[curoccupant++]);
+ }
+
+ /*
+ * Type 0x00c9: Unknown. (2 bytes)
+ */
+ if (aim_gettlv(tlvlist, 0x00c9, 1))
+ unknown_c9 = 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,
+ unknown_c9,
+ creationtime,
+ maxmsglen,
+ unknown_d2,
+ unknown_d5,
+ maxvisiblemsglen);
+ }
+
+ free(roominfo.name);
+ free(userinfo);
+ free(roomname);
+ free(roomdesc);
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+static int userlistchange(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+{
+ struct aim_userinfo_s *userinfo = NULL;
+ aim_rxcallback_t userfunc;
+ int i = 0, curcount = 0, ret = 0;
+
+ while (i < datalen) {
+ curcount++;
+ userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
+ i += aim_extractuserinfo(sess, data+i, &userinfo[curcount-1]);
+ }
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, curcount, userinfo);
+
+ free(userinfo);
+
+ 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(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+{
+ struct aim_userinfo_s userinfo;
+ aim_rxcallback_t userfunc=NULL;
+ int ret = 0, i = 0;
+ unsigned char cookie[8];
+ int channel;
+ struct aim_tlvlist_t *outerlist;
+ char *msg = NULL;
+ struct aim_msgcookie_t *ck;
+
+ memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
+
+ /*
+ * ICBM Cookie. Cache it.
+ */
+ memcpy(cookie, data, 8);
+ i += 8;
+
+ if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
+ if (ck->data)
+ 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 = aimutil_get16(data+i);
+ i += 2;
+
+ if (channel != 0x0003) {
+ faimdprintf(sess, 0, "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
+ return 0;
+ }
+
+ /*
+ * Start parsing TLVs right away.
+ */
+ outerlist = aim_readtlvchain(data+8+2, datalen-8-2);
+
+ /*
+ * Type 0x0003: Source User Information
+ */
+ if (aim_gettlv(outerlist, 0x0003, 1)) {
+ struct aim_tlv_t *userinfotlv;
+
+ userinfotlv = aim_gettlv(outerlist, 0x0003, 1);
+ aim_extractuserinfo(sess, userinfotlv->value, &userinfo);
+ }
+
+ /*
+ * Type 0x0001: Unknown.
+ */
+ if (aim_gettlv(outerlist, 0x0001, 1))
+ ;
+
+ /*
+ * Type 0x0005: Message Block. Conains more TLVs.
+ */
+ if (aim_gettlv(outerlist, 0x0005, 1)) {
+ struct aim_tlvlist_t *innerlist;
+ struct aim_tlv_t *msgblock;
+
+ msgblock = aim_gettlv(outerlist, 0x0005, 1);
+ innerlist = aim_readtlvchain(msgblock->value, msgblock->length);
+
+ /*
+ * Type 0x0001: Message.
+ */
+ if (aim_gettlv(innerlist, 0x0001, 1))
+ msg = aim_gettlv_str(innerlist, 0x0001, 1);
+
+ aim_freetlvchain(&innerlist);
+ }
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, &userinfo, msg);
+
+ free(msg);
+ aim_freetlvchain(&outerlist);
+
+ return ret;
+}
+
+static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+{
+
+ if (snac->subtype == 0x0002)
+ return infoupdate(sess, mod, rx, snac, data, datalen);
+ else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
+ return userlistchange(sess, mod, rx, snac, data, datalen);
+ else if (snac->subtype == 0x0006)
+ return incomingmsg(sess, mod, rx, snac, data, datalen);
+
+ return 0;
+}
+
+faim_internal int chat_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x000e;
+ mod->version = 0x0000;
+ mod->flags = 0;
+ strncpy(mod->name, "chat", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}