📄 family_icbm.c
字号:
/* * Purple's oscar protocol plugin * This file is the legal property of its developers. * Please see the AUTHORS file distributed alongside this file. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*//* * Family 0x0004 - Routines for sending/receiving Instant Messages. * * Note the term ICBM (Inter-Client Basic Message) which blankets * all types of generically routed through-server messages. Within * the ICBM types (family 4), a channel is defined. Each channel * represents a different type of message. Channel 1 is used for * what would commonly be called an "instant message". Channel 2 * is used for negotiating "rendezvous". These transactions end in * something more complex happening, such as a chat invitation, or * a file transfer. Channel 3 is used for chat messages (not in * the same family as these channels). Channel 4 is used for * various ICQ messages. Examples are normal messages, URLs, and * old-style authorization. * * In addition to the channel, every ICBM contains a cookie. For * standard IMs, these are only used for error messages. However, * the more complex rendezvous messages make suitably more complex * use of this field. * * TODO: Split this up into an im.c file an an icbm.c file. It * will be beautiful, you'll see. * * Make sure flap_connection_findbygroup is used by all functions. */#include "oscar.h"#include "peer.h"#ifdef _WIN32#include "win32dep.h"#endif/** * Add a standard ICBM header to the given bstream with the given * information. * * @param bs The bstream to write the ICBM header to. * @param c c is for cookie, and cookie is for me. * @param channel The ICBM channel (1 through 4). * @param sn Null-terminated scrizeen nizame. * @return The number of bytes written. It's really not useful. */static int aim_im_puticbm(ByteStream *bs, const guchar *c, guint16 channel, const char *sn){ byte_stream_putraw(bs, c, 8); byte_stream_put16(bs, channel); byte_stream_put8(bs, strlen(sn)); byte_stream_putstr(bs, sn); return 8+2+1+strlen(sn);}/** * Generates a random ICBM cookie in a character array of length 8 * and copies it into the variable passed as cookie * TODO: Maybe we should stop limiting our characters to the visible range? */void aim_icbm_makecookie(guchar *cookie){ int i; /* Should be like "21CBF95" and null terminated */ for (i = 0; i < 7; i++) cookie[i] = 0x30 + ((guchar)rand() % 10); cookie[7] = '\0';}/* * Takes a msghdr (and a length) and returns a client type * code. Note that this is *only a guess* and has a low likelihood * of actually being accurate. * * Its based on experimental data, with the help of Eric Warmenhoven * who seems to have collected a wide variety of different AIM clients. * * * Heres the current collection: * 0501 0003 0101 0101 01 AOL Mobile Communicator, WinAIM 1.0.414 * 0501 0003 0101 0201 01 WinAIM 2.0.847, 2.1.1187, 3.0.1464, * 4.3.2229, 4.4.2286 * 0501 0004 0101 0102 0101 WinAIM 4.1.2010, libfaim (right here) * 0501 0003 0101 02 WinAIM 5 * 0501 0001 01 iChat x.x, mobile buddies * 0501 0001 0101 01 AOL v6.0, CompuServe 2000 v6.0, any TOC client * 0501 0002 0106 WinICQ 5.45.1.3777.85 * * Note that in this function, only the feature bytes are tested, since * the rest will always be the same. * */guint16 aim_im_fingerprint(const guint8 *msghdr, int len){ static const struct { guint16 clientid; int len; guint8 data[10]; } fingerprints[] = { /* AOL Mobile Communicator, WinAIM 1.0.414 */ { AIM_CLIENTTYPE_MC, 3, {0x01, 0x01, 0x01}}, /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */ { AIM_CLIENTTYPE_WINAIM, 3, {0x01, 0x01, 0x02}}, /* WinAIM 4.1.2010, libfaim */ { AIM_CLIENTTYPE_WINAIM41, 4, {0x01, 0x01, 0x01, 0x02}}, /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */ { AIM_CLIENTTYPE_AOL_TOC, 1, {0x01}}, { 0, 0, {0x00}} }; int i; if (!msghdr || (len <= 0)) return AIM_CLIENTTYPE_UNKNOWN; for (i = 0; fingerprints[i].len; i++) { if (fingerprints[i].len != len) continue; if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0) return fingerprints[i].clientid; } return AIM_CLIENTTYPE_UNKNOWN;}/** * Subtype 0x0002 - Set ICBM parameters. * * I definitely recommend sending this. If you don't, you'll be stuck * with the rather unreasonable defaults. * */int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params){ FlapConnection *conn; FlapFrame *frame; aim_snacid_t snacid; if (!od || !(conn = flap_connection_findbygroup(od, 0x0004))) return -EINVAL; if (!params) return -EINVAL; frame = flap_frame_new(od, 0x02, 10+16); snacid = aim_cachesnac(od, 0x0004, 0x0002, 0x0000, NULL, 0); aim_putsnac(&frame->data, 0x0004, 0x0002, 0x0000, snacid); /* This is read-only (see Parameter Reply). Must be set to zero here. */ byte_stream_put16(&frame->data, 0x0000); /* These are all read-write */ byte_stream_put32(&frame->data, params->flags); byte_stream_put16(&frame->data, params->maxmsglen); byte_stream_put16(&frame->data, params->maxsenderwarn); byte_stream_put16(&frame->data, params->maxrecverwarn); byte_stream_put32(&frame->data, params->minmsginterval); flap_connection_send(conn, frame); return 0;}/** * Subtype 0x0004 - Request ICBM parameter information. * */int aim_im_reqparams(OscarData *od){ FlapConnection *conn; if (!od || !(conn = flap_connection_findbygroup(od, 0x0004))) return -EINVAL; aim_genericreq_n_snacid(od, conn, 0x0004, 0x0004); return 0;}/** * Subtype 0x0005 - Receive parameter information. * */static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs){ aim_rxcallback_t userfunc; struct aim_icbmparameters params; params.maxchan = byte_stream_get16(bs); params.flags = byte_stream_get32(bs); params.maxmsglen = byte_stream_get16(bs); params.maxsenderwarn = byte_stream_get16(bs); params.maxrecverwarn = byte_stream_get16(bs); params.minmsginterval = byte_stream_get32(bs); if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) return userfunc(od, conn, frame, ¶ms); return 0;}/** * Subtype 0x0006 - Send an ICBM (instant message). * * * Possible flags: * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse * AIM_IMFLAGS_ACK -- Requests that the server send an ack * when the message is received (of type 0x0004/0x000c) * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are * online (probably ICQ only). * * Generally, you should use the lowest encoding possible to send * your message. If you only use basic punctuation and the generic * Latin alphabet, use ASCII7 (no flags). If you happen to use non-ASCII7 * characters, but they are all clearly defined in ISO-8859-1, then * use that. Keep in mind that not all characters in the PC ASCII8 * character set are defined in the ISO standard. For those cases (most * notably when the (r) symbol is used), you must use the full UNICODE * encoding for your message. In UNICODE mode, _all_ characters must * occupy 16bits, including ones that are not special. (Remember that * the first 128 UNICODE symbols are equivalent to ASCII7, however they * must be prefixed with a zero high order byte.) * * I strongly discourage the use of UNICODE mode, mainly because none * of the clients I use can parse those messages (and besides that, * wchars are difficult and non-portable to handle in most UNIX environments). * If you really need to include special characters, use the HTML UNICODE * entities. These are of the form ߪ where 2026 is the hex * representation of the UNICODE index (in this case, UNICODE * "Horizontal Ellipsis", or 133 in in ASCII8). * * Implementation note: Since this is one of the most-used functions * in all of libfaim, it is written with performance in mind. As such, * it is not as clear as it could be in respect to how this message is * supposed to be layed out. Most obviously, tlvlists should be used * instead of writing out the bytes manually. * * XXX - more precise verification that we never send SNACs larger than 8192 * XXX - check SNAC size for multipart * */int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args){ FlapConnection *conn; aim_snacid_t snacid; ByteStream data; guchar cookie[8]; int msgtlvlen; static const guint8 deffeatures[] = { 0x01, 0x01, 0x01, 0x02 }; if (!od || !(conn = flap_connection_findbygroup(od, 0x0004))) return -EINVAL; if (!args) return -EINVAL; if (args->flags & AIM_IMFLAGS_MULTIPART) { if (args->mpmsg->numparts == 0) return -EINVAL; } else { if (!args->msg || (args->msglen <= 0)) return -EINVAL; if (args->msglen >= MAXMSGLEN) return -E2BIG; } /* Painfully calculate the size of the message TLV */ msgtlvlen = 1 + 1; /* 0501 */ if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) msgtlvlen += 2 + args->featureslen; else msgtlvlen += 2 + sizeof(deffeatures); if (args->flags & AIM_IMFLAGS_MULTIPART) { aim_mpmsg_section_t *sec; for (sec = args->mpmsg->parts; sec; sec = sec->next) { msgtlvlen += 2 /* 0101 */ + 2 /* block len */; msgtlvlen += 4 /* charset */ + sec->datalen; } } else { msgtlvlen += 2 /* 0101 */ + 2 /* block len */; msgtlvlen += 4 /* charset */ + args->msglen; } byte_stream_new(&data, msgtlvlen + 128); /* Generate an ICBM cookie */ aim_icbm_makecookie(cookie); /* ICBM header */ aim_im_puticbm(&data, cookie, 0x0001, args->destsn); /* Message TLV (type 0x0002) */ byte_stream_put16(&data, 0x0002); byte_stream_put16(&data, msgtlvlen); /* Features TLV (type 0x0501) */ byte_stream_put16(&data, 0x0501); if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) { byte_stream_put16(&data, args->featureslen); byte_stream_putraw(&data, args->features, args->featureslen); } else { byte_stream_put16(&data, sizeof(deffeatures)); byte_stream_putraw(&data, deffeatures, sizeof(deffeatures)); } if (args->flags & AIM_IMFLAGS_MULTIPART) { aim_mpmsg_section_t *sec; /* Insert each message part in a TLV (type 0x0101) */ for (sec = args->mpmsg->parts; sec; sec = sec->next) { byte_stream_put16(&data, 0x0101); byte_stream_put16(&data, sec->datalen + 4); byte_stream_put16(&data, sec->charset); byte_stream_put16(&data, sec->charsubset); byte_stream_putraw(&data, (guchar *)sec->data, sec->datalen); } } else { /* Insert message text in a TLV (type 0x0101) */ byte_stream_put16(&data, 0x0101); /* Message block length */ byte_stream_put16(&data, args->msglen + 0x04); /* Character set */ byte_stream_put16(&data, args->charset); byte_stream_put16(&data, args->charsubset); /* Message. Not terminated */ byte_stream_putraw(&data, (guchar *)args->msg, args->msglen); } /* Set the Autoresponse flag */ if (args->flags & AIM_IMFLAGS_AWAY) { byte_stream_put16(&data, 0x0004); byte_stream_put16(&data, 0x0000); } else if (args->flags & AIM_IMFLAGS_ACK) { /* Set the Request Acknowledge flag */ byte_stream_put16(&data, 0x0003); byte_stream_put16(&data, 0x0000); } if (args->flags & AIM_IMFLAGS_OFFLINE) { byte_stream_put16(&data, 0x0006); byte_stream_put16(&data, 0x0000); } /* * Set the I HAVE A REALLY PURTY ICON flag. * XXX - This should really only be sent on initial * IMs and when you change your icon. */ if (args->flags & AIM_IMFLAGS_HASICON) { byte_stream_put16(&data, 0x0008); byte_stream_put16(&data, 0x000c); byte_stream_put32(&data, args->iconlen); byte_stream_put16(&data, 0x0001); byte_stream_put16(&data, args->iconsum); byte_stream_put32(&data, args->iconstamp); } /* * Set the Buddy Icon Requested flag. * XXX - Every time? Surely not... */ if (args->flags & AIM_IMFLAGS_BUDDYREQ) { byte_stream_put16(&data, 0x0009); byte_stream_put16(&data, 0x0000); } /* XXX - should be optional */ snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1); flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &data); g_free(data.data); /* clean out SNACs over 60sec old */ aim_cleansnacs(od, 60); return 0;}/* * Simple wrapper for aim_im_sendch1_ext() * * You cannot use aim_send_im if you need the HASICON flag. You must * use aim_im_sendch1_ext directly for that. * * aim_send_im also cannot be used if you require UNICODE messages, because * that requires an explicit message length. Use aim_im_sendch1_ext(). * */int aim_im_sendch1(OscarData *od, const char *sn, guint16 flags, const char *msg){ struct aim_sendimext_args args; args.destsn = sn; args.flags = flags; args.msg = msg; args.msglen = strlen(msg); args.charset = 0x0000; args.charsubset = 0x0000; /* Make these don't get set by accident -- they need aim_im_sendch1_ext */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -