📄 family_icbm.c
字号:
return;}static voidincomingim_ch2_buddyicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata){ args->info.icon.checksum = byte_stream_get32(servdata); args->info.icon.length = byte_stream_get32(servdata); args->info.icon.timestamp = byte_stream_get32(servdata); args->info.icon.icon = byte_stream_getraw(servdata, args->info.icon.length); args->destructor = (void *)incomingim_ch2_buddyicon_free; return;}static voidincomingim_ch2_chat_free(OscarData *od, IcbmArgsCh2 *args){ /* XXX - aim_chat_roominfo_free() */ g_free(args->info.chat.roominfo.name); return;}static voidincomingim_ch2_chat(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata){ /* * Chat room info. */ aim_chat_readroominfo(servdata, &args->info.chat.roominfo); args->destructor = (void *)incomingim_ch2_chat_free;}static voidincomingim_ch2_icqserverrelay_free(OscarData *od, IcbmArgsCh2 *args){ g_free((char *)args->info.rtfmsg.rtfmsg);}/* * The relationship between OSCAR_CAPABILITY_ICQSERVERRELAY and OSCAR_CAPABILITY_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. * */static voidincomingim_ch2_icqserverrelay(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata){ guint16 hdrlen, anslen, msglen; if (byte_stream_empty(servdata) < 24) /* Someone sent us a short server relay ICBM. Weird. (Maybe?) */ return; hdrlen = byte_stream_getle16(servdata); byte_stream_advance(servdata, hdrlen); hdrlen = byte_stream_getle16(servdata); byte_stream_advance(servdata, hdrlen); args->info.rtfmsg.msgtype = byte_stream_getle16(servdata); anslen = byte_stream_getle32(servdata); byte_stream_advance(servdata, anslen); msglen = byte_stream_getle16(servdata); args->info.rtfmsg.rtfmsg = byte_stream_getstr(servdata, msglen); args->info.rtfmsg.fgcolor = byte_stream_getle32(servdata); args->info.rtfmsg.bgcolor = byte_stream_getle32(servdata); hdrlen = byte_stream_getle32(servdata); byte_stream_advance(servdata, hdrlen); args->destructor = (void *)incomingim_ch2_icqserverrelay_free;}static voidincomingim_ch2_sendfile_free(OscarData *od, IcbmArgsCh2 *args){ g_free(args->info.sendfile.filename);}/* Someone is sending us a file */static voidincomingim_ch2_sendfile(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata){ int flen; args->destructor = (void *)incomingim_ch2_sendfile_free; /* Maybe there is a better way to tell what kind of sendfile * this is? Maybe TLV t(000a)? */ /* subtype is one of AIM_OFT_SUBTYPE_* */ args->info.sendfile.subtype = byte_stream_get16(servdata); args->info.sendfile.totfiles = byte_stream_get16(servdata); args->info.sendfile.totsize = byte_stream_get32(servdata); /* * I hope to God I'm right when I guess that there is a * 32 char max filename length for single files. I think * OFT tends to do that. Gotta love inconsistency. I saw * a 26 byte filename? */ /* AAA - create an byte_stream_getnullstr function (don't anymore)(maybe) */ /* Use an inelegant way of getting the null-terminated filename, * since there's no easy bstream routine. */ for (flen = 0; byte_stream_get8(servdata); flen++); byte_stream_advance(servdata, -flen -1); args->info.sendfile.filename = byte_stream_getstr(servdata, flen); /* There is sometimes more after the null-terminated filename, * but I'm unsure of its format. */ /* I don't believe him. */ /* There is sometimes a null byte inside a unicode filename, * but as far as I can tell the filename is the last * piece of data that will be in this message. --Jonathan */}typedef void (*ch2_args_destructor_t)(OscarData *od, IcbmArgsCh2 *args);static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie){ aim_rxcallback_t userfunc; aim_tlv_t *block1, *servdatatlv; GSList *list2; aim_tlv_t *tlv; IcbmArgsCh2 args; ByteStream bbs, sdbs, *sdbsptr = NULL; guint8 *cookie2; int ret = 0; char proxyip[30] = {""}; char clientip[30] = {""}; char verifiedip[30] = {""}; memset(&args, 0, sizeof(args)); /* * There's another block of TLVs embedded in the type 5 here. */ block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1); if (block1 == NULL) { /* The server sent us ch2 ICBM without ch2 info? Weird. */ return 1; } byte_stream_init(&bbs, block1->value, block1->length); /* * First two bytes represent the status of the connection. * One of the AIM_RENDEZVOUS_ defines. * * 0 is a request, 1 is a cancel, 2 is an accept */ args.status = byte_stream_get16(&bbs); /* * Next comes the cookie. Should match the ICBM cookie. */ cookie2 = byte_stream_getraw(&bbs, 8); if (memcmp(cookie, cookie2, 8) != 0) { purple_debug_warning("oscar", "Cookies don't match in rendezvous ICBM, bailing out.\n"); g_free(cookie2); return 1; } memcpy(args.cookie, cookie2, 8); g_free(cookie2); /* * The next 16bytes are a capability block so we can * identify what type of rendezvous this is. */ args.type = aim_locate_getcaps(od, &bbs, 0x10); /* * What follows may be TLVs or nothing, depending on the * purpose of the message. * * Ack packets for instance have nothing more to them. */ list2 = aim_tlvlist_read(&bbs); /* * IP address to proxy the file transfer through. * * TODO: I don't like this. Maybe just read in an int? Or inet_ntoa... */ tlv = aim_tlv_gettlv(list2, 0x0002, 1); if ((tlv != NULL) && (tlv->length == 4)) snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu", tlv->value[0], tlv->value[1], tlv->value[2], tlv->value[3]); /* * IP address from the perspective of the client. */ tlv = aim_tlv_gettlv(list2, 0x0003, 1); if ((tlv != NULL) && (tlv->length == 4)) snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu", tlv->value[0], tlv->value[1], tlv->value[2], tlv->value[3]); /* * Verified IP address (from the perspective of Oscar). * * This is added by the server. */ tlv = aim_tlv_gettlv(list2, 0x0004, 1); if ((tlv != NULL) && (tlv->length == 4)) snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu", tlv->value[0], tlv->value[1], tlv->value[2], tlv->value[3]); /* * Port number for something. */ if (aim_tlv_gettlv(list2, 0x0005, 1)) args.port = aim_tlv_get16(list2, 0x0005, 1); /* * File transfer "request number": * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy) * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers */ if (aim_tlv_gettlv(list2, 0x000a, 1)) args.requestnumber = aim_tlv_get16(list2, 0x000a, 1); /* * Terminate connection/error code. 0x0001 means the other user * canceled the connection. */ if (aim_tlv_gettlv(list2, 0x000b, 1)) args.errorcode = aim_tlv_get16(list2, 0x000b, 1); /* * Invitation message / chat description. */ if (aim_tlv_gettlv(list2, 0x000c, 1)) { args.msg = aim_tlv_getstr(list2, 0x000c, 1); args.msglen = aim_tlv_getlength(list2, 0x000c, 1); } /* * Character set. */ if (aim_tlv_gettlv(list2, 0x000d, 1)) args.encoding = aim_tlv_getstr(list2, 0x000d, 1); /* * Language. */ if (aim_tlv_gettlv(list2, 0x000e, 1)) args.language = aim_tlv_getstr(list2, 0x000e, 1);#if 0 /* * Unknown -- no value * * Maybe means we should connect directly to transfer the file? * Also used in ICQ Lite Beta 4.0 URLs. Also empty. */ /* I don't think this indicates a direct transfer; this flag is * also present in a stage 1 proxied file send request -- Jonathan */ if (aim_tlv_gettlv(list2, 0x000f, 1)) { /* Unhandled */ }#endif /* * Flag meaning we should proxy the file transfer through an AIM server */ if (aim_tlv_gettlv(list2, 0x0010, 1)) args.use_proxy = TRUE; if (strlen(proxyip)) args.proxyip = (char *)proxyip; if (strlen(clientip)) args.clientip = (char *)clientip; if (strlen(verifiedip)) args.verifiedip = (char *)verifiedip; /* * This must be present in PROPOSALs, but will probably not * exist in CANCELs and ACCEPTs. Also exists in ICQ Lite * Beta 4.0 URLs (OSCAR_CAPABILITY_ICQSERVERRELAY). * * Service Data blocks are module-specific in format. */ if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) { byte_stream_init(&sdbs, servdatatlv->value, servdatatlv->length); sdbsptr = &sdbs; /* * The rest of the handling depends on what type it is. * * Not all of them have special handling (yet). */ if (args.type & OSCAR_CAPABILITY_BUDDYICON) incomingim_ch2_buddyicon(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); else if (args.type & OSCAR_CAPABILITY_SENDBUDDYLIST) incomingim_ch2_buddylist(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); else if (args.type & OSCAR_CAPABILITY_CHAT) incomingim_ch2_chat(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); else if (args.type & OSCAR_CAPABILITY_ICQSERVERRELAY) incomingim_ch2_icqserverrelay(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); else if (args.type & OSCAR_CAPABILITY_SENDFILE) incomingim_ch2_sendfile(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); } if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, channel, userinfo, &args); if (args.destructor) ((ch2_args_destructor_t)args.destructor)(od, &args); g_free((char *)args.msg); g_free((char *)args.encoding); g_free((char *)args.language); aim_tlvlist_free(list2); return ret;}static int incomingim_ch4(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie){ ByteStream meat; aim_rxcallback_t userfunc; aim_tlv_t *block; struct aim_incomingim_ch4_args args; int ret = 0; /* * Make a bstream for the meaty part. Yum. Meat. */ if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1))) return -1; byte_stream_init(&meat, block->value, block->length); args.uin = byte_stream_getle32(&meat); args.type = byte_stream_getle8(&meat); args.flags = byte_stream_getle8(&meat); args.msglen = byte_stream_getle16(&meat); args.msg = (gchar *)byte_stream_getraw(&meat, args.msglen); if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, channel, userinfo, &args); g_free(args.msg); return ret;}/* * Subtype 0x0007 * * It can easily be said that parsing ICBMs is THE single * most difficult thing to do in the in AIM protocol. In * fact, I think I just did say that. * * Below is the best damned solution I've come up with * over the past sixteen months of battling with it. This * can parse both away and normal messages from every client * I have access to. Its not fast, its not clean. But it works. * */static int incomingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs){ int ret = 0; guchar *cookie; guint16 channel; aim_userinfo_t userinfo; memset(&userinfo, 0x00, sizeof(aim_userinfo_t)); /* * Read ICBM Cookie. */ cookie = byte_stream_getraw(bs, 8); /* * Channel ID. * * Channel 0x0001 is the message channel. It is * used to send basic ICBMs. * * Channel 0x0002 is the Rendezvous channel, which * is where Chat Invitiations and various client-client * connection negotiations come from. * * Channel 0x0003 is used for chat messages. * * Channel 0x0004 is used for ICQ authorization, or * possibly any system notice. * */ channel = byte_stream_get16(bs); /* * Extract the standard user info block. * * Note that although this contains TLVs that appear contiguous * with the TLVs read below, they are two different pieces. The * userinfo block contains the number of TLVs that contain user * information, the rest are not even though there is no separation. * You can start reading the message TLVs after aim_info_extract() * parses out the standard userinfo block. * * That also means that TLV types can be duplicated between the * userinfo block and the rest of the message, however there should * never be two TLVs of the same type in one block. * */ aim_info_extract(od, bs, &userinfo); /* * From here on, its depends on what channel we're on. * * Technically all channels have a TLV list have this, however, * for the common channel 1 case, in-place parsing is used for * performance reasons (
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -