static int consumesnac(aim_session_t *sess, aim_frame_t *rx) { aim_module_t *cur; aim_modsnac_t snac; if (aim_bstream_empty(&rx->data) < 10) return 0; snac.family = aimbs_get16(&rx->data); snac.subtype = aimbs_get16(&rx->data); snac.flags = aimbs_get16(&rx->data); snac.id = aimbs_get32(&rx->data); /* SNAC flags are apparently uniform across all SNACs, so we handle them here */ if (snac.flags & 0x0001) { /* * This means the SNAC will be followed by another SNAC with * related information. We don't need to do anything about * this here. */ } if (snac.flags & 0x8000) { /* * This packet contains the version of the family that this SNAC is * in. You get this when your SSI module is version 2 or higher. * For now we have no need for this, but you could always save * it as a part of aim_modnsac_t, or something. The format is... * 2 byte length of total mini-header (which is 6 bytes), then TLV * of type 0x0001, length 0x0002, value is the 2 byte version * number */ aim_bstream_advance(&rx->data, aimbs_get16(&rx->data)); } for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && (cur->family != snac.family)) continue; if (cur->snachandler(sess, cur, rx, &snac, &rx->data)) return 1; } return 0; }
/* * Subtype 0x0014 - Receive a mini typing notification (mtn) packet. * * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, * and Gaim 0.60 and newer. * */ static int mtn_receive(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_rxcallback_t userfunc; char *sn; guint8 snlen; guint16 type1, type2; aim_bstream_advance(bs, 8); /* Unknown - All 0's */ type1 = aimbs_get16(bs); snlen = aimbs_get8(bs); sn = aimbs_getstr(bs, snlen); type2 = aimbs_get16(bs); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, type1, sn, type2); g_free(sn); return ret; }
/** * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet. */ static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_tlvlist_t *tl; aim_tlv_t *datatlv; aim_bstream_t qbs; fu32_t ouruin; fu16_t cmdlen, cmd, reqid; if (!(tl = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tl, 0x0001, 1))) { aim_tlvlist_free(&tl); faimdprintf(sess, 0, "corrupt ICQ response\n"); return 0; } aim_bstream_init(&qbs, datatlv->value, datatlv->length); cmdlen = aimbs_getle16(&qbs); ouruin = aimbs_getle32(&qbs); cmd = aimbs_getle16(&qbs); reqid = aimbs_getle16(&qbs); faimdprintf(sess, 1, "icq response: %d bytes, %ld, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid); if (cmd == 0x0041) { /* offline message */ struct aim_icq_offlinemsg msg; aim_rxcallback_t userfunc; memset(&msg, 0, sizeof(msg)); msg.sender = aimbs_getle32(&qbs); msg.year = aimbs_getle16(&qbs); msg.month = aimbs_getle8(&qbs); msg.day = aimbs_getle8(&qbs); msg.hour = aimbs_getle8(&qbs); msg.minute = aimbs_getle8(&qbs); msg.type = aimbs_getle8(&qbs); msg.flags = aimbs_getle8(&qbs); msg.msglen = aimbs_getle16(&qbs); msg.msg = aimbs_getstr(&qbs, msg.msglen); if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG))) ret = userfunc(sess, rx, &msg); free(msg.msg); } else if (cmd == 0x0042) { aim_rxcallback_t userfunc; if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE))) ret = userfunc(sess, rx); } else if (cmd == 0x07da) { /* information */ fu16_t subtype; struct aim_icq_info *info; aim_rxcallback_t userfunc; subtype = aimbs_getle16(&qbs); aim_bstream_advance(&qbs, 1); /* 0x0a */ /* find other data from the same request */ for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next); if (!info) { info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); info->reqid = reqid; info->next = sess->icq_info; sess->icq_info = info; } switch (subtype) { case 0x00a0: { /* hide ip status */ /* nothing */ } break; case 0x00aa: { /* password change status */ /* nothing */ } break; case 0x00c8: { /* general and "home" information */ info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->homecountry = aimbs_getle16(&qbs); /* 0x0a 00 02 00 */ /* 1 byte timezone? */ /* 1 byte hide email flag? */ } break; case 0x00dc: { /* personal information */ info->age = aimbs_getle8(&qbs); info->unknown = aimbs_getle8(&qbs); info->gender = aimbs_getle8(&qbs); /* Not specified=0x00, Female=0x01, Male=0x02 */ info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->birthyear = aimbs_getle16(&qbs); info->birthmonth = aimbs_getle8(&qbs); info->birthday = aimbs_getle8(&qbs); info->language1 = aimbs_getle8(&qbs); info->language2 = aimbs_getle8(&qbs); info->language3 = aimbs_getle8(&qbs); /* 0x00 00 01 00 00 01 00 00 00 00 00 */ } break; case 0x00d2: { /* work information */ info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->workcountry = aimbs_getle16(&qbs); info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); aim_bstream_advance(&qbs, 2); /* 0x01 00 */ info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); } break; case 0x00e6: { /* additional personal information */ info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1); } break; case 0x00eb: { /* email address(es) */ int i; info->numaddresses = aimbs_getle16(&qbs); info->email2 = (char **)calloc(info->numaddresses, sizeof(char *)); for (i = 0; i < info->numaddresses; i++) { info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); if (i+1 != info->numaddresses) aim_bstream_advance(&qbs, 1); /* 0x00 */ } } break; case 0x00f0: { /* personal interests */ } break; case 0x00fa: { /* past background and current organizations */ } break; case 0x0104: { /* alias info */ info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); aim_bstream_advance(&qbs, aimbs_getle16(&qbs)); /* email address? */ /* Then 0x00 02 00 */ } break; case 0x010e: { /* unknown */ /* 0x00 00 */ } break; case 0x019a: { /* simple info */ aim_bstream_advance(&qbs, 2); info->uin = aimbs_getle32(&qbs); info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); /* Then 0x00 02 00 00 00 00 00 */ } break; } /* End switch statement */ if (!(snac->flags & 0x0001)) { if (subtype != 0x0104) if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO))) ret = userfunc(sess, rx, info); if (info->uin && info->nick) if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS))) ret = userfunc(sess, rx, info); if (sess->icq_info == info) { sess->icq_info = info->next; } else { struct aim_icq_info *cur; for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next); if (cur->next) cur->next = cur->next->next; } aim_icq_freeinfo(info); } } aim_tlvlist_free(&tl); return ret; }
/* * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is * kind of odd. This sends the client ICQRTF since that is all that I've seen * SERVERRELAY used for. * * Note that this is all little-endian. Cringe. * * This cap is used for auto status message replies, too [ft] * */ static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) { guint16 hdrlen, msglen, dc; guint8 msgtype; guint8 *plugin; int i = 0, tmp = 0; struct im_connection *ic = sess->aux_data; /* at the moment we just can deal with requests, not with cancel or accept */ if (args->status != 0) return; hdrlen = aimbs_getle16(servdata); aim_bstream_advance(servdata, 0x02); /* protocol version */ plugin = aimbs_getraw(servdata, 0x10); /* following data is a message or something plugin specific */ /* as there is no plugin handling, just skip the rest */ aim_bstream_advance(servdata, hdrlen - 0x12); hdrlen = aimbs_getle16(servdata); dc = aimbs_getle16(servdata); /* save the sequence number */ aim_bstream_advance(servdata, hdrlen - 0x02); /* TODO is it a message or something for a plugin? */ for (i = 0; i < 0x10; i++) { tmp |= plugin[i]; } if (!tmp) { /* message follows */ msgtype = aimbs_getle8(servdata); aimbs_getle8(servdata); /* msgflags */ aim_bstream_advance(servdata, 0x04); /* status code and priority code */ msglen = aimbs_getle16(servdata); /* message string length */ args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen); switch(msgtype) { case AIM_MTYPE_PLAIN: args->info.rtfmsg.fgcolor = aimbs_getle32(servdata); args->info.rtfmsg.bgcolor = aimbs_getle32(servdata); hdrlen = aimbs_getle32(servdata); aim_bstream_advance(servdata, hdrlen); /* XXX This is such a hack. */ args->reqclass = AIM_CAPS_ICQRTF; break; case AIM_MTYPE_AUTOAWAY: case AIM_MTYPE_AUTOBUSY: case AIM_MTYPE_AUTONA: case AIM_MTYPE_AUTODND: case AIM_MTYPE_AUTOFFC: case 0x9c: /* ICQ 5 seems to send this */ aim_send_im_ch2_statusmessage(sess, userinfo->sn, args->cookie, ic->away ? ic->away : "", sess->aim_icq_state, dc); break; } } /* message or plugin specific */ g_free(plugin); args->destructor = (void *)incomingim_ch2_icqserverrelay_free; return; }
static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, guint8 *cookie) { guint16 type, length; aim_rxcallback_t userfunc; int ret = 0; struct aim_incomingim_ch1_args args; int endpos; memset(&args, 0, sizeof(args)); aim_mpmsg_init(sess, &args.mpmsg); /* * This used to be done using tlvchains. For performance reasons, * I've changed it to process the TLVs in-place. This avoids lots * of per-IM memory allocations. */ while (aim_bstream_empty(bs)) { type = aimbs_get16(bs); length = aimbs_get16(bs); endpos = aim_bstream_curpos(bs) + length; if (type == 0x0002) { /* Message Block */ /* * This TLV consists of the following: * - 0501 -- Unknown * - Features: Don't know how to interpret these * - 0101 -- Unknown * - Message * */ aimbs_get8(bs); /* 05 */ aimbs_get8(bs); /* 01 */ args.featureslen = aimbs_get16(bs); /* XXX XXX this is all evil! */ args.features = bs->data + bs->offset; aim_bstream_advance(bs, args.featureslen); args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES; /* * The rest of the TLV contains one or more message * blocks... */ incomingim_ch1_parsemsgs(sess, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args); } else if (type == 0x0003) { /* Server Ack Requested */ args.icbmflags |= AIM_IMFLAGS_ACK; } else if (type == 0x0004) { /* Message is Auto Response */ args.icbmflags |= AIM_IMFLAGS_AWAY; } else if (type == 0x0006) { /* Message was received offline. */ /* XXX not sure if this actually gets sent. */ args.icbmflags |= AIM_IMFLAGS_OFFLINE; } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */ args.iconlen = aimbs_get32(bs); aimbs_get16(bs); /* 0x0001 */ args.iconsum = aimbs_get16(bs); args.iconstamp = aimbs_get32(bs); /* * This looks to be a client bug. MacAIM 4.3 will * send this tag, but with all zero values, in the * first message of a conversation. This makes no * sense whatsoever, so I'm going to say its a bug. * * You really shouldn't advertise a zero-length icon * anyway. * */ if (args.iconlen) args.icbmflags |= AIM_IMFLAGS_HASICON; } else if (type == 0x0009) { args.icbmflags |= AIM_IMFLAGS_BUDDYREQ; } else if (type == 0x0017) { args.extdatalen = length; args.extdata = aimbs_getraw(bs, args.extdatalen); } else { // imcb_error(sess->aux_data, "Unknown TLV encountered"); } /* * This is here to protect ourselves from ourselves. That * is, if something above doesn't completly parse its value * section, or, worse, overparses it, this will set the * stream where it needs to be in order to land on the next * TLV when the loop continues. * */ aim_bstream_setpos(bs, endpos); } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, channel, userinfo, &args); aim_mpmsg_free(sess, &args.mpmsg); g_free(args.extdata); return ret; }
/* * Receive the response from an ICQ status message request. This contains the * ICQ status message. Go figure. */ static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_rxcallback_t userfunc; guint16 channel, reason; char *sn; guint8 *ck, snlen; ck = aimbs_getraw(bs, 8); channel = aimbs_get16(bs); snlen = aimbs_get8(bs); sn = aimbs_getstr(bs, snlen); reason = aimbs_get16(bs); switch (reason) { case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */ guint8 statusmsgtype, *msg; guint16 len; guint32 state; len = aimbs_getle16(bs); /* Should be 0x001b */ aim_bstream_advance(bs, len); /* Unknown */ len = aimbs_getle16(bs); /* Should be 0x000e */ aim_bstream_advance(bs, len); /* Unknown */ statusmsgtype = aimbs_getle8(bs); switch (statusmsgtype) { case 0xe8: state = AIM_ICQ_STATE_AWAY; break; case 0xe9: state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY; break; case 0xea: state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT; break; case 0xeb: state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY; break; case 0xec: state = AIM_ICQ_STATE_CHAT; break; default: state = 0; break; } aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */ aimbs_getle16(bs); /* Unknown - 0x0000 */ aimbs_getle16(bs); /* Unknown - 0x0000 */ len = aimbs_getle16(bs); msg = aimbs_getraw(bs, len); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, channel, sn, reason, state, msg); g_free(msg); } break; default: { if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, channel, sn, reason); } break; } /* end switch */ g_free(ck); g_free(sn); return ret; }
/** * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet. */ static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_tlvlist_t *tl; aim_tlv_t *datatlv; aim_bstream_t qbs; guint32 ouruin; guint16 cmdlen, cmd, reqid; if (!(tl = aim_readtlvchain(bs)) || !(datatlv = aim_gettlv(tl, 0x0001, 1))) { aim_freetlvchain(&tl); imcb_error(sess->aux_data, "corrupt ICQ response\n"); return 0; } aim_bstream_init(&qbs, datatlv->value, datatlv->length); cmdlen = aimbs_getle16(&qbs); ouruin = aimbs_getle32(&qbs); cmd = aimbs_getle16(&qbs); reqid = aimbs_getle16(&qbs); if (cmd == 0x0041) { /* offline message */ guint16 msglen; struct aim_icq_offlinemsg msg; aim_rxcallback_t userfunc; memset(&msg, 0, sizeof(msg)); msg.sender = aimbs_getle32(&qbs); msg.year = aimbs_getle16(&qbs); msg.month = aimbs_getle8(&qbs); msg.day = aimbs_getle8(&qbs); msg.hour = aimbs_getle8(&qbs); msg.minute = aimbs_getle8(&qbs); msg.type = aimbs_getle16(&qbs); msglen = aimbs_getle16(&qbs); msg.msg = aimbs_getstr(&qbs, msglen); if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG))) ret = userfunc(sess, rx, &msg); g_free(msg.msg); } else if (cmd == 0x0042) { aim_rxcallback_t userfunc; if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE))) ret = userfunc(sess, rx); } else if (cmd == 0x07da) { /* information */ guint16 subtype; struct aim_icq_info *info; aim_rxcallback_t userfunc; subtype = aimbs_getle16(&qbs); aim_bstream_advance(&qbs, 1); /* 0x0a */ /* find another data from the same request */ for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next); if (!info) { info = g_new0(struct aim_icq_info, 1); info->reqid = reqid; info->next = sess->icq_info; sess->icq_info = info; }